From 291ab9fd870c2a2c51558967fbd9cb93baa0405e Mon Sep 17 00:00:00 2001 From: Kurtis Dinelle Date: Fri, 16 Jan 2026 16:28:07 -0800 Subject: [PATCH] Add serial source --- rust/Cargo.lock | 824 +++++++++++++++++++++++++++++++++++- rust/Cargo.toml | 10 +- rust/build.rs | 4 +- rust/src/acpi.rs | 106 ++--- rust/src/app.rs | 20 +- rust/src/battery.rs | 286 +++++-------- rust/src/common.rs | 11 + rust/src/lib.rs | 14 +- rust/src/main.rs | 22 +- rust/src/mock.rs | 55 +-- rust/src/serial.rs | 298 +++++++++++++ rust/src/widgets/battery.rs | 2 +- 12 files changed, 1367 insertions(+), 285 deletions(-) create mode 100644 rust/src/serial.rs diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 171837a..61ccd55 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -32,6 +32,20 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" +[[package]] +name = "aquamarine" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f50776554130342de4836ba542aa85a4ddb361690d7e8df13774d7284c3d5c2" +dependencies = [ + "include_dir", + "itertools 0.10.5", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.104", +] + [[package]] name = "autocfg" version = "1.5.0" @@ -53,12 +67,115 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "bare-metal" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3" +dependencies = [ + "rustc_version", +] + +[[package]] +name = "battery-service-messages" +version = "0.1.0" +source = "git+https://github.com/OpenDevicePartnership/embedded-services?branch=v0.2.0#e2a466fbaaa4bdd1e26ce4d5b226e8722303d214" +dependencies = [ + "embedded-batteries-async", + "embedded-services", + "num_enum", +] + +[[package]] +name = "bincode" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36eaf5d7b090263e8150820482d5d93cd964a81e4019913c972f4edcc6edb740" +dependencies = [ + "unty", +] + +[[package]] +name = "bit-register" +version = "0.1.0" +source = "git+https://github.com/OpenDevicePartnership/odp-utilities#583015c08ad9855f310bdb25d5cf9abff77b5e08" +dependencies = [ + "num-traits", +] + +[[package]] +name = "bitfield" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719" + +[[package]] +name = "bitfield" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f798d2d157e547aa99aab0967df39edd0b70307312b6f8bd2848e6abe40896e0" + +[[package]] +name = "bitfield" +version = "0.19.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21ba6517c6b0f2bf08be60e187ab64b038438f22dd755614d8fe4d4098c46419" +dependencies = [ + "bitfield-macros", +] + +[[package]] +name = "bitfield-macros" +version = "0.19.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f48d6ace212fdf1b45fd6b566bb40808415344642b76c3224c07c8df9da81e97" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "bitfield-struct" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2be5a46ba01b60005ae2c51a36a29cfe134bcacae2dd5cedcd4615fbaad1494b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + [[package]] name = "bitflags" version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + [[package]] name = "cassowary" version = "0.3.0" @@ -121,13 +238,67 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "core-foundation" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cortex-m" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ec610d8f49840a5b376c69663b6369e71f4b34484b9b2eb29fb918d92516cb9" +dependencies = [ + "bare-metal", + "bitfield 0.13.2", + "embedded-hal 0.2.7", + "volatile-register", +] + +[[package]] +name = "cortex-m-rt" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "801d4dec46b34c299ccf6b036717ae0fce602faa4f4fe816d9013b9a7c9f5ba6" +dependencies = [ + "cortex-m-rt-macros", +] + +[[package]] +name = "cortex-m-rt-macros" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e37549a379a9e0e6e576fd208ee60394ccb8be963889eebba3ffe0980364f472" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "critical-section" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" + [[package]] name = "crossterm" version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" dependencies = [ - "bitflags", + "bitflags 2.9.1", "crossterm_winapi", "mio", "parking_lot", @@ -167,7 +338,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn", + "syn 2.0.104", ] [[package]] @@ -178,19 +349,32 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core", "quote", - "syn", + "syn 2.0.104", +] + +[[package]] +name = "document-features" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" +dependencies = [ + "litrs", ] [[package]] name = "ec_demo" version = "0.1.0" dependencies = [ + "battery-service-messages", "color-eyre", "crossterm", + "embedded-services", "env_logger", "log", "ratatui", + "serialport", "strum 0.27.2", + "thermal-service-messages", "tui-input", "uuid", ] @@ -201,6 +385,162 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +[[package]] +name = "embassy-sync" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73974a3edbd0bd286759b3d483540f0ebef705919a5f56f4fc7709066f71689b" +dependencies = [ + "cfg-if", + "critical-section", + "embedded-io-async", + "futures-core", + "futures-sink", + "heapless", +] + +[[package]] +name = "embassy-time" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4fa65b9284d974dad7a23bb72835c4ec85c0b540d86af7fc4098c88cff51d65" +dependencies = [ + "cfg-if", + "critical-section", + "document-features", + "embassy-time-driver", + "embedded-hal 0.2.7", + "embedded-hal 1.0.0", + "embedded-hal-async", + "futures-core", +] + +[[package]] +name = "embassy-time-driver" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0a244c7dc22c8d0289379c8d8830cae06bb93d8f990194d0de5efb3b5ae7ba6" +dependencies = [ + "document-features", +] + +[[package]] +name = "embedded-batteries" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e14d288a59ef41f4e05468eae9b1c9fef6866977cea86d3f1a1ced295b6cab" +dependencies = [ + "bitfield-struct", + "bitflags 2.9.1", + "embedded-hal 1.0.0", + "zerocopy", +] + +[[package]] +name = "embedded-batteries-async" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98cb543f4eea7e2c57544f345a5cf40fd90e9d3593b96cb7515f6c1d62c7fc68" +dependencies = [ + "bitfield-struct", + "embedded-batteries", + "embedded-hal 1.0.0", +] + +[[package]] +name = "embedded-cfu-protocol" +version = "0.2.0" +source = "git+https://github.com/OpenDevicePartnership/embedded-cfu#e0d776017cf34c902c9f2a2be0c75fe73a3a4dda" +dependencies = [ + "embedded-io-async", +] + +[[package]] +name = "embedded-crc-macros" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f1c75747a43b086df1a87fb2a889590bc0725e0abf54bba6d0c4bf7bd9e762c" + +[[package]] +name = "embedded-hal" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff" +dependencies = [ + "nb 0.1.3", + "void", +] + +[[package]] +name = "embedded-hal" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89" + +[[package]] +name = "embedded-hal-async" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4c685bbef7fe13c3c6dd4da26841ed3980ef33e841cddfa15ce8a8fb3f1884" +dependencies = [ + "embedded-hal 1.0.0", +] + +[[package]] +name = "embedded-io" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" + +[[package]] +name = "embedded-io-async" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ff09972d4073aa8c299395be75161d582e7629cd663171d62af73c8d50dba3f" +dependencies = [ + "embedded-io", +] + +[[package]] +name = "embedded-services" +version = "0.1.0" +source = "git+https://github.com/OpenDevicePartnership/embedded-services?branch=v0.2.0#e2a466fbaaa4bdd1e26ce4d5b226e8722303d214" +dependencies = [ + "bitfield 0.17.0", + "bitflags 2.9.1", + "bitvec", + "cfg-if", + "cortex-m", + "cortex-m-rt", + "critical-section", + "document-features", + "embassy-sync", + "embassy-time", + "embedded-batteries-async", + "embedded-cfu-protocol", + "embedded-hal-async", + "embedded-io", + "embedded-io-async", + "embedded-usb-pd", + "heapless", + "mctp-rs", + "num_enum", + "portable-atomic", + "serde", + "uuid", +] + +[[package]] +name = "embedded-usb-pd" +version = "0.1.0" +source = "git+https://github.com/OpenDevicePartnership/embedded-usb-pd#9a42f07ce99a6d91032d7c9792fd87d4b4f49b6f" +dependencies = [ + "aquamarine", + "bincode", + "bitfield 0.19.4", + "embedded-hal-async", +] + [[package]] name = "env_logger" version = "0.10.2" @@ -230,6 +570,19 @@ dependencies = [ "windows-sys 0.60.2", ] +[[package]] +name = "espi-device" +version = "0.1.0" +source = "git+https://github.com/OpenDevicePartnership/haf-ec-service#e9c43ec493ba9c4e3db84c73530f919448c07b6d" +dependencies = [ + "bit-register", + "bitflags 2.9.1", + "num-traits", + "num_enum", + "static_assertions", + "subenum", +] + [[package]] name = "eyre" version = "0.6.12" @@ -252,12 +605,39 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + [[package]] name = "gimli" version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" +[[package]] +name = "hash32" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" +dependencies = [ + "byteorder", +] + [[package]] name = "hashbrown" version = "0.15.4" @@ -269,6 +649,22 @@ dependencies = [ "foldhash", ] +[[package]] +name = "heapless" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" +dependencies = [ + "hash32", + "stable_deref_trait", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + [[package]] name = "heck" version = "0.5.0" @@ -293,6 +689,25 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" +[[package]] +name = "include_dir" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "923d117408f1e49d914f1a379a309cffe4f18c05cf4e3d12e613a15fc81bd0dd" +dependencies = [ + "include_dir_macros", +] + +[[package]] +name = "include_dir_macros" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cab85a7ed0bd5f0e76d93846e0147172bed2e2d3f859bcc33a8d9699cad1a75" +dependencies = [ + "proc-macro2", + "quote", +] + [[package]] name = "indenter" version = "0.3.3" @@ -315,7 +730,17 @@ dependencies = [ "indoc", "proc-macro2", "quote", - "syn", + "syn 2.0.104", +] + +[[package]] +name = "io-kit-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "617ee6cf8e3f66f3b4ea67a4058564628cde41901316e19f559e14c7c72c5e7b" +dependencies = [ + "core-foundation-sys", + "mach2", ] [[package]] @@ -329,6 +754,15 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + [[package]] name = "itertools" version = "0.13.0" @@ -356,12 +790,38 @@ version = "0.2.174" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" +[[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 = "linux-raw-sys" version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" +[[package]] +name = "litrs" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" + [[package]] name = "lock_api" version = "0.4.13" @@ -387,6 +847,27 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "mach2" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d640282b302c0bb0a2a8e0233ead9035e3bed871f0b7e81fe4a1ec829765db44" +dependencies = [ + "libc", +] + +[[package]] +name = "mctp-rs" +version = "0.1.0" +source = "git+https://github.com/dymk/mctp-rs#f3121512468e4776c4b1d2d648b54c7271b97bd9" +dependencies = [ + "bit-register", + "espi-device", + "num_enum", + "smbus-pec", + "thiserror", +] + [[package]] name = "memchr" version = "2.7.5" @@ -414,6 +895,62 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "nb" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f" +dependencies = [ + "nb 1.1.0", +] + +[[package]] +name = "nb" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d" + +[[package]] +name = "nix" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" +dependencies = [ + "bitflags 1.3.2", + "cfg-if", + "libc", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_enum" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1207a7e20ad57b847bbddc6776b968420d38292bbfe2089accff5e19e82454c" +dependencies = [ + "num_enum_derive", + "rustversion", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff32365de1b6743cb203b710788263c44a03de03802daf96092f2da4fe6ba4d7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + [[package]] name = "object" version = "0.36.7" @@ -470,6 +1007,39 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "portable-atomic" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f89776e4d69bb58bc6993e99ffa1d11f228b839984854c7daeb5d37f87cbe950" + +[[package]] +name = "proc-macro-error-attr2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "proc-macro-error2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" +dependencies = [ + "proc-macro-error-attr2", + "proc-macro2", + "quote", +] + [[package]] name = "proc-macro2" version = "1.0.95" @@ -488,19 +1058,25 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + [[package]] name = "ratatui" version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eabd94c2f37801c20583fc49dd5cd6b0ba68c716787c2dd6ed18571e1e63117b" dependencies = [ - "bitflags", + "bitflags 2.9.1", "cassowary", "compact_str", "crossterm", "indoc", "instability", - "itertools", + "itertools 0.13.0", "lru", "paste", "strum 0.26.3", @@ -515,7 +1091,7 @@ version = "0.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" dependencies = [ - "bitflags", + "bitflags 2.9.1", ] [[package]] @@ -553,13 +1129,22 @@ version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver", +] + [[package]] name = "rustix" version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags", + "bitflags 2.9.1", "errno", "libc", "linux-raw-sys", @@ -584,6 +1169,71 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "serialport" +version = "4.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21f60a586160667241d7702c420fc223939fb3c0bb8d3fac84f78768e8970dee" +dependencies = [ + "bitflags 2.9.1", + "cfg-if", + "core-foundation", + "core-foundation-sys", + "io-kit-sys", + "libudev", + "mach2", + "nix", + "quote", + "scopeguard", + "unescaper", + "windows-sys 0.52.0", +] + [[package]] name = "sharded-slab" version = "0.1.7" @@ -629,6 +1279,21 @@ version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +[[package]] +name = "smbus-pec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca0763a680cd5d72b28f7bfc8a054c117d8841380a6ad4f72f05bd2a34217d3e" +dependencies = [ + "embedded-crc-macros", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + [[package]] name = "static_assertions" version = "1.1.0" @@ -665,11 +1330,11 @@ version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" dependencies = [ - "heck", + "heck 0.5.0", "proc-macro2", "quote", "rustversion", - "syn", + "syn 2.0.104", ] [[package]] @@ -678,10 +1343,33 @@ version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" dependencies = [ - "heck", + "heck 0.5.0", "proc-macro2", "quote", - "syn", + "syn 2.0.104", +] + +[[package]] +name = "subenum" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f5d5dfb8556dd04017db5e318bbeac8ab2b0c67b76bf197bfb79e9b29f18ecf" +dependencies = [ + "heck 0.4.1", + "proc-macro2", + "quote", + "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]] @@ -695,6 +1383,12 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + [[package]] name = "termcolor" version = "1.4.1" @@ -704,6 +1398,36 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "thermal-service-messages" +version = "0.1.0" +source = "git+https://github.com/OpenDevicePartnership/embedded-services?branch=v0.2.0#e2a466fbaaa4bdd1e26ce4d5b226e8722303d214" +dependencies = [ + "embedded-services", + "num_enum", + "uuid", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + [[package]] name = "thread_local" version = "1.1.9" @@ -764,6 +1488,15 @@ dependencies = [ "unicode-width 0.2.0", ] +[[package]] +name = "unescaper" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4064ed685c487dbc25bd3f0e9548f2e34bab9d18cefc700f9ec2dba74ba1138e" +dependencies = [ + "thiserror", +] + [[package]] name = "unicode-ident" version = "1.0.18" @@ -782,7 +1515,7 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b3644627a5af5fa321c95b9b235a72fd24cd29c648c2c379431e6628655627bf" dependencies = [ - "itertools", + "itertools 0.13.0", "unicode-segmentation", "unicode-width 0.1.14", ] @@ -799,6 +1532,12 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" +[[package]] +name = "unty" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d49784317cd0d1ee7ec5c716dd598ec5b4483ea832a2dced265471cc0f690ae" + [[package]] name = "uuid" version = "1.17.0" @@ -811,6 +1550,27 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" +[[package]] +name = "vcell" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002" + +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + +[[package]] +name = "volatile-register" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de437e2a6208b014ab52972a27e59b33fa2920d3e00fe05026167a1c509d19cc" +dependencies = [ + "vcell", +] + [[package]] name = "wasi" version = "0.11.1+wasi-snapshot-preview1" @@ -854,6 +1614,15 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + [[package]] name = "windows-sys" version = "0.59.0" @@ -1000,3 +1769,32 @@ name = "windows_x86_64_msvc" version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "zerocopy" +version = "0.8.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "668f5168d10b9ee831de31933dc111a459c97ec93225beb307aed970d1372dfd" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c7962b26b0a8685668b671ee4b54d007a67d4eaf05fda79ac0ecf41e32270f1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] diff --git a/rust/Cargo.toml b/rust/Cargo.toml index a5ae733..bf1ad1d 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "RUST based UI to demo EC features" authors = ["Phil Weber "] repository = "https://github.com/OpenDevicePartnership/ec_demo" -rust-version = "1.85" +rust-version = "1.88" [dependencies] # dependencies for all targets @@ -18,9 +18,17 @@ log = "0.4" env_logger = "0.10" tui-input = "0.14.0" uuid = { version = "1.17.0", default-features = false } +thermal-service-messages = { git = "https://github.com/OpenDevicePartnership/embedded-services", branch = "v0.2.0" } +battery-service-messages = { git = "https://github.com/OpenDevicePartnership/embedded-services", branch = "v0.2.0" } + +# Serial feature specific +serialport = { version = "4.8.1", optional = true } +embedded-services = { git = "https://github.com/OpenDevicePartnership/embedded-services", branch = "v0.2.0", optional = true } [features] mock = [] +acpi = [] +serial = ["dep:serialport", "dep:embedded-services"] [lints.clippy] suspicious = "forbid" diff --git a/rust/build.rs b/rust/build.rs index ca8142c..7ad10b6 100644 --- a/rust/build.rs +++ b/rust/build.rs @@ -4,8 +4,8 @@ use std::fs; fn main() { // Don't do a fancy build if we're just testing our TUI - if env::var("CARGO_FEATURE_MOCK").is_ok() { - println!("cargo:warning=Skipping build.rs logic because 'mock' feature is enabled."); + if env::var("CARGO_FEATURE_ACPI").is_err() { + println!("cargo:warning=Skipping build.rs logic because 'acpi' feature is not enabled."); return; } diff --git a/rust/src/acpi.rs b/rust/src/acpi.rs index 037316b..6f675c1 100644 --- a/rust/src/acpi.rs +++ b/rust/src/acpi.rs @@ -1,4 +1,7 @@ use crate::{Source, Threshold, common}; +use battery_service_messages::{ + BatteryState, BixFixedStrings, BstReturn, bat_swap_try_from_u32, bat_tech_try_from_u32, power_unit_try_from_u32, +}; use color_eyre::{Result, eyre::eyre}; use std::ffi; @@ -7,25 +10,6 @@ unsafe extern "C" { fn EvaluateAcpi(input: *const i8, input_len: usize, buffer: *mut u8, buf_len: &mut usize) -> i32; } -mod guid { - pub const _SENSOR_CRT_TEMP: uuid::Uuid = uuid::uuid!("218246e7-baf6-45f1-aa13-07e4845256b8"); - pub const _SENSOR_PROCHOT_TEMP: uuid::Uuid = uuid::uuid!("22dc52d2-fd0b-47ab-95b8-26552f9831a5"); - pub const FAN_ON_TEMP: uuid::Uuid = uuid::uuid!("ba17b567-c368-48d5-bc6f-a312a41583c1"); - pub const FAN_RAMP_TEMP: uuid::Uuid = uuid::uuid!("3a62688c-d95b-4d2d-bacc-90d7a5816bcd"); - pub const FAN_MAX_TEMP: uuid::Uuid = uuid::uuid!("dcb758b1-f0fd-4ec7-b2c0-ef1e2a547b76"); - pub const FAN_MIN_RPM: uuid::Uuid = uuid::uuid!("db261c77-934b-45e2-9742-256c62badb7a"); - pub const FAN_MAX_RPM: uuid::Uuid = uuid::uuid!("5cf839df-8be7-42b9-9ac5-3403ca2c8a6a"); - pub const FAN_CURRENT_RPM: uuid::Uuid = uuid::uuid!("adf95492-0776-4ffc-84f3-b6c8b5269683"); -} - -fn cstr_bytes_to_string(raw: &[u8]) -> Result { - Ok(ffi::CStr::from_bytes_until_nul(raw) - .map_err(|_| color_eyre::eyre::eyre!("Invalid byte slice"))? - .to_str() - .map_err(|_| color_eyre::eyre::eyre!("String contains invalid characters"))? - .to_owned()) -} - // A user-friendly ACPI input method containing a name and optional arguments struct AcpiMethodInput<'a, 'b> { name: &'a str, @@ -296,73 +280,93 @@ impl Source for Acpi { } fn get_rpm(&self) -> Result { - acpi_get_var(guid::FAN_CURRENT_RPM) + acpi_get_var(common::guid::FAN_CURRENT_RPM) } fn get_min_rpm(&self) -> Result { - acpi_get_var(guid::FAN_MIN_RPM) + acpi_get_var(common::guid::FAN_MIN_RPM) } fn get_max_rpm(&self) -> Result { - acpi_get_var(guid::FAN_MAX_RPM) + acpi_get_var(common::guid::FAN_MAX_RPM) } fn get_threshold(&self, threshold: Threshold) -> Result { match threshold { - Threshold::On => Ok(common::dk_to_c(acpi_get_var(guid::FAN_ON_TEMP)? as u32)), - Threshold::Ramping => Ok(common::dk_to_c(acpi_get_var(guid::FAN_RAMP_TEMP)? as u32)), - Threshold::Max => Ok(common::dk_to_c(acpi_get_var(guid::FAN_MAX_TEMP)? as u32)), + Threshold::On => Ok(common::dk_to_c(acpi_get_var(common::guid::FAN_ON_TEMP)? as u32)), + Threshold::Ramping => Ok(common::dk_to_c(acpi_get_var(common::guid::FAN_RAMP_TEMP)? as u32)), + Threshold::Max => Ok(common::dk_to_c(acpi_get_var(common::guid::FAN_MAX_TEMP)? as u32)), } } fn set_rpm(&self, rpm: f64) -> Result<()> { - acpi_set_var(guid::FAN_CURRENT_RPM, rpm) + acpi_set_var(common::guid::FAN_CURRENT_RPM, rpm) } - fn get_bst(&self) -> Result { + fn get_bst(&self) -> Result { let data = Acpi::evaluate("\\_SB.ECT0.TBST", None)?; // We are expecting 4 32-bit values if data.count != 4 { Err(eyre!("GET_BST unrecognized output")) } else { - Ok(crate::battery::BstData { - state: crate::battery::ChargeState::try_from(data.arguments[0].data_32)?, - rate: data.arguments[1].data_32, - capacity: data.arguments[2].data_32, - voltage: data.arguments[3].data_32, + Ok(BstReturn { + battery_state: BatteryState::from_bits(data.arguments[0].data_32) + .ok_or(eyre!("Invalid BatteryState"))?, + battery_present_rate: data.arguments[1].data_32, + battery_remaining_capacity: data.arguments[2].data_32, + battery_present_voltage: data.arguments[3].data_32, }) } } - fn get_bix(&self) -> Result { + fn get_bix(&self) -> Result { let data = Acpi::evaluate("\\_SB.ECT0.TBIX", None)?; // We are expecting 21 arguments if data.count != 21 { Err(eyre!("GET_BIX unrecognized output")) } else { - Ok(crate::battery::BixData { + Ok(BixFixedStrings { revision: data.arguments[0].data_32, - power_unit: crate::battery::PowerUnit::try_from(data.arguments[1].data_32)?, + power_unit: power_unit_try_from_u32(data.arguments[1].data_32) + .map_err(|_| eyre!("Invalid PowerUnit"))?, design_capacity: data.arguments[2].data_32, - last_full_capacity: data.arguments[3].data_32, - battery_technology: crate::battery::BatteryTechnology::try_from(data.arguments[4].data_32)?, + last_full_charge_capacity: data.arguments[3].data_32, + battery_technology: bat_tech_try_from_u32(data.arguments[4].data_32) + .map_err(|_| eyre!("Invalid BatteryTechnology"))?, design_voltage: data.arguments[5].data_32, - warning_capacity: data.arguments[6].data_32, - low_capacity: data.arguments[7].data_32, + design_cap_of_warning: data.arguments[6].data_32, + design_cap_of_low: data.arguments[7].data_32, cycle_count: data.arguments[8].data_32, - accuracy: data.arguments[9].data_32, - max_sample_time: data.arguments[10].data_32, - min_sample_time: data.arguments[11].data_32, - max_average_interval: data.arguments[12].data_32, - min_average_interval: data.arguments[13].data_32, - capacity_gran1: data.arguments[14].data_32, - capacity_gran2: data.arguments[15].data_32, - model_number: cstr_bytes_to_string(&data.arguments[16].data)?, - serial_number: cstr_bytes_to_string(&data.arguments[17].data)?, - battery_type: cstr_bytes_to_string(&data.arguments[18].data)?, - oem_info: cstr_bytes_to_string(&data.arguments[19].data)?, - swap_cap: crate::battery::SwapCap::try_from(data.arguments[20].data_32)?, + measurement_accuracy: data.arguments[9].data_32, + max_sampling_time: data.arguments[10].data_32, + min_sampling_time: data.arguments[11].data_32, + max_averaging_interval: data.arguments[12].data_32, + min_averaging_interval: data.arguments[13].data_32, + battery_capacity_granularity_1: data.arguments[14].data_32, + battery_capacity_granularity_2: data.arguments[15].data_32, + model_number: data.arguments[16] + .data + .clone() + .try_into() + .map_err(|_| eyre!("Invalid model number"))?, + serial_number: data.arguments[17] + .data + .clone() + .try_into() + .map_err(|_| eyre!("Invalid serial number"))?, + battery_type: data.arguments[18] + .data + .clone() + .try_into() + .map_err(|_| eyre!("Invalid battery type"))?, + oem_info: data.arguments[19] + .data + .clone() + .try_into() + .map_err(|_| eyre!("Invalid OEM info"))?, + battery_swapping_capability: bat_swap_try_from_u32(data.arguments[20].data_32) + .map_err(|_| eyre!("Invalid BatterySwapCapability"))?, }) } } diff --git a/rust/src/app.rs b/rust/src/app.rs index bf52b14..df73039 100644 --- a/rust/src/app.rs +++ b/rust/src/app.rs @@ -124,16 +124,16 @@ impl App { fn handle_events(&mut self) -> std::io::Result<()> { let evt = event::read()?; - if let Event::Key(key) = evt { - if key.kind == KeyEventKind::Press { - match key.code { - KeyCode::Char('l') | KeyCode::Right => self.next_tab(), - KeyCode::Char('h') | KeyCode::Left => self.previous_tab(), - KeyCode::Char('q') | KeyCode::Esc => self.quit(), - - // Let the current tab handle event in this case - _ => self.handle_tab_event(&evt), - } + if let Event::Key(key) = evt + && key.kind == KeyEventKind::Press + { + match key.code { + KeyCode::Char('l') | KeyCode::Right => self.next_tab(), + KeyCode::Char('h') | KeyCode::Left => self.previous_tab(), + KeyCode::Char('q') | KeyCode::Esc => self.quit(), + + // Let the current tab handle event in this case + _ => self.handle_tab_event(&evt), } } Ok(()) diff --git a/rust/src/battery.rs b/rust/src/battery.rs index ede1778..0ed2ba1 100644 --- a/rust/src/battery.rs +++ b/rust/src/battery.rs @@ -2,7 +2,10 @@ use crate::Source; use crate::app::Module; use crate::common; use crate::widgets::battery; -use color_eyre::{Report, Result, eyre::eyre}; +use battery_service_messages::{ + BatteryState, BatterySwapCapability, BatteryTechnology, BixFixedStrings, BstReturn, PowerUnit, +}; +use core::ffi::CStr; use ratatui::style::Modifier; use ratatui::text::Text; @@ -23,160 +26,52 @@ const BATGAUGE_COLOR_LOW: Color = tailwind::RED.c500; const LABEL_COLOR: Color = tailwind::SLATE.c200; const MAX_SAMPLES: usize = 60; -#[derive(Debug, Clone, Copy, PartialEq, Default)] -pub enum ChargeState { - #[default] - Charging, - Discharging, +fn str_from_bytes(bytes: &[u8]) -> String { + CStr::from_bytes_until_nul(bytes) + .ok() + .and_then(|c| c.to_str().ok()) + .unwrap_or("Invalid") + .to_owned() } -impl TryFrom for ChargeState { - type Error = Report; - fn try_from(value: u32) -> Result { - match value { - 1 => Ok(Self::Discharging), - 2 => Ok(Self::Charging), - _ => Err(eyre!("Unknown charging state")), - } +fn charge_state_as_str(state: BatteryState) -> &'static str { + if state.contains(BatteryState::DISCHARGING) { + "Discharging" + } else { + "Charging" } } -impl ChargeState { - fn as_str(&self) -> &'static str { - match self { - Self::Charging => "Charging", - Self::Discharging => "Discharging", - } +fn power_unit_as_capacity_str(power_unit: PowerUnit) -> &'static str { + match power_unit { + PowerUnit::MilliWatts => "mWh", + PowerUnit::MilliAmps => "mAh", } } -#[derive(Debug, Clone, Copy, PartialEq, Default)] -pub enum PowerUnit { - #[default] - Mw, - Ma, -} - -impl TryFrom for PowerUnit { - type Error = Report; - fn try_from(value: u32) -> Result { - match value { - 0 => Ok(Self::Mw), - 1 => Ok(Self::Ma), - _ => Err(eyre!("Unknown power unit")), - } +fn power_unit_as_rate_str(power_unit: PowerUnit) -> &'static str { + match power_unit { + PowerUnit::MilliWatts => "mW", + PowerUnit::MilliAmps => "mA", } } -impl PowerUnit { - fn as_capacity_str(&self) -> &'static str { - match self { - Self::Mw => "mWh", - Self::Ma => "mAh", - } - } - - fn as_rate_str(&self) -> &'static str { - match self { - Self::Mw => "mW", - Self::Ma => "mA", - } +fn bat_tech_as_str(bat_tech: BatteryTechnology) -> &'static str { + match bat_tech { + BatteryTechnology::Primary => "Primary", + BatteryTechnology::Secondary => "Secondary", } } -#[derive(Debug, Clone, Copy, PartialEq, Default)] -pub enum BatteryTechnology { - #[default] - Primary, - Secondary, -} - -impl TryFrom for BatteryTechnology { - type Error = Report; - fn try_from(value: u32) -> Result { - match value { - 0 => Ok(Self::Primary), - 1 => Ok(Self::Secondary), - _ => Err(eyre!("Unknown battery technology")), - } +fn swap_cap_as_str(swap_cap: BatterySwapCapability) -> &'static str { + match swap_cap { + BatterySwapCapability::NonSwappable => "Non swappable", + BatterySwapCapability::ColdSwappable => "Cold swappable", + BatterySwapCapability::HotSwappable => "Hot swappable", } } -impl BatteryTechnology { - fn as_str(&self) -> &'static str { - match self { - Self::Primary => "Primary", - Self::Secondary => "Secondary", - } - } -} - -#[derive(Debug, Clone, Copy, PartialEq, Default)] -pub enum SwapCap { - #[default] - NonSwappable, - ColdSwappable, - HotSwappable, -} - -impl TryFrom for SwapCap { - type Error = Report; - fn try_from(value: u32) -> Result { - match value { - 0 => Ok(Self::NonSwappable), - 1 => Ok(Self::ColdSwappable), - 2 => Ok(Self::HotSwappable), - _ => Err(eyre!("Unknown swapping capability")), - } - } -} - -impl SwapCap { - fn as_str(&self) -> &'static str { - match self { - Self::NonSwappable => "Non swappable", - Self::ColdSwappable => "Cold swappable", - Self::HotSwappable => "Hot swappable", - } - } -} - -/// BST: ACPI Battery Status -#[derive(Default)] -pub struct BstData { - pub state: ChargeState, - pub rate: u32, - pub capacity: u32, - pub voltage: u32, -} - -/// BIX: ACPI Battery Information eXtended -#[derive(Default)] -pub struct BixData { - pub revision: u32, - pub power_unit: PowerUnit, // 0 - mW, 1 - mA - pub design_capacity: u32, - pub last_full_capacity: u32, - pub battery_technology: BatteryTechnology, // 0 - primary, 1 - secondary - pub design_voltage: u32, - pub warning_capacity: u32, - pub low_capacity: u32, - pub cycle_count: u32, - pub accuracy: u32, // Thousands of a percent - pub max_sample_time: u32, - pub min_sample_time: u32, // Milliseconds - pub max_average_interval: u32, - pub min_average_interval: u32, - pub capacity_gran1: u32, - pub capacity_gran2: u32, - pub model_number: String, - pub serial_number: String, - pub battery_type: String, - pub oem_info: String, - pub swap_cap: SwapCap, -} - -struct BatteryState { +struct BatteryTabState { btp: u32, btp_input: Input, bst_success: bool, @@ -185,7 +80,7 @@ struct BatteryState { samples: common::SampleBuf, } -impl Default for BatteryState { +impl Default for BatteryTabState { fn default() -> Self { Self { btp: 0, @@ -200,9 +95,9 @@ impl Default for BatteryState { #[derive(Default)] pub struct Battery { - bst_data: BstData, - bix_data: BixData, - state: BatteryState, + bst_data: BstReturn, + bix_data: BixFixedStrings, + state: BatteryTabState, t_sec: usize, t_min: usize, source: S, @@ -225,11 +120,11 @@ impl Module for Battery { #[cfg(feature = "mock")] let update_graph = true; #[cfg(not(feature = "mock"))] - let update_graph = (self.t_sec % 60) == 0; + let update_graph = self.t_sec.is_multiple_of(60); self.t_sec += 1; if update_graph { - self.state.samples.insert(self.bst_data.capacity); + self.state.samples.insert(self.bst_data.battery_remaining_capacity); self.t_min += 1; } } @@ -309,7 +204,7 @@ impl Battery { x_axis: "Time (m)".to_string(), x_bounds: [0.0, 60.0], x_labels: common::time_labels(self.t_min, MAX_SAMPLES), - y_axis: format!("Capacity ({})", self.bix_data.power_unit.as_capacity_str()), + y_axis: format!("Capacity ({})", power_unit_as_capacity_str(self.bix_data.power_unit)), y_bounds: [0.0, self.bix_data.design_capacity as f64], y_labels, }; @@ -326,19 +221,29 @@ impl Battery { ]), Row::new(vec![ Text::raw("Power Unit").add_modifier(Modifier::BOLD), - format!("{}", self.bix_data.power_unit.as_rate_str()).into(), + power_unit_as_rate_str(power_unit).into(), ]), Row::new(vec![ Text::raw("Design Capacity").add_modifier(Modifier::BOLD), - format!("{} {}", self.bix_data.design_capacity, power_unit.as_capacity_str()).into(), + format!( + "{} {}", + self.bix_data.design_capacity, + power_unit_as_capacity_str(power_unit) + ) + .into(), ]), Row::new(vec![ Text::raw("Last Full Capacity").add_modifier(Modifier::BOLD), - format!("{} {}", self.bix_data.last_full_capacity, power_unit.as_capacity_str()).into(), + format!( + "{} {}", + self.bix_data.last_full_charge_capacity, + power_unit_as_capacity_str(power_unit) + ) + .into(), ]), Row::new(vec![ Text::raw("Battery Technology").add_modifier(Modifier::BOLD), - format!("{}", self.bix_data.battery_technology.as_str()).into(), + bat_tech_as_str(self.bix_data.battery_technology).into(), ]), Row::new(vec![ Text::raw("Design Voltage").add_modifier(Modifier::BOLD), @@ -346,11 +251,21 @@ impl Battery { ]), Row::new(vec![ Text::raw("Warning Capacity").add_modifier(Modifier::BOLD), - format!("{} {}", self.bix_data.warning_capacity, power_unit.as_capacity_str()).into(), + format!( + "{} {}", + self.bix_data.design_cap_of_warning, + power_unit_as_capacity_str(power_unit) + ) + .into(), ]), Row::new(vec![ Text::raw("Low Capacity").add_modifier(Modifier::BOLD), - format!("{} {}", self.bix_data.low_capacity, power_unit.as_capacity_str()).into(), + format!( + "{} {}", + self.bix_data.design_cap_of_low, + power_unit_as_capacity_str(power_unit) + ) + .into(), ]), Row::new(vec![ Text::raw("Cycle Count").add_modifier(Modifier::BOLD), @@ -358,59 +273,70 @@ impl Battery { ]), Row::new(vec![ Text::raw("Accuracy").add_modifier(Modifier::BOLD), - format!("{}%", self.bix_data.accuracy as f64 / 1000.0).into(), + format!("{}%", self.bix_data.measurement_accuracy as f64 / 1000.0).into(), ]), Row::new(vec![ Text::raw("Max Sample Time").add_modifier(Modifier::BOLD), - format!("{} ms", self.bix_data.max_sample_time).into(), + format!("{} ms", self.bix_data.max_sampling_time).into(), ]), Row::new(vec![ Text::raw("Mix Sample Time").add_modifier(Modifier::BOLD), - format!("{} ms", self.bix_data.min_sample_time).into(), + format!("{} ms", self.bix_data.min_sampling_time).into(), ]), Row::new(vec![ Text::raw("Max Average Interval").add_modifier(Modifier::BOLD), - format!("{} ms", self.bix_data.max_average_interval).into(), + format!("{} ms", self.bix_data.max_averaging_interval).into(), ]), Row::new(vec![ Text::raw("Min Average Interval").add_modifier(Modifier::BOLD), - format!("{} ms", self.bix_data.min_average_interval).into(), + format!("{} ms", self.bix_data.min_averaging_interval).into(), ]), Row::new(vec![ Text::raw("Capacity Granularity 1").add_modifier(Modifier::BOLD), - format!("{} {}", self.bix_data.capacity_gran1, power_unit.as_capacity_str()).into(), + format!( + "{} {}", + self.bix_data.battery_capacity_granularity_1, + power_unit_as_capacity_str(power_unit) + ) + .into(), ]), Row::new(vec![ Text::raw("Capacity Granularity 2").add_modifier(Modifier::BOLD), - format!("{} {}", self.bix_data.capacity_gran2, power_unit.as_capacity_str()).into(), + format!( + "{} {}", + self.bix_data.battery_capacity_granularity_2, + power_unit_as_capacity_str(power_unit) + ) + .into(), ]), Row::new(vec![ Text::raw("Model Number").add_modifier(Modifier::BOLD), - format!("{}", self.bix_data.model_number).into(), + str_from_bytes(&self.bix_data.model_number).into(), ]), Row::new(vec![ Text::raw("Serial Number").add_modifier(Modifier::BOLD), - format!("{}", self.bix_data.serial_number).into(), + str_from_bytes(&self.bix_data.serial_number).into(), ]), Row::new(vec![ Text::raw("Battery Type").add_modifier(Modifier::BOLD), - format!("{}", self.bix_data.battery_type).into(), + str_from_bytes(&self.bix_data.battery_type).into(), ]), Row::new(vec![ Text::raw("OEM Info").add_modifier(Modifier::BOLD), - format!("{}", self.bix_data.oem_info).into(), + str_from_bytes(&self.bix_data.oem_info).into(), ]), Row::new(vec![ Text::raw("Swapping Capability").add_modifier(Modifier::BOLD), - format!("{}", self.bix_data.swap_cap.as_str()).into(), + swap_cap_as_str(self.bix_data.battery_swapping_capability).into(), ]), ] } fn render_bix(&self, area: Rect, buf: &mut Buffer) { let widths = [Constraint::Percentage(30), Constraint::Percentage(70)]; + let title = common::title_str_with_status("Battery Info", self.state.bix_success); let table = Table::new(self.create_info(), widths) - .block(Block::bordered().title("Battery Info")) + .block(Block::bordered().title(title)) .style(Style::new().white()); Widget::render(table, area, buf); } @@ -418,18 +344,24 @@ impl Battery { fn create_status(&self) -> Vec> { let power_unit = self.bix_data.power_unit; vec![ - Line::raw(format!("State: {}", self.bst_data.state.as_str())), + Line::raw(format!( + "State: {}", + charge_state_as_str(self.bst_data.battery_state) + )), Line::raw(format!( "Present Rate: {} {}", - self.bst_data.rate, - power_unit.as_rate_str() + self.bst_data.battery_present_rate, + power_unit_as_rate_str(power_unit) )), Line::raw(format!( "Remaining Capacity: {} {}", - self.bst_data.capacity, - power_unit.as_capacity_str() + self.bst_data.battery_remaining_capacity, + power_unit_as_capacity_str(power_unit) + )), + Line::raw(format!( + "Present Voltage: {} mV", + self.bst_data.battery_present_voltage )), - Line::raw(format!("Present Voltage: {} mV", self.bst_data.voltage)), ] } @@ -443,7 +375,7 @@ impl Battery { vec![Line::raw(format!( "Current: {} {}", self.state.btp, - self.bix_data.power_unit.as_capacity_str() + power_unit_as_capacity_str(self.bix_data.power_unit) ))] } @@ -471,16 +403,20 @@ impl Battery { } fn render_battery(&self, area: Rect, buf: &mut Buffer) { - let mut state = - battery::BatteryState::new(self.bst_data.capacity, self.bst_data.state == ChargeState::Charging); + let mut state = battery::BatteryState::new( + self.bst_data.battery_remaining_capacity, + self.bst_data + .battery_state + .contains(battery_service_messages::BatteryState::CHARGING), + ); battery::Battery::default() .color_high(BATGAUGE_COLOR_HIGH) .color_warning(BATGAUGE_COLOR_MEDIUM) .color_low(BATGAUGE_COLOR_LOW) .design_capacity(self.bix_data.design_capacity) - .warning_capacity(self.bix_data.warning_capacity) - .low_capacity(self.bix_data.low_capacity) + .warning_capacity(self.bix_data.design_cap_of_warning) + .low_capacity(self.bix_data.design_cap_of_low) .render(area, buf, &mut state) } } diff --git a/rust/src/common.rs b/rust/src/common.rs index 52bd26c..23e47e2 100644 --- a/rust/src/common.rs +++ b/rust/src/common.rs @@ -8,6 +8,17 @@ use ratatui::{ }; use std::collections::VecDeque; +pub mod guid { + pub const _SENSOR_CRT_TEMP: uuid::Uuid = uuid::uuid!("218246e7-baf6-45f1-aa13-07e4845256b8"); + pub const _SENSOR_PROCHOT_TEMP: uuid::Uuid = uuid::uuid!("22dc52d2-fd0b-47ab-95b8-26552f9831a5"); + pub const FAN_ON_TEMP: uuid::Uuid = uuid::uuid!("ba17b567-c368-48d5-bc6f-a312a41583c1"); + pub const FAN_RAMP_TEMP: uuid::Uuid = uuid::uuid!("3a62688c-d95b-4d2d-bacc-90d7a5816bcd"); + pub const FAN_MAX_TEMP: uuid::Uuid = uuid::uuid!("dcb758b1-f0fd-4ec7-b2c0-ef1e2a547b76"); + pub const FAN_MIN_RPM: uuid::Uuid = uuid::uuid!("db261c77-934b-45e2-9742-256c62badb7a"); + pub const FAN_MAX_RPM: uuid::Uuid = uuid::uuid!("5cf839df-8be7-42b9-9ac5-3403ca2c8a6a"); + pub const FAN_CURRENT_RPM: uuid::Uuid = uuid::uuid!("adf95492-0776-4ffc-84f3-b6c8b5269683"); +} + #[derive(Default)] pub struct SampleBuf { samples: VecDeque, diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 3832492..4c24817 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -1,11 +1,17 @@ +#[cfg(not(any(feature = "mock", feature = "acpi", feature = "serial")))] +compile_error!("At least one of `mock`, `acpi`, or `serial` must be enabled."); + use color_eyre::Result; -#[cfg(not(feature = "mock"))] +#[cfg(feature = "acpi")] pub mod acpi; #[cfg(feature = "mock")] pub mod mock; +#[cfg(feature = "serial")] +pub mod serial; + pub mod app; pub mod battery; pub mod common; @@ -14,6 +20,8 @@ pub mod thermal; pub mod ucsi; pub mod widgets; +use battery_service_messages::{BixFixedStrings, BstReturn}; + /// Trait implemented by all data sources pub trait Source: Clone { /// Get current temperature @@ -35,10 +43,10 @@ pub trait Source: Clone { fn set_rpm(&self, rpm: f64) -> Result<()>; /// Get battery BST data - fn get_bst(&self) -> Result; + fn get_bst(&self) -> Result; /// Get battery BIX data - fn get_bix(&self) -> Result; + fn get_bix(&self) -> Result; /// Set battery trippoint fn set_btp(&self, trippoint: u32) -> Result<()>; diff --git a/rust/src/main.rs b/rust/src/main.rs index ad2434e..0d51885 100644 --- a/rust/src/main.rs +++ b/rust/src/main.rs @@ -3,13 +3,29 @@ use ec_demo::app::App; fn main() -> Result<()> { color_eyre::install()?; - let terminal = ratatui::init(); - #[cfg(not(feature = "mock"))] - let source = ec_demo::acpi::Acpi::default(); + let terminal = ratatui::init(); #[cfg(feature = "mock")] let source = ec_demo::mock::Mock::default(); + #[cfg(feature = "acpi")] + let source = ec_demo::acpi::Acpi::default(); + + #[cfg(feature = "serial")] + let source = { + // Revisit: Quick and easy for grabbing command line args, but when + // debug tab PR is merged this can switch to clap for arg parsing + let mut args = std::env::args().skip(1); + let path = args.next().expect("Serial port path must be provided"); + let baud = args + .next() + .expect("Serial baud rate must be provided") + .parse::() + .expect("Serial baud rate must be a u32"); + + ec_demo::serial::Serial::new(path.as_str(), baud) + }; + App::new(source).run(terminal) } diff --git a/rust/src/mock.rs b/rust/src/mock.rs index 754e872..50f5b11 100644 --- a/rust/src/mock.rs +++ b/rust/src/mock.rs @@ -1,5 +1,8 @@ use crate::{Source, Threshold, common}; -use color_eyre::Result; +use battery_service_messages::{ + BatteryState, BatterySwapCapability, BatteryTechnology, BixFixedStrings, BstReturn, PowerUnit, +}; +use color_eyre::{Result, eyre::eyre}; use std::sync::{ Mutex, OnceLock, atomic::Ordering, @@ -78,7 +81,7 @@ impl Source for Mock { Ok(()) } - fn get_bst(&self) -> Result { + fn get_bst(&self) -> Result { static STATE: AtomicU32 = AtomicU32::new(2); const MAX_CAPACITY: u32 = 10000; static CAPACITY: AtomicU32 = AtomicU32::new(0); @@ -103,37 +106,37 @@ impl Source for Mock { } CAPACITY.store(new_capacity.clamp(0, MAX_CAPACITY), Ordering::Relaxed); - Ok(crate::battery::BstData { - state: crate::battery::ChargeState::try_from(state)?, - rate: 3839, - capacity, - voltage: 12569, + Ok(BstReturn { + battery_state: BatteryState::from_bits(state).ok_or(eyre!("Invalid BatteryState"))?, + battery_present_rate: 3839, + battery_remaining_capacity: capacity, + battery_present_voltage: 12569, }) } - fn get_bix(&self) -> Result { - Ok(crate::battery::BixData { + fn get_bix(&self) -> Result { + Ok(BixFixedStrings { revision: 1, - power_unit: crate::battery::PowerUnit::Mw, + power_unit: PowerUnit::MilliWatts, design_capacity: 10000, - last_full_capacity: 9890, - battery_technology: crate::battery::BatteryTechnology::Primary, + last_full_charge_capacity: 9890, + battery_technology: BatteryTechnology::Primary, design_voltage: 13000, - warning_capacity: 5000, - low_capacity: 3000, + design_cap_of_warning: 5000, + design_cap_of_low: 3000, cycle_count: 1337, - accuracy: 80000, - max_sample_time: 42, - min_sample_time: 7, - max_average_interval: 5, - min_average_interval: 1, - capacity_gran1: 10, - capacity_gran2: 10, - model_number: "42.0".to_string(), - serial_number: "123-45-678".to_string(), - battery_type: "Li-ion".to_string(), - oem_info: "Battery Bros.".to_string(), - swap_cap: crate::battery::SwapCap::ColdSwappable, + measurement_accuracy: 80000, + max_sampling_time: 42, + min_sampling_time: 7, + max_averaging_interval: 5, + min_averaging_interval: 1, + battery_capacity_granularity_1: 10, + battery_capacity_granularity_2: 10, + model_number: [b'4', b'2', b'.', b'0', 0, 0, 0, 0], + serial_number: [b'1', b'2', b'3', b'-', b'4', b'5', 0, 0], + battery_type: [b'L', b'i', b'-', b'o', b'n', 0, 0, 0], + oem_info: [b'B', b'a', b't', b'B', b'r', b'o', b's', 0], + battery_swapping_capability: BatterySwapCapability::ColdSwappable, }) } diff --git a/rust/src/serial.rs b/rust/src/serial.rs new file mode 100644 index 0000000..29de7a2 --- /dev/null +++ b/rust/src/serial.rs @@ -0,0 +1,298 @@ +use crate::{Source, Threshold, common}; +use battery_service_messages::{AcpiBatteryRequest, AcpiBatteryResponse, BixFixedStrings, BstReturn, Btp}; +use color_eyre::{Result, eyre::eyre}; +use embedded_services::relay::{MessageSerializationError, SerializableMessage}; +use serialport::SerialPort; +use std::{ + io::ErrorKind, + sync::{Arc, Mutex}, + time::Duration, +}; +use thermal_service_messages::{ThermalRequest, ThermalResponse}; + +// The higher, the less likely we don't miss a response, but less key responsiveness... +const TIMEOUT: Duration = Duration::from_millis(20); + +const SMBUS_HEADER_SZ: usize = 4; +const SMBUS_LEN_IDX: usize = 2; +const MCTP_FLAGS_IDX: usize = 7; +const MCTP_HEADER_SZ: usize = 4; +const ODP_HEADER_SZ: usize = 3; +const HEADER_SZ: usize = SMBUS_HEADER_SZ + MCTP_HEADER_SZ + ODP_HEADER_SZ; +const CMD_CODE_SZ: usize = 2; +const BUFFER_SZ: usize = 256; + +const THERMAL_VAR_LEN: u16 = 4; +const SENSOR_INSTANCE: u8 = 0; +const BATTERY_INSTANCE: u8 = 0; + +#[derive(Clone, Copy, Debug)] +enum Destination { + Battery, + Thermal, + // Will be used once debug service is added + _Debug, +} + +impl From for u8 { + fn from(dst: Destination) -> Self { + match dst { + Destination::Battery => 0x08, + Destination::Thermal => 0x09, + Destination::_Debug => 0x0A, + } + } +} + +fn prepend_headers(buffer: &mut [u8], dst: Destination, payload_sz: usize) { + // SMBUS + buffer[0] = 0x2; + buffer[1] = 0xF; + buffer[2] = (MCTP_HEADER_SZ + ODP_HEADER_SZ + payload_sz) as u8; + buffer[3] = 0x1; + + // MCTP + buffer[4] = 0x1; + buffer[5] = dst.into(); + buffer[6] = 0x80; + buffer[7] = 0xD3; + + // ODP + buffer[8] = 0x7D; + buffer[9] = 1 << 1; + buffer[10] = dst.into(); +} + +fn append_cmd( + to: &mut [u8], + from: impl SerializableMessage, + cmd_code: u16, +) -> Result { + to[HEADER_SZ..HEADER_SZ + CMD_CODE_SZ].copy_from_slice(&cmd_code.to_be_bytes()); + let payload_sz = from.serialize(&mut to[HEADER_SZ + CMD_CODE_SZ..])?; + Ok(payload_sz + CMD_CODE_SZ) +} + +#[derive(Clone)] +pub struct Serial { + port: Arc>>, +} + +impl Serial { + pub fn new(path: &str, baud_rate: u32) -> Self { + let port = serialport::new(path, baud_rate) + .timeout(TIMEOUT) + .open() + .expect("Serial port must be available"); + port.clear(serialport::ClearBuffer::All) + .expect("Port must be available"); + + Self { + port: Arc::new(Mutex::new(port)), + } + } +} + +impl Serial { + fn send( + &self, + dst: Destination, + request: REQ, + ) -> Result { + let mut buffer = [0u8; BUFFER_SZ]; + + // Serialize command into buffer + let request_sz = append_cmd(&mut buffer, request, request.discriminant()) + .map_err(|e| eyre!("Serialization error: {e:?}"))?; + + // NOTE: The `mctp-rs` crate does not appear to support serializing requests and deserializing + // responses (only the opposite), so we have to do manual serialization until that is changed. + + // And now that we know request size, serialize headers into beginning of buffer + prepend_headers(&mut buffer, dst, request_sz); + + let mut port = self.port.lock().expect("Mutex must not be poisoned"); + + // Write entire request packet + port.write_all(&buffer[..HEADER_SZ + request_sz]) + .map_err(|e| eyre!("Serial error: {e:?}"))?; + port.flush().map_err(|e| eyre!("Serial error: {e:?}"))?; + + // Read response packets + let mut data_buf = [0u8; BUFFER_SZ]; + let mut offset = 0; + loop { + // Wait for SMBUS header from response packet + let mut buffer = [0u8; BUFFER_SZ]; + port.read_exact(&mut buffer[..SMBUS_HEADER_SZ]) + .map_err(|e| eyre!("Serial error: {e:?}"))?; + let len = buffer[SMBUS_LEN_IDX] as usize; + + // Then read rest of response packet + port.read_exact(&mut buffer[SMBUS_HEADER_SZ..SMBUS_HEADER_SZ + len]) + .map_err(|e| eyre!("Serial error: {e:?}"))?; + + // If this is not a SOM packet, we don't receive the ODP header + let flags = buffer[MCTP_FLAGS_IDX]; + let payload_start_idx = if flags & 0x80 != 0 { + HEADER_SZ + CMD_CODE_SZ + } else { + SMBUS_HEADER_SZ + MCTP_HEADER_SZ + }; + + // Occasionally we receive a bad len and this will fail for some reason, so break instead of panic + let data_slice = if let Some(slice) = buffer.get(payload_start_idx..SMBUS_HEADER_SZ + len) { + slice + } else { + break; + }; + + let len = data_slice.len(); + data_buf[offset..offset + len].copy_from_slice(data_slice); + offset += len; + + // If this is EOM packet, we are done + if flags & 0x40 != 0 { + break; + } + } + + // Revisit: Really annoying bug where we somehow occasionally still have bytes incoming when we get here + // even though we should have correctly consumed all bytes the EC sent in response above. + // + // Cannot figure out why... so to prevent the corruption of all future comms over serial, + // just drain and discard these ghost bytes if any. + let mut tmp = [0u8; BUFFER_SZ]; + let mut is_corrupted = false; + loop { + match port.read(&mut tmp) { + // EOF (don't think we would ever actually get this) + Ok(0) => break, + // We have ghost bytes... mark this response corrupted but keep draining them + Ok(_) => { + is_corrupted = true; + continue; + } + // We saw no ghost bytes detected since we timed out + Err(e) if e.kind() == ErrorKind::TimedOut => break, + Err(e) => return Err(eyre!("Serial error {e:?}")), + } + } + if is_corrupted { + return Err(eyre!("Ghost bytes received")); + } + + RESP::deserialize(request.discriminant(), &data_buf).map_err(|e| eyre!("Deserialization error: {e:?}")) + } + + fn thermal_get_var(&self, guid: uuid::Uuid) -> Result { + let request = ThermalRequest::ThermalGetVarRequest { + instance_id: SENSOR_INSTANCE, + len: THERMAL_VAR_LEN, + var_uuid: guid.to_bytes_le(), + }; + let response = self.send(Destination::Thermal, request)?; + + if let ThermalResponse::ThermalGetVarResponse { val } = response { + Ok(val as f64) + } else { + Err(eyre!("GET_VAR received wrong response")) + } + } + + fn thermal_set_var(&self, guid: uuid::Uuid, raw: u32) -> Result<()> { + let request = ThermalRequest::ThermalSetVarRequest { + instance_id: SENSOR_INSTANCE, + len: THERMAL_VAR_LEN, + var_uuid: guid.to_bytes_le(), + set_var: raw, + }; + let response = self.send(Destination::Thermal, request)?; + + if let ThermalResponse::ThermalSetVarResponse = response { + Ok(()) + } else { + Err(eyre!("SET_VAR received wrong response")) + } + } +} + +impl Source for Serial { + fn get_temperature(&self) -> Result { + let request = ThermalRequest::ThermalGetTmpRequest { + instance_id: SENSOR_INSTANCE, + }; + let response = self.send(Destination::Thermal, request)?; + + if let ThermalResponse::ThermalGetTmpResponse { temperature } = response { + Ok(common::dk_to_c(temperature)) + } else { + Err(eyre!("GET_TMP received wrong response")) + } + } + + fn get_rpm(&self) -> Result { + self.thermal_get_var(common::guid::FAN_CURRENT_RPM) + } + + fn get_min_rpm(&self) -> Result { + self.thermal_get_var(common::guid::FAN_MIN_RPM) + } + + fn get_max_rpm(&self) -> Result { + self.thermal_get_var(common::guid::FAN_MAX_RPM) + } + + fn get_threshold(&self, threshold: Threshold) -> Result { + let raw = match threshold { + Threshold::On => self.thermal_get_var(common::guid::FAN_ON_TEMP), + Threshold::Ramping => self.thermal_get_var(common::guid::FAN_RAMP_TEMP), + Threshold::Max => self.thermal_get_var(common::guid::FAN_MAX_TEMP), + }?; + Ok(common::dk_to_c(raw as u32)) + } + + fn set_rpm(&self, rpm: f64) -> Result<()> { + self.thermal_set_var(common::guid::FAN_CURRENT_RPM, rpm as u32) + } + + fn get_bst(&self) -> Result { + let request = AcpiBatteryRequest::BatteryGetBstRequest { + battery_id: BATTERY_INSTANCE, + }; + let response = self.send(Destination::Battery, request)?; + + if let AcpiBatteryResponse::BatteryGetBstResponse { bst } = response { + Ok(bst) + } else { + Err(eyre!("GET_BST received wrong response")) + } + } + + fn get_bix(&self) -> Result { + let request = AcpiBatteryRequest::BatteryGetBixRequest { + battery_id: BATTERY_INSTANCE, + }; + let response = self.send(Destination::Battery, request)?; + + if let AcpiBatteryResponse::BatteryGetBixResponse { bix } = response { + Ok(bix) + } else { + Err(eyre!("GET_BIX received wrong response")) + } + } + + fn set_btp(&self, trip_point: u32) -> Result<()> { + let request = AcpiBatteryRequest::BatterySetBtpRequest { + battery_id: BATTERY_INSTANCE, + btp: Btp { trip_point }, + }; + let response = self.send(Destination::Battery, request)?; + + if matches!(response, AcpiBatteryResponse::BatterySetBtpResponse {}) { + Ok(()) + } else { + Err(eyre!("SET_BTP received wrong response")) + } + } +} diff --git a/rust/src/widgets/battery.rs b/rust/src/widgets/battery.rs index 6197ab2..d168c8c 100644 --- a/rust/src/widgets/battery.rs +++ b/rust/src/widgets/battery.rs @@ -151,7 +151,7 @@ impl StatefulWidget for Battery { .render(tip_area, buf); if state.is_charging { - Bolt::default().render(battery_area, buf) + Bolt.render(battery_area, buf) } } }