1use embedded_storage::{ReadStorage, Storage};
28
29use crate::partitions::FlashRegion;
30
31const SLOT0_DATA_OFFSET: u32 = 0x0000;
37const SLOT1_DATA_OFFSET: u32 = 0x1000;
38
39#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, strum::FromRepr)]
41#[cfg_attr(feature = "defmt", derive(defmt::Format))]
42pub enum Slot {
43 None,
45 Slot0,
47 Slot1,
49}
50
51impl Slot {
52 pub fn number(&self) -> usize {
54 match self {
55 Slot::None => 0,
56 Slot::Slot0 => 0,
57 Slot::Slot1 => 1,
58 }
59 }
60
61 pub fn next(&self) -> Slot {
63 match self {
64 Slot::None => Slot::Slot0,
65 Slot::Slot0 => Slot::Slot1,
66 Slot::Slot1 => Slot::Slot0,
67 }
68 }
69
70 fn offset(&self) -> u32 {
71 match self {
72 Slot::None => SLOT0_DATA_OFFSET,
73 Slot::Slot0 => SLOT0_DATA_OFFSET,
74 Slot::Slot1 => SLOT1_DATA_OFFSET,
75 }
76 }
77}
78
79#[derive(Debug, PartialEq, Eq, Clone, Copy, Default, Hash, strum::FromRepr)]
81#[cfg_attr(feature = "defmt", derive(defmt::Format))]
82#[repr(u32)]
83pub enum OtaImageState {
84 New = 0x0,
89
90 PendingVerify = 0x1,
94
95 Valid = 0x2,
98
99 Invalid = 0x3,
104
105 Aborted = 0x4,
109
110 #[default]
113 Undefined = 0xFFFFFFFF,
114}
115
116impl TryFrom<u32> for OtaImageState {
117 type Error = crate::partitions::Error;
118
119 fn try_from(value: u32) -> Result<Self, Self::Error> {
120 OtaImageState::from_repr(value).ok_or(crate::partitions::Error::Invalid)
121 }
122}
123
124#[derive(Debug, Clone, Copy, Default)]
127#[cfg_attr(feature = "defmt", derive(defmt::Format))]
128#[repr(C)]
129struct OtaSelectEntry {
130 pub ota_seq: u32,
132 pub seq_label: [u8; 20],
134 pub ota_state: OtaImageState,
136 pub crc: u32,
138}
139
140impl OtaSelectEntry {
141 fn as_bytes_mut(&mut self) -> &mut [u8; 0x20] {
142 debug_assert!(core::mem::size_of::<Self>() == 32);
143 unwrap!(
144 unsafe { core::slice::from_raw_parts_mut(self as *mut _ as *mut u8, 0x20) }.try_into()
145 )
146 }
147}
148
149#[derive(Debug)]
154#[cfg_attr(feature = "defmt", derive(defmt::Format))]
155pub struct Ota<'a, F>
156where
157 F: embedded_storage::Storage,
158{
159 flash: &'a mut FlashRegion<'a, F>,
160}
161
162impl<'a, F> Ota<'a, F>
163where
164 F: embedded_storage::Storage,
165{
166 pub fn new(flash: &'a mut FlashRegion<'a, F>) -> Result<Ota<'a, F>, crate::partitions::Error> {
172 if flash.capacity() != 0x2000
173 || flash.raw.partition_type()
174 != crate::partitions::PartitionType::Data(
175 crate::partitions::DataPartitionSubType::Ota,
176 )
177 {
178 return Err(crate::partitions::Error::InvalidPartition {
179 expected_size: 0x2000,
180 expected_type: crate::partitions::PartitionType::Data(
181 crate::partitions::DataPartitionSubType::Ota,
182 ),
183 });
184 }
185
186 Ok(Ota { flash })
187 }
188
189 pub fn current_slot(&mut self) -> Result<Slot, crate::partitions::Error> {
191 let (seq0, seq1) = self.get_slot_seq()?;
192
193 let slot = if seq0 == 0xffffffff && seq1 == 0xffffffff {
194 Slot::None
195 } else if seq0 == 0xffffffff {
196 Slot::Slot1
197 } else if seq1 == 0xffffffff || seq0 > seq1 {
198 Slot::Slot0
199 } else {
200 Slot::Slot1
201 };
202
203 Ok(slot)
204 }
205
206 fn get_slot_seq(&mut self) -> Result<(u32, u32), crate::partitions::Error> {
207 let mut buffer1 = OtaSelectEntry::default();
208 let mut buffer2 = OtaSelectEntry::default();
209 self.flash.read(SLOT0_DATA_OFFSET, buffer1.as_bytes_mut())?;
210 self.flash.read(SLOT1_DATA_OFFSET, buffer2.as_bytes_mut())?;
211 let seq0 = buffer1.ota_seq;
212 let seq1 = buffer2.ota_seq;
213 Ok((seq0, seq1))
214 }
215
216 pub fn set_current_slot(&mut self, slot: Slot) -> Result<(), crate::partitions::Error> {
220 if slot == Slot::None {
221 self.flash.write(SLOT0_DATA_OFFSET, &[0xffu8; 0x20])?;
222 self.flash.write(SLOT1_DATA_OFFSET, &[0xffu8; 0x20])?;
223 return Ok(());
224 }
225
226 let (seq0, seq1) = self.get_slot_seq()?;
227
228 let new_seq = {
229 if seq0 == 0xffffffff && seq1 == 0xffffffff {
230 1
231 } else if seq0 == 0xffffffff {
232 seq1 + 1
233 } else if seq1 == 0xffffffff {
234 seq0 + 1
235 } else {
236 u32::max(seq0, seq1) + 1
237 }
238 };
239
240 let crc = crate::crypto::Crc32::new();
241 let checksum = crc.crc(&new_seq.to_le_bytes());
242
243 let mut buffer = OtaSelectEntry::default();
244 self.flash.read(slot.offset(), buffer.as_bytes_mut())?;
245 buffer.ota_seq = new_seq;
246 buffer.crc = checksum;
247 self.flash.write(slot.offset(), buffer.as_bytes_mut())?;
248
249 Ok(())
250 }
251
252 pub fn set_current_ota_state(
258 &mut self,
259 state: OtaImageState,
260 ) -> Result<(), crate::partitions::Error> {
261 match self.current_slot()? {
262 Slot::None => Err(crate::partitions::Error::InvalidState),
263 _ => {
264 let offset = self.current_slot()?.offset();
265 let mut buffer = OtaSelectEntry::default();
266 self.flash.read(offset, buffer.as_bytes_mut())?;
267 buffer.ota_state = state;
268 self.flash.write(offset, buffer.as_bytes_mut())?;
269 Ok(())
270 }
271 }
272 }
273
274 pub fn current_ota_state(&mut self) -> Result<OtaImageState, crate::partitions::Error> {
280 match self.current_slot()? {
281 Slot::None => Err(crate::partitions::Error::InvalidState),
282 _ => {
283 let offset = self.current_slot()?.offset();
284 let mut buffer = OtaSelectEntry::default();
285 self.flash.read(offset, buffer.as_bytes_mut())?;
286 Ok(buffer.ota_state)
287 }
288 }
289 }
290}
291
292#[cfg(test)]
293mod tests {
294 use super::*;
295 use crate::partitions::PartitionEntry;
296
297 struct MockFlash {
298 data: [u8; 0x2000],
299 }
300
301 impl embedded_storage::Storage for MockFlash {
302 fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
303 self.data[offset as usize..][..bytes.len()].copy_from_slice(bytes);
304 Ok(())
305 }
306 }
307
308 impl embedded_storage::ReadStorage for MockFlash {
309 type Error = crate::partitions::Error;
310 fn read(&mut self, offset: u32, buffer: &mut [u8]) -> Result<(), Self::Error> {
311 let l = buffer.len();
312 buffer[..l].copy_from_slice(&self.data[offset as usize..][..l]);
313 Ok(())
314 }
315
316 fn capacity(&self) -> usize {
317 unimplemented!()
318 }
319 }
320
321 const PARTITION_RAW: [u8; 32] = [
322 0xaa, 0x50, 1, 0, 0, 0, 0, 0, 0, 0x20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ];
330
331 const SLOT_INITIAL: &[u8] = &[
332 255u8, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
333 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
334 ];
335
336 const SLOT_COUNT_1_UNDEFINED: &[u8] = &[
337 1u8, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
338 255, 255, 255, 255, 255, 255, 255, 255, 255, 154, 152, 67, 71,
339 ];
340
341 const SLOT_COUNT_1_VALID: &[u8] = &[
342 1u8, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
343 255, 255, 255, 255, 255, 2, 0, 0, 0, 154, 152, 67, 71,
344 ];
345
346 const SLOT_COUNT_2_NEW: &[u8] = &[
347 2, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
348 255, 255, 255, 255, 0, 0, 0, 0, 116, 55, 246, 85,
349 ];
350
351 const SLOT_COUNT_3_PENDING: &[u8] = &[
352 3, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
353 255, 255, 255, 255, 1, 0, 0, 0, 17, 80, 74, 237,
354 ];
355
356 #[test]
357 fn test_initial_state_and_next_slot() {
358 let mut binary = PARTITION_RAW;
359
360 let mock_entry = PartitionEntry {
361 binary: &mut binary,
362 };
363
364 let mut mock_flash = MockFlash {
365 data: [0xff; 0x2000],
366 };
367
368 let mut mock_region = FlashRegion {
369 raw: &mock_entry,
370 flash: &mut mock_flash,
371 };
372
373 let mut sut = Ota::new(&mut mock_region).unwrap();
374 assert_eq!(sut.current_slot().unwrap(), Slot::None);
375 assert_eq!(
376 sut.current_ota_state(),
377 Err(crate::partitions::Error::InvalidState)
378 );
379 assert_eq!(
380 sut.set_current_ota_state(OtaImageState::New),
381 Err(crate::partitions::Error::InvalidState)
382 );
383 assert_eq!(
384 sut.current_ota_state(),
385 Err(crate::partitions::Error::InvalidState)
386 );
387
388 sut.set_current_slot(Slot::Slot0).unwrap();
389 assert_eq!(sut.current_slot().unwrap(), Slot::Slot0);
390 assert_eq!(sut.current_ota_state(), Ok(OtaImageState::Undefined));
391
392 assert_eq!(SLOT_COUNT_1_UNDEFINED, &mock_flash.data[0x0000..][..0x20],);
393 assert_eq!(SLOT_INITIAL, &mock_flash.data[0x1000..][..0x20],);
394 }
395
396 #[test]
397 fn test_slot0_valid_next_slot() {
398 let mut binary = PARTITION_RAW;
399
400 let mock_entry = PartitionEntry {
401 binary: &mut binary,
402 };
403
404 let mut mock_flash = MockFlash {
405 data: [0xff; 0x2000],
406 };
407
408 mock_flash.data[0x0000..][..0x20].copy_from_slice(SLOT_COUNT_1_VALID);
409 mock_flash.data[0x1000..][..0x20].copy_from_slice(SLOT_INITIAL);
410
411 let mut mock_region = FlashRegion {
412 raw: &mock_entry,
413 flash: &mut mock_flash,
414 };
415
416 let mut sut = Ota::new(&mut mock_region).unwrap();
417 assert_eq!(sut.current_slot().unwrap(), Slot::Slot0);
418 assert_eq!(sut.current_ota_state(), Ok(OtaImageState::Valid));
419
420 sut.set_current_slot(Slot::Slot1).unwrap();
421 sut.set_current_ota_state(OtaImageState::New).unwrap();
422 assert_eq!(sut.current_slot().unwrap(), Slot::Slot1);
423 assert_eq!(sut.current_ota_state(), Ok(OtaImageState::New));
424
425 assert_eq!(SLOT_COUNT_1_VALID, &mock_flash.data[0x0000..][..0x20],);
426 assert_eq!(SLOT_COUNT_2_NEW, &mock_flash.data[0x1000..][..0x20],);
427 }
428
429 #[test]
430 fn test_slot1_new_next_slot() {
431 let mut binary = PARTITION_RAW;
432
433 let mock_entry = PartitionEntry {
434 binary: &mut binary,
435 };
436
437 let mut mock_flash = MockFlash {
438 data: [0xff; 0x2000],
439 };
440
441 mock_flash.data[0x0000..][..0x20].copy_from_slice(SLOT_COUNT_1_VALID);
442 mock_flash.data[0x1000..][..0x20].copy_from_slice(SLOT_COUNT_2_NEW);
443
444 let mut mock_region = FlashRegion {
445 raw: &mock_entry,
446 flash: &mut mock_flash,
447 };
448
449 let mut sut = Ota::new(&mut mock_region).unwrap();
450 assert_eq!(sut.current_slot().unwrap(), Slot::Slot1);
451 assert_eq!(sut.current_ota_state(), Ok(OtaImageState::New));
452
453 sut.set_current_slot(Slot::Slot0).unwrap();
454 sut.set_current_ota_state(OtaImageState::PendingVerify)
455 .unwrap();
456 assert_eq!(sut.current_slot().unwrap(), Slot::Slot0);
457 assert_eq!(sut.current_ota_state(), Ok(OtaImageState::PendingVerify));
458
459 assert_eq!(SLOT_COUNT_3_PENDING, &mock_flash.data[0x0000..][..0x20],);
460 assert_eq!(SLOT_COUNT_2_NEW, &mock_flash.data[0x1000..][..0x20],);
461 }
462
463 #[test]
464 fn test_ota_slot_next() {
465 assert_eq!(Slot::None.next(), Slot::Slot0);
466 assert_eq!(Slot::Slot0.next(), Slot::Slot1);
467 assert_eq!(Slot::Slot1.next(), Slot::Slot0);
468 }
469}