use core::convert::TryInto;
use core::slice;
use core::{cmp::Ordering, fmt::Debug};
use crate::HardwareRevision;
#[cfg(not(feature = "firmware"))]
include!(concat!(env!("OUT_DIR"), "/memory_map.rs"));
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct FirmwareVersion {
pub semver_major: u8,
pub semver_minor: u8,
pub semver_patch: u8,
}
#[cfg(feature = "defmt")]
impl defmt::Format for FirmwareVersion {
fn format(&self, fmt: defmt::Formatter) {
defmt::write!(
fmt,
"{=u8}.{=u8}.{=u8}",
self.semver_major,
self.semver_minor,
self.semver_patch
)
}
}
impl PartialOrd for FirmwareVersion {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
match self.semver_major.cmp(&other.semver_major) {
Ordering::Equal => {}
ord => return Some(ord),
}
match self.semver_minor.cmp(&other.semver_minor) {
Ordering::Equal => {}
ord => return Some(ord),
}
Some(self.semver_patch.cmp(&other.semver_patch))
}
}
#[derive(Debug, Clone)]
pub struct FirmwareHeader {
pub crc: u32,
pub firmware_size: u32,
pub version: FirmwareVersion,
pub hw_version: HardwareRevision,
pub git_sha: heapless::String<20>,
}
impl FirmwareVersion {
#[must_use]
pub const fn new(major: u8, minor: u8, patch: u8) -> Self {
Self {
semver_major: major,
semver_minor: minor,
semver_patch: patch,
}
}
#[must_use]
pub const fn as_bytes(self) -> [u8; 3] {
[
self.semver_patch as u8,
self.semver_minor as u8,
self.semver_major as u8,
]
}
}
impl FirmwareHeader {
#[must_use]
pub fn new_from_slice(s: &[u8]) -> Self {
let sha_len = s[15] as usize;
let git_sha = if sha_len < 20 {
heapless::String::from(core::str::from_utf8(&s[16..16 + sha_len]).unwrap_or(""))
} else {
heapless::String::new()
};
Self {
crc: u32::from_le_bytes(s[0..4].try_into().unwrap()),
firmware_size: u32::from_le_bytes(s[4..8].try_into().unwrap()),
hw_version: HardwareRevision::from(u32::from_le_bytes(s[8..12].try_into().unwrap())),
version: FirmwareVersion::new(s[14], s[13], s[12]),
git_sha,
}
}
#[must_use]
pub fn as_bytes(&self) -> [u8; 32] {
let mut ret = [0_u8; 32];
let sha = self.git_sha.as_bytes();
ret[0..4].copy_from_slice(&self.crc.to_le_bytes());
ret[4..8].copy_from_slice(&self.firmware_size.to_le_bytes());
ret[8..12].copy_from_slice(&(self.hw_version as u32).to_le_bytes());
ret[12..15].copy_from_slice(&self.version.as_bytes());
ret[15] = sha.len() as u8;
ret[16..16 + sha.len()].copy_from_slice(&sha);
ret
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
#[repr(C)]
pub enum ImageFlag {
New = 0xFF,
CommitPending = 0xFE,
Valid = 0xFC,
Invalid = 0xF8,
}
impl From<u8> for ImageFlag {
fn from(v: u8) -> Self {
match v {
0xFF => Self::New,
0xFE => Self::CommitPending,
0xFC => Self::Valid,
_ => Self::Invalid,
}
}
}
pub struct ImageDescriptor {
pub magic: [u8; 7],
pub image_flags: ImageFlag,
pub sequence_nr: u32,
pub signature_size: usize,
pub signature: [u8; 256],
}
impl Debug for ImageDescriptor {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
if self.magic_valid() {
write!(
f,
"ImageDescriptor {{ image_flags: {:?}, sequence_nr: {}, signature_size: {} }}",
self.image_flags, self.sequence_nr, self.signature_size
)
} else {
write!(f, "ImageDescriptor {{ Invalid }}",)
}
}
}
const MAGIC: [u8; 7] = *b"@FB-DUO";
impl ImageDescriptor {
pub fn new(sequence_nr: u32, signature_size: usize, signature: [u8; 256]) -> Self {
Self {
magic: MAGIC,
image_flags: ImageFlag::New,
sequence_nr,
signature_size,
signature,
}
}
pub fn new_from_slice(s: &[u8]) -> Self {
Self {
magic: s[0..7].try_into().unwrap(),
image_flags: s[7].into(),
sequence_nr: u32::from_le_bytes(s[8..12].try_into().unwrap()),
signature_size: u32::from_le_bytes(s[12..16].try_into().unwrap()) as usize,
signature: s[16..272].try_into().unwrap(),
}
}
pub fn magic_valid(&self) -> bool {
self.magic == MAGIC
}
pub fn as_bytes(&self) -> [u8; 512] {
let mut ret = [0u8; 512];
ret[0..7].copy_from_slice(&self.magic);
ret[7] = self.image_flags as u8;
ret[8..12].copy_from_slice(&self.sequence_nr.to_le_bytes());
ret[12..16].copy_from_slice(&(self.signature_size as u32).to_le_bytes());
ret[16..272].copy_from_slice(&self.signature);
ret
}
}
#[derive(Debug, Copy, Clone)]
pub enum FlashRegion {
Firmware,
UpdateFirmware,
Swap,
}
impl FlashRegion {
pub fn as_slice(&self) -> &'static [u8] {
unsafe {
slice::from_raw_parts(
self.start_addr() as *const u32 as *const u8,
self.len() as *const u32 as usize,
)
}
}
pub fn header(&self) -> FirmwareHeader {
FirmwareHeader::new_from_slice(unsafe {
slice::from_raw_parts((self.start_addr() + 0x200) as *const u8, header_len())
})
}
pub fn image_descriptor(&self) -> ImageDescriptor {
ImageDescriptor::new_from_slice(unsafe {
slice::from_raw_parts(self.start_addr() as *const u8, image_descriptor_len())
})
}
pub fn header_start_offset(&self) -> u32 {
self.start_offset() + image_descriptor_len() as u32
}
pub fn start_offset(&self) -> u32 {
self.start_addr() - 0x0800_0000
}
pub fn start_addr(&self) -> u32 {
#[cfg(feature = "firmware")]
{
extern "C" {
static __firmware_start__: u32;
static __fwupdate_start__: u32;
static __fwswap_start__: u32;
}
return match self {
Self::Firmware => unsafe { &__firmware_start__ as *const u32 as u32 },
Self::UpdateFirmware => unsafe { &__fwupdate_start__ as *const u32 as u32 },
Self::Swap => unsafe { &__fwswap_start__ as *const u32 as u32 },
};
}
#[cfg(not(feature = "firmware"))]
match self {
Self::Firmware => __firmware_start__,
Self::UpdateFirmware => __fwupdate_start__,
Self::Swap => __fwswap_start__,
}
}
pub fn end_offset(&self) -> u32 {
self.end_addr() - 0x08000000
}
pub fn end_addr(&self) -> u32 {
#[cfg(feature = "firmware")]
{
extern "C" {
static __firmware_end__: u32;
static __fwupdate_end__: u32;
static __fwswap_end__: u32;
}
return match self {
Self::Firmware => unsafe { &__firmware_end__ as *const u32 as u32 },
Self::UpdateFirmware => unsafe { &__fwupdate_end__ as *const u32 as u32 },
Self::Swap => unsafe { &__fwswap_end__ as *const u32 as u32 },
};
}
#[cfg(not(feature = "firmware"))]
match self {
Self::Firmware => __firmware_end__,
Self::UpdateFirmware => __fwupdate_end__,
Self::Swap => __fwswap_end__,
}
}
pub fn len(&self) -> usize {
(self.end_addr() - self.start_addr()) as usize
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
#[inline(always)]
pub fn full_header_len() -> usize {
#[cfg(feature = "firmware")]
{
extern "C" {
static __header_start__: u32;
static __header_end__: u32;
}
return unsafe {
&__header_end__ as *const u32 as usize - &__header_start__ as *const u32 as usize
};
}
#[cfg(not(feature = "firmware"))]
return (__header_end__ - __header_start__) as usize;
}
pub fn header_len() -> usize {
full_header_len() - image_descriptor_len()
}
pub fn image_descriptor_len() -> usize {
0x200
}