diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..40061f3 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "dep/wil"] + path = dep/wil + url = https://github.com/microsoft/wil diff --git a/dep/wil b/dep/wil new file mode 160000 index 0000000..c7bfb48 --- /dev/null +++ b/dep/wil @@ -0,0 +1 @@ +Subproject commit c7bfb48c0d1b41968ceb6d4fcde63a802a167b8e diff --git a/inc/eclib.h b/inc/eclib.h index 1508170..10c4ba8 100644 --- a/inc/eclib.h +++ b/inc/eclib.h @@ -24,7 +24,13 @@ SOFTWARE. #pragma once -#define ECLIB_API __declspec(dllexport) +#ifdef __cplusplus +#define EXTERN_C extern "C" +#else +#define EXTERN_C +#endif + +#define ECLIB_API EXTERN_C __declspec(dllexport) ECLIB_API int GetKMDFDriverHandle( _In_ DWORD flags, diff --git a/lib/eclib.c b/lib/eclib.cpp similarity index 95% rename from lib/eclib.c rename to lib/eclib.cpp index 65783bb..2150db0 100644 --- a/lib/eclib.c +++ b/lib/eclib.cpp @@ -36,6 +36,9 @@ SOFTWARE. #include "..\inc\eclib.h" #include "..\inc\ectest.h" +#include +#include + #define MAX_DEVPATH_LENGTH 64 // GUID defined in the KMDF INX file for ectest.sys @@ -195,34 +198,31 @@ int EvaluateAcpi( // Look up handle to ACPI entry wchar_t* dpath = GetGUIDPath(GUID_DEVCLASS_ECTEST, L"ETST0001", pathbuf, sizeof(pathbuf)); - if (dpath == NULL) { + if (dpath == nullptr) { return ERROR_INVALID_PARAMETER; } - HANDLE hDevice = CreateFile(dpath, + wil::unique_handle hDevice(CreateFile(dpath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, - NULL); - - if (hDevice != INVALID_HANDLE_VALUE) { - if( DeviceIoControl(hDevice, - (DWORD)IOCTL_ACPI_EVAL_METHOD_EX, - acpi_input, - (DWORD)input_len, - buffer, - (DWORD)*buf_len, - &bytesReturned, - NULL) == TRUE ) - { - *buf_len = bytesReturned; - return ERROR_SUCCESS; - } - } - - return ERROR_INVALID_PARAMETER; + NULL)); + + RETURN_LAST_ERROR_IF(!hDevice.is_valid()); + RETURN_IF_WIN32_BOOL_FALSE(DeviceIoControl( + hDevice.get(), + static_cast(IOCTL_ACPI_EVAL_METHOD_EX), + acpi_input, + static_cast(input_len), + buffer, + static_cast(*buf_len), + &bytesReturned, + nullptr)); + + *buf_len = bytesReturned; + return ERROR_SUCCESS; } /* @@ -314,7 +314,7 @@ UINT32 WaitForNotification(UINT32 event) // Make sure Initialization has been done if(g_notify.handle == INVALID_HANDLE_VALUE) { return 0; - } + } // Loop until we get event we are looking for for(;;) { diff --git a/lib/eclib.vcxproj b/lib/eclib.vcxproj index 6452dd8..d6ca687 100644 --- a/lib/eclib.vcxproj +++ b/lib/eclib.vcxproj @@ -97,11 +97,16 @@ + + + stdcpp20 + %(AdditionalIncludeDirectories);$(WindowsSdkDir)\Include\$(Version_Number)\um;$(WindowsSdkDir)\Include\$(Version_Number)\shared;$(WindowsSdkDir)\Include\$(Version_Number)\ucrt;$(MSBuildThisFileDirectory)\..\dep\wil\include + + Level3 true - $(WindowsSdkDir)\Include\$(Version_Number)\um;$(WindowsSdkDir)\Include\$(Version_Number)\shared;$(WindowsSdkDir)\Include\$(Version_Number)\ucrt WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) true NotUsing @@ -118,7 +123,6 @@ true true true - $(WindowsSdkDir)\Include\$(Version_Number)\um;$(WindowsSdkDir)\Include\$(Version_Number)\shared;$(WindowsSdkDir)\Include\$(Version_Number)\ucrt WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) true NotUsing @@ -133,7 +137,6 @@ Level3 true - $(WindowsSdkDir)\Include\$(Version_Number)\um;$(WindowsSdkDir)\Include\$(Version_Number)\shared;$(WindowsSdkDir)\Include\$(Version_Number)\ucrt _DEBUG;_LIB;%(PreprocessorDefinitions) true NotUsing @@ -165,7 +168,6 @@ true true true - $(WindowsSdkDir)\Include\$(Version_Number)\um;$(WindowsSdkDir)\Include\$(Version_Number)\shared;$(WindowsSdkDir)\Include\$(Version_Number)\ucrt NDEBUG;_LIB;%(PreprocessorDefinitions) true NotUsing @@ -185,7 +187,6 @@ true true true - $(WindowsSdkDir)\Include\$(Version_Number)\um;$(WindowsSdkDir)\Include\$(Version_Number)\shared;$(WindowsSdkDir)\Include\$(Version_Number)\ucrt NDEBUG;_LIB;%(PreprocessorDefinitions) true NotUsing @@ -200,7 +201,7 @@ - + diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 171837a..6966d69 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", +] + [[package]] name = "autocfg" version = "1.5.0" @@ -53,12 +67,99 @@ 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 = "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", +] + +[[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", +] + [[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,6 +222,44 @@ dependencies = [ "static_assertions", ] +[[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", +] + +[[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" @@ -181,16 +320,28 @@ dependencies = [ "syn", ] +[[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 = [ "color-eyre", "crossterm", + "embedded-mcu-hal", "env_logger", "log", + "num_enum", "ratatui", "strum 0.27.2", + "time-alarm-service-messages", "tui-input", "uuid", ] @@ -201,6 +352,170 @@ 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", + "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-mcu-hal" +version = "0.1.0" +source = "git+https://github.com/OpenDevicePartnership/embedded-mcu#4dadf27b212ce91d884e9a33b3c2725d5d0511fb" +dependencies = [ + "num_enum", +] + +[[package]] +name = "embedded-services" +version = "0.1.0" +source = "git+https://github.com/OpenDevicePartnership/embedded-services?branch=v0.2.0#f4f36486e4b4f0e9a242c1c50a806b6c57b5b87d" +dependencies = [ + "bitfield 0.17.0", + "bitflags", + "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 +545,19 @@ dependencies = [ "windows-sys 0.60.2", ] +[[package]] +name = "espi-device" +version = "0.1.0" +source = "git+https://github.com/OpenDevicePartnership/haf-ec-service#9805f13c044b0e314d415410c57a8a59a40eabeb" +dependencies = [ + "bit-register", + "bitflags", + "num-traits", + "num_enum", + "static_assertions", + "subenum", +] + [[package]] name = "eyre" version = "0.6.12" @@ -252,12 +580,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 +624,16 @@ 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.5.0" @@ -293,6 +658,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" @@ -329,6 +713,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" @@ -362,6 +755,12 @@ 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 +786,18 @@ dependencies = [ "hashbrown", ] +[[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 +825,51 @@ 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 = "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", +] + [[package]] name = "object" version = "0.36.7" @@ -470,24 +926,57 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" +[[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" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.40" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" 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" @@ -500,7 +989,7 @@ dependencies = [ "crossterm", "indoc", "instability", - "itertools", + "itertools 0.13.0", "lru", "paste", "strum 0.26.3", @@ -553,6 +1042,15 @@ 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" @@ -584,6 +1082,51 @@ 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", +] + [[package]] name = "sharded-slab" version = "0.1.7" @@ -629,6 +1172,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" @@ -684,17 +1242,35 @@ dependencies = [ "syn", ] +[[package]] +name = "subenum" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec3d08fe7078c57309d5c3d938e50eba95ba1d33b9c3a101a8465fc6861a5416" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "syn" -version = "2.0.104" +version = "2.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" +checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a" dependencies = [ "proc-macro2", "quote", "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 +1280,26 @@ dependencies = [ "winapi-util", ] +[[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", +] + [[package]] name = "thread_local" version = "1.1.9" @@ -713,6 +1309,18 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "time-alarm-service-messages" +version = "0.1.0" +source = "git+https://github.com/OpenDevicePartnership/embedded-services?branch=v0.2.0#f4f36486e4b4f0e9a242c1c50a806b6c57b5b87d" +dependencies = [ + "bitfield 0.17.0", + "embedded-mcu-hal", + "embedded-services", + "num_enum", + "zerocopy", +] + [[package]] name = "tracing" version = "0.1.41" @@ -782,7 +1390,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 +1407,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 +1425,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" @@ -1000,3 +1635,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.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdea86ddd5568519879b8187e1cf04e24fce28f7fe046ceecbce472ff19a2572" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c15e1b46eff7c6c91195752e0eeed8ef040e391cdece7c25376957d5f15df22" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/rust/Cargo.toml b/rust/Cargo.toml index a5ae733..a394f1d 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -13,11 +13,14 @@ rust-version = "1.85" crossterm = "0.28.1" ratatui = "0.29.0" color-eyre = "0.6.3" +num_enum = { version = "0.7.5", default-features = false } strum = { version = "0.27", default-features = false, features = ["derive"] } log = "0.4" env_logger = "0.10" tui-input = "0.14.0" uuid = { version = "1.17.0", default-features = false } +time-alarm-service-messages = { git = "https://github.com/OpenDevicePartnership/embedded-services", branch = "v0.2.0" } +embedded-mcu-hal = { git = "https://github.com/OpenDevicePartnership/embedded-mcu" } [features] mock = [] diff --git a/rust/src/acpi.rs b/rust/src/acpi.rs index 037316b..faa4c63 100644 --- a/rust/src/acpi.rs +++ b/rust/src/acpi.rs @@ -1,12 +1,28 @@ -use crate::{Source, Threshold, common}; +use crate::{RtcSource, Source, Threshold, common}; use color_eyre::{Result, eyre::eyre}; use std::ffi; +use time_alarm_service_messages::{ + AcpiTimerId, AcpiTimestamp, AlarmExpiredWakePolicy, AlarmTimerSeconds, TimeAlarmDeviceCapabilities, TimerStatus, +}; // This module maps the data returned from call into the C-Library to RUST structures unsafe extern "C" { fn EvaluateAcpi(input: *const i8, input_len: usize, buffer: *mut u8, buf_len: &mut usize) -> i32; } +#[derive(num_enum::IntoPrimitive, num_enum::TryFromPrimitive, Debug, Copy, Clone)] +#[repr(u16)] +/// ACPI argument types - these correspond to the ACPI_METHOD_ARGUMENT_* defines in apiioct.h from the Windows SDK +enum AcpiArgumentType { + Integer = 0x0, + String = 0x1, + Buffer = 0x2, + Package = 0x3, + PackageEx = 0x4, +} + +const ERROR_SUCCESS: i32 = 0; + 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"); @@ -105,6 +121,7 @@ pub struct AcpiMethodArgumentV1 { pub enum AcpiParseError { InsufficientLength, InvalidFormat, + EvaluationFailed(i32), } pub const ACPI_EVAL_INPUT_BUFFER_COMPLEX_SIGNATURE_EX: u32 = u32::from_le_bytes(*b"AeiF"); @@ -240,7 +257,7 @@ impl Acpi { let mut out_buf_len = 1024; let mut out_buf = vec![0u8; out_buf_len]; - let _res = unsafe { + let res = unsafe { EvaluateAcpi( in_buf.as_ptr() as *const i8, in_buf_len, @@ -249,7 +266,32 @@ impl Acpi { ) }; - AcpiEvalOutputBufferV1::try_from(out_buf) + match res { + ERROR_SUCCESS => AcpiEvalOutputBufferV1::try_from(out_buf), + err => Err(AcpiParseError::EvaluationFailed(err)), + } + } + + /// Evaluates the provided method with the provided arguments and returns its single u32 result. + /// Errors if the result is not a single u32. + pub fn evaluate_u32(name: &str, args: Option<&[AcpiMethodArgument]>) -> Result { + let output = Acpi::evaluate(name, args)?; + + if output.count != 1 { + Err(eyre!( + "{} returned unexpected number of arguments: {}", + name, + output.count + )) + } else if output.arguments[0].type_ != AcpiArgumentType::Integer as u16 { + Err(eyre!( + "{} returned argument of unexpected type: {}", + name, + output.arguments[0].type_ + )) + } else { + Ok(output.arguments[0].data_32) + } } } @@ -373,3 +415,53 @@ impl Source for Acpi { Ok(()) } } + +impl RtcSource for Acpi { + fn get_capabilities(&self) -> Result { + Ok(TimeAlarmDeviceCapabilities(Acpi::evaluate_u32( + "\\_SB.ECT0._GCP", + None, + )?)) + } + + fn get_real_time(&self) -> Result { + let result = Acpi::evaluate("\\_SB.ECT0._GRT", None)?; + if result.count != 1 { + return Err(eyre!("GET_REAL_TIME unrecognized output - got result {:?}", result)); + } + + let result = &result.arguments[0]; + if result.type_ != AcpiArgumentType::Buffer as u16 { + return Err(eyre!("GET_REAL_TIME invalid output type {}", result.type_)); + } + + AcpiTimestamp::try_from_bytes(result.data.as_slice()).map_err(|e| { + eyre!( + "GET_REAL_TIME invalid output format: {:?} for bytes {:?}", + e, + result.data.as_slice() + ) + }) + } + + fn get_wake_status(&self, timer_id: AcpiTimerId) -> Result { + Ok(TimerStatus(Acpi::evaluate_u32( + "\\_SB.ECT0._GWS", + Some(&[AcpiMethodArgument::Int(timer_id.into())]), + )?)) + } + + fn get_expired_timer_wake_policy(&self, timer_id: AcpiTimerId) -> Result { + Ok(AlarmExpiredWakePolicy(Acpi::evaluate_u32( + "\\_SB.ECT0._TIP", + Some(&[AcpiMethodArgument::Int(timer_id.into())]), + )?)) + } + + fn get_timer_value(&self, timer_id: AcpiTimerId) -> Result { + Ok(AlarmTimerSeconds(Acpi::evaluate_u32( + "\\_SB.ECT0._TIV", + Some(&[AcpiMethodArgument::Int(timer_id.into())]), + )?)) + } +} diff --git a/rust/src/app.rs b/rust/src/app.rs index bf52b14..bb1e2c8 100644 --- a/rust/src/app.rs +++ b/rust/src/app.rs @@ -77,12 +77,13 @@ impl App { let thermal_source = Rc::clone(&source); let battery_source = Rc::clone(&source); + let rtc_source = Rc::clone(&source); modules.insert( SelectedTab::TabThermal, Box::new(Thermal::new(thermal_source.borrow().clone())), ); - modules.insert(SelectedTab::TabRTC, Box::new(Rtc::new())); + modules.insert(SelectedTab::TabRTC, Box::new(Rtc::new(rtc_source.borrow().clone()))); modules.insert(SelectedTab::TabUCSI, Box::new(Ucsi::new())); modules.insert( SelectedTab::TabBattery, diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 3832492..3c91af7 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -1,5 +1,9 @@ use color_eyre::Result; +use time_alarm_service_messages::{ + AcpiTimerId, AcpiTimestamp, AlarmExpiredWakePolicy, AlarmTimerSeconds, TimeAlarmDeviceCapabilities, TimerStatus, +}; + #[cfg(not(feature = "mock"))] pub mod acpi; @@ -15,7 +19,7 @@ pub mod ucsi; pub mod widgets; /// Trait implemented by all data sources -pub trait Source: Clone { +pub trait Source: Clone + RtcSource { /// Get current temperature fn get_temperature(&self) -> Result; @@ -44,6 +48,23 @@ pub trait Source: Clone { fn set_btp(&self, trippoint: u32) -> Result<()>; } +pub trait RtcSource: Clone { + /// Get RTC capabilities bitfield - see _GCP + fn get_capabilities(&self) -> Result; + + /// Get RTC time as unix timestamp - see _GRT + fn get_real_time(&self) -> Result; + + /// Query the wake status of the timer - see _GWS + fn get_wake_status(&self, timer_id: AcpiTimerId) -> Result; + + /// Get the expired timer wake policy - see _TIP + fn get_expired_timer_wake_policy(&self, timer_id: AcpiTimerId) -> Result; + + /// Get the timer value - see _TIV + fn get_timer_value(&self, timer_id: AcpiTimerId) -> Result; +} + pub enum Threshold { /// On threshold temperature On, diff --git a/rust/src/mock.rs b/rust/src/mock.rs index 754e872..9cc84aa 100644 --- a/rust/src/mock.rs +++ b/rust/src/mock.rs @@ -1,16 +1,23 @@ -use crate::{Source, Threshold, common}; +use crate::{RtcSource, Source, Threshold, common}; use color_eyre::Result; +use embedded_mcu_hal::time::{Datetime, Month, UncheckedDatetime}; use std::sync::{ Mutex, OnceLock, atomic::Ordering, atomic::{AtomicI64, AtomicU32}, }; +use time_alarm_service_messages::{ + AcpiDaylightSavingsTimeStatus, AcpiTimeZone, AcpiTimeZoneOffset, AcpiTimerId, AcpiTimestamp, + AlarmExpiredWakePolicy, AlarmTimerSeconds, TimeAlarmDeviceCapabilities, TimerStatus, +}; static SET_RPM: AtomicI64 = AtomicI64::new(-1); static SAMPLE: OnceLock> = OnceLock::new(); #[derive(Default, Copy, Clone)] -pub struct Mock {} +pub struct Mock { + rtc: MockRtc, +} impl Mock { pub fn new() -> Self { @@ -142,3 +149,79 @@ impl Source for Mock { Ok(()) } } + +#[derive(Copy, Clone)] +struct MockRtc { + time: AcpiTimestamp, + timers: [MockRtcTimer; 2], +} + +#[derive(Copy, Clone)] +struct MockRtcTimer { + value: AlarmTimerSeconds, + wake_policy: AlarmExpiredWakePolicy, + timer_status: TimerStatus, +} + +impl Default for MockRtcTimer { + fn default() -> Self { + Self { + value: AlarmTimerSeconds(0), + wake_policy: AlarmExpiredWakePolicy::INSTANTLY, + timer_status: TimerStatus(0), + } + } +} + +impl MockRtc { + fn new() -> Self { + Self { + time: AcpiTimestamp { + datetime: Datetime::new(UncheckedDatetime { + year: 2026, + month: Month::January, + day: 1, + ..Default::default() + }) + .expect("statically known valid datetime"), + time_zone: AcpiTimeZone::MinutesFromUtc( + AcpiTimeZoneOffset::new(-8 * 60).expect("statically known valid timezone"), + ), + dst_status: AcpiDaylightSavingsTimeStatus::NotObserved, + }, + timers: [MockRtcTimer::default(); 2], + } + } + + fn get_timer(&self, timer_id: AcpiTimerId) -> &MockRtcTimer { + &self.timers[timer_id as usize] + } +} + +impl Default for MockRtc { + fn default() -> Self { + Self::new() + } +} + +impl RtcSource for Mock { + fn get_capabilities(&self) -> Result { + Ok(TimeAlarmDeviceCapabilities(0xF7)) + } + + fn get_real_time(&self) -> Result { + Ok(self.rtc.time) + } + + fn get_wake_status(&self, timer_id: AcpiTimerId) -> Result { + Ok(self.rtc.get_timer(timer_id).timer_status) + } + + fn get_expired_timer_wake_policy(&self, timer_id: AcpiTimerId) -> Result { + Ok(self.rtc.get_timer(timer_id).wake_policy) + } + + fn get_timer_value(&self, timer_id: AcpiTimerId) -> Result { + Ok(self.rtc.get_timer(timer_id).value) + } +} diff --git a/rust/src/rtc.rs b/rust/src/rtc.rs index 0827046..d99c047 100644 --- a/rust/src/rtc.rs +++ b/rust/src/rtc.rs @@ -1,45 +1,256 @@ +use crate::common; +use color_eyre::Result; use crossterm::event::Event; +use embedded_mcu_hal::time::Datetime; use ratatui::{ - buffer::Buffer, - layout::Rect, - style::{Color, Stylize, palette::tailwind}, - text::Line, - widgets::{Block, Borders, Padding, Paragraph, Widget}, + prelude::*, + style::{Color, palette::tailwind}, + widgets::Paragraph, +}; +use time_alarm_service_messages::{ + AcpiDaylightSavingsTimeStatus, AcpiTimeZone, AcpiTimerId, AcpiTimestamp, AlarmExpiredWakePolicy, AlarmTimerSeconds, + TimeAlarmDeviceCapabilities, TimerStatus, }; use crate::app::Module; +use crate::{RtcSource, Source}; const LABEL_COLOR: Color = tailwind::SLATE.c200; +const DATA_NOT_YET_RETRIEVED_MSG: &str = "Data not yet retrieved"; + +mod rtc_timer { + use super::*; + pub struct RtcTimer { + timer_id: AcpiTimerId, + + value: Result, + wake_policy: Result, + timer_status: Result, + } + + impl RtcTimer { + pub fn update(&mut self, source: &impl RtcSource) { + self.value = source.get_timer_value(self.timer_id); + self.wake_policy = source.get_expired_timer_wake_policy(self.timer_id); + self.timer_status = source.get_wake_status(self.timer_id); + } -#[derive(Default)] -pub struct Rtc {} + pub fn new(timer_id: AcpiTimerId) -> Self { + Self { + timer_id, + value: Err(color_eyre::eyre::eyre!(DATA_NOT_YET_RETRIEVED_MSG)), + wake_policy: Err(color_eyre::eyre::eyre!(DATA_NOT_YET_RETRIEVED_MSG)), + timer_status: Err(color_eyre::eyre::eyre!(DATA_NOT_YET_RETRIEVED_MSG)), + } + } -impl Module for Rtc { + pub fn render(&self, title: &str, area: Rect, buf: &mut Buffer) { + let is_healthy = self.value.is_ok() && self.wake_policy.is_ok() && self.timer_status.is_ok(); + let title = common::title_str_with_status(title, is_healthy); + + Paragraph::new(vec![ + Line::raw(format_result("Time remaining: ", &self.value, |value| match *value { + AlarmTimerSeconds::DISABLED => "Timer not set".to_string(), + seconds => format!("{} seconds", seconds.0), + })), + Line::raw(format_result( + "Wake policy: ", + &self.wake_policy, + |wake_policy| match *wake_policy { + AlarmExpiredWakePolicy::NEVER => "never".to_string(), + AlarmExpiredWakePolicy::INSTANTLY => "instantly".to_string(), + wake_policy => format!("after {} seconds", wake_policy.0), + }, + )), + Line::raw(format_result("Timer status: ", &self.timer_status, |timer_status| { + format!( + "{}, {}", + if timer_status.timer_expired() { + "expired".to_string() + } else { + "not expired".to_string() + }, + if timer_status.timer_triggered_wake() { + "triggered wake".to_string() + } else { + "did not trigger wake".to_string() + } + ) + })), + ]) + .block(common::title_block(&title, 0, LABEL_COLOR)) + .render(area, buf); + } + } + + fn format_result(label: &str, res: &Result, f: impl FnOnce(&T) -> String) -> String { + match res { + Ok(value) => format!("{}{}", label, f(value)), + Err(err) => format!("{}Error: {}", label, err), + } + } +} + +use rtc_timer::RtcTimer; + +pub struct Rtc { + source: S, + timers: [RtcTimer; 2], + + capabilities: Result, + timestamp: Result, +} + +impl Module for Rtc { fn title(&self) -> &'static str { "RTC Information" } - fn update(&mut self) {} + fn update(&mut self) { + // Capabilities should be static, so don't try to update after a successful fetch + if self.capabilities.is_err() { + self.capabilities = self.source.get_capabilities(); + } + self.timestamp = self.source.get_real_time(); + for timer in &mut self.timers { + timer.update(&self.source); + } + } fn handle_event(&mut self, _evt: &Event) {} fn render(&self, area: Rect, buf: &mut Buffer) { - let status_title = title_block("RTC Properties"); - Paragraph::default().block(status_title).render(area, buf); + let is_healthy = self.capabilities.is_ok() && self.timestamp.is_ok(); + let title = common::title_str_with_status("Real-time Clock", is_healthy); + let title = common::title_block(&title, 0, LABEL_COLOR); + + let [general_area, timers_area] = common::area_split(area, Direction::Vertical, 70, 30); + let [ac_area, dc_area] = common::area_split(timers_area, Direction::Horizontal, 50, 50); + + let time_messages = match &self.timestamp { + Ok(timestamp) => vec![ + format!("Time: {}", format_time(timestamp.datetime)), + format!("Time Zone: {}", format_time_zone(timestamp.time_zone)), + format!("DST: {}", format_dst(timestamp.dst_status)), + "".to_string(), + ], + Err(err) => vec![format!("Error retrieving RTC time: {}", err)], + }; + + let capabilities_messages: Vec = match &self.capabilities { + Ok(capabilities) => format_capabilities(capabilities), + Err(err) => vec![format!("Error retrieving RTC capabilities: {}", err)], + }; + + let all_messages: Vec> = time_messages + .into_iter() + .chain(capabilities_messages) + .map(Line::raw) + .collect(); + + Paragraph::new(all_messages).block(title).render(general_area, buf); + + self.get_timer(AcpiTimerId::AcPower) + .render("AC Power Timer", ac_area, buf); + self.get_timer(AcpiTimerId::DcPower) + .render("DC Power Timer", dc_area, buf); } } -impl Rtc { - pub fn new() -> Self { - Self {} +fn format_dst(dst: AcpiDaylightSavingsTimeStatus) -> &'static str { + match dst { + AcpiDaylightSavingsTimeStatus::NotObserved => "Not Observed", + AcpiDaylightSavingsTimeStatus::NotAdjusted => "No", + AcpiDaylightSavingsTimeStatus::Adjusted => "Yes", } } -fn title_block(title: &str) -> Block<'_> { - let title = Line::from(title); - Block::new() - .borders(Borders::NONE) - .padding(Padding::vertical(1)) - .title(title) - .fg(LABEL_COLOR) +fn format_capabilities(capabilities: &TimeAlarmDeviceCapabilities) -> Vec { + fn as_supported(supported: bool) -> &'static str { + if supported { "Supported" } else { "Not Supported" } + } + vec![ + "Capabilities:".to_string(), + format!( + " Real time: {}", + as_supported(capabilities.realtime_implemented()) + ), + format!( + " Get Wake Status: {}", + as_supported(capabilities.get_wake_status_supported()) + ), + format!( + " Accuracy: {}", + if capabilities.realtime_accuracy_in_milliseconds() { + "Milliseconds" + } else { + "Seconds" + } + ), + format!( + " AC Wake: {}", + as_supported(capabilities.ac_wake_implemented()) + ), + format!( + " AC S4 Wake: {}", + as_supported(capabilities.ac_s4_wake_supported()) + ), + format!( + " AC S5 Wake: {}", + as_supported(capabilities.ac_s5_wake_supported()) + ), + format!( + " DC Wake: {}", + as_supported(capabilities.dc_wake_implemented()) + ), + format!( + " DC S4 Wake: {}", + as_supported(capabilities.dc_s4_wake_supported()) + ), + format!( + " DC S5 Wake: {}", + as_supported(capabilities.dc_s5_wake_supported()) + ), + ] +} + +fn format_time(time: Datetime) -> String { + format!( + "{:04}-{:02}-{:02} {:02}:{:02}:{:02}", + time.year(), + u8::from(time.month()), + time.day(), + time.hour(), + time.minute(), + time.second() + ) +} + +fn format_time_zone(tz: AcpiTimeZone) -> String { + match tz { + AcpiTimeZone::Unknown => "Unknown".to_string(), + AcpiTimeZone::MinutesFromUtc(offset) => format!( + "UTC{:+03}:{:02}", + offset.minutes_from_utc() / 60, + offset.minutes_from_utc().abs() % 60 + ), + } +} + +impl Rtc { + pub fn new(source: S) -> Self { + let mut result = Self { + source, + capabilities: Err(color_eyre::eyre::eyre!(DATA_NOT_YET_RETRIEVED_MSG)), + timestamp: Err(color_eyre::eyre::eyre!(DATA_NOT_YET_RETRIEVED_MSG)), + timers: [RtcTimer::new(AcpiTimerId::AcPower), RtcTimer::new(AcpiTimerId::DcPower)], + }; + + result.update(); + result + } + + fn get_timer(&self, timer_id: AcpiTimerId) -> &RtcTimer { + &self.timers[timer_id as usize] + } }