Add functions for the HID-specific requests.
This commit is contained in:
Generated
-7
@@ -53,11 +53,6 @@ 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"
|
||||
@@ -1332,7 +1327,6 @@ dependencies = [
|
||||
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",
|
||||
@@ -1473,7 +1467,6 @@ 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"
|
||||
|
||||
+10
-7
@@ -1,11 +1,11 @@
|
||||
use std::convert::TryInto;
|
||||
use std::env;
|
||||
|
||||
use xhcid_interface::{DevDesc, XhciClientHandle};
|
||||
use xhcid_interface::{DevDesc, PortReqRecipient, XhciClientHandle};
|
||||
|
||||
mod report_desc;
|
||||
mod reqs;
|
||||
|
||||
use report_desc::{ReportIter, ReportFlatIter, REPORT_DESC_TY};
|
||||
use report_desc::{ReportFlatIter, ReportIter, REPORT_DESC_TY};
|
||||
|
||||
fn main() {
|
||||
let mut args = env::args().skip(1);
|
||||
@@ -37,11 +37,14 @@ fn main() {
|
||||
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,
|
||||
let mut report_desc_bytes = vec![0u8; report_desc_len as usize];
|
||||
handle
|
||||
.get_descriptor(
|
||||
PortReqRecipient::Interface,
|
||||
REPORT_DESC_TY,
|
||||
0,
|
||||
interface_num,
|
||||
report_desc_len,
|
||||
&mut report_desc_bytes,
|
||||
)
|
||||
.expect("Failed to retrieve report descriptor");
|
||||
|
||||
|
||||
@@ -185,7 +185,9 @@ impl<'a> Iterator for ReportFlatIter<'a> {
|
||||
type Item = ReportItem;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.offset >= self.desc.len() { return None }
|
||||
if self.offset >= self.desc.len() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let first = self.desc[self.offset];
|
||||
let size = first & 0b11;
|
||||
@@ -219,6 +221,9 @@ pub struct ReportIter<'a> {
|
||||
flat: ReportFlatIter<'a>,
|
||||
error: bool,
|
||||
// this is just for reusing the vec
|
||||
// TODO: When GATs are available, this could be done simply using iterators. Every collection
|
||||
// yields a child iterator, which returns the mutable reference to the flat iter to its parent
|
||||
// when dropped.
|
||||
open_collections: Vec<(u8, Vec<ReportIterItem>)>,
|
||||
}
|
||||
#[derive(Debug)]
|
||||
@@ -240,7 +245,9 @@ impl<'a> Iterator for ReportIter<'a> {
|
||||
type Item = ReportIterItem;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.error { return None }
|
||||
if self.error {
|
||||
return None;
|
||||
}
|
||||
|
||||
self.open_collections.clear();
|
||||
|
||||
@@ -265,10 +272,15 @@ impl<'a> Iterator for ReportIter<'a> {
|
||||
return Some(ReportIterItem::Collection(value, finished_collection));
|
||||
}
|
||||
}
|
||||
other if self.open_collections.is_empty() => return Some(ReportIterItem::Item(other)),
|
||||
other => {
|
||||
self.open_collections.last_mut().unwrap().1.push(ReportIterItem::Item(other))
|
||||
other if self.open_collections.is_empty() => {
|
||||
return Some(ReportIterItem::Item(other))
|
||||
}
|
||||
other => self
|
||||
.open_collections
|
||||
.last_mut()
|
||||
.unwrap()
|
||||
.1
|
||||
.push(ReportIterItem::Item(other)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
use std::slice;
|
||||
|
||||
use xhcid_interface::{
|
||||
DeviceReqData, PortReqRecipient, PortReqTy, XhciClientHandle, XhciClientHandleError,
|
||||
};
|
||||
|
||||
const GET_REPORT_REQ: u8 = 0x1;
|
||||
const SET_REPORT_REQ: u8 = 0x9;
|
||||
const GET_IDLE_REQ: u8 = 0x2;
|
||||
const SET_IDLE_REQ: u8 = 0xA;
|
||||
const GET_PROTOCOL_REQ: u8 = 0x3;
|
||||
const SET_PROTOCOL_REQ: u8 = 0xB;
|
||||
|
||||
fn concat(hi: u8, lo: u8) -> u16 {
|
||||
(u16::from(hi) << 8) | u16::from(lo)
|
||||
}
|
||||
|
||||
pub fn get_report(
|
||||
handle: &mut XhciClientHandle,
|
||||
report_ty: u8,
|
||||
report_id: u8,
|
||||
if_num: u16,
|
||||
buffer: &mut [u8],
|
||||
) -> Result<(), XhciClientHandleError> {
|
||||
handle.device_request(
|
||||
PortReqTy::Class,
|
||||
PortReqRecipient::Interface,
|
||||
GET_REPORT_REQ,
|
||||
concat(report_ty, report_id),
|
||||
if_num,
|
||||
DeviceReqData::In(buffer),
|
||||
)
|
||||
}
|
||||
pub fn set_report(
|
||||
handle: &mut XhciClientHandle,
|
||||
report_ty: u8,
|
||||
report_id: u8,
|
||||
if_num: u16,
|
||||
buffer: &[u8],
|
||||
) -> Result<(), XhciClientHandleError> {
|
||||
handle.device_request(
|
||||
PortReqTy::Class,
|
||||
PortReqRecipient::Interface,
|
||||
SET_REPORT_REQ,
|
||||
concat(report_id, report_ty),
|
||||
if_num,
|
||||
DeviceReqData::Out(buffer),
|
||||
)
|
||||
}
|
||||
pub fn get_idle(
|
||||
handle: &mut XhciClientHandle,
|
||||
report_id: u8,
|
||||
if_num: u16,
|
||||
) -> Result<u8, XhciClientHandleError> {
|
||||
let mut idle_rate = 0;
|
||||
let buffer = slice::from_mut(&mut idle_rate);
|
||||
handle.device_request(
|
||||
PortReqTy::Class,
|
||||
PortReqRecipient::Interface,
|
||||
GET_IDLE_REQ,
|
||||
u16::from(report_id),
|
||||
if_num,
|
||||
DeviceReqData::In(buffer),
|
||||
)?;
|
||||
Ok(idle_rate)
|
||||
}
|
||||
pub fn set_idle(
|
||||
handle: &mut XhciClientHandle,
|
||||
duration: u8,
|
||||
report_id: u8,
|
||||
if_num: u16,
|
||||
) -> Result<(), XhciClientHandleError> {
|
||||
handle.device_request(
|
||||
PortReqTy::Class,
|
||||
PortReqRecipient::Interface,
|
||||
SET_IDLE_REQ,
|
||||
concat(duration, report_id),
|
||||
if_num,
|
||||
DeviceReqData::NoData,
|
||||
)
|
||||
}
|
||||
pub fn get_protocol(
|
||||
handle: &mut XhciClientHandle,
|
||||
if_num: u16,
|
||||
) -> Result<u8, XhciClientHandleError> {
|
||||
let mut protocol = 0;
|
||||
let buffer = slice::from_mut(&mut protocol);
|
||||
handle.device_request(
|
||||
PortReqTy::Class,
|
||||
PortReqRecipient::Interface,
|
||||
GET_PROTOCOL_REQ,
|
||||
0,
|
||||
if_num,
|
||||
DeviceReqData::In(buffer),
|
||||
)?;
|
||||
Ok(protocol)
|
||||
}
|
||||
pub fn set_protocol(
|
||||
handle: &mut XhciClientHandle,
|
||||
protocol: u8,
|
||||
if_num: u16,
|
||||
) -> Result<(), XhciClientHandleError> {
|
||||
handle.device_request(
|
||||
PortReqTy::Class,
|
||||
PortReqRecipient::Interface,
|
||||
SET_PROTOCOL_REQ,
|
||||
u16::from(protocol),
|
||||
if_num,
|
||||
DeviceReqData::NoData,
|
||||
)
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
pub extern crate serde;
|
||||
pub extern crate smallvec;
|
||||
|
||||
use std::convert::TryFrom;
|
||||
use std::fs::File;
|
||||
use std::io::prelude::*;
|
||||
use std::{io, result, str};
|
||||
@@ -164,16 +165,35 @@ pub struct HidDesc {
|
||||
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 struct PortReq {
|
||||
pub direction: PortReqDirection,
|
||||
pub req_type: PortReqTy,
|
||||
pub req_recipient: PortReqRecipient,
|
||||
pub request: u8,
|
||||
pub value: u16,
|
||||
pub index: u16,
|
||||
pub length: u16,
|
||||
pub transfers_data: bool,
|
||||
}
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
|
||||
pub enum PortReqDirection {
|
||||
HostToDevice,
|
||||
DeviceToHost,
|
||||
}
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
|
||||
pub enum PortReqTy {
|
||||
Class,
|
||||
Vendor,
|
||||
Standard,
|
||||
}
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
|
||||
pub enum PortReqRecipient {
|
||||
Device,
|
||||
Interface,
|
||||
Endpoint,
|
||||
Other,
|
||||
VendorSpecific,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct XhciClientHandle {
|
||||
@@ -253,6 +273,34 @@ impl str::FromStr for EndpointStatus {
|
||||
}
|
||||
}
|
||||
|
||||
pub enum DeviceReqData<'a> {
|
||||
In(&'a mut [u8]),
|
||||
Out(&'a [u8]),
|
||||
NoData,
|
||||
}
|
||||
impl DeviceReqData<'_> {
|
||||
fn len(&self) -> usize {
|
||||
match self {
|
||||
Self::In(buf) => buf.len(),
|
||||
Self::Out(buf) => buf.len(),
|
||||
Self::NoData => 0,
|
||||
}
|
||||
}
|
||||
fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> DeviceReqData<'a> {
|
||||
fn direction(&self) -> PortReqDirection {
|
||||
match self {
|
||||
DeviceReqData::Out(_) => PortReqDirection::HostToDevice,
|
||||
DeviceReqData::NoData => PortReqDirection::HostToDevice,
|
||||
DeviceReqData::In(_) => PortReqDirection::DeviceToHost,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl XhciClientHandle {
|
||||
pub fn new(scheme: String, port: usize) -> Self {
|
||||
Self { scheme, port }
|
||||
@@ -284,19 +332,22 @@ impl XhciClientHandle {
|
||||
let string = std::fs::read_to_string(path)?;
|
||||
Ok(string.parse()?)
|
||||
}
|
||||
// TODO: Device-specific request, with data
|
||||
pub fn get_class_descriptor(
|
||||
pub fn device_request<'a>(
|
||||
&self,
|
||||
req_type: PortReqTy,
|
||||
req_recipient: PortReqRecipient,
|
||||
request: u8,
|
||||
value: u16,
|
||||
index: u16,
|
||||
length: u16,
|
||||
) -> result::Result<Vec<u8>, XhciClientHandleError> {
|
||||
// TODO: Base this on the to-be-written generic device request function.
|
||||
data: DeviceReqData<'a>,
|
||||
) -> result::Result<(), XhciClientHandleError> {
|
||||
let length = u16::try_from(data.len()).or(Err(XhciClientHandleError::TransferBufTooLarge(data.len())))?;
|
||||
|
||||
let req = PortReq {
|
||||
direction: "device_to_host",
|
||||
req_type: "standard", // TODO: Add as a parameter, with its own enum.
|
||||
req_recipient: "interface",
|
||||
request: 0x06,
|
||||
direction: data.direction(),
|
||||
req_type,
|
||||
req_recipient,
|
||||
request,
|
||||
value,
|
||||
index,
|
||||
length,
|
||||
@@ -307,19 +358,32 @@ impl XhciClientHandle {
|
||||
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() {
|
||||
let json_bytes_written = file.write(&json)?;
|
||||
if json_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));
|
||||
}
|
||||
match data {
|
||||
DeviceReqData::In(buf) => {
|
||||
let bytes_read = file.read(buf)?;
|
||||
|
||||
Ok(buf)
|
||||
if bytes_read != buf.len() {
|
||||
return Err(XhciClientHandleError::InvalidResponse(Invalid));
|
||||
}
|
||||
}
|
||||
DeviceReqData::Out(buf) => {
|
||||
let bytes_read = file.write(&buf)?;
|
||||
|
||||
if bytes_read != buf.len() {
|
||||
return Err(XhciClientHandleError::InvalidResponse(Invalid));
|
||||
}
|
||||
}
|
||||
DeviceReqData::NoData => (),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
pub fn get_descriptor(&self, recipient: PortReqRecipient, ty: u8, idx: u8, windex: u16, buffer: &mut [u8]) -> result::Result<(), XhciClientHandleError> {
|
||||
self.device_request(PortReqTy::Standard, recipient, 0x06, (u16::from(ty) << 8) | u16::from(idx), windex, DeviceReqData::In(buffer))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -333,4 +397,7 @@ pub enum XhciClientHandleError {
|
||||
|
||||
#[error("invalid response")]
|
||||
InvalidResponse(#[from] Invalid),
|
||||
|
||||
#[error("transfer buffer too large ({0} > 65536)")]
|
||||
TransferBufTooLarge(usize),
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use super::DescriptorKind;
|
||||
use crate::driver_interface::*;
|
||||
|
||||
#[repr(packed)]
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
@@ -15,6 +16,14 @@ pub enum ReqDirection {
|
||||
HostToDevice = 0,
|
||||
DeviceToHost = 1,
|
||||
}
|
||||
impl From<PortReqDirection> for ReqDirection {
|
||||
fn from(d: PortReqDirection) -> Self {
|
||||
match d {
|
||||
PortReqDirection::DeviceToHost => Self::DeviceToHost,
|
||||
PortReqDirection::HostToDevice => Self::HostToDevice,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(u8)]
|
||||
pub enum ReqType {
|
||||
@@ -31,6 +40,15 @@ pub enum ReqType {
|
||||
/// Reserved
|
||||
Reserved = 3,
|
||||
}
|
||||
impl From<PortReqTy> for ReqType {
|
||||
fn from(d: PortReqTy) -> Self {
|
||||
match d {
|
||||
PortReqTy::Standard => Self::Standard,
|
||||
PortReqTy::Class => Self::Class,
|
||||
PortReqTy::Vendor => Self::Vendor,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(u8)]
|
||||
pub enum ReqRecipient {
|
||||
@@ -41,6 +59,17 @@ pub enum ReqRecipient {
|
||||
// 4..=30 are reserved
|
||||
VendorSpecific = 31,
|
||||
}
|
||||
impl From<PortReqRecipient> for ReqRecipient {
|
||||
fn from(d: PortReqRecipient) -> Self {
|
||||
match d {
|
||||
PortReqRecipient::Device => Self::Device,
|
||||
PortReqRecipient::Interface => Self::Interface,
|
||||
PortReqRecipient::Endpoint => Self::Endpoint,
|
||||
PortReqRecipient::Other => Self::Other,
|
||||
PortReqRecipient::VendorSpecific => Self::VendorSpecific,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub const USB_SETUP_DIR_BIT: u8 = 1 << 7;
|
||||
pub const USB_SETUP_DIR_SHIFT: u8 = 7;
|
||||
|
||||
@@ -576,34 +576,17 @@ impl Xhci {
|
||||
|
||||
// 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.
|
||||
// be better. Maybe something simple like bincode could be used, if a custom binary struct
|
||||
// is too much overkill.
|
||||
|
||||
// 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 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)),
|
||||
};
|
||||
|
||||
let ty = match req.req_type {
|
||||
"class" => ReqType::Class,
|
||||
"vendor" => ReqType::Vendor,
|
||||
"standard" => ReqType::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 direction = ReqDirection::from(req.direction);
|
||||
let ty = ReqType::from(req.req_type) as u8;
|
||||
let recipient = ReqRecipient::from(req.req_recipient) as u8;
|
||||
|
||||
let transfer_kind = match direction {
|
||||
_ if !req.transfers_data => TransferKind::NoData,
|
||||
|
||||
Reference in New Issue
Block a user