diff --git a/.gitignore b/.gitignore index 50defa51..73a9d532 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ /tests/*/output.txt /tests/*/devices-output.txt /tests/ui/*/output.txt +/tests/hid/*/output.txt /vcpkg /wix/LICENSE-dynamic-libraries.txt /wix/LICENSE-packetry.txt diff --git a/Cargo.lock b/Cargo.lock index a4a66102..aa708170 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1125,6 +1125,15 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hidreport" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c588dafe431c43a03779628a0214172caef9c3ee2de85aaaafa3ac0a4be24da7" +dependencies = [ + "thiserror", +] + [[package]] name = "humansize" version = "2.1.3" @@ -1134,6 +1143,15 @@ dependencies = [ "libm", ] +[[package]] +name = "hut" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e272cc642f49588d581cdb3e7bc2d087d5ce943d03c74dc304235c05ba83b30" +dependencies = [ + "thiserror", +] + [[package]] name = "idna" version = "0.5.0" @@ -1548,7 +1566,9 @@ dependencies = [ "futures-lite", "futures-util", "gtk4", + "hidreport", "humansize", + "hut", "itertools", "libfuzzer-sys", "lrumap", diff --git a/Cargo.toml b/Cargo.toml index 63d69cff..f83c6335 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -53,6 +53,8 @@ anyhow = { version = "1.0.79", features = ["backtrace"] } crc = "3.2.1" usb-ids = "1.2024.4" dark-light = "1.1.1" +hidreport = "0.4.1" +hut = "0.2.1" [dev-dependencies] serde = { version = "1.0.196", features = ["derive"] } diff --git a/src/capture.rs b/src/capture.rs index a3bf48af..59e01008 100644 --- a/src/capture.rs +++ b/src/capture.rs @@ -1750,9 +1750,9 @@ impl ItemSource for CaptureReader { "End of SOF groups"), (Request(transfer), true) if detail => write!(s, "Control transfer on device {addr}\n{}", - transfer.summary()), + transfer.summary(true)), (Request(transfer), true) => write!(s, - "{}", transfer.summary()), + "{}", transfer.summary(false)), (IncompleteRequest, true) => write!(s, "Incomplete control transfer on device {addr}"), (Request(_) | IncompleteRequest, false) => write!(s, diff --git a/src/usb.rs b/src/usb.rs index 406b53b7..8615d9f2 100644 --- a/src/usb.rs +++ b/src/usb.rs @@ -1,10 +1,21 @@ use std::collections::BTreeMap; +use std::fmt::Formatter; use std::mem::size_of; use std::ops::Range; use bytemuck_derive::{Pod, Zeroable}; use bytemuck::pod_read_unaligned; use crc::{Crc, CRC_16_USB}; +use hidreport::{ + Field, + LogicalMaximum, + LogicalMinimum, + ParserError, + Report, + ReportCount, + ReportDescriptor, +}; +use itertools::{Itertools, Position}; use num_enum::{IntoPrimitive, FromPrimitive}; use derive_more::{From, Into, Display}; use usb_ids::FromId; @@ -1160,7 +1171,7 @@ pub struct ControlTransfer { } impl ControlTransfer { - pub fn summary(&self) -> String { + pub fn summary(&self, detail: bool) -> String { let request_type = self.fields.type_fields.request_type(); let direction = self.fields.type_fields.direction(); let request = self.fields.request; @@ -1221,6 +1232,14 @@ impl ControlTransfer { parts.push( format!(": {}", UTF16Bytes(&self.data[2..size]))); }, + (RequestType::Standard, + StandardRequest::GetDescriptor, + DescriptorType::Class(0x22)) + if detail && self.recipient_class == Some(ClassId::HID) => + { + parts.push( + format!("\n{}", HidReportDescriptor::from(&self.data))); + } (..) => {} }; let summary = parts.concat(); @@ -1281,9 +1300,152 @@ impl std::fmt::Display for UTF16ByteVec { } } +struct HidReportDescriptor(Result); + +impl HidReportDescriptor { + fn from(data: &[u8]) -> Self { + Self(ReportDescriptor::try_from(data)) + } +} + +impl std::fmt::Display for HidReportDescriptor{ + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> { + match &self.0 { + Ok(desc) => { + for report in desc.input_reports() { + write_report(f, "Input", report)?; + } + for report in desc.output_reports() { + write_report(f, "Output", report)?; + } + }, + Err(parse_err) => write!(f, + "\nFailed to parse report descriptor: {parse_err}")?, + } + Ok(()) + } +} + +fn write_report(f: &mut Formatter<'_>, kind: &str, report: &impl Report) + -> Result<(), std::fmt::Error> +{ + use Field::*; + use Position::*; + write!(f, "\n○ {kind} report ")?; + match (report.report_id(), report.size_in_bytes()) { + (Some(id), 1) => writeln!(f, "#{id} (1 byte):")?, + (Some(id), n) => writeln!(f, "#{id} ({n} bytes):")?, + (None, 1) => writeln!(f, "(1 byte):")?, + (None, n) => writeln!(f, "({n} bytes):")?, + } + for (position, field) in report.fields().iter().with_position() { + match position { + First | Middle => write!(f, "├── ")?, + Last | Only => write!(f, "└── ")?, + }; + match &field { + Array(array) => { + write!(f, "Array of {} {}: ", + array.report_count, + if array.report_count == ReportCount::from(1) { + "button" + } else { + "buttons" + } + )?; + write_bits(f, &array.bits)?; + let usage_range = array.usage_range(); + write!(f, " [")?; + write_usage(f, &usage_range.minimum())?; + write!(f, " — ")?; + write_usage(f, &usage_range.maximum())?; + write!(f, "]")?; + } + Variable(var) => { + write_usage(f, &var.usage)?; + write!(f, ": ")?; + write_bits(f, &var.bits)?; + let bit_count = var.bits.end - var.bits.start; + if bit_count > 1 { + let max = (1 << bit_count) - 1; + if var.logical_minimum != LogicalMinimum::from(0) || + var.logical_maximum != LogicalMaximum::from(max) + { + write!(f, " (values {} to {})", + var.logical_minimum, + var.logical_maximum)?; + } + } + }, + Constant(constant) => { + write!(f, "Padding: ")?; + write_bits(f, &constant.bits)?; + }, + }; + writeln!(f)?; + } + Ok(()) +} + +fn write_usage(f: &mut Formatter, usage: T) + -> Result<(), std::fmt::Error> +where u32: From +{ + let usage_code: u32 = usage.into(); + match hut::Usage::try_from(usage_code) { + Ok(usage) => write!(f, "{usage}")?, + Err(_) => { + let page: u16 = (usage_code >> 16) as u16; + let id: u16 = usage_code as u16; + match hut::UsagePage::try_from(page) { + Ok(page) => write!(f, + "{} usage 0x{id:02X}", page.name())?, + Err(_) => write!(f, + "Unknown page 0x{page:02X} usage 0x{id:02X}")?, + } + } + }; + Ok(()) +} + +fn write_bits(f: &mut Formatter, bit_range: &Range) + -> Result<(), std::fmt::Error> +{ + let bit_count = bit_range.end - bit_range.start; + let byte_range = (bit_range.start / 8)..((bit_range.end - 1)/ 8); + let byte_count = byte_range.end - byte_range.start; + match (byte_count, bit_count) { + (_, 1) => write!(f, + "byte {} bit {}", + byte_range.start, + bit_range.start % 8)?, + (0, n) if n == 8 && bit_range.start % 8 == 0 => write!(f, + "byte {}", + byte_range.start)?, + (0, _) => write!(f, + "byte {} bits {}-{}", + byte_range.start, + bit_range.start % 8, (bit_range.end - 1) % 8)?, + (_, n) if n % 8 == 0 && bit_range.start % 8 == 0 => write!(f, + "bytes {}-{}", + byte_range.start, + byte_range.end)?, + (_, _) => write!(f, + "byte {} bit {} — byte {} bit {}", + byte_range.start, + bit_range.start % 8, + byte_range.end, + (bit_range.end - 1) % 8)?, + } + Ok(()) +} + #[cfg(test)] mod tests { use super::*; + use std::fs::File; + use std::io::{BufRead, BufReader, BufWriter, Write}; + use std::path::PathBuf; #[test] fn test_parse_sof() { @@ -1339,6 +1501,41 @@ mod tests { panic!("Expected Data but got {:?}", p); } } + + #[test] + fn test_parse_hid() { + let test_dir = PathBuf::from("./tests/hid/"); + let mut list_path = test_dir.clone(); + list_path.push("tests.txt"); + let list_file = File::open(list_path).unwrap(); + for test_name in BufReader::new(list_file).lines() { + let mut test_path = test_dir.clone(); + test_path.push(test_name.unwrap()); + let mut desc_path = test_path.clone(); + let mut ref_path = test_path.clone(); + let mut out_path = test_path.clone(); + desc_path.push("descriptor.bin"); + ref_path.push("reference.txt"); + out_path.push("output.txt"); + { + let data = std::fs::read(desc_path).unwrap(); + let descriptor = HidReportDescriptor::from(&data); + let out_file = File::create(out_path.clone()).unwrap(); + let mut writer = BufWriter::new(out_file); + write!(writer, "{descriptor}").unwrap(); + } + let ref_file = File::open(ref_path).unwrap(); + let out_file = File::open(out_path.clone()).unwrap(); + let ref_reader = BufReader::new(ref_file); + let out_reader = BufReader::new(out_file); + let mut out_lines = out_reader.lines(); + for line in ref_reader.lines() { + let expected = line.unwrap(); + let actual = out_lines.next().unwrap().unwrap(); + assert_eq!(actual, expected); + } + } + } } pub mod prelude { diff --git a/tests/hid/amp/descriptor.bin b/tests/hid/amp/descriptor.bin new file mode 100644 index 00000000..4b0412b2 Binary files /dev/null and b/tests/hid/amp/descriptor.bin differ diff --git a/tests/hid/amp/reference.txt b/tests/hid/amp/reference.txt new file mode 100644 index 00000000..0a03d669 --- /dev/null +++ b/tests/hid/amp/reference.txt @@ -0,0 +1,67 @@ + +○ Input report #3 (2 bytes): +├── Volume Increment: byte 1 bit 0 +├── Volume Decrement: byte 1 bit 1 +├── Play/Pause: byte 1 bit 2 +├── Voice Command: byte 1 bit 3 +├── Scan Previous Track: byte 1 bit 4 +├── Scan Next Track: byte 1 bit 5 +└── Padding: byte 1 bits 6-7 + +○ Input report #5 (17 bytes): +├── Consumer usage 0x00: byte 1 (values 0 to 1) +├── Consumer usage 0x00: byte 2 (values 0 to 1) +├── Consumer usage 0x00: byte 3 (values 0 to 1) +├── Consumer usage 0x00: byte 4 (values 0 to 1) +├── Consumer usage 0x00: byte 5 (values 0 to 1) +├── Consumer usage 0x00: byte 6 (values 0 to 1) +├── Consumer usage 0x00: byte 7 (values 0 to 1) +├── Consumer usage 0x00: byte 8 (values 0 to 1) +├── Consumer usage 0x00: byte 9 (values 0 to 1) +├── Consumer usage 0x00: byte 10 (values 0 to 1) +├── Consumer usage 0x00: byte 11 (values 0 to 1) +├── Consumer usage 0x00: byte 12 (values 0 to 1) +├── Consumer usage 0x00: byte 13 (values 0 to 1) +├── Consumer usage 0x00: byte 14 (values 0 to 1) +├── Consumer usage 0x00: byte 15 (values 0 to 1) +└── Consumer usage 0x00: byte 16 (values 0 to 1) + +○ Output report #4 (39 bytes): +├── Consumer usage 0x00: byte 1 (values 0 to 1) +├── Consumer usage 0x00: byte 2 (values 0 to 1) +├── Consumer usage 0x00: byte 3 (values 0 to 1) +├── Consumer usage 0x00: byte 4 (values 0 to 1) +├── Consumer usage 0x00: byte 5 (values 0 to 1) +├── Consumer usage 0x00: byte 6 (values 0 to 1) +├── Consumer usage 0x00: byte 7 (values 0 to 1) +├── Consumer usage 0x00: byte 8 (values 0 to 1) +├── Consumer usage 0x00: byte 9 (values 0 to 1) +├── Consumer usage 0x00: byte 10 (values 0 to 1) +├── Consumer usage 0x00: byte 11 (values 0 to 1) +├── Consumer usage 0x00: byte 12 (values 0 to 1) +├── Consumer usage 0x00: byte 13 (values 0 to 1) +├── Consumer usage 0x00: byte 14 (values 0 to 1) +├── Consumer usage 0x00: byte 15 (values 0 to 1) +├── Consumer usage 0x00: byte 16 (values 0 to 1) +├── Consumer usage 0x00: byte 17 (values 0 to 1) +├── Consumer usage 0x00: byte 18 (values 0 to 1) +├── Consumer usage 0x00: byte 19 (values 0 to 1) +├── Consumer usage 0x00: byte 20 (values 0 to 1) +├── Consumer usage 0x00: byte 21 (values 0 to 1) +├── Consumer usage 0x00: byte 22 (values 0 to 1) +├── Consumer usage 0x00: byte 23 (values 0 to 1) +├── Consumer usage 0x00: byte 24 (values 0 to 1) +├── Consumer usage 0x00: byte 25 (values 0 to 1) +├── Consumer usage 0x00: byte 26 (values 0 to 1) +├── Consumer usage 0x00: byte 27 (values 0 to 1) +├── Consumer usage 0x00: byte 28 (values 0 to 1) +├── Consumer usage 0x00: byte 29 (values 0 to 1) +├── Consumer usage 0x00: byte 30 (values 0 to 1) +├── Consumer usage 0x00: byte 31 (values 0 to 1) +├── Consumer usage 0x00: byte 32 (values 0 to 1) +├── Consumer usage 0x00: byte 33 (values 0 to 1) +├── Consumer usage 0x00: byte 34 (values 0 to 1) +├── Consumer usage 0x00: byte 35 (values 0 to 1) +├── Consumer usage 0x00: byte 36 (values 0 to 1) +├── Consumer usage 0x00: byte 37 (values 0 to 1) +└── Consumer usage 0x00: byte 38 (values 0 to 1) diff --git a/tests/hid/headset/descriptor.bin b/tests/hid/headset/descriptor.bin new file mode 100644 index 00000000..96ea765f Binary files /dev/null and b/tests/hid/headset/descriptor.bin differ diff --git a/tests/hid/headset/reference.txt b/tests/hid/headset/reference.txt new file mode 100644 index 00000000..75727076 --- /dev/null +++ b/tests/hid/headset/reference.txt @@ -0,0 +1,19 @@ + +○ Input report (4 bytes): +├── Volume Increment: byte 0 bit 0 +├── Volume Decrement: byte 0 bit 1 +├── Mute: byte 0 bit 2 +├── Consumer usage 0x00: byte 0 bit 3 +├── Hook Switch: byte 0 bit 4 +├── Consumer usage 0x00: byte 0 bit 5 +├── Consumer usage 0x00: byte 0 bit 6 +├── Consumer usage 0x00: byte 0 bit 7 +├── Consumer usage 0x00: byte 1 +├── Consumer usage 0x00: byte 2 +└── Consumer usage 0x00: byte 3 + +○ Output report (4 bytes): +├── Consumer usage 0x00: byte 0 +├── Consumer usage 0x00: byte 1 +├── Consumer usage 0x00: byte 2 +└── Consumer usage 0x00: byte 3 diff --git a/tests/hid/hub/descriptor.bin b/tests/hid/hub/descriptor.bin new file mode 100644 index 00000000..a74df6b1 Binary files /dev/null and b/tests/hid/hub/descriptor.bin differ diff --git a/tests/hid/hub/reference.txt b/tests/hid/hub/reference.txt new file mode 100644 index 00000000..a4366881 --- /dev/null +++ b/tests/hid/hub/reference.txt @@ -0,0 +1,6 @@ + +○ Input report (64 bytes): +└── Array of 64 buttons: bytes 0-63 [Vendor Usage 0x01 — Vendor Usage 0x40] + +○ Output report (64 bytes): +└── Array of 64 buttons: bytes 0-63 [Vendor Usage 0x01 — Vendor Usage 0x40] diff --git a/tests/hid/keyboard-0/descriptor.bin b/tests/hid/keyboard-0/descriptor.bin new file mode 100644 index 00000000..f6453003 Binary files /dev/null and b/tests/hid/keyboard-0/descriptor.bin differ diff --git a/tests/hid/keyboard-0/reference.txt b/tests/hid/keyboard-0/reference.txt new file mode 100644 index 00000000..b1ded0c7 --- /dev/null +++ b/tests/hid/keyboard-0/reference.txt @@ -0,0 +1,20 @@ + +○ Input report (8 bytes): +├── Keyboard LeftControl: byte 0 bit 0 +├── Keyboard LeftShift: byte 0 bit 1 +├── Keyboard LeftAlt: byte 0 bit 2 +├── Keyboard Left GUI: byte 0 bit 3 +├── Keyboard RightControl: byte 0 bit 4 +├── Keyboard RightShift: byte 0 bit 5 +├── Keyboard RightAlt: byte 0 bit 6 +├── Keyboard Right GUI: byte 0 bit 7 +├── Padding: byte 1 +└── Array of 6 buttons: bytes 2-7 [Keyboard/Keypad usage 0x00 — Keyboard/Keypad usage 0xFF] + +○ Output report (1 byte): +├── Num Lock: byte 0 bit 0 +├── Caps Lock: byte 0 bit 1 +├── Scroll Lock: byte 0 bit 2 +├── Compose: byte 0 bit 3 +├── Kana: byte 0 bit 4 +└── Padding: byte 0 bits 5-7 diff --git a/tests/hid/keyboard-1/descriptor.bin b/tests/hid/keyboard-1/descriptor.bin new file mode 100644 index 00000000..99d7ebbe Binary files /dev/null and b/tests/hid/keyboard-1/descriptor.bin differ diff --git a/tests/hid/keyboard-1/reference.txt b/tests/hid/keyboard-1/reference.txt new file mode 100644 index 00000000..48464be9 --- /dev/null +++ b/tests/hid/keyboard-1/reference.txt @@ -0,0 +1,6 @@ + +○ Input report #3 (3 bytes): +└── Array of 1 button: bytes 1-2 [Pointer — System Display Toggle LCD Autoscale] + +○ Input report #4 (3 bytes): +└── Array of 1 button: bytes 1-2 [Consumer Control — AC Soft Key Left] diff --git a/tests/hid/kvm-0/descriptor.bin b/tests/hid/kvm-0/descriptor.bin new file mode 100644 index 00000000..f65d1560 Binary files /dev/null and b/tests/hid/kvm-0/descriptor.bin differ diff --git a/tests/hid/kvm-0/reference.txt b/tests/hid/kvm-0/reference.txt new file mode 100644 index 00000000..3b0be580 --- /dev/null +++ b/tests/hid/kvm-0/reference.txt @@ -0,0 +1,20 @@ + +○ Input report (8 bytes): +├── Keyboard LeftControl: byte 0 bit 0 +├── Keyboard LeftShift: byte 0 bit 1 +├── Keyboard LeftAlt: byte 0 bit 2 +├── Keyboard Left GUI: byte 0 bit 3 +├── Keyboard RightControl: byte 0 bit 4 +├── Keyboard RightShift: byte 0 bit 5 +├── Keyboard RightAlt: byte 0 bit 6 +├── Keyboard Right GUI: byte 0 bit 7 +├── Padding: byte 1 +└── Array of 6 buttons: bytes 2-7 [Keyboard/Keypad usage 0x00 — Keyboard LANG2] + +○ Output report (1 byte): +├── Num Lock: byte 0 bit 0 +├── Caps Lock: byte 0 bit 1 +├── Scroll Lock: byte 0 bit 2 +├── Compose: byte 0 bit 3 +├── Kana: byte 0 bit 4 +└── Padding: byte 0 bits 5-7 diff --git a/tests/hid/kvm-1/descriptor.bin b/tests/hid/kvm-1/descriptor.bin new file mode 100644 index 00000000..054edaa9 Binary files /dev/null and b/tests/hid/kvm-1/descriptor.bin differ diff --git a/tests/hid/kvm-1/reference.txt b/tests/hid/kvm-1/reference.txt new file mode 100644 index 00000000..b5929deb --- /dev/null +++ b/tests/hid/kvm-1/reference.txt @@ -0,0 +1,17 @@ + +○ Input report #1 (5 bytes): +├── Button 1: byte 1 bit 0 +├── Button 2: byte 1 bit 1 +├── Button 3: byte 1 bit 2 +├── Button 4: byte 1 bit 3 +├── Button 5: byte 1 bit 4 +├── Padding: byte 1 bits 5-7 +├── X: byte 2 (values -127 to 127) +├── Y: byte 3 (values -127 to 127) +└── Wheel: byte 4 (values -127 to 127) + +○ Input report #2 (2 bytes): +├── System Power Down: byte 1 bit 0 +├── System Sleep: byte 1 bit 1 +├── System Wake Up: byte 1 bit 2 +└── Padding: byte 1 bits 3-7 diff --git a/tests/hid/mouse/descriptor.bin b/tests/hid/mouse/descriptor.bin new file mode 100644 index 00000000..56a98697 Binary files /dev/null and b/tests/hid/mouse/descriptor.bin differ diff --git a/tests/hid/mouse/reference.txt b/tests/hid/mouse/reference.txt new file mode 100644 index 00000000..a0902adb --- /dev/null +++ b/tests/hid/mouse/reference.txt @@ -0,0 +1,12 @@ + +○ Input report #1 (7 bytes): +├── Button 1: byte 1 bit 0 +├── Button 2: byte 1 bit 1 +├── Button 3: byte 1 bit 2 +├── Button 4: byte 1 bit 3 +├── Button 5: byte 1 bit 4 +├── Padding: byte 1 bits 5-7 +├── X: byte 2 bit 0 — byte 3 bit 3 (values -2047 to 2047) +├── Y: byte 3 bit 4 — byte 4 bit 7 (values -2047 to 2047) +├── Wheel: byte 5 (values -127 to 127) +└── AC Pan: byte 6 (values -127 to 127) diff --git a/tests/hid/ps5/descriptor.bin b/tests/hid/ps5/descriptor.bin new file mode 100644 index 00000000..ef38d5b9 Binary files /dev/null and b/tests/hid/ps5/descriptor.bin differ diff --git a/tests/hid/ps5/reference.txt b/tests/hid/ps5/reference.txt new file mode 100644 index 00000000..7b0a12d2 --- /dev/null +++ b/tests/hid/ps5/reference.txt @@ -0,0 +1,139 @@ + +○ Input report #1 (64 bytes): +├── X: byte 1 +├── Y: byte 2 +├── Z: byte 3 +├── Rz: byte 4 +├── Rx: byte 5 +├── Ry: byte 6 +├── Vendor Usage 0x20: byte 7 +├── Hat Switch: byte 8 bits 0-3 (values 0 to 7) +├── Button 1: byte 8 bit 4 +├── Button 2: byte 8 bit 5 +├── Button 3: byte 8 bit 6 +├── Button 4: byte 8 bit 7 +├── Button 5: byte 9 bit 0 +├── Button 6: byte 9 bit 1 +├── Button 7: byte 9 bit 2 +├── Button 8: byte 9 bit 3 +├── Button 9: byte 9 bit 4 +├── Button 10: byte 9 bit 5 +├── Button 11: byte 9 bit 6 +├── Button 12: byte 9 bit 7 +├── Button 13: byte 10 bit 0 +├── Button 14: byte 10 bit 1 +├── Button 15: byte 10 bit 2 +├── Vendor Usage 0x21: byte 10 bit 3 +├── Vendor Usage 0x21: byte 10 bit 4 +├── Vendor Usage 0x21: byte 10 bit 5 +├── Vendor Usage 0x21: byte 10 bit 6 +├── Vendor Usage 0x21: byte 10 bit 7 +├── Vendor Usage 0x21: byte 11 bit 0 +├── Vendor Usage 0x21: byte 11 bit 1 +├── Vendor Usage 0x21: byte 11 bit 2 +├── Vendor Usage 0x21: byte 11 bit 3 +├── Vendor Usage 0x21: byte 11 bit 4 +├── Vendor Usage 0x21: byte 11 bit 5 +├── Vendor Usage 0x21: byte 11 bit 6 +├── Vendor Usage 0x21: byte 11 bit 7 +├── Vendor Usage 0x22: byte 12 +├── Vendor Usage 0x22: byte 13 +├── Vendor Usage 0x22: byte 14 +├── Vendor Usage 0x22: byte 15 +├── Vendor Usage 0x22: byte 16 +├── Vendor Usage 0x22: byte 17 +├── Vendor Usage 0x22: byte 18 +├── Vendor Usage 0x22: byte 19 +├── Vendor Usage 0x22: byte 20 +├── Vendor Usage 0x22: byte 21 +├── Vendor Usage 0x22: byte 22 +├── Vendor Usage 0x22: byte 23 +├── Vendor Usage 0x22: byte 24 +├── Vendor Usage 0x22: byte 25 +├── Vendor Usage 0x22: byte 26 +├── Vendor Usage 0x22: byte 27 +├── Vendor Usage 0x22: byte 28 +├── Vendor Usage 0x22: byte 29 +├── Vendor Usage 0x22: byte 30 +├── Vendor Usage 0x22: byte 31 +├── Vendor Usage 0x22: byte 32 +├── Vendor Usage 0x22: byte 33 +├── Vendor Usage 0x22: byte 34 +├── Vendor Usage 0x22: byte 35 +├── Vendor Usage 0x22: byte 36 +├── Vendor Usage 0x22: byte 37 +├── Vendor Usage 0x22: byte 38 +├── Vendor Usage 0x22: byte 39 +├── Vendor Usage 0x22: byte 40 +├── Vendor Usage 0x22: byte 41 +├── Vendor Usage 0x22: byte 42 +├── Vendor Usage 0x22: byte 43 +├── Vendor Usage 0x22: byte 44 +├── Vendor Usage 0x22: byte 45 +├── Vendor Usage 0x22: byte 46 +├── Vendor Usage 0x22: byte 47 +├── Vendor Usage 0x22: byte 48 +├── Vendor Usage 0x22: byte 49 +├── Vendor Usage 0x22: byte 50 +├── Vendor Usage 0x22: byte 51 +├── Vendor Usage 0x22: byte 52 +├── Vendor Usage 0x22: byte 53 +├── Vendor Usage 0x22: byte 54 +├── Vendor Usage 0x22: byte 55 +├── Vendor Usage 0x22: byte 56 +├── Vendor Usage 0x22: byte 57 +├── Vendor Usage 0x22: byte 58 +├── Vendor Usage 0x22: byte 59 +├── Vendor Usage 0x22: byte 60 +├── Vendor Usage 0x22: byte 61 +├── Vendor Usage 0x22: byte 62 +└── Vendor Usage 0x22: byte 63 + +○ Output report #2 (48 bytes): +├── Vendor Usage 0x23: byte 1 +├── Vendor Usage 0x23: byte 2 +├── Vendor Usage 0x23: byte 3 +├── Vendor Usage 0x23: byte 4 +├── Vendor Usage 0x23: byte 5 +├── Vendor Usage 0x23: byte 6 +├── Vendor Usage 0x23: byte 7 +├── Vendor Usage 0x23: byte 8 +├── Vendor Usage 0x23: byte 9 +├── Vendor Usage 0x23: byte 10 +├── Vendor Usage 0x23: byte 11 +├── Vendor Usage 0x23: byte 12 +├── Vendor Usage 0x23: byte 13 +├── Vendor Usage 0x23: byte 14 +├── Vendor Usage 0x23: byte 15 +├── Vendor Usage 0x23: byte 16 +├── Vendor Usage 0x23: byte 17 +├── Vendor Usage 0x23: byte 18 +├── Vendor Usage 0x23: byte 19 +├── Vendor Usage 0x23: byte 20 +├── Vendor Usage 0x23: byte 21 +├── Vendor Usage 0x23: byte 22 +├── Vendor Usage 0x23: byte 23 +├── Vendor Usage 0x23: byte 24 +├── Vendor Usage 0x23: byte 25 +├── Vendor Usage 0x23: byte 26 +├── Vendor Usage 0x23: byte 27 +├── Vendor Usage 0x23: byte 28 +├── Vendor Usage 0x23: byte 29 +├── Vendor Usage 0x23: byte 30 +├── Vendor Usage 0x23: byte 31 +├── Vendor Usage 0x23: byte 32 +├── Vendor Usage 0x23: byte 33 +├── Vendor Usage 0x23: byte 34 +├── Vendor Usage 0x23: byte 35 +├── Vendor Usage 0x23: byte 36 +├── Vendor Usage 0x23: byte 37 +├── Vendor Usage 0x23: byte 38 +├── Vendor Usage 0x23: byte 39 +├── Vendor Usage 0x23: byte 40 +├── Vendor Usage 0x23: byte 41 +├── Vendor Usage 0x23: byte 42 +├── Vendor Usage 0x23: byte 43 +├── Vendor Usage 0x23: byte 44 +├── Vendor Usage 0x23: byte 45 +├── Vendor Usage 0x23: byte 46 +└── Vendor Usage 0x23: byte 47 diff --git a/tests/hid/tests.txt b/tests/hid/tests.txt new file mode 100644 index 00000000..d874170c --- /dev/null +++ b/tests/hid/tests.txt @@ -0,0 +1,9 @@ +amp +headset +hub +keyboard-0 +keyboard-1 +kvm-0 +kvm-1 +mouse +ps5