NaxGCC-FW/src/hid/procon.rs

760 lines
24 KiB
Rust

///
/// The majority of the logic in this file is derived from HOJA-LIB-RP2040
/// https://github.com/HandHeldLegend/HOJA-LIB-RP2040
///
/// The original author gave their consent for this to be included in the NaxGCC firmware,
/// and for this file to be distributed under the GPLv3, see https://git.naxdy.org/NaxdyOrg/NaxGCC-FW/pulls/26
///
use core::ops::{Deref, DerefMut};
use defmt::{info, trace, Format};
use embassy_rp::clocks::RoscRng;
use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, signal::Signal};
use embassy_time::Instant;
use embassy_usb::{
class::hid::{ReportId, RequestHandler},
control::OutResponse,
};
use packed_struct::{derive::PackedStruct, PackedStruct};
use rand::RngCore;
use crate::{input::ControllerState, usb_comms::HidReportBuilder};
const SW_INFO_SET_MAC: u8 = 0x01;
const SW_CMD_SET_INPUT_MODE: u8 = 0x03;
const SW_CMD_GET_DEVINFO: u8 = 0x02;
const SW_CMD_SET_SHIPMODE: u8 = 0x08;
const SW_CMD_GET_SPI: u8 = 0x10;
const SW_CMD_SET_PAIRING: u8 = 0x01;
const SW_CMD_GET_TRIGGERET: u8 = 0x04;
const ACK_GET_DEVINFO: u8 = 0x82;
const ACK_GET_SPI: u8 = 0x90;
const ACK_SET_PAIRING: u8 = 0x81;
const ACK_GET_TRIGERET: u8 = 0x83;
const ACK_GENERIC: u8 = 0x80;
const RM_SEND_STATE: u8 = 0x30;
const PRO_CONTROLLER_STRING: [u8; 24] = [
0x00, 0x25, 0x08, 0x50, 0x72, 0x6F, 0x20, 0x43, 0x6F, 0x6E, 0x74, 0x72, 0x6F, 0x6C, 0x6C, 0x65,
0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68,
];
#[derive(Debug, Format, Clone, Copy)]
struct ProconRequestInfo {
report_id: ProconRequestId,
response_id: ProconResponseId,
command_id: u8,
raw_data: [u8; 64],
}
#[derive(Debug, Format, Clone, Copy)]
enum ProconRequestId {
GetInfo = 0x80,
Command = 0x01,
Rumble = 0x10,
}
#[derive(Debug, Format, Clone, Copy)]
enum ProconResponseId {
GetInfo = 0x81,
GetState = 0x30,
Command = 0x21,
}
static SIGNAL_PROCON_REQUEST: Signal<CriticalSectionRawMutex, ProconRequestInfo> = Signal::new();
#[rustfmt::skip]
pub const PROCON_REPORT_DESCRIPTOR: &[u8] = &[
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x15, 0x00, // Logical Minimum (0)
0x09, 0x04, // Usage (Joystick)
0xA1, 0x01, // Collection (Application)
0x85, 0x30, // Report ID (48)
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x05, 0x09, // Usage Page (Button)
0x19, 0x01, // Usage Minimum (0x01)
0x29, 0x0A, // Usage Maximum (0x0A)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x75, 0x01, // Report Size (1)
0x95, 0x0A, // Report Count (10)
0x55, 0x00, // Unit Exponent (0)
0x65, 0x00, // Unit (None)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x05, 0x09, // Usage Page (Button)
0x19, 0x0B, // Usage Minimum (0x0B)
0x29, 0x0E, // Usage Maximum (0x0E)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x75, 0x01, // Report Size (1)
0x95, 0x04, // Report Count (4)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x75, 0x01, // Report Size (1)
0x95, 0x02, // Report Count (2)
0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x0B, 0x01, 0x00, 0x01, 0x00, // Usage (0x010001)
0xA1, 0x00, // Collection (Physical)
0x0B, 0x30, 0x00, 0x01, 0x00, // Usage (0x010030)
0x0B, 0x31, 0x00, 0x01, 0x00, // Usage (0x010031)
0x0B, 0x32, 0x00, 0x01, 0x00, // Usage (0x010032)
0x0B, 0x35, 0x00, 0x01, 0x00, // Usage (0x010035)
0x15, 0x00, // Logical Minimum (0)
0x27, 0xFF, 0xFF, 0x00, 0x00, // Logical Maximum (65534)
0x75, 0x10, // Report Size (16)
0x95, 0x04, // Report Count (4)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xC0, // End Collection
0x0B, 0x39, 0x00, 0x01, 0x00, // Usage (0x010039)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x07, // Logical Maximum (7)
0x35, 0x00, // Physical Minimum (0)
0x46, 0x3B, 0x01, // Physical Maximum (315)
0x65, 0x14, // Unit (System: English Rotation, Length: Centimeter)
0x75, 0x04, // Report Size (4)
0x95, 0x01, // Report Count (1)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x05, 0x09, // Usage Page (Button)
0x19, 0x0F, // Usage Minimum (0x0F)
0x29, 0x12, // Usage Maximum (0x12)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x75, 0x01, // Report Size (1)
0x95, 0x04, // Report Count (4)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x75, 0x08, // Report Size (8)
0x95, 0x34, // Report Count (52)
0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x06, 0x00, 0xFF, // Usage Page (Vendor Defined 0xFF00)
0x85, 0x21, // Report ID (33)
0x09, 0x01, // Usage (0x01)
0x75, 0x08, // Report Size (8)
0x95, 0x3F, // Report Count (63)
0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x85, 0x81, // Report ID (-127)
0x09, 0x02, // Usage (0x02)
0x75, 0x08, // Report Size (8)
0x95, 0x3F, // Report Count (63)
0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x85, 0x01, // Report ID (1)
0x09, 0x03, // Usage (0x03)
0x75, 0x08, // Report Size (8)
0x95, 0x3F, // Report Count (63)
0x91, 0x83, // Output (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Volatile)
0x85, 0x10, // Report ID (16)
0x09, 0x04, // Usage (0x04)
0x75, 0x08, // Report Size (8)
0x95, 0x3F, // Report Count (63)
0x91, 0x83, // Output (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Volatile)
0x85, 0x80, // Report ID (-128)
0x09, 0x05, // Usage (0x05)
0x75, 0x08, // Report Size (8)
0x95, 0x3F, // Report Count (63)
0x91, 0x83, // Output (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Volatile)
0x85, 0x82, // Report ID (-126)
0x09, 0x06, // Usage (0x06)
0x75, 0x08, // Report Size (8)
0x95, 0x3F, // Report Count (63)
0x91, 0x83, // Output (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Volatile)
0xC0, // End Collection
// 203 bytes
];
#[derive(Clone, Copy, Debug, Format)]
struct ProconByteReport([u8; 64]);
impl Deref for ProconByteReport {
type Target = [u8; 64];
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for ProconByteReport {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl ProconByteReport {
fn set_report_id(&mut self, id: u8) {
self[0] = id;
}
fn set_battery_status(&mut self) {
self[2] = BatteryStatus::default()
.pack()
.expect("Failed to pack fake procon battery status")[0];
}
fn set_timer(&mut self) {
self[1] = Instant::now().as_millis() as u8;
}
fn set_ack(&mut self, ack: u8) {
self[13] = ack;
}
fn set_subcommand(&mut self, cmd: u8) {
self[14] = cmd;
}
fn set_devinfo(&mut self) {
self[15] = 0x04; // NS Firmware primary (4.x)
self[16] = 0x33; // NS Firmware secondary (x.21)
self[17] = 0x03; // Controller ID primary (Pro Controller)
self[18] = 0x02; // Controller ID secondary
self[25] = 0x01;
self[26] = 0x02;
}
fn sw_spi_readfromaddress(
&mut self,
offset_address: u8,
address: u8,
length: u8,
switch_host_address: &[u8],
) {
let read_info = [address, offset_address, 0x00, 0x00, length];
self[15..(15 + read_info.len())].copy_from_slice(&read_info);
let mut output_spi_data = [0u8; 30];
output_spi_data.iter_mut().enumerate().for_each(|(i, e)| {
*e = sw_spi_getaddressdata(offset_address, address + i as u8, switch_host_address)
});
self[20..(20 + length as usize)].copy_from_slice(&output_spi_data[..(length as usize)]);
}
fn set_trigerret(&mut self, time_10_ms: u16) {
let [upper_ms, lower_ms] = time_10_ms.to_be_bytes();
for i in 0..14 {
self[15 + i] = upper_ms;
self[16 + i] = lower_ms;
}
}
}
fn sw_spi_getaddressdata(offset_address: u8, address: u8, switch_host_address: &[u8]) -> u8 {
match offset_address {
0x00 => 0x00,
0x20..=0x40 => match address {
0x26 | 0x00 => 0x95,
// Size of pairing data
0x27 | 0x01 => 0x22,
// Checksum
0x28 | 0x29 | 0x02 | 0x03 => 0x00,
// Host BT address (Big-endian)
0x2A..=0x2F => switch_host_address[(address - 0x2a) as usize],
0x04..=0x09 => switch_host_address[(address - 4) as usize],
// Bluetooth LTK (Little-endian) NOT IMPLEMENTED YET
0x30..=0x3F => 0x00,
0x0A..=0x19 => 0x00,
// Host capability 0x68 is Nintendo Switch. 0x08 is PC
0x4A | 0x24 => 0x68,
0x4B | 0x25 => 0,
_ => 0x00,
},
0x50 => 0x00,
0x60 => match address {
0x00..0x0f => 0xff,
0x12 => 0x03,
0x13 => 0x02,
0x1b => 0x01,
0x20 => 35,
0x21 => 0,
0x22 => 185,
0x23 => 255,
0x24 => 26,
0x25 => 1,
0x26 => 0,
0x27 => 64,
0x28 => 0,
0x29 => 64,
0x2A => 0,
0x2B => 64,
0x2C => 1,
0x2D => 0,
0x2E => 1,
0x2F => 0,
0x30 => 1,
0x31 => 0,
0x32 => 0x3B,
0x33 => 0x34,
0x34 => 0x3B,
0x35 => 0x34,
0x36 => 0x3B,
0x37 => 0x34,
0x3d..=0x45 => mk_switch_analog_calibration_data()[(address - 0x3d) as usize],
0x46..=0x4e => mk_switch_analog_calibration_data()[(address - 0x3d) as usize],
0x4F => 0xFF,
0x50 => 26,
0x51 => 26,
0x52 => 26,
0x53..=0x55 => 94,
0x56 => 255,
0x57 => 255,
0x58 => 255,
0x59..=0x5B => 255,
0x5C => 0x01,
0x80 => 80,
0x81 => 253,
0x82 => 0,
0x83 => 0,
0x84 => 198,
0x85 => 15,
0x98 | 0x86 => 15,
0x99 | 0x87 => 48,
0x9A | 0x88 => 97,
0x9B | 0x89 => 174,
0x9C | 0x8A => 144,
0x9D | 0x8B => 217,
0x9E | 0x8C => 212,
0x9F | 0x8D => 20,
0xA0 | 0x8E => 84,
0xA1 | 0x8F => 65,
0xA2 | 0x90 => 21,
0xA3 | 0x91 => 84,
0xA4 | 0x92 => 199,
0xA5 | 0x93 => 121,
0xA6 | 0x94 => 156,
0xA7 | 0x95 => 51,
0xA8 | 0x96 => 54,
0xA9 | 0x97 => 99,
_ => 0,
},
0x80 => match address {
0x10..=0x1a => 0xff,
0x1b..=0x25 => 0xff,
0x26..=0x3f => 0xff,
_ => 0xff,
},
_ => 0xff,
}
}
fn mk_switch_analog_calibration_data() -> [u8; 18] {
fn switch_analog_encode(in_lower: u16, in_upper: u16) -> [u8; 3] {
let mut out = [0u8; 3];
[out[0], out[1]] = in_lower.to_le_bytes();
out[1] |= ((in_upper & 0xf) << 4) as u8;
out[2] = ((in_upper & 0xff0) >> 4) as u8;
out
}
const MIN: u16 = 128 << 4;
const MAXX: u16 = 128 << 4;
const CENTER: u16 = 128 << 4;
let mut out = [0u8; 18];
out[0..3].copy_from_slice(&switch_analog_encode(MAXX, MAXX));
out[3..6].copy_from_slice(&switch_analog_encode(CENTER, CENTER));
out[6..9].copy_from_slice(&switch_analog_encode(MIN, MIN));
out[9..12].copy_from_slice(&switch_analog_encode(CENTER, CENTER));
out[12..15].copy_from_slice(&switch_analog_encode(MIN, MIN));
out[15..18].copy_from_slice(&switch_analog_encode(MAXX, MAXX));
info!("Returning switch data: {:x}", out);
out
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default, PackedStruct, Format)]
#[packed_struct(bit_numbering = "lsb0", size_bytes = "1")]
pub struct ProconButtonsRight {
#[packed_field(bits = "0")]
pub button_y: bool,
#[packed_field(bits = "1")]
pub button_x: bool,
#[packed_field(bits = "2")]
pub button_b: bool,
#[packed_field(bits = "3")]
pub button_a: bool,
#[packed_field(bits = "4")]
pub trigger_r_sr: bool,
#[packed_field(bits = "5")]
pub trigger_r_sl: bool,
#[packed_field(bits = "6")]
pub trigger_r: bool,
#[packed_field(bits = "7")]
pub trigger_zr: bool,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default, PackedStruct, Format)]
#[packed_struct(bit_numbering = "lsb0", size_bytes = "1")]
pub struct ProconButtonsShared {
#[packed_field(bits = "0")]
pub button_minus: bool,
#[packed_field(bits = "1")]
pub button_plus: bool,
#[packed_field(bits = "2")]
pub button_sb_right: bool,
#[packed_field(bits = "3")]
pub button_sb_left: bool,
#[packed_field(bits = "4")]
pub button_home: bool,
#[packed_field(bits = "5")]
pub button_capture: bool,
#[packed_field(bits = "6")]
pub none: bool,
#[packed_field(bits = "7")]
pub change_grip_active: bool,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default, PackedStruct, Format)]
#[packed_struct(bit_numbering = "lsb0", size_bytes = "1")]
pub struct ProconButtonsLeft {
#[packed_field(bits = "0")]
pub dpad_down: bool,
#[packed_field(bits = "1")]
pub dpad_up: bool,
#[packed_field(bits = "2")]
pub dpad_right: bool,
#[packed_field(bits = "3")]
pub dped_left: bool,
#[packed_field(bits = "4")]
pub trigger_l_sr: bool,
#[packed_field(bits = "5")]
pub trigger_l_sl: bool,
#[packed_field(bits = "6")]
pub trigger_l: bool,
#[packed_field(bits = "7")]
pub trigger_zl: bool,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default, PackedStruct, Format)]
#[packed_struct(bit_numbering = "msb0", endian = "lsb", size_bytes = "9")]
pub struct ProconState {
#[packed_field(bits = "0..=7")]
pub buttons_right: ProconButtonsRight,
#[packed_field(bits = "8..=15")]
pub buttons_shared: ProconButtonsShared,
#[packed_field(bits = "16..=23")]
pub buttons_left: ProconButtonsLeft,
#[packed_field(bits = "24..=39")]
pub lstick_x: u16,
#[packed_field(bits = "40..=47")]
pub lstick_y: u8,
#[packed_field(bits = "48..=63")]
pub rstick_x: u16,
#[packed_field(bits = "64..=71")]
pub rstick_y: u8,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PackedStruct, Format)]
#[packed_struct(bit_numbering = "lsb0", endian = "msb", size_bytes = "1")]
struct BatteryStatus {
#[packed_field(bits = "0..=3")]
connection: u8,
#[packed_field(bits = "4..=7")]
battery_level: u8,
}
impl Default for BatteryStatus {
fn default() -> Self {
Self {
connection: 1,
battery_level: 8,
}
}
}
impl From<&ControllerState> for ProconState {
fn from(value: &ControllerState) -> Self {
Self {
buttons_left: ProconButtonsLeft {
dpad_down: value.dpad_down,
dpad_right: value.dpad_right,
dpad_up: value.dpad_up,
dped_left: value.dpad_left,
trigger_l: value.trigger_zl,
trigger_zl: value.trigger_l,
..Default::default()
},
buttons_right: ProconButtonsRight {
button_a: value.button_a,
button_b: value.button_b,
button_x: value.button_x,
button_y: value.button_y,
trigger_r: value.trigger_zr,
trigger_zr: value.trigger_r,
..Default::default()
},
buttons_shared: ProconButtonsShared {
button_plus: value.button_start && !value.trigger_zr,
button_home: value.button_start && value.trigger_zr,
..Default::default()
},
lstick_x: value.stick_state.ax as u16 * 16,
lstick_y: value.stick_state.ay,
rstick_x: value.stick_state.cx as u16 * 16,
rstick_y: value.stick_state.cy,
}
}
}
pub struct ProconReportBuilder {
switch_reporting_mode: u8,
switch_mac_address: [u8; 6],
switch_host_address: [u8; 6],
switch_ltk: [u8; 16],
}
impl Default for ProconReportBuilder {
fn default() -> Self {
Self {
switch_reporting_mode: 0,
switch_mac_address: gen_switch_mac_address(),
switch_host_address: [0u8; 6],
switch_ltk: gen_switch_ltk(),
}
}
}
fn gen_switch_mac_address() -> [u8; 6] {
let mut mac_addr = [0u8; 6];
mac_addr.iter_mut().for_each(|e| {
*e = RoscRng.next_u64() as u8;
});
mac_addr[0] &= 0xfe;
mac_addr[5] = 0x9b;
mac_addr
}
fn gen_switch_ltk() -> [u8; 16] {
let mut switch_ltk = [0u8; 16];
switch_ltk.iter_mut().for_each(|e| {
*e = RoscRng.next_u64() as u8;
});
switch_ltk
}
impl ProconReportBuilder {
fn get_info_report(&self, current_report_info: &ProconRequestInfo) -> [u8; 64] {
let mut report = ProconByteReport([0u8; 64]);
report.set_report_id(ProconResponseId::GetInfo as u8);
if current_report_info.command_id == SW_INFO_SET_MAC {
report[1] = SW_INFO_SET_MAC;
report[3] = 0x03;
self.switch_mac_address
.iter()
.rev()
.enumerate()
.for_each(|(i, e)| {
report[4 + i] = *e;
});
} else {
report[1] = current_report_info.command_id;
}
*report
}
fn get_state_report(&self, state: &ProconState) -> [u8; 64] {
static mut UNKNOWN: u8 = 0xA;
unsafe {
UNKNOWN = match UNKNOWN {
0xA => 0xB,
0xB => 0xC,
_ => 0xA,
};
}
let mut report = ProconByteReport([0u8; 64]);
let data = state
.pack()
.expect("Failed to pack pro controller input data");
report.set_report_id(ProconResponseId::GetState as u8);
report.set_timer();
report.set_battery_status();
report[3..=11].copy_from_slice(&data);
report[12] = unsafe { UNKNOWN };
*report
}
fn get_command_report(&mut self, current_report_info: &ProconRequestInfo) -> [u8; 64] {
let mut report = ProconByteReport([0u8; 64]);
report.set_report_id(ProconResponseId::Command as u8);
report.set_timer();
report.set_battery_status();
report.set_subcommand(current_report_info.command_id);
match current_report_info.command_id {
SW_CMD_SET_INPUT_MODE => {
report.set_ack(ACK_GENERIC);
self.switch_reporting_mode = current_report_info.raw_data[11];
info!(
"Switch reporting mode is now {:x}",
self.switch_reporting_mode
);
}
SW_CMD_GET_DEVINFO => {
report.set_ack(ACK_GET_DEVINFO);
report.set_devinfo();
}
SW_CMD_GET_SPI => {
report.set_ack(ACK_GET_SPI);
report.sw_spi_readfromaddress(
current_report_info.raw_data[12],
current_report_info.raw_data[11],
current_report_info.raw_data[15],
&self.switch_host_address,
);
}
SW_CMD_SET_SHIPMODE => {
report.set_ack(ACK_GENERIC);
}
SW_CMD_SET_PAIRING => {
report.set_ack(ACK_SET_PAIRING);
self.perform_pairing(&mut report, current_report_info);
}
SW_CMD_GET_TRIGGERET => {
report.set_ack(ACK_GET_TRIGERET);
report.set_trigerret(100);
}
_ => {
report.set_ack(ACK_GENERIC);
}
}
*report
}
fn perform_pairing(
&mut self,
report: &mut ProconByteReport,
current_report_info: &ProconRequestInfo,
) {
let pairing_phase = current_report_info.raw_data[11];
let host_address = &current_report_info.raw_data[12..];
match pairing_phase {
1 => {
self.switch_host_address
.iter_mut()
.enumerate()
.for_each(|(i, e)| *e = host_address[5 - i]);
report[16..=21].copy_from_slice(&self.switch_mac_address);
report[22..(22 + PRO_CONTROLLER_STRING.len())]
.copy_from_slice(&PRO_CONTROLLER_STRING);
}
2 => {
report[15] = 2;
report[16..(16 + self.switch_ltk.len())].copy_from_slice(&self.switch_ltk);
}
3 => {
report[15] = 3;
}
_ => {}
}
}
}
impl HidReportBuilder<64> for ProconReportBuilder {
async fn get_hid_report(&mut self, state: &ControllerState) -> [u8; 64] {
let current_report_info = if self.switch_reporting_mode == RM_SEND_STATE {
SIGNAL_PROCON_REQUEST.try_take()
} else {
Some(SIGNAL_PROCON_REQUEST.wait().await)
};
if let Some(current_report_info) = current_report_info {
match current_report_info.report_id {
ProconRequestId::GetInfo => self.get_info_report(&current_report_info),
ProconRequestId::Command => self.get_command_report(&current_report_info),
ProconRequestId::Rumble => self.get_state_report(&ProconState::from(state)),
}
} else {
self.get_state_report(&ProconState::from(state))
}
}
}
pub struct ProconRequestHandler;
impl RequestHandler for ProconRequestHandler {
fn get_report(&mut self, id: ReportId, _buf: &mut [u8]) -> Option<usize> {
info!("Get report for {:?}", id);
None
}
fn set_report(&mut self, id: ReportId, data: &[u8]) -> OutResponse {
trace!("Set report for {:?}: {:x}", id, data);
let mut buf = [0u8; 64];
let len_to_copy = buf.len().min(data.len());
buf[..len_to_copy].copy_from_slice(&data[..len_to_copy]);
if let ReportId::Out(id) = id {
if id == ProconRequestId::GetInfo as u8 {
SIGNAL_PROCON_REQUEST.signal(ProconRequestInfo {
command_id: buf[1],
report_id: ProconRequestId::GetInfo,
response_id: ProconResponseId::GetInfo,
raw_data: buf,
});
} else if id == ProconRequestId::Command as u8 {
SIGNAL_PROCON_REQUEST.signal(ProconRequestInfo {
command_id: buf[10],
report_id: ProconRequestId::Command,
response_id: ProconResponseId::Command,
raw_data: buf,
});
} else if id == ProconRequestId::Rumble as u8 {
// TODO: handle rumble
}
}
OutResponse::Accepted
}
fn set_idle_ms(&mut self, id: Option<ReportId>, dur: u32) {
info!("Set idle rate for {:?} to {:?}", id, dur);
}
fn get_idle_ms(&mut self, id: Option<ReportId>) -> Option<u32> {
info!("Get idle rate for {:?}", id);
None
}
}