From 39f43a51a1e46de24c7387e242d80d396f8d8421 Mon Sep 17 00:00:00 2001 From: Timothy Finnegan Date: Tue, 10 Sep 2024 00:43:42 +0000 Subject: [PATCH] Added some documentation to the XHCI daemon, clarified the scheme interface with a refactor --- Cargo.lock | 39 + xhcid/Cargo.toml | 1 + xhcid/src/driver_interface.rs | 20 +- xhcid/src/lib.rs | 25 + xhcid/src/main.rs | 105 ++- xhcid/src/usb/device.rs | 124 +++ xhcid/src/usb/endpoint.rs | 12 + xhcid/src/usb/hub.rs | 4 +- xhcid/src/usb/interface.rs | 1 + xhcid/src/usb/mod.rs | 26 + xhcid/src/xhci/capability.rs | 151 +++- xhcid/src/xhci/context.rs | 26 +- xhcid/src/xhci/event.rs | 10 +- xhcid/src/xhci/irq_reactor.rs | 195 +++-- xhcid/src/xhci/mod.rs | 261 +++++-- xhcid/src/xhci/operational.rs | 62 ++ xhcid/src/xhci/ring.rs | 33 +- xhcid/src/xhci/scheme.rs | 1353 +++++++++++++++++++++++---------- xhcid/src/xhci/trb.rs | 21 +- 19 files changed, 1896 insertions(+), 573 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2430ad1b44..aa9a3a5ccf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -54,6 +54,15 @@ dependencies = [ "redox_syscall 0.5.3", ] +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + [[package]] name = "alxd" version = "0.1.0" @@ -1150,6 +1159,35 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20145670ba436b55d91fc92d25e71160fbfbdd57831631c8d7d36377a476f1cb" +[[package]] +name = "regex" +version = "1.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" + [[package]] name = "rehid" version = "0.1.0" @@ -1906,6 +1944,7 @@ dependencies = [ "redox-daemon", "redox_event", "redox_syscall 0.5.3", + "regex", "serde", "serde_json", "smallvec 1.13.2", diff --git a/xhcid/Cargo.toml b/xhcid/Cargo.toml index 820d1e5291..865f4d9600 100644 --- a/xhcid/Cargo.toml +++ b/xhcid/Cargo.toml @@ -31,3 +31,4 @@ toml = "0.5" common = { path = "../common" } pcid = { path = "../pcid" } libredox = "0.1.3" +regex = "1.10.6" diff --git a/xhcid/src/driver_interface.rs b/xhcid/src/driver_interface.rs index a1eedb7381..37bc50d68d 100644 --- a/xhcid/src/driver_interface.rs +++ b/xhcid/src/driver_interface.rs @@ -188,7 +188,8 @@ impl EndpDesc { } else { None } - }).flatten() + }) + .flatten() } pub fn isoch_mult(&self, lec: bool) -> u8 { if !lec && self.is_isoch() { @@ -439,11 +440,17 @@ impl XhciClientHandle { Ok(string.parse()?) } pub fn open_endpoint_ctl(&self, num: u8) -> result::Result { - let path = format!("/scheme/{}/port{}/endpoints/{}/ctl", self.scheme, self.port, num); + let path = format!( + "/scheme/{}/port{}/endpoints/{}/ctl", + self.scheme, self.port, num + ); Ok(File::open(path)?) } pub fn open_endpoint_data(&self, num: u8) -> result::Result { - let path = format!("/scheme/{}/port{}/endpoints/{}/data", self.scheme, self.port, num); + let path = format!( + "/scheme/{}/port{}/endpoints/{}/data", + self.scheme, self.port, num + ); Ok(File::open(path)?) } pub fn open_endpoint(&self, num: u8) -> result::Result { @@ -653,9 +660,10 @@ impl XhciEndpHandle { let res = self.ctl_res()?; match res { - XhciEndpCtlRes::TransferResult(PortTransferStatus { kind: PortTransferStatusKind::Success, .. }) - if bytes_read != expected_len as usize => - { + XhciEndpCtlRes::TransferResult(PortTransferStatus { + kind: PortTransferStatusKind::Success, + .. + }) if bytes_read != expected_len as usize => { Err(Invalid("no short packet, but fewer bytes were read/written").into()) } XhciEndpCtlRes::TransferResult(r) => Ok(r), diff --git a/xhcid/src/lib.rs b/xhcid/src/lib.rs index 16b3d91b64..e1bfb5e222 100644 --- a/xhcid/src/lib.rs +++ b/xhcid/src/lib.rs @@ -1,3 +1,28 @@ +//! The eXtensible Host Controller Interface (XHCI) Daemon Interface +//! +//! This crate implements the driver interface for interacting with the Redox xhcid daemon from +//! another userspace process. +//! +//! XHCI is a standard for the USB Host Controller interface specified by Intel that provides a +//! common register interface for systems to use to interact with the Universal Serial Bus (USB) +//! subsystem. +//! +//! USB consists of three types of devices: The Host Controller/Root Hub, USB Hubs, and Endpoints. +//! Endpoints represent actual devices connected to the USB fabric. USB Hubs are intermediaries +//! between the Host Controller and the endpoints that report when devices have been connected/disconnected. +//! The Host Controller provides the interface to the USB subsystem that software running on the +//! system's CPU can interact with. It's a tree-like structure, which the Host Controller enumerating +//! and addressing all the hubs and endpoints in the tree. Data then flows through the fabric +//! using the USB protocol (2.0 or 3.2) as packets. Hubs have multiple ports that endpoints can +//! connect to, and they notify the Host Controller/Root Hub when devices are hot plugged or removed. +//! +//! This documentation will refer directly to the relevant standards, which are as follows: +//! +//! - XHCI - [eXtensible Host Controller Interface for Universal Serial Bus (xHCI) Requirements Specification](https://www.intel.com/content/dam/www/public/us/en/documents/technical-specifications/extensible-host-controler-interface-usb-xhci.pdf) +//! - USB2 - [Universal Serial Bus Specification](https://www.usb.org/document-library/usb-20-specification) +//! - USB32 - [Universal Serial Bus 3.2 Specification Revision 1.1](https://usb.org/document-library/usb-32-revision-11-june-2022) +//! +#![warn(missing_docs)] pub extern crate plain; mod driver_interface; diff --git a/xhcid/src/main.rs b/xhcid/src/main.rs index c2fa344044..e3798cd580 100644 --- a/xhcid/src/main.rs +++ b/xhcid/src/main.rs @@ -1,7 +1,33 @@ +//! The eXtensible Host Controller Interface (XHCI) Daemon +//! +//! This crate provides the executable xhcid daemon that implements the driver for interacting with +//! a PCIe XHCI device +//! +//! XHCI is a standard for the USB Host Controller interface specified by Intel that provides a +//! common register interface for systems to use to interact with the Universal Serial Bus (USB) +//! subsystem. +//! +//! USB consists of three types of devices: The Host Controller/Root Hub, USB Hubs, and Endpoints. +//! Endpoints represent actual devices connected to the USB fabric. USB Hubs are intermediaries +//! between the Host Controller and the endpoints that report when devices have been connected/disconnected. +//! The Host Controller provides the interface to the USB subsystem that software running on the +//! system's CPU can interact with. It's a tree-like structure, which the Host Controller enumerating +//! and addressing all the hubs and endpoints in the tree. Data then flows through the fabric +//! using the USB protocol (2.0 or 3.2) as packets. Hubs have multiple ports that endpoints can +//! connect to, and they notify the Host Controller/Root Hub when devices are hot plugged or removed. +//! +//! This documentation will refer directly to the relevant standards, which are as follows: +//! +//! - XHCI - [eXtensible Host Controller Interface for Universal Serial Bus (xHCI) Requirements Specification](https://www.intel.com/content/dam/www/public/us/en/documents/technical-specifications/extensible-host-controler-interface-usb-xhci.pdf) +//! - USB2 - [Universal Serial Bus Specification](https://www.usb.org/document-library/usb-20-specification) +//! - USB32 - [Universal Serial Bus 3.2 Specification Revision 1.1](https://usb.org/document-library/usb-32-revision-11-june-2022) +//! +#![warn(missing_docs)] #[macro_use] extern crate bitflags; use std::convert::{TryFrom, TryInto}; +use std::env; use std::fs::{self, File}; use std::future::Future; use std::io::{self, Read, Write}; @@ -9,21 +35,22 @@ use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; use std::pin::Pin; use std::ptr::NonNull; use std::sync::{Arc, Mutex}; -use std::env; use libredox::flag; -use pcid_interface::{MsiSetFeatureInfo, PciFunctionHandle, PciFeature, PciFeatureInfo, SetFeatureInfo}; #[cfg(target_arch = "x86_64")] use pcid_interface::irq_helpers::allocate_single_interrupt_vector_for_msi; use pcid_interface::irq_helpers::read_bsp_apic_id; use pcid_interface::msi::MsixTableEntry; +use pcid_interface::{ + MsiSetFeatureInfo, PciFeature, PciFeatureInfo, PciFunctionHandle, SetFeatureInfo, +}; use event::{Event, RawEventQueue}; use syscall::data::Packet; use syscall::error::EWOULDBLOCK; use syscall::flag::EventFlags; -use syscall::scheme::Scheme; use syscall::io::Io; +use syscall::scheme::Scheme; use crate::xhci::{InterruptMethod, Xhci}; @@ -40,17 +67,25 @@ async fn handle_packet(hci: Arc, packet: Packet) -> Packet { } #[cfg(target_arch = "x86_64")] -fn get_int_method(pcid_handle: &mut PciFunctionHandle, bar0_address: usize) -> (Option, InterruptMethod) { +fn get_int_method( + pcid_handle: &mut PciFunctionHandle, + bar0_address: usize, +) -> (Option, InterruptMethod) { let pci_config = pcid_handle.config(); - let all_pci_features = pcid_handle.fetch_all_features().expect("xhcid: failed to fetch pci features"); + let all_pci_features = pcid_handle + .fetch_all_features() + .expect("xhcid: failed to fetch pci features"); log::debug!("XHCI PCI FEATURES: {:?}", all_pci_features); let has_msi = all_pci_features.iter().any(|feature| feature.is_msi()); let has_msix = all_pci_features.iter().any(|feature| feature.is_msix()); if has_msi && !has_msix { - let mut capability = match pcid_handle.feature_info(PciFeature::Msi).expect("xhcid: failed to retrieve the MSI capability structure from pcid") { + let mut capability = match pcid_handle + .feature_info(PciFeature::Msi) + .expect("xhcid: failed to retrieve the MSI capability structure from pcid") + { PciFeatureInfo::Msi(s) => s, PciFeatureInfo::MsiX(_) => panic!(), }; @@ -60,28 +95,37 @@ fn get_int_method(pcid_handle: &mut PciFunctionHandle, bar0_address: usize) -> ( // pcid_interface, so that this can be shared between nvmed, xhcid, ixgebd, etc.. let destination_id = read_bsp_apic_id().expect("xhcid: failed to read BSP apic id"); - let (msg_addr_and_data, interrupt_handle) = allocate_single_interrupt_vector_for_msi(destination_id); + let (msg_addr_and_data, interrupt_handle) = + allocate_single_interrupt_vector_for_msi(destination_id); let set_feature_info = MsiSetFeatureInfo { multi_message_enable: Some(0), message_address_and_data: Some(msg_addr_and_data), mask_bits: None, }; - pcid_handle.set_feature_info(SetFeatureInfo::Msi(set_feature_info)).expect("xhcid: failed to set feature info"); + pcid_handle + .set_feature_info(SetFeatureInfo::Msi(set_feature_info)) + .expect("xhcid: failed to set feature info"); - pcid_handle.enable_feature(PciFeature::Msi).expect("xhcid: failed to enable MSI"); + pcid_handle + .enable_feature(PciFeature::Msi) + .expect("xhcid: failed to enable MSI"); log::debug!("Enabled MSI"); (Some(interrupt_handle), InterruptMethod::Msi) } else if has_msix { - let msix_info = match pcid_handle.feature_info(PciFeature::MsiX).expect("xhcid: failed to retrieve the MSI-X capability structure from pcid") { + let msix_info = match pcid_handle + .feature_info(PciFeature::MsiX) + .expect("xhcid: failed to retrieve the MSI-X capability structure from pcid") + { PciFeatureInfo::Msi(_) => panic!(), PciFeatureInfo::MsiX(s) => s, }; msix_info.validate(pci_config.func.bars); assert_eq!(msix_info.table_bar, 0); - let virt_table_base = (bar0_address + msix_info.table_offset as usize) as *mut MsixTableEntry; + let virt_table_base = + (bar0_address + msix_info.table_offset as usize) as *mut MsixTableEntry; let mut info = xhci::MappedMsixRegs { virt_table_base: NonNull::new(virt_table_base).unwrap(), @@ -98,14 +142,20 @@ fn get_int_method(pcid_handle: &mut PciFunctionHandle, bar0_address: usize) -> ( let table_entry_pointer = info.table_entry_pointer(k); let destination_id = read_bsp_apic_id().expect("xhcid: failed to read BSP apic id"); - let (msg_addr_and_data, interrupt_handle) = allocate_single_interrupt_vector_for_msi(destination_id); + let (msg_addr_and_data, interrupt_handle) = + allocate_single_interrupt_vector_for_msi(destination_id); table_entry_pointer.write_addr_and_data(msg_addr_and_data); table_entry_pointer.unmask(); - (Some(interrupt_handle), InterruptMethod::MsiX(Mutex::new(info))) + ( + Some(interrupt_handle), + InterruptMethod::MsiX(Mutex::new(info)), + ) }; - pcid_handle.enable_feature(PciFeature::MsiX).expect("xhcid: failed to enable MSI-X"); + pcid_handle + .enable_feature(PciFeature::MsiX) + .expect("xhcid: failed to enable MSI-X"); log::debug!("Enabled MSI-X"); method @@ -122,7 +172,10 @@ fn get_int_method(pcid_handle: &mut PciFunctionHandle, bar0_address: usize) -> ( //TODO: MSI on non-x86_64? #[cfg(not(target_arch = "x86_64"))] -fn get_int_method(pcid_handle: &mut PciFunctionHandle, address: usize) -> (Option, InterruptMethod) { +fn get_int_method( + pcid_handle: &mut PciFunctionHandle, + address: usize, +) -> (Option, InterruptMethod) { let pci_config = pcid_handle.config(); if let Some(irq) = pci_config.func.legacy_interrupt_line { @@ -167,19 +220,17 @@ fn daemon(daemon: redox_daemon::Daemon) -> ! { println!(" + XHCI {}", pci_config.func.display()); let scheme_name = format!("usb.{}", name); - let socket_fd = libredox::call::open( - format!(":{}", scheme_name), - flag::O_RDWR | flag::O_CREAT, - 0, - ) - .expect("xhcid: failed to create usb scheme"); - let socket = Arc::new(Mutex::new(unsafe { - File::from_raw_fd(socket_fd as RawFd) - })); + let socket_fd = + libredox::call::open(format!(":{}", scheme_name), flag::O_RDWR | flag::O_CREAT, 0) + .expect("xhcid: failed to create usb scheme"); + let socket = Arc::new(Mutex::new(unsafe { File::from_raw_fd(socket_fd as RawFd) })); daemon.ready().expect("xhcid: failed to notify parent"); - let hci = Arc::new(Xhci::new(scheme_name, address, interrupt_method, pcid_handle).expect("xhcid: failed to allocate device")); + let hci = Arc::new( + Xhci::new(scheme_name, address, interrupt_method, pcid_handle) + .expect("xhcid: failed to allocate device"), + ); xhci::start_irq_reactor(&hci, irq_file); futures::executor::block_on(hci.probe()).expect("xhcid: failed to probe"); @@ -212,7 +263,9 @@ fn daemon(daemon: redox_daemon::Daemon) -> ! { packet.a = a; todo.push(packet); } else { - socket.write(&packet).expect("xhcid failed to write to socket"); + socket + .write(&packet) + .expect("xhcid failed to write to socket"); } } diff --git a/xhcid/src/usb/device.rs b/xhcid/src/usb/device.rs index c7d1835cf7..59a09f1f98 100644 --- a/xhcid/src/usb/device.rs +++ b/xhcid/src/usb/device.rs @@ -1,66 +1,190 @@ +//! Implements the "Device" USB Descriptor. +//! +//! This descriptor is described in USB32 section 9.6.1 + +/// A USB Device Descriptor. +/// +/// This is common to all USB standards, and "provides information that applies globally to the +/// device and all the device's configurations" (USB32 9.6.1) +/// +/// A given device will only have one device descriptor. +/// +/// USB32 Table 9-11 describes the USB packet offsets of the fields described by this structure. #[repr(packed)] #[derive(Clone, Copy, Debug, Default)] pub struct DeviceDescriptor { + /// The length of this descriptor in bytes. + /// The bLength field in USB32 Table 9-11 pub length: u8, + /// The descriptor type. See [DescriptorKind] + /// The bDescriptorType field in USB32 Table 9-11. pub kind: u8, + /// The USB standard version in binary-coded decimal. + /// + /// USB 2.1 would be encoded as 210H, 3.2 would be 320H. + /// The bcdUSB field in USB32 Table 9-11 pub usb: u16, + /// The USB Class Code. + /// + /// bDeviceClass in USB32 Table 9-11. + /// + /// These are values assigned by USB-IF that describes the type of device connected via USB. + /// + /// A value of FF indicates a vendor-specific class. A value of 0 indicates that all the + /// interfaces in a configuration will provide their own class information. pub class: u8, + /// The USB Sub Device Class Code. + /// + /// bDeviceSubClass in USB32 Table 9-11 + /// + /// These specify subclasses of a device class specified by the 'class' field. pub sub_class: u8, + /// The USB Protocol code. + /// + /// bDeviceProtocol in USB32 Table 9-11 + /// + /// This qualified by the class and sub_class fields, and specifies the application-layer protocol + /// (the protocol encapsulated by USB) of this device. pub protocol: u8, + /// The maximum packet size for endpoint 0. + /// + /// bMaxPacketSize0 in USB32 Table 9-11 pub packet_size: u8, + /// The USB Vendor ID + /// + /// idVendor in USB32 Table 9-11 pub vendor: u16, + /// The USB Product ID + /// + /// idProduct in USB32 Table 9-11 pub product: u16, + /// The device release number in binary-coded decimal. + /// + /// bcdDevice in USB32 Table 9-11 pub release: u16, + /// Index of the String Descriptor describing the device manufacturer + /// + /// iManufacturer in USB32 Table 9-11 pub manufacturer_str: u8, + /// Index of the String Descriptor describing the product + /// + /// iProduct in Table 9-11 pub product_str: u8, + /// Index of the string descriptor describing the device's serial number + /// + /// iSerialNumber in USB32 Table 9-11 pub serial_str: u8, + /// The number of possible configurations (Configuration Descriptors) for this device. + /// + /// bNumConfigurations in USB32 Table 9-11 pub configurations: u8, } unsafe impl plain::Plain for DeviceDescriptor {} impl DeviceDescriptor { + /// Gets the USB Minor Version pub fn minor_usb_vers(&self) -> u8 { (self.usb & 0xFF) as u8 } + /// Gets the USB Major Version pub fn major_usb_vers(&self) -> u8 { ((self.usb >> 8) & 0xFF) as u8 } } +/// The 8-byte version of the Device Descriptor +/// +/// This is a subset of the full Device Descriptor. When the system is first performing device +/// enumeration, it will request only the first eight bytes of the DeviceDescriptor from each +/// device as this contains the crucial information, and then it will request the full descriptor +/// at a later point. +/// +/// See [DeviceDescriptor] #[repr(packed)] #[derive(Clone, Copy, Debug, Default)] pub struct DeviceDescriptor8Byte { + /// See [DeviceDescriptor] pub length: u8, + /// See [DeviceDescriptor] pub kind: u8, + /// See [DeviceDescriptor] pub usb: u16, + /// See [DeviceDescriptor] pub class: u8, + /// See [DeviceDescriptor] pub sub_class: u8, + /// See [DeviceDescriptor] pub protocol: u8, + /// See [DeviceDescriptor] pub packet_size: u8, } unsafe impl plain::Plain for DeviceDescriptor8Byte {} impl DeviceDescriptor8Byte { + /// Gets the USB Minor Version pub fn minor_usb_vers(&self) -> u8 { (self.usb & 0xFF) as u8 } + + /// Gets the USB Major Version pub fn major_usb_vers(&self) -> u8 { ((self.usb >> 8) & 0xFF) as u8 } } +/// A Device Qualifier Descriptor +/// +/// This is a descriptor specific to the USB2 standard, and was deprecated in USB3. USB2 devices +/// will still provide this value. +/// +/// A Device Qualifier is sent by a high-speed capable USB2 device to describe information in its +/// descriptor that would change if it was operating at the other speed. If it was at low speed, +/// the qualifier would describe the device at high speed. If it was at high speed, the qualifier +/// would describe the device at low speed. +/// +/// See USB2 section 9.6.2 +/// +/// The packet offsets are described in USB2 Table 9-9 #[repr(packed)] pub struct DeviceQualifier { + /// The size of the descriptor. + /// + /// bLength in USB2 Table 9-9 pub length: u8, + /// The Device Descriptor Type (see [xhci_interface::usb::DescriptorKind]) + /// + /// bDescriptorType in USB2 Table 9-9 pub kind: u8, + /// The USB specification version number in binary-coded decimal + /// + /// bDeviceClass in USB2 Table 9-9 pub usb: u16, + /// The USB Device Class Code + /// + /// bDeviceClass in USB2 Table 9-9 pub class: u8, + /// The USB Device Sub Class Code + /// + /// bDeviceSubClass in USB2 Table 9-9 pub sub_class: u8, + /// The USB Device Protocol Code + /// + /// bDeviceProtocol in USB2 Table 9-9 pub protocol: u8, + /// The maximum packet size for the other speed\ + /// + /// bMaxPacketSize0 in USB2 Table9-9 pub pkgsz_other_speed: u8, + /// The number of device configurations for the other speed + /// + /// bNumConfiguration in USB2 Table 9-9 pub num_other_speed_cfgs: u8, + /// Reserved for future use by the USB2 standard + /// + /// (DeviceQualifier was dropped in USB3, so it was never used!) + /// bReserved in USB2 Table 9-9 pub _rsvd: u8, } diff --git a/xhcid/src/usb/endpoint.rs b/xhcid/src/usb/endpoint.rs index 39e28eb6f4..c8f8163c62 100644 --- a/xhcid/src/usb/endpoint.rs +++ b/xhcid/src/usb/endpoint.rs @@ -1,5 +1,16 @@ use plain::Plain; +/// The descriptor for a USB Endpoint. +/// +/// Each endpoint for a particular interface has its own descriptor. The information in this +/// structure is used by the host to determine the bandwidth requirements of the endpoint. +/// +/// This is returned automatically when you send a request for a ConfigurationDescriptor, +/// and cannot be requested individually. +/// +/// See USB32 9.6.6 +/// +/// The offsets for the fields in the packet are described in USB32 Table 9-26 #[repr(packed)] #[derive(Clone, Copy, Debug, Default)] pub struct EndpointDescriptor { @@ -11,6 +22,7 @@ pub struct EndpointDescriptor { pub interval: u8, } +/// Mask that is ANDed to the [EndpointDescriptor].attributes buffer to get the endpoint type. pub const ENDP_ATTR_TY_MASK: u8 = 0x3; #[repr(u8)] diff --git a/xhcid/src/usb/hub.rs b/xhcid/src/usb/hub.rs index 854ff30a74..99022ec7a7 100644 --- a/xhcid/src/usb/hub.rs +++ b/xhcid/src/usb/hub.rs @@ -9,7 +9,7 @@ pub struct HubDescriptor { pub current: u8, // device_removable: bitmap of ports, maximum of 256 bits (32 bytes) // power_control_mask: bitmap of ports, maximum of 256 bits (32 bytes) - bitmaps: [u8; 64] + bitmaps: [u8; 64], } unsafe impl plain::Plain for HubDescriptor {} @@ -23,7 +23,7 @@ impl Default for HubDescriptor { characteristics: 0, power_on_good: 0, current: 0, - bitmaps: [0; 64] + bitmaps: [0; 64], } } } diff --git a/xhcid/src/usb/interface.rs b/xhcid/src/usb/interface.rs index 5931e71893..1030993bb8 100644 --- a/xhcid/src/usb/interface.rs +++ b/xhcid/src/usb/interface.rs @@ -1,5 +1,6 @@ use plain::Plain; +/// #[repr(packed)] #[derive(Clone, Copy, Debug, Default)] pub struct InterfaceDescriptor { diff --git a/xhcid/src/usb/mod.rs b/xhcid/src/usb/mod.rs index 246cc77f8b..d0d659882a 100644 --- a/xhcid/src/usb/mod.rs +++ b/xhcid/src/usb/mod.rs @@ -1,3 +1,13 @@ +//! The Universal Serial Bus (USB) Module +//! +//! The implementations in this module are common to all USB interfaces (though individual elements +//! may be specific to only 2.0 or 3.2), and are used by specialized driver components like [xhci] +//! to implement the driver interface. +//! +//! The [Universal Serial Bus Specification](https://www.usb.org/document-library/usb-20-specification) and the [Universal Serial Bus 3.2 Specification](https://usb.org/document-library/usb-32-revision-11-june-2022) are +//! the documents that inform this implementation. +//! +//! See the crate-level documentation for the acronyms used to refer to specific documents. pub use self::bos::{bos_capability_descs, BosAnyDevDesc, BosDescriptor, BosSuperSpeedDesc}; pub use self::config::ConfigDescriptor; pub use self::device::{DeviceDescriptor, DeviceDescriptor8Byte}; @@ -9,22 +19,38 @@ pub use self::hub::*; pub use self::interface::InterfaceDescriptor; pub use self::setup::{Setup, SetupReq}; +/// Enumerates the list of descriptor kinds that can be reported by a USB device to report its +/// attributes to the system. (See USB32 Sections 9.5 and 9.6) #[derive(Clone, Copy, Debug)] #[repr(u8)] pub enum DescriptorKind { + /// No Descriptor TODO: Determine why this state exists, and what it does in the code. None = 0, + /// A Device Descriptor. See [DeviceDescriptor] Device = 1, + /// A Configuration Descriptor. See [ConfigDescriptor] Configuration = 2, + /// A String Descriptor. See (USB32 Section 9.6.9). String = 3, + /// An Interface Descriptor. See [InterfaceDescriptor] Interface = 4, + /// An Endpoint Descriptor. See [EndpointDescriptor] Endpoint = 5, + /// A Device Qualifier. USB2-specific. See [DeviceQualifier] DeviceQualifier = 6, + /// The "Other Speed Configuration" descriptor. USB2-specific. See (USB2 9.6.4] OtherSpeedConfiguration = 7, + /// TODO: Determine the standard that specifies this InterfacePower = 8, + /// TODO: Determine the standard that specifies this (Possibly USB-C?) OnTheGo = 9, + /// A Binary Device Object Store Descriptor. See [BosDescriptor] BinaryObjectStorage = 15, + /// TODO: Track down the HID standard for references Hid = 33, + /// A USB Hub Device Descriptor. See [HubDescriptor] Hub = 41, + /// A Super Speed Endpoint Companion Descriptor. See [SuperSpeedCompanionDescriptor] SuperSpeedCompanion = 48, } diff --git a/xhcid/src/xhci/capability.rs b/xhcid/src/xhci/capability.rs index 5479be88fb..ab3ffb8502 100644 --- a/xhcid/src/xhci/capability.rs +++ b/xhcid/src/xhci/capability.rs @@ -1,77 +1,216 @@ use syscall::io::{Io, Mmio}; +/// Represents the memory-mapped Capability Registers of the XHCI +/// +/// These are read-only registers that specify the capabilities +/// of the host controller implementation. +/// +/// They are used by the driver to determine what subsystems to +/// configure during initialization. +/// +/// See XHCI Section 5.3. Table 5-9 describes the offsets of the registers +/// in memory. #[repr(packed)] pub struct CapabilityRegs { + /// The length of the Capability Registers data structure in XHCI memory. + /// + /// While only the registers in this structure are defined by the XHCI standard, + /// the standard defines an arbitrary amount of space following those registers that + /// are reserved for the standard. As such, you need to know the offset to the operational + /// registers, which immediately follow. + /// + /// CAPLENGTH in XHC Table 5-9. See XHC 5.3.1 pub len: Mmio, + /// Reserved byte + /// + /// Rsvd in XHC Table 5-9 _rsvd: Mmio, + /// The XHCI interface version number in Binary-Encoded Decimal. + /// + /// This specifies the version of the XHCI specification that is supported by this controller. + /// HCIVERSION in XHC Table 5-9 pub hci_ver: Mmio, + /// The HCI Structural Parameters 1 Register. + /// + /// -Bits 0 - 7 describe the number of device slots supported by this controller + /// -Bits 8 - 18 describe the number of interrupters supported by this controller + /// -Bits 19-23 are reserved + /// -Bits 24-31 specify the maximum number of ports supported by this controller. + /// + /// HCPARAMS1 in XHC Table 5-9. See 5.3.3 pub hcs_params1: Mmio, + /// The HCI Structural Parameters 2 Register. + /// + /// - Bits 0-3 describe the Isochronus Scheduling Threshold (IST) + /// - Bits 4-7 describe the Event Ring Segment Table Max (ERST Max). The maximum number of event + /// ring segment table entries is 2^(ERST Max) + /// - Bits 8-20 are reserved + /// - Bits 25-21 describe the high order five bits of the maximum number of scratchpad buffers + /// - Bit 26 is the Scratchpad Restore Buffer (SPR). (See XHC 4.23.2) + /// - Bits 26-31 describe the low order five bits of the maximum number of scratchpad buffers + /// + /// HCPARAMS2 in XHC Table 5-9. See 5.3.4 pub hcs_params2: Mmio, + /// The HCI Structural Parameters 3 Register. + /// + /// - Bits 0-7 describes the worst-case U1 Device Exit Latency. Values are in microseconds, from 00h to 0Ah. 0B-FFh are reserved + /// - Bits 8-15 are reserved + /// - Bits 16-31 describe the worst-case U2 Device Exit Latency. Values are in microseconds, from 0000h to 07FFh. 0800-FFFFh are reserved + /// + /// HCPARAMS3 in XHC Table 5-9. See XHC 5.3.5 pub hcs_params3: Mmio, + /// The HCI Capability Parameters 1 Register. + /// + /// This register defines optional capabilities supported by the xHCI + /// + /// - Bit 0 is the 64-bit Address Capability Flag (AC64). 0 = 32-bit pointers, 1 = 64-bit pointers. + /// - Bit 1 is the Bandwidth Negotation Capability Flag (BNC) + /// - Bit 2 is the Context Size Flag (CSZ). 0 = 32-byte, 1 = 64-byte Context Data Structures + /// - Bit 3 is the Port Power Control Flag (PPC). Indicates whether the implementation supports port power control. + /// - Bit 4 is the Port Indicators Flag (PIND). Indicates whether the XHC root hub supports port indicator control + /// - Bit 5 is the Light Host Controller Reset Capability Flag (LHRC). Indicates whether the implementation supports a light reset + /// - Bit 6 is the Latency Tolerance Messaging Capability Flag (LTC). Indicates whether the implementation supports Latency Tolerance Messaging + /// - Bit 7 is the no Secondary SID Support Flag (NSS). Indicates whether secondary stream ids is supported. 1 = NO, 0 = YES + /// - Bit 8 is the Parse All Event Data Flag (PAE). (See XHC Table 5-13) + /// - Bit 9 is the Stopped - Short Packet Capability Flag (SPC). (See XHC 4.6.9) + /// - Bit 10 is the Stopped EDTLA Capability Flag (SEC). (See XHC 4.6.9, 4.12, and 6.4.4.1) + /// - Bit 11 is the Contiguous Frame ID Capability Flag (CFC). (See XHC 4.11.2.5) + /// - Bits 12-15 are the Maximum Primary Stream Array Size (MaxPSASize). Identifies the maximum size of PSA that the implementation supports. + /// - Bits 16-31 The xHCI Extended Capabilities Pointer (xECP). Points to an extended capabilities list. (See XHC Table 5-13 to see how to process this value) + /// + /// HCCPARAMS1 in XHC Table 5-9. See XHC 5.3.6 pub hcc_params1: Mmio, + /// The Doorbell Offset Register + /// + /// This register defines the offset of the Doorbell Array base address from the Base. + /// + /// Bits 0-1 are reserved. + /// Bits 2-31 contain the offset. + /// + /// DBOFF in XHC Table 5-9. See XHC 5.3.7 pub db_offset: Mmio, + /// The Runtime Register Space Offset + /// + /// The offset of the xHCI Runtime Registers from the Base. + /// + /// - Bits 0-4 are reserved. + /// - Bits 5-31 contain the offset. + /// + /// RTSOFF in XHC Table 5-9. See XHC 5.3.8 pub rts_offset: Mmio, + /// The HC Capability Parameters 2 Register + /// + /// This register defines optional capabilities supported by the xHCI + /// + /// - Bit 0 is the UC3 Entry Capability Flag (U3C). See XHC 4.15.1 + /// - Bit 1 is the Configure Endpoint Command Max Latency Too Large Capability Flag (CMC). See XHC 4.23.5.2 and 5.4.1 + /// - Bit 2 is the Force Save Context Capability (FCS). See XHC 4.23.2 and 5.4.1 + /// - Bit 3 is the Compliance Transition Capability (CTC). See XHC 4.19.2.4.1 + /// - Bit 4 is the Large ESIT Payload Capability (LEC). See XHC 6.2.3.8 + /// - Bit 5 is the Configuration Information Capability (CIC). See XHC 6.2.5.1 + /// - Bit 6 is the Extended TBC Capability (ETC). See XHC 4.11.2.3 + /// - Bit 7 is the Extended TBC TRB Status Capability (ETC_TSC). See XHC 4.11.2.3 + /// - Bit 8 is the Get/Set Extended Property Capability (GSC). See Sections XHC 4.6.17 and 4.6.18 + /// - Bits 10-31 are reserved. pub hcc_params2: Mmio, + //TODO: VTIOSOFF register for I/O virtualization } +/// The mask to use to get the AC64 bit from HCCPARAMS1. See [CapabilityRegs] pub const HCC_PARAMS1_AC64_BIT: u32 = 1 << HCC_PARAMS1_AC64_SHIFT; +/// The shift to use to get the AC64 bit from HCCParams1. See [CapabilityRegs] pub const HCC_PARAMS1_AC64_SHIFT: u8 = 0; +/// The Mask to use to get the MAXPSASIZE value from HCCParams1. See [CapabilityRegs] pub const HCC_PARAMS1_MAXPSASIZE_MASK: u32 = 0xF000; // 15:12 +/// The shift to use to get the MAXPSASIZE value from HCCParams1. See [CapabilityRegs] pub const HCC_PARAMS1_MAXPSASIZE_SHIFT: u8 = 12; +/// The mask to use to get the XECP value from HCCParams1. See [CapabilityRegs] pub const HCC_PARAMS1_XECP_MASK: u32 = 0xFFFF_0000; +/// The shift to use to get the XECP value from HCCParams1. See [CapabilityRegs] pub const HCC_PARAMS1_XECP_SHIFT: u8 = 16; +/// The mask to use to get the LEC bit from HCCParams2. See [CapabilityRegs] pub const HCC_PARAMS2_LEC_BIT: u32 = 1 << 4; +/// The mask to use to get the CIC bit from HCCParams2. See [CapabilityRegs] pub const HCC_PARAMS2_CIC_BIT: u32 = 1 << 5; - +/// The mask to use to get MAXPORTS from HCSParams1. See [CapabilityRegs] pub const HCS_PARAMS1_MAX_PORTS_MASK: u32 = 0xFF00_0000; +/// The shift to use to get MAXPORTS from HCSParams1. See [CapabilityRegs] pub const HCS_PARAMS1_MAX_PORTS_SHIFT: u8 = 24; +/// The shift to use to get MAXSLOTS from HCSParams1. See [CapabilityRegs] pub const HCS_PARAMS1_MAX_SLOTS_MASK: u32 = 0x0000_00FF; +/// The shift to use to get MAXSLOTS from HCSParams1. See [CapabilityRegs] pub const HCS_PARAMS1_MAX_SLOTS_SHIFT: u8 = 0; - +/// The mask to use to get MAXSCRATPADBUFS_LO from HCSParams2. See [CapabilityRegs] pub const HCS_PARAMS2_MAX_SCRATCHPAD_BUFS_LO_MASK: u32 = 0xF800_0000; +/// The shift to use to get MAXSCRATCHPADBUFS_LO from HCSParams2. See [CapabilityRegs] pub const HCS_PARAMS2_MAX_SCRATCHPAD_BUFS_LO_SHIFT: u8 = 27; +/// The mask to use to get the SPR bit from HCSParams2. See [CapabilityRegs] pub const HCS_PARAMS2_SPR_BIT: u32 = 1 << HCS_PARAMS2_SPR_SHIFT; +/// The shift to use to get the SPR bit from HCSParams2. See [CapabilityRegs] pub const HCS_PARAMS2_SPR_SHIFT: u8 = 26; +/// The mask to use to get MAXSCRATCHPADBUFS_HI from HCSParams2. See [CapabilityRegs] pub const HCS_PARAMS2_MAX_SCRATCHPAD_BUFS_HI_MASK: u32 = 0x03E0_0000; +/// The shift to use to get MAXSCRATCHPADBUFS_HI from HCSParams2. See [CapabilityRegs] + pub const HCS_PARAMS2_MAX_SCRATCHPAD_BUFS_HI_SHIFT: u8 = 21; impl CapabilityRegs { + /// Gets the ACS64 bit from HCCParams1. pub fn ac64(&self) -> bool { self.hcc_params1.readf(HCC_PARAMS1_AC64_BIT) } + /// Gets the LEC bit from HCCParams2. pub fn lec(&self) -> bool { self.hcc_params2.readf(HCC_PARAMS2_LEC_BIT) } + /// Gets the CIC bit from HCCParams2. pub fn cic(&self) -> bool { self.hcc_params2.readf(HCC_PARAMS2_CIC_BIT) } + + /// Gets the Max PSA Size from HCCParams1 pub fn max_psa_size(&self) -> u8 { ((self.hcc_params1.read() & HCC_PARAMS1_MAXPSASIZE_MASK) >> HCC_PARAMS1_MAXPSASIZE_SHIFT) as u8 } + + /// Gets the maximum number of ports from HCCParams1 pub fn max_ports(&self) -> u8 { ((self.hcs_params1.read() & HCS_PARAMS1_MAX_PORTS_MASK) >> HCS_PARAMS1_MAX_PORTS_SHIFT) as u8 } + + /// Gets the maximum number of ports from HCCParams 2 pub fn max_slots(&self) -> u8 { (self.hcs_params1.read() & HCS_PARAMS1_MAX_SLOTS_MASK) as u8 } + + /// Gets the extended capability pointer from HCCParams1 in DWORDs. pub fn ext_caps_ptr_in_dwords(&self) -> u16 { ((self.hcc_params1.read() & HCC_PARAMS1_XECP_MASK) >> HCC_PARAMS1_XECP_SHIFT) as u16 } + + /// Gets the lower five bits from the Max Scratchpad Buffer Lo Register in HCSParams2 pub fn max_scratchpad_bufs_lo(&self) -> u8 { - ((self.hcs_params2.read() & HCS_PARAMS2_MAX_SCRATCHPAD_BUFS_LO_MASK) >> HCS_PARAMS2_MAX_SCRATCHPAD_BUFS_LO_SHIFT) as u8 + ((self.hcs_params2.read() & HCS_PARAMS2_MAX_SCRATCHPAD_BUFS_LO_MASK) + >> HCS_PARAMS2_MAX_SCRATCHPAD_BUFS_LO_SHIFT) as u8 } + + /// Gets the SPR register from HCSParams2 pub fn spr(&self) -> bool { self.hcs_params2.readf(HCS_PARAMS2_SPR_BIT) } + + /// Gets the higher five bits from the Max Scratchpad Buffer Hi Register in HCSParams2 pub fn max_scratchpad_bufs_hi(&self) -> u8 { - ((self.hcs_params2.read() & HCS_PARAMS2_MAX_SCRATCHPAD_BUFS_HI_MASK) >> HCS_PARAMS2_MAX_SCRATCHPAD_BUFS_HI_SHIFT) as u8 + ((self.hcs_params2.read() & HCS_PARAMS2_MAX_SCRATCHPAD_BUFS_HI_MASK) + >> HCS_PARAMS2_MAX_SCRATCHPAD_BUFS_HI_SHIFT) as u8 } + + /// Gets the maximum number of scratchpad buffers supported by this implementation. pub fn max_scratchpad_bufs(&self) -> u16 { - u16::from(self.max_scratchpad_bufs_lo()) - | (u16::from(self.max_scratchpad_bufs_hi()) << 5) + u16::from(self.max_scratchpad_bufs_lo()) | (u16::from(self.max_scratchpad_bufs_hi()) << 5) } } diff --git a/xhcid/src/xhci/context.rs b/xhcid/src/xhci/context.rs index 5f364a5524..aabbeaf754 100644 --- a/xhcid/src/xhci/context.rs +++ b/xhcid/src/xhci/context.rs @@ -1,14 +1,14 @@ use std::collections::BTreeMap; use log::debug; -use syscall::PAGE_SIZE; use syscall::error::Result; use syscall::io::{Io, Mmio}; +use syscall::PAGE_SIZE; use common::dma::Dma; -use super::Xhci; use super::ring::Ring; +use super::Xhci; #[repr(packed)] pub struct SlotContext { @@ -185,17 +185,19 @@ impl ScratchpadBufferArray { pub fn new(ac64: bool, entries: u16) -> Result { let mut entries = unsafe { Xhci::alloc_dma_zeroed_unsized_raw(ac64, entries as usize)? }; - let pages = entries.iter_mut().map(|entry: &mut ScratchpadBufferEntry| -> Result<_, syscall::Error> { - let dma = unsafe { Dma::<[u8; PAGE_SIZE]>::zeroed()?.assume_init() }; - assert_eq!(dma.physical() % PAGE_SIZE, 0); - entry.set_addr(dma.physical() as u64); - Ok(dma) - }).collect::, _>>()?; + let pages = entries + .iter_mut() + .map( + |entry: &mut ScratchpadBufferEntry| -> Result<_, syscall::Error> { + let dma = unsafe { Dma::<[u8; PAGE_SIZE]>::zeroed()?.assume_init() }; + assert_eq!(dma.physical() % PAGE_SIZE, 0); + entry.set_addr(dma.physical() as u64); + Ok(dma) + }, + ) + .collect::, _>>()?; - Ok(Self { - entries, - pages, - }) + Ok(Self { entries, pages }) } pub fn register(&self) -> usize { self.entries.physical() diff --git a/xhcid/src/xhci/event.rs b/xhcid/src/xhci/event.rs index b42a126ae7..c955d6488d 100644 --- a/xhcid/src/xhci/event.rs +++ b/xhcid/src/xhci/event.rs @@ -3,9 +3,9 @@ use syscall::io::{Io, Mmio}; use common::dma::Dma; -use super::Xhci; use super::ring::Ring; use super::trb::Trb; +use super::Xhci; #[repr(packed)] pub struct EventRingSte { @@ -29,8 +29,12 @@ impl EventRing { ring: Ring::new(ac64, 256, false)?, }; - ring.ste[0].address_low.write(ring.ring.trbs.physical() as u32); - ring.ste[0].address_high.write((ring.ring.trbs.physical() as u64 >> 32) as u32); + ring.ste[0] + .address_low + .write(ring.ring.trbs.physical() as u32); + ring.ste[0] + .address_high + .write((ring.ring.trbs.physical() as u64 >> 32) as u32); ring.ste[0].size.write(ring.ring.trbs.len() as u16); Ok(ring) diff --git a/xhcid/src/xhci/irq_reactor.rs b/xhcid/src/xhci/irq_reactor.rs index f496280ed7..2de350cbba 100644 --- a/xhcid/src/xhci/irq_reactor.rs +++ b/xhcid/src/xhci/irq_reactor.rs @@ -3,24 +3,24 @@ use std::fs::File; use std::future::Future; use std::io::prelude::*; use std::pin::Pin; -use std::sync::{Arc, Mutex}; use std::sync::atomic::{self, AtomicUsize}; +use std::sync::{Arc, Mutex}; use std::{io, mem, task, thread}; use std::os::unix::io::AsRawFd; -use crossbeam_channel::{Sender, Receiver}; -use log::{debug, error, info, warn, trace}; +use crossbeam_channel::{Receiver, Sender}; use futures::Stream; +use log::{debug, error, info, trace, warn}; use syscall::Io; use event::{Event, EventQueue, RawEventQueue}; -use super::Xhci; use super::doorbell::Doorbell; +use super::event::EventRing; use super::ring::Ring; use super::trb::{Trb, TrbCompletionCode, TrbType}; -use super::event::EventRing; +use super::Xhci; /// Short-term states (as in, they are removed when the waker is consumed, but probably pushed back /// by the future unless it completed). @@ -65,8 +65,14 @@ impl RingId { /// is lost. #[derive(Clone, Copy, Debug)] pub enum StateKind { - CommandCompletion { phys_ptr: u64 }, - Transfer { first_phys_ptr: u64, last_phys_ptr: u64, ring_id: RingId }, + CommandCompletion { + phys_ptr: u64, + }, + Transfer { + first_phys_ptr: u64, + last_phys_ptr: u64, + ring_id: RingId, + }, Other(TrbType), } @@ -80,14 +86,12 @@ impl StateKind { } } - pub struct IrqReactor { hci: Arc, irq_file: Option, receiver: Receiver, states: Vec, - // TODO: Since the IRQ reactor is the only part of this driver that gets event TRBs, perhaps // the event ring should be owned here? } @@ -111,7 +115,14 @@ impl IrqReactor { debug!("Running IRQ reactor in polling mode."); let hci_clone = Arc::clone(&self.hci); - let mut event_trb_index = { hci_clone.primary_event_ring.lock().unwrap().ring.next_index() }; + let mut event_trb_index = { + hci_clone + .primary_event_ring + .lock() + .unwrap() + .ring + .next_index() + }; 'trb_loop: loop { self.pause(); @@ -146,17 +157,32 @@ impl IrqReactor { debug!("Running IRQ reactor with IRQ file and event queue"); let hci_clone = Arc::clone(&self.hci); - let mut event_queue = RawEventQueue::new().expect("xhcid irq_reactor: failed to create IRQ event queue"); + let mut event_queue = + RawEventQueue::new().expect("xhcid irq_reactor: failed to create IRQ event queue"); let irq_fd = self.irq_file.as_ref().unwrap().as_raw_fd(); - event_queue.subscribe(irq_fd as usize, 0, event::EventFlags::READ).unwrap(); + event_queue + .subscribe(irq_fd as usize, 0, event::EventFlags::READ) + .unwrap(); - let mut event_trb_index = { hci_clone.primary_event_ring.lock().unwrap().ring.next_index() }; + let mut event_trb_index = { + hci_clone + .primary_event_ring + .lock() + .unwrap() + .ring + .next_index() + }; for _event in event_queue { trace!("IRQ event queue notified"); let mut buffer = [0u8; 8]; - let _ = self.irq_file.as_mut().unwrap().read(&mut buffer).expect("Failed to read from irq scheme"); + let _ = self + .irq_file + .as_mut() + .unwrap() + .read(&mut buffer) + .expect("Failed to read from irq scheme"); if !self.hci.received_irq() { // continue only when an IRQ to this device was received @@ -178,12 +204,20 @@ impl IrqReactor { let event_trb = &mut event_ring.ring.trbs[event_trb_index]; if event_trb.completion_code() == TrbCompletionCode::Invalid as u8 { - if count == 0 { warn!("xhci: Received interrupt, but no event was found in the event ring. Ignoring interrupt.") } + if count == 0 { + warn!("xhci: Received interrupt, but no event was found in the event ring. Ignoring interrupt.") + } // no more events were found, continue the loop return; - } else { count += 1 } + } else { + count += 1 + } - trace!("Found event TRB type {}: {:?}", event_trb.trb_type(), event_trb); + trace!( + "Found event TRB type {}: {:?}", + event_trb.trb_type(), + event_trb + ); if self.check_event_ring_full(event_trb.clone()) { info!("Had to resize event TRB, retrying..."); @@ -205,15 +239,27 @@ impl IrqReactor { fn update_erdp(&self, event_ring: &EventRing) { let dequeue_pointer_and_dcs = event_ring.erdp(); let dequeue_pointer = dequeue_pointer_and_dcs & 0xFFFF_FFFF_FFFF_FFFE; - assert_eq!(dequeue_pointer & 0xFFFF_FFFF_FFFF_FFF0, dequeue_pointer, "unaligned ERDP received from primary event ring"); + assert_eq!( + dequeue_pointer & 0xFFFF_FFFF_FFFF_FFF0, + dequeue_pointer, + "unaligned ERDP received from primary event ring" + ); trace!("Updated ERDP to {:#0x}", dequeue_pointer); - self.hci.run.lock().unwrap().ints[0].erdp_low.write(dequeue_pointer as u32); - self.hci.run.lock().unwrap().ints[0].erdp_high.write((dequeue_pointer >> 32) as u32); + self.hci.run.lock().unwrap().ints[0] + .erdp_low + .write(dequeue_pointer as u32); + self.hci.run.lock().unwrap().ints[0] + .erdp_high + .write((dequeue_pointer >> 32) as u32); } fn handle_requests(&mut self) { - self.states.extend(self.receiver.try_iter().inspect(|req| trace!("Received request: {:X?}", req))); + self.states.extend( + self.receiver + .try_iter() + .inspect(|req| trace!("Received request: {:X?}", req)), + ); } fn acknowledge(&mut self, trb: Trb) { //TODO: handle TRBs without an attached state @@ -225,19 +271,27 @@ impl IrqReactor { trace!("ACK STATE {}: {:X?}", index, self.states[index].kind); match self.states[index].kind { - StateKind::CommandCompletion { phys_ptr } if trb.trb_type() == TrbType::CommandCompletion as u8 => { + StateKind::CommandCompletion { phys_ptr } + if trb.trb_type() == TrbType::CommandCompletion as u8 => + { if trb.completion_trb_pointer() == Some(phys_ptr) { trace!("Found matching command completion future"); let state = self.states.remove(index); // Before waking, it's crucial that the command TRB that generated this event // is fetched before removing this event TRB from the queue. - let command_trb = match self.hci.cmd.lock().unwrap().phys_addr_to_entry_mut(self.hci.cap.ac64(), phys_ptr) { + let command_trb = match self + .hci + .cmd + .lock() + .unwrap() + .phys_addr_to_entry_mut(self.hci.cap.ac64(), phys_ptr) + { Some(command_trb) => { let t = command_trb.clone(); command_trb.reserved(false); t - }, + } None => { warn!("The xHC supplied a pointer to a command TRB that was outside the known command ring bounds. Ignoring event TRB {:?}.", trb); continue; @@ -259,8 +313,16 @@ impl IrqReactor { } } - 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() { + 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() + { match trb.transfer_event_trb_pointer() { Some(phys_ptr) => { let matches = if first_phys_ptr <= last_phys_ptr { @@ -279,7 +341,7 @@ impl IrqReactor { state.waker.wake(); return; } - }, + } None => { // Ring Overrun, Ring Underrun, or Virtual Function Event Ring Full. // @@ -305,18 +367,23 @@ impl IrqReactor { return; } - _ => () + _ => (), } index += 1; } - warn!("Lost event TRB type {}, completion code: {}: {:X?}", trb.trb_type(), trb.completion_code(), trb); + warn!( + "Lost event TRB type {}, completion code: {}: {:X?}", + trb.trb_type(), + trb.completion_code(), + trb + ); } fn acknowledge_failed_transfer_trbs(&mut self, trb: Trb) { let mut index = 0; loop { - if ! self.states[index].is_isoch_or_vf { + if !self.states[index].is_isoch_or_vf { index += 1; if index >= self.states.len() { break; @@ -335,7 +402,8 @@ impl IrqReactor { /// Full. If so, it grows the event ring. The return value is whether the event ring was full, /// and then grown. fn check_event_ring_full(&mut self, event_trb: Trb) -> bool { - let had_event_ring_full_error = event_trb.trb_type() == TrbType::HostController as u8 && event_trb.completion_code() == TrbCompletionCode::EventRingFull as u8; + let had_event_ring_full_error = event_trb.trb_type() == TrbType::HostController as u8 + && event_trb.completion_code() == TrbCompletionCode::EventRingFull as u8; if had_event_ring_full_error { self.grow_event_ring(); @@ -386,7 +454,11 @@ impl EventDoorbell { } enum EventTrbFuture { - Pending { state: FutureState, sender: Sender, doorbell_opt: Option }, + Pending { + state: FutureState, + sender: Sender, + doorbell_opt: Option, + }, Finished, } @@ -397,18 +469,24 @@ impl Future for EventTrbFuture { let this = self.get_mut(); let message = match this { - &mut Self::Pending { ref state, ref sender, ref mut doorbell_opt } => match state.message.lock().unwrap().take() { + &mut Self::Pending { + ref state, + ref sender, + ref mut doorbell_opt, + } => match state.message.lock().unwrap().take() { Some(message) => message, None => { // Register state with IRQ reactor trace!("Send state {:X?}", state.state_kind); - sender.send(State { - message: Arc::clone(&state.message), - is_isoch_or_vf: state.is_isoch_or_vf, - kind: state.state_kind, - waker: context.waker().clone(), - }).expect("IRQ reactor thread unexpectedly stopped"); + sender + .send(State { + message: Arc::clone(&state.message), + is_isoch_or_vf: state.is_isoch_or_vf, + kind: state.state_kind, + waker: context.waker().clone(), + }) + .expect("IRQ reactor thread unexpectedly stopped"); // Doorbell must be rung after sending state if let Some(doorbell) = doorbell_opt.take() { @@ -417,7 +495,7 @@ impl Future for EventTrbFuture { return task::Poll::Pending; } - } + }, &mut Self::Finished => panic!("Polling finished EventTrbFuture again."), }; *this = Self::Finished; @@ -427,7 +505,8 @@ impl Future for EventTrbFuture { impl Xhci { pub fn get_transfer_trb(&self, paddr: u64, id: RingId) -> Option { - self.with_ring(id, |ring| ring.phys_addr_to_entry(self.cap.ac64(), paddr)).flatten() + self.with_ring(id, |ring| ring.phys_addr_to_entry(self.cap.ac64(), paddr)) + .flatten() } pub fn with_ring T>(&self, id: RingId, function: F) -> Option { use super::RingOrStreams; @@ -442,7 +521,11 @@ impl Xhci { Some(function(ring_ref)) } - pub fn with_ring_mut T>(&self, id: RingId, function: F) -> Option { + pub fn with_ring_mut T>( + &self, + id: RingId, + function: F, + ) -> Option { use super::RingOrStreams; let mut slot_state = self.port_states.get_mut(&(id.port as usize))?; @@ -455,8 +538,15 @@ impl Xhci { Some(function(ring_ref)) } - 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() { + 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) } @@ -477,8 +567,13 @@ impl Xhci { doorbell_opt: Some(doorbell), } } - pub fn next_command_completion_event_trb(&self, command_ring: &Ring, trb: &Trb, doorbell: EventDoorbell) -> impl Future + Send + Sync + 'static { - if ! trb.is_command_trb() { + pub fn next_command_completion_event_trb( + &self, + command_ring: &Ring, + trb: &Trb, + doorbell: EventDoorbell, + ) -> impl Future + Send + Sync + 'static { + if !trb.is_command_trb() { panic!("Invalid TRB type given to next_command_completion_event_trb(): {} (TRB {:?}. Expected command TRB.", trb.trb_type(), trb) } EventTrbFuture::Pending { @@ -494,7 +589,10 @@ impl Xhci { doorbell_opt: Some(doorbell), } } - pub fn next_misc_event_trb(&self, trb_type: TrbType) -> impl Future + Send + Sync + 'static { + pub fn next_misc_event_trb( + &self, + trb_type: TrbType, + ) -> impl Future + Send + Sync + 'static { let valid_trb_types = [ TrbType::PortStatusChange as u8, TrbType::BandwidthRequest as u8, @@ -503,7 +601,7 @@ impl Xhci { TrbType::DeviceNotification as u8, TrbType::MfindexWrap as u8, ]; - if ! valid_trb_types.contains(&(trb_type as u8)) { + if !valid_trb_types.contains(&(trb_type as u8)) { panic!("Invalid TRB type given to next_misc_event_trb(): {:?}. Only event TRB types that are neither transfer events or command completion events can be used.", trb_type) } EventTrbFuture::Pending { @@ -516,5 +614,4 @@ impl Xhci { doorbell_opt: None, } } - } diff --git a/xhcid/src/xhci/mod.rs b/xhcid/src/xhci/mod.rs index 52b582adea..07a6826bb1 100644 --- a/xhcid/src/xhci/mod.rs +++ b/xhcid/src/xhci/mod.rs @@ -1,17 +1,28 @@ +//! The eXtensible Host Controller Interface (XHCI) Module +//! +//! This module implements the XHCI functionality of Redox's USB driver daemon. +//! +//! XHCI is a standard for the USB Host Controller interface specified by Intel that provides a +//! common register interface for systems to use to interact with the Universal Serial Bus (USB) +//! subsystem. +//! +//! The standard can be found [here](https://www.intel.com/content/dam/www/public/us/en/documents/technical-specifications/extensible-host-controler-interface-usb-xhci.pdf). +//! The standard is referenced frequently throughout this documentation. The acronyms used for specific +//! documents are specified in the crate-level documentation. use std::collections::BTreeMap; use std::convert::TryFrom; use std::fs::File; use std::future::Future; use std::pin::Pin; use std::ptr::NonNull; -use std::sync::{Arc, Mutex, MutexGuard, Weak}; use std::sync::atomic::{AtomicBool, AtomicUsize}; +use std::sync::{Arc, Mutex, MutexGuard, Weak}; use std::{mem, process, slice, sync::atomic, task, thread}; -use syscall::PAGE_SIZE; -use syscall::error::{Error, Result, EBADF, EBADMSG, ENOENT, EIO}; +use syscall::error::{Error, Result, EBADF, EBADMSG, EIO, ENOENT}; use syscall::io::Io; +use syscall::PAGE_SIZE; use chashmap::CHashMap; use common::dma::Dma; @@ -22,7 +33,7 @@ use serde::Deserialize; use crate::usb; use pcid_interface::msi::{MsixInfo, MsixTableEntry}; -use pcid_interface::{PciFunctionHandle, PciFeature}; +use pcid_interface::{PciFeature, PciFunctionHandle}; mod capability; mod context; @@ -40,9 +51,9 @@ mod trb; use self::capability::CapabilityRegs; use self::context::{DeviceContextList, InputContext, ScratchpadBufferArray, StreamContextArray}; use self::doorbell::Doorbell; -use self::irq_reactor::{EventDoorbell, IrqReactor, NewPendingTrb, RingId}; use self::event::EventRing; use self::extended::{CapabilityId, ExtendedCapabilitiesIter, ProtocolSpeed, SupportedProtoCap}; +use self::irq_reactor::{EventDoorbell, IrqReactor, NewPendingTrb, RingId}; use self::operational::OperationalRegs; use self::port::Port; use self::ring::Ring; @@ -53,6 +64,8 @@ use self::scheme::EndpIfState; use crate::driver_interface::*; +/// Specifies the configurable interrupt mechanism used by the xhci subsystem for registering +/// device state change notifications. pub enum InterruptMethod { /// No interrupts whatsoever; the driver will instead rely on polling event rings. Polling, @@ -83,13 +96,32 @@ impl MappedMsixRegs { 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<()> { + 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); + 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 ring = port_state + .endpoint_states + .get_mut(&0) + .ok_or(Error::new(EIO))? + .ring() + .expect("no ring for the default control pipe"); let first_index = ring.next_index(); let (cmd, cycle) = (&mut ring.trbs[first_index], ring.cycle); @@ -118,7 +150,7 @@ impl Xhci { &ring, &ring.trbs[first_index], &ring.trbs[last_index], - EventDoorbell::new(self, usize::from(slot), Self::def_control_endp_doorbell()) + EventDoorbell::new(self, usize::from(slot), Self::def_control_endp_doorbell()), ) }; @@ -132,33 +164,63 @@ impl Xhci { Ok(()) } - async fn fetch_dev_desc_8_byte(&self, port: usize, slot: u8) -> Result { + async fn fetch_dev_desc_8_byte( + &self, + port: usize, + slot: u8, + ) -> Result { let mut desc = unsafe { self.alloc_dma_zeroed::()? }; - self.get_desc_raw(port, slot, usb::DescriptorKind::Device, 0, &mut desc).await?; + self.get_desc_raw(port, slot, usb::DescriptorKind::Device, 0, &mut desc) + .await?; Ok(*desc) } async fn fetch_dev_desc(&self, port: usize, slot: u8) -> Result { let mut desc = unsafe { self.alloc_dma_zeroed::()? }; - self.get_desc_raw(port, slot, usb::DescriptorKind::Device, 0, &mut desc).await?; + self.get_desc_raw(port, slot, usb::DescriptorKind::Device, 0, &mut desc) + .await?; Ok(*desc) } - async fn fetch_config_desc(&self, port: usize, slot: u8, config: u8) -> Result<(usb::ConfigDescriptor, [u8; 4087])> { + async fn fetch_config_desc( + &self, + port: usize, + slot: u8, + config: u8, + ) -> Result<(usb::ConfigDescriptor, [u8; 4087])> { let mut desc = unsafe { self.alloc_dma_zeroed::<(usb::ConfigDescriptor, [u8; 4087])>()? }; - self.get_desc_raw(port, slot, usb::DescriptorKind::Configuration, config, &mut desc).await?; + self.get_desc_raw( + port, + slot, + usb::DescriptorKind::Configuration, + config, + &mut desc, + ) + .await?; Ok(*desc) } - async fn fetch_bos_desc(&self, port: usize, slot: u8) -> Result<(usb::BosDescriptor, [u8; 4087])> { + async fn fetch_bos_desc( + &self, + port: usize, + slot: u8, + ) -> Result<(usb::BosDescriptor, [u8; 4087])> { let mut desc = unsafe { self.alloc_dma_zeroed::<(usb::BosDescriptor, [u8; 4087])>()? }; - self.get_desc_raw(port, slot, usb::DescriptorKind::BinaryObjectStorage, 0, &mut desc).await?; + self.get_desc_raw( + port, + slot, + usb::DescriptorKind::BinaryObjectStorage, + 0, + &mut desc, + ) + .await?; Ok(*desc) } async fn fetch_string_desc(&self, port: usize, slot: u8, index: u8) -> Result { let mut sdesc = unsafe { self.alloc_dma_zeroed::<(u8, u8, [u16; 127])>()? }; - self.get_desc_raw(port, slot, usb::DescriptorKind::String, index, &mut sdesc).await?; + self.get_desc_raw(port, slot, usb::DescriptorKind::String, index, &mut sdesc) + .await?; let len = sdesc.0 as usize; if len > 2 { @@ -169,16 +231,26 @@ impl Xhci { } } +/// The eXtensible Host Controller Interface (XHCI) data structure pub struct Xhci { // immutable + /// The Host Controller Interface Capability Registers. These read-only registers specify the + /// limits and capabilities of the host controller implementation (See XHCI section 5.3) cap: &'static CapabilityRegs, //page_size: usize, // XXX: It would be really useful to be able to mutably access individual elements of a slice, // without having to wrap every element in a lock (which wouldn't work since they're packed). + /// The Host Controller Interface Operational Registers. These registers provide the software + /// interface to configure and monitor the state of the XHCI (See XHCI section 5.4) op: Mutex<&'static mut OperationalRegs>, ports: Mutex<&'static mut [Port]>, + /// The Host Controller Interface Doorbell Registers. There is one register per device slot, + /// and these registers are used by system software to notify the XHC that it has work to perform + /// for a specific device slot. (See XHCI sections 4.7 and 5.6) dbs: Arc>, + /// The Host Controller Interface Runtime Registers. These handle interrupt and event processing, + /// and provide time-sensitive information such as the current microframe. (See XHCI section 5.5) run: Mutex<&'static mut RuntimeRegs>, cmd: Mutex, primary_event_ring: Mutex, @@ -224,8 +296,7 @@ impl PortState { //TODO: fetch using endpoint number instead fn get_endp_desc(&self, endp_idx: u8) -> Option<&EndpDesc> { let cfg_idx = self.cfg_idx?; - let config_desc = self.dev_desc.as_ref()? - .config_descs.get(cfg_idx as usize)?; + let config_desc = self.dev_desc.as_ref()?.config_descs.get(cfg_idx as usize)?; let mut endp_count = 0; for if_desc in config_desc.interface_descs.iter() { for endp_desc in if_desc.endpoints.iter() { @@ -258,16 +329,24 @@ impl EndpointState { } impl Xhci { - pub fn new(scheme_name: String, address: usize, interrupt_method: InterruptMethod, pcid_handle: PciFunctionHandle) -> Result { + pub fn new( + scheme_name: String, + address: usize, + interrupt_method: InterruptMethod, + pcid_handle: PciFunctionHandle, + ) -> Result { + //Locate the capability registers from the mapped PCI Bar let cap = unsafe { &mut *(address as *mut CapabilityRegs) }; debug!("CAP REGS BASE {:X}", address); //let page_size = ... + //The operational registers appear immediately after the capability registers. let op_base = address + cap.len.read() as usize; let op = unsafe { &mut *(op_base as *mut OperationalRegs) }; debug!("OP REGS BASE {:X}", op_base); + //Reset the XHCI device let (max_slots, max_ports) = { debug!("Waiting for xHC becoming ready."); // Wait until controller is ready @@ -300,11 +379,13 @@ impl Xhci { (max_slots, max_ports) }; + //Get the address of the port register table let port_base = op_base + 0x400; let ports = unsafe { slice::from_raw_parts_mut(port_base as *mut Port, max_ports as usize) }; debug!("PORT BASE {:X}", port_base); + //Get the address of the dorbell register table let db_base = address + cap.db_offset.read() as usize; let dbs = unsafe { slice::from_raw_parts_mut(db_base as *mut Doorbell, 256) }; debug!("DOORBELL REGS BASE {:X}", db_base); @@ -325,7 +406,6 @@ impl Xhci { cap, //page_size, - op: Mutex::new(op), ports: Mutex::new(ports), dbs: Arc::new(Mutex::new(dbs)), @@ -371,23 +451,37 @@ impl Xhci { // Set enabled slots debug!("Setting enabled slots to {}.", max_slots); self.op.get_mut().unwrap().config.write(max_slots as u32); - debug!("Enabled Slots: {}", self.op.get_mut().unwrap().config.read() & 0xFF); + debug!( + "Enabled Slots: {}", + self.op.get_mut().unwrap().config.read() & 0xFF + ); // Set device context address array pointer let dcbaap = self.dev_ctx.dcbaap(); debug!("Writing DCBAAP: {:X}", dcbaap); self.op.get_mut().unwrap().dcbaap_low.write(dcbaap as u32); - self.op.get_mut().unwrap().dcbaap_high.write((dcbaap as u64 >> 32) as u32); + self.op + .get_mut() + .unwrap() + .dcbaap_high + .write((dcbaap as u64 >> 32) as u32); // Set command ring control register let crcr = self.cmd.get_mut().unwrap().register(); assert_eq!(crcr & 0xFFFF_FFFF_FFFF_FFC1, crcr, "unaligned CRCR"); debug!("Writing CRCR: {:X}", crcr); self.op.get_mut().unwrap().crcr_low.write(crcr as u32); - self.op.get_mut().unwrap().crcr_high.write((crcr as u64 >> 32) as u32); + self.op + .get_mut() + .unwrap() + .crcr_high + .write((crcr as u64 >> 32) as u32); // Set event ring segment table registers - debug!("Interrupter 0: {:p}", self.run.get_mut().unwrap().ints.as_ptr()); + debug!( + "Interrupter 0: {:p}", + self.run.get_mut().unwrap().ints.as_ptr() + ); { let int = &mut self.run.get_mut().unwrap().ints[0]; @@ -410,7 +504,6 @@ impl Xhci { debug!("Enabling Primary Interrupter."); int.iman.writef(1 << 1 | 1, true); - } self.op.get_mut().unwrap().usb_cmd.writef(1 << 2, true); @@ -446,7 +539,7 @@ impl Xhci { port.portsc.writef(port::PortFlags::PORT_PR.bits(), true); while port.portsc.readf(port::PortFlags::PORT_PR.bits()) { - //while ! port.flags().contains(port::PortFlags::PORT_PRC) { + //while ! port.flags().contains(port::PortFlags::PORT_PRC) { if instant.elapsed().as_secs() >= 1 { warn!("timeout"); break; @@ -467,7 +560,11 @@ impl Xhci { } let scratchpad_buf_arr = ScratchpadBufferArray::new(self.cap.ac64(), buf_count)?; self.dev_ctx.dcbaa[0] = scratchpad_buf_arr.register() as u64; - debug!("Setting up {} scratchpads, at {:#0x}", buf_count, scratchpad_buf_arr.register()); + debug!( + "Setting up {} scratchpads, at {:#0x}", + buf_count, + scratchpad_buf_arr.register() + ); self.scratchpad_buf_arr = Some(scratchpad_buf_arr); Ok(()) @@ -476,8 +573,9 @@ impl Xhci { pub async fn enable_port_slot(&self, slot_ty: u8) -> Result { assert_eq!(slot_ty & 0x1F, slot_ty); - let (event_trb, command_trb) = - self.execute_command(|cmd, cycle| cmd.enable_slot(slot_ty, cycle)).await; + let (event_trb, command_trb) = self + .execute_command(|cmd, cycle| cmd.enable_slot(slot_ty, cycle)) + .await; self::scheme::handle_event_trb("ENABLE_SLOT", &event_trb, &command_trb)?; self.event_handler_finished(); @@ -485,7 +583,9 @@ impl Xhci { Ok(event_trb.event_slot()) } pub async fn disable_port_slot(&self, slot: u8) -> Result<()> { - let (event_trb, command_trb) = self.execute_command(|cmd, cycle| cmd.disable_slot(slot, cycle)).await; + let (event_trb, command_trb) = self + .execute_command(|cmd, cycle| cmd.disable_slot(slot, cycle)) + .await; self::scheme::handle_event_trb("DISABLE_SLOT", &event_trb, &command_trb)?; self.event_handler_finished(); @@ -512,7 +612,10 @@ impl Xhci { } pub async fn probe(&self) -> Result<()> { - debug!("XHCI capabilities: {:?}", self.capabilities_iter().collect::>()); + debug!( + "XHCI capabilities: {:?}", + self.capabilities_iter().collect::>() + ); let port_count = { self.ports.lock().unwrap().len() }; @@ -548,7 +651,10 @@ impl Xhci { info!("Enabled port {}, which the xHC mapped to {}", i, slot); let mut input = unsafe { self.alloc_dma_zeroed::()? }; - let mut ring = match self.address_device(&mut input, i, slot_ty, slot, speed).await { + let mut ring = match self + .address_device(&mut input, i, slot_ty, slot, speed) + .await + { Ok(ok) => ok, Err(err) => { error!("Failed to address device for port {}: {}", i, err); @@ -582,7 +688,8 @@ impl Xhci { let mut input = port_state.input_context.lock().unwrap(); - self.update_max_packet_size(&mut *input, slot, dev_desc_8_byte).await?; + self.update_max_packet_size(&mut *input, slot, dev_desc_8_byte) + .await?; } let dev_desc = self.get_desc(i, slot).await?; @@ -594,7 +701,8 @@ impl Xhci { let mut input = port_state.input_context.lock().unwrap(); let dev_desc = port_state.dev_desc.as_ref().unwrap(); - self.update_default_control_pipe(&mut *input, slot, dev_desc).await?; + self.update_default_control_pipe(&mut *input, slot, dev_desc) + .await?; } match self.spawn_drivers(i) { @@ -611,7 +719,7 @@ impl Xhci { &self, input_context: &mut Dma, slot_id: u8, - dev_desc: usb::DeviceDescriptor8Byte + dev_desc: usb::DeviceDescriptor8Byte, ) -> Result<()> { let new_max_packet_size = if dev_desc.major_usb_vers() == 2 { u32::from(dev_desc.packet_size) @@ -624,9 +732,11 @@ impl Xhci { b |= (new_max_packet_size) << 16; endp_ctx.b.write(b); - let (event_trb, command_trb) = self.execute_command(|trb, cycle| { - trb.evaluate_context(slot_id, input_context.physical(), false, cycle) - }).await; + let (event_trb, command_trb) = self + .execute_command(|trb, cycle| { + trb.evaluate_context(slot_id, input_context.physical(), false, cycle) + }) + .await; self::scheme::handle_event_trb("EVALUATE_CONTEXT", &event_trb, &command_trb)?; self.event_handler_finished(); @@ -654,9 +764,11 @@ impl Xhci { b |= (new_max_packet_size) << 16; endp_ctx.b.write(b); - let (event_trb, command_trb) = self.execute_command(|trb, cycle| { - trb.evaluate_context(slot_id, input_context.physical(), false, cycle) - }).await; + let (event_trb, command_trb) = self + .execute_command(|trb, cycle| { + trb.evaluate_context(slot_id, input_context.physical(), false, cycle) + }) + .await; self::scheme::handle_event_trb("EVALUATE_CONTEXT", &event_trb, &command_trb)?; self.event_handler_finished(); @@ -753,12 +865,19 @@ impl Xhci { let input_context_physical = input_context.physical(); - let (event_trb, _) = self.execute_command(|trb, cycle| { - trb.address_device(slot, input_context_physical, false, cycle) - }).await; + let (event_trb, _) = self + .execute_command(|trb, cycle| { + trb.address_device(slot, input_context_physical, false, cycle) + }) + .await; if event_trb.completion_code() != TrbCompletionCode::Success as u8 { - error!("Failed to address device at slot {} (port {}), completion code 0x{:X}", slot, i, event_trb.completion_code()); + error!( + "Failed to address device at slot {} (port {}), completion code 0x{:X}", + slot, + i, + event_trb.completion_code() + ); self.event_handler_finished(); return Err(Error::new(EIO)); } @@ -768,10 +887,18 @@ impl Xhci { } pub fn uses_msi(&self) -> bool { - if let InterruptMethod::Msi = self.interrupt_method { true } else { false } + if let InterruptMethod::Msi = self.interrupt_method { + true + } else { + false + } } pub fn uses_msix(&self) -> bool { - if let InterruptMethod::MsiX(_) = self.interrupt_method { true } else { false } + if let InterruptMethod::MsiX(_) = self.interrupt_method { + true + } else { + false + } } // TODO: Perhaps use an rwlock? pub fn msix_info(&self) -> Option> { @@ -795,10 +922,18 @@ impl Xhci { if self.uses_msi() || self.uses_msix() { // Since using MSI and MSI-X implies having no IRQ sharing whatsoever, the IP bit // doesn't have to be touched. - trace!("Successfully received MSI/MSI-X interrupt, IP={}, EHB={}", runtime_regs.ints[0].iman.readf(1), runtime_regs.ints[0].erdp_low.readf(3)); + trace!( + "Successfully received MSI/MSI-X interrupt, IP={}, EHB={}", + runtime_regs.ints[0].iman.readf(1), + runtime_regs.ints[0].erdp_low.readf(3) + ); true } else if runtime_regs.ints[0].iman.readf(1) { - trace!("Successfully received INTx# interrupt, IP={}, EHB={}", runtime_regs.ints[0].iman.readf(1), runtime_regs.ints[0].erdp_low.readf(3)); + trace!( + "Successfully received INTx# interrupt, IP={}, EHB={}", + runtime_regs.ints[0].iman.readf(1), + runtime_regs.ints[0].erdp_low.readf(3) + ); // If MSI and/or MSI-X are not used, the interrupt might have to be shared, and thus there is // a special register to specify whether the IRQ actually came from the xHC. runtime_regs.ints[0].iman.writef(1, true); @@ -809,7 +944,6 @@ impl Xhci { // The interrupt came from a different device. false } - } fn spawn_drivers(&self, port: usize) -> Result<()> { // TODO: There should probably be a way to select alternate interfaces, and not just the @@ -822,7 +956,8 @@ impl Xhci { //TODO: support choosing config? let config_desc = &ps .dev_desc - .as_ref().ok_or_else(|| { + .as_ref() + .ok_or_else(|| { log::warn!("Missing device descriptor"); Error::new(EBADF) })? @@ -868,7 +1003,10 @@ impl Xhci { .or(Err(Error::new(ENOENT)))?; self.drivers.insert(port, process); } else { - warn!("No driver for USB class {}.{}", ifdesc.class, ifdesc.sub_class); + warn!( + "No driver for USB class {}.{}", + ifdesc.class, ifdesc.sub_class + ); } } @@ -964,13 +1102,18 @@ impl Xhci { ]; match self.supported_protocol(port) { - Some(supp_proto) => if supp_proto.psic() != 0 { - unsafe { supp_proto.protocol_speeds().iter() } - } else { - DEFAULT_SUPP_PROTO_SPEEDS.iter() - }, + Some(supp_proto) => { + if supp_proto.psic() != 0 { + unsafe { supp_proto.protocol_speeds().iter() } + } else { + DEFAULT_SUPP_PROTO_SPEEDS.iter() + } + } None => { - log::warn!("falling back to default supported protocol speeds for port {}", port); + log::warn!( + "falling back to default supported protocol speeds for port {}", + port + ); DEFAULT_SUPP_PROTO_SPEEDS.iter() } } diff --git a/xhcid/src/xhci/operational.rs b/xhcid/src/xhci/operational.rs index 855de69808..4dcc760e9f 100644 --- a/xhcid/src/xhci/operational.rs +++ b/xhcid/src/xhci/operational.rs @@ -4,21 +4,83 @@ use syscall::io::{Io, Mmio}; use super::CapabilityRegs; +/// The XHCI Operational Registers +/// +/// These registers specify the operational state of the XHCI device, and are used to receive status +/// messages and transmit commands. These registers are offset from the XHCI base address by the +/// "length" field of the [CapabilityRegs] +/// +/// See XHCI section 5.4. Table 5-18 describes the offset of these registers in memory. #[repr(packed)] pub struct OperationalRegs { + /// The USB Command Register (USBCMD) + /// + /// Describes the command to be executed by the XHCI. Writes to this register case a command + /// to be executed. + /// + /// - Bit 0 is the Run/Stop bit (R/S). Writing a value of 1 stops the xHC from executing the schedule, 1 resumes. Latency is ~16ms at worst. (See XHCI Table 5-20) + /// - Bit 1 is the Host Controller Reset Bit (HCRST). Used by software to reset the host controller (See XHCI Table 5-20) + /// - Bit 2 is the Interrupter Enable Bit (INTE). Enables interrupting the host system. + /// - Bit 3 is the Host System Error Enable Bit (HSEE). Enables out-of-band error signalling to the host. + /// - Bits 4-6 are reserved. + /// - Bit 7 is the Light Host Controller Reset Bit (LHCRST). Resets the driver without affecting the state of the ports. Affected by [CapabilityRegs] + /// - Bit 8 is the Controller Save State Bit (CSS). See XHCI Table 5-20 + /// - Bit 9 is the Controller Restore State Bit (CRS). See XHCI Table 5-20 + /// - Bit 10 is the Enable Wrap Event Bit (EWE). See XHCI Table 5-20 + /// - Bit 11 is the Enable U3 MFINDEX Stop Bit (EU3S). See XHCI Table 5-20 + /// - Bit 12 is reserved. + /// - Bit 13 is the CEM Enable Bit (CME). See XHCI Table 5-20 + /// - Bit 14 is the Extended TBC Enable Bit (ETE). See XHCI Table 5-20 + /// - Bit 15 is the Extended TBC TRB Status Enable Bit (TSC_En). See XHCI Table 5-20 + /// - Bit 16 is the VTIO Enable Bit (VTIOE). Controls the enable state of the VTIO capability. + /// - Bits 17-31 are reserved. + /// pub usb_cmd: Mmio, + /// The USB Status Register (USBSTS) + /// + /// This register indicates pending interrupts and various states of the host controller. + /// + /// Software sets a bit to '0' in this register by writing a 1 to it. + /// + /// pub usb_sts: Mmio, + /// The PAGESIZE Register (PAGESIZE) + /// + /// pub page_size: Mmio, + /// Reserved bits (RsvdZ) _rsvd: [Mmio; 2], + /// The Device Notification Control Register (DNCTRL) + /// + /// pub dn_ctrl: Mmio, + /// The Command Ring Control Register Lower 32 bits (CRCR) + /// + /// pub crcr_low: Mmio, + /// The Command Ring Control Register Upper 32 bits (CRCR) + /// + /// pub crcr_high: Mmio, + /// Reserved bits (RsvdZ) _rsvd2: [Mmio; 4], + /// Device Context Base Address Array Pointer Lower 32 bits (DCBAAP) + /// + /// pub dcbaap_low: Mmio, + /// Device Context Base Address Array Pointer Upper 32 bits (DCBAAP) + /// + /// pub dcbaap_high: Mmio, + /// The Configure Register (CONFIG) + /// + /// pub config: Mmio, + // The standard has another set of reserved bits from 3C-3FFh here + // The standard has 400-13FFh has a Port Register Set here (likely defined in port.rs). } +/// The mask to get the CIE bit from the Config register. See [OperationalRegs] pub const OP_CONFIG_CIE_BIT: u32 = 1 << 9; impl OperationalRegs { diff --git a/xhcid/src/xhci/ring.rs b/xhcid/src/xhci/ring.rs index 0bb74f7098..e0e76d5228 100644 --- a/xhcid/src/xhci/ring.rs +++ b/xhcid/src/xhci/ring.rs @@ -4,8 +4,8 @@ use syscall::error::Result; use common::dma::Dma; -use super::Xhci; use super::trb::Trb; +use super::Xhci; pub struct Ring { pub link: bool, @@ -59,7 +59,10 @@ impl Ring { /// Endless iterator that iterates through the ring items, over and over again. The iterator /// doesn't enqueue or dequeue anything. pub fn iter(&self) -> impl Iterator + '_ { - Iter { ring: self, i: self.i } + Iter { + ring: self, + i: self.i, + } } /// Takes a physical address and returns the index into this ring, that the index represents. /// Returns `None` if the address is outside the bounds of this ring. @@ -67,10 +70,19 @@ impl Ring { /// # Panics /// Panics if paddr is not a multiple of 16 bytes, i.e. the size of a TRB. pub fn phys_addr_to_index(&self, ac64: bool, paddr: u64) -> Option { - let base = (self.trbs.physical() as u64) & if ac64 { 0xFFFF_FFFF_FFFF_FFFF } else { 0xFFFF_FFFF }; + let base = (self.trbs.physical() as u64) + & if ac64 { + 0xFFFF_FFFF_FFFF_FFFF + } else { + 0xFFFF_FFFF + }; let offset = paddr.checked_sub(base)? as usize; - assert_eq!(offset % mem::size_of::(), 0, "unaligned TRB physical address"); + assert_eq!( + offset % mem::size_of::(), + 0, + "unaligned TRB physical address" + ); let index = offset / mem::size_of::(); @@ -100,12 +112,20 @@ impl Ring { let trb_virt_pointer = trb as *const Trb; let trbs_base_virt_pointer = self.trbs.as_ptr(); - if (trb_virt_pointer as usize) < (trbs_base_virt_pointer as usize) || (trb_virt_pointer as usize) > (trbs_base_virt_pointer as usize) + self.trbs.len() * mem::size_of::() { + if (trb_virt_pointer as usize) < (trbs_base_virt_pointer as usize) + || (trb_virt_pointer as usize) + > (trbs_base_virt_pointer as usize) + self.trbs.len() * mem::size_of::() + { panic!("Gave a TRB outside of the ring, when retrieving its physical address in that ring. TRB: {:?} (at address {:p})", trb, trb); } let trb_offset_from_base = trb_virt_pointer as u64 - trbs_base_virt_pointer as u64; - let trbs_base_phys_ptr = (self.trbs.physical() as u64) & if ac64 { 0xFFFF_FFFF_FFFF_FFFF } else { 0xFFFF_FFFF }; + let trbs_base_phys_ptr = (self.trbs.physical() as u64) + & if ac64 { + 0xFFFF_FFFF_FFFF_FFFF + } else { + 0xFFFF_FFFF + }; let trb_phys_ptr = trbs_base_phys_ptr + trb_offset_from_base; trb_phys_ptr } @@ -119,7 +139,6 @@ impl Ring { struct Iter<'ring> { ring: &'ring Ring, i: usize, - } impl<'ring> Iterator for Iter<'ring> { type Item = &'ring Trb; diff --git a/xhcid/src/xhci/scheme.rs b/xhcid/src/xhci/scheme.rs index d3dec98d5a..b5824d5f61 100644 --- a/xhcid/src/xhci/scheme.rs +++ b/xhcid/src/xhci/scheme.rs @@ -1,3 +1,21 @@ +//! Provides the File Descriptor Scheme Interface to the XHCI. +//! +//! This file implements the basic unix file operations that are used to interface with the XHCI +//! driver. While an external program could interact with the driver using this interface, a +//! higher-level abstraction can be found in driver_interface.rs. It is recommended that you use +//! the functions in that module to interact with the driver. +//! +//! The XHCI driver has the following set of schemes: +//! +//! port +//! port/configure +//! port/request +//! port/endpoints +//! port/descriptors +//! port/state +//! port/endpoints/ +//! port/endpoints//ctl +//! port/endpoints//data use std::convert::TryFrom; use std::io::prelude::*; use std::ops::Deref; @@ -6,7 +24,7 @@ use std::{cmp, fmt, io, mem, path, str}; use common::dma::Dma; use futures::executor::block_on; -use log::{debug, error, info, warn, trace}; +use log::{debug, error, info, trace, warn}; use serde::{Deserialize, Serialize}; use smallvec::{smallvec, SmallVec}; @@ -36,6 +54,28 @@ use super::trb::{TransferKind, Trb, TrbCompletionCode, TrbType}; use super::usb::endpoint::{EndpointTy, ENDP_ATTR_TY_MASK}; use crate::driver_interface::*; +use regex::Regex; + +lazy_static! { + static ref REGEX_PORT_CONFIGURE: Regex = Regex::new(r"^port(\d{1,3})/configure$") + .expect("Failed to create the regex for the port/configure scheme."); + static ref REGEX_PORT_DESCRIPTORS: Regex = Regex::new(r"^port(\d{1,3})/descriptors$") + .expect("Failed to create the regex for the port/descriptors"); + static ref REGEX_PORT_STATE: Regex = Regex::new(r"^port(\d{1,3})/state$") + .expect("Failed to create the regex for the port/state scheme"); + static ref REGEX_PORT_REQUEST: Regex = Regex::new(r"^port(\d{1,3})/request$") + .expect("Failed to create the regex for the port/request scheme"); + static ref REGEX_PORT_ENDPOINTS: Regex = Regex::new(r"^port(\d{1,3})/endpoints$") + .expect("Failed to create the regex for the port/endpoints scheme"); + static ref REGEX_PORT_SPECIFIC_ENDPOINT: Regex = Regex::new(r"^port(\d{1,3})/endpoints/(\d{1,3})$") + .expect("Failed to create the regex for the port/endpoints/ scheme"); + static ref REGEX_PORT_SUB_ENDPOINT: Regex = Regex::new( + r"port(\d{1,3})/endpoints/(\d{1,3})/(ctl|data)$" + ) + .expect("Failed to create the regex for the port/endpoints// scheme"); + static ref REGEX_TOP_LEVEL: Regex = + Regex::new(r"^$").expect("Failed to create the regex for the top-level scheme"); +} pub enum ControlFlow { Continue, @@ -84,6 +124,9 @@ pub enum PortReqState { Tmp, } +/// The Handle to a specific scheme that is returned by an open() operation. +/// +/// Contains some information about the data requested via the handle. #[derive(Debug)] pub enum Handle { TopLevel(usize, Vec), // offset, contents (ports) @@ -96,6 +139,259 @@ pub enum Handle { ConfigureEndpoints(usize), // port } +/// The type of handle. +/// +/// This is used by fstat() to determine whether to return a: +/// - MODE_DIR +/// - MODE_FILE +/// - MODE_CHR +pub(crate) enum HandleType { + Directory, + File, + Character, +} + +/// Parameters to a handle that were extracted from a scheme. +/// +/// This structure is used to easily convert a scheme filesystem path to +/// the parameters that we care about when constructing a handle. +#[derive(Debug)] +enum SchemeParameters { + /// The scheme references the top-level XHCI driver endpoint + TopLevel, + /// /port + Port(usize), // port number + /// /port/descriptors + PortDesc(usize), // port number + /// /port/state + PortState(usize), // port number + /// /port/request + PortReq(usize), // port number + /// /port/endpoints + Endpoints(usize), // port number + /// /port/endpoints//(data|ctl) + /// + /// This can also represent + /// /port/endpoints/ + Endpoint(usize, u8, String), // port number, endpoint number, handle type + /// /port/configure + ConfigureEndpoints(usize), // port number +} + +impl Handle { + /// Converts a handle back into the scheme that generated it. + /// + /// This is useful for implementing fpath, as the input parameters for our existing schemes + /// are generally static for the lifetime of the driver and can easily be retrieved. + /// + /// # Returns + /// - A [String] containing the scheme path that the handle is associated with. + pub(crate) fn to_scheme(&self) -> String { + match self { + Handle::TopLevel(_, _) => String::from(""), + Handle::Port(port_num, _, _) => { + format!("port{}", port_num) + } + Handle::PortDesc(port_num, _, _) => { + format!("port{}/descriptors", port_num) + } + Handle::PortState(port_num, _) => { + format!("port{}/state", port_num) + } + Handle::PortReq(port_num, _) => { + format!("port{}/request", port_num) + } + Handle::Endpoints(port_num, _, _) => { + format!("port{}/endpoints", port_num) + } + Handle::Endpoint(port_num, endpoint_num, handle_type) => match handle_type { + EndpointHandleTy::Data => { + format!("port{}/endpoints/{}/data", port_num, endpoint_num) + } + EndpointHandleTy::Ctl => { + format!("port{}/endpoints/{}/ctl", port_num, endpoint_num) + } + EndpointHandleTy::Root(_, _) => { + format!("port{}/endpoints/{}", port_num, endpoint_num) + } + }, + Handle::ConfigureEndpoints(port_num) => { + format!("port{}/configure", port_num) + } + } + } + + /// Gets the access mode for this handle + /// + /// Handles can be a file, a directory, or a character interface. The mode that we use is + /// entirely dependent upon the functionality of the scheme endpoint, so this returns the value + /// that should be associated with that endpoint. + /// + /// # Returns + /// - [HandleType] - The access mode associated with the handle. + pub(crate) fn get_handle_type(&self) -> HandleType { + match self { + &Handle::TopLevel(_, _) => HandleType::Directory, + &Handle::Port(_, _, _) => HandleType::Directory, + &Handle::Endpoints(_, _, _) => HandleType::Directory, + &Handle::PortDesc(_, _, _) => HandleType::File, + &Handle::PortReq(_, PortReqState::WaitingForDeviceBytes(_, _)) => HandleType::Character, + &Handle::PortReq(_, PortReqState::WaitingForHostBytes(_, _)) => HandleType::Character, + &Handle::PortReq(_, PortReqState::Tmp) => unreachable!(), + &Handle::PortReq(_, PortReqState::TmpSetup(_)) => unreachable!(), + &Handle::PortState(_, _) => HandleType::Character, + &Handle::PortReq(_, _) => HandleType::Character, + &Handle::ConfigureEndpoints(_) => HandleType::Character, + &Handle::Endpoint(_, _, ref st) => match st { + EndpointHandleTy::Data => HandleType::Character, + EndpointHandleTy::Ctl => HandleType::Character, + EndpointHandleTy::Root(_, _) => HandleType::Directory, + }, + } + } + + /// Gets the length of the file buffer as returned by fstat in Stat.st_size + /// + /// As some of these endpoints did not return a length in the origin code, this + /// provides an Option + /// + /// # Returns + /// Either the size of the buffer, or [Option::None] if the buffer does not exist. + pub(crate) fn get_buf_len(&self) -> Option { + match self { + &Handle::TopLevel(_, ref buf) => Some(buf.len()), + &Handle::Port(_, _, ref buf) => Some(buf.len()), + &Handle::Endpoints(_, _, ref buf) => Some(buf.len()), + &Handle::PortDesc(_, _, ref buf) => Some(buf.len()), + &Handle::PortReq(_, PortReqState::WaitingForDeviceBytes(ref buf, _)) => Some(buf.len()), + &Handle::PortReq(_, PortReqState::WaitingForHostBytes(ref buf, _)) => Some(buf.len()), + &Handle::PortReq(_, PortReqState::Tmp) => None, + &Handle::PortReq(_, PortReqState::TmpSetup(_)) => None, + &Handle::PortState(_, _) => None, + &Handle::PortReq(_, _) => None, + &Handle::ConfigureEndpoints(_) => None, + &Handle::Endpoint(_, _, ref st) => match st { + EndpointHandleTy::Data => None, + EndpointHandleTy::Ctl => None, + EndpointHandleTy::Root(_, ref buf) => Some(buf.len()), + }, + } + } +} + +impl SchemeParameters { + /// This function gets a partially populated handle from a scheme string. + /// + /// This function is intended to be used by the driver's 'open' filesystem + /// hook to determine if the given string value represents a valid scheme + /// + /// # Arguments + /// 'scheme: &[str]' - A scheme in string format. + /// + /// # Returns + /// A [Result] containing: + /// - A [SchemeParameters] object representing the scheme that was passed, populated with the input parameters + /// - [ENOENT] if the passed scheme path is not valid for this driver. + /// + /// # Notes + /// ENOENT is returned so that it can easily be forwarded to the caller of open(). It cleans + /// up the function considerably to be able to use the ? syntax. + pub fn from_scheme(scheme: &str) -> Result { + fn get_string_from_regex( + rgx: &Regex, + scheme: &str, + capture_idx: usize, + ) -> syscall::Result { + if let Some(capture_list) = rgx.captures(scheme) { + if let Some(value) = capture_list.get(capture_idx + 1) { + return Ok(value.as_str().to_string()); + } + } + + Err(Error::new(ENOENT)) + }; + + fn get_usize_from_regex( + rgx: &Regex, + scheme: &str, + capture_idx: usize, + ) -> syscall::Result { + if let Some(capture_list) = rgx.captures(scheme) { + if let Some(value) = capture_list.get(capture_idx + 1) { + if let Ok(integer) = value.as_str().parse::() { + return Ok(integer); + } + } + } + + Err(Error::new(ENOENT)) + }; + + fn get_u8_from_regex(rgx: &Regex, scheme: &str, capture_idx: usize) -> syscall::Result { + if let Some(capture_list) = rgx.captures(scheme) { + if let Some(value) = capture_list.get(capture_idx + 1) { + if let Ok(integer) = value.as_str().parse::() { + return Ok(integer); + } + } + } + + Err(Error::new(ENOENT)) + }; + + //We don't implement From::<&path::Path> because we don't want to make this a part of + //the public interface. This function does not guarantee that the handle is VALID, only + //that the scheme is valid. open() will validate the contents of the enumeration instance, + //and store it if it's valid. + + //Generate the regular expressions for all of our valid schemes. + + //Check if we have a match and either return a partially initialized scheme, OR ENOENT + if REGEX_PORT_CONFIGURE.is_match(scheme) { + let port_num = get_usize_from_regex(®EX_PORT_CONFIGURE, scheme, 0)?; + + Ok(Self::ConfigureEndpoints(port_num)) + } else if REGEX_PORT_DESCRIPTORS.is_match(scheme) { + + let port_num = get_usize_from_regex(®EX_PORT_DESCRIPTORS, scheme, 0)?; + + Ok(Self::PortDesc(port_num)) + } else if REGEX_PORT_STATE.is_match(scheme) { + + let port_num = get_usize_from_regex(®EX_PORT_STATE, scheme, 0)?; + + Ok(Self::PortState(port_num)) + } else if REGEX_PORT_REQUEST.is_match(scheme) { + + let port_num = get_usize_from_regex(®EX_PORT_REQUEST, scheme, 0)?; + + Ok(Self::PortReq(port_num)) + } else if REGEX_PORT_ENDPOINTS.is_match(scheme) { + + let port_num = get_usize_from_regex(®EX_PORT_ENDPOINTS, scheme, 0)?; + + Ok(Self::Endpoints(port_num)) + } else if REGEX_PORT_SPECIFIC_ENDPOINT.is_match(scheme) { + + let port_num = get_usize_from_regex(®EX_PORT_SPECIFIC_ENDPOINT, scheme, 0)?; + let endpoint_num = get_u8_from_regex(®EX_PORT_SPECIFIC_ENDPOINT, scheme, 1)?; + + Ok(Self::Endpoint(port_num, endpoint_num, String::from("root"))) + } else if REGEX_PORT_SUB_ENDPOINT.is_match(scheme) { + + let port_num = get_usize_from_regex(®EX_PORT_SUB_ENDPOINT, scheme, 0)?; + let endpoint_num = get_u8_from_regex(®EX_PORT_SUB_ENDPOINT, scheme, 1)?; + let handle_type = get_string_from_regex(®EX_PORT_SUB_ENDPOINT, scheme, 2)?; + + Ok(Self::Endpoint(port_num, endpoint_num, handle_type)) + } else if REGEX_TOP_LEVEL.is_match(scheme) { + Ok(Self::TopLevel) + } else { + Err(Error::new(ENOENT)) + } + } +} + #[derive(Clone, Copy)] struct DmaSliceDbg<'a, T>(&'a Dma<[T]>); @@ -115,15 +411,25 @@ impl fmt::Debug for PortReqState { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Self::Init => f.debug_struct("PortReqState::Init").finish(), - Self::WaitingForDeviceBytes(ref dma, setup) => f.debug_tuple("PortReqState::WaitingForDeviceBytes").field(&DmaSliceDbg(dma)).field(&setup).finish(), - Self::WaitingForHostBytes(ref dma, setup) => f.debug_tuple("PortReqState::WaitingForHostBytes").field(&DmaSliceDbg(dma)).field(&setup).finish(), - Self::TmpSetup(setup) => f.debug_tuple("PortReqState::TmpSetup").field(&setup).finish(), + Self::WaitingForDeviceBytes(ref dma, setup) => f + .debug_tuple("PortReqState::WaitingForDeviceBytes") + .field(&DmaSliceDbg(dma)) + .field(&setup) + .finish(), + Self::WaitingForHostBytes(ref dma, setup) => f + .debug_tuple("PortReqState::WaitingForHostBytes") + .field(&DmaSliceDbg(dma)) + .field(&setup) + .finish(), + Self::TmpSetup(setup) => f + .debug_tuple("PortReqState::TmpSetup") + .field(&setup) + .finish(), Self::Tmp => f.debug_struct("PortReqState::Init").finish(), } } } - // TODO: Even though the driver interface descriptors are originally intended for JSON, they should suffice... for // now. @@ -233,7 +539,10 @@ impl Xhci { alternate_setting: desc.alternate_setting, class: desc.class, interface_str: if desc.interface_str > 0 { - Some(self.fetch_string_desc(port_id, slot, desc.interface_str).await?) + Some( + self.fetch_string_desc(port_id, slot, desc.interface_str) + .await?, + ) } else { None }, @@ -250,10 +559,7 @@ impl Xhci { /// /// # Locking /// This function will lock `Xhci::cmd` and `Xhci::dbs`. - pub async fn execute_command( - &self, - f: F, - ) -> (Trb, Trb) { + pub async fn execute_command(&self, f: F) -> (Trb, Trb) { { // If ERDP EHB bit is set, clear it before sending command //TODO: find out why this bit is set earlier! @@ -278,7 +584,7 @@ impl Xhci { self.next_command_completion_event_trb( &*command_ring, command_trb, - EventDoorbell::new(self, 0, 0) + EventDoorbell::new(self, 0, 0), ) }; @@ -286,7 +592,11 @@ impl Xhci { let event_trb = trbs.event_trb; let command_trb = trbs.src_trb.expect("Command completion event TRBs shall always have a valid pointer to a valid source command TRB"); - assert_eq!(event_trb.trb_type(), TrbType::CommandCompletion as u8, "The IRQ reactor (or the xHC) gave an invalid event TRB"); + assert_eq!( + event_trb.trb_type(), + TrbType::CommandCompletion as u8, + "The IRQ reactor (or the xHC) gave an invalid event TRB" + ); (event_trb, command_trb) } @@ -307,12 +617,11 @@ impl Xhci { let mut endpoint_state = port_state .endpoint_states - .get_mut(&0).ok_or(Error::new(EIO))?; - - let ring = endpoint_state - .ring() + .get_mut(&0) .ok_or(Error::new(EIO))?; + let ring = endpoint_state.ring().ok_or(Error::new(EIO))?; + let first_index = ring.next_index(); let (cmd, cycle) = (&mut ring.trbs[first_index], ring.cycle); cmd.setup(setup, tk, cycle); @@ -343,7 +652,7 @@ impl Xhci { ring, &ring.trbs[first_index], &ring.trbs[last_index], - EventDoorbell::new(self, usize::from(slot), Self::def_control_endp_doorbell()) + EventDoorbell::new(self, usize::from(slot), Self::def_control_endp_doorbell()), ) }; @@ -378,20 +687,14 @@ impl Xhci { let slot = port_state.slot; let (doorbell_data_stream, doorbell_data_no_stream) = { - let endp_desc = port_state.get_endp_desc(endp_idx).ok_or(Error::new(EBADFD))?; + let endp_desc = port_state + .get_endp_desc(endp_idx) + .ok_or(Error::new(EBADFD))?; //TODO: clean this up ( - Self::endp_doorbell( - endp_num, - endp_desc, - stream_id, - ), - Self::endp_doorbell( - endp_num, - endp_desc, - 0, - ), + Self::endp_doorbell(endp_num, endp_desc, stream_id), + Self::endp_doorbell(endp_num, endp_desc, 0), ) }; @@ -408,10 +711,13 @@ impl Xhci { EndpointState { transfer: super::RingOrStreams::Streams(stream_ctx_array), .. - } => (true, stream_ctx_array - .rings - .get_mut(&1) - .ok_or(Error::new(EBADF))?), + } => ( + true, + stream_ctx_array + .rings + .get_mut(&1) + .ok_or(Error::new(EBADF))?, + ), }; let future = loop { @@ -421,16 +727,24 @@ impl Xhci { match d(trb, cycle) { ControlFlow::Break => { break self.next_transfer_event_trb( - super::irq_reactor::RingId { port: port_num as u8, endpoint_num: endp_num, stream_id }, + 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 - } else { - doorbell_data_no_stream - }), + EventDoorbell::new( + self, + usize::from(slot), + if has_streams { + doorbell_data_stream + } else { + doorbell_data_no_stream + }, + ), ); } ControlFlow::Continue => continue, @@ -449,9 +763,7 @@ impl Xhci { if event_trb.completion_code() != TrbCompletionCode::ShortPacket as u8 && event_trb.transfer_length() != 0 { - error!( - "Event trb didn't yield a short packet, but some bytes were not transferred" - ); + error!("Event trb didn't yield a short packet, but some bytes were not transferred"); return Err(Error::new(EIO)); } @@ -469,13 +781,15 @@ impl Xhci { TransferKind::NoData, "DEVICE_REQ_NO_DATA", |_, _| ControlFlow::Break, - ).await?; + ) + .await?; Ok(()) } async fn set_configuration(&self, port: usize, config: u8) -> Result<()> { debug!("Setting configuration value {} to port {}", config, port); - self.device_req_no_data(port, usb::Setup::set_configuration(config)).await + self.device_req_no_data(port, usb::Setup::set_configuration(config)) + .await } async fn set_interface( @@ -484,18 +798,24 @@ impl Xhci { interface_num: u8, alternate_setting: u8, ) -> Result<()> { - debug!("Setting interface value {} (alternate setting {}) to port {}", interface_num, alternate_setting, port); + debug!( + "Setting interface value {} (alternate setting {}) to port {}", + interface_num, alternate_setting, port + ); self.device_req_no_data( port, usb::Setup::set_interface(interface_num, alternate_setting), - ).await + ) + .await } async fn reset_endpoint(&self, port_num: usize, endp_num: u8, tsp: bool) -> Result<()> { let endp_idx = endp_num.checked_sub(1).ok_or(Error::new(EIO))?; let port_state = self.port_states.get(&port_num).ok_or(Error::new(EBADFD))?; - let endp_desc = port_state.get_endp_desc(endp_idx).ok_or(Error::new(EBADFD))?; + let endp_desc = port_state + .get_endp_desc(endp_idx) + .ok_or(Error::new(EBADFD))?; let endp_num_xhc = Self::endp_num_to_dci(endp_num, endp_desc); let slot = self @@ -504,9 +824,11 @@ impl Xhci { .ok_or(Error::new(EBADF))? .slot; - let (event_trb, command_trb) = self.execute_command(|trb, cycle| { - trb.reset_endpoint(slot, endp_num_xhc, tsp, cycle); - }).await; + let (event_trb, command_trb) = self + .execute_command(|trb, cycle| { + trb.reset_endpoint(slot, endp_num_xhc, tsp, cycle); + }) + .await; self.event_handler_finished(); handle_event_trb("RESET_ENDPOINT", &event_trb, &command_trb) @@ -589,14 +911,20 @@ impl Xhci { fn port_state(&self, port: usize) -> Result> { self.port_states.get(&port).ok_or(Error::new(EBADF)) } - fn port_state_mut(&self, port: usize) -> Result> { + fn port_state_mut( + &self, + port: usize, + ) -> Result> { self.port_states.get_mut(&port).ok_or(Error::new(EBADF)) } async fn configure_endpoints(&self, port: usize, json_buf: &[u8]) -> Result<()> { let mut req: ConfigureEndpointsReq = serde_json::from_slice(json_buf).or(Err(Error::new(EBADMSG)))?; - info!("Running configure endpoints command, at port {}, request: {:?}", port, req); + info!( + "Running configure endpoints command, at port {}, request: {:?}", + port, req + ); if req.interface_desc.is_some() != req.alternate_setting.is_some() { return Err(Error::new(EBADMSG)); @@ -607,7 +935,13 @@ impl Xhci { port_state.cfg_idx = Some(req.config_desc); - let config_desc = port_state.dev_desc.as_ref().unwrap().config_descs.get(usize::from(req.config_desc)).ok_or(Error::new(EBADFD))?; + let config_desc = port_state + .dev_desc + .as_ref() + .unwrap() + .config_descs + .get(usize::from(req.config_desc)) + .ok_or(Error::new(EBADFD))?; //TODO: USE ENDPOINTS FROM ALL INTERFACES let mut endp_desc_count = 0; @@ -638,9 +972,8 @@ impl Xhci { let log_max_psa_size = self.cap.max_psa_size(); let port_speed_id = self.ports.lock().unwrap()[port].speed(); - let speed_id: &ProtocolSpeed = self - .lookup_psiv(port as u8, port_speed_id) - .ok_or_else(|| { + let speed_id: &ProtocolSpeed = + self.lookup_psiv(port as u8, port_speed_id).ok_or_else(|| { warn!("no speed_id"); Error::new(EIO) })?; @@ -732,8 +1065,8 @@ impl Xhci { warn!("trying to use control endpoint"); return Err(Error::new(EIO)); // only endpoint zero is of type control, and is configured separately with the address device command. } - EndpointTy::Bulk | EndpointTy::Isoch => 3072, // 3 KiB - EndpointTy::Interrupt => 1024, // 1 KiB + EndpointTy::Bulk | EndpointTy::Isoch => 3072, // 3 KiB + EndpointTy::Interrupt => 1024, // 1 KiB }; assert_eq!(ep_ty & 0x7, ep_ty); @@ -742,7 +1075,8 @@ impl Xhci { assert_ne!(ep_ty, 0); // 0 means invalid. let ring_ptr = if usb_log_max_streams.is_some() { - let mut array = StreamContextArray::new(self.cap.ac64(), 1 << (primary_streams + 1))?; + let mut array = + StreamContextArray::new(self.cap.ac64(), 1 << (primary_streams + 1))?; // TODO: Use as many stream rings as needed. array.add_ring(self.cap.ac64(), 1, true)?; @@ -785,10 +1119,14 @@ impl Xhci { let mut input_context = port_state.input_context.lock().unwrap(); input_context.add_context.writef(1 << endp_num_xhc, true); - let endp_ctx = input_context.device.endpoints.get_mut(endp_num_xhc as usize - 1).ok_or_else(|| { - warn!("failed to find endpoint {}", endp_num_xhc - 1); - Error::new(EIO) - })?; + let endp_ctx = input_context + .device + .endpoints + .get_mut(endp_num_xhc as usize - 1) + .ok_or_else(|| { + warn!("failed to find endpoint {}", endp_num_xhc - 1); + Error::new(EIO) + })?; endp_ctx.a.write( u32::from(mult) << 8 @@ -818,9 +1156,11 @@ impl Xhci { let slot = port_state.slot; let input_context_physical = port_state.input_context.lock().unwrap().physical(); - let (event_trb, command_trb) = self.execute_command(|trb, cycle| { - trb.configure_endpoint(slot, input_context_physical, cycle) - }).await; + let (event_trb, command_trb) = self + .execute_command(|trb, cycle| { + trb.configure_endpoint(slot, input_context_physical, cycle) + }) + .await; self.event_handler_finished(); @@ -832,7 +1172,8 @@ impl Xhci { if let Some(interface_num) = req.interface_desc { if let Some(alternate_setting) = req.alternate_setting { - self.set_interface(port, interface_num, alternate_setting).await?; + self.set_interface(port, interface_num, alternate_setting) + .await?; } } @@ -849,31 +1190,47 @@ impl Xhci { } let dma_buffer = unsafe { self.alloc_dma_zeroed_unsized(buf.len())? }; - let (completion_code, bytes_transferred, dma_buffer) = self.transfer( - port_num, - endp_idx, - Some(dma_buffer), - PortReqDirection::DeviceToHost, - ).await?; + let (completion_code, bytes_transferred, dma_buffer) = self + .transfer( + port_num, + endp_idx, + Some(dma_buffer), + PortReqDirection::DeviceToHost, + ) + .await?; buf.copy_from_slice(&*dma_buffer.as_ref().unwrap()); Ok((completion_code, bytes_transferred)) } - async fn transfer_write(&self, port_num: usize, endp_idx: u8, sbuf: &[u8]) -> Result<(u8, u32)> { + async fn transfer_write( + &self, + port_num: usize, + endp_idx: u8, + sbuf: &[u8], + ) -> Result<(u8, u32)> { if sbuf.is_empty() { return Err(Error::new(EINVAL)); } let mut dma_buffer = unsafe { self.alloc_dma_zeroed_unsized(sbuf.len()) }?; dma_buffer.copy_from_slice(sbuf); - trace!("TRANSFER_WRITE port {} ep {}, buffer at {:p}, size {}, dma buffer {:?}", port_num, endp_idx + 1, sbuf.as_ptr(), sbuf.len(), DmaSliceDbg(&dma_buffer)); - - let (completion_code, bytes_transferred, _) = self.transfer( + trace!( + "TRANSFER_WRITE port {} ep {}, buffer at {:p}, size {}, dma buffer {:?}", port_num, - endp_idx, - Some(dma_buffer), - PortReqDirection::HostToDevice, - ).await?; + endp_idx + 1, + sbuf.as_ptr(), + sbuf.len(), + DmaSliceDbg(&dma_buffer) + ); + + let (completion_code, bytes_transferred, _) = self + .transfer( + port_num, + endp_idx, + Some(dma_buffer), + PortReqDirection::HostToDevice, + ) + .await?; Ok((completion_code, bytes_transferred)) } pub const fn def_control_endp_doorbell() -> u32 { @@ -916,7 +1273,9 @@ impl Xhci { .get_mut(&port_num) .ok_or(Error::new(EBADFD))?; - let endp_desc: &EndpDesc = port_state.get_endp_desc(endp_idx).ok_or(Error::new(EBADFD))?; + let endp_desc: &EndpDesc = port_state + .get_endp_desc(endp_idx) + .ok_or(Error::new(EBADFD))?; let direction = endp_desc.direction(); @@ -932,23 +1291,30 @@ impl Xhci { let max_transfer_size = 65536u32; let (buffer, idt, estimated_td_size) = { - let (buffer, idt) = - if dma_buf.as_ref().map(|buf| buf.len()).unwrap_or(0) <= 8 && max_packet_size >= 8 && direction != EndpDirection::In { - dma_buf.as_ref().map(|sbuf| { + let (buffer, idt) = if dma_buf.as_ref().map(|buf| buf.len()).unwrap_or(0) <= 8 + && max_packet_size >= 8 + && direction != EndpDirection::In + { + dma_buf + .as_ref() + .map(|sbuf| { let mut bytes = [0u8; 8]; bytes[..sbuf.len()].copy_from_slice(&sbuf); (u64::from_le_bytes(bytes), true) }) .unwrap_or((0, false)) - } else { - ( - dma_buf.as_ref().map(|dma| dma.physical()).unwrap_or(0) as u64, - false, - ) - }; + } else { + ( + dma_buf.as_ref().map(|dma| dma.physical()).unwrap_or(0) as u64, + false, + ) + }; let estimated_td_size = cmp::min( u8::try_from( - div_round_up(dma_buf.as_ref().map(|buf| buf.len()).unwrap_or(0), max_transfer_size as usize) * mem::size_of::(), + div_round_up( + dma_buf.as_ref().map(|buf| buf.len()).unwrap_or(0), + max_transfer_size as usize, + ) * mem::size_of::(), ) .ok() .unwrap_or(0x1F), @@ -963,56 +1329,57 @@ impl Xhci { drop(port_state); - let event = self.execute_transfer( - port_num, - endp_num, - stream_id, - "CUSTOM_TRANSFER", - |trb, cycle| { - let len = cmp::min(bytes_left, max_transfer_size as usize) as u32; + let event = self + .execute_transfer( + port_num, + endp_num, + stream_id, + "CUSTOM_TRANSFER", + |trb, cycle| { + let len = cmp::min(bytes_left, max_transfer_size as usize) as u32; - // set the interrupt on completion (IOC) flag for the last trb. - let ioc = bytes_left <= max_transfer_size as usize; - let chain = !ioc; + // set the interrupt on completion (IOC) flag for the last trb. + let ioc = bytes_left <= max_transfer_size as usize; + let chain = !ioc; - let interrupter = 0; - let ent = false; - let isp = true; - let bei = false; - trb.normal( - buffer, - len, - cycle, - estimated_td_size, - interrupter, - ent, - isp, - chain, - ioc, - idt, - bei, - ); + let interrupter = 0; + let ent = false; + let isp = true; + let bei = false; + trb.normal( + buffer, + len, + cycle, + estimated_td_size, + interrupter, + ent, + isp, + chain, + ioc, + idt, + bei, + ); - bytes_left -= len as usize; + bytes_left -= len as usize; - if bytes_left != 0 { - ControlFlow::Continue - } else { - ControlFlow::Break - } - }, - ).await?; + if bytes_left != 0 { + ControlFlow::Continue + } else { + ControlFlow::Break + } + }, + ) + .await?; self.event_handler_finished(); - let bytes_transferred = dma_buf.as_ref().map(|buf| buf.len() as u32 - event.transfer_length()).unwrap_or(0); + let bytes_transferred = dma_buf + .as_ref() + .map(|buf| buf.len() as u32 - event.transfer_length()) + .unwrap_or(0); Ok((event.completion_code(), bytes_transferred, dma_buf)) } - pub async fn get_desc( - &self, - port_id: usize, - slot: u8, - ) -> Result { + pub async fn get_desc(&self, port_id: usize, slot: u8) -> Result { let ports = self.ports.lock().unwrap(); let port = ports.get(port_id).ok_or(Error::new(ENOENT))?; if !port.flags().contains(port::PortFlags::PORT_CCS) { @@ -1024,35 +1391,55 @@ impl Xhci { let (manufacturer_str, product_str, serial_str) = ( if raw_dd.manufacturer_str > 0 { - Some(self.fetch_string_desc(port_id, slot, raw_dd.manufacturer_str).await?) + Some( + self.fetch_string_desc(port_id, slot, raw_dd.manufacturer_str) + .await?, + ) } else { None }, if raw_dd.product_str > 0 { - Some(self.fetch_string_desc(port_id, slot, raw_dd.product_str).await?) + Some( + self.fetch_string_desc(port_id, slot, raw_dd.product_str) + .await?, + ) } else { None }, if raw_dd.serial_str > 0 { - Some(self.fetch_string_desc(port_id, slot, raw_dd.serial_str).await?) + Some( + self.fetch_string_desc(port_id, slot, raw_dd.serial_str) + .await?, + ) } else { None }, ); - log::debug!("manufacturer {:?} product {:?} serial {:?}", manufacturer_str, product_str, serial_str); + log::debug!( + "manufacturer {:?} product {:?} serial {:?}", + manufacturer_str, + product_str, + serial_str + ); //TODO let (bos_desc, bos_data) = self.fetch_bos_desc(port_id, slot).await?; let supports_superspeed = false; - //TODO usb::bos_capability_descs(bos_desc, &bos_data).any(|desc| desc.is_superspeed()); + //TODO usb::bos_capability_descs(bos_desc, &bos_data).any(|desc| desc.is_superspeed()); let supports_superspeedplus = false; - //TODO usb::bos_capability_descs(bos_desc, &bos_data).any(|desc| desc.is_superspeedplus()); + //TODO usb::bos_capability_descs(bos_desc, &bos_data).any(|desc| desc.is_superspeedplus()); let mut config_descs = SmallVec::new(); for index in 0..raw_dd.configurations { let (desc, data) = self.fetch_config_desc(port_id, slot, index).await?; - log::debug!("port {} slot {} config {} desc {:X?}", port_id, slot, index, desc); + log::debug!( + "port {} slot {} config {} desc {:X?}", + port_id, + slot, + index, + desc + ); let extra_length = desc.total_length as usize - mem::size_of_val(&desc); let data = &data[..extra_length]; @@ -1083,7 +1470,7 @@ impl Xhci { Some(unexpected) => { log::warn!("expected endpoint, got {:X?}", unexpected); break; - }, + } None => break, }; let mut endp = EndpDesc::from(next); @@ -1106,7 +1493,10 @@ impl Xhci { endpoints.push(endp); } - interface_descs.push(self.new_if_desc(port_id, slot, idesc, endpoints, hid_descs).await?); + interface_descs.push( + self.new_if_desc(port_id, slot, idesc, endpoints, hid_descs) + .await?, + ); } else { log::warn!("expected interface, got {:?}", item); // TODO @@ -1117,7 +1507,10 @@ impl Xhci { config_descs.push(ConfDesc { kind: desc.kind, configuration: if desc.configuration_str > 0 { - Some(self.fetch_string_desc(port_id, slot, desc.configuration_str).await?) + Some( + self.fetch_string_desc(port_id, slot, desc.configuration_str) + .await?, + ) } else { None }, @@ -1126,7 +1519,7 @@ impl Xhci { max_power: desc.max_power, interface_descs, }); - }; + } Ok(DevDesc { kind: raw_dd.kind, @@ -1182,7 +1575,8 @@ impl Xhci { ); ControlFlow::Break }, - ).await?; + ) + .await?; Ok(()) } fn port_req_init_st(&self, port_num: usize, req: &PortReq) -> Result { @@ -1219,8 +1613,13 @@ impl Xhci { }; Ok(match transfer_kind { - TransferKind::In => PortReqState::WaitingForDeviceBytes(data_buffer_opt.ok_or(Error::new(EINVAL))?, setup), - TransferKind::Out => PortReqState::WaitingForHostBytes(data_buffer_opt.ok_or(Error::new(EINVAL))?, setup), + TransferKind::In => PortReqState::WaitingForDeviceBytes( + data_buffer_opt.ok_or(Error::new(EINVAL))?, + setup, + ), + TransferKind::Out => { + PortReqState::WaitingForHostBytes(data_buffer_opt.ok_or(Error::new(EINVAL))?, setup) + } TransferKind::NoData => PortReqState::TmpSetup(setup), _ => unreachable!(), }) @@ -1242,7 +1641,8 @@ impl Xhci { if let PortReqState::TmpSetup(setup) = st { // No need for any additional reads or writes, before completing. - self.port_req_transfer(port_num, None, setup, TransferKind::NoData).await?; + self.port_req_transfer(port_num, None, setup, TransferKind::NoData) + .await?; st = PortReqState::Init; } @@ -1254,7 +1654,8 @@ impl Xhci { } dma_buffer.copy_from_slice(buf); - self.port_req_transfer(port_num, Some(&mut dma_buffer), setup, TransferKind::Out).await?; + self.port_req_transfer(port_num, Some(&mut dma_buffer), setup, TransferKind::Out) + .await?; st = PortReqState::Init; buf.len() @@ -1281,7 +1682,8 @@ impl Xhci { if buf.len() != dma_buffer.len() { return Err(Error::new(EINVAL)); } - self.port_req_transfer(port_num, Some(&mut dma_buffer), setup, TransferKind::In).await?; + self.port_req_transfer(port_num, Some(&mut dma_buffer), setup, TransferKind::In) + .await?; buf.copy_from_slice(&dma_buffer); st = PortReqState::Init; @@ -1301,133 +1703,211 @@ impl Xhci { } Ok(bytes_read) } -} -impl Scheme for Xhci { - fn open(&self, path_str: &str, flags: usize, uid: u32, _gid: u32) -> Result { - if uid != 0 { - return Err(Error::new(EACCES)); + /// Implements open() for the root level scheme + /// + /// # Arguments + /// - 'flags: [usize]' - The flags parameter passed to open() + /// + /// # Returns + /// This function returns a [Result] containing either + /// + /// - Handle::TopLevel - The file was opened. + /// - EISDIR - This is a directory endpoint, but neither O_DIRECTORY nor O_STAT were passed. + /// + fn open_handle_top_level(&self, flags: usize) -> Result { + if flags & O_DIRECTORY != 0 || flags & O_STAT != 0 { + let mut contents = Vec::new(); + + let ports_guard = self.ports.lock().unwrap(); + + for (index, _) in ports_guard + .iter() + .enumerate() + .filter(|(_, port)| port.flags().contains(port::PortFlags::PORT_CCS)) + { + write!(contents, "port{}\n", index).unwrap(); + } + + Ok(Handle::TopLevel(0, contents)) + } else { + Err(Error::new(EISDIR)) + } + } + + /// implements open() for /port/descriptors + /// + /// # Arguments + /// - 'port_num: [usize]' - The port number specified in the scheme path + /// - 'flags: [usize]' - The flags parameter passed to open() + /// + /// # Returns + /// This function returns a [Result] containing either: + /// + /// - [Handle::PortDesc] - The handle was opened successfully + /// - [ENOTDIR] - Directory-specific flags were passed to open(), but this endpoint is not a directory. + fn open_handle_port_descriptors(&self, port_num: usize, flags: usize) -> Result { + if flags & O_DIRECTORY != 0 && flags & O_STAT == 0 { + return Err(Error::new(ENOTDIR)); } - let path_str = path_str - .trim_start_matches('/'); + let contents = self.port_desc_json(port_num)?; + Ok(Handle::PortDesc(port_num, 0, contents)) + } - let components = path::Path::new(path_str) - .components() - .map(|component| -> Option<_> { - match component { - path::Component::Normal(n) => Some(n.to_str()?), - _ => None, - } - }) - .collect::>>() + /// implements open() for /port + /// + /// # Arguments + /// - 'port_num: [usize]' - The port number specified in the scheme path + /// - 'flags: [usize]' - The flags parameter passed to open() + /// + /// # Returns + /// This function returns a [Result] containing either: + /// + /// - [Handle::Port] - The handle was opened successfully + /// - [ENOENT] - The scheme is valid, but there is no port associated with the given port_num + /// - [EISDIR] - open() was called on this scheme endpoint, but no directory-specific flags were passed to open + fn open_handle_port(&self, port_num: usize, flags: usize) -> Result { + // The != here is unintuitive. You would assume that you could do + // flags & O_DIRECTORY || flags & O_STAT, but rust doesn't allow + // you to cast integers to booleans. + if (flags & O_DIRECTORY != 0) || (flags & O_STAT != 0) { + let mut contents = Vec::new(); + + write!(contents, "descriptors\nendpoints\n").unwrap(); + + if self.slot_state( + self.port_states + .get(&port_num) + .ok_or(Error::new(ENOENT))? + .slot as usize, + ) != SlotState::Configured as u8 + { + write!(contents, "configure\n").unwrap(); + } + + Ok(Handle::Port(port_num, 0, contents)) + } else { + Err(Error::new(EISDIR)) + } + } + + /// implements open() for /port/state + /// + /// # Arguments + /// - 'port_num: [usize]' - The port number specified in the scheme path + /// - 'flags: [usize]' - The flags parameter passed to open() + /// + /// # Returns + /// This function returns a [Result] containing either: + /// + /// - [Handle::Port] - The handle was opened successfully + /// - [ENOTDIR] - open() was called on this scheme endpoint, but directory-specific flags were passed to open + fn open_handle_port_state(&self, port_num: usize, flags: usize) -> Result { + if flags & O_DIRECTORY != 0 && flags & O_STAT == 0 { + return Err(Error::new(ENOTDIR)); + } + + Ok(Handle::PortState(port_num, 0)) + } + + /// implements open() for /port/endpoints + /// + /// # Arguments + /// - 'port_num: [usize]' - The port number specified in the scheme path + /// - 'flags: [usize]' - The flags parameter passed to open() + /// + /// # Returns + /// This function returns a [Result] containing either: + /// + /// - [Handle::Port] - The handle was opened successfully + /// - [EISDIR] - open() was called on this scheme endpoint, but no directory-specific flags were passed to open + fn open_handle_port_endpoints(&self, port_num: usize, flags: usize) -> Result { + if flags & O_DIRECTORY == 0 && flags & O_STAT == 0 { + return Err(Error::new(EISDIR)); + }; + let mut contents = Vec::new(); + let ps = self.port_states.get(&port_num).ok_or(Error::new(ENOENT))?; + + /*for (ep_num, _) in self.dev_ctx.contexts[ps.slot as usize].endpoints.iter().enumerate().filter(|(_, ep)| ep.a.read() & 0b111 == 1) { + write!(contents, "{}\n", ep_num).unwrap(); + }*/ + + for ep_num in ps.endpoint_states.keys() { + write!(contents, "{}\n", ep_num).unwrap(); + } + + Ok(Handle::Endpoints(port_num, 0, contents)) + } + + /// implements open() for /port/endpoints/ + /// + /// # Arguments + /// - 'port_num: [usize]' - The port number specified in the scheme path + /// - 'endpoint_num: [u8]' - The endpoint number to access + /// - 'flags: [usize]' - The flags parameter passed to open() + /// + /// # Returns + /// This function returns a [Result] containing either: + /// + /// - [Handle::Port] - The handle was opened successfully + /// - [EISDIR] - open() was called on this scheme endpoint, but no directory-specific flags were passed to open + /// - [ENOENT] - The scheme is valid, but there is no port associated with the given port_num + fn open_handle_endpoint_root( + &self, + port_num: usize, + endpoint_num: u8, + flags: usize, + ) -> Result { + if flags & O_DIRECTORY == 0 && flags & O_STAT == 0 { + return Err(Error::new(EISDIR)); + } + + let port_state = self + .port_states + .get_mut(&port_num) .ok_or(Error::new(ENOENT))?; - let handle = match &components[..] { - &[] => { - if flags & O_DIRECTORY != 0 || flags & O_STAT != 0 { - let mut contents = Vec::new(); + /*if self.dev_ctx.contexts[port_state.slot as usize].endpoints.get(endpoint_num as usize).ok_or(Error::new(ENOENT))?.a.read() & 0b111 != 1 { + return Err(Error::new(ENXIO)); // TODO: Find a proper error code for "endpoint not initialized". + }*/ - let ports_guard = self.ports.lock().unwrap(); + if !port_state.endpoint_states.contains_key(&endpoint_num) { + return Err(Error::new(ENOENT)); + } + let contents = "ctl\ndata\n".as_bytes().to_owned(); - for (index, _) in ports_guard - .iter() - .enumerate() - .filter(|(_, port)| port.flags().contains(port::PortFlags::PORT_CCS)) - { - write!(contents, "port{}\n", index).unwrap(); - } - - Handle::TopLevel(0, contents) - } else { - return Err(Error::new(EISDIR)); - } - } - &[port, port_tl] if port.starts_with("port") => { - let port_num = port[4..].parse::().or(Err(Error::new(ENOENT)))?; - if !self.port_states.contains_key(&port_num) { - return Err(Error::new(ENOENT)); - } - - match port_tl { - "descriptors" => { - if flags & O_DIRECTORY != 0 && flags & O_STAT == 0 { - return Err(Error::new(ENOTDIR)); - } - - let contents = self.port_desc_json(port_num)?; - Handle::PortDesc(port_num, 0, contents) - } - "configure" => { - if flags & O_DIRECTORY != 0 && flags & O_STAT == 0 { - return Err(Error::new(ENOTDIR)); - } - if flags & O_RDWR != O_WRONLY && flags & O_STAT == 0 { - return Err(Error::new(EACCES)); - } - - Handle::ConfigureEndpoints(port_num) - } - "state" => { - if flags & O_DIRECTORY != 0 && flags & O_STAT == 0 { - return Err(Error::new(ENOTDIR)); - } - - Handle::PortState(port_num, 0) - } - "request" => { - if flags & O_DIRECTORY != 0 && flags & O_STAT == 0 { - return Err(Error::new(ENOTDIR)); - } - Handle::PortReq(port_num, PortReqState::Init) - } - "endpoints" => { - if flags & O_DIRECTORY == 0 && flags & O_STAT == 0 { - return Err(Error::new(EISDIR)); - }; - let mut contents = Vec::new(); - let ps = self.port_states.get(&port_num).ok_or(Error::new(ENOENT))?; - - /*for (ep_num, _) in self.dev_ctx.contexts[ps.slot as usize].endpoints.iter().enumerate().filter(|(_, ep)| ep.a.read() & 0b111 == 1) { - write!(contents, "{}\n", ep_num).unwrap(); - }*/ - - for ep_num in ps.endpoint_states.keys() { - write!(contents, "{}\n", ep_num).unwrap(); - } - - Handle::Endpoints(port_num, 0, contents) - } - _ => return Err(Error::new(ENOENT)), - } - } - &[port, "endpoints", endpoint_num_str] if port.starts_with("port") => { - let port_num = port[4..].parse::().or(Err(Error::new(ENOENT)))?; - let endpoint_num = endpoint_num_str.parse::().or(Err(Error::new(ENOENT)))?; - - if flags & O_DIRECTORY == 0 && flags & O_STAT == 0 { - return Err(Error::new(EISDIR)); - } - - let port_state = self - .port_states - .get_mut(&port_num) - .ok_or(Error::new(ENOENT))?; - - /*if self.dev_ctx.contexts[port_state.slot as usize].endpoints.get(endpoint_num as usize).ok_or(Error::new(ENOENT))?.a.read() & 0b111 != 1 { - return Err(Error::new(ENXIO)); // TODO: Find a proper error code for "endpoint not initialized". - }*/ - if !port_state.endpoint_states.contains_key(&endpoint_num) { - return Err(Error::new(ENOENT)); - } - let contents = "ctl\ndata\n".as_bytes().to_owned(); - - Handle::Endpoint(port_num, endpoint_num, EndpointHandleTy::Root(0, contents)) - } - &[port, "endpoints", endpoint_num_str, sub] if port.starts_with("port") => { - let port_num = port[4..].parse::().or(Err(Error::new(ENOENT)))?; - let endpoint_num = endpoint_num_str.parse::().or(Err(Error::new(ENOENT)))?; + Ok(Handle::Endpoint( + port_num, + endpoint_num, + EndpointHandleTy::Root(0, contents), + )) + } + /// implements open() for /port/endpoints//data and /port/endpoints//ctl + /// + /// # Arguments + /// - 'port_num: [usize]' - The port number specified in the scheme path + /// - 'endpoint_num: [u8]' - The endpoint number to access + /// - 'handle_type: [String]' - The type of the handle + /// - 'flags: [usize]' - The flags parameter passed to open() + /// + /// # Returns + /// This function returns a [Result] containing either: + /// + /// - [Handle::Port] - The handle was opened successfully + /// - [EISDIR] - open() was called on this scheme endpoint, but no directory-specific flags were passed to open + /// - [ENOENT] - The scheme is valid, but there is no port associated with the given port_num, or no endpoint with the given endpoint_num + fn open_handle_single_endpoint( + &self, + port_num: usize, + endpoint_num: u8, + handle_type: String, + flags: usize, + ) -> Result { + match handle_type.as_str() { + "root" => self.open_handle_endpoint_root(port_num, endpoint_num, flags), + "ctl" | "data" => { if flags & O_DIRECTORY != 0 && flags & O_STAT == 0 { return Err(Error::new(EISDIR)); } @@ -1438,36 +1918,99 @@ impl Scheme for Xhci { return Err(Error::new(ENOENT)); } - let st = match sub { + let st = match handle_type.as_str() { "ctl" => EndpointHandleTy::Ctl, "data" => EndpointHandleTy::Data, _ => return Err(Error::new(ENOENT)), }; - Handle::Endpoint(port_num, endpoint_num, st) + Ok(Handle::Endpoint(port_num, endpoint_num, st)) } - &[port] if port.starts_with("port") => { - if flags & O_DIRECTORY != 0 || flags & O_STAT != 0 { - let port_num = port[4..].parse::().or(Err(Error::new(ENOENT)))?; - let mut contents = Vec::new(); + _ => panic!( + "Scheme parser returned an invalid string '{}' for the endpoint handle type", + handle_type + ), + } + } - write!(contents, "descriptors\nendpoints\n").unwrap(); + /// implements open() for /port/configure + /// + /// # Arguments + /// - 'port_num: [usize]' - The port number specified in the scheme path + /// - 'endpoint_num: [u8]' - The endpoint number to access + /// - 'flags: [usize]' - The flags parameter passed to open() + /// + /// # Returns + /// This function returns a [Result] containing either: + /// + /// - [Handle::Port] - The handle was opened successfully + /// - [EISDIR] - open() was called on this scheme endpoint, but no directory-specific flags were passed to open + /// - [ENOENT] - The scheme is valid, but there is no port associated with the given port_num, or no endpoint with the given endpoint_num + fn open_handle_configure_endpoints(&self, port_num: usize, flags: usize) -> Result { + if flags & O_DIRECTORY != 0 && flags & O_STAT == 0 { + return Err(Error::new(ENOTDIR)); + } - if self.slot_state( - self.port_states - .get(&port_num) - .ok_or(Error::new(ENOENT))? - .slot as usize, - ) != SlotState::Configured as u8 - { - write!(contents, "configure\n").unwrap(); - } + if flags & O_RDWR != O_WRONLY && flags & O_STAT == 0 { + return Err(Error::new(EACCES)); + } - Handle::Port(port_num, 0, contents) - } else { - return Err(Error::new(EISDIR)); - } + Ok(Handle::ConfigureEndpoints(port_num)) + } + + /// implements open() for /port/request + /// + /// # Arguments + /// - 'port_num: [usize]' - The port number specified in the scheme path + /// - 'flags: [usize]' - The flags parameter passed to open() + /// + /// # Returns + /// This function returns a [Result] containing either: + /// + /// - [Handle::Port] - The handle was opened successfully + /// - [ENOTDIR] - open() was called on this scheme endpoint, but directory-specific flags were passed to open + fn open_handle_port_request(&self, port_num: usize, flags: usize) -> Result { + if flags & O_DIRECTORY != 0 && flags & O_STAT == 0 { + return Err(Error::new(ENOTDIR)); + } + + Ok(Handle::PortReq(port_num, PortReqState::Init)) + } +} + +impl Scheme for Xhci { + fn open(&self, path_str: &str, flags: usize, uid: u32, _gid: u32) -> Result { + if uid != 0 { + return Err(Error::new(EACCES)); + } + + //Parse the scheme, determine if it's in the valid format, return an error if not. + //This doesn't guarantee that the parameters themselves are valid (i.e. bounded correctly) + //only that the scheme itself was parseable. + let scheme_parameters = SchemeParameters::from_scheme(path_str)?; + + //Once we have our scheme parsed into parameters, we can match on those parameters to + //find the correct routine to open a handle + let handle = match scheme_parameters { + SchemeParameters::TopLevel => self.open_handle_top_level(flags)?, + SchemeParameters::Port(port_number) => self.open_handle_port(port_number, flags)?, + SchemeParameters::PortDesc(port_number) => { + self.open_handle_port_descriptors(port_number, flags)? + } + SchemeParameters::PortState(port_number) => { + self.open_handle_port_state(port_number, flags)? + } + SchemeParameters::PortReq(port_number) => { + self.open_handle_port_request(port_number, flags)? + } + SchemeParameters::Endpoints(port_number) => { + self.open_handle_port_endpoints(port_number, flags)? + } + SchemeParameters::Endpoint(port_number, endpoint_number, handle_type) => { + self.open_handle_single_endpoint(port_number, endpoint_number, handle_type, flags)? + } + SchemeParameters::ConfigureEndpoints(port_number) => { + self.open_handle_configure_endpoints(port_number, flags)? } - _ => return Err(Error::new(ENOENT)), }; let fd = self.next_handle.fetch_add(1, atomic::Ordering::Relaxed); @@ -1482,37 +2025,22 @@ impl Scheme for Xhci { fn fstat(&self, id: usize, stat: &mut Stat) -> Result { let mut guard = self.handles.get(&id).ok_or(Error::new(EBADF))?; - match &*guard { - &Handle::TopLevel(_, ref buf) - | &Handle::Port(_, _, ref buf) - | &Handle::Endpoints(_, _, ref buf) => { - stat.st_mode = MODE_DIR; - stat.st_size = buf.len() as u64; - } - &Handle::PortDesc(_, _, ref buf) => { - stat.st_mode = MODE_FILE; - stat.st_size = buf.len() as u64; - } - &Handle::PortReq(_, PortReqState::WaitingForDeviceBytes(ref buf, _)) - | &Handle::PortReq(_, PortReqState::WaitingForHostBytes(ref buf, _)) => { - stat.st_mode = MODE_CHR; - stat.st_size = buf.len() as u64; - } - &Handle::PortReq(_, PortReqState::Tmp) - | &Handle::PortReq(_, PortReqState::TmpSetup(_)) => unreachable!(), + stat.st_mode = match (&*guard).get_handle_type() { + HandleType::Directory => MODE_DIR, + HandleType::File => MODE_FILE, + HandleType::Character => MODE_CHR, + }; - &Handle::PortState(_, _) | &Handle::PortReq(_, _) => stat.st_mode = MODE_CHR, - &Handle::Endpoint(_, _, ref st) => match st { - &EndpointHandleTy::Ctl | &EndpointHandleTy::Data => stat.st_mode = MODE_CHR, - &EndpointHandleTy::Root(_, ref buf) => { - stat.st_mode = MODE_DIR; - stat.st_size = buf.len() as u64; - } - }, - &Handle::ConfigureEndpoints(_) => { - stat.st_mode = MODE_CHR | 0o200; // write only - } + stat.st_size = match (&*guard).get_buf_len() { + None => stat.st_size, + Some(size) => size as u64, + }; + + //If we have a handle to the configure scheme, we need to mark it as write only. + if let &Handle::ConfigureEndpoints(_) = (&*guard) { + stat.st_mode = stat.st_mode | 0o200; } + Ok(0) } @@ -1520,33 +2048,16 @@ impl Scheme for Xhci { let mut cursor = io::Cursor::new(buffer); let guard = self.handles.get(&fd).ok_or(Error::new(EBADF))?; - match &*guard { - &Handle::TopLevel(_, _) => write!(cursor, "/").unwrap(), - &Handle::Port(port_num, _, _) => write!(cursor, "/port{}/", port_num).unwrap(), - &Handle::PortDesc(port_num, _, _) => { - write!(cursor, "/port{}/descriptors", port_num).unwrap() - } - &Handle::PortState(port_num, _) => write!(cursor, "/port{}/state", port_num).unwrap(), - &Handle::PortReq(port_num, _) => write!(cursor, "/port{}/request", port_num).unwrap(), - &Handle::Endpoints(port_num, _, _) => { - write!(cursor, "/port{}/endpoints/", port_num).unwrap() - } - &Handle::Endpoint(port_num, endp_num, ref st) => write!( - cursor, - "/port{}/endpoints/{}/{}", - port_num, - endp_num, - match st { - &EndpointHandleTy::Root(_, _) => "", - &EndpointHandleTy::Ctl => "ctl", - &EndpointHandleTy::Data => "data", - } + let scheme = (&*guard).to_scheme(); + + write!(cursor, "{}", scheme.as_str()).expect( + format!( + "Failed to convert the file descriptor with value {} to the associated file path", + fd ) - .unwrap(), - &Handle::ConfigureEndpoints(port_num) => { - write!(cursor, "/port{}/configure", port_num).unwrap() - } - } + .as_str(), + ); + let src_len = usize::try_from(cursor.seek(io::SeekFrom::End(0)).unwrap()).unwrap(); Ok(src_len) } @@ -1554,7 +2065,13 @@ impl Scheme for Xhci { fn seek(&self, fd: usize, pos: isize, whence: usize) -> Result { let mut guard = self.handles.get_mut(&fd).ok_or(Error::new(EBADF))?; - trace!("SEEK fd={}, handle={:?}, pos {}, whence {}", fd, guard, pos, whence); + trace!( + "SEEK fd={}, handle={:?}, pos {}, whence {}", + fd, + guard, + pos, + whence + ); match &mut *guard { // Directories, or fixed files @@ -1584,14 +2101,20 @@ impl Scheme for Xhci { } // Write-once configure or transfer Handle::Endpoint(_, _, _) | Handle::ConfigureEndpoints(_) | Handle::PortReq(_, _) => { - return Err(Error::new(ESPIPE)) + Err(Error::new(ESPIPE)) } } } fn read(&self, fd: usize, buf: &mut [u8]) -> Result { let mut guard = self.handles.get_mut(&fd).ok_or(Error::new(EBADF))?; - trace!("READ fd={}, handle={:?}, buf=(addr {:p}, length {})", fd, guard, buf.as_ptr(), buf.len()); + trace!( + "READ fd={}, handle={:?}, buf=(addr {:p}, length {})", + fd, + guard, + buf.as_ptr(), + buf.len() + ); match &mut *guard { Handle::TopLevel(ref mut offset, ref src_buf) | Handle::Port(_, ref mut offset, ref src_buf) @@ -1606,12 +2129,12 @@ impl Scheme for Xhci { Ok(bytes_to_read) } - Handle::ConfigureEndpoints(_) => return Err(Error::new(EBADF)), + Handle::ConfigureEndpoints(_) => Err(Error::new(EBADF)), &mut Handle::Endpoint(port_num, endp_num, ref mut st) => match st { EndpointHandleTy::Ctl => self.on_read_endp_ctl(port_num, endp_num, buf), EndpointHandleTy::Data => block_on(self.on_read_endp_data(port_num, endp_num, buf)), - EndpointHandleTy::Root(_, _) => return Err(Error::new(EBADF)), + EndpointHandleTy::Root(_, _) => Err(Error::new(EBADF)), }, &mut Handle::PortState(port_num, ref mut offset) => { let ps = self.port_states.get(&port_num).ok_or(Error::new(EBADF))?; @@ -1646,7 +2169,13 @@ impl Scheme for Xhci { } fn write(&self, fd: usize, buf: &[u8]) -> Result { let mut guard = self.handles.get_mut(&fd).ok_or(Error::new(EBADF))?; - trace!("WRITE fd={}, handle={:?}, buf=(addr {:p}, length {})", fd, guard, buf.as_ptr(), buf.len()); + trace!( + "WRITE fd={}, handle={:?}, buf=(addr {:p}, length {})", + fd, + guard, + buf.as_ptr(), + buf.len() + ); match &mut *guard { &mut Handle::ConfigureEndpoints(port_num) => { @@ -1655,8 +2184,10 @@ impl Scheme for Xhci { } &mut Handle::Endpoint(port_num, endp_num, ref ep_file_ty) => match ep_file_ty { EndpointHandleTy::Ctl => block_on(self.on_write_endp_ctl(port_num, endp_num, buf)), - EndpointHandleTy::Data => block_on(self.on_write_endp_data(port_num, endp_num, buf)), - EndpointHandleTy::Root(_, _) => return Err(Error::new(EBADF)), + EndpointHandleTy::Data => { + block_on(self.on_write_endp_data(port_num, endp_num, buf)) + } + EndpointHandleTy::Root(_, _) => Err(Error::new(EBADF)), }, &mut Handle::PortReq(port_num, ref mut st) => { let state = std::mem::replace(st, PortReqState::Tmp); @@ -1665,7 +2196,7 @@ impl Scheme for Xhci { } // TODO: Introduce PortReqState::Waiting, which this write call changes to // PortReqState::ReadyToWrite when all bytes are written. - _ => return Err(Error::new(EBADF)), + _ => Err(Error::new(EBADF)), } } fn close(&self, fd: usize) -> Result { @@ -1677,16 +2208,14 @@ impl Scheme for Xhci { } impl Xhci { pub fn get_endp_status(&self, port_num: usize, endp_num: u8) -> Result { - let port_state = self - .port_states - .get(&port_num) - .ok_or(Error::new(EBADFD))?; + let port_state = self.port_states.get(&port_num).ok_or(Error::new(EBADFD))?; let slot = port_state.slot; let endp_desc = port_state .dev_desc - .as_ref().unwrap() + .as_ref() + .unwrap() .config_descs .get(0) .ok_or(Error::new(EIO))? @@ -1748,7 +2277,8 @@ impl Xhci { index: 0, // TODO: interface num length: 0, }, - ).await?; + ) + .await?; } Ok(()) } @@ -1778,7 +2308,8 @@ impl Xhci { let endp_desc = port_state .dev_desc - .as_ref().unwrap() + .as_ref() + .unwrap() .config_descs .get(0) .ok_or(Error::new(EIO))? @@ -1792,18 +2323,15 @@ impl Xhci { let doorbell = if endp_num != 0 { let stream_id = 1u16; - Self::endp_doorbell( - endp_num, - endp_desc, - if has_streams { stream_id } else { 0 }, - ) + Self::endp_doorbell(endp_num, endp_desc, if has_streams { stream_id } else { 0 }) } else { Self::def_control_endp_doorbell() }; self.dbs.lock().unwrap()[slot as usize].write(doorbell); - self.set_tr_deque_ptr(port_num, endp_num, deque_ptr_and_cycle).await?; + self.set_tr_deque_ptr(port_num, endp_num, deque_ptr_and_cycle) + .await?; Ok(()) } @@ -1813,7 +2341,8 @@ impl Xhci { .get(&port_num) .ok_or(Error::new(EIO))? .dev_desc - .as_ref().unwrap() + .as_ref() + .unwrap() .config_descs .first() .ok_or(Error::new(EIO))? @@ -1838,19 +2367,23 @@ impl Xhci { let port_state = self.port_states.get(&port_num).ok_or(Error::new(EBADFD))?; let slot = port_state.slot; - let endp_desc = port_state.get_endp_desc(endp_idx).ok_or(Error::new(EBADFD))?; + let endp_desc = port_state + .get_endp_desc(endp_idx) + .ok_or(Error::new(EBADFD))?; let endp_num_xhc = Self::endp_num_to_dci(endp_num, endp_desc); - let (event_trb, command_trb) = self.execute_command(|trb, cycle| { - trb.set_tr_deque_ptr( - deque_ptr_and_cycle, - cycle, - StreamContextType::PrimaryRing, - 1, - endp_num_xhc, - slot, - ) - }).await; + let (event_trb, command_trb) = self + .execute_command(|trb, cycle| { + trb.set_tr_deque_ptr( + deque_ptr_and_cycle, + cycle, + StreamContextType::PrimaryRing, + 1, + endp_num_xhc, + slot, + ) + }) + .await; self.event_handler_finished(); handle_event_trb("SET_TR_DEQUEUE_PTR", &event_trb, &command_trb) @@ -1881,7 +2414,10 @@ impl Xhci { } }, XhciEndpCtlReq::Reset { no_clear_feature } => match ep_if_state { - EndpIfState::Init => self.on_req_reset_device(port_num, endp_num, !no_clear_feature).await?, + EndpIfState::Init => { + self.on_req_reset_device(port_num, endp_num, !no_clear_feature) + .await? + } other => { return Err(Error::new(EBADF)); } @@ -1891,8 +2427,9 @@ impl Xhci { if direction == XhciEndpCtlDirection::NoData { // Yield the result directly because no bytes have to be sent or received // beforehand. - let (completion_code, bytes_transferred, _) = - self.transfer(port_num, endp_num - 1, None, PortReqDirection::DeviceToHost).await?; + let (completion_code, bytes_transferred, _) = self + .transfer(port_num, endp_num - 1, None, PortReqDirection::DeviceToHost) + .await?; if bytes_transferred > 0 { return Err(Error::new(EIO)); } @@ -1947,8 +2484,14 @@ impl Xhci { endp_num: u8, buf: &[u8], ) -> Result { - let mut port_state = self.port_states.get_mut(&port_num).ok_or(Error::new(EBADFD))?; - let mut endpoint_state = port_state.endpoint_states.get_mut(&endp_num).ok_or(Error::new(EBADFD))?; + let mut port_state = self + .port_states + .get_mut(&port_num) + .ok_or(Error::new(EBADFD))?; + let mut endpoint_state = port_state + .endpoint_states + .get_mut(&endp_num) + .ok_or(Error::new(EBADFD))?; let ep_if_state = &mut endpoint_state.driver_if_state; @@ -1970,8 +2513,14 @@ impl Xhci { // invoking further data transfer calls if any single transfer returns fewer bytes // than requested. - let mut port_state = self.port_states.get_mut(&port_num).ok_or(Error::new(EBADFD))?; - let mut endpoint_state = port_state.endpoint_states.get_mut(&endp_num).ok_or(Error::new(EBADFD))?; + let mut port_state = self + .port_states + .get_mut(&port_num) + .ok_or(Error::new(EBADFD))?; + let mut endpoint_state = port_state + .endpoint_states + .get_mut(&endp_num) + .ok_or(Error::new(EBADFD))?; let ep_if_state = &mut endpoint_state.driver_if_state; if let &mut EndpIfState::WaitingForDataPipe { @@ -1980,7 +2529,9 @@ impl Xhci { ref mut bytes_transferred, } = ep_if_state { - if *bytes_transferred + some_bytes_transferred == bytes_to_transfer || completion_code != TrbCompletionCode::Success as u8 { + if *bytes_transferred + some_bytes_transferred == bytes_to_transfer + || completion_code != TrbCompletionCode::Success as u8 + { *ep_if_state = EndpIfState::WaitingForTransferResult(result); } else { *bytes_transferred += some_bytes_transferred; @@ -1993,12 +2544,7 @@ impl Xhci { _ => return Err(Error::new(EBADF)), } } - pub fn on_read_endp_ctl( - &self, - port_num: usize, - endp_num: u8, - buf: &mut [u8], - ) -> Result { + pub fn on_read_endp_ctl(&self, port_num: usize, endp_num: u8, buf: &mut [u8]) -> Result { let port_state = &mut self .port_states .get_mut(&port_num) @@ -2082,7 +2628,9 @@ impl Xhci { ref mut bytes_transferred, } = ep_if_state { - if *bytes_transferred + some_bytes_transferred == bytes_to_transfer || completion_code != TrbCompletionCode::Success as u8 { + if *bytes_transferred + some_bytes_transferred == bytes_to_transfer + || completion_code != TrbCompletionCode::Success as u8 + { *ep_if_state = EndpIfState::WaitingForTransferResult(result); } else { *bytes_transferred += some_bytes_transferred; @@ -2103,26 +2651,39 @@ impl Xhci { pub fn event_handler_finished(&self) { trace!("Event handler finished"); // write 1 to EHB to clear it - self.run.lock().unwrap().ints[0].erdp_low.writef(1 << 3, true); + self.run.lock().unwrap().ints[0] + .erdp_low + .writef(1 << 3, true); } } pub fn handle_event_trb(name: &str, event_trb: &Trb, command_trb: &Trb) -> Result<()> { if event_trb.completion_code() == TrbCompletionCode::Success as u8 { Ok(()) } else { - error!("{} command (TRB {:?}) failed with event trb {:?}", name, command_trb, event_trb); + error!( + "{} command (TRB {:?}) failed with event trb {:?}", + name, command_trb, event_trb + ); Err(Error::new(EIO)) } } pub fn handle_transfer_event_trb(name: &str, event_trb: &Trb, transfer_trb: &Trb) -> Result<()> { - if event_trb.completion_code() == TrbCompletionCode::Success as u8 || event_trb.completion_code() == TrbCompletionCode::ShortPacket as u8 { + if event_trb.completion_code() == TrbCompletionCode::Success as u8 + || event_trb.completion_code() == TrbCompletionCode::ShortPacket as u8 + { Ok(()) } else { - error!("{} transfer {:?} failed with event {:?}", name, transfer_trb, event_trb); + error!( + "{} transfer {:?} failed with event {:?}", + name, transfer_trb, event_trb + ); Err(Error::new(EIO)) } } +use lazy_static::lazy_static; use std::ops::{Add, Div, Rem}; +use std::path::Path; + pub fn div_round_up(a: T, b: T) -> T where T: Add + Div + Rem + PartialEq + From + Copy, diff --git a/xhcid/src/xhci/trb.rs b/xhcid/src/xhci/trb.rs index 1072981503..37fee43a95 100644 --- a/xhcid/src/xhci/trb.rs +++ b/xhcid/src/xhci/trb.rs @@ -157,8 +157,7 @@ impl Trb { } pub fn read_data(&self) -> u64 { - (self.data_low.read() as u64) | - ((self.data_high.read() as u64) << 32) + (self.data_low.read() as u64) | ((self.data_high.read() as u64) << 32) } pub fn completion_code(&self) -> u8 { @@ -168,7 +167,9 @@ impl Trb { self.status.read() & TRB_STATUS_COMPLETION_PARAM_MASK } fn has_completion_trb_pointer(&self) -> bool { - if self.completion_code() == TrbCompletionCode::RingUnderrun as u8 || self.completion_code() == TrbCompletionCode::RingOverrun as u8 { + if self.completion_code() == TrbCompletionCode::RingUnderrun as u8 + || self.completion_code() == TrbCompletionCode::RingOverrun as u8 + { false } else if self.completion_code() == TrbCompletionCode::VfEventRingFull as u8 { false @@ -245,9 +246,7 @@ impl Trb { self.set( 0, 0, - (u32::from(slot) << 24) - | ((TrbType::DisableSlot as u32) << 10) - | u32::from(cycle) + (u32::from(slot) << 24) | ((TrbType::DisableSlot as u32) << 10) | u32::from(cycle), ); } @@ -382,7 +381,15 @@ impl Trb { ); } - pub fn status(&mut self, interrupter: u16, input: bool, ioc: bool, ch: bool, ent: bool, cycle: bool) { + pub fn status( + &mut self, + interrupter: u16, + input: bool, + ioc: bool, + ch: bool, + ent: bool, + cycle: bool, + ) { self.set( 0, u32::from(interrupter) << 22,