diff --git a/src/config.rs b/src/config.rs index 581d4b3..d2c0e2d 100644 --- a/src/config.rs +++ b/src/config.rs @@ -17,6 +17,7 @@ use packed_struct::{ use crate::{ helpers::{PackedFloat, ToPackedFloatArray, ToRegularArray, XyValuePair}, + hid::gcc::{GcButtons1, GcButtons2, GcState}, input::{ read_ext_adc, Stick, StickAxis, FLOAT_ORIGIN, SPI_ACS_SHARED, SPI_CCS_SHARED, SPI_SHARED, }, @@ -25,10 +26,7 @@ use crate::{ LinearizedCalibration, NotchCalibration, NotchStatus, CALIBRATION_ORDER, NOTCH_ADJUSTMENT_ORDER, NO_OF_ADJ_NOTCHES, NO_OF_CALIBRATION_POINTS, NO_OF_NOTCHES, }, - usb_comms::{ - GcButtons1, GcButtons2, MUTEX_CONTROLLER_MODE, MUTEX_INPUT_CONSISTENCY_MODE, - SIGNAL_CHANGE_RUMBLE_STRENGTH, - }, + usb_comms::{MUTEX_INPUT_CONSISTENCY_MODE, SIGNAL_CHANGE_RUMBLE_STRENGTH}, ADDR_OFFSET, FLASH_SIZE, }; @@ -39,7 +37,7 @@ use embassy_sync::{ }; use embassy_time::Timer; -use crate::{input::CHANNEL_GCC_STATE, usb_comms::GcState}; +use crate::input::CHANNEL_GCC_STATE; /// Whether we are currently calibrating the sticks. Updates are dispatched when the status changes. /// Initial status is assumed to be false. diff --git a/src/hid/gcc.rs b/src/hid/gcc.rs new file mode 100644 index 0000000..8930bc8 --- /dev/null +++ b/src/hid/gcc.rs @@ -0,0 +1,141 @@ +use defmt::{info, trace, Format}; +use embassy_usb::{ + class::hid::{ReportId, RequestHandler}, + control::OutResponse, +}; + +use crate::usb_comms::{HidReportBuilder, SIGNAL_RUMBLE}; +use packed_struct::{derive::PackedStruct, PackedStruct}; + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Default, PackedStruct, Format)] +#[packed_struct(bit_numbering = "lsb0", size_bytes = "1")] +pub struct GcButtons1 { + #[packed_field(bits = "0")] + pub button_a: bool, + #[packed_field(bits = "1")] + pub button_b: bool, + #[packed_field(bits = "2")] + pub button_x: bool, + #[packed_field(bits = "3")] + pub button_y: bool, + #[packed_field(bits = "4")] + pub dpad_left: bool, + #[packed_field(bits = "5")] + pub dpad_right: bool, + #[packed_field(bits = "6")] + pub dpad_down: bool, + #[packed_field(bits = "7")] + pub dpad_up: bool, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Default, PackedStruct, Format)] +#[packed_struct(bit_numbering = "lsb0", size_bytes = "1")] +pub struct GcButtons2 { + #[packed_field(bits = "0")] + pub button_start: bool, + #[packed_field(bits = "1")] + pub button_z: bool, + #[packed_field(bits = "2")] + pub button_r: bool, + #[packed_field(bits = "3")] + pub button_l: bool, + #[packed_field(bits = "4..=7")] + pub blank1: u8, +} + +/// +/// Struct representing the controller state. Used for HID communications in +/// GCC adapter mode, as well as for the internal representation of the controller. +/// +#[derive(Clone, Copy, Debug, PartialEq, Eq, PackedStruct, Format)] +#[packed_struct(bit_numbering = "msb0", size_bytes = "8")] +pub struct GcState { + #[packed_field(bits = "0..=7")] + pub buttons_1: GcButtons1, + #[packed_field(bits = "8..=15")] + pub buttons_2: GcButtons2, + #[packed_field(bits = "16..=23")] + pub stick_x: u8, + #[packed_field(bits = "24..=31")] + pub stick_y: u8, + #[packed_field(bits = "32..=39")] + pub cstick_x: u8, + #[packed_field(bits = "40..=47")] + pub cstick_y: u8, + #[packed_field(bits = "48..=55")] + pub trigger_l: u8, + #[packed_field(bits = "56..=63")] + pub trigger_r: u8, +} + +impl Default for GcState { + fn default() -> Self { + Self { + buttons_1: GcButtons1::default(), + buttons_2: GcButtons2::default(), + stick_x: 127, + stick_y: 127, + cstick_x: 127, + cstick_y: 127, + trigger_l: 0, + trigger_r: 0, + } + } +} + +#[derive(Default)] +pub struct GcReportBuilder { + gc_first: bool, +} + +impl HidReportBuilder<37> for GcReportBuilder { + async fn get_hid_report(&mut self, state: &GcState) -> [u8; 37] { + let mut buffer = [0u8; 37]; + + buffer[0] = 0x21; + buffer[1] |= 0x14; + + let data = state.pack().expect("Failed to pack GC input data"); + + if !self.gc_first { + buffer[1] |= 0x04; + buffer[10] |= 0x04; + buffer[19] |= 0x04; + buffer[28] |= 0x04; + self.gc_first = true; + } else { + // controller in "port 1" + buffer[2..=9].copy_from_slice(&data); + } + + buffer + } +} + +pub struct GccRequestHandler; + +impl RequestHandler for GccRequestHandler { + fn get_report(&mut self, id: ReportId, _buf: &mut [u8]) -> Option { + info!("Get report for {:?}", id); + None + } + + fn set_report(&mut self, id: ReportId, data: &[u8]) -> OutResponse { + trace!("Set report for {:?}: {:x}", id, data); + + if data.len() > 1 { + SIGNAL_RUMBLE.signal((data[1] & 0x01) != 0); + } + + OutResponse::Accepted + } + + fn set_idle_ms(&mut self, id: Option, dur: u32) { + info!("Set idle rate for {:?} to {:?}", id, dur); + } + + fn get_idle_ms(&mut self, id: Option) -> Option { + info!("Get idle rate for {:?}", id); + None + } +} diff --git a/src/hid/mod.rs b/src/hid/mod.rs new file mode 100644 index 0000000..1b356d2 --- /dev/null +++ b/src/hid/mod.rs @@ -0,0 +1,2 @@ +pub mod gcc; +pub mod procon; diff --git a/src/procon_hid.rs b/src/hid/procon.rs similarity index 99% rename from src/procon_hid.rs rename to src/hid/procon.rs index 676a323..a7579e9 100644 --- a/src/procon_hid.rs +++ b/src/hid/procon.rs @@ -18,7 +18,9 @@ use embassy_usb::{ use packed_struct::{derive::PackedStruct, PackedStruct}; use rand::RngCore; -use crate::usb_comms::{GcState, HidReportBuilder}; +use crate::usb_comms::HidReportBuilder; + +use super::gcc::GcState; const SW_INFO_SET_MAC: u8 = 0x01; diff --git a/src/input.rs b/src/input.rs index 773607a..bba8efa 100644 --- a/src/input.rs +++ b/src/input.rs @@ -22,9 +22,10 @@ use crate::{ }, filter::{run_waveshaping, FilterGains, KalmanState, WaveshapingValues, FILTER_GAINS}, helpers::XyValuePair, + hid::gcc::GcState, input_filter::{DummyFilter, InputFilter}, stick::{linearize, notch_remap, StickParams}, - usb_comms::{GcState, MUTEX_CONTROLLER_MODE, MUTEX_INPUT_CONSISTENCY_MODE}, + usb_comms::{MUTEX_CONTROLLER_MODE, MUTEX_INPUT_CONSISTENCY_MODE}, }; /// Used to send the button state to the usb task and the calibration task diff --git a/src/input_filter.rs b/src/input_filter.rs index 7db05c4..3058a94 100644 --- a/src/input_filter.rs +++ b/src/input_filter.rs @@ -2,7 +2,7 @@ use defmt::warn; use crate::{ config::{is_awaitable_button_pressed, AwaitableButtons}, - usb_comms::GcState, + hid::gcc::GcState, }; /** diff --git a/src/main.rs b/src/main.rs index 505abc5..8b47fc4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,9 +3,9 @@ mod config; mod filter; mod helpers; +mod hid; mod input; mod input_filter; -mod procon_hid; mod stick; mod usb_comms; diff --git a/src/usb_comms.rs b/src/usb_comms.rs index 91f40a6..cdf2275 100644 --- a/src/usb_comms.rs +++ b/src/usb_comms.rs @@ -4,7 +4,7 @@ */ use core::{default::Default, future::Future}; -use defmt::{debug, info, trace, warn, Format}; +use defmt::{debug, info, trace, warn}; use embassy_futures::join::join; use embassy_rp::{ peripherals::{PIN_25, PIN_29, PWM_SLICE4, PWM_SLICE6, USB}, @@ -15,22 +15,23 @@ use embassy_rp::{ use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, mutex::Mutex, signal::Signal}; use embassy_time::{Duration, Instant, Timer}; use embassy_usb::{ - class::hid::{HidReader, HidReaderWriter, HidWriter, ReportId, RequestHandler, State}, - control::OutResponse, + class::hid::{HidReader, HidReaderWriter, HidWriter, RequestHandler, State}, driver::Driver, msos::{self, windows_version}, Builder, Handler, UsbDevice, }; use libm::powf; -use packed_struct::{derive::PackedStruct, PackedStruct}; use crate::{ config::{ControllerMode, InputConsistencyMode}, + hid::{ + gcc::{GcReportBuilder, GcState, GccRequestHandler}, + procon::{ProconReportBuilder, ProconRequestHandler, PROCON_REPORT_DESCRIPTOR}, + }, input::CHANNEL_GCC_STATE, - procon_hid::{ProconReportBuilder, ProconRequestHandler, PROCON_REPORT_DESCRIPTOR}, }; -static SIGNAL_RUMBLE: Signal = Signal::new(); +pub static SIGNAL_RUMBLE: Signal = Signal::new(); /// We could turn the config change signal into a PubSubChannel instead, but that /// would just transmit unnecessary amounts of data. @@ -116,135 +117,6 @@ struct UsbConfig { report_descriptor: &'static [u8], } -#[derive(Clone, Copy, Debug, PartialEq, Eq, Default, PackedStruct, Format)] -#[packed_struct(bit_numbering = "lsb0", size_bytes = "1")] -pub struct GcButtons1 { - #[packed_field(bits = "0")] - pub button_a: bool, - #[packed_field(bits = "1")] - pub button_b: bool, - #[packed_field(bits = "2")] - pub button_x: bool, - #[packed_field(bits = "3")] - pub button_y: bool, - #[packed_field(bits = "4")] - pub dpad_left: bool, - #[packed_field(bits = "5")] - pub dpad_right: bool, - #[packed_field(bits = "6")] - pub dpad_down: bool, - #[packed_field(bits = "7")] - pub dpad_up: bool, -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq, Default, PackedStruct, Format)] -#[packed_struct(bit_numbering = "lsb0", size_bytes = "1")] -pub struct GcButtons2 { - #[packed_field(bits = "0")] - pub button_start: bool, - #[packed_field(bits = "1")] - pub button_z: bool, - #[packed_field(bits = "2")] - pub button_r: bool, - #[packed_field(bits = "3")] - pub button_l: bool, - #[packed_field(bits = "4..=7")] - pub blank1: u8, -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq, PackedStruct, Format)] -#[packed_struct(bit_numbering = "msb0", size_bytes = "8")] -pub struct GcState { - #[packed_field(bits = "0..=7")] - pub buttons_1: GcButtons1, - #[packed_field(bits = "8..=15")] - pub buttons_2: GcButtons2, - #[packed_field(bits = "16..=23")] - pub stick_x: u8, - #[packed_field(bits = "24..=31")] - pub stick_y: u8, - #[packed_field(bits = "32..=39")] - pub cstick_x: u8, - #[packed_field(bits = "40..=47")] - pub cstick_y: u8, - #[packed_field(bits = "48..=55")] - pub trigger_l: u8, - #[packed_field(bits = "56..=63")] - pub trigger_r: u8, -} - -impl Default for GcState { - fn default() -> Self { - Self { - buttons_1: GcButtons1::default(), - buttons_2: GcButtons2::default(), - stick_x: 127, - stick_y: 127, - cstick_x: 127, - cstick_y: 127, - trigger_l: 0, - trigger_r: 0, - } - } -} - -#[derive(Default)] -struct GcReportBuilder { - gc_first: bool, -} - -impl HidReportBuilder<37> for GcReportBuilder { - async fn get_hid_report(&mut self, state: &GcState) -> [u8; 37] { - let mut buffer = [0u8; 37]; - - buffer[0] = 0x21; - buffer[1] |= 0x14; - - let data = state.pack().expect("Failed to pack GC input data"); - - if !self.gc_first { - buffer[1] |= 0x04; - buffer[10] |= 0x04; - buffer[19] |= 0x04; - buffer[28] |= 0x04; - self.gc_first = true; - } else { - // controller in "port 1" - buffer[2..=9].copy_from_slice(&data); - } - - buffer - } -} - -struct GccRequestHandler; - -impl RequestHandler for GccRequestHandler { - fn get_report(&mut self, id: ReportId, _buf: &mut [u8]) -> Option { - info!("Get report for {:?}", id); - None - } - - fn set_report(&mut self, id: ReportId, data: &[u8]) -> OutResponse { - trace!("Set report for {:?}: {:x}", id, data); - - if data.len() > 1 { - SIGNAL_RUMBLE.signal((data[1] & 0x01) != 0); - } - - OutResponse::Accepted - } - - fn set_idle_ms(&mut self, id: Option, dur: u32) { - info!("Set idle rate for {:?} to {:?}", id, dur); - } - - fn get_idle_ms(&mut self, id: Option) -> Option { - info!("Get idle rate for {:?}", id); - None - } -} - struct MyDeviceHandler { configured: bool, }