Compare commits

...

7 commits

Author SHA1 Message Date
3b60499fff
chore: improve clippy CI
All checks were successful
Code quality / check (pull_request) Successful in 3m41s
2024-10-14 17:22:46 +02:00
e8664c9f78
chore: fix deprecated ref to static mut 2024-10-14 17:22:46 +02:00
fa3f7f24bd
flake: update to latest nixpkgs 2024-10-14 17:22:46 +02:00
1a2e71b213
chore: use binary cache in PRs 2024-10-14 17:22:46 +02:00
a2e90c3734
chore: refactor HID file organization 2024-10-14 17:22:46 +02:00
a5ed42b365
flake: remove obsolete embassy input 2024-10-14 17:22:46 +02:00
e9ce8970d8
feat: implement pro-controller mode 2024-10-14 17:22:45 +02:00
14 changed files with 1113 additions and 481 deletions

View file

@ -10,8 +10,19 @@ jobs:
check:
runs-on: nix-flakes
steps:
- uses: actions/checkout@v4
- name: Set up attic binary cache
uses: https://git.naxdy.org/NaxdyOrg/attic-action@v0.3
with:
endpoint: "${{ vars.BINARY_CACHE_URL }}"
token: ""
cache: "${{ vars.PUBLIC_BINARY_CACHE_NAME }}"
skip-push: true
- name: Run Clippy
run: |
nix develop . --command cargo clippy -- -Dwarnings
nix build .#clippy --print-build-logs -j auto

View file

@ -20,9 +20,15 @@ jobs:
token: "${{ secrets.PUBLIC_BINARY_CACHE_AUTH_KEY }}"
cache: "${{ vars.PUBLIC_BINARY_CACHE_NAME }}"
- uses: actions/checkout@v4
- name: Run Clippy
run: |
nix build .#clippy --print-build-logs -j auto
- name: Build firmware image
run: |
nix build .# -o dist --print-build-logs
- name: (Re-)generate tag
run: |
git config --global user.email "noreply@naxdy.org"
@ -32,6 +38,7 @@ jobs:
git tag nightly -m "Nightly Release"
git checkout nightly
git push --set-upstream origin nightly --force
- name: Publish nightly release
uses: https://gitea.com/actions/gitea-release-action@v1.3.0
with:

161
Cargo.lock generated
View file

@ -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",
]

View file

@ -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" }

View file

