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:
Generated
+39
@@ -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",
|
||||
|
||||
@@ -31,3 +31,4 @@ toml = "0.5"
|
||||
common = { path = "../common" }
|
||||
pcid = { path = "../pcid" }
|
||||
libredox = "0.1.3"
|
||||
regex = "1.10.6"
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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,5 +1,6 @@
|
||||
use plain::Plain;
|
||||
|
||||
///
|
||||
#[repr(packed)]
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub struct InterfaceDescriptor {
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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()
|
||||
|
||||
@@ -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
@@ -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
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
File diff suppressed because it is too large
Load Diff
+14
-7
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user