diff --git a/Cargo.lock b/Cargo.lock index 98cec86..0eafdfb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -18,46 +18,77 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] -name = "autocfg" -version = "1.2.0" +name = "aligned-vec" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" +checksum = "4aa90d7ce82d4be67b64039a3d588d38dbcc6736577de4a847025ce5b0c468d1" [[package]] -name = "backtrace" -version = "0.3.71" +name = "anstream" +version = "0.6.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" +checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" dependencies = [ - "addr2line", - "cc", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", ] [[package]] -name = "bitflags" -version = "2.5.0" +name = "anstyle" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" [[package]] -name = "bytemuck" -version = "1.16.0" +name = "anstyle-parse" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78834c15cb5d5efe3452d58b1e8ba890dd62d21907f867f383358198e56ebca5" +checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" dependencies = [ - "bytemuck_derive", + "utf8parse", ] [[package]] -name = "bytemuck_derive" -version = "1.6.0" +name = "anstyle-query" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4da9a32f3fed317401fa3c862968128267c3106685286e15d5aaa3d7389c2f60" +checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" +dependencies = [ + "anstyle", + "windows-sys 0.52.0", +] + +[[package]] +name = "anyhow" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" + +[[package]] +name = "arbitrary" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" + +[[package]] +name = "arg_enum_proc_macro" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" dependencies = [ "proc-macro2", "quote", @@ -65,294 +96,1978 @@ dependencies = [ ] [[package]] -name = "cc" -version = "1.0.95" +name = "arrayvec" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d32a725bc159af97c3e629873bb9f88fb8cf8a4867175f76dc987815ea07c83b" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" [[package]] -name = "cfg-if" -version = "1.0.0" +name = "async-channel" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" +dependencies = [ + "concurrent-queue", + "event-listener 2.5.3", + "futures-core", +] [[package]] -name = "color-eyre" -version = "0.6.3" +name = "async-channel" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55146f5e46f237f7423d74111267d4597b59b0dad0ffaf7303bce9945d843ad5" +checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" dependencies = [ - "backtrace", - "color-spantrace", - "eyre", - "indenter", - "once_cell", - "owo-colors", - "tracing-error", + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", ] [[package]] -name = "color-spantrace" -version = "0.2.1" +name = "async-executor" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd6be1b2a7e382e2b98b43b2adcca6bb0e465af0bdd38123873ae61eb17a72c2" +checksum = "c8828ec6e544c02b0d6691d21ed9f9218d0384a82542855073c2a3f58304aaf0" dependencies = [ - "once_cell", - "owo-colors", - "tracing-core", - "tracing-error", + "async-task", + "concurrent-queue", + "fastrand 2.1.0", + "futures-lite 2.3.0", + "slab", ] [[package]] -name = "crosec" -version = "0.1.0" +name = "async-global-executor" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" dependencies = [ - "bytemuck", - "nix", - "num-derive", - "num-traits", - "thiserror", + "async-channel 2.3.1", + "async-executor", + "async-io 2.3.3", + "async-lock 3.4.0", + "blocking", + "futures-lite 2.3.0", + "once_cell", ] [[package]] -name = "ectool" -version = "0.1.0" +name = "async-io" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" dependencies = [ - "bytemuck", - "color-eyre", - "crosec", + "async-lock 2.8.0", + "autocfg", + "cfg-if", + "concurrent-queue", + "futures-lite 1.13.0", + "log", + "parking", + "polling 2.8.0", + "rustix 0.37.27", + "slab", + "socket2", + "waker-fn", ] [[package]] -name = "eyre" -version = "0.6.12" +name = "async-io" +version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" +checksum = "0d6baa8f0178795da0e71bc42c9e5d13261aac7ee549853162e66a241ba17964" dependencies = [ - "indenter", - "once_cell", + "async-lock 3.4.0", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite 2.3.0", + "parking", + "polling 3.7.2", + "rustix 0.38.34", + "slab", + "tracing", + "windows-sys 0.52.0", ] [[package]] -name = "gimli" -version = "0.28.1" +name = "async-lock" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" +dependencies = [ + "event-listener 2.5.3", +] [[package]] -name = "indenter" -version = "0.3.3" +name = "async-lock" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" +checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" +dependencies = [ + "event-listener 5.3.1", + "event-listener-strategy", + "pin-project-lite", +] [[package]] -name = "lazy_static" -version = "1.4.0" +name = "async-std" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d" +dependencies = [ + "async-channel 1.9.0", + "async-global-executor", + "async-io 1.13.0", + "async-lock 2.8.0", + "crossbeam-utils", + "futures-channel", + "futures-core", + "futures-io", + "futures-lite 1.13.0", + "gloo-timers", + "kv-log-macro", + "log", + "memchr", + "once_cell", + "pin-project-lite", + "pin-utils", + "slab", + "wasm-bindgen-futures", +] [[package]] -name = "libc" -version = "0.2.153" +name = "async-task" +version = "4.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" [[package]] -name = "memchr" -version = "2.7.2" +name = "atomic-waker" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] -name = "miniz_oxide" -version = "0.7.2" +name = "autocfg" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" -dependencies = [ - "adler", -] +checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" [[package]] -name = "nix" -version = "0.27.1" +name = "av1-grain" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" +checksum = "6678909d8c5d46a42abcf571271e15fdbc0a225e3646cf23762cd415046c78bf" dependencies = [ - "bitflags", - "cfg-if", - "libc", + "anyhow", + "arrayvec", + "log", + "nom", + "num-rational", + "v_frame", ] [[package]] -name = "num-derive" -version = "0.4.2" +name = "avif-serialize" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +checksum = "876c75a42f6364451a033496a14c44bffe41f5f4a8236f697391f11024e596d2" dependencies = [ - "proc-macro2", - "quote", - "syn", + "arrayvec", ] [[package]] -name = "num-traits" -version = "0.2.18" +name = "backtrace" +version = "0.3.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" dependencies = [ - "autocfg", + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", ] [[package]] -name = "object" -version = "0.32.2" +name = "bit_field" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" -dependencies = [ - "memchr", -] +checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" [[package]] -name = "once_cell" -version = "1.19.0" +name = "bitflags" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] -name = "owo-colors" -version = "3.5.0" +name = "bitflags" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" [[package]] -name = "pin-project-lite" -version = "0.2.14" +name = "bitstream-io" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" +checksum = "7c12d1856e42f0d817a835fe55853957c85c8c8a470114029143d3f12671446e" [[package]] -name = "proc-macro2" -version = "1.0.81" +name = "blocking" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" +checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" dependencies = [ - "unicode-ident", + "async-channel 2.3.1", + "async-task", + "futures-io", + "futures-lite 2.3.0", + "piper", ] [[package]] -name = "quote" -version = "1.0.36" +name = "built" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" -dependencies = [ - "proc-macro2", -] +checksum = "c6a6c0b39c38fd754ac338b00a88066436389c0f029da5d37d1e01091d9b7c17" [[package]] -name = "rustc-demangle" -version = "0.1.23" +name = "bumpalo" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] -name = "sharded-slab" -version = "0.1.7" +name = "bytemuck" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +checksum = "78834c15cb5d5efe3452d58b1e8ba890dd62d21907f867f383358198e56ebca5" dependencies = [ - "lazy_static", + "bytemuck_derive", ] [[package]] -name = "syn" -version = "2.0.60" +name = "bytemuck_derive" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" +checksum = "4da9a32f3fed317401fa3c862968128267c3106685286e15d5aaa3d7389c2f60" dependencies = [ "proc-macro2", "quote", - "unicode-ident", + "syn", ] [[package]] -name = "thiserror" -version = "1.0.59" +name = "byteorder" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa" -dependencies = [ - "thiserror-impl", -] +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] -name = "thiserror-impl" -version = "1.0.59" +name = "byteorder-lite" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] +checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" [[package]] -name = "thread_local" -version = "1.1.8" +name = "cc" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +checksum = "d32a725bc159af97c3e629873bb9f88fb8cf8a4867175f76dc987815ea07c83b" dependencies = [ - "cfg-if", + "jobserver", + "libc", "once_cell", ] [[package]] -name = "tracing" -version = "0.1.40" +name = "cfg-expr" +version = "0.15.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" dependencies = [ - "pin-project-lite", - "tracing-core", + "smallvec", + "target-lexicon", ] [[package]] -name = "tracing-core" -version = "0.1.32" +name = "cfg-if" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clap" +version = "4.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9689a29b593160de5bc4aacab7b5d54fb52231de70122626c178e6a368994c7" dependencies = [ - "once_cell", - "valuable", + "clap_builder", + "clap_derive", ] [[package]] -name = "tracing-error" -version = "0.2.0" +name = "clap_builder" +version = "4.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d686ec1c0f384b1277f097b2f279a2ecc11afe8c133c1aabf036a27cb4cd206e" +checksum = "2e5387378c84f6faa26890ebf9f0a92989f8873d4d380467bcd0d8d8620424df" dependencies = [ - "tracing", - "tracing-subscriber", + "anstream", + "anstyle", + "clap_lex", + "strsim", ] [[package]] -name = "tracing-subscriber" -version = "0.3.18" +name = "clap_derive" +version = "4.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +checksum = "c780290ccf4fb26629baa7a1081e68ced113f1d3ec302fa5948f1c381ebf06c6" dependencies = [ - "sharded-slab", - "thread_local", - "tracing-core", + "heck", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "unicode-ident" -version = "1.0.12" +name = "clap_lex" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" [[package]] -name = "valuable" -version = "0.1.0" +name = "color-eyre" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" +checksum = "55146f5e46f237f7423d74111267d4597b59b0dad0ffaf7303bce9945d843ad5" +dependencies = [ + "backtrace", + "color-spantrace", + "eyre", + "indenter", + "once_cell", + "owo-colors", + "tracing-error", +] + +[[package]] +name = "color-spantrace" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd6be1b2a7e382e2b98b43b2adcca6bb0e465af0bdd38123873ae61eb17a72c2" +dependencies = [ + "once_cell", + "owo-colors", + "tracing-core", + "tracing-error", +] + +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + +[[package]] +name = "colorchoice" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" + +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crosec" +version = "0.1.0" +dependencies = [ + "async-std", + "bytemuck", + "clap", + "nix", + "num", + "num-derive", + "num-traits", + "strum", + "strum_macros", + "thiserror", + "uom", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "ectool" +version = "0.1.0" +dependencies = [ + "clap", + "color-eyre", + "crosec", + "image", + "num-traits", + "strum", + "uom", +] + +[[package]] +name = "either" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + +[[package]] +name = "event-listener" +version = "5.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" +dependencies = [ + "event-listener 5.3.1", + "pin-project-lite", +] + +[[package]] +name = "exr" +version = "1.72.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "887d93f60543e9a9362ef8a21beedd0a833c5d9610e18c67abe15a5963dcb1a4" +dependencies = [ + "bit_field", + "flume", + "half", + "lebe", + "miniz_oxide", + "rayon-core", + "smallvec", + "zune-inflate", +] + +[[package]] +name = "eyre" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" +dependencies = [ + "indenter", + "once_cell", +] + +[[package]] +name = "fastrand" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +dependencies = [ + "instant", +] + +[[package]] +name = "fastrand" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" + +[[package]] +name = "fdeflate" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f9bfee30e4dedf0ab8b422f03af778d9612b63f502710fc500a334ebe2de645" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "flate2" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "flume" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" +dependencies = [ + "spin", +] + +[[package]] +name = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-io" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + +[[package]] +name = "futures-lite" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" +dependencies = [ + "fastrand 1.9.0", + "futures-core", + "futures-io", + "memchr", + "parking", + "pin-project-lite", + "waker-fn", +] + +[[package]] +name = "futures-lite" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" +dependencies = [ + "fastrand 2.1.0", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + +[[package]] +name = "getrandom" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "gif" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb2d69b19215e18bb912fa30f7ce15846e301408695e44e0ef719f1da9e19f2" +dependencies = [ + "color_quant", + "weezl", +] + +[[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + +[[package]] +name = "gloo-timers" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "half" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" +dependencies = [ + "cfg-if", + "crunchy", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "hermit-abi" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" + +[[package]] +name = "image" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd54d660e773627692c524beaad361aca785a4f9f5730ce91f42aabe5bce3d11" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "exr", + "gif", + "image-webp", + "num-traits", + "png", + "qoi", + "ravif", + "rayon", + "rgb", + "tiff", + "zune-core", + "zune-jpeg", +] + +[[package]] +name = "image-webp" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d730b085583c4d789dfd07fdcf185be59501666a90c97c40162b37e4fdad272d" +dependencies = [ + "byteorder-lite", + "thiserror", +] + +[[package]] +name = "imgref" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44feda355f4159a7c757171a77de25daf6411e217b4cabd03bd6650690468126" + +[[package]] +name = "indenter" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" + +[[package]] +name = "indexmap" +version = "2.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "instant" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "interpolate_name" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "io-lifetimes" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" +dependencies = [ + "hermit-abi 0.3.9", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" + +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + +[[package]] +name = "jobserver" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e" +dependencies = [ + "libc", +] + +[[package]] +name = "jpeg-decoder" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" + +[[package]] +name = "js-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "kv-log-macro" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" +dependencies = [ + "log", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "lebe" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" + +[[package]] +name = "libc" +version = "0.2.153" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" + +[[package]] +name = "libfuzzer-sys" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a96cfd5557eb82f2b83fed4955246c988d331975a002961b07c81584d107e7f7" +dependencies = [ + "arbitrary", + "cc", + "once_cell", +] + +[[package]] +name = "linux-raw-sys" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +dependencies = [ + "value-bag", +] + +[[package]] +name = "loop9" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062" +dependencies = [ + "imgref", +] + +[[package]] +name = "maybe-rayon" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519" +dependencies = [ + "cfg-if", + "rayon", +] + +[[package]] +name = "memchr" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +dependencies = [ + "adler", + "simd-adler32", +] + +[[package]] +name = "new_debug_unreachable" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" + +[[package]] +name = "nix" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" +dependencies = [ + "bitflags 2.5.0", + "cfg-if", + "libc", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "noop_proc_macro" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" + +[[package]] +name = "num" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c165a9ab64cf766f73521c0dd2cfdff64f488b8f0b3e621face3462d3db536d7" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "object" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "owo-colors" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" + +[[package]] +name = "parking" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "piper" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae1d5c74c9876f070d3e8fd503d748c7d974c3e48da8f41350fa5222ef9b4391" +dependencies = [ + "atomic-waker", + "fastrand 2.1.0", + "futures-io", +] + +[[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + +[[package]] +name = "png" +version = "0.17.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06e4b0d3d1312775e782c86c91a111aa1f910cbb65e1337f9975b5f9a554b5e1" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + +[[package]] +name = "polling" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" +dependencies = [ + "autocfg", + "bitflags 1.3.2", + "cfg-if", + "concurrent-queue", + "libc", + "log", + "pin-project-lite", + "windows-sys 0.48.0", +] + +[[package]] +name = "polling" +version = "3.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3ed00ed3fbf728b5816498ecd316d1716eecaced9c0c8d2c5a6740ca214985b" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi 0.4.0", + "pin-project-lite", + "rustix 0.38.34", + "tracing", + "windows-sys 0.52.0", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "proc-macro2" +version = "1.0.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "profiling" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43d84d1d7a6ac92673717f9f6d1518374ef257669c24ebc5ac25d5033828be58" +dependencies = [ + "profiling-procmacros", +] + +[[package]] +name = "profiling-procmacros" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8021cf59c8ec9c432cfc2526ac6b8aa508ecaf29cd415f271b8406c1b851c3fd" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "qoi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "quick-error" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rav1e" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd87ce80a7665b1cce111f8a16c1f3929f6547ce91ade6addf4ec86a8dda5ce9" +dependencies = [ + "arbitrary", + "arg_enum_proc_macro", + "arrayvec", + "av1-grain", + "bitstream-io", + "built", + "cfg-if", + "interpolate_name", + "itertools", + "libc", + "libfuzzer-sys", + "log", + "maybe-rayon", + "new_debug_unreachable", + "noop_proc_macro", + "num-derive", + "num-traits", + "once_cell", + "paste", + "profiling", + "rand", + "rand_chacha", + "simd_helpers", + "system-deps", + "thiserror", + "v_frame", + "wasm-bindgen", +] + +[[package]] +name = "ravif" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc13288f5ab39e6d7c9d501759712e6969fcc9734220846fc9ed26cae2cc4234" +dependencies = [ + "avif-serialize", + "imgref", + "loop9", + "quick-error", + "rav1e", + "rayon", + "rgb", +] + +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "rgb" +version = "0.8.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05aaa8004b64fd573fc9d002f4e632d51ad4f026c2b5ba95fcb6c2f32c2c47d8" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustix" +version = "0.37.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea8ca367a3a01fe35e6943c400addf443c0f57670e6ec51196f71a4b8762dd2" +dependencies = [ + "bitflags 1.3.2", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys 0.3.8", + "windows-sys 0.48.0", +] + +[[package]] +name = "rustix" +version = "0.38.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +dependencies = [ + "bitflags 2.5.0", + "errno", + "libc", + "linux-raw-sys 0.4.14", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustversion" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "serde" +version = "1.0.203" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.203" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_spanned" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" +dependencies = [ + "serde", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + +[[package]] +name = "simd_helpers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95890f873bec569a0362c235787f3aca6e1e887302ba4840839bcc6459c42da6" +dependencies = [ + "quote", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "socket2" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "strum" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" + +[[package]] +name = "strum_macros" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn", +] + +[[package]] +name = "syn" +version = "2.0.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "system-deps" +version = "6.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" +dependencies = [ + "cfg-expr", + "heck", + "pkg-config", + "toml", + "version-compare", +] + +[[package]] +name = "target-lexicon" +version = "0.12.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f" + +[[package]] +name = "thiserror" +version = "1.0.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "tiff" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e" +dependencies = [ + "flate2", + "jpeg-decoder", + "weezl", +] + +[[package]] +name = "toml" +version = "0.8.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f49eb2ab21d2f26bd6db7bf383edc527a7ebaee412d17af4d40fdccd442f335" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f21c7aaf97f1bd9ca9d4f9e73b0a6c74bd5afef56f2bc931943a6e1c37e04e38" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-error" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d686ec1c0f384b1277f097b2f279a2ecc11afe8c133c1aabf036a27cb4cd206e" +dependencies = [ + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "sharded-slab", + "thread_local", + "tracing-core", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "uom" +version = "0.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffd36e5350a65d112584053ee91843955826bf9e56ec0d1351214e01f6d7cd9c" +dependencies = [ + "num-traits", + "typenum", +] + +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + +[[package]] +name = "v_frame" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6f32aaa24bacd11e488aa9ba66369c7cd514885742c9fe08cfe85884db3e92b" +dependencies = [ + "aligned-vec", + "num-traits", + "wasm-bindgen", +] + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "value-bag" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a84c137d37ab0142f0f2ddfe332651fdbf252e7b7dbb4e67b6c1f1b2e925101" + +[[package]] +name = "version-compare" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" + +[[package]] +name = "waker-fn" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "317211a0dc0ceedd78fb2ca9a44aed3d7b9b26f81870d485c07122b4350673b7" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" + +[[package]] +name = "web-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "weezl" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[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.5", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +dependencies = [ + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" + +[[package]] +name = "winnow" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41ff33f391015ecab21cd092389215eb265ef9496a9a07b6bee7d3529831deda" +dependencies = [ + "memchr", +] + +[[package]] +name = "zune-core" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" + +[[package]] +name = "zune-inflate" +version = "0.2.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "zune-jpeg" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec866b44a2a1fd6133d363f073ca1b179f438f99e7e5bfb1e33f7181facfe448" +dependencies = [ + "zune-core", +] diff --git a/crosec/Cargo.toml b/crosec/Cargo.toml index ae9169e..02cd582 100644 --- a/crosec/Cargo.toml +++ b/crosec/Cargo.toml @@ -6,8 +6,17 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +async-std = "1.12.0" bytemuck = { version = "1.16.0", features = ["derive"] } +clap = { version = "4.5.6", optional = true } nix = { version = "0.27.1", features = ["ioctl"] } +num = "0.4.3" num-derive = "0.4.2" num-traits = "0.2.18" +strum = "0.26.2" +strum_macros = "0.26.4" thiserror = "1.0.57" +uom = "0.36.0" + +[features] +clap = ["dep:clap"] diff --git a/crosec/src/battery.rs b/crosec/src/battery.rs new file mode 100644 index 0000000..c8a0d5c --- /dev/null +++ b/crosec/src/battery.rs @@ -0,0 +1,72 @@ +use crate::commands::get_cmd_versions::{ec_cmd_get_cmd_versions, V1}; +use crate::commands::CrosEcCmd; +use crate::read_mem_any::read_mem_any; +use crate::read_mem_string::read_mem_string; +use crate::{ + EcCmdResult, EC_MEM_MAP_BATTERY_CAPACITY, EC_MEM_MAP_BATTERY_CYCLE_COUNT, + EC_MEM_MAP_BATTERY_DESIGN_CAPACITY, EC_MEM_MAP_BATTERY_DESIGN_VOLTAGE, + EC_MEM_MAP_BATTERY_FLAGS, EC_MEM_MAP_BATTERY_LAST_FULL_CHARGE_CAPACITY, + EC_MEM_MAP_BATTERY_MANUFACTURER, EC_MEM_MAP_BATTERY_MODEL, EC_MEM_MAP_BATTERY_RATE, + EC_MEM_MAP_BATTERY_SERIAL, EC_MEM_MAP_BATTERY_TYPE, EC_MEM_MAP_BATTERY_VERSION, + EC_MEM_MAP_BATTERY_VOLTAGE, +}; +use std::fs::File; + +#[derive(Debug, Clone)] +pub struct BatteryInfo { + pub oem_name: String, + pub model_number: String, + pub chemistry: String, + pub serial_number: String, + pub design_capacity: i32, + pub last_full_charge: i32, + pub design_output_voltage: i32, + pub cycle_count: i32, + pub present_voltage: i32, + pub present_current: i32, + pub remaining_capacity: i32, + pub flags: u8, +} + +pub fn battery(file: &mut File) -> EcCmdResult { + if ec_cmd_get_cmd_versions(file, CrosEcCmd::BatteryGetStatic)? & V1 != 0 { + panic!( + "Battery info needs to be gotten with the {:?} command", + CrosEcCmd::BatteryGetStatic + ); + } else { + let battery_version = read_mem_any::(file, EC_MEM_MAP_BATTERY_VERSION).unwrap(); + if battery_version < 1 { + panic!("Battery version {battery_version} is not supported"); + } + let flags = read_mem_any::(file, EC_MEM_MAP_BATTERY_FLAGS).unwrap(); + let oem_name = read_mem_string(file, EC_MEM_MAP_BATTERY_MANUFACTURER).unwrap(); + let model_number = read_mem_string(file, EC_MEM_MAP_BATTERY_MODEL).unwrap(); + let chemistry = read_mem_string(file, EC_MEM_MAP_BATTERY_TYPE).unwrap(); + let serial_number = read_mem_string(file, EC_MEM_MAP_BATTERY_SERIAL).unwrap(); + let design_capacity = + read_mem_any::(file, EC_MEM_MAP_BATTERY_DESIGN_CAPACITY).unwrap(); + let last_full_charge = + read_mem_any::(file, EC_MEM_MAP_BATTERY_LAST_FULL_CHARGE_CAPACITY).unwrap(); + let design_output_voltage = + read_mem_any::(file, EC_MEM_MAP_BATTERY_DESIGN_VOLTAGE).unwrap(); + let cycle_count = read_mem_any::(file, EC_MEM_MAP_BATTERY_CYCLE_COUNT).unwrap(); + let present_voltage = read_mem_any::(file, EC_MEM_MAP_BATTERY_VOLTAGE).unwrap(); + let present_current = read_mem_any::(file, EC_MEM_MAP_BATTERY_RATE).unwrap(); + let remaining_capacity = read_mem_any::(file, EC_MEM_MAP_BATTERY_CAPACITY).unwrap(); + Ok(BatteryInfo { + flags, + oem_name, + model_number, + chemistry, + serial_number, + design_capacity, + last_full_charge, + design_output_voltage, + cycle_count, + present_voltage, + present_current, + remaining_capacity, + }) + } +} diff --git a/crosec/src/commands/board_version.rs b/crosec/src/commands/board_version.rs new file mode 100644 index 0000000..0b5b3de --- /dev/null +++ b/crosec/src/commands/board_version.rs @@ -0,0 +1,10 @@ +use std::fs::File; +use std::os::fd::AsRawFd; + +use crate::commands::CrosEcCmd; +use crate::ec_command::ec_command_bytemuck; +use crate::EcCmdResult; + +pub fn ec_cmd_board_version(file: &mut File) -> EcCmdResult { + ec_command_bytemuck(CrosEcCmd::GetBoardVersion, 0, &(), file.as_raw_fd()) +} diff --git a/crosec/src/commands/charge_control.rs b/crosec/src/commands/charge_control.rs new file mode 100644 index 0000000..73ff4ba --- /dev/null +++ b/crosec/src/commands/charge_control.rs @@ -0,0 +1,76 @@ +use crate::commands::get_cmd_versions::{ec_cmd_get_cmd_versions, V2}; +use crate::commands::CrosEcCmd; +use crate::ec_command::ec_command_bytemuck; +use crate::EcCmdResult; +use bytemuck::{Pod, Zeroable}; +use std::os::fd::AsRawFd; + +#[repr(C)] +#[derive(Pod, Zeroable, Copy, Clone, Default, Debug)] +pub struct Sustainer { + pub min_percent: i8, + pub max_percent: i8, +} + +#[derive(Debug)] +pub enum ChargeControl { + Normal(Option), + Idle, + Discharge, +} + +pub fn supports_get_and_sustainer(file: &mut File) -> EcCmdResult { + let versions = ec_cmd_get_cmd_versions(file, CrosEcCmd::ChargeControl)?; + Ok(versions & V2 != 0) +} + +#[repr(C)] +#[derive(Pod, Zeroable, Copy, Clone)] +pub struct EcParamsChargeControl { + mode: u32, + command: u8, + reserved: u8, + sustain: Sustainer, +} + +const CHARGE_CONTROL_MODE_SET: u8 = 0; +// const CHARGE_CONTROL_MODE_GET: u8 = 1; + +const CHARGE_CONTROL_COMMAND_NORMAL: u32 = 0; +const CHARGE_CONTROL_COMMAND_IDLE: u32 = 1; +const CHARGE_CONTROL_COMMAND_DISCHARGE: u32 = 2; + +pub fn get_charge_control(_file: &mut File) -> EcCmdResult { + panic!("Not implemented yet"); +} + +pub fn set_charge_control( + file: &mut File, + charge_control: ChargeControl, +) -> EcCmdResult<()> { + ec_command_bytemuck( + CrosEcCmd::ChargeControl, + { + let version = ec_cmd_get_cmd_versions(file, CrosEcCmd::ChargeControl)?; + Ok(if version & V2 != 0 { 2 } else { 1 }) + }?, + &EcParamsChargeControl { + command: CHARGE_CONTROL_MODE_SET, + mode: match charge_control { + ChargeControl::Normal(_) => CHARGE_CONTROL_COMMAND_NORMAL, + ChargeControl::Idle => CHARGE_CONTROL_COMMAND_IDLE, + ChargeControl::Discharge => CHARGE_CONTROL_COMMAND_DISCHARGE, + }, + reserved: Default::default(), + sustain: match charge_control { + ChargeControl::Normal(sustain) => sustain.unwrap_or(Sustainer { + min_percent: -1, + max_percent: -1, + }), + _ => Default::default(), + }, + }, + file.as_raw_fd(), + )?; + Ok(()) +} diff --git a/crosec/src/commands/charge_current_limit.rs b/crosec/src/commands/charge_current_limit.rs new file mode 100644 index 0000000..e86f6cb --- /dev/null +++ b/crosec/src/commands/charge_current_limit.rs @@ -0,0 +1,29 @@ +use std::os::fd::AsRawFd; + +use bytemuck::{Pod, Zeroable}; +use uom::si::{electric_current::milliampere, f32::ElectricCurrent}; + +use crate::{ec_command::ec_command_bytemuck, EcCmdResult}; + +use super::CrosEcCmd; + +#[repr(C)] +#[derive(Pod, Zeroable, Copy, Clone)] +pub struct EcParamsChargeCurrentLimit { + limit: u32, // in mA +} + +/// Limit the charging current. The EC command sends the charging current limit to the nearest mA. +pub fn set_charge_current_limit( + file: &mut File, + limit: ElectricCurrent, +) -> EcCmdResult<()> { + ec_command_bytemuck( + CrosEcCmd::ChargeCurrentLimit, + 0, + &EcParamsChargeCurrentLimit { + limit: limit.get::() as u32, + }, + file.as_raw_fd(), + ) +} diff --git a/crosec/src/commands/fp_download.rs b/crosec/src/commands/fp_download.rs new file mode 100644 index 0000000..edcf721 --- /dev/null +++ b/crosec/src/commands/fp_download.rs @@ -0,0 +1,115 @@ +use std::{os::fd::AsRawFd, thread::sleep, time::Duration}; + +use bytemuck::{bytes_of, Pod, Zeroable}; + +use crate::ec_command::ec_command_with_dynamic_output_size; + +use super::{fp_info::EcResponseFpInfo, get_protocol_info::EcResponseGetProtocolInfo, CrosEcCmd}; + +#[repr(C)] +#[derive(Pod, Zeroable, Clone, Copy)] +struct EcParamsFpFrame { + /// The offset contains the template index or FP_FRAME_INDEX_RAW_IMAGE + /// in the high nibble, and the real offset within the frame in + /// FP_FRAME_OFFSET_MASK. + offset: u32, + size: u32, +} + +const FP_FRAME_INDEX_RAW_IMAGE: u32 = 0; + +/// This can be changed. `3` is what the ChromiumOS ectool uses. +const MAX_ATTEMPTS: usize = 3; + +pub enum DownloadType { + /// (aka `FP_FRAME_INDEX_SIMPLE_IMAGE`) for the a single grayscale image + SimpleImage, + /// (aka `FP_FRAME_INDEX_RAW_IMAGE`) for the full vendor raw finger image. + RawImage, + Template(usize), +} + +/// Downloads a frame buffer from the FPMCU. +/// The downloaded data might be either the finger image or a finger template. +pub fn fp_download( + file: &mut File, + fp_info: &EcResponseFpInfo, + protocol_info: &EcResponseGetProtocolInfo, + download_type: &DownloadType, +) -> Vec { + let (size, index) = match download_type { + DownloadType::SimpleImage => (fp_info.get_simple_image_size(), FP_FRAME_INDEX_RAW_IMAGE), + DownloadType::RawImage => (fp_info.frame_size as usize, FP_FRAME_INDEX_RAW_IMAGE), + DownloadType::Template(template_index) => { + (fp_info.template_size as usize, *template_index as u32 + 1) + } + }; + // The template may be (and probably is) bigger than the max output size, so we need to download it in chunks + let number_of_chunks = size.div_ceil(protocol_info.max_ec_output_size()); + let mut chunks = Vec::>::with_capacity(number_of_chunks); + for chunk_index in 0..number_of_chunks { + let bytes_read = chunk_index * protocol_info.max_ec_output_size(); + let remaining_bytes = size - bytes_read; + let current_chunk_size = remaining_bytes.min(protocol_info.max_ec_output_size()); + let mut attempt = 0; + loop { + let result = ec_command_with_dynamic_output_size( + CrosEcCmd::FpFrame, + 0, + bytes_of(&EcParamsFpFrame { + offset: (index << 28) + (bytes_read as u32), + size: current_chunk_size as u32, + }), + current_chunk_size, + file.as_raw_fd(), + ); + if let Ok(chunk) = result { + chunks.push(chunk); + break; + } else { + attempt += 1; + if attempt == MAX_ATTEMPTS { + panic!("Could not successfully get the fp frame in {MAX_ATTEMPTS} attempts"); + } + // Using micros and not millis to be more like original `usleep(100000)` from ChromiumOS's ectool + sleep(Duration::from_micros(100_000)); + } + } + } + chunks.concat() +} + +/// A safe wrapper around the actual template so you don't try to upload arbitrary data +pub struct FpTemplate { + vec: Vec, +} + +impl From for Vec { + fn from(value: FpTemplate) -> Self { + value.vec + } +} + +impl FpTemplate { + pub fn buffer(&self) -> &Vec { + &self.vec + } + + /// Make sure your buffer is actually a compatible fp template + /// # Safety + /// Make sure you're uploading a template from the same FPMCU with the same version and the same seed set. + pub unsafe fn from_vec_unchecked(vec: Vec) -> Self { + FpTemplate { vec } + } +} + +pub fn fp_download_template( + file: &mut File, + fp_info: &EcResponseFpInfo, + protocol_info: &EcResponseGetProtocolInfo, + index: usize, +) -> FpTemplate { + FpTemplate { + vec: fp_download(file, fp_info, protocol_info, &DownloadType::Template(index)), + } +} diff --git a/crosec/src/commands/fp_get_encryption_status.rs b/crosec/src/commands/fp_get_encryption_status.rs new file mode 100644 index 0000000..ee57681 --- /dev/null +++ b/crosec/src/commands/fp_get_encryption_status.rs @@ -0,0 +1,23 @@ +use crate::commands::CrosEcCmd; +use crate::ec_command::ec_command_bytemuck; +use crate::EcCmdResult; +use bytemuck::{Pod, Zeroable}; +use std::os::fd::AsRawFd; + +#[repr(u32)] +pub enum FpEncryptionStatus { + SeedSet = 0b1, +} + +#[derive(Pod, Zeroable, Copy, Clone)] +#[repr(C)] +pub struct EcResponseFpGetEncryptionStatus { + pub valid_flags: u32, + pub status: u32, +} + +pub fn fp_get_encryption_status( + file: &mut File, +) -> EcCmdResult { + ec_command_bytemuck(CrosEcCmd::FpGetEncryptionStatus, 0, &(), file.as_raw_fd()) +} diff --git a/crosec/src/commands/fp_info.rs b/crosec/src/commands/fp_info.rs new file mode 100644 index 0000000..14cd7a2 --- /dev/null +++ b/crosec/src/commands/fp_info.rs @@ -0,0 +1,52 @@ +use std::os::fd::AsRawFd; + +use bytemuck::{Pod, Zeroable}; + +use crate::{ec_command::ec_command_bytemuck, EcCmdResult}; + +use super::{ + get_cmd_versions::{ec_cmd_get_cmd_versions, V1}, + CrosEcCmd, +}; + +#[repr(C, align(4))] +#[derive(Pod, Zeroable, Clone, Copy, Debug)] +pub struct EcResponseFpInfo { + pub vendor_id: u32, + pub product_id: u32, + pub model_id: u32, + pub version: u32, + /// The size of the PGM image, in bytes + pub frame_size: u32, + pub pixel_format: u32, + pub width: u16, + pub height: u16, + pub bpp: u16, + pub errors: u16, + /// The template size, in bytes + pub template_size: u32, + /// The maximum number of templates the FP can store and match at once + pub template_max: u16, + /// The number of templates loaded into the FP + pub template_valid: u16, + /// The first bit (the rightmost) represents template 0, the 2nd bit form the right represents template 1, etc. + /// If the bit is 1, that means that the template has been updated by the FP and the updated version has not been downloaded yet. + pub template_dirty: u32, + /// This version could increase after an update to the FP firmware + pub template_version: u32, +} +impl EcResponseFpInfo { + pub(crate) fn get_simple_image_size(&self) -> usize { + (self.width as usize) * (self.height as usize) * (self.bpp as usize) / 8 + } +} + +pub fn fp_info(file: &mut File) -> EcCmdResult { + let fd = file.as_raw_fd(); + let versions = ec_cmd_get_cmd_versions(file, CrosEcCmd::FpInfo)?; + if versions & V1 == 0 { + panic!("fp doesn't support V1. Other versions are currently not implemented"); + } + let info: EcResponseFpInfo = ec_command_bytemuck(CrosEcCmd::FpInfo, 1, &(), fd)?; + Ok(info) +} diff --git a/crosec/src/commands/fp_mode.rs b/crosec/src/commands/fp_mode.rs new file mode 100644 index 0000000..a0c1a5c --- /dev/null +++ b/crosec/src/commands/fp_mode.rs @@ -0,0 +1,63 @@ +use std::os::fd::AsRawFd; + +use bytemuck::{Pod, Zeroable}; +use strum::IntoEnumIterator; +use strum_macros::{EnumIter, EnumString, IntoStaticStr}; + +use crate::{ec_command::ec_command_bytemuck, EcCmdResult}; + +use super::CrosEcCmd; + +/// Note that with the ChromiumOS ectool, to start enrolling, as well as continue the next step in enrolling, you do `ectool --name=cros_fp fpmode enroll`. The equivalent of this is to do `ectool fp-mode EnrollImage EnrollSession`. +#[derive(EnumString, EnumIter, IntoStaticStr, Clone, Copy, Debug)] +#[cfg_attr(feature = "clap", derive(clap::ValueEnum))] +#[repr(u32)] +pub enum FpMode { + Reset = 0b00000000000000000000000000000000, + DeepSleep = 0b00000000000000000000000000000001, + FingerDown = 0b00000000000000000000000000000010, + FingerUp = 0b00000000000000000000000000000100, + Capture = 0b00000000000000000000000000001000, + EnrollSession = 0b00000000000000000000000000010000, + EnrollImage = 0b00000000000000000000000000100000, + Match = 0b00000000000000000000000001000000, + ResetSensor = 0b00000000000000000000000010000000, + Maintanence = 0b00000000000000000000000100000000, + DontChange = 0b10000000000000000000000000000000, +} + +impl FpMode { + pub fn display(fp_mode: u32) -> String { + let flags = match fp_mode { + 0 => >::into(Self::Reset).to_owned(), + fp_mode => Self::iter() + .filter(|flag| fp_mode & *flag as u32 != 0) + .map(>::into) + .collect::>() + .join(", "), + }; + format!("{fp_mode:#b} ({flags})") + } +} + +#[repr(C)] +#[derive(Pod, Zeroable, Clone, Copy)] +struct EcParamsFpMode { + mode: u32, +} + +#[repr(C)] +#[derive(Pod, Zeroable, Clone, Copy)] +struct EcResponseFpMode { + mode: u32, +} + +pub fn fp_mode(file: &mut File, mode: u32) -> EcCmdResult { + let response: EcResponseFpMode = ec_command_bytemuck( + CrosEcCmd::FpMode, + 0, + &EcParamsFpMode { mode }, + file.as_raw_fd(), + )?; + Ok(response.mode) +} diff --git a/crosec/src/commands/fp_set_seed.rs b/crosec/src/commands/fp_set_seed.rs new file mode 100644 index 0000000..b582807 --- /dev/null +++ b/crosec/src/commands/fp_set_seed.rs @@ -0,0 +1,34 @@ +use std::os::fd::AsRawFd; + +use bytemuck::{Pod, Zeroable}; + +use crate::{ec_command::ec_command_bytemuck, EcCmdResult}; + +use super::CrosEcCmd; + +pub const FP_CONTEXT_TPM_BYTES: usize = 32; +const FP_TEMPLATE_FORMAT_VERSION: u16 = 4; + +#[repr(C, align(4))] +#[derive(Pod, Zeroable, Clone, Copy)] +struct EcParamsFpSeed { + pub struct_version: u16, + pub reserved: u16, + pub seed: [u8; FP_CONTEXT_TPM_BYTES], +} + +pub fn fp_set_seed( + file: &mut File, + seed: [u8; FP_CONTEXT_TPM_BYTES], +) -> EcCmdResult<()> { + ec_command_bytemuck( + CrosEcCmd::FpSetSeed, + 0, + &EcParamsFpSeed { + struct_version: FP_TEMPLATE_FORMAT_VERSION, + reserved: Default::default(), + seed, + }, + file.as_raw_fd(), + ) +} diff --git a/crosec/src/commands/fp_stats.rs b/crosec/src/commands/fp_stats.rs new file mode 100644 index 0000000..4d2f8f8 --- /dev/null +++ b/crosec/src/commands/fp_stats.rs @@ -0,0 +1,29 @@ +use std::{fs::File, os::fd::AsRawFd}; + +use bytemuck::{Pod, Zeroable}; + +use crate::{ec_command::ec_command_bytemuck, EcCmdResult}; + +use super::CrosEcCmd; + +#[repr(C, packed)] +#[derive(Pod, Zeroable, Clone, Copy, Debug)] +pub struct EcResponseFpStats { + pub capture_time_us: u32, + pub matching_time_us: u32, + pub overall_time_us: u32, + pub overall_t0: OverallT0, + pub timestamps_invalid: u8, + pub template_matched: i8, +} + +#[repr(C, packed)] +#[derive(Pod, Zeroable, Clone, Copy, Debug)] +pub struct OverallT0 { + pub lo: u32, + pub hi: u32, +} + +pub fn fp_stats(file: &mut File) -> EcCmdResult { + ec_command_bytemuck(CrosEcCmd::FpStats, 0, &(), file.as_raw_fd()) +} diff --git a/crosec/src/commands/fp_upload_template.rs b/crosec/src/commands/fp_upload_template.rs new file mode 100644 index 0000000..679ce4e --- /dev/null +++ b/crosec/src/commands/fp_upload_template.rs @@ -0,0 +1,69 @@ +use std::{mem::offset_of, os::fd::AsRawFd}; + +use bytemuck::{bytes_of, Pod, Zeroable}; + +use crate::{ec_command::ec_command_with_dynamic_output_size, EcCmdResult}; + +use super::{ + fp_download::FpTemplate, fp_info::EcResponseFpInfo, + get_protocol_info::EcResponseGetProtocolInfo, CrosEcCmd, +}; + +#[derive(Pod, Zeroable, Clone, Copy)] +#[repr(C)] +struct EcParamsFpTemplateWithoutData { + offset: u32, + size: u32, + data: [u8; 0], +} + +/// Flag in the 'size' field indicating that the full template has been sent +const FP_TEMPLATE_COMMIT: u32 = 0x80000000; + +pub fn fp_upload_template( + file: &mut File, + protocol_info: &EcResponseGetProtocolInfo, + fp_info: &EcResponseFpInfo, + template: &FpTemplate, +) -> EcCmdResult<()> { + assert_eq!( + template.buffer().len(), + fp_info.template_size as usize, + "The given template must match the fp sensor's template size" + ); + // TODO(b/78544921): removing 32 bits is a workaround for the MCU bug + // Idk what this bug is, but the ChromiumOS ectool removes 4 bytes, so we should too + let max_chunk_size = + protocol_info.max_ec_output_size() - offset_of!(EcParamsFpTemplateWithoutData, data) - 4; + let number_of_chunks = template + .buffer() + .len() + .div_ceil(protocol_info.max_ec_input_size()); + for chunk_index in 0..number_of_chunks { + ec_command_with_dynamic_output_size( + CrosEcCmd::FpTemplate, + 0, + &{ + let bytes_uploaded = chunk_index * max_chunk_size; + let size = (template.buffer().len() - bytes_uploaded).min(max_chunk_size); + let mut vec = bytes_of(&EcParamsFpTemplateWithoutData { + offset: bytes_uploaded as u32, + size: { + let mut size = size as u32; + if chunk_index == number_of_chunks - 1 { + size |= FP_TEMPLATE_COMMIT; + } + size + }, + data: [], + }) + .to_vec(); + vec.extend_from_slice(&template.buffer()[bytes_uploaded..bytes_uploaded + size]); + vec + }, + 0, + file.as_raw_fd(), + )?; + } + Ok(()) +} diff --git a/crosec/src/commands/get_chip_info.rs b/crosec/src/commands/get_chip_info.rs index e6cf836..744b6d5 100644 --- a/crosec/src/commands/get_chip_info.rs +++ b/crosec/src/commands/get_chip_info.rs @@ -1,6 +1,8 @@ +use std::{fs::File, os::fd::AsRawFd}; + use bytemuck::{Pod, Zeroable}; -use crate::{commands::CrosEcCmd, ec_command, EcCmdResult, EcInterface}; +use crate::{commands::CrosEcCmd, ec_command::ec_command_bytemuck, EcCmdResult}; #[repr(C, align(4))] #[derive(Pod, Zeroable, Copy, Clone)] @@ -10,17 +12,9 @@ struct EcResponseGetChipInfo { revision: [u8; 32], } -pub fn ec_cmd_get_chip_info() -> EcCmdResult<(String, String, String)> { - let params = EcResponseGetChipInfo { - vendor: [0; 32], - name: [0; 32], - revision: [0; 32], - }; - - let params_slice = bytemuck::bytes_of(¶ms); - - let result = ec_command(CrosEcCmd::GetChipInfo, 0, params_slice, EcInterface::Dev(String::from("/dev/cros_ec")))?; - let response = bytemuck::from_bytes::(&result); +pub fn ec_cmd_get_chip_info(file: &mut File) -> EcCmdResult<(String, String, String)> { + let response: EcResponseGetChipInfo = + ec_command_bytemuck(CrosEcCmd::GetChipInfo, 0, &(), file.as_raw_fd())?; let vendor = String::from_utf8(response.vendor.to_vec()).unwrap_or_default(); let name = String::from_utf8(response.name.to_vec()).unwrap_or_default(); diff --git a/crosec/src/commands/get_cmd_versions.rs b/crosec/src/commands/get_cmd_versions.rs new file mode 100644 index 0000000..cca1cc4 --- /dev/null +++ b/crosec/src/commands/get_cmd_versions.rs @@ -0,0 +1,48 @@ +use std::os::fd::AsRawFd; + +use bytemuck::{Pod, Zeroable}; + +use crate::commands::CrosEcCmd; +use crate::ec_command::ec_command_bytemuck; +use crate::EcCmdResult; + +#[repr(C)] +#[derive(Pod, Copy, Clone, Zeroable)] +struct EcParamsGetCmdVersionV1 { + cmd: u16, +} + +#[repr(C)] +#[derive(Pod, Zeroable, Copy, Clone)] +struct EcParamsGetCmdVersionV0 { + cmd: u8, +} + +#[repr(C)] +#[derive(Pod, Zeroable, Copy, Clone)] +struct EcResponseGetCmdVersion { + version_mask: u32, +} + +pub const V0: u32 = 0b001; +pub const V1: u32 = 0b010; +pub const V2: u32 = 0b100; + +pub fn ec_cmd_get_cmd_versions(file: &mut File, cmd: CrosEcCmd) -> EcCmdResult { + let fd = file.as_raw_fd(); + let response: EcResponseGetCmdVersion = match ec_command_bytemuck( + CrosEcCmd::GetCmdVersions, + 1, + &EcParamsGetCmdVersionV1 { cmd: cmd as u16 }, + fd, + ) { + Ok(response) => Ok(response), + Err(_e) => ec_command_bytemuck( + CrosEcCmd::GetCmdVersions, + 0, + &EcParamsGetCmdVersionV0 { cmd: cmd as u8 }, + fd, + ), + }?; + Ok(response.version_mask) +} diff --git a/crosec/src/commands/get_features.rs b/crosec/src/commands/get_features.rs new file mode 100644 index 0000000..684ac4a --- /dev/null +++ b/crosec/src/commands/get_features.rs @@ -0,0 +1,12 @@ +use std::fs::File; +use std::os::fd::AsRawFd; + +use crate::commands::CrosEcCmd; +use crate::ec_command::ec_command_bytemuck; +use crate::EcCmdResult; + +pub const EC_FEATURE_PWM_FAN: u64 = 0b100; + +pub fn ec_cmd_get_features(file: &mut File) -> EcCmdResult { + ec_command_bytemuck(CrosEcCmd::GetFeatures, 0, &(), file.as_raw_fd()) +} diff --git a/crosec/src/commands/get_protocol_info.rs b/crosec/src/commands/get_protocol_info.rs new file mode 100644 index 0000000..66c6228 --- /dev/null +++ b/crosec/src/commands/get_protocol_info.rs @@ -0,0 +1,47 @@ +use std::{mem::size_of, os::fd::AsRawFd}; + +use bytemuck::{Pod, Zeroable}; + +use crate::{commands::CrosEcCmd, ec_command::ec_command_bytemuck, EcCmdResult}; + +#[derive(Pod, Zeroable, Clone, Copy, Debug)] +#[repr(C)] +pub struct EcResponseGetProtocolInfo { + protocol_versions: u32, + max_request_packet_size: u16, + max_response_packet_size: u16, + flags: u32, +} + +impl EcResponseGetProtocolInfo { + pub fn max_ec_input_size(&self) -> usize { + self.max_request_packet_size as usize - size_of::() + } + + pub fn max_ec_output_size(&self) -> usize { + self.max_response_packet_size as usize - size_of::() + } +} + +#[repr(C)] +struct EcHostRequest { + struct_version: u8, + checksum: u8, + command: u16, + command_version: u8, + reserved: u8, + data_len: u16, +} + +#[repr(C)] +struct EcHostResponse { + struct_version: u8, + checksum: u8, + result: u16, + data_len: u16, + reserved: u16, +} + +pub fn get_protocol_info(file: &mut File) -> EcCmdResult { + ec_command_bytemuck(CrosEcCmd::GetProtocolInfo, 0, &(), file.as_raw_fd()) +} diff --git a/crosec/src/commands/get_uptime_info.rs b/crosec/src/commands/get_uptime_info.rs new file mode 100644 index 0000000..0f54fee --- /dev/null +++ b/crosec/src/commands/get_uptime_info.rs @@ -0,0 +1,51 @@ +use std::os::fd::AsRawFd; + +use bytemuck::{Pod, Zeroable}; + +use crate::{ec_command::ec_command_bytemuck, EcCmdResult}; + +use super::CrosEcCmd; + +#[repr(C)] +#[derive(Pod, Zeroable, Clone, Copy, Debug)] +pub struct EcResponseUptimeInfo { + /// Number of milliseconds since the last EC boot. Sysjump resets + /// typically do not restart the EC's time_since_boot epoch. + /// + /// WARNING: The EC's sense of time is much less accurate than the AP's + /// sense of time, in both phase and frequency. This timebase is similar + /// to CLOCK_MONOTONIC_RAW, but with 1% or more frequency error. + pub time_since_ec_boot_ms: u32, + + /// Number of times the AP was reset by the EC since the last EC boot. + /// Note that the AP may be held in reset by the EC during the initial + /// boot sequence, such that the very first AP boot may count as more + /// than one here. + pub ap_resets_since_ec_boot: u32, + + /// The set of flags which describe the EC's most recent reset. + /// See EC_RESET_FLAG_* for details. + pub ec_reset_flags: u32, + + /// Empty log entries have both the cause and timestamp set to zero. + pub recent_ap_reset: [ApResetLogEntry; 4], +} + +#[repr(C)] +#[derive(Pod, Zeroable, Clone, Copy, Debug)] +pub struct ApResetLogEntry { + /// See enum chipset_{reset,shutdown}_reason for details. + pub reset_cause: u16, + + /// Reserved for protocol growth. + pub reserved: u16, + + /// The time of the reset's assertion, in milliseconds since the + /// last EC boot, in the same epoch as time_since_ec_boot_ms. + /// Set to zero if the log entry is empty. + pub reset_time_ms: u32, +} + +pub fn ec_cmd_get_uptime_info(file: &mut File) -> EcCmdResult { + ec_command_bytemuck(CrosEcCmd::GetUptimeInfo, 0, &(), file.as_raw_fd()) +} diff --git a/crosec/src/commands/hello.rs b/crosec/src/commands/hello.rs index 661c074..acfa1a0 100644 --- a/crosec/src/commands/hello.rs +++ b/crosec/src/commands/hello.rs @@ -1,6 +1,10 @@ +use std::fs::File; +use std::os::fd::AsRawFd; + use bytemuck::{NoUninit, Pod, Zeroable}; -use crate::{commands::CrosEcCmd, ec_command, EcCmdResult, EcInterface}; +use crate::ec_command::ec_command_bytemuck; +use crate::{commands::CrosEcCmd, EcCmdResult}; const INPUT_DATA: u32 = 0xa0b0c0d0; const EXPECTED_OUTPUT: u32 = 0xa1b2c3d4; @@ -17,15 +21,14 @@ struct EcResponseHello { out_data: u32, } -pub fn ec_cmd_hello() -> EcCmdResult { - let params = EcParamsHello { - in_data: INPUT_DATA, - }; - let params_slice = bytemuck::bytes_of(¶ms); - - let result = ec_command(CrosEcCmd::Hello, 0, params_slice, EcInterface::Dev(String::from("/dev/cros_ec")))?; - Ok(bytemuck::try_from_bytes::(&result).map_or( - false, - |response| response.out_data == EXPECTED_OUTPUT) - ) +pub fn ec_cmd_hello(file: &mut File) -> EcCmdResult { + let response = ec_command_bytemuck::<_, EcResponseHello>( + CrosEcCmd::Hello, + 0, + &EcParamsHello { + in_data: INPUT_DATA, + }, + file.as_raw_fd(), + )?; + Ok(response.out_data == EXPECTED_OUTPUT) } diff --git a/crosec/src/commands/mod.rs b/crosec/src/commands/mod.rs index b9c4cc2..8154e73 100644 --- a/crosec/src/commands/mod.rs +++ b/crosec/src/commands/mod.rs @@ -1,11 +1,49 @@ +use num_derive::FromPrimitive; + +#[derive(Copy, Clone, FromPrimitive, Debug)] #[repr(u32)] pub enum CrosEcCmd { Hello = 0x0001, Version = 0x0002, GetBuildInfo = 0x0004, GetChipInfo = 0x0005, + GetBoardVersion = 0x0006, + ReadMemMap = 0x0007, + GetCmdVersions = 0x0008, + GetProtocolInfo = 0x000B, + GetFeatures = 0x000D, + SetFanTargetRpm = 0x0021, + ChargeControl = 0x0096, + ConsoleSnapshot = 0x0097, + ConsoleRead = 0x0098, + GetUptimeInfo = 0x0121, + FpMode = 0x0402, + FpInfo = 0x0403, + FpFrame = 0x0404, + FpTemplate = 0x0405, + FpStats = 0x0407, + FpSetSeed = 0x0408, + FpGetEncryptionStatus = 0x0409, + BatteryGetStatic = 0x0600, + ChargeCurrentLimit = 0x00A1, } +pub mod board_version; +pub mod charge_control; +pub mod charge_current_limit; +pub mod fp_download; +pub mod fp_get_encryption_status; +pub mod fp_info; +pub mod fp_mode; +pub mod fp_set_seed; +pub mod fp_stats; +pub mod fp_upload_template; pub mod get_chip_info; +pub mod get_cmd_versions; +pub mod get_features; +pub mod get_protocol_info; +pub mod get_uptime_info; pub mod hello; +pub mod read_mem; +pub mod set_fan_target_rpm; pub mod version; diff --git a/crosec/src/commands/read_mem.rs b/crosec/src/commands/read_mem.rs new file mode 100644 index 0000000..22f9537 --- /dev/null +++ b/crosec/src/commands/read_mem.rs @@ -0,0 +1,28 @@ +use crate::CROS_EC_IOC_MAGIC; +use nix::ioctl_readwrite; +use std::{ffi::c_int, fs::File, os::fd::AsRawFd}; + +const EC_MEM_MAP_SIZE: usize = 255; + +#[repr(C)] +struct EcResponseReadMemV2 { + offset: u32, + bytes: u32, + buffer: [u8; EC_MEM_MAP_SIZE], +} + +ioctl_readwrite!(cros_ec_read_mem, CROS_EC_IOC_MAGIC, 1, EcResponseReadMemV2); + +pub fn ec_cmd_read_mem(file: &mut File, offset: u32, bytes: u32) -> Result, c_int> { + let mut response = EcResponseReadMemV2 { + offset, + bytes, + buffer: [0; EC_MEM_MAP_SIZE], + }; + let status = unsafe { cros_ec_read_mem(file.as_raw_fd(), &mut response) }.unwrap(); + if status >= 0 { + Ok(response.buffer[..bytes as usize].to_vec()) + } else { + Err(status) + } +} diff --git a/crosec/src/commands/set_fan_target_rpm.rs b/crosec/src/commands/set_fan_target_rpm.rs new file mode 100644 index 0000000..d145371 --- /dev/null +++ b/crosec/src/commands/set_fan_target_rpm.rs @@ -0,0 +1,66 @@ +use std::fs::File; +use std::os::fd::AsRawFd; + +use bytemuck::{NoUninit, Pod, Zeroable}; + +use crate::commands::CrosEcCmd; +use crate::ec_command::ec_command_bytemuck; +use crate::EcCmdResult; + +#[repr(C)] +#[derive(Pod, Zeroable, Copy, Clone)] +struct EcParamsSetFanTargetRpmV0 { + rpm: u32, +} + +#[derive(Clone, Copy, NoUninit)] +#[repr(C, align(1))] +struct EcParamsSetFanTargetRpmV1 { + rpm: u32, + fan_index: u8, + _padding: [u8; 3], +} + +// impl EcParamsSetFanTargetRpmV1 { +// pub fn to_le_bytes(self) -> [u8; size_of::() + size_of::()] { +// [ +// self.rpm.to_le_bytes().to_vec(), +// self.fan_index.to_le_bytes().to_vec(), +// ] +// .concat() +// .try_into() +// .unwrap() +// } +// } + +pub fn ec_cmd_set_fan_target_rpm( + file: &mut File, + rpm: u32, + fan_index: Option, +) -> EcCmdResult<()> { + // v0 can only set the RPM for all fans + // v1 can set the RPM for a specific fan + match fan_index { + Some(index) => { + ec_command_bytemuck( + CrosEcCmd::SetFanTargetRpm, + 1, + &EcParamsSetFanTargetRpmV1 { + rpm, + fan_index: index, + _padding: Default::default(), + }, + file.as_raw_fd(), + )?; + } + None => { + ec_command_bytemuck( + CrosEcCmd::SetFanTargetRpm, + 0, + &EcParamsSetFanTargetRpmV0 { rpm }, + file.as_raw_fd(), + )?; + } + }; + Ok(()) +} diff --git a/crosec/src/commands/version.rs b/crosec/src/commands/version.rs index 97ea7b2..033eef5 100644 --- a/crosec/src/commands/version.rs +++ b/crosec/src/commands/version.rs @@ -1,11 +1,16 @@ -use std::mem::size_of; +use std::{fs::File, os::fd::AsRawFd}; use bytemuck::{Pod, Zeroable}; use num_derive::FromPrimitive; use num_traits::FromPrimitive; -use crate::{commands::CrosEcCmd, ec_command, EcCmdResult, EcInterface}; -use crate::dev::BUF_SIZE; +use crate::{ + commands::CrosEcCmd, + ec_command::{ec_command_bytemuck, ec_command_with_dynamic_output_size}, + EcCmdResult, +}; + +use super::get_protocol_info::EcResponseGetProtocolInfo; const TOOLVERSION: &str = env!("CARGO_PKG_VERSION"); @@ -28,7 +33,10 @@ enum EcImage { RwB = 4, } -pub fn ec_cmd_version() -> EcCmdResult<(String, String, String, String, String)> { +pub fn ec_cmd_version( + file: &mut File, + protocol_info: &EcResponseGetProtocolInfo, +) -> EcCmdResult<(String, String, String, String, String)> { let params = EcResponseVersionV1 { version_string_ro: [0; 32], version_string_rw: [0; 32], @@ -37,12 +45,8 @@ pub fn ec_cmd_version() -> EcCmdResult<(String, String, String, String, String)> cros_fwid_rw: [0; 32], }; - let build_string: [u8; BUF_SIZE] = [0; BUF_SIZE]; - let params_slice = bytemuck::bytes_of(¶ms); - - let mut result = ec_command(CrosEcCmd::Version, 0, params_slice, EcInterface::Dev(String::from("/dev/cros_ec")))?; - result.resize(size_of::(), Default::default()); - let response = bytemuck::from_bytes::(&result); + let response: EcResponseVersionV1 = + ec_command_bytemuck(CrosEcCmd::Version, 0, ¶ms, file.as_raw_fd())?; let ro_ver = String::from_utf8(response.version_string_ro.to_vec()).unwrap_or_default(); let rw_ver = String::from_utf8(response.version_string_rw.to_vec()).unwrap_or_default(); @@ -56,9 +60,13 @@ pub fn ec_cmd_version() -> EcCmdResult<(String, String, String, String, String)> None => String::from("Unknown"), }; - let build_string_slice = &build_string; - - let result = ec_command(CrosEcCmd::GetBuildInfo, 0, build_string_slice, EcInterface::Dev(String::from("/dev/cros_ec")))?; + let result = ec_command_with_dynamic_output_size( + CrosEcCmd::GetBuildInfo, + 0, + &[0; 248], + protocol_info.max_ec_output_size(), + file.as_raw_fd(), + )?; let build_info = String::from_utf8(result).unwrap_or(String::from("")); Ok((ro_ver, rw_ver, image, build_info, String::from(TOOLVERSION))) diff --git a/crosec/src/console.rs b/crosec/src/console.rs new file mode 100644 index 0000000..d681747 --- /dev/null +++ b/crosec/src/console.rs @@ -0,0 +1,30 @@ +use std::fs::File; +use std::os::fd::AsRawFd; + +use crate::commands::get_protocol_info::EcResponseGetProtocolInfo; +use crate::commands::CrosEcCmd; +use crate::ec_command::{ec_command_bytemuck, ec_command_with_dynamic_output_size}; +use crate::EcCmdResult; + +pub fn console(file: &mut File, protocol_info: &EcResponseGetProtocolInfo) -> EcCmdResult { + ec_command_bytemuck(CrosEcCmd::ConsoleSnapshot, 0, &(), file.as_raw_fd())?; + let mut console = String::default(); + loop { + let output = ec_command_with_dynamic_output_size( + CrosEcCmd::ConsoleRead, + 0, + Default::default(), + protocol_info.max_ec_output_size(), + file.as_raw_fd(), + )?; + let chunk = String::from_utf8(output).unwrap(); + // Get rid of trailing null characters + let chunk = chunk.trim_end_matches('\0'); + if !chunk.is_empty() { + console += chunk; + } else { + break; + } + } + Ok(console) +} diff --git a/crosec/src/dev.rs b/crosec/src/dev.rs deleted file mode 100644 index 42ac48c..0000000 --- a/crosec/src/dev.rs +++ /dev/null @@ -1,65 +0,0 @@ -use std::fs::File; -use crate::commands::CrosEcCmd; -use crate::EcCmdResult; -use crate::EcError; -use nix::ioctl_readwrite; -use num_traits::FromPrimitive; -use std::os::unix::io::AsRawFd; - -use super::EcResponseStatus; - -pub const IN_SIZE: usize = 256; -pub const BUF_SIZE: usize = IN_SIZE - 8; - -#[repr(C)] -struct _CrosEcCommandV2 { - version: u32, - command: u32, - outsize: u32, - insize: u32, - result: u32, - data: [u8; 0], -} - -#[repr(C)] -struct CrosEcCommandV2 { - version: u32, - command: CrosEcCmd, - outsize: u32, - insize: u32, - result: u32, - data: [u8; IN_SIZE], -} - -const CROS_EC_IOC_MAGIC: u8 = 0xEC; -ioctl_readwrite!(cros_ec_cmd, CROS_EC_IOC_MAGIC, 0, _CrosEcCommandV2); - -pub fn dev_ec_command(command: CrosEcCmd, command_version: u8, data: &[u8], dev_path: &str) -> EcCmdResult> { - - let size = std::cmp::min(IN_SIZE, data.len()); - - let mut cmd = CrosEcCommandV2 { - version: command_version as u32, - command, - outsize: size as u32, - insize: IN_SIZE as u32, - result: 0xFF, - data: [0; IN_SIZE], - }; - - cmd.data[0..size].copy_from_slice(data); - let cmd_ptr = &mut cmd as *mut _ as *mut _CrosEcCommandV2; - - let cros_ec_fd = File::open(dev_path).unwrap(); - let fildes = cros_ec_fd.as_raw_fd(); - - let result = unsafe { cros_ec_cmd(fildes, cmd_ptr) }; - let status = - FromPrimitive::from_u32(cmd.result).ok_or(EcError::UnknownResponseCode(cmd.result))?; - let EcResponseStatus::Success = status else { - return Err(EcError::Response(status)); - }; - result - .map(|result| cmd.data[0..result as usize].to_vec()) - .map_err(|err| EcError::DeviceError(err)) -} diff --git a/crosec/src/ec_command.rs b/crosec/src/ec_command.rs new file mode 100644 index 0000000..b5f303a --- /dev/null +++ b/crosec/src/ec_command.rs @@ -0,0 +1,76 @@ +use crate::commands::CrosEcCmd; +use crate::EcError; +use crate::{EcCmdResult, CROS_EC_IOC_MAGIC}; +use bytemuck::{bytes_of, from_bytes, AnyBitPattern, NoUninit, Pod, Zeroable}; +use nix::ioctl_readwrite; +use num_traits::FromPrimitive; +use std::cmp::max; +use std::mem::size_of; +use std::os::raw::c_int; + +use super::EcResponseStatus; + +#[derive(Debug, Pod, Zeroable, Clone, Copy)] +#[repr(C)] +struct CrosEcCommandV2 { + version: u32, + command: u32, + ec_input_size: u32, + ec_output_size: u32, + result: u32, + data: [u8; 0], +} + +ioctl_readwrite!(cros_ec_cmd, CROS_EC_IOC_MAGIC, 0, CrosEcCommandV2); + +pub fn ec_command_with_dynamic_output_size( + command: CrosEcCmd, + command_version: u8, + input_buffer: &[u8], + output_size: usize, + fd: c_int, +) -> EcCmdResult> { + let buffer_size = max(input_buffer.len(), output_size); + let cmd_without_data = CrosEcCommandV2 { + version: command_version as u32, + command: command as u32, + ec_input_size: input_buffer.len() as u32, + ec_output_size: output_size as u32, + result: 0xFF, + data: [], + }; + let mut cmd_vec = bytemuck::bytes_of(&cmd_without_data).to_vec(); + cmd_vec.extend({ + let mut buffer = input_buffer.to_vec(); + buffer.resize(buffer_size, Default::default()); + buffer + }); + let result = unsafe { cros_ec_cmd(fd, cmd_vec.as_mut_ptr() as *mut _ as *mut CrosEcCommandV2) }; + let _output_size = result.map_err(EcError::DeviceError)?; + let cmd_without_data = + bytemuck::from_bytes::(&cmd_vec[..size_of::()]); + let status = FromPrimitive::from_u32(cmd_without_data.result) + .ok_or(EcError::UnknownResponseCode(cmd_without_data.result))?; + match status { + EcResponseStatus::Success => Ok(cmd_vec + [size_of::()..size_of::() + output_size] + .to_vec()), + status => Err(EcError::Response(status)), + } +} + +pub fn ec_command_bytemuck( + command: CrosEcCmd, + command_version: u8, + input: &Request, + fd: c_int, +) -> EcCmdResult { + let response = ec_command_with_dynamic_output_size( + command, + command_version, + bytes_of(input), + size_of::(), + fd, + )?; + Ok(from_bytes::(&response).to_owned()) +} diff --git a/crosec/src/get_number_of_fans.rs b/crosec/src/get_number_of_fans.rs new file mode 100644 index 0000000..13b0228 --- /dev/null +++ b/crosec/src/get_number_of_fans.rs @@ -0,0 +1,41 @@ +use crate::commands::get_features::{ec_cmd_get_features, EC_FEATURE_PWM_FAN}; +use crate::read_mem_any::read_mem_any; +use crate::{EcError, EC_FAN_SPEED_ENTRIES, EC_FAN_SPEED_NOT_PRESENT, EC_MEM_MAP_FAN}; +use std::ffi::c_int; +use std::fmt::{Debug, Display, Formatter}; +use std::fs::File; + +#[derive(Debug)] +pub enum Error { + GetFeatures(EcError), + ReadMem(c_int), +} + +impl std::error::Error for Error {} + +impl Display for Error { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + Self::GetFeatures(e) => { + write!(f, "Error getting features: {:#?}", e) + } + Self::ReadMem(e) => { + write!(f, "Error reading memory: {:#?}", e) + } + } + } +} + +pub fn get_number_of_fans(file: &mut File) -> Result { + let features = ec_cmd_get_features(file).map_err(Error::GetFeatures)?; + let number_of_fans = if features & EC_FEATURE_PWM_FAN != 0 { + read_mem_any::<[u16; EC_FAN_SPEED_ENTRIES]>(file, EC_MEM_MAP_FAN) + .map_err(Error::ReadMem)? + .into_iter() + .filter(|data| *data != EC_FAN_SPEED_NOT_PRESENT) + .count() + } else { + 0 + }; + Ok(number_of_fans) +} diff --git a/crosec/src/lib.rs b/crosec/src/lib.rs index d59d0f0..35432f4 100644 --- a/crosec/src/lib.rs +++ b/crosec/src/lib.rs @@ -1,17 +1,17 @@ -pub mod commands; -pub mod dev; +#![warn(unused_crate_dependencies)] -use crate::commands::CrosEcCmd; -use dev::dev_ec_command; use nix::errno::Errno; use num_derive::FromPrimitive; use thiserror::Error; -// In the future, portio should be supported as well -pub enum EcInterface { - Dev(String), - Default, -} +pub mod battery; +pub mod commands; +pub mod console; +pub mod ec_command; +pub mod get_number_of_fans; +pub mod read_mem_any; +pub mod read_mem_string; +pub mod wait_event; #[derive(FromPrimitive, Debug, Copy, Clone)] pub enum EcResponseStatus { @@ -50,9 +50,36 @@ pub enum EcError { pub type EcCmdResult = Result; -pub fn ec_command(command: CrosEcCmd, command_version: u8, data: &[u8], interface: EcInterface) -> EcCmdResult> { - match interface { - EcInterface::Dev(path) => dev_ec_command(command, command_version, data, &path), - EcInterface::Default => dev_ec_command(command, command_version, data, "/dev/cros_ec"), - } -} \ No newline at end of file +pub const CROS_EC_PATH: &str = "/dev/cros_ec"; +pub const CROS_FP_PATH: &str = "/dev/cros_fp"; + +pub const EC_FAN_SPEED_ENTRIES: usize = 4; +pub const EC_FAN_SPEED_NOT_PRESENT: u16 = 0xffff; +pub const EC_FAN_SPEED_STALLED: u16 = 0xfffe; +pub const EC_MEM_MAP_MAX_TEXT_SIZE: usize = 8; + +pub const EC_MEM_MAP_FAN: u8 = 0x10; +/// Version of data in 0x40 - 0x7f +pub const EC_MEM_MAP_BATTERY_VERSION: u8 = 0x24; +/// Battery Present Voltage +pub const EC_MEM_MAP_BATTERY_VOLTAGE: u8 = 0x40; +/// Battery Present Rate +pub const EC_MEM_MAP_BATTERY_RATE: u8 = 0x44; +/// Battery Remaining Capacity +pub const EC_MEM_MAP_BATTERY_CAPACITY: u8 = 0x48; +/// Battery State, see below (8-bit) +pub const EC_MEM_MAP_BATTERY_FLAGS: u8 = 0x4c; +/// Battery Count (8-bit) +pub const EC_MEM_MAP_BATTERY_COUNT: u8 = 0x4d; +/// Current Battery Data Index (8-bit) +pub const EC_MEM_MAP_BATTERY_INDEX: u8 = 0x4e; +pub const EC_MEM_MAP_BATTERY_DESIGN_CAPACITY: u8 = 0x50; +pub const EC_MEM_MAP_BATTERY_DESIGN_VOLTAGE: u8 = 0x54; +pub const EC_MEM_MAP_BATTERY_LAST_FULL_CHARGE_CAPACITY: u8 = 0x58; +pub const EC_MEM_MAP_BATTERY_CYCLE_COUNT: u8 = 0x5c; +pub const EC_MEM_MAP_BATTERY_MANUFACTURER: u8 = 0x60; +pub const EC_MEM_MAP_BATTERY_MODEL: u8 = 0x68; +pub const EC_MEM_MAP_BATTERY_SERIAL: u8 = 0x70; +pub const EC_MEM_MAP_BATTERY_TYPE: u8 = 0x78; + +pub const CROS_EC_IOC_MAGIC: u8 = 0xEC; diff --git a/crosec/src/read_mem_any.rs b/crosec/src/read_mem_any.rs new file mode 100644 index 0000000..a0fb7b4 --- /dev/null +++ b/crosec/src/read_mem_any.rs @@ -0,0 +1,13 @@ +use std::ffi::c_int; +use std::fs::File; +use std::mem::size_of; + +use bytemuck::AnyBitPattern; + +use crate::commands::read_mem::ec_cmd_read_mem; + +pub fn read_mem_any(file: &mut File, offset: u8) -> Result { + let result = ec_cmd_read_mem(file, offset as u32, size_of::() as u32)?; + let result = bytemuck::from_bytes(&result); + Ok(*result) +} diff --git a/crosec/src/read_mem_string.rs b/crosec/src/read_mem_string.rs new file mode 100644 index 0000000..7547b1b --- /dev/null +++ b/crosec/src/read_mem_string.rs @@ -0,0 +1,9 @@ +use crate::commands::read_mem::ec_cmd_read_mem; +use crate::EC_MEM_MAP_MAX_TEXT_SIZE; +use std::ffi::c_int; +use std::fs::File; + +pub fn read_mem_string(file: &mut File, offset: u8) -> Result { + let string = ec_cmd_read_mem(file, offset as u32, EC_MEM_MAP_MAX_TEXT_SIZE as u32)?; + Ok(String::from_utf8(string).unwrap()) +} diff --git a/crosec/src/wait_event/event.rs b/crosec/src/wait_event/event.rs new file mode 100644 index 0000000..acdd3c5 --- /dev/null +++ b/crosec/src/wait_event/event.rs @@ -0,0 +1,114 @@ +use async_std::io::ReadExt; +use num::FromPrimitive; +use std::io; +use std::mem::size_of; +use strum::IntoEnumIterator; +use strum_macros::EnumIter; + +use bytemuck::{from_bytes, Pod, Zeroable}; +use num_derive::FromPrimitive; + +use crate::wait_event::fingerprint::EcMkbpEventFingerprint; + +use super::host_event::EcMkbpEventHostEvent; + +#[derive(Debug, Clone, Copy, Pod, Zeroable)] +#[repr(C, packed)] +pub struct EcResponseMotionSenseFifoInfo { + size: u16, + count: u16, + timestamp: u32, + total_lost: u16, + lost: [u16; 0], +} + +#[derive(Debug)] +#[repr(u8)] +pub enum EcMkbpEvent { + KeyMatrix([u8; 13]), + HostEvent(EcMkbpEventHostEvent), + HostEvent64(u64), + SensorFifo(EcResponseMotionSenseFifoInfo), + Buttons(u32), + Switches(u32), + Fingerprint(EcMkbpEventFingerprint), + Sysrq(u32), + CecEvent(u32), +} +impl EcMkbpEvent { + fn max_event_size() -> usize { + EcMkbpEventType::iter() + .map(|e| e.data_size()) + .max() + .unwrap_or_default() + } + + fn from_bytes(bytes: &[u8]) -> Self { + let event_type = EcMkbpEventType::from_u8(bytes[0]).unwrap(); + event_type.event_from_bytes(bytes[1..1 + event_type.data_size()].to_vec()) + } + + pub(crate) fn read_sync(stream: &mut T) -> io::Result { + let mut buf: Vec = + vec![Default::default(); size_of::() + Self::max_event_size()]; + let bytes_read = stream.read(&mut buf)?; + Ok(Self::from_bytes(&buf[..bytes_read])) + } + + pub(crate) async fn read_async( + stream: &mut T, + ) -> io::Result { + let mut buf: Vec = + vec![Default::default(); size_of::() + Self::max_event_size()]; + let bytes_read = stream.read(&mut buf).await?; + Ok(Self::from_bytes(&buf[..bytes_read])) + } +} + +#[derive(Debug, FromPrimitive, Clone, Copy, EnumIter, PartialEq)] +#[cfg_attr(feature = "clap", derive(clap::ValueEnum))] +#[repr(u8)] +pub enum EcMkbpEventType { + KeyMatrix, + HostEvent, + SensorFifo, + Buttons, + Switches, + Fingerprint, + Sysrq, + HostEvent64, + CecEvent, + CecMessage, + DpAltModeEntered, + OnlineCalibration, + Pchg, +} + +impl EcMkbpEventType { + fn data_size(&self) -> usize { + match self { + Self::KeyMatrix => size_of::<[u8; 13]>(), + Self::HostEvent => size_of::(), + Self::SensorFifo => size_of::(), + Self::Buttons => size_of::(), + Self::Switches => size_of::(), + Self::Fingerprint => size_of::(), + Self::Sysrq => size_of::(), + Self::HostEvent64 => size_of::(), + Self::CecEvent => size_of::(), + _ => 0, + } + } + + fn event_from_bytes(&self, event: Vec) -> EcMkbpEvent { + match self { + EcMkbpEventType::Fingerprint => { + EcMkbpEvent::Fingerprint(from_bytes::(&event).to_owned()) + } + EcMkbpEventType::HostEvent => { + EcMkbpEvent::HostEvent(from_bytes::(&event).to_owned()) + } + event_type => panic!("{event_type:#?} from_bytes not implemented yet"), + } + } +} diff --git a/crosec/src/wait_event/fingerprint.rs b/crosec/src/wait_event/fingerprint.rs new file mode 100644 index 0000000..0a46b61 --- /dev/null +++ b/crosec/src/wait_event/fingerprint.rs @@ -0,0 +1,125 @@ +use std::fmt::Debug; + +use bytemuck::{Pod, Zeroable}; + +#[derive(Debug)] +pub enum EcMkbpEventFingerprintEnrollError { + LowQuality, + Immobile, + LowCoverage, + Internal, +} + +#[derive(Debug)] +pub struct EcMkbpEventFingerprintEnroll { + pub percentage: u8, + pub error: Option, +} + +#[derive(Debug)] +pub struct EcMkbpEventFingerprintMatch { + pub index: usize, + /// If `Some`, his means that the fingerprint matched an existing template and the existing template was updated to more accurately match future fingerprints. + /// `None` if `EC_MKBP_FP_ERR_MATCH_YES`. + /// `Some(Ok)` if `EC_MKBP_FP_ERR_MATCH_YES_UPDATED`. + /// `Some(Err)` if `EC_MKBP_FP_ERR_MATCH_YES_UPDATE_FAILED`. + // TODO: Find the CrOS EC documentation for this and add the link here + pub update: Option>, +} + +#[derive(Debug)] +pub enum EcMkbpEventFingerprintNoMatchError { + /// `EC_MKBP_FP_ERR_MATCH_NO_INTERNAL` - Probably means there was an internal error. + Internal, + /// `EC_MKBP_FP_ERR_MATCH_NO_TEMPLATES` - This either means there are no templates, or something's wrong with the templates. Idk which one. + Templates, + /// `EC_MKBP_FP_ERR_MATCH_NO_LOW_QUALITY` - My guess is this might happen if the sensor or finger is dirty + LowQuality, + /// `EC_MKBP_FP_ERR_MATCH_NO_LOW_COVERAGE` - My guess is this might happen if only a small part of a finger is sensed + LowCoverage, +} + +#[derive(Debug)] +pub enum EcMkbpEventFingerprintMatchResult { + Match(EcMkbpEventFingerprintMatch), + NoMatch(Result<(), EcMkbpEventFingerprintNoMatchError>), +} + +#[derive(Debug)] +pub enum EcMkbpEventFingerprintRust { + /// Contains the enroll progress, as a percentage + Enroll(EcMkbpEventFingerprintEnroll), + Match(EcMkbpEventFingerprintMatchResult), + FingerDown, + FingerUp, + ImageReady, +} + +const EC_MKBP_EVENT_FINGERPRINT_ERROR_MASK: u32 = 0x0000000F; + +#[derive(Clone, Copy, Pod, Zeroable)] +#[repr(C)] +pub struct EcMkbpEventFingerprint { + fp_events: u32, +} +impl EcMkbpEventFingerprint { + /// Get a Rust-friendly format. Uses CPU to call and format uses more memory. + pub fn rust(&self) -> EcMkbpEventFingerprintRust { + match self.fp_events { + fp_events if fp_events & (1 << 27) != 0 => { + EcMkbpEventFingerprintRust::Enroll(EcMkbpEventFingerprintEnroll { + percentage: ((self.fp_events & 0x00000FF0) >> 4).try_into().unwrap(), + error: match self.fp_events & EC_MKBP_EVENT_FINGERPRINT_ERROR_MASK { + 0 => None, + 1 => Some(EcMkbpEventFingerprintEnrollError::LowQuality), + 2 => Some(EcMkbpEventFingerprintEnrollError::Immobile), + 3 => Some(EcMkbpEventFingerprintEnrollError::LowCoverage), + 5 => Some(EcMkbpEventFingerprintEnrollError::Internal), + unknown_error => panic!("Unknown error: {unknown_error}"), + }, + }) + } + fp_events if fp_events & (1 << 28) != 0 => EcMkbpEventFingerprintRust::Match({ + let code = self.fp_events & EC_MKBP_EVENT_FINGERPRINT_ERROR_MASK; + let get_match_index = || ((self.fp_events & 0x0000F000) >> 12) as usize; + match code { + 0 => EcMkbpEventFingerprintMatchResult::NoMatch(Ok(())), + 6 => EcMkbpEventFingerprintMatchResult::NoMatch(Err( + EcMkbpEventFingerprintNoMatchError::Internal, + )), + 7 => EcMkbpEventFingerprintMatchResult::NoMatch(Err( + EcMkbpEventFingerprintNoMatchError::Templates, + )), + 2 => EcMkbpEventFingerprintMatchResult::NoMatch(Err( + EcMkbpEventFingerprintNoMatchError::LowQuality, + )), + 4 => EcMkbpEventFingerprintMatchResult::NoMatch(Err( + EcMkbpEventFingerprintNoMatchError::LowCoverage, + )), + 1 => EcMkbpEventFingerprintMatchResult::Match(EcMkbpEventFingerprintMatch { + index: get_match_index(), + update: None, + }), + 3 => EcMkbpEventFingerprintMatchResult::Match(EcMkbpEventFingerprintMatch { + index: get_match_index(), + update: Some(Ok(())), + }), + 5 => EcMkbpEventFingerprintMatchResult::Match(EcMkbpEventFingerprintMatch { + index: get_match_index(), + update: Some(Err(())), + }), + code => panic!("Unknown error code: {code} ({code:#b})"), + } + }), + fp_events if fp_events & (1 << 29) != 0 => EcMkbpEventFingerprintRust::FingerDown, + fp_events if fp_events & (1 << 30) != 0 => EcMkbpEventFingerprintRust::FingerUp, + fp_events if fp_events & (1 << 31) != 0 => EcMkbpEventFingerprintRust::ImageReady, + fp_events => panic!("Unknown FP event: {fp_events} ({fp_events:#b})"), + } + } +} +impl Debug for EcMkbpEventFingerprint { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.rust().fmt(f) + } +} diff --git a/crosec/src/wait_event/host_event.rs b/crosec/src/wait_event/host_event.rs new file mode 100644 index 0000000..051588e --- /dev/null +++ b/crosec/src/wait_event/host_event.rs @@ -0,0 +1,94 @@ +use std::fmt::Debug; + +use bytemuck::{Pod, Zeroable}; +use num::FromPrimitive; +use num_derive::FromPrimitive; +use strum_macros::IntoStaticStr; + +/// Host event codes. ACPI query EC command uses code 0 to mean "no event +/// pending". We explicitly specify each value in the enum listing so they won't +/// change if we delete/insert an item or rearrange the list (it needs to be +/// stable across platforms, not just within a single compiled instance). +#[repr(u32)] +#[derive(FromPrimitive, IntoStaticStr, Debug)] +pub enum HostEventCode { + None = 0, + LidClosed = 1 << 0, + LidOpen = 1 << 1, + PowerButton = 1 << 2, + AcConnected = 1 << 3, + AcDisconnected = 1 << 4, + BatteryLow = 1 << 5, + BatteryCritical = 1 << 6, + Battery = 1 << 7, + ThermalThreshold = 1 << 8, + /// Event generated by a device attached to the EC + Device = 1 << 9, + Thermal = 1 << 10, + /// GPU related event. Formerly named EC_HOST_EVENT_USB_CHARGER. + Gpu = 1 << 11, + KeyPressed = 1 << 12, + /// EC has finished initializing the host interface. The host can check + /// for this event following sending a EC_CMD_REBOOT_EC command to + /// determine when the EC is ready to accept subsequent commands. + InterfaceReady = 1 << 13, + /// Keyboard recovery combo has been pressed + KeyboardRecovery = 1 << 14, + /// Shutdown due to thermal overload + ThermalShutdown = 1 << 15, + /// Shutdown due to battery level too low + BatteryShutdown = 1 << 16, + /// Suggest that the AP throttle itself + ThrottleStart = 1 << 17, + /// Suggest that the AP resume normal speed + ThrottleStop = 1 << 18, + /// Hang detect logic detected a hang and host event timeout expired + HangDetect = 1 << 19, + /// Hang detect logic detected a hang and warm rebooted the AP + HangReboot = 1 << 20, + /// PD MCU triggering host event + PdMcu = 1 << 21, + /// Battery Status flags have changed + BatteryStatus = 1 << 22, + /// EC encountered a panic, triggering a reset + Panic = 1 << 23, + /// Keyboard fastboot combo has been pressed + KeyboardFastboot = 1 << 24, + /// EC RTC event occurred + Rtc = 1 << 25, + /// Emulate MKBP event + Mkbp = 1 << 26, + /// EC desires to change state of host-controlled USB mux + UsbMux = 1 << 27, + /// The device has changed "modes". This can be one of the following: + /// - TABLET/LAPTOP mode + /// - detachable base attach/detach event + ModeChange = 1 << 28, + /// Keyboard recovery combo with hardware reinitialization + KeyboardRecoveryHwReinit = 1 << 29, + /// WoV + Wov = 1 << 30, + /// The high bit of the event mask is not used as a host event code. If + /// it reads back as set, then the entire event mask should be considered + /// invalid by the host. This can happen when reading the raw event status + /// via EC_MEMMAP_HOST_EVENTS but the LPC interface is not initialized on + /// the EC, or improperly configured on the host. + Invalid = 1 << 31, +} + +#[derive(Pod, Zeroable, Clone, Copy)] +#[repr(C)] +pub struct EcMkbpEventHostEvent { + host_event: u32, +} +impl EcMkbpEventHostEvent { + /// Try getting the host event code enum. If it for some reason is something else, that's an error. + pub fn rust(self) -> Result { + HostEventCode::from_u32(self.host_event).ok_or(self.host_event) + } +} +impl Debug for EcMkbpEventHostEvent { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.rust().fmt(f) + } +} diff --git a/crosec/src/wait_event/mod.rs b/crosec/src/wait_event/mod.rs new file mode 100644 index 0000000..4d858cd --- /dev/null +++ b/crosec/src/wait_event/mod.rs @@ -0,0 +1,87 @@ +use std::{io, os::fd::AsRawFd}; + +use nix::{ + libc::{ioctl, poll, pollfd}, + request_code_none, +}; + +use event::{EcMkbpEvent, EcMkbpEventType}; + +use crate::CROS_EC_IOC_MAGIC; + +pub mod event; +pub mod fingerprint; +pub mod host_event; + +const POLL_IN: i16 = 0x001; + +#[derive(Debug)] +pub enum PollData { + EventHappened(EcMkbpEvent), + Timeout, + SomethingElseHappened(i16), +} + +fn get_mask>(event_types: I) -> i32 { + let mut mask = i32::default(); + for event_type in event_types { + mask |= 1 << event_type as u8; + } + assert_ne!(mask, 0, "Must specify at least one event type"); + mask +} + +/// If no timeout is specified, this function will wait for an unlimited amount of time +pub fn wait_event_sync>( + file: &mut File, + event_types: I, + timeout: Option, +) -> Result { + unsafe { + ioctl( + file.as_raw_fd(), + request_code_none!(CROS_EC_IOC_MAGIC, 2), + get_mask(event_types), + ) + }; + match timeout { + Some(timeout) => { + let mut fds = pollfd { + fd: file.as_raw_fd(), + events: POLL_IN, + revents: Default::default(), + }; + let result = unsafe { poll(&mut fds, 1, timeout) }; + match result { + 0 => Ok(PollData::Timeout), + 1 => match fds.revents { + POLL_IN => Ok(PollData::EventHappened( + EcMkbpEvent::read_sync(file).unwrap(), + )), + events => Ok(PollData::SomethingElseHappened(events)), + }, + result => Err(result), + } + } + None => Ok(PollData::EventHappened( + EcMkbpEvent::read_sync(file).unwrap(), + )), + } +} + +pub async fn wait_event_async< + File: AsRawFd + async_std::io::Read + Unpin, + I: IntoIterator, +>( + file: &mut File, + event_types: I, +) -> io::Result { + unsafe { + ioctl( + file.as_raw_fd(), + request_code_none!(CROS_EC_IOC_MAGIC, 2), + get_mask(event_types), + ) + }; + EcMkbpEvent::read_async(file).await +} diff --git a/ectool/Cargo.toml b/ectool/Cargo.toml index a634273..3604e14 100644 --- a/ectool/Cargo.toml +++ b/ectool/Cargo.toml @@ -2,10 +2,15 @@ name = "ectool" version = "0.1.0" edition = "2021" +description = "A Rust CLI for interfacing with the ChromeOS Embedded Controller." # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -crosec = { version = "0.1.0", path = "../crosec" } +crosec = { version = "0.1.0", path = "../crosec", features = ["clap"] } color-eyre = "0.6.2" -bytemuck = "1.16.0" +clap = { version = "4.5.4", features = ["derive"] } +num-traits = "0.2.19" +image = "0.25.1" +strum = "0.26.3" +uom = "0.36.0" diff --git a/ectool/src/charge_control_subcommand.rs b/ectool/src/charge_control_subcommand.rs new file mode 100644 index 0000000..dd2255e --- /dev/null +++ b/ectool/src/charge_control_subcommand.rs @@ -0,0 +1,73 @@ +use std::fs::File; + +use clap::Subcommand; +use color_eyre::eyre::Result; +use crosec::{ + commands::charge_control::{ + get_charge_control, set_charge_control, supports_get_and_sustainer, ChargeControl, + Sustainer, + }, + CROS_EC_PATH, +}; + +#[derive(Subcommand)] +pub enum ChargeControlSubcommand { + /// Charge the battery with external power and power the device with external power + Normal { + /// Minimum battery % to keep the battery at + min_percent: Option, + /// Maximum battery & to keep the battery at. If this isn't specified, this will be set to the same as the min %. + max_percent: Option, + }, + /// Power the device with external power, but do not charge the battery + Idle, + /// Power the device with the battery and do not charge the battery + Discharge, +} + +pub fn charge_control_subcommand(command: Option) -> Result<()> { + { + let mut file = File::open(CROS_EC_PATH)?; + match command { + None => { + if supports_get_and_sustainer(&mut file)? { + let charge_control = get_charge_control(&mut file)?; + println!("{charge_control:#?}"); + } else { + println!("This EC doesn't support getting charge control"); + } + } + Some(command) => match command { + ChargeControlSubcommand::Normal { + min_percent, + max_percent, + } => match min_percent { + Some(min_percent) => { + let max_percent = max_percent.unwrap_or(min_percent); + set_charge_control( + &mut file, + ChargeControl::Normal(Some(Sustainer { + min_percent: min_percent as i8, + max_percent: max_percent as i8, + })), + )?; + println!("Set charge control to normal with sustainer from {min_percent}% to {max_percent}%"); + } + None => { + set_charge_control(&mut file, ChargeControl::Normal(None))?; + println!("Set charge control to normal"); + } + }, + ChargeControlSubcommand::Idle => { + println!("Set charge control to idle"); + set_charge_control(&mut file, ChargeControl::Idle)?; + } + ChargeControlSubcommand::Discharge => { + println!("Set charge control to discharge"); + set_charge_control(&mut file, ChargeControl::Discharge)?; + } + }, + } + } + Ok(()) +} diff --git a/ectool/src/charge_current_limit_subcommand.rs b/ectool/src/charge_current_limit_subcommand.rs new file mode 100644 index 0000000..7fdcd4f --- /dev/null +++ b/ectool/src/charge_current_limit_subcommand.rs @@ -0,0 +1,11 @@ +use std::fs::File; + +use color_eyre::eyre::Result; +use crosec::{commands::charge_current_limit::set_charge_current_limit, CROS_EC_PATH}; +use uom::si::electric_current::{milliampere, ElectricCurrent}; + +pub fn charge_current_limit_subcommand(limit: u32) -> Result<()> { + let mut file = File::open(CROS_EC_PATH)?; + set_charge_current_limit(&mut file, ElectricCurrent::new::(limit as f32))?; + Ok(()) +} diff --git a/ectool/src/check_seed.rs b/ectool/src/check_seed.rs new file mode 100644 index 0000000..37530d7 --- /dev/null +++ b/ectool/src/check_seed.rs @@ -0,0 +1,11 @@ +use crosec::commands::fp_set_seed::FP_CONTEXT_TPM_BYTES; + +pub fn check_seed(seed: &str) -> Result<[u8; FP_CONTEXT_TPM_BYTES], String> { + match as TryInto<[u8; FP_CONTEXT_TPM_BYTES]>>::try_into(seed.as_bytes().to_owned()) { + Ok(seed) => Ok(seed), + Err(seed) => { + let seed_len = seed.len(); + Err(format!("The seed must be {FP_CONTEXT_TPM_BYTES} bytes long. The seed you inputted is {seed_len} bytes long.")) + } + } +} diff --git a/ectool/src/fp_download_subcommand.rs b/ectool/src/fp_download_subcommand.rs new file mode 100644 index 0000000..47dc18f --- /dev/null +++ b/ectool/src/fp_download_subcommand.rs @@ -0,0 +1,68 @@ +use std::{ + fs::File, + io::{stdout, Write}, +}; + +use clap::{Subcommand, ValueEnum}; +use color_eyre::eyre::Result; +use crosec::{ + commands::{ + fp_download::{fp_download, fp_download_template, DownloadType}, + fp_info::fp_info, + get_protocol_info::get_protocol_info, + }, + CROS_FP_PATH, +}; +use image::{ + codecs::pnm::{PnmEncoder, PnmSubtype, SampleEncoding}, + ImageEncoder, +}; + +#[derive(ValueEnum, Clone, Copy, Default)] +pub enum FrameType { + Raw, + #[default] + Pgm, +} + +#[derive(Subcommand)] +pub enum FpDownloadSubcommand { + Frame { frame_type: Option }, + Template { index: usize }, +} + +pub fn fp_download_subcommand(command: FpDownloadSubcommand) -> Result<()> { + let mut file = File::open(CROS_FP_PATH)?; + let fp_info = fp_info(&mut file)?; + let protocol_info = get_protocol_info(&mut file)?; + match command { + FpDownloadSubcommand::Frame { frame_type } => match frame_type.unwrap_or_default() { + FrameType::Raw => { + let frame = + fp_download(&mut file, &fp_info, &protocol_info, &DownloadType::RawImage); + stdout().write_all(&frame)?; + } + FrameType::Pgm => { + let frame = fp_download( + &mut file, + &fp_info, + &protocol_info, + &DownloadType::SimpleImage, + ); + PnmEncoder::new(stdout()) + .with_subtype(PnmSubtype::Graymap(SampleEncoding::Binary)) + .write_image( + &frame, + fp_info.width as u32, + fp_info.height as u32, + image::ExtendedColorType::L8, + )?; + } + }, + FpDownloadSubcommand::Template { index } => { + let template = fp_download_template(&mut file, &fp_info, &protocol_info, index); + stdout().write_all(template.buffer())?; + } + } + Ok(()) +} diff --git a/ectool/src/fp_get_encryption_status_command.rs b/ectool/src/fp_get_encryption_status_command.rs new file mode 100644 index 0000000..fc7bfe7 --- /dev/null +++ b/ectool/src/fp_get_encryption_status_command.rs @@ -0,0 +1,16 @@ +use crosec::commands::fp_get_encryption_status::{ + fp_get_encryption_status, EcResponseFpGetEncryptionStatus, +}; +use crosec::CROS_FP_PATH; +use std::fs::File; + +pub fn fp_get_encryption_status_command() -> color_eyre::Result<()> { + let mut file = File::open(CROS_FP_PATH)?; + let EcResponseFpGetEncryptionStatus { + status, + valid_flags, + } = fp_get_encryption_status(&mut file)?; + println!("FPMCU encryption status: {status:#b}"); + println!("Valid flags: {valid_flags:#b}"); + Ok(()) +} diff --git a/ectool/src/fp_upload_template_command.rs b/ectool/src/fp_upload_template_command.rs new file mode 100644 index 0000000..76529f7 --- /dev/null +++ b/ectool/src/fp_upload_template_command.rs @@ -0,0 +1,26 @@ +use std::{ + fs::File, + io::{stdin, Read}, +}; + +use color_eyre::eyre::Result; +use crosec::{ + commands::{ + fp_download::FpTemplate, fp_info::fp_info, fp_upload_template::fp_upload_template, + get_protocol_info::get_protocol_info, + }, + CROS_FP_PATH, +}; + +pub fn fp_upload_template_command() -> Result<()> { + let mut buf = Default::default(); + println!("Reading from stdin. If this command is taking a long time, it's probably because there is no EOF inputted from stdin."); + stdin().read_to_end(&mut buf)?; + let template = unsafe { FpTemplate::from_vec_unchecked(buf) }; + let mut file = File::open(CROS_FP_PATH)?; + let protocol_info = get_protocol_info(&mut file)?; + let fp_info = fp_info(&mut file)?; + fp_upload_template(&mut file, &protocol_info, &fp_info, &template)?; + println!("Uploaded template"); + Ok(()) +} diff --git a/ectool/src/get_uptime_info_command.rs b/ectool/src/get_uptime_info_command.rs new file mode 100644 index 0000000..82a588c --- /dev/null +++ b/ectool/src/get_uptime_info_command.rs @@ -0,0 +1,12 @@ +use std::fs::File; + +use crosec::commands::get_uptime_info::ec_cmd_get_uptime_info; + +use crate::Device; + +pub fn get_uptime_info_commnad(device: Option) -> color_eyre::Result<()> { + let mut file = File::open(device.unwrap_or_default().get_path())?; + let uptime_info = ec_cmd_get_uptime_info(&mut file)?; + println!("Uptime info: {uptime_info:#?}"); + Ok(()) +} diff --git a/ectool/src/main.rs b/ectool/src/main.rs index ca73d0d..950c6c4 100644 --- a/ectool/src/main.rs +++ b/ectool/src/main.rs @@ -1,33 +1,300 @@ +#![warn(unused_crate_dependencies)] + +use std::fs::File; + +use charge_control_subcommand::{charge_control_subcommand, ChargeControlSubcommand}; +use charge_current_limit_subcommand::charge_current_limit_subcommand; +use check_seed::check_seed; +use clap::{Parser, Subcommand, ValueEnum}; use color_eyre::eyre::Result; +use crosec::commands::fp_info::fp_info; +use crosec::commands::fp_mode::{fp_mode, FpMode}; +use crosec::commands::fp_set_seed::{fp_set_seed, FP_CONTEXT_TPM_BYTES}; +use crosec::commands::fp_stats::fp_stats; +use crosec::commands::get_protocol_info::get_protocol_info; +use crosec::wait_event::{event::EcMkbpEventType, wait_event_sync}; +use fp_download_subcommand::{fp_download_subcommand, FpDownloadSubcommand}; +use fp_upload_template_command::fp_upload_template_command; +use get_uptime_info_command::get_uptime_info_commnad; +use num_traits::cast::FromPrimitive; +use strum::IntoEnumIterator; + +use crate::fp_get_encryption_status_command::fp_get_encryption_status_command; +use crosec::battery::battery; +use crosec::commands::board_version::ec_cmd_board_version; +use crosec::commands::get_cmd_versions::ec_cmd_get_cmd_versions; +use crosec::commands::get_features::{ec_cmd_get_features, EC_FEATURE_PWM_FAN}; +use crosec::commands::set_fan_target_rpm::ec_cmd_set_fan_target_rpm; use crosec::commands::{ - get_chip_info::ec_cmd_get_chip_info, hello::ec_cmd_hello, version::ec_cmd_version, + get_chip_info::ec_cmd_get_chip_info, hello::ec_cmd_hello, version::ec_cmd_version, CrosEcCmd, }; +use crosec::console::console; +use crosec::get_number_of_fans::{get_number_of_fans, Error}; +use crosec::read_mem_any::read_mem_any; +use crosec::{ + CROS_EC_PATH, CROS_FP_PATH, EC_FAN_SPEED_ENTRIES, EC_FAN_SPEED_NOT_PRESENT, + EC_FAN_SPEED_STALLED, EC_MEM_MAP_FAN, +}; + +mod charge_control_subcommand; +mod charge_current_limit_subcommand; +mod check_seed; +mod fp_download_subcommand; +mod fp_get_encryption_status_command; +mod fp_upload_template_command; +mod get_uptime_info_command; + +#[derive(Parser)] +#[command(version, about)] +struct Cli { + #[command(subcommand)] + command: Commands, +} + +#[derive(Clone, Copy, ValueEnum, Default)] +enum Device { + #[default] + Ec, + Fp, +} + +impl Device { + pub fn get_path(&self) -> &'static str { + match self { + Self::Ec => CROS_EC_PATH, + Self::Fp => CROS_FP_PATH, + } + } +} + +#[derive(Subcommand)] +enum Commands { + /// Checks for basic communication with EC + Hello { + #[arg()] + device: Option, + }, + /// Prints EC version + Version, + /// Prints chip info + ChipInfo, + /// Prints the board version + BoardVersion, + /// Prints supported version mask for a command number + CmdVersions { + command: u32, + }, + /// Set target fan RPM + SetFanTargetRpm { + rpm: u32, + #[arg()] + index: Option, + }, + /// Get supported features + GetFeatures, + /// Get number of fans + GetNumberOfFans, + /// Get the speed of fans, in RPM + GetFanRpm, + /// Prints the last output to the EC debug console + Console { + #[arg()] + device: Option, + }, + /// Prints battery info + Battery, + ChargeControl { + #[command(subcommand)] + command: Option, + }, + FpInfo, + FpStats, + FpSetSeed { + #[arg(value_parser = check_seed)] + seed: [u8; FP_CONTEXT_TPM_BYTES], + }, + FpMode { + mode: Vec, + }, + WaitEvent { + event_types: Vec, + /// Timeout in milliseconds + #[arg(short, long)] + timeout: Option, + #[arg(short, long)] + device: Option, + }, + FpDownload { + #[command(subcommand)] + command: FpDownloadSubcommand, + }, + /// Uploads template from stdin + FpUploadTemplate, + FpGetEncryptionStatus, + GetUptimeInfo { + device: Option, + }, + ChargeCurrentLimit { + /// Limit in mA + #[arg()] + limit: u32, + }, +} fn main() -> Result<()> { color_eyre::install()?; - println!("Hello command"); - let status = ec_cmd_hello()?; - if status { - println!("EC says hello!"); - } else { - println!("EC did not say hello :("); - } + let cli = Cli::parse(); - println!("Version command"); - let (ro_ver, rw_ver, firmware_copy, build_info, tool_version) = ec_cmd_version()?; - println!("RO version: {ro_ver}"); - println!("RW version: {rw_ver}"); - println!("Firmware copy: {firmware_copy}"); - println!("Build info: {build_info}"); - println!("Tool version: {tool_version}"); - - println!("Chip info command"); - let (vendor, name, revision) = ec_cmd_get_chip_info()?; - println!("Chip info:"); - println!(" vendor: {vendor}"); - println!(" name: {name}"); - println!(" revision: {revision}"); + match cli.command { + Commands::Hello { device } => { + let mut file = File::open(device.unwrap_or_default().get_path())?; + let status = ec_cmd_hello(&mut file)?; + if status { + println!("EC says hello!"); + } else { + println!("EC did not say hello :("); + } + } + Commands::Version => { + let mut file = File::open(CROS_EC_PATH)?; + let max_sizes = get_protocol_info(&mut file)?; + let (ro_ver, rw_ver, firmware_copy, build_info, tool_version) = + ec_cmd_version(&mut file, &max_sizes)?; + println!("RO version: {ro_ver}"); + println!("RW version: {rw_ver}"); + println!("Firmware copy: {firmware_copy}"); + println!("Build info: {build_info}"); + println!("Tool version: {tool_version}"); + } + Commands::ChipInfo => { + let mut file = File::open(CROS_EC_PATH)?; + let (vendor, name, revision) = ec_cmd_get_chip_info(&mut file)?; + println!("Chip info:"); + println!(" vendor: {vendor}"); + println!(" name: {name}"); + println!(" revision: {revision}"); + } + Commands::BoardVersion => { + let mut file = File::open(CROS_EC_PATH)?; + let board_version = ec_cmd_board_version(&mut file)?; + println!("Board version: {board_version}"); + } + Commands::CmdVersions { command } => match CrosEcCmd::from_u32(command) { + Some(cmd) => { + let mut file = File::open(CROS_EC_PATH)?; + let versions = ec_cmd_get_cmd_versions(&mut file, cmd)?; + println!("Versions: {versions:#b}"); + } + None => { + println!("Unknown Command"); + } + }, + Commands::SetFanTargetRpm { rpm, index } => { + let mut file = File::open(CROS_EC_PATH)?; + ec_cmd_set_fan_target_rpm(&mut file, rpm, index)?; + match index { + Some(index) => { + println!("Set RPM to {rpm} for fan {index}"); + } + None => { + println!("Set RPM to {rpm} for all fans"); + } + } + } + Commands::GetFeatures => { + let mut file = File::open(CROS_EC_PATH)?; + let features = ec_cmd_get_features(&mut file)?; + println!("EC supported features: {features:#b}"); + } + Commands::GetNumberOfFans => { + let mut file = File::open(CROS_EC_PATH)?; + let number_of_fans = get_number_of_fans(&mut file).unwrap(); + println!("Number of fans: {number_of_fans}"); + } + Commands::GetFanRpm => { + let mut file = File::open(CROS_EC_PATH)?; + let features = ec_cmd_get_features(&mut file).map_err(Error::GetFeatures)?; + if features & EC_FEATURE_PWM_FAN != 0 { + read_mem_any::<[u16; EC_FAN_SPEED_ENTRIES]>(&mut file, EC_MEM_MAP_FAN) + .map_err(Error::ReadMem)? + .into_iter() + .enumerate() + .for_each(|(i, fan)| match fan { + EC_FAN_SPEED_NOT_PRESENT => {} + EC_FAN_SPEED_STALLED => { + println!("Fan {i} stalled"); + } + fan_speed => { + println!("Fan {i} RPM: {fan_speed}"); + } + }); + } else { + println!("No fans"); + }; + } + Commands::Console { device } => { + let mut file = File::open(device.unwrap_or_default().get_path())?; + let max_sizes = get_protocol_info(&mut file)?; + let console = console(&mut file, &max_sizes)?; + let console = console.trim(); + println!("{console}"); + } + Commands::Battery => { + let mut file = File::open(CROS_EC_PATH)?; + let battery_info = battery(&mut file)?; + println!("{battery_info:#?}"); + } + Commands::ChargeControl { command } => charge_control_subcommand(command)?, + Commands::FpInfo => { + let mut file = File::open(CROS_FP_PATH)?; + let info = fp_info(&mut file)?; + println!("{info:#?}"); + } + Commands::FpStats => { + let mut file = File::open(CROS_FP_PATH)?; + let stats = fp_stats(&mut file)?; + println!("{stats:#?}"); + } + Commands::FpSetSeed { seed } => { + let mut file = File::open(CROS_FP_PATH)?; + fp_set_seed(&mut file, seed)?; + println!("Set fp seed"); + } + Commands::FpMode { mode } => { + let mode = if !mode.is_empty() { + let mut mode_number: u32 = 0; + for mode in mode { + mode_number |= mode as u32; + } + mode_number + } else { + FpMode::DontChange as u32 + }; + let mut file = File::open(CROS_FP_PATH)?; + let mode = fp_mode(&mut file, mode)?; + let display = FpMode::display(mode); + println!("FP mode: {display}"); + } + Commands::WaitEvent { + mut event_types, + device, + timeout, + } => { + if event_types.is_empty() { + event_types = EcMkbpEventType::iter().collect(); + } + let mut file = File::open(device.unwrap_or_default().get_path())?; + println!("Waiting for event..."); + let result = wait_event_sync(&mut file, event_types, timeout).unwrap(); + println!("{result:#?}"); + } + Commands::FpDownload { command } => fp_download_subcommand(command)?, + Commands::FpUploadTemplate => fp_upload_template_command()?, + Commands::FpGetEncryptionStatus => fp_get_encryption_status_command()?, + Commands::GetUptimeInfo { device } => get_uptime_info_commnad(device)?, + Commands::ChargeCurrentLimit { limit } => charge_current_limit_subcommand(limit)?, + } Ok(()) -} \ No newline at end of file +} diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..420238d --- /dev/null +++ b/flake.lock @@ -0,0 +1,130 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1710146030, + "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "flake-utils_2": { + "inputs": { + "systems": "systems_2" + }, + "locked": { + "lastModified": 1705309234, + "narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1717602782, + "narHash": "sha256-pL9jeus5QpX5R+9rsp3hhZ+uplVHscNJh8n8VpqscM0=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e8057b67ebf307f01bdcc8fba94d94f75039d1f6", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1706487304, + "narHash": "sha256-LE8lVX28MV2jWJsidW13D2qrHU/RUUONendL2Q/WlJg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "90f456026d284c22b3e3497be980b2e47d0b28ac", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs", + "rust-overlay": "rust-overlay" + } + }, + "rust-overlay": { + "inputs": { + "flake-utils": "flake-utils_2", + "nixpkgs": "nixpkgs_2" + }, + "locked": { + "lastModified": 1717813066, + "narHash": "sha256-wqbRwq3i7g5EHIui0bIi84mdqZ/It1AXBSLJ5tafD28=", + "owner": "oxalica", + "repo": "rust-overlay", + "rev": "6dc3e45fe4aee36efeed24d64fc68b1f989d5465", + "type": "github" + }, + "original": { + "owner": "oxalica", + "repo": "rust-overlay", + "type": "github" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "systems_2": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..afea022 --- /dev/null +++ b/flake.nix @@ -0,0 +1,26 @@ +{ + description = "A devShell example"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + rust-overlay.url = "github:oxalica/rust-overlay"; + flake-utils.url = "github:numtide/flake-utils"; + }; + + outputs = { self, nixpkgs, rust-overlay, flake-utils, ... }: + flake-utils.lib.eachDefaultSystem (system: + let + overlays = [ (import rust-overlay) ]; + pkgs = import nixpkgs { + inherit system overlays; + }; + in + { + devShells.default = with pkgs; mkShell { + buildInputs = [ + rust-bin.stable.latest.default + ]; + }; + } + ); +}