@ -1,22 +1,5 @@
{
"nodes": {
"embassy-rs-patched": {
"flake": false,
"locked": {
"lastModified": 1710754402,
"narHash": "sha256-HyTw5VQXlSqz9UOq0Dc6G/NWNzQOPXf3PNWffMkfLC4=",
"ref": "naxgcc-fw",
"rev": "2ee4657727b9679998d941de00b43e1754f570bf",
"revCount": 6754,
"type": "git",
"url": "https://gitea@git.naxdy.org/NaxdyOrg/embassy"
},
"original": {
"ref": "naxgcc-fw",
"type": "git",
"url": "https://gitea@git.naxdy.org/NaxdyOrg/embassy"
}
},
"flake-utils": {
"inputs": {
"systems": "systems"
@ -35,24 +18,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": [
@ -75,27 +40,27 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1710283656,
"narHash": "sha256-nI+AOy4uK6jLGBi9nsbHjL1EdSIzoo8oa+9oeVhbyFc=",
"lastModified": 1728740863,
"narHash": "sha256-u+rxA79a0lyhG+u+oPBRtTDtzz8kvkc9a6SWSt9ekVc=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "51063ed4f2343a59fdeebb279bb81d87d453942b",
"rev": "a3f9ad65a0bf298ed5847629a57808b97e6e8077",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-23.11",
"ref": "nixos-24.05",
"repo": "nixpkgs",
"type": "github"
}
},
"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": {
@ -107,7 +72,6 @@
},
"root": {
"inputs": {
"embassy-rs-patched": "embassy-rs-patched",
"flake-utils": "flake-utils",
"naersk": "naersk",
"nixpkgs": "nixpkgs",
@ -116,15 +80,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 +110,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",

View file

@ -2,17 +2,12 @@
description = "Firmware for the NaxGCC";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-23.11";
nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-24.05";
rust-overlay.url = "github:oxalica/rust-overlay";
flake-utils.url = "github:numtide/flake-utils";
embassy-rs-patched = {
url = "git+https://gitea@git.naxdy.org/NaxdyOrg/embassy?ref=naxgcc-fw";
flake = false;
};
naersk = {
url = "github:nmattia/naersk";
inputs.nixpkgs.follows = "nixpkgs";
@ -25,7 +20,6 @@
, rust-overlay
, flake-utils
, naersk
, embassy-rs-patched
}: (flake-utils.lib.eachDefaultSystem (system:
let
pkgs = import nixpkgs {
@ -51,12 +45,6 @@
};
CARGO_BUILD_TARGET = "thumbv6m-none-eabi";
prepCmd = ''
mkdir -p lib
rm lib/embassy-rs || true
ln -s "${embassy-rs-patched}" lib/embassy-rs
'';
in
{
packages.default = self.packages.${system}.naxgcc-fw-uf2;
@ -66,20 +54,26 @@
${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
'';
packages.naxgcc-fw = naersk_lib.buildPackage {
pname = (builtins.fromTOML (builtins.readFile ./Cargo.toml)).package.name;
version = (builtins.fromTOML (builtins.readFile ./Cargo.toml)).package.version;
packages.naxgcc-fw = pkgs.callPackage
({ mode ? "build" }: naersk_lib.buildPackage {
pname = (builtins.fromTOML (builtins.readFile ./Cargo.toml)).package.name;
version = (builtins.fromTOML (builtins.readFile ./Cargo.toml)).package.version;
prePatch = prepCmd;
inherit mode;
src = self;
src = self;
cargoBuildOptions = _orig: _orig ++ [
"--target=${CARGO_BUILD_TARGET}"
];
cargoBuildOptions = _orig: _orig ++ [
"--target=${CARGO_BUILD_TARGET}"
];
# if a tree falls in the forest and no one is around to hear it, does it make a sound?
DEFMT_LOG = "off";
# if a tree falls in the forest and no one is around to hear it, does it make a sound?
DEFMT_LOG = "off";
})
{ };
packages.clippy = self.packages.${system}.naxgcc-fw.override {
mode = "clippy";
};
devShells.default = pkgs.mkShell {
@ -92,8 +86,6 @@
DEFMT_LOG = "debug";
inherit CARGO_BUILD_TARGET;
shellHook = prepCmd;
};
}));
}

View file

@ -16,11 +16,8 @@ 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},
hid::gcc::{GcButtons1, GcButtons2, GcState},
input::{
read_ext_adc, Stick, StickAxis, FLOAT_ORIGIN, SPI_ACS_SHARED, SPI_CCS_SHARED, SPI_SHARED,
},
@ -29,6 +26,7 @@ use crate::{
LinearizedCalibration, NotchCalibration, NotchStatus, CALIBRATION_ORDER,
NOTCH_ADJUSTMENT_ORDER, NO_OF_ADJ_NOTCHES, NO_OF_CALIBRATION_POINTS, NO_OF_NOTCHES,
},
usb_comms::{MUTEX_INPUT_CONSISTENCY_MODE, SIGNAL_CHANGE_RUMBLE_STRENGTH},
ADDR_OFFSET, FLASH_SIZE,
};
@ -39,7 +37,7 @@ use embassy_sync::{
};
use embassy_time::Timer;
use crate::{gcc_hid::GcReport, input::CHANNEL_GCC_STATE};
use crate::input::CHANNEL_GCC_STATE;
/// Whether we are currently calibrating the sticks. Updates are dispatched when the status changes.
/// Initial status is assumed to be false.
@ -81,7 +79,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 +501,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 +564,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 +590,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 +600,6 @@ impl Default for ControllerConfig {
astick_config: StickConfig::default(),
rumble_strength: 9,
cstick_config: StickConfig::default(),
controller_mode: ControllerMode::GcAdapter,
}
}
}
@ -651,6 +647,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 +682,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 +782,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 +1148,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 +1206,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 +1234,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 +1263,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 +1322,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 +1408,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 +1494,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 +1569,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 +1625,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 +1673,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 +1709,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 +1743,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 +1769,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 +1795,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 +1852,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 +1866,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 {

141
src/hid/gcc.rs Normal file
View file

@ -0,0 +1,141 @@
use defmt::{info, trace, Format};
use embassy_usb::{
class::hid::{ReportId, RequestHandler},
control::OutResponse,
};
use crate::usb_comms::{HidReportBuilder, SIGNAL_RUMBLE};
use packed_struct::{derive::PackedStruct, PackedStruct};
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default, PackedStruct, Format)]
#[packed_struct(bit_numbering = "lsb0", size_bytes = "1")]
pub struct GcButtons1 {
#[packed_field(bits = "0")]
pub button_a: bool,
#[packed_field(bits = "1")]
pub button_b: bool,
#[packed_field(bits = "2")]
pub button_x: bool,
#[packed_field(bits = "3")]
pub button_y: bool,
#[packed_field(bits = "4")]
pub dpad_left: bool,
#[packed_field(bits = "5")]
pub dpad_right: bool,
#[packed_field(bits = "6")]
pub dpad_down: bool,
#[packed_field(bits = "7")]
pub dpad_up: bool,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default, PackedStruct, Format)]
#[packed_struct(bit_numbering = "lsb0", size_bytes = "1")]
pub struct GcButtons2 {
#[packed_field(bits = "0")]
pub button_start: bool,
#[packed_field(bits = "1")]
pub button_z: bool,
#[packed_field(bits = "2")]
pub button_r: bool,
#[packed_field(bits = "3")]
pub button_l: bool,
#[packed_field(bits = "4..=7")]
pub blank1: u8,
}
///
/// Struct representing the controller state. Used for HID communications in
/// GCC adapter mode, as well as for the internal representation of the controller.
///
#[derive(Clone, Copy, Debug, PartialEq, Eq, PackedStruct, Format)]
#[packed_struct(bit_numbering = "msb0", size_bytes = "8")]
pub struct GcState {
#[packed_field(bits = "0..=7")]
pub buttons_1: GcButtons1,
#[packed_field(bits = "8..=15")]
pub buttons_2: GcButtons2,
#[packed_field(bits = "16..=23")]
pub stick_x: u8,
#[packed_field(bits = "24..=31")]
pub stick_y: u8,
#[packed_field(bits = "32..=39")]
pub cstick_x: u8,
#[packed_field(bits = "40..=47")]
pub cstick_y: u8,
#[packed_field(bits = "48..=55")]
pub trigger_l: u8,
#[packed_field(bits = "56..=63")]
pub trigger_r: u8,
}
impl Default for GcState {
fn default() -> Self {
Self {
buttons_1: GcButtons1::default(),
buttons_2: GcButtons2::default(),
stick_x: 127,
stick_y: 127,
cstick_x: 127,
cstick_y: 127,
trigger_l: 0,
trigger_r: 0,
}
}
}
#[derive(Default)]
pub struct GcReportBuilder {
gc_first: bool,
}
impl HidReportBuilder<37> for GcReportBuilder {
async fn get_hid_report(&mut self, state: &GcState) -> [u8; 37] {
let mut buffer = [0u8; 37];
buffer[0] = 0x21;
buffer[1] |= 0x14;
let data = state.pack().expect("Failed to pack GC input data");
if !self.gc_first {
buffer[1] |= 0x04;
buffer[10] |= 0x04;
buffer[19] |= 0x04;
buffer[28] |= 0x04;
self.gc_first = true;
} else {
// controller in "port 1"
buffer[2..=9].copy_from_slice(&data);
}
buffer
}
}
pub struct GccRequestHandler;
impl RequestHandler for GccRequestHandler {
fn get_report(&mut self, id: ReportId, _buf: &mut [u8]) -> Option<usize> {
info!("Get report for {:?}", id);
None
}
fn set_report(&mut self, id: ReportId, data: &[u8]) -> OutResponse {
trace!("Set report for {:?}: {:x}", id, data);
if data.len() > 1 {
SIGNAL_RUMBLE.signal((data[1] & 0x01) != 0);
}
OutResponse::Accepted
}
fn set_idle_ms(&mut self, id: Option<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
}
}

