From 8c158328a3c411dce791f4e459aa83a8285430b9 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sun, 2 Feb 2020 11:31:13 +0100 Subject: [PATCH] Add class and vendor device reqs to the API. --- xhcid/src/usb/mod.rs | 1 + xhcid/src/usb/setup.rs | 73 ++++++++++++++++++++++++--- xhcid/src/xhci/scheme.rs | 105 +++++++++++++++++++++++++++++++++++++-- 3 files changed, 168 insertions(+), 11 deletions(-) diff --git a/xhcid/src/usb/mod.rs b/xhcid/src/usb/mod.rs index 0abfacc1f0..bd441c82be 100644 --- a/xhcid/src/usb/mod.rs +++ b/xhcid/src/usb/mod.rs @@ -18,6 +18,7 @@ pub enum DescriptorKind { InterfacePower, OnTheGo, BinaryObjectStorage = 15, + Hid = 33, SuperSpeedCompanion = 48, } diff --git a/xhcid/src/usb/setup.rs b/xhcid/src/usb/setup.rs index 366ed8f405..ca26114f2f 100644 --- a/xhcid/src/usb/setup.rs +++ b/xhcid/src/usb/setup.rs @@ -10,8 +10,65 @@ pub struct Setup { pub length: u16, } +#[repr(u8)] +pub enum ReqDirection { + HostToDevice = 0, + DeviceToHost = 1, +} + +#[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, +} + +#[repr(u8)] +pub enum ReqRecipient { + Device = 0, + Interface = 1, + Endpoint = 2, + Other = 3, + // 4..=30 are reserved + VendorSpecific = 31, +} + +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 get_status() -> Self { + 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, @@ -21,7 +78,7 @@ impl Setup { } } - pub fn clear_feature(feature: u16) -> Self { + pub const fn clear_feature(feature: u16) -> Self { Self { kind: 0b0000_0000, request: 0x01, @@ -31,7 +88,7 @@ impl Setup { } } - pub fn set_feature(feature: u16) -> Self { + pub const fn set_feature(feature: u16) -> Self { Self { kind: 0b0000_0000, request: 0x03, @@ -41,7 +98,7 @@ impl Setup { } } - pub fn set_address(address: u16) -> Self { + pub const fn set_address(address: u16) -> Self { Self { kind: 0b0000_0000, request: 0x05, @@ -51,7 +108,7 @@ impl Setup { } } - pub fn get_descriptor(kind: DescriptorKind, index: u8, language: u16, length: u16) -> Self { + pub const fn get_descriptor(kind: DescriptorKind, index: u8, language: u16, length: u16) -> Self { Self { kind: 0b1000_0000, request: 0x06, @@ -61,7 +118,7 @@ impl Setup { } } - pub fn set_descriptor(kind: u8, index: u8, language: u16, length: u16) -> Self { + pub const fn set_descriptor(kind: u8, index: u8, language: u16, length: u16) -> Self { Self { kind: 0b0000_0000, request: 0x07, @@ -71,7 +128,7 @@ impl Setup { } } - pub fn get_configuration() -> Self { + pub const fn get_configuration() -> Self { Self { kind: 0b1000_0000, request: 0x08, @@ -81,7 +138,7 @@ impl Setup { } } - pub fn set_configuration(value: u16) -> Self { + pub const fn set_configuration(value: u16) -> Self { Self { kind: 0b0000_0000, request: 0x09, diff --git a/xhcid/src/xhci/scheme.rs b/xhcid/src/xhci/scheme.rs index 4688e19942..6ef83756fc 100644 --- a/xhcid/src/xhci/scheme.rs +++ b/xhcid/src/xhci/scheme.rs @@ -46,6 +46,7 @@ pub enum Handle { Port(usize, usize, Vec), // port, offset, contents PortDesc(usize, usize, Vec), // port, offset, contents PortState(usize, usize), // port, offset + PortReq(usize), Endpoints(usize, usize, Vec), // port, offset, contents Endpoint(usize, u8, EndpointHandleTy), // port, endpoint, offset, state ConfigureEndpoints(usize), // port @@ -588,6 +589,87 @@ impl Xhci { bytes_to_read } + fn port_req(&mut self, port_num: usize, buf: &[u8]) -> Result<()> { + use usb::setup::*; + + #[derive(Deserialize)] + struct PortReq<'a> { + direction: &'a str, + req_type: &'a str, + req_recipient: &'a str, + request: u8, + value: u16, + index: u16, + length: u16, + } + // TODO: This json format might be too high level, but is useful for debugging, + // but when actual device-specific drivers are written, a binary format would + // be better. + + let req = serde_json::from_slice::>(buf).or(Err(Error::new(EBADMSG)))?; + + let direction = match req.direction { + "host_to_device" => ReqDirection::HostToDevice, + "device_to_host" => ReqDirection::DeviceToHost, + _ => return Err(Error::new(EBADMSG)), + } as u8; + + let ty = match req.req_type { + "class" => ReqType::Class, + "vendor" => ReqType::Vendor, + "standard" | _ => return Err(Error::new(EBADMSG)), + } as u8; + + let recipient = match req.req_recipient { + "device" => ReqRecipient::Device, + "interface" => ReqRecipient::Interface, + "endpoint" => ReqRecipient::Endpoint, + "other" => ReqRecipient::Other, + "vendor_specific" => ReqRecipient::VendorSpecific, + _ => return Err(Error::new(EBADMSG)), + } as u8; + + let setup = Setup { + kind: (direction << USB_SETUP_DIR_SHIFT) | (ty << USB_SETUP_REQ_TY_SHIFT) | (recipient << USB_SETUP_RECIPIENT_SHIFT), + request: req.request, + value: req.value, + index: req.index, + length: req.length, + }; + + let port_state = self.port_states.get_mut(&port_num).ok_or(Error::new(EBADF))?; + let ring = match port_state.endpoint_states.get_mut(&0).ok_or(Error::new(EIO))? { + EndpointState::Ready(super::RingOrStreams::Ring(ref mut ring)) => ring, + + // Control endpoints never use streams + _ => return Err(Error::new(EIO)), + }; + + { + let (cmd, cycle) = ring.next(); + cmd.setup( + setup, + TransferKind::NoData, // FIXME + cycle, + ); + } + { + let (cmd, cycle) = ring.next(); + cmd.status(false, cycle); + } + self.dbs[port_state.slot as usize].write(1); + + { + let event = self.cmd.next_event(); + while event.data.read() == 0 { + println!(" - Waiting for event"); + } + } + + self.run.ints[0].erdp.write(self.cmd.erdp()); + + Ok(()) + } } impl SchemeMut for Xhci { @@ -649,6 +731,15 @@ impl SchemeMut for Xhci { Handle::PortState(port_num, 0) } + "request" => { + if flags & O_DIRECTORY != 0 && flags & O_STAT == 0 { + return Err(Error::new(ENOTDIR)); + } + if flags & O_RDWR != O_WRONLY && flags & O_STAT == 0 { + return Err(Error::new(EACCES)); + } + Handle::PortReq(port_num) + } "endpoints" => { if flags & O_DIRECTORY == 0 && flags & O_STAT == 0 { return Err(Error::new(EISDIR)); @@ -755,7 +846,9 @@ impl SchemeMut for Xhci { stat.st_size = buf.len() as u64; } } - Handle::ConfigureEndpoints(_) => stat.st_mode = MODE_CHR, + Handle::ConfigureEndpoints(_) | Handle::PortReq(_) => { + stat.st_mode = MODE_CHR | 0o200; // write only + } } Ok(0) } @@ -768,6 +861,7 @@ impl SchemeMut for Xhci { Handle::Port(port_num, _, _) => write!(src, "/port{}/", port_num).unwrap(), Handle::PortDesc(port_num, _, _) => write!(src, "/port{}/descriptors", port_num).unwrap(), Handle::PortState(port_num, _) => write!(src, "/port{}/state", port_num).unwrap(), + Handle::PortReq(port_num) => write!(src, "/port{}/request", port_num).unwrap(), Handle::Endpoints(port_num, _, _) => write!(src, "/port{}/endpoints/", port_num).unwrap(), Handle::Endpoint(port_num, endp_num, st) => write!(src, "/port{}/endpoints/{}/{}", port_num, endp_num, match st { EndpointHandleTy::Root(_, _) => "", @@ -804,7 +898,7 @@ impl SchemeMut for Xhci { Ok(*offset) } // Write-once configure or transfer - Handle::Endpoint(_, _, _) | Handle::ConfigureEndpoints(_) => return Err(Error::new(ESPIPE)), + Handle::Endpoint(_, _, _) | Handle::ConfigureEndpoints(_) | Handle::PortReq(_) => return Err(Error::new(ESPIPE)), } } @@ -819,7 +913,8 @@ impl SchemeMut for Xhci { Ok(bytes_to_read) } - Handle::ConfigureEndpoints(_) => return Err(Error::new(EBADF)), + Handle::ConfigureEndpoints(_) | Handle::PortReq(_) => return Err(Error::new(EBADF)), + &mut Handle::Endpoint(port_num, endp_num, ref mut st) => match st { EndpointHandleTy::Transfer => { self.transfer_read(port_num, endp_num, buf)?; @@ -873,6 +968,10 @@ impl SchemeMut for Xhci { // TODO Ok(buf.len()) } + &Handle::PortReq(port_num) => { + self.port_req(port_num, buf)?; + Ok(buf.len()) + } _ => return Err(Error::new(EBADF)), } }