Implement gadget creation per controller

This commit is contained in:
Marc Riera 2023-03-17 00:35:16 +01:00
parent 6c8783bd93
commit 983e807fc7
7 changed files with 96 additions and 70 deletions

View file

@ -3,36 +3,46 @@ use std::fs::File;
use std::io::{Read, Write}; use std::io::{Read, Write};
use std::process::Command; use std::process::Command;
use crate::state::ControllerState; use crate::controller::physical::ControllerState;
mod dgoc44u; mod dgoc44u;
mod tcpp20009; mod tcpp20009;
mod vok00106; mod vok00106;
#[derive(PartialEq)] #[derive(PartialEq)]
pub enum ControllerModel { pub enum ControllerModel {
NONE,
DGOC44U, DGOC44U,
TCPP20009, TCPP20009,
VOK00106, VOK00106,
} }
pub fn set_model(state: &ControllerState) -> ControllerModel { pub struct DeviceDescriptor {
b_device_class: u8,
b_device_sub_class: u8,
id_vendor: u16,
id_product: u16,
i_manufacturer: &'static str,
i_product: &'static str,
i_serial_number: &'static str,
}
pub fn set_model(state: &ControllerState) -> Option<ControllerModel> {
if state.button_right { if state.button_right {
println!("Selected controller DGOC44-U."); println!("Selected controller DGOC44-U.");
init_gadget(); init_gadget(&dgoc44u::DEVICE_DESCRIPTOR, &dgoc44u::DESCRIPTORS, &dgoc44u::STRINGS);
return ControllerModel::DGOC44U; return Some(ControllerModel::DGOC44U);
} }
else if state.button_up { else if state.button_up {
println!("Selected controller TCPP-20009."); println!("Selected controller TCPP-20009.");
return ControllerModel::TCPP20009; init_gadget(&tcpp20009::DEVICE_DESCRIPTOR, &tcpp20009::DESCRIPTORS, &tcpp20009::STRINGS);
return Some(ControllerModel::TCPP20009);
} }
else if state.button_a { else if state.button_a {
println!("Selected controller VOK-00106."); println!("Selected controller VOK-00106.");
return ControllerModel::VOK00106; return Some(ControllerModel::VOK00106);
} }
else { else {
println!("No controller selected."); println!("No controller selected.");
return ControllerModel::NONE; return None;
} }
} }
@ -47,19 +57,21 @@ pub fn set_state(state: &mut ControllerState, model: &ControllerModel) {
ControllerModel::VOK00106 => { ControllerModel::VOK00106 => {
vok00106::update_gadget(state); vok00106::update_gadget(state);
} }
_ => (),
} }
} }
fn init_gadget() { fn init_gadget(device: &DeviceDescriptor, descriptors: &[u8], strings: &[u8]) {
Command::new("modprobe").args(["g_ffs"]).output(); Command::new("modprobe").arg("g_ffs")
Command::new("mkdir").args(["-p","/tmp/ffs-mascon"]).output(); .arg(String::from("bDeviceClass=")+&device.b_device_class.to_string())
Command::new("mount").args(["-t","functionfs","mascon","/tmp/ffs-mascon"]).output(); .arg(String::from("bDeviceSubclass=")+&device.b_device_sub_class.to_string())
.arg(String::from("idVendor=")+&device.id_vendor.to_string())
let descriptors = [0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, .arg(String::from("idProduct=")+&device.id_product.to_string())
0x09, 0x04, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x00, .arg(String::from("iManufacturer=")+device.i_manufacturer)
0x07, 0x05, 0x81, 0x03, 0x08, 0x00, 0x00]; .arg(String::from("iProduct=")+device.i_product)
let strings = [0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; .arg(String::from("iSerialNumber=")+device.i_serial_number)
.output().ok();
Command::new("mkdir").args(["-p","/tmp/ffs-mascon"]).output().ok();
Command::new("mount").args(["-t","functionfs","mascon","/tmp/ffs-mascon"]).output().ok();
thread::spawn(move || { thread::spawn(move || {
if let Ok(mut ep0) = File::open("/tmp/ffs-mascon/ep0") { if let Ok(mut ep0) = File::open("/tmp/ffs-mascon/ep0") {
@ -70,9 +82,9 @@ fn init_gadget() {
} }
}); });
if let Ok(mut ep0) = File::create("/tmp/ffs-mascon/ep0") { if let Ok(mut ep0) = File::create("/tmp/ffs-mascon/ep0") {
ep0.write(&descriptors).ok(); ep0.write(descriptors).ok();
println!("USB Gadget: Descriptors written to EP0"); println!("USB Gadget: Descriptors written to EP0");
ep0.write(&strings).ok(); ep0.write(strings).ok();
println!("USB Gadget: Strings written to EP0"); println!("USB Gadget: Strings written to EP0");
} }
} }

View file

@ -1,5 +1,14 @@
use bitflags::bitflags; use bitflags::bitflags;
use crate::state::ControllerState; use crate::controller::physical::ControllerState;
use crate::controller::emulated::DeviceDescriptor;
pub const DESCRIPTORS: [u8; 41] = [0x03, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 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];
pub const STRINGS: [u8; 16] = [0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
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"};
const POWER_NOTCHES: [u8; 6] = [0x81, 0x6D, 0x54, 0x3F, 0x21, 0x00]; 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]; const BRAKE_NOTCHES: [u8; 10] = [0x79, 0x8A, 0x94, 0x9A, 0xA2, 0xA8, 0xAF, 0xB2, 0xB5, 0xB9];
@ -20,6 +29,7 @@ bitflags! {
} }
} }
pub fn update_gadget(state: &mut ControllerState) { pub fn update_gadget(state: &mut ControllerState) {
// Calculate data for handles // Calculate data for handles
let power = POWER_NOTCHES[state.power as usize]; let power = POWER_NOTCHES[state.power as usize];

View file

@ -1,5 +1,13 @@
use bitflags::bitflags; use bitflags::bitflags;
use crate::state::ControllerState; use crate::controller::physical::ControllerState;
use crate::controller::emulated::DeviceDescriptor;
pub const DESCRIPTORS: [u8; 32] = [0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x09, 0x04, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x00,
0x07, 0x05, 0x81, 0x03, 0x08, 0x00, 0x14];
pub const STRINGS: [u8; 16] = [0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
pub const DEVICE_DESCRIPTOR: DeviceDescriptor = DeviceDescriptor{b_device_class: 0xFF, b_device_sub_class: 0x4, id_vendor: 0x0AE4, id_product: 0x0004, i_manufacturer: "TAITO", i_product: "Densha de Go! Plug & Play (Type 2 mode)", i_serial_number: "TCPP20010"};
const POWER_NOTCHES: [u8; 6] = [0x81, 0x6D, 0x54, 0x3F, 0x21, 0x00]; 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]; const BRAKE_NOTCHES: [u8; 10] = [0x79, 0x8A, 0x94, 0x9A, 0xA2, 0xA8, 0xAF, 0xB2, 0xB5, 0xB9];

View file

@ -1,4 +1,4 @@
use crate::state::ControllerState; use crate::controller::physical::ControllerState;
const POWER_NOTCHES: [&str; 6] = ["TSA50", "TSA55", "TSA65", "TSA75", "TSA85", "TSA95"]; const POWER_NOTCHES: [&str; 6] = ["TSA50", "TSA55", "TSA65", "TSA75", "TSA85", "TSA95"];
const BRAKE_NOTCHES: [&str; 10] = ["TSA50", "TSA45", "TSA35", "TSA25", "TSA15", "TSA05", "TSE99", "TSB40", "TSB30", "TSB20"]; const BRAKE_NOTCHES: [&str; 10] = ["TSA50", "TSA45", "TSA35", "TSA25", "TSA15", "TSA05", "TSE99", "TSB40", "TSB30", "TSB20"];

View file

@ -5,7 +5,23 @@ use std::io::Result;
use evdev::Device; use evdev::Device;
use evdev::Key; use evdev::Key;
use crate::state::ControllerState; #[derive(Default)]
pub struct ControllerState {
pub power: u8,
pub brake: u8,
pub button_select: bool,
pub button_start: bool,
pub button_a: bool,
pub button_b: bool,
pub button_c: bool,
pub button_d: bool,
pub button_up: bool,
pub button_down: bool,
pub button_left: bool,
pub button_right: bool,
pub lamp: bool,
pub rumble: bool,
}
const USED_KEYS: [Key; 26] = [Key::KEY_0, Key::KEY_1, Key::KEY_2, Key::KEY_3, Key::KEY_4, Key::KEY_5, const USED_KEYS: [Key; 26] = [Key::KEY_0, Key::KEY_1, Key::KEY_2, Key::KEY_3, Key::KEY_4, Key::KEY_5,
Key::KEY_B, Key::KEY_C, Key::KEY_D, Key::KEY_E, Key::KEY_F, Key::KEY_G, Key::KEY_H, Key::KEY_I, Key::KEY_J, Key::KEY_P, Key::KEY_B, Key::KEY_C, Key::KEY_D, Key::KEY_E, Key::KEY_F, Key::KEY_G, Key::KEY_H, Key::KEY_I, Key::KEY_J, Key::KEY_P,

View file

@ -1,12 +1,10 @@
mod controller; mod controller;
mod state;
use std::io::Result; use std::io::Result;
use std::process::Command; use std::process::Command;
use std::time::Duration; use std::time::Duration;
use std::thread::sleep; use std::thread::sleep;
use controller::emulated::ControllerModel;
use controller::physical::{set_lamp,set_rumble}; use controller::physical::{set_lamp,set_rumble};
fn main() -> Result<()> { fn main() -> Result<()> {
@ -17,35 +15,34 @@ fn main() -> Result<()> {
sleep(Duration::from_secs(3)); sleep(Duration::from_secs(3));
let mut controller_state = Default::default(); let mut controller_state = Default::default();
controller::physical::get_state(&mut controller_state, &dev); controller::physical::get_state(&mut controller_state, &dev);
let controller_model = controller::emulated::set_model(&controller_state);
// If no model selected, quit // Check selected controller model
if controller_model == ControllerModel::NONE { if let Some(controller_model) = controller::emulated::set_model(&controller_state) {
return Result::Ok(());
} // Stop main game
stop_game();
// Stop main game
stop_game(); // Vibrate to end selection mode
set_rumble(true);
// Vibrate to end selection mode sleep(Duration::from_millis(500));
set_rumble(true); set_rumble(false);
sleep(Duration::from_millis(500));
set_rumble(false); loop {
// Fetch events from input devices
loop { controller::physical::get_state(&mut controller_state, &dev);
// Fetch events from input devices
controller::physical::get_state(&mut controller_state, &dev); // Send input to virtual controller
controller::emulated::set_state(&mut controller_state, &controller_model);
// Send input to virtual controller
controller::emulated::set_state(&mut controller_state, &controller_model); // Update lamp and rumble
set_lamp(controller_state.lamp);
// Update lamp and rumble set_rumble(controller_state.rumble);
set_lamp(controller_state.lamp);
set_rumble(controller_state.rumble); // Wait between cycles
sleep(Duration::from_millis(20));
// Wait between cycles }
sleep(Duration::from_millis(20));
} }
return Result::Ok(());
}, },
Err(_e) => println!("ERROR: Could not read input devices! Exiting."), Err(_e) => println!("ERROR: Could not read input devices! Exiting."),
} }

View file

@ -1,17 +0,0 @@
#[derive(Default)]
pub struct ControllerState {
pub power: u8,
pub brake: u8,
pub button_select: bool,
pub button_start: bool,
pub button_a: bool,
pub button_b: bool,
pub button_c: bool,
pub button_d: bool,
pub button_up: bool,
pub button_down: bool,
pub button_left: bool,
pub button_right: bool,
pub lamp: bool,
pub rumble: bool,
}