use core::borrow::BorrowMut;
use alloc::boxed::Box;
use config::{FatFsType, FormatConfiguration};
use ::log::warn;
use crate::hal::sd::SdCardDriver;
use crate::sys::*;
extern crate alloc;
pub mod config {
#[derive(Copy, Clone, Eq, PartialEq)]
pub enum FatFsType {
Auto,
Fat,
Fat32,
ExFat,
}
pub struct FormatConfiguration {
pub fs_type: FatFsType,
#[cfg(not(esp_idf_version_major = "4"))]
pub fat_backup_copy: bool,
#[cfg(not(esp_idf_version_major = "4"))]
pub volume_data_alignment: core::num::NonZeroU16,
#[cfg(not(esp_idf_version_major = "4"))]
pub root_dir_entries: core::num::NonZeroU16,
pub cluster_size: u32,
}
impl FormatConfiguration {
pub const fn new() -> Self {
Self {
fs_type: FatFsType::Auto,
#[cfg(not(esp_idf_version_major = "4"))]
fat_backup_copy: false,
#[cfg(not(esp_idf_version_major = "4"))]
volume_data_alignment: unsafe { core::num::NonZeroU16::new_unchecked(1) },
#[cfg(not(esp_idf_version_major = "4"))]
root_dir_entries: unsafe { core::num::NonZeroU16::new_unchecked(512) },
cluster_size: 4096,
}
}
}
impl Default for FormatConfiguration {
fn default() -> Self {
Self::new()
}
}
}
enum Partition<T> {
SdCard(T),
RawPartition,
}
pub struct MountedFatfs<'a, T> {
fs: &'a mut Fatfs<T>,
fatfs: Box<FATFS>,
}
impl<T> MountedFatfs<'_, T> {
pub fn fatfs(&self) -> &FATFS {
&self.fatfs
}
}
impl<T> Drop for MountedFatfs<'_, T> {
fn drop(&mut self) {
let drive_path = self.fs.drive_path();
let res = unsafe { f_mount(core::ptr::null_mut(), drive_path.as_ptr(), 0) };
if res != FRESULT_FR_OK {
panic!("Unmount failed: {res}");
}
}
}
pub struct Fatfs<T> {
drive: u8,
_partition: Partition<T>,
}
impl<T> Fatfs<T> {
pub fn new_sdcard<H>(drive: u8, mut sd_card_driver: T) -> Result<Self, EspError>
where
T: BorrowMut<SdCardDriver<H>>,
{
unsafe {
ff_diskio_register_sdmmc(
drive,
sd_card_driver.borrow_mut().card() as *const _ as *mut _,
);
}
Ok(Self {
drive,
_partition: Partition::SdCard(sd_card_driver),
})
}
pub fn drive(&self) -> u8 {
self.drive
}
pub fn format(
&mut self,
configuration: &FormatConfiguration,
buf: &mut [u8],
) -> Result<(), EspError> {
let drive_path = self.drive_path();
#[cfg(not(esp_idf_version_major = "4"))]
{
let opt = MKFS_PARM {
fmt: match configuration.fs_type {
FatFsType::Auto => FM_ANY,
FatFsType::Fat => FM_FAT,
FatFsType::Fat32 => FM_FAT32,
FatFsType::ExFat => FM_EXFAT,
} as _,
au_size: configuration.cluster_size,
n_fat: if configuration.fat_backup_copy { 2 } else { 1 },
n_root: configuration.root_dir_entries.get() as _,
align: configuration.volume_data_alignment.get() as _,
};
unsafe {
f_mkfs(
drive_path.as_ptr(),
&opt,
buf.as_mut_ptr() as *mut _,
buf.len() as _,
);
}
}
#[cfg(esp_idf_version_major = "4")]
{
unsafe {
f_mkfs(
drive_path.as_ptr(),
match configuration.fs_type {
FatFsType::Auto => FM_ANY,
FatFsType::Fat => FM_FAT,
FatFsType::Fat32 => FM_FAT32,
FatFsType::ExFat => FM_EXFAT,
} as _,
configuration.cluster_size,
buf.as_mut_ptr() as *mut _,
buf.len() as _,
);
}
}
Ok(())
}
pub fn mount(&mut self) -> Result<MountedFatfs<'_, T>, EspError> {
let mut fatfs: Box<FATFS> = Box::default();
let drive_path = self.drive_path();
let res = unsafe { f_mount(&mut *fatfs, drive_path.as_ptr(), 0) };
if res != FRESULT_FR_OK {
warn!("Mount failed: {res}");
Err(EspError::from_infallible::<ESP_FAIL>())?
}
Ok(MountedFatfs { fs: self, fatfs })
}
pub(crate) fn drive_path_from(drive: u8) -> [core::ffi::c_char; 2] {
[drive as _, 0]
}
pub(crate) fn drive_path(&self) -> [core::ffi::c_char; 2] {
Self::drive_path_from(self.drive)
}
}
impl Fatfs<()> {
pub unsafe fn new_raw_part(
drive: u8,
partition: *const esp_partition_t,
) -> Result<Self, EspError> {
unsafe {
ff_diskio_register_raw_partition(drive, partition);
}
Ok(Self {
drive,
_partition: Partition::RawPartition,
})
}
pub unsafe fn new_wl_part(drive: u8, partition: wl_handle_t) -> Result<Self, EspError> {
unsafe {
ff_diskio_register_wl_partition(drive, partition);
}
Ok(Self {
drive,
_partition: Partition::RawPartition,
})
}
}
impl<T> Drop for Fatfs<T> {
fn drop(&mut self) {
unsafe {
ff_diskio_register(self.drive, core::ptr::null_mut());
}
}
}