From c99cd3a843d2d98cdb84ccb3f7319cdd598c07f6 Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Fri, 12 Apr 2024 09:27:36 -0600 Subject: [PATCH] usbhidd: use rehid ReportHandler --- Cargo.lock | 3 +- usbhidd/src/keymap.rs | 10 +- usbhidd/src/main.rs | 461 +++++++++++++++--------------------------- usbhidd/src/reqs.rs | 5 +- 4 files changed, 176 insertions(+), 303 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d683e99a04..1c8f413c60 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1140,9 +1140,10 @@ checksum = "20145670ba436b55d91fc92d25e71160fbfbdd57831631c8d7d36377a476f1cb" [[package]] name = "rehid" version = "0.1.0" -source = "git+https://gitlab.redox-os.org/redox-os/rehid.git#9bf6c9ef7cc4f95932fcaabfa3141a24bfd39bbf" +source = "git+https://gitlab.redox-os.org/redox-os/rehid.git#d3c74a8abbdb75d93260883e3378c46b3e875fa9" dependencies = [ "bitflags 2.5.0", + "log", "ux", ] diff --git a/usbhidd/src/keymap.rs b/usbhidd/src/keymap.rs index 236e7caa07..46264606a7 100644 --- a/usbhidd/src/keymap.rs +++ b/usbhidd/src/keymap.rs @@ -57,7 +57,7 @@ pub mod us { ['\0', '\0'], ['\0', '\0'], ['\0', '\0'], - [' ', ' '] + [' ', ' '], ]; pub fn get_char(scancode: u8, shift: bool) -> char { @@ -236,7 +236,7 @@ pub mod dvorak { ['\0', '\0'], ['\0', '\0'], ['\0', '\0'], - [' ', ' '] + [' ', ' '], ]; pub fn get_char(scancode: u8, shift: bool) -> char { @@ -311,7 +311,7 @@ pub mod azerty { ['\0', '\0'], ['\0', '\0'], ['\0', '\0'], - [' ', ' '] + [' ', ' '], ]; pub fn get_char(scancode: u8, shift: bool) -> char { @@ -386,7 +386,7 @@ pub mod bepo { ['\0', '\0'], ['\0', '\0'], ['\0', '\0'], - [' ', ' '] + [' ', ' '], ]; pub fn get_char(scancode: u8, shift: bool) -> char { @@ -461,7 +461,7 @@ pub mod it { ['\0', '\0'], ['\0', '\0'], ['\0', '\0'], - [' ', ' '] + [' ', ' '], ]; pub fn get_char(scancode: u8, shift: bool) -> char { diff --git a/usbhidd/src/main.rs b/usbhidd/src/main.rs index 4235c78457..da9c143aa7 100644 --- a/usbhidd/src/main.rs +++ b/usbhidd/src/main.rs @@ -2,52 +2,51 @@ use std::collections::VecDeque; use std::env; use std::fs::File; use std::io::Write; -use std::os::unix::io::AsRawFd; -use bitflags::bitflags; use orbclient::KeyEvent as OrbKeyEvent; use redox_log::{OutputBuilder, RedoxLogger}; -use rehid::{binary_view::BinaryView, report_desc::{self, ReportTy}, usage_tables}; +use rehid::{ + report_desc::{self, ReportTy, REPORT_DESC_TY}, + report_handler::ReportHandler, + usage_tables::{GenericDesktopUsage, UsagePage}, +}; use xhcid_interface::{ConfigureEndpointsReq, DevDesc, PortReqRecipient, XhciClientHandle}; mod keymap; mod reqs; -use report_desc::{LocalItemsState, MainCollectionFlags, MainItem, MainItemFlags, GlobalItemsState, ReportFlatIter, ReportItem, ReportIter, ReportIterItem, REPORT_DESC_TY}; - -fn div_round_up(num: u32, denom: u32) -> u32 { - if num % denom == 0 { num / denom } else { num / denom + 1 } -} - fn setup_logging() -> Option<&'static RedoxLogger> { - let mut logger = RedoxLogger::new() - .with_output( - OutputBuilder::stderr() - .with_filter(log::LevelFilter::Info) // limit global output to important info - .with_ansi_escape_codes() - .flush_on_newline(true) - .build() - ); + let mut logger = RedoxLogger::new().with_output( + OutputBuilder::stderr() + .with_filter(log::LevelFilter::Info) // limit global output to important info + .with_ansi_escape_codes() + .flush_on_newline(true) + .build(), + ); #[cfg(target_os = "redox")] match OutputBuilder::in_redox_logging_scheme("usb", "device", "hid.log") { - Ok(b) => logger = logger.with_output( - // TODO: Add a configuration file for this - b.with_filter(log::LevelFilter::Info) - .flush_on_newline(true) - .build() - ), + Ok(b) => { + logger = logger.with_output( + // TODO: Add a configuration file for this + b.with_filter(log::LevelFilter::Info) + .flush_on_newline(true) + .build(), + ) + } Err(error) => eprintln!("Failed to create hid.log: {}", error), } #[cfg(target_os = "redox")] match OutputBuilder::in_redox_logging_scheme("usb", "device", "hid.ansi.log") { - Ok(b) => logger = logger.with_output( - b.with_filter(log::LevelFilter::Info) - .with_ansi_escape_codes() - .flush_on_newline(true) - .build() - ), + Ok(b) => { + logger = logger.with_output( + b.with_filter(log::LevelFilter::Info) + .with_ansi_escape_codes() + .flush_on_newline(true) + .build(), + ) + } Err(error) => eprintln!("Failed to create hid.ansi.log: {}", error), } @@ -63,7 +62,13 @@ fn setup_logging() -> Option<&'static RedoxLogger> { } } -fn send_key_event(display: &mut File, usage_page: u32, usage: u8, pressed: bool, shift_opt: Option) { +fn send_key_event( + display: &mut File, + usage_page: u32, + usage: u32, + pressed: bool, + shift_opt: Option, +) { let scancode = match usage_page { 0x07 => match usage { 0x04 => orbclient::K_A, @@ -170,7 +175,7 @@ fn send_key_event(display: &mut File, usage_page: u32, usage: u8, pressed: bool, 0xE0 => orbclient::K_CTRL, // TODO: left control 0xE1 => orbclient::K_LEFT_SHIFT, 0xE2 => orbclient::K_ALT, - 0xE3 => 0x5B, // left super + 0xE3 => 0x5B, // left super 0xE4 => orbclient::K_CTRL, // TODO: right control 0xE5 => orbclient::K_RIGHT_SHIFT, 0xE6 => orbclient::K_ALT_GR, @@ -179,12 +184,12 @@ fn send_key_event(display: &mut File, usage_page: u32, usage: u8, pressed: bool, _ => { log::warn!("unknown usage_page {:#x} usage {:#x}", usage_page, usage); return; - }, + } }, _ => { log::warn!("unknown usage_page {:#x}", usage_page); return; - }, + } }; //TODO: other keymaps @@ -225,7 +230,9 @@ fn main() { log::info!( "USB HID driver spawned with scheme `{}`, port {}, protocol {}", - scheme, port, protocol + scheme, + port, + protocol ); let handle = XhciClientHandle::new(scheme, port); @@ -251,292 +258,160 @@ fn main() { ) .expect("Failed to retrieve report descriptor"); - let report_desc = ReportIter::new(ReportFlatIter::new(&report_desc_bytes)).collect::>(); + let mut handler = + ReportHandler::new(&report_desc_bytes).expect("failed to parse report descriptor"); - for item in &report_desc { - log::debug!("{:?}", item); - } - - handle.configure_endpoints(&ConfigureEndpointsReq { config_desc: 0, interface_desc: None, alternate_setting: None }).expect("Failed to configure endpoints"); - - let (mut global_state, mut local_state, mut stack) = (GlobalItemsState::default(), LocalItemsState::default(), Vec::new()); - - let (_, application_collection, application_global_state, application_local_state) = report_desc.iter().filter_map(|item: &ReportIterItem| { - log::trace!("1: {:?}", item); - match item { - &ReportIterItem::Item(ref item) => { - report_desc::update_global_state(&mut global_state, &mut stack, item).unwrap(); - report_desc::update_local_state(&mut local_state, item); - None - } - &ReportIterItem::Collection(n, ref collection) => { - let lc_state = std::mem::replace(&mut local_state, LocalItemsState::default()); - Some((n, collection, global_state, lc_state)) - } - } - }).find(|&(n, _, _, _)| n == MainCollectionFlags::Application as u8).expect("Failed to find application collection"); + handle + .configure_endpoints(&ConfigureEndpointsReq { + config_desc: 0, + interface_desc: None, + alternate_setting: None, + }) + .expect("Failed to configure endpoints"); // Get all main items, and their global item options. { - let mut collections = VecDeque::new(); - collections.push_back(application_collection); - let mut items = Vec::new(); - while let Some(collection) = collections.pop_front() { - for item in collection { - log::trace!("2: {:?}", item); - match item { - ReportIterItem::Item(item) => match item { - ReportItem::Global(_) => { - report_desc::update_global_state(&mut global_state, &mut stack, item).unwrap(); - } - ReportItem::Main(m) => { - let lc_state = std::mem::replace(&mut local_state, LocalItemsState::default()); - items.push((global_state, lc_state, m)); - } - ReportItem::Local(_) => { - report_desc::update_local_state(&mut local_state, item); - }, - }, - //TODO: does local state need to be different for inner collections? - ReportIterItem::Collection(_, collection) => { - collections.push_back(collection); - }, - } - } - } - let mut bit_offset = 0; - let inputs = items.iter().filter_map(|(global_state, local_state, item)| { - log::trace!("3: {:?}", item); - - if let &MainItem::Input(flags) = item { - let report_size = match global_state.report_size { - Some(s) => s, - None => return None, - }; - let report_count = match global_state.report_count { - Some(c) => c, - None => return None, - }; - - let bit_length = report_size * report_count; - let offset = bit_offset; - bit_offset += bit_length; - - Some((bit_length, offset, global_state, local_state, MainItemFlags::from_bits_truncate(*flags))) - } else { - None - } - }).collect::>(); - let total_bit_length = inputs.iter().map(|(bit_length, _, _, _, _)| bit_length).sum(); - - let total_byte_length = div_round_up(total_bit_length, 8); - - let mut report_buffer = vec! [0u8; total_byte_length as usize]; - let mut last_buffer = report_buffer.clone(); + let mut report_buffer = vec![0u8; handler.total_byte_length]; let report_ty = ReportTy::Input; let report_id = 0; - let mut display = File::open("input:producer").expect("Failed to open orbital input socket"); - - let mut pressed_keys = Vec::<(u32, u8)>::new(); - let mut last_pressed_keys = pressed_keys.clone(); - let mut last_buttons = (false, false, false); - + let mut display = + File::open("input:producer").expect("Failed to open orbital input socket"); + let mut left_shift = false; + let mut right_shift = false; + let mut last_mouse_pos = (0, 0); + let mut last_buttons = [false, false, false]; loop { + //TODO: get frequency from device std::thread::sleep(std::time::Duration::from_millis(10)); - 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"); + reqs::get_report( + &handle, + report_ty, + report_id, + interface_num, + &mut report_buffer, + ) + .expect("Failed to get report"); - if report_buffer == last_buffer { - continue - } - - for &(bit_length, bit_offset, global_state, local_state, input) in &inputs { - let report_size = global_state.report_size.unwrap(); - let report_count = global_state.report_count.unwrap(); - - log::trace!( - "size {} count {} offset {} length {} usage page {:?}", - report_size, - report_count, - bit_offset, - bit_length, - global_state.usage_page - ); - - // TODO: For now, the dynamic value usages cannot overlap with selector usages... - // for now. - - if local_state.usage_min == Some(224) && local_state.usage_max == Some(231) { - // The usages that this descriptor references are all dynamic values. - } else { - // The usages are selectors. - } - - if input.contains(MainItemFlags::VARIABLE) { - // The item is a variable. - - let binary_view = BinaryView::new(&report_buffer, bit_offset as usize, bit_length as usize); - - if report_count == 8 && report_size == 1 && global_state.usage_page == Some(7) && local_state.usage_min == Some(224) && local_state.usage_max == Some(231) && global_state.logical_min == Some(0) && global_state.logical_max == Some(1) { - let bits = binary_view.read_u8(0).expect("Failed to read array item"); - for bit in 0..8 { - if bits & (1 << bit) > 0 { - pressed_keys.push((0x07, 0xE0 + bit)); - } + let mut mouse_pos = last_mouse_pos; + let mut mouse_dx = 0i32; + let mut mouse_dy = 0i32; + let mut buttons = last_buttons; + for event in handler + .handle(&report_buffer) + .expect("failed to parse report") + { + log::debug!("{:X?}", event); + if event.usage_page == UsagePage::GenericDesktop as u32 { + if event.usage == GenericDesktopUsage::X as u32 { + if event.relative { + mouse_dx += event.value; + } else { + mouse_pos.0 = event.value; } - log::trace!("Report variable {:#x?}", bits); - } else if report_count == 2 && report_size == 16 && global_state.usage_page == Some(1) { - //TODO: Make this less hard-coded - let raw_x = - binary_view.read_u8(0).expect("Failed to read array item") as u16 | - (binary_view.read_u8(8).expect("Failed to read array item") as u16) << 8; - let raw_y = - binary_view.read_u8(16).expect("Failed to read array item") as u16 | - (binary_view.read_u8(24).expect("Failed to read array item") as u16) << 8; - - // ps2d uses 0..=65535 as range, while usb uses 0..=32767. orbital - // expects the former range, so multiply by two here to translate - // the usb coordinates to what orbital expects. - let x = raw_x * 2; - let y = raw_y * 2; - - log::trace!("Touchscreen {}, {} => {}, {}", raw_x, raw_y, x, y); - if x != 0 || y != 0 { - let mouse_event = orbclient::event::MouseEvent { - x: x as i32, - y: y as i32, - }; - - match display.write(&mouse_event.to_event()) { - Ok(_) => (), - Err(err) => { - log::warn!("failed to send mouse event to orbital: {}", err); - } - } - } - } else if report_count == 3 && report_size == 8 && global_state.usage_page == Some(1) { - //TODO: Make this less hard-coded - let dx = binary_view.read_u8(0).expect("Failed to read array item") as i8; - let dy = binary_view.read_u8(8).expect("Failed to read array item") as i8; - let dz = binary_view.read_u8(16).expect("Failed to read array item") as i8; - log::trace!("Mouse {}, {}, {}", dx, dy, dz); - if dx != 0 || dy != 0 { - let mouse_event = orbclient::event::MouseRelativeEvent { - dx: dx as i32, - dy: dy as i32, - }; - - match display.write(&mouse_event.to_event()) { - Ok(_) => (), - Err(err) => { - log::warn!("failed to send mouse event to orbital: {}", err); - } - } - } - if dz != 0 { - let scroll_event = orbclient::event::ScrollEvent { - x: dz as i32, - y: 0, - }; - - match display.write(&scroll_event.to_event()) { - Ok(_) => (), - Err(err) => { - log::warn!("failed to send scroll event to orbital: {}", err); - } - } - } - } else if report_count == 3 && report_size == 1 && global_state.usage_page == Some(9) { - //TODO: Make this less hard-coded - let left = binary_view.get(0).expect("Failed to read array item"); - let right = binary_view.get(1).expect("Failed to read array item"); - let middle = binary_view.get(2).expect("Failed to read array item"); - log::trace!("Left {}, Right {}, Middle {}", left, right, middle); - if last_buttons != (left, right, middle) { - last_buttons = (left, right, middle); - - let button_event = orbclient::event::ButtonEvent { - left, - right, - middle, - }; - - match display.write(&button_event.to_event()) { - Ok(_) => (), - Err(err) => { - log::warn!("failed to send button event to orbital: {}", err); - } - } + } else if event.usage == GenericDesktopUsage::Y as u32 { + if event.relative { + mouse_dy += event.value; + } else { + mouse_pos.1 = event.value; } } else { - log::trace!("Unknown report variable item: size {} count {} offset {} usage page {:?}", report_size, report_count, bit_offset, global_state.usage_page); + log::info!( + "unsupported generic desktop usage 0x{:X}:0x{:X} value {}", + event.usage_page, + event.usage, + event.value + ); + } + } else if event.usage_page == UsagePage::KeyboardOrKeypad as u32 { + let (pressed, shift_opt) = if event.value != 0 { + (true, Some(left_shift | right_shift)) + } else { + (false, None) + }; + if event.usage == 0xE1 { + left_shift = pressed; + } else if event.usage == 0xE5 { + right_shift = pressed; + } + send_key_event( + &mut display, + event.usage_page, + event.usage, + pressed, + shift_opt, + ); + } else if event.usage_page == UsagePage::Button as u32 { + if event.usage > 0 && event.usage as usize <= buttons.len() { + buttons[event.usage as usize - 1] = event.value != 0; + } else { + log::info!( + "unsupported buttons usage 0x{:X}:0x{:X} value {}", + event.usage_page, + event.usage, + event.value + ); } } else { - // The item is an array. + log::info!( + "unsupported usage 0x{:X}:0x{:X} value {}", + event.usage_page, + event.usage, + event.value + ); + } + } - log::trace!("INPUT FLAGS: {:?}", input); - if report_size == 8 { - for report_index in 0..report_count as usize { - let binary_view = BinaryView::new(&report_buffer, bit_offset as usize + report_index * report_size as usize, report_size as usize); - let usage = binary_view.read_u8(0).expect("Failed to read array item"); - if usage != 0 { - pressed_keys.push((global_state.usage_page.unwrap_or(0), usage)); - } - log::trace!("Report index array {}: {:#x}", report_index, usage); - } - } else { - log::trace!("Unknown report array item: size {} count {} offset {} usage page {:?}", report_size, report_count, bit_offset, global_state.usage_page); + if mouse_pos != last_mouse_pos { + last_mouse_pos = mouse_pos; + + // ps2d uses 0..=65535 as range, while usb uses 0..=32767. orbital + // expects the former range, so multiply by two here to translate + // the usb coordinates to what orbital expects. + let mouse_event = orbclient::event::MouseEvent { + x: mouse_pos.0 * 2, + y: mouse_pos.1 * 2, + }; + + match display.write(&mouse_event.to_event()) { + Ok(_) => (), + Err(err) => { + log::warn!("failed to send mouse event to orbital: {}", err); } } } + if mouse_dx != 0 || mouse_dy != 0 { + let mouse_event = orbclient::event::MouseRelativeEvent { + dx: mouse_dx, + dy: mouse_dy, + }; - for &(usage_page, usage) in last_pressed_keys.iter() { - if ! pressed_keys.contains(&(usage_page, usage)) { - log::debug!("Released {:#x},{:#x}", usage_page, usage); - send_key_event(&mut display, usage_page, usage, false, None); + match display.write(&mouse_event.to_event()) { + Ok(_) => (), + Err(err) => { + log::warn!("failed to send mouse event to orbital: {}", err); + } } } - for &(usage_page, usage) in pressed_keys.iter() { - if ! last_pressed_keys.contains(&(usage_page, usage)) { - log::debug!("Pressed {:#x},{:#x}", usage_page, usage); - send_key_event(&mut display, usage_page, usage, true, Some( - pressed_keys.contains(&(0x07, 0xE1)) || pressed_keys.contains(&(0x07, 0xE5)) - )); + if buttons != last_buttons { + last_buttons = buttons; + + let button_event = orbclient::event::ButtonEvent { + left: buttons[0], + right: buttons[1], + middle: buttons[2], + }; + + match display.write(&button_event.to_event()) { + Ok(_) => (), + Err(err) => { + log::warn!("failed to send button event to orbital: {}", err); + } } } - - std::mem::swap(&mut pressed_keys, &mut last_pressed_keys); - pressed_keys.clear(); } } } - -#[cfg(test)] -mod tests { - #[test] - fn binary_view() { - // 0000 1000 1100 0111 - // E S - let view = super::BinaryView::new(&[0xC7, 0x08], 3, 11); - assert_eq!(view.get(0), Some(false)); - assert_eq!(view.get(2), Some(false)); - assert_eq!(view.get(3), Some(true)); - assert_eq!(view.get(7), Some(false)); - assert_eq!(view.get(17), None); - - assert_eq!(view.read_u8(0), Some(0b0001_1000)); - assert_eq!(view.read_u8(1), Some(0b1000_1100)); - assert_eq!(view.read_u8(2), Some(0b0100_0110)); - assert_eq!(view.read_u8(3), Some(0b0010_0011)); - assert_eq!(view.read_u8(7), None); - - // 0000 1000 1100 0111 - // E S - let view = super::BinaryView::new(&[0xC7, 0x08], 8, 8); - assert_eq!(view.read_u8(0), Some(0b0000_1000)); - } -} diff --git a/usbhidd/src/reqs.rs b/usbhidd/src/reqs.rs index b7887b2490..c74281dd3d 100644 --- a/usbhidd/src/reqs.rs +++ b/usbhidd/src/reqs.rs @@ -80,10 +80,7 @@ pub fn set_idle( DeviceReqData::NoData, ) } -pub fn get_protocol( - handle: &XhciClientHandle, - if_num: u16, -) -> Result { +pub fn get_protocol(handle: &XhciClientHandle, if_num: u16) -> Result { let mut protocol = 0; let buffer = slice::from_mut(&mut protocol); handle.device_request(