Skip to content

Commit

Permalink
firmware: clean up and add FIrmwareHeader formatter
Browse files Browse the repository at this point in the history
Cleans up some code, and adds a formatter implementation for
`FirmwareHeader`.
  • Loading branch information
ssp-rs committed Dec 18, 2023
1 parent 80c65c7 commit bfa9595
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 8 deletions.
2 changes: 2 additions & 0 deletions src/types/firmware.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ pub use dataset::*;
pub use header::*;
pub use ram::*;

pub const FIRMWARE_ACK: u8 = 0x32;

/// Parses an ITL firmware file into [FirmwareHeader], [FirmwareRam], and [FirmwareData]
/// structures.
#[cfg(feature = "std")]
Expand Down
41 changes: 34 additions & 7 deletions src/types/firmware/dataset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ use crate::{Error, Result};
pub const FIRMWARE_DATA_MAX: usize = u32::MAX as usize;
/// Length of a [FirmwareData] section.
pub const FIRMWARE_DATA_SECTION_LEN: usize = 128;
/// Default length of a [FirmwareData] block.
///
/// A block consists of one or more sections.
pub const DEFAULT_DATA_BLOCK_LEN: usize = 128;

/// Represents the Firmware DATA block in the ITL firmware file.
///
Expand All @@ -19,9 +23,12 @@ pub const FIRMWARE_DATA_SECTION_LEN: usize = 128;
/// The device responds with its calculated XOR-checksum byte.
///
/// If there is a mismatch, abort the firmware update, and retry.
#[repr(C)]
#[derive(Clone, Debug, PartialEq)]
pub struct FirmwareData {
block: Vec<u8>,
index: usize,
block_len: usize,
}

impl FirmwareData {
Expand All @@ -30,6 +37,7 @@ impl FirmwareData {
Self {
block: Vec::new(),
index: 0,
block_len: DEFAULT_DATA_BLOCK_LEN,
}
}

Expand All @@ -38,9 +46,7 @@ impl FirmwareData {
/// Returns:
/// - `Ok(FirmwareData)` on success
/// - `Err(Error)` if `val` length exceeds [FIRMWARE_DATA_MAX].
// TODO: should we do checks for valid machine instructions? Are there specifications available
// for the ITL instruction set used on their devices?
pub fn create(val: &[u8]) -> Result<Self> {
pub fn create(val: &[u8], block_len: usize) -> Result<Self> {
let len = val.len();
if len > FIRMWARE_DATA_MAX {
Err(Error::Firmware(format!(
Expand All @@ -50,6 +56,7 @@ impl FirmwareData {
Ok(Self {
block: val.into(),
index: 0,
block_len,
})
}
}
Expand Down Expand Up @@ -87,17 +94,37 @@ impl FirmwareData {
}
}

/// Gets the block length returned by the RAM programming command.
pub fn block_len(&self) -> usize {
self.block_len
}

/// Sets the block length returned by the RAM programming command.
pub fn set_block_len(&mut self, len: u16) {
self.block_len = len as usize;
}

/// Gets the current index in the [FirmwareData] block.
pub const fn index(&self) -> usize {
self.index
}

/// Gets the length of the [FirmwareData] buffer.
pub fn len(&self) -> usize {
self.block.len()
}

/// Gets whether the [FirmwareData] buffer is empty.
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}

impl TryFrom<&[u8]> for FirmwareData {
type Error = Error;

fn try_from(val: &[u8]) -> Result<Self> {
Self::create(val)
Self::create(val, DEFAULT_DATA_BLOCK_LEN)
}
}

Expand Down Expand Up @@ -129,7 +156,7 @@ mod tests {
let mut index = 0;
let exp_sections = 32;

let mut data_block = FirmwareData::create(&exp_data_buf)?;
let mut data_block = FirmwareData::create(&exp_data_buf, FIRMWARE_DATA_SECTION_LEN)?;

while let Some(section) = data_block.next_section() {
assert_eq!(section, &exp_data_buf[index..index + section.len()]);
Expand All @@ -150,7 +177,7 @@ mod tests {
let mut index = 0;
let exp_sections = 2;

let mut data_block = FirmwareData::create(&exp_data_buf)?;
let mut data_block = FirmwareData::create(&exp_data_buf, FIRMWARE_DATA_SECTION_LEN)?;

while let Some(section) = data_block.next_section() {
assert_eq!(section, &exp_data_buf[index..index + section.len()]);
Expand All @@ -169,7 +196,7 @@ mod tests {
let bad_data_buf = vec![0xff; FIRMWARE_DATA_MAX + 1];
let bad_slice: &[u8] = bad_data_buf.as_ref();

assert!(FirmwareData::create(bad_slice).is_err());
assert!(FirmwareData::create(bad_slice, FIRMWARE_DATA_SECTION_LEN).is_err());
assert!(FirmwareData::try_from(bad_slice).is_err());
}
}
37 changes: 36 additions & 1 deletion src/types/firmware/header.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use core::mem;
use core::{fmt, mem};

use crate::{Error, Result};

Expand Down Expand Up @@ -47,6 +47,16 @@ impl FirmwareHeader {
u32::from_be_bytes([0, self.magic[0], self.magic[1], self.magic[2]])
}

/// Gets the [FirmwareHeader] magic bytes as a string.
pub fn magic_str(&self) -> &str {
core::str::from_utf8(self.magic.as_ref()).unwrap_or("")
}

/// Gets the [FirmwareHeader] reserved bytes.
pub(crate) fn reserved(&self) -> u32 {
u32::from_be_bytes([0, self._reserved[0], self._reserved[1], self._reserved[2]])
}

/// Gets the [FirmwareHeader] file code for the firmware data.
pub const fn file_code(&self) -> u8 {
self.file_code
Expand Down Expand Up @@ -179,6 +189,31 @@ impl<const N: usize> TryFrom<[u8; N]> for FirmwareHeader {
}
}

impl fmt::Display for FirmwareHeader {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let magic = self.magic_str();
let reserved = self.reserved();
let file_code = self.file_code();
let ram_len = self.ram_len();

write!(f, "{{")?;
write!(f, r#""magic": "{magic}", "#)?;
write!(f, r#""reserved": {reserved}, "#)?;
write!(f, r#""file_code": {file_code}, "#)?;
write!(f, r#""ram_len": {ram_len}, "#)?;
write!(f, r#""data": ["#)?;

for (i, d) in self.data().iter().enumerate() {
if i != 0 {
write!(f, ", ")?;
}
write!(f, "{d}")?;
}

write!(f, "]}}")
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
2 changes: 2 additions & 0 deletions src/types/firmware/ram.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ pub const FIRMWARE_RAM_SECTION_LEN: usize = 128;
/// An XOR-checksum is calculated over each byte in the [FirmwareRam] block. At the end of
/// transmission, the device will return its XOR-checksum byte, which should be checked against
/// the local checksum. If there is a mismatch, abort the firmware update, and retry.
#[repr(C)]
#[derive(Clone, Debug, PartialEq)]
pub struct FirmwareRam {
block: Vec<u8>,
index: usize,
Expand Down

0 comments on commit bfa9595

Please sign in to comment.