Add class and vendor device reqs to the API.

This commit is contained in:
4lDO2
2020-02-02 11:31:13 +01:00
parent d4f1480d1b
commit 8c158328a3
3 changed files with 168 additions and 11 deletions
+1
View File
@@ -18,6 +18,7 @@ pub enum DescriptorKind {
InterfacePower,
OnTheGo,
BinaryObjectStorage = 15,
Hid = 33,
SuperSpeedCompanion = 48,
}
+65 -8
View File
@@ -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
View File
@@ -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)),
}
}