Add a basic HID driver unable to parse report descs.

Custom device requests are now implemented though.
This commit is contained in:
4lDO2
2020-02-08 10:21:21 +01:00
parent 1b74f335b0
commit b308dad7c6
15 changed files with 712 additions and 90 deletions
Generated
+46
View File
@@ -53,6 +53,11 @@ dependencies = [
"safemem 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "base64"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "bgad"
version = "0.1.0"
@@ -1070,6 +1075,24 @@ dependencies = [
"redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "thiserror"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"thiserror-impl 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "thiserror-impl"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "time"
version = "0.1.42"
@@ -1305,9 +1328,22 @@ dependencies = [
"percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "usbhidd"
version = "0.1.0"
dependencies = [
"base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"ux 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"xhcid 0.1.0",
]
[[package]]
name = "usbscsid"
version = "0.1.0"
dependencies = [
"xhcid 0.1.0",
]
[[package]]
name = "utf8parse"
@@ -1322,6 +1358,11 @@ dependencies = [
"rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "ux"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "vboxd"
version = "0.1.0"
@@ -1423,6 +1464,7 @@ dependencies = [
"serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)",
"smallvec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"spin 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)",
"thiserror 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)",
"toml 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -1431,6 +1473,7 @@ dependencies = [
"checksum arrayvec 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9"
"checksum autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2"
"checksum autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
"checksum base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7"
"checksum base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643"
"checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d"
"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
@@ -1541,6 +1584,8 @@ dependencies = [
"checksum stb_truetype 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f77b6b07e862c66a9f3e62a07588fee67cd90a9135a2b942409f195507b4fb51"
"checksum syn 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1e4ff033220a41d1a57d8125eab57bf5263783dfdcc18688b1dacc6ce9651ef8"
"checksum termion 1.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "818ef3700c2a7b447dca1a1dd28341fe635e6ee103c806c636bb9c929991b2cd"
"checksum thiserror 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)" = "205684fd018ca14432b12cce6ea3d46763311a571c3d294e71ba3f01adcf1aad"
"checksum thiserror-impl 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)" = "57e4d2e50ca050ed44fb58309bdce3efa79948f84f9993ad1978de5eebdce5a7"
"checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f"
"checksum tokio 0.1.13 (git+https://gitlab.redox-os.org/redox-os/tokio)" = "<none>"
"checksum tokio-codec 0.1.1 (git+https://gitlab.redox-os.org/redox-os/tokio)" = "<none>"
@@ -1566,6 +1611,7 @@ dependencies = [
"checksum url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a"
"checksum utf8parse 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8772a4ccbb4e89959023bc5b7cb8623a795caa7092d99f3aa9501b9484d4557d"
"checksum uuid 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)" = "90dbc611eb48397705a6b0f6e917da23ae517e4d127123d2cf7674206627d32a"
"checksum ux 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "88dfeb711b61ce620c0cb6fd9f8e3e678622f0c971da2a63c4b3e25e88ed012f"
"checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd"
"checksum vte 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4f42f536e22f7fcbb407639765c8fd78707a33109301f834a594758bedd6e8cf"
"checksum webpki 0.18.1 (registry+https://github.com/rust-lang/crates.io-index)" = "17d7967316d8411ca3b01821ee6c332bde138ba4363becdb492f12e514daa17f"
+1
View File
@@ -15,5 +15,6 @@ members = [
"vboxd",
"vesad",
"xhcid",
"usbhidd",
"usbscsid",
]
+1
View File
@@ -0,0 +1 @@
/target
+14
View File
@@ -0,0 +1,14 @@
[package]
name = "usbhidd"
version = "0.1.0"
authors = ["4lDO2 <4lDO2@protonmail.com>"]
edition = "2018"
license = "MIT"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
base64 = "0.11"
bitflags = "1.2"
ux = "0.1"
xhcid = { path = "../xhcid" }
+266
View File
@@ -0,0 +1,266 @@
use std::convert::TryInto;
use std::env;
use bitflags::bitflags;
use ux::{u2, u4};
use xhcid_interface::{DevDesc, XhciClientHandle};
/*#[repr(u8)]
enum Protocol {
}*/
bitflags! {
pub struct MainItemFlags: u32 {
const CONSTANT = 1 << 0;
const VARIABLE = 1 << 1;
const RELATIVE = 1 << 2;
const WRAP = 1 << 3;
const NONLINEAR = 1 << 4;
const NO_PREFERRED_STATE = 1 << 5;
const NULL_STATE = 1 << 6;
const VOLATILE = 1 << 7;
const BUFFERED_BYTES = 1 << 8;
}
}
#[repr(u8)]
pub enum MainCollectionFlags {
Physical = 0,
Application,
Logical,
Report,
NamedArray,
UsageSwitch,
UsageModifier,
}
const REPORT_DESC_TY: u8 = 34;
#[derive(Debug)]
enum MainItem {
Input(u32),
Output(u32),
Feature(u32),
Collection(u8),
EndOfCollection,
}
#[derive(Debug)]
enum GlobalItem {
UsagePage(u32),
LogicalMinimum(u32),
LogicalMaximum(u32),
PhysicalMinimum(u32),
PhysicalMaximum(u32),
UnitExponent(u32),
Unit(u32),
ReportSize(u32),
RepordId(u32),
ReportCount(u32),
Push(u32),
Pop(u32),
}
#[derive(Debug)]
enum LocalItem {
Usage(u32),
UsageMinimum(u32),
UsageMaximum(u32),
DesignatorIndex(u32),
DesignatorMinimum(u32),
DesignatorMaximum(u32),
StringIndex(u32),
StringMinimum(u32),
StringMaximum(u32),
Delimeter(u32),
}
#[derive(Debug)]
enum ReportItem {
Main(MainItem),
Global(GlobalItem),
Local(LocalItem),
}
impl From<MainItem> for ReportItem {
fn from(main: MainItem) -> Self {
Self::Main(main)
}
}
impl From<GlobalItem> for ReportItem {
fn from(main: GlobalItem) -> Self {
Self::Global(main)
}
}
impl From<LocalItem> for ReportItem {
fn from(main: LocalItem) -> Self {
Self::Local(main)
}
}
impl ReportItem {
fn size(size: u2) -> u8 {
match u8::from(size) {
0 => 0,
1 => 1,
2 => 2,
3 => 4,
_ => unreachable!(),
}
}
fn parse_short(size: u2, ty: u2, tag: u4, bytes: &[u8]) -> Option<(Self, usize)> {
Some(match (u8::from(tag), u8::from(ty)) {
(tag, 0b00) => {
let real_size = Self::size(size) as usize;
let mut value_bytes = [0u8; 4];
if real_size > 0 {
value_bytes[..real_size].copy_from_slice(&bytes[..real_size])
};
let value = u32::from_le_bytes(value_bytes);
match tag {
0b1000 => (MainItem::Input(value).into(), 1 + real_size),
0b1001 => (MainItem::Output(value).into(), 1 + real_size),
0b1011 => (MainItem::Feature(value).into(), 1 + real_size),
0b1010 => (MainItem::Collection(bytes[0]).into(), 2),
0b1100 => (MainItem::EndOfCollection.into(), 1 + real_size),
_ => return None,
}
}
(tag, 0b01) => {
let real_size = Self::size(size) as usize;
let mut value_bytes = [0u8; 4];
if real_size > 0 {
value_bytes[..real_size].copy_from_slice(&bytes[..real_size])
};
let value = u32::from_le_bytes(value_bytes);
match tag {
0b0000 => (GlobalItem::UsagePage(value).into(), 1 + real_size),
0b0001 => (GlobalItem::LogicalMinimum(value).into(), 1 + real_size),
0b0010 => (GlobalItem::LogicalMaximum(value).into(), 1 + real_size),
0b0011 => (GlobalItem::PhysicalMinimum(value).into(), 1 + real_size),
0b0100 => (GlobalItem::PhysicalMaximum(value).into(), 1 + real_size),
0b0101 => (GlobalItem::UnitExponent(value).into(), 1 + real_size),
0b0110 => (GlobalItem::Unit(value).into(), 1 + real_size),
0b0111 => (GlobalItem::ReportSize(value).into(), 1 + real_size),
0b1000 => (GlobalItem::RepordId(value).into(), 1 + real_size),
0b1001 => (GlobalItem::ReportCount(value).into(), 1 + real_size),
0b1010 => (GlobalItem::Push(value).into(), 1 + real_size),
0b1011 => (GlobalItem::Pop(value).into(), 1 + real_size),
_ => return None,
}
}
(tag, 0b10) => {
let real_size = Self::size(size) as usize;
let mut value_bytes = [0u8; 4];
if real_size > 0 {
value_bytes[..real_size].copy_from_slice(&bytes[..real_size])
};
let value = u32::from_le_bytes(value_bytes);
match tag {
0b0000 => (LocalItem::Usage(value).into(), 1 + real_size),
0b0001 => (LocalItem::UsageMinimum(value).into(), 1 + real_size),
0b0010 => (LocalItem::UsageMaximum(value).into(), 1 + real_size),
0b0011 => (LocalItem::DesignatorIndex(value).into(), 1 + real_size),
0b0100 => (LocalItem::DesignatorMinimum(value).into(), 1 + real_size),
0b0101 => (LocalItem::DesignatorMaximum(value).into(), 1 + real_size),
0b0111 => (LocalItem::StringIndex(value).into(), 1 + real_size),
0b1000 => (LocalItem::StringMinimum(value).into(), 1 + real_size),
0b1001 => (LocalItem::StringMaximum(value).into(), 1 + real_size),
0b1010 => (LocalItem::Delimeter(value).into(), 1 + real_size),
_ => return None,
}
}
(_, 0b11) => panic!("Calling parse_short but with long item"),
_ => unreachable!(),
})
}
fn parse_long(size: u8, long_tag: u8, bytes: &[u8]) -> (Self, usize) {
todo!()
}
}
struct ReportIter<'a> {
desc: &'a [u8],
offset: usize,
}
impl<'a> ReportIter<'a> {
fn new(desc: &'a [u8]) -> Self {
Self { desc, offset: 0 }
}
}
impl<'a> Iterator for ReportIter<'a> {
type Item = ReportItem;
fn next(&mut self) -> Option<Self::Item> {
let first = self.desc[self.offset];
let size = first & 0b11;
let ty = first & 0b1100 >> 2;
let tag = first & 0b11110000 >> 4;
if size == 0b10 && ty == 3 && tag == 0b1111 {
// Long Item
let size = self.desc[self.offset + 1];
let long_tag = self.desc[self.offset + 2];
let data = &self.desc[self.offset + 2..self.offset + 2 + size as usize];
let (item, len) = ReportItem::parse_long(size, long_tag, data);
self.offset += len;
Some(item)
} else {
// Short Item
// Although there is a 2-bit size field, the size doesn't have to be the actual size of the data.
let data = &self.desc[self.offset + 1..];
let (item, len) =
ReportItem::parse_short(u2::new(size), u2::new(ty), u4::new(tag), data)?;
self.offset += len;
Some(item)
}
}
}
fn main() {
let mut args = env::args().skip(1);
const USAGE: &'static str = "usbhidd <scheme> <port> <protocol>";
let scheme = args.next().expect(USAGE);
let port = args
.next()
.expect(USAGE)
.parse::<usize>()
.expect("Expected integer as input of port");
let protocol = args.next().expect(USAGE);
println!(
"USB HID driver spawned with scheme `{}`, port {}, protocol {}",
scheme, port, protocol
);
let handle = XhciClientHandle::new(scheme, port);
let dev_desc: DevDesc = handle
.get_standard_descs()
.expect("Failed to get standard descriptors");
let hid_desc = dev_desc.config_descs[0].interface_descs[0].hid_descs[0];
// TODO: Currently it's assumed that config 0 and interface 0 are used.
let interface_num = 0;
let report_desc_len = hid_desc.desc_len;
assert_eq!(hid_desc.desc_ty, REPORT_DESC_TY);
let report_desc_bytes: Vec<u8> = handle
.get_class_descriptor(
u16::from(REPORT_DESC_TY) << 8,
interface_num,
report_desc_len,
)
.expect("Failed to retrieve report descriptor");
use std::io::Write as _;
std::io::stdout()
.write_all(base64::encode(&report_desc_bytes).as_bytes())
.unwrap();
let iterator = ReportIter::new(&report_desc_bytes);
for item in iterator {
println!("HID ITEM {:?}", item);
}
}
+2
View File
@@ -3,7 +3,9 @@ name = "usbscsid"
version = "0.1.0"
authors = ["4lDO2 <4lDO2@protonmail.com>"]
edition = "2018"
license = "MIT"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
xhcid = { path = "../xhcid" }
+1
View File
@@ -21,4 +21,5 @@ redox_syscall = { git = "https://gitlab.redox-os.org/4lDO2/syscall.git", branch
serde = { version = "1", features = ["derive"] }
serde_json = "1"
smallvec = { version = "1", features = ["serde"] }
thiserror = "1"
toml = "0.5"
+6
View File
@@ -3,3 +3,9 @@ name = "SCSI over USB"
class = 8 # Mass Storage class
subclass = 6 # SCSI transparent command set
command = ["/bin/usbscsid", "$SCHEME", "$PORT", "$IF_PROTO"]
[[drivers]]
name = "USB HID"
class = 3 # HID class
subclass = -1
command = ["/bin/usbhidd", "$SCHEME", "$PORT", "$IF_PROTO"]
+202
View File
@@ -1,9 +1,14 @@
pub extern crate serde;
pub extern crate smallvec;
use std::fs::File;
use std::io::prelude::*;
use std::{io, result, str};
use serde::{Deserialize, Serialize};
use smallvec::SmallVec;
use syscall::{Error, Result, EINVAL};
use thiserror::Error;
pub use crate::usb::{EndpointTy, ENDP_ATTR_TY_MASK};
@@ -100,6 +105,31 @@ impl EndpDesc {
_ => return Err(Error::new(EINVAL)),
})
}
pub fn max_streams(&self) -> u8 {
self.ssc
.as_ref()
.map(|ssc| {
if self.is_bulk() {
1 << (ssc.attributes & 0x1F)
} else {
0
}
})
.unwrap_or(0)
}
pub fn isoch_mult(&self, lec: bool) -> u8 {
if !lec && self.is_isoch() {
self.ssc
.as_ref()
.map(|ssc| ssc.attributes & 0x3)
.unwrap_or(0)
} else {
0
}
}
pub fn max_burst(&self) -> u8 {
self.ssc.map(|ssc| ssc.max_burst).unwrap_or(0)
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct IfDesc {
@@ -133,3 +163,175 @@ pub struct HidDesc {
pub optional_desc_ty: u8,
pub optional_desc_len: u16,
}
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
pub struct PortReq<'a> {
pub direction: &'a str,
pub req_type: &'a str,
pub req_recipient: &'a str,
pub request: u8,
pub value: u16,
pub index: u16,
pub length: u16,
pub transfers_data: bool,
}
#[derive(Debug)]
pub struct XhciClientHandle {
scheme: String,
port: usize,
}
#[repr(u8)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum PortState {
EnabledOrDisabled,
Default,
Addressed,
Configured,
}
impl PortState {
pub fn as_str(&self) -> &'static str {
match self {
Self::EnabledOrDisabled => "enabled_or_disabled",
Self::Default => "default",
Self::Addressed => "addressed",
Self::Configured => "configured",
}
}
}
#[derive(Debug, Error)]
#[error("invalid input")]
pub struct Invalid;
impl str::FromStr for PortState {
type Err = Invalid;
fn from_str(s: &str) -> result::Result<Self, Self::Err> {
Ok(match s {
"enabled_or_disabled" | "enabled/disabled" => Self::EnabledOrDisabled,
"default" => Self::Default,
"addressed" => Self::Addressed,
"configured" => Self::Configured,
_ => return Err(Invalid),
})
}
}
#[repr(u8)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum EndpointStatus {
Disabled,
Enabled,
Halted,
Stopped,
Error,
}
impl EndpointStatus {
pub fn as_str(&self) -> &'static str {
match self {
Self::Disabled => "disabled",
Self::Enabled => "enabled",
Self::Halted => "halted",
Self::Stopped => "stopped",
Self::Error => "error",
}
}
}
impl str::FromStr for EndpointStatus {
type Err = Invalid;
fn from_str(s: &str) -> result::Result<Self, Self::Err> {
Ok(match s {
"disabled" => Self::Disabled,
"enabled" => Self::Enabled,
"halted" => Self::Halted,
"stopped" => Self::Stopped,
"error" => Self::Error,
_ => return Err(Invalid),
})
}
}
impl XhciClientHandle {
pub fn new(scheme: String, port: usize) -> Self {
Self { scheme, port }
}
pub fn get_standard_descs(&self) -> result::Result<DevDesc, XhciClientHandleError> {
let path = format!("{}:port{}/descriptors", self.scheme, self.port);
let json = std::fs::read(path)?;
std::io::stdout().lock().write_all(&json)?;
Ok(serde_json::from_slice(&json)?)
}
pub fn configure_endpoints(
&self,
req: &ConfigureEndpointsReq,
) -> result::Result<(), XhciClientHandleError> {
let path = format!("{}:port{}/configure_endpoints", self.scheme, self.port);
serde_json::to_writer(File::open(path)?, req)?;
Ok(())
}
pub fn port_state(&self) -> result::Result<PortState, XhciClientHandleError> {
let path = format!("{}:port{}/state", self.scheme, self.port);
let string = std::fs::read_to_string(path)?;
Ok(string.parse()?)
}
pub fn endpoint_status(
&self,
num: u8,
) -> result::Result<EndpointStatus, XhciClientHandleError> {
let path = format!("{}:port{}/endpoints/{}/status", self.scheme, self.port, num);
let string = std::fs::read_to_string(path)?;
Ok(string.parse()?)
}
// TODO: Device-specific request, with data
pub fn get_class_descriptor(
&self,
value: u16,
index: u16,
length: u16,
) -> result::Result<Vec<u8>, XhciClientHandleError> {
// TODO: Base this on the to-be-written generic device request function.
let req = PortReq {
direction: "device_to_host",
req_type: "standard", // TODO: Add as a parameter, with its own enum.
req_recipient: "interface",
request: 0x06,
value,
index,
length,
transfers_data: true,
};
let json = serde_json::to_vec(&req)?;
let path = format!("{}:port{}/request", self.scheme, self.port);
let mut file = File::open(path)?;
let bytes_written = file.write(&json)?;
if bytes_written != json.len() {
return Err(XhciClientHandleError::InvalidResponse(Invalid));
}
let mut buf = vec![0u8; length as usize];
let bytes_read = file.read(&mut buf)?;
if bytes_read != buf.len() {
println!("HIDd B");
return Err(XhciClientHandleError::InvalidResponse(Invalid));
}
Ok(buf)
}
}
#[derive(Debug, Error)]
pub enum XhciClientHandleError {
#[error("i/o error: {0}")]
IoError(#[from] io::Error),
#[error("serialization error: {0}")]
SerializationError(#[from] serde_json::Error),
#[error("invalid response")]
InvalidResponse(#[from] Invalid),
}
+13
View File
@@ -12,3 +12,16 @@ pub struct ConfigDescriptor {
}
unsafe impl plain::Plain for ConfigDescriptor {}
#[repr(packed)]
#[derive(Clone, Copy, Debug, Default)]
pub struct OtherSpeedConfig {
pub length: u8,
pub kind: u8,
pub total_length: u16,
pub interfaces: u8,
pub configuration_value: u8,
pub configuration_str: u8,
pub attributes: u8,
pub max_power: u8,
}
+15
View File
@@ -27,3 +27,18 @@ impl DeviceDescriptor {
((self.usb >> 8) & 0xFF) as u8
}
}
#[repr(packed)]
pub struct DeviceQualifier {
pub length: u8,
pub kind: u8,
pub usb: u16,
pub class: u8,
pub sub_class: u8,
pub protocol: u8,
pub pkgsz_other_speed: u8,
pub num_other_speed_cfgs: u8,
pub _rsvd: u8,
}
unsafe impl plain::Plain for DeviceQualifier {}
+10
View File
@@ -0,0 +1,10 @@
use syscall::Mmio;
#[repr(packed)]
pub struct SupportedProtoCap {
a: Mmio<u32>,
b: Mmio<u32>,
c: Mmio<u32>,
d: Mmio<u32>,
protocols: [Mmio<u32>],
}
+15 -7
View File
@@ -1,5 +1,5 @@
use std::collections::BTreeMap;
use std::ffi::OsStr;
use std::convert::TryFrom;
use std::pin::Pin;
use std::sync::{atomic::AtomicBool, Arc, Mutex, Weak};
use std::{mem, process, slice, sync::atomic, task};
@@ -15,6 +15,7 @@ mod command;
mod context;
mod doorbell;
mod event;
mod extended;
mod operational;
mod port;
mod ring;
@@ -450,11 +451,13 @@ impl Xhci {
.ok_or(Error::new(EBADF))?;
let drivers_usercfg: &DriversConfig = &DRIVERS_CONFIG;
if let Some(driver) = drivers_usercfg
.drivers
.iter()
.find(|driver| driver.class == ifdesc.class && driver.subclass == ifdesc.sub_class)
{
if let Some(driver) = drivers_usercfg.drivers.iter().find(|driver| {
driver.class == ifdesc.class
&& driver
.subclass()
.map(|subclass| subclass == ifdesc.sub_class)
.unwrap_or(true)
}) {
println!("Loading driver \"{}\"", driver.name);
let (command, args) = driver.command.split_first().ok_or(Error::new(EBADMSG))?;
@@ -485,9 +488,14 @@ impl Xhci {
struct DriverConfig {
name: String,
class: u8,
subclass: u8,
subclass: i16, // The subclass may be meaningless for some drivers, hence negative values (and values above 255) mean "undefined".
command: Vec<String>,
}
impl DriverConfig {
fn subclass(&self) -> Option<u8> {
u8::try_from(self.subclass).ok()
}
}
#[derive(Deserialize)]
struct DriversConfig {
drivers: Vec<DriverConfig>,
+119 -83
View File
@@ -3,14 +3,14 @@ use std::io::prelude::*;
use std::{cmp, mem, path, str};
use serde::{Deserialize, Serialize};
use smallvec::SmallVec;
use smallvec::{smallvec, SmallVec};
use syscall::io::{Dma, Io};
use syscall::scheme::SchemeMut;
use syscall::{
Error, Result, Stat, EACCES, EBADF, EBADMSG, EEXIST, EINVAL, EIO, EISDIR, ENOENT, ENOSYS,
ENOTDIR, ENXIO, ESPIPE, MODE_CHR, MODE_DIR, MODE_FILE, O_CREAT, O_DIRECTORY, O_RDONLY, O_RDWR,
O_STAT, O_WRONLY, SEEK_CUR, SEEK_END, SEEK_SET,
ENOTDIR, ENXIO, EOVERFLOW, ESPIPE, MODE_CHR, MODE_DIR, MODE_FILE, O_CREAT, O_DIRECTORY,
O_RDONLY, O_RDWR, O_STAT, O_WRONLY, SEEK_CUR, SEEK_END, SEEK_SET,
};
use super::{port, usb};
@@ -42,12 +42,17 @@ pub enum EndpointHandleTy {
Root(usize, Vec<u8>), // offset, content
}
pub enum PortReqState {
Init,
Ready(TransferKind, Dma<[u8]>), // direction, buffers
}
pub enum Handle {
TopLevel(usize, Vec<u8>), // offset, contents (ports)
Port(usize, usize, Vec<u8>), // port, offset, contents
PortDesc(usize, usize, Vec<u8>), // port, offset, contents
PortState(usize, usize), // port, offset
PortReq(usize), // port
PortReq(usize, PortReqState), // port, state
Endpoints(usize, usize, Vec<u8>), // port, offset, contents
Endpoint(usize, u8, EndpointHandleTy), // port, endpoint, offset, state
ConfigureEndpoints(usize), // port
@@ -229,8 +234,6 @@ impl Xhci {
const CONTEXT_ENTRIES_SHIFT: u8 = 27;
let current_slot_a = input_context.device.slot.a.read();
let current_context_entries =
((current_slot_a & CONTEXT_ENTRIES_MASK) >> CONTEXT_ENTRIES_SHIFT) as u8;
let endpoints =
&port_state.dev_desc.config_descs[req.config_desc].interface_descs[0].endpoints;
@@ -261,18 +264,8 @@ impl Xhci {
.ok_or(Error::new(EIO))?;
let endp_desc = endpoints.get(index as usize).ok_or(Error::new(EIO))?;
let superspeed_companion = endp_desc.ssc;
// TODO: Check if streams are actually supported.
let max_streams = superspeed_companion
.map(|ssc| {
if endp_desc.is_bulk() {
1 << (ssc.attributes & 0x1F)
} else {
0
}
})
.unwrap_or(0);
let max_streams = endp_desc.max_streams();
let max_psa_size = self.cap.max_psa_size();
// TODO: Secondary streams.
@@ -291,18 +284,10 @@ impl Xhci {
// I presume that USB 3 devices can never be in low/full/high-speed mode, but
// always SuperSpeed (gen 1 and 2 etc.).
let max_burst_size = superspeed_companion.map(|ssc| ssc.max_burst).unwrap_or(0);
let max_burst_size = endp_desc.max_burst();
let max_packet_size = endp_desc.max_packet_size;
let mult = if !lec && endp_desc.is_isoch() {
if let Some(ssc) = superspeed_companion {
ssc.attributes & 0x3
} else {
0
}
} else {
0
};
let mult = endp_desc.isoch_mult(lec);
let interval = endp_desc.interval;
let max_error_count = 3;
@@ -378,8 +363,6 @@ impl Xhci {
endp_ctx.c.write(u32::from(avg_trb_len));
}
input_context.dump_control();
self.run.ints[0].erdp.write(self.cmd.erdp());
{
@@ -571,14 +554,13 @@ impl Xhci {
config_descs,
})
}
fn write_port_desc(&mut self, port_id: usize, contents: &mut Vec<u8>) -> Result<()> {
fn port_desc_json(&mut self, port_id: usize) -> Result<Vec<u8>> {
let dev_desc = &self
.port_states
.get(&port_id)
.ok_or(Error::new(ENOENT))?
.dev_desc;
serde_json::to_writer_pretty(contents, dev_desc).unwrap();
Ok(())
serde_json::to_vec(dev_desc).or(Err(Error::new(EIO)))
}
fn write_dyn_string(string: &[u8], buf: &mut [u8], offset: &mut usize) -> usize {
let max_bytes_to_read = cmp::min(string.len(), buf.len());
@@ -589,35 +571,29 @@ impl Xhci {
bytes_to_read
}
fn port_req(&mut self, port_num: usize, buf: &[u8]) -> Result<()> {
fn port_req(&mut self, fd: usize, 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.
// FIXME: Make sure there aren't any other PortReq handles, perhaps by storing the state in
// PortState?
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)),
"standard" => ReqType::Standard,
_ => return Err(Error::new(EBADMSG)),
} as u8;
let recipient = match req.req_recipient {
@@ -629,8 +605,14 @@ impl Xhci {
_ => return Err(Error::new(EBADMSG)),
} as u8;
let transfer_kind = match direction {
_ if !req.transfers_data => TransferKind::NoData,
ReqDirection::DeviceToHost => TransferKind::In,
ReqDirection::HostToDevice => TransferKind::Out,
};
let setup = Setup {
kind: (direction << USB_SETUP_DIR_SHIFT)
kind: ((direction as u8) << USB_SETUP_DIR_SHIFT)
| (ty << USB_SETUP_REQ_TY_SHIFT)
| (recipient << USB_SETUP_RECIPIENT_SHIFT),
request: req.request,
@@ -643,6 +625,7 @@ impl Xhci {
.port_states
.get_mut(&port_num)
.ok_or(Error::new(EBADF))?;
let ring = match port_state
.endpoint_states
.get_mut(&0)
@@ -654,17 +637,32 @@ impl Xhci {
_ => return Err(Error::new(EIO)),
};
let mut buffer = None;
{
let (cmd, cycle) = ring.next();
cmd.setup(
setup,
TransferKind::NoData, // FIXME
cmd.setup(setup, transfer_kind, cycle);
}
if transfer_kind != TransferKind::NoData {
let (cmd, cycle) = ring.next();
// TODO: Reuse buffers, or something.
// TODO: Validate the size.
// TODO: Sizes above 65536, *perhaps*.
let data_buffer = unsafe { Dma::<[u8]>::zeroed_unsized(req.length as usize)? };
assert_eq!(data_buffer.len(), req.length as usize);
cmd.data(
data_buffer.physical(),
req.length,
transfer_kind == TransferKind::In,
cycle,
);
buffer = Some(data_buffer);
}
{
let (cmd, cycle) = ring.next();
cmd.status(false, cycle);
cmd.status(transfer_kind == TransferKind::In, cycle);
}
self.dbs[port_state.slot as usize].write(1);
@@ -673,10 +671,27 @@ impl Xhci {
while event.data.read() == 0 {
println!(" - Waiting for event");
}
if event.completion_code() != TrbCompletionCode::Success as u8
|| event.trb_type() != TrbType::Transfer as u8
{
println!(
"Custom device request failed with EVENT {:#0x} {:#0x} {:#0x}",
event.data.read(),
event.status.read(),
event.control.read()
);
}
}
self.run.ints[0].erdp.write(self.cmd.erdp());
match self.handles.get_mut(&fd).ok_or(Error::new(EBADF))? {
Handle::PortReq(_, ref mut st) if buffer.is_some() => {
*st = PortReqState::Ready(transfer_kind, buffer.unwrap())
}
_ => return Err(Error::new(EBADF)),
}
Ok(())
}
}
@@ -733,9 +748,7 @@ impl SchemeMut for Xhci {
return Err(Error::new(ENOTDIR));
}
let mut contents = Vec::new();
self.write_port_desc(port_num, &mut contents)?;
let contents = self.port_desc_json(port_num)?;
Handle::PortDesc(port_num, 0, contents)
}
"configure" => {
@@ -759,10 +772,7 @@ impl SchemeMut for Xhci {
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)
Handle::PortReq(port_num, PortReqState::Init)
}
"endpoints" => {
if flags & O_DIRECTORY == 0 && flags & O_STAT == 0 {
@@ -852,12 +862,12 @@ impl SchemeMut for Xhci {
write!(contents, "descriptors\nendpoints\n").unwrap();
if dbg!(self.slot_state(
if self.slot_state(
self.port_states
.get(&port_num)
.ok_or(Error::new(ENOENT))?
.slot as usize
)) != SlotState::Configured as u8
.slot as usize,
) != SlotState::Configured as u8
{
write!(contents, "configure\n").unwrap();
}
@@ -889,7 +899,12 @@ impl SchemeMut for Xhci {
stat.st_mode = MODE_FILE;
stat.st_size = buf.len() as u64;
}
Handle::PortState(_, _) => stat.st_mode = MODE_CHR,
Handle::PortReq(_, PortReqState::Ready(TransferKind::In, ref buf)) => {
stat.st_mode = MODE_CHR;
stat.st_size = buf.len() as u64;
}
Handle::PortState(_, _) | Handle::PortReq(_, _) => stat.st_mode = MODE_CHR,
Handle::Endpoint(_, _, st) => match st {
EndpointHandleTy::Status(_) | EndpointHandleTy::Transfer => stat.st_mode = MODE_CHR,
EndpointHandleTy::Root(_, ref buf) => {
@@ -897,7 +912,7 @@ impl SchemeMut for Xhci {
stat.st_size = buf.len() as u64;
}
},
Handle::ConfigureEndpoints(_) | Handle::PortReq(_) => {
Handle::ConfigureEndpoints(_) => {
stat.st_mode = MODE_CHR | 0o200; // write only
}
}
@@ -914,7 +929,7 @@ impl SchemeMut for Xhci {
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::PortReq(port_num, _) => write!(src, "/port{}/request", port_num).unwrap(),
Handle::Endpoints(port_num, _, _) => {
write!(src, "/port{}/endpoints/", port_num).unwrap()
}
@@ -967,7 +982,7 @@ impl SchemeMut for Xhci {
Ok(*offset)
}
// Write-once configure or transfer
Handle::Endpoint(_, _, _) | Handle::ConfigureEndpoints(_) | Handle::PortReq(_) => {
Handle::Endpoint(_, _, _) | Handle::ConfigureEndpoints(_) | Handle::PortReq(_, _) => {
return Err(Error::new(ESPIPE))
}
}
@@ -988,7 +1003,7 @@ impl SchemeMut for Xhci {
Ok(bytes_to_read)
}
Handle::ConfigureEndpoints(_) | Handle::PortReq(_) => return Err(Error::new(EBADF)),
Handle::ConfigureEndpoints(_) => return Err(Error::new(EBADF)),
&mut Handle::Endpoint(port_num, endp_num, ref mut st) => match st {
EndpointHandleTy::Transfer => {
@@ -1012,14 +1027,16 @@ impl SchemeMut for Xhci {
& ENDPOINT_CONTEXT_STATUS_MASK;
let string = match status {
// TODO: Give this its own enum.
0 => "disabled",
1 => "enabled",
2 => "halted",
3 => "stopped",
4 => "error",
_ => "unknown",
0 => Some(EndpointStatus::Disabled),
1 => Some(EndpointStatus::Enabled),
2 => Some(EndpointStatus::Halted),
3 => Some(EndpointStatus::Stopped),
4 => Some(EndpointStatus::Error),
_ => None,
}
.as_ref()
.map(EndpointStatus::as_str)
.unwrap_or("unknown")
.as_bytes();
Ok(Self::write_dyn_string(string, buf, offset))
@@ -1037,34 +1054,53 @@ impl SchemeMut for Xhci {
.state();
let string = match state {
// TODO: Give this its own enum.
0 => "enabled_or_disabled", // Maybe "enabled/disabled"?
1 => "default",
2 => "addressed",
3 => "configured",
_ => "unknown",
0 => Some(PortState::EnabledOrDisabled),
1 => Some(PortState::Default),
2 => Some(PortState::Addressed),
3 => Some(PortState::Configured),
_ => None,
}
.as_ref()
.map(PortState::as_str)
.unwrap_or("unknown")
.as_bytes();
Ok(Self::write_dyn_string(string, buf, offset))
}
Handle::PortReq(_, st) => {
if let PortReqState::Ready(TransferKind::In, ref src_buf) = st {
if buf.len() != src_buf.len() {
return Err(Error::new(EINVAL));
}
buf.copy_from_slice(src_buf);
Ok(buf.len())
} else {
Err(Error::new(EBADF))
}
}
}
}
fn write(&mut self, fd: usize, buf: &[u8]) -> Result<usize> {
match self.handles.get(&fd).ok_or(Error::new(EBADF))? {
&Handle::ConfigureEndpoints(port_num) => {
match self.handles.get_mut(&fd).ok_or(Error::new(EBADF))? {
&mut Handle::ConfigureEndpoints(port_num) => {
self.configure_endpoints(port_num, buf)?;
Ok(buf.len())
}
&Handle::Endpoint(port_num, endp_num, EndpointHandleTy::Transfer) => {
&mut Handle::Endpoint(port_num, endp_num, EndpointHandleTy::Transfer) => {
self.transfer_write(port_num, endp_num, buf)?;
// TODO
Ok(buf.len())
}
&Handle::PortReq(port_num) => {
self.port_req(port_num, buf)?;
&mut Handle::PortReq(port_num, PortReqState::Init) => {
self.port_req(fd, port_num, buf)?;
Ok(buf.len())
}
// TODO: Introduce PortReqState::Waiting, which this write call changes to
// PortReqState::ReadyToWrite when all bytes are written.
&mut Handle::PortReq(
port_num,
PortReqState::Ready(TransferKind::Out, ref mut src_buf),
) => return Err(Error::new(ENOSYS)),
_ => return Err(Error::new(EBADF)),
}
}
+1
View File
@@ -96,6 +96,7 @@ pub enum TrbCompletionCode {
}
#[repr(u8)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum TransferKind {
NoData,
Reserved,