Initial commit, some working features
Signed-off-by: Keenan Tims <ktims@gotroot.ca>
This commit is contained in:
commit
ee007da5d4
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/target
|
823
Cargo.lock
generated
Normal file
823
Cargo.lock
generated
Normal file
@ -0,0 +1,823 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ahash"
|
||||||
|
version = "0.8.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"once_cell",
|
||||||
|
"version_check",
|
||||||
|
"zerocopy",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aho-corasick"
|
||||||
|
version = "1.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstream"
|
||||||
|
version = "0.6.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44"
|
||||||
|
dependencies = [
|
||||||
|
"anstyle",
|
||||||
|
"anstyle-parse",
|
||||||
|
"anstyle-query",
|
||||||
|
"anstyle-wincon",
|
||||||
|
"colorchoice",
|
||||||
|
"utf8parse",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle-parse"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140"
|
||||||
|
dependencies = [
|
||||||
|
"utf8parse",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle-query"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b"
|
||||||
|
dependencies = [
|
||||||
|
"windows-sys 0.48.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle-wincon"
|
||||||
|
version = "3.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628"
|
||||||
|
dependencies = [
|
||||||
|
"anstyle",
|
||||||
|
"windows-sys 0.48.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anyhow"
|
||||||
|
version = "1.0.75"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "autocfg"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "1.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "2.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "byteorder"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cc"
|
||||||
|
version = "1.0.83"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap"
|
||||||
|
version = "4.4.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ac495e00dcec98c83465d5ad66c5c4fabd652fd6686e7c6269b117e729a6f17b"
|
||||||
|
dependencies = [
|
||||||
|
"clap_builder",
|
||||||
|
"clap_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap-num"
|
||||||
|
version = "1.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "488557e97528174edaa2ee268b23a809e0c598213a4bbcb4f34575a46fda147e"
|
||||||
|
dependencies = [
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_builder"
|
||||||
|
version = "4.4.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c77ed9a32a62e6ca27175d00d29d05ca32e396ea1eb5fb01d8256b669cec7663"
|
||||||
|
dependencies = [
|
||||||
|
"anstream",
|
||||||
|
"anstyle",
|
||||||
|
"clap_lex",
|
||||||
|
"strsim",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_derive"
|
||||||
|
version = "4.4.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442"
|
||||||
|
dependencies = [
|
||||||
|
"heck",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.38",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_lex"
|
||||||
|
version = "0.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "colorchoice"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "console"
|
||||||
|
version = "0.15.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c926e00cc70edefdc64d3a5ff31cc65bb97a3460097762bd23afb4d8145fccf8"
|
||||||
|
dependencies = [
|
||||||
|
"encode_unicode",
|
||||||
|
"lazy_static",
|
||||||
|
"libc",
|
||||||
|
"unicode-width",
|
||||||
|
"windows-sys 0.45.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crc32fast"
|
||||||
|
version = "1.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "encode_unicode"
|
||||||
|
version = "0.3.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "env_logger"
|
||||||
|
version = "0.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0"
|
||||||
|
dependencies = [
|
||||||
|
"humantime",
|
||||||
|
"is-terminal",
|
||||||
|
"log",
|
||||||
|
"regex",
|
||||||
|
"termcolor",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "equivalent"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "errno"
|
||||||
|
version = "0.3.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ac3e13f66a2f95e32a39eaa81f6b95d42878ca0e1db0c7543723dfe12557e860"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"windows-sys 0.48.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.14.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156"
|
||||||
|
dependencies = [
|
||||||
|
"ahash",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "heck"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hermit-abi"
|
||||||
|
version = "0.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hidapi"
|
||||||
|
version = "2.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "723777263b0dcc5730aec947496bd8c3940ba63c15f5633b288cc615f4f6af79"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"libc",
|
||||||
|
"pkg-config",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "humantime"
|
||||||
|
version = "2.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ihex"
|
||||||
|
version = "3.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "365a784774bb381e8c19edb91190a90d7f2625e057b55de2bc0f6b57bc779ff2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "indexmap"
|
||||||
|
version = "2.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f"
|
||||||
|
dependencies = [
|
||||||
|
"equivalent",
|
||||||
|
"hashbrown",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "indicatif"
|
||||||
|
version = "0.17.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fb28741c9db9a713d93deb3bb9515c20788cef5815265bee4980e87bde7e0f25"
|
||||||
|
dependencies = [
|
||||||
|
"console",
|
||||||
|
"instant",
|
||||||
|
"number_prefix",
|
||||||
|
"portable-atomic",
|
||||||
|
"unicode-width",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "instant"
|
||||||
|
version = "0.1.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "is-terminal"
|
||||||
|
version = "0.4.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b"
|
||||||
|
dependencies = [
|
||||||
|
"hermit-abi",
|
||||||
|
"rustix",
|
||||||
|
"windows-sys 0.48.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lazy_static"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.149"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "linux-raw-sys"
|
||||||
|
version = "0.4.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "log"
|
||||||
|
version = "0.4.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lpc55prog"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"bitflags 1.3.2",
|
||||||
|
"byteorder",
|
||||||
|
"clap",
|
||||||
|
"clap-num",
|
||||||
|
"console",
|
||||||
|
"env_logger",
|
||||||
|
"hidapi",
|
||||||
|
"ihex",
|
||||||
|
"indicatif",
|
||||||
|
"log",
|
||||||
|
"nom",
|
||||||
|
"num_enum",
|
||||||
|
"object",
|
||||||
|
"strum",
|
||||||
|
"strum_macros",
|
||||||
|
"uuid",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.6.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "minimal-lexical"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nom"
|
||||||
|
version = "7.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
"minimal-lexical",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-traits"
|
||||||
|
version = "0.2.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num_enum"
|
||||||
|
version = "0.5.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9"
|
||||||
|
dependencies = [
|
||||||
|
"num_enum_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num_enum_derive"
|
||||||
|
version = "0.5.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro-crate",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 1.0.109",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "number_prefix"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "object"
|
||||||
|
version = "0.32.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0"
|
||||||
|
dependencies = [
|
||||||
|
"crc32fast",
|
||||||
|
"hashbrown",
|
||||||
|
"indexmap",
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell"
|
||||||
|
version = "1.18.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pkg-config"
|
||||||
|
version = "0.3.27"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "portable-atomic"
|
||||||
|
version = "1.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3bccab0e7fd7cc19f820a1c8c91720af652d0c88dc9664dd72aef2614f04af3b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro-crate"
|
||||||
|
version = "1.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919"
|
||||||
|
dependencies = [
|
||||||
|
"once_cell",
|
||||||
|
"toml_edit",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.69"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.33"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex"
|
||||||
|
version = "1.10.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-automata",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-automata"
|
||||||
|
version = "0.4.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-syntax"
|
||||||
|
version = "0.8.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustix"
|
||||||
|
version = "0.38.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.4.1",
|
||||||
|
"errno",
|
||||||
|
"libc",
|
||||||
|
"linux-raw-sys",
|
||||||
|
"windows-sys 0.48.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustversion"
|
||||||
|
version = "1.0.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strsim"
|
||||||
|
version = "0.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strum"
|
||||||
|
version = "0.24.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strum_macros"
|
||||||
|
version = "0.24.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59"
|
||||||
|
dependencies = [
|
||||||
|
"heck",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"rustversion",
|
||||||
|
"syn 1.0.109",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[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.38"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "termcolor"
|
||||||
|
version = "1.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6093bad37da69aab9d123a8091e4be0aa4a03e4d601ec641c327398315f62b64"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_datetime"
|
||||||
|
version = "0.6.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_edit"
|
||||||
|
version = "0.19.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421"
|
||||||
|
dependencies = [
|
||||||
|
"indexmap",
|
||||||
|
"toml_datetime",
|
||||||
|
"winnow",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-width"
|
||||||
|
version = "0.1.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "utf8parse"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "uuid"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "88ad59a7560b41a70d191093a945f0b87bc1deeda46fb237479708a1d6b6cdfc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "version_check"
|
||||||
|
version = "0.9.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi"
|
||||||
|
version = "0.3.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-i686-pc-windows-gnu",
|
||||||
|
"winapi-x86_64-pc-windows-gnu",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-i686-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-util"
|
||||||
|
version = "0.1.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596"
|
||||||
|
dependencies = [
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-x86_64-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.45.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets 0.42.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.48.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets 0.48.5",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-targets"
|
||||||
|
version = "0.42.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
|
||||||
|
dependencies = [
|
||||||
|
"windows_aarch64_gnullvm 0.42.2",
|
||||||
|
"windows_aarch64_msvc 0.42.2",
|
||||||
|
"windows_i686_gnu 0.42.2",
|
||||||
|
"windows_i686_msvc 0.42.2",
|
||||||
|
"windows_x86_64_gnu 0.42.2",
|
||||||
|
"windows_x86_64_gnullvm 0.42.2",
|
||||||
|
"windows_x86_64_msvc 0.42.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-targets"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
|
||||||
|
dependencies = [
|
||||||
|
"windows_aarch64_gnullvm 0.48.5",
|
||||||
|
"windows_aarch64_msvc 0.48.5",
|
||||||
|
"windows_i686_gnu 0.48.5",
|
||||||
|
"windows_i686_msvc 0.48.5",
|
||||||
|
"windows_x86_64_gnu 0.48.5",
|
||||||
|
"windows_x86_64_gnullvm 0.48.5",
|
||||||
|
"windows_x86_64_msvc 0.48.5",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_gnullvm"
|
||||||
|
version = "0.42.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_gnullvm"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_msvc"
|
||||||
|
version = "0.42.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_msvc"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnu"
|
||||||
|
version = "0.42.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnu"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_msvc"
|
||||||
|
version = "0.42.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_msvc"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnu"
|
||||||
|
version = "0.42.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnu"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnullvm"
|
||||||
|
version = "0.42.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnullvm"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_msvc"
|
||||||
|
version = "0.42.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_msvc"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winnow"
|
||||||
|
version = "0.5.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "176b6138793677221d420fd2f0aeeced263f197688b36484660da767bca2fa32"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerocopy"
|
||||||
|
version = "0.7.24"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "092cd76b01a033a9965b9097da258689d9e17c69ded5dcf41bca001dd20ebc6d"
|
||||||
|
dependencies = [
|
||||||
|
"zerocopy-derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerocopy-derive"
|
||||||
|
version = "0.7.24"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a13a20a7c6a90e2034bcc65495799da92efcec6a8dd4f3fcb6f7a48988637ead"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.38",
|
||||||
|
]
|
30
Cargo.toml
Normal file
30
Cargo.toml
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
[package]
|
||||||
|
name = "lpc55prog"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
description = "A flash programmer for NXP LP55xx microcontrollers' ISP boot rom"
|
||||||
|
authors = ["Keenan Tims <ktims@gotroot.ca>"]
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
strip="debuginfo"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
anyhow = "1.0.75"
|
||||||
|
bitflags = "1.3.2"
|
||||||
|
byteorder = "1.5.0"
|
||||||
|
clap = { version = "4.4.6", features = ["derive"] }
|
||||||
|
clap-num = "1.0.2"
|
||||||
|
console = "0.15.7"
|
||||||
|
env_logger = "0.10.0"
|
||||||
|
hidapi = "2.1.3"
|
||||||
|
ihex = "3.0.0"
|
||||||
|
indicatif = "0.17.7"
|
||||||
|
log = "0.4.20"
|
||||||
|
nom = "7.1.3"
|
||||||
|
num_enum = "0.5.11"
|
||||||
|
object = { version = "0.32.1", default-features = false, features = ["elf", "write_std", "read_core"] }
|
||||||
|
strum = "0.24.1"
|
||||||
|
strum_macros = "0.24.3"
|
||||||
|
uuid = "1.4.1"
|
194
src/file.rs
Normal file
194
src/file.rs
Normal file
@ -0,0 +1,194 @@
|
|||||||
|
extern crate object;
|
||||||
|
|
||||||
|
use clap::ValueEnum;
|
||||||
|
use log::debug;
|
||||||
|
use log::warn;
|
||||||
|
use object::elf;
|
||||||
|
use object::Endianness;
|
||||||
|
|
||||||
|
use ihex;
|
||||||
|
use object::write::elf::{FileHeader, SectionHeader};
|
||||||
|
|
||||||
|
// Perhaps should refactor so we don't have UI ValueEnum here?
|
||||||
|
#[derive(Debug, Clone, ValueEnum)]
|
||||||
|
pub enum MemoryFileType {
|
||||||
|
Elf,
|
||||||
|
Ihex,
|
||||||
|
Bin,
|
||||||
|
}
|
||||||
|
|
||||||
|
const M33_FLAGS: u32 = elf::EF_ARM_EABI_VER5 | elf::EF_ARM_ABI_FLOAT_HARD; // processor has hard float, even if code may not use it
|
||||||
|
|
||||||
|
struct ElfWriter;
|
||||||
|
struct IhexWriter;
|
||||||
|
struct BinWriter;
|
||||||
|
|
||||||
|
/// Build a new object to manage writing type T
|
||||||
|
impl MemoryFileType {
|
||||||
|
pub fn mem_writer(&self) -> Box<dyn MemoryWriter> {
|
||||||
|
match *self {
|
||||||
|
MemoryFileType::Elf => Box::new(ElfWriter),
|
||||||
|
MemoryFileType::Ihex => Box::new(IhexWriter),
|
||||||
|
MemoryFileType::Bin => Box::new(BinWriter),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn mem_writer_from_filename(filename: &str) -> Box<dyn MemoryWriter> {
|
||||||
|
let filename = filename.to_lowercase();
|
||||||
|
let (_, extension) = filename.rsplit_once('.').unwrap_or(("", ""));
|
||||||
|
match extension {
|
||||||
|
"elf" => Box::new(ElfWriter),
|
||||||
|
"hex" | "ihex" => Box::new(IhexWriter),
|
||||||
|
"bin" | "raw" => Box::new(BinWriter),
|
||||||
|
_ => {
|
||||||
|
warn!("Can't determine an appropriate output type based on filename {}, writing as raw binary", filename);
|
||||||
|
Box::new(BinWriter)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An object that can write a block of memory to a file
|
||||||
|
pub trait MemoryWriter {
|
||||||
|
fn write_mem(
|
||||||
|
&mut self,
|
||||||
|
output: &mut dyn std::io::Write,
|
||||||
|
addr: u32,
|
||||||
|
data: &[u8],
|
||||||
|
) -> Result<(), Box<dyn std::error::Error>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
// object is not well documented, see how this API must be used here:
|
||||||
|
// https://github.com/gimli-rs/object/blob/master/src/write/elf/object.rs#L217
|
||||||
|
impl MemoryWriter for ElfWriter {
|
||||||
|
fn write_mem(
|
||||||
|
&mut self,
|
||||||
|
output: &mut dyn std::io::Write,
|
||||||
|
addr: u32,
|
||||||
|
data: &[u8],
|
||||||
|
) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
debug!("Writing as ELF with base address 0x{:x}", addr);
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
|
||||||
|
let mut writer = object::write::elf::Writer::new(Endianness::Little, false, &mut buf);
|
||||||
|
|
||||||
|
// Calculating offsets
|
||||||
|
|
||||||
|
writer.reserve_file_header();
|
||||||
|
|
||||||
|
let _text_id = writer.reserve_section_index();
|
||||||
|
let data_ofs = writer.reserve(data.len(), 4);
|
||||||
|
let text_name = writer.add_section_name(".text".as_bytes());
|
||||||
|
|
||||||
|
writer.reserve_null_symbol_index();
|
||||||
|
let symtab_num_local = writer.symbol_count();
|
||||||
|
writer.reserve_symtab_section_index();
|
||||||
|
writer.reserve_symtab();
|
||||||
|
writer.reserve_symtab_shndx();
|
||||||
|
writer.reserve_strtab_section_index();
|
||||||
|
writer.reserve_strtab();
|
||||||
|
writer.reserve_shstrtab_section_index();
|
||||||
|
writer.reserve_shstrtab();
|
||||||
|
writer.reserve_section_headers();
|
||||||
|
|
||||||
|
// Writing
|
||||||
|
|
||||||
|
writer.write_file_header(&FileHeader {
|
||||||
|
abi_version: elf::EV_CURRENT,
|
||||||
|
os_abi: elf::ELFOSABI_NONE,
|
||||||
|
e_type: elf::ET_EXEC,
|
||||||
|
e_machine: elf::EM_ARM,
|
||||||
|
e_entry: addr.into(),
|
||||||
|
e_flags: M33_FLAGS,
|
||||||
|
// e_ident: elf::Ident,
|
||||||
|
// e_type: elf::ET_EXEC,
|
||||||
|
// e_entry: addr,
|
||||||
|
// e_flags: elf::EF_ARM_ABI_FLOAT_HARD | elf::EF_ARM_EABI_VER5,
|
||||||
|
// e_machine: elf::EM_ARM,
|
||||||
|
// e_version: elf::EV_CURRENT,
|
||||||
|
})?;
|
||||||
|
writer.write_align(4);
|
||||||
|
writer.write(&data);
|
||||||
|
writer.write_null_symbol();
|
||||||
|
writer.write_symtab_shndx();
|
||||||
|
writer.write_strtab();
|
||||||
|
|
||||||
|
writer.write_shstrtab();
|
||||||
|
writer.write_null_section_header();
|
||||||
|
|
||||||
|
writer.write_section_header(&SectionHeader {
|
||||||
|
name: Some(text_name),
|
||||||
|
sh_type: elf::SHT_PROGBITS,
|
||||||
|
sh_flags: (elf::SHF_ALLOC | elf::SHF_EXECINSTR).into(),
|
||||||
|
sh_addr: addr.into(),
|
||||||
|
sh_offset: data_ofs as u64,
|
||||||
|
sh_size: data.len() as u64,
|
||||||
|
sh_link: elf::SHN_UNDEF.into(),
|
||||||
|
sh_info: 0,
|
||||||
|
sh_addralign: 4,
|
||||||
|
sh_entsize: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
writer.write_symtab_section_header(symtab_num_local);
|
||||||
|
writer.write_symtab_shndx_section_header();
|
||||||
|
writer.write_strtab_section_header();
|
||||||
|
writer.write_shstrtab_section_header();
|
||||||
|
|
||||||
|
output.write_all(&buf)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
// let triple = target_lexicon::Triple {
|
||||||
|
// architecture: Architecture::Arm(target_lexicon::ArmArchitecture::Armv8mBase),
|
||||||
|
// vendor: Vendor::Unknown,
|
||||||
|
// operating_system: OperatingSystem::None_,
|
||||||
|
// environment: Environment::Eabihf,
|
||||||
|
// binary_format: BinaryFormat::Elf,
|
||||||
|
// };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MemoryWriter for IhexWriter {
|
||||||
|
fn write_mem(
|
||||||
|
&mut self,
|
||||||
|
output: &mut dyn std::io::Write,
|
||||||
|
addr: u32,
|
||||||
|
data: &[u8],
|
||||||
|
) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let mut records = Vec::new();
|
||||||
|
|
||||||
|
const BLOCK_SIZE: u32 = u16::MAX as u32 + 1;
|
||||||
|
const CHUNK_SIZE: u32 = 16;
|
||||||
|
|
||||||
|
for (block, data) in data.chunks(BLOCK_SIZE as usize).enumerate() {
|
||||||
|
records.push(ihex::Record::ExtendedLinearAddress(
|
||||||
|
(((addr + block as u32 * BLOCK_SIZE) & 0xffff0000) >> 16)
|
||||||
|
.try_into()
|
||||||
|
.unwrap(),
|
||||||
|
));
|
||||||
|
records.extend(
|
||||||
|
data.chunks(CHUNK_SIZE as usize)
|
||||||
|
.enumerate()
|
||||||
|
.map(|(offset, chunk)| ihex::Record::Data {
|
||||||
|
offset: ((offset as u32 * CHUNK_SIZE) & 0xffff).try_into().unwrap(),
|
||||||
|
value: chunk.to_vec(),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
records.push(ihex::Record::EndOfFile {});
|
||||||
|
|
||||||
|
let obj = ihex::create_object_file_representation(&records)?;
|
||||||
|
|
||||||
|
Ok(output.write_all(obj.as_bytes())?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MemoryWriter for BinWriter {
|
||||||
|
fn write_mem(
|
||||||
|
&mut self,
|
||||||
|
output: &mut dyn std::io::Write,
|
||||||
|
_addr: u32, // raw bin writer doesn't care about address
|
||||||
|
data: &[u8],
|
||||||
|
) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
Ok(output.write_all(data)?)
|
||||||
|
}
|
||||||
|
}
|
417
src/main.rs
Normal file
417
src/main.rs
Normal file
@ -0,0 +1,417 @@
|
|||||||
|
use core::fmt;
|
||||||
|
use std::{
|
||||||
|
ffi::CString,
|
||||||
|
fs::File,
|
||||||
|
fs::{self, OpenOptions},
|
||||||
|
io::BufWriter,
|
||||||
|
os::unix::prelude::OsStrExt,
|
||||||
|
path::PathBuf,
|
||||||
|
};
|
||||||
|
|
||||||
|
use anyhow::{anyhow, Context, Error};
|
||||||
|
use clap_num::maybe_hex;
|
||||||
|
use env_logger::Env;
|
||||||
|
use log::{debug, error, info, log_enabled, warn, Level};
|
||||||
|
|
||||||
|
use hidapi::{DeviceInfo, HidApi};
|
||||||
|
use object::Object;
|
||||||
|
|
||||||
|
use crate::file::{MemoryFileType, MemoryWriter};
|
||||||
|
use clap::{
|
||||||
|
builder::{TypedValueParser, ValueParserFactory},
|
||||||
|
error::ErrorKind,
|
||||||
|
ArgAction, Args, Parser, Subcommand, ValueEnum,
|
||||||
|
};
|
||||||
|
use console::style;
|
||||||
|
use indicatif::{HumanBytes, ProgressBar, ProgressStyle};
|
||||||
|
|
||||||
|
pub mod file;
|
||||||
|
pub mod packet;
|
||||||
|
pub mod protocol;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct NoDevicesError;
|
||||||
|
impl std::error::Error for NoDevicesError {
|
||||||
|
fn cause(&self) -> Option<&dyn std::error::Error> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl fmt::Display for NoDevicesError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.write_str("No devices found")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
struct VidPid {
|
||||||
|
vid: u16,
|
||||||
|
pid: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct VidPidParser;
|
||||||
|
|
||||||
|
impl TypedValueParser for VidPidParser {
|
||||||
|
type Value = VidPid;
|
||||||
|
fn parse_ref(
|
||||||
|
&self,
|
||||||
|
cmd: &clap::builder::Command,
|
||||||
|
_: Option<&clap::builder::Arg>,
|
||||||
|
value: &std::ffi::OsStr,
|
||||||
|
) -> Result<Self::Value, clap::error::Error> {
|
||||||
|
let value = value
|
||||||
|
.to_str()
|
||||||
|
.ok_or(clap::Error::new(ErrorKind::InvalidUtf8).with_cmd(cmd))?;
|
||||||
|
if let Some((raw_vid, raw_pid)) = value.split_once(':') {
|
||||||
|
if let (Ok(vid), Ok(pid)) = (
|
||||||
|
u16::from_str_radix(raw_vid, 16),
|
||||||
|
u16::from_str_radix(raw_pid, 16),
|
||||||
|
) {
|
||||||
|
return Ok(VidPid { vid, pid });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(clap::Error::new(ErrorKind::InvalidValue).with_cmd(cmd))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ValueParserFactory for VidPid {
|
||||||
|
type Parser = VidPidParser;
|
||||||
|
fn value_parser() -> Self::Parser {
|
||||||
|
VidPidParser
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Parser, Debug)]
|
||||||
|
#[command(author, version, about)]
|
||||||
|
struct Cli {
|
||||||
|
#[command(subcommand)]
|
||||||
|
command: Commands,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Args, Debug)]
|
||||||
|
struct UsbDeviceSpecifier {
|
||||||
|
/// USB VID:PID as colon-separated pair
|
||||||
|
#[arg(short = 'i', long, default_value = "1fc9:0021", group = "device")]
|
||||||
|
usb_id: VidPid,
|
||||||
|
|
||||||
|
/// hidraw device node path
|
||||||
|
#[arg(short, long, group = "device", conflicts_with = "usb_id")]
|
||||||
|
dev_path: Option<PathBuf>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(ValueEnum, Debug, Clone)]
|
||||||
|
enum ObjectFileType {
|
||||||
|
Elf,
|
||||||
|
Ihex,
|
||||||
|
Bin,
|
||||||
|
Auto,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ObjectFileType {
|
||||||
|
fn mem_writer(&self, filename: &str) -> Box<dyn MemoryWriter> {
|
||||||
|
match self {
|
||||||
|
Self::Auto => MemoryFileType::mem_writer_from_filename(filename),
|
||||||
|
Self::Bin => MemoryFileType::Bin.mem_writer(),
|
||||||
|
Self::Ihex => MemoryFileType::Ihex.mem_writer(),
|
||||||
|
Self::Elf => MemoryFileType::Elf.mem_writer(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Args, Debug)]
|
||||||
|
struct ReadArgs {
|
||||||
|
file: PathBuf,
|
||||||
|
#[arg(short = 't', long = "type", value_enum, default_value_t = ObjectFileType::Auto)]
|
||||||
|
/// Type of object file to generate
|
||||||
|
filetype: ObjectFileType,
|
||||||
|
|
||||||
|
/// USB device to act on
|
||||||
|
#[command(flatten)]
|
||||||
|
devspec: UsbDeviceSpecifier,
|
||||||
|
/// Base memory address on microcontroller (defaults to start of flash)
|
||||||
|
#[arg(short = 'a', long = "base-address", value_parser=maybe_hex::<u32>)]
|
||||||
|
addr: Option<u32>,
|
||||||
|
/// Size to read in bytes (defaults to size of flash)
|
||||||
|
#[arg(short, long, value_parser=maybe_hex::<u32>)]
|
||||||
|
size: Option<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Args, Debug)]
|
||||||
|
struct WriteArgs {
|
||||||
|
file: PathBuf,
|
||||||
|
|
||||||
|
/// USB device to act on
|
||||||
|
#[command(flatten)]
|
||||||
|
devspec: UsbDeviceSpecifier,
|
||||||
|
/// Base memory address on microcontroller (defaults to start of flash)
|
||||||
|
#[arg(short = 'a', long = "write-address", value_parser=maybe_hex::<u32>)]
|
||||||
|
addr: Option<u32>,
|
||||||
|
/// Base memory address in <FILE> (defaults to the start of the first TEXT block in the file)
|
||||||
|
#[arg(long="read-address", value_parser=maybe_hex::<u32>)]
|
||||||
|
read_addr: Option<u32>,
|
||||||
|
/// Size to write in bytes (defaults to size of flash)
|
||||||
|
#[arg(short, long, value_parser=maybe_hex::<u32>)]
|
||||||
|
size: Option<u32>,
|
||||||
|
/// Don't reset the microcontroller after writing
|
||||||
|
#[arg(short, long, action=ArgAction::SetFalse)]
|
||||||
|
no_reset: bool,
|
||||||
|
#[arg(short, long)]
|
||||||
|
/// Erase all flash before writing
|
||||||
|
erase: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Args, Debug)]
|
||||||
|
struct ListArgs {
|
||||||
|
/// USB device to act on
|
||||||
|
#[command(flatten)]
|
||||||
|
devspec: UsbDeviceSpecifier,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Args, Debug)]
|
||||||
|
struct EraseArgs {
|
||||||
|
/// USB device to act on
|
||||||
|
#[command(flatten)]
|
||||||
|
devspec: UsbDeviceSpecifier,
|
||||||
|
|
||||||
|
/// Address to start erasing at
|
||||||
|
#[arg(short = 'a', long="base-address", value_parser=maybe_hex::<u32>, requires="size")]
|
||||||
|
addr: Option<u32>,
|
||||||
|
#[arg(short, long, value_parser=maybe_hex::<u32>)]
|
||||||
|
size: Option<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Args, Debug)]
|
||||||
|
struct ResetArgs {
|
||||||
|
#[command(flatten)]
|
||||||
|
devspec: UsbDeviceSpecifier,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Subcommand, Debug)]
|
||||||
|
enum Commands {
|
||||||
|
/// Read microcontroller memory to a file
|
||||||
|
Read(ReadArgs),
|
||||||
|
/// Write a file to microcontroller memory
|
||||||
|
Write(WriteArgs),
|
||||||
|
/// Erase the microcontroller's flash
|
||||||
|
Erase(EraseArgs),
|
||||||
|
/// List available ISP devices
|
||||||
|
List(ListArgs),
|
||||||
|
/// Reset the microcontroller
|
||||||
|
Reset(ResetArgs),
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_write_style() -> ProgressStyle {
|
||||||
|
ProgressStyle::with_template(
|
||||||
|
"[{elapsed}] {msg:4}{spinner} {prefix:20} {bar:40.cyan/blue} {bytes:>7}/{total_bytes:7}",
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
.progress_chars("##-")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_file_to_flash(args: &WriteArgs) -> Result<(), Error> {
|
||||||
|
let api = HidApi::new()?;
|
||||||
|
let isp = connect_device(&api, &args.devspec)?;
|
||||||
|
|
||||||
|
let infile = fs::read(args.file.as_path())
|
||||||
|
.with_context(|| format!("Opening {} for reading", args.file.display()))?;
|
||||||
|
let in_obj = object::File::parse(&*infile)
|
||||||
|
.with_context(|| format!("Parsing {} as a binary object", args.file.display()))?;
|
||||||
|
|
||||||
|
println!("{:?}", in_obj.sections());
|
||||||
|
|
||||||
|
let flash_start = args.addr.unwrap_or(isp.GetFlashStartAddress()?);
|
||||||
|
let flash_size = args.size.unwrap_or(isp.GetFlashSizeInBytes()?);
|
||||||
|
|
||||||
|
if !args.no_reset {
|
||||||
|
isp.reset()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_flash_to_file(args: &ReadArgs) -> Result<(), Error> {
|
||||||
|
let api = hidapi::HidApi::new()?; // is this free, or should it be passed?
|
||||||
|
let isp = connect_device(&api, &args.devspec)?;
|
||||||
|
|
||||||
|
let flash_start = match args.addr {
|
||||||
|
None => isp.GetFlashStartAddress()?,
|
||||||
|
Some(addr) => addr,
|
||||||
|
};
|
||||||
|
|
||||||
|
let flash_size = match args.size {
|
||||||
|
None => isp.GetFlashSizeInBytes()?,
|
||||||
|
Some(size) => size,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut buf = Vec::with_capacity(flash_size as usize);
|
||||||
|
|
||||||
|
let read_pb = ProgressBar::new(flash_size.into())
|
||||||
|
.with_style(read_write_style())
|
||||||
|
.with_prefix("Reading flash");
|
||||||
|
// .with_finish(indicatif::ProgressFinish::WithMessage("Done".into()));
|
||||||
|
|
||||||
|
// Allow failure, we might still have some data to write
|
||||||
|
match isp.ReadMemory(
|
||||||
|
&mut read_pb.wrap_write(&mut buf),
|
||||||
|
flash_start,
|
||||||
|
flash_size,
|
||||||
|
None,
|
||||||
|
) {
|
||||||
|
Ok(_) => read_pb.finish_with_message(style("DONE").bold().green().to_string()),
|
||||||
|
Err(e) => {
|
||||||
|
read_pb.abandon_with_message(style("FAIL").bold().red().to_string());
|
||||||
|
error!(
|
||||||
|
"reading from flash: {}. Continuing with incomplete data.",
|
||||||
|
e
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut writer = args.filetype.mem_writer(args.file.to_str().unwrap_or(""));
|
||||||
|
|
||||||
|
let write_pb: ProgressBar = ProgressBar::new(buf.len() as u64)
|
||||||
|
.with_style(read_write_style())
|
||||||
|
.with_prefix("Writing object file")
|
||||||
|
.with_finish(indicatif::ProgressFinish::WithMessage(
|
||||||
|
style("DONE").bold().green().to_string().into(),
|
||||||
|
));
|
||||||
|
|
||||||
|
let output = BufWriter::new(File::create(args.file.as_path())?);
|
||||||
|
writer
|
||||||
|
.write_mem(&mut write_pb.wrap_write(output), flash_start, &buf)
|
||||||
|
.or_else(|e| Err(anyhow!("Error writing to object file: {}", e)))?;
|
||||||
|
write_pb.finish_using_style();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn erase_flash(args: &EraseArgs) -> Result<(), Error> {
|
||||||
|
let api = hidapi::HidApi::new()?;
|
||||||
|
let isp = connect_device(&api, &args.devspec)?;
|
||||||
|
|
||||||
|
let erase_pb = ProgressBar::new(1)
|
||||||
|
.with_style(read_write_style())
|
||||||
|
.with_prefix("Erasing flash");
|
||||||
|
if args.addr.is_none() && args.size.is_none() {
|
||||||
|
isp.flash_erase_all(None)?;
|
||||||
|
erase_pb.finish();
|
||||||
|
} else {
|
||||||
|
let flash_start = isp.GetFlashStartAddress()?;
|
||||||
|
let flash_size = isp.GetFlashSizeInBytes()?;
|
||||||
|
let start_addr = args.addr.unwrap_or(flash_start);
|
||||||
|
let size = args.addr.unwrap_or(flash_size);
|
||||||
|
if start_addr + size > flash_start + flash_size {
|
||||||
|
warn!("Looks like you're attempting to erase beyond the end of flash (0x{:x}-0x{:x})! This will probably fail.", start_addr, start_addr+size);
|
||||||
|
}
|
||||||
|
isp.flash_erase_region(None, start_addr, size)?;
|
||||||
|
erase_pb.finish();
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset(args: &ResetArgs) -> Result<(), Error> {
|
||||||
|
let api = hidapi::HidApi::new()?;
|
||||||
|
let isp = connect_device(&api, &args.devspec)?;
|
||||||
|
|
||||||
|
isp.reset()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_matching_devices<'a>(
|
||||||
|
api: &'a hidapi::HidApi,
|
||||||
|
devspec: &UsbDeviceSpecifier,
|
||||||
|
) -> Result<Vec<&'a DeviceInfo>, Error> {
|
||||||
|
match &devspec.dev_path {
|
||||||
|
Some(path) => {
|
||||||
|
let path_ref: &std::ffi::OsStr = path.as_ref();
|
||||||
|
let path_str = CString::new(path_ref.as_bytes())?;
|
||||||
|
let v: Vec<_> = api
|
||||||
|
.device_list()
|
||||||
|
.filter(|dev| dev.path().eq(&path_str))
|
||||||
|
.collect();
|
||||||
|
Ok(v)
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
let v: Vec<_> = api
|
||||||
|
.device_list()
|
||||||
|
.filter(|dev| {
|
||||||
|
dev.vendor_id() == devspec.usb_id.vid && dev.product_id() == devspec.usb_id.pid
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
Ok(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn connect_device(
|
||||||
|
api: &hidapi::HidApi,
|
||||||
|
devspec: &UsbDeviceSpecifier,
|
||||||
|
) -> Result<protocol::UsbIsp, Error> {
|
||||||
|
let matches = get_matching_devices(api, devspec)?;
|
||||||
|
if matches.len() < 1 {
|
||||||
|
return Err(NoDevicesError {}.into());
|
||||||
|
} else if matches.len() > 1 {
|
||||||
|
warn!("More than one device matched specifier, connecting to first match.");
|
||||||
|
}
|
||||||
|
debug!(
|
||||||
|
"Connecting to device {} {} at `{}`",
|
||||||
|
matches[0].manufacturer_string().unwrap_or(""),
|
||||||
|
matches[0].product_string().unwrap_or("device"),
|
||||||
|
matches[0].path().to_str()?
|
||||||
|
);
|
||||||
|
let dev = matches[0].open_device(api)?;
|
||||||
|
let proto = protocol::UsbIsp::new(dev);
|
||||||
|
debug!("Connected to device {}", proto.GetUniqueDeviceId()?);
|
||||||
|
Ok(proto)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_device(api: &HidApi, dev: &DeviceInfo) -> Result<(), Error> {
|
||||||
|
let hid_dev = dev.open_device(&api)?;
|
||||||
|
let isp = protocol::UsbIsp::new(hid_dev);
|
||||||
|
println!(
|
||||||
|
"{:<14} [{:04x}:{:04x}] {:<13} {:10} {}",
|
||||||
|
style(dev.path().to_str()?).magenta(),
|
||||||
|
style(dev.vendor_id()).cyan(),
|
||||||
|
style(dev.product_id()).cyan(),
|
||||||
|
style(isp.GetSystemDeviceId()?).yellow(),
|
||||||
|
style(HumanBytes(isp.GetFlashSizeInBytes()? as u64)).green(),
|
||||||
|
style(isp.GetUniqueDeviceId()?).white()
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_all_devices(devspec: &UsbDeviceSpecifier) -> Result<(), Error> {
|
||||||
|
let api = hidapi::HidApi::new()?;
|
||||||
|
|
||||||
|
let devices: Vec<_> = get_matching_devices(&api, &devspec)?;
|
||||||
|
if devices.len() > 0 {
|
||||||
|
println!(
|
||||||
|
"{:^14}|{:^11}|{:^13}|{:^10}|{:^36}",
|
||||||
|
"Path", "VID:PID", "Chip", "Flash", "UUID"
|
||||||
|
);
|
||||||
|
println!(
|
||||||
|
"{:-<14}/{:-<11}/{:-<13}/{:-<10}/{:-<36}",
|
||||||
|
"", "", "", "", ""
|
||||||
|
);
|
||||||
|
|
||||||
|
for dev in devices {
|
||||||
|
print_device(&api, dev)?;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
println!("No devices found :(");
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> Result<(), Error> {
|
||||||
|
env_logger::Builder::from_env(Env::default().default_filter_or("info")).init();
|
||||||
|
let cli = Cli::parse();
|
||||||
|
|
||||||
|
match cli.command {
|
||||||
|
Commands::Read(args) => read_flash_to_file(&args),
|
||||||
|
Commands::Write(args) => write_file_to_flash(&args),
|
||||||
|
Commands::Erase(args) => erase_flash(&args),
|
||||||
|
Commands::Reset(args) => reset(&args),
|
||||||
|
Commands::List(args) => print_all_devices(&args.devspec),
|
||||||
|
}
|
||||||
|
}
|
352
src/packet.rs
Normal file
352
src/packet.rs
Normal file
@ -0,0 +1,352 @@
|
|||||||
|
use nom::bytes::complete::take;
|
||||||
|
use nom::error::{Error, ErrorKind};
|
||||||
|
use nom::multi::many0;
|
||||||
|
use nom::number::complete;
|
||||||
|
use nom::IResult;
|
||||||
|
use nom::{Err, InputTake};
|
||||||
|
|
||||||
|
use nom::sequence::Tuple;
|
||||||
|
use num_enum::{FromPrimitive, IntoPrimitive, TryFromPrimitive};
|
||||||
|
use std::convert::TryFrom;
|
||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
use bitflags::bitflags;
|
||||||
|
use byteorder::{LittleEndian, WriteBytesExt};
|
||||||
|
|
||||||
|
use strum_macros::{EnumIter, IntoStaticStr};
|
||||||
|
|
||||||
|
#[repr(u8)]
|
||||||
|
#[derive(Copy, Clone, TryFromPrimitive, IntoPrimitive, Debug)]
|
||||||
|
pub enum ReportId {
|
||||||
|
CommandOut = 1,
|
||||||
|
DataOut = 2,
|
||||||
|
CommandIn = 3,
|
||||||
|
DataIn = 4,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(u8)]
|
||||||
|
#[derive(Copy, Clone, TryFromPrimitive, IntoPrimitive, Debug, EnumIter)]
|
||||||
|
pub enum CommandTag {
|
||||||
|
FlashEraseAll = 0x01,
|
||||||
|
FlashEraseRegion = 0x02,
|
||||||
|
ReadMemory = 0x03,
|
||||||
|
WriteMemory = 0x04,
|
||||||
|
FillMemory = 0x05,
|
||||||
|
GetProperty = 0x07,
|
||||||
|
ReceiveSbFile = 0x08,
|
||||||
|
Execute = 0x09,
|
||||||
|
Call = 0x0a,
|
||||||
|
Reset = 0x0b,
|
||||||
|
SetProperty = 0x0c,
|
||||||
|
ConfigureMemory = 0x11,
|
||||||
|
KeyProvision = 0x15,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(u8)]
|
||||||
|
#[derive(Copy, Clone, TryFromPrimitive, IntoPrimitive, Debug)]
|
||||||
|
pub enum ResponseTag {
|
||||||
|
GenericResponse = 0xa0,
|
||||||
|
ReadMemoryResponse = 0xa3,
|
||||||
|
GetPropertyResponse = 0xa7,
|
||||||
|
FlashReadOnceResponse = 0xaf,
|
||||||
|
KeyProvisionResponse = 0xb5,
|
||||||
|
}
|
||||||
|
#[repr(u32)]
|
||||||
|
#[derive(Copy, Clone, PartialEq, IntoStaticStr, FromPrimitive, IntoPrimitive, Debug)]
|
||||||
|
pub enum StatusCode {
|
||||||
|
Success = 0,
|
||||||
|
StatusFlashAlignmentError = 101,
|
||||||
|
StatusFlashEccError = 116,
|
||||||
|
StatusMemoryRangeInvalid = 10200,
|
||||||
|
StatusMemoryBlankPageReadDisallowed = 10211,
|
||||||
|
#[num_enum(catch_all)]
|
||||||
|
Unknown(u32),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for StatusCode {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
StatusCode::Success => f.write_str("Success"),
|
||||||
|
StatusCode::Unknown(code) => f.write_fmt(format_args!("Error({})", code)),
|
||||||
|
code => {
|
||||||
|
let name: &'static str = code.into();
|
||||||
|
f.write_fmt(format_args!("{}({})", name, u32::from(code.to_owned())))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum ResponseParameters {
|
||||||
|
GenericResponse(GenericResponseParams),
|
||||||
|
ReadMemoryResponse(ReadMemoryResponseParams),
|
||||||
|
GetPropertyResponse(GetPropertyResponseParams),
|
||||||
|
FlashReadOnceResponse(FlashReadOnceResponseParams),
|
||||||
|
KeyProvisionResponse(KeyProvisionResponseParams),
|
||||||
|
}
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct GenericResponseParams {
|
||||||
|
pub status: StatusCode,
|
||||||
|
pub command: CommandTag,
|
||||||
|
}
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ReadMemoryResponseParams {
|
||||||
|
pub status: StatusCode,
|
||||||
|
pub data_bytes: u32,
|
||||||
|
}
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct GetPropertyResponseParams {
|
||||||
|
pub status: StatusCode,
|
||||||
|
pub properties: Vec<u32>,
|
||||||
|
}
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct FlashReadOnceResponseParams {
|
||||||
|
status: StatusCode,
|
||||||
|
byte_count: u32,
|
||||||
|
read_data: [u8; 20],
|
||||||
|
}
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct KeyProvisionResponseParams {
|
||||||
|
status: StatusCode,
|
||||||
|
data_bytes: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
pub struct CommandFlags: u8 {
|
||||||
|
const DATA_FOLLOWS = 0b00000001;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for CommandFlags {
|
||||||
|
fn default() -> Self {
|
||||||
|
CommandFlags { bits: 0 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct CommandPacket {
|
||||||
|
pub tag: CommandTag,
|
||||||
|
pub flags: CommandFlags,
|
||||||
|
pub reserved: u8,
|
||||||
|
pub param_count: u8,
|
||||||
|
pub params: Vec<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ResponsePacket {
|
||||||
|
pub tag: ResponseTag,
|
||||||
|
pub flags: CommandFlags,
|
||||||
|
pub reserved: u8,
|
||||||
|
pub param_count: u8,
|
||||||
|
pub params: ResponseParameters,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct DataPacket {
|
||||||
|
pub bytes: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct UsbPacket {
|
||||||
|
report_id: ReportId,
|
||||||
|
_padding: u8,
|
||||||
|
packet_length: u16,
|
||||||
|
packet: Packet,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
//TODO: Refactor this to use traits and borrows
|
||||||
|
pub enum Packet {
|
||||||
|
CommandPacket(CommandPacket),
|
||||||
|
ResponsePacket(ResponsePacket),
|
||||||
|
DataPacket(DataPacket),
|
||||||
|
}
|
||||||
|
|
||||||
|
fn status_code(input: &[u8]) -> IResult<&[u8], StatusCode> {
|
||||||
|
let (input, code) = complete::le_u32(input)?;
|
||||||
|
Ok((input, StatusCode::from(code)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn response_tag(input: &[u8]) -> IResult<&[u8], ResponseTag> {
|
||||||
|
let res = take(1u8)(input);
|
||||||
|
match res {
|
||||||
|
Ok(ir) => match ResponseTag::try_from(ir.1[0]) {
|
||||||
|
Ok(val) => Ok((ir.0, val)),
|
||||||
|
Err(e) => {
|
||||||
|
println!("{:?}", e);
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(e) => Err(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn command_tag(input: &[u8]) -> IResult<&[u8], CommandTag> {
|
||||||
|
let res = take(1u8)(input);
|
||||||
|
match res {
|
||||||
|
Ok(ir) => match CommandTag::try_from(ir.1[0]) {
|
||||||
|
Ok(val) => Ok((ir.0, val)),
|
||||||
|
Err(e) => {
|
||||||
|
println!("{:?}", e);
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(e) => Err(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generic_response_params(input: &[u8]) -> IResult<&[u8], ResponseParameters> {
|
||||||
|
let (input, (status, command)) = (status_code, complete::u8).parse(input)?;
|
||||||
|
Ok((
|
||||||
|
input,
|
||||||
|
ResponseParameters::GenericResponse(GenericResponseParams {
|
||||||
|
status,
|
||||||
|
command: CommandTag::try_from(command).unwrap(),
|
||||||
|
}),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_property_response_params(input: &[u8]) -> IResult<&[u8], ResponseParameters> {
|
||||||
|
let (input, (status, properties)) = (status_code, many0(complete::le_u32)).parse(input)?;
|
||||||
|
Ok((
|
||||||
|
input,
|
||||||
|
ResponseParameters::GetPropertyResponse(GetPropertyResponseParams { status, properties }),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_read_memory_response_params(input: &[u8]) -> IResult<&[u8], ResponseParameters> {
|
||||||
|
let (input, (status, properties)) = (status_code, many0(complete::le_u32)).parse(input)?;
|
||||||
|
assert!(properties.len() == 1);
|
||||||
|
Ok((
|
||||||
|
input,
|
||||||
|
ResponseParameters::ReadMemoryResponse(ReadMemoryResponseParams {
|
||||||
|
status,
|
||||||
|
data_bytes: properties[0],
|
||||||
|
}),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn report_id(input: &[u8]) -> IResult<&[u8], ReportId> {
|
||||||
|
let (input, id) = complete::u8(input)?;
|
||||||
|
match ReportId::try_from(id) {
|
||||||
|
Ok(val) => Ok((input, val)),
|
||||||
|
Err(_) => Err(Err::Error(Error::new(input, ErrorKind::Fail))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn usb_packet(input: &[u8]) -> IResult<&[u8], Packet> {
|
||||||
|
let (input, (reportid, _, packet_length)) =
|
||||||
|
(report_id, complete::u8, complete::le_u16).parse(input)?;
|
||||||
|
let (input, buf) = take(packet_length)(input)?;
|
||||||
|
|
||||||
|
let pack = match reportid {
|
||||||
|
ReportId::CommandOut => command_packet(buf)?,
|
||||||
|
ReportId::DataOut => unimplemented!(),
|
||||||
|
ReportId::CommandIn => response_packet(buf)?,
|
||||||
|
ReportId::DataIn => data_packet(buf, packet_length)?,
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok((input, pack.1))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn response_packet(input: &[u8]) -> IResult<&[u8], Packet> {
|
||||||
|
let (input, (tag, flags, reserved, param_count)) =
|
||||||
|
(response_tag, complete::u8, complete::u8, complete::u8).parse(input)?;
|
||||||
|
let (input, params) = match tag {
|
||||||
|
ResponseTag::GenericResponse => generic_response_params(input)?,
|
||||||
|
ResponseTag::GetPropertyResponse => get_property_response_params(input)?,
|
||||||
|
ResponseTag::ReadMemoryResponse => get_read_memory_response_params(input)?,
|
||||||
|
ResponseTag::FlashReadOnceResponse => unimplemented!(),
|
||||||
|
ResponseTag::KeyProvisionResponse => unimplemented!(),
|
||||||
|
};
|
||||||
|
Ok((
|
||||||
|
input,
|
||||||
|
Packet::ResponsePacket(ResponsePacket {
|
||||||
|
tag,
|
||||||
|
flags: CommandFlags::from_bits_truncate(flags),
|
||||||
|
reserved,
|
||||||
|
param_count,
|
||||||
|
params,
|
||||||
|
}),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn data_packet(input: &[u8], packet_length: u16) -> IResult<&[u8], Packet> {
|
||||||
|
let data = input.take(packet_length as usize);
|
||||||
|
Ok((
|
||||||
|
input,
|
||||||
|
Packet::DataPacket(DataPacket {
|
||||||
|
bytes: data.to_vec(),
|
||||||
|
}),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn command_packet(input: &[u8]) -> IResult<&[u8], Packet> {
|
||||||
|
let (input, (tag, flags, reserved, param_count)) =
|
||||||
|
(command_tag, complete::u8, complete::u8, complete::u8).parse(input)?;
|
||||||
|
Ok((
|
||||||
|
input,
|
||||||
|
Packet::CommandPacket(CommandPacket {
|
||||||
|
tag,
|
||||||
|
flags: CommandFlags::from_bits_truncate(flags),
|
||||||
|
reserved,
|
||||||
|
param_count,
|
||||||
|
params: Vec::new(),
|
||||||
|
}),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Packet {
|
||||||
|
pub fn write<W: std::io::Write>(&self, w: &mut W) -> Result<(), std::io::Error> {
|
||||||
|
//TODO: Refactor to a trait
|
||||||
|
match self {
|
||||||
|
Packet::CommandPacket(p) => {
|
||||||
|
w.write(&[
|
||||||
|
p.tag as u8,
|
||||||
|
p.flags.bits() as u8,
|
||||||
|
p.reserved as u8,
|
||||||
|
p.param_count as u8,
|
||||||
|
])?;
|
||||||
|
for param in &p.params {
|
||||||
|
w.write_u32::<LittleEndian>(*param)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Packet::DataPacket(p) => {
|
||||||
|
w.write(p.bytes.as_slice())?;
|
||||||
|
}
|
||||||
|
Packet::ResponsePacket(p) => {
|
||||||
|
unimplemented!("Serializing response packets is not implemented");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
pub fn length(&self) -> u16 {
|
||||||
|
let header_len = 4u16;
|
||||||
|
match self {
|
||||||
|
Packet::CommandPacket(p) => header_len + (p.param_count as u16) * 4,
|
||||||
|
Packet::ResponsePacket(p) => header_len + (p.param_count as u16) * 4,
|
||||||
|
Packet::DataPacket(p) => p.bytes.len() as u16,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UsbPacket {
|
||||||
|
pub fn new(report_id: ReportId, packet: Packet) -> UsbPacket {
|
||||||
|
UsbPacket {
|
||||||
|
report_id,
|
||||||
|
_padding: 0,
|
||||||
|
packet_length: packet.length(),
|
||||||
|
packet,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn length(&self) -> usize {
|
||||||
|
self.packet_length as usize + 4
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write<W: std::io::Write>(&self, w: &mut W) -> Result<(), std::io::Error> {
|
||||||
|
w.write(&[self.report_id as u8, 0])?;
|
||||||
|
w.write_u16::<LittleEndian>(self.packet_length)?;
|
||||||
|
self.packet.write(w)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
635
src/protocol.rs
Normal file
635
src/protocol.rs
Normal file
@ -0,0 +1,635 @@
|
|||||||
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
|
use anyhow::{anyhow, Error};
|
||||||
|
use byteorder::{ByteOrder, LittleEndian, NativeEndian};
|
||||||
|
use hidapi::{HidApi, HidDevice};
|
||||||
|
use log::debug;
|
||||||
|
use num_enum::{FromPrimitive, IntoPrimitive, TryFromPrimitive};
|
||||||
|
use std::fmt::{Debug, Display};
|
||||||
|
use std::io::{Cursor, Write};
|
||||||
|
use strum::IntoEnumIterator;
|
||||||
|
use strum_macros::EnumIter;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use crate::packet::{
|
||||||
|
self, GenericResponseParams, GetPropertyResponseParams, Packet, ResponsePacket,
|
||||||
|
ResponseParameters, StatusCode, UsbPacket,
|
||||||
|
};
|
||||||
|
use crate::packet::{CommandFlags, CommandPacket, CommandTag};
|
||||||
|
|
||||||
|
#[repr(u32)]
|
||||||
|
#[derive(IntoPrimitive, EnumIter, Debug, PartialEq, Clone)]
|
||||||
|
pub enum MemoryId {
|
||||||
|
Internal = 0x000,
|
||||||
|
XoInternal = 0x010,
|
||||||
|
QspiNor = 0x001,
|
||||||
|
SemcNor = 0x008,
|
||||||
|
FlexSpiNor = 0x009,
|
||||||
|
SemcRawNand = 0x100,
|
||||||
|
FlexSpiNand = 0x101,
|
||||||
|
SpiNorEeprom = 0x110,
|
||||||
|
Sd = 0x120,
|
||||||
|
Emmc = 0x121,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(u8)]
|
||||||
|
#[derive(IntoPrimitive, EnumIter, Debug, PartialEq, Clone)]
|
||||||
|
pub enum Property {
|
||||||
|
CurrentVersion = 1,
|
||||||
|
AvailablePeripherals = 2,
|
||||||
|
FlashStartAddress = 3,
|
||||||
|
FlashSizeInBytes = 4,
|
||||||
|
Availablecommands = 7,
|
||||||
|
CheckStatus = 8,
|
||||||
|
MaxPacketSize = 0x0b,
|
||||||
|
ReservedRegions = 0x0c,
|
||||||
|
SystemDeviceId = 0x10,
|
||||||
|
LifeCycleState = 0x11,
|
||||||
|
UniqueDeviceId = 0x12,
|
||||||
|
ExternalMemoryAttributes = 0x19,
|
||||||
|
IrqNotifierPin = 0x1c,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(u32)]
|
||||||
|
#[derive(IntoPrimitive, FromPrimitive, EnumIter, Debug, PartialEq, Clone)]
|
||||||
|
pub enum LifeCycleState {
|
||||||
|
Development = 0x5aa55aa5,
|
||||||
|
Deployment = 0xc33cc33c,
|
||||||
|
#[num_enum(catch_all)]
|
||||||
|
Unknown(u32),
|
||||||
|
}
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct BootLoaderVersion {
|
||||||
|
pub name: char,
|
||||||
|
pub major: u8,
|
||||||
|
pub minor: u8,
|
||||||
|
pub bugfix: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<u32> for BootLoaderVersion {
|
||||||
|
fn from(value: u32) -> Self {
|
||||||
|
Self {
|
||||||
|
name: char::from_u32(value >> 24).unwrap(),
|
||||||
|
major: (value >> 16 & 0xff) as u8,
|
||||||
|
minor: (value >> 8 & 0xff) as u8,
|
||||||
|
bugfix: (value & 0xff) as u8,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct AvailablePeripherals {
|
||||||
|
pub usb_hid: bool,
|
||||||
|
pub spi_slave: bool,
|
||||||
|
pub i2c_slave: bool,
|
||||||
|
pub lpuart: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<u32> for AvailablePeripherals {
|
||||||
|
fn from(value: u32) -> Self {
|
||||||
|
let bits = value;
|
||||||
|
Self {
|
||||||
|
usb_hid: bits & (1 << 4) != 0,
|
||||||
|
spi_slave: bits & (1 << 2) != 0,
|
||||||
|
i2c_slave: bits & (1 << 1) != 0,
|
||||||
|
lpuart: bits & 1 != 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct AvailableCommands {
|
||||||
|
bits: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AvailableCommands {
|
||||||
|
fn command_available(&self, tag: &packet::CommandTag) -> bool {
|
||||||
|
let mask = 1 << (*tag as u8 - 1);
|
||||||
|
self.bits & mask != 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<u32> for AvailableCommands {
|
||||||
|
fn from(value: u32) -> Self {
|
||||||
|
Self { bits: value }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for AvailableCommands {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_list()
|
||||||
|
.entries(packet::CommandTag::iter().filter(|tag| self.command_available(tag)))
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ReservedRegion {
|
||||||
|
start: u32,
|
||||||
|
end: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ReservedRegions {
|
||||||
|
count: u8,
|
||||||
|
regions: Vec<ReservedRegion>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<GetPropertyResponseParams> for ReservedRegions {
|
||||||
|
fn from(value: GetPropertyResponseParams) -> Self {
|
||||||
|
let mut regions = Vec::new();
|
||||||
|
let count = value.properties[0] as usize;
|
||||||
|
for i in 1..count {
|
||||||
|
regions.push(ReservedRegion {
|
||||||
|
start: value.properties[i],
|
||||||
|
end: value.properties[i + 1],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Self {
|
||||||
|
count: count.try_into().unwrap(),
|
||||||
|
regions,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(u32)]
|
||||||
|
#[derive(IntoPrimitive, FromPrimitive, EnumIter, PartialEq, Clone)]
|
||||||
|
pub enum LpcDeviceId {
|
||||||
|
LPC55S28 = 0xA010119C,
|
||||||
|
LPC55S26 = 0xA010229A,
|
||||||
|
LPC5528 = 0xA010111C,
|
||||||
|
LPC5526 = 0xA010221A,
|
||||||
|
#[num_enum(catch_all)]
|
||||||
|
Unknown(u32),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for LpcDeviceId {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::LPC5526 => write!(f, "LPC5526"),
|
||||||
|
Self::LPC5528 => write!(f, "LPC5528"),
|
||||||
|
Self::LPC55S26 => write!(f, "LPC55S26"),
|
||||||
|
Self::LPC55S28 => write!(f, "LPC55S28"),
|
||||||
|
Self::Unknown(i) => write!(f, "0x{:x}", i),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(u32)]
|
||||||
|
#[derive(IntoPrimitive, EnumIter, PartialEq, Clone)]
|
||||||
|
pub enum LpcDieId {
|
||||||
|
Rev0a = 0x0,
|
||||||
|
Rev1b = 0x1,
|
||||||
|
#[num_enum(catch_all)]
|
||||||
|
Unknown(u32),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<u32> for LpcDieId {
|
||||||
|
fn from(value: u32) -> Self {
|
||||||
|
match value & 0xf {
|
||||||
|
0x0 => Self::Rev0a,
|
||||||
|
0x1 => Self::Rev1b,
|
||||||
|
i => Self::Unknown(i),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for LpcDieId {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Rev0a => write!(f, "0A"),
|
||||||
|
Self::Rev1b => write!(f, "1B"),
|
||||||
|
Self::Unknown(i) => write!(f, "0x{:x}", i),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SystemDeviceId {
|
||||||
|
pub device_id: LpcDeviceId,
|
||||||
|
pub die_id: LpcDieId,
|
||||||
|
}
|
||||||
|
impl From<GetPropertyResponseParams> for SystemDeviceId {
|
||||||
|
fn from(value: GetPropertyResponseParams) -> Self {
|
||||||
|
Self {
|
||||||
|
device_id: value.properties[0].into(),
|
||||||
|
die_id: value.properties[1].into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Display for SystemDeviceId {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.pad(&format!("{} ({})", self.device_id, self.die_id))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct IrqNotifierPinSetting {
|
||||||
|
pub gpio_pin: u8,
|
||||||
|
pub gpio_port: u8,
|
||||||
|
pub enabled: bool,
|
||||||
|
}
|
||||||
|
impl From<u32> for IrqNotifierPinSetting {
|
||||||
|
fn from(bits: u32) -> Self {
|
||||||
|
Self {
|
||||||
|
gpio_pin: (bits & 0xff) as u8,
|
||||||
|
gpio_port: ((bits >> 8) & 0xff) as u8,
|
||||||
|
enabled: bits & 0x80000000 != 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Into<u32> for &IrqNotifierPinSetting {
|
||||||
|
fn into(self) -> u32 {
|
||||||
|
(if self.enabled { 1u32 << 31 } else { 0u32 })
|
||||||
|
| ((self.gpio_port as u32) << 8)
|
||||||
|
| self.gpio_pin as u32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ExternalMemoryAttributes {
|
||||||
|
pub start_address: Option<u32>,
|
||||||
|
pub size_kb: Option<u32>,
|
||||||
|
pub page_size_bytes: Option<u32>,
|
||||||
|
pub sector_size_bytes: Option<u32>,
|
||||||
|
pub block_size_bytes: Option<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<GetPropertyResponseParams> for ExternalMemoryAttributes {
|
||||||
|
fn from(value: GetPropertyResponseParams) -> Self {
|
||||||
|
//TODO: use nom here??
|
||||||
|
let props = value.properties;
|
||||||
|
let supported = u32::from_le(props[0]);
|
||||||
|
Self {
|
||||||
|
start_address: if supported & 1 != 0 {
|
||||||
|
Some(u32::from_le(props[1]))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
|
size_kb: if supported & 2 != 0 {
|
||||||
|
Some(u32::from_le(props[2]))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
|
page_size_bytes: if supported & 4 != 0 {
|
||||||
|
Some(u32::from_le(props[3]))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
|
sector_size_bytes: if supported & 8 != 0 {
|
||||||
|
Some(u32::from_le(props[4]))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
|
block_size_bytes: if supported & 0x10 != 0 {
|
||||||
|
Some(u32::from_le(props[5]))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct CheckStatus {
|
||||||
|
pub crc: u32,
|
||||||
|
pub last_error: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<GetPropertyResponseParams> for CheckStatus {
|
||||||
|
fn from(value: GetPropertyResponseParams) -> Self {
|
||||||
|
Self {
|
||||||
|
crc: value.properties[0],
|
||||||
|
last_error: value.properties[0],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct UsbIsp {
|
||||||
|
device: HidDevice,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UsbIsp {
|
||||||
|
pub fn new(device: HidDevice) -> Self {
|
||||||
|
Self { device }
|
||||||
|
}
|
||||||
|
pub fn send_command(&self, p: CommandPacket) -> Result<ResponsePacket, Error> {
|
||||||
|
debug!("Writing packet: {:?}", p);
|
||||||
|
self.write_packet(Packet::CommandPacket(p))
|
||||||
|
}
|
||||||
|
pub fn write_packet(&self, p: Packet) -> Result<ResponsePacket, Error> {
|
||||||
|
let usb_packet = match p {
|
||||||
|
Packet::CommandPacket(p) => {
|
||||||
|
UsbPacket::new(packet::ReportId::CommandOut, Packet::CommandPacket(p))
|
||||||
|
}
|
||||||
|
Packet::ResponsePacket(_) => panic!("We should not be writing responses"),
|
||||||
|
Packet::DataPacket(p) => {
|
||||||
|
UsbPacket::new(packet::ReportId::DataOut, Packet::DataPacket(p))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let mut buf = Vec::with_capacity(usb_packet.length());
|
||||||
|
usb_packet.write(&mut buf)?;
|
||||||
|
|
||||||
|
//FIXME: don't expect a response on data packets
|
||||||
|
let rsize = self.device.write(&buf)?;
|
||||||
|
let mut rbuf = [0u8; 64];
|
||||||
|
let res_size = self.device.read(&mut rbuf[..])?;
|
||||||
|
let parsed = packet::usb_packet(&rbuf[..res_size]).unwrap(); //TODO: handle more gracefully, but requires ownership of I
|
||||||
|
|
||||||
|
match parsed.1 {
|
||||||
|
Packet::ResponsePacket(p) => Ok(p),
|
||||||
|
Packet::CommandPacket(_) => Err(anyhow!("Unexpected command packet on IN stream")),
|
||||||
|
Packet::DataPacket(_) => {
|
||||||
|
Err(anyhow!("Data IN packets not expected in reply to commands"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_data(&self, buf: &mut impl Write, byte_count: usize) -> Result<(), Error> {
|
||||||
|
let mut rbuf = [0u8; 64];
|
||||||
|
let mut read = 0;
|
||||||
|
while read < byte_count {
|
||||||
|
let res_size = self.device.read(&mut rbuf[..])?;
|
||||||
|
let parsed = packet::usb_packet(&rbuf[..res_size]).unwrap(); //TODO: handle more gracefully, but requires ownership of I
|
||||||
|
match parsed.1 {
|
||||||
|
Packet::DataPacket(p) => {
|
||||||
|
read += buf.write(&p.bytes)?;
|
||||||
|
}
|
||||||
|
Packet::ResponsePacket(r) => match r.params {
|
||||||
|
ResponseParameters::GenericResponse(rp) if rp.status != StatusCode::Success => {
|
||||||
|
// Success here is unexpected, let it fall through to the default case
|
||||||
|
return Err(anyhow!("Error while reading data: {}", rp.status));
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return Err(anyhow!(
|
||||||
|
"Unexpected response params when reading data {:?}",
|
||||||
|
r.params
|
||||||
|
))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
p => return Err(anyhow!("Unexpected packet type when reading data {:?}", p)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_last_error(&self) -> u32 {
|
||||||
|
// Same as get_property but less checking
|
||||||
|
let command = CommandPacket {
|
||||||
|
tag: CommandTag::GetProperty,
|
||||||
|
flags: CommandFlags::empty(),
|
||||||
|
reserved: 0,
|
||||||
|
param_count: 1,
|
||||||
|
params: vec![Property::CheckStatus as u32],
|
||||||
|
};
|
||||||
|
let pr = self.send_command(command).ok();
|
||||||
|
match pr {
|
||||||
|
Some(pr) => match pr.params {
|
||||||
|
ResponseParameters::GetPropertyResponse(resp) => match resp.status {
|
||||||
|
packet::StatusCode::Success => CheckStatus::from(resp).last_error,
|
||||||
|
_ => 0,
|
||||||
|
},
|
||||||
|
_ => 0,
|
||||||
|
},
|
||||||
|
None => 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn get_property(
|
||||||
|
&self,
|
||||||
|
prop: Property,
|
||||||
|
extra_params: Option<Vec<u32>>,
|
||||||
|
) -> Result<packet::GetPropertyResponseParams, Error> {
|
||||||
|
let mut command = CommandPacket {
|
||||||
|
tag: CommandTag::GetProperty,
|
||||||
|
flags: CommandFlags::empty(),
|
||||||
|
reserved: 0,
|
||||||
|
param_count: 1,
|
||||||
|
params: vec![prop as u32],
|
||||||
|
};
|
||||||
|
match extra_params {
|
||||||
|
Some(v) => {
|
||||||
|
command.params.extend(v.iter());
|
||||||
|
command.param_count += v.len() as u8;
|
||||||
|
}
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
let pr = self.send_command(command)?;
|
||||||
|
match pr.params {
|
||||||
|
ResponseParameters::GetPropertyResponse(resp) => match resp.status {
|
||||||
|
packet::StatusCode::Success => Ok(resp),
|
||||||
|
code => Err(anyhow!("Error status returned ({})", code)),
|
||||||
|
},
|
||||||
|
_ => Err(anyhow!(
|
||||||
|
"Unexpected response to GetBootloaderVersion command"
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn set_property(
|
||||||
|
&self,
|
||||||
|
prop: Property,
|
||||||
|
params: Vec<u32>,
|
||||||
|
) -> Result<packet::GenericResponseParams, Error> {
|
||||||
|
let command = CommandPacket {
|
||||||
|
tag: CommandTag::SetProperty,
|
||||||
|
flags: CommandFlags::empty(),
|
||||||
|
reserved: 0,
|
||||||
|
param_count: 1 + params.len() as u8,
|
||||||
|
params: vec![prop as u32],
|
||||||
|
};
|
||||||
|
let pr = self.send_command(command)?;
|
||||||
|
match pr.params {
|
||||||
|
ResponseParameters::GenericResponse(resp) => match resp.status {
|
||||||
|
packet::StatusCode::Success => Ok(resp),
|
||||||
|
code => Err(anyhow!("Error status returned ({:?})", code)),
|
||||||
|
},
|
||||||
|
_ => Err(anyhow!(
|
||||||
|
"Unexpected response to GetBootloaderVersion command"
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn get_basic_property(
|
||||||
|
&self,
|
||||||
|
prop: Property,
|
||||||
|
) -> Result<packet::GetPropertyResponseParams, Error> {
|
||||||
|
self.get_property(prop, None)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn flash_erase_all(&self, memory_id: Option<MemoryId>) -> Result<(), Error> {
|
||||||
|
let memory_id = memory_id.unwrap_or(MemoryId::Internal);
|
||||||
|
let command = CommandPacket {
|
||||||
|
tag: CommandTag::FlashEraseAll,
|
||||||
|
flags: CommandFlags::empty(),
|
||||||
|
reserved: 0,
|
||||||
|
param_count: 1,
|
||||||
|
params: vec![memory_id.into()],
|
||||||
|
};
|
||||||
|
self.send_command(command).map(|_| ())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn flash_erase_region(
|
||||||
|
&self,
|
||||||
|
memory_id: Option<MemoryId>,
|
||||||
|
start_address: u32,
|
||||||
|
byte_count: u32,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
if start_address % 4 != 0 || byte_count % 4 != 0 {
|
||||||
|
return Err(anyhow!(
|
||||||
|
"Erase address and size must be aligned on a 4-byte boundary"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let memory_id = memory_id.unwrap_or(MemoryId::Internal);
|
||||||
|
let command = CommandPacket {
|
||||||
|
tag: CommandTag::FlashEraseRegion,
|
||||||
|
flags: CommandFlags::empty(),
|
||||||
|
reserved: 0,
|
||||||
|
param_count: 3,
|
||||||
|
params: vec![start_address, byte_count, memory_id.into()],
|
||||||
|
};
|
||||||
|
self.send_command(command).map(|_| ())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reset(&self) -> Result<(), Error> {
|
||||||
|
let command = CommandPacket {
|
||||||
|
tag: CommandTag::Reset,
|
||||||
|
flags: CommandFlags::empty(),
|
||||||
|
reserved: 0,
|
||||||
|
param_count: 0,
|
||||||
|
params: vec![],
|
||||||
|
};
|
||||||
|
self.send_command(command).map(|_| ())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn GetBootloaderVersion(&self) -> Result<BootLoaderVersion, Error> {
|
||||||
|
let prop_value = self
|
||||||
|
.get_basic_property(Property::CurrentVersion)?
|
||||||
|
.properties[0];
|
||||||
|
Ok(prop_value.into())
|
||||||
|
}
|
||||||
|
pub fn GetAvailablePeripherals(&self) -> Result<AvailablePeripherals, Error> {
|
||||||
|
let prop_value = self
|
||||||
|
.get_basic_property(Property::AvailablePeripherals)?
|
||||||
|
.properties[0];
|
||||||
|
Ok(prop_value.into())
|
||||||
|
}
|
||||||
|
pub fn GetAvailableCommands(&self) -> Result<AvailableCommands, Error> {
|
||||||
|
let prop_value = self
|
||||||
|
.get_basic_property(Property::Availablecommands)?
|
||||||
|
.properties[0];
|
||||||
|
Ok(prop_value.into())
|
||||||
|
}
|
||||||
|
pub fn GetFlashStartAddress(&self) -> Result<u32, Error> {
|
||||||
|
let prop_value = self.get_basic_property(Property::FlashStartAddress)?;
|
||||||
|
Ok(prop_value.properties[0])
|
||||||
|
}
|
||||||
|
pub fn GetFlashSizeInBytes(&self) -> Result<u32, Error> {
|
||||||
|
let prop_value = self.get_basic_property(Property::FlashSizeInBytes)?;
|
||||||
|
Ok(prop_value.properties[0])
|
||||||
|
}
|
||||||
|
pub fn GetMaxPacketSize(&self) -> Result<u32, Error> {
|
||||||
|
let prop_value = self.get_basic_property(Property::MaxPacketSize)?;
|
||||||
|
Ok(prop_value.properties[0])
|
||||||
|
}
|
||||||
|
pub fn GetReservedRegions(&self) -> Result<ReservedRegions, Error> {
|
||||||
|
let prop_value = self.get_basic_property(Property::ReservedRegions)?;
|
||||||
|
Ok(prop_value.into())
|
||||||
|
}
|
||||||
|
pub fn GetSystemDeviceId(&self) -> Result<SystemDeviceId, Error> {
|
||||||
|
let prop_value = self.get_basic_property(Property::SystemDeviceId)?;
|
||||||
|
Ok(prop_value.into())
|
||||||
|
}
|
||||||
|
pub fn GetLifeCycleState(&self) -> Result<LifeCycleState, Error> {
|
||||||
|
let prop_value = self.get_basic_property(Property::LifeCycleState)?;
|
||||||
|
Ok(LifeCycleState::try_from(prop_value.properties[0])?)
|
||||||
|
}
|
||||||
|
pub fn GetUniqueDeviceId(&self) -> Result<Uuid, Error> {
|
||||||
|
let prop_value = self.get_basic_property(Property::UniqueDeviceId)?;
|
||||||
|
let mut buf = [0u8; 16];
|
||||||
|
for i in 0..4 {
|
||||||
|
buf[i * 4..i * 4 + 4].copy_from_slice(&prop_value.properties[i].to_ne_bytes());
|
||||||
|
}
|
||||||
|
Ok(Uuid::from_bytes(buf))
|
||||||
|
}
|
||||||
|
pub fn GetIrqNotifierPin(&self) -> Result<IrqNotifierPinSetting, Error> {
|
||||||
|
let prop_value = self.get_basic_property(Property::IrqNotifierPin)?;
|
||||||
|
Ok(prop_value.properties[0].into())
|
||||||
|
}
|
||||||
|
pub fn SetIrqNotifierPin(&self, setting: &IrqNotifierPinSetting) -> Result<(), Error> {
|
||||||
|
let command = CommandPacket {
|
||||||
|
tag: CommandTag::SetProperty,
|
||||||
|
flags: CommandFlags::empty(),
|
||||||
|
reserved: 0,
|
||||||
|
param_count: 2,
|
||||||
|
params: vec![Property::IrqNotifierPin as u32, setting.into()],
|
||||||
|
};
|
||||||
|
let pr = self.send_command(command.clone())?;
|
||||||
|
match pr.params {
|
||||||
|
ResponseParameters::GenericResponse(resp) => match resp.status {
|
||||||
|
packet::StatusCode::Success => Ok(()),
|
||||||
|
code => Err(anyhow!("Error status returned ({:?})", code)),
|
||||||
|
},
|
||||||
|
_ => Err(anyhow!(
|
||||||
|
"Unexpected response to {:?} command ({:?})",
|
||||||
|
command.tag,
|
||||||
|
pr.params
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn GetExternalMemoryAttributes(
|
||||||
|
&self,
|
||||||
|
id: MemoryId,
|
||||||
|
) -> Result<ExternalMemoryAttributes, Error> {
|
||||||
|
let prop_value =
|
||||||
|
self.get_property(Property::ExternalMemoryAttributes, Some(vec![id as u32]))?;
|
||||||
|
Ok(prop_value.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ReadMemory(
|
||||||
|
&self,
|
||||||
|
buf: &mut impl std::io::Write,
|
||||||
|
start_address: u32,
|
||||||
|
byte_count: u32,
|
||||||
|
memory_id: Option<MemoryId>,
|
||||||
|
) -> Result<u32, Error> {
|
||||||
|
let command = CommandPacket {
|
||||||
|
tag: CommandTag::ReadMemory,
|
||||||
|
flags: CommandFlags::empty(),
|
||||||
|
reserved: 0,
|
||||||
|
param_count: 3,
|
||||||
|
params: vec![
|
||||||
|
start_address,
|
||||||
|
byte_count,
|
||||||
|
match memory_id {
|
||||||
|
Some(v) => v.into(),
|
||||||
|
None => MemoryId::Internal.into(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
let pr = self.send_command(command)?;
|
||||||
|
let params = match pr.params {
|
||||||
|
ResponseParameters::ReadMemoryResponse(params) => match params.status {
|
||||||
|
StatusCode::Success => params,
|
||||||
|
_ => return Err(anyhow!("Error returned from device: {:?}", params.status)),
|
||||||
|
},
|
||||||
|
_ => return Err(anyhow!("Unexpected reply to ReadMemory {:?}", pr.params)),
|
||||||
|
};
|
||||||
|
self.read_data(buf, params.data_bytes as usize)?;
|
||||||
|
|
||||||
|
// We expect a GenericResponse with success after the transfer
|
||||||
|
let mut rbuf = vec![0u8; 64];
|
||||||
|
let res_size = self.device.read(&mut rbuf[..])?;
|
||||||
|
let parsed = packet::usb_packet(&rbuf[..res_size]).unwrap().1; //TODO: handle more gracefully, but requires ownership of I
|
||||||
|
|
||||||
|
match parsed {
|
||||||
|
Packet::ResponsePacket(p) => match p.params {
|
||||||
|
ResponseParameters::GenericResponse(rp) => match rp.status {
|
||||||
|
StatusCode::Success => Ok(params.data_bytes),
|
||||||
|
_ => Err(anyhow!("After read memory transfer: {}", rp.status)),
|
||||||
|
},
|
||||||
|
_ => Err(anyhow!("Unexpected response after read memory: {:?}", p)),
|
||||||
|
},
|
||||||
|
_ => Err(anyhow!(
|
||||||
|
"Unexpected packet type after read memory: {:?}",
|
||||||
|
parsed
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user