milestone: desktop path Phases 1-5

Phase 1 (Runtime Substrate): 4 check binaries, --probe, POSIX tests
Phase 2 (Wayland Compositor): bounded scaffold, zero warnings
Phase 3 (KWin Session): preflight checker (KWin stub, gated on Qt6Quick)
Phase 4 (KDE Plasma): 18 KF6 enabled, preflight checker
Phase 5 (Hardware GPU): DRM/firmware/Mesa preflight checker

Build: zero warnings, all scripts syntax-clean. Oracle-verified.
This commit is contained in:
2026-04-29 09:54:06 +01:00
parent b23714f542
commit 8acc73d774
508 changed files with 76526 additions and 396 deletions
@@ -0,0 +1 @@
/target
@@ -0,0 +1,15 @@
[package]
name = "usbctl"
version = "0.1.0"
authors = ["4lDO2 <4lDO2@protonmail.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
clap.workspace = true
xhcid = { path = "../xhcid" }
common = { path = "../../common" }
[lints]
workspace = true
@@ -0,0 +1,54 @@
use clap::{Arg, Command};
use xhcid_interface::{PortId, XhciClientHandle};
fn main() {
common::init();
let matches = Command::new("usbctl")
.arg(
Arg::new("SCHEME")
.num_args(1)
.required(true)
.long("scheme")
.short('s'),
)
.subcommand(
Command::new("port")
.arg(Arg::new("PORT").num_args(1).required(true))
.subcommand(Command::new("status"))
.subcommand(
Command::new("endpoint")
.arg(Arg::new("ENDPOINT_NUM").num_args(1).required(true))
.subcommand(Command::new("status")),
),
)
.get_matches();
let scheme = matches.get_one::<String>("SCHEME").expect("no scheme");
if let Some(port_scmd_matches) = matches.subcommand_matches("port") {
let port = port_scmd_matches
.get_one::<String>("PORT")
.expect("invalid utf-8 for PORT argument")
.parse::<PortId>()
.expect("expected PORT ID");
let handle = XhciClientHandle::new(scheme.to_owned(), port)
.expect("Failed to open XhciClientHandle");
if let Some(_status_scmd_matches) = port_scmd_matches.subcommand_matches("status") {
let state = handle.port_state().expect("Failed to get port state");
println!("{}", state.as_str());
} else if let Some(endp_scmd_matches) = port_scmd_matches.subcommand_matches("endpoint") {
let endp_num = endp_scmd_matches
.get_one::<String>("ENDPOINT_NUM")
.expect("no valid ENDPOINT_NUM")
.parse::<u8>()
.expect("expected ENDPOINT_NUM to be an 8-bit integer");
let mut endp_handle = handle
.open_endpoint(endp_num)
.expect("Failed to open endpoint");
let state = endp_handle.status().expect("Failed to get endpoint state");
println!("{}", state.as_str());
}
}
}
@@ -0,0 +1 @@
/target
@@ -0,0 +1,18 @@
[package]
name = "usbhubd"
description = "USB Hub driver"
version = "0.1.0"
edition = "2018"
license = "MIT"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
log.workspace = true
redox_syscall.workspace = true
xhcid = { path = "../xhcid" }
common = { path = "../../common" }
[lints]
workspace = true
@@ -0,0 +1,249 @@
use std::{env, thread, time};
use xhcid_interface::{
plain, usb, ConfigureEndpointsReq, DevDesc, DeviceReqData, PortId, PortReqRecipient, PortReqTy,
XhciClientHandle,
};
fn main() {
common::init();
let mut args = env::args().skip(1);
const USAGE: &'static str = "usbhubd <scheme> <port> <interface>";
let scheme = args.next().expect(USAGE);
let port_id = args
.next()
.expect(USAGE)
.parse::<PortId>()
.expect("Expected port ID");
let interface_num = args
.next()
.expect(USAGE)
.parse::<u8>()
.expect("Expected integer as input of interface");
log::info!(
"USB HUB driver spawned with scheme `{}`, port {}, interface {}",
scheme,
port_id,
interface_num
);
let name = format!("{}_{}_{}_hub", scheme, port_id, interface_num);
common::setup_logging(
"usb",
"device",
&name,
common::output_level(),
common::file_level(),
);
let handle =
XhciClientHandle::new(scheme.clone(), port_id).expect("Failed to open XhciClientHandle");
let desc: DevDesc = handle
.get_standard_descs()
.expect("Failed to get standard descriptors");
let (conf_desc, if_desc) = desc
.config_descs
.iter()
.find_map(|conf_desc| {
let if_desc = conf_desc.interface_descs.iter().find_map(|if_desc| {
if if_desc.number == interface_num {
Some(if_desc.clone())
} else {
None
}
})?;
Some((conf_desc.clone(), if_desc))
})
.expect("Failed to find suitable configuration");
// Read hub descriptor
let (ports, usb_3) = if desc.major_version() >= 3 {
// USB 3.0 hubs
let mut hub_desc = usb::HubDescriptorV3::default();
handle
.device_request(
PortReqTy::Class,
PortReqRecipient::Device,
usb::SetupReq::GetDescriptor as u8,
u16::from(usb::HubDescriptorV3::DESCRIPTOR_KIND) << 8,
0,
DeviceReqData::In(unsafe { plain::as_mut_bytes(&mut hub_desc) }),
)
.expect("Failed to read hub descriptor");
(hub_desc.ports, true)
} else {
// USB 2.0 and earlier hubs
let mut hub_desc = usb::HubDescriptorV2::default();
handle
.device_request(
PortReqTy::Class,
PortReqRecipient::Device,
usb::SetupReq::GetDescriptor as u8,
u16::from(usb::HubDescriptorV2::DESCRIPTOR_KIND) << 8,
0,
DeviceReqData::In(unsafe { plain::as_mut_bytes(&mut hub_desc) }),
)
.expect("Failed to read hub descriptor");
(hub_desc.ports, false)
};
// Configure as hub device
handle
.configure_endpoints(&ConfigureEndpointsReq {
config_desc: conf_desc.configuration_value,
interface_desc: None, //TODO: stalls on USB 3 hub: Some(interface_num),
alternate_setting: None, //TODO: stalls on USB 3 hub: Some(if_desc.alternate_setting),
hub_ports: Some(ports),
})
.expect("Failed to configure endpoints after reading hub descriptor");
if usb_3 {
handle
.device_request(
PortReqTy::Class,
PortReqRecipient::Device,
0x0c, // SET_HUB_DEPTH
port_id.hub_depth().into(),
0,
DeviceReqData::NoData,
)
.expect("Failed to set hub depth");
}
// Initialize states
struct PortState {
port_id: PortId,
port_sts: usb::HubPortStatus,
handle: XhciClientHandle,
attached: bool,
}
impl PortState {
pub fn ensure_attached(&mut self, attached: bool) {
if attached == self.attached {
return;
}
if attached {
self.handle.attach().expect("Failed to attach");
} else {
self.handle.detach().expect("Failed to detach");
}
self.attached = attached;
}
}
let mut states = Vec::new();
for port in 1..=ports {
let child_port_id = port_id.child(port).expect("Cannot get child port ID");
states.push(PortState {
port_id: child_port_id,
port_sts: if usb_3 {
usb::HubPortStatus::V3(usb::HubPortStatusV3::default())
} else {
usb::HubPortStatus::V2(usb::HubPortStatusV2::default())
},
handle: XhciClientHandle::new(scheme.clone(), child_port_id)
.expect("Failed to open XhciClientHandle"),
attached: false,
});
}
//TODO: use change flags?
loop {
for port in 1..=ports {
let port_idx: usize = port.checked_sub(1).unwrap().into();
let state = states.get_mut(port_idx).unwrap();
let port_sts = if usb_3 {
let mut port_sts = usb::HubPortStatusV3::default();
handle
.device_request(
PortReqTy::Class,
PortReqRecipient::Other,
usb::SetupReq::GetStatus as u8,
0,
port as u16,
DeviceReqData::In(unsafe { plain::as_mut_bytes(&mut port_sts) }),
)
.expect("Failed to retrieve port status");
usb::HubPortStatus::V3(port_sts)
} else {
let mut port_sts = usb::HubPortStatusV2::default();
handle
.device_request(
PortReqTy::Class,
PortReqRecipient::Other,
usb::SetupReq::GetStatus as u8,
0,
port as u16,
DeviceReqData::In(unsafe { plain::as_mut_bytes(&mut port_sts) }),
)
.expect("Failed to retrieve port status");
usb::HubPortStatus::V2(port_sts)
};
if state.port_sts != port_sts {
state.port_sts = port_sts;
log::info!("port {} status {:X?}", port, port_sts);
}
// Ensure port is powered on
if !port_sts.is_powered() {
log::info!("power on port {port}");
handle
.device_request(
PortReqTy::Class,
PortReqRecipient::Other,
usb::SetupReq::SetFeature as u8,
usb::HubPortFeature::PortPower as u16,
port as u16,
DeviceReqData::NoData,
)
.expect("Failed to set port power");
state.ensure_attached(false);
continue;
}
// Ignore disconnected port
if !port_sts.is_connected() {
state.ensure_attached(false);
continue;
}
// Ignore port in reset
if port_sts.is_resetting() {
state.ensure_attached(false);
continue;
}
// Ensure port is enabled
if !port_sts.is_enabled() {
log::info!("reset port {port}");
handle
.device_request(
PortReqTy::Class,
PortReqRecipient::Other,
usb::SetupReq::SetFeature as u8,
usb::HubPortFeature::PortReset as u16,
port as u16,
DeviceReqData::NoData,
)
.expect("Failed to set port enable");
state.ensure_attached(false);
continue;
}
state.ensure_attached(true);
}
//TODO: use interrupts or poll faster?
thread::sleep(time::Duration::new(1, 0));
}
//TODO: read interrupt port for changes
}
@@ -0,0 +1 @@
/target
@@ -0,0 +1,41 @@
[package]
name = "xhcid"
description = "xHCI controller driver"
version = "0.1.0"
edition = "2018"
[[bin]]
name = "xhcid"
path = "src/main.rs"
[lib]
name = "xhcid_interface"
path = "src/lib.rs"
[dependencies]
bitflags.workspace = true
chashmap = "2.2.2"
crossbeam-channel = "0.4"
futures = "0.3"
plain.workspace = true
lazy_static = "1.4"
log.workspace = true
redox_event.workspace = true
redox-scheme.workspace = true
scheme-utils = { path = "../../../scheme-utils" }
redox_syscall.workspace = true
serde.workspace = true
serde_json.workspace = true
smallvec = { workspace = true, features = ["serde"] }
thiserror.workspace = true
toml.workspace = true
config = { path = "../../../config" }
common = { path = "../../common" }
daemon = { path = "../../../daemon" }
pcid = { path = "../../pcid" }
libredox.workspace = true
regex = "1.10.6"
[lints]
workspace = true
@@ -0,0 +1,7 @@
[[drivers]]
name = "XHCI"
class = 0x0C
subclass = 0x03
interface = 0x30
command = ["xhcid"]
use_channel = true
@@ -0,0 +1,18 @@
#TODO: causes XHCI errors
#[[drivers]]
#name = "SCSI over USB"
#class = 8 # Mass Storage class
#subclass = 6 # SCSI transparent command set
#command = ["usbscsid", "$SCHEME", "$PORT", "$IF_PROTO"]
[[drivers]]
name = "USB HUB"
class = 9 # HUB class
subclass = -1
command = ["usbhubd", "$SCHEME", "$PORT", "$IF_NUM"]
[[drivers]]
name = "USB HID"
class = 3 # HID class
subclass = -1
command = ["usbhidd", "$SCHEME", "$PORT", "$IF_NUM"]
@@ -0,0 +1,889 @@
pub extern crate serde;
pub extern crate smallvec;
use std::convert::TryFrom;
use std::fs::File;
use std::io::prelude::*;
use std::num::NonZeroU8;
use std::os::fd::{FromRawFd, RawFd};
use std::string::FromUtf8Error;
use std::{fmt, io, result, str};
use serde::{Deserialize, Serialize};
use smallvec::SmallVec;
use syscall::{Error, Result, EINVAL};
use thiserror::Error;
pub use crate::usb::{EndpointTy, ENDP_ATTR_TY_MASK};
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct ConfigureEndpointsReq {
/// Index into the configuration descriptors of the device descriptor.
pub config_desc: u8,
pub interface_desc: Option<u8>,
pub alternate_setting: Option<u8>,
pub hub_ports: Option<u8>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct DevDesc {
pub kind: u8,
pub usb: u16,
pub class: u8,
pub sub_class: u8,
pub protocol: u8,
pub packet_size: u8,
pub vendor: u16,
pub product: u16,
pub release: u16,
pub manufacturer_str: Option<String>,
pub product_str: Option<String>,
pub serial_str: Option<String>,
pub config_descs: SmallVec<[ConfDesc; 1]>,
}
impl DevDesc {
pub fn major_version(&self) -> u8 {
((self.usb & 0xFF00) >> 8) as u8
}
pub fn minor_version(&self) -> u8 {
self.usb as u8
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ConfDesc {
pub kind: u8,
pub configuration_value: u8,
pub configuration: Option<String>,
pub attributes: u8,
pub max_power: u8,
pub interface_descs: SmallVec<[IfDesc; 1]>,
}
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
pub struct EndpDesc {
pub kind: u8,
pub address: u8,
pub attributes: u8,
pub max_packet_size: u16,
pub interval: u8,
pub ssc: Option<SuperSpeedCmp>,
pub sspc: Option<SuperSpeedPlusIsochCmp>,
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum EndpDirection {
Out,
In,
Bidirectional,
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum EndpBinaryDirection {
Out,
In,
}
impl From<PortReqDirection> for EndpBinaryDirection {
fn from(d: PortReqDirection) -> Self {
match d {
PortReqDirection::DeviceToHost => Self::In,
PortReqDirection::HostToDevice => Self::Out,
}
}
}
impl From<EndpBinaryDirection> for EndpDirection {
fn from(b: EndpBinaryDirection) -> Self {
match b {
EndpBinaryDirection::In => Self::In,
EndpBinaryDirection::Out => Self::Out,
}
}
}
impl From<PortReqDirection> for EndpDirection {
fn from(d: PortReqDirection) -> Self {
match d {
PortReqDirection::HostToDevice => Self::Out,
PortReqDirection::DeviceToHost => Self::In,
}
}
}
impl EndpDesc {
pub fn ty(self) -> EndpointTy {
match self.attributes & ENDP_ATTR_TY_MASK {
0 => EndpointTy::Ctrl,
1 => EndpointTy::Isoch,
2 => EndpointTy::Bulk,
3 => EndpointTy::Interrupt,
_ => unreachable!(),
}
}
pub fn is_control(&self) -> bool {
self.ty() == EndpointTy::Ctrl
}
pub fn is_interrupt(&self) -> bool {
self.ty() == EndpointTy::Interrupt
}
pub fn is_bulk(&self) -> bool {
self.ty() == EndpointTy::Bulk
}
pub fn is_isoch(&self) -> bool {
self.ty() == EndpointTy::Isoch
}
pub fn direction(&self) -> EndpDirection {
if self.is_control() {
return EndpDirection::Bidirectional;
}
if self.address & 0x80 != 0 {
EndpDirection::In
} else {
EndpDirection::Out
}
}
pub fn xhci_ep_type(&self) -> Result<u8> {
Ok(match self.direction() {
EndpDirection::Out if self.is_isoch() => 1,
EndpDirection::Out if self.is_bulk() => 2,
EndpDirection::Out if self.is_interrupt() => 3,
EndpDirection::Bidirectional if self.is_control() => 4,
EndpDirection::In if self.is_isoch() => 5,
EndpDirection::In if self.is_bulk() => 6,
EndpDirection::In if self.is_interrupt() => 7,
_ => return Err(Error::new(EINVAL)),
})
}
pub fn is_superspeed(&self) -> bool {
self.ssc.is_some()
}
pub fn is_superspeedplus(&self) -> bool {
self.sspc.is_some()
}
fn interrupt_usage_bits(&self) -> u8 {
assert!(self.is_interrupt());
(self.attributes & 0x20) >> 4
}
pub fn is_periodic(&self) -> bool {
#[repr(u8)]
enum InterruptUsageBits {
Periodic,
Notification,
Rsvd2,
Rsvd3,
}
if self.is_interrupt() {
self.interrupt_usage_bits() == InterruptUsageBits::Periodic as u8
} else {
self.is_isoch()
}
}
pub fn log_max_streams(&self) -> Option<NonZeroU8> {
self.ssc
.as_ref()
.map(|ssc| {
if self.is_bulk() {
let raw = ssc.attributes & 0x1F;
NonZeroU8::new(raw)
} else {
None
}
})
.flatten()
}
pub fn isoch_mult(&self, lec: bool) -> u8 {
if !lec && self.is_isoch() {
if self.is_superspeedplus() {
return 0;
}
self.ssc
.as_ref()
.map(|ssc| ssc.attributes & 0x3)
.unwrap_or(0)
} else {
0
}
}
pub fn max_burst(&self) -> u8 {
self.ssc.map(|ssc| ssc.max_burst).unwrap_or(0)
}
pub fn has_ssp_companion(&self) -> bool {
self.ssc
.map(|ssc| ssc.attributes & (1 << 7) != 0)
.unwrap_or(false)
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct IfDesc {
pub kind: u8,
pub number: u8,
pub alternate_setting: u8,
pub class: u8,
pub sub_class: u8,
pub protocol: u8,
pub interface_str: Option<String>,
pub endpoints: SmallVec<[EndpDesc; 4]>,
pub hid_descs: SmallVec<[HidDesc; 1]>,
}
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
pub struct SuperSpeedCmp {
pub kind: u8,
pub max_burst: u8,
pub attributes: u8,
pub bytes_per_interval: u16,
}
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
pub struct SuperSpeedPlusIsochCmp {
pub kind: u8,
pub bytes_per_interval: u32,
}
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
pub struct HidDesc {
pub kind: u8,
pub hid_spec_release: u16,
pub country: u8,
pub desc_count: u8,
pub desc_ty: u8,
pub desc_len: u16,
pub optional_desc_ty: u8,
pub optional_desc_len: u16,
}
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
pub struct PortReq {
pub direction: PortReqDirection,
pub req_type: PortReqTy,
pub req_recipient: PortReqRecipient,
pub request: u8,
pub value: u16,
pub index: u16,
pub length: u16,
pub transfers_data: bool,
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub enum PortReqDirection {
HostToDevice,
DeviceToHost,
}
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
pub enum PortReqTy {
Class,
Vendor,
Standard,
}
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
pub enum PortReqRecipient {
Device,
Interface,
Endpoint,
Other,
VendorSpecific,
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct PortId {
pub root_hub_port_num: u8,
pub route_string: u32,
}
impl PortId {
pub fn root_hub_port_index(&self) -> usize {
self.root_hub_port_num.checked_sub(1).unwrap().into()
}
pub fn hub_depth(&self) -> u8 {
let mut hub_depth = 0;
let mut route_string = self.route_string;
while route_string != 0 {
route_string >>= 4;
hub_depth += 1;
}
hub_depth
}
pub fn child(&self, value: u8) -> Result<Self, String> {
let depth = self.hub_depth();
if depth >= 5 {
return Err(format!("too many route string components"));
}
if value & 0xF0 != 0 {
return Err(format!(
"value {:?} is too large for route string component",
value
));
}
Ok(Self {
root_hub_port_num: self.root_hub_port_num,
route_string: self.route_string | u32::from(value) << (depth * 4),
})
}
pub fn parent(&self) -> Option<(Self, u8)> {
let depth = self.hub_depth();
let parent_depth = depth.checked_sub(1)?;
let parent_shift = parent_depth * 4;
let parent_mask = 0xF << parent_shift;
Some((
Self {
root_hub_port_num: self.root_hub_port_num,
route_string: self.route_string & !parent_mask,
},
u8::try_from((self.route_string & parent_mask) >> parent_shift).unwrap(),
))
}
}
impl fmt::Display for PortId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.root_hub_port_num)?;
// USB 3.1 Revision 1.1 Specification Section 8.9 Route String Field
// The Route String is a 20-bit field in downstream directed packets that the hub uses to route
// each packet to the designated downstream port. It is composed of a concatenation of the
// downstream port numbers (4 bits per hub) for each hub traversed to reach a device.
let mut route_string = self.route_string;
while route_string != 0 {
write!(f, ".{}", route_string & 0xF)?;
route_string >>= 4;
}
Ok(())
}
}
impl str::FromStr for PortId {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut root_hub_port_num = 0;
let mut route_string = 0;
for (i, part) in s.split('.').enumerate() {
let value: u8 = part
.parse()
.map_err(|e| format!("failed to parse {:?}: {}", part, e))?;
// Neither root hub port number nor route string support 0 components
// to identify downstream ports
if value == 0 {
return Err(format!("zero is not a valid port ID component"));
}
// Parse root hub port number
if i == 0 {
root_hub_port_num = value;
continue;
}
// Parse route string component
let depth = i - 1;
if depth >= 5 {
return Err(format!("too many route string components"));
}
if value & 0xF0 != 0 {
return Err(format!(
"value {:?} is too large for route string component",
value
));
}
route_string |= u32::from(value) << (depth * 4);
}
Ok(Self {
root_hub_port_num,
route_string,
})
}
}
pub struct XhciClientHandle {
fd: libredox::Fd,
scheme: String,
port: PortId,
}
impl fmt::Debug for XhciClientHandle {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("XhciClientHandle")
.field("scheme", &self.scheme)
.field("port", &self.port)
.field("fd", &"libredox::Fd")
.finish()
}
}
#[repr(u8)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum PortState {
EnabledOrDisabled,
Default,
Addressed,
Configured,
}
impl PortState {
pub fn as_str(&self) -> &'static str {
match self {
Self::EnabledOrDisabled => "enabled_or_disabled",
Self::Default => "default",
Self::Addressed => "addressed",
Self::Configured => "configured",
}
}
}
#[derive(Debug, Error)]
#[error("invalid input")]
pub struct Invalid(pub &'static str);
impl str::FromStr for PortState {
type Err = Invalid;
fn from_str(s: &str) -> result::Result<Self, Self::Err> {
Ok(match s {
"enabled_or_disabled" | "enabled/disabled" => Self::EnabledOrDisabled,
"default" => Self::Default,
"addressed" => Self::Addressed,
"configured" => Self::Configured,
_ => return Err(Invalid("read reserved port state")),
})
}
}
#[repr(u8)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub enum EndpointStatus {
Disabled,
Enabled,
Halted,
Stopped,
Error,
}
#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub struct PortTransferStatus {
pub kind: PortTransferStatusKind,
pub bytes_transferred: u32,
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub enum PortTransferStatusKind {
Success,
ShortPacket,
Stalled,
Unknown,
}
impl Default for PortTransferStatusKind {
fn default() -> Self {
Self::Success
}
}
impl EndpointStatus {
pub fn as_str(&self) -> &'static str {
match self {
Self::Disabled => "disabled",
Self::Enabled => "enabled",
Self::Halted => "halted",
Self::Stopped => "stopped",
Self::Error => "error",
}
}
}
impl str::FromStr for EndpointStatus {
type Err = Invalid;
fn from_str(s: &str) -> result::Result<Self, Self::Err> {
Ok(match s {
"disabled" => Self::Disabled,
"enabled" => Self::Enabled,
"halted" => Self::Halted,
"stopped" => Self::Stopped,
"error" => Self::Error,
_ => return Err(Invalid("read reserved endpoint state")),
})
}
}
pub enum DeviceReqData<'a> {
In(&'a mut [u8]),
Out(&'a [u8]),
NoData,
}
impl DeviceReqData<'_> {
pub fn len(&self) -> usize {
match self {
Self::In(buf) => buf.len(),
Self::Out(buf) => buf.len(),
Self::NoData => 0,
}
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn map_buf<T, F: Fn(&[u8]) -> T>(&self, f: F) -> Option<T> {
match self {
Self::In(sbuf) => Some(f(sbuf)),
Self::Out(dbuf) => Some(f(dbuf)),
_ => None,
}
}
pub fn direction(&self) -> PortReqDirection {
match self {
DeviceReqData::Out(_) => PortReqDirection::HostToDevice,
DeviceReqData::NoData => PortReqDirection::HostToDevice,
DeviceReqData::In(_) => PortReqDirection::DeviceToHost,
}
}
}
impl XhciClientHandle {
pub fn new(scheme: String, port: PortId) -> result::Result<Self, XhciClientHandleError> {
let path = format!("/scheme/{}/port{}", scheme, port);
let fd = libredox::Fd::open(&path, libredox::flag::O_DIRECTORY, 0)?;
Ok(Self { fd, scheme, port })
}
fn read(&self, path: &str) -> result::Result<Vec<u8>, XhciClientHandleError> {
let target_fd = self.fd.openat(path, libredox::flag::O_RDONLY, 0)?;
let stat = target_fd.stat()?;
let mut buf: Vec<u8> = vec![0u8; stat.st_size as usize];
let count = target_fd.read(&mut buf)?;
buf.truncate(count);
Ok(buf)
}
fn read_to_string(&self, path: &str) -> result::Result<String, XhciClientHandleError> {
let buf = self.read(path)?;
Ok(String::from_utf8(buf)?)
}
pub fn attach(&self) -> result::Result<(), XhciClientHandleError> {
let file = self.fd.openat("attach", libredox::flag::O_WRONLY, 0)?;
let _bytes_written = file.write(&[])?;
Ok(())
}
pub fn detach(&self) -> result::Result<(), XhciClientHandleError> {
let file = self.fd.openat("detach", libredox::flag::O_WRONLY, 0)?;
let _bytes_written = file.write(&[])?;
Ok(())
}
pub fn get_standard_descs(&self) -> result::Result<DevDesc, XhciClientHandleError> {
let json = self.read("descriptors")?;
Ok(serde_json::from_slice(&json)?)
}
pub fn configure_endpoints(
&self,
req: &ConfigureEndpointsReq,
) -> result::Result<(), XhciClientHandleError> {
let json = serde_json::to_vec(req)?;
let file = self.fd.openat("configure", libredox::flag::O_WRONLY, 0)?;
let json_bytes_written = file.write(&json)?;
if json_bytes_written != json.len() {
return Err(XhciClientHandleError::InvalidResponse(Invalid(
"configure_endpoints didn't read as many bytes as were requested",
)));
}
Ok(())
}
pub fn port_state(&self) -> result::Result<PortState, XhciClientHandleError> {
let string = self.read_to_string("state")?;
Ok(string.parse()?)
}
pub fn open_endpoint_ctl(&self, num: u8) -> result::Result<File, XhciClientHandleError> {
let path = format!("endpoints/{}/ctl", num);
let fd = self.fd.openat(&path, libredox::flag::O_RDWR, 0)?;
Ok(unsafe { File::from_raw_fd(fd.into_raw() as RawFd) })
}
pub fn open_endpoint_data(&self, num: u8) -> result::Result<File, XhciClientHandleError> {
let path = format!("endpoints/{}/data", num);
let fd = self.fd.openat(&path, libredox::flag::O_RDWR, 0)?;
Ok(unsafe { File::from_raw_fd(fd.into_raw() as RawFd) })
}
pub fn open_endpoint(&self, num: u8) -> result::Result<XhciEndpHandle, XhciClientHandleError> {
Ok(XhciEndpHandle {
ctl: self.open_endpoint_ctl(num)?,
data: self.open_endpoint_data(num)?,
})
}
pub fn device_request<'a>(
&self,
req_type: PortReqTy,
req_recipient: PortReqRecipient,
request: u8,
value: u16,
index: u16,
data: DeviceReqData<'a>,
) -> result::Result<(), XhciClientHandleError> {
let length = u16::try_from(data.len())
.or(Err(XhciClientHandleError::TransferBufTooLarge(data.len())))?;
let req = PortReq {
direction: data.direction(),
req_type,
req_recipient,
request,
value,
index,
length,
transfers_data: !matches!(data, DeviceReqData::NoData),
};
let json = serde_json::to_vec(&req)?;
let file = self.fd.openat("request", libredox::flag::O_RDWR, 0)?;
let json_bytes_written = file.write(&json)?;
if json_bytes_written != json.len() {
return Err(XhciClientHandleError::InvalidResponse(Invalid(
"device_request didn't return the same number of bytes as were written",
)));
}
match data {
DeviceReqData::In(buf) => {
let bytes_read = file.read(buf)?;
if bytes_read != buf.len() {
return Err(XhciClientHandleError::InvalidResponse(Invalid(
"device_request didn't transfer (host2dev) all bytes",
)));
}
}
DeviceReqData::Out(buf) => {
let bytes_read = file.write(&buf)?;
if bytes_read != buf.len() {
return Err(XhciClientHandleError::InvalidResponse(Invalid(
"device_request didn't transfer (dev2host) all bytes",
)));
}
}
DeviceReqData::NoData => (),
}
Ok(())
}
pub fn get_descriptor(
&self,
recipient: PortReqRecipient,
ty: u8,
idx: u8,
windex: u16,
buffer: &mut [u8],
) -> result::Result<(), XhciClientHandleError> {
self.device_request(
PortReqTy::Standard,
recipient,
0x06,
(u16::from(ty) << 8) | u16::from(idx),
windex,
DeviceReqData::In(buffer),
)
}
pub fn clear_feature(
&self,
recipient: PortReqRecipient,
index: u16,
feature_sel: u16,
) -> result::Result<(), XhciClientHandleError> {
self.device_request(
PortReqTy::Standard,
recipient,
0x01,
feature_sel,
index,
DeviceReqData::NoData,
)
}
}
#[derive(Debug)]
pub struct XhciEndpHandle {
data: File,
ctl: File,
}
/// The direction of a transfer.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub enum XhciEndpCtlDirection {
/// Host to device
Out,
/// Device to host
In,
/// No data, and hence no I/O on the Data interface file at all.
NoData,
}
/// A request to an endpoint Ctl interface file. Currently serialized with JSON.
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
#[non_exhaustive]
pub enum XhciEndpCtlReq {
// TODO: Reduce the number of direction enums from 5 to perhaps 2.
// TODO: Allow to send multiple buffers in one transfer.
/// Tells xhcid that a buffer is about to be sent from the Data interface file, to the
/// endpoint.
Transfer {
/// The direction of the transfer. If the direction is `XhciEndpCtlDirection::NoData`, no
/// bytes will be transferred, and therefore no reads or writes shall be done to the Data
/// driver interface file.
direction: XhciEndpCtlDirection,
/// The number of bytes to be read or written. This field must be set to zero if the
/// direction is `XhciEndpCtlDirection::NoData`. When all bytes have been read or written,
/// the transfer will be considered complete by xhcid, and a non-pending status will be
/// returned.
count: u32,
},
// TODO: Allow clients to specify what to reset.
/// Tells xhcid that the endpoint is going to be reset.
Reset {
/// Only issue the Reset Endpoint and Set TR Dequeue Pointer commands, and let the client
/// itself send a potential ClearFeature(ENDPOINT_HALT).
no_clear_feature: bool,
},
/// Tells xhcid that the endpoint status is going to be retrieved from the Ctl interface file.
Status,
}
/// A response from an endpoint Ctl interface file. Currently serialized with JSON.
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
#[non_exhaustive]
pub enum XhciEndpCtlRes {
/// Xhcid responded with the current state of an endpoint.
Status(EndpointStatus),
/// Xhci sent the result of a transfer.
TransferResult(PortTransferStatus),
/// Xhcid is waiting for data to be sent or received on the Data interface file.
Pending,
/// No Ctl request is currently being processed by xhcid.
Idle,
}
impl XhciEndpHandle {
fn ctl_req(&mut self, ctl_req: &XhciEndpCtlReq) -> result::Result<(), XhciClientHandleError> {
let ctl_buffer = serde_json::to_vec(ctl_req)?;
let ctl_bytes_written = self.ctl.write(&ctl_buffer)?;
if ctl_bytes_written != ctl_buffer.len() {
return Err(Invalid("xhcid didn't process all of the ctl bytes").into());
}
Ok(())
}
fn ctl_res(&mut self) -> result::Result<XhciEndpCtlRes, XhciClientHandleError> {
// a response must never exceed 256 bytes
let mut ctl_buffer = [0u8; 256];
let ctl_bytes_read = self.ctl.read(&mut ctl_buffer)?;
let ctl_res = serde_json::from_slice(&ctl_buffer[..ctl_bytes_read as usize])?;
Ok(ctl_res)
}
pub fn reset(&mut self, no_clear_feature: bool) -> result::Result<(), XhciClientHandleError> {
self.ctl_req(&XhciEndpCtlReq::Reset { no_clear_feature })
}
pub fn status(&mut self) -> result::Result<EndpointStatus, XhciClientHandleError> {
self.ctl_req(&XhciEndpCtlReq::Status)?;
match self.ctl_res()? {
XhciEndpCtlRes::Status(s) => Ok(s),
_ => Err(Invalid("expected status response").into()),
}
}
fn generic_transfer<F: FnOnce(&mut File) -> io::Result<usize>>(
&mut self,
direction: XhciEndpCtlDirection,
f: F,
expected_len: u32,
) -> result::Result<PortTransferStatus, XhciClientHandleError> {
let req = XhciEndpCtlReq::Transfer {
direction,
count: expected_len,
};
self.ctl_req(&req)?;
let bytes_read = f(&mut self.data)?;
let res = self.ctl_res()?;
match res {
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),
_ => Err(Invalid("expected transfer result").into()),
}
}
pub fn transfer_write(
&mut self,
buf: &[u8],
) -> result::Result<PortTransferStatus, XhciClientHandleError> {
self.generic_transfer(
XhciEndpCtlDirection::Out,
|data| data.write(buf),
buf.len() as u32,
)
}
pub fn transfer_read(
&mut self,
buf: &mut [u8],
) -> result::Result<PortTransferStatus, XhciClientHandleError> {
let len = buf.len() as u32;
self.generic_transfer(XhciEndpCtlDirection::In, |data| data.read(buf), len)
}
pub fn transfer_nodata(&mut self) -> result::Result<PortTransferStatus, XhciClientHandleError> {
self.generic_transfer(XhciEndpCtlDirection::NoData, |_| Ok(0), 0)
}
fn transfer_stream(&mut self, total_len: u32) -> TransferStream<'_> {
TransferStream {
bytes_to_transfer: total_len,
bytes_transferred: 0,
bytes_per_transfer: 32768, // TODO
endp_handle: self,
}
}
pub fn transfer_write_stream(&mut self, total_len: u32) -> TransferWriteStream<'_> {
TransferWriteStream {
inner: self.transfer_stream(total_len),
}
}
pub fn transfer_read_stream(&mut self, total_len: u32) -> TransferReadStream<'_> {
TransferReadStream {
inner: self.transfer_stream(total_len),
}
}
}
pub struct TransferWriteStream<'a> {
inner: TransferStream<'a>,
}
pub struct TransferReadStream<'a> {
inner: TransferStream<'a>,
}
struct TransferStream<'a> {
bytes_to_transfer: u32,
bytes_transferred: u32,
bytes_per_transfer: u32,
endp_handle: &'a mut XhciEndpHandle,
}
#[derive(Debug, Error)]
pub enum XhciClientHandleError {
#[error("i/o error: {0}")]
IoError(#[from] io::Error),
#[error("serialization error: {0}")]
SerializationError(#[from] serde_json::Error),
#[error("invalid response")]
InvalidResponse(#[from] Invalid),
#[error("transfer buffer too large ({0} > 65536)")]
TransferBufTooLarge(usize),
#[error("unexpected short packet of size {0}")]
UnexpectedShortPacket(usize),
#[error("utf8 error: {0}")]
Utf8Error(#[from] FromUtf8Error),
}
impl From<libredox::error::Error> for XhciClientHandleError {
fn from(error: libredox::error::Error) -> Self {
Self::IoError(error.into())
}
}
@@ -0,0 +1,30 @@
//! 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)
//!
pub extern crate plain;
mod driver_interface;
pub mod usb;
pub use driver_interface::*;
@@ -0,0 +1,181 @@
//! 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)
//!
#![allow(warnings)]
#[macro_use]
extern crate bitflags;
use std::fs::File;
use std::sync::Arc;
use pcid_interface::irq_helpers::read_bsp_apic_id;
#[cfg(target_arch = "x86_64")]
use pcid_interface::irq_helpers::{
allocate_first_msi_interrupt_on_bsp, allocate_single_interrupt_vector_for_msi,
};
use pcid_interface::{PciFeature, PciFeatureInfo, PciFunctionHandle};
use redox_scheme::{scheme::register_sync_scheme, Socket};
use scheme_utils::Blocking;
use crate::xhci::{InterruptMethod, Xhci};
// Declare as pub so that no warnings appear due to parts of the interface code not being used by
// the driver. Since there's also a dedicated crate for the driver interface, those warnings don't
// mean anything.
pub mod driver_interface;
mod usb;
mod xhci;
#[cfg(target_arch = "x86_64")]
fn get_int_method(pcid_handle: &mut PciFunctionHandle) -> (Option<File>, InterruptMethod) {
let pci_config = pcid_handle.config();
let all_pci_features = pcid_handle.fetch_all_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_msix {
let msix_info = match pcid_handle.feature_info(PciFeature::MsiX) {
PciFeatureInfo::Msi(_) => panic!(),
PciFeatureInfo::MsiX(s) => s,
};
let mut info = unsafe { msix_info.map_and_mask_all(pcid_handle) };
// Allocate one msi vector.
let method = {
// primary interrupter
let k = 0;
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);
table_entry_pointer.write_addr_and_data(msg_addr_and_data);
table_entry_pointer.unmask();
(Some(interrupt_handle), InterruptMethod::Msi)
};
pcid_handle.enable_feature(PciFeature::MsiX);
log::debug!("Enabled MSI-X");
method
} else if has_msi {
let interrupt_handle = allocate_first_msi_interrupt_on_bsp(pcid_handle);
(Some(interrupt_handle), InterruptMethod::Msi)
} else if let Some(irq) = pci_config.func.legacy_interrupt_line {
log::debug!("Legacy IRQ {}", irq);
// legacy INTx# interrupt pins.
(Some(irq.irq_handle("xhcid")), InterruptMethod::Intx)
} else {
// no interrupts at all
(None, InterruptMethod::Polling)
}
}
//TODO: MSI on non-x86_64?
#[cfg(not(target_arch = "x86_64"))]
fn get_int_method(pcid_handle: &mut PciFunctionHandle) -> (Option<File>, InterruptMethod) {
let pci_config = pcid_handle.config();
if let Some(irq) = pci_config.func.legacy_interrupt_line {
// legacy INTx# interrupt pins.
(Some(irq.irq_handle("xhcid")), InterruptMethod::Intx)
} else {
// no interrupts at all
(None, InterruptMethod::Polling)
}
}
//TODO: cleanup CSZ support
fn daemon_with_context_size<const N: usize>(
daemon: daemon::Daemon,
mut pcid_handle: PciFunctionHandle,
) -> ! {
let pci_config = pcid_handle.config();
let mut name = pci_config.func.name();
name.push_str("_xhci");
common::setup_logging(
"usb",
"host",
&name,
common::output_level(),
common::file_level(),
);
log::debug!("XHCI PCI CONFIG: {:?}", pci_config);
let address = unsafe { pcid_handle.map_bar(0) }.ptr.as_ptr() as usize;
let (irq_file, interrupt_method) = (None, InterruptMethod::Polling); //get_int_method(&mut pcid_handle);
//TODO: Fix interrupts.
log::info!("XHCI {}", pci_config.func.display());
let scheme_name = format!("usb.{}", name);
let socket = Socket::create().expect("xhcid: failed to create usb scheme");
let handler = Blocking::new(&socket, 16);
let hci = Arc::new(
Xhci::<N>::new(scheme_name.clone(), address, interrupt_method, pcid_handle)
.expect("xhcid: failed to allocate device"),
);
register_sync_scheme(&socket, &scheme_name, &mut &*hci)
.expect("xhcid: failed to regsiter scheme to namespace");
daemon.ready();
xhci::start_irq_reactor(&hci, irq_file);
xhci::start_device_enumerator(&hci);
hci.poll();
handler
.process_requests_blocking(&*hci)
.expect("xhcid: failed to process requests");
}
fn main() {
pcid_interface::pci_daemon(daemon);
}
fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> ! {
let address = unsafe { pcid_handle.map_bar(0) }.ptr.as_ptr() as usize;
let cap = unsafe { &mut *(address as *mut xhci::CapabilityRegs) };
if cap.csz() {
daemon_with_context_size::<{ xhci::CONTEXT_64 }>(daemon, pcid_handle)
} else {
daemon_with_context_size::<{ xhci::CONTEXT_32 }>(daemon, pcid_handle)
}
}
@@ -0,0 +1,182 @@
use std::slice;
#[repr(C, packed)]
#[derive(Clone, Copy, Debug, Default)]
pub struct BosDescriptor {
pub len: u8,
pub kind: u8,
pub total_len: u16,
pub cap_count: u8,
}
unsafe impl plain::Plain for BosDescriptor {}
#[repr(C, packed)]
#[derive(Clone, Copy, Debug, Default)]
pub struct BosDevDescriptorBase {
pub len: u8,
pub kind: u8,
pub cap_ty: u8,
}
unsafe impl plain::Plain for BosDevDescriptorBase {}
#[repr(C, packed)]
#[derive(Clone, Copy, Debug, Default)]
pub struct BosSuperSpeedDesc {
pub len: u8,
pub kind: u8,
pub cap_ty: u8,
pub attrs: u8,
pub speed_supp: u16,
pub func_supp: u8,
pub u1_dev_exit_lat: u8,
pub u2_dev_exit_lat: u16,
}
#[repr(C, packed)]
#[derive(Clone, Copy, Debug, Default)]
pub struct BosSuperSpeedPlusDesc {
pub len: u8,
pub kind: u8,
pub cap_ty: u8,
pub _rsvd0: u8,
pub attrs: u32,
pub func_supp: u32,
pub _rsvd1: u16,
}
unsafe impl plain::Plain for BosSuperSpeedPlusDesc {}
#[repr(C, packed)]
#[derive(Clone, Copy, Debug, Default)]
pub struct BosUsb2ExtDesc {
pub len: u8,
pub kind: u8,
pub cap_ty: u8,
pub attrs: u32,
}
unsafe impl plain::Plain for BosUsb2ExtDesc {}
#[repr(u8)]
pub enum DeviceCapability {
Usb2Ext = 0x02,
SuperSpeed,
SuperSpeedPlus = 0x0A,
}
unsafe impl plain::Plain for BosSuperSpeedDesc {}
impl BosSuperSpeedPlusDesc {
pub fn ssac(&self) -> u8 {
(self.attrs & 0x0000_000F) as u8
}
pub fn sublink_speed_attr(&self) -> &[u32] {
unsafe {
slice::from_raw_parts(
(self as *const Self).add(1) as *const u32,
self.ssac() as usize + 1,
)
}
}
}
pub struct BosDevDescIter<'a> {
bytes: &'a [u8],
}
impl<'a> BosDevDescIter<'a> {
pub fn new(bytes: &'a [u8]) -> Self {
Self { bytes }
}
}
impl<'a> From<&'a [u8]> for BosDevDescIter<'a> {
fn from(slice: &'a [u8]) -> Self {
Self::new(slice)
}
}
impl<'a> Iterator for BosDevDescIter<'a> {
type Item = (BosDevDescriptorBase, &'a [u8]);
fn next(&mut self) -> Option<Self::Item> {
if let Some(desc) = plain::from_bytes::<BosDevDescriptorBase>(self.bytes).ok() {
if desc.len as usize > self.bytes.len() {
return None;
};
let bytes_ret = &self.bytes[..desc.len as usize];
self.bytes = &self.bytes[desc.len as usize..];
Some((*desc, bytes_ret))
} else {
return None;
}
}
}
#[derive(Clone, Copy, Debug)]
pub enum BosAnyDevDesc {
Usb2Ext(BosUsb2ExtDesc),
SuperSpeed(BosSuperSpeedDesc),
SuperSpeedPlus(BosSuperSpeedPlusDesc),
Unknown,
}
impl BosAnyDevDesc {
pub fn is_superspeed(&self) -> bool {
match self {
Self::SuperSpeed(_) => true,
_ => false,
}
}
pub fn is_superspeedplus(&self) -> bool {
match self {
Self::SuperSpeedPlus(_) => true,
_ => false,
}
}
}
pub struct BosAnyDevDescIter<'a> {
inner: BosDevDescIter<'a>,
}
impl<'a> From<BosDevDescIter<'a>> for BosAnyDevDescIter<'a> {
fn from(ll: BosDevDescIter<'a>) -> Self {
Self { inner: ll }
}
}
impl<'a> From<&'a [u8]> for BosAnyDevDescIter<'a> {
fn from(slice: &'a [u8]) -> Self {
Self::from(BosDevDescIter::from(slice))
}
}
impl<'a> Iterator for BosAnyDevDescIter<'a> {
type Item = BosAnyDevDesc;
fn next(&mut self) -> Option<Self::Item> {
let (base, slice) = self.inner.next()?;
if base.cap_ty == DeviceCapability::Usb2Ext as u8 {
Some(BosAnyDevDesc::Usb2Ext(*plain::from_bytes(slice).ok()?))
} else if base.cap_ty == DeviceCapability::SuperSpeed as u8 {
Some(BosAnyDevDesc::SuperSpeed(*plain::from_bytes(slice).ok()?))
} else if base.cap_ty == DeviceCapability::SuperSpeedPlus as u8 {
Some(BosAnyDevDesc::SuperSpeedPlus(
*plain::from_bytes(slice).ok()?,
))
} else if base.cap_ty == 0 {
// TODO
return None;
} else {
log::warn!("unknown USB device capability of type: {:#x}", base.cap_ty);
Some(BosAnyDevDesc::Unknown)
}
}
}
pub fn bos_capability_descs<'a>(
desc: BosDescriptor,
data: &'a [u8],
) -> impl Iterator<Item = BosAnyDevDesc> + 'a {
BosAnyDevDescIter::from(&data[..desc.total_len as usize - std::mem::size_of_val(&desc)])
.take(desc.cap_count as usize)
}
@@ -0,0 +1,27 @@
#[repr(C, packed)]
#[derive(Clone, Copy, Debug, Default)]
pub struct ConfigDescriptor {
pub length: u8,
pub kind: u8,
pub total_length: u16,
pub interfaces: u8,
pub configuration_value: u8,
pub configuration_str: u8,
pub attributes: u8,
pub max_power: u8,
}
unsafe impl plain::Plain for ConfigDescriptor {}
#[repr(C, packed)]
#[derive(Clone, Copy, Debug, Default)]
pub struct OtherSpeedConfig {
pub length: u8,
pub kind: u8,
pub total_length: u16,
pub interfaces: u8,
pub configuration_value: u8,
pub configuration_str: u8,
pub attributes: u8,
pub max_power: u8,
}
@@ -0,0 +1,191 @@
//! 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(C, 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(C, 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(C, 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,
}
unsafe impl plain::Plain for DeviceQualifier {}
@@ -0,0 +1,86 @@
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(C, packed)]
#[derive(Clone, Copy, Debug, Default)]
pub struct EndpointDescriptor {
pub length: u8,
pub kind: u8,
pub address: u8,
pub attributes: u8,
pub max_packet_size: u16,
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)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum EndpointTy {
Ctrl = 0,
Isoch = 1,
Bulk = 2,
Interrupt = 3,
}
impl EndpointDescriptor {
fn ty(self) -> EndpointTy {
match self.attributes & ENDP_ATTR_TY_MASK {
0 => EndpointTy::Ctrl,
1 => EndpointTy::Isoch,
2 => EndpointTy::Bulk,
3 => EndpointTy::Interrupt,
_ => unreachable!(),
}
}
}
unsafe impl Plain for EndpointDescriptor {}
#[repr(C, packed)]
#[derive(Clone, Copy, Debug, Default)]
pub struct SuperSpeedCompanionDescriptor {
pub length: u8,
pub kind: u8,
pub max_burst: u8,
pub attributes: u8,
pub bytes_per_interval: u16,
}
unsafe impl Plain for SuperSpeedCompanionDescriptor {}
#[repr(C, packed)]
#[derive(Clone, Copy, Debug, Default)]
pub struct SuperSpeedPlusIsochCmpDescriptor {
pub length: u8,
pub kind: u8,
pub reserved: u16,
pub bytes_per_interval: u32,
}
unsafe impl Plain for SuperSpeedPlusIsochCmpDescriptor {}
#[repr(C, packed)]
#[derive(Clone, Copy, Debug, Default)]
pub struct HidDescriptor {
pub length: u8,
pub kind: u8,
pub hid_spec_release: u16,
pub country_code: u8,
pub num_descriptors: u8,
pub report_desc_ty: u8,
pub report_desc_len: u16,
pub optional_desc_ty: u8,
pub optional_desc_len: u16,
}
unsafe impl Plain for HidDescriptor {}
@@ -0,0 +1,187 @@
#[repr(C, packed)]
#[derive(Clone, Copy, Debug)]
pub struct HubDescriptorV2 {
pub length: u8,
pub kind: u8,
pub ports: u8,
pub characteristics: u16,
pub power_on_good: u8,
pub current: u8,
/*TODO: USB 2 and 3 disagree on the descriptor, so some fields are disabled
// 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],
*/
}
unsafe impl plain::Plain for HubDescriptorV2 {}
impl HubDescriptorV2 {
pub const DESCRIPTOR_KIND: u8 = 0x29;
}
impl Default for HubDescriptorV2 {
fn default() -> Self {
Self {
length: 0,
kind: 0,
ports: 0,
characteristics: 0,
power_on_good: 0,
current: 0,
/*
bitmaps: [0; 64],
*/
}
}
}
#[repr(C, packed)]
#[derive(Clone, Copy, Debug)]
pub struct HubDescriptorV3 {
pub length: u8,
pub kind: u8,
pub ports: u8,
pub characteristics: u16,
pub power_on_good: u8,
pub current: u8,
pub decode_latency: u8,
pub delay: u16,
/*TODO: USB 2 and 3 disagree on the descriptor, so some fields are disabled
// 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],
*/
}
unsafe impl plain::Plain for HubDescriptorV3 {}
impl HubDescriptorV3 {
pub const DESCRIPTOR_KIND: u8 = 0x2A;
}
impl Default for HubDescriptorV3 {
fn default() -> Self {
Self {
length: 0,
kind: 0,
ports: 0,
characteristics: 0,
power_on_good: 0,
current: 0,
decode_latency: 0,
delay: 0,
/*
bitmaps: [0; 64],
*/
}
}
}
// This only includes matching features from both USB 2.0 and 3.0 specs
#[derive(Clone, Copy, Debug)]
#[repr(u8)]
pub enum HubPortFeature {
PortConnection = 0,
PortOverCurrent = 3,
PortReset = 4,
PortLinkState = 5,
PortPower = 8,
CPortConnection = 16,
CPortOverCurrent = 19,
CPortReset = 20,
}
bitflags::bitflags! {
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
#[repr(transparent)]
pub struct HubPortStatusV2: u32 {
const CONNECTION = 1 << 0;
const ENABLE = 1 << 1;
const SUSPEND = 1 << 2;
const OVER_CURRENT = 1 << 3;
const RESET = 1 << 4;
// bits 5-7 reserved
const POWER = 1 << 8;
const LOW_SPEED = 1 << 9;
const HIGH_SPEED = 1 << 10;
const TEST = 1 << 11;
const INDICATOR = 1 << 12;
// bits 13-15 reserved
const CONNECTION_CHANGED = 1 << 16;
const ENABLE_CHANGED = 1 << 17;
const SUSPEND_CHANGED = 1 << 18;
const OVER_CURRENT_CHANGED = 1 << 19;
const RESET_CHANGED = 1 << 20;
// bits 21 - 31 reserved
}
}
unsafe impl plain::Plain for HubPortStatusV2 {}
bitflags::bitflags! {
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
#[repr(transparent)]
pub struct HubPortStatusV3: u32 {
const CONNECTION = 1 << 0;
const ENABLE = 1 << 1;
// bit 2 reserved
const OVER_CURRENT = 1 << 3;
const RESET = 1 << 4;
const LINK_STATE_0 = 1 << 5;
const LINK_STATE_1 = 1 << 6;
const LINK_STATE_2 = 1 << 7;
const LINK_STATE_3 = 1 << 8;
const POWER = 1 << 9;
const SPEED_0 = 1 << 10;
const SPEED_1 = 1 << 11;
const SPEED_2 = 1 << 12;
// bits 13 - 15 reserved
const CONNECTION_CHANGED = 1 << 16;
// bits 17-18
const OVER_CURRENT_CHANGED = 1 << 19;
const RESET_CHANGED = 1 << 20;
const BH_RESET_CHANGED = 1 << 21;
const LINK_STATE_CHANGED = 1 << 22;
const CONFIG_ERROR = 1 << 23;
// bits 24 - 31 reserved
}
}
unsafe impl plain::Plain for HubPortStatusV3 {}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum HubPortStatus {
V2(HubPortStatusV2),
V3(HubPortStatusV3),
}
impl HubPortStatus {
pub fn is_powered(&self) -> bool {
match self {
Self::V2(x) => x.contains(HubPortStatusV2::POWER),
Self::V3(x) => x.contains(HubPortStatusV3::POWER),
}
}
pub fn is_connected(&self) -> bool {
match self {
Self::V2(x) => x.contains(HubPortStatusV2::CONNECTION),
Self::V3(x) => x.contains(HubPortStatusV3::CONNECTION),
}
}
pub fn is_resetting(&self) -> bool {
match self {
Self::V2(x) => x.contains(HubPortStatusV2::RESET),
Self::V3(x) => x.contains(HubPortStatusV3::RESET),
}
}
pub fn is_enabled(&self) -> bool {
match self {
Self::V2(x) => x.contains(HubPortStatusV2::ENABLE),
Self::V3(x) => x.contains(HubPortStatusV3::ENABLE),
}
}
}
@@ -0,0 +1,18 @@
use plain::Plain;
///
#[repr(C, packed)]
#[derive(Clone, Copy, Debug, Default)]
pub struct InterfaceDescriptor {
pub length: u8,
pub kind: u8,
pub number: u8,
pub alternate_setting: u8,
pub endpoints: u8,
pub class: u8,
pub sub_class: u8,
pub protocol: u8,
pub interface_str: u8,
}
unsafe impl Plain for InterfaceDescriptor {}
@@ -0,0 +1,63 @@
//! 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};
pub use self::endpoint::{
EndpointDescriptor, EndpointTy, HidDescriptor, SuperSpeedCompanionDescriptor,
SuperSpeedPlusIsochCmpDescriptor, ENDP_ATTR_TY_MASK,
};
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,
}
pub(crate) mod bos;
pub(crate) mod config;
pub(crate) mod device;
pub(crate) mod endpoint;
pub(crate) mod hub;
pub(crate) mod interface;
pub(crate) mod setup;
@@ -0,0 +1,209 @@
use super::DescriptorKind;
use crate::driver_interface::*;
#[repr(C, packed)]
#[derive(Clone, Copy, Debug, Default)]
pub struct Setup {
pub kind: u8,
pub request: u8,
pub value: u16,
pub index: u16,
pub length: u16,
}
#[repr(u8)]
pub enum ReqDirection {
HostToDevice = 0,
DeviceToHost = 1,
}
impl From<PortReqDirection> for ReqDirection {
fn from(d: PortReqDirection) -> Self {
match d {
PortReqDirection::DeviceToHost => Self::DeviceToHost,
PortReqDirection::HostToDevice => Self::HostToDevice,
}
}
}
#[repr(u8)]
pub enum ReqType {
/// Standard device requests, such as SET_ADDRESS and SET_CONFIGURATION. These aren't directly
/// accessible using the API, but are sent from xhcid when required.
Standard = 0,
/// Class specific requests that are directly accessible from the API.
Class = 1,
/// Vendor specific requests that are accessible using the API.
Vendor = 2,
/// Reserved
Reserved = 3,
}
impl From<PortReqTy> for ReqType {
fn from(d: PortReqTy) -> Self {
match d {
PortReqTy::Standard => Self::Standard,
PortReqTy::Class => Self::Class,
PortReqTy::Vendor => Self::Vendor,
}
}
}
#[repr(u8)]
pub enum ReqRecipient {
Device = 0,
Interface = 1,
Endpoint = 2,
Other = 3,
// 4..=30 are reserved
VendorSpecific = 31,
}
impl From<PortReqRecipient> for ReqRecipient {
fn from(d: PortReqRecipient) -> Self {
match d {
PortReqRecipient::Device => Self::Device,
PortReqRecipient::Interface => Self::Interface,
PortReqRecipient::Endpoint => Self::Endpoint,
PortReqRecipient::Other => Self::Other,
PortReqRecipient::VendorSpecific => Self::VendorSpecific,
}
}
}
#[repr(u8)]
pub enum SetupReq {
GetStatus = 0x00,
ClearFeature = 0x01,
SetFeature = 0x03,
SetAddress = 0x05,
GetDescriptor = 0x06,
SetDescriptor = 0x07,
GetConfiguration = 0x08,
SetConfiguration = 0x09,
GetInterface = 0x0A,
SetInterface = 0x0B,
SynchFrame = 0x0C,
}
pub const USB_SETUP_DIR_BIT: u8 = 1 << 7;
pub const USB_SETUP_DIR_SHIFT: u8 = 7;
pub const USB_SETUP_REQ_TY_MASK: u8 = 0x60;
pub const USB_SETUP_REQ_TY_SHIFT: u8 = 5;
pub const USB_SETUP_RECIPIENT_MASK: u8 = 0x1F;
pub const USB_SETUP_RECIPIENT_SHIFT: u8 = 0;
impl Setup {
pub fn direction(&self) -> ReqDirection {
if self.kind & USB_SETUP_DIR_BIT == 0 {
ReqDirection::HostToDevice
} else {
ReqDirection::DeviceToHost
}
}
pub const fn req_ty(&self) -> u8 {
(self.kind & USB_SETUP_REQ_TY_MASK) >> USB_SETUP_REQ_TY_SHIFT
}
pub const fn req_recipient(&self) -> u8 {
(self.kind & USB_SETUP_RECIPIENT_MASK) >> USB_SETUP_RECIPIENT_SHIFT
}
pub fn is_allowed_from_api(&self) -> bool {
self.req_ty() == ReqType::Class as u8 || self.req_ty() == ReqType::Vendor as u8
}
pub const fn get_status() -> Self {
Self {
kind: 0b1000_0000,
request: 0x00,
value: 0,
index: 0,
length: 2,
}
}
pub const fn clear_feature(feature: u16) -> Self {
Self {
kind: 0b0000_0000,
request: 0x01,
value: feature,
index: 0,
length: 0,
}
}
pub const fn set_feature(feature: u16) -> Self {
Self {
kind: 0b0000_0000,
request: 0x03,
value: feature,
index: 0,
length: 0,
}
}
pub const fn set_address(address: u16) -> Self {
Self {
kind: 0b0000_0000,
request: 0x05,
value: address,
index: 0,
length: 0,
}
}
pub const fn get_descriptor(
kind: DescriptorKind,
index: u8,
language: u16,
length: u16,
) -> Self {
Self {
kind: 0b1000_0000,
request: 0x06,
value: ((kind as u16) << 8) | (index as u16),
index: language,
length: length,
}
}
pub const fn set_descriptor(kind: u8, index: u8, language: u16, length: u16) -> Self {
Self {
kind: 0b0000_0000,
request: 0x07,
value: ((kind as u16) << 8) | (index as u16),
index: language,
length: length,
}
}
pub const fn get_configuration() -> Self {
Self {
kind: 0b1000_0000,
request: 0x08,
value: 0,
index: 0,
length: 1,
}
}
pub const fn set_configuration(value: u8) -> Self {
Self {
kind: 0b0000_0000,
request: 0x09,
value: value as u16,
index: 0,
length: 0,
}
}
pub const fn set_interface(interface: u8, alternate_setting: u8) -> Self {
Self {
kind: 0b0000_0001,
request: 0x0B,
value: alternate_setting as u16,
index: interface as u16,
length: 0,
}
}
}
@@ -0,0 +1,225 @@
use common::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(C, 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 CSZ bit from HCCPARAMS1. See [CapabilityRegs]
pub const HCC_PARAMS1_CSZ_BIT: u32 = 1 << HCC_PARAMS1_CSZ_SHIFT;
/// The shift to use to get the CSZ bit from HCCParams1. See [CapabilityRegs]
pub const HCC_PARAMS1_CSZ_SHIFT: u8 = 2;
/// 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 context size (CSZ) bit from HCCParams1.
pub fn csz(&self) -> bool {
self.hcc_params1.readf(HCC_PARAMS1_CSZ_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
}
/// 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
}
/// 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)
}
}
@@ -0,0 +1,228 @@
use std::collections::BTreeMap;
use common::io::{Io, Mmio};
use log::debug;
use syscall::error::Result;
use syscall::PAGE_SIZE;
use common::dma::Dma;
use super::ring::Ring;
use super::Xhci;
pub const CONTEXT_32: usize = 0;
pub const CONTEXT_64: usize = 1;
#[repr(C, packed)]
struct Rsvd64<const N: usize>([[Mmio<u32>; 8]; N]);
#[repr(C, packed)]
pub struct SlotContext<const N: usize> {
pub a: Mmio<u32>,
pub b: Mmio<u32>,
pub c: Mmio<u32>,
pub d: Mmio<u32>,
_rsvd: [Mmio<u32>; 4],
_rsvd64: Rsvd64<N>,
}
pub const SLOT_CONTEXT_STATE_MASK: u32 = 0xF800_0000;
pub const SLOT_CONTEXT_STATE_SHIFT: u8 = 27;
#[repr(u8)]
pub enum SlotState {
EnabledOrDisabled = 0,
Default = 1,
Addressed = 2,
Configured = 3,
}
#[repr(C, packed)]
pub struct EndpointContext<const N: usize> {
pub a: Mmio<u32>,
pub b: Mmio<u32>,
pub trl: Mmio<u32>,
pub trh: Mmio<u32>,
pub c: Mmio<u32>,
_rsvd: [Mmio<u32>; 3],
_rsvd64: Rsvd64<N>,
}
pub const ENDPOINT_CONTEXT_STATUS_MASK: u32 = 0x7;
#[repr(C, packed)]
pub struct DeviceContext<const N: usize> {
pub slot: SlotContext<N>,
pub endpoints: [EndpointContext<N>; 31],
}
#[repr(C, packed)]
pub struct InputContext<const N: usize> {
pub drop_context: Mmio<u32>,
pub add_context: Mmio<u32>,
_rsvd: [Mmio<u32>; 5],
pub control: Mmio<u32>,
_rsvd64: Rsvd64<N>,
pub device: DeviceContext<N>,
}
impl<const N: usize> InputContext<N> {
pub fn dump_control(&self) {
debug!(
"INPUT CONTEXT: {} {} [{} {} {} {} {}] {}",
self.drop_context.read(),
self.add_context.read(),
self._rsvd[0].read(),
self._rsvd[1].read(),
self._rsvd[2].read(),
self._rsvd[3].read(),
self._rsvd[4].read(),
self.control.read()
);
}
}
pub struct DeviceContextList<const N: usize> {
pub dcbaa: Dma<[u64; 256]>,
pub contexts: Box<[Dma<DeviceContext<N>>]>,
}
impl<const N: usize> DeviceContextList<N> {
pub fn new(ac64: bool, max_slots: u8) -> Result<Self> {
let mut dcbaa = unsafe { Xhci::<N>::alloc_dma_zeroed_raw::<[u64; 256]>(ac64)? };
let mut contexts = vec![];
// Create device context buffers for each slot
for i in 0..max_slots as usize {
let context: Dma<DeviceContext<N>> = unsafe { Xhci::<N>::alloc_dma_zeroed_raw(ac64) }?;
dcbaa[i] = context.physical() as u64;
contexts.push(context);
}
Ok(DeviceContextList {
dcbaa,
contexts: contexts.into_boxed_slice(),
})
}
pub fn dcbaap(&self) -> u64 {
self.dcbaa.physical() as u64
}
}
#[repr(C, packed)]
pub struct StreamContext {
trl: Mmio<u32>,
trh: Mmio<u32>,
edtla: Mmio<u32>,
rsvd: Mmio<u32>,
}
unsafe impl plain::Plain for StreamContext {}
#[repr(u8)]
pub enum StreamContextType {
SecondaryRing,
PrimaryRing,
PrimarySsa8,
PrimarySsa16,
PrimarySsa32,
PrimarySsa64,
PrimarySsa128,
PrimarySsa256,
}
pub struct StreamContextArray {
pub contexts: Dma<[StreamContext]>,
pub rings: BTreeMap<u16, Ring>,
}
impl StreamContextArray {
pub fn new<const N: usize>(ac64: bool, count: usize) -> Result<Self> {
unsafe {
Ok(Self {
contexts: Xhci::<N>::alloc_dma_zeroed_unsized_raw(ac64, count)?,
rings: BTreeMap::new(),
})
}
}
pub fn add_ring<const N: usize>(
&mut self,
ac64: bool,
stream_id: u16,
link: bool,
) -> Result<()> {
// NOTE: stream_id 0 is reserved
assert_ne!(stream_id, 0);
let ring = Ring::new::<N>(ac64, 16, link)?;
let pointer = ring.register();
let sct = StreamContextType::PrimaryRing;
assert_eq!(pointer & (!0xE), pointer);
{
let context = &mut self.contexts[stream_id as usize];
context.trl.write((pointer as u32) | ((sct as u32) << 1));
context.trh.write((pointer >> 32) as u32);
// TODO: stopped edtla
}
self.rings.insert(stream_id, ring);
Ok(())
}
pub fn register(&self) -> u64 {
self.contexts.physical() as u64
}
}
#[repr(C, packed)]
pub struct ScratchpadBufferEntry {
pub value_low: Mmio<u32>,
pub value_high: Mmio<u32>,
}
impl ScratchpadBufferEntry {
pub fn set_addr(&mut self, addr: u64) {
self.value_low.write(addr as u32);
self.value_high.write((addr >> 32) as u32);
}
}
pub struct ScratchpadBufferArray {
pub entries: Dma<[ScratchpadBufferEntry]>,
pub pages: Vec<Dma<[u8; PAGE_SIZE]>>,
}
impl ScratchpadBufferArray {
pub fn new<const N: usize>(ac64: bool, entries: u16) -> Result<Self> {
let mut entries =
unsafe { Xhci::<N>::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<_>, _>>()?;
Ok(Self { entries, pages })
}
pub fn register(&self) -> usize {
self.entries.physical()
}
}
#[cfg(test)]
mod test {
use super::*;
use core::mem;
#[test]
fn context_size() {
assert_eq!(mem::size_of::<SlotContext<CONTEXT_32>>(), 32);
assert_eq!(mem::size_of::<SlotContext<CONTEXT_64>>(), 64);
assert_eq!(mem::size_of::<EndpointContext<CONTEXT_32>>(), 32);
assert_eq!(mem::size_of::<EndpointContext<CONTEXT_64>>(), 64);
}
}
@@ -0,0 +1,143 @@
use crate::xhci::port::PortFlags;
use crate::xhci::{PortId, Xhci};
use common::io::Io;
use crossbeam_channel;
use log::{debug, info, warn};
use std::sync::Arc;
use std::time::Duration;
use syscall::EAGAIN;
pub struct DeviceEnumerationRequest {
pub port_id: PortId,
}
pub struct DeviceEnumerator<const N: usize> {
hci: Arc<Xhci<N>>,
request_queue: crossbeam_channel::Receiver<DeviceEnumerationRequest>,
}
impl<const N: usize> DeviceEnumerator<N> {
pub fn new(hci: Arc<Xhci<N>>) -> Self {
let request_queue = hci.device_enumerator_receiver.clone();
DeviceEnumerator { hci, request_queue }
}
pub fn run(&mut self) {
loop {
debug!("Start Device Enumerator Loop");
let request = match self.request_queue.recv() {
Ok(req) => req,
Err(err) => {
panic!("Failed to received an enumeration request! error: {}", err)
}
};
let port_id = request.port_id;
let port_array_index = port_id.root_hub_port_index();
debug!("Device Enumerator request for port {}", port_id);
let (len, flags) = {
let ports = self.hci.ports.lock().unwrap();
let len = ports.len();
if port_array_index >= len {
warn!(
"Received out of bounds Device Enumeration request for port {}",
port_id
);
continue;
}
(len, ports[port_array_index].flags())
};
if flags.contains(PortFlags::CCS) {
debug!(
"Received Device Connect Port Status Change Event with port flags {:?}",
flags
);
//If the port isn't enabled (i.e. it's a USB2 port), we need to reset it if it isn't resetting already
//A USB3 port won't generate a Connect Status Change until it's already enabled, so this check
//will always be skipped for USB3 ports
if !flags.contains(PortFlags::PED) {
let disabled_state = flags.contains(PortFlags::PP)
&& flags.contains(PortFlags::CCS)
&& !flags.contains(PortFlags::PED)
&& !flags.contains(PortFlags::PR);
if !disabled_state {
panic!(
"Port {} isn't in the disabled state! Current flags: {:?}",
port_id, flags
);
} else {
debug!("Port {} has entered the disabled state.", port_id);
}
//THIS LOCKS THE PORTS. DO NOT LOCK PORTS BEFORE THIS POINT
debug!("Received a device connect on port {}, but it's not enabled. Resetting the port.", port_id);
let _ = self.hci.reset_port(port_id);
let mut ports = self.hci.ports.lock().unwrap();
let port = &mut ports[port_array_index];
port.clear_prc();
std::thread::sleep(Duration::from_millis(16)); //Some controllers need some extra time to make the transition.
let flags = port.flags();
let enabled_state = flags.contains(PortFlags::PP)
&& flags.contains(PortFlags::CCS)
&& flags.contains(PortFlags::PED)
&& !flags.contains(PortFlags::PR);
if !enabled_state {
warn!(
"Port {} isn't in the enabled state! Current flags: {:?}",
port_id, flags
);
} else {
debug!(
"Port {} is in the enabled state. Proceeding with enumeration",
port_id
);
}
}
let result = futures::executor::block_on(self.hci.attach_device(port_id));
match result {
Ok(_) => {
info!("Device on port {} was attached", port_id);
}
Err(err) => {
if err.errno == EAGAIN {
debug!("Received a device connect notification for an already connected device. Ignoring...")
} else {
warn!("processing of device attach request failed! Error: {}", err);
}
}
}
} else {
debug!(
"Device Enumerator received Detach request on port {} which is in state {}",
port_id,
self.hci.get_pls(port_id)
);
let result = futures::executor::block_on(self.hci.detach_device(port_id));
match result {
Ok(was_connected) => {
if was_connected {
info!("Device on port {} was detached", port_id);
}
}
Err(err) => {
warn!("processing of device attach request failed! Error: {}", err);
}
}
}
}
}
}
@@ -0,0 +1,14 @@
use common::io::{Io, Mmio};
#[repr(C, packed)]
pub struct Doorbell(Mmio<u32>);
impl Doorbell {
pub fn read(&self) -> u32 {
self.0.read()
}
pub fn write(&mut self, data: u32) {
self.0.write(data);
}
}
@@ -0,0 +1,52 @@
use common::io::{Io, Mmio};
use syscall::error::Result;
use common::dma::Dma;
use super::ring::Ring;
use super::trb::Trb;
use super::Xhci;
#[repr(C, packed)]
pub struct EventRingSte {
pub address_low: Mmio<u32>,
pub address_high: Mmio<u32>,
pub size: Mmio<u16>,
_rsvd: Mmio<u16>,
_rsvd2: Mmio<u32>,
}
// TODO: Use atomic operations, and perhaps an occasional lock for reallocating.
pub struct EventRing {
pub ste: Dma<[EventRingSte]>,
pub ring: Ring,
}
impl EventRing {
pub fn new<const N: usize>(ac64: bool) -> Result<EventRing> {
let mut ring = EventRing {
ste: unsafe { Xhci::<N>::alloc_dma_zeroed_unsized_raw(ac64, 1)? },
ring: Ring::new::<N>(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].size.write(ring.ring.trbs.len() as u16);
Ok(ring)
}
pub fn next(&mut self) -> &mut Trb {
self.ring.next().0
}
pub fn erdp(&self) -> u64 {
self.ring.register() & 0xFFFF_FFFF_FFFF_FFF0
}
pub fn erstba(&self) -> u64 {
self.ste.physical() as u64
}
}
@@ -0,0 +1,287 @@
use common::io::{Io, Mmio};
use std::ops::Range;
use std::ptr::NonNull;
use std::{fmt, mem, ptr, slice};
pub struct ExtendedCapabilitiesIter {
base: *const u8,
}
impl ExtendedCapabilitiesIter {
pub unsafe fn new(base: *const u8) -> Self {
Self { base }
}
}
impl Iterator for ExtendedCapabilitiesIter {
type Item = (NonNull<u8>, u8); // pointer, capability id
fn next(&mut self) -> Option<Self::Item> {
unsafe {
let current = NonNull::new(self.base as *mut _)?;
let reg = current.cast::<Mmio<u32>>().as_ref().read();
let capability_id = (reg & 0xFF) as u8;
let next_rel_in_dwords = ((reg & 0xFF00) >> 8) as u8;
let next_rel = u16::from(next_rel_in_dwords) << 2;
self.base = if next_rel != 0 {
self.base.offset(next_rel as isize)
} else {
ptr::null()
};
Some((current, capability_id))
}
}
}
#[repr(u8)]
pub enum CapabilityId {
// bit 0 is reserved
UsbLegacySupport = 1,
SupportedProtocol,
ExtendedPowerManagement,
IoVirtualization,
MessageInterrupt,
LocalMem,
// bits 7-9 are reserved
UsbDebugCapability = 10,
// bits 11-16 are reserved
ExtendedMessageInterrupt = 17,
// bits 18-191 are reserved
// bits 192-255 are vendor-defined
}
#[repr(C, packed)]
pub struct SupportedProtoCap {
a: Mmio<u32>,
b: Mmio<u32>,
c: Mmio<u32>,
d: Mmio<u32>,
protocol_speeds: [u8; 0],
}
#[repr(C, packed)]
pub struct ProtocolSpeed {
a: Mmio<u32>,
}
pub const PROTO_SPEED_PSIV_MASK: u32 = 0x0000_000F;
pub const PROTO_SPEED_PSIV_SHIFT: u8 = 0;
pub const PROTO_SPEED_PSIE_MASK: u32 = 0x0000_0030;
pub const PROTO_SPEED_PSIE_SHIFT: u8 = 4;
pub const PROTO_SPEED_PLT_MASK: u32 = 0x0000_00C0;
pub const PROTO_SPEED_PLT_SHIFT: u8 = 6;
pub const PROTO_SPEED_PFD_BIT: u32 = 1 << PROTO_SPEED_PFD_SHIFT;
pub const PROTO_SPEED_PFD_SHIFT: u8 = 8;
pub const PROTO_SPEED_LP_MASK: u32 = 0x0000_C000;
pub const PROTO_SPEED_LP_SHIFT: u8 = 14;
pub const PROTO_SPEED_PSIM_MASK: u32 = 0xFFFF_0000;
pub const PROTO_SPEED_PSIM_SHIFT: u8 = 16;
#[repr(u8)]
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum Psie {
Bps,
Kbps,
Mbps,
Gbps,
}
#[repr(u8)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum Plt {
Symmetric,
Reserved,
AsymmetricRx,
AsymmetricTx,
}
#[repr(u8)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum Lp {
SuperSpeed,
SuperSpeedPlus,
Rsvd2,
Rsvd3,
}
impl ProtocolSpeed {
pub const fn from_raw(raw: u32) -> Self {
Self { a: Mmio::new(raw) }
}
pub fn is_lowspeed(&self) -> bool {
self.psim() == 1500 && self.psie() == Psie::Kbps && !self.pfd()
}
pub fn is_fullspeed(&self) -> bool {
self.psim() == 12 && self.psie() == Psie::Mbps && !self.pfd()
}
pub fn is_highspeed(&self) -> bool {
self.psim() == 480 && self.psie() == Psie::Mbps && !self.pfd()
}
pub fn is_superspeed_gen1x1(&self) -> bool {
self.psim() == 5 && self.psie() == Psie::Gbps && self.pfd() && self.lp() == Lp::SuperSpeed
}
pub fn is_superspeedplus_gen2x1(&self) -> bool {
self.psim() == 10
&& self.psie() == Psie::Gbps
&& self.pfd()
&& self.lp() == Lp::SuperSpeedPlus
}
pub fn is_superspeedplus_gen1x2(&self) -> bool {
self.psim() == 10
&& self.psie() == Psie::Gbps
&& self.pfd()
&& self.lp() == Lp::SuperSpeedPlus
}
pub fn is_superspeedplus_gen2x2(&self) -> bool {
self.psim() == 20
&& self.psie() == Psie::Gbps
&& self.pfd()
&& self.lp() == Lp::SuperSpeedPlus
}
pub fn is_superspeed_gen_x(&self) -> bool {
self.is_superspeed_gen1x1()
|| self.is_superspeedplus_gen2x1()
|| self.is_superspeedplus_gen1x2()
|| self.is_superspeedplus_gen2x2()
}
/// Protocol speed ID value
pub fn psiv(&self) -> u8 {
((self.a.read() & PROTO_SPEED_PSIV_MASK) >> PROTO_SPEED_PSIV_SHIFT) as u8
}
pub fn psie_raw(&self) -> u8 {
((self.a.read() & PROTO_SPEED_PSIE_MASK) >> PROTO_SPEED_PSIE_SHIFT) as u8
}
/// Protocol speed ID exponent
pub fn psie(&self) -> Psie {
// safe because psie_raw can only return values in 0..=3
unsafe { mem::transmute(self.psie_raw()) }
}
pub fn plt_raw(&self) -> u8 {
((self.a.read() & PROTO_SPEED_PLT_MASK) >> PROTO_SPEED_PLT_SHIFT) as u8
}
/// PSI type
pub fn plt(&self) -> Plt {
// safe because plt_raw can only return values in 0..=3
unsafe { mem::transmute(self.plt_raw()) }
}
/// PSI Full-duplex
pub fn pfd(&self) -> bool {
self.a.readf(PROTO_SPEED_PFD_BIT)
}
pub fn lp_raw(&self) -> u8 {
((self.a.read() & PROTO_SPEED_LP_MASK) >> PROTO_SPEED_LP_SHIFT) as u8
}
/// Link protocol
pub fn lp(&self) -> Lp {
// safe because lp_raw can only return values in 0..=3
unsafe { mem::transmute(self.lp_raw()) }
}
/// Protocol speed ID mantissa
pub fn psim(&self) -> u16 {
((self.a.read() & PROTO_SPEED_PSIM_MASK) >> PROTO_SPEED_PSIM_SHIFT) as u16
}
}
impl fmt::Debug for ProtocolSpeed {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("ProtocolSpeed")
.field("psiv", &self.psiv())
.field("psie", &self.psie())
.field("plt", &self.plt())
.field("pfd", &self.pfd())
.field("lp", &self.lp())
.field("psim", &self.psim())
.finish()
}
}
pub const SUPP_PROTO_CAP_REV_MIN_MASK: u32 = 0x00FF_0000;
pub const SUPP_PROTO_CAP_REV_MIN_SHIFT: u8 = 16;
pub const SUPP_PROTO_CAP_REV_MAJ_MASK: u32 = 0xFF00_0000;
pub const SUPP_PROTO_CAP_REV_MAJ_SHIFT: u8 = 24;
pub const SUPP_PROTO_CAP_COMPAT_PORT_OFF_MASK: u32 = 0x0000_00FF;
pub const SUPP_PROTO_CAP_COMPAT_PORT_OFF_SHIFT: u8 = 0;
pub const SUPP_PROTO_CAP_COMPAT_PORT_CNT_MASK: u32 = 0x0000_FF00;
pub const SUPP_PROTO_CAP_COMPAT_PORT_CNT_SHIFT: u8 = 8;
pub const SUPP_PROTO_CAP_PROTO_DEF_MASK: u32 = 0x0FFF_0000;
pub const SUPP_PROTO_CAP_PROTO_DEF_SHIFT: u8 = 16;
pub const SUPP_PROTO_CAP_PSIC_MASK: u32 = 0xF000_0000;
pub const SUPP_PROTO_CAP_PSIC_SHIFT: u8 = 28;
pub const SUPP_PROTO_CAP_PORT_SLOT_TYPE_MASK: u32 = 0x0000_001F;
pub const SUPP_PROTO_CAP_PORT_SLOT_TYPE_SHIFT: u8 = 0;
impl SupportedProtoCap {
pub unsafe fn protocol_speeds(&self) -> &[ProtocolSpeed] {
slice::from_raw_parts(
&self.protocol_speeds as *const u8 as *const _,
self.psic() as usize,
)
}
pub unsafe fn protocol_speeds_mut(&mut self) -> &mut [ProtocolSpeed] {
// XXX: Variance really is annoying sometimes.
slice::from_raw_parts_mut(
&self.protocol_speeds as *const u8 as *mut u8 as *mut _,
self.psic() as usize,
)
}
pub fn rev_minor(&self) -> u8 {
((self.a.read() & SUPP_PROTO_CAP_REV_MIN_MASK) >> SUPP_PROTO_CAP_REV_MIN_SHIFT) as u8
}
pub fn rev_major(&self) -> u8 {
((self.a.read() & SUPP_PROTO_CAP_REV_MAJ_MASK) >> SUPP_PROTO_CAP_REV_MAJ_SHIFT) as u8
}
pub fn name_string(&self) -> [u8; 4] {
// TODO: Little endian, right?
u32::to_le_bytes(self.b.read())
}
pub fn compat_port_offset(&self) -> u8 {
((self.c.read() & SUPP_PROTO_CAP_COMPAT_PORT_OFF_MASK)
>> SUPP_PROTO_CAP_COMPAT_PORT_OFF_SHIFT) as u8
}
pub fn compat_port_count(&self) -> u8 {
((self.c.read() & SUPP_PROTO_CAP_COMPAT_PORT_CNT_MASK)
>> SUPP_PROTO_CAP_COMPAT_PORT_CNT_SHIFT) as u8
}
pub fn compat_port_range(&self) -> Range<u8> {
self.compat_port_offset()..self.compat_port_offset() + self.compat_port_count()
}
pub fn proto_defined(&self) -> u16 {
((self.c.read() & SUPP_PROTO_CAP_PROTO_DEF_MASK) >> SUPP_PROTO_CAP_PROTO_DEF_SHIFT) as u16
}
pub fn psic(&self) -> u8 {
((self.c.read() & SUPP_PROTO_CAP_PSIC_MASK) >> SUPP_PROTO_CAP_PSIC_SHIFT) as u8
}
pub fn proto_slot_ty(&self) -> u8 {
((self.d.read() & SUPP_PROTO_CAP_PORT_SLOT_TYPE_MASK)
>> SUPP_PROTO_CAP_PORT_SLOT_TYPE_SHIFT) as u8
}
}
impl fmt::Debug for SupportedProtoCap {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("SupportedProtoCap")
.field("rev_minor", &self.rev_minor())
.field("rev_major", &self.rev_major())
.field("name_string", &String::from_utf8_lossy(&self.name_string()))
.field("compat_port_offset", &self.compat_port_count())
.field("compat_port_count", &self.compat_port_offset())
.field("proto_defined", &self.proto_defined())
.field("psic", &self.psic())
.field("proto_slot_ty", &self.proto_slot_ty())
.field("proto_speeds", unsafe {
&self.protocol_speeds().to_owned()
})
.finish()
}
}
@@ -0,0 +1,743 @@
use std::fs::File;
use std::future::Future;
use std::io::prelude::*;
use std::pin::Pin;
use std::sync::{Arc, Mutex};
use std::task;
use std::os::unix::io::AsRawFd;
use crossbeam_channel::{Receiver, Sender};
use log::{debug, error, info, trace, warn};
use super::doorbell::Doorbell;
use super::event::EventRing;
use super::ring::Ring;
use super::trb::{Trb, TrbCompletionCode, TrbType};
use super::{PortId, Xhci};
use crate::xhci::device_enumerator::DeviceEnumerationRequest;
use crate::xhci::port::PortFlags;
use common::io::Io as _;
use event::RawEventQueue;
/// Short-term states (as in, they are removed when the waker is consumed, but probably pushed back
/// by the future unless it completed).
#[derive(Debug)]
pub struct State {
waker: task::Waker,
kind: StateKind,
message: Arc<Mutex<Option<NextEventTrb>>>,
is_isoch_or_vf: bool,
}
impl State {
fn finish(self, message: Option<NextEventTrb>) {
*self.message.lock().unwrap() = message;
trace!("Waking up future with waker: {:?}", self.waker);
self.waker.wake();
}
}
#[derive(Debug)]
pub struct NextEventTrb {
pub event_trb: Trb,
pub src_trb: Option<Trb>,
}
// TODO: Perhaps all of the transfer rings used by the xHC should be stored linearly, and then
// indexed using this struct instead.
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct RingId {
pub port: PortId,
pub endpoint_num: u8,
pub stream_id: u16,
}
impl RingId {
pub const fn default_control_pipe(port: PortId) -> Self {
Self {
port,
endpoint_num: 0,
stream_id: 0,
}
}
}
/// The state specific to a TRB-type. Since some of the event TDs may asynchronously appear, for
/// example the Command Completion Event and the Transfer Event TDs, they have to be
/// distinguishable. Luckily, the xHC also gives us the actual (physical) pointer to the source
/// TRB, from the command ring, unless the event TD has one the completion codes Ring Underrun,
/// Ring Overrun, or VF Event Ring Full Error. When these errors are encountered, it simply
/// indicates that the commands causing the errors continue to be pending, and thus no information
/// 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,
},
Other(TrbType),
}
impl StateKind {
pub fn trb_type(&self) -> TrbType {
match self {
&Self::CommandCompletion { .. } => TrbType::CommandCompletion,
&Self::Transfer { .. } => TrbType::Transfer,
&Self::Other(ty) => ty,
}
}
}
pub struct IrqReactor<const N: usize> {
hci: Arc<Xhci<N>>,
irq_file: Option<File>,
irq_receiver: Receiver<NewPendingTrb>,
device_enumerator_sender: Sender<DeviceEnumerationRequest>,
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?
}
pub type NewPendingTrb = State;
impl<const N: usize> IrqReactor<N> {
pub fn new(hci: Arc<Xhci<N>>, irq_file: Option<File>) -> Self {
let device_enumerator_sender = hci.device_enumerator_sender.clone();
let irq_receiver = hci.irq_reactor_receiver.clone();
Self {
hci,
irq_file,
irq_receiver,
device_enumerator_sender,
states: Vec::new(),
}
}
// TODO: Configure the amount of time wait when no more work can be done (for IRQ-less polling).
fn pause(&self) {
std::thread::sleep(std::time::Duration::from_millis(2));
}
fn run_polling(mut self) -> ! {
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()
};
'trb_loop: loop {
self.pause();
let mut event_ring = hci_clone.primary_event_ring.lock().unwrap();
let event_trb = &mut event_ring.ring.trbs[event_trb_index];
if event_trb.completion_code() == TrbCompletionCode::Invalid as u8 {
continue 'trb_loop;
}
trace!(
"Found event TRB at index {} with type {} and cycle bit {}: {:?}",
event_trb_index,
event_trb.trb_type(),
event_trb.cycle() as u8,
event_trb
);
if self.check_event_ring_full(event_trb.clone()) {
info!("Had to resize event TRB, retrying...");
continue 'trb_loop;
}
trace!("Handling requests");
self.handle_requests();
trace!("Requests handled");
match event_trb.trb_type() {
_ if event_trb.trb_type() == TrbType::PortStatusChange as u8 => {
trace!("Received a port status change!");
self.handle_port_status_change(event_trb.clone())
} //TODO Handle the other unprompted events
_ => {
self.acknowledge(event_trb.clone());
}
}
event_trb.reserved(false);
self.update_erdp(&*event_ring);
hci_clone.event_handler_finished();
event_trb_index = event_ring.ring.next_index();
}
}
fn mask_interrupts(&mut self) {
let mut run = self.hci.run.lock().unwrap();
debug!("Masking interrupts!");
if !run.ints[0].iman.readf(1 << 1) {
warn!("Attempted to mask interrupts when they were already disabled!")
}
run.ints[0].iman.writef(1 << 1, false);
}
fn unmask_interrupts(&mut self) {
let mut run = self.hci.run.lock().unwrap();
debug!("unmasking interrupts!");
if run.ints[0].iman.readf(1 << 1) {
warn!("Attempted to unmask interrupts when they were already enabled!")
}
run.ints[0].iman.writef(1 << 1, true);
}
fn run_with_irq_file(mut self) -> ! {
debug!("Running IRQ reactor with IRQ file and event queue");
let hci_clone = Arc::clone(&self.hci);
let 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();
trace!("IRQ Reactor has created its event queue.");
let mut event_trb_index = {
hci_clone
.primary_event_ring
.lock()
.unwrap()
.ring
.next_index()
};
trace!("IRQ reactor has grabbed the next index in the event ring.");
'trb_loop: loop {
let _event = event_queue.next_event().unwrap();
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");
if !self.hci.received_irq() {
// continue only when an IRQ to this device was received
trace!("no interrupt pending");
continue 'trb_loop;
}
self.mask_interrupts();
trace!("IRQ reactor received an IRQ");
let _ = self.irq_file.as_mut().unwrap().write(&buffer);
// TODO: More event rings, probably even with different IRQs.
let mut event_ring = hci_clone.primary_event_ring.lock().unwrap();
let mut count = 0;
loop {
trace!("count: {}", count);
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.")
}
//hci_clone.event_handler_finished();
self.unmask_interrupts();
continue 'trb_loop;
} else {
count += 1
}
info!(
"Found event TRB at index {} with type {} and cycle bit {}: {:?}",
event_trb_index,
event_trb.trb_type(),
event_trb.cycle() as u8,
event_trb
);
if self.check_event_ring_full(event_trb.clone()) {
info!("Had to resize event TRB, retrying...");
//hci_clone.event_handler_finished();
if self.hci.interrupt_is_pending(0) {
warn!("After incrementing the dequeue pointer, the interrupt bit is still pending.")
} else {
debug!("The interrupt bit is no longer pending.");
}
self.unmask_interrupts();
continue 'trb_loop;
}
self.handle_requests();
match event_trb.trb_type() {
_ if event_trb.trb_type() == TrbType::PortStatusChange as u8 => {
trace!("Received a port status change!");
self.handle_port_status_change(event_trb.clone())
} //TODO Handle the other unprompted events
_ => {
trace!("Received a non-status trb");
self.acknowledge(event_trb.clone());
}
}
event_trb.reserved(false);
self.update_erdp(&*event_ring);
self.hci.event_handler_finished();
event_trb_index = event_ring.ring.next_index();
}
}
}
/// Handles device attach/detach events as indicated by a PortStatusChange
fn handle_port_status_change(&mut self, trb: Trb) {
if let Some(root_hub_port_num) = trb.port_status_change_port_id() {
let port_id = PortId {
root_hub_port_num,
route_string: 0,
};
trace!("Received Port Status Change Request on port {}", port_id);
self.device_enumerator_sender
.send(DeviceEnumerationRequest { port_id })
.expect(
format!(
"Failed to transmit device numeration request on port {}",
port_id
)
.as_str(),
);
{
let mut ports = self.hci.ports.lock().unwrap();
let root_port_index = port_id.root_hub_port_index();
if root_port_index >= ports.len() {
warn!(
"Received out of bounds transmit device numeration request on root index {} at port {} [port len was: {}]",
root_port_index, port_id, ports.len()
);
return;
}
let port = &mut ports[root_port_index];
port.clear_csc();
}
} else {
warn!(
"Received a TRB of type {}, which was unexpected",
trb.trb_type()
)
}
}
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"
);
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);
}
fn handle_requests(&mut self) {
self.states.extend(
self.irq_receiver
.try_iter()
.inspect(|req| trace!("Received request: {:X?}", req)),
);
}
fn acknowledge(&mut self, trb: Trb) {
//TODO: handle TRBs without an attached state
trace!("ACK TRB {:X?}", trb);
let mut index = 0;
while index < self.states.len() {
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 =>
{
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)
{
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;
}
};
// TODO: Validate the command TRB.
state.finish(Some(NextEventTrb {
src_trb: Some(command_trb.clone()),
event_trb: trb.clone(),
}));
return;
} else if trb.completion_trb_pointer().is_none() {
warn!("Command TRB somehow resulted in an error that only can be caused by transfer TRBs. Ignoring event TRB: {:?}.", trb);
}
}
StateKind::Transfer {
first_phys_ptr,
last_phys_ptr,
ring_id,
} => {
// Check if the TRB matches the transfer
if trb.trb_type() == TrbType::Transfer as u8 {
match trb.transfer_event_trb_pointer() {
Some(phys_ptr) => {
let matches = if first_phys_ptr <= last_phys_ptr {
phys_ptr >= first_phys_ptr && phys_ptr <= last_phys_ptr
} else {
// Handle ring buffer wrap
phys_ptr >= first_phys_ptr || phys_ptr <= last_phys_ptr
};
if matches {
let src_trb = self.hci.get_transfer_trb(phys_ptr, ring_id);
// Give the source transfer TRB together with the event TRB, to the future.
let state = self.states.remove(index);
state.finish(Some(NextEventTrb {
src_trb: src_trb,
event_trb: trb.clone(),
}));
return;
}
}
None => {
// Ring Overrun, Ring Underrun, or Virtual Function Event Ring Full.
//
// These errors are caused when either an isoch transfer that shall write data, doesn't
// have any data since the ring is empty, or if an isoch receive is impossible due to a
// full ring. The Virtual Function Event Ring Full is only for Virtual Machine
// Managers, and since this isn't implemented yet, they are irrelevant.
//
// The best solution here is to differentiate between isoch transfers (and
// virtual function event rings when virtualization gets implemented), with
// regular commands and transfers, and send the error TRB to all of them, or
// possibly an error code wrapped in a Result.
self.acknowledge_failed_transfer_trbs(trb);
return;
}
}
}
// Also check if the transfer is on a dead ring
if self.hci.with_ring(ring_id, |_ring| ()).is_none() {
log::debug!("State {} is a dead transfer", index);
let state = self.states.remove(index);
state.finish(Some(NextEventTrb {
src_trb: None,
//TODO: don't send this TRB as it may not be related
event_trb: trb.clone(),
}));
continue;
}
}
StateKind::Other(trb_type) if trb_type as u8 == trb.trb_type() => {
let state = self.states.remove(index);
state.finish(None);
return;
}
_ => (),
}
index += 1;
}
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 {
index += 1;
if index >= self.states.len() {
break;
}
continue;
}
let state = self.states.remove(index);
state.finish(Some(NextEventTrb {
event_trb: trb.clone(),
src_trb: None,
}));
}
}
/// Checks if an event TRB is a Host Controller Event, with the completion code Event Ring
/// 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;
if had_event_ring_full_error {
self.grow_event_ring();
}
had_event_ring_full_error
}
/// Grows the event ring
fn grow_event_ring(&mut self) {
// TODO
error!("TODO: grow event ring");
}
pub fn run(self) -> ! {
if self.irq_file.is_some() {
self.run_with_irq_file();
} else {
self.run_polling();
}
}
}
struct FutureState {
message: Arc<Mutex<Option<NextEventTrb>>>,
is_isoch_or_vf: bool,
state_kind: StateKind,
}
pub struct EventDoorbell {
dbs: Arc<Mutex<&'static mut [Doorbell]>>,
index: usize,
data: u32,
}
impl EventDoorbell {
pub fn new<const N: usize>(hci: &Xhci<N>, index: usize, data: u32) -> Self {
Self {
//TODO: simplify this logic, maybe just use a raw pointer?
dbs: hci.dbs.clone(),
index,
data,
}
}
pub fn ring(self) {
trace!("Ring doorbell {} with data {}", self.index, self.data);
self.dbs.lock().unwrap()[self.index].write(self.data);
trace!("Doorbell was rung.");
}
}
enum EventTrbFuture {
Pending {
state: FutureState,
sender: Sender<State>,
doorbell_opt: Option<EventDoorbell>,
},
Finished,
}
impl Future for EventTrbFuture {
type Output = NextEventTrb;
fn poll(self: Pin<&mut Self>, context: &mut task::Context) -> task::Poll<Self::Output> {
let this = self.get_mut();
trace!("Start poll!");
let message = match this {
&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");
// Doorbell must be rung after sending state
if let Some(doorbell) = doorbell_opt.take() {
doorbell.ring();
}
return task::Poll::Pending;
}
},
&mut Self::Finished => panic!("Polling finished EventTrbFuture again."),
};
trace!("finished!");
*this = Self::Finished;
task::Poll::Ready(message)
}
}
impl<const N: usize> Xhci<N> {
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()
}
pub fn with_ring<T, F: FnOnce(&Ring) -> T>(&self, id: RingId, function: F) -> Option<T> {
use super::RingOrStreams;
let slot_state = self.port_states.get(&id.port)?;
let endpoint_state = slot_state.endpoint_states.get(&id.endpoint_num)?;
let ring_ref = match endpoint_state.transfer {
RingOrStreams::Ring(ref ring) => ring,
RingOrStreams::Streams(ref ctx_arr) => ctx_arr.rings.get(&id.stream_id)?,
};
Some(function(ring_ref))
}
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)?;
let mut endpoint_state = slot_state.endpoint_states.get_mut(&id.endpoint_num)?;
let ring_ref = match endpoint_state.transfer {
RingOrStreams::Ring(ref mut ring) => ring,
RingOrStreams::Streams(ref mut ctx_arr) => ctx_arr.rings.get_mut(&id.stream_id)?,
};
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() {
panic!("Invalid TRB type given to next_transfer_event_trb(): {} (TRB {:?}. Expected transfer TRB.", last_trb.trb_type(), last_trb)
}
let is_isoch_or_vf = last_trb.trb_type() == TrbType::Isoch as u8;
let first_phys_ptr = ring.trb_phys_ptr(self.cap.ac64(), first_trb);
let last_phys_ptr = ring.trb_phys_ptr(self.cap.ac64(), last_trb);
EventTrbFuture::Pending {
state: FutureState {
is_isoch_or_vf,
state_kind: StateKind::Transfer {
ring_id,
first_phys_ptr,
last_phys_ptr,
},
message: Arc::new(Mutex::new(None)),
},
sender: self.irq_reactor_sender.clone(),
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 {
trace!(
"Sending command at phys_ptr {:X}",
command_ring.trb_phys_ptr(self.cap.ac64(), trb)
);
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 {
state: FutureState {
// This is only possible for transfers if they are isochronous, or for Force Event TRBs (virtualization).
is_isoch_or_vf: false,
state_kind: StateKind::CommandCompletion {
phys_ptr: command_ring.trb_phys_ptr(self.cap.ac64(), trb),
},
message: Arc::new(Mutex::new(None)),
},
sender: self.irq_reactor_sender.clone(),
doorbell_opt: Some(doorbell),
}
}
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,
TrbType::Doorbell as u8,
TrbType::HostController as u8,
TrbType::DeviceNotification as u8,
TrbType::MfindexWrap 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 {
state: FutureState {
is_isoch_or_vf: false,
state_kind: StateKind::Other(trb_type),
message: Arc::new(Mutex::new(None)),
},
sender: self.irq_reactor_sender.clone(),
doorbell_opt: None,
}
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,101 @@
use common::io::{Io, Mmio};
/// 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(C, 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).
}
// Run/stop
pub const USB_CMD_RS: u32 = 1 << 0;
/// Host controller reset
pub const USB_CMD_HCRST: u32 = 1 << 1;
// Interrupter enable
pub const USB_CMD_INTE: u32 = 1 << 2;
/// Host controller halted
pub const USB_STS_HCH: u32 = 1 << 0;
/// Host controller not ready
pub const USB_STS_CNR: u32 = 1 << 11;
/// The mask to get the CIE bit from the Config register. See [OperationalRegs]
pub const OP_CONFIG_CIE_BIT: u32 = 1 << 9;
impl OperationalRegs {
pub fn cie(&self) -> bool {
self.config.readf(OP_CONFIG_CIE_BIT)
}
pub fn set_cie(&mut self, value: bool) {
self.config.writef(OP_CONFIG_CIE_BIT, value)
}
}
@@ -0,0 +1,114 @@
use common::io::{Io, Mmio};
// RO - read-only
// ROS - read-only sticky
// RW - read/write
// RWS - read/write sticky
// RW1CS - read/write-1-to-clear sticky
// RW1S - read/write-1-to-set
// Sticky register values may preserve values through chip hardware reset
bitflags! {
#[derive(Debug)]
pub struct PortFlags: u32 {
const CCS = 1 << 0; // ROS
const PED = 1 << 1; // RW1CS
const RSVD_2 = 1 << 2; // RsvdZ
const OCA = 1 << 3; // RO
const PR = 1 << 4; // RW1S
const PLS_0 = 1 << 5; // RWS
const PLS_1 = 1 << 6; // RWS
const PLS_2 = 1 << 7; // RWS
const PLS_3 = 1 << 8; // RWS
const PP = 1 << 9; // RWS
const SPEED_0 = 1 << 10; // ROS
const SPEED_1 = 1 << 11; // ROS
const SPEED_2 = 1 << 12; // ROS
const SPEED_3 = 1 << 13; // ROS
const PIC_AMB = 1 << 14; // RWS
const PIC_GRN = 1 << 15; // RWS
const LWS = 1 << 16; // RW
const CSC = 1 << 17; // RW1CS
const PEC = 1 << 18; // RW1CS
const WRC = 1 << 19; // RW1CS
const OCC = 1 << 20; // RW1CS
const PRC = 1 << 21; // RW1CS
const PLC = 1 << 22; // RW1CS
const CEC = 1 << 23; // RW1CS
const CAS = 1 << 24; // RO
const WCE = 1 << 25; // RWS
const WDE = 1 << 26; // RWS
const WOE = 1 << 27; // RWS
const RSVD_28 = 1 << 28; // RsvdZ
const RSVD_29 = 1 << 29; // RsvdZ
const DR = 1 << 30; // RO
const WPR = 1 << 31; // RW1S
}
}
#[repr(C, packed)]
pub struct Port {
// This has write one to clear fields, do not expose it, handle writes carefully!
portsc: Mmio<u32>,
pub portpmsc: Mmio<u32>,
pub portli: Mmio<u32>,
pub porthlpmc: Mmio<u32>,
}
impl Port {
pub fn read(&self) -> u32 {
self.portsc.read()
}
pub fn clear_csc(&mut self) {
self.portsc
.write((self.flags_preserved() | PortFlags::CSC).bits());
}
pub fn clear_prc(&mut self) {
self.portsc
.write((self.flags_preserved() | PortFlags::PRC).bits());
}
pub fn set_pr(&mut self) {
self.portsc
.write((self.flags_preserved() | PortFlags::PR).bits());
}
pub fn state(&self) -> u8 {
((self.read() & (0b1111 << 5)) >> 5) as u8
}
pub fn speed(&self) -> u8 {
((self.read() & (0b1111 << 10)) >> 10) as u8
}
pub fn flags(&self) -> PortFlags {
PortFlags::from_bits_truncate(self.read())
}
// Read only preserved flags
pub fn flags_preserved(&self) -> PortFlags {
// RO(S) and RW(S) bits should be preserved
// RW1S and RW1CS bits should not
let preserved = PortFlags::CCS
| PortFlags::OCA
| PortFlags::PLS_0
| PortFlags::PLS_1
| PortFlags::PLS_2
| PortFlags::PLS_3
| PortFlags::PP
| PortFlags::SPEED_0
| PortFlags::SPEED_1
| PortFlags::SPEED_2
| PortFlags::SPEED_3
| PortFlags::PIC_AMB
| PortFlags::PIC_GRN
| PortFlags::WCE
| PortFlags::WDE
| PortFlags::WOE
| PortFlags::DR;
self.flags() & preserved
}
}
@@ -0,0 +1,164 @@
use std::mem;
use syscall::error::Result;
use common::dma::Dma;
use super::trb::Trb;
use super::Xhci;
pub struct Ring {
pub link: bool,
pub trbs: Dma<[Trb]>,
pub i: usize,
pub cycle: bool,
}
impl Ring {
pub fn new<const N: usize>(ac64: bool, length: usize, link: bool) -> Result<Ring> {
Ok(Ring {
link,
trbs: unsafe { Xhci::<N>::alloc_dma_zeroed_unsized_raw(ac64, length)? },
i: 0,
cycle: link,
})
}
pub fn register(&self) -> u64 {
let base = self.trbs.physical() as *const Trb;
let addr = unsafe { base.offset(self.i as isize) };
addr as u64 | self.cycle as u64
}
pub fn next_index(&mut self) -> usize {
let mut i;
loop {
i = self.i;
self.i += 1;
if self.i >= self.trbs.len() {
self.i = 0;
if self.link {
let address = self.trbs.physical();
self.trbs[i].link(address, true, self.cycle);
self.cycle = !self.cycle;
} else {
break;
}
} else {
break;
}
}
i
}
pub fn next(&mut self) -> (&mut Trb, bool) {
let i = self.next_index();
(&mut self.trbs[i], self.cycle)
}
/// 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,
}
}
/// 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.
///
/// # 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 offset = paddr.checked_sub(base)? as usize;
assert_eq!(
offset % mem::size_of::<Trb>(),
0,
"unaligned TRB physical address"
);
let index = offset / mem::size_of::<Trb>();
if index > self.trbs.len() {
return None;
}
Some(index)
}
pub fn phys_addr_to_entry_ref(&self, ac64: bool, paddr: u64) -> Option<&Trb> {
Some(&self.trbs[self.phys_addr_to_index(ac64, paddr)?])
}
pub fn phys_addr_to_entry_mut(&mut self, ac64: bool, paddr: u64) -> Option<&mut Trb> {
let index = self.phys_addr_to_index(ac64, paddr)?;
Some(&mut self.trbs[index])
}
pub fn phys_addr_to_entry(&self, ac64: bool, paddr: u64) -> Option<Trb> {
Some(self.trbs[self.phys_addr_to_index(ac64, paddr)?].clone())
}
pub(crate) fn start_virt_addr(&self) -> *const Trb {
self.trbs.as_ptr()
}
pub(crate) fn end_virt_addr(&self) -> *const Trb {
unsafe { self.start_virt_addr().offset(self.trbs.len() as isize) }
}
pub fn trb_phys_ptr(&self, ac64: bool, trb: &Trb) -> u64 {
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>()
{
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 trb_phys_ptr = trbs_base_phys_ptr + trb_offset_from_base;
trb_phys_ptr
}
/*
/// Endless mutable iterator that iterates through the ring items, over and over again. The
/// iterator doesn't enqueue or dequeue anything, but the trbs are mutably borrowed.
pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut Trb> + '_ {
IterMut { ring: self, i: self.i }
}*/
}
struct Iter<'ring> {
ring: &'ring Ring,
i: usize,
}
impl<'ring> Iterator for Iter<'ring> {
type Item = &'ring Trb;
fn next(&mut self) -> Option<Self::Item> {
let i = self.i;
self.i = (self.i + 1) % self.ring.trbs.len();
Some(&self.ring.trbs[i])
}
}
/*struct IterMut<'ring> {
ring: &'ring mut Ring,
i: usize,
}
impl<'ring> Iterator for IterMut<'ring> {
type Item = &'ring mut Trb;
fn next(&mut self) -> Option<Self::Item> {
let i = self.i;
self.i = (self.i + 1) % self.ring.trbs.len();
Some(&mut self.ring.trbs[i])
}
}*/
@@ -0,0 +1,20 @@
use common::io::Mmio;
#[repr(C, packed)]
pub struct Interrupter {
pub iman: Mmio<u32>,
pub imod: Mmio<u32>,
pub erstsz: Mmio<u32>,
_rsvd: Mmio<u32>,
pub erstba_low: Mmio<u32>,
pub erstba_high: Mmio<u32>,
pub erdp_low: Mmio<u32>,
pub erdp_high: Mmio<u32>,
}
#[repr(C, packed)]
pub struct RuntimeRegs {
pub mfindex: Mmio<u32>,
_rsvd: [Mmio<u32>; 7],
pub ints: [Interrupter; 1024],
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,510 @@
use super::context::StreamContextType;
use crate::usb;
use common::io::{Io, Mmio};
use log::trace;
use std::{fmt, mem};
#[repr(u8)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum TrbType {
Reserved,
/* Transfer */
Normal,
SetupStage,
DataStage,
StatusStage,
Isoch,
Link,
EventData,
NoOp,
/* Command */
EnableSlot,
DisableSlot,
AddressDevice,
ConfigureEndpoint,
EvaluateContext,
ResetEndpoint,
StopEndpoint,
SetTrDequeuePointer,
ResetDevice,
ForceEvent,
NegotiateBandwidth,
SetLatencyToleranceValue,
GetPortBandwidth,
ForceHeader,
NoOpCmd,
/* Reserved */
GetExtendedProperty,
SetExtendedProperty,
Rsv26,
Rsv27,
Rsv28,
Rsv29,
Rsv30,
Rsv31,
/* Events */
Transfer,
CommandCompletion,
PortStatusChange,
BandwidthRequest,
Doorbell,
HostController,
DeviceNotification,
MfindexWrap,
/* Reserved from 40 to 47, vendor devined from 48 to 63 */
}
#[repr(u8)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum TrbCompletionCode {
Invalid = 0x00,
Success = 0x01,
DataBuffer = 0x02,
BabbleDetected = 0x03,
UsbTransaction = 0x04,
Trb = 0x05,
Stall = 0x06,
Resource = 0x07,
Bandwidth = 0x08,
NoSlotsAvailable = 0x09,
InvalidStreamType = 0x0A,
SlotNotEnabled = 0x0B,
EndpointNotEnabled = 0x0C,
ShortPacket = 0x0D,
RingUnderrun = 0x0E,
RingOverrun = 0x0F,
VfEventRingFull = 0x10,
Parameter = 0x11,
BandwidthOverrun = 0x12,
ContextState = 0x13,
NoPingResponse = 0x14,
EventRingFull = 0x15,
IncompatibleDevice = 0x16,
MissedService = 0x17,
CommandRingStopped = 0x18,
CommandAborted = 0x19,
Stopped = 0x1A,
StoppedLengthInvalid = 0x1B,
StoppedShortPacket = 0x1C,
MaxExitLatencyTooLarge = 0x1D,
Rsv30 = 0x1E,
IsochBuffer = 0x1F,
EventLost = 0x20,
Undefined = 0x21,
InvalidStreamId = 0x22,
SecondaryBandwidth = 0x23,
SplitTransaction = 0x24,
/* Values from 37 to 191 are reserved */
/* 192 to 223 are vendor defined errors */
/* 224 to 255 are vendor defined information */
}
#[repr(u8)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum TransferKind {
NoData,
Reserved,
Out,
In,
}
#[repr(C, packed)]
pub struct Trb {
pub data_low: Mmio<u32>,
pub data_high: Mmio<u32>,
pub status: Mmio<u32>,
pub control: Mmio<u32>,
}
impl Clone for Trb {
fn clone(&self) -> Self {
Self {
data_low: Mmio::new(self.data_low.read()),
data_high: Mmio::new(self.data_high.read()),
status: Mmio::new(self.status.read()),
control: Mmio::new(self.control.read()),
}
}
}
pub const TRB_STATUS_COMPLETION_CODE_SHIFT: u8 = 24;
pub const TRB_STATUS_COMPLETION_CODE_MASK: u32 = 0xFF00_0000;
pub const TRB_STATUS_COMPLETION_PARAM_SHIFT: u8 = 0;
pub const TRB_STATUS_COMPLETION_PARAM_MASK: u32 = 0x00FF_FFFF;
pub const TRB_STATUS_TRANSFER_LENGTH_SHIFT: u8 = 0;
pub const TRB_STATUS_TRANSFER_LENGTH_MASK: u32 = 0x00FF_FFFF;
pub const TRB_CONTROL_TRB_TYPE_SHIFT: u8 = 10;
pub const TRB_CONTROL_TRB_TYPE_MASK: u32 = 0x0000_FC00;
pub const TRB_CONTROL_EVENT_DATA_SHIFT: u8 = 2;
pub const TRB_CONTROL_EVENT_DATA_BIT: u32 = 1 << TRB_CONTROL_EVENT_DATA_SHIFT;
pub const TRB_CONTROL_ENDPOINT_ID_MASK: u32 = 0x001F_0000;
pub const TRB_CONTROL_ENDPOINT_ID_SHIFT: u8 = 16;
impl Trb {
pub fn set(&mut self, data: u64, status: u32, control: u32) {
self.data_low.write(data as u32);
self.data_high.write((data >> 32) as u32);
self.status.write(status);
self.control.write(control);
}
pub fn reserved(&mut self, cycle: bool) {
self.set(0, 0, ((TrbType::Reserved as u32) << 10) | (cycle as u32));
}
pub fn read_data(&self) -> u64 {
(self.data_low.read() as u64) | ((self.data_high.read() as u64) << 32)
}
pub fn completion_code(&self) -> u8 {
(self.status.read() >> TRB_STATUS_COMPLETION_CODE_SHIFT) as u8
}
pub fn completion_param(&self) -> u32 {
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
{
false
} else if self.completion_code() == TrbCompletionCode::VfEventRingFull as u8 {
false
} else {
true
}
}
pub fn completion_trb_pointer(&self) -> Option<u64> {
debug_assert_eq!(self.trb_type(), TrbType::CommandCompletion as u8);
if self.has_completion_trb_pointer() {
Some(self.read_data())
} else {
None
}
}
pub fn transfer_event_trb_pointer(&self) -> Option<u64> {
debug_assert_eq!(self.trb_type(), TrbType::Transfer as u8);
if self.has_completion_trb_pointer() {
Some(self.read_data())
} else {
None
}
}
pub fn port_status_change_port_id(&self) -> Option<u8> {
debug_assert_eq!(self.trb_type(), TrbType::PortStatusChange as u8);
if self.has_completion_trb_pointer() {
let data = self.read_data();
Some(((data >> 24) & 0xFF) as u8)
} else {
None
}
}
pub fn event_slot(&self) -> u8 {
(self.control.read() >> 24) as u8
}
/// Returns the number of bytes that should have been transmitten, but weren't.
pub fn transfer_length(&self) -> u32 {
self.status.read() & TRB_STATUS_TRANSFER_LENGTH_MASK
}
pub fn event_data_bit(&self) -> bool {
self.control.readf(TRB_CONTROL_EVENT_DATA_BIT)
}
pub fn event_data(&self) -> Option<u64> {
if self.event_data_bit() {
Some(self.read_data())
} else {
None
}
}
pub fn endpoint_id(&self) -> u8 {
((self.control.read() & TRB_CONTROL_ENDPOINT_ID_MASK) >> TRB_CONTROL_ENDPOINT_ID_SHIFT)
as u8
}
pub fn trb_type(&self) -> u8 {
((self.control.read() & TRB_CONTROL_TRB_TYPE_MASK) >> TRB_CONTROL_TRB_TYPE_SHIFT) as u8
}
pub fn link(&mut self, address: usize, toggle: bool, cycle: bool) {
self.set(
address as u64,
0,
((TrbType::Link as u32) << 10) | ((toggle as u32) << 1) | (cycle as u32),
);
}
pub fn no_op_cmd(&mut self, cycle: bool) {
self.set(0, 0, ((TrbType::NoOpCmd as u32) << 10) | (cycle as u32));
}
pub fn enable_slot(&mut self, slot_type: u8, cycle: bool) {
trace!("Enabling slot with type {}", slot_type);
self.set(
0,
0,
(((slot_type as u32) & 0x1F) << 16)
| ((TrbType::EnableSlot as u32) << 10)
| (cycle as u32),
);
}
pub fn disable_slot(&mut self, slot: u8, cycle: bool) {
self.set(
0,
0,
(u32::from(slot) << 24) | ((TrbType::DisableSlot as u32) << 10) | u32::from(cycle),
);
}
pub fn address_device(&mut self, slot_id: u8, input_ctx_ptr: usize, bsr: bool, cycle: bool) {
assert_eq!(
(input_ctx_ptr as u64) & 0xFFFF_FFFF_FFFF_FFF0,
input_ctx_ptr as u64,
"unaligned input context ptr"
);
self.set(
input_ctx_ptr as u64,
0,
(u32::from(slot_id) << 24)
| ((TrbType::AddressDevice as u32) << 10)
| (u32::from(bsr) << 9)
| u32::from(cycle),
);
}
// Synchronizes the input context endpoints with the device context endpoints, I think.
pub fn configure_endpoint(&mut self, slot_id: u8, input_ctx_ptr: usize, cycle: bool) {
assert_eq!(
(input_ctx_ptr as u64) & 0xFFFF_FFFF_FFFF_FFF0,
input_ctx_ptr as u64,
"unaligned input context ptr"
);
self.set(
input_ctx_ptr as u64,
0,
(u32::from(slot_id) << 24)
| ((TrbType::ConfigureEndpoint as u32) << 10)
| u32::from(cycle),
);
}
pub fn evaluate_context(&mut self, slot_id: u8, input_ctx_ptr: usize, bsr: bool, cycle: bool) {
assert_eq!(
(input_ctx_ptr as u64) & 0xFFFF_FFFF_FFFF_FFF0,
input_ctx_ptr as u64,
"unaligned input context ptr"
);
self.set(
input_ctx_ptr as u64,
0,
(u32::from(slot_id) << 24)
| ((TrbType::EvaluateContext as u32) << 10)
| (u32::from(bsr) << 9)
| u32::from(cycle),
);
}
pub fn reset_endpoint(&mut self, slot_id: u8, endp_num_xhc: u8, tsp: bool, cycle: bool) {
assert_eq!(endp_num_xhc & 0x1F, endp_num_xhc);
self.set(
0,
0,
(u32::from(slot_id) << 24)
| (u32::from(endp_num_xhc) << 16)
| ((TrbType::ResetEndpoint as u32) << 10)
| (u32::from(tsp) << 9)
| u32::from(cycle),
);
}
/// The deque_ptr has to contain the DCS bit (bit 0).
pub fn set_tr_deque_ptr(
&mut self,
deque_ptr: u64,
cycle: bool,
sct: StreamContextType,
stream_id: u16,
endp_num_xhc: u8,
slot: u8,
) {
assert_eq!(deque_ptr & 0xFFFF_FFFF_FFFF_FFF1, deque_ptr);
assert_eq!(endp_num_xhc & 0x1F, endp_num_xhc);
self.set(
deque_ptr | ((sct as u64) << 1),
u32::from(stream_id) << 16,
(u32::from(slot) << 24)
| (u32::from(endp_num_xhc) << 16)
| ((TrbType::SetTrDequeuePointer as u32) << 10)
| u32::from(cycle),
)
}
pub fn stop_endpoint(&mut self, slot_id: u8, endp_num_xhc: u8, suspend: bool, cycle: bool) {
assert_eq!(endp_num_xhc & 0x1F, endp_num_xhc);
self.set(
0,
0,
(u32::from(slot_id) << 24)
| (u32::from(suspend) << 23)
| (u32::from(endp_num_xhc) << 16)
| ((TrbType::StopEndpoint as u32) << 10)
| u32::from(cycle),
);
}
pub fn reset_device(&mut self, slot_id: u8, cycle: bool) {
self.set(
0,
0,
(u32::from(slot_id) << 24) | ((TrbType::ResetDevice as u32) << 10) | u32::from(cycle),
);
}
pub fn transfer_no_op(&mut self, interrupter: u8, ent: bool, ch: bool, ioc: bool, cycle: bool) {
self.set(
0,
u32::from(interrupter) << 22,
((TrbType::NoOp as u32) << 10)
| (u32::from(ioc) << 5)
| (u32::from(ch) << 4)
| (u32::from(ent) << 1)
| u32::from(cycle),
);
}
pub fn setup(&mut self, setup: usb::Setup, transfer: TransferKind, cycle: bool) {
self.set(
unsafe { mem::transmute(setup) },
8,
((transfer as u32) << 16)
| ((TrbType::SetupStage as u32) << 10)
| (1 << 6)
| (cycle as u32),
);
}
pub fn data(&mut self, buffer: usize, length: u16, input: bool, cycle: bool) {
self.set(
buffer as u64,
length as u32,
((input as u32) << 16) | ((TrbType::DataStage as u32) << 10) | (cycle as u32),
);
}
pub fn cycle(&self) -> bool {
self.control.readf(0x01)
}
pub fn status(
&mut self,
interrupter: u16,
input: bool,
ioc: bool,
ch: bool,
ent: bool,
cycle: bool,
) {
self.set(
0,
u32::from(interrupter) << 22,
(u32::from(input) << 16)
| ((TrbType::StatusStage as u32) << 10)
| (u32::from(ioc) << 5)
| (u32::from(ch) << 4)
| (u32::from(ent) << 1)
| (cycle as u32),
);
}
pub fn normal(
&mut self,
buffer: u64,
len: u32,
cycle: bool,
estimated_td_size: u8,
interrupter: u8,
ent: bool,
isp: bool,
chain: bool,
ioc: bool,
idt: bool,
bei: bool,
) {
assert_eq!(estimated_td_size & 0x1F, estimated_td_size);
// NOTE: The interrupter target and no snoop flags have been omitted.
self.set(
buffer,
len | (u32::from(estimated_td_size) << 17) | (u32::from(interrupter) << 22),
u32::from(cycle)
| (u32::from(ent) << 1)
| (u32::from(isp) << 2)
| (u32::from(chain) << 4)
| (u32::from(ioc) << 5)
| (u32::from(idt) << 6)
| (u32::from(bei) << 9)
| ((TrbType::Normal as u32) << 10),
)
}
pub fn is_command_trb(&self) -> bool {
let valid_trb_types = [
TrbType::NoOpCmd as u8,
TrbType::EnableSlot as u8,
TrbType::DisableSlot as u8,
TrbType::AddressDevice as u8,
TrbType::ConfigureEndpoint as u8,
TrbType::EvaluateContext as u8,
TrbType::ResetEndpoint as u8,
TrbType::StopEndpoint as u8,
TrbType::SetTrDequeuePointer as u8,
TrbType::ResetDevice as u8,
TrbType::ForceEvent as u8,
TrbType::NegotiateBandwidth as u8,
TrbType::SetLatencyToleranceValue as u8,
TrbType::GetPortBandwidth as u8,
TrbType::ForceHeader as u8,
TrbType::GetExtendedProperty as u8,
TrbType::SetExtendedProperty as u8,
];
valid_trb_types.contains(&self.trb_type())
}
pub fn is_transfer_trb(&self) -> bool {
// XXX: Unfortunately, the only way to use match statements with integer constants, is to
// precast them into valid enum values, which either requires a derive macro such as
// num_traits's #[derive(FromPrimitive)], or manually writing the reverse match statement
// first.
let valid_trb_types = [
TrbType::Normal as u8,
TrbType::SetupStage as u8,
TrbType::DataStage as u8,
TrbType::StatusStage as u8,
TrbType::Isoch as u8,
TrbType::NoOp as u8,
];
valid_trb_types.contains(&self.trb_type())
}
}
impl fmt::Debug for Trb {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"Trb {{ data: {:>016X}, status: {:>08X}, control: {:>08X} }}",
self.read_data(),
self.status.read(),
self.control.read()
)
}
}
impl fmt::Display for Trb {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"({:>016X}, {:>08X}, {:>08X})",
self.read_data(),
self.status.read(),
self.control.read()
)
}
}