aca2f2913d
Build system (src/cook/fetch.rs): - Atomic patch application: applies patches to staging directory (cp -al), atomically swaps on success, discards on failure — source tree is never left in a partially-patched state - normalize_patch(): strips diff --git/index/new-file-mode headers that the build system's patch command does not recognize - cleanup_workspace_pollution(): removes orphaned recipes/Cargo.toml and recipes/Cargo.lock to prevent workspace conflicts - Added --allow-protected CLI flag to repo binary Input stack (local/patches/base/P3-*.patch): - P3-ps2d-led-feedback: PS/2 LED state handling + InputProducer migration - P3-inputd-keymap-bridge: InputProducer enum, keymap bridge query - P3-usbhidd-hardening: HID descriptor validation, static lookup table, 8-button mouse support, transfer retry with exponential backoff - P3-init-colored-output: ANSI-color coded init daemon output (green OK, red FAILED, yellow SKIP/WARN) XKB bridge (local/recipes/system/redbear-keymapd/source/src/xkb.rs): - Parses X11 xkb/symbols/* format, maps XKB keycodes to PS/2 scancodes, 80+ X11 keysym names to Unicode, 4-level key support Patch governance (local/patches/base/absorbed/README.md): - Documents consolidation of P0-P3 patches into redox.patch
726 lines
28 KiB
Diff
726 lines
28 KiB
Diff
diff --git a/drivers/input/usbhidd/src/main.rs b/drivers/input/usbhidd/src/main.rs
|
||
index 15c5b778..472ec4cf 100644
|
||
--- a/drivers/input/usbhidd/src/main.rs
|
||
+++ b/drivers/input/usbhidd/src/main.rs
|
||
@@ -1,7 +1,7 @@
|
||
-use anyhow::{Context, Result};
|
||
+use anyhow::{bail, ensure, Context, Result};
|
||
use std::{env, thread, time};
|
||
|
||
-use inputd::ProducerHandle;
|
||
+use inputd::InputProducer;
|
||
use orbclient::KeyEvent as OrbKeyEvent;
|
||
use rehid::{
|
||
report_desc::{ReportTy, REPORT_DESC_TY},
|
||
@@ -15,161 +15,219 @@ use xhcid_interface::{
|
||
|
||
mod reqs;
|
||
|
||
-fn send_key_event(display: &mut ProducerHandle, usage_page: u16, usage: u16, pressed: bool) {
|
||
- let scancode = match usage_page {
|
||
- 0x07 => match usage {
|
||
- 0x04 => orbclient::K_A,
|
||
- 0x05 => orbclient::K_B,
|
||
- 0x06 => orbclient::K_C,
|
||
- 0x07 => orbclient::K_D,
|
||
- 0x08 => orbclient::K_E,
|
||
- 0x09 => orbclient::K_F,
|
||
- 0x0A => orbclient::K_G,
|
||
- 0x0B => orbclient::K_H,
|
||
- 0x0C => orbclient::K_I,
|
||
- 0x0D => orbclient::K_J,
|
||
- 0x0E => orbclient::K_K,
|
||
- 0x0F => orbclient::K_L,
|
||
- 0x10 => orbclient::K_M,
|
||
- 0x11 => orbclient::K_N,
|
||
- 0x12 => orbclient::K_O,
|
||
- 0x13 => orbclient::K_P,
|
||
- 0x14 => orbclient::K_Q,
|
||
- 0x15 => orbclient::K_R,
|
||
- 0x16 => orbclient::K_S,
|
||
- 0x17 => orbclient::K_T,
|
||
- 0x18 => orbclient::K_U,
|
||
- 0x19 => orbclient::K_V,
|
||
- 0x1A => orbclient::K_W,
|
||
- 0x1B => orbclient::K_X,
|
||
- 0x1C => orbclient::K_Y,
|
||
- 0x1D => orbclient::K_Z,
|
||
- 0x1E => orbclient::K_1,
|
||
- 0x1F => orbclient::K_2,
|
||
- 0x20 => orbclient::K_3,
|
||
- 0x21 => orbclient::K_4,
|
||
- 0x22 => orbclient::K_5,
|
||
- 0x23 => orbclient::K_6,
|
||
- 0x24 => orbclient::K_7,
|
||
- 0x25 => orbclient::K_8,
|
||
- 0x26 => orbclient::K_9,
|
||
- 0x27 => orbclient::K_0,
|
||
- 0x28 => orbclient::K_ENTER,
|
||
- 0x29 => orbclient::K_ESC,
|
||
- 0x2A => orbclient::K_BKSP,
|
||
- 0x2B => orbclient::K_TAB,
|
||
- 0x2C => orbclient::K_SPACE,
|
||
- 0x2D => orbclient::K_MINUS,
|
||
- 0x2E => orbclient::K_EQUALS,
|
||
- 0x2F => orbclient::K_BRACE_OPEN,
|
||
- 0x30 => orbclient::K_BRACE_CLOSE,
|
||
- 0x31 => orbclient::K_BACKSLASH,
|
||
- // 0x32 non-us # and ~
|
||
- 0x32 => 0x56,
|
||
- 0x33 => orbclient::K_SEMICOLON,
|
||
- 0x34 => orbclient::K_QUOTE,
|
||
- 0x35 => orbclient::K_TICK,
|
||
- 0x36 => orbclient::K_COMMA,
|
||
- 0x37 => orbclient::K_PERIOD,
|
||
- 0x38 => orbclient::K_SLASH,
|
||
- 0x39 => orbclient::K_CAPS,
|
||
- 0x3A => orbclient::K_F1,
|
||
- 0x3B => orbclient::K_F2,
|
||
- 0x3C => orbclient::K_F3,
|
||
- 0x3D => orbclient::K_F4,
|
||
- 0x3E => orbclient::K_F5,
|
||
- 0x3F => orbclient::K_F6,
|
||
- 0x40 => orbclient::K_F7,
|
||
- 0x41 => orbclient::K_F8,
|
||
- 0x42 => orbclient::K_F9,
|
||
- 0x43 => orbclient::K_F10,
|
||
- 0x44 => orbclient::K_F11,
|
||
- 0x45 => orbclient::K_F12,
|
||
- 0x46 => orbclient::K_PRTSC,
|
||
- 0x47 => orbclient::K_SCROLL,
|
||
- // 0x48 pause
|
||
- 0x49 => orbclient::K_INS,
|
||
- 0x4A => orbclient::K_HOME,
|
||
- 0x4B => orbclient::K_PGUP,
|
||
- 0x4C => orbclient::K_DEL,
|
||
- 0x4D => orbclient::K_END,
|
||
- 0x4E => orbclient::K_PGDN,
|
||
- 0x4F => orbclient::K_RIGHT,
|
||
- 0x50 => orbclient::K_LEFT,
|
||
- 0x51 => orbclient::K_DOWN,
|
||
- 0x52 => orbclient::K_UP,
|
||
- 0x53 => orbclient::K_NUM,
|
||
- 0x54 => orbclient::K_NUM_SLASH,
|
||
- 0x55 => orbclient::K_NUM_ASTERISK,
|
||
- 0x56 => orbclient::K_NUM_MINUS,
|
||
- 0x57 => orbclient::K_NUM_PLUS,
|
||
- 0x58 => orbclient::K_NUM_ENTER,
|
||
- 0x59 => orbclient::K_NUM_1,
|
||
- 0x5A => orbclient::K_NUM_2,
|
||
- 0x5B => orbclient::K_NUM_3,
|
||
- 0x5C => orbclient::K_NUM_4,
|
||
- 0x5D => orbclient::K_NUM_5,
|
||
- 0x5E => orbclient::K_NUM_6,
|
||
- 0x5F => orbclient::K_NUM_7,
|
||
- 0x60 => orbclient::K_NUM_8,
|
||
- 0x61 => orbclient::K_NUM_9,
|
||
- 0x62 => orbclient::K_NUM_0,
|
||
- // 0x62 num .
|
||
- // 0x64 non-us \ and |
|
||
- 0x64 => orbclient::K_APP,
|
||
- 0x66 => orbclient::K_POWER,
|
||
- // 0x67 num =
|
||
- // unmapped values
|
||
- 0xE0 => orbclient::K_LEFT_CTRL,
|
||
- 0xE1 => orbclient::K_LEFT_SHIFT,
|
||
- 0xE2 => orbclient::K_ALT,
|
||
- 0xE3 => orbclient::K_LEFT_SUPER,
|
||
- 0xE4 => orbclient::K_RIGHT_CTRL,
|
||
- 0xE5 => orbclient::K_RIGHT_SHIFT,
|
||
- 0xE6 => orbclient::K_ALT_GR,
|
||
- 0xE7 => orbclient::K_RIGHT_SUPER,
|
||
- // reserved values
|
||
- _ => {
|
||
- log::warn!("unknown usage_page {:#x} usage {:#x}", usage_page, usage);
|
||
- return;
|
||
- }
|
||
- },
|
||
- _ => {
|
||
- log::warn!("unknown usage_page {:#x}", usage_page);
|
||
+const MAX_MOUSE_BUTTONS: usize = 8;
|
||
+const MAX_RETRIES: u32 = 3;
|
||
+const RETRY_BASE_DELAY_MS: u64 = 2;
|
||
+const REPORT_DESC_MAX_SIZE: usize = 4096;
|
||
+const REPORT_DESC_MIN_SIZE: usize = 3;
|
||
+
|
||
+/// USB HID Usage (Keyboard page 0x07) → orbclient scancode lookup table.
|
||
+/// Covers usage codes 0x04–0xE7 per USB HID Usage Tables spec v1.12.
|
||
+/// Entries default to 0 (unmapped); only defined usages are populated.
|
||
+static HID_KEYBOARD_TO_SCANCODE: [u8; 0xE8] = {
|
||
+ let mut table = [0u8; 0xE8];
|
||
+ table[0x04] = orbclient::K_A;
|
||
+ table[0x05] = orbclient::K_B;
|
||
+ table[0x06] = orbclient::K_C;
|
||
+ table[0x07] = orbclient::K_D;
|
||
+ table[0x08] = orbclient::K_E;
|
||
+ table[0x09] = orbclient::K_F;
|
||
+ table[0x0A] = orbclient::K_G;
|
||
+ table[0x0B] = orbclient::K_H;
|
||
+ table[0x0C] = orbclient::K_I;
|
||
+ table[0x0D] = orbclient::K_J;
|
||
+ table[0x0E] = orbclient::K_K;
|
||
+ table[0x0F] = orbclient::K_L;
|
||
+ table[0x10] = orbclient::K_M;
|
||
+ table[0x11] = orbclient::K_N;
|
||
+ table[0x12] = orbclient::K_O;
|
||
+ table[0x13] = orbclient::K_P;
|
||
+ table[0x14] = orbclient::K_Q;
|
||
+ table[0x15] = orbclient::K_R;
|
||
+ table[0x16] = orbclient::K_S;
|
||
+ table[0x17] = orbclient::K_T;
|
||
+ table[0x18] = orbclient::K_U;
|
||
+ table[0x19] = orbclient::K_V;
|
||
+ table[0x1A] = orbclient::K_W;
|
||
+ table[0x1B] = orbclient::K_X;
|
||
+ table[0x1C] = orbclient::K_Y;
|
||
+ table[0x1D] = orbclient::K_Z;
|
||
+ table[0x1E] = orbclient::K_1;
|
||
+ table[0x1F] = orbclient::K_2;
|
||
+ table[0x20] = orbclient::K_3;
|
||
+ table[0x21] = orbclient::K_4;
|
||
+ table[0x22] = orbclient::K_5;
|
||
+ table[0x23] = orbclient::K_6;
|
||
+ table[0x24] = orbclient::K_7;
|
||
+ table[0x25] = orbclient::K_8;
|
||
+ table[0x26] = orbclient::K_9;
|
||
+ table[0x27] = orbclient::K_0;
|
||
+ table[0x28] = orbclient::K_ENTER;
|
||
+ table[0x29] = orbclient::K_ESC;
|
||
+ table[0x2A] = orbclient::K_BKSP;
|
||
+ table[0x2B] = orbclient::K_TAB;
|
||
+ table[0x2C] = orbclient::K_SPACE;
|
||
+ table[0x2D] = orbclient::K_MINUS;
|
||
+ table[0x2E] = orbclient::K_EQUALS;
|
||
+ table[0x2F] = orbclient::K_BRACE_OPEN;
|
||
+ table[0x30] = orbclient::K_BRACE_CLOSE;
|
||
+ table[0x31] = orbclient::K_BACKSLASH;
|
||
+ table[0x32] = 0x56;
|
||
+ table[0x33] = orbclient::K_SEMICOLON;
|
||
+ table[0x34] = orbclient::K_QUOTE;
|
||
+ table[0x35] = orbclient::K_TICK;
|
||
+ table[0x36] = orbclient::K_COMMA;
|
||
+ table[0x37] = orbclient::K_PERIOD;
|
||
+ table[0x38] = orbclient::K_SLASH;
|
||
+ table[0x39] = orbclient::K_CAPS;
|
||
+ table[0x3A] = orbclient::K_F1;
|
||
+ table[0x3B] = orbclient::K_F2;
|
||
+ table[0x3C] = orbclient::K_F3;
|
||
+ table[0x3D] = orbclient::K_F4;
|
||
+ table[0x3E] = orbclient::K_F5;
|
||
+ table[0x3F] = orbclient::K_F6;
|
||
+ table[0x40] = orbclient::K_F7;
|
||
+ table[0x41] = orbclient::K_F8;
|
||
+ table[0x42] = orbclient::K_F9;
|
||
+ table[0x43] = orbclient::K_F10;
|
||
+ table[0x44] = orbclient::K_F11;
|
||
+ table[0x45] = orbclient::K_F12;
|
||
+ table[0x46] = orbclient::K_PRTSC;
|
||
+ table[0x47] = orbclient::K_SCROLL;
|
||
+ table[0x49] = orbclient::K_INS;
|
||
+ table[0x4A] = orbclient::K_HOME;
|
||
+ table[0x4B] = orbclient::K_PGUP;
|
||
+ table[0x4C] = orbclient::K_DEL;
|
||
+ table[0x4D] = orbclient::K_END;
|
||
+ table[0x4E] = orbclient::K_PGDN;
|
||
+ table[0x4F] = orbclient::K_RIGHT;
|
||
+ table[0x50] = orbclient::K_LEFT;
|
||
+ table[0x51] = orbclient::K_DOWN;
|
||
+ table[0x52] = orbclient::K_UP;
|
||
+ table[0x53] = orbclient::K_NUM;
|
||
+ table[0x54] = orbclient::K_NUM_SLASH;
|
||
+ table[0x55] = orbclient::K_NUM_ASTERISK;
|
||
+ table[0x56] = orbclient::K_NUM_MINUS;
|
||
+ table[0x57] = orbclient::K_NUM_PLUS;
|
||
+ table[0x58] = orbclient::K_NUM_ENTER;
|
||
+ table[0x59] = orbclient::K_NUM_1;
|
||
+ table[0x5A] = orbclient::K_NUM_2;
|
||
+ table[0x5B] = orbclient::K_NUM_3;
|
||
+ table[0x5C] = orbclient::K_NUM_4;
|
||
+ table[0x5D] = orbclient::K_NUM_5;
|
||
+ table[0x5E] = orbclient::K_NUM_6;
|
||
+ table[0x5F] = orbclient::K_NUM_7;
|
||
+ table[0x60] = orbclient::K_NUM_8;
|
||
+ table[0x61] = orbclient::K_NUM_9;
|
||
+ table[0x62] = orbclient::K_NUM_0;
|
||
+ table[0x64] = orbclient::K_APP;
|
||
+ table[0x66] = orbclient::K_POWER;
|
||
+ table[0xE0] = orbclient::K_LEFT_CTRL;
|
||
+ table[0xE1] = orbclient::K_LEFT_SHIFT;
|
||
+ table[0xE2] = orbclient::K_ALT;
|
||
+ table[0xE3] = orbclient::K_LEFT_SUPER;
|
||
+ table[0xE4] = orbclient::K_RIGHT_CTRL;
|
||
+ table[0xE5] = orbclient::K_RIGHT_SHIFT;
|
||
+ table[0xE6] = orbclient::K_ALT_GR;
|
||
+ table[0xE7] = orbclient::K_RIGHT_SUPER;
|
||
+ table
|
||
+};
|
||
+
|
||
+fn hid_usage_to_scancode(usage: u16) -> Option<u8> {
|
||
+ let idx = usage as usize;
|
||
+ if idx < HID_KEYBOARD_TO_SCANCODE.len() {
|
||
+ let sc = HID_KEYBOARD_TO_SCANCODE[idx];
|
||
+ if sc != 0 {
|
||
+ return Some(sc);
|
||
+ }
|
||
+ }
|
||
+ None
|
||
+}
|
||
+
|
||
+fn send_key_event(display: &mut InputProducer, usage_page: u16, usage: u16, pressed: bool) {
|
||
+ if usage_page != 0x07 {
|
||
+ log::warn!("send_key_event: unexpected usage_page {:#x}", usage_page);
|
||
+ return;
|
||
+ }
|
||
+ let scancode = match hid_usage_to_scancode(usage) {
|
||
+ Some(sc) => sc,
|
||
+ None => {
|
||
+ log::warn!("unmapped HID keyboard usage {:#x}", usage);
|
||
return;
|
||
}
|
||
};
|
||
-
|
||
let key_event = OrbKeyEvent {
|
||
character: '\0',
|
||
scancode,
|
||
pressed,
|
||
};
|
||
+ if let Err(err) = display.write_event(key_event.to_event()) {
|
||
+ log::warn!("failed to send key event: {}", err);
|
||
+ }
|
||
+}
|
||
|
||
- match display.write_event(key_event.to_event()) {
|
||
- Ok(_) => (),
|
||
- Err(err) => {
|
||
- log::warn!("failed to send key event to orbital: {}", err);
|
||
+fn validate_report_descriptor(bytes: &[u8]) -> Result<()> {
|
||
+ ensure!(
|
||
+ bytes.len() >= REPORT_DESC_MIN_SIZE,
|
||
+ "report descriptor too short: {} bytes (minimum {})",
|
||
+ bytes.len(),
|
||
+ REPORT_DESC_MIN_SIZE
|
||
+ );
|
||
+ ensure!(
|
||
+ bytes.len() <= REPORT_DESC_MAX_SIZE,
|
||
+ "report descriptor too large: {} bytes (maximum {})",
|
||
+ bytes.len(),
|
||
+ REPORT_DESC_MAX_SIZE
|
||
+ );
|
||
+ let mut depth = 0u32;
|
||
+ let mut i = 0;
|
||
+ while i < bytes.len() {
|
||
+ let b = bytes[i];
|
||
+ let size = match b & 0x03 {
|
||
+ 0 => 0,
|
||
+ 1 => 1,
|
||
+ 2 => 2,
|
||
+ 3 => 4,
|
||
+ _ => unreachable!(),
|
||
+ };
|
||
+ let item_type = (b >> 2) & 0x03;
|
||
+ if item_type == 0x0A {
|
||
+ depth += 1;
|
||
+ } else if item_type == 0x0C {
|
||
+ if depth == 0 {
|
||
+ bail!(
|
||
+ "unbalanced Collection/EndCollection in report descriptor at offset {}",
|
||
+ i
|
||
+ );
|
||
+ }
|
||
+ depth -= 1;
|
||
}
|
||
+ i += 1 + size as usize;
|
||
}
|
||
+ ensure!(
|
||
+ depth == 0,
|
||
+ "unbalanced Collection/EndCollection (depth {} at end)",
|
||
+ depth
|
||
+ );
|
||
+ Ok(())
|
||
}
|
||
|
||
fn main() -> Result<()> {
|
||
let mut args = env::args().skip(1);
|
||
|
||
- const USAGE: &'static str = "usbhidd <scheme> <port> <interface>";
|
||
+ const USAGE: &str = "usbhidd <scheme> <port> <interface>";
|
||
|
||
- let scheme = args.next().expect(USAGE);
|
||
- let port = args
|
||
+ let scheme = args.next().context(USAGE)?;
|
||
+ let port: PortId = args
|
||
.next()
|
||
- .expect(USAGE)
|
||
- .parse::<PortId>()
|
||
- .expect("Expected port ID");
|
||
- let interface_num = args
|
||
+ .context(USAGE)?
|
||
+ .parse()
|
||
+ .map_err(|e| anyhow::anyhow!("Expected port ID: {}", e))?;
|
||
+ let interface_num: u8 = args
|
||
.next()
|
||
- .expect(USAGE)
|
||
- .parse::<u8>()
|
||
- .expect("Expected integer as input of interface");
|
||
+ .context(USAGE)?
|
||
+ .parse()
|
||
+ .map_err(|e| anyhow::anyhow!("Expected interface number: {}", e))?;
|
||
|
||
let name = format!("{}_{}_{}_hid", scheme, port, interface_num);
|
||
common::setup_logging(
|
||
@@ -218,7 +276,6 @@ fn main() -> Result<()> {
|
||
}
|
||
});
|
||
let hid_desc = if_desc.hid_descs.iter().find_map(|hid_desc| {
|
||
- //TODO: should we do any filtering?
|
||
Some(hid_desc)
|
||
})?;
|
||
Some((if_desc.clone(), endp_desc_opt, hid_desc))
|
||
@@ -240,31 +297,39 @@ fn main() -> Result<()> {
|
||
})
|
||
.context("Failed to configure endpoints")?;
|
||
|
||
- //TODO: do we need to set protocol to report? It fails for mice.
|
||
-
|
||
- //TODO: dynamically create good values, fix xhcid so it does not block on each request
|
||
- // This sets all reports to a duration of 4ms
|
||
reqs::set_idle(&handle, 1, 0, interface_num as u16).context("Failed to set idle")?;
|
||
|
||
- let report_desc_len = hid_desc.desc_len;
|
||
- assert_eq!(hid_desc.desc_ty, REPORT_DESC_TY);
|
||
+ let report_desc_len = hid_desc.desc_len as usize;
|
||
+ ensure!(
|
||
+ hid_desc.desc_ty == REPORT_DESC_TY,
|
||
+ "unexpected HID descriptor type: expected {}, got {}",
|
||
+ REPORT_DESC_TY,
|
||
+ hid_desc.desc_ty
|
||
+ );
|
||
+ ensure!(
|
||
+ report_desc_len >= REPORT_DESC_MIN_SIZE && report_desc_len <= REPORT_DESC_MAX_SIZE,
|
||
+ "suspicious report descriptor length: {}",
|
||
+ report_desc_len
|
||
+ );
|
||
|
||
- let mut report_desc_bytes = vec![0u8; report_desc_len as usize];
|
||
+ let mut report_desc_bytes = vec![0u8; report_desc_len];
|
||
handle
|
||
.get_descriptor(
|
||
PortReqRecipient::Interface,
|
||
REPORT_DESC_TY,
|
||
0,
|
||
- //TODO: should this be an index into interface_descs?
|
||
interface_num as u16,
|
||
&mut report_desc_bytes,
|
||
)
|
||
.context("Failed to retrieve report descriptor")?;
|
||
|
||
+ validate_report_descriptor(&report_desc_bytes)
|
||
+ .context("HID report descriptor validation failed")?;
|
||
+
|
||
let mut handler =
|
||
- ReportHandler::new(&report_desc_bytes).expect("failed to parse report descriptor");
|
||
+ ReportHandler::new(&report_desc_bytes).map_err(|e| anyhow::anyhow!("failed to parse report descriptor: {}", e))?;
|
||
|
||
- let report_len = match endp_desc_opt {
|
||
+ let report_len = match &endp_desc_opt {
|
||
Some((_endp_num, endp_desc)) => endp_desc.max_packet_size as usize,
|
||
None => handler.total_byte_length as usize,
|
||
};
|
||
@@ -272,7 +337,9 @@ fn main() -> Result<()> {
|
||
let report_ty = ReportTy::Input;
|
||
let report_id = 0;
|
||
|
||
- let mut display = ProducerHandle::new().context("Failed to open input socket")?;
|
||
+ let producer_name = format!("usb-{}-if{}", port, interface_num);
|
||
+ let mut display = InputProducer::new_named_or_fallback(&producer_name)
|
||
+ .context("Failed to open input socket")?;
|
||
let mut endpoint_opt = match endp_desc_opt {
|
||
Some((endp_num, _endp_desc)) => match handle.open_endpoint(endp_num as u8) {
|
||
Ok(ok) => Some(ok),
|
||
@@ -286,172 +353,168 @@ fn main() -> Result<()> {
|
||
let mut left_shift = false;
|
||
let mut right_shift = false;
|
||
let mut last_mouse_pos = (0, 0);
|
||
- let mut last_buttons = [false, false, false];
|
||
+ let mut last_buttons = [false; MAX_MOUSE_BUTTONS];
|
||
+ let mut consecutive_errors: u32 = 0;
|
||
+
|
||
loop {
|
||
- //TODO: get frequency from device
|
||
- //TODO: use sleeps when accuracy is better: thread::sleep(time::Duration::from_millis(10));
|
||
- let timer = time::Instant::now();
|
||
- while timer.elapsed() < time::Duration::from_millis(1) {
|
||
- thread::yield_now();
|
||
- }
|
||
+ thread::sleep(time::Duration::from_millis(1));
|
||
|
||
- if let Some(endpoint) = &mut endpoint_opt {
|
||
- // interrupt transfer
|
||
- endpoint
|
||
- .transfer_read(&mut report_buffer)
|
||
- .context("failed to get report")?;
|
||
+ let transfer_result: Result<(), anyhow::Error> = if let Some(endpoint) = &mut endpoint_opt {
|
||
+ endpoint.transfer_read(&mut report_buffer)
|
||
+ .map(|_| ())
|
||
+ .context("interrupt transfer failed")
|
||
} else {
|
||
- // control transfer
|
||
- reqs::get_report(
|
||
- &handle,
|
||
- report_ty,
|
||
- report_id,
|
||
- //TODO: should this be an index into interface_descs?
|
||
- interface_num as u16,
|
||
- &mut report_buffer,
|
||
- )
|
||
- .context("failed to get report")?;
|
||
+ reqs::get_report(&handle, report_ty, report_id, interface_num as u16, &mut report_buffer)
|
||
+ .map(|_| ())
|
||
+ .context("control transfer failed")
|
||
+ };
|
||
+
|
||
+ if let Err(err) = transfer_result {
|
||
+ consecutive_errors += 1;
|
||
+ if consecutive_errors >= MAX_RETRIES {
|
||
+ bail!(
|
||
+ "transfer failed {} times consecutively: {}",
|
||
+ consecutive_errors,
|
||
+ err
|
||
+ );
|
||
+ }
|
||
+ let delay = RETRY_BASE_DELAY_MS * (1 << consecutive_errors.min(4));
|
||
+ log::warn!(
|
||
+ "transfer failed ({}/{}), retry in {}ms: {}",
|
||
+ consecutive_errors,
|
||
+ MAX_RETRIES,
|
||
+ delay,
|
||
+ err
|
||
+ );
|
||
+ thread::sleep(time::Duration::from_millis(delay));
|
||
+ continue;
|
||
}
|
||
+ consecutive_errors = 0;
|
||
|
||
let mut mouse_pos = last_mouse_pos;
|
||
let mut mouse_dx = 0i32;
|
||
let mut mouse_dy = 0i32;
|
||
let mut scroll_y = 0i32;
|
||
let mut buttons = last_buttons;
|
||
- for event in handler
|
||
- .handle(&report_buffer)
|
||
- .expect("failed to parse report")
|
||
- {
|
||
- log::debug!("{}", event);
|
||
- if event.usage_page == UsagePage::GenericDesktop as u16 {
|
||
- if event.usage == GenericDesktopUsage::X as u16 {
|
||
- if event.relative {
|
||
- mouse_dx += event.value as i32;
|
||
- } else {
|
||
- mouse_pos.0 = event.value as i32;
|
||
- }
|
||
- } else if event.usage == GenericDesktopUsage::Y as u16 {
|
||
- if event.relative {
|
||
- mouse_dy += event.value as i32;
|
||
- } else {
|
||
- mouse_pos.1 = event.value as i32;
|
||
- }
|
||
- } else if event.usage == GenericDesktopUsage::Wheel as u16 {
|
||
- //TODO: what is X scroll?
|
||
- if event.relative {
|
||
- scroll_y += event.value as i32;
|
||
- } else {
|
||
- log::warn!("absolute mouse wheel not supported");
|
||
+
|
||
+ match handler.handle(&report_buffer) {
|
||
+ Ok(events) => {
|
||
+ for event in events {
|
||
+ log::debug!("{}", event);
|
||
+ if event.usage_page == UsagePage::GenericDesktop as u16 {
|
||
+ if event.usage == GenericDesktopUsage::X as u16 {
|
||
+ if event.relative {
|
||
+ mouse_dx += event.value as i32;
|
||
+ } else {
|
||
+ mouse_pos.0 = event.value as i32;
|
||
+ }
|
||
+ } else if event.usage == GenericDesktopUsage::Y as u16 {
|
||
+ if event.relative {
|
||
+ mouse_dy += event.value as i32;
|
||
+ } else {
|
||
+ mouse_pos.1 = event.value as i32;
|
||
+ }
|
||
+ } else if event.usage == GenericDesktopUsage::Wheel as u16 {
|
||
+ if event.relative {
|
||
+ scroll_y += event.value as i32;
|
||
+ } else {
|
||
+ log::warn!("absolute mouse wheel not supported");
|
||
+ }
|
||
+ } else if event.usage == GenericDesktopUsage::Z as u16 {
|
||
+ if event.relative && event.value != 0 {
|
||
+ let scroll_event = orbclient::event::ScrollEvent {
|
||
+ x: event.value as i32,
|
||
+ y: 0,
|
||
+ };
|
||
+ if let Err(err) = display.write_event(scroll_event.to_event()) {
|
||
+ log::warn!("failed to send hscroll: {}", err);
|
||
+ }
|
||
+ }
|
||
+ } else {
|
||
+ 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 u16 {
|
||
+ let pressed = event.value != 0;
|
||
+ 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);
|
||
+ } else if event.usage_page == UsagePage::Button as u16 {
|
||
+ let btn_idx = event.usage as usize;
|
||
+ if btn_idx > 0 && btn_idx <= MAX_MOUSE_BUTTONS {
|
||
+ buttons[btn_idx - 1] = event.value != 0;
|
||
+ } else if btn_idx > MAX_MOUSE_BUTTONS {
|
||
+ log::debug!(
|
||
+ "ignoring button {} (max {})",
|
||
+ btn_idx,
|
||
+ MAX_MOUSE_BUTTONS
|
||
+ );
|
||
+ }
|
||
+ } else if event.usage_page < 0xFF00 {
|
||
+ log::info!(
|
||
+ "unsupported usage 0x{:X}:0x{:X} value {}",
|
||
+ event.usage_page,
|
||
+ event.usage,
|
||
+ event.value
|
||
+ );
|
||
}
|
||
- } else {
|
||
- 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 u16 {
|
||
- 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);
|
||
- } else if event.usage_page == UsagePage::Button as u16 {
|
||
- 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 if event.usage_page >= 0xFF00 {
|
||
- // Ignore vendor defined event
|
||
- } else {
|
||
- log::info!(
|
||
- "unsupported usage 0x{:X}:0x{:X} value {}",
|
||
- event.usage_page,
|
||
- event.usage,
|
||
- event.value
|
||
- );
|
||
+ }
|
||
+ Err(err) => {
|
||
+ log::warn!("failed to parse HID report: {}", err);
|
||
}
|
||
}
|
||
|
||
if mouse_pos != last_mouse_pos {
|
||
last_mouse_pos = mouse_pos;
|
||
-
|
||
- // TODO
|
||
// ps2d uses 0..=65535 as range, while usb uses 0..=32767. orbital
|
||
// expects the former range, so multiply by two here to temporarily
|
||
- // align with orbital expectation. This workaround will make cursor
|
||
- // looks out of sync in QEMU using virtio-vga with usb-tablet.
|
||
+ // align with orbital expectation.
|
||
let mouse_event = orbclient::event::MouseEvent {
|
||
x: mouse_pos.0 * 2,
|
||
y: mouse_pos.1 * 2,
|
||
};
|
||
-
|
||
- match display.write_event(mouse_event.to_event()) {
|
||
- Ok(_) => (),
|
||
- Err(err) => {
|
||
- log::warn!("failed to send mouse event to orbital: {}", err);
|
||
- }
|
||
+ if let Err(err) = display.write_event(mouse_event.to_event()) {
|
||
+ log::warn!("failed to send mouse event: {}", err);
|
||
}
|
||
}
|
||
|
||
if mouse_dx != 0 || mouse_dy != 0 {
|
||
- // TODO: This is a filter to prevent random mouse jumps
|
||
if mouse_dx > -127 && mouse_dx < 127 {
|
||
let mouse_event = orbclient::event::MouseRelativeEvent {
|
||
dx: mouse_dx,
|
||
dy: mouse_dy,
|
||
};
|
||
-
|
||
- match display.write_event(mouse_event.to_event()) {
|
||
- Ok(_) => (),
|
||
- Err(err) => {
|
||
- log::warn!("failed to send mouse event to orbital: {}", err);
|
||
- }
|
||
+ if let Err(err) = display.write_event(mouse_event.to_event()) {
|
||
+ log::warn!("failed to send relative mouse event: {}", err);
|
||
}
|
||
}
|
||
}
|
||
|
||
if scroll_y != 0 {
|
||
let scroll_event = orbclient::event::ScrollEvent { x: 0, y: scroll_y };
|
||
-
|
||
- match display.write_event(scroll_event.to_event()) {
|
||
- Ok(_) => (),
|
||
- Err(err) => {
|
||
- log::warn!("failed to send scroll event to orbital: {}", err);
|
||
- }
|
||
+ if let Err(err) = display.write_event(scroll_event.to_event()) {
|
||
+ log::warn!("failed to send scroll event: {}", err);
|
||
}
|
||
}
|
||
|
||
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_event(button_event.to_event()) {
|
||
- Ok(_) => (),
|
||
- Err(err) => {
|
||
- log::warn!("failed to send button event to orbital: {}", err);
|
||
- }
|
||
+ if let Err(err) = display.write_event(button_event.to_event()) {
|
||
+ log::warn!("failed to send button event: {}", err);
|
||
}
|
||
}
|
||
-
|
||
- // log::trace!("took {}ms", timer.elapsed().as_millis())
|
||
}
|
||
}
|