use core::sync::atomic::{AtomicBool, Ordering};
use crate::object_dict::{
ConstByteRefField, ConstField, ObjectAccess, ProvidesSubObjects, SubObjectAccess,
};
use zencan_common::{
constants::values::BOOTLOADER_ERASE_CMD,
objects::{ObjectCode, SubInfo},
sdo::AbortCode,
AtomicCell,
};
#[derive(Debug, Default)]
pub struct BootloaderInfo<const APP: bool, const NUM_SECTIONS: u8> {
reset_flag: ResetField,
}
impl<const APP: bool, const NUM_SECTIONS: u8> BootloaderInfo<APP, NUM_SECTIONS> {
pub const fn new() -> Self {
Self {
reset_flag: ResetField::new(),
}
}
pub fn reset_flag(&self) -> bool {
self.reset_flag.load()
}
}
#[derive(Debug, Default)]
struct ResetField {
flag: AtomicBool,
}
impl ResetField {
pub const fn new() -> Self {
Self {
flag: AtomicBool::new(false),
}
}
pub fn load(&self) -> bool {
self.flag.load(Ordering::Relaxed)
}
}
impl SubObjectAccess for ResetField {
fn read(&self, _offset: usize, _buf: &mut [u8]) -> Result<usize, AbortCode> {
Err(AbortCode::WriteOnly)
}
fn read_size(&self) -> usize {
0
}
fn write(&self, data: &[u8]) -> Result<(), AbortCode> {
if data.len() == 4 {
if data == [0x42, 0x4F, 0x4F, 0x54] {
self.flag.store(true, Ordering::Relaxed);
Ok(())
} else {
Err(AbortCode::InvalidValue)
}
} else if data.len() < 4 {
Err(AbortCode::DataTypeMismatchLengthLow)
} else {
Err(AbortCode::DataTypeMismatchLengthHigh)
}
}
}
const fn get_config_value(app: bool) -> u32 {
let mut config = 1;
if app {
config |= 2;
}
config
}
impl<const APP: bool, const NUM_SECTIONS: u8> ProvidesSubObjects
for BootloaderInfo<APP, NUM_SECTIONS>
{
fn get_sub_object(&self, sub: u8) -> Option<(SubInfo, &dyn SubObjectAccess)> {
match sub {
0 => Some((
SubInfo::MAX_SUB_NUMBER,
const { &ConstField::new(3u8.to_le_bytes()) },
)),
1 => Some((SubInfo::new_u32().ro_access(), {
const { &ConstField::new(get_config_value(APP).to_le_bytes()) }
})),
2 => Some((SubInfo::new_u8().ro_access(), {
const { &ConstField::new(NUM_SECTIONS.to_le_bytes()) }
})),
3 => Some((SubInfo::new_u32().wo_access(), &self.reset_flag)),
_ => None,
}
}
fn object_code(&self) -> ObjectCode {
ObjectCode::Record
}
}
pub trait BootloaderSectionCallbacks: Sync {
fn erase(&self) -> bool;
fn write(&self, data: &[u8]);
fn finalize(&self) -> bool;
}
#[allow(missing_debug_implementations)]
pub struct BootloaderSection {
name: &'static str,
size: u32,
callbacks: AtomicCell<Option<&'static dyn BootloaderSectionCallbacks>>,
}
impl BootloaderSection {
pub const fn new(name: &'static str, size: u32) -> Self {
Self {
name,
size,
callbacks: AtomicCell::new(None),
}
}
pub fn register_callbacks(&self, callbacks: &'static dyn BootloaderSectionCallbacks) {
self.callbacks.store(Some(callbacks));
}
}
impl ObjectAccess for BootloaderSection {
fn read(&self, sub: u8, offset: usize, buf: &mut [u8]) -> Result<usize, AbortCode> {
match sub {
0 => ConstField::new(4u8.to_le_bytes()).read(offset, buf),
1 => ConstField::new(1u8.to_le_bytes()).read(offset, buf),
2 => ConstByteRefField::new(self.name.as_bytes()).read(offset, buf),
3 => Err(AbortCode::WriteOnly),
4 => Err(AbortCode::WriteOnly),
_ => Err(AbortCode::NoSuchSubIndex),
}
}
fn read_size(&self, sub: u8) -> Result<usize, AbortCode> {
match sub {
0 => Ok(1),
1 => Ok(1),
2 => Ok(self.name.len()),
3 => Ok(0),
4 => Ok(0),
_ => Err(AbortCode::NoSuchSubIndex),
}
}
fn write(&self, sub: u8, data: &[u8]) -> Result<(), AbortCode> {
match sub {
0 => Err(AbortCode::ReadOnly),
1 => Err(AbortCode::ReadOnly),
2 => Err(AbortCode::ReadOnly),
3 => {
if data == BOOTLOADER_ERASE_CMD.to_le_bytes() {
if let Some(cb) = self.callbacks.load() {
if cb.erase() {
Ok(())
} else {
Err(AbortCode::GeneralError)
}
} else {
Err(AbortCode::ResourceNotAvailable)
}
} else {
Err(AbortCode::InvalidValue)
}
}
4 => {
if let Some(callbacks) = self.callbacks.load() {
callbacks.write(data);
if callbacks.finalize() {
Ok(())
} else {
Err(AbortCode::GeneralError)
}
} else {
Err(AbortCode::ResourceNotAvailable)
}
}
_ => Err(AbortCode::NoSuchSubIndex),
}
}
fn object_code(&self) -> ObjectCode {
ObjectCode::Record
}
fn sub_info(&self, sub: u8) -> Result<SubInfo, AbortCode> {
match sub {
0 => Ok(SubInfo::MAX_SUB_NUMBER),
1 => Ok(SubInfo::new_u8().ro_access()),
2 => Ok(SubInfo::new_visibile_str(self.name.len()).ro_access()),
3 => Ok(SubInfo::new_u32().wo_access()),
4 => Ok(SubInfo {
size: self.size as usize,
data_type: zencan_common::objects::DataType::Domain,
access_type: zencan_common::objects::AccessType::Rw,
pdo_mapping: zencan_common::objects::PdoMappable::None,
persist: false,
}),
_ => Err(AbortCode::NoSuchSubIndex),
}
}
}