From 8c3bf9ef3fd5f75e43631243dc7f77727f82707e Mon Sep 17 00:00:00 2001
From: atse <atse@m-labs.hk>
Date: Wed, 17 Jan 2024 12:33:16 +0800
Subject: [PATCH] Add command to show firmware version

Makes debugging easier
---
 Cargo.lock             | 158 ++++++++++++++++++++++++++++++++++++++++-
 Cargo.toml             |   3 +
 build.rs               |   2 +
 src/command_handler.rs |  20 ++++++
 src/command_parser.rs  |   8 +++
 src/firmware_ver.rs    |  20 ++++++
 src/main.rs            |   1 +
 7 files changed, 211 insertions(+), 1 deletion(-)
 create mode 100644 src/firmware_ver.rs

diff --git a/Cargo.lock b/Cargo.lock
index 877ce74..2669372 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -62,6 +62,21 @@ version = "1.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
 
+[[package]]
+name = "bitflags"
+version = "2.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf"
+
+[[package]]
+name = "built"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38d17f4d6e4dc36d1a02fbedc2753a096848e7c1b0772f7654eab8e2c927dd53"
+dependencies = [
+ "git2",
+]
+
 [[package]]
 name = "byteorder"
 version = "1.3.4"
@@ -74,6 +89,16 @@ version = "0.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
 
+[[package]]
+name = "cc"
+version = "1.0.83"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
+dependencies = [
+ "jobserver",
+ "libc",
+]
+
 [[package]]
 name = "cfg-if"
 version = "0.1.10"
@@ -204,6 +229,15 @@ dependencies = [
  "void",
 ]
 
+[[package]]
+name = "form_urlencoded"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
+dependencies = [
+ "percent-encoding",
+]
+
 [[package]]
 name = "generic-array"
 version = "0.12.3"
@@ -232,6 +266,19 @@ dependencies = [
  "version_check",
 ]
 
+[[package]]
+name = "git2"
+version = "0.18.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fbf97ba92db08df386e10c8ede66a2a0369bd277090afd8710e19e38de9ec0cd"
+dependencies = [
+ "bitflags 2.4.2",
+ "libc",
+ "libgit2-sys",
+ "log",
+ "url",
+]
+
 [[package]]
 name = "hash32"
 version = "0.1.1"
@@ -254,12 +301,61 @@ dependencies = [
  "stable_deref_trait",
 ]
 
+[[package]]
+name = "idna"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6"
+dependencies = [
+ "unicode-bidi",
+ "unicode-normalization",
+]
+
+[[package]]
+name = "jobserver"
+version = "0.1.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.152"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7"
+
+[[package]]
+name = "libgit2-sys"
+version = "0.16.1+1.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f2a2bb3680b094add03bb3732ec520ece34da31a8cd2d633d1389d0f0fb60d0c"
+dependencies = [
+ "cc",
+ "libc",
+ "libz-sys",
+ "pkg-config",
+]
+
 [[package]]
 name = "libm"
 version = "0.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c7d73b3f436185384286bd8098d17ec07c9a7d2388a6599f824d8502b529702a"
 
+[[package]]
+name = "libz-sys"
+version = "1.1.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "037731f5d3aaa87a5675e895b63ddff1a87624bc29f77004ea829809654e48f6"
+dependencies = [
+ "cc",
+ "libc",
+ "pkg-config",
+ "vcpkg",
+]
+
 [[package]]
 name = "log"
 version = "0.4.11"
@@ -342,6 +438,18 @@ dependencies = [
  "cortex-m-semihosting",
 ]
 
+[[package]]
+name = "percent-encoding"
+version = "2.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
+
+[[package]]
+name = "pkg-config"
+version = "0.3.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2900ede94e305130c13ddd391e0ab7cbaeb783945ae07a279c268cb05109c6cb"
+
 [[package]]
 name = "postcard"
 version = "0.5.1"
