Allow stalled endpoints to be reset.
This commit is contained in:
@@ -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)"),
|
||||
|
||||
@@ -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<usize> {
|
||||
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 {
|
||||
|
||||
+29
-1
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user