1use core::cmp::min;
92use core::fmt::Write;
93use core::marker::PhantomData;
94use core::mem;
95use core::ptr;
96
97use ::log::*;
98use embedded_svc::ota::OtaUpdateFinished;
99
100use embedded_svc::io;
101use embedded_svc::ota::{FirmwareInfoLoader, Ota, OtaUpdate};
102
103pub use embedded_svc::ota::{FirmwareInfo, LoadResult, Slot, SlotState, UpdateProgress};
104
105use crate::sys::*;
106
107use crate::io::EspIOError;
108use crate::private::{cstr::*, mutex};
109
110static TAKEN: mutex::Mutex<bool> = mutex::Mutex::new(false);
111
112#[deprecated(note = "Use `EspFirmwareInfoLoad` instead")]
113pub struct EspFirmwareInfoLoader(heapless::Vec<u8, 512>);
114
115#[allow(deprecated)]
116impl EspFirmwareInfoLoader {
117 pub const fn new() -> Self {
118 Self(heapless::Vec::new())
119 }
120
121 pub fn load(&mut self, buf: &[u8]) -> Result<LoadResult, EspError> {
122 if !self.is_loaded() {
123 let remaining = self.0.capacity() - self.0.len();
124 if remaining > 0 {
125 self.0
126 .extend_from_slice(&buf[..min(buf.len(), remaining)])
127 .unwrap();
128 }
129 }
130
131 Ok(if self.is_loaded() {
132 LoadResult::Loaded
133 } else {
134 LoadResult::LoadMore
135 })
136 }
137
138 pub fn is_loaded(&self) -> bool {
139 self.0.len()
140 >= mem::size_of::<esp_image_header_t>()
141 + mem::size_of::<esp_image_segment_header_t>()
142 + mem::size_of::<esp_app_desc_t>()
143 }
144
145 pub fn get_info(&self) -> Result<FirmwareInfo, EspError> {
146 if self.is_loaded() {
147 let app_desc_slice = &self.0[mem::size_of::<esp_image_header_t>()
148 + mem::size_of::<esp_image_segment_header_t>()
149 ..mem::size_of::<esp_image_header_t>()
150 + mem::size_of::<esp_image_segment_header_t>()
151 + mem::size_of::<esp_app_desc_t>()];
152
153 let app_desc = unsafe {
154 (app_desc_slice.as_ptr() as *const esp_app_desc_t)
155 .as_ref()
156 .unwrap()
157 };
158
159 let mut info = FirmwareInfo {
160 version: heapless::String::new(),
161 released: heapless::String::new(),
162 description: None,
163 signature: None,
164 download_id: None,
165 };
166
167 EspFirmwareInfoLoad::load_firmware_info(&mut info, app_desc)?;
168
169 Ok(info)
170 } else {
171 Err(EspError::from_infallible::<ESP_ERR_INVALID_SIZE>())
172 }
173 }
174}
175
176#[allow(deprecated)]
177impl Default for EspFirmwareInfoLoader {
178 fn default() -> Self {
179 Self::new()
180 }
181}
182
183#[allow(deprecated)]
184impl io::ErrorType for EspFirmwareInfoLoader {
185 type Error = EspIOError;
186}
187
188#[allow(deprecated)]
189impl FirmwareInfoLoader for EspFirmwareInfoLoader {
190 fn load(&mut self, buf: &[u8]) -> Result<LoadResult, Self::Error> {
191 Ok(EspFirmwareInfoLoader::load(self, buf)?)
192 }
193
194 fn is_loaded(&self) -> bool {
195 EspFirmwareInfoLoader::is_loaded(self)
196 }
197
198 fn get_info(&self) -> Result<FirmwareInfo, Self::Error> {
199 Ok(EspFirmwareInfoLoader::get_info(self)?)
200 }
201}
202
203#[derive(Debug, Clone)]
205pub struct EspNativeFirmwareInfo<'a> {
206 pub image_header: &'a esp_image_header_t,
208 pub segment_header: &'a esp_image_segment_header_t,
210 pub app_desc: &'a esp_app_desc_t,
212}
213
214pub struct EspFirmwareInfoLoad;
219
220impl EspFirmwareInfoLoad {
221 pub fn fetch_native<'a>(&self, data: &'a [u8]) -> Option<EspNativeFirmwareInfo<'a>> {
226 let loaded = data.len()
227 >= mem::size_of::<esp_image_header_t>()
228 + mem::size_of::<esp_image_segment_header_t>()
229 + mem::size_of::<esp_app_desc_t>();
230
231 if loaded {
232 let image_header_slice = &data[..mem::size_of::<esp_image_header_t>()];
233 let image_segment_header_slice = &data[mem::size_of::<esp_image_header_t>()
234 ..mem::size_of::<esp_image_header_t>()
235 + mem::size_of::<esp_image_segment_header_t>()];
236 let app_desc_slice = &data[mem::size_of::<esp_image_header_t>()
237 + mem::size_of::<esp_image_segment_header_t>()
238 ..mem::size_of::<esp_image_header_t>()
239 + mem::size_of::<esp_image_segment_header_t>()
240 + mem::size_of::<esp_app_desc_t>()];
241
242 let image_header = unsafe {
243 (image_header_slice.as_ptr() as *const esp_image_header_t)
244 .as_ref()
245 .unwrap()
246 };
247
248 let segment_header = unsafe {
249 (image_segment_header_slice.as_ptr() as *const esp_image_segment_header_t)
250 .as_ref()
251 .unwrap()
252 };
253
254 let app_desc = unsafe {
255 (app_desc_slice.as_ptr() as *const esp_app_desc_t)
256 .as_ref()
257 .unwrap()
258 };
259
260 Some(EspNativeFirmwareInfo {
261 image_header,
262 segment_header,
263 app_desc,
264 })
265 } else {
266 None
267 }
268 }
269
270 pub fn fetch(&self, data: &[u8], info: &mut FirmwareInfo) -> Result<bool, EspIOError> {
275 if let Some(native_info) = self.fetch_native(data) {
276 Self::load_firmware_info(info, native_info.app_desc)?;
277
278 Ok(true)
279 } else {
280 Ok(false)
281 }
282 }
283
284 pub fn load_firmware_info(
285 info: &mut FirmwareInfo,
286 app_desc: &esp_app_desc_t,
287 ) -> Result<(), EspError> {
288 info.version.clear();
289 info.version
290 .push_str(unsafe { from_cstr_ptr(&app_desc.version as *const _) })
291 .map_err(|_| EspError::from_infallible::<ESP_ERR_INVALID_SIZE>())?;
292
293 info.released.clear();
294 write!(
295 &mut info.released,
296 "{} {}",
297 unsafe { from_cstr_ptr(&app_desc.date as *const _) },
298 unsafe { from_cstr_ptr(&app_desc.time as *const _) }
299 )
300 .map_err(|_| EspError::from_infallible::<ESP_ERR_INVALID_SIZE>())?;
301
302 if let Some(description) = info.description.as_mut() {
303 description.clear();
304 description
305 .push_str(unsafe { from_cstr_ptr(&app_desc.project_name as *const _) })
306 .map_err(|_| EspError::from_infallible::<ESP_ERR_INVALID_SIZE>())?;
307 }
308
309 if let Some(signature) = info.signature.as_mut() {
310 signature.clear();
311 signature
312 .extend_from_slice(&app_desc.app_elf_sha256)
313 .map_err(|_| EspError::from_infallible::<ESP_ERR_INVALID_SIZE>())?;
314 }
315
316 if let Some(download_id) = info.download_id.as_mut() {
317 download_id.clear();
318 }
319
320 Ok(())
321 }
322}
323
324impl io::ErrorType for EspFirmwareInfoLoad {
325 type Error = EspIOError;
326}
327
328#[derive(Debug)]
329pub struct EspOtaUpdate<'a> {
330 update_partition: *const esp_partition_t,
331 update_handle: esp_ota_handle_t,
332 _data: PhantomData<&'a mut ()>,
333}
334
335impl<'a> EspOtaUpdate<'a> {
336 pub fn write(&mut self, buf: &[u8]) -> Result<(), EspError> {
344 self.check_write()?;
345
346 if !buf.is_empty() {
347 esp!(unsafe { esp_ota_write(self.update_handle, buf.as_ptr() as _, buf.len() as _) })?;
348 }
349
350 Ok(())
351 }
352
353 pub fn flush(&mut self) -> Result<(), EspError> {
360 self.check_write()?;
361
362 Ok(())
363 }
364
365 pub fn finish(self) -> Result<EspOtaUpdateFinished<'a>, EspError> {
374 self.check_write()?;
375
376 esp!(unsafe { esp_ota_end(self.update_handle) })?;
377 let update_partition = self.update_partition;
378
379 mem::forget(self);
382
383 Ok(EspOtaUpdateFinished {
384 update_partition,
385 _data: PhantomData,
386 })
387 }
388
389 pub fn complete(self) -> Result<(), EspError> {
391 self.check_write()?;
392
393 esp!(unsafe { esp_ota_end(self.update_handle) })?;
394 esp!(unsafe { esp_ota_set_boot_partition(self.update_partition) })?;
395
396 mem::forget(self);
399
400 Ok(())
401 }
402
403 pub fn abort(self) -> Result<(), EspError> {
405 Ok(())
407 }
408
409 fn check_write(&self) -> Result<(), EspError> {
410 if !self.update_partition.is_null() {
411 Ok(())
412 } else {
413 Err(EspError::from_infallible::<ESP_FAIL>())
414 }
415 }
416}
417
418impl Drop for EspOtaUpdate<'_> {
419 fn drop(&mut self) {
420 unsafe { esp_ota_abort(self.update_handle) };
429 }
430}
431
432#[derive(Debug)]
433pub struct EspOtaUpdateFinished<'a> {
434 update_partition: *const esp_partition_t,
435 _data: PhantomData<&'a mut ()>,
436}
437
438impl EspOtaUpdateFinished<'_> {
439 pub fn activate(self) -> Result<(), EspError> {
442 esp!(unsafe { esp_ota_set_boot_partition(self.update_partition) })
443 }
444}
445
446#[derive(Debug)]
447pub struct EspOta(());
448
449impl EspOta {
450 pub fn new() -> Result<Self, EspError> {
456 let mut taken = TAKEN.lock();
457
458 if *taken {
459 return Err(EspError::from_infallible::<ESP_ERR_INVALID_STATE>());
460 }
461
462 *taken = true;
463
464 Ok(Self(()))
465 }
466
467 pub fn get_boot_slot(&self) -> Result<Slot, EspError> {
473 if let Some(partition) = unsafe { esp_ota_get_boot_partition().as_ref() } {
474 self.get_slot(partition)
475 } else {
476 Err(EspError::from_infallible::<ESP_ERR_NOT_FOUND>())
477 }
478 }
479
480 pub fn get_running_slot(&self) -> Result<Slot, EspError> {
486 if let Some(partition) = unsafe { esp_ota_get_running_partition().as_ref() } {
487 self.get_slot(partition)
488 } else {
489 Err(EspError::from_infallible::<ESP_ERR_NOT_FOUND>())
490 }
491 }
492
493 pub fn get_update_slot(&self) -> Result<Slot, EspError> {
499 if let Some(partition) = unsafe { esp_ota_get_next_update_partition(ptr::null()).as_ref() }
500 {
501 self.get_slot(partition)
502 } else {
503 Err(EspError::from_infallible::<ESP_ERR_NOT_FOUND>())
504 }
505 }
506
507 pub fn get_last_invalid_slot(&self) -> Result<Option<Slot>, EspError> {
509 if let Some(partition) = unsafe { esp_ota_get_last_invalid_partition().as_ref() } {
510 Ok(Some(self.get_slot(partition)?))
511 } else {
512 Ok(None)
513 }
514 }
515
516 pub fn is_factory_reset_supported(&self) -> Result<bool, EspError> {
518 self.get_factory_partition()
519 .map(|factory| !factory.is_null())
520 }
521
522 pub fn factory_reset(&mut self) -> Result<(), EspError> {
528 let factory = self.get_factory_partition()?;
529
530 esp!(unsafe { esp_ota_set_boot_partition(factory) })?;
531
532 Ok(())
533 }
534
535 pub fn initiate_update(&mut self) -> Result<EspOtaUpdate<'_>, EspError> {
542 self.initiate_update_with_known_size(OTA_SIZE_UNKNOWN as usize)
543 }
544
545 pub fn initiate_update_with_known_size(
551 &mut self,
552 file_size: usize,
553 ) -> Result<EspOtaUpdate<'_>, EspError> {
554 let partition = unsafe { esp_ota_get_next_update_partition(ptr::null()) };
558
559 let mut handle: esp_ota_handle_t = Default::default();
560
561 esp!(unsafe { esp_ota_begin(partition, file_size, &mut handle) })?;
562
563 Ok(EspOtaUpdate {
564 update_partition: partition,
565 update_handle: handle,
566 _data: PhantomData,
567 })
568 }
569
570 pub fn mark_running_slot_valid(&mut self) -> Result<(), EspError> {
575 Ok(esp!(unsafe { esp_ota_mark_app_valid_cancel_rollback() })?)
576 }
577
578 pub fn mark_running_slot_invalid_and_reboot(&mut self) -> EspError {
587 if let Err(err) = esp!(unsafe { esp_ota_mark_app_invalid_rollback_and_reboot() }) {
588 err
589 } else {
590 unreachable!()
591 }
592 }
593
594 fn get_factory_partition(&self) -> Result<*const esp_partition_t, EspError> {
595 let partition_iterator = unsafe {
596 esp_partition_find(
597 esp_partition_type_t_ESP_PARTITION_TYPE_APP,
598 esp_partition_subtype_t_ESP_PARTITION_SUBTYPE_APP_FACTORY,
599 b"factory\0" as *const _ as *const _,
600 )
601 };
602
603 if partition_iterator.is_null() {
604 return Err(EspError::from_infallible::<ESP_ERR_NOT_SUPPORTED>());
605 }
606
607 let partition = unsafe { esp_partition_get(partition_iterator) };
608
609 unsafe { esp_partition_iterator_release(partition_iterator) };
610
611 Ok(partition)
612 }
613
614 fn get_slot(&self, partition: &esp_partition_t) -> Result<Slot, EspError> {
615 Ok(Slot {
616 label: unsafe { from_cstr_ptr(&partition.label as *const _ as *const _) }
617 .try_into()
618 .unwrap(),
619 state: self.get_state(partition)?,
620 firmware: self.get_firmware_info(partition)?,
621 })
622 }
623
624 fn get_state(&self, partition: &esp_partition_t) -> Result<SlotState, EspError> {
625 let mut state: esp_ota_img_states_t = Default::default();
626
627 let err =
628 unsafe { esp_ota_get_state_partition(partition as *const _, &mut state as *mut _) };
629
630 Ok(if err == ESP_ERR_NOT_FOUND {
631 SlotState::Unknown
632 } else if err == ESP_ERR_NOT_SUPPORTED {
633 SlotState::Factory
634 } else {
635 esp!(err)?;
636
637 #[allow(non_upper_case_globals)]
638 match state {
639 esp_ota_img_states_t_ESP_OTA_IMG_NEW
640 | esp_ota_img_states_t_ESP_OTA_IMG_PENDING_VERIFY => SlotState::Unverified,
641 esp_ota_img_states_t_ESP_OTA_IMG_VALID => SlotState::Valid,
642 esp_ota_img_states_t_ESP_OTA_IMG_INVALID
643 | esp_ota_img_states_t_ESP_OTA_IMG_ABORTED => SlotState::Invalid,
644 esp_ota_img_states_t_ESP_OTA_IMG_UNDEFINED => SlotState::Unknown,
645 _ => SlotState::Unknown,
646 }
647 })
648 }
649
650 fn get_firmware_info(
651 &self,
652 partition: &esp_partition_t,
653 ) -> Result<Option<FirmwareInfo>, EspError> {
654 let mut app_desc: esp_app_desc_t = Default::default();
655
656 let err =
657 unsafe { esp_ota_get_partition_description(partition as *const _, &mut app_desc) };
658
659 Ok(if err == ESP_ERR_NOT_FOUND {
660 None
661 } else {
662 esp!(err)?;
663
664 let mut info = FirmwareInfo {
665 version: heapless::String::new(),
666 released: heapless::String::new(),
667 description: Some(heapless::String::new()),
668 signature: Some(heapless::Vec::new()),
669 download_id: None,
670 };
671
672 EspFirmwareInfoLoad::load_firmware_info(&mut info, &app_desc)?;
673
674 Some(info)
675 })
676 }
677}
678
679impl Drop for EspOta {
680 fn drop(&mut self) {
681 *TAKEN.lock() = false;
682
683 info!("Dropped");
684 }
685}
686
687impl io::ErrorType for EspOta {
688 type Error = EspIOError;
689}
690
691impl Ota for EspOta {
692 type Update<'a>
693 = EspOtaUpdate<'a>
694 where
695 Self: 'a;
696
697 fn get_boot_slot(&self) -> Result<Slot, Self::Error> {
698 EspOta::get_boot_slot(self).map_err(EspIOError)
699 }
700
701 fn get_running_slot(&self) -> Result<Slot, Self::Error> {
702 EspOta::get_running_slot(self).map_err(EspIOError)
703 }
704
705 fn get_update_slot(&self) -> Result<Slot, Self::Error> {
706 EspOta::get_update_slot(self).map_err(EspIOError)
707 }
708
709 fn is_factory_reset_supported(&self) -> Result<bool, Self::Error> {
710 EspOta::is_factory_reset_supported(self).map_err(EspIOError)
711 }
712
713 fn factory_reset(&mut self) -> Result<(), Self::Error> {
714 EspOta::factory_reset(self).map_err(EspIOError)
715 }
716
717 fn initiate_update(&mut self) -> Result<Self::Update<'_>, Self::Error> {
718 EspOta::initiate_update(self).map_err(EspIOError)
719 }
720
721 fn mark_running_slot_valid(&mut self) -> Result<(), Self::Error> {
722 EspOta::mark_running_slot_valid(self).map_err(EspIOError)
723 }
724
725 fn mark_running_slot_invalid_and_reboot(&mut self) -> Self::Error {
726 EspIOError(EspOta::mark_running_slot_invalid_and_reboot(self))
727 }
728}
729
730unsafe impl Send for EspOtaUpdate<'_> {}
731
732impl io::ErrorType for EspOtaUpdate<'_> {
733 type Error = EspIOError;
734}
735
736impl<'a> OtaUpdate for EspOtaUpdate<'a> {
737 type OtaUpdateFinished = EspOtaUpdateFinished<'a>;
738
739 fn finish(self) -> Result<Self::OtaUpdateFinished, Self::Error> {
740 let finish = EspOtaUpdate::finish(self)?;
741
742 Ok(finish)
743 }
744
745 fn complete(self) -> Result<(), Self::Error> {
746 EspOtaUpdate::complete(self)?;
747
748 Ok(())
749 }
750
751 fn abort(self) -> Result<(), Self::Error> {
752 EspOtaUpdate::abort(self)?;
753
754 Ok(())
755 }
756}
757
758impl io::Write for EspOtaUpdate<'_> {
759 fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
760 EspOtaUpdate::write(self, buf)?;
761
762 Ok(buf.len())
763 }
764
765 fn flush(&mut self) -> Result<(), Self::Error> {
766 EspOtaUpdate::flush(self)?;
767
768 Ok(())
769 }
770}
771
772unsafe impl Send for EspOtaUpdateFinished<'_> {}
773
774impl io::ErrorType for EspOtaUpdateFinished<'_> {
775 type Error = EspIOError;
776}
777
778impl OtaUpdateFinished for EspOtaUpdateFinished<'_> {
779 fn activate(self) -> Result<(), Self::Error> {
780 EspOtaUpdateFinished::activate(self)?;
781
782 Ok(())
783 }
784}