1use core::sync::atomic::{AtomicBool, Ordering};
6
7use crate::object_dict::{
8 ConstByteRefField, ConstField, ObjectAccess, ProvidesSubObjects, SubObjectAccess,
9};
10use zencan_common::{
11 constants::values::BOOTLOADER_ERASE_CMD,
12 objects::{ObjectCode, SubInfo},
13 sdo::AbortCode,
14 AtomicCell,
15};
16
17#[derive(Debug, Default)]
19pub struct BootloaderInfo<const APP: bool, const NUM_SECTIONS: u8> {
20 reset_flag: ResetField,
21}
22
23impl<const APP: bool, const NUM_SECTIONS: u8> BootloaderInfo<APP, NUM_SECTIONS> {
24 pub const fn new() -> Self {
26 Self {
27 reset_flag: ResetField::new(),
28 }
29 }
30
31 pub fn reset_flag(&self) -> bool {
36 self.reset_flag.load()
37 }
38}
39
40#[derive(Debug, Default)]
41struct ResetField {
42 flag: AtomicBool,
43}
44
45impl ResetField {
46 pub const fn new() -> Self {
47 Self {
48 flag: AtomicBool::new(false),
49 }
50 }
51
52 pub fn load(&self) -> bool {
53 self.flag.load(Ordering::Relaxed)
54 }
55}
56
57impl SubObjectAccess for ResetField {
58 fn read(&self, _offset: usize, _buf: &mut [u8]) -> Result<usize, AbortCode> {
59 Err(AbortCode::WriteOnly)
60 }
61
62 fn read_size(&self) -> usize {
63 0
64 }
65
66 fn write(&self, data: &[u8]) -> Result<(), AbortCode> {
67 if data.len() == 4 {
68 if data == [0x42, 0x4F, 0x4F, 0x54] {
69 self.flag.store(true, Ordering::Relaxed);
70 Ok(())
71 } else {
72 Err(AbortCode::InvalidValue)
73 }
74 } else if data.len() < 4 {
75 Err(AbortCode::DataTypeMismatchLengthLow)
76 } else {
77 Err(AbortCode::DataTypeMismatchLengthHigh)
78 }
79 }
80}
81
82const fn get_config_value(app: bool) -> u32 {
87 let mut config = 1;
88 if app {
89 config |= 2;
90 }
91 config
92}
93
94impl<const APP: bool, const NUM_SECTIONS: u8> ProvidesSubObjects
95 for BootloaderInfo<APP, NUM_SECTIONS>
96{
97 fn get_sub_object(&self, sub: u8) -> Option<(SubInfo, &dyn SubObjectAccess)> {
98 match sub {
99 0 => Some((
100 SubInfo::MAX_SUB_NUMBER,
101 const { &ConstField::new(3u8.to_le_bytes()) },
102 )),
103 1 => Some((SubInfo::new_u32().ro_access(), {
104 const { &ConstField::new(get_config_value(APP).to_le_bytes()) }
105 })),
106 2 => Some((SubInfo::new_u8().ro_access(), {
107 const { &ConstField::new(NUM_SECTIONS.to_le_bytes()) }
108 })),
109 3 => Some((SubInfo::new_u32().wo_access(), &self.reset_flag)),
110 _ => None,
111 }
112 }
113
114 fn object_code(&self) -> ObjectCode {
115 ObjectCode::Record
116 }
117}
118
119pub trait BootloaderSectionCallbacks: Sync {
121 fn erase(&self) -> bool;
125
126 fn write(&self, data: &[u8]);
131
132 fn finalize(&self) -> bool;
139}
140
141#[allow(missing_debug_implementations)]
143pub struct BootloaderSection {
144 name: &'static str,
145 size: u32,
146 callbacks: AtomicCell<Option<&'static dyn BootloaderSectionCallbacks>>,
147}
148
149impl BootloaderSection {
150 pub const fn new(name: &'static str, size: u32) -> Self {
152 Self {
153 name,
154 size,
155 callbacks: AtomicCell::new(None),
156 }
157 }
158
159 pub fn register_callbacks(&self, callbacks: &'static dyn BootloaderSectionCallbacks) {
161 self.callbacks.store(Some(callbacks));
162 }
163}
164
165impl ObjectAccess for BootloaderSection {
166 fn read(&self, sub: u8, offset: usize, buf: &mut [u8]) -> Result<usize, AbortCode> {
167 match sub {
168 0 => ConstField::new(4u8.to_le_bytes()).read(offset, buf),
169 1 => ConstField::new(1u8.to_le_bytes()).read(offset, buf),
170 2 => ConstByteRefField::new(self.name.as_bytes()).read(offset, buf),
171 3 => Err(AbortCode::WriteOnly),
172 4 => Err(AbortCode::WriteOnly),
173 _ => Err(AbortCode::NoSuchSubIndex),
174 }
175 }
176
177 fn read_size(&self, sub: u8) -> Result<usize, AbortCode> {
178 match sub {
179 0 => Ok(1),
180 1 => Ok(1),
181 2 => Ok(self.name.len()),
182 3 => Ok(0),
183 4 => Ok(0),
184 _ => Err(AbortCode::NoSuchSubIndex),
185 }
186 }
187
188 fn write(&self, sub: u8, data: &[u8]) -> Result<(), AbortCode> {
189 match sub {
190 0 => Err(AbortCode::ReadOnly),
191 1 => Err(AbortCode::ReadOnly),
192 2 => Err(AbortCode::ReadOnly),
193 3 => {
194 if data == BOOTLOADER_ERASE_CMD.to_le_bytes() {
195 if let Some(cb) = self.callbacks.load() {
196 if cb.erase() {
197 Ok(())
198 } else {
199 Err(AbortCode::GeneralError)
200 }
201 } else {
202 Err(AbortCode::ResourceNotAvailable)
203 }
204 } else {
205 Err(AbortCode::InvalidValue)
206 }
207 }
208 4 => {
209 if let Some(callbacks) = self.callbacks.load() {
210 callbacks.write(data);
211 if callbacks.finalize() {
212 Ok(())
214 } else {
215 Err(AbortCode::GeneralError)
216 }
217 } else {
218 Err(AbortCode::ResourceNotAvailable)
219 }
220 }
221 _ => Err(AbortCode::NoSuchSubIndex),
222 }
223 }
224
225 fn object_code(&self) -> ObjectCode {
226 ObjectCode::Record
227 }
228
229 fn sub_info(&self, sub: u8) -> Result<SubInfo, AbortCode> {
230 match sub {
231 0 => Ok(SubInfo::MAX_SUB_NUMBER),
232 1 => Ok(SubInfo::new_u8().ro_access()),
233 2 => Ok(SubInfo::new_visibile_str(self.name.len()).ro_access()),
234 3 => Ok(SubInfo::new_u32().wo_access()),
235 4 => Ok(SubInfo {
236 size: self.size as usize,
237 data_type: zencan_common::objects::DataType::Domain,
238 access_type: zencan_common::objects::AccessType::Rw,
239 pdo_mapping: zencan_common::objects::PdoMappable::None,
240 persist: false,
241 }),
242 _ => Err(AbortCode::NoSuchSubIndex),
243 }
244 }
245}