Merge branch 'usb-rework' into 'main'

[xhcid] Push class specific descriptor parsing to the drivers

See merge request redox-os/base!255
This commit is contained in:
Jeremy Soller
2026-05-07 14:37:44 -06:00
5 changed files with 207 additions and 80 deletions
+2
View File
@@ -13,8 +13,10 @@ anyhow.workspace = true
bitflags.workspace = true
log.workspace = true
orbclient.workspace = true
plain.workspace = true
redox_syscall.workspace = true
rehid = { git = "https://gitlab.redox-os.org/redox-os/rehid.git" }
smallvec.workspace = true
xhcid = { path = "../../usb/xhcid" }
common = { path = "../../common" }
+92
View File
@@ -0,0 +1,92 @@
use anyhow::{anyhow, Result};
use plain::Plain;
use smallvec::SmallVec;
use std::convert::TryInto;
#[repr(u8)]
#[derive(Clone, Copy, Debug, Default)]
pub enum HidClassType {
#[default]
HID = 0x21,
Report,
Physical,
}
#[repr(C, packed)]
#[derive(Clone, Copy, Debug, Default)]
pub struct HidClassDescriptor {
pub desc_type: HidClassType,
pub desc_len: u16,
}
unsafe impl Plain for HidClassDescriptor {}
#[derive(Clone, 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 descriptors: SmallVec<[HidClassDescriptor; 1]>,
}
impl HidDescriptor {
// Size of the fixed part of HidDescriptor
const HID_DESC_FIXED_SIZE: u8 = 6;
const HID_CLASS_DESC_SIZE: u8 = 3;
pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
let length = bytes[0];
let kind = bytes[1];
// A valid descriptor has at least one class descriptor
if (length < Self::HID_DESC_FIXED_SIZE + Self::HID_CLASS_DESC_SIZE) {
return Err(anyhow!("Invalid length"));
}
if (kind != HidClassType::HID as u8) {
return Err(anyhow!("This is not a hid descriptor"));
}
let num_descriptors = bytes[5];
if (length != Self::HID_DESC_FIXED_SIZE + num_descriptors * Self::HID_CLASS_DESC_SIZE) {
return Err(anyhow!(
"Len doesn't match the given number of descriptors ({num_descriptors})"
));
}
let mut descriptors =
SmallVec::<[HidClassDescriptor; 1]>::with_capacity(num_descriptors as usize);
for i in 0..num_descriptors {
match HidClassDescriptor::from_bytes(
&bytes[(Self::HID_DESC_FIXED_SIZE + i * Self::HID_CLASS_DESC_SIZE) as usize
..(Self::HID_DESC_FIXED_SIZE + (i + 1) * Self::HID_CLASS_DESC_SIZE) as usize],
) {
Ok(desc) => descriptors.push(*desc),
Err(e) => return Err(anyhow!("{e:?}")),
}
}
Ok(Self {
length,
kind,
hid_spec_release: u16::from_ne_bytes(bytes[2..4].try_into()?),
country_code: bytes[4],
num_descriptors,
descriptors,
})
}
pub fn get_report_desc(&self) -> Result<HidClassDescriptor> {
for desc in self.descriptors.iter() {
match desc.desc_type {
HidClassType::Report => return Ok(*desc),
_ => (),
}
}
return Err(anyhow!("No Report descriptor found"));
}
}
+7 -5
View File
@@ -1,4 +1,4 @@
use anyhow::{Context, Result};
use anyhow::{anyhow, Context, Result};
use std::{env, thread, time};
use inputd::ProducerHandle;
@@ -13,6 +13,9 @@ use xhcid_interface::{
XhciClientHandle,
};
use crate::descs::HidDescriptor;
mod descs;
mod reqs;
fn send_key_event(display: &mut ProducerHandle, usage_page: u16, usage: u16, pressed: bool) {
@@ -217,9 +220,9 @@ fn main() -> Result<()> {
None
}
});
let hid_desc = if_desc.hid_descs.iter().find_map(|hid_desc| {
let hid_desc = if_desc.unknown_descs.iter().find_map(|unknown_desc| {
//TODO: should we do any filtering?
Some(hid_desc)
HidDescriptor::from_bytes(&unknown_desc.all_bytes).ok()
})?;
Some((if_desc.clone(), endp_desc_opt, hid_desc))
} else {
@@ -246,8 +249,7 @@ fn main() -> Result<()> {
// This sets all reports to a duration of 4ms
reqs::set_idle(&handle, 1, 0, interface_num as u16).context("Failed to set idle")?;
let report_desc_len = hid_desc.desc_len;
assert_eq!(hid_desc.desc_ty, REPORT_DESC_TY);
let report_desc_len = hid_desc.get_report_desc()?.desc_len;
let mut report_desc_bytes = vec![0u8; report_desc_len as usize];
handle
+10 -14
View File
@@ -59,9 +59,10 @@ pub struct ConfDesc {
pub attributes: u8,
pub max_power: u8,
pub interface_descs: SmallVec<[IfDesc; 1]>,
pub unknown_descs: SmallVec<[UnknownDesc; 1]>,
}
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq)]
pub struct EndpDesc {
pub kind: u8,
pub address: u8,
@@ -215,7 +216,7 @@ impl EndpDesc {
.unwrap_or(false)
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct IfDesc {
pub kind: u8,
pub number: u8,
@@ -225,31 +226,26 @@ pub struct IfDesc {
pub protocol: u8,
pub interface_str: Option<String>,
pub endpoints: SmallVec<[EndpDesc; 4]>,
pub hid_descs: SmallVec<[HidDesc; 1]>,
pub unknown_descs: SmallVec<[UnknownDesc; 1]>,
}
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq)]
pub struct SuperSpeedCmp {
pub kind: u8,
pub max_burst: u8,
pub attributes: u8,
pub bytes_per_interval: u16,
}
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq)]
pub struct SuperSpeedPlusIsochCmp {
pub kind: u8,
pub bytes_per_interval: u32,
}
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
pub struct HidDesc {
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct UnknownDesc {
pub len: u8,
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,
pub all_bytes: Vec<u8>,
}
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
pub struct PortReq {
+96 -61
View File
@@ -477,21 +477,6 @@ impl From<usb::EndpointDescriptor> for EndpDesc {
}
}
impl From<usb::HidDescriptor> for HidDesc {
fn from(d: usb::HidDescriptor) -> Self {
Self {
kind: d.kind,
hid_spec_release: d.hid_spec_release,
country: d.country_code,
desc_count: d.num_descriptors,
desc_ty: d.report_desc_ty,
desc_len: d.report_desc_len,
optional_desc_ty: d.optional_desc_ty,
optional_desc_len: d.optional_desc_len,
}
}
}
impl From<usb::SuperSpeedCompanionDescriptor> for SuperSpeedCmp {
fn from(d: usb::SuperSpeedCompanionDescriptor) -> Self {
Self {
@@ -519,9 +504,9 @@ pub enum AnyDescriptor {
Config(usb::ConfigDescriptor),
Interface(usb::InterfaceDescriptor),
Endpoint(usb::EndpointDescriptor),
Hid(usb::HidDescriptor),
SuperSpeedCompanion(usb::SuperSpeedCompanionDescriptor),
SuperSpeedPlusCompanion(usb::SuperSpeedPlusIsochCmpDescriptor),
Unknown(Vec<u8>),
}
impl AnyDescriptor {
@@ -534,6 +519,7 @@ impl AnyDescriptor {
let kind = bytes[1];
if bytes.len() < len.into() {
error!("Parsed length is bigger than buffer len");
return None;
}
@@ -543,13 +529,9 @@ impl AnyDescriptor {
2 => Self::Config(*plain::from_bytes(bytes).ok()?),
4 => Self::Interface(*plain::from_bytes(bytes).ok()?),
5 => Self::Endpoint(*plain::from_bytes(bytes).ok()?),
33 => Self::Hid(*plain::from_bytes(bytes).ok()?),
48 => Self::SuperSpeedCompanion(*plain::from_bytes(bytes).ok()?),
49 => Self::SuperSpeedPlusCompanion(*plain::from_bytes(bytes).ok()?),
_ => {
//panic!("Descriptor unknown {}: bytes {:#0x?}", kind, bytes);
return None;
}
_ => Self::Unknown(bytes[..len as usize].iter().cloned().collect()),
},
len.into(),
))
@@ -563,7 +545,7 @@ impl<const N: usize> Xhci<N> {
slot: u8,
desc: usb::InterfaceDescriptor,
endps: impl IntoIterator<Item = EndpDesc>,
hid_descs: impl IntoIterator<Item = HidDesc>,
unknown_descs: impl IntoIterator<Item = UnknownDesc>,
lang_id: u16,
) -> Result<IfDesc> {
Ok(IfDesc {
@@ -582,7 +564,7 @@ impl<const N: usize> Xhci<N> {
protocol: desc.protocol,
sub_class: desc.sub_class,
endpoints: endps.into_iter().collect(),
hid_descs: hid_descs.into_iter().collect(),
unknown_descs: unknown_descs.into_iter().collect(),
})
}
/// Pushes a command TRB to the command ring, rings the doorbell, and then awaits its Command
@@ -1539,53 +1521,105 @@ impl<const N: usize> Xhci<N> {
}
let mut interface_descs = SmallVec::new();
let mut unknown_descs = SmallVec::new();
let mut iface_unknown_descs = SmallVec::<[UnknownDesc; 1]>::new();
let mut iter = descriptors.into_iter().peekable();
let mut cur_iface = None;
let mut cur_endpoint = None;
let mut endpoints = SmallVec::<[EndpDesc; 4]>::new();
while let Some(item) = iter.next() {
if let AnyDescriptor::Interface(idesc) = item {
let mut endpoints = SmallVec::<[EndpDesc; 4]>::new();
let mut hid_descs = SmallVec::<[HidDesc; 1]>::new();
debug!("Processing descriptor {item:?}");
while endpoints.len() < idesc.endpoints as usize {
let next = match iter.next() {
Some(AnyDescriptor::Endpoint(n)) => n,
Some(AnyDescriptor::Hid(h)) if idesc.class == 3 => {
hid_descs.push(h.into());
continue;
}
Some(unexpected) => {
log::warn!("expected endpoint, got {:X?}", unexpected);
break;
}
None => break,
};
let mut endp = EndpDesc::from(next);
loop {
match iter.peek() {
Some(AnyDescriptor::SuperSpeedCompanion(n)) => {
endp.ssc = Some(SuperSpeedCmp::from(n.clone()));
iter.next().unwrap();
match item {
AnyDescriptor::Interface(idesc) => {
if let Some(cur_idesc) = cur_iface {
if let Some(endp) = cur_endpoint {
if !endpoints.contains(&endp) {
endpoints.push(endp);
}
Some(AnyDescriptor::SuperSpeedPlusCompanion(n)) => {
endp.sspc = Some(SuperSpeedPlusIsochCmp::from(n.clone()));
iter.next().unwrap();
}
_ => break,
}
interface_descs.push(
self.new_if_desc(
port_id,
slot,
cur_idesc,
endpoints,
iface_unknown_descs,
lang_id,
)
.await?,
);
}
endpoints.push(endp);
cur_iface = Some(idesc);
endpoints = SmallVec::new();
iface_unknown_descs = SmallVec::new();
}
AnyDescriptor::Endpoint(endpdesc) => {
if let Some(desc) = cur_endpoint {
endpoints.push(desc);
}
cur_endpoint = Some(EndpDesc::from(endpdesc));
}
AnyDescriptor::SuperSpeedCompanion(ssc) => {
if let Some(endp) = cur_endpoint.as_mut() {
endp.ssc = Some(SuperSpeedCmp::from(ssc.clone()));
} else {
error!("Found a superspeedcompanion descriptor before encountering any endpoint descriptor.");
}
}
AnyDescriptor::SuperSpeedPlusCompanion(sscp) => {
if let Some(endp) = cur_endpoint.as_mut() {
endp.sspc = Some(SuperSpeedPlusIsochCmp::from(sscp.clone()));
} else {
error!("Found a superspeedpluscompanion descriptor before encountering any endpoint descriptor.");
}
}
AnyDescriptor::Unknown(bytes) => {
// If we're processing an interface, add the desc to it, otherwise add it to the configuration
if cur_iface.is_none() {
unknown_descs.push(UnknownDesc {
len: bytes[0],
kind: bytes[1],
all_bytes: bytes,
});
} else {
iface_unknown_descs.push(UnknownDesc {
len: bytes[0],
kind: bytes[1],
all_bytes: bytes,
});
}
}
_ => {
error!("Found unexpected descriptor {item:?} while parsing config descriptors, skipping");
continue;
}
}
}
interface_descs.push(
self.new_if_desc(port_id, slot, idesc, endpoints, hid_descs, lang_id)
.await?,
);
} else {
log::warn!("expected interface, got {:?}", item);
// TODO
//break;
// Push the last endpoint
if let Some(endp) = cur_endpoint {
if !endpoints.contains(&endp) {
endpoints.push(endp);
}
}
// Push the last interface
if let Some(idesc) = cur_iface {
let new_if_desc = self
.new_if_desc(
port_id,
slot,
idesc,
endpoints,
iface_unknown_descs,
lang_id,
)
.await?;
if !interface_descs.contains(&new_if_desc) {
interface_descs.push(new_if_desc);
}
}
@@ -1603,6 +1637,7 @@ impl<const N: usize> Xhci<N> {
attributes: desc.attributes,
max_power: desc.max_power,
interface_descs,
unknown_descs,
});
}