From 05392696feba6002a84d8da1f0144cfbf88a91c3 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Tue, 11 Feb 2020 11:39:29 +0100 Subject: [PATCH] Use more correct params in configure_endpoint. Most importantly in the endpoint contexts of the input context, specifying the properties of the endpoints that are being configured. --- usbscsid/src/protocol/bot.rs | 8 ++- xhcid/src/driver_interface.rs | 46 +++++++++++- xhcid/src/usb/bos.rs | 42 ++++++++++- xhcid/src/usb/endpoint.rs | 11 ++- xhcid/src/usb/mod.rs | 2 +- xhcid/src/xhci/scheme.rs | 131 +++++++++++++++++++++++++++------- 6 files changed, 204 insertions(+), 36 deletions(-) diff --git a/usbscsid/src/protocol/bot.rs b/usbscsid/src/protocol/bot.rs index 86065ac128..ab4c1085b7 100644 --- a/usbscsid/src/protocol/bot.rs +++ b/usbscsid/src/protocol/bot.rs @@ -85,6 +85,7 @@ pub struct BulkOnlyTransport<'a> { bulk_out_num: u8, max_lun: u8, current_tag: u32, + interface_num: u8, } pub const FEATURE_ENDPOINT_HALT: u16 = 0; @@ -96,6 +97,8 @@ impl<'a> BulkOnlyTransport<'a> { let bulk_in_num = (endpoints.iter().position(|endpoint| endpoint.direction() == EndpDirection::In).unwrap() + 1) as u8; let bulk_out_num = (endpoints.iter().position(|endpoint| endpoint.direction() == EndpDirection::Out).unwrap() + 1) as u8; + bulk_only_mass_storage_reset(handle, if_desc.number.into())?; + let max_lun = get_max_lun(handle, 0)?; println!("BOT_MAX_LUN {}", max_lun); @@ -109,13 +112,14 @@ impl<'a> BulkOnlyTransport<'a> { handle, max_lun, current_tag: 0, + interface_num: if_desc.number, }) } fn clear_stall(&mut self, endp_num: u8) -> Result<(), XhciClientHandleError> { self.handle.clear_feature(PortReqRecipient::Endpoint, u16::from(endp_num), FEATURE_ENDPOINT_HALT) } fn reset_recovery(&mut self) -> Result<(), ProtocolError> { - bulk_only_mass_storage_reset(self.handle, 0)?; + bulk_only_mass_storage_reset(self.handle, self.interface_num.into())?; self.clear_stall(self.bulk_in_num.into())?; self.clear_stall(self.bulk_out_num.into())?; @@ -128,8 +132,6 @@ impl<'a> BulkOnlyTransport<'a> { impl<'a> Protocol for BulkOnlyTransport<'a> { fn send_command(&mut self, cb: &[u8], data: DeviceReqData) -> Result<(), ProtocolError> { - dbg!(self.bulk_in_status.current_status()?); - dbg!(self.bulk_out_status.current_status()?); self.current_tag += 1; let tag = self.current_tag; diff --git a/xhcid/src/driver_interface.rs b/xhcid/src/driver_interface.rs index e029e4ab94..7fbc3b1f49 100644 --- a/xhcid/src/driver_interface.rs +++ b/xhcid/src/driver_interface.rs @@ -38,6 +38,15 @@ pub struct DevDesc { pub config_descs: SmallVec<[ConfDesc; 1]>, } +impl DevDesc { + pub fn major_version(&self) -> u8 { + ((self.usb & 0xFF00) >> 8) as u8 + } + pub fn minor_version(&self) -> u8 { + self.usb as u8 + } +} + #[derive(Clone, Debug, Serialize, Deserialize)] pub struct ConfDesc { pub kind: u8, @@ -56,6 +65,7 @@ pub struct EndpDesc { pub max_packet_size: u16, pub interval: u8, pub ssc: Option, + pub sspc: Option, } #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub enum EndpDirection { @@ -131,6 +141,31 @@ impl EndpDesc { _ => return Err(Error::new(EINVAL)), }) } + pub fn is_superspeed(&self) -> bool { + self.ssc.is_some() + } + pub fn is_superspeedplus(&self) -> bool { + todo!() + } + fn interrupt_usage_bits(&self) -> u8 { + assert!(self.is_interrupt()); + (self.attributes & 0x20) >> 4 + } + pub fn is_periodic(&self) -> bool { + #[repr(u8)] + enum InterruptUsageBits { + Periodic, + Notification, + Rsvd2, + Rsvd3, + } + + if self.is_interrupt() { + self.interrupt_usage_bits() == InterruptUsageBits::Periodic as u8 + } else { + self.is_isoch() + } + } pub fn max_streams(&self) -> u8 { self.ssc .as_ref() @@ -156,6 +191,9 @@ impl EndpDesc { pub fn max_burst(&self) -> u8 { self.ssc.map(|ssc| ssc.max_burst).unwrap_or(0) } + pub fn has_ssp_companion(&self) -> bool { + self.ssc.map(|ssc| ssc.attributes & (1 << 7) != 0).unwrap_or(false) + } } #[derive(Clone, Debug, Serialize, Deserialize)] pub struct IfDesc { @@ -177,7 +215,11 @@ pub struct SuperSpeedCmp { pub attributes: u8, pub bytes_per_interval: u16, } - +#[derive(Clone, Copy, Debug, Serialize, Deserialize)] +pub struct SuperSpeedPlusIsochCmp { + pub kind: u8, + pub bytes_per_interval: u32, +} #[derive(Clone, Copy, Debug, Serialize, Deserialize)] pub struct HidDesc { pub kind: u8, @@ -536,7 +578,7 @@ impl XhciEndpTransferHandle { let mut status_buf = [0u8; 32]; let status_bytes_read = self.0.read(&mut status_buf)?; - let status = serde_json::from_slice(dbg!(&status_buf[..status_bytes_read]))?; + let status = serde_json::from_slice(&status_buf[..status_bytes_read])?; if let PortTransferStatus::ShortPacket(len) = status { if len as usize != bytes_transferred { diff --git a/xhcid/src/usb/bos.rs b/xhcid/src/usb/bos.rs index d2ef922288..116a8f6d76 100644 --- a/xhcid/src/usb/bos.rs +++ b/xhcid/src/usb/bos.rs @@ -1,3 +1,5 @@ +use std::slice; + #[repr(packed)] #[derive(Clone, Copy, Debug, Default)] pub struct BosDescriptor { @@ -32,6 +34,21 @@ pub struct BosSuperSpeedDesc { pub u1_dev_exit_lat: u8, pub u2_dev_exit_lat: u16, } +#[repr(packed)] +#[derive(Clone, Copy, Debug, Default)] +pub struct BosSuperSpeedPlusDesc { + pub len: u8, + pub kind: u8, + pub cap_ty: u8, + pub _rsvd0: u8, + pub attrs: u32, + pub func_supp: u32, + pub _rsvd1: u16, + sublink_speed_attr: [u32; 0], +} + +unsafe impl plain::Plain for BosSuperSpeedPlusDesc {} + #[repr(packed)] #[derive(Clone, Copy, Debug, Default)] pub struct BosUsb2ExtDesc { @@ -46,12 +63,22 @@ unsafe impl plain::Plain for BosUsb2ExtDesc {} #[repr(u8)] pub enum DeviceCapability { - Usb2Ext = 2, - SuperSpeed = 3, + Usb2Ext = 0x02, + SuperSpeed, + SuperSpeedPlus = 0x0A, } unsafe impl plain::Plain for BosSuperSpeedDesc {} +impl BosSuperSpeedPlusDesc { + pub fn ssac(&self) -> u8 { + (self.attrs & 0x0000_000F) as u8 + } + pub fn sublink_speed_attr(&self) -> &[u32] { + unsafe { slice::from_raw_parts(&self.sublink_speed_attr as *const u32, self.ssac() as usize + 1) } + } +} + pub struct BosDevDescIter<'a> { bytes: &'a [u8], } @@ -84,8 +111,9 @@ impl<'a> Iterator for BosDevDescIter<'a> { #[derive(Clone, Copy, Debug)] pub enum BosAnyDevDesc { - SuperSpeed(BosSuperSpeedDesc), Usb2Ext(BosUsb2ExtDesc), + SuperSpeed(BosSuperSpeedDesc), + SuperSpeedPlus(BosSuperSpeedPlusDesc), } impl BosAnyDevDesc { @@ -95,6 +123,12 @@ impl BosAnyDevDesc { _ => false, } } + pub fn is_superspeedplus(&self) -> bool { + match self { + Self::SuperSpeedPlus(_) => true, + _ => false, + } + } } pub struct BosAnyDevDescIter<'a> { @@ -120,6 +154,8 @@ impl<'a> Iterator for BosAnyDevDescIter<'a> { Some(BosAnyDevDesc::Usb2Ext(*plain::from_bytes(slice).ok()?)) } else if base.cap_ty == DeviceCapability::SuperSpeed as u8 { Some(BosAnyDevDesc::SuperSpeed(*plain::from_bytes(slice).ok()?)) + } else if base.cap_ty == DeviceCapability::SuperSpeedPlus as u8 { + Some(BosAnyDevDesc::SuperSpeedPlus(*plain::from_bytes(slice).ok()?)) } else if base.cap_ty == 0 { // TODO return None; diff --git a/xhcid/src/usb/endpoint.rs b/xhcid/src/usb/endpoint.rs index f6aa200b56..42ab8b1022 100644 --- a/xhcid/src/usb/endpoint.rs +++ b/xhcid/src/usb/endpoint.rs @@ -45,9 +45,18 @@ pub struct SuperSpeedCompanionDescriptor { pub attributes: u8, pub bytes_per_interval: u16, } - unsafe impl Plain for SuperSpeedCompanionDescriptor {} +#[repr(packed)] +#[derive(Clone, Copy, Debug, Default)] +pub struct SuperSpeedPlusIsochCmpDescriptor { + pub length: u8, + pub kind: u8, + pub reserved: u16, + pub bytes_per_interval: u32, +} +unsafe impl Plain for SuperSpeedPlusIsochCmpDescriptor {} + #[repr(packed)] #[derive(Clone, Copy, Debug, Default)] pub struct HidDescriptor { diff --git a/xhcid/src/usb/mod.rs b/xhcid/src/usb/mod.rs index f6000a7843..4b22430793 100644 --- a/xhcid/src/usb/mod.rs +++ b/xhcid/src/usb/mod.rs @@ -2,7 +2,7 @@ pub use self::bos::{bos_capability_descs, BosAnyDevDesc, BosDescriptor, BosSuper pub use self::config::ConfigDescriptor; pub use self::device::DeviceDescriptor; pub use self::endpoint::{ - EndpointDescriptor, EndpointTy, HidDescriptor, SuperSpeedCompanionDescriptor, ENDP_ATTR_TY_MASK, + EndpointDescriptor, EndpointTy, HidDescriptor, SuperSpeedCompanionDescriptor, SuperSpeedPlusIsochCmpDescriptor, ENDP_ATTR_TY_MASK, }; pub use self::interface::InterfaceDescriptor; pub use self::setup::Setup; diff --git a/xhcid/src/xhci/scheme.rs b/xhcid/src/xhci/scheme.rs index 0ee5dc2448..3ca6a3953e 100644 --- a/xhcid/src/xhci/scheme.rs +++ b/xhcid/src/xhci/scheme.rs @@ -21,6 +21,7 @@ use super::context::{ InputContext, SlotState, StreamContext, StreamContextArray, ENDPOINT_CONTEXT_STATUS_MASK, }; use super::doorbell::Doorbell; +use super::extended::ProtocolSpeed; use super::operational::OperationalRegs; use super::ring::Ring; use super::runtime::RuntimeRegs; @@ -82,6 +83,7 @@ impl From for EndpDesc { interval: d.interval, max_packet_size: d.max_packet_size, ssc: None, + sspc: None, } } } @@ -111,6 +113,15 @@ impl From for SuperSpeedCmp { } } } +impl From for SuperSpeedPlusIsochCmp { + fn from(r: usb::SuperSpeedPlusIsochCmpDescriptor) -> Self { + Self { + kind: r.kind, + bytes_per_interval: r.bytes_per_interval, + } + } +} + impl IfDesc { fn new( dev: &mut Device, @@ -146,6 +157,7 @@ pub enum AnyDescriptor { Endpoint(usb::EndpointDescriptor), Hid(usb::HidDescriptor), SuperSpeedCompanion(usb::SuperSpeedCompanionDescriptor), + SuperSpeedPlusCompanion(usb::SuperSpeedPlusIsochCmpDescriptor), } impl AnyDescriptor { @@ -169,6 +181,7 @@ impl AnyDescriptor { 5 => Self::Endpoint(*plain::from_bytes(bytes).ok()?), 33 => Self::Hid(*plain::from_bytes(bytes).ok()?), 48 => Self::SuperSpeedCompanion(*plain::from_bytes(bytes).ok()?), + 49 => Self::SuperSpeedPlusCompanion(*plain::from_bytes(bytes).ok()?), _ => { //panic!("Descriptor unknown {}: bytes {:#0x?}", kind, bytes); return None; @@ -269,6 +282,65 @@ impl Xhci { Ok(()) } + fn endp_ctx_interval(speed_id: &ProtocolSpeed, endp_desc: &EndpDesc) -> u8 { + /// Logarithmic (base 2) 125 µs periods per millisecond. + const MILLISEC_PERIODS: u8 = 3; + + // TODO: Also check the Speed ID for superspeed(plus). + if (speed_id.is_lowspeed() || speed_id.is_fullspeed()) && endp_desc.is_interrupt() { + // The interval field has values 1-255, ranging from 1 ms to 255 ms. + // TODO: This is correct, right? + let last_power_of_two = 8 - endp_desc.interval.leading_zeros() as u8; + last_power_of_two - 1 + MILLISEC_PERIODS + } else if speed_id.is_fullspeed() && endp_desc.is_isoch() { + // bInterval has values 1-16, ranging from 1 ms to 32,768 ms. + endp_desc.interval - 1 + MILLISEC_PERIODS + } else if (speed_id.is_fullspeed() || endp_desc.is_superspeed() || endp_desc.is_superspeedplus()) && (endp_desc.is_interrupt() || endp_desc.is_isoch()) { + // bInterval has values 1-16, but ranging from 125 µs to 4096 ms. + endp_desc.interval - 1 + } else { + // This includes superspeed(plus) control and bulk endpoints in particular. + 0 + } + } + fn endp_ctx_max_burst(speed_id: &ProtocolSpeed, dev_desc: &DevDesc, endp_desc: &EndpDesc) -> u8 { + if speed_id.is_highspeed() && (endp_desc.is_interrupt() || endp_desc.is_isoch()) { + assert_eq!(dev_desc.major_version(), 2); + ((endp_desc.max_packet_size & 0x0C00) >> 11) as u8 + } else if endp_desc.is_superspeed() { + endp_desc.max_burst() + } else { + 0 + } + + } + fn endp_ctx_max_packet_size(endp_desc: &EndpDesc) -> u16 { + // TODO: Control endpoint? Encoding? + endp_desc.max_packet_size & 0x03FF + } + fn endp_ctx_max_esit_payload(speed_id: &ProtocolSpeed, dev_desc: &DevDesc, endp_desc: &EndpDesc, max_packet_size: u16, max_burst_size: u8) -> u32 { + const KIB: u32 = 1024; + + if dev_desc.major_version() == 2 && endp_desc.is_periodic() { + u32::from(max_packet_size) * (u32::from(max_burst_size) + 1) + } else if !endp_desc.has_ssp_companion() { + u32::from(endp_desc.ssc.as_ref().unwrap().bytes_per_interval) + } else if endp_desc.has_ssp_companion() { + endp_desc.sspc.as_ref().unwrap().bytes_per_interval + } else if speed_id.is_fullspeed() && endp_desc.is_interrupt() { + 64 + } else if speed_id.is_fullspeed() && endp_desc.is_isoch() { + 1 * KIB + } else if (speed_id.is_highspeed() && (endp_desc.is_interrupt() || endp_desc.is_isoch())) || endp_desc.is_superspeed() && endp_desc.is_interrupt() { + 3 * KIB + } else if endp_desc.is_superspeed() && endp_desc.is_isoch() { + 48 * KIB + } else { + // TODO: Is "maximum allowed" ESIT payload, the same as "maximum" ESIT payload. + 0 + } + } + fn configure_endpoints(&mut self, port: usize, json_buf: &[u8]) -> Result<()> { let mut req: ConfigureEndpointsReq = serde_json::from_slice(json_buf).or(Err(Error::new(EBADMSG)))?; @@ -286,9 +358,7 @@ impl Xhci { } let port_speed_id = self.ports[port].speed(); - // FIXME - let speed_id = self.lookup_psiv(port as u8, port_speed_id); - dbg!(speed_id); + let speed_id: &ProtocolSpeed = self.lookup_psiv(port as u8, port_speed_id).ok_or(Error::new(EIO))?; let port_state = self.port_states.get_mut(&port).ok_or(Error::new(ENOENT))?; let input_context: &mut Dma = &mut port_state.input_context; @@ -302,12 +372,12 @@ impl Xhci { let current_slot_a = input_context.device.slot.a.read(); - let endpoints = &port_state - .dev_desc + let dev_desc = &port_state.dev_desc; + let endpoints = &dev_desc .config_descs .get(req.config_desc as usize) .ok_or(Error::new(EBADMSG))? - .interface_descs[0] + .interface_descs.get(req.interface_desc.unwrap_or(0) as usize).ok_or(Error::new(EBADMSG))? .endpoints; if endpoints.len() >= 31 { @@ -342,7 +412,6 @@ impl Xhci { .ok_or(Error::new(EIO))?; let endp_desc = endpoints.get(index as usize).ok_or(Error::new(EIO))?; - // TODO: Check if streams are actually supported. let max_streams = endp_desc.max_streams(); let max_psa_size = self.cap.max_psa_size(); @@ -357,17 +426,17 @@ impl Xhci { // TODO: Interval related fields // TODO: Max ESIT payload size. - // TODO: The max burst size is non-zero for high-speed isoch endpoints. How are the USB2 - // speeds detected? - // I presume that USB 3 devices can never be in low/full/high-speed mode, but - // always SuperSpeed (gen 1 and 2 etc.). - - let max_burst_size = endp_desc.max_burst(); - let max_packet_size = endp_desc.max_packet_size; - let mult = endp_desc.isoch_mult(lec); - let interval = endp_desc.interval; + let max_packet_size = Self::endp_ctx_max_packet_size(endp_desc); + let max_burst_size = Self::endp_ctx_max_burst(speed_id, dev_desc, endp_desc); + + let max_esit_payload = Self::endp_ctx_max_esit_payload(speed_id, dev_desc, endp_desc, max_packet_size, max_burst_size); + let max_esit_payload_lo = max_esit_payload as u16; + let max_esit_payload_hi = ((max_esit_payload & 0x00FF_0000) >> 16) as u8; + + let interval = Self::endp_ctx_interval(speed_id, endp_desc); + let max_error_count = 3; let ep_ty = endp_desc.xhci_ep_type()?; let host_initiate_disable = false; @@ -384,7 +453,6 @@ impl Xhci { assert_eq!(ep_ty & 0x7, ep_ty); assert_eq!(mult & 0x3, mult); assert_eq!(max_error_count & 0x3, max_error_count); - assert_ne!(ep_ty, 0); // 0 means invalid. let ring_ptr = if max_streams != 0 { @@ -399,7 +467,6 @@ impl Xhci { array_ptr, "stream ctx ptr not aligned to 16 bytes" ); - port_state.endpoint_states.insert( endp_num, EndpointState::Ready(super::RingOrStreams::Streams(array)), @@ -415,22 +482,20 @@ impl Xhci { ring_ptr, "ring pointer not aligned to 16 bytes" ); - port_state.endpoint_states.insert( endp_num, EndpointState::Ready(super::RingOrStreams::Ring(ring)), ); - ring_ptr }; - assert_eq!(primary_streams & 0x1F, primary_streams); endp_ctx.a.write( u32::from(mult) << 8 - | u32::from(interval) << 16 | u32::from(primary_streams) << 10 - | u32::from(linear_stream_array) << 15, + | u32::from(linear_stream_array) << 15 + | u32::from(interval) << 16 + | u32::from(max_esit_payload_hi) << 24 ); endp_ctx.b.write( max_error_count << 1 @@ -439,9 +504,14 @@ impl Xhci { | u32::from(max_burst_size) << 8 | u32::from(max_packet_size) << 16, ); + endp_ctx.trl.write(ring_ptr as u32); endp_ctx.trh.write((ring_ptr >> 32) as u32); - endp_ctx.c.write(u32::from(avg_trb_len)); + + endp_ctx.c.write( + u32::from(avg_trb_len) + | (u32::from(max_esit_payload_lo) << 16) + ); } self.run.ints[0].erdp.write(self.cmd.erdp()); @@ -719,8 +789,9 @@ impl Xhci { ); let (bos_desc, bos_data) = dev.get_bos()?; - let has_superspeed = + let supports_superspeed = usb::bos_capability_descs(bos_desc, &bos_data).any(|desc| desc.is_superspeed()); + let supports_superspeedplus = usb::bos_capability_descs(bos_desc, &bos_data).any(|desc| desc.is_superspeedplus()); let config_descs = (0..raw_dd.configurations) .map(|index| -> Result<_> { @@ -756,12 +827,20 @@ impl Xhci { }; let mut endp = EndpDesc::from(next); - if has_superspeed { + if supports_superspeed { let next = match iter.next() { Some(AnyDescriptor::SuperSpeedCompanion(n)) => n, _ => break, }; endp.ssc = Some(SuperSpeedCmp::from(next)); + + if endp.has_ssp_companion() && supports_superspeedplus { + let next = match iter.next() { + Some(AnyDescriptor::SuperSpeedPlusCompanion(n)) => n, + _ => break, + }; + endp.sspc = Some(SuperSpeedPlusIsochCmp::from(next)); + } } endpoints.push(endp); }