xhcid: associate stalls on setup stage with transfers
This commit is contained in:
@@ -66,7 +66,7 @@ impl RingId {
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum StateKind {
|
||||
CommandCompletion { phys_ptr: u64 },
|
||||
Transfer { phys_ptr: u64, ring_id: RingId },
|
||||
Transfer { first_phys_ptr: u64, last_phys_ptr: u64, ring_id: RingId },
|
||||
Other(TrbType),
|
||||
}
|
||||
|
||||
@@ -259,32 +259,42 @@ impl IrqReactor {
|
||||
}
|
||||
}
|
||||
|
||||
StateKind::Transfer { phys_ptr, ring_id } if trb.trb_type() == TrbType::Transfer as u8 => {
|
||||
StateKind::Transfer { first_phys_ptr, last_phys_ptr, ring_id } if trb.trb_type() == TrbType::Transfer as u8 => {
|
||||
if let Some(src_trb) = trb.transfer_event_trb_pointer().map(|ptr| self.hci.get_transfer_trb(ptr, ring_id)).flatten() {
|
||||
if trb.transfer_event_trb_pointer() == Some(phys_ptr) {
|
||||
// Give the source transfer TRB together with the event TRB, to the future.
|
||||
|
||||
let state = self.states.remove(index);
|
||||
*state.message.lock().unwrap() = Some(NextEventTrb {
|
||||
src_trb: Some(src_trb),
|
||||
event_trb: trb.clone(),
|
||||
});
|
||||
state.waker.wake();
|
||||
return;
|
||||
} else if trb.transfer_event_trb_pointer().is_none() {
|
||||
// Ring Overrun, Ring Underrun, or Virtual Function Event Ring Full.
|
||||
//
|
||||
// These errors are caused when either an isoch transfer that shall write data, doesn't
|
||||
// have any data since the ring is empty, or if an isoch receive is impossible due to a
|
||||
// full ring. The Virtual Function Event Ring Full is only for Virtual Machine
|
||||
// Managers, and since this isn't implemented yet, they are irrelevant.
|
||||
//
|
||||
// The best solution here is to differentiate between isoch transfers (and
|
||||
// virtual function event rings when virtualization gets implemented), with
|
||||
// regular commands and transfers, and send the error TRB to all of them, or
|
||||
// possibly an error code wrapped in a Result.
|
||||
self.acknowledge_failed_transfer_trbs(trb);
|
||||
return;
|
||||
match trb.transfer_event_trb_pointer() {
|
||||
Some(phys_ptr) => {
|
||||
let matches = if first_phys_ptr <= last_phys_ptr {
|
||||
phys_ptr >= first_phys_ptr && phys_ptr <= last_phys_ptr
|
||||
} else {
|
||||
// Handle ring buffer wrap
|
||||
phys_ptr >= first_phys_ptr || phys_ptr <= last_phys_ptr
|
||||
};
|
||||
if matches {
|
||||
// Give the source transfer TRB together with the event TRB, to the future.
|
||||
let state = self.states.remove(index);
|
||||
*state.message.lock().unwrap() = Some(NextEventTrb {
|
||||
src_trb: Some(src_trb),
|
||||
event_trb: trb.clone(),
|
||||
});
|
||||
state.waker.wake();
|
||||
return;
|
||||
}
|
||||
},
|
||||
None => {
|
||||
// Ring Overrun, Ring Underrun, or Virtual Function Event Ring Full.
|
||||
//
|
||||
// These errors are caused when either an isoch transfer that shall write data, doesn't
|
||||
// have any data since the ring is empty, or if an isoch receive is impossible due to a
|
||||
// full ring. The Virtual Function Event Ring Full is only for Virtual Machine
|
||||
// Managers, and since this isn't implemented yet, they are irrelevant.
|
||||
//
|
||||
// The best solution here is to differentiate between isoch transfers (and
|
||||
// virtual function event rings when virtualization gets implemented), with
|
||||
// regular commands and transfers, and send the error TRB to all of them, or
|
||||
// possibly an error code wrapped in a Result.
|
||||
self.acknowledge_failed_transfer_trbs(trb);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -445,19 +455,21 @@ impl Xhci {
|
||||
|
||||
Some(function(ring_ref))
|
||||
}
|
||||
pub fn next_transfer_event_trb(&self, ring_id: RingId, ring: &Ring, trb: &Trb, doorbell: EventDoorbell) -> impl Future<Output = NextEventTrb> + Send + Sync + 'static {
|
||||
if ! trb.is_transfer_trb() {
|
||||
panic!("Invalid TRB type given to next_transfer_event_trb(): {} (TRB {:?}. Expected transfer TRB.", trb.trb_type(), trb)
|
||||
pub fn next_transfer_event_trb(&self, ring_id: RingId, ring: &Ring, first_trb: &Trb, last_trb: &Trb, doorbell: EventDoorbell) -> impl Future<Output = NextEventTrb> + Send + Sync + 'static {
|
||||
if ! last_trb.is_transfer_trb() {
|
||||
panic!("Invalid TRB type given to next_transfer_event_trb(): {} (TRB {:?}. Expected transfer TRB.", last_trb.trb_type(), last_trb)
|
||||
}
|
||||
|
||||
let is_isoch_or_vf = trb.trb_type() == TrbType::Isoch as u8;
|
||||
let phys_ptr = ring.trb_phys_ptr(self.cap.ac64(), trb);
|
||||
let is_isoch_or_vf = last_trb.trb_type() == TrbType::Isoch as u8;
|
||||
let first_phys_ptr = ring.trb_phys_ptr(self.cap.ac64(), first_trb);
|
||||
let last_phys_ptr = ring.trb_phys_ptr(self.cap.ac64(), last_trb);
|
||||
EventTrbFuture::Pending {
|
||||
state: FutureState {
|
||||
is_isoch_or_vf,
|
||||
state_kind: StateKind::Transfer {
|
||||
ring_id,
|
||||
phys_ptr,
|
||||
first_phys_ptr,
|
||||
last_phys_ptr,
|
||||
},
|
||||
message: Arc::new(Mutex::new(None)),
|
||||
},
|
||||
|
||||
@@ -85,12 +85,14 @@ impl Xhci {
|
||||
/// Gets descriptors, before the port state is initiated.
|
||||
async fn get_desc_raw<T>(&self, port: usize, slot: u8, kind: usb::DescriptorKind, index: u8, desc: &mut Dma<T>) -> Result<()> {
|
||||
let len = mem::size_of::<T>();
|
||||
log::debug!("get_desc_raw port {} slot {} kind {:?} index {} len {}", port, slot, kind, index, len);
|
||||
|
||||
let future = {
|
||||
let mut port_state = self.port_states.get_mut(&port).ok_or(Error::new(ENOENT))?;
|
||||
let ring = port_state.endpoint_states.get_mut(&0).ok_or(Error::new(EIO))?.ring().expect("no ring for the default control pipe");
|
||||
|
||||
let (cmd, cycle) = ring.next();
|
||||
let first_index = ring.next_index();
|
||||
let (cmd, cycle) = (&mut ring.trbs[first_index], ring.cycle);
|
||||
cmd.setup(
|
||||
usb::Setup::get_descriptor(kind, index, 0, len as u16),
|
||||
TransferKind::In,
|
||||
@@ -114,6 +116,7 @@ impl Xhci {
|
||||
self.next_transfer_event_trb(
|
||||
RingId::default_control_pipe(port as u8),
|
||||
&ring,
|
||||
&ring.trbs[first_index],
|
||||
&ring.trbs[last_index],
|
||||
EventDoorbell::new(self, usize::from(slot), Self::def_control_endp_doorbell())
|
||||
)
|
||||
|
||||
@@ -313,7 +313,8 @@ impl Xhci {
|
||||
.ring()
|
||||
.ok_or(Error::new(EIO))?;
|
||||
|
||||
let (cmd, cycle) = ring.next();
|
||||
let first_index = ring.next_index();
|
||||
let (cmd, cycle) = (&mut ring.trbs[first_index], ring.cycle);
|
||||
cmd.setup(setup, tk, cycle);
|
||||
|
||||
if tk != TransferKind::NoData {
|
||||
@@ -340,6 +341,7 @@ impl Xhci {
|
||||
self.next_transfer_event_trb(
|
||||
RingId::default_control_pipe(port_num as u8),
|
||||
ring,
|
||||
&ring.trbs[first_index],
|
||||
&ring.trbs[last_index],
|
||||
EventDoorbell::new(self, usize::from(slot), Self::def_control_endp_doorbell())
|
||||
)
|
||||
@@ -430,6 +432,8 @@ impl Xhci {
|
||||
break self.next_transfer_event_trb(
|
||||
super::irq_reactor::RingId { port: port_num as u8, endpoint_num: endp_num, stream_id },
|
||||
ring,
|
||||
//TODO: find first TRB
|
||||
&ring.trbs[last_index],
|
||||
&ring.trbs[last_index],
|
||||
EventDoorbell::new(self, usize::from(slot), if has_streams {
|
||||
doorbell_data_stream
|
||||
@@ -2128,7 +2132,7 @@ pub fn handle_transfer_event_trb(name: &str, event_trb: &Trb, transfer_trb: &Trb
|
||||
if event_trb.completion_code() == TrbCompletionCode::Success as u8 || event_trb.completion_code() == TrbCompletionCode::ShortPacket as u8 {
|
||||
Ok(())
|
||||
} else {
|
||||
error!("{} transfer (TRB {:?}) failed with event trb {:?}", name, transfer_trb, event_trb);
|
||||
error!("{} transfer {:?} failed with event {:?}", name, transfer_trb, event_trb);
|
||||
Err(Error::new(EIO))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user