2
src/hid/mod.rs Normal file
View file

@ -0,0 +1,2 @@
pub mod gcc;
pub mod procon;

623
src/hid/procon.rs Normal file
View file

@ -0,0 +1,623 @@
///
/// 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::HidReportBuilder;
use super::gcc::GcState;
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 = &current_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(&current_report_info),
ProconRequestId::Command => self.get_command_report(&current_report_info),
ProconRequestId::Rumble => self.get_state_report(&ProconState::from(state)),
}
} else {
self.get_state_report(&ProconState::from(state))
}
}
}
pub struct ProconRequestHandler;
impl RequestHandler for ProconRequestHandler {
fn get_report(&mut self, id: ReportId, _buf: &mut [u8]) -> Option<usize> {
info!("Get report for {:?}", id);
None
}
fn set_report(&mut self, id: ReportId, data: &[u8]) -> OutResponse {
trace!("Set report for {:?}: {:x}", id, data);
let mut buf = [0u8; 64];
let len_to_copy = buf.len().min(data.len());
buf[..len_to_copy].copy_from_slice(&data[..len_to_copy]);
if let ReportId::Out(id) = id {
if id == ProconRequestId::GetInfo as u8 {
SIGNAL_PROCON_REQUEST.signal(ProconRequestInfo {
command_id: buf[1],
report_id: ProconRequestId::GetInfo,
response_id: ProconResponseId::GetInfo,
raw_data: buf,
});
} else if id == ProconRequestId::Command as u8 {
SIGNAL_PROCON_REQUEST.signal(ProconRequestInfo {
command_id: buf[10],
report_id: ProconRequestId::Command,
response_id: ProconResponseId::Command,
raw_data: buf,
});
} else if id == ProconRequestId::Rumble as u8 {
// TODO: handle rumble
}
}
OutResponse::Accepted
}
fn set_idle_ms(&mut self, id: Option<ReportId>, dur: u32) {
info!("Set idle rate for {:?} to {:?}", id, dur);
}
fn get_idle_ms(&mut self, id: Option<ReportId>) -> Option<u32> {
info!("Get idle rate for {:?}", id);
None
}
}

