Merge branch 'document_xhic_daemon' into 'master'

Added some documentation to the XHCI daemon, clarified the scheme interface with a refactor

See merge request redox-os/drivers!198
This commit is contained in:
Jeremy Soller
2024-09-10 00:43:42 +00:00
19 changed files with 1896 additions and 573 deletions
Generated
+39
View File
@@ -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",
+1
View File
@@ -31,3 +31,4 @@ toml = "0.5"
common = { path = "../common" }
pcid = { path = "../pcid" }
libredox = "0.1.3"
regex = "1.10.6"
+14 -6
View File
@@ -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<File, XhciClientHandleError> {
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<File, XhciClientHandleError> {
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<XhciEndpHandle, XhciClientHandleError> {
@@ -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),
+25
View File
@@ -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;
+79 -26
View File
@@ -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<Xhci>, packet: Packet) -> Packet {
}
#[cfg(target_arch = "x86_64")]
fn get_int_method(pcid_handle: &mut PciFunctionHandle, bar0_address: usize) -> (Option<File>, InterruptMethod) {
fn get_int_method(
pcid_handle: &mut PciFunctionHandle,
bar0_address: usize,
) -> (Option<File>, 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<File>, InterruptMethod) {
fn get_int_method(
pcid_handle: &mut PciFunctionHandle,
address: usize,
) -> (Option<File>, 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");
}
}
+124
View File
@@ -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,
}
+12
View File
@@ -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)]
+2 -2
View File
@@ -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],
}
}
}
+1
View File
@@ -1,5 +1,6 @@
use plain::Plain;
///
#[repr(packed)]
#[derive(Clone, Copy, Debug, Default)]
pub struct InterfaceDescriptor {
+26
View File
@@ -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,
}
+145 -6
View File
@@ -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<u8>,
/// Reserved byte
///
/// Rsvd in XHC Table 5-9
_rsvd: Mmio<u8>,
/// 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<u16>,
/// 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<u32>,
/// 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<u32>,
/// 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<u32>,
/// 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<u32>,
/// 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<u32>,
/// 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<u32>,
/// 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<u32>,
//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)
}
}
+14 -12
View File
@@ -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<Self> {
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::<Result<Vec<_>, _>>()?;
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::<Result<Vec<_>, _>>()?;
Ok(Self {
entries,
pages,
})
Ok(Self { entries, pages })
}
pub fn register(&self) -> usize {
self.entries.physical()
+7 -3
View File
@@ -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)
+146 -49
View File
@@ -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<Xhci>,
irq_file: Option<File>,
receiver: Receiver<NewPendingTrb>,
states: Vec<State>,
// 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<State>, doorbell_opt: Option<EventDoorbell> },
Pending {
state: FutureState,
sender: Sender<State>,
doorbell_opt: Option<EventDoorbell>,
},
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<Trb> {
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, F: FnOnce(&Ring) -> T>(&self, id: RingId, function: F) -> Option<T> {
use super::RingOrStreams;
@@ -442,7 +521,11 @@ impl Xhci {
Some(function(ring_ref))
}
pub fn with_ring_mut<T, F: FnOnce(&mut Ring) -> T>(&self, id: RingId, function: F) -> Option<T> {
pub fn with_ring_mut<T, F: FnOnce(&mut Ring) -> T>(
&self,
id: RingId,
function: F,
) -> Option<T> {
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<Output = NextEventTrb> + 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<Output = NextEventTrb> + 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<Output = NextEventTrb> + 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<Output = NextEventTrb> + 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<Output = NextEventTrb> + Send + Sync + 'static {
pub fn next_misc_event_trb(
&self,
trb_type: TrbType,
) -> impl Future<Output = NextEventTrb> + 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,
}
}
}
+202 -59
View File
@@ -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<T>(&self, port: usize, slot: u8, kind: usb::DescriptorKind, index: u8, desc: &mut Dma<T>) -> Result<()> {
async fn get_desc_raw<T>(
&self,
port: usize,
slot: u8,
kind: usb::DescriptorKind,
index: u8,
desc: &mut Dma<T>,
) -> Result<()> {
let len = mem::size_of::<T>();
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<usb::DeviceDescriptor8Byte> {
async fn fetch_dev_desc_8_byte(
&self,
port: usize,
slot: u8,
) -> Result<usb::DeviceDescriptor8Byte> {
let mut desc = unsafe { self.alloc_dma_zeroed::<usb::DeviceDescriptor8Byte>()? };
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<usb::DeviceDescriptor> {
let mut desc = unsafe { self.alloc_dma_zeroed::<usb::DeviceDescriptor>()? };
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<String> {
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<Mutex<&'static mut [Doorbell]>>,
/// 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<Ring>,
primary_event_ring: Mutex<EventRing>,
@@ -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<Xhci> {
pub fn new(
scheme_name: String,
address: usize,
interrupt_method: InterruptMethod,
pcid_handle: PciFunctionHandle,
) -> Result<Xhci> {
//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<u8> {
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::<Vec<_>>());
debug!(
"XHCI capabilities: {:?}",
self.capabilities_iter().collect::<Vec<_>>()
);
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::<InputContext>()? };
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<InputContext>,
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<MutexGuard<'_, MappedMsixRegs>> {
@@ -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()
}
}
+62
View File
@@ -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<u32>,
/// 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<u32>,
/// The PAGESIZE Register (PAGESIZE)
///
///
pub page_size: Mmio<u32>,
/// Reserved bits (RsvdZ)
_rsvd: [Mmio<u32>; 2],
/// The Device Notification Control Register (DNCTRL)
///
///
pub dn_ctrl: Mmio<u32>,
/// The Command Ring Control Register Lower 32 bits (CRCR)
///
///
pub crcr_low: Mmio<u32>,
/// The Command Ring Control Register Upper 32 bits (CRCR)
///
///
pub crcr_high: Mmio<u32>,
/// Reserved bits (RsvdZ)
_rsvd2: [Mmio<u32>; 4],
/// Device Context Base Address Array Pointer Lower 32 bits (DCBAAP)
///
///
pub dcbaap_low: Mmio<u32>,
/// Device Context Base Address Array Pointer Upper 32 bits (DCBAAP)
///
///
pub dcbaap_high: Mmio<u32>,
/// The Configure Register (CONFIG)
///
///
pub config: Mmio<u32>,
// 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 {
+26 -7
View File
@@ -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<Item = &Trb> + '_ {
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<usize> {
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::<Trb>(), 0, "unaligned TRB physical address");
assert_eq!(
offset % mem::size_of::<Trb>(),
0,
"unaligned TRB physical address"
);
let index = offset / mem::size_of::<Trb>();
@@ -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::<Trb>() {
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::<Trb>()
{
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;
+957 -396
View File
File diff suppressed because it is too large Load Diff
+14 -7
View File
@@ -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,