Add class and vendor device reqs to the API.
This commit is contained in:
@@ -18,6 +18,7 @@ pub enum DescriptorKind {
|
||||
InterfacePower,
|
||||
OnTheGo,
|
||||
BinaryObjectStorage = 15,
|
||||
Hid = 33,
|
||||
SuperSpeedCompanion = 48,
|
||||
}
|
||||
|
||||
|
||||
+65
-8
@@ -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,
|
||||
|
||||
+102
-3
@@ -46,6 +46,7 @@ pub enum Handle {
|
||||
Port(usize, usize, Vec<u8>), // port, offset, contents
|
||||
PortDesc(usize, usize, Vec<u8>), // port, offset, contents
|
||||
PortState(usize, usize), // port, offset
|
||||
PortReq(usize),
|
||||
Endpoints(usize, usize, Vec<u8>), // 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::<PortReq<'_>>(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)),
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user