use core::array;
use core::cell::RefCell;
use defmt::info;
use embassy_rp::Peri;
use embassy_rp::flash::{Blocking, ERASE_SIZE, Flash as EmbassyFlash};
use embassy_rp::peripherals::FLASH;
use embassy_sync::blocking_mutex::Mutex;
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
use portable_atomic::{AtomicU32, Ordering};
use serde::{Deserialize, Serialize};
use static_cell::StaticCell;
use crate::{Error, Result};
use device_envoy_core::flash_block::{
self as core_flash, FlashBlock as CoreFlashBlock, FlashBlockError, FlashDevice,
};
pub use device_envoy_core::flash_block::FlashBlock;
#[cfg(feature = "pico2")]
const INTERNAL_FLASH_SIZE: usize = 4 * 1024 * 1024;
#[cfg(all(not(feature = "pico2"), feature = "pico1"))]
const INTERNAL_FLASH_SIZE: usize = 2 * 1024 * 1024;
#[cfg(all(not(feature = "pico2"), not(feature = "pico1")))]
pub const INTERNAL_FLASH_SIZE: usize = 2 * 1024 * 1024;
const TOTAL_BLOCKS: u32 = (INTERNAL_FLASH_SIZE / ERASE_SIZE) as u32;
struct RpFlashAdapter<'a>(&'a mut EmbassyFlash<'static, FLASH, Blocking, INTERNAL_FLASH_SIZE>);
impl FlashDevice for RpFlashAdapter<'_> {
type Error = embassy_rp::flash::Error;
fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), embassy_rp::flash::Error> {
self.0.blocking_read(offset, bytes)
}
fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), embassy_rp::flash::Error> {
self.0.blocking_write(offset, bytes)
}
fn erase(&mut self, from: u32, to: u32) -> Result<(), embassy_rp::flash::Error> {
self.0.blocking_erase(from, to)
}
}
fn flash_block_error_to_crate_error(e: FlashBlockError<embassy_rp::flash::Error>) -> Error {
match e {
FlashBlockError::Io(err) => Error::Flash(err),
FlashBlockError::FormatError => Error::FormatError,
FlashBlockError::StorageCorrupted => Error::StorageCorrupted,
}
}
struct FlashManager {
flash: Mutex<
CriticalSectionRawMutex,
RefCell<EmbassyFlash<'static, FLASH, Blocking, INTERNAL_FLASH_SIZE>>,
>,
next_block: AtomicU32,
}
impl FlashManager {
fn new(peripheral: Peri<'static, FLASH>) -> Self {
Self {
flash: Mutex::new(core::cell::RefCell::new(EmbassyFlash::new_blocking(
peripheral,
))),
next_block: AtomicU32::new(0),
}
}
fn with_flash<R>(
&self,
f: impl FnOnce(&mut EmbassyFlash<'static, FLASH, Blocking, INTERNAL_FLASH_SIZE>) -> Result<R>,
) -> Result<R> {
self.flash.lock(|flash| {
let mut flash_ref = flash.borrow_mut();
f(&mut *flash_ref)
})
}
fn reserve<const N: usize>(&'static self) -> Result<[FlashBlockRp; N]> {
let start = self.next_block.fetch_add(N as u32, Ordering::SeqCst);
let end = start.checked_add(N as u32).ok_or(Error::IndexOutOfBounds)?;
if end > TOTAL_BLOCKS {
self.next_block.fetch_sub(N as u32, Ordering::SeqCst);
return Err(Error::IndexOutOfBounds);
}
Ok(array::from_fn(|idx| FlashBlockRp {
manager: self,
block: start + idx as u32,
}))
}
}
#[derive(Clone, Copy)]
pub struct FlashBlockRp {
manager: &'static FlashManager,
block: u32,
}
impl CoreFlashBlock for FlashBlockRp {
type Error = Error;
fn load<T>(&mut self) -> Result<Option<T>>
where
T: Serialize + for<'de> Deserialize<'de>,
{
let offset = block_offset(self.block);
let result = self.manager.with_flash(|flash| {
let mut adapter = RpFlashAdapter(flash);
core_flash::load_block::<{ ERASE_SIZE }, T, _>(&mut adapter, offset)
.map_err(flash_block_error_to_crate_error)
})?;
if result.is_some() {
info!("Flash: Loaded data from block {}", self.block);
} else {
info!("Flash: No data at block {}", self.block);
}
Ok(result)
}
fn save<T>(&mut self, value: &T) -> Result<()>
where
T: Serialize + for<'de> Deserialize<'de>,
{
let offset = block_offset(self.block);
self.manager.with_flash(|flash| {
let mut adapter = RpFlashAdapter(flash);
core_flash::save_block::<{ ERASE_SIZE }, _, _>(&mut adapter, offset, value)
.map_err(flash_block_error_to_crate_error)
})?;
info!("Flash: Saved to block {}", self.block);
Ok(())
}
fn clear(&mut self) -> Result<()> {
let offset = block_offset(self.block);
self.manager.with_flash(|flash| {
let mut adapter = RpFlashAdapter(flash);
core_flash::clear_block::<{ ERASE_SIZE }, _>(&mut adapter, offset)
.map_err(flash_block_error_to_crate_error)
})?;
info!("Flash: Cleared block {}", self.block);
Ok(())
}
}
impl FlashBlockRp {
pub fn new_array<const N: usize>(
peripheral: Peri<'static, FLASH>,
) -> Result<[FlashBlockRp; N]> {
static FLASH_BLOCK_RP_STATIC: FlashBlockRpStatic = FlashBlockRpStatic::new();
let manager = FLASH_BLOCK_RP_STATIC.manager(peripheral);
manager.reserve::<N>()
}
}
pub(crate) struct FlashBlockRpStatic {
manager_cell: StaticCell<FlashManager>,
manager_ref: Mutex<CriticalSectionRawMutex, core::cell::RefCell<Option<&'static FlashManager>>>,
}
impl FlashBlockRpStatic {
#[must_use]
const fn new() -> Self {
Self {
manager_cell: StaticCell::new(),
manager_ref: Mutex::new(core::cell::RefCell::new(None)),
}
}
fn manager(&'static self, peripheral: Peri<'static, FLASH>) -> &'static FlashManager {
self.manager_ref.lock(|slot_cell| {
let mut slot = slot_cell.borrow_mut();
if slot.is_none() {
let manager_mut = self.manager_cell.init(FlashManager::new(peripheral));
let manager_ref: &'static FlashManager = manager_mut;
*slot = Some(manager_ref);
}
slot.expect("manager initialized")
})
}
}
fn block_offset(block_id: u32) -> u32 {
let capacity = INTERNAL_FLASH_SIZE as u32;
capacity - (block_id + 1) * ERASE_SIZE as u32
}