Compare commits

..

No commits in common. "main" and "v1.2.0" have entirely different histories.
main ... v1.2.0

15 changed files with 234 additions and 1041 deletions

View file

@ -1,10 +0,0 @@
This release introduces XInput mode for the NaxGCC. This mode is mostly useful for playing games on PC, as it offers the best out-of-the-box compatiblity experience with most titles. Similar to Pro-Controller Mode, this is not a permanent configuration that you set, but a mode that is activated by pressing a button while plugging your controller in.
To enter XInput Mode, press and hold the `X` button while plugging in your controller. While in XInput mode, your NaxGCC will _always_ poll at 1ms intervals, regardless of your chosen input consistency mode setting. Once you go back to GCC or Pro-Controller Mode, your desired input consistency setting will be restored.
> [!NOTE]
> As of this version, rumble will _not_ work while in XInput mode.
---
To update your firmware, plug in your controller to your computer while keeping the `A+X+Y` buttons held. Then drag & drop the `.uf2` file (found below, under Downloads) onto the storage device that appears.

View file

@ -10,6 +10,8 @@ jobs:
check: check:
runs-on: nix-flakes runs-on: nix-flakes
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@ -23,4 +25,4 @@ jobs:
- name: Run Clippy - name: Run Clippy
run: | run: |
nix flake check . --print-build-logs -j auto nix build .#clippy --print-build-logs -j auto

View file

@ -21,9 +21,9 @@ jobs:
cache: "${{ vars.PUBLIC_BINARY_CACHE_NAME }}" cache: "${{ vars.PUBLIC_BINARY_CACHE_NAME }}"
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Run flake checks - name: Run Clippy
run: | run: |
nix flake check . --print-build-logs -j auto nix build .#clippy --print-build-logs -j auto
- name: Build firmware image - name: Build firmware image
run: | run: |

2
Cargo.lock generated
View file

