diff --git a/xhcid/src/xhci/irq_reactor.rs b/xhcid/src/xhci/irq_reactor.rs index a697561ad5..45d22a0eb7 100644 --- a/xhcid/src/xhci/irq_reactor.rs +++ b/xhcid/src/xhci/irq_reactor.rs @@ -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 + 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 + 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)), }, diff --git a/xhcid/src/xhci/mod.rs b/xhcid/src/xhci/mod.rs index 4e4da6fbd7..33762adf84 100644 --- a/xhcid/src/xhci/mod.rs +++ b/xhcid/src/xhci/mod.rs @@ -85,12 +85,14 @@ impl Xhci { /// Gets descriptors, before the port state is initiated. async fn get_desc_raw(&self, port: usize, slot: u8, kind: usb::DescriptorKind, index: u8, desc: &mut Dma) -> Result<()> { let len = mem::size_of::(); + 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()) ) diff --git a/xhcid/src/xhci/scheme.rs b/xhcid/src/xhci/scheme.rs index afb5a200ad..2a12fb5a4c 100644 --- a/xhcid/src/xhci/scheme.rs +++ b/xhcid/src/xhci/scheme.rs @@ -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)) } }