sdrr_fw_parser/
lab.rs

1//! sdrr-fw-parser - One ROM Lab object handling
2
3// Copyright (C) 2025 Piers Finlayson <piers@piers.rocks>
4//
5// MIT License
6
7use deku::prelude::*;
8
9#[cfg(not(feature = "std"))]
10use alloc::{format, string::String};
11#[allow(unused_imports)]
12use log::{debug, error, info, trace, warn};
13
14use crate::{Reader, STM32F4_FLASH_BASE, STM32F4_RAM_BASE, Source, read_str_at_ptr};
15
16/// Container for both the parsed (flash) firmware information and (RAM)
17/// runtime information.
18#[derive(Debug, serde::Serialize, serde::Deserialize)]
19pub struct OneRomLab {
20    pub flash: Option<LabFlash>,
21    /// Runtime information
22    pub ram: Option<LabRam>,
23}
24
25/// Main One ROM Lab flash information data structure.
26///
27/// Reflects `onerom_lab::FlashInfo`
28#[derive(Debug, serde::Serialize, serde::Deserialize)]
29pub struct LabFlash {
30    pub major_version: String,
31    pub minor_version: String,
32    pub patch_version: String,
33    pub build_number: String,
34    pub mcu: String,
35    pub hw_rev: String,
36    pub features: String,
37    pub rtt_ptr: u32,
38}
39
40/// Main One ROM Lab RAM information data structure.
41///
42/// Reflects `onerom_lab::RamInfo`
43#[derive(Debug, serde::Serialize, serde::Deserialize)]
44pub struct LabRam {
45    pub rom_data_ptr: u32,
46    pub rpc_cmd_channel_ptr: u32,
47    pub rpc_rsp_channel_ptr: u32,
48    pub rpc_cmd_channel_size: u16,
49    pub rpc_rsp_channel_size: u16,
50}
51
52#[derive(Debug, DekuRead, DekuWrite)]
53#[deku(endian = "little", magic = b"ONEL")]
54// Used internally to construct [`LabFlash`]
55//
56// Reflects `onerom_lab::FlashInfo`
57//
58// &str in the original struct is a fat pointer (ptr + len), so we split into
59// 2 fields here and use `crate::read_str_at_ptr` to read the actual string.
60struct LabFlashInt {
61    major_version_ptr: u32,
62    major_version_len: u32,
63
64    minor_version_ptr: u32,
65    minor_version_len: u32,
66
67    patch_version_ptr: u32,
68    patch_version_len: u32,
69
70    build_number_ptr: u32,
71    build_number_len: u32,
72
73    mcu_ptr: u32,
74    mcu_len: u32,
75
76    hw_rev_ptr: u32,
77    hw_rev_len: u32,
78
79    features_ptr: u32,
80    features_len: u32,
81
82    rtt_ptr: u32,
83}
84
85impl LabFlashInt {
86    const SIZE: usize = 256;
87    const OFFSET: usize = 0x200;
88    const _SOURCE: Source = Source::Ram;
89
90    const fn size() -> usize {
91        Self::SIZE
92    }
93
94    const fn offset() -> usize {
95        Self::OFFSET
96    }
97
98    const fn _source() -> Source {
99        Self::_SOURCE
100    }
101
102    fn parse_and_validate(data: &[u8]) -> Result<Self, String> {
103        if data.len() < Self::size() {
104            return Err("Lab flash data too short".into());
105        }
106
107        let (_, info) = Self::from_bytes((data, 0))
108            .map_err(|e| format!("Failed to parse Lab flash data: {}", e))?;
109
110        Ok(info)
111    }
112}
113
114#[derive(Debug, DekuRead, DekuWrite)]
115#[deku(endian = "little", magic = b"onel")]
116// Used internally to construct [`LabFlash`]
117//
118// Reflects `onerom_lab::FlashInfo`
119struct LabRamInt {
120    rom_data_ptr: u32,
121    rpc_cmd_channel_ptr: u32,
122    rpc_rsp_channel_ptr: u32,
123    rpc_cmd_channel_size: u16,
124    rpc_rsp_channel_size: u16,
125}
126
127impl LabRamInt {
128    const SIZE: usize = 256;
129    const OFFSET: usize = 0;
130    const _SOURCE: Source = Source::Ram;
131
132    const fn size() -> usize {
133        Self::SIZE
134    }
135
136    const fn offset() -> usize {
137        Self::OFFSET
138    }
139
140    const fn _source() -> Source {
141        Self::_SOURCE
142    }
143
144    fn parse_and_validate(data: &[u8]) -> Result<Self, String> {
145        if data.len() < Self::size() {
146            return Err("Lab RAM data too short".into());
147        }
148
149        let (_, info) = Self::from_bytes((data, 0))
150            .map_err(|e| format!("Failed to parse Lab RAM data: {}", e))?;
151
152        Ok(info)
153    }
154}
155
156pub struct LabParser<'a, R: Reader> {
157    reader: &'a mut R,
158    base_flash_address: u32,
159    base_ram_address: u32,
160}
161
162impl<'a, R: Reader> LabParser<'a, R> {
163    pub fn new(reader: &'a mut R) -> Self {
164        Self {
165            reader,
166            base_flash_address: STM32F4_FLASH_BASE,
167            base_ram_address: STM32F4_RAM_BASE,
168        }
169    }
170
171    pub fn with_base_flash_address(
172        reader: &'a mut R,
173        base_flash_address: u32,
174        base_ram_address: u32,
175    ) -> Self {
176        Self {
177            reader,
178            base_flash_address,
179            base_ram_address,
180        }
181    }
182
183    async fn retrieve_flash_info(&mut self) -> Result<LabFlashInt, String> {
184        let addr = self.base_flash_address + LabFlashInt::offset() as u32;
185        let mut buf = [0u8; LabFlashInt::size()];
186        self.reader
187            .read(addr, &mut buf)
188            .await
189            .map_err(|_| "Failed to read Lab Flash info")?;
190        LabFlashInt::parse_and_validate(&buf)
191    }
192
193    async fn retrieve_ram_info(&mut self) -> Result<LabRamInt, String> {
194        let addr = self.base_ram_address + LabRamInt::offset() as u32;
195        let mut buf = [0u8; LabRamInt::size()];
196        self.reader
197            .read(addr, &mut buf)
198            .await
199            .map_err(|_| "Failed to read Lab RAM info")?;
200        LabRamInt::parse_and_validate(&buf)
201    }
202
203    pub async fn detect(&mut self) -> bool {
204        self.retrieve_flash_info().await.is_ok()
205    }
206
207    pub async fn parse(&mut self) -> OneRomLab {
208        let flash = match self.parse_flash().await {
209            Ok(info) => Some(info),
210            Err(e) => {
211                warn!("Failed to parse flash: {e}");
212                None
213            }
214        };
215        let ram = match self.parse_ram().await {
216            Ok(info) => Some(info),
217            Err(e) => {
218                warn!("Failed to parse RAM: {e}");
219                None
220            }
221        };
222
223        OneRomLab { flash, ram }
224    }
225
226    async fn parse_flash(&mut self) -> Result<LabFlash, String> {
227        let info = self
228            .retrieve_flash_info()
229            .await
230            .inspect_err(|e| warn!("Failed to retrieve flash: {e}"))?;
231        
232        let major_version = self
233            .read_flash_str_at_ptr(info.major_version_len, info.major_version_ptr)
234            .await
235            .inspect_err(|e| warn!("Failed to read major version: {e}"))?;
236        let minor_version = self
237            .read_flash_str_at_ptr(info.minor_version_len, info.minor_version_ptr)
238            .await
239            .inspect_err(|e| warn!("Failed to read minor version: {e}"))?;
240        let patch_version = self
241            .read_flash_str_at_ptr(info.patch_version_len, info.patch_version_ptr)
242            .await
243            .inspect_err(|e| warn!("Failed to read patch version: {e}"))?;
244        let build_number = self
245            .read_flash_str_at_ptr(info.build_number_len, info.build_number_ptr)
246            .await
247            .inspect_err(|e| warn!("Failed to read build number: {e}"))?;
248        let mcu = self
249            .read_flash_str_at_ptr(info.mcu_len, info.mcu_ptr)
250            .await
251            .inspect_err(|e| warn!("Failed to read MCU: {e}"))?;
252        let hw_rev = self
253            .read_flash_str_at_ptr(info.hw_rev_len, info.hw_rev_ptr)
254            .await
255            .inspect_err(|e| warn!("Failed to read HW revision: {e}"))?;
256        let features = self
257            .read_flash_str_at_ptr(info.features_len, info.features_ptr)
258            .await
259            .inspect_err(|e| warn!("Failed to read features: {e}"))?;
260
261        let flash = LabFlash {
262            major_version,
263            minor_version,
264            patch_version,
265            build_number,
266            mcu,
267            hw_rev,
268            features,
269            rtt_ptr: info.rtt_ptr,
270        };
271
272        Ok(flash)
273    }
274
275    async fn parse_ram(&mut self) -> Result<LabRam, String> {
276        let info = self.retrieve_ram_info().await?;
277
278        let ram = LabRam {
279            rom_data_ptr: info.rom_data_ptr,
280            rpc_cmd_channel_ptr: info.rpc_cmd_channel_ptr,
281            rpc_rsp_channel_ptr: info.rpc_rsp_channel_ptr,
282            rpc_cmd_channel_size: info.rpc_cmd_channel_size,
283            rpc_rsp_channel_size: info.rpc_rsp_channel_size,
284        };
285
286        Ok(ram)
287    }
288
289    async fn read_flash_str_at_ptr(&mut self, len: u32, ptr: u32) -> Result<String, String> {
290        if len > 0 && ptr < self.base_flash_address {
291            return Err(format!("Invalid pointer: 0x{:08X}", ptr));
292        }
293
294        read_str_at_ptr(self.reader, len, ptr).await
295    }
296}