@ -866,7 +866,7 @@ checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
[[package]] [[package]]
name = "naxgcc-fw" name = "naxgcc-fw"
version = "1.3.0" version = "1.2.0"
dependencies = [ dependencies = [
"cortex-m", "cortex-m",
"cortex-m-rt", "cortex-m-rt",

View file

@ -1,6 +1,6 @@
[package] [package]
name = "naxgcc-fw" name = "naxgcc-fw"
version = "1.3.0" version = "1.2.0"
edition = "2021" edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View file

@ -47,15 +47,14 @@
CARGO_BUILD_TARGET = "thumbv6m-none-eabi"; CARGO_BUILD_TARGET = "thumbv6m-none-eabi";
in in
{ {
packages = { packages.default = self.packages.${system}.naxgcc-fw-uf2;
default = self.packages.${system}.naxgcc-fw-uf2;
naxgcc-fw-uf2 = pkgs.runCommandLocal "${self.packages.${system}.naxgcc-fw.pname}-uf2-${self.packages.${system}.naxgcc-fw.version}" { } '' packages.naxgcc-fw-uf2 = pkgs.runCommandLocal "${self.packages.${system}.naxgcc-fw.pname}-uf2-${self.packages.${system}.naxgcc-fw.version}" { } ''
mkdir -p $out/bin mkdir -p $out/bin
${pkgs.elf2uf2-rs}/bin/elf2uf2-rs ${self.packages.${system}.naxgcc-fw}/bin/${self.packages.${system}.naxgcc-fw.pname} $out/bin/${self.packages.${system}.naxgcc-fw.pname}.uf2 ${pkgs.elf2uf2-rs}/bin/elf2uf2-rs ${self.packages.${system}.naxgcc-fw}/bin/${self.packages.${system}.naxgcc-fw.pname} $out/bin/${self.packages.${system}.naxgcc-fw.pname}.uf2
''; '';
naxgcc-fw = pkgs.callPackage packages.naxgcc-fw = pkgs.callPackage
({ mode ? "build" }: naersk_lib.buildPackage { ({ mode ? "build" }: naersk_lib.buildPackage {
pname = (builtins.fromTOML (builtins.readFile ./Cargo.toml)).package.name; pname = (builtins.fromTOML (builtins.readFile ./Cargo.toml)).package.name;
version = (builtins.fromTOML (builtins.readFile ./Cargo.toml)).package.version; version = (builtins.fromTOML (builtins.readFile ./Cargo.toml)).package.version;
@ -72,13 +71,10 @@
DEFMT_LOG = "off"; DEFMT_LOG = "off";
}) })
{ }; { };
};
checks = { packages.clippy = self.packages.${system}.naxgcc-fw.override {
clippy = self.packages.${system}.naxgcc-fw.override {
mode = "clippy"; mode = "clippy";
}; };
};
devShells.default = pkgs.mkShell { devShells.default = pkgs.mkShell {
nativeBuildInputs = builtins.attrValues { nativeBuildInputs = builtins.attrValues {

View file

@ -19,8 +19,7 @@ use crate::{
helpers::{PackedFloat, ToPackedFloatArray, ToRegularArray, XyValuePair}, helpers::{PackedFloat, ToPackedFloatArray, ToRegularArray, XyValuePair},
hid::gcc::{GcButtons1, GcButtons2, GcState}, hid::gcc::{GcButtons1, GcButtons2, GcState},
input::{ input::{
read_ext_adc, ControllerState, Stick, StickAxis, FLOAT_ORIGIN, SPI_ACS_SHARED, read_ext_adc, Stick, StickAxis, FLOAT_ORIGIN, SPI_ACS_SHARED, SPI_CCS_SHARED, SPI_SHARED,
SPI_CCS_SHARED, SPI_SHARED,
}, },
stick::{ stick::{
calc_stick_values, legalize_notches, AppliedCalibration, CleanedCalibrationPoints, calc_stick_values, legalize_notches, AppliedCalibration, CleanedCalibrationPoints,
@ -38,7 +37,7 @@ use embassy_sync::{
}; };
use embassy_time::Timer; use embassy_time::Timer;
use crate::input::CHANNEL_CONTROLLER_STATE; use crate::input::CHANNEL_GCC_STATE;
/// Whether we are currently calibrating the sticks. Updates are dispatched when the status changes. /// Whether we are currently calibrating the sticks. Updates are dispatched when the status changes.
/// Initial status is assumed to be false. /// Initial status is assumed to be false.
@ -54,10 +53,8 @@ pub static SIGNAL_OVERRIDE_STICK_STATE: Signal<
> = Signal::new(); > = Signal::new();
/// Dispatched when we want to override the GCC state for a short amount of time. /// Dispatched when we want to override the GCC state for a short amount of time.
pub static SIGNAL_OVERRIDE_CONTROLLER_STATE: Signal< pub static SIGNAL_OVERRIDE_GCC_STATE: Signal<CriticalSectionRawMutex, OverrideGcReportInstruction> =
CriticalSectionRawMutex, Signal::new();
OverrideGcReportInstruction,
> = Signal::new();
/// Dispatched when we want to enter config mode, sent from core1 so config mode /// Dispatched when we want to enter config mode, sent from core1 so config mode
/// doesn't need to watch for the entire button combo every time. /// doesn't need to watch for the entire button combo every time.
@ -82,7 +79,7 @@ const MAX_ANALOG_SCALER: u8 = 125;
/// a certain mode. /// a certain mode.
#[derive(Default, Debug, Clone, Format)] #[derive(Default, Debug, Clone, Format)]
pub struct OverrideGcReportInstruction { pub struct OverrideGcReportInstruction {
pub report: ControllerState, pub report: GcState,
pub duration_ms: u64, pub duration_ms: u64,
} }
@ -574,8 +571,6 @@ pub enum ControllerMode {
GcAdapter = 0, GcAdapter = 0,
/// Pretend to be a Nintendo Switch Pro Controller connected via USB. /// Pretend to be a Nintendo Switch Pro Controller connected via USB.
Procon = 1, Procon = 1,
/// Act as an XInput device, and also advertise itself with 1000Hz polling capability.
XInput = 2,
} }
#[derive(Debug, Clone, Format, PackedStruct)] #[derive(Debug, Clone, Format, PackedStruct)]
@ -687,7 +682,7 @@ trait ButtonPressProvider {
} }
impl<'a, T: RawMutex, const I: usize, const J: usize, const K: usize> ButtonPressProvider impl<'a, T: RawMutex, const I: usize, const J: usize, const K: usize> ButtonPressProvider
for Subscriber<'a, T, ControllerState, I, J, K> for Subscriber<'a, T, GcState, I, J, K>
{ {
async fn wait_for_button_press(&mut self, button_to_wait_for: &AwaitableButtons) { async fn wait_for_button_press(&mut self, button_to_wait_for: &AwaitableButtons) {
loop { loop {
@ -787,25 +782,25 @@ impl<'a, T: RawMutex, const I: usize, const J: usize, const K: usize> ButtonPres
} }
pub fn is_awaitable_button_pressed( pub fn is_awaitable_button_pressed(
report: &ControllerState, report: &GcState,
button_to_wait_for: &AwaitableButtons, button_to_wait_for: &AwaitableButtons,
) -> bool { ) -> bool {
match button_to_wait_for { match button_to_wait_for {
AwaitableButtons::A => report.button_a, AwaitableButtons::A => report.buttons_1.button_a,
AwaitableButtons::B => report.button_b, AwaitableButtons::B => report.buttons_1.button_b,
AwaitableButtons::X => report.button_x, AwaitableButtons::X => report.buttons_1.button_x,
AwaitableButtons::Y => report.button_y, AwaitableButtons::Y => report.buttons_1.button_y,
AwaitableButtons::Up => report.dpad_up, AwaitableButtons::Up => report.buttons_1.dpad_up,
AwaitableButtons::Down => report.dpad_down, AwaitableButtons::Down => report.buttons_1.dpad_down,
AwaitableButtons::Left => report.dpad_left, AwaitableButtons::Left => report.buttons_1.dpad_left,
AwaitableButtons::Right => report.dpad_right, AwaitableButtons::Right => report.buttons_1.dpad_right,
AwaitableButtons::Start => report.button_start, AwaitableButtons::Start => report.buttons_2.button_start,
AwaitableButtons::L => report.trigger_l, AwaitableButtons::L => report.buttons_2.button_l || report.trigger_l > 10,
AwaitableButtons::R => report.trigger_r, AwaitableButtons::R => report.buttons_2.button_r || report.trigger_r > 10,
AwaitableButtons::Z => report.trigger_zr, AwaitableButtons::Z => report.buttons_2.button_z,
AwaitableButtons::Wildcard => true, AwaitableButtons::Wildcard => true,
AwaitableButtons::Impossible => false, AwaitableButtons::Impossible => false,
AwaitableButtons::NotZ => !report.trigger_zr, AwaitableButtons::NotZ => !report.buttons_2.button_z,
} }
} }
@ -1026,7 +1021,7 @@ impl<'a> StickCalibrationProcess<'a> {
pub async fn calibrate_stick(&mut self) { pub async fn calibrate_stick(&mut self) {
info!("Beginning stick calibration for {}", self.which_stick); info!("Beginning stick calibration for {}", self.which_stick);
let mut gcc_subscriber = CHANNEL_CONTROLLER_STATE.subscriber().unwrap(); let mut gcc_subscriber = CHANNEL_GCC_STATE.subscriber().unwrap();
SIGNAL_IS_CALIBRATING.signal(true); SIGNAL_IS_CALIBRATING.signal(true);
let mut done = false; let mut done = false;
@ -1140,7 +1135,7 @@ fn get_stick_display_coords(current_step: usize) -> (f32, f32) {
} }
pub async fn override_gcc_state_and_wait(state: &OverrideGcReportInstruction) { pub async fn override_gcc_state_and_wait(state: &OverrideGcReportInstruction) {
SIGNAL_OVERRIDE_CONTROLLER_STATE.signal(state.clone()); SIGNAL_OVERRIDE_GCC_STATE.signal(state.clone());
Timer::after_millis(state.duration_ms).await; Timer::after_millis(state.duration_ms).await;
} }
@ -1153,7 +1148,7 @@ async fn configuration_main_loop<
>( >(
current_config: &ControllerConfig, current_config: &ControllerConfig,
flash: &mut Flash<'static, FLASH, Async, FLASH_SIZE>, flash: &mut Flash<'static, FLASH, Async, FLASH_SIZE>,
gcc_subscriber: &mut Subscriber<'a, M, ControllerState, C, S, P>, gcc_subscriber: &mut Subscriber<'a, M, GcState, C, S, P>,
) -> ControllerConfig { ) -> ControllerConfig {
let mut final_config = current_config.clone(); let mut final_config = current_config.clone();
let config_options = [ let config_options = [
@ -1229,8 +1224,7 @@ async fn configuration_main_loop<
stick_y: 127, stick_y: 127,
cstick_x: 127, cstick_x: 127,
cstick_y: 127, cstick_y: 127,
} },
.into(),
duration_ms: 1000, duration_ms: 1000,
}) })
.await; .await;
@ -1258,8 +1252,7 @@ async fn configuration_main_loop<
stick_y: 255, stick_y: 255,
cstick_x: 127, cstick_x: 127,
cstick_y: 127, cstick_y: 127,
} },
.into(),
duration_ms: 1000, duration_ms: 1000,
}) })
.await; .await;
@ -1288,8 +1281,7 @@ async fn configuration_main_loop<
stick_y: 127, stick_y: 127,
cstick_x: 255, cstick_x: 255,
cstick_y: 255, cstick_y: 255,
} },
.into(),
duration_ms: 1000, duration_ms: 1000,
}) })
.await; .await;
@ -1376,8 +1368,7 @@ async fn configuration_main_loop<
StickAxis::YAxis => *to_adjust, StickAxis::YAxis => *to_adjust,
}, },
}) as u8, }) as u8,
} },
.into(),
duration_ms: 750, duration_ms: 750,
}) })
.await; .await;
@ -1463,8 +1454,7 @@ async fn configuration_main_loop<
StickAxis::YAxis => *to_adjust, StickAxis::YAxis => *to_adjust,
}, },
}) as u8, }) as u8,
} },
.into(),
duration_ms: 750, duration_ms: 750,
}) })
.await; .await;
@ -1550,8 +1540,7 @@ async fn configuration_main_loop<
StickAxis::YAxis => *to_adjust, StickAxis::YAxis => *to_adjust,
}, },
}) as u8, }) as u8,
} },
.into(),
duration_ms: 750, duration_ms: 750,
}) })
.await; .await;
@ -1606,8 +1595,7 @@ async fn configuration_main_loop<
Stick::ControlStick => 0, Stick::ControlStick => 0,
Stick::CStick => *to_adjust, Stick::CStick => *to_adjust,
}) as u8, }) as u8,
} },
.into(),
duration_ms: 750, duration_ms: 750,
}) })
.await; .await;
@ -1663,8 +1651,7 @@ async fn configuration_main_loop<
Stick::ControlStick => 0, Stick::ControlStick => 0,
Stick::CStick => *to_adjust, Stick::CStick => *to_adjust,
}) as u8, }) as u8,
} },
.into(),
duration_ms: 750, duration_ms: 750,
}) })
.await; .await;
@ -1705,8 +1692,7 @@ async fn configuration_main_loop<
stick_y: 127 + *to_adjust, stick_y: 127 + *to_adjust,
cstick_x: 127, cstick_x: 127,
cstick_y: 127, cstick_y: 127,
} },
.into(),
duration_ms: 750, duration_ms: 750,
}) })
.await; .await;
@ -1747,8 +1733,7 @@ async fn configuration_main_loop<
}) as u8, }) as u8,
cstick_x: 127, cstick_x: 127,
cstick_y: 127, cstick_y: 127,
} },
.into(),
duration_ms: 750, duration_ms: 750,
}) })
.await; .await;
@ -1776,8 +1761,7 @@ async fn configuration_main_loop<
stick_y: 127 + final_config.astick_config.y_waveshaping, stick_y: 127 + final_config.astick_config.y_waveshaping,
cstick_x: 127 + final_config.cstick_config.x_waveshaping, cstick_x: 127 + final_config.cstick_config.x_waveshaping,
cstick_y: 127 + final_config.cstick_config.y_waveshaping, cstick_y: 127 + final_config.cstick_config.y_waveshaping,
} },
.into(),
duration_ms: 1000, duration_ms: 1000,
}) })
.await; .await;
@ -1803,8 +1787,7 @@ async fn configuration_main_loop<
stick_y: 127 + final_config.astick_config.y_smoothing, stick_y: 127 + final_config.astick_config.y_smoothing,
cstick_x: 127 + final_config.cstick_config.x_smoothing, cstick_x: 127 + final_config.cstick_config.x_smoothing,
cstick_y: 127 + final_config.cstick_config.y_smoothing, cstick_y: 127 + final_config.cstick_config.y_smoothing,
} },
.into(),
duration_ms: 1000, duration_ms: 1000,
}) })
.await; .await;
@ -1830,8 +1813,7 @@ async fn configuration_main_loop<
stick_y: (127 + final_config.astick_config.y_snapback) as u8, stick_y: (127 + final_config.astick_config.y_snapback) as u8,
cstick_x: (127 + final_config.cstick_config.x_snapback) as u8, cstick_x: (127 + final_config.cstick_config.x_snapback) as u8,
cstick_y: (127 + final_config.cstick_config.y_snapback) as u8, cstick_y: (127 + final_config.cstick_config.y_snapback) as u8,
} },
.into(),
duration_ms: 1000, duration_ms: 1000,
}) })
.await; .await;
@ -1852,7 +1834,7 @@ async fn configuration_main_loop<
#[embassy_executor::task] #[embassy_executor::task]
pub async fn config_task(mut flash: Flash<'static, FLASH, Async, FLASH_SIZE>) { pub async fn config_task(mut flash: Flash<'static, FLASH, Async, FLASH_SIZE>) {
let mut gcc_subscriber = CHANNEL_CONTROLLER_STATE.subscriber().unwrap(); let mut gcc_subscriber = CHANNEL_GCC_STATE.subscriber().unwrap();
info!("Config task is running."); info!("Config task is running.");
@ -1902,8 +1884,7 @@ pub async fn config_task(mut flash: Flash<'static, FLASH, Async, FLASH_SIZE>) {
stick_y: 127, stick_y: 127,
cstick_x: 127, cstick_x: 127,
cstick_y: 127, cstick_y: 127,
} },
.into(),
duration_ms: 1000, duration_ms: 1000,
}) })
.await; .await;
@ -1919,7 +1900,7 @@ pub async fn config_task(mut flash: Flash<'static, FLASH, Async, FLASH_SIZE>) {
#[embassy_executor::task] #[embassy_executor::task]
pub async fn enter_config_mode_task() { pub async fn enter_config_mode_task() {
let mut gcc_subscriber = CHANNEL_CONTROLLER_STATE.subscriber().unwrap(); let mut gcc_subscriber = CHANNEL_GCC_STATE.subscriber().unwrap();
info!("Enter config mode task is running."); info!("Enter config mode task is running.");

View file

@ -4,10 +4,7 @@ use embassy_usb::{
control::OutResponse, control::OutResponse,
}; };
use crate::{ use crate::usb_comms::{HidReportBuilder, SIGNAL_RUMBLE};
input::ControllerState,
usb_comms::{HidReportBuilder, SIGNAL_RUMBLE},
};
use packed_struct::{derive::PackedStruct, PackedStruct}; use packed_struct::{derive::PackedStruct, PackedStruct};
#[rustfmt::skip] #[rustfmt::skip]
@ -128,36 +125,6 @@ pub struct GcState {
pub trigger_r: u8, pub trigger_r: u8,
} }
impl From<ControllerState> for GcState {
fn from(value: ControllerState) -> Self {
Self {
buttons_1: GcButtons1 {
button_a: value.button_a,
button_b: value.button_b,
button_x: value.button_x,
button_y: value.button_y,
dpad_left: value.dpad_left,
dpad_right: value.dpad_right,
dpad_down: value.dpad_down,
dpad_up: value.dpad_up,
},
buttons_2: GcButtons2 {
button_start: value.button_start,
button_z: value.trigger_zr,
button_r: value.trigger_r,
button_l: value.trigger_l,
blank1: 0,
},
stick_x: value.stick_state.ax,
stick_y: value.stick_state.ay,
cstick_x: value.stick_state.cx,
cstick_y: value.stick_state.cy,
trigger_l: if value.trigger_l { 255 } else { 0 },
trigger_r: if value.trigger_r { 255 } else { 0 },
}
}
}
impl Default for GcState { impl Default for GcState {
fn default() -> Self { fn default() -> Self {
Self { Self {
@ -179,15 +146,13 @@ pub struct GcReportBuilder {
} }
impl HidReportBuilder<37> for GcReportBuilder { impl HidReportBuilder<37> for GcReportBuilder {
async fn get_hid_report(&mut self, state: &ControllerState) -> [u8; 37] { async fn get_hid_report(&mut self, state: &GcState) -> [u8; 37] {
let mut buffer = [0u8; 37]; let mut buffer = [0u8; 37];
buffer[0] = 0x21; buffer[0] = 0x21;
buffer[1] |= 0x14; buffer[1] |= 0x14;
let gcc_state: GcState = (*state).into(); let data = state.pack().expect("Failed to pack GC input data");
let data = gcc_state.pack().expect("Failed to pack GC input data");
if !self.gc_first { if !self.gc_first {
buffer[1] |= 0x04; buffer[1] |= 0x04;

View file

@ -1,70 +1,2 @@
use embassy_usb::{
class::hid::{HidReader, HidReaderWriter, HidWriter, ReadError, RequestHandler},
driver::{Driver, EndpointError},
};
pub mod gcc; pub mod gcc;
pub mod procon; pub mod procon;
pub mod xinput;
/// Custom trait to unify the API between embassy's HID writer, and our XInput reader/writer (and any
/// custom writers we may create in the future)
pub trait HidReaderWriterSplit<'d, D: Driver<'d>, const READ_N: usize, const WRITE_N: usize> {
fn split(
self,
) -> (
impl UsbReader<'d, D, READ_N>,
impl UsbWriter<'d, D, WRITE_N>,
);
}
/// Custom trait to unify the API between embassy's HID writer, and our XInput reader (and any
/// custom writers we may create in the future)
pub trait UsbReader<'d, D: Driver<'d>, const READ_N: usize> {
async fn run<T: RequestHandler>(self, use_report_ids: bool, handler: &mut T) -> !;
async fn read(&mut self, buf: &mut [u8]) -> Result<usize, ReadError>;
}
/// Custom trait to unify the API between embassy's HID writer, and our XInput writer (and any
/// custom writers we may create in the future)
pub trait UsbWriter<'d, D: Driver<'d>, const WRITE_N: usize> {
async fn ready(&mut self);
async fn write(&mut self, report: &[u8]) -> Result<(), EndpointError>;
}
impl<'d, D: Driver<'d>, const READ_N: usize, const WRITE_N: usize>
HidReaderWriterSplit<'d, D, READ_N, WRITE_N> for HidReaderWriter<'d, D, READ_N, WRITE_N>
{
fn split(
self,
) -> (
impl UsbReader<'d, D, READ_N>,
impl UsbWriter<'d, D, WRITE_N>,
) {
self.split()
}
}
impl<'d, D: Driver<'d>, const READ_N: usize> UsbReader<'d, D, READ_N> for HidReader<'d, D, READ_N> {
async fn run<T: RequestHandler>(self, use_report_ids: bool, handler: &mut T) -> ! {
self.run(use_report_ids, handler).await
}
async fn read(&mut self, buf: &mut [u8]) -> Result<usize, ReadError> {
self.read(buf).await
}
}
impl<'d, D: Driver<'d>, const WRITE_N: usize> UsbWriter<'d, D, WRITE_N>
for HidWriter<'d, D, WRITE_N>
{
async fn ready(&mut self) {
self.ready().await
}
async fn write(&mut self, report: &[u8]) -> Result<(), EndpointError> {
self.write(report).await
}
}

View file

@ -18,7 +18,9 @@ use embassy_usb::{
use packed_struct::{derive::PackedStruct, PackedStruct}; use packed_struct::{derive::PackedStruct, PackedStruct};
use rand::RngCore; use rand::RngCore;
use crate::{input::ControllerState, usb_comms::HidReportBuilder}; use crate::usb_comms::HidReportBuilder;
use super::gcc::GcState;
const SW_INFO_SET_MAC: u8 = 0x01; const SW_INFO_SET_MAC: u8 = 0x01;
@ -484,36 +486,36 @@ impl Default for BatteryStatus {
} }
} }
impl From<&ControllerState> for ProconState { impl From<&GcState> for ProconState {
fn from(value: &ControllerState) -> Self { fn from(value: &GcState) -> Self {
Self { Self {
buttons_left: ProconButtonsLeft { buttons_left: ProconButtonsLeft {
dpad_down: value.dpad_down, dpad_down: value.buttons_1.dpad_down,
dpad_right: value.dpad_right, dpad_right: value.buttons_1.dpad_right,
dpad_up: value.dpad_up, dpad_up: value.buttons_1.dpad_up,
dped_left: value.dpad_left, dped_left: value.buttons_1.dpad_left,
trigger_l: value.trigger_zl, trigger_l: value.buttons_2.button_l,
trigger_zl: value.trigger_l, trigger_zl: value.buttons_2.button_l,
..Default::default() ..Default::default()
}, },
buttons_right: ProconButtonsRight { buttons_right: ProconButtonsRight {
button_a: value.button_a, button_a: value.buttons_1.button_a,
button_b: value.button_b, button_b: value.buttons_1.button_b,
button_x: value.button_x, button_x: value.buttons_1.button_x,
button_y: value.button_y, button_y: value.buttons_1.button_y,
trigger_r: value.trigger_zr, trigger_r: value.buttons_2.button_z,
trigger_zr: value.trigger_r, trigger_zr: value.buttons_2.button_r,
..Default::default() ..Default::default()
}, },
buttons_shared: ProconButtonsShared { buttons_shared: ProconButtonsShared {
button_plus: value.button_start && !value.trigger_zr, button_plus: value.buttons_2.button_start && !value.buttons_2.button_z,
button_home: value.button_start && value.trigger_zr, button_home: value.buttons_2.button_start && value.buttons_2.button_z,
..Default::default() ..Default::default()
}, },
lstick_x: value.stick_state.ax as u16 * 16, lstick_x: value.stick_x as u16 * 16,
lstick_y: value.stick_state.ay, lstick_y: value.stick_y,
rstick_x: value.stick_state.cx as u16 * 16, rstick_x: value.cstick_x as u16 * 16,
rstick_y: value.stick_state.cy, rstick_y: value.cstick_y,
} }
} }
} }
@ -692,7 +694,7 @@ impl ProconReportBuilder {
} }
impl HidReportBuilder<64> for ProconReportBuilder { impl HidReportBuilder<64> for ProconReportBuilder {
async fn get_hid_report(&mut self, state: &ControllerState) -> [u8; 64] { async fn get_hid_report(&mut self, state: &GcState) -> [u8; 64] {
let current_report_info = if self.switch_reporting_mode == RM_SEND_STATE { let current_report_info = if self.switch_reporting_mode == RM_SEND_STATE {
SIGNAL_PROCON_REQUEST.try_take() SIGNAL_PROCON_REQUEST.try_take()
} else { } else {

View file

@ -1,544 +0,0 @@
///
/// # XInput Protocol Implementation
///
/// The implementations for `XInputReader` and `XInputWriter` and the logic surrounding them is
/// mostly taken from embassy.
///
/// Unfortunately, the embassy hid classes don't allow us to specify a custom interface protocol,
/// hence the little bit of code duplication.
///
use core::{
mem::MaybeUninit,
sync::atomic::{AtomicUsize, Ordering},
};
use defmt::{info, trace, warn, Format};
use embassy_usb::{
class::hid::{Config, ReadError, ReportId, RequestHandler},
control::{InResponse, OutResponse, Recipient, Request, RequestType},
driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut},
types::InterfaceNumber,
Builder, Handler,
};
use packed_struct::{derive::PackedStruct, PackedStruct};
use crate::{input::ControllerState, usb_comms::HidReportBuilder};
use super::{HidReaderWriterSplit, UsbReader, UsbWriter};
/// lol
pub const XINPUT_REPORT_DESCRIPTOR: &[u8] = &[];
const HID_DESC_DESCTYPE_HID: u8 = 0x21;
const HID_DESC_DESCTYPE_HID_REPORT: u8 = 0x22;
const HID_REQ_SET_IDLE: u8 = 0x0a;
const HID_REQ_GET_IDLE: u8 = 0x02;
const HID_REQ_GET_REPORT: u8 = 0x01;
const HID_REQ_SET_REPORT: u8 = 0x09;
const HID_REQ_GET_PROTOCOL: u8 = 0x03;
const HID_REQ_SET_PROTOCOL: u8 = 0x0b;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default, PackedStruct, Format)]
#[packed_struct(bit_numbering = "lsb0", size_bytes = "1")]
pub struct XInputButtons1 {
#[packed_field(bits = "0")]
pub dpad_up: bool,
#[packed_field(bits = "1")]
pub dpad_down: bool,
#[packed_field(bits = "2")]
pub dpad_left: bool,
#[packed_field(bits = "3")]
pub dpad_right: bool,
#[packed_field(bits = "4")]
pub button_menu: bool,
#[packed_field(bits = "5")]
pub button_back: bool,
#[packed_field(bits = "6")]
pub button_stick_l: bool,
#[packed_field(bits = "7")]
pub button_stick_r: bool,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default, PackedStruct, Format)]
#[packed_struct(bit_numbering = "lsb0", size_bytes = "1")]
pub struct XInputButtons2 {
#[packed_field(bits = "0")]
pub bumper_l: bool,
#[packed_field(bits = "1")]
pub bumper_r: bool,
#[packed_field(bits = "2")]
pub button_guide: bool,
#[packed_field(bits = "3")]
pub blank_1: bool,
#[packed_field(bits = "4")]
pub button_a: bool,
#[packed_field(bits = "5")]
pub button_b: bool,
#[packed_field(bits = "6")]
pub button_x: bool,
#[packed_field(bits = "7")]
pub button_y: bool,
}
///
/// HID report that is sent back to the host.
///
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default, PackedStruct, Format)]
#[packed_struct(bit_numbering = "msb0", endian = "lsb", size_bytes = "32")]
pub struct XInputReport {
#[packed_field(bits = "0..=7")]
pub report_id: u8,
#[packed_field(bits = "8..=15")]
pub report_size: u8,
#[packed_field(bits = "16..=23")]
pub buttons_1: XInputButtons1,
#[packed_field(bits = "24..=31")]
pub buttons_2: XInputButtons2,
#[packed_field(bits = "32..=39")]
pub analog_trigger_l: u8,
#[packed_field(bits = "40..=47")]
pub analog_trigger_r: u8,
#[packed_field(bits = "48..=63")]
pub stick_left_x: i16,
#[packed_field(bits = "64..=79")]
pub stick_left_y: i16,
#[packed_field(bits = "80..=95")]
pub stick_right_x: i16,
#[packed_field(bits = "96..=111")]
pub stick_right_y: i16,
#[packed_field(bits = "112..=255")]
pub reserved: [u8; 18],
}
impl From<&ControllerState> for XInputReport {
fn from(value: &ControllerState) -> Self {
Self {
report_id: 0,
report_size: 20,
buttons_1: XInputButtons1 {
dpad_up: value.dpad_up,
dpad_down: value.dpad_down,
dpad_right: value.dpad_right,
dpad_left: value.dpad_left,
button_menu: value.button_start,
button_back: false,
button_stick_l: false,
button_stick_r: false,
},
buttons_2: XInputButtons2 {
blank_1: false,
bumper_l: value.trigger_zl,
bumper_r: value.trigger_zr,
button_a: value.button_a,
button_b: value.button_b,
button_x: value.button_x,
button_y: value.button_y,
button_guide: false,
},
analog_trigger_l: if value.trigger_l { 255 } else { 0 },
analog_trigger_r: if value.trigger_r { 255 } else { 0 },
stick_left_x: (value.stick_state.ax as i16 - 127).clamp(-127, 127) * 257,
stick_left_y: (value.stick_state.ay as i16 - 127).clamp(-127, 127) * 257,
stick_right_x: (value.stick_state.cx as i16 - 127).clamp(-127, 127) * 257,
stick_right_y: (value.stick_state.cy as i16 - 127).clamp(-127, 127) * 257,
reserved: [0u8; 18],
}
}
}
///
/// Takes in a GcState, converts it to an `XInputReport` and returns its packed version.
///
pub struct XInputReportBuilder;
impl HidReportBuilder<32> for XInputReportBuilder {
async fn get_hid_report(&mut self, state: &ControllerState) -> [u8; 32] {
XInputReport::from(state)
.pack()
.expect("Failed to pack XInput State")
}
}
///
/// Handles packets sent from the host.
///
pub struct XInputRequestHandler;
impl RequestHandler for XInputRequestHandler {
fn get_report(
&mut self,
id: embassy_usb::class::hid::ReportId,
buf: &mut [u8],
) -> Option<usize> {
let _ = (id, buf);
None
}
fn set_report(
&mut self,
id: embassy_usb::class::hid::ReportId,
data: &[u8],
) -> embassy_usb::control::OutResponse {
let _ = (id, data);
info!("Set report for {:?}: {:x}", id, data);
embassy_usb::control::OutResponse::Accepted
}
fn get_idle_ms(&mut self, id: Option<embassy_usb::class::hid::ReportId>) -> Option<u32> {
let _ = id;
None
}
fn set_idle_ms(&mut self, id: Option<embassy_usb::class::hid::ReportId>, duration_ms: u32) {
let _ = (id, duration_ms);
}
}
/// Taken from embassy.
pub struct XInputWriter<'d, D: Driver<'d>, const N: usize> {
ep_in: D::EndpointIn,
}
impl<'d, D: Driver<'d>, const N: usize> UsbWriter<'d, D, N> for XInputWriter<'d, D, N> {
/// Waits for the interrupt in endpoint to be enabled.
async fn ready(&mut self) {
self.ep_in.wait_enabled().await;
}
/// Writes `report` to its interrupt endpoint.
async fn write(&mut self, report: &[u8]) -> Result<(), EndpointError> {
assert!(report.len() <= N);
let max_packet_size = usize::from(self.ep_in.info().max_packet_size);
let zlp_needed = report.len() < N && (report.len() % max_packet_size == 0);
for chunk in report.chunks(max_packet_size) {
self.ep_in.write(chunk).await?;
}
if zlp_needed {
self.ep_in.write(&[]).await?;
}
Ok(())
}
}
/// Taken from embassy.
pub struct XInputReader<'d, D: Driver<'d>, const N: usize> {
ep_out: D::EndpointOut,
offset: &'d AtomicUsize,
}
impl<'d, D: Driver<'d>, const N: usize> UsbReader<'d, D, N> for XInputReader<'d, D, N> {
async fn run<T: RequestHandler>(mut self, use_report_ids: bool, handler: &mut T) -> ! {
let offset = self.offset.load(Ordering::Acquire);
assert!(offset == 0);
let mut buf = [0; N];
loop {
match self.read(&mut buf).await {
Ok(len) => {
let id = if use_report_ids { buf[0] } else { 0 };
handler.set_report(ReportId::Out(id), &buf[..len]);
}
Err(ReadError::BufferOverflow) => warn!(
"Host ent output report larger than the configured maximum output report length ({})",
N
),
Err(ReadError::Disabled) => self.ep_out.wait_enabled().await,
Err(ReadError::Sync(_)) => unreachable!(),
}
}
}
async fn read(&mut self, buf: &mut [u8]) -> Result<usize, ReadError> {
assert!(N != 0);
assert!(buf.len() >= N);
// Read packets from the endpoint
let max_packet_size = usize::from(self.ep_out.info().max_packet_size);
let starting_offset = self.offset.load(Ordering::Acquire);
let mut total = starting_offset;
loop {
for chunk in buf[starting_offset..N].chunks_mut(max_packet_size) {
match self.ep_out.read(chunk).await {
Ok(size) => {
total += size;
if size < max_packet_size || total == N {
self.offset.store(0, Ordering::Release);
break;
}
self.offset.store(total, Ordering::Release);
}
Err(err) => {
self.offset.store(0, Ordering::Release);
return Err(err.into());
}
}
}
// Some hosts may send ZLPs even when not required by the HID spec, so we'll loop as long as total == 0.
if total > 0 {
break;
}
}
if starting_offset > 0 {
Err(ReadError::Sync(starting_offset..total))
} else {
Ok(total)
}
}
}
/// Taken from embassy, with a few modifications to the descriptor.
pub struct XInputReaderWriter<'d, D: Driver<'d>, const READ_N: usize, const WRITE_N: usize> {
reader: XInputReader<'d, D, READ_N>,
writer: XInputWriter<'d, D, WRITE_N>,
}
impl<'d, D: Driver<'d>, const READ_N: usize, const WRITE_N: usize>
XInputReaderWriter<'d, D, READ_N, WRITE_N>
{
pub fn new(
builder: &mut Builder<'d, D>,
state: &'d mut XInputState<'d>,
config: Config<'d>,
) -> Self {
let mut func = builder.function(0xff, 0x5d, 0x01);
let mut iface = func.interface();
let if_num = iface.interface_number();
let mut alt = iface.alt_setting(0xff, 0x5d, 0x01, None);
#[rustfmt::skip]
alt.descriptor(0x21, &[
0x10, 0x01, // bcdHID 1.10
0x01, // bCountryCode
0x24, // bNumDescriptors
0x81, // bDescriptorType[0] (Unknown 0x81)
0x14, 0x03, // wDescriptorLength[0] 788
0x00, // bDescriptorType[1] (Unknown 0x00)
0x03, 0x13, // wDescriptorLength[1] 4867
0x02, // bDescriptorType[2] (Unknown 0x02)
0x00, 0x03, // wDescriptorLength[2] 768
0x00, // bDescriptorType[3] (Unknown 0x00)
]);
let ep_in = alt.endpoint_interrupt_in(config.max_packet_size_in, config.poll_ms);
let ep_out = alt.endpoint_interrupt_out(config.max_packet_size_out, config.poll_ms);
drop(func);
let control = state.control.write(XInputControl::new(
if_num,
config.report_descriptor,
config.request_handler,
&state.out_report_offset,
));
builder.handler(control);
Self {
reader: XInputReader {
ep_out,
offset: &state.out_report_offset,
},
writer: XInputWriter { ep_in },
}
}
}
impl<'d, D: Driver<'d>, const READ_N: usize, const WRITE_N: usize>
HidReaderWriterSplit<'d, D, READ_N, WRITE_N> for XInputReaderWriter<'d, D, READ_N, WRITE_N>
{
fn split(
self,
) -> (
impl UsbReader<'d, D, READ_N>,
impl UsbWriter<'d, D, WRITE_N>,
) {
(self.reader, self.writer)
}
}
pub struct XInputState<'d> {
control: MaybeUninit<XInputControl<'d>>,
out_report_offset: AtomicUsize,
}
impl<'d> Default for XInputState<'d> {
fn default() -> Self {
Self::new()
}
}
impl<'d> XInputState<'d> {
pub const fn new() -> Self {
XInputState {
control: MaybeUninit::uninit(),
out_report_offset: AtomicUsize::new(0),
}
}
}
/// Taken from embassy.
struct XInputControl<'d> {
if_num: InterfaceNumber,
report_descriptor: &'d [u8],
request_handler: Option<&'d mut dyn RequestHandler>,
out_report_offset: &'d AtomicUsize,
hid_descriptor: [u8; 16],
}
impl<'d> XInputControl<'d> {
fn new(
if_num: InterfaceNumber,
report_descriptor: &'d [u8],
request_handler: Option<&'d mut dyn RequestHandler>,
out_report_offset: &'d AtomicUsize,
) -> Self {
XInputControl {
if_num,
report_descriptor,
request_handler,
out_report_offset,
#[rustfmt::skip]
hid_descriptor: [
0x10, // bLength
0x21, // bDescriptorType (HID)
0x10, 0x01, // bcdHID 1.10
0x01, // bCountryCode
0x24, // bNumDescriptors
0x81, // bDescriptorType[0] (Unknown 0x81)
0x14, 0x03, // wDescriptorLength[0] 788
0x00, // bDescriptorType[1] (Unknown 0x00)
0x03, 0x13, // wDescriptorLength[1] 4867
0x02, // bDescriptorType[2] (Unknown 0x02)
0x00, 0x03, // wDescriptorLength[2] 768
0x00, // bDescriptorType[3] (Unknown 0x00)
],
}
}
}
/// Helper function, since the function in `ReportId` is private.
const fn try_u16_to_report_id(value: u16) -> Result<ReportId, ()> {
match value >> 8 {
1 => Ok(ReportId::In(value as u8)),
2 => Ok(ReportId::Out(value as u8)),
3 => Ok(ReportId::Feature(value as u8)),
_ => Err(()),
}
}
impl<'d> Handler for XInputControl<'d> {
fn reset(&mut self) {
self.out_report_offset.store(0, Ordering::Release);
}
fn control_out(&mut self, req: Request, data: &[u8]) -> Option<OutResponse> {
if (req.request_type, req.recipient, req.index)
!= (
RequestType::Class,
Recipient::Interface,
self.if_num.0 as u16,
)
{
return None;
}
trace!("HID control_out {:?} {=[u8]:x}", req, data);
match req.request {
HID_REQ_SET_IDLE => {
if let Some(handler) = self.request_handler.as_mut() {
let id = req.value as u8;
let id = (id != 0).then_some(ReportId::In(id));
let dur = u32::from(req.value >> 8);
let dur = if dur == 0 { u32::MAX } else { 4 * dur };
handler.set_idle_ms(id, dur);
}
Some(OutResponse::Accepted)
}
HID_REQ_SET_REPORT => {
match (
try_u16_to_report_id(req.value),
self.request_handler.as_mut(),
) {
(Ok(id), Some(handler)) => Some(handler.set_report(id, data)),
_ => Some(OutResponse::Rejected),
}
}
HID_REQ_SET_PROTOCOL => {
if req.value == 1 {
Some(OutResponse::Accepted)
} else {
warn!("HID Boot Protocol is unsupported.");
Some(OutResponse::Rejected) // UNSUPPORTED: Boot Protocol
}
}
_ => Some(OutResponse::Rejected),
}
}
fn control_in<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> Option<InResponse<'a>> {
if req.index != self.if_num.0 as u16 {
return None;
}
match (req.request_type, req.recipient) {
(RequestType::Standard, Recipient::Interface) => match req.request {
Request::GET_DESCRIPTOR => match (req.value >> 8) as u8 {
HID_DESC_DESCTYPE_HID_REPORT => {
Some(InResponse::Accepted(self.report_descriptor))
}
HID_DESC_DESCTYPE_HID => Some(InResponse::Accepted(&self.hid_descriptor)),
_ => Some(InResponse::Rejected),
},
_ => Some(InResponse::Rejected),
},
(RequestType::Class, Recipient::Interface) => {
trace!("HID control_in {:?}", req);
match req.request {
HID_REQ_GET_REPORT => {
let size = match try_u16_to_report_id(req.value) {
Ok(id) => self
.request_handler
.as_mut()
.and_then(|x| x.get_report(id, buf)),
Err(_) => None,
};
if let Some(size) = size {
Some(InResponse::Accepted(&buf[0..size]))
} else {
Some(InResponse::Rejected)
}
}
HID_REQ_GET_IDLE => {
if let Some(handler) = self.request_handler.as_mut() {
let id = req.value as u8;
let id = (id != 0).then_some(ReportId::In(id));
if let Some(dur) = handler.get_idle_ms(id) {
let dur = u8::try_from(dur / 4).unwrap_or(0);
buf[0] = dur;
Some(InResponse::Accepted(&buf[0..1]))
} else {
Some(InResponse::Rejected)
}
} else {
Some(InResponse::Rejected)
}
}
HID_REQ_GET_PROTOCOL => {
// UNSUPPORTED: Boot Protocol
buf[0] = 1;
Some(InResponse::Accepted(&buf[0..1]))
}
_ => Some(InResponse::Rejected),
}
}
_ => None,
}
}
}

View file

@ -17,8 +17,8 @@ use libm::{fmaxf, fminf};
use crate::{ use crate::{
config::{ config::{
ControllerConfig, ControllerMode, InputConsistencyMode, OverrideGcReportInstruction, ControllerConfig, ControllerMode, InputConsistencyMode, OverrideGcReportInstruction,
OverrideStickState, SIGNAL_CONFIG_CHANGE, SIGNAL_IS_CALIBRATING, OverrideStickState, SIGNAL_CONFIG_CHANGE, SIGNAL_IS_CALIBRATING, SIGNAL_OVERRIDE_GCC_STATE,
SIGNAL_OVERRIDE_CONTROLLER_STATE, SIGNAL_OVERRIDE_STICK_STATE, SIGNAL_OVERRIDE_STICK_STATE,
}, },
filter::{run_waveshaping, FilterGains, KalmanState, WaveshapingValues, FILTER_GAINS}, filter::{run_waveshaping, FilterGains, KalmanState, WaveshapingValues, FILTER_GAINS},
helpers::XyValuePair, helpers::XyValuePair,
@ -29,13 +29,8 @@ use crate::{
}; };
/// Used to send the button state to the usb task and the calibration task /// Used to send the button state to the usb task and the calibration task
pub static CHANNEL_CONTROLLER_STATE: PubSubChannel< pub static CHANNEL_GCC_STATE: PubSubChannel<CriticalSectionRawMutex, GcState, 1, 4, 1> =
CriticalSectionRawMutex, PubSubChannel::new();
ControllerState,
1,
4,
1,
> = PubSubChannel::new();
/// Used to send the stick state from the stick task to the main input task /// Used to send the stick state from the stick task to the main input task
static SIGNAL_STICK_STATE: Signal<CriticalSectionRawMutex, StickState> = Signal::new(); static SIGNAL_STICK_STATE: Signal<CriticalSectionRawMutex, StickState> = Signal::new();
@ -48,7 +43,7 @@ pub static SPI_CCS_SHARED: Mutex<ThreadModeRawMutex, Option<Output<'static>>> =
const STICK_HYST_VAL: f32 = 0.3; const STICK_HYST_VAL: f32 = 0.3;
pub const FLOAT_ORIGIN: f32 = 127.5; pub const FLOAT_ORIGIN: f32 = 127.5;
#[derive(Clone, Copy, Debug, Format, PartialEq, Eq)] #[derive(Clone, Debug, Default, Format)]
pub struct StickState { pub struct StickState {
pub ax: u8, pub ax: u8,
pub ay: u8, pub ay: u8,
@ -56,17 +51,6 @@ pub struct StickState {
pub cy: u8, pub cy: u8,
} }
impl Default for StickState {
fn default() -> Self {
Self {
ax: 127,
ay: 127,
cx: 127,
cy: 127,
}
}
}
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default)]
struct StickPositions { struct StickPositions {
x: f32, x: f32,
@ -85,51 +69,6 @@ struct RawStickValues {
c_unfiltered: XyValuePair<f32>, c_unfiltered: XyValuePair<f32>,
} }
#[derive(Clone, Copy, Debug, Default, Format, PartialEq, Eq)]
pub struct ControllerState {
pub button_a: bool,
pub button_b: bool,
pub button_x: bool,
pub button_y: bool,
pub trigger_zr: bool,
pub trigger_zl: bool,
pub trigger_l: bool,
pub trigger_r: bool,
pub button_start: bool,
pub dpad_up: bool,
pub dpad_down: bool,
pub dpad_left: bool,
pub dpad_right: bool,
pub stick_state: StickState,
}
/// This is only implemented for backwards-compatibility purposes
impl From<GcState> for ControllerState {
fn from(value: GcState) -> Self {
Self {
button_a: value.buttons_1.button_a,
button_b: value.buttons_1.button_b,
button_x: value.buttons_1.button_x,
button_y: value.buttons_1.button_y,
trigger_zr: value.buttons_2.button_z,
trigger_zl: false,
trigger_l: value.trigger_l > 170,
trigger_r: value.trigger_r > 170,
button_start: value.buttons_2.button_start,
dpad_up: value.buttons_1.dpad_up,
dpad_down: value.buttons_1.dpad_down,
dpad_left: value.buttons_1.dpad_left,
dpad_right: value.buttons_1.dpad_right,
stick_state: StickState {
ax: value.stick_x,
ay: value.stick_y,
cx: value.stick_x,
cy: value.stick_y,
},
}
}
}
#[derive(PartialEq, Eq, Debug, Clone, Format, Copy)] #[derive(PartialEq, Eq, Debug, Clone, Format, Copy)]
pub enum Stick { pub enum Stick {
ControlStick, ControlStick,
@ -224,7 +163,7 @@ async fn update_stick_states(
let loop_end = Instant::now(); let loop_end = Instant::now();
done = loop_end >= end_time - (loop_end - loop_start) && adc_count >= 4; done = loop_end >= end_time - (loop_end - loop_start);
} }
trace!("ADC Count: {}", adc_count); trace!("ADC Count: {}", adc_count);
@ -373,7 +312,7 @@ async fn update_stick_states(
raw_stick_values.c_unfiltered.x = fminf(125., fmaxf(-125., remapped_c_unfiltered.x)); raw_stick_values.c_unfiltered.x = fminf(125., fmaxf(-125., remapped_c_unfiltered.x));
raw_stick_values.c_unfiltered.y = fminf(125., fmaxf(-125., remapped_c_unfiltered.y)); raw_stick_values.c_unfiltered.y = fminf(125., fmaxf(-125., remapped_c_unfiltered.y));
let mut out_stick_state = *current_stick_state; let mut out_stick_state = current_stick_state.clone();
let diff_x = (remapped.x + FLOAT_ORIGIN) - current_stick_state.ax as f32; let diff_x = (remapped.x + FLOAT_ORIGIN) - current_stick_state.ax as f32;
if !(-STICK_HYST_VAL..=(1.0 + STICK_HYST_VAL)).contains(&diff_x) { if !(-STICK_HYST_VAL..=(1.0 + STICK_HYST_VAL)).contains(&diff_x) {
@ -406,7 +345,7 @@ async fn update_stick_states(
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
fn update_button_states( fn update_button_states(
controller_state: &mut ControllerState, gcc_state: &mut GcState,
btn_a: &Input<'_>, btn_a: &Input<'_>,
btn_b: &Input<'_>, btn_b: &Input<'_>,
btn_x: &Input<'_>, btn_x: &Input<'_>,
@ -414,37 +353,42 @@ fn update_button_states(
btn_start: &Input<'_>, btn_start: &Input<'_>,
btn_l: &Input<'_>, btn_l: &Input<'_>,
btn_r: &Input<'_>, btn_r: &Input<'_>,
btn_zr: &Input<'_>, btn_z: &Input<'_>,
btn_zl: &Input<'_>,
btn_dleft: &Input<'_>, btn_dleft: &Input<'_>,
btn_dright: &Input<'_>, btn_dright: &Input<'_>,
btn_dup: &Input<'_>, btn_dup: &Input<'_>,
btn_ddown: &Input<'_>, btn_ddown: &Input<'_>,
) { ) {
controller_state.button_a = btn_a.is_low(); gcc_state.buttons_1.button_a = btn_a.is_low();
controller_state.button_b = btn_b.is_low(); gcc_state.buttons_1.button_b = btn_b.is_low();
controller_state.button_x = btn_x.is_low(); gcc_state.buttons_1.button_x = btn_x.is_low();
controller_state.button_y = btn_y.is_low(); gcc_state.buttons_1.button_y = btn_y.is_low();
controller_state.trigger_zr = btn_zr.is_low(); gcc_state.buttons_2.button_z = btn_z.is_low();
controller_state.trigger_zl = btn_zl.is_low(); gcc_state.buttons_2.button_start = btn_start.is_low();
controller_state.button_start = btn_start.is_low(); gcc_state.buttons_2.button_l = btn_l.is_low();
controller_state.trigger_l = btn_l.is_low(); gcc_state.buttons_2.button_r = btn_r.is_low();
controller_state.trigger_r = btn_r.is_low(); gcc_state.buttons_1.dpad_left = btn_dleft.is_low();
controller_state.dpad_left = btn_dleft.is_low(); gcc_state.buttons_1.dpad_right = btn_dright.is_low();
controller_state.dpad_right = btn_dright.is_low(); gcc_state.buttons_1.dpad_up = btn_dup.is_low();
controller_state.dpad_up = btn_dup.is_low(); gcc_state.buttons_1.dpad_down = btn_ddown.is_low();
controller_state.dpad_down = btn_ddown.is_low(); gcc_state.trigger_l = match gcc_state.buttons_2.button_l {
true => 255,
false => 0,
};
gcc_state.trigger_r = match gcc_state.buttons_2.button_r {
true => 255,
false => 0,
};
} }
#[embassy_executor::task] #[embassy_executor::task]
pub async fn input_integrity_benchmark() { pub async fn input_integrity_benchmark() {
loop { loop {
SIGNAL_OVERRIDE_CONTROLLER_STATE.signal(OverrideGcReportInstruction { SIGNAL_OVERRIDE_GCC_STATE.signal(OverrideGcReportInstruction {
report: { report: {
ControllerState { let mut report = GcState::default();
dpad_up: true, report.buttons_1.dpad_up = true;
..Default::default() report
}
}, },
duration_ms: 100, duration_ms: 100,
}); });
@ -458,8 +402,7 @@ pub async fn input_integrity_benchmark() {
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
#[embassy_executor::task] #[embassy_executor::task]
pub async fn update_button_state_task( pub async fn update_button_state_task(
btn_zr: Input<'static>, btn_z: Input<'static>,
btn_zl: Input<'static>,
btn_a: Input<'static>, btn_a: Input<'static>,
btn_b: Input<'static>, btn_b: Input<'static>,
btn_dright: Input<'static>, btn_dright: Input<'static>,
@ -485,8 +428,6 @@ pub async fn update_button_state_task(
let mut m = MUTEX_CONTROLLER_MODE.lock().await; let mut m = MUTEX_CONTROLLER_MODE.lock().await;
*m = if btn_start.is_low() { *m = if btn_start.is_low() {
Some(ControllerMode::Procon) Some(ControllerMode::Procon)
} else if btn_x.is_low() {
Some(ControllerMode::XInput)
} else { } else {
Some(ControllerMode::GcAdapter) Some(ControllerMode::GcAdapter)
}; };
@ -499,11 +440,11 @@ pub async fn update_button_state_task(
MUTEX_INPUT_CONSISTENCY_MODE.lock().await.unwrap() MUTEX_INPUT_CONSISTENCY_MODE.lock().await.unwrap()
}; };
let mut previous_state = ControllerState::default(); let mut previous_state = GcState::default();
let mut controller_state = ControllerState::default(); let mut gcc_state = GcState::default();
let gcc_publisher = CHANNEL_CONTROLLER_STATE.publisher().unwrap(); let gcc_publisher = CHANNEL_GCC_STATE.publisher().unwrap();
let mut override_stick_state: Option<OverrideStickState> = None; let mut override_stick_state: Option<OverrideStickState> = None;
@ -516,7 +457,7 @@ pub async fn update_button_state_task(
loop { loop {
update_button_states( update_button_states(
&mut controller_state, &mut gcc_state,
&btn_a, &btn_a,
&btn_b, &btn_b,
&btn_x, &btn_x,
@ -524,8 +465,7 @@ pub async fn update_button_state_task(
&btn_start, &btn_start,
&btn_l, &btn_l,
&btn_r, &btn_r,
&btn_zr, &btn_z,
&btn_zl,
&btn_dleft, &btn_dleft,
&btn_dright, &btn_dright,
&btn_dup, &btn_dup,
@ -534,7 +474,10 @@ pub async fn update_button_state_task(
// not every loop pass is going to update the stick state // not every loop pass is going to update the stick state
if let Some(stick_state) = SIGNAL_STICK_STATE.try_take() { if let Some(stick_state) = SIGNAL_STICK_STATE.try_take() {
controller_state.stick_state = stick_state gcc_state.stick_x = stick_state.ax;
gcc_state.stick_y = stick_state.ay;
gcc_state.cstick_x = stick_state.cx;
gcc_state.cstick_y = stick_state.cy;
} }
if let Some(override_stick_state_opt) = SIGNAL_OVERRIDE_STICK_STATE.try_take() { if let Some(override_stick_state_opt) = SIGNAL_OVERRIDE_STICK_STATE.try_take() {
@ -543,7 +486,7 @@ pub async fn update_button_state_task(
} }
// check for a gcc state override (usually coming from the config task) // check for a gcc state override (usually coming from the config task)
if let Some(override_gcc_state) = SIGNAL_OVERRIDE_CONTROLLER_STATE.try_take() { if let Some(override_gcc_state) = SIGNAL_OVERRIDE_GCC_STATE.try_take() {
trace!("Overridden gcc state: {:?}", override_gcc_state.report); trace!("Overridden gcc state: {:?}", override_gcc_state.report);
let end_time = Instant::now() + Duration::from_millis(override_gcc_state.duration_ms); let end_time = Instant::now() + Duration::from_millis(override_gcc_state.duration_ms);
while Instant::now() < end_time { while Instant::now() < end_time {
@ -561,20 +504,20 @@ pub async fn update_button_state_task(
}; };
if let Some(override_state) = &override_stick_state { if let Some(override_state) = &override_stick_state {
let mut overriden_gcc_state = controller_state; let mut overriden_gcc_state = gcc_state;
match override_state.which_stick { match override_state.which_stick {
Stick::ControlStick => { Stick::ControlStick => {
overriden_gcc_state.stick_state.ax = override_state.x; overriden_gcc_state.stick_x = override_state.x;
overriden_gcc_state.stick_state.ay = override_state.y; overriden_gcc_state.stick_y = override_state.y;
} }
Stick::CStick => { Stick::CStick => {
overriden_gcc_state.stick_state.cx = override_state.x; overriden_gcc_state.cstick_x = override_state.x;
overriden_gcc_state.stick_state.cy = override_state.y; overriden_gcc_state.cstick_y = override_state.y;
} }
} }
gcc_publisher.publish_immediate(overriden_gcc_state); gcc_publisher.publish_immediate(overriden_gcc_state);
} else { } else {
input_filter.apply_filter(&mut controller_state); input_filter.apply_filter(&mut gcc_state);
if input_consistency_mode == InputConsistencyMode::SuperHack { if input_consistency_mode == InputConsistencyMode::SuperHack {
// transmit state always for the first 5 seconds to give the console time to initialize the controller // transmit state always for the first 5 seconds to give the console time to initialize the controller
if initializing && Instant::now().duration_since(init_time) > Duration::from_secs(5) if initializing && Instant::now().duration_since(init_time) > Duration::from_secs(5)
@ -582,12 +525,12 @@ pub async fn update_button_state_task(
initializing = false; initializing = false;
} }
if controller_state != previous_state || initializing { if gcc_state != previous_state || initializing {
gcc_publisher.publish_immediate(controller_state); gcc_publisher.publish_immediate(gcc_state);
previous_state = controller_state; previous_state = gcc_state;
} }
} else { } else {
gcc_publisher.publish_immediate(controller_state); gcc_publisher.publish_immediate(gcc_state);
} }
} }
@ -676,7 +619,7 @@ pub async fn update_stick_states_task(
last_loop_time = n; last_loop_time = n;
}; };
SIGNAL_STICK_STATE.signal(current_stick_state); SIGNAL_STICK_STATE.signal(current_stick_state.clone());
yield_now().await; yield_now().await;
ticker.next().await; ticker.next().await;

View file

@ -2,7 +2,7 @@ use defmt::warn;
use crate::{ use crate::{
config::{is_awaitable_button_pressed, AwaitableButtons}, config::{is_awaitable_button_pressed, AwaitableButtons},
input::ControllerState, hid::gcc::GcState,
}; };
/** /**
@ -14,7 +14,7 @@ use crate::{
*/ */
pub trait InputFilter: Sized { pub trait InputFilter: Sized {
fn apply_filter(&mut self, controller_state: &mut ControllerState); fn apply_filter(&mut self, gcc_state: &mut GcState);
} }
/// Presses a single button if another button is pressed. /// Presses a single button if another button is pressed.
@ -26,44 +26,46 @@ pub struct SingleButtonMacroFilter {
} }
impl InputFilter for SingleButtonMacroFilter { impl InputFilter for SingleButtonMacroFilter {
fn apply_filter(&mut self, controller_state: &mut ControllerState) { fn apply_filter(&mut self, gcc_state: &mut GcState) {
if is_awaitable_button_pressed(controller_state, &self.btn_instigator) { if is_awaitable_button_pressed(gcc_state, &self.btn_instigator) {
match self.btn_to_press { match self.btn_to_press {
AwaitableButtons::A => { AwaitableButtons::A => {
controller_state.button_a = true; gcc_state.buttons_1.button_a = true;
} }
AwaitableButtons::B => { AwaitableButtons::B => {
controller_state.button_b = true; gcc_state.buttons_1.button_b = true;
} }
AwaitableButtons::X => { AwaitableButtons::X => {
controller_state.button_x = true; gcc_state.buttons_1.button_x = true;
} }
AwaitableButtons::Y => { AwaitableButtons::Y => {
controller_state.button_y = true; gcc_state.buttons_1.button_y = true;
} }
AwaitableButtons::L => { AwaitableButtons::L => {
controller_state.trigger_l = true; gcc_state.trigger_l = 255;
gcc_state.buttons_2.button_l = true;
} }
AwaitableButtons::R => { AwaitableButtons::R => {
controller_state.trigger_r = true; gcc_state.trigger_r = 255;
gcc_state.buttons_2.button_r = true;
} }
AwaitableButtons::Z => { AwaitableButtons::Z => {
controller_state.trigger_zr = true; gcc_state.buttons_2.button_z = true;
} }
AwaitableButtons::Start => { AwaitableButtons::Start => {
controller_state.button_start = true; gcc_state.buttons_2.button_start = true;
} }
AwaitableButtons::Up => { AwaitableButtons::Up => {
controller_state.dpad_up = true; gcc_state.buttons_1.dpad_up = true;
} }
AwaitableButtons::Down => { AwaitableButtons::Down => {
controller_state.dpad_down = true; gcc_state.buttons_1.dpad_down = true;
} }
AwaitableButtons::Left => { AwaitableButtons::Left => {
controller_state.dpad_left = true; gcc_state.buttons_1.dpad_left = true;
} }
AwaitableButtons::Right => { AwaitableButtons::Right => {
controller_state.dpad_right = true; gcc_state.buttons_1.dpad_right = true;
} }
b => { b => {
warn!( warn!(
@ -81,22 +83,22 @@ impl InputFilter for SingleButtonMacroFilter {
pub struct CStickUpTiltFilter; pub struct CStickUpTiltFilter;
impl InputFilter for CStickUpTiltFilter { impl InputFilter for CStickUpTiltFilter {
fn apply_filter(&mut self, controller_state: &mut ControllerState) { fn apply_filter(&mut self, gcc_state: &mut GcState) {
if controller_state.stick_state.cy > 157 { if gcc_state.cstick_y > 157 {
if (137..=201).contains(&controller_state.stick_state.cx) { if (137..=201).contains(&gcc_state.cstick_x) {
controller_state.stick_state.cx = 201; gcc_state.cstick_x = 201;
controller_state.stick_state.cy = 255; gcc_state.cstick_y = 255;
} else if (53..=117).contains(&controller_state.stick_state.cx) { } else if (53..=117).contains(&gcc_state.cstick_x) {
controller_state.stick_state.cx = 53; gcc_state.cstick_x = 53;
controller_state.stick_state.cy = 255; gcc_state.cstick_y = 255;
} }
} else if controller_state.stick_state.cy < 97 { } else if gcc_state.cstick_y < 97 {
if (137..=221).contains(&controller_state.stick_state.cx) { if (137..=221).contains(&gcc_state.cstick_x) {
controller_state.stick_state.cx = 221; gcc_state.cstick_x = 221;
controller_state.stick_state.cy = 0; gcc_state.cstick_y = 0;
} else if (53..=117).contains(&controller_state.stick_state.cx) { } else if (53..=117).contains(&gcc_state.cstick_x) {
controller_state.stick_state.cx = 33; gcc_state.cstick_x = 33;
controller_state.stick_state.cy = 0; gcc_state.cstick_y = 0;
} }
} }
} }
@ -108,22 +110,22 @@ impl InputFilter for CStickUpTiltFilter {
pub struct CStickAngledFTiltFilter; pub struct CStickAngledFTiltFilter;
impl InputFilter for CStickAngledFTiltFilter { impl InputFilter for CStickAngledFTiltFilter {
fn apply_filter(&mut self, controller_state: &mut ControllerState) { fn apply_filter(&mut self, gcc_state: &mut GcState) {
if controller_state.stick_state.cy > 147 { if gcc_state.cstick_y > 147 {
if (147..=225).contains(&controller_state.stick_state.cx) { if (147..=225).contains(&gcc_state.cstick_x) {
controller_state.stick_state.cx = 205; gcc_state.cstick_x = 205;
controller_state.stick_state.cy = 205; gcc_state.cstick_y = 205;
} else if (30..=107).contains(&controller_state.stick_state.cx) { } else if (30..=107).contains(&gcc_state.cstick_x) {
controller_state.stick_state.cx = 50; gcc_state.cstick_x = 50;
controller_state.stick_state.cy = 205; gcc_state.cstick_y = 205;
} }
} else if controller_state.stick_state.cy < 107 { } else if gcc_state.cstick_y < 107 {
if (147..=225).contains(&controller_state.stick_state.cx) { if (147..=225).contains(&gcc_state.cstick_x) {
controller_state.stick_state.cx = 205; gcc_state.cstick_x = 205;
controller_state.stick_state.cy = 50; gcc_state.cstick_y = 50;
} else if (30..=107).contains(&controller_state.stick_state.cx) { } else if (30..=107).contains(&gcc_state.cstick_x) {
controller_state.stick_state.cx = 50; gcc_state.cstick_x = 50;
controller_state.stick_state.cy = 50; gcc_state.cstick_y = 50;
} }
} }
} }
@ -134,5 +136,5 @@ impl InputFilter for CStickAngledFTiltFilter {
pub struct DummyFilter; pub struct DummyFilter;
impl InputFilter for DummyFilter { impl InputFilter for DummyFilter {
fn apply_filter(&mut self, _gcc_state: &mut ControllerState) {} fn apply_filter(&mut self, _gcc_state: &mut GcState) {}
} }

View file

@ -89,7 +89,6 @@ fn main() -> ! {
spawner spawner
.spawn(update_button_state_task( .spawn(update_button_state_task(
Input::new(AnyPin::from(p.PIN_20), gpio::Pull::Up), Input::new(AnyPin::from(p.PIN_20), gpio::Pull::Up),
Input::new(AnyPin::from(p.PIN_15), gpio::Pull::Up),
Input::new(AnyPin::from(p.PIN_17), gpio::Pull::Up), Input::new(AnyPin::from(p.PIN_17), gpio::Pull::Up),
Input::new(AnyPin::from(p.PIN_16), gpio::Pull::Up), Input::new(AnyPin::from(p.PIN_16), gpio::Pull::Up),
Input::new(AnyPin::from(p.PIN_11), gpio::Pull::Up), Input::new(AnyPin::from(p.PIN_11), gpio::Pull::Up),

View file

@ -15,7 +15,7 @@ use embassy_rp::{
use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, mutex::Mutex, signal::Signal}; use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, mutex::Mutex, signal::Signal};
use embassy_time::{Duration, Instant, Timer}; use embassy_time::{Duration, Instant, Timer};
use embassy_usb::{ use embassy_usb::{
class::hid::{HidReaderWriter, RequestHandler, State}, class::hid::{HidReader, HidReaderWriter, HidWriter, RequestHandler, State},
driver::Driver, driver::Driver,
msos::{self, windows_version}, msos::{self, windows_version},
Builder, Handler, UsbDevice, Builder, Handler, UsbDevice,
@ -25,15 +25,10 @@ use libm::powf;
use crate::{ use crate::{
config::{ControllerMode, InputConsistencyMode}, config::{ControllerMode, InputConsistencyMode},
hid::{ hid::{
gcc::{GcReportBuilder, GccRequestHandler, GCC_REPORT_DESCRIPTOR}, gcc::{GcReportBuilder, GcState, GccRequestHandler, GCC_REPORT_DESCRIPTOR},
procon::{ProconReportBuilder, ProconRequestHandler, PROCON_REPORT_DESCRIPTOR}, procon::{ProconReportBuilder, ProconRequestHandler, PROCON_REPORT_DESCRIPTOR},
xinput::{
XInputReaderWriter, XInputReportBuilder, XInputRequestHandler, XInputState,
XINPUT_REPORT_DESCRIPTOR,
}, },
HidReaderWriterSplit, UsbReader, UsbWriter, input::CHANNEL_GCC_STATE,
},
input::{ControllerState, CHANNEL_CONTROLLER_STATE},
}; };
pub static SIGNAL_RUMBLE: Signal<CriticalSectionRawMutex, bool> = Signal::new(); pub static SIGNAL_RUMBLE: Signal<CriticalSectionRawMutex, bool> = Signal::new();
@ -56,7 +51,7 @@ pub static MUTEX_CONTROLLER_MODE: Mutex<CriticalSectionRawMutex, Option<Controll
const DEVICE_INTERFACE_GUID: &str = "{ecceff35-146c-4ff3-acd9-8f992d09acdd}"; const DEVICE_INTERFACE_GUID: &str = "{ecceff35-146c-4ff3-acd9-8f992d09acdd}";
pub trait HidReportBuilder<const LEN: usize> { pub trait HidReportBuilder<const LEN: usize> {
async fn get_hid_report(&mut self, state: &ControllerState) -> [u8; LEN]; async fn get_hid_report(&mut self, state: &GcState) -> [u8; LEN];
} }
struct UsbConfig { struct UsbConfig {
@ -78,11 +73,6 @@ impl From<ControllerMode> for UsbConfig {
pid: 0x2009, pid: 0x2009,
report_descriptor: PROCON_REPORT_DESCRIPTOR, report_descriptor: PROCON_REPORT_DESCRIPTOR,
}, },
ControllerMode::XInput => Self {
vid: 0x045e,
pid: 0x028e,
report_descriptor: XINPUT_REPORT_DESCRIPTOR,
},
} }
} }
} }
@ -132,41 +122,27 @@ impl Handler for MyDeviceHandler {
} }
} }
fn mk_hid_reader_writer<'d, S, D, T, F, const R: usize, const W: usize>( fn mk_hid_reader_writer<'d, D: Driver<'d>, const R: usize, const W: usize>(
input_consistency_mode: InputConsistencyMode, input_consistency_mode: InputConsistencyMode,
controller_mode: ControllerMode,
report_descriptor: &'d [u8], report_descriptor: &'d [u8],
mut builder: Builder<'d, D>, mut builder: Builder<'d, D>,
state: &'d mut S, state: &'d mut State<'d>,
mut init_func: F, ) -> (UsbDevice<'d, D>, HidReader<'d, D, R>, HidWriter<'d, D, W>) {
) -> (
UsbDevice<'d, D>,
impl UsbReader<'d, D, R>,
impl UsbWriter<'d, D, W>,
)
where
D: Driver<'d>,
T: HidReaderWriterSplit<'d, D, R, W> + 'd,
F: FnMut(&mut Builder<'d, D>, &'d mut S, embassy_usb::class::hid::Config<'d>) -> T + 'd,
{
let hid_config = embassy_usb::class::hid::Config { let hid_config = embassy_usb::class::hid::Config {
report_descriptor, report_descriptor,
request_handler: None, request_handler: None,
poll_ms: if let ControllerMode::XInput = controller_mode { poll_ms: match input_consistency_mode {
1
} else {
match input_consistency_mode {
InputConsistencyMode::Original => 8, InputConsistencyMode::Original => 8,
InputConsistencyMode::ConsistencyHack InputConsistencyMode::ConsistencyHack
| InputConsistencyMode::SuperHack | InputConsistencyMode::SuperHack
| InputConsistencyMode::PC => 1, | InputConsistencyMode::PC => 1,
}
}, },
max_packet_size_in: W as u16, max_packet_size_in: W as u16,
max_packet_size_out: R as u16, max_packet_size_out: R as u16,
}; };
let hid = init_func(&mut builder, state, hid_config); let hid: HidReaderWriter<'d, D, R, W> =
HidReaderWriter::<'_, D, R, W>::new(&mut builder, state, hid_config);
let usb = builder.build(); let usb = builder.build();
@ -175,16 +151,13 @@ where
(usb, reader, writer) (usb, reader, writer)
} }
#[allow(clippy::too_many_arguments)] fn mk_usb_transfer_futures<'d, D, H, Rq, const R: usize, const W: usize>(
fn mk_usb_transfer_futures<'d, S, D, H, F, Rq, T, const R: usize, const W: usize>(
input_consistency_mode: InputConsistencyMode, input_consistency_mode: InputConsistencyMode,
controller_mode: ControllerMode,
usb_config: &UsbConfig, usb_config: &UsbConfig,
request_handler: &'d mut Rq, request_handler: &'d mut Rq,
builder: Builder<'d, D>, builder: Builder<'d, D>,
state: &'d mut State<'d>,
mut hid_report_builder: H, mut hid_report_builder: H,
state: &'d mut S,
init_func: F,
) -> ( ) -> (
impl Future<Output = ()> + 'd, impl Future<Output = ()> + 'd,
impl Future<Output = ()> + 'd, impl Future<Output = ()> + 'd,
@ -194,16 +167,12 @@ where
D: Driver<'d> + 'd, D: Driver<'d> + 'd,
H: HidReportBuilder<W> + 'd, H: HidReportBuilder<W> + 'd,
Rq: RequestHandler, Rq: RequestHandler,
T: HidReaderWriterSplit<'d, D, R, W> + 'd,
F: FnMut(&mut Builder<'d, D>, &'d mut S, embassy_usb::class::hid::Config<'d>) -> T + 'd,
{ {
let (mut usb, reader, mut writer) = mk_hid_reader_writer( let (mut usb, reader, mut writer) = mk_hid_reader_writer::<_, R, W>(
input_consistency_mode, input_consistency_mode,
controller_mode,
usb_config.report_descriptor, usb_config.report_descriptor,
builder, builder,
state, state,
init_func,
); );
let usb_fut = async move { let usb_fut = async move {
@ -216,7 +185,7 @@ where
}; };
let in_fut = async move { let in_fut = async move {
let mut gcc_subscriber = CHANNEL_CONTROLLER_STATE.subscriber().unwrap(); let mut gcc_subscriber = CHANNEL_GCC_STATE.subscriber().unwrap();
let mut last_report_time = Instant::now(); let mut last_report_time = Instant::now();
let mut rate_limit_end_time = Instant::now(); let mut rate_limit_end_time = Instant::now();
@ -231,7 +200,6 @@ where
// From the console's perspective, we are basically a laggy adapter, taking // From the console's perspective, we are basically a laggy adapter, taking
// a minimum of 333 extra us to send a report every time it's polled, but it // a minimum of 333 extra us to send a report every time it's polled, but it
// works to our advantage. // works to our advantage.
if controller_mode != ControllerMode::XInput {
match input_consistency_mode { match input_consistency_mode {
InputConsistencyMode::SuperHack | InputConsistencyMode::ConsistencyHack => { InputConsistencyMode::SuperHack | InputConsistencyMode::ConsistencyHack => {
// "Ticker at home", so we can use this for both consistency and SuperHack mode // "Ticker at home", so we can use this for both consistency and SuperHack mode
@ -239,7 +207,6 @@ where
} }
InputConsistencyMode::Original | InputConsistencyMode::PC => {} InputConsistencyMode::Original | InputConsistencyMode::PC => {}
} }
}
writer.ready().await; writer.ready().await;
@ -257,7 +224,6 @@ where
debug!("Report written in {}us", micros); debug!("Report written in {}us", micros);
if input_consistency_mode != InputConsistencyMode::Original if input_consistency_mode != InputConsistencyMode::Original
&& input_consistency_mode != InputConsistencyMode::PC && input_consistency_mode != InputConsistencyMode::PC
&& controller_mode != ControllerMode::XInput
{ {
while rate_limit_end_time < currtime { while rate_limit_end_time < currtime {
rate_limit_end_time += Duration::from_micros(8333); rate_limit_end_time += Duration::from_micros(8333);
@ -325,37 +291,20 @@ pub async fn usb_transfer_task(raw_serial: [u8; 8], driver: EmbassyDriver<'stati
trace!("Start of config"); trace!("Start of config");
let mut usb_config = embassy_usb::Config::new(config.vid, config.pid); let mut usb_config = embassy_usb::Config::new(config.vid, config.pid);
usb_config.manufacturer = Some("Naxdy"); usb_config.manufacturer = Some("Naxdy");
usb_config.product = Some(if controller_mode == ControllerMode::XInput { usb_config.product = Some(match input_consistency_mode {
"NaxGCC (XInput Mode)"
} else {
match input_consistency_mode {
InputConsistencyMode::Original => "NaxGCC (OG Mode)", InputConsistencyMode::Original => "NaxGCC (OG Mode)",
InputConsistencyMode::ConsistencyHack => "NaxGCC (Consistency Mode)", InputConsistencyMode::ConsistencyHack => "NaxGCC (Consistency Mode)",
InputConsistencyMode::SuperHack => "NaxGCC (SuperHack Mode)", InputConsistencyMode::SuperHack => "NaxGCC (SuperHack Mode)",
InputConsistencyMode::PC => "NaxGCC (PC Mode)", InputConsistencyMode::PC => "NaxGCC (PC Mode)",
}
}); });
usb_config.serial_number = Some(serial); usb_config.serial_number = Some(serial);
usb_config.max_power = 200; usb_config.max_power = 200;
usb_config.max_packet_size_0 = 64; usb_config.max_packet_size_0 = 64;
usb_config.device_class = if controller_mode == ControllerMode::XInput { usb_config.device_class = 0;
0xff
} else {
0
};
usb_config.device_protocol = 0; usb_config.device_protocol = 0;
usb_config.self_powered = false; usb_config.self_powered = false;
usb_config.device_sub_class = if controller_mode == ControllerMode::XInput { usb_config.device_sub_class = 0;
0xff
} else {
0
};
usb_config.supports_remote_wakeup = true; usb_config.supports_remote_wakeup = true;
usb_config.device_release = if controller_mode == ControllerMode::XInput {
0x0572
} else {
0x0010
};
let mut config_descriptor = [0; 256]; let mut config_descriptor = [0; 256];
let mut bos_descriptor = [0; 256]; let mut bos_descriptor = [0; 256];
@ -364,6 +313,8 @@ pub async fn usb_transfer_task(raw_serial: [u8; 8], driver: EmbassyDriver<'stati
let mut device_handler = MyDeviceHandler::new(); let mut device_handler = MyDeviceHandler::new();
let mut state = State::new();
let mut builder = Builder::new( let mut builder = Builder::new(
driver, driver,
usb_config, usb_config,
@ -386,55 +337,29 @@ pub async fn usb_transfer_task(raw_serial: [u8; 8], driver: EmbassyDriver<'stati
match controller_mode { match controller_mode {
ControllerMode::GcAdapter => { ControllerMode::GcAdapter => {
let mut state = State::new();
let mut request_handler = GccRequestHandler; let mut request_handler = GccRequestHandler;
let (usb_fut_wrapped, in_fut, out_fut) = mk_usb_transfer_futures( let (usb_fut_wrapped, in_fut, out_fut) = mk_usb_transfer_futures::<_, _, _, 5, 37>(
input_consistency_mode, input_consistency_mode,
controller_mode,
&config, &config,
&mut request_handler, &mut request_handler,
builder, builder,
GcReportBuilder::default(),
&mut state, &mut state,
HidReaderWriter::<_, 5, 37>::new, GcReportBuilder::default(),
); );
join(usb_fut_wrapped, join(in_fut, out_fut)).await; join(usb_fut_wrapped, join(in_fut, out_fut)).await;
} }
ControllerMode::Procon => { ControllerMode::Procon => {
let mut state = State::new();
let mut request_handler = ProconRequestHandler; let mut request_handler = ProconRequestHandler;
let (usb_fut_wrapped, in_fut, out_fut) = mk_usb_transfer_futures( let (usb_fut_wrapped, in_fut, out_fut) = mk_usb_transfer_futures::<_, _, _, 64, 64>(
input_consistency_mode, input_consistency_mode,
controller_mode,
&config, &config,
&mut request_handler, &mut request_handler,
builder, builder,
&mut state,
ProconReportBuilder::default(), ProconReportBuilder::default(),
&mut state,
HidReaderWriter::<_, 64, 64>::new,
);
join(usb_fut_wrapped, join(in_fut, out_fut)).await;
}
ControllerMode::XInput => {
let mut state = XInputState::new();
let mut request_handler = XInputRequestHandler;
let (usb_fut_wrapped, in_fut, out_fut) = mk_usb_transfer_futures(
input_consistency_mode,
controller_mode,
&config,
&mut request_handler,
builder,
XInputReportBuilder,
&mut state,
XInputReaderWriter::<_, 32, 32>::new,
); );
join(usb_fut_wrapped, join(in_fut, out_fut)).await; join(usb_fut_wrapped, join(in_fut, out_fut)).await;