Compare commits
7 commits
dcc1f0d170
...
3b60499fff
Author | SHA1 | Date | |
---|---|---|---|
3b60499fff | |||
e8664c9f78 | |||
fa3f7f24bd | |||
1a2e71b213 | |||
a2e90c3734 | |||
a5ed42b365 | |||
e9ce8970d8 |
14 changed files with 1113 additions and 481 deletions
|
@ -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
|
||||
|
|
|
@ -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
161
Cargo.lock
generated
|
@ -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",
|
||||
]
|
||||
|
|
39
Cargo.toml
39
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" }
|
||||
|
|
72
flake.lock
72
flake.lock
|
@ -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",
|
||||
|
|
42
flake.nix
42
flake.nix
|
@ -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;
|
||||
};
|
||||
}));
|
||||
}
|
||||
|
|
|
@ -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
141
src/hid/gcc.rs
Normal 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
2
src/hid/mod.rs
Normal file
|
@ -0,0 +1,2 @@
|
|||
pub mod gcc;
|
||||
pub mod procon;
|
623
src/hid/procon.rs
Normal file
623
src/hid/procon.rs
Normal 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 = ¤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
|
||||
}
|
||||
}
|
100
src/input.rs
100
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,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;
|
||||
|
|
|
@ -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) {}
|
||||
}
|
||||
|
|
74
src/main.rs
74
src/main.rs
|
@ -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.");
|
||||
|
|
|
@ -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;
|
Loading…
Reference in a new issue