diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000000000000000000000000000000000000..a7427f918f6b5e776eb54734dc594801309f1fa6 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,40 @@ +image: "rust:latest" + +variables: + CARGO_HOME: $CI_PROJECT_DIR/.cargo + +cache: + key: $CI_COMMIT_REF_SLUG + paths: + - target/ + - .cargo + +# Install C compiler in case we need it +before_script: + - apt-get update -yqq + - apt-get install -yqq --no-install-recommends build-essential + +# Use cargo to test the project +check: + before_script: + - rustc --version && cargo --version # Print version info for debugging + script: + - cargo check + +test: + before_script: + - rustc --version && cargo --version # Print version info for debugging + script: + - cargo test + +lint: + before_script: + - rustc --version && cargo --version # Print version info for debugging + - rustup component add rustfmt + - rustfmt --version + - rustup component add clippy + - cargo-clippy --version + script: + - cargo build + - cargo fmt --all -- --check + - cargo clippy -- -D warnings diff --git a/Cargo.lock b/Cargo.lock index 3768acf6b488fab24822ff2e740877564c355553..4a6ec268976011680bb514e0695de0a363dcfdd5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,55 +3,40 @@ version = 3 [[package]] -name = "CoreFoundation-sys" -version = "0.1.4" +name = "addr2line" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0e9889e6db118d49d88d84728d0e964d973a5680befb5f85f55141beea5c20b" +checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" dependencies = [ - "libc", - "mach 0.1.2", + "gimli", ] [[package]] -name = "IOKit-sys" -version = "0.1.5" +name = "adler" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99696c398cbaf669d2368076bdb3d627fb0ce51a26899d7c61228c5c0af3bf4a" -dependencies = [ - "CoreFoundation-sys", - "libc", - "mach 0.1.2", -] +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] -name = "aho-corasick" -version = "0.7.20" +name = "backtrace" +version = "0.3.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" +checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca" dependencies = [ - "memchr", + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", ] [[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" +name = "cc" +version = "1.0.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "block-buffer" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" -dependencies = [ - "generic-array", -] +checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d" [[package]] name = "cfg-if" @@ -60,38 +45,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] -name = "cpufeatures" -version = "0.2.5" +name = "color-eyre" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +checksum = "5a667583cca8c4f8436db8de46ea8233c42a7d9ae424a82d338f2e4675229204" dependencies = [ - "libc", -] - -[[package]] -name = "crc16" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "338089f42c427b86394a5ee60ff321da23a5c89c9d89514c829687b26359fcff" - -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array", - "typenum", + "backtrace", + "color-spantrace", + "eyre", + "indenter", + "once_cell", + "owo-colors", + "tracing-error", ] [[package]] -name = "digest" -version = "0.10.6" +name = "color-spantrace" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +checksum = "1ba75b3d9449ecdccb27ecbc479fdc0b87fa2dd43d2f8298f9bf0e59aacc8dce" dependencies = [ - "block-buffer", - "crypto-common", + "once_cell", + "owo-colors", + "tracing-core", + "tracing-error", ] [[package]] @@ -111,38 +88,32 @@ dependencies = [ ] [[package]] -name = "generic-array" -version = "0.14.6" +name = "eyre" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +checksum = "4c2b6b5a29c02cdc822728b7d7b8ae1bab3e3b05d44522770ddd49722eeac7eb" dependencies = [ - "typenum", - "version_check", + "indenter", + "once_cell", ] [[package]] -name = "hermit-abi" -version = "0.1.19" +name = "gimli" +version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] +checksum = "dec7af912d60cdbd3677c1af9352ebae6fb8394d165568a2234df0fa00f87793" [[package]] -name = "ioctl-rs" -version = "0.1.6" +name = "indenter" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7970510895cee30b3e9128319f2cefd4bde883a39f38baa279567ba3a7eb97d" -dependencies = [ - "libc", -] +checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" [[package]] -name = "leb128" -version = "0.2.5" +name = "lazy_static" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" @@ -150,44 +121,6 @@ version = "0.2.138" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8" -[[package]] -name = "libudev" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b324152da65df7bb95acfcaab55e3097ceaab02fb19b228a9eb74d55f135e0" -dependencies = [ - "libc", - "libudev-sys", -] - -[[package]] -name = "libudev-sys" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c8469b4a23b962c1396b9b451dda50ef5b283e8dd309d69033475fa9b334324" -dependencies = [ - "libc", - "pkg-config", -] - -[[package]] -name = "mach" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fd13ee2dd61cc82833ba05ade5a30bb3d63f7ced605ef827063c63078302de9" -dependencies = [ - "libc", -] - -[[package]] -name = "mach" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" -dependencies = [ - "libc", -] - [[package]] name = "memchr" version = "2.5.0" @@ -195,120 +128,62 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] -name = "nix" -version = "0.24.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069" -dependencies = [ - "bitflags", - "cfg-if", - "libc", -] - -[[package]] -name = "num_cpus" -version = "1.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6058e64324c71e02bc2b150e4f3bc8286db6c83092132ffa3f6b1eab0f9def5" -dependencies = [ - "hermit-abi", - "libc", -] - -[[package]] -name = "once_cell" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" - -[[package]] -name = "pin-project-lite" -version = "0.2.9" +name = "minimal-lexical" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] -name = "pkg-config" -version = "0.3.26" +name = "miniz_oxide" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" - -[[package]] -name = "proc-macro2" -version = "1.0.47" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" +checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" dependencies = [ - "unicode-ident", + "adler", ] [[package]] -name = "quote" -version = "1.0.21" +name = "nom" +version = "7.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36" dependencies = [ - "proc-macro2", + "memchr", + "minimal-lexical", ] [[package]] -name = "regex" -version = "1.7.0" +name = "object" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a" +checksum = "239da7f290cfa979f43f85a8efeee9a8a76d0827c356d37f9d3d7254d6b537fb" dependencies = [ - "aho-corasick", "memchr", - "regex-syntax", ] [[package]] -name = "regex-syntax" -version = "0.6.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" - -[[package]] -name = "serial" -version = "0.4.0" +name = "once_cell" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1237a96570fc377c13baa1b88c7589ab66edced652e43ffb17088f003db3e86" -dependencies = [ - "serial-core", - "serial-unix", - "serial-windows", -] +checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" [[package]] -name = "serial-core" -version = "0.4.0" +name = "owo-colors" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f46209b345401737ae2125fe5b19a77acce90cd53e1658cda928e4fe9a64581" -dependencies = [ - "libc", -] +checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" [[package]] -name = "serial-unix" -version = "0.4.0" +name = "pin-project-lite" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f03fbca4c9d866e24a459cbca71283f545a37f8e3e002ad8c70593871453cab7" -dependencies = [ - "ioctl-rs", - "libc", - "serial-core", - "termios", -] +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" [[package]] -name = "serial-windows" -version = "0.4.0" +name = "rustc-demangle" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15c6d3b776267a75d31bbdfd5d36c0ca051251caafc285827052bc53bcdc8162" -dependencies = [ - "libc", - "serial-core", -] +checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" [[package]] name = "serial2" @@ -322,117 +197,90 @@ dependencies = [ ] [[package]] -name = "serialport" -version = "4.2.0" +name = "serial_enumerator" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aab92efb5cf60ad310548bc3f16fa6b0d950019cb7ed8ff41968c3d03721cf12" +checksum = "12db7426ede8268552c2370897ebe98e64520ec27969c65f7b95800aafe636a0" dependencies = [ - "CoreFoundation-sys", - "IOKit-sys", - "bitflags", - "cfg-if", - "libudev", - "mach 0.3.2", - "nix", - "regex", - "winapi", + "nom", + "windows", ] [[package]] -name = "sha2" -version = "0.10.6" +name = "sharded-slab" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" dependencies = [ - "cfg-if", - "cpufeatures", - "digest", + "lazy_static", ] [[package]] -name = "syn" -version = "1.0.105" +name = "thread_local" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b9b43d45702de4c839cb9b51d9f529c5dd26a4aff255b42b1ebc03e88ee908" +checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", + "once_cell", ] [[package]] -name = "termios" -version = "0.2.2" +name = "tracing" +version = "0.1.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5d9cf598a6d7ce700a4e6a9199da127e6819a61e64b68609683cc9a01b5683a" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" dependencies = [ - "libc", + "cfg-if", + "pin-project-lite", + "tracing-core", ] [[package]] -name = "thiserror" -version = "1.0.37" +name = "tracing-core" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" +checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" dependencies = [ - "thiserror-impl", + "once_cell", + "valuable", ] [[package]] -name = "thiserror-impl" -version = "1.0.37" +name = "tracing-error" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" +checksum = "d686ec1c0f384b1277f097b2f279a2ecc11afe8c133c1aabf036a27cb4cd206e" dependencies = [ - "proc-macro2", - "quote", - "syn", + "tracing", + "tracing-subscriber", ] [[package]] -name = "tokio" -version = "1.23.0" +name = "tracing-subscriber" +version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eab6d665857cc6ca78d6e80303a02cea7a7851e85dfbd77cbdc09bd129f1ef46" +checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70" dependencies = [ - "autocfg", - "num_cpus", - "pin-project-lite", - "windows-sys", + "sharded-slab", + "thread_local", + "tracing-core", ] [[package]] name = "tudelft-serial-upload" version = "0.1.0" dependencies = [ - "crc16", + "color-eyre", "expect-test", - "leb128", - "serial", "serial2", - "serialport", - "sha2", - "thiserror", - "tokio", + "serial_enumerator", ] [[package]] -name = "typenum" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" - -[[package]] -name = "unicode-ident" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" - -[[package]] -name = "version_check" -version = "0.9.4" +name = "valuable" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" [[package]] name = "winapi" @@ -457,58 +305,44 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] -name = "windows-sys" -version = "0.42.0" +name = "windows" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +checksum = "aac7fef12f4b59cd0a29339406cc9203ab44e440ddff6b3f5a41455349fa9cf3" dependencies = [ - "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", "windows_i686_msvc", "windows_x86_64_gnu", - "windows_x86_64_gnullvm", "windows_x86_64_msvc", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" - [[package]] name = "windows_aarch64_msvc" -version = "0.42.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" +checksum = "c3d027175d00b01e0cbeb97d6ab6ebe03b12330a35786cbaca5252b1c4bf5d9b" [[package]] name = "windows_i686_gnu" -version = "0.42.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" +checksum = "8793f59f7b8e8b01eda1a652b2697d87b93097198ae85f823b969ca5b89bba58" [[package]] name = "windows_i686_msvc" -version = "0.42.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" +checksum = "8602f6c418b67024be2996c512f5f995de3ba417f4c75af68401ab8756796ae4" [[package]] name = "windows_x86_64_gnu" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" +checksum = "f3d615f419543e0bd7d2b3323af0d86ff19cbc4f816e6453f36a2c2ce889c354" [[package]] name = "windows_x86_64_msvc" -version = "0.42.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" +checksum = "11d95421d9ed3672c280884da53201a5c46b7b2765ca6faf34b0d71cf34a3561" diff --git a/Cargo.toml b/Cargo.toml index a99f1174f96cd2bdf86a4dead104ca7824b6b3e8..7c61dda476c37b32e3273e467a51f4815924b773 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,14 +6,9 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -sha2 = "0.10.6" -leb128 = "0.2.5" -serial = "0.4.0" serial2 = "0.1.7" -serialport = "4.2.0" -crc16 = "0.4.0" -thiserror = "1.0.37" -tokio = {version="1.23.0", features=["time", "rt-multi-thread"]} +serial_enumerator = "0.2.5" +color-eyre = "0.6.2" [dev-dependencies] expect-test = "1.4.0" diff --git a/README.md b/README.md index f200ec120cde6d2c74e526aada52cd97ff11c550..8e48de0bb93ea5de834c662a29d3797550bc3919 100644 --- a/README.md +++ b/README.md @@ -3,5 +3,3 @@ Serial upload library for the quadrupel drone project of the Embedded Systems Lab at the TU Delft (`CESE4030`) - -Modified version of <https://github.com/ferrous-systems/nrfdfu-rs>, in library form. diff --git a/legacy/fcb_software b/legacy/fcb_software deleted file mode 160000 index ae7862257f18f29a727a1c07ac60b7a42d3822e3..0000000000000000000000000000000000000000 --- a/legacy/fcb_software +++ /dev/null @@ -1 +0,0 @@ -Subproject commit ae7862257f18f29a727a1c07ac60b7a42d3822e3 diff --git a/nrfdfu-rs b/nrfdfu-rs deleted file mode 160000 index f44c4b0307fd8924b5e6805a8b5db412c8bd406e..0000000000000000000000000000000000000000 --- a/nrfdfu-rs +++ /dev/null @@ -1 +0,0 @@ -Subproject commit f44c4b0307fd8924b5e6805a8b5db412c8bd406e diff --git a/src/crc.rs b/src/crc.rs index 7905b44eccbb1d725b4a62fafa1c15ef5105f593..2e4cd10166285333cda31c542c108552179b9b0e 100644 --- a/src/crc.rs +++ b/src/crc.rs @@ -10,7 +10,8 @@ pub fn calc_crc16(data: &[u8], start: Option<u16>) -> u16 { crc ^= (crc << 8) << 4; crc ^= ((crc & 0x00FF) << 4) << 1; } - return crc; + + crc } pub fn calc_crc16_default(data: &[u8]) -> u16 { diff --git a/src/error.rs b/src/error.rs deleted file mode 100644 index 1132b28a43a8d8226e484c1f3f8c75a4f0b6aacb..0000000000000000000000000000000000000000 --- a/src/error.rs +++ /dev/null @@ -1,30 +0,0 @@ -use thiserror::Error; - -#[derive(Debug, Error)] -pub enum UploadError { - #[error("failed to upload due to the following errors:\n{}", _0.iter().map(|i| i.to_string()).collect::<Vec<_>>().join("\n"))] - Multiple(Vec<UploadError>), - - #[error("errored while listing available serial ports: {0}")] - ListSerialPort(serialport::Error), - - #[error("errored while opening serial port at {0}: {1}")] - OpenPortError(String, serialport::Error), - - #[error("errored while writing to serial port: {0}")] - WriteError(std::io::Error), - - #[error("errored while reading from serial port: {0}")] - ReadError(std::io::Error), - - #[error("errored while reading from file to upload: {0}")] - ReadFile(String), - - #[error("wait for ack timed out")] - TimeoutError, - - #[error("malformed response: escape byte followed by either nothing or an invalid byte: {0:?}")] - InvalidOrNoByteAfterEscape(Option<u8>) -} - -pub type Result<T, E = UploadError> = std::result::Result<T, E>; diff --git a/src/lib.rs b/src/lib.rs index 94f6522415ea7d11bf7613795308b197bde5d56e..303e7d8934b63c3907cd7c98236ba863b5f68015 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,15 +1,14 @@ extern crate core; +mod crc; mod selector; -mod upload; -mod error; mod serial; -mod crc; +mod upload; use std::time::Duration; +pub use color_eyre; pub use selector::PortSelector; -pub use error::UploadError; -pub use upload::{upload, upload_or_stop, async_upload, upload_file_or_stop, upload_file}; +pub use upload::{upload, upload_file, upload_file_or_stop, upload_or_stop}; -const SERIAL_TIMEOUT: Duration = Duration::from_secs(5); +const SERIAL_TIMEOUT: Duration = Duration::from_secs(1); diff --git a/src/selector.rs b/src/selector.rs index b0e9136349f7809c0a4857a306668687b0e5e0d5..260bcca68dd2b8bf592555e8165f798b31629e31 100644 --- a/src/selector.rs +++ b/src/selector.rs @@ -1,6 +1,5 @@ -use serialport::{available_ports, SerialPortType}; -use crate::error::Result; -use crate::UploadError::ListSerialPort; +use color_eyre::Result; +use serial_enumerator::get_serial_list; #[derive(Default)] // TODO: manufacturer @@ -23,7 +22,7 @@ pub enum PortSelector<'a> { /// Choose to a specific, named serial port /// Note that a conversion from strings exists for this /// variant, so you can just write `upload("/dev/ttyUSB0", ...)` for example. - Named(&'a str) + Named(&'a str), } impl<'a, T: AsRef<str>> From<&'a T> for PortSelector<'a> { @@ -33,11 +32,8 @@ impl<'a, T: AsRef<str>> From<&'a T> for PortSelector<'a> { } pub fn find_available_serial_ports() -> Result<impl Iterator<Item = String>> { - Ok( - available_ports() - .map_err(ListSerialPort)? - .into_iter() - .filter(|i| matches!(i.port_type, SerialPortType::UsbPort(_))) - .map(|i| i.port_name) - ) + Ok(get_serial_list() + .into_iter() + .filter(|i| i.usb_info.is_some()) + .map(|i| i.name)) } diff --git a/src/serial.rs b/src/serial.rs index cf861bd2b35543b667c3ab850f6d7c8c9b665463..345dc7f7173a0bd1f38ec72b0172aa65fa6187d7 100644 --- a/src/serial.rs +++ b/src/serial.rs @@ -1,14 +1,13 @@ -use std::io::{Read, stdout, Write}; -use std::process::exit; +use color_eyre::eyre::{bail, WrapErr}; +use serial2::{FlowControl, SerialPort, Settings}; +use std::io::{stdout, Read, Write}; +use std::sync::mpsc::channel; +use std::thread::{sleep, spawn}; use std::time::Duration; -use serial2::{SerialPort, Settings, FlowControl}; -use serial::core::SerialDevice; -use tokio::time::{sleep, timeout}; + use crate::crc::calc_crc16_default; -use crate::UploadError::{InvalidOrNoByteAfterEscape, ReadError, TimeoutError, WriteError}; -use crate::error::Result; use crate::SERIAL_TIMEOUT; - +use color_eyre::Result; const DFU_INIT_PACKET: u32 = 1; const DFU_START_PACKET: u32 = 3; @@ -20,20 +19,26 @@ const SEND_INIT_PACKET_WAIT_TIME: Duration = Duration::from_secs(1); pub struct Serial { port: SerialPort, + pub(crate) path: String, sequence_number: u8, } impl Serial { - pub fn open(name: String) -> Result<Self> { - let mut port = SerialPort::open(&name, |mut s: Settings| { + pub fn open(path: String) -> Result<Self> { + let mut port = SerialPort::open(&path, |mut s: Settings| { s.set_flow_control(FlowControl::RtsCts); s.set_baud_rate(921600)?; Ok(s) - }).unwrap(); - port.set_read_timeout(SERIAL_TIMEOUT).unwrap(); + }) + .wrap_err("failed to open serial port")?; + + // port.set_read_timeout(SERIAL_TIMEOUT).wrap_err("failed to set read timeout")?; + port.set_write_timeout(SERIAL_TIMEOUT) + .wrap_err("failed to set write timeout")?; Ok(Self { port, + path, sequence_number: 0, }) } @@ -45,7 +50,7 @@ impl Serial { /// For a description of the SLIP header go to: /// http://developer.nordicsemi.com/nRF51_SDK/doc/7.2.0/s110/html/a00093.html - fn create_slip_header(&mut self, pkt_len: usize) -> [u8; 4] { + fn create_slip_header(&mut self, pkt_len: usize) -> ([u8; 4], u8) { assert!(pkt_len < 0x1000); // sequence number @@ -55,8 +60,6 @@ impl Serial { // reliable packet (yes, our (USB) connection is reliable) let rp = true as u8; - println!("SLIP: seq={seq}, pkt_len={pkt_len}"); - // we always send HCI packet, pkt type 14. let pkt_type = 14; @@ -64,31 +67,33 @@ impl Serial { let b2 = pkt_type | ((pkt_len & 0x00f) << 4) as u8; let b3 = ((pkt_len & 0xff0) >> 4) as u8; - println!("b1={b1} b2={b2} b3={b3}"); - - [ - b1, - b2, - b3, - (!b1.wrapping_add(b2).wrapping_add(b3)).wrapping_add(1), - ] + ( + [ + b1, + b2, + b3, + (!b1.wrapping_add(b2).wrapping_add(b3)).wrapping_add(1), + ], + seq, + ) } fn encode_int(i: u32) -> [u8; 4] { i.to_le_bytes() } - fn create_packet(&mut self, data: &[u8]) -> Vec<u8> { + fn create_packet(&mut self, data: &[u8]) -> (Vec<u8>, u8) { let mut temp_res = Vec::new(); + let (bytes, seq_nr) = self.create_slip_header(data.len()); // create header - temp_res.extend_from_slice(&self.create_slip_header(data.len())); + temp_res.extend_from_slice(&bytes); // add data temp_res.extend_from_slice(data); // add crc temp_res.extend_from_slice(&calc_crc16_default(&temp_res).to_le_bytes()); - Self::escape(&temp_res) + (Self::escape(&temp_res), seq_nr) } fn escape(unescaped: &[u8]) -> Vec<u8> { @@ -96,7 +101,7 @@ impl Serial { for &i in unescaped { match i { 0xc0 => res.extend_from_slice(&[0xdb, 0xdc]), - 0xdb => res.extend_from_slice(&[0xc0, 0xdd]), + 0xdb => res.extend_from_slice(&[0xdb, 0xdd]), a => res.push(a), } } @@ -113,46 +118,58 @@ impl Serial { 0xdb => match iter.next() { Some(0xdc) => 0xc0, Some(0xdd) => 0xdb, - i => return Err(InvalidOrNoByteAfterEscape(i.copied())) - } - i => i + i => bail!("encountered invalid byte '{i:?}' after escape character"), + }, + i => i, }); } Ok(res) } - pub async fn send_data(&mut self, data: &[u8]) -> Result<()> { - let packet = self.create_packet(data); + pub fn send_data(&mut self, data: &[u8]) -> Result<()> { + let (packet, seq_nr) = self.create_packet(data); - println!("send: {packet:?}"); + // println!("send: {:?}", packet.iter().map(|i| format!("{:02x}", i).chars().collect::<Vec<_>>()).flatten().collect::<String>()); - self.port.write_all(&packet) - .map_err(WriteError)?; - sleep(Duration::from_millis(500)).await; + self.port + .write_all(&packet) + .wrap_err("failed to write to serial port")?; + sleep(Duration::from_millis(40)); - if let Ok(i) = timeout( - SERIAL_TIMEOUT, - self.wait_for_ack(), - ).await { - i.map(|_| ()) - } else { - Err(TimeoutError) + let res = self.wait_for_ack() + .wrap_err("waiting for message acknowledgement. If this is due to a timeout, try resetting your board, or turning it off and on again")?; + + if res != (seq_nr + 1) % 8 { + bail!("received invalid sequence number, retry transmission") } + + Ok(()) } - pub async fn wait_for_ack(&mut self) -> Result<u8> { + pub fn wait_for_ack(&mut self) -> Result<u8> { + let (tx, rx) = channel(); + + spawn(move || { + if rx.recv_timeout(SERIAL_TIMEOUT).is_err() { + println!("Your read operation seems to be timing out. Make sure you reset your board before uploading a program"); + println!("and try turning it off and on again. We'll keep trying to send data, but most likely the upload has failed now."); + } + }); + let mut response = Vec::new(); while response.iter().filter(|&&i| i == 0xc0).count() < 2 { let mut temp = [0u8; 6]; - self.port.read_exact(&mut temp) - .map_err(ReadError)?; - println!("got bytes: {:?}", temp); - + self.port + .read_exact(&mut temp) + .wrap_err("failed to read from serial port")?; response.extend_from_slice(&temp); } + // ignore error, if the thread died then that's too bad. + let _ = tx.send(()); + let unescaped = Self::unescape(&response)?; // remove 0xc0 at the start and end @@ -161,88 +178,94 @@ impl Serial { Ok(message[0] >> 3 & 0x07) } - pub async fn send_start_dfu(&mut self, file_size: u32) -> Result<()> { + pub fn send_start_dfu(&mut self, file_size: u32) -> Result<()> { let mut res = Vec::new(); - res.extend_from_slice(&Self::encode_int(DFU_START_PACKET)); res.extend_from_slice(&Self::encode_int(4)); res.extend_from_slice(&Self::encode_int(0)); res.extend_from_slice(&Self::encode_int(0)); res.extend_from_slice(&Self::encode_int(file_size)); - println!("packet: {res:?}"); - self.send_data(&res).await?; + self.send_data(&res)?; Ok(()) } - pub async fn send_init_packet(&mut self, file: &[u8]) -> Result<()> { + pub fn send_init_packet(&mut self, file: &[u8]) -> Result<()> { let mut res = vec![]; res.extend_from_slice(&Self::encode_int(DFU_INIT_PACKET)); - res.extend_from_slice(&[0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0xfe, 0xff]); + res.extend_from_slice(&[ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0xfe, 0xff, + ]); res.extend_from_slice(&calc_crc16_default(file).to_le_bytes()); // padding required as per the python reference implementation. No further docs found on this res.extend_from_slice(&[0, 0]); - self.send_data(&res).await?; + self.send_data(&res)?; Ok(()) } - pub async fn send_stop_packet(&mut self) -> Result<()> { + pub fn send_stop_packet(&mut self) -> Result<()> { let mut res = vec![]; res.extend_from_slice(&Self::encode_int(DFU_STOP_DATA_PACKET)); - self.send_data(&res).await?; + self.send_data(&res)?; Ok(()) } - pub async fn send_data_packet(&mut self, data: &[u8]) -> Result<()> { + pub fn send_data_packet(&mut self, data: &[u8]) -> Result<()> { let mut res = vec![]; res.extend_from_slice(&Self::encode_int(DFU_DATA_PACKET)); - res.extend_from_slice(&data); + res.extend_from_slice(data); - self.send_data(&res).await?; + self.send_data(&res)?; Ok(()) } - pub async fn try_do_upload(&mut self, file: &[u8]) -> Result<()> { + pub fn try_do_upload(&mut self, file: &[u8]) -> Result<()> { println!("starting connection..."); - self.send_start_dfu(file.len() as u32).await?; + self.send_start_dfu(file.len() as u32)?; // wait before we actually send data to the board after // we send the start_dfu message - sleep(SEND_START_DFU_WAIT_TIME).await; + sleep(SEND_START_DFU_WAIT_TIME); - println!("intitializing upload..."); - self.send_init_packet(file).await?; + println!("initializing upload..."); + self.send_init_packet(file)?; // wait before we actually send data to the board after // we send the init_packet message - sleep(SEND_INIT_PACKET_WAIT_TIME).await; + sleep(SEND_INIT_PACKET_WAIT_TIME); let total_chunks = (file.len() + DFU_MAX_PACKET_SIZE - 1) / DFU_MAX_PACKET_SIZE; - println!("uploading in {total_chunks} chunks ({}kb)...", file.len() as f64 / 1024.0); + println!( + "uploading in {total_chunks} chunks ({}kb)...", + file.len() as f64 / 1024.0 + ); for (index, i) in file.chunks(DFU_MAX_PACKET_SIZE).enumerate() { - if let Err(e) = self.send_data_packet(i).await { + if let Err(e) = self.send_data_packet(i) { println!(); return Err(e); } - print!("\rframes uploaded: {index}/{total_chunks} = {:.1}%", (index as f64 / total_chunks as f64) * 100.0); + print!( + "\rframes uploaded: {}/{total_chunks} = {:.1}%", + index + 1, + ((index + 1) as f64 / total_chunks as f64) * 100.0 + ); stdout().flush().unwrap(); } println!(); - println!("finalizing upload.."); - self.send_stop_packet().await?; + println!("finalizing upload..."); + self.send_stop_packet()?; println!("done"); Ok(()) } } - diff --git a/src/upload.rs b/src/upload.rs index 77eca3b9941b27b98af0de961dfdba21c952c4ad..f959a406bf2dc657a6bc1ee1dfd0e1761329c566 100644 --- a/src/upload.rs +++ b/src/upload.rs @@ -1,33 +1,39 @@ +use crate::serial::Serial; +use crate::{selector, PortSelector}; +use color_eyre::eyre::{bail, eyre, Context}; +use color_eyre::Result; use std::fs::read; use std::path::Path; -use std::process::{Command, exit}; -use crate::{error, PortSelector, selector, UploadError}; -use crate::serial::Serial; -use crate::UploadError::ReadFile; +use std::process::{exit, Command}; -fn copy_object(source: &Path, target: &Path) -> Result<(), String> { +fn copy_object(source: &Path, target: &Path) -> Result<()> { if Command::new("rust-objcopy").output().is_err() { - return Err("rust-objcopy not found, try installing cargo-binutils or refer to the course website".to_string()); + bail!( + "rust-objcopy not found, try installing cargo-binutils or refer to the course website" + ); } let op = Command::new("rust-objcopy") .arg("-O") .arg("binary") - .arg(&source) - .arg(&target) + .arg(source) + .arg(target) .output() - .map_err(|e| e.to_string())?; + .wrap_err("failed to run rust-objcopy")?; println!("creating binary file at {target:?}"); if !op.status.success() { - return Err(format!("running rust-objcopy failed: {}", String::from_utf8_lossy(&op.stderr))); + bail!( + "running rust-objcopy failed: {}", + String::from_utf8_lossy(&op.stderr) + ); } Ok(()) } -fn read_file(file: &Path) -> Result<Vec<u8>, String> { +fn read_file(file: &Path) -> Result<Vec<u8>> { let mut target = file.to_path_buf(); target.set_extension("bin"); @@ -35,17 +41,19 @@ fn read_file(file: &Path) -> Result<Vec<u8>, String> { copy_object(file, &target)?; println!("reading binary file"); - read(target).map_err(|e| e.to_string()) + read(target).wrap_err("failed to read converted binary file to send to board") } /// Upload a file to a connected board. Select which serial port the board is on with the [`PortSelector`]. /// The file is expected to be the compiled `.elf` file created by cargo/rustc /// Exit with an exit code of 1 when the upload fails. -pub fn upload_file_or_stop<'a>(port: PortSelector<'a>, file: impl AsRef<Path>) { - match read_file(file.as_ref()) { +pub fn upload_file_or_stop(port: PortSelector, file: impl AsRef<Path>) { + match read_file(file.as_ref()) + .wrap_err_with(|| format!("failed to read from file {:?}", file.as_ref())) + { Ok(i) => upload_or_stop(port, i), Err(e) => { - eprintln!("{e}"); + eprintln!("{e:?}"); exit(1); } } @@ -54,17 +62,21 @@ pub fn upload_file_or_stop<'a>(port: PortSelector<'a>, file: impl AsRef<Path>) { /// Upload a file to a connected board. Select which serial port the board is on with the [`PortSelector`] /// The file is expected to be the compiled `.elf` file created by cargo/rustc /// Returns an error when the upload fails. -pub fn upload_file<'a>(port: PortSelector<'a>, file: impl AsRef<Path>) -> error::Result<()> { - upload(port, read_file(file.as_ref()).map_err(ReadFile)?) +pub fn upload_file(port: PortSelector, file: impl AsRef<Path>) -> Result<()> { + upload( + port, + read_file(file.as_ref()) + .wrap_err_with(|| format!("failed to read from file {:?}", file.as_ref()))?, + ) } /// Upload (already read) bytes to a connected board. Select which serial port the board is on with the [`PortSelector`] /// The bytes are the exact bytes that are uploaded to the board. That means it should be a binary file, and *not* contain /// ELF headers or similar /// Exit with an exit code of 1 when the upload fails. -pub fn upload_or_stop<'a>(port: PortSelector<'a>, file: impl AsRef<[u8]>) { - if let Err(e) = upload(port.into(), file.as_ref()) { - eprintln!("{e}"); +pub fn upload_or_stop(port: PortSelector, file: impl AsRef<[u8]>) { + if let Err(e) = upload(port, file.as_ref()) { + eprintln!("{e:?}"); exit(1); } } @@ -73,37 +85,23 @@ pub fn upload_or_stop<'a>(port: PortSelector<'a>, file: impl AsRef<[u8]>) { /// The bytes are the exact bytes that are uploaded to the board. That means it should be a binary file, and *not* contain /// ELF headers or similar /// Returns an error when the upload fails. -pub fn upload<'a>(port: PortSelector<'a>, file: impl AsRef<[u8]>) -> error::Result<()> { - let rt = tokio::runtime::Builder::new_multi_thread() - .enable_all() - .build() - .unwrap(); - - rt.block_on(async { - upload_internal(port, file.as_ref()).await - }) -} - -/// Async version of [`upload`], so you can supply your own `tokio` runtime. No other runtimes are supported. -pub async fn async_upload<'a>(port: PortSelector<'a>, file: impl AsRef<[u8]>) -> error::Result<()> { - upload_internal(port, file.as_ref()).await +pub fn upload(port: PortSelector, file: impl AsRef<[u8]>) -> Result<()> { + upload_internal(port, file.as_ref()) } -async fn upload_internal(port: PortSelector<'_>, file: &[u8]) -> error::Result<()> { - let (ports_to_try, stop_after_first_error): (Vec<error::Result<Serial>>, bool) = match port { - PortSelector::SearchFirst => { - ( - selector::find_available_serial_ports()? - .map(Serial::open) - .collect(), - true - ) - }, +fn upload_internal(port: PortSelector<'_>, file: &[u8]) -> Result<()> { + let (ports_to_try, stop_after_first_error): (Vec<Result<Serial>>, bool) = match port { + PortSelector::SearchFirst => ( + selector::find_available_serial_ports()? + .map(Serial::open) + .collect(), + true, + ), PortSelector::SearchAll => ( selector::find_available_serial_ports()? - .map(Serial::open) - .collect(), - false + .map(Serial::open) + .collect(), + false, ), PortSelector::ChooseInteractive => todo!(), PortSelector::Named(n) => (vec![Serial::open(n.to_string())], false), @@ -127,22 +125,27 @@ async fn upload_internal(port: PortSelector<'_>, file: &[u8]) -> error::Result<( } }; - if let Err(e) = port.try_do_upload(file).await { + if let Err(e) = port + .try_do_upload(file) + .wrap_err_with(|| format!("failed to upload to port {}", port.path)) + { if stop_after_first_error || num_ports == 1 { - return Err(e) + return Err(e); } else { eprintln!("WARNING: {e}"); errors.push(e); - continue + continue; } } - break + break; } if errors.is_empty() { Ok(()) } else { - Err(UploadError::Multiple(errors)) + Err(eyre!( + "uploading failed because none of the ports tried worked (see previous warnings)" + )) } }