Initial commit, some working features

Signed-off-by: Keenan Tims <ktims@gotroot.ca>
This commit is contained in:
Keenan Tims 2023-11-03 17:47:18 -07:00
commit ee007da5d4
Signed by: ktims
GPG Key ID: 11230674D69038D4
7 changed files with 2452 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/target

823
Cargo.lock generated Normal file
View 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
View 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
View 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
View 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
View 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
View 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
)),
}
}
}