Add HID report dumper

This commit is contained in:
John 2025-08-02 18:05:45 -04:00
parent 0bb97b167f
commit 24539a95d2
2 changed files with 77 additions and 7 deletions

View File

@ -18,6 +18,19 @@ pub mod descriptor {
}
}
mod endpoint {
/// READ from the endpoint
pub const READ: u8 = 0x80;
/// WRITE to the endpoint
pub const WRITE: u8 = 0x00;
/// HID endpoint for getting HID data
pub const HID: u8 = 0x01;
/// Control endpoint for control transactions
pub const CON: u8 = 0x02;
}
use endpoint::*;
pub struct Controller<'t, Ctx: rusb::UsbContext> {
device: &'t DeviceHandle<Ctx>,
verbose: bool,
@ -51,14 +64,14 @@ impl<'t, Ctx: rusb::UsbContext> Controller<'t, Ctx> {
}
fn write(&self, data: &[u8], timeout: Duration) -> rusb::Result<usize> {
self.device.write_bulk(0x02, data, timeout)
self.device.write_bulk(WRITE | CON, data, timeout)
}
fn read(&self, buf: &mut [u8], timeout: Duration) -> rusb::Result<usize> {
self.device.read_bulk(0x82, buf, timeout)
self.device.read_bulk(READ | CON, buf, timeout)
}
pub fn init(&self) -> rusb::Result<usize> {
pub fn init(&self, imu: bool) -> rusb::Result<usize> {
let mut out: usize = 0;
let timeout = Duration::from_secs(1);
@ -74,10 +87,14 @@ impl<'t, Ctx: rusb::UsbContext> Controller<'t, Ctx> {
out += self.dbg_write(UNKNOWN_COMMAND_0x15_ARG_0x03, timeout)?;
out += self.set_player_led(0)?;
out += self.dbg_write(IMU_COMMAND_0x02, timeout)?;
if imu {
out += self.dbg_write(IMU_COMMAND_0x02, timeout)?; // Enable IMU?
}
out += self.dbg_write(OUT_UNKNOWN_COMMAND_0x11, timeout)?;
out += self.dbg_write(UNKNOWN_COMMAND_0x0A, timeout)?;
out += self.dbg_write(IMU_COMMAND_0x04, timeout)?;
if imu {
out += self.dbg_write(IMU_COMMAND_0x04, timeout)?; // Lock and enable IMU output?
}
out += self.dbg_write(ENABLE_HAPTICS, timeout)?;
out += self.dbg_write(OUT_UNKNOWN_COMMAND_0x10, timeout)?;
out += self.dbg_write(OUT_UNKNOWN_COMMAND_0x01, timeout)?;
@ -141,6 +158,22 @@ impl<'t, Ctx: rusb::UsbContext> Controller<'t, Ctx> {
Ok(data)
}
pub fn detach(&self) -> rusb::Result<()> {
self.device.detach_kernel_driver(0)
}
pub fn attach(&self) -> rusb::Result<()> {
self.device.attach_kernel_driver(0)
}
pub fn read_hid(&self) -> rusb::Result<[u8; 64]> {
let mut buf = [0; 64];
self.device
.read_bulk(READ | HID, &mut buf, Duration::from_secs(10))?;
Ok(buf)
}
}
// --- Here there be raw USB bullshit --- \\

View File

@ -6,6 +6,7 @@ use procon2::{
Controller,
descriptor::{VID, pid},
};
use rusb::UsbContext;
#[rustfmt::skip]
const PLAYER: [u8; 16] = [
@ -33,10 +34,16 @@ struct Args {
verbose: bool,
/// Dumps the firmware of all connected controllers
dump: bool,
/// If set, doesn't init the IMU
imu_off: bool,
/// Dumps every N hid messages
hid: Option<usize>,
}
fn main() -> Result<(), Box<dyn Error>> {
let Args { verbose, dump } = Args::from_args();
#[rustfmt::skip]
let Args { verbose, dump, imu_off, hid } = Args::from_args();
let mut devices = vec![];
@ -47,13 +54,18 @@ fn main() -> Result<(), Box<dyn Error>> {
println!("Found player {}: {device:?}!", devices.len() + 1);
let device = device.open()?;
let controller = Controller::new(&device);
controller.verbose(verbose).init()?;
controller.verbose(verbose).init(!imu_off)?;
device.claim_interface(1)?;
devices.push(device);
}
}
if let Some(get_nth) = hid {
hid_dump(Controller::new(&devices[0]), get_nth)?;
return Ok(());
}
if dump {
for device in &devices {
let path = make_path(device)?;
@ -135,3 +147,28 @@ fn make_path<Ctx: rusb::UsbContext>(device: &rusb::DeviceHandle<Ctx>) -> rusb::R
Ok(out_path)
}
fn hid_dump<Ctx: UsbContext>(device: Controller<Ctx>, every_nth: usize) -> rusb::Result<()> {
device.detach().ok(); // may or may not have a kernel driver
let mut stdout = std::io::stdout().lock();
let mut leds = 0xe1u8;
loop {
for _ in 0..(every_nth.saturating_sub(1)) {
device.read_hid()?;
}
device.set_player_led(leds)?;
leds = leds.rotate_right(1);
let report = device.read_hid()?;
for (idx, byte) in report.iter().enumerate() {
if idx % 16 == 0 {
write!(stdout, "\n{idx:02x}: ").ok();
}
write!(stdout, " {byte:02x}").ok();
}
writeln!(stdout).ok();
}
// device.attach()?;
// Ok(())
}