From 029f5c7af31c2d4eb8c9b4f23978f85c8ab41943 Mon Sep 17 00:00:00 2001 From: Marc Riera Irigoyen Date: Sun, 16 Apr 2023 14:23:26 +0200 Subject: [PATCH] Implement HID report descriptor handling --- src/controller/emulated.rs | 97 +++++++++++++++++++--------- src/controller/emulated/dgoc44u.rs | 40 +++++++++++- src/controller/emulated/tcpp20009.rs | 2 +- 3 files changed, 108 insertions(+), 31 deletions(-) diff --git a/src/controller/emulated.rs b/src/controller/emulated.rs index 7f05c89..9db4a7f 100644 --- a/src/controller/emulated.rs +++ b/src/controller/emulated.rs @@ -20,7 +20,7 @@ const ENDPOINT0: &str = "/tmp/ffs/ep0"; const ENDPOINT1: &str = "/tmp/ffs/ep1"; const ANDROID_GADGET: &str = "/sys/class/android_usb/android0"; -#[derive(PartialEq)] +#[derive(PartialEq, Debug, Clone, Copy)] pub enum ControllerModel { DGOC44U, TCPP20009, @@ -43,49 +43,55 @@ pub struct DeviceDescriptor { } pub fn set_model(state: &ControllerState) -> Option { - if state.button_right { - println!("Selected controller DGOC44-U."); - init_gadget(&dgoc44u::DEVICE_DESCRIPTOR, &dgoc44u::DESCRIPTORS, &dgoc44u::STRINGS); - return Some(ControllerModel::DGOC44U); + let model; + let model_name; + let descriptors: (&DeviceDescriptor, &[u8], &[u8]); + if !state.button_right { + model_name = "DGOC44-U"; + model = ControllerModel::DGOC44U; + descriptors = (&dgoc44u::DEVICE_DESCRIPTOR, &dgoc44u::DESCRIPTORS, &dgoc44u::STRINGS); } else if state.button_d { - println!("Selected controller TCPP-20009."); - init_gadget(&tcpp20009::DEVICE_DESCRIPTOR, &tcpp20009::DESCRIPTORS, &tcpp20009::STRINGS); - return Some(ControllerModel::TCPP20009); + model_name = "TCPP-20009"; + model = ControllerModel::TCPP20009; + descriptors = (&tcpp20009::DEVICE_DESCRIPTOR, &tcpp20009::DESCRIPTORS, &tcpp20009::STRINGS); } else if state.button_b { - println!("Selected controller TCPP-20011."); - init_gadget(&tcpp20011::DEVICE_DESCRIPTOR, &tcpp20011::DESCRIPTORS, &tcpp20011::STRINGS); - return Some(ControllerModel::TCPP20011); + model_name = "TCPP-20011"; + model = ControllerModel::TCPP20011; + descriptors = (&tcpp20011::DEVICE_DESCRIPTOR, &tcpp20011::DESCRIPTORS, &tcpp20011::STRINGS); } else if state.button_c && state.power == 0 { - println!("Selected controller SOTP-031201 (P4/B7 mode)."); - init_gadget(&sotp031201_p4b7::DEVICE_DESCRIPTOR, &sotp031201_p4b7::DESCRIPTORS, &sotp031201_p4b7::STRINGS); - return Some(ControllerModel::SOTP031201P4B7); + model_name = "SOTP-031201 (P4/B7 mode)"; + model = ControllerModel::SOTP031201P4B7; + descriptors = (&sotp031201_p4b7::DEVICE_DESCRIPTOR, &sotp031201_p4b7::DESCRIPTORS, &sotp031201_p4b7::STRINGS); } else if state.button_c && state.power == 1 { - println!("Selected controller SOTP-031201 (P4/B2-B7 mode)."); - init_gadget(&sotp031201_p4b2b7::DEVICE_DESCRIPTOR, &sotp031201_p4b2b7::DESCRIPTORS, &sotp031201_p4b2b7::STRINGS); - return Some(ControllerModel::SOTP031201P4B2B7); + model_name = "SOTP-031201 (P4/B2-B7 mode)"; + model = ControllerModel::SOTP031201P4B2B7; + descriptors = (&sotp031201_p4b2b7::DEVICE_DESCRIPTOR, &sotp031201_p4b2b7::DESCRIPTORS, &sotp031201_p4b2b7::STRINGS); } else if state.button_c && state.power == 2 { - println!("Selected controller SOTP-031201 (P5/B5 mode)."); - init_gadget(&sotp031201_p5b5::DEVICE_DESCRIPTOR, &sotp031201_p5b5::DESCRIPTORS, &sotp031201_p5b5::STRINGS); - return Some(ControllerModel::SOTP031201P5B5); + model_name = "SOTP-031201 (P5/B5 mode)"; + model = ControllerModel::SOTP031201P5B5; + descriptors = (&sotp031201_p5b5::DEVICE_DESCRIPTOR, &sotp031201_p5b5::DESCRIPTORS, &sotp031201_p5b5::STRINGS); } else if state.button_c && state.power == 3 { - println!("Selected controller SOTP-031201 (P5/B7 mode)."); - init_gadget(&sotp031201_p5b7::DEVICE_DESCRIPTOR, &sotp031201_p5b7::DESCRIPTORS, &sotp031201_p5b7::STRINGS); - return Some(ControllerModel::SOTP031201P5B7); + model_name = "SOTP-031201 (P5/B7 mode)"; + model = ControllerModel::SOTP031201P5B7; + descriptors = (&sotp031201_p5b7::DEVICE_DESCRIPTOR, &sotp031201_p5b7::DESCRIPTORS, &sotp031201_p5b7::STRINGS); } /* else if state.button_a { - println!("Selected controller VOK-00106."); - return Some(ControllerModel::VOK00106); - } */ + model_name = "VOK-00106"; + model = ControllerModel::VOK00106; + } */ else { println!("No controller selected."); return None; } + println!("Selected controller {}.", model_name); + init_gadget(&model, descriptors); + return Some(model); } pub fn set_state(state: &mut ControllerState, model: &ControllerModel) { @@ -117,7 +123,32 @@ pub fn set_state(state: &mut ControllerState, model: &ControllerModel) { } } -fn init_gadget(device: &DeviceDescriptor, descriptors: &[u8], strings: &[u8]) { +pub fn handle_ctrl_transfer(model: ControllerModel, data: &[u8]) { + if data[1] == 6 { + let report; + match model { + ControllerModel::DGOC44U => { + report = Some(&dgoc44u::HID_REPORT_DESCRIPTOR); + } + _ => { + report = None; + } + } + match report { + Some(rep) => { + if let Ok(mut file) = File::create(ENDPOINT0) { + file.write(rep).ok(); + } + } + None => (), + } + } + else { + // Other control transfer, pass it to emulated controller loop + } +} + +fn init_gadget(model: &ControllerModel, (device, descriptors, strings): (&DeviceDescriptor, &[u8], &[u8])) { // Init g_ffs kernel module Command::new("modprobe").arg("g_ffs") .arg(String::from("bDeviceClass=")+&device.b_device_class.to_string()) @@ -131,11 +162,19 @@ fn init_gadget(device: &DeviceDescriptor, descriptors: &[u8], strings: &[u8]) { Command::new("mkdir").args(["-p",&FFS_MOUNT]).output().ok(); Command::new("mount").args(["-t","functionfs","ffs",&FFS_MOUNT]).output().ok(); + let controller_model = model.clone(); + thread::spawn(move || { if let Ok(mut ep0) = File::open(&ENDPOINT0) { - let mut buffer = [0; 4]; + let mut buffer = [0; 12]; loop { - ep0.read(&mut buffer).ok(); + if let Ok(_result) = ep0.read(&mut buffer) { + if buffer[8] == 0x4 { + // Control transfer received + handle_ctrl_transfer(controller_model, &buffer[0..8]); + } + + } } } }); diff --git a/src/controller/emulated/dgoc44u.rs b/src/controller/emulated/dgoc44u.rs index b7cbdea..a77685d 100644 --- a/src/controller/emulated/dgoc44u.rs +++ b/src/controller/emulated/dgoc44u.rs @@ -4,7 +4,10 @@ use bitflags::bitflags; use crate::controller::physical::ControllerState; use crate::controller::emulated::{DeviceDescriptor, ENDPOINT1}; -pub const DESCRIPTORS: [u8; 41] = [0x01, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +pub const DESCRIPTORS: [u8; 66] = [0x01, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, +0x09, 0x04, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x00, +0x09, 0x21, 0x11, 0x01, 0x00, 0x01, 0x22, 0x3F, 0x00, +0x07, 0x05, 0x81, 0x03, 0x08, 0x00, 0x14, 0x09, 0x04, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x00, 0x09, 0x21, 0x11, 0x01, 0x00, 0x01, 0x22, 0x3F, 0x00, 0x07, 0x05, 0x81, 0x03, 0x08, 0x00, 0x14]; @@ -12,6 +15,41 @@ pub const STRINGS: [u8; 16] = [0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0 pub const DEVICE_DESCRIPTOR: DeviceDescriptor = DeviceDescriptor{b_device_class: 0x0, b_device_sub_class: 0x0, id_vendor: 0x0AE4, id_product: 0x0003, i_manufacturer: "TAITO", i_product: "Densha de Go! Plug & Play", i_serial_number: "DGOC-44U"}; +pub const HID_REPORT_DESCRIPTOR: [u8; 63] = [ + 0x05, 0x01, // Usage Page (Generic Desktop Ctrls) + 0x09, 0x04, // Usage (Joystick) + 0xA1, 0x01, // Collection (Application) + 0x09, 0x01, // Usage (Pointer) + 0xA1, 0x00, // Collection (Physical) + 0x09, 0x30, // Usage (X) + 0x09, 0x31, // Usage (Y) + 0x15, 0x00, // Logical Minimum (0) + 0x26, 0xFF, 0x00, // Logical Maximum (255) + 0x75, 0x08, // Report Size (8) + 0x95, 0x02, // Report Count (2) + 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) + 0xC0, // End Collection + 0x75, 0x08, // Report Size (8) + 0x95, 0x01, // Report Count (1) + 0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) + 0x05, 0x09, // Usage Page (Button) + 0x19, 0x01, // Usage Minimum (0x01) + 0x29, 0x06, // Usage Maximum (0x06) + 0x15, 0x00, // Logical Minimum (0) + 0x25, 0x01, // Logical Maximum (1) + 0x35, 0x00, // Physical Minimum (0) + 0x45, 0x01, // Physical Maximum (1) + 0x75, 0x01, // Report Size (1) + 0x95, 0x06, // Report Count (6) + 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) + 0x95, 0x02, // Report Count (2) + 0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) + 0x75, 0x08, // Report Size (8) + 0x95, 0x02, // Report Count (2) + 0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) + 0xC0 // End Collection +]; + const POWER_NOTCHES: [u8; 6] = [0x81, 0x6D, 0x54, 0x3F, 0x21, 0x00]; const BRAKE_NOTCHES: [u8; 10] = [0x79, 0x8A, 0x94, 0x9A, 0xA2, 0xA8, 0xAF, 0xB2, 0xB5, 0xB9]; diff --git a/src/controller/emulated/tcpp20009.rs b/src/controller/emulated/tcpp20009.rs index 79a31cd..705832b 100644 --- a/src/controller/emulated/tcpp20009.rs +++ b/src/controller/emulated/tcpp20009.rs @@ -59,4 +59,4 @@ pub fn update_gadget(state: &mut ControllerState) { if let Ok(mut file) = File::create(ENDPOINT1) { file.write(&data).ok(); } -} \ No newline at end of file +}