use core::mem;
use core::ptr;
extern crate alloc;
use alloc::borrow::Cow;
use alloc::vec;
use ::log::*;
use embedded_svc::io;
use embedded_svc::ota;
use esp_idf_hal::mutex;
use esp_idf_sys::*;
use crate::private::{common::*, cstr::*};
static TAKEN: mutex::Mutex<bool> = mutex::Mutex::new(false);
impl From<Newtype<&esp_app_desc_t>> for ota::FirmwareInfo {
fn from(app_desc: Newtype<&esp_app_desc_t>) -> Self {
let app_desc = app_desc.0;
Self {
version: from_cstr_ptr(&app_desc.version as *const _).into_owned(),
signature: Some(app_desc.app_elf_sha256.into()),
released: from_cstr_ptr(&app_desc.date as *const _).into_owned()
+ from_cstr_ptr(&app_desc.time as *const _).as_ref(),
description: from_cstr_ptr(&app_desc.project_name as *const _).into_owned(),
download_id: None,
}
}
}
pub struct EspFirmwareInfoLoader(vec::Vec<u8>);
impl EspFirmwareInfoLoader {
pub fn new() -> Self {
Self(vec::Vec::new())
}
}
impl Default for EspFirmwareInfoLoader {
fn default() -> Self {
Self::new()
}
}
impl ota::FirmwareInfoLoader for EspFirmwareInfoLoader {
type Error = EspError;
fn load(&mut self, buf: &[u8]) -> Result<ota::LoadResult, Self::Error> {
if !self.is_loaded() {
self.0.extend_from_slice(buf);
}
Ok(if self.is_loaded() {
ota::LoadResult::Loaded
} else {
ota::LoadResult::LoadMore
})
}
fn is_loaded(&self) -> bool {
self.0.len()
>= mem::size_of::<esp_image_header_t>()
+ mem::size_of::<esp_image_segment_header_t>()
+ mem::size_of::<esp_app_desc_t>()
}
fn get_info(&self) -> Result<ota::FirmwareInfo, Self::Error> {
if self.is_loaded() {
let app_desc_slice = &self.0[0..mem::size_of::<esp_image_header_t>()
+ mem::size_of::<esp_image_segment_header_t>()];
let app_desc = unsafe {
(app_desc_slice.as_ptr() as *const esp_app_desc_t)
.as_ref()
.unwrap()
};
Ok(ota::FirmwareInfo::from(Newtype(app_desc)))
} else {
Err(EspError::from(ESP_ERR_INVALID_SIZE as _).unwrap())
}
}
}
pub struct EspSlot(esp_partition_t);
impl ota::OtaSlot for EspSlot {
type Error = EspError;
fn get_label(&self) -> Result<Cow<'_, str>, Self::Error> {
Ok(from_cstr_ptr(&self.0.label as *const _ as *const _))
}
fn get_state(&self) -> Result<ota::SlotState, Self::Error> {
let mut state: esp_ota_img_states_t = Default::default();
let err = unsafe { esp_ota_get_state_partition(&self.0 as *const _, &mut state as *mut _) };
Ok(if err == ESP_ERR_NOT_FOUND as i32 {
ota::SlotState::Unknown
} else {
esp!(err)?;
#[allow(non_upper_case_globals)]
match state {
esp_ota_img_states_t_ESP_OTA_IMG_NEW
| esp_ota_img_states_t_ESP_OTA_IMG_PENDING_VERIFY => ota::SlotState::Unverified,
esp_ota_img_states_t_ESP_OTA_IMG_VALID => ota::SlotState::Valid,
esp_ota_img_states_t_ESP_OTA_IMG_INVALID
| esp_ota_img_states_t_ESP_OTA_IMG_ABORTED => ota::SlotState::Invalid,
esp_ota_img_states_t_ESP_OTA_IMG_UNDEFINED => ota::SlotState::Unknown,
_ => ota::SlotState::Unknown,
}
})
}
fn get_firmware_info(&self) -> Result<Option<ota::FirmwareInfo>, Self::Error> {
let mut app_desc: esp_app_desc_t = Default::default();
let err = unsafe { esp_ota_get_partition_description(&self.0 as *const _, &mut app_desc) };
Ok(if err == ESP_ERR_NOT_FOUND as i32 {
None
} else {
esp!(err)?;
Some(ota::FirmwareInfo::from(Newtype(&app_desc)))
})
}
}
pub struct Read;
pub struct Update {
partition: *const esp_partition_t,
handle: esp_ota_handle_t,
}
#[derive(Debug)]
pub struct EspOta<MODE>(MODE);
impl EspOta<Read> {
pub fn new() -> Result<Self, EspError> {
let mut taken = TAKEN.lock();
if *taken {
esp!(ESP_ERR_INVALID_STATE as i32)?;
}
*taken = true;
Ok(Self(Read))
}
fn get_factory_partition(&self) -> Result<*const esp_partition_t, EspError> {
let partition_iterator = unsafe {
esp_partition_find(
esp_partition_type_t_ESP_PARTITION_TYPE_APP,
esp_partition_subtype_t_ESP_PARTITION_SUBTYPE_APP_FACTORY,
b"factory\0" as *const _ as *const _,
)
};
if partition_iterator.is_null() {
esp!(ESP_ERR_NOT_SUPPORTED)?;
}
let partition = unsafe { esp_partition_get(partition_iterator) };
unsafe { esp_partition_iterator_release(partition_iterator) };
Ok(partition)
}
}
impl<MODE> Drop for EspOta<MODE> {
fn drop(&mut self) {
*TAKEN.lock() = false;
info!("Dropped");
}
}
impl ota::Ota for EspOta<Read> {
type Slot<'a> = EspSlot;
type Update<'a> = EspOta<Update>;
type Error = EspError;
fn get_boot_slot(&self) -> Result<Self::Slot<'_>, Self::Error> {
Ok(EspSlot(unsafe {
*esp_ota_get_boot_partition().as_ref().unwrap()
}))
}
fn get_running_slot(&self) -> Result<Self::Slot<'_>, Self::Error> {
Ok(EspSlot(unsafe {
*esp_ota_get_boot_partition().as_ref().unwrap()
}))
}
fn get_update_slot(&self) -> Result<Self::Slot<'_>, Self::Error> {
Ok(EspSlot(unsafe {
*esp_ota_get_next_update_partition(ptr::null())
.as_ref()
.unwrap()
}))
}
fn is_factory_reset_supported(&self) -> Result<bool, Self::Error> {
self.get_factory_partition()
.map(|factory| !factory.is_null())
}
fn factory_reset(&mut self) -> Result<(), Self::Error> {
let factory = self.get_factory_partition()?;
esp!(unsafe { esp_ota_set_boot_partition(factory) })?;
Ok(())
}
fn initiate_update(&mut self) -> Result<Self::Update<'_>, Self::Error> {
let partition = unsafe { esp_ota_get_next_update_partition(ptr::null()) };
let mut handle: esp_ota_handle_t = Default::default();
esp!(unsafe { esp_ota_begin(partition, OTA_SIZE_UNKNOWN, &mut handle as *mut _) })?;
Ok(EspOta(Update { partition, handle }))
}
fn mark_running_slot_valid(&mut self) -> Result<(), Self::Error> {
esp!(unsafe { esp_ota_mark_app_valid_cancel_rollback() })
}
fn mark_running_slot_invalid_and_reboot(&mut self) -> Self::Error {
if let Err(err) = esp!(unsafe { esp_ota_mark_app_invalid_rollback_and_reboot() }) {
err
} else {
unreachable!()
}
}
}
impl ota::OtaUpdate for EspOta<Update> {
fn complete(self) -> Result<(), Self::Error> {
esp!(unsafe { esp_ota_end(self.0.handle) })?;
esp!(unsafe { esp_ota_set_boot_partition(self.0.partition) })?;
Ok(())
}
fn abort(self) -> Result<(), Self::Error> {
esp!(unsafe { esp_ota_abort(self.0.handle) })?;
Ok(())
}
}
impl io::Write for EspOta<Update> {
type Error = EspError;
fn do_write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
esp!(unsafe { esp_ota_write(self.0.handle, buf.as_ptr() as _, buf.len() as _) })?;
Ok(buf.len())
}
}