diff --git a/Cargo.lock b/Cargo.lock index 3326bb7579..45920958a4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1336,6 +1336,7 @@ dependencies = [ name = "usbscsid" version = "0.1.0" dependencies = [ + "thiserror 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", "xhcid 0.1.0", ] diff --git a/ps2d/.gitignore b/ps2d/.gitignore new file mode 100644 index 0000000000..ea8c4bf7f3 --- /dev/null +++ b/ps2d/.gitignore @@ -0,0 +1 @@ +/target diff --git a/usbhidd/src/main.rs b/usbhidd/src/main.rs index 0f86635770..7f21eb6d9d 100644 --- a/usbhidd/src/main.rs +++ b/usbhidd/src/main.rs @@ -1,11 +1,17 @@ use std::env; -use xhcid_interface::{DevDesc, PortReqRecipient, XhciClientHandle}; +use xhcid_interface::{ConfigureEndpointsReq, DevDesc, PortReqRecipient, XhciClientHandle}; mod report_desc; mod reqs; +mod usage_tables; -use report_desc::{ReportFlatIter, ReportIter, REPORT_DESC_TY}; +use report_desc::{MainCollectionFlags, GlobalItemsState, ReportFlatIter, ReportItem, ReportIter, ReportIterItem, REPORT_DESC_TY}; +use reqs::ReportTy; + +fn div_round_up(num: u32, denom: u32) -> u32 { + if num % denom == 0 { num / denom } else { num / denom + 1 } +} fn main() { let mut args = env::args().skip(1); @@ -48,9 +54,64 @@ fn main() { ) .expect("Failed to retrieve report descriptor"); - let iterator = ReportIter::new(ReportFlatIter::new(&report_desc_bytes)); + let report_desc = ReportIter::new(ReportFlatIter::new(&report_desc_bytes)).collect::>(); - for item in iterator { - println!("HID ITEM {:?}", item); + for item in &report_desc { + println!("{:?}", item); } + + handle.configure_endpoints(&ConfigureEndpointsReq { config_desc: 0 }).expect("Failed to configure endpoints"); + + let (mut state, mut stack) = (GlobalItemsState::default(), Vec::new()); + + let (_, application_collection) = report_desc.iter().inspect(|item: &&ReportIterItem| if let ReportIterItem::Item(ref item) = item { + report_desc::update_state(&mut state, &mut stack, item).unwrap() + }).filter_map(ReportIterItem::as_collection).find(|&(n, _)| n == MainCollectionFlags::Application as u8).expect("Failed to find application collection"); + + // Get all main items, and their global item options. + { + let items = application_collection.iter().filter_map(ReportIterItem::as_item).filter_map(|item| match item { + ReportItem::Global(_) => { + report_desc::update_state(&mut state, &mut stack, item).unwrap(); + None + } + ReportItem::Main(m) => Some((state, m)), + ReportItem::Local(_) => None, + }); + let total_length = items.filter_map(|(state, item)| { + let report_size = match state.report_size { + Some(s) => s, + None => return None, + }; + let report_count = match state.report_count { + Some(c) => c, + None => return None, + }; + let bit_length = report_size * report_count; + + if item.report_ty() != Some(ReportTy::Input) { + return None; + } + Some(bit_length) + }).sum(); + let length = div_round_up(total_length, 8); + + let mut report_buffer = vec! [0u8; length as usize]; + let mut last_buffer = report_buffer.clone(); + let report_ty = ReportTy::Input; + let report_id = 0; + + loop { + std::mem::swap(&mut report_buffer, &mut last_buffer); + reqs::get_report(&handle, report_ty, report_id, interface_num, &mut report_buffer).expect("Failed to get report"); + if report_buffer != last_buffer { + for byte in &report_buffer { + print!("{:#0x} ", byte); + } + println!(); + } + std::thread::sleep(std::time::Duration::from_millis(10)) + } + } + } diff --git a/usbhidd/src/report_desc.rs b/usbhidd/src/report_desc.rs index 85d8526f85..9a0019aaba 100644 --- a/usbhidd/src/report_desc.rs +++ b/usbhidd/src/report_desc.rs @@ -1,6 +1,8 @@ use bitflags::bitflags; use ux::{u2, u4}; +use crate::reqs::ReportTy; + /*#[repr(u8)] enum Protocol { @@ -40,6 +42,16 @@ pub enum MainItem { Collection(u8), EndOfCollection, } +impl MainItem { + pub fn report_ty(&self) -> Option { + match self { + Self::Input(_) => Some(ReportTy::Input), + Self::Output(_) => Some(ReportTy::Output), + Self::Feature(_) => Some(ReportTy::Feature), + _ => None, + } + } +} #[derive(Debug)] pub enum GlobalItem { UsagePage(u32), @@ -50,11 +62,51 @@ pub enum GlobalItem { UnitExponent(u32), Unit(u32), ReportSize(u32), - RepordId(u32), + ReportId(u32), ReportCount(u32), - Push(u32), - Pop(u32), + Push, + Pop, } + +#[derive(Clone, Copy, Debug, Default)] +pub struct GlobalItemsState { + pub usage_page: Option, + pub logical_min: Option, + pub logical_max: Option, + pub physical_min: Option, + pub physical_max: Option, + pub unit_exponent: Option, + pub unit: Option, + pub report_size: Option, + pub report_id: Option, + pub report_count: Option, +} + +#[derive(Debug)] +pub struct Invalid; + +pub fn update_state(current_state: &mut GlobalItemsState, stack: &mut Vec, report_item: &ReportItem) -> Result<(), Invalid> { + match report_item { + ReportItem::Global(global) => match global { + &GlobalItem::UsagePage(u) => current_state.usage_page = Some(u), + &GlobalItem::LogicalMinimum(m) => current_state.logical_min = Some(m), + &GlobalItem::LogicalMaximum(m) => current_state.logical_max = Some(m), + &GlobalItem::PhysicalMinimum(m) => current_state.physical_min = Some(m), + &GlobalItem::PhysicalMaximum(m) => current_state.physical_max = Some(m), + &GlobalItem::UnitExponent(e) => current_state.unit_exponent = Some(e), + &GlobalItem::Unit(u) => current_state.unit = Some(u), + &GlobalItem::ReportSize(s) => current_state.report_size = Some(s), + &GlobalItem::ReportId(i) => current_state.report_id = Some(i), + &GlobalItem::ReportCount(c) => current_state.report_count = Some(c), + &GlobalItem::Push => stack.push(*current_state), + &GlobalItem::Pop => *current_state = stack.pop().ok_or(Invalid)?, + } + ReportItem::Local(local) => (), // TODO + ReportItem::Main(_) => (), + } + Ok(()) +} + #[derive(Debug)] pub enum LocalItem { Usage(u32), @@ -74,6 +126,14 @@ pub enum ReportItem { Global(GlobalItem), Local(LocalItem), } +impl ReportItem { + pub fn as_main_item(&self) -> Option<&MainItem> { + match self { + Self::Main(m) => Some(m), + _ => None, + } + } +} impl From for ReportItem { fn from(main: MainItem) -> Self { Self::Main(main) @@ -135,10 +195,10 @@ impl ReportItem { 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), + 0b1000 => (GlobalItem::ReportId(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), + 0b1010 => (GlobalItem::Push.into(), 1), + 0b1011 => (GlobalItem::Pop.into(), 1), _ => return None, } } @@ -232,6 +292,20 @@ pub enum ReportIterItem { Item(ReportItem), Collection(u8, Vec), } +impl ReportIterItem { + pub fn as_item(&self) -> Option<&ReportItem> { + match self { + Self::Item(i) => Some(i), + _ => None, + } + } + pub fn as_collection(&self) -> Option<(u8, &[ReportIterItem])> { + match self { + &Self::Collection(n, ref c) => Some((n, c)), + _ => None, + } + } +} impl<'a> ReportIter<'a> { pub fn new(flat: ReportFlatIter<'a>) -> Self { Self { diff --git a/usbhidd/src/reqs.rs b/usbhidd/src/reqs.rs index e834474fc2..680497279b 100644 --- a/usbhidd/src/reqs.rs +++ b/usbhidd/src/reqs.rs @@ -11,13 +11,21 @@ const SET_IDLE_REQ: u8 = 0xA; const GET_PROTOCOL_REQ: u8 = 0x3; const SET_PROTOCOL_REQ: u8 = 0xB; +#[repr(u8)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum ReportTy { + Input = 1, + Output, + Feature, +} + fn concat(hi: u8, lo: u8) -> u16 { (u16::from(hi) << 8) | u16::from(lo) } pub fn get_report( - handle: &mut XhciClientHandle, - report_ty: u8, + handle: &XhciClientHandle, + report_ty: ReportTy, report_id: u8, if_num: u16, buffer: &mut [u8], @@ -26,14 +34,14 @@ pub fn get_report( PortReqTy::Class, PortReqRecipient::Interface, GET_REPORT_REQ, - concat(report_ty, report_id), + concat(report_ty as u8, report_id), if_num, DeviceReqData::In(buffer), ) } pub fn set_report( - handle: &mut XhciClientHandle, - report_ty: u8, + handle: &XhciClientHandle, + report_ty: ReportTy, report_id: u8, if_num: u16, buffer: &[u8], @@ -42,13 +50,13 @@ pub fn set_report( PortReqTy::Class, PortReqRecipient::Interface, SET_REPORT_REQ, - concat(report_id, report_ty), + concat(report_id, report_ty as u8), if_num, DeviceReqData::Out(buffer), ) } pub fn get_idle( - handle: &mut XhciClientHandle, + handle: &XhciClientHandle, report_id: u8, if_num: u16, ) -> Result { @@ -65,7 +73,7 @@ pub fn get_idle( Ok(idle_rate) } pub fn set_idle( - handle: &mut XhciClientHandle, + handle: &XhciClientHandle, duration: u8, report_id: u8, if_num: u16, @@ -80,7 +88,7 @@ pub fn set_idle( ) } pub fn get_protocol( - handle: &mut XhciClientHandle, + handle: &XhciClientHandle, if_num: u16, ) -> Result { let mut protocol = 0; @@ -96,7 +104,7 @@ pub fn get_protocol( Ok(protocol) } pub fn set_protocol( - handle: &mut XhciClientHandle, + handle: &XhciClientHandle, protocol: u8, if_num: u16, ) -> Result<(), XhciClientHandleError> { diff --git a/usbhidd/src/usage_tables.rs b/usbhidd/src/usage_tables.rs new file mode 100644 index 0000000000..cf7b8d3424 --- /dev/null +++ b/usbhidd/src/usage_tables.rs @@ -0,0 +1,45 @@ +// See https://www.usb.org/sites/default/files/documents/hut1_12v2.pdf + +#[repr(u8)] +pub enum UsagePage { + GenericDesktop = 1, + SimulationsControl, + VrControls, + SportControls, + GameControls, + GenericDeviceControls, + KeyboardOrKeypad, + Led, + Button, + Ordinal, + TelephonyDevice, + Consumer, + Digitizer, + Unicode = 0x10, + AlphanumericDisplay = 0x14, + MedicalInstrument = 0x40, +} + +#[repr(u8)] +pub enum GenericDesktopUsage { + Pointer = 0x01, + Mouse, + Joystick = 0x04, + GamePad, + Keyboard, + Keypad, + MultiAxisController, + + // 0x0A-0x2F are reserved + + CountedBuffer = 0x3A, + SysControl = 0x80, +} + +#[repr(u8)] +pub enum KeyboardOrKeypadUsage { + KbdErrorRollover = 0x1, + KbdPostFail, + KbdErrorUndefined, + // the rest are used as regular keycodes +} diff --git a/usbscsid/Cargo.toml b/usbscsid/Cargo.toml index 90c80d0779..7b639e5938 100644 --- a/usbscsid/Cargo.toml +++ b/usbscsid/Cargo.toml @@ -8,4 +8,5 @@ license = "MIT" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +thiserror = "1" xhcid = { path = "../xhcid" } diff --git a/usbscsid/src/main.rs b/usbscsid/src/main.rs index f78299fb27..13ceec3a12 100644 --- a/usbscsid/src/main.rs +++ b/usbscsid/src/main.rs @@ -1,5 +1,7 @@ use std::env; +use xhcid_interface::XhciClientHandle; + pub mod protocol; fn main() { @@ -8,8 +10,11 @@ fn main() { const USAGE: &'static str = "usbscsid "; let scheme = args.next().expect(USAGE); - let port = args.next().expect(USAGE); - let protocol = args.next().expect(USAGE); + let port = args.next().expect(USAGE).parse::().expect("port has to be a number"); + let protocol = args.next().expect(USAGE).parse::().expect("protocol has to be a number 0-255"); println!("USB SCSI driver spawned with scheme `{}`, port {}, protocol {}", scheme, port, protocol); + + let handle = XhciClientHandle::new(scheme, port); + let protocol = protocol::setup(&handle, protocol); } diff --git a/usbscsid/src/protocol/bot.rs b/usbscsid/src/protocol/bot.rs index ab47b1a9a5..f2e8b5aab4 100644 --- a/usbscsid/src/protocol/bot.rs +++ b/usbscsid/src/protocol/bot.rs @@ -1,4 +1,9 @@ -use super::Protocol; +use std::slice; + +use thiserror::Error; +use xhcid_interface::{DeviceReqData, PortReqTy, PortReqRecipient, XhciClientHandle, XhciClientHandleError}; + +use super::{Protocol, ProtocolError}; pub const CBW_SIGNATURE: u32 = 0x43425355; @@ -35,13 +40,33 @@ pub struct CommandStatusWrapper { pub status: u8, } -pub struct BulkOnlyTransport; +pub struct BulkOnlyTransport<'a> { + handle: &'a XhciClientHandle, +} -impl Protocol for BulkOnlyTransport { - fn send_command_block(&mut self, cb: &[u8]) { +impl<'a> BulkOnlyTransport<'a> { + pub fn init(handle: &'a XhciClientHandle) -> Result { + Ok(Self { + handle, + }) + } +} + +impl<'a> Protocol for BulkOnlyTransport<'a> { + fn send_command_block(&mut self, cb: &[u8]) -> Result<(), ProtocolError> { todo!() } - fn recv_command_block(&mut self, cb: &mut [u8]) { + fn recv_command_block(&mut self, cb: &mut [u8]) -> Result<(), ProtocolError> { todo!() } } + +pub fn bulk_only_mass_storage_reset(handle: &XhciClientHandle, if_num: u16) -> Result<(), XhciClientHandleError> { + handle.device_request(PortReqTy::Class, PortReqRecipient::Interface, 0xFF, 0, if_num, DeviceReqData::NoData) +} +pub fn get_max_lun(handle: &XhciClientHandle, if_num: u16) -> Result { + let mut lun = 0; + let buffer = slice::from_mut(&mut lun); + handle.device_request(PortReqTy::Class, PortReqRecipient::Interface, 0xFE, 0, if_num, DeviceReqData::In(buffer))?; + Ok(lun) +} diff --git a/usbscsid/src/protocol/mod.rs b/usbscsid/src/protocol/mod.rs index ce63b4e4df..986f7801d6 100644 --- a/usbscsid/src/protocol/mod.rs +++ b/usbscsid/src/protocol/mod.rs @@ -1,12 +1,32 @@ +use thiserror::Error; +use xhcid_interface::{XhciClientHandle, XhciClientHandleError}; + +#[derive(Debug, Error)] +pub enum ProtocolError { + #[error("Too large command block ({0} > 16)")] + TooLargeCommandBlock(usize), + + #[error("xhcid connection error: {0}")] + XhciError(#[from] XhciClientHandleError), +} + pub trait Protocol { - fn send_command_block(&mut self, cb: &[u8]); - fn recv_command_block(&mut self, cb: &mut [u8]); + fn send_command_block(&mut self, cb: &[u8]) -> Result<(), ProtocolError>; + fn recv_command_block(&mut self, cb: &mut [u8]) -> Result<(), ProtocolError>; } /// Bulk-only transport pub mod bot; -/// Control-Bulk-Interface transpoint -mod cbi { +mod uas { // TODO } + +use bot::BulkOnlyTransport; + +pub fn setup<'a>(handle: &'a XhciClientHandle, protocol: u8) -> Option> { + match protocol { + 0x50 => Some(Box::new(BulkOnlyTransport::init(handle).unwrap())), + _ => None, + } +} diff --git a/xhcid/src/driver_interface.rs b/xhcid/src/driver_interface.rs index a2cb08b361..9d78352a3a 100644 --- a/xhcid/src/driver_interface.rs +++ b/xhcid/src/driver_interface.rs @@ -2,7 +2,7 @@ pub extern crate serde; pub extern crate smallvec; use std::convert::TryFrom; -use std::fs::File; +use std::fs::{File, OpenOptions}; use std::io::prelude::*; use std::{io, result, str}; @@ -315,8 +315,13 @@ impl XhciClientHandle { &self, req: &ConfigureEndpointsReq, ) -> result::Result<(), XhciClientHandleError> { - let path = format!("{}:port{}/configure_endpoints", self.scheme, self.port); - serde_json::to_writer(File::open(path)?, req)?; + let path = format!("{}:port{}/configure", self.scheme, self.port); + let json = serde_json::to_vec(req)?; + let mut file = OpenOptions::new().read(false).write(true).open(path)?; + let json_bytes_written = file.write(&json)?; + if json_bytes_written != json.len() { + return Err(XhciClientHandleError::InvalidResponse(Invalid)); + } Ok(()) } pub fn port_state(&self) -> result::Result {