Skip to content

Commit

Permalink
add async support using bisync crate
Browse files Browse the repository at this point in the history
This moves the existing API into a `blocking` module and duplicates
the API into a new `asynchronous` module, using `async fn` where
applicable.

Fixes rust-embedded-community#50
  • Loading branch information
Be-ing committed Feb 6, 2025
1 parent e7eef2a commit b18b485
Show file tree
Hide file tree
Showing 36 changed files with 886 additions and 804 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ The format is based on [Keep a Changelog] and this project adheres to [Semantic

### Changed

- __Breaking Change__: Existing API moved into `blocking` module. Adjust your imports from `embedded_sdmmc::` to `embedded_sdmmc::blocking` to keep old code building.
- __Breaking Change__: `VolumeManager` now uses interior-mutability (with a `RefCell`) and so most methods are now `&self`. This also makes it easier to open multiple `File`, `Directory` or `Volume` objects at once.
- __Breaking Change__: The `VolumeManager`, `File`, `Directory` and `Volume` no longer implement `Send` or `Sync.
- `VolumeManager` uses an interior block cache of 512 bytes, increasing its size by about 520 bytes but hugely reducing stack space required at run-time.
Expand All @@ -17,6 +18,7 @@ The format is based on [Keep a Changelog] and this project adheres to [Semantic

### Added

- Async API in `asynchronous module`
- `File` now implements the `embedded-io` `Read`, `Write` and `Seek` traits.
- New `iterate_dir_lfn` method on `VolumeManager` and `Directory` - provides decoded Long File Names as `Option<&str>`

Expand Down
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,13 @@ version = "0.8.0"
rust-version = "1.76"

[dependencies]
bisync = "0.2.3"
byteorder = {version = "1", default-features = false}
defmt = {version = "0.3", optional = true}
embedded-hal = "1.0.0"
embedded-hal-async = "1.0.0"
embedded-io = "0.6.1"
embedded-io-async = "0.6.1"
heapless = "^0.8"
log = {version = "0.4", default-features = false, optional = true}

Expand Down
6 changes: 3 additions & 3 deletions examples/append_file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ use linux::*;

const FILE_TO_APPEND: &str = "README.TXT";

use embedded_sdmmc::{Error, Mode, VolumeIdx};
use embedded_sdmmc::blocking::{Error, Mode, VolumeIdx};

type VolumeManager = embedded_sdmmc::VolumeManager<LinuxBlockDevice, Clock, 8, 4, 4>;
type VolumeManager = embedded_sdmmc::blocking::VolumeManager<LinuxBlockDevice, Clock, 8, 4, 4>;

fn main() -> Result<(), embedded_sdmmc::Error<std::io::Error>> {
fn main() -> Result<(), Error<std::io::Error>> {
env_logger::init();
let mut args = std::env::args().skip(1);
let filename = args.next().unwrap_or_else(|| "/dev/mmcblk0".into());
Expand Down
10 changes: 5 additions & 5 deletions examples/big_dir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,19 @@ extern crate embedded_sdmmc;
mod linux;
use linux::*;

use embedded_sdmmc::Error;
use embedded_sdmmc::blocking::{Error, Mode, VolumeIdx};

type VolumeManager = embedded_sdmmc::VolumeManager<LinuxBlockDevice, Clock, 8, 4, 4>;
type VolumeManager = embedded_sdmmc::blocking::VolumeManager<LinuxBlockDevice, Clock, 8, 4, 4>;

fn main() -> Result<(), embedded_sdmmc::Error<std::io::Error>> {
fn main() -> Result<(), Error<std::io::Error>> {
env_logger::init();
let mut args = std::env::args().skip(1);
let filename = args.next().unwrap_or_else(|| "/dev/mmcblk0".into());
let print_blocks = args.find(|x| x == "-v").map(|_| true).unwrap_or(false);
let lbd = LinuxBlockDevice::new(filename, print_blocks).map_err(Error::DeviceError)?;
let volume_mgr: VolumeManager = VolumeManager::new_with_limits(lbd, Clock, 0xAA00_0000);
let volume = volume_mgr
.open_volume(embedded_sdmmc::VolumeIdx(1))
.open_volume(VolumeIdx(1))
.unwrap();
println!("Volume: {:?}", volume);
let root_dir = volume.open_root_dir().unwrap();
Expand All @@ -28,7 +28,7 @@ fn main() -> Result<(), embedded_sdmmc::Error<std::io::Error>> {
let file = root_dir
.open_file_in_dir(
file_name.as_str(),
embedded_sdmmc::Mode::ReadWriteCreateOrTruncate,
Mode::ReadWriteCreateOrTruncate,
)
.unwrap();
let buf = b"hello world, from rust";
Expand Down
6 changes: 3 additions & 3 deletions examples/create_file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ use linux::*;

const FILE_TO_CREATE: &str = "CREATE.TXT";

use embedded_sdmmc::{Error, Mode, VolumeIdx};
use embedded_sdmmc::blocking::{Error, Mode, VolumeIdx};

type VolumeManager = embedded_sdmmc::VolumeManager<LinuxBlockDevice, Clock, 8, 4, 4>;
type VolumeManager = embedded_sdmmc::blocking::VolumeManager<LinuxBlockDevice, Clock, 8, 4, 4>;

fn main() -> Result<(), embedded_sdmmc::Error<std::io::Error>> {
fn main() -> Result<(), Error<std::io::Error>> {
env_logger::init();
let mut args = std::env::args().skip(1);
let filename = args.next().unwrap_or_else(|| "/dev/mmcblk0".into());
Expand Down
6 changes: 3 additions & 3 deletions examples/delete_file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@ use linux::*;

const FILE_TO_DELETE: &str = "README.TXT";

use embedded_sdmmc::{Error, VolumeIdx};
use embedded_sdmmc::blocking::{Error, VolumeIdx};

type VolumeManager = embedded_sdmmc::VolumeManager<LinuxBlockDevice, Clock, 8, 4, 4>;
type VolumeManager = embedded_sdmmc::blocking::VolumeManager<LinuxBlockDevice, Clock, 8, 4, 4>;

fn main() -> Result<(), embedded_sdmmc::Error<std::io::Error>> {
fn main() -> Result<(), Error<std::io::Error>> {
env_logger::init();
let mut args = std::env::args().skip(1);
let filename = args.next().unwrap_or_else(|| "/dev/mmcblk0".into());
Expand Down
2 changes: 1 addition & 1 deletion examples/linux/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Helpers for using embedded-sdmmc on Linux
use chrono::Timelike;
use embedded_sdmmc::{Block, BlockCount, BlockDevice, BlockIdx, TimeSource, Timestamp};
use embedded_sdmmc::blocking::{Block, BlockCount, BlockDevice, BlockIdx, TimeSource, Timestamp};
use std::cell::RefCell;
use std::fs::{File, OpenOptions};
use std::io::prelude::*;
Expand Down
8 changes: 4 additions & 4 deletions examples/list_dir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,12 @@
mod linux;
use linux::*;

use embedded_sdmmc::{ShortFileName, VolumeIdx};
use embedded_sdmmc::blocking::{ShortFileName, VolumeIdx};

type Error = embedded_sdmmc::Error<std::io::Error>;
type Error = embedded_sdmmc::blocking::Error<std::io::Error>;

type Directory<'a> = embedded_sdmmc::Directory<'a, LinuxBlockDevice, Clock, 8, 4, 4>;
type VolumeManager = embedded_sdmmc::VolumeManager<LinuxBlockDevice, Clock, 8, 4, 4>;
type Directory<'a> = embedded_sdmmc::blocking::Directory<'a, LinuxBlockDevice, Clock, 8, 4, 4>;
type VolumeManager = embedded_sdmmc::blocking::VolumeManager<LinuxBlockDevice, Clock, 8, 4, 4>;

fn main() -> Result<(), Error> {
env_logger::init();
Expand Down
6 changes: 3 additions & 3 deletions examples/read_file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,11 @@ use linux::*;

const FILE_TO_READ: &str = "README.TXT";

use embedded_sdmmc::{Error, Mode, VolumeIdx};
use embedded_sdmmc::blocking::{Error, Mode, VolumeIdx};

type VolumeManager = embedded_sdmmc::VolumeManager<LinuxBlockDevice, Clock, 8, 4, 4>;
type VolumeManager = embedded_sdmmc::blocking::VolumeManager<LinuxBlockDevice, Clock, 8, 4, 4>;

fn main() -> Result<(), embedded_sdmmc::Error<std::io::Error>> {
fn main() -> Result<(), Error<std::io::Error>> {
env_logger::init();
let mut args = std::env::args().skip(1);
let filename = args.next().unwrap_or_else(|| "/dev/mmcblk0".into());
Expand Down
26 changes: 13 additions & 13 deletions examples/readme_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,9 @@ impl embedded_hal::delay::DelayNs for FakeDelayer {

struct FakeTimesource();

impl embedded_sdmmc::TimeSource for FakeTimesource {
fn get_timestamp(&self) -> embedded_sdmmc::Timestamp {
embedded_sdmmc::Timestamp {
impl embedded_sdmmc::blocking::TimeSource for FakeTimesource {
fn get_timestamp(&self) -> embedded_sdmmc::blocking::Timestamp {
embedded_sdmmc::blocking::Timestamp {
year_since_1970: 0,
zero_indexed_month: 0,
zero_indexed_day: 0,
Expand All @@ -95,18 +95,18 @@ impl embedded_sdmmc::TimeSource for FakeTimesource {

#[derive(Debug, Clone)]
enum Error {
Filesystem(embedded_sdmmc::Error<embedded_sdmmc::SdCardError>),
Disk(embedded_sdmmc::SdCardError),
Filesystem(embedded_sdmmc::blocking::Error<embedded_sdmmc::blocking::SdCardError>),
Disk(embedded_sdmmc::blocking::SdCardError),
}

impl From<embedded_sdmmc::Error<embedded_sdmmc::SdCardError>> for Error {
fn from(value: embedded_sdmmc::Error<embedded_sdmmc::SdCardError>) -> Error {
impl From<embedded_sdmmc::blocking::Error<embedded_sdmmc::blocking::SdCardError>> for Error {
fn from(value: embedded_sdmmc::blocking::Error<embedded_sdmmc::blocking::SdCardError>) -> Error {
Error::Filesystem(value)
}
}

impl From<embedded_sdmmc::SdCardError> for Error {
fn from(value: embedded_sdmmc::SdCardError) -> Error {
impl From<embedded_sdmmc::blocking::SdCardError> for Error {
fn from(value: embedded_sdmmc::blocking::SdCardError) -> Error {
Error::Disk(value)
}
}
Expand All @@ -120,21 +120,21 @@ fn main() -> Result<(), Error> {
// END Fake stuff that will be replaced with real peripherals

// Build an SD Card interface out of an SPI device, a chip-select pin and the delay object
let sdcard = embedded_sdmmc::SdCard::new(sdmmc_spi, delay);
let sdcard = embedded_sdmmc::blocking::SdCard::new(sdmmc_spi, delay);
// Get the card size (this also triggers card initialisation because it's not been done yet)
println!("Card size is {} bytes", sdcard.num_bytes()?);
// Now let's look for volumes (also known as partitions) on our block device.
// To do this we need a Volume Manager. It will take ownership of the block device.
let volume_mgr = embedded_sdmmc::VolumeManager::new(sdcard, time_source);
let volume_mgr = embedded_sdmmc::blocking::VolumeManager::new(sdcard, time_source);
// Try and access Volume 0 (i.e. the first partition).
// The volume object holds information about the filesystem on that volume.
let volume0 = volume_mgr.open_volume(embedded_sdmmc::VolumeIdx(0))?;
let volume0 = volume_mgr.open_volume(embedded_sdmmc::blocking::VolumeIdx(0))?;
println!("Volume 0: {:?}", volume0);
// Open the root directory (mutably borrows from the volume).
let root_dir = volume0.open_root_dir()?;
// Open a file called "MY_FILE.TXT" in the root directory
// This mutably borrows the directory.
let my_file = root_dir.open_file_in_dir("MY_FILE.TXT", embedded_sdmmc::Mode::ReadOnly)?;
let my_file = root_dir.open_file_in_dir("MY_FILE.TXT", embedded_sdmmc::blocking::Mode::ReadOnly)?;
// Print the contents of the file, assuming it's in ISO-8859-1 encoding
while !my_file.is_eof() {
let mut buffer = [0u8; 32];
Expand Down
10 changes: 5 additions & 5 deletions examples/shell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,12 @@
use std::{cell::RefCell, io::prelude::*};

use embedded_sdmmc::{
use embedded_sdmmc::blocking::{
Error as EsError, LfnBuffer, RawDirectory, RawVolume, ShortFileName, VolumeIdx,
};

type VolumeManager = embedded_sdmmc::VolumeManager<LinuxBlockDevice, Clock, 8, 8, 4>;
type Directory<'a> = embedded_sdmmc::Directory<'a, LinuxBlockDevice, Clock, 8, 8, 4>;
type VolumeManager = embedded_sdmmc::blocking::VolumeManager<LinuxBlockDevice, Clock, 8, 8, 4>;
type Directory<'a> = embedded_sdmmc::blocking::Directory<'a, LinuxBlockDevice, Clock, 8, 8, 4>;

use crate::linux::{Clock, LinuxBlockDevice};

Expand Down Expand Up @@ -324,7 +324,7 @@ impl Context {
/// print a text file
fn cat(&self, filename: &Path) -> Result<(), Error> {
let (dir, filename) = self.resolve_filename(filename)?;
let f = dir.open_file_in_dir(filename, embedded_sdmmc::Mode::ReadOnly)?;
let f = dir.open_file_in_dir(filename, embedded_sdmmc::blocking::Mode::ReadOnly)?;
let mut data = Vec::new();
while !f.is_eof() {
let mut buffer = vec![0u8; 65536];
Expand All @@ -344,7 +344,7 @@ impl Context {
/// print a binary file
fn hexdump(&self, filename: &Path) -> Result<(), Error> {
let (dir, filename) = self.resolve_filename(filename)?;
let f = dir.open_file_in_dir(filename, embedded_sdmmc::Mode::ReadOnly)?;
let f = dir.open_file_in_dir(filename, embedded_sdmmc::blocking::Mode::ReadOnly)?;
let mut data = Vec::new();
while !f.is_eof() {
let mut buffer = vec![0u8; 65536];
Expand Down
30 changes: 17 additions & 13 deletions src/blockdevice.rs → src/inner/blockdevice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
//! Generic code for handling block devices, such as types for identifying
//! a particular block on a block device by its index.
use super::super::bisync;

/// A standard 512 byte block (also known as a sector).
///
/// IBM PC formatted 5.25" and 3.5" floppy disks, IDE/SATA Hard Drives up to
Expand Down Expand Up @@ -75,15 +77,16 @@ impl Default for Block {

/// A block device - a device which can read and write blocks (or
/// sectors). Only supports devices which are <= 2 TiB in size.
#[bisync]
pub trait BlockDevice {
/// The errors that the `BlockDevice` can return. Must be debug formattable.
type Error: core::fmt::Debug;
/// Read one or more blocks, starting at the given block index.
fn read(&self, blocks: &mut [Block], start_block_idx: BlockIdx) -> Result<(), Self::Error>;
async fn read(&self, blocks: &mut [Block], start_block_idx: BlockIdx) -> Result<(), Self::Error>;
/// Write one or more blocks, starting at the given block index.
fn write(&self, blocks: &[Block], start_block_idx: BlockIdx) -> Result<(), Self::Error>;
async fn write(&self, blocks: &[Block], start_block_idx: BlockIdx) -> Result<(), Self::Error>;
/// Determine how many blocks this device can hold.
fn num_blocks(&self) -> Result<BlockCount, Self::Error>;
async fn num_blocks(&self) -> Result<BlockCount, Self::Error>;
}

/// A caching layer for block devices
Expand All @@ -96,6 +99,7 @@ pub struct BlockCache<D> {
block_idx: Option<BlockIdx>,
}

#[bisync]
impl<D> BlockCache<D>
where
D: BlockDevice,
Expand All @@ -110,42 +114,42 @@ where
}

/// Read a block, and return a reference to it.
pub fn read(&mut self, block_idx: BlockIdx) -> Result<&Block, D::Error> {
pub async fn read(&mut self, block_idx: BlockIdx) -> Result<&Block, D::Error> {
if self.block_idx != Some(block_idx) {
self.block_idx = None;
self.block_device.read(&mut self.block, block_idx)?;
self.block_device.read(&mut self.block, block_idx).await?;
self.block_idx = Some(block_idx);
}
Ok(&self.block[0])
}

/// Read a block, and return a reference to it.
pub fn read_mut(&mut self, block_idx: BlockIdx) -> Result<&mut Block, D::Error> {
pub async fn read_mut(&mut self, block_idx: BlockIdx) -> Result<&mut Block, D::Error> {
if self.block_idx != Some(block_idx) {
self.block_idx = None;
self.block_device.read(&mut self.block, block_idx)?;
self.block_device.read(&mut self.block, block_idx).await?;
self.block_idx = Some(block_idx);
}
Ok(&mut self.block[0])
}

/// Write back a block you read with [`Self::read_mut`] and then modified.
pub fn write_back(&mut self) -> Result<(), D::Error> {
pub async fn write_back(&mut self) -> Result<(), D::Error> {
self.block_device.write(
&self.block,
self.block_idx.expect("write_back with no read"),
)
).await
}

/// Write back a block you read with [`Self::read_mut`] and then modified, but to two locations.
///
/// This is useful for updating two File Allocation Tables.
pub fn write_back_with_duplicate(&mut self, duplicate: BlockIdx) -> Result<(), D::Error> {
pub async fn write_back_with_duplicate(&mut self, duplicate: BlockIdx) -> Result<(), D::Error> {
self.block_device.write(
&self.block,
self.block_idx.expect("write_back with no read"),
)?;
self.block_device.write(&self.block, duplicate)?;
).await?;
self.block_device.write(&self.block, duplicate).await?;
Ok(())
}

Expand Down Expand Up @@ -256,7 +260,7 @@ impl BlockCount {
/// How many blocks are required to hold this many bytes.
///
/// ```
/// # use embedded_sdmmc::BlockCount;
/// # use embedded_sdmmc::blocking::BlockCount;
/// assert_eq!(BlockCount::from_bytes(511), BlockCount(1));
/// assert_eq!(BlockCount::from_bytes(512), BlockCount(1));
/// assert_eq!(BlockCount::from_bytes(513), BlockCount(2));
Expand Down
2 changes: 1 addition & 1 deletion src/fat/bpb.rs → src/inner/fat/bpb.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Boot Parameter Block
use crate::{
use super::super::{
blockdevice::BlockCount,
fat::{FatType, OnDiskDirEntry},
};
Expand Down
2 changes: 1 addition & 1 deletion src/fat/info.rs → src/inner/fat/info.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{BlockCount, BlockIdx, ClusterId};
use super::super::{BlockCount, BlockIdx, ClusterId};
use byteorder::{ByteOrder, LittleEndian};

/// Indentifies the supported types of FAT format
Expand Down
2 changes: 1 addition & 1 deletion src/fat/mod.rs → src/inner/fat/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ pub use volume::{parse_volume, FatVolume, VolumeName};
#[cfg(test)]
mod test {

use super::super::{Attributes, BlockIdx, ClusterId, DirEntry, ShortFileName, Timestamp};
use super::*;
use crate::{Attributes, BlockIdx, ClusterId, DirEntry, ShortFileName, Timestamp};

fn parse(input: &str) -> Vec<u8> {
let mut output = Vec::new();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
//! Directory Entry as stored on-disk
use crate::{fat::FatType, Attributes, BlockIdx, ClusterId, DirEntry, ShortFileName, Timestamp};
use super::super::{
fat::FatType, Attributes, BlockIdx, ClusterId, DirEntry, ShortFileName, Timestamp,
};
use byteorder::{ByteOrder, LittleEndian};

/// A 32-byte directory entry as stored on-disk in a directory file.
Expand Down
Loading

0 comments on commit b18b485

Please sign in to comment.