View file

@ -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,20 @@ 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,
hid::gcc::GcState,
input_filter::{DummyFilter, InputFilter},
stick::{linearize, notch_remap, StickParams},
usb_comms::{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 +37,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 +83,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 +345,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 +386,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 +402,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 +424,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 +440,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 +548,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 +610,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;

View file

@ -2,7 +2,7 @@ use defmt::warn;
use crate::{
config::{is_awaitable_button_pressed, AwaitableButtons},
gcc_hid::GcReport,
hid::gcc::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) {}
}

View file

@ -1,19 +1,18 @@
//! 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 hid;
mod input;
mod input_filter;
mod stick;
mod usb_comms;
use core::ptr::addr_of_mut;
use config::config_task;
use defmt::{debug, info};
use defmt::info;
use embassy_executor::Executor;
use embassy_rp::{
bind_interrupts,
@ -24,14 +23,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 _};
@ -75,34 +74,37 @@ fn main() -> ! {
let spi_acs = Output::new(AnyPin::from(p_acs), Level::High); // active low
let spi_ccs = Output::new(AnyPin::from(p_ccs), Level::High); // active low
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))
.unwrap();
// spawner.spawn(input_integrity_benchmark()).unwrap();
spawner
.spawn(update_button_state_task(
Input::new(AnyPin::from(p.PIN_20), 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_11), gpio::Pull::Up),
Input::new(AnyPin::from(p.PIN_9), gpio::Pull::Up),
Input::new(AnyPin::from(p.PIN_10), gpio::Pull::Up),
Input::new(AnyPin::from(p.PIN_8), gpio::Pull::Up),
Input::new(AnyPin::from(p.PIN_22), gpio::Pull::Up),
Input::new(AnyPin::from(p.PIN_21), gpio::Pull::Up),
Input::new(AnyPin::from(p.PIN_18), gpio::Pull::Up),
Input::new(AnyPin::from(p.PIN_19), gpio::Pull::Up),
Input::new(AnyPin::from(p.PIN_5), gpio::Pull::Up),
))
.unwrap()
});
});
spawn_core1(
p.CORE1,
unsafe { addr_of_mut!(CORE1_STACK).as_mut().unwrap() },
move || {
let executor1 = EXECUTOR1.init(Executor::new());
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_SLICE4, p.PWM_SLICE6))
.unwrap();
// spawner.spawn(input_integrity_benchmark()).unwrap();
spawner
.spawn(update_button_state_task(
Input::new(AnyPin::from(p.PIN_20), 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_11), gpio::Pull::Up),
Input::new(AnyPin::from(p.PIN_9), gpio::Pull::Up),
Input::new(AnyPin::from(p.PIN_10), gpio::Pull::Up),
Input::new(AnyPin::from(p.PIN_8), gpio::Pull::Up),
Input::new(AnyPin::from(p.PIN_22), gpio::Pull::Up),
Input::new(AnyPin::from(p.PIN_21), gpio::Pull::Up),
Input::new(AnyPin::from(p.PIN_18), gpio::Pull::Up),
Input::new(AnyPin::from(p.PIN_19), gpio::Pull::Up),
Input::new(AnyPin::from(p.PIN_5), gpio::Pull::Up),
))
.unwrap()
});
},
);
let executor0 = EXECUTOR0.init(Executor::new());
info!("Initialized.");

