From 1a7625c0bcefb0761dd17f329fce90ffb1fd6909 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Wed, 12 Feb 2020 13:46:28 +0100 Subject: [PATCH] Allow stalled endpoints to be reset. --- usbscsid/src/protocol/bot.rs | 2 +- xhcid/src/xhci/scheme.rs | 63 ++++++++++++++++++++++++++++++++++-- xhcid/src/xhci/trb.rs | 30 ++++++++++++++++- 3 files changed, 90 insertions(+), 5 deletions(-) diff --git a/usbscsid/src/protocol/bot.rs b/usbscsid/src/protocol/bot.rs index dc2af73f54..faa2e427be 100644 --- a/usbscsid/src/protocol/bot.rs +++ b/usbscsid/src/protocol/bot.rs @@ -151,7 +151,7 @@ impl<'a> Protocol for BulkOnlyTransport<'a> { PortTransferStatus::ShortPacket(31) => (), PortTransferStatus::Stalled => { println!("bulk out endpoint stalled when sending CBW"); - self.reset_recovery()?; + self.clear_stall_out()?; dbg!(self.bulk_in.status()?, self.bulk_out.status()?); } _ => panic!("invalid number of CBW bytes written; expected a short packed of length 31 (0x1F)"), diff --git a/xhcid/src/xhci/scheme.rs b/xhcid/src/xhci/scheme.rs index 2f78a222e2..38cb196584 100644 --- a/xhcid/src/xhci/scheme.rs +++ b/xhcid/src/xhci/scheme.rs @@ -18,7 +18,7 @@ use super::{Device, EndpointState, Xhci}; use super::command::CommandRing; use super::context::{ - InputContext, SlotState, StreamContext, StreamContextArray, ENDPOINT_CONTEXT_STATUS_MASK, + InputContext, SlotState, StreamContext, StreamContextArray, StreamContextType, ENDPOINT_CONTEXT_STATUS_MASK, }; use super::doorbell::Doorbell; use super::extended::ProtocolSpeed; @@ -1450,7 +1450,64 @@ impl Xhci { _ => return Err(Error::new(EIO)), }) } - pub fn on_req_reset_device(&mut self, port_num: usize, endp_num: u8, no_clear_feature: bool) { + pub fn on_req_reset_device(&mut self, port_num: usize, endp_num: u8, no_clear_feature: bool) -> Result<()> { + // Change the endpoint state from anything, but most likely HALTED (otherwise resetting + // would be quite meaningless), to stopped. + self.reset_endpoint(port_num, endp_num, false)?; + self.restart_endpoint(port_num, endp_num) + } + pub fn restart_endpoint(&mut self, port_num: usize, endp_num: u8) -> Result<()> { + let port_state = self.port_states.get_mut(&port_num).ok_or(Error::new(EBADFD))?; + let endpoint_state: &mut EndpointState = port_state.endpoint_states.get_mut(&endp_num).ok_or(Error::new(EBADFD))?; + let ring = match &mut endpoint_state.transfer { + &mut super::RingOrStreams::Ring(ref mut ring) => ring, + &mut super::RingOrStreams::Streams(ref mut arr) => arr.rings.get_mut(&1).ok_or(Error::new(EBADFD))?, + }; + let (cmd, cycle) = ring.next(); + cmd.transfer_no_op(0, false, false, false, false); + let deque_ptr_and_cycle = ring.register(); + let slot = port_state.slot; + + self.set_tr_deque_ptr(slot, endp_num, deque_ptr_and_cycle)?; + + let endp_num_xhc = endp_num + 1; + self.dbs[slot as usize].write( + (1 << 16) // stream id + | u32::from(endp_num_xhc) + ); + Ok(()) + } + pub fn set_tr_deque_ptr(&mut self, slot: u8, endp_num: u8, deque_ptr_and_cycle: u64) -> Result<()> { + let endp_num_xhc = endp_num + 1; + + // TODO: Merge these command boilerplates into a single function. + self.run.ints[0].erdp.write(self.cmd.erdp()); + + { + let (cmd, cycle, event) = self.cmd.next(); + + + // TODO: I guess this very command is the one used to actually multiplex between + // streams. + cmd.set_tr_deque_ptr(deque_ptr_and_cycle, cycle, StreamContextType::PrimaryRing, 1, endp_num_xhc, slot); + + self.dbs[0].write(0); + + while event.data.read() == 0 { + println!(" - Waiting for event"); + } + + if event.completion_code() != TrbCompletionCode::Success as u8 + || event.trb_type() != TrbType::CommandCompletion as u8 + { + println!("SET_TR_DEQUEUE_POINTER failed with event TRB ({:#0x} {:#0x} {:#0x}) and command TRB ({:#0x} {:#0x} {:#0x})", event.data.read(), event.status.read(), event.control.read(), cmd.data.read(), cmd.status.read(), cmd.control.read()); + return Err(Error::new(EIO)); + } + + cmd.reserved(false); + event.reserved(false); + } + Ok(()) } pub fn on_write_endp_ctl(&mut self, port_num: usize, endp_num: u8, buf: &[u8]) -> Result { let ep_if_state = &mut self.port_states.get_mut(&port_num).ok_or(Error::new(EBADF))?.endpoint_states.get_mut(&endp_num).ok_or(Error::new(EBADF))?.driver_if_state; @@ -1461,7 +1518,7 @@ impl Xhci { _ => return Err(Error::new(EBADF)), } XhciEndpCtlReq::Reset { no_clear_feature } => match ep_if_state { - EndpIfState::Init => self.on_req_reset_device(port_num, endp_num, no_clear_feature), + EndpIfState::Init => self.on_req_reset_device(port_num, endp_num, no_clear_feature)?, _ => return Err(Error::new(EBADF)), } XhciEndpCtlReq::Transfer(direction) => match ep_if_state { diff --git a/xhcid/src/xhci/trb.rs b/xhcid/src/xhci/trb.rs index 9af2b96ee0..8e070d36e6 100644 --- a/xhcid/src/xhci/trb.rs +++ b/xhcid/src/xhci/trb.rs @@ -2,6 +2,8 @@ use crate::usb; use std::{fmt, mem}; use syscall::io::{Io, Mmio}; +use super::context::StreamContextType; + #[repr(u8)] pub enum TrbType { Reserved, @@ -242,6 +244,20 @@ impl Trb { | u32::from(cycle), ); } + /// The deque_ptr has to contain the DCS bit (bit 0). + pub fn set_tr_deque_ptr(&mut self, deque_ptr: u64, cycle: bool, sct: StreamContextType, stream_id: u16, endp_num_xhc: u8, slot: u8) { + assert_eq!(deque_ptr & 0xFFFF_FFFF_FFFF_FFF1, deque_ptr); + assert_eq!(endp_num_xhc & 0x1F, endp_num_xhc); + + self.set( + deque_ptr | ((sct as u64) << 1), + u32::from(stream_id) << 16, + (u32::from(slot) << 24) + | (u32::from(endp_num_xhc) << 16) + | ((TrbType::SetTrDequeuePointer as u32) << 10) + | u32::from(cycle), + ) + } pub fn stop_endpoint(&mut self, slot_id: u8, endp_num_xhc: u8, suspend: bool, cycle: bool) { assert_eq!(endp_num_xhc & 0x1F, endp_num_xhc); self.set( @@ -262,6 +278,18 @@ impl Trb { ); } + pub fn transfer_no_op(&mut self, interrupter: u8, ent: bool, ch: bool, ioc: bool, cycle: bool) { + self.set( + 0, + u32::from(interrupter) << 22, + ((TrbType::NoOp as u32) << 10) + | (u32::from(ioc) << 5) + | (u32::from(ch) << 4) + | (u32::from(ent) << 1) + | u32::from(cycle) + ); + } + pub fn setup(&mut self, setup: usb::Setup, transfer: TransferKind, cycle: bool) { self.set( unsafe { mem::transmute(setup) }, @@ -309,7 +337,7 @@ impl Trb { // NOTE: The interrupter target and no snoop flags have been omitted. self.set( buffer, - u32::from(len) | (u32::from(estimated_td_size) << 17) | (u32::from(interrupter) << 21), + u32::from(len) | (u32::from(estimated_td_size) << 17) | (u32::from(interrupter) << 22), u32::from(cycle) | (u32::from(ent) << 1) | (u32::from(isp) << 2)