Skip to content

Commit

Permalink
Support for LittleFS (#498)
Browse files Browse the repository at this point in the history
* Support for LittleFS

* Remove all Littlefs-specific configurations

* Make the info API non-mut

* Make the info API non-mut
  • Loading branch information
ivmarkov authored Oct 11, 2024
1 parent 820305e commit 87f77b6
Show file tree
Hide file tree
Showing 6 changed files with 387 additions and 20 deletions.
3 changes: 3 additions & 0 deletions .github/configs/sdkconfig.defaults
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,6 @@ CONFIG_OPENTHREAD_ENABLED=y

# Border Router again, mDNS
#CONFIG_MDNS_MULTIPLE_INSTANCE=y

# Littlefs on SD cards example
#CONFIG_LITTLEFS_SDMMC_SUPPORT=y
20 changes: 0 additions & 20 deletions components_esp32c3.lock

This file was deleted.

147 changes: 147 additions & 0 deletions examples/sd_spi_littlefs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
//! An example of mounting, formatting, and using an SD card with Littlefs
//!
//! To use, put this in your `Cargo.toml`:
//! ```
//! [[package.metadata.esp-idf-sys.extra_components]]
//! remote_component = { name = "joltwallet/littlefs", version = "1.14" }
//! ```
//!
//! To use with an SD card, put this in your `sdkconfig.defaults`:
//! ```
//! CONFIG_LITTLEFS_SDMMC_SUPPORT=y
//! ```
//!
//! NOTE: While this example is using the SD card via the SPI interface,
//! it is also possible to use the SD card via the SDMMC interface. Moreover,
//! it is possible to initialize and use Littlefs with the internal Flash
//! storage as well, by using one of the two unsafe constructors that take
//! a partition label or a raw partition pointer.

#![allow(unexpected_cfgs)]

fn main() -> anyhow::Result<()> {
esp_idf_svc::sys::link_patches();
esp_idf_svc::log::EspLogger::initialize_default();

#[cfg(not(esp32))]
{
log::error!("This example is configured for esp32, please adjust pins to your module.");
}

#[cfg(esp32)]
{
#[cfg(i_have_done_all_configs_from_the_top_comment)]
// Remove this `cfg` when you have done all of the above for the example to compile
example_main()?;

// Remove this whole code block when you have done all of the above for the example to compile
#[cfg(not(i_have_done_all_configs_from_the_top_comment))]
{
log::error!("Please follow the instructions in the source code.");
}
}

Ok(())
}

#[cfg(i_have_done_all_configs_from_the_top_comment)] // Remove this `cfg` when you have done all of the above for the example to compile
#[cfg(esp32)]
fn example_main() -> anyhow::Result<()> {
use std::fs::{read_dir, File};
use std::io::{Read, Seek, Write};

use esp_idf_svc::fs::littlefs::Littlefs;
use esp_idf_svc::hal::gpio::AnyIOPin;
use esp_idf_svc::hal::prelude::*;
use esp_idf_svc::hal::sd::{spi::SdSpiHostDriver, SdCardConfiguration, SdCardDriver};
use esp_idf_svc::hal::spi::{config::DriverConfig, Dma, SpiDriver};
use esp_idf_svc::io::vfs::MountedLittlefs;
use esp_idf_svc::log::EspLogger;

use log::info;

esp_idf_svc::sys::link_patches();

EspLogger::initialize_default();

let peripherals = Peripherals::take()?;
let pins = peripherals.pins;

let spi_driver = SpiDriver::new(
peripherals.spi3,
pins.gpio18,
pins.gpio23,
Some(pins.gpio19),
&DriverConfig::default().dma(Dma::Auto(4096)),
)?;

let sd_card_driver = SdCardDriver::new_spi(
SdSpiHostDriver::new(
spi_driver,
Some(pins.gpio5),
AnyIOPin::none(),
AnyIOPin::none(),
AnyIOPin::none(),
#[cfg(not(any(
esp_idf_version_major = "4",
all(esp_idf_version_major = "5", esp_idf_version_minor = "0"),
all(esp_idf_version_major = "5", esp_idf_version_minor = "1"),
)))] // For ESP-IDF v5.2 and later
None,
)?,
&SdCardConfiguration::new(),
)?;

let littlefs = Littlefs::new_sdcard(sd_card_driver)?;

// Format it first, as chances are, the user won't have easy access to the Littlefs filesystem from her PC
// Commented out for safety
// log::info!("Formatting the SD card");
// littlefs.format()?;
// log::info!("SD card formatted");

// Keep it around or else it will be dropped and unmounted
let mounted_littlefs = MountedLittlefs::mount(littlefs, "/sdcard")?;

info!("Filesystem usage: {:?}", mounted_littlefs.info()?);

let content = b"Hello, world!";

{
let mut file = File::create("/sdcard/test.txt")?;

info!("File {file:?} created");

file.write_all(content).expect("Write failed");

info!("File {file:?} written with {content:?}");

file.seek(std::io::SeekFrom::Start(0)).expect("Seek failed");

info!("File {file:?} seeked");
}