@@ -469,7 +577,7 @@ version = "0.7.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3e4a069bef843d170df47e7c0a8bf8d037f217d9f5b325865acc3e466ffe40d3"
 dependencies = [
- "bitflags",
+ "bitflags 1.2.1",
  "byteorder",
  "log",
  "managed",
@@ -553,6 +661,7 @@ version = "0.0.0"
 dependencies = [
  "bare-metal 1.0.0",
  "bit_field",
+ "built",
  "byteorder",
  "cortex-m 0.6.7",
  "cortex-m-log",
@@ -576,12 +685,42 @@ dependencies = [
  "usbd-serial",
 ]
 
+[[package]]
+name = "tinyvec"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
+dependencies = [
+ "tinyvec_macros",
+]
+
+[[package]]
+name = "tinyvec_macros"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
+
 [[package]]
 name = "typenum"
 version = "1.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33"
 
+[[package]]
+name = "unicode-bidi"
+version = "0.3.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75"
+
+[[package]]
+name = "unicode-normalization"
+version = "0.1.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921"
+dependencies = [
+ "tinyvec",
+]
+
 [[package]]
 name = "unicode-xid"
 version = "0.2.1"
@@ -599,6 +738,17 @@ dependencies = [
  "typenum",
 ]
 
+[[package]]
+name = "url"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633"
+dependencies = [
+ "form_urlencoded",
+ "idna",
+ "percent-encoding",
+]
+
 [[package]]
 name = "usb-device"
 version = "0.2.7"
@@ -622,6 +772,12 @@ version = "0.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "876e32dcadfe563a4289e994f7cb391197f362b6315dc45e8ba4aa6f564a4b3c"
 
+[[package]]
+name = "vcpkg"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
+
 [[package]]
 name = "version_check"
 version = "0.9.2"
diff --git a/Cargo.toml b/Cargo.toml
index 4775d89..6629842 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -38,6 +38,9 @@ heapless = "0.5"
 serde-json-core = "0.1"
 sfkv = "0.1"
 
+[build-dependencies]
+built = { version = "0.7", features = ["git2"] }
+
 [features]
 semihosting = ["panic-semihosting", "cortex-m-log/semihosting"]
 
diff --git a/build.rs b/build.rs
index 98f603e..c701e95 100644
--- a/build.rs
+++ b/build.rs
@@ -15,4 +15,6 @@ fn main() {
     // Only re-run the build script when memory.x is changed,
     // instead of when any part of the source code changes.
     println!("cargo:rerun-if-changed=memory.x");
+
+    built::write_built_file().expect("Failed to acquire build-time information");
 }
diff --git a/src/command_handler.rs b/src/command_handler.rs
index 3f30083..4d6ac38 100644
--- a/src/command_handler.rs
+++ b/src/command_handler.rs
@@ -25,6 +25,7 @@ use super::{
     session::Session,
     FanCtrl,
     hw_rev::HWRev,
+    firmware_ver,
 };
 
 use uom::{
@@ -57,6 +58,10 @@ pub enum Error {
     FlashError
 }
 
+mod built_info {
+    include!(concat!(env!("OUT_DIR"), "/built.rs"));
+}
+
 pub type JsonBuffer = Vec<u8, U1024>;
 
 fn send_line(socket: &mut TcpSocket, data: &[u8]) -> bool {
@@ -412,6 +417,20 @@ impl Handler {
         }
     }
 
+    fn show_firmware_version(socket: &mut TcpSocket) -> Result<Handler, Error> {
+        match firmware_ver::summary() {
+            Ok(buf) => {
+                send_line(socket, &buf);
+                Ok(Handler::Handled)
+            }
+            Err(e) => {
+                error!("unable to serialize FirmwareVer summary: {:?}", e);
+                let _ = writeln!(socket, "{{\"error\":\"{:?}\"}}", e);
+                Err(Error::ReportError)
+            }
+        }
+    }
+
     pub fn handle_command(command: Command, socket: &mut TcpSocket, channels: &mut Channels, session: &Session, store: &mut FlashStore, ipv4_config: &mut Ipv4Config, fan_ctrl: &mut FanCtrl, hwrev: HWRev) -> Result<Self, Error> {
         match command {
             Command::Quit => Ok(Handler::CloseSocket),
@@ -441,6 +460,7 @@ impl Handler {
             Command::FanCurve { k_a, k_b, k_c } => Handler::fan_curve(socket, fan_ctrl, k_a, k_b, k_c),
             Command::FanCurveDefaults => Handler::fan_defaults(socket, fan_ctrl),
             Command::ShowHWRev => Handler::show_hwrev(socket, hwrev),
+            Command::ShowFirmwareVer => Handler::show_firmware_version(socket),
         }
     }
 }
\ No newline at end of file
diff --git a/src/command_parser.rs b/src/command_parser.rs
index b9e70bb..b126316 100644
--- a/src/command_parser.rs
+++ b/src/command_parser.rs
@@ -191,6 +191,7 @@ pub enum Command {
     },
     FanCurveDefaults,
     ShowHWRev,
+    ShowFirmwareVer,
 }
 
 fn end(input: &[u8]) -> IResult<&[u8], ()> {
@@ -600,6 +601,7 @@ fn command(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> {
          fan,
          fan_curve,
          value(Ok(Command::ShowHWRev), tag("hwrev")),
+         value(Ok(Command::ShowFirmwareVer), tag("version")),
     ))(input)
 }
 
@@ -861,4 +863,10 @@ mod test {
         let command = Command::parse(b"hwrev");
         assert_eq!(command, Ok(Command::ShowHWRev));
     }
+
+    #[test]
+    fn parse_firmware_ver() {
+        let command = Command::parse(b"version");
+        assert_eq!(command, Ok(Command::ShowFirmwareVer));
+    }
 }
diff --git a/src/firmware_ver.rs b/src/firmware_ver.rs
new file mode 100644
index 0000000..72cc99b
--- /dev/null
+++ b/src/firmware_ver.rs
@@ -0,0 +1,20 @@
+use serde::Serialize;
+use crate::command_handler::JsonBuffer;
+
+mod built_info {
+    include!(concat!(env!("OUT_DIR"), "/built.rs"));
+}
+
+#[derive(Serialize)]
+pub struct FirmwareSummary {
+    git_hash: Option<&'static str>,
+    is_dirty: Option<bool>,
+}
+
+pub fn summary() -> Result<JsonBuffer, serde_json_core::ser::Error> {
+    let summary = FirmwareSummary {
+        git_hash: built_info::GIT_COMMIT_HASH,
+        is_dirty: built_info::GIT_DIRTY,
+    };
+    serde_json_core::to_vec(&summary)
+}
diff --git a/src/main.rs b/src/main.rs
index 353a161..b81cd69 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -55,6 +55,7 @@ use command_handler::Handler;
 mod fan_ctrl;
 use fan_ctrl::FanCtrl;
 mod hw_rev;
+mod firmware_ver;
 
 const HSE: MegaHertz = MegaHertz(8);
 #[cfg(not(feature = "semihosting"))]