View file

@ -4,10 +4,10 @@
*/
use core::{default::Default, future::Future};
use defmt::{debug, info, trace, warn, Format};
use defmt::{debug, info, trace, warn};
use embassy_futures::join::join;
use embassy_rp::{
peripherals::{PIN_25, PIN_29, PWM_CH4, PWM_CH6, USB},
peripherals::{PIN_25, PIN_29, PWM_SLICE4, PWM_SLICE6, USB},
pwm::Pwm,
usb::Driver as EmbassyDriver,
};
@ -15,21 +15,23 @@ use embassy_rp::{
use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, mutex::Mutex, signal::Signal};
use embassy_time::{Duration, Instant, Timer};
use embassy_usb::{
class::hid::{HidReader, HidReaderWriter, HidWriter, ReportId, RequestHandler, State},
control::OutResponse,
class::hid::{HidReader, HidReaderWriter, HidWriter, RequestHandler, State},
driver::Driver,
msos::{self, windows_version},
Builder, Handler, UsbDevice,
};
use libm::powf;
use packed_struct::{derive::PackedStruct, PackedStruct};
use crate::{
config::{ControllerMode, InputConsistencyMode},
hid::{
gcc::{GcReportBuilder, GcState, GccRequestHandler},
procon::{ProconReportBuilder, ProconRequestHandler, PROCON_REPORT_DESCRIPTOR},
},
input::CHANNEL_GCC_STATE,
};
static SIGNAL_RUMBLE: Signal<CriticalSectionRawMutex, bool> = Signal::new();
pub static SIGNAL_RUMBLE: Signal<CriticalSectionRawMutex, bool> = Signal::new();
/// We could turn the config change signal into a PubSubChannel instead, but that
/// would just transmit unnecessary amounts of data.
@ -105,8 +107,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 {
@ -115,127 +117,6 @@ struct UsbConfig {
report_descriptor: &'static [u8],
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default, PackedStruct, Format)]
#[packed_struct(bit_numbering = "lsb0", size_bytes = "1")]
pub struct GcButtons1 {
#[packed_field(bits = "0")]
pub button_a: bool,
#[packed_field(bits = "1")]
pub button_b: bool,
#[packed_field(bits = "2")]
pub button_x: bool,
#[packed_field(bits = "3")]
pub button_y: bool,
#[packed_field(bits = "4")]
pub dpad_left: bool,
#[packed_field(bits = "5")]
pub dpad_right: bool,
#[packed_field(bits = "6")]
pub dpad_down: bool,
#[packed_field(bits = "7")]
pub dpad_up: bool,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default, PackedStruct, Format)]
#[packed_struct(bit_numbering = "lsb0", size_bytes = "1")]
pub struct GcButtons2 {
#[packed_field(bits = "0")]
pub button_start: bool,
#[packed_field(bits = "1")]
pub button_z: bool,
#[packed_field(bits = "2")]
pub button_r: bool,
#[packed_field(bits = "3")]
pub button_l: bool,
#[packed_field(bits = "4..=7")]
pub blank1: u8,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PackedStruct, Format)]
#[packed_struct(bit_numbering = "msb0", size_bytes = "8")]
pub struct GcReport {
#[packed_field(bits = "0..=7")]
pub buttons_1: GcButtons1,
#[packed_field(bits = "8..=15")]
pub buttons_2: GcButtons2,
#[packed_field(bits = "16..=23")]
pub stick_x: u8,
#[packed_field(bits = "24..=31")]
pub stick_y: u8,
#[packed_field(bits = "32..=39")]
pub cstick_x: u8,
#[packed_field(bits = "40..=47")]
pub cstick_y: u8,
#[packed_field(bits = "48..=55")]
pub trigger_l: u8,
#[packed_field(bits = "56..=63")]
pub trigger_r: u8,
}
impl Default for GcReport {
fn default() -> Self {
Self {
buttons_1: GcButtons1::default(),
buttons_2: GcButtons2::default(),
stick_x: 127,
stick_y: 127,
cstick_x: 127,
cstick_y: 127,
trigger_l: 0,
trigger_r: 0,
}
}
}
impl HidReportable<37> for GcReport {
fn get_hid_report(&self) -> [u8; 37] {
static mut GC_FIRST: bool = false;
let mut buffer = [0u8; 37];
buffer[0] = 0x21;
buffer[1] |= 0x14;
let data = self.pack().expect("Failed to pack GC input data");
if unsafe { !GC_FIRST } {
buffer[1] |= 0x04;
buffer[10] |= 0x04;
buffer[19] |= 0x04;
buffer[28] |= 0x04;
unsafe { GC_FIRST = true };
} else {
// controller in "port 1"
buffer[2..=9].copy_from_slice(&data[0..=7]);
}
buffer
}
}
struct GccRequestHandler {}
impl RequestHandler for GccRequestHandler {
fn get_report(&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);
OutResponse::Accepted
}
fn set_idle_ms(&self, id: Option<ReportId>, dur: u32) {
info!("Set idle rate for {:?} to {:?}", id, dur);
}
fn get_idle_ms(&self, id: Option<ReportId>) -> Option<u32> {
info!("Get idle rate for {:?}", id);
None
}
}
struct MyDeviceHandler {
configured: bool,
}
@ -249,6 +130,9 @@ impl MyDeviceHandler {
impl Handler for MyDeviceHandler {
fn enabled(&mut self, enabled: bool) {
self.configured = true;
// reason: in production, info! compiles to nothing
#[allow(clippy::if_same_then_else)]
if enabled {
info!("Device enabled");
} else {
@ -281,13 +165,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 +191,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 +205,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 +250,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 +277,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 +291,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 +298,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 +357,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 +369,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 +386,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 +430,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;