{
let mut file = File::open("/sdcard/test.txt")?;

info!("File {file:?} opened");

let mut file_content = String::new();

file.read_to_string(&mut file_content).expect("Read failed");

info!("File {file:?} read: {file_content}");

assert_eq!(file_content.as_bytes(), content);
}

{
let directory = read_dir("/sdcard")?;

for entry in directory {
log::info!("Entry: {:?}", entry?.file_name());
}
}

Ok(())
}
2 changes: 2 additions & 0 deletions src/fs.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
#[cfg(all(feature = "alloc", esp_idf_comp_fatfs_enabled))]
pub mod fatfs;
#[cfg(all(feature = "alloc", esp_idf_comp_joltwallet__littlefs_enabled))]
pub mod littlefs;
#[cfg(all(feature = "alloc", esp_idf_comp_spiffs_enabled))]
pub mod spiffs;
120 changes: 120 additions & 0 deletions src/fs/littlefs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
//! Littlefs filesystem.
//!
//! To use, put this in your `Cargo.toml`:
//! ```
//! [[package.metadata.esp-idf-sys.extra_components]]
//! remote_component = { name = "joltwallet/littlefs", version = "1.14" }
//! ```
//!
//! To use with an SD card, put this in your `sdkconfig.defaults`:
//! ```
//! CONFIG_LITTLEFS_SDMMC_SUPPORT=y
//! ```

use alloc::ffi::CString;

use crate::{private::cstr::to_cstring_arg, sys::*};

extern crate alloc;

#[allow(dead_code)]
enum Partition<T> {
SdCard(T),
PartitionLabel(CString),
RawPartition(*mut esp_partition_t),
}

#[derive(Clone)]
pub(crate) enum PartitionRawData {
#[cfg(esp_idf_littlefs_sdmmc_support)]
SdCard(*mut sdmmc_card_t),
PartitionLabel(*const i8),
RawPartition(*mut esp_partition_t),
}

/// Information about the filesystem.
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct LittleFsInfo {
pub total_bytes: usize,
pub used_bytes: usize,
}

/// Represents a Littlefs filesystem.
pub struct Littlefs<T> {
_partition: Partition<T>,
partition_raw_data: PartitionRawData,
}

impl<T> Littlefs<T> {
/// Create a new Littlefs filesystem instance for a given SD card driver.
///
/// # Arguments
/// - SD card driver.
#[cfg(esp_idf_littlefs_sdmmc_support)]
pub fn new_sdcard<H>(sd_card_driver: T) -> Result<Self, EspError>
where
T: core::borrow::BorrowMut<crate::hal::sd::SdCardDriver<H>>,
{
let card_raw_ptr = sd_card_driver.borrow().card() as *const _ as *mut _;

Ok(Self {
_partition: Partition::SdCard(sd_card_driver),
partition_raw_data: PartitionRawData::SdCard(card_raw_ptr),
})
}

/// Create a new Littlefs filesystem instance for a given partition label.
///
/// # Safety
/// - This method should be used with a valid partition label.
/// - While the partition is in use by the filesystem, it should not be modified or used elsewhere.
///
/// # Arguments
/// - `partition_label`: Partition label.
pub unsafe fn new_partition(partition_label: &str) -> Result<Self, EspError> {
let partition_label = to_cstring_arg(partition_label)?;
let partition_raw_data = PartitionRawData::PartitionLabel(partition_label.as_ptr());

Ok(Self {
_partition: Partition::PartitionLabel(partition_label),
partition_raw_data,
})
}

/// Create a new Littlefs filesystem instance for a given partition.
///
/// # Safety
/// - This method should be used with a valid partition.
/// - While the partition is in use by the filesystem, it should not be modified or used elsewhere.
///
/// # Arguments
/// - `partition`: the raw ESP-IDF partition.
pub unsafe fn new_raw_partition(partition: *mut esp_partition_t) -> Result<Self, EspError> {
Ok(Self {
_partition: Partition::RawPartition(partition),
partition_raw_data: PartitionRawData::RawPartition(partition),
})
}

pub(crate) fn partition_raw_data(&self) -> PartitionRawData {
self.partition_raw_data.clone()
}

/// Format the Littlefs partition.
pub fn format(&mut self) -> Result<(), EspError> {
match self.partition_raw_data {
#[cfg(esp_idf_littlefs_sdmmc_support)]
PartitionRawData::SdCard(sd_card_ptr) => {
esp!(unsafe { esp_littlefs_format_sdmmc(sd_card_ptr) })?;
}
PartitionRawData::PartitionLabel(label) => {
esp!(unsafe { esp_littlefs_format(label) })?;
}
PartitionRawData::RawPartition(partition) => {
esp!(unsafe { esp_littlefs_format_partition(partition) })?;
}
}

Ok(())
}
}
Loading

0 comments on commit 87f77b6

Please sign in to comment.