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> { pub struct Controller<'t, Ctx: rusb::UsbContext> {
device: &'t DeviceHandle<Ctx>, device: &'t DeviceHandle<Ctx>,
verbose: bool, verbose: bool,
@ -51,14 +64,14 @@ impl<'t, Ctx: rusb::UsbContext> Controller<'t, Ctx> {
} }
fn write(&self, data: &[u8], timeout: Duration) -> rusb::Result<usize> { 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> { 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 mut out: usize = 0;
let timeout = Duration::from_secs(1); 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.dbg_write(UNKNOWN_COMMAND_0x15_ARG_0x03, timeout)?;
out += self.set_player_led(0)?; 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(OUT_UNKNOWN_COMMAND_0x11, timeout)?;
out += self.dbg_write(UNKNOWN_COMMAND_0x0A, 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(ENABLE_HAPTICS, timeout)?;
out += self.dbg_write(OUT_UNKNOWN_COMMAND_0x10, timeout)?; out += self.dbg_write(OUT_UNKNOWN_COMMAND_0x10, timeout)?;
out += self.dbg_write(OUT_UNKNOWN_COMMAND_0x01, timeout)?; out += self.dbg_write(OUT_UNKNOWN_COMMAND_0x01, timeout)?;
@ -141,6 +158,22 @@ impl<'t, Ctx: rusb::UsbContext> Controller<'t, Ctx> {
Ok(data) 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 --- \\ // --- Here there be raw USB bullshit --- \\

View File

@ -6,6 +6,7 @@ use procon2::{
Controller, Controller,
descriptor::{VID, pid}, descriptor::{VID, pid},
}; };
use rusb::UsbContext;
#[rustfmt::skip] #[rustfmt::skip]
const PLAYER: [u8; 16] = [ const PLAYER: [u8; 16] = [
@ -33,10 +34,16 @@ struct Args {
verbose: bool, verbose: bool,
/// Dumps the firmware of all connected controllers /// Dumps the firmware of all connected controllers
dump: bool, 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>> { 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![]; let mut devices = vec![];
@ -47,13 +54,18 @@ fn main() -> Result<(), Box<dyn Error>> {
println!("Found player {}: {device:?}!", devices.len() + 1); println!("Found player {}: {device:?}!", devices.len() + 1);
let device = device.open()?; let device = device.open()?;
let controller = Controller::new(&device); let controller = Controller::new(&device);
controller.verbose(verbose).init()?; controller.verbose(verbose).init(!imu_off)?;
device.claim_interface(1)?; device.claim_interface(1)?;
devices.push(device); devices.push(device);
} }
} }
if let Some(get_nth) = hid {
hid_dump(Controller::new(&devices[0]), get_nth)?;
return Ok(());
}
if dump { if dump {
for device in &devices { for device in &devices {
let path = make_path(device)?; let path = make_path(device)?;
@ -135,3 +147,28 @@ fn make_path<Ctx: rusb::UsbContext>(device: &rusb::DeviceHandle<Ctx>) -> rusb::R
Ok(out_path) 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(())
}