Add a basic HID driver unable to parse report descs.
Custom device requests are now implemented though.
This commit is contained in:
Generated
+46
@@ -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"
|
||||
|
||||
@@ -15,5 +15,6 @@ members = [
|
||||
"vboxd",
|
||||
"vesad",
|
||||
"xhcid",
|
||||
"usbhidd",
|
||||
"usbscsid",
|
||||
]
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
/target
|
||||
@@ -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" }
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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" }
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"]
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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 {}
|
||||
|
||||
@@ -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
@@ -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
@@ -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)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,6 +96,7 @@ pub enum TrbCompletionCode {
|
||||
}
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum TransferKind {
|
||||
NoData,
|
||||
Reserved,
|
||||
|
||||
Reference in New Issue
Block a user