From 6ac96671513d40bfec5d0e14712323a4845db698 Mon Sep 17 00:00:00 2001
From: Benjamin Koch <bbbsnowball@gmail.com>
Date: Sun, 11 Jun 2023 00:36:44 +0200
Subject: [PATCH] refactoring: move ProgramInfoBuilder into dedicated file

---
 firmware/rust1/src/bin/heizung.rs  | 203 +----------------------------
 firmware/rust1/src/lib.rs          |   1 +
 firmware/rust1/src/program_info.rs | 203 +++++++++++++++++++++++++++++
 3 files changed, 208 insertions(+), 199 deletions(-)
 create mode 100644 firmware/rust1/src/program_info.rs

diff --git a/firmware/rust1/src/bin/heizung.rs b/firmware/rust1/src/bin/heizung.rs
index 1845fe3..371d6c9 100644
--- a/firmware/rust1/src/bin/heizung.rs
+++ b/firmware/rust1/src/bin/heizung.rs
@@ -25,6 +25,7 @@ use heapless::String;
 
 use heizung::i2c_scan::{self, ScanResultForBus};
 use heizung::modbus_server::{ModbusServer, ModbusRegisters, ModbusErrorCode, ModbusAdressMatch, U16Pusher};
+use heizung::program_info::ProgramInfoBuilder;
 use heizung::rs485::RS485;
 use heizung::uf2updater::UF2UpdateHandler;
 use heizung::watchdog::WatchdogFixed;
