From b871d897c7c3ccb42d3f7f08d309037bd261f11f Mon Sep 17 00:00:00 2001 From: Jakob Lechner Date: Thu, 28 Nov 2024 00:41:29 +0100 Subject: [PATCH] initial commit --- .cargo/config.toml | 15 + .envrc | 1 + .gitignore | 3 + Cargo.lock | 622 +++++++++++++++++++++++++++++++++++ Cargo.toml | 30 ++ Makefile | 12 + memory.x | 15 + shell.nix | 10 + src/main.rs | 316 ++++++++++++++++++ src/serial_number.rs.example | 1 + 10 files changed, 1025 insertions(+) create mode 100644 .cargo/config.toml create mode 100644 .envrc create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 Makefile create mode 100644 memory.x create mode 100644 shell.nix create mode 100644 src/main.rs create mode 100644 src/serial_number.rs.example diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..01c7fca --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,15 @@ +[target.'cfg(all(target_arch = "arm", target_os = "none"))'] +runner = "probe-rs run --chip RP2040 --protocol swd" +# runner = "elf2uf2-rs -d" + +rustflags = [ + "-C", "linker=flip-link", + "-C", "link-arg=--nmagic", + "-C", "link-arg=-Tlink.x", + "-C", "link-arg=-Tdefmt.x", + "-Z", "trap-unreachable=no", + "-C", "no-vectorize-loops", +] + +[build] +target = "thumbv6m-none-eabi" diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..1d953f4 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use nix diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2105e7c --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/target +/.direnv +/src/serial_number.rs diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..ddcb754 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,622 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "bare-metal" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3" +dependencies = [ + "rustc_version", +] + +[[package]] +name = "bitfield" +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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cortex-m" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ec610d8f49840a5b376c69663b6369e71f4b34484b9b2eb29fb918d92516cb9" +dependencies = [ + "bare-metal", + "bitfield 0.13.2", + "embedded-hal 0.2.7", + "volatile-register", +] + +[[package]] +name = "cortex-m-rt" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "801d4dec46b34c299ccf6b036717ae0fce602faa4f4fe816d9013b9a7c9f5ba6" +dependencies = [ + "cortex-m-rt-macros", +] + +[[package]] +name = "cortex-m-rt-macros" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e37549a379a9e0e6e576fd208ee60394ccb8be963889eebba3ffe0980364f472" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", +] + +[[package]] +name = "crc-any" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a62ec9ff5f7965e4d7280bd5482acd20aadb50d632cf6c1d74493856b011fa73" +dependencies = [ + "debug-helper", +] + +[[package]] +name = "critical-section" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" + +[[package]] +name = "debug-helper" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f578e8e2c440e7297e008bb5486a3a8a194775224bbc23729b0dbdfaeebf162e" + +[[package]] +name = "defmt" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86f6162c53f659f65d00619fe31f14556a6e9f8752ccc4a41bd177ffcf3d6130" +dependencies = [ + "bitflags", + "defmt-macros", +] + +[[package]] +name = "defmt-macros" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d135dd939bad62d7490b0002602d35b358dce5fd9233a709d3c1ef467d4bde6" +dependencies = [ + "defmt-parser", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.89", +] + +[[package]] +name = "defmt-parser" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3983b127f13995e68c1e29071e5d115cd96f215ccb5e6812e3728cd6f92653b3" +dependencies = [ + "thiserror", +] + +[[package]] +name = "defmt-rtt" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bab697b3dbbc1750b7c8b821aa6f6e7f2480b47a99bc057a2ed7b170ebef0c51" +dependencies = [ + "critical-section", + "defmt", +] + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "embedded-dma" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "994f7e5b5cb23521c22304927195f236813053eb9c065dd2226a32ba64695446" +dependencies = [ + "stable_deref_trait", +] + +[[package]] +name = "embedded-hal" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff" +dependencies = [ + "nb 0.1.3", + "void", +] + +[[package]] +name = "embedded-hal" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89" + +[[package]] +name = "embedded-hal-async" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4c685bbef7fe13c3c6dd4da26841ed3980ef33e841cddfa15ce8a8fb3f1884" +dependencies = [ + "embedded-hal 1.0.0", +] + +[[package]] +name = "embedded-hal-nb" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fba4268c14288c828995299e59b12babdbe170f6c6d73731af1b4648142e8605" +dependencies = [ + "embedded-hal 1.0.0", + "nb 1.1.0", +] + +[[package]] +name = "embedded-io" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" + +[[package]] +name = "frunk" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "874b6a17738fc273ec753618bac60ddaeac48cb1d7684c3e7bd472e57a28b817" +dependencies = [ + "frunk_core", + "frunk_derives", +] + +[[package]] +name = "frunk_core" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3529a07095650187788833d585c219761114005d5976185760cf794d265b6a5c" + +[[package]] +name = "frunk_derives" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e99b8b3c28ae0e84b604c75f721c21dc77afb3706076af5e8216d15fd1deaae3" +dependencies = [ + "frunk_proc_macro_helpers", + "quote", + "syn 2.0.89", +] + +[[package]] +name = "frunk_proc_macro_helpers" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05a956ef36c377977e512e227dcad20f68c2786ac7a54dacece3746046fea5ce" +dependencies = [ + "frunk_core", + "proc-macro2", + "quote", + "syn 2.0.89", +] + +[[package]] +name = "fugit" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17186ad64927d5ac8f02c1e77ccefa08ccd9eaa314d5a4772278aa204a22f7e7" +dependencies = [ + "gcd", +] + +[[package]] +name = "gcd" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d758ba1b47b00caf47f24925c0074ecb20d6dfcffe7f6d53395c0465674841a" + +[[package]] +name = "hash32" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" +dependencies = [ + "byteorder", +] + +[[package]] +name = "heapless" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" +dependencies = [ + "hash32", + "stable_deref_trait", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "nb" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f" +dependencies = [ + "nb 1.1.0", +] + +[[package]] +name = "nb" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d" + +[[package]] +name = "num_enum" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" +dependencies = [ + "num_enum_derive 0.5.11", +] + +[[package]] +name = "num_enum" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a015b430d3c108a207fd776d2e2196aaf8b1cf8cf93253e3a097ff3085076a1" +dependencies = [ + "num_enum_derive 0.6.1", +] + +[[package]] +name = "num_enum_derive" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "num_enum_derive" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96667db765a921f7b295ffee8b60472b686a51d4f21c2ee4ffdb94c7013b65a6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", +] + +[[package]] +name = "panic-probe" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4047d9235d1423d66cc97da7d07eddb54d4f154d6c13805c6d0793956f4f25b0" +dependencies = [ + "cortex-m", + "defmt", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pio" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76e09694b50f89f302ed531c1f2a7569f0be5867aee4ab4f8f729bbeec0078e3" +dependencies = [ + "arrayvec", + "num_enum 0.5.11", + "paste", +] + +[[package]] +name = "portable-atomic" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "280dc24453071f1b63954171985a0b0d30058d287960968b9b2aca264c8d4ee6" + +[[package]] +name = "proc-macro-error-attr2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "proc-macro-error2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" +dependencies = [ + "proc-macro-error-attr2", + "proc-macro2", + "quote", + "syn 2.0.89", +] + +[[package]] +name = "proc-macro2" +version = "1.0.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" + +[[package]] +name = "rp-pico" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9342d3ac7011ac688300979e9b52a81f0add1d05feb02868cf94bfee0705b28" +dependencies = [ + "cortex-m-rt", + "fugit", + "rp2040-boot2", + "rp2040-hal", + "usb-device", +] + +[[package]] +name = "rp2040-boot2" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c92f344f63f950ee36cf4080050e4dce850839b9175da38f9d2ffb69b4dbb21" +dependencies = [ + "crc-any", +] + +[[package]] +name = "rp2040-hal" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d11e711940087f2cdff8aeae9f4b902e2014c06a00b39a1092686b81ec973d6f" +dependencies = [ + "bitfield 0.14.0", + "cortex-m", + "critical-section", + "embedded-dma", + "embedded-hal 0.2.7", + "embedded-hal 1.0.0", + "embedded-hal-async", + "embedded-hal-nb", + "embedded-io", + "frunk", + "fugit", + "itertools", + "nb 1.1.0", + "paste", + "pio", + "rand_core", + "rp2040-hal-macros", + "rp2040-pac", + "usb-device", + "vcell", + "void", +] + +[[package]] +name = "rp2040-hal-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86479063e497efe1ae81995ef9071f54fd1c7427e04d6c5b84cde545ff672a5e" +dependencies = [ + "cortex-m-rt", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "rp2040-pac" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83cbcd3f7a0ca7bbe61dc4eb7e202842bee4e27b769a7bf3a4a72fa399d6e404" +dependencies = [ + "cortex-m", + "cortex-m-rt", + "critical-section", + "vcell", +] + +[[package]] +name = "rp2040-usb-ramdisk" +version = "0.1.0" +dependencies = [ + "cortex-m", + "cortex-m-rt", + "defmt", + "defmt-rtt", + "embedded-hal 1.0.0", + "panic-probe", + "rp-pico", + "rp2040-boot2", + "rp2040-hal", + "usb-device", + "usbd-storage", +] + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver", +] + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d46482f1c1c87acd84dea20c1bf5ebff4c757009ed6bf19cfd36fb10e92c4e" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c006c85c7651b3cf2ada4584faa36773bd07bac24acfb39f3c431b36d7e667aa" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f077553d607adc1caf65430528a576c757a71ed73944b66ebb58ef2bbd243568" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", +] + +[[package]] +name = "unicode-ident" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" + +[[package]] +name = "usb-device" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98816b1accafbb09085168b90f27e93d790b4bfa19d883466b5e53315b5f06a6" +dependencies = [ + "defmt", + "heapless", + "portable-atomic", +] + +[[package]] +name = "usbd-storage" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee23ab04c7d76b63c417cf2fa672d720477d2e71c6822ac6e5ceaf526f48f394" +dependencies = [ + "defmt", + "num_enum 0.6.1", + "usb-device", +] + +[[package]] +name = "vcell" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002" + +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + +[[package]] +name = "volatile-register" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de437e2a6208b014ab52972a27e59b33fa2920d3e00fe05026167a1c509d19cc" +dependencies = [ + "vcell", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..0799c3b --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "rp2040-usb-ramdisk" +version = "0.1.0" +edition = "2024" + +[dependencies] +cortex-m = "0.7.7" +cortex-m-rt = { version = "0.7.5", optional = true } +defmt = "0.3.10" +defmt-rtt = "0.4.1" +embedded-hal = "1.0.0" +panic-probe = { version = "0.3.2", features = ["print-defmt"] } +rp-pico = "0.9.0" +rp2040-boot2 = { version = "0.3.0", optional = true } +rp2040-hal = "0.10.2" +usb-device = "0.3.2" +usbd-storage = { version = "1.0.0", features = ["scsi", "bbb", "defmt"] } + +[features] +default = ["boot2", "rt", "critical-section-impl"] +boot2 = ["rp2040-boot2"] +rt = ["cortex-m-rt","rp2040-hal/rt"] +critical-section-impl = ["rp2040-hal/critical-section-impl"] +cortex-m-rt = ["dep:cortex-m-rt"] + +[profile.dev] +panic = "abort" + +[profile.release] +panic = "abort" diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..de09fbc --- /dev/null +++ b/Makefile @@ -0,0 +1,12 @@ +BIN=rp2040-usb-ramdisk + +.PHONY: flash +flash: target/thumbv6m-none-eabi/release/$(BIN) + elf2uf2-rs --deploy $< + +target/thumbv6m-none-eabi/release/$(BIN): src/main.rs src/serial_number.rs Cargo.toml + cargo build --target thumbv6m-none-eabi --release + +target/thumbv6m-none-eabi/release/$(BIN).uf2: target/thumbv6m-none-eabi/release/$(BIN) + elf2uf2-rs $< $@ + diff --git a/memory.x b/memory.x new file mode 100644 index 0000000..070eac7 --- /dev/null +++ b/memory.x @@ -0,0 +1,15 @@ +MEMORY { + BOOT2 : ORIGIN = 0x10000000, LENGTH = 0x100 + FLASH : ORIGIN = 0x10000100, LENGTH = 2048K - 0x100 + RAM : ORIGIN = 0x20000000, LENGTH = 256K +} + +EXTERN(BOOT2_FIRMWARE) + +SECTIONS { + /* ### Boot loader */ + .boot2 ORIGIN(BOOT2) : + { + KEEP(*(.boot2)); + } > BOOT2 +} INSERT BEFORE .text; \ No newline at end of file diff --git a/shell.nix b/shell.nix new file mode 100644 index 0000000..d866bbc --- /dev/null +++ b/shell.nix @@ -0,0 +1,10 @@ +{ pkgs ? import {} }: +pkgs.mkShell { + nativeBuildInputs = with pkgs; [ + elf2uf2-rs + gcc + gnumake + probe-rs + rustup + ]; +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..f6f199d --- /dev/null +++ b/src/main.rs @@ -0,0 +1,316 @@ +#![no_std] +#![no_main] + +use rp_pico::entry; + +use defmt as _; +use defmt_rtt as _; +use panic_probe as _; + +use embedded_hal::digital::StatefulOutputPin; + +use rp_pico::hal::pac; + +use rp_pico::hal; + +use usb_device::{class_prelude::*, prelude::*}; + +use usbd_storage::subclass::scsi::{Scsi, ScsiCommand}; +use usbd_storage::subclass::Command; +use usbd_storage::transport::bbb::{BulkOnly, BulkOnlyError}; +use usbd_storage::transport::TransportError; + +mod serial_number; + +const DISK_BLOCK_SIZE: u32 = 512; +const DISK_BLOCK_NUM: u32 = 256; + +const USB_PACKET_SIZE: u16 = 64; // 8,16,32,64 +const MAX_LUN: u8 = 0; // max 0x0F + +const INQUIRY_RESPONSE: [u8; 36] = { + let vendor: [u8; 8] = *b"jalr\0\0\0\0"; + let model: [u8; 16] = *b"USB RAM disk\0\0\0\0"; + let rev: [u8; 4] = *b"1312"; + [ + 0x00, // periph qualifier, periph device type + 0x80, // Removable + 0x04, // SPC-2 compliance + 0x02, // NormACA, HiSu, Response data format + 0x20, // 36 bytes in total + 0x00, // additional fields, none set + 0x00, // additional fields, none set + 0x00, // additional fields, none set + vendor[0], vendor[1], vendor[2], vendor[3], + vendor[4], vendor[5], vendor[6], vendor[7], + model[0], model[1], model[2], model[3], + model[4], model[5], model[6], model[7], + model[8], model[9], model[10], model[11], + model[12], model[13], model[14], model[15], + rev[0], rev[1], rev[2], rev[3] + ] +}; + +#[derive(Default)] +struct State { + storage_offset: usize, + sense_key: Option, + sense_key_code: Option, + sense_qualifier: Option, +} + +impl State { + fn reset(&mut self) { + self.storage_offset = 0; + self.sense_key = None; + self.sense_key_code = None; + self.sense_qualifier = None; + } +} + +#[entry] +fn main() -> ! { + let mut pac = pac::Peripherals::take().unwrap(); + + let mut watchdog = hal::Watchdog::new(pac.WATCHDOG); + + let clocks = hal::clocks::init_clocks_and_plls( + rp_pico::XOSC_CRYSTAL_FREQ, + pac.XOSC, + pac.CLOCKS, + pac.PLL_SYS, + pac.PLL_USB, + &mut pac.RESETS, + &mut watchdog, + ) + .ok() + .unwrap(); + + let timer = hal::timer::Timer::new(pac.TIMER, &mut pac.RESETS, &clocks); + + let sio = hal::Sio::new(pac.SIO); + let pins = rp_pico::Pins::new( + pac.IO_BANK0, + pac.PADS_BANK0, + sio.gpio_bank0, + &mut pac.RESETS, + ); + + let mut led_pin = pins.led.into_push_pull_output(); + + let usb_bus = UsbBusAllocator::new(hal::usb::UsbBus::new( + pac.USBCTRL_REGS, + pac.USBCTRL_DPRAM, + clocks.usb_clock, + true, + &mut pac.RESETS, + )); + + let mut usb_transport_buf: [u8; 512] = [0; 512]; + let mut scsi = usbd_storage::subclass::scsi::Scsi::new(&usb_bus, USB_PACKET_SIZE, MAX_LUN, usb_transport_buf.as_mut_slice()).unwrap(); + + let mut usb_device = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0xcafe, 0x4002)) + .strings(&[StringDescriptors::new(LangID::EN) + .manufacturer("jalr") + .product("RAM Mass Storage") + .serial_number(serial_number::SERIAL)]) + .unwrap() + .device_class(0x08) + .self_powered(false) + .build(); + + let mut start_time: u64 = 0; + + let mut storage: [u8; (DISK_BLOCK_SIZE * DISK_BLOCK_NUM) as usize] = [0u8; (DISK_BLOCK_SIZE * DISK_BLOCK_NUM) as usize]; + + // Initialize Boot Sector (Block 0) + storage[0x000..0x03E].copy_from_slice(&[ + /* 000 */ 0xEB, 0x3C, 0x90, b'm', b'k', b'f', b's', b'.', b'f', b'a', b't', 0x00, 0x02, 0x04, 0x01, 0x00, + /* 010 */ 0x02, 0x00, 0x02, (DISK_BLOCK_NUM & 0xff) as u8, + (DISK_BLOCK_NUM >> 8) as u8, + 0xF8, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 020 */ 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x29, 0xDE, 0xAD, 0xBE, 0xEF, b'R', b'A', b'M', b'_', b'U', + /* 030 */ b'S', b'B', b' ', b' ', b' ', b' ', b'F', b'A', b'T', b'1', b'2', b' ', b' ', b' ' + ]); + storage[0x1FE..0x200].copy_from_slice(&[0x55, 0xAA]); + storage[0x200..0x203].copy_from_slice(&[0xF8, 0xFF, 0xFF]); + storage[0x400..0x403].copy_from_slice(&[0xF8, 0xFF, 0xFF]); + storage[0x600..0x61A].copy_from_slice(&[ + /* 600 */ b'R', b'A', b'M', b'_', b'U', b'S', b'B', b' ', b' ', b' ', b' ', 0x08, 0x00, 0x00, 0xF9, 0x98, + /* 610 */ 0x3A, 0x58, 0x3A, 0x58, 0x00, 0x00, 0xF9, 0x98, 0x3A, 0x58 + ]); + + let mut state: State = State { + storage_offset: 0, + sense_key: None, + sense_key_code: None, + sense_qualifier: None, + }; + + defmt::info!("entering main loop"); + + loop { + let interval = match (usb_device.state(), led_pin.is_set_high().unwrap_or_else(|_| false)) { + // The USB device has just been created or reset. + (UsbDeviceState::Default, true) => 100_000, + (UsbDeviceState::Default, false) => 100_000, + // The USB device has received an address from the host. + (UsbDeviceState::Addressed, true) => 250_000, + (UsbDeviceState::Addressed, false) => 250_000, + // The USB device has been configured and is fully functional. + (UsbDeviceState::Configured, true) => 1_000_000, + (UsbDeviceState::Configured, false) => 0, + // The USB device has been suspended by the host or it has been unplugged from the USB bus. + (UsbDeviceState::Suspend, true) => 25_000, + (UsbDeviceState::Suspend, false) => 2_000_000, + }; + if timer.get_counter().ticks() - start_time > interval { + let _ = led_pin.toggle(); + start_time = timer.get_counter().ticks(); + } + + if !usb_device.poll(&mut [&mut scsi]) { + continue; + } + + if matches!(usb_device.state(), UsbDeviceState::Default) { + state.reset(); + } + + let _ = scsi.poll(|command| { + if let Err(err) = process_command(command, &mut state, &mut storage) { + defmt::error!("Got error: {}", err); + } + }); + } +} + +fn process_command( + mut command: Command>>, + state: &mut State, + storage: &mut [u8; (DISK_BLOCK_SIZE * DISK_BLOCK_NUM) as usize] +) -> Result<(), TransportError> { + defmt::info!("Handling: {}", command.kind); + + match command.kind { + ScsiCommand::TestUnitReady { .. } => { + command.pass(); + } + ScsiCommand::Inquiry { .. } => { + command.try_write_data_all(&INQUIRY_RESPONSE)?; + command.pass(); + } + ScsiCommand::RequestSense { .. } => { + command.try_write_data_all(&[ + 0x70, // RESPONSE CODE. Set to 70h for information on current errors + 0x00, // obsolete + state.sense_key.unwrap_or(0), // Bits 3..0: SENSE KEY. Contains information describing the error. + 0x00, + 0x00, + 0x00, + 0x00, // INFORMATION. Device-specific or command-specific information. + 0x00, // ADDITIONAL SENSE LENGTH. + 0x00, + 0x00, + 0x00, + 0x00, // COMMAND-SPECIFIC INFORMATION + state.sense_key_code.unwrap_or(0), // ASC + state.sense_qualifier.unwrap_or(0), // ASCQ + 0x00, + 0x00, + 0x00, + 0x00, + ])?; + state.reset(); + command.pass(); + }, + ScsiCommand::ReadCapacity10 { .. } => { + let mut data = [0u8; 8]; + let _ = &mut data[0..4].copy_from_slice(&u32::to_be_bytes(DISK_BLOCK_NUM - 1)); + let _ = &mut data[4..8].copy_from_slice(&u32::to_be_bytes(DISK_BLOCK_SIZE)); + command.try_write_data_all(&data)?; + command.pass(); + } + ScsiCommand::ReadCapacity16 { .. } => { + let mut data = [0u8; 16]; + let _ = &mut data[0..8].copy_from_slice(&u32::to_be_bytes(DISK_BLOCK_NUM - 1)); + let _ = &mut data[8..12].copy_from_slice(&u32::to_be_bytes(DISK_BLOCK_SIZE)); + command.try_write_data_all(&data)?; + command.pass(); + } + ScsiCommand::ReadFormatCapacities { .. } => { + let mut data = [0u8; 12]; + let _ = &mut data[0..4].copy_from_slice(&[ + 0x00, 0x00, 0x00, 0x08, // capacity list length + ]); + let _ = &mut data[4..8].copy_from_slice(&u32::to_be_bytes(DISK_BLOCK_NUM as u32)); // number of blocks + data[8] = 0x01; //unformatted media + let block_length_be = u32::to_be_bytes(DISK_BLOCK_SIZE); + data[9] = block_length_be[1]; + data[10] = block_length_be[2]; + data[11] = block_length_be[3]; + + command.try_write_data_all(&data)?; + command.pass(); + } + ScsiCommand::Read { lba, len } => { + let lba = lba as u32; + let len = len as u32; + if state.storage_offset != (len * DISK_BLOCK_SIZE) as usize { + let start = (DISK_BLOCK_SIZE * lba) as usize + state.storage_offset; + let end = (DISK_BLOCK_SIZE * lba) as usize + (DISK_BLOCK_SIZE * len) as usize; + + // Uncomment this in order to push data in chunks smaller than a USB packet. + // let end = min(start + USB_PACKET_SIZE as usize - 1, end); + + defmt::info!("Data transfer >>>>>>>> [{}..{}]", start, end); + let count = command.write_data(&mut storage[start..end])?; + state.storage_offset += count; + } else { + command.pass(); + state.storage_offset = 0; + } + }, + ScsiCommand::Write { lba, len } => { + let lba = lba as u32; + let len = len as u32; + if state.storage_offset != (len * DISK_BLOCK_SIZE) as usize { + let start = (DISK_BLOCK_SIZE * lba) as usize + state.storage_offset; + let end = (DISK_BLOCK_SIZE * lba) as usize + (DISK_BLOCK_SIZE * len) as usize; + defmt::info!("Data transfer <<<<<<<< [{}..{}]", start, end); + let count = command.read_data(&mut storage[start..end])?; + state.storage_offset += count; + + if state.storage_offset == (len * DISK_BLOCK_SIZE) as usize { + command.pass(); + state.storage_offset = 0; + } + } else { + command.pass(); + state.storage_offset = 0; + } + }, + ScsiCommand::ModeSense6 { .. } => { + command.try_write_data_all(&[ + 0x03, // number of bytes that follow + 0x00, // the media type is SBC + 0x00, // not write-protected, no cache-control bytes support + 0x00, // no mode-parameter block descriptors + ])?; + command.pass(); + } + ScsiCommand::ModeSense10 { .. } => { + command.try_write_data_all(&[0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])?; + command.pass(); + } + ref unknown_scsi_kind => { + defmt::error!("Unknown SCSI command: {}", unknown_scsi_kind); + state.sense_key.replace(0x05); // illegal request Sense Key + state.sense_key_code.replace(0x20); // Invalid command operation ASC + state.sense_qualifier.replace(0x00); // Invalid command operation ASCQ + command.fail(); + } + } + + Ok(()) +} diff --git a/src/serial_number.rs.example b/src/serial_number.rs.example new file mode 100644 index 0000000..e61eae8 --- /dev/null +++ b/src/serial_number.rs.example @@ -0,0 +1 @@ +pub const SERIAL: &str = "example-4711";