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:
@@ -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" }
|
||||
|
||||
@@ -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"));
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user