Add functions for the HID-specific requests.

This commit is contained in:
4lDO2
2020-02-08 12:28:17 +01:00
parent 53ddfbb88e
commit 3fd4bc4b3b
7 changed files with 262 additions and 64 deletions
Generated
-7
View File
@@ -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
View File
@@ -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");
+17 -5
View File
@@ -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)),
}
}
}
+111
View File
@@ -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,
)
}
+89 -22
View File
@@ -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),
}
+29
View File
@@ -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;
+6 -23
View File
@@ -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,