@@ -700,203 +701,7 @@ fn init_early() {
 // Add program info.
 #[used]
 #[link_section = ".program_info"]
-pub static PROGRAM_INFO: [u8; 4096] = make_program_info();
-
-struct ProgramInfoBuilder {
-    addr: u32,
-    buf: [u8; 4096],
-    info_pos: usize,
-    data_pos: usize,
-    info_table_size: usize,
-    data_end_offset: usize,
-}
-
-impl ProgramInfoBuilder {
-    const fn new(addr: u32, info_table_size: u32, copy_table_size: u32) -> Self {
-        let copy_table_size = copy_table_size as usize;
-        let dummy_item_ptr = addr + info_table_size;
-        let dummy_item_size = 4;
-
-        core::assert!(info_table_size % 4 == 0);
-
-        ProgramInfoBuilder {
-            addr,
-            buf: {
-                let mut x = [0xff; 4096];
-                // mark end of copy table with zero in first entry
-                x[4096 - copy_table_size + 0] = 0;
-                x[4096 - copy_table_size + 1] = 0;
-                x[4096 - copy_table_size + 2] = 0;
-                x[4096 - copy_table_size + 3] = 0;
-                x
-            },
-            info_pos: 0,
-            data_pos: info_table_size as usize + dummy_item_size,
-            data_end_offset: 4096 - copy_table_size as usize,
-            info_table_size: info_table_size as usize,
-        }
-    }
-
-    const fn build(mut self: Self) -> [u8; 4096] {
-        if self.info_pos < self.info_table_size {
-            self = self.align();
-            let addr = self.addr + self.data_pos as u32;
-            self = self.push_u32(0xffffffff).fill_info_table(addr);
-        }
-        self.buf
-    }
-
-    const fn push_info_u8(mut self: Self, value: u8) -> Self {
-        self.buf[self.info_pos] = value;
-        self.info_pos += 1;
-        if self.info_pos > self.info_table_size {
-            core::panic!("ProgramInfoBuilder: too much data for info table")
-        }
-        self
-    }
-
-    const fn push_info_ptr(mut self: Self, value: u32) -> Self {
-        self
-            .push_info_u8((value & 0xff) as u8)
-            .push_info_u8(((value >> 8) & 0xff) as u8)
-            .push_info_u8(((value >> 16) & 0xff) as u8)
-            .push_info_u8(((value >> 24) & 0xff) as u8)
-    }
-
-    const fn push_current_info_ptr(mut self: Self) -> Self {
-        self = self.align();
-        let addr = self.addr + self.data_pos as u32;
-        self.push_info_ptr(addr)
-    }
-
-    const fn fill_info_table(mut self: Self, value: u32) -> Self {
-        if self.info_pos < self.info_table_size {
-            self = self.push_info_ptr(value);
-        }
-        if self.info_pos < self.info_table_size {
-            self = self.push_info_ptr(value);
-        }
-        if self.info_pos < self.info_table_size {
-            self = self.push_info_ptr(value);
-        }
-        if self.info_pos < self.info_table_size {
-            self = self.push_info_ptr(value);
-        }
-        if self.info_pos < self.info_table_size {
-            self.fill_info_table(value)
-        } else {
-            self
-        }
-    }
-
-    const fn push(mut self: Self, value: u8) -> Self {
-        self.buf[self.data_pos] = value;
-        self.data_pos += 1;
-        if self.data_pos > self.data_end_offset {
-            core::panic!("ProgramInfoBuilder: too much data")
-        }
-        self
-    }
-
-    const fn align(mut self: Self) -> Self {
-        if self.data_pos % 4 != 0 {
-            self = self.push(0xff);
-        }
-        if self.data_pos % 4 != 0 {
-            self = self.push(0xff);
-        }
-        if self.data_pos % 4 != 0 {
-            self = self.push(0xff);
-        }
-        self
-    }
-
-    const fn push_u16(self: Self, value: u16) -> Self {
-        self
-            .push((value & 0xff) as u8)
-            .push(((value >> 8) & 0xff) as u8)
-    }
-
-    const fn push_u32(self: Self, value: u32) -> Self {
-        self
-            .push((value & 0xff) as u8)
-            .push(((value >> 8) & 0xff) as u8)
-            .push(((value >> 16) & 0xff) as u8)
-            .push(((value >> 24) & 0xff) as u8)
-    }
-
-    const fn push_raspberry_tag(self: Self) -> Self {
-        self.push('R' as u8)
-            .push('P' as u8)
-    }
-
-    const fn push_extra_data(mut self: Self, value: &[u8]) -> (Self, u32) {
-        if self.data_pos - self.info_pos < value.len() {
-            core::panic!("ProgramInfoBuilder: too much data")
-        }
-        let offset = self.data_pos;
-        //self.buf[offset..self.data_pos].copy_from_slice(value);
-        //for i in 0..value.len() {
-        //    self.buf[offset+i] = value[i];
-        //}
-        if value.len() > 0 { self.buf[offset+0] = value[0]; }
-        if value.len() > 1 { self.buf[offset+1] = value[1]; }
-        if value.len() > 2 { self.buf[offset+2] = value[2]; }
-        if value.len() > 3 { self.buf[offset+3] = value[3]; }
-        if value.len() > 4 { self.buf[offset+4] = value[4]; }
-        if value.len() > 5 { self.buf[offset+5] = value[5]; }
-        if value.len() > 6 { self.buf[offset+6] = value[6]; }
-        if value.len() > 7 { self.buf[offset+7] = value[7]; }
-        if value.len() > 8 { core::panic!("too long") }
-
-        self.data_pos += value.len();
-        let addr = self.addr + offset as u32;
-        (self, addr)
-    }
-
-    const fn push_string(mut self: Self, value: &str) -> Self {
-        if self.data_pos - self.info_pos < value.len() {
-            core::panic!("ProgramInfoBuilder: too much data")
-        }
-        let x1 = self.data_pos;
-        self = self.push_u32(0);
-
-        let (self2, addr) = self.push_extra_data(value.as_bytes());
-        self = self2;
-        (self, _) = self.push_extra_data(&[0]);
-        
-        let x2 = self.data_pos;
-        self.data_pos = x1;
-        self = self.push_u32(addr);
-        self.data_pos = x2;
-        
-        self
-    }
-
-    const BINARY_INFO_TYPE_ID_AND_INT: u16 = 5;
-    const BINARY_INFO_TYPE_ID_AND_STRING: u16 = 6;
-    const BINARY_INFO_ID_RP_PROGRAM_NAME: u32 = 0x02031c86;
-    const BINARY_INFO_ID_RP_BINARY_END: u32 = 0x68f465de;
-
-    const fn push_program_name(self: Self, value: &str) -> Self {
-        self.push_current_info_ptr()
-            .push_u16(Self::BINARY_INFO_TYPE_ID_AND_STRING)
-            .push_raspberry_tag()
-            .push_u32(Self::BINARY_INFO_ID_RP_PROGRAM_NAME)
-            .push_string(value)
-    }
-
-    #[allow(unused)]
-    const fn push_binary_end(self: Self, value: &str) -> Self {
-        self.push_current_info_ptr()
-            .push_u16(Self::BINARY_INFO_TYPE_ID_AND_INT)
-            .push_raspberry_tag()
-            .push_u32(Self::BINARY_INFO_ID_RP_BINARY_END)
-            .push_string(value)
-    }
-}
-
-const fn make_program_info() -> [u8; 4096] {
+pub static PROGRAM_INFO: [u8; 4096] = {
     // all of these must match the values in the bootloader
     const PROGRAM_INFO_FLASH_OFFSET: u32 = 0x10087000 - PROGRAM_INFO_TOTAL_SIZE;  // last 4k of active partition
     const PROGRAM_INFO_TOTAL_SIZE: u32 = 4096;
@@ -904,6 +709,6 @@ const fn make_program_info() -> [u8; 4096] {
     const PROGRAM_INFO_COPY_TABLE_SIZE: u32 = 128;  // 3*u32 for each entry, picotool stops after 10 entries
 
     ProgramInfoBuilder::new(PROGRAM_INFO_FLASH_OFFSET, PROGRAM_INFO_TABLE_SIZE, PROGRAM_INFO_COPY_TABLE_SIZE)
-        .push_program_name("Test 123")
+        .program_name("Test 123")
         .build()
-}
+};
diff --git a/firmware/rust1/src/lib.rs b/firmware/rust1/src/lib.rs
index da50916..052ee5d 100644
--- a/firmware/rust1/src/lib.rs
+++ b/firmware/rust1/src/lib.rs
@@ -8,3 +8,4 @@ pub mod clear_bootloader_state;
 pub mod uf2;
 pub mod uf2updater;
 pub mod watchdog;
+pub mod program_info;
diff --git a/firmware/rust1/src/program_info.rs b/firmware/rust1/src/program_info.rs
new file mode 100644
index 0000000..de20db1
--- /dev/null
+++ b/firmware/rust1/src/program_info.rs
@@ -0,0 +1,203 @@
+
+pub struct ProgramInfoBuilder {
+    addr: u32,
+    buf: [u8; 4096],
+    info_pos: usize,
+    data_pos: usize,
+    info_table_size: usize,
+    data_end_offset: usize,
+}
+
+const fn copy(mut buf: [u8; 4096], offset: usize, source: &[u8], source_offset: usize) -> [u8; 4096] {
+    if source_offset >= source.len() {
+        buf
+    } else {
+        buf[offset] = source[source_offset];
+        copy(buf, offset+1, source, source_offset+1)
+    }
+}
+
+impl ProgramInfoBuilder {
+    pub const fn new(addr: u32, info_table_size: u32, copy_table_size: u32) -> Self {
+        let copy_table_size = copy_table_size as usize;
+
+        core::assert!(info_table_size % 4 == 0);
+
+        ProgramInfoBuilder {
+            addr,
+            buf: {
+                let mut x = [0xff; 4096];
+                // mark end of copy table with zero in first entry
+                x[4096 - copy_table_size + 0] = 0;
+                x[4096 - copy_table_size + 1] = 0;
+                x[4096 - copy_table_size + 2] = 0;
+                x[4096 - copy_table_size + 3] = 0;
+                x
+            },
+            info_pos: 0,
+            data_pos: info_table_size as usize,
+            data_end_offset: 4096 - copy_table_size as usize,
+            info_table_size: info_table_size as usize,
+        }
+    }
+
+    pub const fn build(mut self: Self) -> [u8; 4096] {
+        if self.info_pos < self.info_table_size {
+            self = self.align();
+            let addr = self.addr + self.data_pos as u32;
+            self = self.push_u32(0xffffffff).fill_info_table(addr);
+        }
+        self.buf
+    }
+
+    const fn push_info_u8(mut self: Self, value: u8) -> Self {
+        self.buf[self.info_pos] = value;
+        self.info_pos += 1;
+        if self.info_pos > self.info_table_size {
+            core::panic!("ProgramInfoBuilder: too much data for info table")
+        }
+        self
+    }
+
+    const fn push_info_ptr(self: Self, value: u32) -> Self {
+        self
+            .push_info_u8((value & 0xff) as u8)
+            .push_info_u8(((value >> 8) & 0xff) as u8)
+            .push_info_u8(((value >> 16) & 0xff) as u8)
+            .push_info_u8(((value >> 24) & 0xff) as u8)
+    }
+
+    const fn push_current_info_ptr(mut self: Self) -> Self {
+        self = self.align();
+        let addr = self.addr + self.data_pos as u32;
+        self.push_info_ptr(addr)
+    }
+
+    const fn fill_info_table(mut self: Self, value: u32) -> Self {
+        if self.info_pos < self.info_table_size {
+            self = self.push_info_ptr(value);
+        }
+        if self.info_pos < self.info_table_size {
+            self = self.push_info_ptr(value);
+        }
+        if self.info_pos < self.info_table_size {
+            self = self.push_info_ptr(value);
+        }
+        if self.info_pos < self.info_table_size {
+            self = self.push_info_ptr(value);
+        }
+        if self.info_pos < self.info_table_size {
+            self.fill_info_table(value)
+        } else {
+            self
+        }
+    }
+
+    const fn push(mut self: Self, value: u8) -> Self {
+        self.buf[self.data_pos] = value;
+        self.data_pos += 1;
+        if self.data_pos > self.data_end_offset {
+            core::panic!("ProgramInfoBuilder: too much data")
+        }
+        self
+    }
+
+    pub const fn align(mut self: Self) -> Self {
+        if self.data_pos % 4 != 0 {
+            self = self.push(0xff);
+        }
+        if self.data_pos % 4 != 0 {
+            self = self.push(0xff);
+        }
+        if self.data_pos % 4 != 0 {
+            self = self.push(0xff);
+        }
+        self
+    }
+
+    const fn push_u16(self: Self, value: u16) -> Self {
+        self
+            .push((value & 0xff) as u8)
+            .push(((value >> 8) & 0xff) as u8)
+    }
+
+    const fn push_u32(self: Self, value: u32) -> Self {
+        self
+            .push((value & 0xff) as u8)
+            .push(((value >> 8) & 0xff) as u8)
+            .push(((value >> 16) & 0xff) as u8)
+            .push(((value >> 24) & 0xff) as u8)
+    }
+
+    const fn push_raspberry_tag(self: Self) -> Self {
+        self.push('R' as u8)
+            .push('P' as u8)
+    }
+
+    const fn push_extra_data(mut self: Self, value: &[u8]) -> (Self, u32) {
+        if self.data_end_offset - self.data_pos < value.len() {
+            core::panic!("ProgramInfoBuilder: too much data")
+        }
+        let offset = self.data_pos;
+        //self.buf[offset..offset+value.len()].copy_from_slice(value);
+        self.buf = copy(self.buf, offset, value, 0);
+
+        self.data_pos += value.len();
+        let addr = self.addr + offset as u32;
+        (self, addr)
+    }
+
+    //NOTE This will push the address and then add the string so this must be the last item of the data record.
+    const fn push_string(mut self: Self, value: &str) -> Self {
+        if self.data_pos - self.info_pos < value.len() {
+            core::panic!("ProgramInfoBuilder: too much data")
+        }
+
+        // push dummy value (will be replaced by address)
+        let x1 = self.data_pos;
+        self = self.push_u32(0);
+
+        let (self2, addr) = self.push_extra_data(value.as_bytes());
+        self = self2;
+        (self, _) = self.push_extra_data(&[0]);
+
+        // replace dummy by actual address
+        let x2 = self.data_pos;
+        self.data_pos = x1;
+        self = self.push_u32(addr);
+        self.data_pos = x2;
+        
+        self
+    }
+
+    const BINARY_INFO_TYPE_ID_AND_INT: u16 = 5;
+    const BINARY_INFO_TYPE_ID_AND_STRING: u16 = 6;
+
+    pub const BINARY_INFO_ID_RP_PROGRAM_NAME: u32 = 0x02031c86;
+    pub const BINARY_INFO_ID_RP_PROGRAM_VERSION_STRING: u32 = 0x11a9bc3a;
+    pub const BINARY_INFO_ID_RP_PROGRAM_BUILD_DATE_STRING: u32 = 0x9da22254;
+    pub const BINARY_INFO_ID_RP_BINARY_END: u32 = 0x68f465de;
+    pub const BINARY_INFO_ID_RP_PROGRAM_URL: u32 = 0x1856239a;
+    pub const BINARY_INFO_ID_RP_PROGRAM_DESCRIPTION: u32 = 0xb6a07c19;
+    pub const BINARY_INFO_ID_RP_PROGRAM_FEATURE: u32 = 0xa1f4b453;
+    pub const BINARY_INFO_ID_RP_PROGRAM_BUILD_ATTRIBUTE: u32 = 0x4275f0d3;
+    pub const BINARY_INFO_ID_RP_SDK_VERSION: u32 = 0x5360b3ab;
+    pub const BINARY_INFO_ID_RP_PICO_BOARD: u32 = 0xb63cffbb;
+    pub const BINARY_INFO_ID_RP_BOOT2_NAME: u32 = 0x7f8882e1;
+
+    pub const fn program_name(self: Self, value: &str) -> Self {
+        self.push_current_info_ptr()
+            .push_u16(Self::BINARY_INFO_TYPE_ID_AND_STRING)
+            .push_raspberry_tag()
+            .push_u32(Self::BINARY_INFO_ID_RP_PROGRAM_NAME)
+            .push_string(value)
+    }
+
+    pub const fn binary_end_address(self: Self, value: u32) -> Self {
+        self.push_current_info_ptr()
+            .push_u16(Self::BINARY_INFO_TYPE_ID_AND_INT)
+            .push_raspberry_tag()
+            .push_u32(Self::BINARY_INFO_ID_RP_BINARY_END)
+            .push_u32(value)
+    }
+}
-- 
GitLab