3 Commits

Author SHA1 Message Date
e95774ae16 WIP 2025-07-02 18:06:54 -06:00
bd7c4e3e27 WIP basic ui
fix
2025-06-30 21:43:20 -06:00
55b7c2970c add status bar to console 2025-06-30 19:37:55 -06:00
11 changed files with 371 additions and 1064 deletions

404
Cargo.lock generated
View File

@@ -12,18 +12,6 @@ dependencies = [
"regex",
]
[[package]]
name = "ahash"
version = "0.8.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75"
dependencies = [
"cfg-if",
"once_cell",
"version_check",
"zerocopy",
]
[[package]]
name = "aho-corasick"
version = "1.1.3"
@@ -39,6 +27,12 @@ version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
[[package]]
name = "arrform"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7cf566ecc5c9d82b973e81d30babf6583c9b497f86295c952d538c3254ef4e6"
[[package]]
name = "ascii-canvas"
version = "3.0.0"
@@ -54,7 +48,7 @@ version = "4.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef1e3e699d84ab1b0911a1010c5c106aa34ae89aeac103be5ce0c3859db1e891"
dependencies = [
"term 1.1.0",
"term 1.0.2",
]
[[package]]
@@ -87,6 +81,21 @@ dependencies = [
"rustc_version",
]
[[package]]
name = "bisync"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5020822f6d6f23196ccaf55e228db36f9de1cf788052b37992e17cbc96ec41a7"
dependencies = [
"bisync_macros",
]
[[package]]
name = "bisync_macros"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d21f40d350a700f6aa107e45fb26448cf489d34794b2ba4522181dc9f1173af6"
[[package]]
name = "bit-set"
version = "0.5.3"
@@ -123,12 +132,6 @@ 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"
@@ -141,18 +144,6 @@ version = "2.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
[[package]]
name = "bitvec"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c"
dependencies = [
"funty",
"radium",
"tap",
"wyz",
]
[[package]]
name = "block-buffer"
version = "0.10.4"
@@ -218,7 +209,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ec610d8f49840a5b376c69663b6369e71f4b34484b9b2eb29fb918d92516cb9"
dependencies = [
"bare-metal",
"bitfield 0.13.2",
"bitfield",
"embedded-hal 0.2.7",
"volatile-register",
]
@@ -462,13 +453,12 @@ checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
[[package]]
name = "embassy-embedded-hal"
version = "0.3.1"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8578db196d74db92efdd5ebc546736dac1685499ee245b22eff92fa5e4b57945"
checksum = "41fea5ef5bed4d3468dfd44f5c9fa4cda8f54c86d4fb4ae683eacf9d39e2ea12"
dependencies = [
"embassy-futures",
"embassy-hal-internal 0.3.0",
"embassy-sync 0.7.0",
"embassy-sync 0.6.2",
"embassy-time",
"embedded-hal 0.2.7",
"embedded-hal 1.0.0",
@@ -521,15 +511,6 @@ dependencies = [
"num-traits",
]
[[package]]
name = "embassy-hal-internal"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95285007a91b619dc9f26ea8f55452aa6c60f7115a4edc05085cd2bd3127cd7a"
dependencies = [
"num-traits",
]
[[package]]
name = "embassy-net-driver"
version = "0.2.0"
@@ -538,13 +519,13 @@ checksum = "524eb3c489760508f71360112bca70f6e53173e6fe48fc5f0efd0f5ab217751d"
[[package]]
name = "embassy-net-driver-channel"
version = "0.3.1"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25a567ab50319d866ad5e6c583ed665ba9b07865389644d3d82e45bf1497c934"
checksum = "4818c32afec43e3cae234f324bad9a976c9aa7501022d26ff60a4017a1a006b7"
dependencies = [
"embassy-futures",
"embassy-net-driver",
"embassy-sync 0.7.0",
"embassy-sync 0.6.2",
]
[[package]]
@@ -561,7 +542,7 @@ dependencies = [
"document-features",
"embassy-embedded-hal",
"embassy-futures",
"embassy-hal-internal 0.2.0",
"embassy-hal-internal",
"embassy-sync 0.6.2",
"embassy-time",
"embassy-usb-driver",
@@ -599,7 +580,7 @@ dependencies = [
"document-features",
"embassy-embedded-hal",
"embassy-futures",
"embassy-hal-internal 0.2.0",
"embassy-hal-internal",
"embassy-sync 0.6.2",
"embassy-time",
"embassy-time-driver",
@@ -664,7 +645,6 @@ dependencies = [
"defmt 0.3.100",
"document-features",
"embassy-time-driver",
"embassy-time-queue-utils",
"embedded-hal 0.2.7",
"embedded-hal 1.0.0",
"embedded-hal-async",
@@ -690,29 +670,13 @@ dependencies = [
"heapless",
]
[[package]]
name = "embassy-usb"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e651b9b7b47b514e6e6d1940a6e2e300891a2c33641917130643602a0cb6386"
dependencies = [
"embassy-futures",
"embassy-net-driver-channel",
"embassy-sync 0.6.2",
"embassy-usb-driver",
"heapless",
"ssmarshal",
"usbd-hid",
]
[[package]]
name = "embassy-usb-driver"
version = "0.1.1"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "340c5ce591ef58c6449e43f51d2c53efe1bf0bb6a40cbf80afa0d259c7d52c76"
checksum = "4fc247028eae04174b6635104a35b1ed336aabef4654f5e87a8f32327d231970"
dependencies = [
"defmt 1.0.1",
"embedded-io-async",
"defmt 0.3.100",
]
[[package]]
@@ -802,15 +766,39 @@ dependencies = [
]
[[package]]
name = "embedded-sdmmc"
version = "0.9.0"
name = "embedded-layout"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce3c7f9ea039eeafc4a49597b7bd5ae3a1c8e51b2803a381cb0f29ce90fe1ec6"
checksum = "9a90553247f2b05c59ac7894ea13d830636c2b1203fa03bff400eddbd1fa9f52"
dependencies = [
"embedded-graphics",
"embedded-layout-macros",
]
[[package]]
name = "embedded-layout-macros"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f6e621fe4c7e05b695274b722dc0a60bacd1c8696b58191baa0154713d52400"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.104",
]
[[package]]
name = "embedded-sdmmc"
version = "0.8.0"
source = "git+https://github.com/Be-ing/embedded-sdmmc-rs?branch=bisync#835b2e4f9d3482b6287f674d7ecf6ae5d0618c18"
dependencies = [
"bisync",
"byteorder",
"defmt 0.3.100",
"embassy-futures",
"embedded-hal 1.0.0",
"embedded-hal-async",
"embedded-io",
"embedded-io-async",
"heapless",
]
@@ -838,12 +826,6 @@ dependencies = [
"log",
]
[[package]]
name = "encode_unicode"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
[[package]]
name = "equivalent"
version = "1.0.2"
@@ -889,12 +871,6 @@ version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "funty"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
[[package]]
name = "futures"
version = "0.3.31"
@@ -1018,15 +994,6 @@ 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.15.4"
@@ -1049,6 +1016,15 @@ version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c"
[[package]]
name = "home"
version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf"
dependencies = [
"windows-sys",
]
[[package]]
name = "ident_case"
version = "1.0.1"
@@ -1062,7 +1038,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661"
dependencies = [
"equivalent",
"hashbrown 0.15.4",
"hashbrown",
]
[[package]]
@@ -1073,7 +1049,7 @@ checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9"
dependencies = [
"hermit-abi",
"libc",
"windows-sys 0.59.0",
"windows-sys",
]
[[package]]
@@ -1152,7 +1128,7 @@ dependencies = [
"regex-syntax 0.8.5",
"sha3",
"string_cache",
"term 1.1.0",
"term 1.0.2",
"unicode-xid",
"walkdir",
]
@@ -1339,7 +1315,7 @@ dependencies = [
"libc",
"redox_syscall",
"smallvec",
"windows-targets 0.52.6",
"windows-targets",
]
[[package]]
@@ -1387,6 +1363,7 @@ checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315"
name = "picocalc-os-rs"
version = "0.1.0"
dependencies = [
"arrform",
"bitflags 2.9.1",
"bt-hci",
"cortex-m",
@@ -1401,20 +1378,18 @@ dependencies = [
"embassy-rp 0.4.0",
"embassy-sync 0.7.0",
"embassy-time",
"embassy-usb",
"embedded-graphics",
"embedded-hal 0.2.7",
"embedded-hal 1.0.0",
"embedded-hal-async",
"embedded-hal-bus",
"embedded-layout",
"embedded-sdmmc",
"heapless",
"num_enum 0.7.4",
"panic-probe",
"portable-atomic",
"spin",
"st7365p-lcd",
"static_cell",
"talc",
"trouble-host",
]
@@ -1597,12 +1572,6 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "radium"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
[[package]]
name = "rand_core"
version = "0.6.4"
@@ -1666,9 +1635,9 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
[[package]]
name = "rgb"
version = "0.8.52"
version = "0.8.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c6a884d2998352bb4daf0183589aec883f16a6da1f4dde84d8e2e9a5409a1ce"
checksum = "57397d16646700483b67d2dd6511d79318f9d057fdbd21a4066aeac8b41d310a"
dependencies = [
"bytemuck",
]
@@ -1743,26 +1712,6 @@ version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
[[package]]
name = "serde"
version = "1.0.219"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.219"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.104",
]
[[package]]
name = "sha2-const-stable"
version = "0.1.0"
@@ -1809,35 +1758,13 @@ dependencies = [
"rgb",
]
[[package]]
name = "spin"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591"
dependencies = [
"lock_api",
]
[[package]]
name = "ssmarshal"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3e6ad23b128192ed337dfa4f1b8099ced0c2bf30d61e551b65fda5916dbb850"
dependencies = [
"encode_unicode",
"serde",
]
[[package]]
name = "st7365p-lcd"
version = "0.11.0"
source = "git+https://github.com/legitcamper/st7365p-lcd-rs?branch=async#87abf450404865dcb535292e9e1a6a2457fd4599"
version = "0.10.0"
dependencies = [
"bitvec",
"embedded-graphics-core",
"embedded-hal 1.0.0",
"embedded-hal-async",
"heapless",
"nb 1.1.0",
]
@@ -1896,21 +1823,6 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "talc"
version = "4.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3ae828aa394de34c7de08f522d1b86bd1c182c668d27da69caadda00590f26d"
dependencies = [
"lock_api",
]
[[package]]
name = "tap"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
[[package]]
name = "term"
version = "0.7.0"
@@ -1924,11 +1836,12 @@ dependencies = [
[[package]]
name = "term"
version = "1.1.0"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a43bddab41f8626c7bdaab872bbba75f8df5847b516d77c569c746e2ae5eb746"
checksum = "8a984c8d058c627faaf5e8e2ed493fa3c51771889196de1016cf9c1c6e90d750"
dependencies = [
"windows-sys 0.60.2",
"home",
"windows-sys",
]
[[package]]
@@ -2046,53 +1959,6 @@ version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
[[package]]
name = "usb-device"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "98816b1accafbb09085168b90f27e93d790b4bfa19d883466b5e53315b5f06a6"
dependencies = [
"heapless",
"portable-atomic",
]
[[package]]
name = "usbd-hid"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6f291ab53d428685cc780f08a2eb9d5d6ff58622db2b36e239a4f715f1e184c"
dependencies = [
"serde",
"ssmarshal",
"usb-device",
"usbd-hid-macros",
]
[[package]]
name = "usbd-hid-descriptors"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee54712c5d778d2fb2da43b1ce5a7b5060886ef7b09891baeb4bf36910a3ed"
dependencies = [
"bitfield 0.14.0",
]
[[package]]
name = "usbd-hid-macros"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb573c76e7884035ac5e1ab4a81234c187a82b6100140af0ab45757650ccda38"
dependencies = [
"byteorder",
"hashbrown 0.13.2",
"log",
"proc-macro2",
"quote",
"serde",
"syn 1.0.109",
"usbd-hid-descriptors",
]
[[package]]
name = "uuid"
version = "1.17.0"
@@ -2226,7 +2092,7 @@ version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
dependencies = [
"windows-sys 0.59.0",
"windows-sys",
]
[[package]]
@@ -2241,16 +2107,7 @@ version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "windows-sys"
version = "0.60.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
dependencies = [
"windows-targets 0.53.2",
"windows-targets",
]
[[package]]
@@ -2259,30 +2116,14 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm 0.52.6",
"windows_aarch64_msvc 0.52.6",
"windows_i686_gnu 0.52.6",
"windows_i686_gnullvm 0.52.6",
"windows_i686_msvc 0.52.6",
"windows_x86_64_gnu 0.52.6",
"windows_x86_64_gnullvm 0.52.6",
"windows_x86_64_msvc 0.52.6",
]
[[package]]
name = "windows-targets"
version = "0.53.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef"
dependencies = [
"windows_aarch64_gnullvm 0.53.0",
"windows_aarch64_msvc 0.53.0",
"windows_i686_gnu 0.53.0",
"windows_i686_gnullvm 0.53.0",
"windows_i686_msvc 0.53.0",
"windows_x86_64_gnu 0.53.0",
"windows_x86_64_gnullvm 0.53.0",
"windows_x86_64_msvc 0.53.0",
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
@@ -2291,105 +2132,48 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_aarch64_msvc"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnu"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_gnullvm"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_i686_msvc"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnu"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "windows_x86_64_msvc"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486"
[[package]]
name = "wyz"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed"
dependencies = [
"tap",
]
[[package]]
name = "zerocopy"
version = "0.8.26"

View File

@@ -12,7 +12,7 @@ opt-level = "z"
[features]
default = ["rp235x", "defmt"]
# rp2040 = ["embassy-rp/rp2040"]
rp2040 = ["embassy-rp/rp2040"]
rp235x = ["embassy-rp/rp235xb"]
trouble = ["dep:bt-hci", "dep:cyw43", "dep:cyw43-pio", "dep:trouble-host"]
defmt = [
@@ -44,10 +44,9 @@ embassy-rp = { version = "0.4.0", features = [
"binary-info",
] }
embassy-futures = "0.1.1"
embassy-time = { version = "0.4.0", features = ["generic-queue-8"] }
embassy-embedded-hal = "0.3.1"
embassy-sync = "0.7"
embassy-usb = "0.4.0"
embassy-time = "0.4.0"
embassy-embedded-hal = "0.3.0"
embassy-sync = { version = "0.7" }
trouble-host = { version = "0.1", features = [
"derive",
"scan",
@@ -61,22 +60,22 @@ cyw43-pio = { version = "0.3.0", optional = true }
embedded-hal-bus = { version = "0.3.0", features = ["async"] }
embedded-hal = "0.2.7"
embedded-hal-1 = { version = "1.0.0", package = "embedded-hal" }
embedded-hal-async = "1.0.0"
cortex-m = { version = "0.7.7" }
cortex-m-rt = "0.7.5"
panic-probe = "0.3"
portable-atomic = { version = "1.11", features = ["critical-section"] }
static_cell = "2.1.1"
bitflags = "2.9.1"
heapless = "0.8.0"
arrform = "0.1.1"
defmt = { version = "0.3", optional = true }
defmt-rtt = "0.4.2"
embedded-graphics = { version = "0.8.1" }
embedded-sdmmc = { version = "0.9", default-features = false }
st7365p-lcd = { git = "https://github.com/legitcamper/st7365p-lcd-rs", branch = "async" }
static_cell = "2.1.1"
bitflags = "2.9.1"
talc = "4.4.3"
spin = "0.10.0"
heapless = "0.8.0"
num_enum = { version = "0.7.4", default-features = false }
embedded-layout = "0.4.2"
embedded-sdmmc = { git = "https://github.com/Be-ing/embedded-sdmmc-rs", branch = "bisync", default-features = false }
# st7365p-lcd = { git = "https://github.com/legitcamper/st7365p-lcd-rs", branch = "async" }
st7365p-lcd = { path = "../ST7365P-lcd-rs" }

View File

@@ -1,68 +1,180 @@
use core::sync::atomic::Ordering;
use arrform::{ArrForm, arrform};
use core::fmt::Debug;
use defmt::info;
use embassy_rp::{
gpio::{Level, Output},
peripherals::{PIN_13, PIN_14, PIN_15, SPI1},
spi::{Async, Spi},
};
use embassy_sync::{blocking_mutex::raw::ThreadModeRawMutex, signal::Signal};
use embassy_time::{Delay, Instant, Timer};
use embassy_time::{Delay, Timer};
use embedded_graphics::{
Drawable,
draw_target::DrawTarget,
mono_font::{MonoTextStyle, ascii::FONT_10X20},
mono_font::{
MonoTextStyle,
ascii::{FONT_6X10, FONT_9X15, FONT_10X20},
},
pixelcolor::Rgb565,
prelude::{Dimensions, Point, RgbColor, Size},
primitives::Rectangle,
text::{Alignment, Text},
prelude::{Point, RgbColor, Size},
primitives::{PrimitiveStyle, Rectangle, StyledDrawable},
text::Text,
};
use embedded_hal_bus::spi::ExclusiveDevice;
use portable_atomic::AtomicBool;
use st7365p_lcd::{FrameBuffer, ST7365P};
use embedded_hal_1::digital::OutputPin;
use embedded_hal_async::spi::SpiDevice;
use embedded_hal_bus::spi::{ExclusiveDevice, NoDelay};
use embedded_layout::{
align::{horizontal, vertical},
layout::linear::LinearLayout,
object_chain::Chain,
prelude::*,
};
use heapless::{String, Vec};
use st7365p_lcd::{FrameBuffer, Orientation, ST7365P};
const SCREEN_WIDTH: usize = 320;
const SCREEN_HEIGHT: usize = 320;
pub const SCREEN_WIDTH: usize = 320;
pub const SCREEN_HEIGHT: usize = 320;
pub static DISPLAY_SIGNAL: Signal<ThreadModeRawMutex, ()> = Signal::new();
pub const STATUS_BAR_WIDTH: usize = 320;
pub const STATUS_BAR_HEIGHT: usize = 40;
pub async fn display_handler(
spi: Spi<'static, SPI1, Async>,
cs: PIN_13,
data: PIN_14,
reset: PIN_15,
) {
let spi_device = ExclusiveDevice::new(spi, Output::new(cs, Level::Low), Delay).unwrap();
let mut display = ST7365P::new(
spi_device,
Output::new(data, Level::Low),
Some(Output::new(reset, Level::High)),
false,
true,
Delay,
);
let mut framebuffer: FrameBuffer<
SCREEN_WIDTH,
SCREEN_HEIGHT,
{ SCREEN_WIDTH * SCREEN_HEIGHT },
> = FrameBuffer::new();
display.init().await.unwrap();
display.set_custom_orientation(0x40).await.unwrap();
framebuffer.draw(&mut display).await.unwrap();
display.set_on().await.unwrap();
pub type FRAMEBUFFER = FrameBuffer<
SCREEN_WIDTH,
SCREEN_HEIGHT,
ExclusiveDevice<Spi<'static, SPI1, Async>, Output<'static>, Delay>,
Output<'static>,
Output<'static>,
>;
DISPLAY_SIGNAL.signal(());
pub struct UI<const MAX_SELECTIONS: usize, const MAX_STR_LEN: usize> {
pub status_bar: StatusBar,
pub selections_list: SelectionList<MAX_SELECTIONS, MAX_STR_LEN>,
}
loop {
DISPLAY_SIGNAL.wait().await;
impl<const MAX_SELECTIONS: usize, const MAX_STR_LEN: usize> UI<MAX_SELECTIONS, MAX_STR_LEN> {
pub fn new() -> Self {
Self {
status_bar: StatusBar {
battery: 100,
backlight: 100,
volume: 100,
},
selections_list: SelectionList::new(Vec::new()),
}
}
// text.draw(&mut framebuffer).unwrap();
pub fn draw<D: DrawTarget<Color = Rgb565>>(&mut self, target: &mut D)
where
<D as DrawTarget>::Error: Debug,
{
self.draw_status_bar(target);
self.draw_selection(target);
}
let start = Instant::now();
framebuffer
.partial_draw_batched(&mut display)
.await
.unwrap();
info!("Elapsed {}ms", start.elapsed().as_millis());
fn draw_selection<D: DrawTarget<Color = Rgb565>>(&mut self, target: &mut D) {
let text_style = MonoTextStyle::new(&FONT_9X15, Rgb565::WHITE);
let selection = Rectangle::new(
Point::new(0, STATUS_BAR_HEIGHT as i32 + 1),
Size::new(
SCREEN_WIDTH as u32,
(SCREEN_HEIGHT - STATUS_BAR_HEIGHT) as u32 - 1,
),
);
let _ = if self.selections_list.selections.is_empty() {
LinearLayout::horizontal(Chain::new(Text::new(
"No Programs found on SD Card\nEnsure programs end with '.rhai',\nand are located in the root directory",
Point::zero(),
text_style,
)))
.arrange()
.align_to(&selection, horizontal::Center, vertical::Center).draw(target)
} else {
LinearLayout::horizontal(
Chain::new(Text::new(
arrform!(20, "Bat: {}", self.status_bar.battery).as_str(),
Point::zero(),
text_style,
))
.append(Text::new(" ", Point::zero(), text_style))
.append(Text::new(
arrform!(20, "Lght: {}", self.status_bar.backlight).as_str(),
Point::zero(),
text_style,
))
.append(Text::new(" ", Point::zero(), text_style))
.append(Text::new(
arrform!(20, "Vol: {}", self.status_bar.volume).as_str(),
Point::zero(),
text_style,
)),
)
.arrange()
.align_to(&selection, horizontal::Left, vertical::Center)
.draw(target)
};
}
fn draw_status_bar<D: DrawTarget<Color = Rgb565>>(&mut self, target: &mut D) {
let text_style = MonoTextStyle::new(&FONT_9X15, Rgb565::WHITE);
let status_bar = Rectangle::new(
Point::new(0, 0),
Size::new(STATUS_BAR_WIDTH as u32, STATUS_BAR_HEIGHT as u32),
);
let _ = LinearLayout::horizontal(
Chain::new(Text::new(
arrform!(20, "Bat: {}", self.status_bar.battery).as_str(),
Point::zero(),
text_style,
))
.append(Text::new(
arrform!(20, "Lght: {}", self.status_bar.backlight).as_str(),
Point::zero(),
text_style,
))
.append(Text::new(
arrform!(20, "Vol: {}", self.status_bar.volume).as_str(),
Point::zero(),
text_style,
)),
)
.arrange()
.align_to(&status_bar, horizontal::Center, vertical::Center)
.draw(target);
}
}
pub struct StatusBar {
pub battery: u8,
pub backlight: u8,
pub volume: u8,
}
pub struct SelectionList<const MAX_SELECTION: usize, const MAX_STR_LEN: usize> {
current_selection: u16,
selections: Vec<String<MAX_STR_LEN>, MAX_SELECTION>,
}
impl<const MAX_SELECTION: usize, const MAX_STR_LEN: usize>
SelectionList<MAX_SELECTION, MAX_STR_LEN>
{
pub fn new(selections: Vec<String<MAX_STR_LEN>, MAX_SELECTION>) -> Self {
Self {
selections,
current_selection: 0,
}
}
pub fn down(&mut self) {
if self.current_selection + 1 < self.selections.len() as u16 {
self.current_selection += 1
}
}
pub fn up(&mut self) {
if self.current_selection > self.selections.len() as u16 {
self.current_selection -= 1
}
}
}

View File

@@ -1,88 +1,111 @@
#![feature(impl_trait_in_assoc_type)]
#![feature(ascii_char)]
#![no_std]
#![no_main]
use crate::{
display::DISPLAY_SIGNAL,
peripherals::keyboard::{KeyCode, KeyState, read_keyboard_fifo},
storage::SdCard,
usb::usb_handler,
#[cfg(feature = "defmt")]
use defmt::*;
use embedded_graphics::{
pixelcolor::Rgb565,
prelude::{Point, RgbColor, Size},
primitives::{PrimitiveStyle, Rectangle, StyledDrawable},
};
use {defmt_rtt as _, panic_probe as _};
use core::cell::RefCell;
use embassy_executor::Spawner;
use embassy_futures::join::join;
use embassy_rp::{
gpio::{Input, Level, Output, Pull},
peripherals::{I2C1, USB},
spi::Spi,
usb as embassy_rp_usb,
use crate::display::{FRAMEBUFFER, SCREEN_HEIGHT, SCREEN_WIDTH, UI};
use crate::peripherals::{
conf_peripherals,
keyboard::{KeyEvent, read_keyboard_fifo},
};
use embassy_rp::{i2c, i2c::I2c, spi};
use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex;
use embassy_sync::mutex::Mutex;
use embassy_executor::Spawner;
use embassy_rp::peripherals::{I2C1, PIN_13, PIN_14, PIN_15, SPI1};
use embassy_rp::spi::Spi;
use embassy_rp::{
bind_interrupts,
gpio::{Level, Output},
i2c,
i2c::I2c,
spi,
};
use embassy_sync::blocking_mutex::raw::NoopRawMutex;
use embassy_sync::channel::Channel;
use embassy_time::{Delay, Timer};
use embedded_graphics::primitives::Rectangle;
use embedded_hal_1::spi::SpiDevice;
use embedded_hal_bus::spi::ExclusiveDevice;
use embedded_sdmmc::SdCard as SdmmcSdCard;
use heapless::String;
use embedded_sdmmc::asynchronous::{File, SdCard, ShortFileName, VolumeIdx, VolumeManager};
use st7365p_lcd::{FrameBuffer, ST7365P};
use static_cell::StaticCell;
mod peripherals;
use peripherals::conf_peripherals;
mod display;
use display::display_handler;
mod scsi;
mod storage;
mod usb;
mod utils;
mod peripherals;
embassy_rp::bind_interrupts!(struct Irqs {
I2C1_IRQ => i2c::InterruptHandler<I2C1>;
USBCTRL_IRQ => embassy_rp_usb::InterruptHandler<USB>;
});
embassy_rp::bind_interrupts!(
struct Irqs {
I2C1_IRQ => i2c::InterruptHandler<I2C1>;
}
);
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
async fn main(spawner: Spawner) {
let p = embassy_rp::init(Default::default());
// MCU i2c bus for peripherals
let mut config = i2c::Config::default();
config.frequency = 400_000;
let i2c1 = I2c::new_async(p.I2C1, p.PIN_7, p.PIN_6, Irqs, config);
let mut i2c1_config = i2c::Config::default();
i2c1_config.frequency = 100_000;
let i2c1 = I2c::new_async(p.I2C1, p.PIN_7, p.PIN_6, Irqs, i2c1_config);
conf_peripherals(i2c1).await;
// SPI1 bus display
let mut config = spi::Config::default();
config.frequency = 16_000_000;
let spi1 = spi::Spi::new(
p.SPI1, p.PIN_10, p.PIN_11, p.PIN_12, p.DMA_CH0, p.DMA_CH1, config,
let mut spi1_config = spi::Config::default();
spi1_config.frequency = 16_000_000;
let spi1 = Spi::new(
p.SPI1,
p.PIN_10,
p.PIN_11,
p.PIN_12,
p.DMA_CH0,
p.DMA_CH1,
spi1_config,
);
let usb = embassy_rp_usb::Driver::new(p.USB, Irqs);
let sdcard = {
let mut config = spi::Config::default();
config.frequency = 400_000;
let spi = Spi::new_blocking(
p.SPI0,
p.PIN_18, // clk
p.PIN_19, // mosi
p.PIN_16, // miso
config.clone(),
);
let cs = Output::new(p.PIN_17, Level::High);
let det = Input::new(p.PIN_22, Pull::None);
let device = ExclusiveDevice::new(spi, cs, Delay).unwrap();
let sdcard = SdmmcSdCard::new(device, Delay);
config.frequency = 32_000_000;
sdcard.spi(|dev| dev.bus_mut().set_config(&config));
SdCard::new(sdcard, det)
};
usb_handler(usb, sdcard).await;
spawner
.spawn(main_task(spi1, p.PIN_13, p.PIN_14, p.PIN_15))
.unwrap();
}
#[embassy_executor::task]
async fn main_task(
spi1: Spi<'static, SPI1, spi::Async>,
spi1_cs: PIN_13,
spi1_data: PIN_14,
spi1_reset: PIN_15,
) {
let spi_device = ExclusiveDevice::new(spi1, Output::new(spi1_cs, Level::Low), Delay).unwrap();
let display = ST7365P::new(
spi_device,
Output::new(spi1_data, Level::Low),
Some(Output::new(spi1_reset, Level::High)),
false,
true,
SCREEN_WIDTH as u32,
SCREEN_HEIGHT as u32,
);
let mut framebuffer: FRAMEBUFFER = FrameBuffer::new(display);
framebuffer.init(&mut Delay).await.unwrap();
framebuffer
.display
.set_custom_orientation(0x60)
.await
.unwrap();
// let mut ui: UI<50, 25> = UI::new();
// read_keyboard_fifo().await;
Rectangle::new(Point::new(0, 0), Size::new(319, 319))
.draw_styled(&PrimitiveStyle::with_fill(Rgb565::RED), &mut framebuffer)
.unwrap();
// ui.draw(&mut framebuffer);
framebuffer.draw().await.unwrap();
loop {
info!("Done");
Timer::after_millis(500).await;
}
}

View File

@@ -1,3 +1,12 @@
use defmt::{info, warn};
use embassy_rp::{
i2c::{Async, I2c},
peripherals::I2C1,
};
use embassy_sync::{
blocking_mutex::raw::{CriticalSectionRawMutex, NoopRawMutex},
channel::Sender,
};
use crate::peripherals::PERIPHERAL_BUS;

View File

@@ -5,10 +5,13 @@ use embassy_rp::{
i2c::{Async, I2c},
peripherals::I2C1,
};
use embassy_sync::{blocking_mutex::raw::NoopRawMutex, lazy_lock::LazyLock, mutex::Mutex};
use embassy_time::Timer;
use embassy_sync::{
blocking_mutex::raw::NoopRawMutex, channel::Sender, lazy_lock::LazyLock, mutex::Mutex,
};
use embassy_time::{Duration, Timer};
pub mod keyboard;
use keyboard::{KeyCode, KeyEvent, KeyState};
use crate::peripherals::keyboard::{configure_keyboard, read_keyboard_fifo};
@@ -28,11 +31,7 @@ pub async fn conf_peripherals(i2c: I2CBUS) {
PERIPHERAL_BUS.get().lock().await.replace(i2c);
configure_keyboard(200, 100).await;
// empty keys
while read_keyboard_fifo().await.is_some() {}
// set_lcd_backlight(255).await;
set_lcd_backlight(255).await;
set_key_backlight(0).await;
}

View File

@@ -1,308 +0,0 @@
use crate::format;
use embassy_usb::driver::{Driver, EndpointIn, EndpointOut};
use embassy_usb::types::StringIndex;
use embassy_usb::{Builder, Config};
use embedded_sdmmc::{Block, BlockIdx};
use heapless::Vec;
mod scsi_types;
use scsi_types::*;
use crate::storage::SdCard;
const BULK_ENDPOINT_PACKET_SIZE: usize = 64;
pub struct MassStorageClass<'d, D: Driver<'d>> {
sdcard: SdCard,
bulk_out: D::EndpointOut,
bulk_in: D::EndpointIn,
}
impl<'d, D: Driver<'d>> MassStorageClass<'d, D> {
pub fn new(builder: &mut Builder<'d, D>, sdcard: SdCard) -> Self {
let mut function = builder.function(0x08, SUBCLASS_SCSI, 0x50); // Mass Storage class
let mut interface = function.interface();
let mut alt = interface.alt_setting(0x08, SUBCLASS_SCSI, 0x50, None);
let bulk_out = alt.endpoint_bulk_out(BULK_ENDPOINT_PACKET_SIZE as u16);
let bulk_in = alt.endpoint_bulk_in(BULK_ENDPOINT_PACKET_SIZE as u16);
Self {
bulk_out,
bulk_in,
sdcard,
}
}
pub async fn poll(&mut self) {
loop {
let mut cbw_buf = [0u8; 31];
if let Ok(n) = self.bulk_out.read(&mut cbw_buf).await {
if n == 31 {
if let Some(cbw) = CommandBlockWrapper::parse(&cbw_buf[..n]) {
// TODO: validate cbw
if self.handle_command(&cbw.CBWCB).await.is_ok() {
self.send_csw_success(cbw.dCBWTag).await
} else {
self.send_csw_fail(cbw.dCBWTag).await
}
}
}
}
}
}
async fn handle_command(&mut self, cbw: &[u8]) -> Result<(), ()> {
let mut response: Vec<u8, BULK_ENDPOINT_PACKET_SIZE> = Vec::new();
let mut block = [Block::new(); 1];
match parse_cb(cbw) {
ScsiCommand::Unknown => {
#[cfg(feature = "defmt")]
defmt::warn!("Got unexpected scsi command: {}", cbw);
Err(())
}
ScsiCommand::Inquiry {
evpd,
page_code,
alloc_len,
} => {
if !evpd {
response.push(0x00).map_err(|_| ())?; // Direct-access block device
response.push(0x80).map_err(|_| ())?; // Removable
response.push(0x05).map_err(|_| ())?; // SPC-3 compliance
response.push(0x02).map_err(|_| ())?; // Response data format
response.push(0x00).map_err(|_| ())?; // Additional length - edited later
response.push(0x00).map_err(|_| ())?; // FLAGS
response.push(0x00).map_err(|_| ())?; // FLAGS
response.push(0).map_err(|_| ())?; // FLAGS
assert!(response.len() == 8);
let vendor = b"LEGTCMPR";
assert!(vendor.len() == 8);
response.extend_from_slice(vendor)?;
let product = b"Pico Calc Sdcard";
assert!(product.len() == 16);
response.extend_from_slice(product)?;
let version = b"1.00";
assert!(version.len() == 4);
response.extend_from_slice(version)?; // 4-byte firmware version
let addl_len = response.len() - 5;
response[4] = addl_len as u8;
assert!(response.len() == 36);
} else {
match page_code {
0x00 => {
response
.extend_from_slice(&[
0x00, // Peripheral Qualifier + Peripheral Device Type (0x00 = Direct-access block device)
0x00, // Page Code (same as requested: 0x00)
0x00, 0x03, // Page Length: 3 bytes follow
0x00, // Supported VPD Page: 0x00 (this one — the "Supported VPD Pages" page itself)
0x80, // Supported VPD Page: 0x80 (Unit Serial Number)
0x83, // Supported VPD Page: 0x83 (Device Identification)
])
.map_err(|_| ())?
}
0x80 => {
let serial = b"Pico Calc";
response.extend_from_slice(&[
0x00, // Peripheral Qualifier & Device Type
0x80, // Page Code = 0x80 (Unit Serial Number)
0x00, // Reserved
serial.len() as u8,
])?;
response.extend_from_slice(serial)?;
}
0x83 => {
let id = b"SdCard";
response.extend_from_slice(&[
0x00,
0x83, // Page code
0x00,
(4 + id.len()) as u8, // Length
0x02, // ASCII identifier
0x01, // Identifier type
0x00, // Reserved
id.len() as u8,
])?;
response.extend_from_slice(id)?;
}
_ => (),
}
};
let len = core::cmp::min(alloc_len as usize, response.len());
self.bulk_in.write(&response[..len]).await.map_err(|_| ())
}
ScsiCommand::TestUnitReady => {
if self.sdcard.is_attached() {
Ok(())
} else {
Err(())
}
}
ScsiCommand::RequestSense { desc, alloc_len } => Ok(()),
ScsiCommand::ModeSense6 {
dbd,
page_control,
page_code,
subpage_code,
alloc_len,
} => {
// DBD=0, no block descriptors; total length = 4
let response = [
0x03, // Mode data length (excluding this byte): 3
0x00, // Medium type
0x00, // Device-specific parameter
0x00, // Block descriptor length = 0 (DBD = 1)
];
let len = alloc_len.min(response.len() as u8) as usize;
self.bulk_in.write(&response[..len]).await.map_err(|_| ())
}
ScsiCommand::ModeSense10 {
dbd,
page_control,
page_code,
subpage_code,
alloc_len,
} => {
let response = [
0x00, 0x06, // Mode data length = 6
0x00, // Medium type
0x00, // Device-specific parameter
0x00, 0x00, // Reserved
0x00, 0x00, // Block descriptor length = 0
];
let len = alloc_len.min(response.len() as u16) as usize;
self.bulk_in.write(&response[..len]).await.map_err(|_| ())
}
ScsiCommand::ReadCapacity10 => {
let block_size = SdCard::BLOCK_SIZE as u64;
let total_blocks = self.sdcard.size() / block_size;
let last_lba = total_blocks.checked_sub(1).unwrap_or(0);
response.extend_from_slice(&(last_lba as u32).to_be_bytes())?;
response.extend_from_slice(&(block_size as u32).to_be_bytes())?;
self.bulk_in.write(&response).await.map_err(|_| ())
}
ScsiCommand::ReadCapacity16 { alloc_len } => {
let block_size = SdCard::BLOCK_SIZE as u64;
let total_blocks = self.sdcard.size() / block_size;
let last_lba = total_blocks.checked_sub(1).unwrap_or(0);
response.extend_from_slice(&last_lba.to_be_bytes())?; // 8 bytes last LBA
response.extend_from_slice(&(block_size as u32).to_be_bytes())?; // 4 bytes block length
response.extend_from_slice(&[0u8; 20])?; // 20 reserved bytes zeroed
let len = alloc_len.min(response.len() as u32) as usize;
self.bulk_in.write(&response[..len]).await.map_err(|_| ())
}
ScsiCommand::Read { lba, len } => {
for i in 0..len {
let block_idx = BlockIdx(lba as u32 + i as u32);
self.sdcard.read_blocks(&mut block, block_idx)?;
for chunk in block[0].contents.chunks(BULK_ENDPOINT_PACKET_SIZE.into()) {
self.bulk_in.write(chunk).await.map_err(|_| ())?;
}
}
Ok(())
}
ScsiCommand::Write { lba, len } => {
for i in 0..len {
let block_idx = BlockIdx(lba as u32 + i as u32);
for chunk in block[0]
.contents
.chunks_mut(BULK_ENDPOINT_PACKET_SIZE.into())
{
self.bulk_out.read(chunk).await.map_err(|_| ())?;
}
self.sdcard.write_blocks(&mut block, block_idx)?;
}
Ok(())
}
ScsiCommand::ReadFormatCapacities { alloc_len } => {
let block_size = SdCard::BLOCK_SIZE as u32;
let num_blocks = (self.sdcard.size() / block_size as u64) as u32;
let mut response = [0u8; 12];
// Capacity List Length (8 bytes follows)
response[3] = 8;
// Descriptor
response[4..8].copy_from_slice(&num_blocks.to_be_bytes());
response[8] = 0x03; // formatted media
response[9..12].copy_from_slice(&block_size.to_be_bytes()[1..4]); // only 3 bytes
let response_len = alloc_len.min(response.len() as u16) as usize;
self.bulk_in
.write(&response[..response_len])
.await
.map_err(|_| ())
}
ScsiCommand::PreventAllowMediumRemoval { prevent: _prevent } => Ok(()),
ScsiCommand::StartStopUnit { start, load_eject } => Ok(()),
}
}
pub async fn send_csw_success(&mut self, tag: u32) {
self.send_csw(tag, 0x00, 0).await;
}
pub async fn send_csw_fail(&mut self, tag: u32) {
defmt::error!("Command Failed");
self.send_csw(tag, 0x01, 0).await; // 0x01 = Command Failed
}
pub async fn send_csw(&mut self, tag: u32, status: u8, residue: u32) {
let mut csw = [0u8; 13];
csw[0..4].copy_from_slice(&0x53425355u32.to_le_bytes()); // Signature "USBS"
csw[4..8].copy_from_slice(&tag.to_le_bytes());
csw[8..12].copy_from_slice(&residue.to_le_bytes());
csw[12] = status;
let _ = self.bulk_in.write(&csw).await;
}
}
#[repr(C, packed)]
struct CommandBlockWrapper {
dCBWSignature: u32,
dCBWTag: u32,
dCBWDataTransferLength: u32,
bmCBWFlags: u8,
bCBWLUN: u8,
bCBWCBLength: u8,
CBWCB: [u8; 16],
}
impl CommandBlockWrapper {
fn parse(buf: &[u8]) -> Option<Self> {
if buf.len() < 31 {
return None;
}
let dCBWSignature = u32::from_le_bytes(buf[0..4].try_into().ok()?);
if dCBWSignature != 0x43425355 {
return None; // invalid signature
}
Some(Self {
dCBWSignature,
dCBWTag: u32::from_le_bytes(buf[4..8].try_into().ok()?),
dCBWDataTransferLength: u32::from_le_bytes(buf[8..12].try_into().ok()?),
bmCBWFlags: buf[12],
bCBWLUN: buf[13],
bCBWCBLength: buf[14],
CBWCB: buf[15..31].try_into().ok()?,
})
}
}

View File

@@ -1,164 +0,0 @@
use num_enum::TryFromPrimitive;
#[derive(Debug, Clone, Copy)]
pub enum ScsiError {
NotReady,
}
/// THE CODE BELOW ORIGINATES FROM: https://github.com/apohrebniak/usbd-storage/blob/master/usbd-storage/src/subclass/scsi.rs
/// SCSI device subclass code
pub const SUBCLASS_SCSI: u8 = 0x06; // SCSI Transparent command set
/* SCSI codes */
/* SPC */
const TEST_UNIT_READY: u8 = 0x00;
const REQUEST_SENSE: u8 = 0x03;
const INQUIRY: u8 = 0x12;
const MODE_SENSE_6: u8 = 0x1A;
const MODE_SENSE_10: u8 = 0x5A;
/* SBC */
const READ_10: u8 = 0x28;
const READ_16: u8 = 0x88;
const READ_CAPACITY_10: u8 = 0x25;
const READ_CAPACITY_16: u8 = 0x9E;
const WRITE_10: u8 = 0x2A;
/* MMC */
const READ_FORMAT_CAPACITIES: u8 = 0x23;
const PREVENT_ALLOW_MEDIUM_REMOVAL: u8 = 0x1E;
const START_STOP_UNIT: u8 = 0x1B;
/// SCSI command
///
/// Refer to specifications (SPC,SAM,SBC,MMC,etc.)
#[derive(Copy, Clone, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub enum ScsiCommand {
Unknown,
/* SPC */
Inquiry {
evpd: bool,
page_code: u8,
alloc_len: u16,
},
TestUnitReady,
RequestSense {
desc: bool,
alloc_len: u8,
},
ModeSense6 {
dbd: bool,
page_control: PageControl,
page_code: u8,
subpage_code: u8,
alloc_len: u8,
},
ModeSense10 {
dbd: bool,
page_control: PageControl,
page_code: u8,
subpage_code: u8,
alloc_len: u16,
},
/* SBC */
ReadCapacity10,
ReadCapacity16 {
alloc_len: u32,
},
Read {
lba: u64,
len: u64,
},
Write {
lba: u64,
len: u64,
},
/* MMC */
ReadFormatCapacities {
alloc_len: u16,
},
PreventAllowMediumRemoval {
prevent: bool,
},
StartStopUnit {
start: bool,
load_eject: bool,
},
}
#[repr(u8)]
#[derive(Copy, Clone, Debug, TryFromPrimitive)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum PageControl {
CurrentValues = 0b00,
ChangeableValues = 0b01,
DefaultValues = 0b10,
SavedValues = 0b11,
}
#[allow(dead_code)]
pub fn parse_cb(cb: &[u8]) -> ScsiCommand {
match cb[0] {
TEST_UNIT_READY => ScsiCommand::TestUnitReady,
INQUIRY => ScsiCommand::Inquiry {
evpd: (cb[1] & 0b00000001) != 0,
page_code: cb[2],
alloc_len: u16::from_be_bytes([cb[3], cb[4]]),
},
REQUEST_SENSE => ScsiCommand::RequestSense {
desc: (cb[1] & 0b00000001) != 0,
alloc_len: cb[4],
},
READ_CAPACITY_10 => ScsiCommand::ReadCapacity10,
READ_CAPACITY_16 => ScsiCommand::ReadCapacity16 {
alloc_len: u32::from_be_bytes([cb[10], cb[11], cb[12], cb[13]]),
},
READ_10 => ScsiCommand::Read {
lba: u32::from_be_bytes([cb[2], cb[3], cb[4], cb[5]]) as u64,
len: u16::from_be_bytes([cb[7], cb[8]]) as u64,
},
READ_16 => ScsiCommand::Read {
lba: u64::from_be_bytes((&cb[2..10]).try_into().unwrap()),
len: u32::from_be_bytes((&cb[10..14]).try_into().unwrap()) as u64,
},
WRITE_10 => ScsiCommand::Write {
lba: u32::from_be_bytes([cb[2], cb[3], cb[4], cb[5]]) as u64,
len: u16::from_be_bytes([cb[7], cb[8]]) as u64,
},
MODE_SENSE_6 => ScsiCommand::ModeSense6 {
dbd: (cb[1] & 0b00001000) != 0,
page_control: PageControl::try_from_primitive(cb[2] >> 6).unwrap(),
page_code: cb[2] & 0b00111111,
subpage_code: cb[3],
alloc_len: cb[4],
},
MODE_SENSE_10 => ScsiCommand::ModeSense10 {
dbd: (cb[1] & 0b00001000) != 0,
page_control: PageControl::try_from_primitive(cb[2] >> 6).unwrap(),
page_code: cb[2] & 0b00111111,
subpage_code: cb[3],
alloc_len: u16::from_be_bytes([cb[7], cb[8]]),
},
READ_FORMAT_CAPACITIES => ScsiCommand::ReadFormatCapacities {
alloc_len: u16::from_be_bytes([cb[7], cb[8]]),
},
PREVENT_ALLOW_MEDIUM_REMOVAL => ScsiCommand::PreventAllowMediumRemoval {
prevent: (cb[1] & 0b00000001) != 0,
},
START_STOP_UNIT => ScsiCommand::StartStopUnit {
start: (cb[4] & 0b00000001) != 0,
load_eject: (cb[4] & 0b00000010) != 0,
},
_ => ScsiCommand::Unknown,
}
}

View File

@@ -1,100 +0,0 @@
use embassy_rp::gpio::{Input, Output};
use embassy_rp::peripherals::SPI0;
use embassy_rp::spi::{Blocking, Spi};
use embassy_time::Delay;
use embedded_hal_bus::spi::ExclusiveDevice;
use embedded_sdmmc::{
Block, BlockCount, BlockDevice, BlockIdx, Directory, SdCard as SdmmcSdCard, TimeSource,
Timestamp, Volume, VolumeIdx, VolumeManager, sdcard::Error,
};
pub const MAX_DIRS: usize = 4;
pub const MAX_FILES: usize = 5;
pub const MAX_VOLUMES: usize = 1;
type Device = ExclusiveDevice<Spi<'static, SPI0, Blocking>, Output<'static>, embassy_time::Delay>;
type SD = SdmmcSdCard<Device, Delay>;
type VolMgr = VolumeManager<SD, DummyTimeSource, MAX_DIRS, MAX_FILES, MAX_VOLUMES>;
type Vol<'a> = Volume<'a, SD, DummyTimeSource, MAX_DIRS, MAX_FILES, MAX_VOLUMES>;
type Dir<'a> = Directory<'a, SD, DummyTimeSource, MAX_DIRS, MAX_FILES, MAX_VOLUMES>;
pub struct DummyTimeSource {}
impl TimeSource for DummyTimeSource {
fn get_timestamp(&self) -> Timestamp {
Timestamp::from_calendar(2022, 1, 1, 0, 0, 0).unwrap()
}
}
pub struct SdCard {
det: Input<'static>,
volume_mgr: VolMgr,
}
impl SdCard {
pub const BLOCK_SIZE: u16 = 512;
pub fn new(sdcard: SD, det: Input<'static>) -> Self {
let volume_mgr = VolumeManager::<_, _, MAX_DIRS, MAX_FILES, MAX_VOLUMES>::new_with_limits(
sdcard,
DummyTimeSource {},
5000,
);
Self {
det: det,
volume_mgr,
}
}
/// Returns true if an SD card is inserted.
/// The DET pin is active-low via mechanical switch in the socket.
pub fn is_attached(&self) -> bool {
self.det.is_low()
}
pub fn open_volume(&mut self) -> Result<Vol<'_>, ()> {
if self.is_attached() {
return Ok(self.volume_mgr.open_volume(VolumeIdx(0)).map_err(|_| ())?);
}
Err(())
}
pub fn size(&self) -> u64 {
let mut result = 0;
self.volume_mgr.device(|sd| {
result = sd.num_bytes().unwrap_or(0);
DummyTimeSource {}
});
result
}
pub fn num_blocks(&self) -> u32 {
let mut result = 0;
self.volume_mgr.device(|sd| {
result = sd.num_blocks().unwrap_or(BlockCount(0)).0;
DummyTimeSource {}
});
result
}
pub fn read_blocks(&self, blocks: &mut [Block], start_block_idx: BlockIdx) -> Result<(), ()> {
let mut res: Result<(), Error> = Ok(());
self.volume_mgr.device(|sd| {
res = sd.read(blocks, start_block_idx);
DummyTimeSource {}
});
res.map_err(|_| ())
}
pub fn write_blocks(&self, blocks: &mut [Block], start_block_idx: BlockIdx) -> Result<(), ()> {
let mut res: Result<(), Error> = Ok(());
self.volume_mgr.device(|sd| {
let res = sd.write(blocks, start_block_idx);
DummyTimeSource {}
});
res.map_err(|_| ())
}
}

View File

@@ -1,36 +0,0 @@
use crate::{scsi::MassStorageClass, storage::SdCard};
use embassy_futures::select::select;
use embassy_rp::{peripherals::USB, usb::Driver};
use embassy_usb::{Builder, Config};
pub async fn usb_handler(driver: Driver<'static, USB>, sdcard: SdCard) {
let mut config = Config::new(0xc0de, 0xcafe);
config.manufacturer = Some("LegitCamper");
config.product = Some("PicoCalc");
config.serial_number = Some("01001100");
config.max_power = 100;
config.max_packet_size_0 = 64;
let mut config_descriptor = [0; 256];
let mut bos_descriptor = [0; 64];
let mut control_buf = [0; 64];
let mut builder = Builder::new(
driver,
config,
&mut config_descriptor,
&mut bos_descriptor,
&mut [],
&mut control_buf,
);
let mut scsi = MassStorageClass::new(&mut builder, sdcard);
let mut usb = builder.build();
loop {
select(usb.run(), scsi.poll()).await;
defmt::warn!("rebuilding usb");
usb.disable().await;
}
}

View File

@@ -1,11 +0,0 @@
#[macro_export]
macro_rules! format {
($len:literal, $($arg:tt)*) => {{
use heapless::String;
use core::fmt::Write;
let mut s: String<$len> = String::new();
let _ = write!(&mut s, $($arg)*);
s
}}
}