diff --git a/Cargo.lock b/Cargo.lock index 5c0f1c1..e69fe6e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,18 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + [[package]] name = "aho-corasick" version = "1.1.2" @@ -77,6 +89,12 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719" +[[package]] +name = "bitfield" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d7e60934ceec538daadb9d8432424ed043a904d8e0243f3c6446bce549a46ac" + [[package]] name = "bitflags" version = "1.3.2" @@ -109,9 +127,9 @@ checksum = "a2ef034f05691a48569bd920a96c81b9d91bbad1ab5ac7c4616c1f6ef36cb79f" [[package]] name = "byteorder" -version = "1.4.3" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "cfg-if" @@ -136,7 +154,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ec610d8f49840a5b376c69663b6369e71f4b34484b9b2eb29fb918d92516cb9" dependencies = [ "bare-metal", - "bitfield", + "bitfield 0.13.2", "embedded-hal 0.2.7", "volatile-register", ] @@ -309,7 +327,8 @@ checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" [[package]] name = "embassy-embedded-hal" -version = "0.1.0" +version = "0.2.0" +source = "git+https://git.naxdy.org/NaxdyOrg/embassy.git?branch=naxgcc-fw#16151cce0271573a66206aa6e85932c20bbf0c70" dependencies = [ "defmt", "embassy-futures", @@ -325,7 +344,8 @@ dependencies = [ [[package]] name = "embassy-executor" -version = "0.5.0" +version = "0.6.0" +source = "git+https://git.naxdy.org/NaxdyOrg/embassy.git?branch=naxgcc-fw#16151cce0271573a66206aa6e85932c20bbf0c70" dependencies = [ "cortex-m", "critical-section", @@ -338,7 +358,8 @@ dependencies = [ [[package]] name = "embassy-executor-macros" -version = "0.4.0" +version = "0.5.0" +source = "git+https://git.naxdy.org/NaxdyOrg/embassy.git?branch=naxgcc-fw#16151cce0271573a66206aa6e85932c20bbf0c70" dependencies = [ "darling", "proc-macro2", @@ -349,13 +370,15 @@ dependencies = [ [[package]] name = "embassy-futures" version = "0.1.1" +source = "git+https://git.naxdy.org/NaxdyOrg/embassy.git?branch=naxgcc-fw#16151cce0271573a66206aa6e85932c20bbf0c70" dependencies = [ "defmt", ] [[package]] name = "embassy-hal-internal" -version = "0.1.0" +version = "0.2.0" +source = "git+https://git.naxdy.org/NaxdyOrg/embassy.git?branch=naxgcc-fw#16151cce0271573a66206aa6e85932c20bbf0c70" dependencies = [ "cortex-m", "critical-section", @@ -366,10 +389,12 @@ dependencies = [ [[package]] name = "embassy-net-driver" version = "0.2.0" +source = "git+https://git.naxdy.org/NaxdyOrg/embassy.git?branch=naxgcc-fw#16151cce0271573a66206aa6e85932c20bbf0c70" [[package]] name = "embassy-net-driver-channel" -version = "0.2.0" +version = "0.3.0" +source = "git+https://git.naxdy.org/NaxdyOrg/embassy.git?branch=naxgcc-fw#16151cce0271573a66206aa6e85932c20bbf0c70" dependencies = [ "embassy-futures", "embassy-net-driver", @@ -378,7 +403,8 @@ dependencies = [ [[package]] name = "embassy-rp" -version = "0.1.0" +version = "0.2.0" +source = "git+https://git.naxdy.org/NaxdyOrg/embassy.git?branch=naxgcc-fw#16151cce0271573a66206aa6e85932c20bbf0c70" dependencies = [ "atomic-polyfill", "cfg-if", @@ -403,7 +429,6 @@ dependencies = [ "embedded-storage", "embedded-storage-async", "fixed", - "futures", "nb 1.1.0", "pio", "pio-proc", @@ -414,7 +439,8 @@ dependencies = [ [[package]] name = "embassy-sync" -version = "0.5.0" +version = "0.6.0" +source = "git+https://git.naxdy.org/NaxdyOrg/embassy.git?branch=naxgcc-fw#16151cce0271573a66206aa6e85932c20bbf0c70" dependencies = [ "cfg-if", "critical-section", @@ -426,7 +452,8 @@ dependencies = [ [[package]] name = "embassy-time" -version = "0.3.0" +version = "0.3.2" +source = "git+https://git.naxdy.org/NaxdyOrg/embassy.git?branch=naxgcc-fw#16151cce0271573a66206aa6e85932c20bbf0c70" dependencies = [ "cfg-if", "critical-section", @@ -444,6 +471,7 @@ dependencies = [ [[package]] name = "embassy-time-driver" version = "0.1.0" +source = "git+https://git.naxdy.org/NaxdyOrg/embassy.git?branch=naxgcc-fw#16151cce0271573a66206aa6e85932c20bbf0c70" dependencies = [ "document-features", ] @@ -451,10 +479,12 @@ dependencies = [ [[package]] name = "embassy-time-queue-driver" version = "0.1.0" +source = "git+https://git.naxdy.org/NaxdyOrg/embassy.git?branch=naxgcc-fw#16151cce0271573a66206aa6e85932c20bbf0c70" [[package]] name = "embassy-usb" -version = "0.1.0" +version = "0.3.0" +source = "git+https://git.naxdy.org/NaxdyOrg/embassy.git?branch=naxgcc-fw#16151cce0271573a66206aa6e85932c20bbf0c70" dependencies = [ "defmt", "embassy-futures", @@ -469,6 +499,7 @@ dependencies = [ [[package]] name = "embassy-usb-driver" version = "0.1.0" +source = "git+https://git.naxdy.org/NaxdyOrg/embassy.git?branch=naxgcc-fw#16151cce0271573a66206aa6e85932c20bbf0c70" dependencies = [ "defmt", ] @@ -630,59 +661,12 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" -[[package]] -name = "futures" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" -dependencies = [ - "futures-core", - "futures-sink", -] - [[package]] name = "futures-core" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" -[[package]] -name = "futures-io" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" - -[[package]] -name = "futures-macro" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.52", -] - -[[package]] -name = "futures-sink" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" - [[package]] name = "futures-task" version = "0.3.30" @@ -696,8 +680,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ "futures-core", - "futures-macro", - "futures-sink", "futures-task", "pin-project-lite", "pin-utils", @@ -733,6 +715,15 @@ dependencies = [ "byteorder", ] +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash", +] + [[package]] name = "hashbrown" version = "0.14.3" @@ -768,7 +759,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b0b929d511467233429c45a44ac1dcaa21ba0f5ba11e4879e6ed28ddb4f9df4" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.14.3", ] [[package]] @@ -1453,15 +1444,19 @@ checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" [[package]] name = "usb-device" -version = "0.2.9" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f6cc3adc849b5292b4075fc0d5fdcf2f24866e88e336dd27a8943090a520508" +checksum = "98816b1accafbb09085168b90f27e93d790b4bfa19d883466b5e53315b5f06a6" +dependencies = [ + "heapless", + "portable-atomic", +] [[package]] name = "usbd-hid" -version = "0.6.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "975bd411f4a939986751ea09992a24fa47c4d25c6ed108d04b4c2999a4fd0132" +checksum = "e6f291ab53d428685cc780f08a2eb9d5d6ff58622db2b36e239a4f715f1e184c" dependencies = [ "serde", "ssmarshal", @@ -1471,20 +1466,22 @@ dependencies = [ [[package]] name = "usbd-hid-descriptors" -version = "0.1.2" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcbee8c6735e90894fba04770bc41e11fd3c5256018856e15dc4dd1e6c8a3dd1" +checksum = "0eee54712c5d778d2fb2da43b1ce5a7b5060886ef7b09891baeb4bf36910a3ed" dependencies = [ - "bitfield", + "bitfield 0.14.0", ] [[package]] name = "usbd-hid-macros" -version = "0.6.0" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "261079a9ada015fa1acac7cc73c98559f3a92585e15f508034beccf6a2ab75a2" +checksum = "bb573c76e7884035ac5e1ab4a81234c187a82b6100140af0ab45757650ccda38" dependencies = [ "byteorder", + "hashbrown 0.13.2", + "log", "proc-macro2", "quote", "serde", @@ -1687,3 +1684,23 @@ checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" dependencies = [ "tap", ] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] diff --git a/Cargo.toml b/Cargo.toml index 3233b94..c02148b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,33 +9,33 @@ edition = "2021" embassy-time = { version = "0.3.0", features = [ "defmt", "defmt-timestamp-uptime", -], path = "lib/embassy-rs/embassy-time" } -embassy-embedded-hal = { version = "0.1.0", features = [ +], git = "https://git.naxdy.org/NaxdyOrg/embassy.git", branch = "naxgcc-fw" } +embassy-embedded-hal = { version = "0.2.0", features = [ "defmt", -], path = "lib/embassy-rs/embassy-embedded-hal" } -embassy-sync = { version = "0.5.0", features = [ +], git = "https://git.naxdy.org/NaxdyOrg/embassy.git", branch = "naxgcc-fw" } +embassy-sync = { version = "0.6.0", features = [ "defmt", -], path = "lib/embassy-rs/embassy-sync" } -embassy-executor = { version = "0.5.0", features = [ +], git = "https://git.naxdy.org/NaxdyOrg/embassy.git", branch = "naxgcc-fw" } +embassy-executor = { version = "0.6.0", features = [ "task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers", -], path = "lib/embassy-rs/embassy-executor" } -embassy-rp = { version = "0.1.0", features = [ +], git = "https://git.naxdy.org/NaxdyOrg/embassy.git", branch = "naxgcc-fw" } +embassy-rp = { version = "0.2.0", features = [ "defmt", "unstable-pac", "time-driver", "critical-section-impl", -], path = "lib/embassy-rs/embassy-rp" } -embassy-usb = { version = "0.1.0", features = [ +], git = "https://git.naxdy.org/NaxdyOrg/embassy.git", branch = "naxgcc-fw" } +embassy-usb = { version = "0.3.0", features = [ "defmt", -], path = "lib/embassy-rs/embassy-usb" } +], git = "https://git.naxdy.org/NaxdyOrg/embassy.git", branch = "naxgcc-fw" } embassy-futures = { version = "0.1.0", features = [ "defmt", -], path = "lib/embassy-rs/embassy-futures" } +], git = "https://git.naxdy.org/NaxdyOrg/embassy.git", branch = "naxgcc-fw" } defmt = "0.3" defmt-rtt = "0.4" fixed = "1.23.1" @@ -48,7 +48,7 @@ libm = { version = "0.2.8" } cortex-m = { version = "0.7.6", features = ["inline-asm"] } cortex-m-rt = "0.7.0" panic-probe = { version = "0.3", features = ["print-defmt"] } -packed_struct = { version = "0.10.1", default_features = false } +packed_struct = { version = "0.10.1", default-features = false } format_no_std = "1.0.2" rand = { version = "0.8.5", default-features = false } @@ -79,14 +79,14 @@ overflow-checks = false codegen-units = 8 debug = false debug-assertions = false -opt-level = 0 +opt-level = 3 overflow-checks = false [profile.release.build-override] codegen-units = 8 debug = false debug-assertions = false -opt-level = 0 +opt-level = 3 overflow-checks = false # cargo test @@ -106,12 +106,3 @@ debug-assertions = false incremental = false lto = 'fat' opt-level = 3 - -# [patch.crates-io] -# embassy-rp = { path = "lib/embassy-rs/embassy-rp" } -# embassy-time = { path = "lib/embassy-rs/embassy-time" } -# embassy-embedded-hal = { path = "lib/embassy-rs/embassy-embedded-hal" } -# embassy-usb = { path = "lib/embassy-rs/embassy-usb" } -# embassy-sync = { path = "lib/embassy-rs/embassy-sync" } -# embassy-executor = { path = "lib/embassy-rs/embassy-executor" } -# embassy-futures = { path = "lib/embassy-rs/embassy-futures" } diff --git a/flake.lock b/flake.lock index 516b25e..9fe6565 100644 --- a/flake.lock +++ b/flake.lock @@ -35,24 +35,6 @@ "type": "github" } }, - "flake-utils_2": { - "inputs": { - "systems": "systems_2" - }, - "locked": { - "lastModified": 1705309234, - "narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "flake-utils", - "type": "github" - } - }, "naersk": { "inputs": { "nixpkgs": [ @@ -91,11 +73,11 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1706487304, - "narHash": "sha256-LE8lVX28MV2jWJsidW13D2qrHU/RUUONendL2Q/WlJg=", + "lastModified": 1718428119, + "narHash": "sha256-WdWDpNaq6u1IPtxtYHHWpl5BmabtpmLnMAx0RdJ/vo8=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "90f456026d284c22b3e3497be980b2e47d0b28ac", + "rev": "e6cea36f83499eb4e9cd184c8a8e823296b50ad5", "type": "github" }, "original": { @@ -116,15 +98,14 @@ }, "rust-overlay": { "inputs": { - "flake-utils": "flake-utils_2", "nixpkgs": "nixpkgs_2" }, "locked": { - "lastModified": 1710382258, - "narHash": "sha256-2FW1q+o34VBweYQiEkRaSEkNMq3ecrn83VzETeGiVbY=", + "lastModified": 1728700003, + "narHash": "sha256-Ox1pvEHxLK6lAdaKQW21Zvk65SPDag+cD8YA444R/og=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "8ce81e71ab04a7e906fae62da086d6ee5d6cfc21", + "rev": "fc1e58ebabe0cef4442eedea07556ff0c9eafcfe", "type": "github" }, "original": { @@ -147,21 +128,6 @@ "repo": "default", "type": "github" } - }, - "systems_2": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } } }, "root": "root", diff --git a/src/config.rs b/src/config.rs index 100ad5a..2f0962e 100644 --- a/src/config.rs +++ b/src/config.rs @@ -16,10 +16,6 @@ use packed_struct::{ }; use crate::{ - gcc_hid::{ - GcButtons1, GcButtons2, MUTEX_CONTROLLER_MODE, MUTEX_INPUT_CONSISTENCY_MODE, - SIGNAL_CHANGE_RUMBLE_STRENGTH, - }, helpers::{PackedFloat, ToPackedFloatArray, ToRegularArray, XyValuePair}, input::{ read_ext_adc, Stick, StickAxis, FLOAT_ORIGIN, SPI_ACS_SHARED, SPI_CCS_SHARED, SPI_SHARED, @@ -29,6 +25,10 @@ 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, + }, ADDR_OFFSET, FLASH_SIZE, }; @@ -39,7 +39,7 @@ use embassy_sync::{ }; use embassy_time::Timer; -use crate::{gcc_hid::GcReport, input::CHANNEL_GCC_STATE}; +use crate::{input::CHANNEL_GCC_STATE, usb_comms::GcState}; /// Whether we are currently calibrating the sticks. Updates are dispatched when the status changes. /// Initial status is assumed to be false. @@ -81,7 +81,7 @@ const MAX_ANALOG_SCALER: u8 = 125; /// a certain mode. #[derive(Default, Debug, Clone, Format)] pub struct OverrideGcReportInstruction { - pub report: GcReport, + pub report: GcState, pub duration_ms: u64, } @@ -503,7 +503,7 @@ enum NotchAdjustmentType { /// This needs to be incremented for ANY change to ControllerConfig /// else we risk loading uninitialized memory. -pub const CONTROLLER_CONFIG_REVISION: u8 = 2; +pub const CONTROLLER_CONFIG_REVISION: u8 = 1; #[derive(Debug, Clone, Format, PackedStruct)] #[packed_struct(endian = "msb")] @@ -566,6 +566,7 @@ pub enum InputConsistencyMode { PC = 3, } +/// Not saved, but set upon plugging in the controller. #[derive(Debug, Clone, Copy, Format, PrimitiveEnum_u8, PartialEq, Eq)] pub enum ControllerMode { /// Advertise itself as a GCC adapter with 1 controller (itself) connected. @@ -591,8 +592,6 @@ pub struct ControllerConfig { pub astick_config: StickConfig, #[packed_field(size_bytes = "328")] pub cstick_config: StickConfig, - #[packed_field(size_bits = "8", ty = "enum")] - pub controller_mode: ControllerMode, } impl Default for ControllerConfig { @@ -603,7 +602,6 @@ impl Default for ControllerConfig { astick_config: StickConfig::default(), rumble_strength: 9, cstick_config: StickConfig::default(), - controller_mode: ControllerMode::GcAdapter, } } } @@ -651,6 +649,8 @@ impl ControllerConfig { /// Trait for providing button presses, used in the calibration process. trait ButtonPressProvider { /// Wait for a single button press. + // TODO: remove allow once this is used somewhere + #[allow(dead_code)] async fn wait_for_button_press(&mut self, button_to_wait_for: &AwaitableButtons); /// Wait for a single button release. @@ -684,7 +684,7 @@ trait ButtonPressProvider { } impl<'a, T: RawMutex, const I: usize, const J: usize, const K: usize> ButtonPressProvider - for Subscriber<'a, T, GcReport, I, J, K> + for Subscriber<'a, T, GcState, I, J, K> { async fn wait_for_button_press(&mut self, button_to_wait_for: &AwaitableButtons) { loop { @@ -784,7 +784,7 @@ impl<'a, T: RawMutex, const I: usize, const J: usize, const K: usize> ButtonPres } pub fn is_awaitable_button_pressed( - report: &GcReport, + report: &GcState, button_to_wait_for: &AwaitableButtons, ) -> bool { match button_to_wait_for { @@ -1150,7 +1150,7 @@ async fn configuration_main_loop< >( current_config: &ControllerConfig, flash: &mut Flash<'static, FLASH, Async, FLASH_SIZE>, - gcc_subscriber: &mut Subscriber<'a, M, GcReport, C, S, P>, + gcc_subscriber: &mut Subscriber<'a, M, GcState, C, S, P>, ) -> ControllerConfig { let mut final_config = current_config.clone(); let config_options = [ @@ -1208,7 +1208,7 @@ async fn configuration_main_loop< // exit 0 => { override_gcc_state_and_wait(&OverrideGcReportInstruction { - report: GcReport { + report: GcState { trigger_r: 255, trigger_l: 255, buttons_2: GcButtons2 { @@ -1236,7 +1236,7 @@ async fn configuration_main_loop< // calibrate lstick 1 => { override_gcc_state_and_wait(&OverrideGcReportInstruction { - report: GcReport { + report: GcState { trigger_r: 255, trigger_l: 255, buttons_2: GcButtons2 { @@ -1265,7 +1265,7 @@ async fn configuration_main_loop< // calibrate rstick 2 => { override_gcc_state_and_wait(&OverrideGcReportInstruction { - report: GcReport { + report: GcState { trigger_r: 255, trigger_l: 255, buttons_2: GcButtons2 { @@ -1324,7 +1324,7 @@ async fn configuration_main_loop< .clamp(-ABS_MAX_SNAPBACK, ABS_MAX_SNAPBACK); override_gcc_state_and_wait(&OverrideGcReportInstruction { - report: GcReport { + report: GcState { trigger_r: 255, trigger_l: 255, buttons_2: GcButtons2 { @@ -1410,7 +1410,7 @@ async fn configuration_main_loop< .clamp(0, MAX_WAVESHAPING) as u8; override_gcc_state_and_wait(&OverrideGcReportInstruction { - report: GcReport { + report: GcState { trigger_r: 255, trigger_l: 255, buttons_2: GcButtons2 { @@ -1496,7 +1496,7 @@ async fn configuration_main_loop< .clamp(0, MAX_SMOOTHING) as u8; override_gcc_state_and_wait(&OverrideGcReportInstruction { - report: GcReport { + report: GcState { trigger_r: 255, trigger_l: 255, buttons_2: GcButtons2 { @@ -1571,7 +1571,7 @@ async fn configuration_main_loop< .clamp(-1, MAX_CARDINAL_SNAP); override_gcc_state_and_wait(&OverrideGcReportInstruction { - report: GcReport { + report: GcState { trigger_r: 255, trigger_l: 255, buttons_2: GcButtons2 { @@ -1627,7 +1627,7 @@ async fn configuration_main_loop< as u8; override_gcc_state_and_wait(&OverrideGcReportInstruction { - report: GcReport { + report: GcState { trigger_r: 255, trigger_l: 255, buttons_2: GcButtons2 { @@ -1675,7 +1675,7 @@ async fn configuration_main_loop< SIGNAL_CHANGE_RUMBLE_STRENGTH.signal(*to_adjust); override_gcc_state_and_wait(&OverrideGcReportInstruction { - report: GcReport { + report: GcState { trigger_r: 255, trigger_l: 255, buttons_2: GcButtons2 { @@ -1711,7 +1711,7 @@ async fn configuration_main_loop< }; override_gcc_state_and_wait(&OverrideGcReportInstruction { - report: GcReport { + report: GcState { trigger_r: 255, trigger_l: 255, buttons_2: GcButtons2 { @@ -1745,7 +1745,7 @@ async fn configuration_main_loop< // display waveshaping values on both sticks 38 => { override_gcc_state_and_wait(&OverrideGcReportInstruction { - report: GcReport { + report: GcState { trigger_r: 255, trigger_l: 255, buttons_2: GcButtons2 { @@ -1771,7 +1771,7 @@ async fn configuration_main_loop< // display stick smoothing values on both sticks 39 => { override_gcc_state_and_wait(&OverrideGcReportInstruction { - report: GcReport { + report: GcState { trigger_r: 255, trigger_l: 255, buttons_2: GcButtons2 { @@ -1797,7 +1797,7 @@ async fn configuration_main_loop< // display snapback values on both sticks 40 => { override_gcc_state_and_wait(&OverrideGcReportInstruction { - report: GcReport { + report: GcState { trigger_r: 255, trigger_l: 255, buttons_2: GcButtons2 { @@ -1854,11 +1854,6 @@ pub async fn config_task(mut flash: Flash<'static, FLASH, Async, FLASH_SIZE>) { *m_input_consistency = Some(current_config.input_consistency_mode); } - { - let mut m_is_procon = MUTEX_CONTROLLER_MODE.lock().await; - *m_is_procon = Some(current_config.controller_mode); - } - SIGNAL_CHANGE_RUMBLE_STRENGTH.signal(current_config.rumble_strength); SIGNAL_CONFIG_CHANGE.signal(current_config.clone()); @@ -1873,7 +1868,7 @@ pub async fn config_task(mut flash: Flash<'static, FLASH, Async, FLASH_SIZE>) { info!("Entering config mode."); override_gcc_state_and_wait(&OverrideGcReportInstruction { - report: GcReport { + report: GcState { trigger_r: 255, trigger_l: 255, buttons_2: GcButtons2 { diff --git a/src/input.rs b/src/input.rs index 874e17d..773607a 100644 --- a/src/input.rs +++ b/src/input.rs @@ -1,7 +1,7 @@ use defmt::{debug, info, trace, Format}; use embassy_futures::yield_now; use embassy_rp::{ - gpio::{AnyPin, Input, Output, Pin}, + gpio::{Input, Output}, peripherals::SPI0, spi::{Blocking, Spi}, }; @@ -16,19 +16,19 @@ use libm::{fmaxf, fminf}; use crate::{ config::{ - ControllerConfig, InputConsistencyMode, OverrideGcReportInstruction, OverrideStickState, - SIGNAL_CONFIG_CHANGE, SIGNAL_IS_CALIBRATING, SIGNAL_OVERRIDE_GCC_STATE, + ControllerConfig, ControllerMode, InputConsistencyMode, OverrideGcReportInstruction, + OverrideStickState, SIGNAL_CONFIG_CHANGE, SIGNAL_IS_CALIBRATING, SIGNAL_OVERRIDE_GCC_STATE, SIGNAL_OVERRIDE_STICK_STATE, }, filter::{run_waveshaping, FilterGains, KalmanState, WaveshapingValues, FILTER_GAINS}, - gcc_hid::{GcReport, MUTEX_INPUT_CONSISTENCY_MODE}, helpers::XyValuePair, input_filter::{DummyFilter, InputFilter}, stick::{linearize, notch_remap, StickParams}, + usb_comms::{GcState, MUTEX_CONTROLLER_MODE, MUTEX_INPUT_CONSISTENCY_MODE}, }; /// Used to send the button state to the usb task and the calibration task -pub static CHANNEL_GCC_STATE: PubSubChannel<CriticalSectionRawMutex, GcReport, 1, 4, 1> = +pub static CHANNEL_GCC_STATE: PubSubChannel<CriticalSectionRawMutex, GcState, 1, 4, 1> = PubSubChannel::new(); /// Used to send the stick state from the stick task to the main input task @@ -36,10 +36,8 @@ static SIGNAL_STICK_STATE: Signal<CriticalSectionRawMutex, StickState> = Signal: pub static SPI_SHARED: Mutex<ThreadModeRawMutex, Option<Spi<'static, SPI0, Blocking>>> = Mutex::new(None); -pub static SPI_ACS_SHARED: Mutex<ThreadModeRawMutex, Option<Output<'static, AnyPin>>> = - Mutex::new(None); -pub static SPI_CCS_SHARED: Mutex<ThreadModeRawMutex, Option<Output<'static, AnyPin>>> = - Mutex::new(None); +pub static SPI_ACS_SHARED: Mutex<ThreadModeRawMutex, Option<Output<'static>>> = Mutex::new(None); +pub static SPI_CCS_SHARED: Mutex<ThreadModeRawMutex, Option<Output<'static>>> = Mutex::new(None); const STICK_HYST_VAL: f32 = 0.3; pub const FLOAT_ORIGIN: f32 = 127.5; @@ -84,18 +82,12 @@ pub enum StickAxis { #[inline(never)] #[link_section = ".time_critical.read_ext_adc"] -pub fn read_ext_adc< - 'a, - Acs: Pin, - Ccs: Pin, - I: embassy_rp::spi::Instance, - M: embassy_rp::spi::Mode, ->( +pub fn read_ext_adc<'a, I: embassy_rp::spi::Instance, M: embassy_rp::spi::Mode>( which_stick: Stick, which_axis: StickAxis, spi: &mut Spi<'a, I, M>, - spi_acs: &mut Output<'a, Acs>, - spi_ccs: &mut Output<'a, Ccs>, + spi_acs: &mut Output<'a>, + spi_ccs: &mut Output<'a>, ) -> u16 { let mut buf = [0b11010000; 3]; @@ -352,19 +344,19 @@ async fn update_stick_states( #[allow(clippy::too_many_arguments)] fn update_button_states( - gcc_state: &mut GcReport, - btn_a: &Input<'_, AnyPin>, - btn_b: &Input<'_, AnyPin>, - btn_x: &Input<'_, AnyPin>, - btn_y: &Input<'_, AnyPin>, - btn_start: &Input<'_, AnyPin>, - btn_l: &Input<'_, AnyPin>, - btn_r: &Input<'_, AnyPin>, - btn_z: &Input<'_, AnyPin>, - btn_dleft: &Input<'_, AnyPin>, - btn_dright: &Input<'_, AnyPin>, - btn_dup: &Input<'_, AnyPin>, - btn_ddown: &Input<'_, AnyPin>, + gcc_state: &mut GcState, + btn_a: &Input<'_>, + btn_b: &Input<'_>, + btn_x: &Input<'_>, + btn_y: &Input<'_>, + btn_start: &Input<'_>, + btn_l: &Input<'_>, + btn_r: &Input<'_>, + btn_z: &Input<'_>, + btn_dleft: &Input<'_>, + btn_dright: &Input<'_>, + btn_dup: &Input<'_>, + btn_ddown: &Input<'_>, ) { gcc_state.buttons_1.button_a = btn_a.is_low(); gcc_state.buttons_1.button_b = btn_b.is_low(); @@ -393,7 +385,7 @@ pub async fn input_integrity_benchmark() { loop { SIGNAL_OVERRIDE_GCC_STATE.signal(OverrideGcReportInstruction { report: { - let mut report = GcReport::default(); + let mut report = GcState::default(); report.buttons_1.dpad_up = true; report }, @@ -409,18 +401,18 @@ pub async fn input_integrity_benchmark() { #[allow(clippy::too_many_arguments)] #[embassy_executor::task] pub async fn update_button_state_task( - btn_z: Input<'static, AnyPin>, - btn_a: Input<'static, AnyPin>, - btn_b: Input<'static, AnyPin>, - btn_dright: Input<'static, AnyPin>, - btn_dup: Input<'static, AnyPin>, - btn_ddown: Input<'static, AnyPin>, - btn_dleft: Input<'static, AnyPin>, - btn_l: Input<'static, AnyPin>, - btn_r: Input<'static, AnyPin>, - btn_x: Input<'static, AnyPin>, - btn_y: Input<'static, AnyPin>, - btn_start: Input<'static, AnyPin>, + btn_z: Input<'static>, + btn_a: Input<'static>, + btn_b: Input<'static>, + btn_dright: Input<'static>, + btn_dup: Input<'static>, + btn_ddown: Input<'static>, + btn_dleft: Input<'static>, + btn_l: Input<'static>, + btn_r: Input<'static>, + btn_x: Input<'static>, + btn_y: Input<'static>, + btn_start: Input<'static>, ) { // upon loop entry, we check for the reset combo once if btn_a.is_low() && btn_x.is_low() && btn_y.is_low() { @@ -431,6 +423,15 @@ pub async fn update_button_state_task( loop {} } + { + let mut m = MUTEX_CONTROLLER_MODE.lock().await; + *m = if btn_start.is_low() { + Some(ControllerMode::Procon) + } else { + Some(ControllerMode::GcAdapter) + }; + } + let input_consistency_mode = { while MUTEX_INPUT_CONSISTENCY_MODE.lock().await.is_none() { Timer::after(Duration::from_millis(100)).await; @@ -438,9 +439,9 @@ pub async fn update_button_state_task( MUTEX_INPUT_CONSISTENCY_MODE.lock().await.unwrap() }; - let mut previous_state = GcReport::default(); + let mut previous_state = GcState::default(); - let mut gcc_state = GcReport::default(); + let mut gcc_state = GcState::default(); let gcc_publisher = CHANNEL_GCC_STATE.publisher().unwrap(); @@ -546,8 +547,8 @@ pub async fn update_button_state_task( #[link_section = ".time_critical.update_stick_states_task"] pub async fn update_stick_states_task( spi: Spi<'static, SPI0, embassy_rp::spi::Blocking>, - spi_acs: Output<'static, AnyPin>, - spi_ccs: Output<'static, AnyPin>, + spi_acs: Output<'static>, + spi_ccs: Output<'static>, ) { Timer::after_secs(1).await; *SPI_SHARED.lock().await = Some(spi); @@ -608,7 +609,7 @@ pub async fn update_stick_states_task( let n = Instant::now(); match (n - last_loop_time).as_micros() { - a if a > 1666 => debug!("Loop took {} us", a), + a if a > 800 => debug!("Loop took {} us", a), _ => {} }; last_loop_time = n; diff --git a/src/input_filter.rs b/src/input_filter.rs index 3e000e8..7db05c4 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}, - gcc_hid::GcReport, + usb_comms::GcState, }; /** @@ -14,7 +14,7 @@ use crate::{ */ pub trait InputFilter: Sized { - fn apply_filter(&mut self, gcc_state: &mut GcReport); + fn apply_filter(&mut self, gcc_state: &mut GcState); } /// Presses a single button if another button is pressed. @@ -26,7 +26,7 @@ pub struct SingleButtonMacroFilter { } impl InputFilter for SingleButtonMacroFilter { - fn apply_filter(&mut self, gcc_state: &mut GcReport) { + fn apply_filter(&mut self, gcc_state: &mut GcState) { if is_awaitable_button_pressed(gcc_state, &self.btn_instigator) { match self.btn_to_press { AwaitableButtons::A => { @@ -83,7 +83,7 @@ impl InputFilter for SingleButtonMacroFilter { pub struct CStickUpTiltFilter; impl InputFilter for CStickUpTiltFilter { - fn apply_filter(&mut self, gcc_state: &mut GcReport) { + fn apply_filter(&mut self, gcc_state: &mut GcState) { if gcc_state.cstick_y > 157 { if (137..=201).contains(&gcc_state.cstick_x) { gcc_state.cstick_x = 201; @@ -110,7 +110,7 @@ impl InputFilter for CStickUpTiltFilter { pub struct CStickAngledFTiltFilter; impl InputFilter for CStickAngledFTiltFilter { - fn apply_filter(&mut self, gcc_state: &mut GcReport) { + fn apply_filter(&mut self, gcc_state: &mut GcState) { if gcc_state.cstick_y > 147 { if (147..=225).contains(&gcc_state.cstick_x) { gcc_state.cstick_x = 205; @@ -136,5 +136,5 @@ impl InputFilter for CStickAngledFTiltFilter { pub struct DummyFilter; impl InputFilter for DummyFilter { - fn apply_filter(&mut self, _gcc_state: &mut GcReport) {} + fn apply_filter(&mut self, _gcc_state: &mut GcState) {} } diff --git a/src/main.rs b/src/main.rs index 244f920..505abc5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,19 +1,16 @@ -//! This example test the RP Pico on board LED. -//! -//! It does not work with the RP Pico W board. See wifi_blinky.rs. - #![no_std] #![no_main] mod config; mod filter; -mod gcc_hid; mod helpers; mod input; mod input_filter; +mod procon_hid; mod stick; +mod usb_comms; use config::config_task; -use defmt::{debug, info}; +use defmt::info; use embassy_executor::Executor; use embassy_rp::{ bind_interrupts, @@ -24,14 +21,14 @@ use embassy_rp::{ spi::{self, Spi}, usb::{Driver, InterruptHandler}, }; -use gcc_hid::usb_transfer_task; use gpio::{Level, Output}; +use usb_comms::usb_transfer_task; use input::{update_button_state_task, update_stick_states_task}; use static_cell::StaticCell; use crate::config::enter_config_mode_task; -use crate::gcc_hid::rumble_task; +use crate::usb_comms::rumble_task; use {defmt_rtt as _, panic_probe as _}; @@ -77,12 +74,11 @@ fn main() -> ! { spawn_core1(p.CORE1, unsafe { &mut CORE1_STACK }, move || { let executor1 = EXECUTOR1.init(Executor::new()); - debug!("Mana"); executor1.run(|spawner| { spawner.spawn(usb_transfer_task(uid, driver)).unwrap(); spawner.spawn(enter_config_mode_task()).unwrap(); spawner - .spawn(rumble_task(p.PIN_25, p.PIN_29, p.PWM_CH4, p.PWM_CH6)) + .spawn(rumble_task(p.PIN_25, p.PIN_29, p.PWM_SLICE4, p.PWM_SLICE6)) .unwrap(); // spawner.spawn(input_integrity_benchmark()).unwrap(); spawner diff --git a/src/procon_hid.rs b/src/procon_hid.rs new file mode 100644 index 0000000..676a323 --- /dev/null +++ b/src/procon_hid.rs @@ -0,0 +1,621 @@ +/// +/// 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::usb_comms::{GcState, 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) { + 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)); + + self[19..(19 + 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) -> u8 { + // TODO + 0 +} + +#[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 = "msb", 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..=60")] + 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<&GcState> for ProconState { + fn from(value: &GcState) -> Self { + Self { + buttons_left: ProconButtonsLeft { + dpad_down: value.buttons_1.dpad_down, + dpad_right: value.buttons_1.dpad_right, + dpad_up: value.buttons_1.dpad_up, + dped_left: value.buttons_1.dpad_left, + trigger_l: value.buttons_2.button_l, + trigger_zl: value.buttons_2.button_l, + ..Default::default() + }, + buttons_right: ProconButtonsRight { + 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_r: value.buttons_2.button_r, + trigger_zr: value.buttons_2.button_z, + ..Default::default() + }, + buttons_shared: ProconButtonsShared { + button_plus: value.buttons_2.button_start && !value.buttons_2.button_z, + button_home: value.buttons_2.button_start && value.buttons_2.button_z, + ..Default::default() + }, + lstick_x: value.stick_x as u16 * 257, + lstick_y: value.stick_y, + rstick_x: value.cstick_x as u16 * 257, + rstick_y: value.cstick_y, + } + } +} + +pub struct ProconReportBuilder { + switch_reporting_mode: u8, + switch_mac_address: [u8; 6], + switch_ltk: [u8; 16], +} + +impl Default for ProconReportBuilder { + fn default() -> Self { + Self { + switch_reporting_mode: 0, + switch_mac_address: [0u8; 6], + switch_ltk: gen_switch_ltk(), + } + } +} + +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], + ); + } + 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 = ¤t_report_info.raw_data[12..]; + + match pairing_phase { + 1 => { + self.switch_mac_address + .iter_mut() + .enumerate() + .for_each(|(i, e)| *e = host_address[5 - i]); + + self.switch_mac_address + .iter() + .rev() + .enumerate() + .for_each(|(i, e)| { + report[16 + i] = *e; + }); + + 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: &GcState) -> [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(¤t_report_info), + ProconRequestId::Command => self.get_command_report(¤t_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 + } +} diff --git a/src/gcc_hid.rs b/src/usb_comms.rs similarity index 82% rename from src/gcc_hid.rs rename to src/usb_comms.rs index 23edcdf..91f40a6 100644 --- a/src/gcc_hid.rs +++ b/src/usb_comms.rs @@ -7,7 +7,7 @@ use core::{default::Default, future::Future}; use defmt::{debug, info, trace, warn, Format}; use embassy_futures::join::join; use embassy_rp::{ - peripherals::{PIN_25, PIN_29, PWM_CH4, PWM_CH6, USB}, + peripherals::{PIN_25, PIN_29, PWM_SLICE4, PWM_SLICE6, USB}, pwm::Pwm, usb::Driver as EmbassyDriver, }; @@ -27,6 +27,7 @@ use packed_struct::{derive::PackedStruct, PackedStruct}; use crate::{ config::{ControllerMode, InputConsistencyMode}, input::CHANNEL_GCC_STATE, + procon_hid::{ProconReportBuilder, ProconRequestHandler, PROCON_REPORT_DESCRIPTOR}, }; static SIGNAL_RUMBLE: Signal<CriticalSectionRawMutex, bool> = Signal::new(); @@ -105,8 +106,8 @@ pub const GCC_REPORT_DESCRIPTOR: &[u8] = &[ 0xC0, // End Collection ]; -trait HidReportable<const LEN: usize> { - fn get_hid_report(&self) -> [u8; LEN]; +pub trait HidReportBuilder<const LEN: usize> { + async fn get_hid_report(&mut self, state: &GcState) -> [u8; LEN]; } struct UsbConfig { @@ -153,7 +154,7 @@ pub struct GcButtons2 { #[derive(Clone, Copy, Debug, PartialEq, Eq, PackedStruct, Format)] #[packed_struct(bit_numbering = "msb0", size_bytes = "8")] -pub struct GcReport { +pub struct GcState { #[packed_field(bits = "0..=7")] pub buttons_1: GcButtons1, #[packed_field(bits = "8..=15")] @@ -172,7 +173,7 @@ pub struct GcReport { pub trigger_r: u8, } -impl Default for GcReport { +impl Default for GcState { fn default() -> Self { Self { buttons_1: GcButtons1::default(), @@ -187,50 +188,58 @@ impl Default for GcReport { } } -impl HidReportable<37> for GcReport { - fn get_hid_report(&self) -> [u8; 37] { - static mut GC_FIRST: bool = false; +#[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 = self.pack().expect("Failed to pack GC input data"); + let data = state.pack().expect("Failed to pack GC input data"); - if unsafe { !GC_FIRST } { + if !self.gc_first { buffer[1] |= 0x04; buffer[10] |= 0x04; buffer[19] |= 0x04; buffer[28] |= 0x04; - unsafe { GC_FIRST = true }; + self.gc_first = true; } else { // controller in "port 1" - buffer[2..=9].copy_from_slice(&data[0..=7]); + buffer[2..=9].copy_from_slice(&data); } buffer } } -struct GccRequestHandler {} +struct GccRequestHandler; impl RequestHandler for GccRequestHandler { - fn get_report(&self, id: ReportId, _buf: &mut [u8]) -> Option<usize> { + fn get_report(&mut self, id: ReportId, _buf: &mut [u8]) -> Option<usize> { info!("Get report for {:?}", id); None } - fn set_report(&self, id: ReportId, data: &[u8]) -> OutResponse { - info!("Set report for {:?}: {=[u8]}", id, data); + 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(&self, id: Option<ReportId>, dur: u32) { + fn set_idle_ms(&mut self, id: Option<ReportId>, dur: u32) { info!("Set idle rate for {:?} to {:?}", id, dur); } - fn get_idle_ms(&self, id: Option<ReportId>) -> Option<u32> { + fn get_idle_ms(&mut self, id: Option<ReportId>) -> Option<u32> { info!("Get idle rate for {:?}", id); None } @@ -281,13 +290,12 @@ impl Handler for MyDeviceHandler { fn mk_hid_reader_writer<'d, D: Driver<'d>, const R: usize, const W: usize>( input_consistency_mode: InputConsistencyMode, report_descriptor: &'d [u8], - request_handler: &'d GccRequestHandler, mut builder: Builder<'d, D>, state: &'d mut State<'d>, ) -> (UsbDevice<'d, D>, HidReader<'d, D, R>, HidWriter<'d, D, W>) { let hid_config = embassy_usb::class::hid::Config { report_descriptor, - request_handler: Some(request_handler), + request_handler: None, poll_ms: match input_consistency_mode { InputConsistencyMode::Original => 8, InputConsistencyMode::ConsistencyHack @@ -308,13 +316,13 @@ fn mk_hid_reader_writer<'d, D: Driver<'d>, const R: usize, const W: usize>( (usb, reader, writer) } -fn mk_usb_transfer_futures<'d, D, F, H, const R: usize, const W: usize>( +fn mk_usb_transfer_futures<'d, D, H, Rq, const R: usize, const W: usize>( input_consistency_mode: InputConsistencyMode, - usb_config: UsbConfig, - request_handler: &'d GccRequestHandler, + usb_config: &UsbConfig, + request_handler: &'d mut Rq, builder: Builder<'d, D>, state: &'d mut State<'d>, - preprocess_report: F, + mut hid_report_builder: H, ) -> ( impl Future<Output = ()> + 'd, impl Future<Output = ()> + 'd, @@ -322,13 +330,12 @@ fn mk_usb_transfer_futures<'d, D, F, H, const R: usize, const W: usize>( ) where D: Driver<'d> + 'd, - F: Fn(GcReport) -> H + 'd, - H: HidReportable<W>, + H: HidReportBuilder<W> + 'd, + Rq: RequestHandler, { - let (mut usb, mut reader, mut writer) = mk_hid_reader_writer::<_, R, W>( + let (mut usb, reader, mut writer) = mk_hid_reader_writer::<_, R, W>( input_consistency_mode, usb_config.report_descriptor, - request_handler, builder, state, ); @@ -368,9 +375,9 @@ where writer.ready().await; - let state = preprocess_report(gcc_subscriber.next_message_pure().await); + let state = gcc_subscriber.next_message_pure().await; - let report = state.get_hid_report(); + let report = hid_report_builder.get_hid_report(&state).await; trace!("Writing report: {:08b}", report); @@ -395,19 +402,8 @@ where }; let out_fut = async move { - loop { - trace!("Readery loop"); - let mut buf = [0u8; R]; - match reader.read(&mut buf).await { - Ok(_e) => { - debug!("READ SOMETHIN: {:08b}", buf); - SIGNAL_RUMBLE.signal((buf[1] & 0x01) != 0); - } - Err(e) => { - warn!("Failed to read: {:?}", e); - } - } - } + trace!("Readery loop"); + reader.run(true, request_handler).await; }; let usb_fut_wrapped = async { @@ -420,12 +416,6 @@ where #[embassy_executor::task] pub async fn usb_transfer_task(raw_serial: [u8; 8], driver: EmbassyDriver<'static, USB>) { - let config = UsbConfig { - vid: 0x057e, - pid: 0x0337, - report_descriptor: GCC_REPORT_DESCRIPTOR, - }; - let input_consistency_mode = { while MUTEX_INPUT_CONSISTENCY_MODE.lock().await.is_none() { Timer::after(Duration::from_millis(100)).await; @@ -433,6 +423,27 @@ pub async fn usb_transfer_task(raw_serial: [u8; 8], driver: EmbassyDriver<'stati MUTEX_INPUT_CONSISTENCY_MODE.lock().await.unwrap() }; + let controller_mode = { + while MUTEX_CONTROLLER_MODE.lock().await.is_none() { + Timer::after(Duration::from_millis(100)).await; + } + + MUTEX_CONTROLLER_MODE.lock().await.unwrap() + }; + + let config = match controller_mode { + ControllerMode::GcAdapter => UsbConfig { + vid: 0x057e, + pid: 0x0337, + report_descriptor: GCC_REPORT_DESCRIPTOR, + }, + ControllerMode::Procon => UsbConfig { + vid: 0x57e, + pid: 0x2009, + report_descriptor: PROCON_REPORT_DESCRIPTOR, + }, + }; + let mut serial_buffer = [0u8; 64]; let serial = format_no_std::show( @@ -471,13 +482,11 @@ pub async fn usb_transfer_task(raw_serial: [u8; 8], driver: EmbassyDriver<'stati usb_config.device_sub_class = 0; usb_config.supports_remote_wakeup = true; - let mut device_descriptor = [0; 256]; let mut config_descriptor = [0; 256]; let mut bos_descriptor = [0; 256]; let mut msos_descriptor = [0; 256]; let mut control_buf = [0; 64]; - let request_handler = GccRequestHandler {}; let mut device_handler = MyDeviceHandler::new(); let mut state = State::new(); @@ -485,7 +494,6 @@ pub async fn usb_transfer_task(raw_serial: [u8; 8], driver: EmbassyDriver<'stati let mut builder = Builder::new( driver, usb_config, - &mut device_descriptor, &mut config_descriptor, &mut bos_descriptor, &mut msos_descriptor, @@ -503,16 +511,36 @@ pub async fn usb_transfer_task(raw_serial: [u8; 8], driver: EmbassyDriver<'stati builder.handler(&mut device_handler); - let (usb_fut_wrapped, in_fut, out_fut) = mk_usb_transfer_futures::<_, _, _, 5, 37>( - input_consistency_mode, - config, - &request_handler, - builder, - &mut state, - |state| state, - ); + match controller_mode { + ControllerMode::GcAdapter => { + let mut request_handler = GccRequestHandler; - join(usb_fut_wrapped, join(in_fut, out_fut)).await; + let (usb_fut_wrapped, in_fut, out_fut) = mk_usb_transfer_futures::<_, _, _, 5, 37>( + input_consistency_mode, + &config, + &mut request_handler, + builder, + &mut state, + GcReportBuilder::default(), + ); + + join(usb_fut_wrapped, join(in_fut, out_fut)).await; + } + ControllerMode::Procon => { + let mut request_handler = ProconRequestHandler; + + let (usb_fut_wrapped, in_fut, out_fut) = mk_usb_transfer_futures::<_, _, _, 64, 64>( + input_consistency_mode, + &config, + &mut request_handler, + builder, + &mut state, + ProconReportBuilder::default(), + ); + + join(usb_fut_wrapped, join(in_fut, out_fut)).await; + } + }; } fn calc_rumble_power(strength: u8) -> u16 { @@ -527,8 +555,8 @@ fn calc_rumble_power(strength: u8) -> u16 { pub async fn rumble_task( pin_rumble: PIN_25, pin_brake: PIN_29, - pwm_ch_rumble: PWM_CH4, - pwm_ch_brake: PWM_CH6, + pwm_ch_rumble: PWM_SLICE4, + pwm_ch_brake: PWM_SLICE6, ) { let mut rumble_config: embassy_rp::pwm::Config = Default::default(); rumble_config.top = 255;