Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
google
GitHub Repository: google/crosvm
Path: blob/main/devices/src/usb/xhci/interrupter.rs
5394 views
1
// Copyright 2019 The ChromiumOS Authors
2
// Use of this source code is governed by a BSD-style license that can be
3
// found in the LICENSE file.
4
5
use std::time::Duration;
6
use std::time::Instant;
7
8
use base::Clock;
9
use base::Error as SysError;
10
use base::Event;
11
use remain::sorted;
12
use thiserror::Error;
13
use vm_memory::GuestAddress;
14
use vm_memory::GuestMemory;
15
16
use super::event_ring::Error as EventRingError;
17
use super::event_ring::EventRing;
18
use super::xhci_abi::CommandCompletionEventTrb;
19
use super::xhci_abi::Error as TrbError;
20
use super::xhci_abi::PortStatusChangeEventTrb;
21
use super::xhci_abi::TransferEventTrb;
22
use super::xhci_abi::Trb;
23
use super::xhci_abi::TrbCast;
24
use super::xhci_abi::TrbCompletionCode;
25
use super::xhci_abi::TrbType;
26
use super::xhci_regs::XhciRegs;
27
use super::xhci_regs::ERDP_EVENT_HANDLER_BUSY;
28
use super::xhci_regs::IMAN_INTERRUPT_PENDING;
29
use super::xhci_regs::USB_STS_EVENT_INTERRUPT;
30
use crate::register_space::Register;
31
32
#[sorted]
33
#[derive(Error, Debug)]
34
pub enum Error {
35
#[error("cannot add event: {0}")]
36
AddEvent(EventRingError),
37
#[error("cannot cast trb: {0}")]
38
CastTrb(TrbError),
39
#[error("cannot send interrupt: {0}")]
40
SendInterrupt(SysError),
41
#[error("cannot set seg table base addr: {0}")]
42
SetSegTableBaseAddr(EventRingError),
43
#[error("cannot set seg table size: {0}")]
44
SetSegTableSize(EventRingError),
45
}
46
47
type Result<T> = std::result::Result<T, Error>;
48
49
/// See spec 4.17 for interrupters. Controller can send an event back to guest kernel driver
50
/// through interrupter.
51
pub struct Interrupter {
52
interrupt_evt: Event,
53
usbsts: Register<u32>,
54
iman: Register<u32>,
55
erdp: Register<u64>,
56
event_handler_busy: bool,
57
enabled: bool,
58
moderation_interval: u16,
59
moderation_counter: u16,
60
event_ring: EventRing,
61
last_interrupt_time: Instant,
62
clock: Clock,
63
}
64
65
impl Interrupter {
66
/// Create a new interrupter.
67
pub fn new(mem: GuestMemory, irq_evt: Event, regs: &XhciRegs) -> Self {
68
let clock = Clock::new();
69
Interrupter {
70
interrupt_evt: irq_evt,
71
usbsts: regs.usbsts.clone(),
72
iman: regs.iman.clone(),
73
erdp: regs.erdp.clone(),
74
event_handler_busy: false,
75
enabled: false,
76
moderation_interval: 4000, // default to 1ms as per xhci 5.5.2.2
77
moderation_counter: 0, // xhci specs leave this as undefined
78
event_ring: EventRing::new(mem),
79
last_interrupt_time: clock.now(),
80
clock,
81
}
82
}
83
84
/// Returns true if event ring is empty.
85
pub fn event_ring_is_empty(&self) -> bool {
86
self.event_ring.is_empty()
87
}
88
89
/// Add event to event ring.
90
fn add_event(&mut self, trb: Trb) -> Result<()> {
91
self.event_ring.add_event(trb).map_err(Error::AddEvent)?;
92
self.interrupt_if_needed()
93
}
94
95
/// Send port status change trb for port.
96
pub fn send_port_status_change_trb(&mut self, port_id: u8) -> Result<()> {
97
let mut trb = Trb::new();
98
let psctrb = trb
99
.cast_mut::<PortStatusChangeEventTrb>()
100
.map_err(Error::CastTrb)?;
101
psctrb.set_port_id(port_id);
102
psctrb.set_completion_code(TrbCompletionCode::Success);
103
psctrb.set_trb_type(TrbType::PortStatusChangeEvent);
104
self.add_event(trb)
105
}
106
107
/// Send command completion trb.
108
pub fn send_command_completion_trb(
109
&mut self,
110
completion_code: TrbCompletionCode,
111
slot_id: u8,
112
trb_addr: GuestAddress,
113
) -> Result<()> {
114
let mut trb = Trb::new();
115
let ctrb = trb
116
.cast_mut::<CommandCompletionEventTrb>()
117
.map_err(Error::CastTrb)?;
118
ctrb.set_trb_pointer(trb_addr.0);
119
ctrb.set_command_completion_parameter(0);
120
ctrb.set_completion_code(completion_code);
121
ctrb.set_trb_type(TrbType::CommandCompletionEvent);
122
ctrb.set_vf_id(0);
123
ctrb.set_slot_id(slot_id);
124
self.add_event(trb)
125
}
126
127
/// Send transfer event trb.
128
pub fn send_transfer_event_trb(
129
&mut self,
130
completion_code: TrbCompletionCode,
131
trb_pointer: u64,
132
transfer_length: u32,
133
event_data: bool,
134
slot_id: u8,
135
endpoint_id: u8,
136
) -> Result<()> {
137
let mut trb = Trb::new();
138
let event_trb = trb.cast_mut::<TransferEventTrb>().map_err(Error::CastTrb)?;
139
event_trb.set_trb_pointer(trb_pointer);
140
event_trb.set_trb_transfer_length(transfer_length);
141
event_trb.set_completion_code(completion_code);
142
event_trb.set_event_data(event_data.into());
143
event_trb.set_trb_type(TrbType::TransferEvent);
144
event_trb.set_endpoint_id(endpoint_id);
145
event_trb.set_slot_id(slot_id);
146
self.add_event(trb)
147
}
148
149
/// Enable/Disable this interrupter.
150
pub fn set_enabled(&mut self, enabled: bool) -> Result<()> {
151
xhci_trace!("interrupter set_enabled({})", enabled);
152
self.enabled = enabled;
153
self.interrupt_if_needed()
154
}
155
156
/// Set interrupt moderation.
157
pub fn set_moderation(&mut self, interval: u16, counter: u16) -> Result<()> {
158
xhci_trace!("interrupter set_moderation({}, {})", interval, counter);
159
self.moderation_interval = interval;
160
self.moderation_counter = counter;
161
self.interrupt_if_needed()
162
}
163
164
/// Set event ring seg table size.
165
pub fn set_event_ring_seg_table_size(&mut self, size: u16) -> Result<()> {
166
xhci_trace!("interrupter set_event_ring_seg_table_size({})", size);
167
self.event_ring
168
.set_seg_table_size(size)
169
.map_err(Error::SetSegTableSize)
170
}
171
172
/// Set event ring segment table base address.
173
pub fn set_event_ring_seg_table_base_addr(&mut self, addr: GuestAddress) -> Result<()> {
174
xhci_trace!("interrupter set_table_base_addr({:#x})", addr.0);
175
self.event_ring
176
.set_seg_table_base_addr(addr)
177
.map_err(Error::SetSegTableBaseAddr)
178
}
179
180
/// Set event ring dequeue pointer.
181
pub fn set_event_ring_dequeue_pointer(&mut self, addr: GuestAddress, busy: bool) -> Result<()> {
182
xhci_trace!(
183
"interrupter set_dequeue_pointer(addr = {:#x}, busy = {})",
184
addr.0,
185
busy
186
);
187
self.event_ring.set_dequeue_pointer(addr);
188
self.event_handler_busy = busy;
189
self.interrupt_if_needed()
190
}
191
192
/// Send and interrupt.
193
pub fn interrupt(&mut self) -> Result<()> {
194
self.event_handler_busy = true;
195
self.usbsts.set_bits(USB_STS_EVENT_INTERRUPT);
196
self.iman.set_bits(IMAN_INTERRUPT_PENDING);
197
self.erdp.set_bits(ERDP_EVENT_HANDLER_BUSY);
198
self.moderation_counter = self.moderation_interval;
199
self.last_interrupt_time = self.clock.now();
200
self.interrupt_evt.signal().map_err(Error::SendInterrupt)
201
}
202
203
fn interrupt_interval(&self) -> Duration {
204
// Formula from xhci spec 4.17.2 in nanoseconds, but we use the imodc value instead of the
205
// imodi value because our implementation automatically adjusts the range of the duration
206
// based on the remaining time left in the moderation counter, which may be software
207
// defined.
208
Duration::new(0, 250 * u32::from(self.moderation_counter))
209
}
210
211
fn interrupt_if_needed(&mut self) -> Result<()> {
212
let can_interrupt = self.last_interrupt_time.elapsed() >= self.interrupt_interval();
213
if self.enabled && can_interrupt && !self.event_ring.is_empty() && !self.event_handler_busy
214
{
215
self.interrupt()?;
216
}
217
Ok(())
218
}
219
}
220
221