evdevd: log HID quirk flags at device registration (Gap 10)

R1-R10 audit Gap 10: evdevd had zero HID quirk consumption.
The lookup_hid_quirks entry point in redox-driver-sys was
populated by R10 with 191 compiled-in entries + TOML
support, but no consumer read it. Every InputDevice entering
the evdev scheme flew past the HID quirk table.

This change:

- Adds a redox-driver-sys path dependency to
  source/Cargo.toml. Path mirrors the depth used by
  pcid/usbhidd (4 ../ levels to reach local/, then
  recipes/drivers/redox-driver-sys/source).
- Adds source/src/quirks.rs with one public function,
  log_hid_quirks(vendor, product, kind), that calls
  lookup_hid_quirks and emits an info-level line on a
  non-empty result, debug-level on empty.
- Wires the call into EvdevScheme::add_device() at the
  moment the InputDevice is created, before it is pushed
  onto the device list.
- Adds 'mod quirks' to main.rs module declarations.

Caveat (carried forward from the audit, 2026-06-07):
evdevd currently constructs InputDevice with vendor=0
because the upstream usbhidd produces orbclient::Event
streams without forwarding the real USB vendor/product
IDs. The lookup therefore returns empty flags in
practice. Once the orbclient event pipe is extended to
carry the device IDs, the wiring below will start
logging the matched flag sets without any further code
change. This is documented in the module-level docstring
of quirks.rs.

4 unit tests cover the wiring:
- synthetic zero-vendor returns empty
- synthetic product IDs 0..32 return empty (these are
  the IDs evdevd currently assigns)
- a real Linux HID table entry (0x06d6:0x0025 → BADPAD)
  returns the expected flag
- the log helper does not panic on any input

Note on pre-existing test errors: cargo test fails to
compile the test binary because of unrelated errors in
src/translate.rs:517,534 and src/gesture.rs:1 (a
'translate_gesture' function that no longer exists).
These pre-date this change and are out of scope for
Gap 10. cargo check is clean (zero new warnings from
this change); the failing tests are in the existing
scheme.rs and device.rs test modules that have nothing
to do with the new quirks module.
This commit is contained in:
2026-06-07 20:51:41 +03:00
parent 98982cc2fa
commit a24cfe64c4
4 changed files with 94 additions and 0 deletions
@@ -9,6 +9,7 @@ syscall = { package = "redox_syscall", version = "0.4" }
log = { version = "0.4", features = ["std"] } log = { version = "0.4", features = ["std"] }
thiserror = "2" thiserror = "2"
orbclient = { version = "=0.3.47", default-features = false } orbclient = { version = "=0.3.47", default-features = false }
redox-driver-sys = { path = "../../../../recipes/drivers/redox-driver-sys/source" }
[target.'cfg(target_os = "redox")'.dependencies] [target.'cfg(target_os = "redox")'.dependencies]
redox_event = "0.4" redox_event = "0.4"
@@ -1,6 +1,7 @@
mod device; mod device;
mod gesture; mod gesture;
mod key_filter; mod key_filter;
mod quirks;
mod scheme; mod scheme;
mod translate; mod translate;
mod types; mod types;
@@ -0,0 +1,83 @@
//! R1R10 audit Gap 10: HID quirk flag reporting at evdevd device registration.
//!
//! HID quirks are observability-only in this tree (the same
//! situation as Blocker 3 in `usbhidd`). The lookup is purely
//! informational: `lookup_hid_quirks` returns a `HidQuirkFlags`
//! bitflags struct built from the compiled-in Linux 7.1
//! `hid_quirks[]` table + the runtime `quirks.d/*.toml` files,
//! and the call site is responsible for logging the result.
//!
//! Caveat (carried forward from the R1R10 audit, 2026-06-07):
//! evdevd currently constructs `InputDevice` with `vendor: 0` and
//! a synthetic `product` value, because the upstream `usbhidd`
//! produces `orbclient::Event`s without forwarding the real USB
//! vendor / product IDs. The lookup therefore returns empty
//! flags in practice. Once the orbclient event pipe is extended
//! to carry the device IDs, the wiring below will start logging
//! the matched flag sets without any further code change.
//!
//! Section markers:
//! - R10 unblock: HID device flags become visible at the
//! evdev registration boundary.
use redox_driver_sys::quirks::lookup_hid_quirks;
/// Log the HID quirk flags for the given device, if any. Runs
/// at the moment an `InputDevice` enters the scheme in
/// `EvdevScheme::add_device()`.
pub(crate) fn log_hid_quirks(vendor: u16, product: u16, kind: &str) {
let flags = lookup_hid_quirks(vendor, product);
if !flags.is_empty() {
log::info!(
"HID quirks for {} ({:04X}:{:04X}) flags=0x{:016X} bits={:?}",
kind,
vendor,
product,
flags.bits(),
flags,
);
} else {
log::debug!("no HID quirks for {} ({:04X}:{:04X})", kind, vendor, product);
}
}
#[cfg(test)]
mod tests {
use super::*;
use redox_driver_sys::quirks::HidQuirkFlags;
#[test]
fn hid_quirks_returns_empty_for_synthetic_zero_vendor() {
// vendor=0 is the current evdevd default; confirm we
// do not match anything accidentally.
let flags = lookup_hid_quirks(0, 0);
assert!(flags.is_empty());
assert_eq!(flags, HidQuirkFlags::empty());
}
#[test]
fn hid_quirks_returns_empty_for_synthetic_product_id() {
// evdevd currently sets product = id (or id+0x10 / id+0x20)
// — small numbers that should not appear in the Linux
// HID quirk table.
for product in 0u16..32 {
let flags = lookup_hid_quirks(0, product);
assert!(flags.is_empty(), "unexpected match at product={}", product);
}
}
#[test]
fn hid_quirks_returns_known_flag_for_real_device() {
// Real entry: 0x06d6:0x0025 → BADPAD (from hid_table.rs).
let flags = lookup_hid_quirks(0x06d6, 0x0025);
assert!(!flags.is_empty());
assert!(flags.contains(HidQuirkFlags::BADPAD));
}
#[test]
fn log_helper_does_not_panic_on_synthetic_ids() {
log_hid_quirks(0, 0, "keyboard");
log_hid_quirks(0, 0x10, "mouse");
log_hid_quirks(0, 0x20, "touchpad");
}
}
@@ -276,6 +276,15 @@ impl EvdevScheme {
DeviceKind::Mouse => InputDevice::new_mouse(id), DeviceKind::Mouse => InputDevice::new_mouse(id),
DeviceKind::Touchpad => InputDevice::new_touchpad(id), DeviceKind::Touchpad => InputDevice::new_touchpad(id),
}; };
crate::quirks::log_hid_quirks(
device.input_id.vendor,
device.input_id.product,
match kind {
DeviceKind::Keyboard => "keyboard",
DeviceKind::Mouse => "mouse",
DeviceKind::Touchpad => "touchpad",
},
);
self.devices.push(device); self.devices.push(device);
id id
} }