From bb8190393294aad6d6685f202dfda79560d3e677 Mon Sep 17 00:00:00 2001 From: Be Date: Wed, 5 Feb 2025 19:20:05 -0600 Subject: [PATCH 1/5] add async support using bisync crate This moves the existing API into a `blocking` module and duplicates the API into a new `asynchronous` module, using `async fn` where applicable. Fixes https://github.com/rust-embedded-community/embedded-sdmmc-rs/issues/50 --- CHANGELOG.md | 2 + Cargo.toml | 3 + examples/append_file.rs | 4 +- examples/big_dir.rs | 4 +- examples/create_file.rs | 4 +- examples/delete_file.rs | 4 +- examples/linux/mod.rs | 2 +- examples/list_dir.rs | 8 +- examples/read_file.rs | 4 +- examples/readme_test.rs | 4 +- examples/shell.rs | 6 +- src/{ => inner}/blockdevice.rs | 30 +- src/{ => inner}/fat/bpb.rs | 2 +- src/{ => inner}/fat/info.rs | 2 +- src/{ => inner}/fat/mod.rs | 2 +- src/{ => inner}/fat/ondiskdirentry.rs | 4 +- src/{ => inner}/fat/volume.rs | 167 +++++----- src/{ => inner}/filesystem/attributes.rs | 0 src/{ => inner}/filesystem/cluster.rs | 0 src/{ => inner}/filesystem/directory.rs | 75 ++--- src/{ => inner}/filesystem/filename.rs | 2 +- src/{ => inner}/filesystem/files.rs | 77 ++--- src/{ => inner}/filesystem/handles.rs | 0 src/{ => inner}/filesystem/mod.rs | 0 src/{ => inner}/filesystem/timestamp.rs | 0 src/inner/mod.rs | 356 +++++++++++++++++++++ src/{ => inner}/sdcard/mod.rs | 231 +++++++------- src/{ => inner}/sdcard/proto.rs | 0 src/{ => inner}/volume_mgr.rs | 115 +++---- src/lib.rs | 376 ++--------------------- tests/directories.rs | 62 ++-- tests/open_files.rs | 2 +- tests/read_file.rs | 32 +- tests/utils/mod.rs | 20 +- tests/volume.rs | 48 +-- tests/write_file.rs | 2 +- 36 files changed, 866 insertions(+), 784 deletions(-) rename src/{ => inner}/blockdevice.rs (90%) rename src/{ => inner}/fat/bpb.rs (99%) rename src/{ => inner}/fat/info.rs (98%) rename src/{ => inner}/fat/mod.rs (99%) rename src/{ => inner}/fat/ondiskdirentry.rs (98%) rename src/{ => inner}/fat/volume.rs (93%) rename src/{ => inner}/filesystem/attributes.rs (100%) rename src/{ => inner}/filesystem/cluster.rs (100%) rename src/{ => inner}/filesystem/directory.rs (86%) rename src/{ => inner}/filesystem/filename.rs (99%) rename src/{ => inner}/filesystem/files.rs (85%) rename src/{ => inner}/filesystem/handles.rs (100%) rename src/{ => inner}/filesystem/mod.rs (100%) rename src/{ => inner}/filesystem/timestamp.rs (100%) create mode 100644 src/inner/mod.rs rename src/{ => inner}/sdcard/mod.rs (76%) rename src/{ => inner}/sdcard/proto.rs (100%) rename src/{ => inner}/volume_mgr.rs (96%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a5e92eb..9836a949 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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. @@ -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>` diff --git a/Cargo.toml b/Cargo.toml index 49b13171..120bfc37 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,10 +14,13 @@ version = "0.8.0" rust-version = "1.76" [dependencies] +bisync = "0.3.0" 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} diff --git a/examples/append_file.rs b/examples/append_file.rs index 54b7577e..d8915a65 100644 --- a/examples/append_file.rs +++ b/examples/append_file.rs @@ -19,9 +19,9 @@ 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; +type VolumeManager = embedded_sdmmc::blocking::VolumeManager; fn main() -> Result<(), Error> { env_logger::init(); diff --git a/examples/big_dir.rs b/examples/big_dir.rs index bfc7e83c..5f4e8d3c 100644 --- a/examples/big_dir.rs +++ b/examples/big_dir.rs @@ -21,9 +21,9 @@ mod linux; use linux::*; -use embedded_sdmmc::{Error, Mode, VolumeIdx}; +use embedded_sdmmc::blocking::{Error, Mode, VolumeIdx}; -type VolumeManager = embedded_sdmmc::VolumeManager; +type VolumeManager = embedded_sdmmc::blocking::VolumeManager; fn main() -> Result<(), Error> { env_logger::init(); diff --git a/examples/create_file.rs b/examples/create_file.rs index 7f3cfb48..e114a094 100644 --- a/examples/create_file.rs +++ b/examples/create_file.rs @@ -19,9 +19,9 @@ 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; +type VolumeManager = embedded_sdmmc::blocking::VolumeManager; fn main() -> Result<(), Error> { env_logger::init(); diff --git a/examples/delete_file.rs b/examples/delete_file.rs index 3df1978e..55243f4e 100644 --- a/examples/delete_file.rs +++ b/examples/delete_file.rs @@ -22,9 +22,9 @@ 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; +type VolumeManager = embedded_sdmmc::blocking::VolumeManager; fn main() -> Result<(), Error> { env_logger::init(); diff --git a/examples/linux/mod.rs b/examples/linux/mod.rs index 6eefe23d..c52e979f 100644 --- a/examples/linux/mod.rs +++ b/examples/linux/mod.rs @@ -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::*; diff --git a/examples/list_dir.rs b/examples/list_dir.rs index e12807af..ee0306ee 100644 --- a/examples/list_dir.rs +++ b/examples/list_dir.rs @@ -34,12 +34,12 @@ mod linux; use linux::*; -use embedded_sdmmc::{ShortFileName, VolumeIdx}; +use embedded_sdmmc::blocking::{ShortFileName, VolumeIdx}; -type Error = embedded_sdmmc::Error; +type Error = embedded_sdmmc::blocking::Error; -type Directory<'a> = embedded_sdmmc::Directory<'a, LinuxBlockDevice, Clock, 8, 4, 4>; -type VolumeManager = embedded_sdmmc::VolumeManager; +type Directory<'a> = embedded_sdmmc::blocking::Directory<'a, LinuxBlockDevice, Clock, 8, 4, 4>; +type VolumeManager = embedded_sdmmc::blocking::VolumeManager; fn main() -> Result<(), Error> { env_logger::init(); diff --git a/examples/read_file.rs b/examples/read_file.rs index 0800de94..f97e481f 100644 --- a/examples/read_file.rs +++ b/examples/read_file.rs @@ -36,9 +36,9 @@ 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; +type VolumeManager = embedded_sdmmc::blocking::VolumeManager; fn main() -> Result<(), Error> { env_logger::init(); diff --git a/examples/readme_test.rs b/examples/readme_test.rs index 0d63d80b..d5f924c1 100644 --- a/examples/readme_test.rs +++ b/examples/readme_test.rs @@ -7,7 +7,7 @@ use core::cell::RefCell; -use embedded_sdmmc::{Error, SdCardError, TimeSource, Timestamp}; +use embedded_sdmmc::blocking::{Error, SdCardError, TimeSource, Timestamp}; pub struct DummyCsPin; @@ -121,7 +121,7 @@ fn main() -> Result<(), MyError> { let time_source = FakeTimesource(); // END Fake stuff that will be replaced with real peripherals - use embedded_sdmmc::{Mode, SdCard, VolumeIdx, VolumeManager}; + use embedded_sdmmc::blocking::{Mode, SdCard, VolumeIdx, VolumeManager}; // Build an SD Card interface out of an SPI device, a chip-select pin and the delay object let sdcard = SdCard::new(sdmmc_spi, delay); // Get the card size (this also triggers card initialisation because it's not been done yet) diff --git a/examples/shell.rs b/examples/shell.rs index 42682769..ea75086f 100644 --- a/examples/shell.rs +++ b/examples/shell.rs @@ -85,12 +85,12 @@ use std::{cell::RefCell, io::prelude::*}; -use embedded_sdmmc::{ +use embedded_sdmmc::blocking::{ Error as EsError, LfnBuffer, Mode, RawDirectory, RawVolume, ShortFileName, VolumeIdx, }; -type VolumeManager = embedded_sdmmc::VolumeManager; -type Directory<'a> = embedded_sdmmc::Directory<'a, LinuxBlockDevice, Clock, 8, 8, 4>; +type VolumeManager = embedded_sdmmc::blocking::VolumeManager; +type Directory<'a> = embedded_sdmmc::blocking::Directory<'a, LinuxBlockDevice, Clock, 8, 8, 4>; use crate::linux::{Clock, LinuxBlockDevice}; diff --git a/src/blockdevice.rs b/src/inner/blockdevice.rs similarity index 90% rename from src/blockdevice.rs rename to src/inner/blockdevice.rs index 674ae55d..130e89de 100644 --- a/src/blockdevice.rs +++ b/src/inner/blockdevice.rs @@ -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 @@ -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; + async fn num_blocks(&self) -> Result; } /// A caching layer for block devices @@ -96,6 +99,7 @@ pub struct BlockCache { block_idx: Option, } +#[bisync] impl BlockCache where D: BlockDevice, @@ -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(()) } @@ -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)); diff --git a/src/fat/bpb.rs b/src/inner/fat/bpb.rs similarity index 99% rename from src/fat/bpb.rs rename to src/inner/fat/bpb.rs index c7e83b62..54118305 100644 --- a/src/fat/bpb.rs +++ b/src/inner/fat/bpb.rs @@ -1,6 +1,6 @@ //! Boot Parameter Block -use crate::{ +use super::super::{ blockdevice::BlockCount, fat::{FatType, OnDiskDirEntry}, }; diff --git a/src/fat/info.rs b/src/inner/fat/info.rs similarity index 98% rename from src/fat/info.rs rename to src/inner/fat/info.rs index f9f8e2c3..e520f432 100644 --- a/src/fat/info.rs +++ b/src/inner/fat/info.rs @@ -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 diff --git a/src/fat/mod.rs b/src/inner/fat/mod.rs similarity index 99% rename from src/fat/mod.rs rename to src/inner/fat/mod.rs index c27a8cdd..9d100988 100644 --- a/src/fat/mod.rs +++ b/src/inner/fat/mod.rs @@ -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 { let mut output = Vec::new(); diff --git a/src/fat/ondiskdirentry.rs b/src/inner/fat/ondiskdirentry.rs similarity index 98% rename from src/fat/ondiskdirentry.rs rename to src/inner/fat/ondiskdirentry.rs index 83707e47..9ad3dcb8 100644 --- a/src/fat/ondiskdirentry.rs +++ b/src/inner/fat/ondiskdirentry.rs @@ -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. diff --git a/src/fat/volume.rs b/src/inner/fat/volume.rs similarity index 93% rename from src/fat/volume.rs rename to src/inner/fat/volume.rs index 78b88f7f..34ee2cb2 100644 --- a/src/fat/volume.rs +++ b/src/inner/fat/volume.rs @@ -1,15 +1,16 @@ //! FAT-specific volume support. -use crate::{ - debug, +use super::super::super::bisync; +use super::super::{ fat::{ Bpb, Fat16Info, Fat32Info, FatSpecificInfo, FatType, InfoSector, OnDiskDirEntry, RESERVED_ENTRIES, }, filesystem::FilenameError, - trace, warn, Attributes, Block, BlockCache, BlockCount, BlockDevice, BlockIdx, ClusterId, - DirEntry, DirectoryInfo, Error, LfnBuffer, ShortFileName, TimeSource, VolumeType, + Attributes, Block, BlockCache, BlockCount, BlockDevice, BlockIdx, ClusterId, DirEntry, + DirectoryInfo, Error, LfnBuffer, ShortFileName, TimeSource, VolumeType, }; +use crate::{debug, trace, warn}; use byteorder::{ByteOrder, LittleEndian}; use core::convert::TryFrom; @@ -170,9 +171,10 @@ pub struct FatVolume { pub(crate) fat_specific_info: FatSpecificInfo, } +#[bisync] impl FatVolume { /// Write a new entry in the FAT - pub fn update_info_sector( + pub async fn update_info_sector( &mut self, block_cache: &mut BlockCache, ) -> Result<(), Error> @@ -189,7 +191,7 @@ impl FatVolume { } trace!("Reading info sector"); let block = block_cache - .read_mut(fat32_info.info_location) + .read_mut(fat32_info.info_location).await .map_err(Error::DeviceError)?; if let Some(count) = self.free_clusters_count { block[488..492].copy_from_slice(&count.to_le_bytes()); @@ -198,7 +200,7 @@ impl FatVolume { block[492..496].copy_from_slice(&next_free_cluster.0.to_le_bytes()); } trace!("Writing info sector"); - block_cache.write_back()?; + block_cache.write_back().await?; } } Ok(()) @@ -213,7 +215,7 @@ impl FatVolume { } /// Write a new entry in the FAT - fn update_fat( + async fn update_fat( &mut self, block_cache: &mut BlockCache, cluster: ClusterId, @@ -234,7 +236,7 @@ impl FatVolume { let this_fat_ent_offset = (fat_offset % Block::LEN_U32) as usize; trace!("Reading FAT for update"); let block = block_cache - .read_mut(this_fat_block_num) + .read_mut(this_fat_block_num).await .map_err(Error::DeviceError)?; // See let entry = match new_value { @@ -260,7 +262,7 @@ impl FatVolume { let this_fat_ent_offset = (fat_offset % Block::LEN_U32) as usize; trace!("Reading FAT for update"); let block = block_cache - .read_mut(this_fat_block_num) + .read_mut(this_fat_block_num).await .map_err(Error::DeviceError)?; let entry = match new_value { ClusterId::INVALID => 0x0FFF_FFF6, @@ -279,15 +281,15 @@ impl FatVolume { } trace!("Updating FAT"); if let Some(duplicate) = second_fat_block_num { - block_cache.write_back_with_duplicate(duplicate)?; + block_cache.write_back_with_duplicate(duplicate).await?; } else { - block_cache.write_back()?; + block_cache.write_back().await?; } Ok(()) } /// Look in the FAT to see which cluster comes next. - pub(crate) fn next_cluster( + pub(crate) async fn next_cluster( &self, block_cache: &mut BlockCache, cluster: ClusterId, @@ -304,7 +306,7 @@ impl FatVolume { let this_fat_block_num = self.lba_start + self.fat_start.offset_bytes(fat_offset); let this_fat_ent_offset = (fat_offset % Block::LEN_U32) as usize; trace!("Walking FAT"); - let block = block_cache.read(this_fat_block_num)?; + let block = block_cache.read(this_fat_block_num).await?; let fat_entry = LittleEndian::read_u16(&block[this_fat_ent_offset..=this_fat_ent_offset + 1]); match fat_entry { @@ -327,7 +329,7 @@ impl FatVolume { let this_fat_block_num = self.lba_start + self.fat_start.offset_bytes(fat_offset); let this_fat_ent_offset = (fat_offset % Block::LEN_U32) as usize; trace!("Walking FAT"); - let block = block_cache.read(this_fat_block_num)?; + let block = block_cache.read(this_fat_block_num).await?; let fat_entry = LittleEndian::read_u32(&block[this_fat_ent_offset..=this_fat_ent_offset + 3]) & 0x0FFF_FFFF; @@ -390,7 +392,7 @@ impl FatVolume { /// Finds a empty entry space and writes the new entry to it, allocates a new cluster if it's /// needed - pub(crate) fn write_new_directory_entry( + pub(crate) async fn write_new_directory_entry( &mut self, block_cache: &mut BlockCache, time_source: &T, @@ -427,7 +429,7 @@ impl FatVolume { for block_idx in first_dir_block_num.range(dir_size) { trace!("Reading directory"); let block = block_cache - .read_mut(block_idx) + .read_mut(block_idx).await .map_err(Error::DeviceError)?; for (i, dir_entry_bytes) in block.chunks_exact_mut(OnDiskDirEntry::LEN).enumerate() @@ -447,19 +449,19 @@ impl FatVolume { dir_entry_bytes .copy_from_slice(&entry.serialize(FatType::Fat16)[..]); trace!("Updating directory"); - block_cache.write_back()?; + block_cache.write_back().await?; return Ok(entry); } } } if cluster != ClusterId::ROOT_DIR { - current_cluster = match self.next_cluster(block_cache, cluster) { + current_cluster = match self.next_cluster(block_cache, cluster).await { Ok(n) => { first_dir_block_num = self.cluster_to_block(n); Some(n) } Err(Error::EndOfFile) => { - let c = self.alloc_cluster(block_cache, Some(cluster), true)?; + let c = self.alloc_cluster(block_cache, Some(cluster), true).await?; first_dir_block_num = self.cluster_to_block(c); Some(c) } @@ -488,7 +490,7 @@ impl FatVolume { // Read a block of directory entries trace!("Reading directory"); let block = block_cache - .read_mut(block_idx) + .read_mut(block_idx).await .map_err(Error::DeviceError)?; // Are any entries in the block we just loaded blank? If so // we can use them. @@ -510,20 +512,20 @@ impl FatVolume { dir_entry_bytes .copy_from_slice(&entry.serialize(FatType::Fat32)[..]); trace!("Updating directory"); - block_cache.write_back()?; + block_cache.write_back().await?; return Ok(entry); } } } // Well none of the blocks in that cluster had any space in // them, let's fetch another one. - current_cluster = match self.next_cluster(block_cache, cluster) { + current_cluster = match self.next_cluster(block_cache, cluster).await { Ok(n) => { first_dir_block_num = self.cluster_to_block(n); Some(n) } Err(Error::EndOfFile) => { - let c = self.alloc_cluster(block_cache, Some(cluster), true)?; + let c = self.alloc_cluster(block_cache, Some(cluster), true).await?; first_dir_block_num = self.cluster_to_block(c); Some(c) } @@ -539,7 +541,7 @@ impl FatVolume { /// Calls callback `func` with every valid entry in the given directory. /// Useful for performing directory listings. - pub(crate) fn iterate_dir( + pub(crate) async fn iterate_dir( &self, block_cache: &mut BlockCache, dir_info: &DirectoryInfo, @@ -551,10 +553,10 @@ impl FatVolume { { match &self.fat_specific_info { FatSpecificInfo::Fat16(fat16_info) => { - self.iterate_fat16(dir_info, fat16_info, block_cache, |de, _| func(de)) + self.iterate_fat16(dir_info, fat16_info, block_cache, |de, _| func(de)).await } FatSpecificInfo::Fat32(fat32_info) => { - self.iterate_fat32(dir_info, fat32_info, block_cache, |de, _| func(de)) + self.iterate_fat32(dir_info, fat32_info, block_cache, |de, _| func(de)).await } } } @@ -563,7 +565,7 @@ impl FatVolume { /// including the Long File Name. /// /// Useful for performing directory listings. - pub(crate) fn iterate_dir_lfn( + pub(crate) async fn iterate_dir_lfn( &self, block_cache: &mut BlockCache, lfn_buffer: &mut LfnBuffer<'_>, @@ -650,7 +652,7 @@ impl FatVolume { } else { func(de, None) } - }) + }).await } FatSpecificInfo::Fat32(fat32_info) => { self.iterate_fat32(dir_info, fat32_info, block_cache, |de, odde| { @@ -667,12 +669,12 @@ impl FatVolume { } else { func(de, None) } - }) + }).await } } } - fn iterate_fat16( + async fn iterate_fat16( &self, dir_info: &DirectoryInfo, fat16_info: &Fat16Info, @@ -703,7 +705,7 @@ impl FatVolume { while let Some(cluster) = current_cluster { for block_idx in first_dir_block_num.range(dir_size) { trace!("Reading FAT"); - let block = block_cache.read(block_idx)?; + let block = block_cache.read(block_idx).await?; for (i, dir_entry_bytes) in block.chunks_exact(OnDiskDirEntry::LEN).enumerate() { let dir_entry = OnDiskDirEntry::new(dir_entry_bytes); if dir_entry.is_end() { @@ -718,7 +720,7 @@ impl FatVolume { } } if cluster != ClusterId::ROOT_DIR { - current_cluster = match self.next_cluster(block_cache, cluster) { + current_cluster = match self.next_cluster(block_cache, cluster).await { Ok(n) => { first_dir_block_num = self.cluster_to_block(n); Some(n) @@ -732,7 +734,7 @@ impl FatVolume { Ok(()) } - fn iterate_fat32( + async fn iterate_fat32( &self, dir_info: &DirectoryInfo, fat32_info: &Fat32Info, @@ -753,7 +755,7 @@ impl FatVolume { let start_block_idx = self.cluster_to_block(cluster); for block_idx in start_block_idx.range(BlockCount(u32::from(self.blocks_per_cluster))) { trace!("Reading FAT"); - let block = block_cache.read(block_idx).map_err(Error::DeviceError)?; + let block = block_cache.read(block_idx).await.map_err(Error::DeviceError)?; for (i, dir_entry_bytes) in block.chunks_exact(OnDiskDirEntry::LEN).enumerate() { let dir_entry = OnDiskDirEntry::new(dir_entry_bytes); if dir_entry.is_end() { @@ -767,7 +769,7 @@ impl FatVolume { } } } - current_cluster = match self.next_cluster(block_cache, cluster) { + current_cluster = match self.next_cluster(block_cache, cluster).await { Ok(n) => Some(n), _ => None, }; @@ -776,7 +778,7 @@ impl FatVolume { } /// Get an entry from the given directory - pub(crate) fn find_directory_entry( + pub(crate) async fn find_directory_entry( &self, block_cache: &mut BlockCache, dir_info: &DirectoryInfo, @@ -812,13 +814,13 @@ impl FatVolume { FatType::Fat16, match_name, block, - ) { + ).await { Err(Error::NotFound) => continue, x => return x, } } if cluster != ClusterId::ROOT_DIR { - current_cluster = match self.next_cluster(block_cache, cluster) { + current_cluster = match self.next_cluster(block_cache, cluster).await { Ok(n) => { first_dir_block_num = self.cluster_to_block(n); Some(n) @@ -844,12 +846,12 @@ impl FatVolume { FatType::Fat32, match_name, block, - ) { + ).await { Err(Error::NotFound) => continue, x => return x, } } - current_cluster = match self.next_cluster(block_cache, cluster) { + current_cluster = match self.next_cluster(block_cache, cluster).await { Ok(n) => Some(n), _ => None, } @@ -860,7 +862,7 @@ impl FatVolume { } /// Finds an entry in a given block of directory entries. - fn find_entry_in_block( + async fn find_entry_in_block( &self, block_cache: &mut BlockCache, fat_type: FatType, @@ -871,7 +873,7 @@ impl FatVolume { D: BlockDevice, { trace!("Reading directory"); - let block = block_cache.read(block_idx).map_err(Error::DeviceError)?; + let block = block_cache.read(block_idx).await.map_err(Error::DeviceError)?; for (i, dir_entry_bytes) in block.chunks_exact(OnDiskDirEntry::LEN).enumerate() { let dir_entry = OnDiskDirEntry::new(dir_entry_bytes); if dir_entry.is_end() { @@ -888,7 +890,7 @@ impl FatVolume { } /// Delete an entry from the given directory - pub(crate) fn delete_directory_entry( + pub(crate) async fn delete_directory_entry( &self, block_cache: &mut BlockCache, dir_info: &DirectoryInfo, @@ -921,7 +923,7 @@ impl FatVolume { while let Some(cluster) = current_cluster { // Scan the cluster / root dir a block at a time for block_idx in first_dir_block_num.range(dir_size) { - match self.delete_entry_in_block(block_cache, match_name, block_idx) { + match self.delete_entry_in_block(block_cache, match_name, block_idx).await { Err(Error::NotFound) => { // Carry on } @@ -934,7 +936,7 @@ impl FatVolume { } // if it's not the root dir, find the next cluster so we can keep looking if cluster != ClusterId::ROOT_DIR { - current_cluster = match self.next_cluster(block_cache, cluster) { + current_cluster = match self.next_cluster(block_cache, cluster).await { Ok(n) => { first_dir_block_num = self.cluster_to_block(n); Some(n) @@ -961,7 +963,7 @@ impl FatVolume { for block_idx in start_block_idx.range(BlockCount(u32::from(self.blocks_per_cluster))) { - match self.delete_entry_in_block(block_cache, match_name, block_idx) { + match self.delete_entry_in_block(block_cache, match_name, block_idx).await { Err(Error::NotFound) => { // Carry on continue; @@ -974,7 +976,7 @@ impl FatVolume { } } // Find the next cluster - current_cluster = match self.next_cluster(block_cache, cluster) { + current_cluster = match self.next_cluster(block_cache, cluster).await { Ok(n) => Some(n), _ => None, } @@ -991,7 +993,7 @@ impl FatVolume { /// /// Entries are marked as deleted by setting the first byte of the file name /// to a special value. - fn delete_entry_in_block( + async fn delete_entry_in_block( &self, block_cache: &mut BlockCache, match_name: &ShortFileName, @@ -1002,7 +1004,7 @@ impl FatVolume { { trace!("Reading directory"); let block = block_cache - .read_mut(block_idx) + .read_mut(block_idx).await .map_err(Error::DeviceError)?; for (i, dir_entry_bytes) in block.chunks_exact_mut(OnDiskDirEntry::LEN).enumerate() { let dir_entry = OnDiskDirEntry::new(dir_entry_bytes); @@ -1014,14 +1016,14 @@ impl FatVolume { // set first byte to the 'unused' marker block[start] = 0xE5; trace!("Updating directory"); - return block_cache.write_back().map_err(Error::DeviceError); + return block_cache.write_back().await.map_err(Error::DeviceError); } } Err(Error::NotFound) } /// Finds the next free cluster after the start_cluster and before end_cluster - pub(crate) fn find_next_free_cluster( + pub(crate) async fn find_next_free_cluster( &self, block_cache: &mut BlockCache, start_cluster: ClusterId, @@ -1048,7 +1050,7 @@ impl FatVolume { .map_err(|_| Error::ConversionError)?; trace!("Reading block {:?}", this_fat_block_num); let block = block_cache - .read(this_fat_block_num) + .read(this_fat_block_num).await .map_err(Error::DeviceError)?; while this_fat_ent_offset <= Block::LEN - 2 { let fat_entry = LittleEndian::read_u16( @@ -1078,7 +1080,7 @@ impl FatVolume { .map_err(|_| Error::ConversionError)?; trace!("Reading block {:?}", this_fat_block_num); let block = block_cache - .read(this_fat_block_num) + .read(this_fat_block_num).await .map_err(Error::DeviceError)?; while this_fat_ent_offset <= Block::LEN - 4 { let fat_entry = LittleEndian::read_u32( @@ -1098,7 +1100,7 @@ impl FatVolume { } /// Tries to allocate a cluster - pub(crate) fn alloc_cluster( + pub(crate) async fn alloc_cluster( &mut self, block_cache: &mut BlockCache, prev_cluster: Option, @@ -1118,7 +1120,7 @@ impl FatVolume { start_cluster, end_cluster ); - let new_cluster = match self.find_next_free_cluster(block_cache, start_cluster, end_cluster) + let new_cluster = match self.find_next_free_cluster(block_cache, start_cluster, end_cluster).await { Ok(cluster) => cluster, Err(_) if start_cluster.0 > RESERVED_ENTRIES => { @@ -1127,12 +1129,12 @@ impl FatVolume { ClusterId(RESERVED_ENTRIES), end_cluster ); - self.find_next_free_cluster(block_cache, ClusterId(RESERVED_ENTRIES), end_cluster)? + self.find_next_free_cluster(block_cache, ClusterId(RESERVED_ENTRIES), end_cluster).await? } Err(e) => return Err(e), }; // This new cluster is the end of the file's chain - self.update_fat(block_cache, new_cluster, ClusterId::END_OF_FILE)?; + self.update_fat(block_cache, new_cluster, ClusterId::END_OF_FILE).await?; // If there's something before this new one, update the FAT to point it at us if let Some(cluster) = prev_cluster { trace!( @@ -1140,7 +1142,7 @@ impl FatVolume { cluster, new_cluster ); - self.update_fat(block_cache, cluster, new_cluster)?; + self.update_fat(block_cache, cluster, new_cluster).await?; } trace!( "Finding next free between {:?}..={:?}", @@ -1148,14 +1150,14 @@ impl FatVolume { end_cluster ); self.next_free_cluster = - match self.find_next_free_cluster(block_cache, new_cluster, end_cluster) { + match self.find_next_free_cluster(block_cache, new_cluster, end_cluster).await { Ok(cluster) => Some(cluster), Err(_) if new_cluster.0 > RESERVED_ENTRIES => { match self.find_next_free_cluster( block_cache, ClusterId(RESERVED_ENTRIES), end_cluster, - ) { + ).await { Ok(cluster) => Some(cluster), Err(e) => return Err(e), } @@ -1173,7 +1175,7 @@ impl FatVolume { for block_idx in start_block_idx.range(num_blocks) { trace!("Zeroing cluster {:?}", block_idx); let _block = block_cache.blank_mut(block_idx); - block_cache.write_back()?; + block_cache.write_back().await?; } } debug!("All done, returning {:?}", new_cluster); @@ -1181,7 +1183,7 @@ impl FatVolume { } /// Marks the input cluster as an EOF and all the subsequent clusters in the chain as free - pub(crate) fn truncate_cluster_chain( + pub(crate) async fn truncate_cluster_chain( &mut self, block_cache: &mut BlockCache, cluster: ClusterId, @@ -1194,7 +1196,7 @@ impl FatVolume { return Ok(()); } let mut next = { - match self.next_cluster(block_cache, cluster) { + match self.next_cluster(block_cache, cluster).await { Ok(n) => n, Err(Error::EndOfFile) => return Ok(()), Err(e) => return Err(e), @@ -1207,15 +1209,15 @@ impl FatVolume { } else { self.next_free_cluster = Some(next); } - self.update_fat(block_cache, cluster, ClusterId::END_OF_FILE)?; + self.update_fat(block_cache, cluster, ClusterId::END_OF_FILE).await?; loop { - match self.next_cluster(block_cache, next) { + match self.next_cluster(block_cache, next).await { Ok(n) => { - self.update_fat(block_cache, next, ClusterId::EMPTY)?; + self.update_fat(block_cache, next, ClusterId::EMPTY).await?; next = n; } Err(Error::EndOfFile) => { - self.update_fat(block_cache, next, ClusterId::EMPTY)?; + self.update_fat(block_cache, next, ClusterId::EMPTY).await?; break; } Err(e) => return Err(e), @@ -1228,7 +1230,7 @@ impl FatVolume { } /// Writes a Directory Entry to the disk - pub(crate) fn write_entry_to_disk( + pub(crate) async fn write_entry_to_disk( &self, block_cache: &mut BlockCache, entry: &DirEntry, @@ -1242,14 +1244,14 @@ impl FatVolume { }; trace!("Reading directory for update"); let block = block_cache - .read_mut(entry.entry_block) + .read_mut(entry.entry_block).await .map_err(Error::DeviceError)?; let start = usize::try_from(entry.entry_offset).map_err(|_| Error::ConversionError)?; block[start..start + 32].copy_from_slice(&entry.serialize(fat_type)[..]); trace!("Updating directory"); - block_cache.write_back().map_err(Error::DeviceError)?; + block_cache.write_back().await.map_err(Error::DeviceError)?; Ok(()) } @@ -1258,7 +1260,7 @@ impl FatVolume { /// 1) Creates the directory entry in the parent /// 2) Allocates a new cluster to hold the new directory /// 3) Writes out the `.` and `..` entries in the new directory - pub(crate) fn make_dir( + pub(crate) async fn make_dir( &mut self, block_cache: &mut BlockCache, time_source: &T, @@ -1271,11 +1273,11 @@ impl FatVolume { T: TimeSource, { let mut new_dir_entry_in_parent = - self.write_new_directory_entry(block_cache, time_source, parent, sfn, att)?; + self.write_new_directory_entry(block_cache, time_source, parent, sfn, att).await?; if new_dir_entry_in_parent.cluster == ClusterId::EMPTY { - new_dir_entry_in_parent.cluster = self.alloc_cluster(block_cache, None, false)?; + new_dir_entry_in_parent.cluster = self.alloc_cluster(block_cache, None, false).await?; // update the parent dir with the cluster of the new dir - self.write_entry_to_disk(block_cache, &new_dir_entry_in_parent)?; + self.write_entry_to_disk(block_cache, &new_dir_entry_in_parent).await?; } let new_dir_start_block = self.cluster_to_block(new_dir_entry_in_parent.cluster); debug!("Made new dir entry {:?}", new_dir_entry_in_parent); @@ -1285,7 +1287,7 @@ impl FatVolume { let block = block_cache.blank_mut(new_dir_start_block); // make the "." entry let dot_entry_in_child = DirEntry { - name: crate::ShortFileName::this_dir(), + name: ShortFileName::this_dir(), mtime: now, ctime: now, attributes: att, @@ -1302,7 +1304,7 @@ impl FatVolume { offset += OnDiskDirEntry::LEN; // make the ".." entry let dot_dot_entry_in_child = DirEntry { - name: crate::ShortFileName::parent_dir(), + name: ShortFileName::parent_dir(), mtime: now, ctime: now, attributes: att, @@ -1321,14 +1323,14 @@ impl FatVolume { block[offset..offset + OnDiskDirEntry::LEN] .copy_from_slice(&dot_dot_entry_in_child.serialize(fat_type)[..]); - block_cache.write_back()?; + block_cache.write_back().await?; for block_idx in new_dir_start_block .range(BlockCount(u32::from(self.blocks_per_cluster))) .skip(1) { let _block = block_cache.blank_mut(block_idx); - block_cache.write_back()?; + block_cache.write_back().await?; } Ok(()) @@ -1337,7 +1339,8 @@ impl FatVolume { /// Load the boot parameter block from the start of the given partition and /// determine if the partition contains a valid FAT16 or FAT32 file system. -pub fn parse_volume( +#[bisync] +pub async fn parse_volume( block_cache: &mut BlockCache, lba_start: BlockIdx, num_blocks: BlockCount, @@ -1347,7 +1350,7 @@ where D::Error: core::fmt::Debug, { trace!("Reading BPB"); - let block = block_cache.read(lba_start).map_err(Error::DeviceError)?; + let block = block_cache.read(lba_start).await.map_err(Error::DeviceError)?; let bpb = Bpb::create_from_bytes(block).map_err(Error::FormatError)?; let fat_start = BlockCount(u32::from(bpb.reserved_block_count())); let second_fat_start = if bpb.num_fats() == 2 { @@ -1415,7 +1418,7 @@ where // Now we don't need the BPB, update the volume with data from the info sector trace!("Reading info block"); let info_block = block_cache - .read(lba_start + info_location) + .read(lba_start + info_location).await .map_err(Error::DeviceError)?; let info_sector = InfoSector::create_from_bytes(info_block).map_err(Error::FormatError)?; diff --git a/src/filesystem/attributes.rs b/src/inner/filesystem/attributes.rs similarity index 100% rename from src/filesystem/attributes.rs rename to src/inner/filesystem/attributes.rs diff --git a/src/filesystem/cluster.rs b/src/inner/filesystem/cluster.rs similarity index 100% rename from src/filesystem/cluster.rs rename to src/inner/filesystem/cluster.rs diff --git a/src/filesystem/directory.rs b/src/inner/filesystem/directory.rs similarity index 86% rename from src/filesystem/directory.rs rename to src/inner/filesystem/directory.rs index 527807bc..3c93b0a6 100644 --- a/src/filesystem/directory.rs +++ b/src/inner/filesystem/directory.rs @@ -1,7 +1,11 @@ -use crate::blockdevice::BlockIdx; -use crate::fat::{FatType, OnDiskDirEntry}; -use crate::filesystem::{Attributes, ClusterId, Handle, LfnBuffer, ShortFileName, Timestamp}; -use crate::{Error, RawVolume, VolumeManager}; +use super::super::super::bisync; +use super::super::blockdevice::BlockIdx; +use super::super::fat::{FatType, OnDiskDirEntry}; +use super::super::filesystem::{ + Attributes, ClusterId, Handle, LfnBuffer, ShortFileName, Timestamp, +}; +use super::super::{BlockDevice, File, Mode, TimeSource}; +use super::super::{Error, RawVolume, VolumeManager}; use super::ToShortFileName; @@ -34,7 +38,7 @@ pub struct DirEntry { /// still have the directory open, and it won't let you open the directory /// again. /// -/// Instead you must pass it to [`crate::VolumeManager::close_dir`] to close it +/// Instead you must pass it to [`VolumeManager::close_dir`] to close it /// cleanly. /// /// If you want your directories to close themselves on drop, create your own @@ -60,8 +64,8 @@ impl RawDirectory { volume_mgr: &VolumeManager, ) -> Directory where - D: crate::BlockDevice, - T: crate::TimeSource, + D: BlockDevice, + T: TimeSource, { Directory::new(self, volume_mgr) } @@ -83,18 +87,19 @@ pub struct Directory< const MAX_FILES: usize, const MAX_VOLUMES: usize, > where - D: crate::BlockDevice, - T: crate::TimeSource, + D: BlockDevice, + T: TimeSource, { raw_directory: RawDirectory, volume_mgr: &'a VolumeManager, } +#[bisync] impl<'a, D, T, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize> Directory<'a, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES> where - D: crate::BlockDevice, - T: crate::TimeSource, + D: BlockDevice, + T: TimeSource, { /// Create a new `Directory` from a `RawDirectory` pub fn new( @@ -110,37 +115,37 @@ where /// Open a directory. /// /// You can then read the directory entries with `iterate_dir` and `open_file_in_dir`. - pub fn open_dir( + pub async fn open_dir( &self, name: N, ) -> Result, Error> where N: ToShortFileName, { - let d = self.volume_mgr.open_dir(self.raw_directory, name)?; + let d = self.volume_mgr.open_dir(self.raw_directory, name).await?; Ok(d.to_directory(self.volume_mgr)) } /// Change to a directory, mutating this object. /// /// You can then read the directory entries with `iterate_dir` and `open_file_in_dir`. - pub fn change_dir(&mut self, name: N) -> Result<(), Error> + pub async fn change_dir(&mut self, name: N) -> Result<(), Error> where N: ToShortFileName, { - let d = self.volume_mgr.open_dir(self.raw_directory, name)?; + let d = self.volume_mgr.open_dir(self.raw_directory, name).await?; self.volume_mgr.close_dir(self.raw_directory).unwrap(); self.raw_directory = d; Ok(()) } /// Look in a directory for a named file. - pub fn find_directory_entry(&self, name: N) -> Result> + pub async fn find_directory_entry(&self, name: N) -> Result> where N: ToShortFileName, { self.volume_mgr - .find_directory_entry(self.raw_directory, name) + .find_directory_entry(self.raw_directory, name).await } /// Call a callback function for each directory entry in a directory. @@ -154,11 +159,11 @@ where /// object is already locked in order to do the iteration. /// /// - pub fn iterate_dir(&self, func: F) -> Result<(), Error> + pub async fn iterate_dir(&self, func: F) -> Result<(), Error> where F: FnMut(&DirEntry), { - self.volume_mgr.iterate_dir(self.raw_directory, func) + self.volume_mgr.iterate_dir(self.raw_directory, func).await } /// Call a callback function for each directory entry in a directory, and @@ -176,7 +181,7 @@ where /// object is already locked in order to do the iteration. /// /// - pub fn iterate_dir_lfn( + pub async fn iterate_dir_lfn( &self, lfn_buffer: &mut LfnBuffer<'_>, func: F, @@ -185,38 +190,38 @@ where F: FnMut(&DirEntry, Option<&str>), { self.volume_mgr - .iterate_dir_lfn(self.raw_directory, lfn_buffer, func) + .iterate_dir_lfn(self.raw_directory, lfn_buffer, func).await } /// Open a file with the given full path. A file can only be opened once. - pub fn open_file_in_dir( + pub async fn open_file_in_dir( &self, name: N, - mode: crate::Mode, - ) -> Result, crate::Error> + mode: Mode, + ) -> Result, Error> where N: super::ToShortFileName, { let f = self .volume_mgr - .open_file_in_dir(self.raw_directory, name, mode)?; + .open_file_in_dir(self.raw_directory, name, mode).await?; Ok(f.to_file(self.volume_mgr)) } /// Delete a closed file with the given filename, if it exists. - pub fn delete_file_in_dir(&self, name: N) -> Result<(), Error> + pub async fn delete_file_in_dir(&self, name: N) -> Result<(), Error> where N: ToShortFileName, { - self.volume_mgr.delete_file_in_dir(self.raw_directory, name) + self.volume_mgr.delete_file_in_dir(self.raw_directory, name).await } /// Make a directory inside this directory - pub fn make_dir_in_dir(&self, name: N) -> Result<(), Error> + pub async fn make_dir_in_dir(&self, name: N) -> Result<(), Error> where N: ToShortFileName, { - self.volume_mgr.make_dir_in_dir(self.raw_directory, name) + self.volume_mgr.make_dir_in_dir(self.raw_directory, name).await } /// Convert back to a raw directory @@ -240,8 +245,8 @@ where impl<'a, D, T, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize> Drop for Directory<'a, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES> where - D: crate::BlockDevice, - T: crate::TimeSource, + D: BlockDevice, + T: TimeSource, { fn drop(&mut self) { _ = self.volume_mgr.close_dir(self.raw_directory) @@ -251,8 +256,8 @@ where impl<'a, D, T, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize> core::fmt::Debug for Directory<'a, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES> where - D: crate::BlockDevice, - T: crate::TimeSource, + D: BlockDevice, + T: TimeSource, { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { write!(f, "Directory({})", self.raw_directory.0 .0) @@ -263,8 +268,8 @@ where impl<'a, D, T, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize> defmt::Format for Directory<'a, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES> where - D: crate::BlockDevice, - T: crate::TimeSource, + D: BlockDevice, + T: TimeSource, { fn format(&self, fmt: defmt::Formatter) { defmt::write!(fmt, "Directory({})", self.raw_directory.0 .0) diff --git a/src/filesystem/filename.rs b/src/inner/filesystem/filename.rs similarity index 99% rename from src/filesystem/filename.rs rename to src/inner/filesystem/filename.rs index 31d28548..1e9a0705 100644 --- a/src/filesystem/filename.rs +++ b/src/inner/filesystem/filename.rs @@ -1,6 +1,6 @@ //! Filename related types -use crate::fat::VolumeName; +use super::super::fat::VolumeName; use crate::trace; /// Various filename related errors that can occur. diff --git a/src/filesystem/files.rs b/src/inner/filesystem/files.rs similarity index 85% rename from src/filesystem/files.rs rename to src/inner/filesystem/files.rs index 870d85df..ff9e0222 100644 --- a/src/filesystem/files.rs +++ b/src/inner/filesystem/files.rs @@ -1,9 +1,10 @@ -use super::TimeSource; -use crate::{ +use super::super::super::{bisync, only_sync}; +use super::super::super::{ErrorType, Read, Seek, SeekFrom, Write}; +use super::super::{ filesystem::{ClusterId, DirEntry, Handle}, BlockDevice, Error, RawVolume, VolumeManager, }; -use embedded_io::{ErrorType, Read, Seek, SeekFrom, Write}; +use super::TimeSource; /// A handle for an open file on disk. /// @@ -12,7 +13,7 @@ use embedded_io::{ErrorType, Read, Seek, SeekFrom, Write}; /// Additionally, the VolumeManager will think you still have the file open if /// you just drop it, and it won't let you open the file again. /// -/// Instead you must pass it to [`crate::VolumeManager::close_file`] to close it +/// Instead you must pass it to [`VolumeManager::close_file`] to close it /// cleanly. /// /// If you want your files to close themselves on drop, create your own File @@ -32,8 +33,8 @@ impl RawFile { volume_mgr: &VolumeManager, ) -> File where - D: crate::BlockDevice, - T: crate::TimeSource, + D: BlockDevice, + T: TimeSource, { File::new(self, volume_mgr) } @@ -49,18 +50,19 @@ impl RawFile { /// the [`File::close`] method. pub struct File<'a, D, T, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize> where - D: crate::BlockDevice, - T: crate::TimeSource, + D: BlockDevice, + T: TimeSource, { raw_file: RawFile, volume_mgr: &'a VolumeManager, } +#[bisync] impl<'a, D, T, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize> File<'a, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES> where - D: crate::BlockDevice, - T: crate::TimeSource, + D: BlockDevice, + T: TimeSource, { /// Create a new `File` from a `RawFile` pub fn new( @@ -76,13 +78,13 @@ where /// Read from the file /// /// Returns how many bytes were read, or an error. - pub fn read(&self, buffer: &mut [u8]) -> Result> { - self.volume_mgr.read(self.raw_file, buffer) + pub async fn read(&self, buffer: &mut [u8]) -> Result> { + self.volume_mgr.read(self.raw_file, buffer).await } /// Write to the file - pub fn write(&self, buffer: &[u8]) -> Result<(), crate::Error> { - self.volume_mgr.write(self.raw_file, buffer) + pub async fn write(&self, buffer: &[u8]) -> Result<(), Error> { + self.volume_mgr.write(self.raw_file, buffer).await } /// Check if a file is at End Of File. @@ -93,18 +95,18 @@ where } /// Seek a file with an offset from the current position. - pub fn seek_from_current(&self, offset: i32) -> Result<(), crate::Error> { + pub fn seek_from_current(&self, offset: i32) -> Result<(), Error> { self.volume_mgr .file_seek_from_current(self.raw_file, offset) } /// Seek a file with an offset from the start of the file. - pub fn seek_from_start(&self, offset: u32) -> Result<(), crate::Error> { + pub fn seek_from_start(&self, offset: u32) -> Result<(), Error> { self.volume_mgr.file_seek_from_start(self.raw_file, offset) } /// Seek a file with an offset back from the end of the file. - pub fn seek_from_end(&self, offset: u32) -> Result<(), crate::Error> { + pub fn seek_from_end(&self, offset: u32) -> Result<(), Error> { self.volume_mgr.file_seek_from_end(self.raw_file, offset) } @@ -130,26 +132,28 @@ where } /// Flush any written data by updating the directory entry. - pub fn flush(&self) -> Result<(), Error> { - self.volume_mgr.flush_file(self.raw_file) + pub async fn flush(&self) -> Result<(), Error> { + self.volume_mgr.flush_file(self.raw_file).await } /// Consume the `File` handle and close it. The behavior of this is similar /// to using [`core::mem::drop`] or letting the `File` go out of scope, /// except this lets the user handle any errors that may occur in the process, /// whereas when using drop, any errors will be discarded silently. - pub fn close(self) -> Result<(), Error> { - let result = self.volume_mgr.close_file(self.raw_file); + pub async fn close(self) -> Result<(), Error> { + let result = self.volume_mgr.close_file(self.raw_file).await; core::mem::forget(self); result } } +// async drop does not yet exist :( +#[only_sync] impl<'a, D, T, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize> Drop for File<'a, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES> where - D: crate::BlockDevice, - T: crate::TimeSource, + D: BlockDevice, + T: TimeSource, { fn drop(&mut self) { _ = self.volume_mgr.close_file(self.raw_file); @@ -159,8 +163,8 @@ where impl<'a, D, T, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize> core::fmt::Debug for File<'a, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES> where - D: crate::BlockDevice, - T: crate::TimeSource, + D: BlockDevice, + T: TimeSource, { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { write!(f, "File({})", self.raw_file.0 .0) @@ -175,9 +179,10 @@ impl< const MAX_VOLUMES: usize, > ErrorType for File<'_, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES> { - type Error = crate::Error; + type Error = Error; } +#[bisync] impl< D: BlockDevice, T: TimeSource, @@ -186,15 +191,16 @@ impl< const MAX_VOLUMES: usize, > Read for File<'_, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES> { - fn read(&mut self, buf: &mut [u8]) -> Result { + async fn read(&mut self, buf: &mut [u8]) -> Result { if buf.is_empty() { Ok(0) } else { - self.read(buf) + Self::read(self, buf).await } } } +#[bisync] impl< D: BlockDevice, T: TimeSource, @@ -203,20 +209,21 @@ impl< const MAX_VOLUMES: usize, > Write for File<'_, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES> { - fn write(&mut self, buf: &[u8]) -> Result { + async fn write(&mut self, buf: &[u8]) -> Result { if buf.is_empty() { Ok(0) } else { - self.write(buf)?; + Self::write(self, buf).await?; Ok(buf.len()) } } - fn flush(&mut self) -> Result<(), Self::Error> { - Self::flush(self) + async fn flush(&mut self) -> Result<(), Self::Error> { + Self::flush(self).await } } +#[bisync] impl< D: BlockDevice, T: TimeSource, @@ -225,7 +232,7 @@ impl< const MAX_VOLUMES: usize, > Seek for File<'_, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES> { - fn seek(&mut self, pos: SeekFrom) -> Result { + async fn seek(&mut self, pos: SeekFrom) -> Result { match pos { SeekFrom::Start(offset) => { self.seek_from_start(offset.try_into().map_err(|_| Error::InvalidOffset)?)? @@ -245,8 +252,8 @@ impl< impl<'a, D, T, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize> defmt::Format for File<'a, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES> where - D: crate::BlockDevice, - T: crate::TimeSource, + D: BlockDevice, + T: TimeSource, { fn format(&self, fmt: defmt::Formatter) { defmt::write!(fmt, "File({})", self.raw_file.0 .0) diff --git a/src/filesystem/handles.rs b/src/inner/filesystem/handles.rs similarity index 100% rename from src/filesystem/handles.rs rename to src/inner/filesystem/handles.rs diff --git a/src/filesystem/mod.rs b/src/inner/filesystem/mod.rs similarity index 100% rename from src/filesystem/mod.rs rename to src/inner/filesystem/mod.rs diff --git a/src/filesystem/timestamp.rs b/src/inner/filesystem/timestamp.rs similarity index 100% rename from src/filesystem/timestamp.rs rename to src/inner/filesystem/timestamp.rs diff --git a/src/inner/mod.rs b/src/inner/mod.rs new file mode 100644 index 00000000..de1410be --- /dev/null +++ b/src/inner/mod.rs @@ -0,0 +1,356 @@ +// **************************************************************************** +// +// Imports +// +// **************************************************************************** + +#![deny(missing_docs)] +#![allow(async_fn_in_trait)] + +pub mod blockdevice; +pub mod fat; +pub mod filesystem; +pub mod sdcard; + +use core::fmt::Debug; +use embedded_io::ErrorKind; +use filesystem::Handle; + +use super::{bisync, only_sync}; + +#[doc(inline)] +pub use blockdevice::{Block, BlockCache, BlockCount, BlockDevice, BlockIdx}; + +#[doc(inline)] +pub use fat::{FatVolume, VolumeName}; + +#[doc(inline)] +pub use filesystem::{ + Attributes, ClusterId, DirEntry, Directory, File, FilenameError, LfnBuffer, Mode, RawDirectory, + RawFile, ShortFileName, TimeSource, Timestamp, MAX_FILE_SIZE, +}; + +use filesystem::DirectoryInfo; + +#[doc(inline)] +pub use sdcard::Error as SdCardError; + +#[doc(inline)] +pub use sdcard::SdCard; + +mod volume_mgr; +#[doc(inline)] +pub use volume_mgr::VolumeManager; + +// **************************************************************************** +// +// Public Types +// +// **************************************************************************** + +/// All the ways the functions in this crate can fail. +#[cfg_attr(feature = "defmt-log", derive(defmt::Format))] +#[derive(Debug, Clone)] +pub enum Error +where + E: core::fmt::Debug, +{ + /// The underlying block device threw an error. + DeviceError(E), + /// The filesystem is badly formatted (or this code is buggy). + FormatError(&'static str), + /// The given `VolumeIdx` was bad, + NoSuchVolume, + /// The given filename was bad + FilenameError(FilenameError), + /// Out of memory opening volumes + TooManyOpenVolumes, + /// Out of memory opening directories + TooManyOpenDirs, + /// Out of memory opening files + TooManyOpenFiles, + /// Bad handle given + BadHandle, + /// That file or directory doesn't exist + NotFound, + /// You can't open a file twice or delete an open file + FileAlreadyOpen, + /// You can't open a directory twice + DirAlreadyOpen, + /// You can't open a directory as a file + OpenedDirAsFile, + /// You can't open a file as a directory + OpenedFileAsDir, + /// You can't delete a directory as a file + DeleteDirAsFile, + /// You can't close a volume with open files or directories + VolumeStillInUse, + /// You can't open a volume twice + VolumeAlreadyOpen, + /// We can't do that yet + Unsupported, + /// Tried to read beyond end of file + EndOfFile, + /// Found a bad cluster + BadCluster, + /// Error while converting types + ConversionError, + /// The device does not have enough space for the operation + NotEnoughSpace, + /// Cluster was not properly allocated by the library + AllocationError, + /// Jumped to free space during FAT traversing + UnterminatedFatChain, + /// Tried to open Read-Only file with write mode + ReadOnly, + /// Tried to create an existing file + FileAlreadyExists, + /// Bad block size - only 512 byte blocks supported + BadBlockSize(u16), + /// Bad offset given when seeking + InvalidOffset, + /// Disk is full + DiskFull, + /// A directory with that name already exists + DirAlreadyExists, + /// The filesystem tried to gain a lock whilst already locked. + /// + /// This is either a bug in the filesystem, or you tried to access the + /// filesystem API from inside a directory iterator (that isn't allowed). + LockError, +} + +impl embedded_io::Error for Error { + fn kind(&self) -> ErrorKind { + match self { + Error::DeviceError(_) + | Error::FormatError(_) + | Error::FileAlreadyOpen + | Error::DirAlreadyOpen + | Error::VolumeStillInUse + | Error::VolumeAlreadyOpen + | Error::EndOfFile + | Error::DiskFull + | Error::NotEnoughSpace + | Error::AllocationError + | Error::LockError => ErrorKind::Other, + Error::NoSuchVolume + | Error::FilenameError(_) + | Error::BadHandle + | Error::InvalidOffset => ErrorKind::InvalidInput, + Error::TooManyOpenVolumes | Error::TooManyOpenDirs | Error::TooManyOpenFiles => { + ErrorKind::OutOfMemory + } + Error::NotFound => ErrorKind::NotFound, + Error::OpenedDirAsFile + | Error::OpenedFileAsDir + | Error::DeleteDirAsFile + | Error::BadCluster + | Error::ConversionError + | Error::UnterminatedFatChain => ErrorKind::InvalidData, + Error::Unsupported | Error::BadBlockSize(_) => ErrorKind::Unsupported, + Error::ReadOnly => ErrorKind::PermissionDenied, + Error::FileAlreadyExists | Error::DirAlreadyExists => ErrorKind::AlreadyExists, + } + } +} + +impl From for Error +where + E: core::fmt::Debug, +{ + fn from(value: E) -> Error { + Error::DeviceError(value) + } +} + +/// A handle to a volume. +/// +/// A volume is a partition with a filesystem within it. +/// +/// Do NOT drop this object! It doesn't hold a reference to the Volume Manager +/// it was created from and the VolumeManager will think you still have the +/// volume open if you just drop it, and it won't let you open the file again. +/// +/// Instead you must pass it to [`VolumeManager::close_volume`] to close +/// it cleanly. +#[cfg_attr(feature = "defmt-log", derive(defmt::Format))] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct RawVolume(Handle); + +impl RawVolume { + /// Convert a raw volume into a droppable [`Volume`] + pub fn to_volume< + D, + T, + const MAX_DIRS: usize, + const MAX_FILES: usize, + const MAX_VOLUMES: usize, + >( + self, + volume_mgr: &VolumeManager, + ) -> Volume + where + D: BlockDevice, + T: TimeSource, + { + Volume::new(self, volume_mgr) + } +} + +/// A handle for an open volume on disk, which closes on drop. +/// +/// In contrast to a `RawVolume`, a `Volume` holds a mutable reference to its +/// parent `VolumeManager`, which restricts which operations you can perform. +/// +/// If you drop a value of this type, it closes the volume automatically, but +/// any error that may occur will be ignored. To handle potential errors, use +/// the [`Volume::close`] method. +pub struct Volume<'a, D, T, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize> +where + D: BlockDevice, + T: TimeSource, +{ + raw_volume: RawVolume, + volume_mgr: &'a VolumeManager, +} + +#[bisync] +impl<'a, D, T, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize> + Volume<'a, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES> +where + D: BlockDevice, + T: TimeSource, +{ + /// Create a new `Volume` from a `RawVolume` + pub fn new( + raw_volume: RawVolume, + volume_mgr: &'a VolumeManager, + ) -> Volume<'a, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES> { + Volume { + raw_volume, + volume_mgr, + } + } + + /// Open the volume's root directory. + /// + /// You can then read the directory entries with `iterate_dir`, or you can + /// use `open_file_in_dir`. + pub fn open_root_dir( + &self, + ) -> Result, Error> { + let d = self.volume_mgr.open_root_dir(self.raw_volume)?; + Ok(d.to_directory(self.volume_mgr)) + } + + /// Convert back to a raw volume + pub fn to_raw_volume(self) -> RawVolume { + let v = self.raw_volume; + core::mem::forget(self); + v + } + + /// Consume the `Volume` handle and close it. The behavior of this is similar + /// to using [`core::mem::drop`] or letting the `Volume` go out of scope, + /// except this lets the user handle any errors that may occur in the process, + /// whereas when using drop, any errors will be discarded silently. + pub async fn close(self) -> Result<(), Error> { + let result = self.volume_mgr.close_volume(self.raw_volume).await; + core::mem::forget(self); + result + } +} + +// async drop does not yet exist :( +#[only_sync] +impl<'a, D, T, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize> Drop + for Volume<'a, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES> +where + D: BlockDevice, + T: TimeSource, +{ + fn drop(&mut self) { + _ = self.volume_mgr.close_volume(self.raw_volume) + } +} + +impl<'a, D, T, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize> + core::fmt::Debug for Volume<'a, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES> +where + D: BlockDevice, + T: TimeSource, +{ + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "Volume({})", self.raw_volume.0 .0) + } +} + +#[cfg(feature = "defmt-log")] +impl<'a, D, T, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize> + defmt::Format for Volume<'a, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES> +where + D: BlockDevice, + T: TimeSource, +{ + fn format(&self, fmt: defmt::Formatter) { + defmt::write!(fmt, "Volume({})", self.raw_volume.0 .0) + } +} + +/// Internal information about a Volume +#[cfg_attr(feature = "defmt-log", derive(defmt::Format))] +#[derive(Debug, PartialEq, Eq)] +pub(crate) struct VolumeInfo { + /// Handle for this volume. + raw_volume: RawVolume, + /// Which volume (i.e. partition) we opened on the disk + idx: VolumeIdx, + /// What kind of volume this is + volume_type: VolumeType, +} + +/// This enum holds the data for the various different types of filesystems we +/// support. +#[cfg_attr(feature = "defmt-log", derive(defmt::Format))] +#[derive(Debug, PartialEq, Eq)] +pub enum VolumeType { + /// FAT16/FAT32 formatted volumes. + Fat(FatVolume), +} + +/// A number which identifies a volume (or partition) on a disk. +/// +/// `VolumeIdx(0)` is the first primary partition on an MBR partitioned disk. +#[cfg_attr(feature = "defmt-log", derive(defmt::Format))] +#[derive(Debug, PartialEq, Eq, Copy, Clone)] +pub struct VolumeIdx(pub usize); + +/// Marker for a FAT32 partition. Sometimes also use for FAT16 formatted +/// partitions. +const PARTITION_ID_FAT32_LBA: u8 = 0x0C; +/// Marker for a FAT16 partition with LBA. Seen on a Raspberry Pi SD card. +const PARTITION_ID_FAT16_LBA: u8 = 0x0E; +/// Marker for a FAT16 partition. Seen on a card formatted with the official +/// SD-Card formatter. +const PARTITION_ID_FAT16: u8 = 0x06; +/// Marker for a FAT16 partition smaller than 32MB. Seen on the wowki simulated +/// microsd card +const PARTITION_ID_FAT16_SMALL: u8 = 0x04; +/// Marker for a FAT32 partition. What Macosx disk utility (and also SD-Card formatter?) +/// use. +const PARTITION_ID_FAT32_CHS_LBA: u8 = 0x0B; + +// **************************************************************************** +// +// Unit Tests +// +// **************************************************************************** + +// None + +// **************************************************************************** +// +// End Of File +// +// **************************************************************************** diff --git a/src/sdcard/mod.rs b/src/inner/sdcard/mod.rs similarity index 76% rename from src/sdcard/mod.rs rename to src/inner/sdcard/mod.rs index 553791f9..0985ba42 100644 --- a/src/sdcard/mod.rs +++ b/src/inner/sdcard/mod.rs @@ -5,7 +5,9 @@ pub mod proto; -use crate::{trace, Block, BlockCount, BlockDevice, BlockIdx}; +use super::super::{bisync, DelayNs, SpiDevice}; + +use super::blockdevice::{Block, BlockCount, BlockDevice, BlockIdx}; use core::cell::RefCell; use proto::*; @@ -13,7 +15,7 @@ use proto::*; // Imports // **************************************************************************** -use crate::{debug, warn}; +use crate::{debug, trace, warn}; // **************************************************************************** // Types and Implementations @@ -37,16 +39,17 @@ use crate::{debug, warn}; /// [`SpiDevice`]: embedded_hal::spi::SpiDevice pub struct SdCard where - SPI: embedded_hal::spi::SpiDevice, - DELAYER: embedded_hal::delay::DelayNs, + SPI: SpiDevice, + DELAYER: DelayNs, { inner: RefCell>, } +#[bisync] impl SdCard where - SPI: embedded_hal::spi::SpiDevice, - DELAYER: embedded_hal::delay::DelayNs, + SPI: SpiDevice, + DELAYER: DelayNs, { /// Create a new SD/MMC Card driver using a raw SPI interface. /// @@ -98,19 +101,19 @@ where /// Return the usable size of this SD card in bytes. /// /// This will trigger card (re-)initialisation. - pub fn num_bytes(&self) -> Result { + pub async fn num_bytes(&self) -> Result { let mut inner = self.inner.borrow_mut(); - inner.check_init()?; - inner.num_bytes() + inner.check_init().await?; + inner.num_bytes().await } /// Can this card erase single blocks? /// /// This will trigger card (re-)initialisation. - pub fn erase_single_block_enabled(&self) -> Result { + pub async fn erase_single_block_enabled(&self) -> Result { let mut inner = self.inner.borrow_mut(); - inner.check_init()?; - inner.erase_single_block_enabled() + inner.check_init().await?; + inner.erase_single_block_enabled().await } /// Mark the card as requiring a reset. @@ -124,9 +127,9 @@ where /// Get the card type. /// /// This will trigger card (re-)initialisation. - pub fn get_card_type(&self) -> Option { + pub async fn get_card_type(&self) -> Option { let mut inner = self.inner.borrow_mut(); - inner.check_init().ok()?; + inner.check_init().await.ok()?; inner.card_type } @@ -152,40 +155,41 @@ where } } +#[bisync] impl BlockDevice for SdCard where - SPI: embedded_hal::spi::SpiDevice, - DELAYER: embedded_hal::delay::DelayNs, + SPI: SpiDevice, + DELAYER: DelayNs, { type Error = Error; /// Read one or more blocks, starting at the given block index. /// /// This will trigger card (re-)initialisation. - 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> { let mut inner = self.inner.borrow_mut(); debug!("Read {} blocks @ {}", blocks.len(), start_block_idx.0,); - inner.check_init()?; - inner.read(blocks, start_block_idx) + inner.check_init().await?; + inner.read(blocks, start_block_idx).await } /// Write one or more blocks, starting at the given block index. /// /// This will trigger card (re-)initialisation. - 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> { let mut inner = self.inner.borrow_mut(); debug!("Writing {} blocks @ {}", blocks.len(), start_block_idx.0); - inner.check_init()?; - inner.write(blocks, start_block_idx) + inner.check_init().await?; + inner.write(blocks, start_block_idx).await } /// Determine how many blocks this device can hold. /// /// This will trigger card (re-)initialisation. - fn num_blocks(&self) -> Result { + async fn num_blocks(&self) -> Result { let mut inner = self.inner.borrow_mut(); - inner.check_init()?; - inner.num_blocks() + inner.check_init().await?; + inner.num_blocks().await } } @@ -194,8 +198,8 @@ where /// All the APIs required `&mut self`. struct SdCardInner where - SPI: embedded_hal::spi::SpiDevice, - DELAYER: embedded_hal::delay::DelayNs, + SPI: SpiDevice, + DELAYER: DelayNs, { spi: SPI, delayer: DELAYER, @@ -203,13 +207,14 @@ where options: AcquireOpts, } +#[bisync] impl SdCardInner where - SPI: embedded_hal::spi::SpiDevice, - DELAYER: embedded_hal::delay::DelayNs, + SPI: SpiDevice, + DELAYER: DelayNs, { /// Read one or more blocks, starting at the given block index. - fn read(&mut self, blocks: &mut [Block], start_block_idx: BlockIdx) -> Result<(), Error> { + async fn read(&mut self, blocks: &mut [Block], start_block_idx: BlockIdx) -> Result<(), Error> { let start_idx = match self.card_type { Some(CardType::SD1 | CardType::SD2) => start_block_idx.0 * 512, Some(CardType::SDHC) => start_block_idx.0, @@ -218,22 +223,22 @@ where if blocks.len() == 1 { // Start a single-block read - self.card_command(CMD17, start_idx)?; - self.read_data(&mut blocks[0].contents)?; + self.card_command(CMD17, start_idx).await?; + self.read_data(&mut blocks[0].contents).await?; } else { // Start a multi-block read - self.card_command(CMD18, start_idx)?; + self.card_command(CMD18, start_idx).await?; for block in blocks.iter_mut() { - self.read_data(&mut block.contents)?; + self.read_data(&mut block.contents).await?; } // Stop the read - self.card_command(CMD12, 0)?; + self.card_command(CMD12, 0).await?; } Ok(()) } /// Write one or more blocks, starting at the given block index. - fn write(&mut self, blocks: &[Block], start_block_idx: BlockIdx) -> Result<(), Error> { + async fn write(&mut self, blocks: &[Block], start_block_idx: BlockIdx) -> Result<(), Error> { let start_idx = match self.card_type { Some(CardType::SD1 | CardType::SD2) => start_block_idx.0 * 512, Some(CardType::SDHC) => start_block_idx.0, @@ -241,39 +246,39 @@ where }; if blocks.len() == 1 { // Start a single-block write - self.card_command(CMD24, start_idx)?; - self.write_data(DATA_START_BLOCK, &blocks[0].contents)?; - self.wait_not_busy(Delay::new_write())?; - if self.card_command(CMD13, 0)? != 0x00 { + self.card_command(CMD24, start_idx).await?; + self.write_data(DATA_START_BLOCK, &blocks[0].contents).await?; + self.wait_not_busy(Delay::new_write()).await?; + if self.card_command(CMD13, 0).await? != 0x00 { return Err(Error::WriteError); } - if self.read_byte()? != 0x00 { + if self.read_byte().await? != 0x00 { return Err(Error::WriteError); } } else { // > It is recommended using this command preceding CMD25, some of the cards will be faster for Multiple // > Write Blocks operation. Note that the host should send ACMD23 just before WRITE command if the host // > wants to use the pre-erased feature - self.card_acmd(ACMD23, blocks.len() as u32)?; + self.card_acmd(ACMD23, blocks.len() as u32).await?; // wait for card to be ready before sending the next command - self.wait_not_busy(Delay::new_write())?; + self.wait_not_busy(Delay::new_write()).await?; // Start a multi-block write - self.card_command(CMD25, start_idx)?; + self.card_command(CMD25, start_idx).await?; for block in blocks.iter() { - self.wait_not_busy(Delay::new_write())?; - self.write_data(WRITE_MULTIPLE_TOKEN, &block.contents)?; + self.wait_not_busy(Delay::new_write()).await?; + self.write_data(WRITE_MULTIPLE_TOKEN, &block.contents).await?; } // Stop the write - self.wait_not_busy(Delay::new_write())?; - self.write_byte(STOP_TRAN_TOKEN)?; + self.wait_not_busy(Delay::new_write()).await?; + self.write_byte(STOP_TRAN_TOKEN).await?; } Ok(()) } /// Determine how many blocks this device can hold. - fn num_blocks(&mut self) -> Result { - let csd = self.read_csd()?; + async fn num_blocks(&mut self) -> Result { + let csd = self.read_csd().await?; debug!("CSD: {:?}", csd); let num_blocks = match csd { Csd::V1(ref contents) => contents.card_capacity_blocks(), @@ -283,8 +288,8 @@ where } /// Return the usable size of this SD card in bytes. - fn num_bytes(&mut self) -> Result { - let csd = self.read_csd()?; + async fn num_bytes(&mut self) -> Result { + let csd = self.read_csd().await?; debug!("CSD: {:?}", csd); match csd { Csd::V1(ref contents) => Ok(contents.card_capacity_bytes()), @@ -293,8 +298,8 @@ where } /// Can this card erase single blocks? - pub fn erase_single_block_enabled(&mut self) -> Result { - let csd = self.read_csd()?; + pub async fn erase_single_block_enabled(&mut self) -> Result { + let csd = self.read_csd().await?; match csd { Csd::V1(ref contents) => Ok(contents.erase_single_block_enabled()), Csd::V2(ref contents) => Ok(contents.erase_single_block_enabled()), @@ -302,22 +307,22 @@ where } /// Read the 'card specific data' block. - fn read_csd(&mut self) -> Result { + async fn read_csd(&mut self) -> Result { match self.card_type { Some(CardType::SD1) => { let mut csd = CsdV1::new(); - if self.card_command(CMD9, 0)? != 0 { + if self.card_command(CMD9, 0).await? != 0 { return Err(Error::RegisterReadError); } - self.read_data(&mut csd.data)?; + self.read_data(&mut csd.data).await?; Ok(Csd::V1(csd)) } Some(CardType::SD2 | CardType::SDHC) => { let mut csd = CsdV2::new(); - if self.card_command(CMD9, 0)? != 0 { + if self.card_command(CMD9, 0).await? != 0 { return Err(Error::RegisterReadError); } - self.read_data(&mut csd.data)?; + self.read_data(&mut csd.data).await?; Ok(Csd::V2(csd)) } None => Err(Error::CardNotFound), @@ -327,27 +332,27 @@ where /// Read an arbitrary number of bytes from the card using the SD Card /// protocol and an optional CRC. Always fills the given buffer, so make /// sure it's the right size. - fn read_data(&mut self, buffer: &mut [u8]) -> Result<(), Error> { + async fn read_data(&mut self, buffer: &mut [u8]) -> Result<(), Error> { // Get first non-FF byte. let mut delay = Delay::new_read(); let status = loop { - let s = self.read_byte()?; + let s = self.read_byte().await?; if s != 0xFF { break s; } - delay.delay(&mut self.delayer, Error::TimeoutReadBuffer)?; + delay.delay(&mut self.delayer, Error::TimeoutReadBuffer).await?; }; if status != DATA_START_BLOCK { return Err(Error::ReadError); } buffer.fill(0xFF); - self.transfer_bytes(buffer)?; + self.transfer_bytes(buffer).await?; // These two bytes are always sent. They are either a valid CRC, or // junk, depending on whether CRC mode was enabled. let mut crc_bytes = [0xFF; 2]; - self.transfer_bytes(&mut crc_bytes)?; + self.transfer_bytes(&mut crc_bytes).await?; if self.options.use_crc { let crc = u16::from_be_bytes(crc_bytes); let calc_crc = crc16(buffer); @@ -361,9 +366,9 @@ where /// Write an arbitrary number of bytes to the card using the SD protocol and /// an optional CRC. - fn write_data(&mut self, token: u8, buffer: &[u8]) -> Result<(), Error> { - self.write_byte(token)?; - self.write_bytes(buffer)?; + async fn write_data(&mut self, token: u8, buffer: &[u8]) -> Result<(), Error> { + self.write_byte(token).await?; + self.write_bytes(buffer).await?; let crc_bytes = if self.options.use_crc { crc16(buffer).to_be_bytes() } else { @@ -371,9 +376,9 @@ where }; // These two bytes are always sent. They are either a valid CRC, or // junk, depending on whether CRC mode was enabled. - self.write_bytes(&crc_bytes)?; + self.write_bytes(&crc_bytes).await?; - let status = self.read_byte()?; + let status = self.read_byte().await?; if (status & DATA_RES_MASK) != DATA_RES_ACCEPTED { Err(Error::WriteError) } else { @@ -382,35 +387,35 @@ where } /// Check the card is initialised. - fn check_init(&mut self) -> Result<(), Error> { + async fn check_init(&mut self) -> Result<(), Error> { if self.card_type.is_none() { // If we don't know what the card type is, try and initialise the // card. This will tell us what type of card it is. - self.acquire() + self.acquire().await } else { Ok(()) } } /// Initializes the card into a known state (or at least tries to). - fn acquire(&mut self) -> Result<(), Error> { + async fn acquire(&mut self) -> Result<(), Error> { debug!("acquiring card with opts: {:?}", self.options); - let f = |s: &mut Self| { + let f = async { // Assume it hasn't worked let mut card_type; trace!("Reset card.."); // Enter SPI mode. - let mut delay = Delay::new(s.options.acquire_retries); + let mut delay = Delay::new(self.options.acquire_retries); for _attempts in 1.. { trace!("Enter SPI mode, attempt: {}..", _attempts); - match s.card_command(CMD0, 0) { + match self.card_command(CMD0, 0).await { Err(Error::TimeoutCommand(0)) => { // Try again? warn!("Timed out, trying again.."); // Try flushing the card as done here: https://github.com/greiman/SdFat/blob/master/src/SdCard/SdSpiCard.cpp#L170, // https://github.com/rust-embedded-community/embedded-sdmmc-rs/pull/65#issuecomment-1270709448 for _ in 0..0xFF { - s.write_byte(0xFF)?; + self.write_byte(0xFF).await?; } } Err(e) => { @@ -425,67 +430,67 @@ where } } - delay.delay(&mut s.delayer, Error::CardNotFound)?; + delay.delay(&mut self.delayer, Error::CardNotFound).await?; } // Enable CRC - debug!("Enable CRC: {}", s.options.use_crc); + debug!("Enable CRC: {}", self.options.use_crc); // "The SPI interface is initialized in the CRC OFF mode in default" // -- SD Part 1 Physical Layer Specification v9.00, Section 7.2.2 Bus Transfer Protection - if s.options.use_crc && s.card_command(CMD59, 1)? != R1_IDLE_STATE { + if self.options.use_crc && self.card_command(CMD59, 1).await? != R1_IDLE_STATE { return Err(Error::CantEnableCRC); } // Check card version let mut delay = Delay::new_command(); let arg = loop { - if s.card_command(CMD8, 0x1AA)? == (R1_ILLEGAL_COMMAND | R1_IDLE_STATE) { + if self.card_command(CMD8, 0x1AA).await? == (R1_ILLEGAL_COMMAND | R1_IDLE_STATE) { card_type = CardType::SD1; break 0; } let mut buffer = [0xFF; 4]; - s.transfer_bytes(&mut buffer)?; + self.transfer_bytes(&mut buffer).await?; let status = buffer[3]; if status == 0xAA { card_type = CardType::SD2; break 0x4000_0000; } - delay.delay(&mut s.delayer, Error::TimeoutCommand(CMD8))?; + delay.delay(&mut self.delayer, Error::TimeoutCommand(CMD8)).await?; }; let mut delay = Delay::new_command(); - while s.card_acmd(ACMD41, arg)? != R1_READY_STATE { - delay.delay(&mut s.delayer, Error::TimeoutACommand(ACMD41))?; + while self.card_acmd(ACMD41, arg).await? != R1_READY_STATE { + delay.delay(&mut self.delayer, Error::TimeoutACommand(ACMD41)).await?; } if card_type == CardType::SD2 { - if s.card_command(CMD58, 0)? != 0 { + if self.card_command(CMD58, 0).await? != 0 { return Err(Error::Cmd58Error); } let mut buffer = [0xFF; 4]; - s.transfer_bytes(&mut buffer)?; + self.transfer_bytes(&mut buffer).await?; if (buffer[0] & 0xC0) == 0xC0 { card_type = CardType::SDHC; } // Ignore the other three bytes } debug!("Card version: {:?}", card_type); - s.card_type = Some(card_type); + self.card_type = Some(card_type); Ok(()) }; - let result = f(self); + let result = f.await; let _ = self.read_byte(); result } /// Perform an application-specific command. - fn card_acmd(&mut self, command: u8, arg: u32) -> Result { - self.card_command(CMD55, 0)?; - self.card_command(command, arg) + async fn card_acmd(&mut self, command: u8, arg: u32) -> Result { + self.card_command(CMD55, 0).await?; + self.card_command(command, arg).await } /// Perform a command. - fn card_command(&mut self, command: u8, arg: u32) -> Result { + async fn card_command(&mut self, command: u8, arg: u32) -> Result { if command != CMD0 && command != CMD12 { - self.wait_not_busy(Delay::new_command())?; + self.wait_not_busy(Delay::new_command()).await?; } let mut buf = [ @@ -498,66 +503,67 @@ where ]; buf[5] = crc7(&buf[0..5]); - self.write_bytes(&buf)?; + self.write_bytes(&buf).await?; // skip stuff byte for stop read if command == CMD12 { - let _result = self.read_byte()?; + let _result = self.read_byte().await?; } let mut delay = Delay::new_command(); loop { - let result = self.read_byte()?; + let result = self.read_byte().await?; if (result & 0x80) == ERROR_OK { return Ok(result); } - delay.delay(&mut self.delayer, Error::TimeoutCommand(command))?; + delay.delay(&mut self.delayer, Error::TimeoutCommand(command)).await?; } } /// Receive a byte from the SPI bus by clocking out an 0xFF byte. - fn read_byte(&mut self) -> Result { - self.transfer_byte(0xFF) + async fn read_byte(&mut self) -> Result { + self.transfer_byte(0xFF).await } /// Send a byte over the SPI bus and ignore what comes back. - fn write_byte(&mut self, out: u8) -> Result<(), Error> { - let _ = self.transfer_byte(out)?; + async fn write_byte(&mut self, out: u8) -> Result<(), Error> { + let _ = self.transfer_byte(out).await?; Ok(()) } /// Send one byte and receive one byte over the SPI bus. - fn transfer_byte(&mut self, out: u8) -> Result { + async fn transfer_byte(&mut self, out: u8) -> Result { let mut read_buf = [0u8; 1]; self.spi - .transfer(&mut read_buf, &[out]) + .transfer(&mut read_buf, &[out]).await .map_err(|_| Error::Transport)?; Ok(read_buf[0]) } /// Send multiple bytes and ignore what comes back over the SPI bus. - fn write_bytes(&mut self, out: &[u8]) -> Result<(), Error> { - self.spi.write(out).map_err(|_e| Error::Transport)?; + async fn write_bytes(&mut self, out: &[u8]) -> Result<(), Error> { + self.spi.write(out).await.map_err(|_e| Error::Transport)?; Ok(()) } /// Send multiple bytes and replace them with what comes back over the SPI bus. - fn transfer_bytes(&mut self, in_out: &mut [u8]) -> Result<(), Error> { + async fn transfer_bytes(&mut self, in_out: &mut [u8]) -> Result<(), Error> { self.spi .transfer_in_place(in_out) + .await .map_err(|_e| Error::Transport)?; Ok(()) } /// Spin until the card returns 0xFF, or we spin too many times and /// timeout. - fn wait_not_busy(&mut self, mut delay: Delay) -> Result<(), Error> { + async fn wait_not_busy(&mut self, mut delay: Delay) -> Result<(), Error> { loop { - let s = self.read_byte()?; + let s = self.read_byte().await?; if s == 0xFF { break; } - delay.delay(&mut self.delayer, Error::TimeoutWaitNotBusy)?; + delay.delay(&mut self.delayer, Error::TimeoutWaitNotBusy).await?; } Ok(()) } @@ -650,6 +656,7 @@ struct Delay { retries_left: u32, } +#[bisync] impl Delay { /// The default number of retries for a read operation. /// @@ -699,14 +706,14 @@ impl Delay { /// Checks the retry counter first, and if we hit the max retry limit, the /// value `err` is returned. Otherwise we wait for 10us and then return /// `Ok(())`. - fn delay(&mut self, delayer: &mut T, err: Error) -> Result<(), Error> + async fn delay(&mut self, delayer: &mut T, err: Error) -> Result<(), Error> where - T: embedded_hal::delay::DelayNs, + T: DelayNs, { if self.retries_left == 0 { Err(err) } else { - delayer.delay_us(10); + delayer.delay_us(10).await; self.retries_left -= 1; Ok(()) } diff --git a/src/sdcard/proto.rs b/src/inner/sdcard/proto.rs similarity index 100% rename from src/sdcard/proto.rs rename to src/inner/sdcard/proto.rs diff --git a/src/volume_mgr.rs b/src/inner/volume_mgr.rs similarity index 96% rename from src/volume_mgr.rs rename to src/inner/volume_mgr.rs index de34a911..bb660cd5 100644 --- a/src/volume_mgr.rs +++ b/src/inner/volume_mgr.rs @@ -2,6 +2,8 @@ //! //! The volume manager handles partitions and open files on a block device. +use super::super::bisync; + use core::cell::RefCell; use core::convert::TryFrom; use core::ops::DerefMut; @@ -9,17 +11,19 @@ use core::ops::DerefMut; use byteorder::{ByteOrder, LittleEndian}; use heapless::Vec; -use crate::{ - debug, fat, +use super::{ + fat, filesystem::{ Attributes, ClusterId, DirEntry, DirectoryInfo, FileInfo, HandleGenerator, LfnBuffer, Mode, RawDirectory, RawFile, TimeSource, ToShortFileName, MAX_FILE_SIZE, }, - trace, Block, BlockCache, BlockCount, BlockDevice, BlockIdx, Error, RawVolume, ShortFileName, - Volume, VolumeIdx, VolumeInfo, VolumeType, PARTITION_ID_FAT16, PARTITION_ID_FAT16_LBA, + Block, BlockCache, BlockCount, BlockDevice, BlockIdx, Error, RawVolume, ShortFileName, Volume, + VolumeIdx, VolumeInfo, VolumeName, VolumeType, PARTITION_ID_FAT16, PARTITION_ID_FAT16_LBA, PARTITION_ID_FAT16_SMALL, PARTITION_ID_FAT32_CHS_LBA, PARTITION_ID_FAT32_LBA, }; +use crate::{debug, trace}; + /// Wraps a block device and gives access to the FAT-formatted volumes within /// it. /// @@ -61,6 +65,7 @@ where } } +#[bisync] impl VolumeManager where @@ -107,11 +112,11 @@ where /// /// We do not support GUID Partition Table disks. Nor do we support any /// concept of drive letters - that is for a higher layer to handle. - pub fn open_volume( + pub async fn open_volume( &self, volume_idx: VolumeIdx, ) -> Result, Error> { - let v = self.open_raw_volume(volume_idx)?; + let v = self.open_raw_volume(volume_idx).await?; Ok(v.to_volume(self)) } @@ -122,7 +127,7 @@ where /// /// This function gives you a `RawVolume` and you must close the volume by /// calling `VolumeManager::close_volume`. - pub fn open_raw_volume(&self, volume_idx: VolumeIdx) -> Result> { + pub async fn open_raw_volume(&self, volume_idx: VolumeIdx) -> Result> { const PARTITION1_START: usize = 446; const PARTITION2_START: usize = PARTITION1_START + PARTITION_INFO_LENGTH; const PARTITION3_START: usize = PARTITION2_START + PARTITION_INFO_LENGTH; @@ -151,7 +156,7 @@ where trace!("Reading partition table"); let block = data .block_cache - .read(BlockIdx(0)) + .read(BlockIdx(0)).await .map_err(Error::DeviceError)?; // We only support Master Boot Record (MBR) partitioned cards, not // GUID Partition Table (GPT) @@ -197,7 +202,7 @@ where | PARTITION_ID_FAT16_LBA | PARTITION_ID_FAT16 | PARTITION_ID_FAT16_SMALL => { - let volume = fat::parse_volume(&mut data.block_cache, lba_start, num_blocks)?; + let volume = fat::parse_volume(&mut data.block_cache, lba_start, num_blocks).await?; let id = RawVolume(data.id_generator.generate()); let info = VolumeInfo { raw_volume: id, @@ -243,7 +248,7 @@ where /// You can then read the directory entries with `iterate_dir` and `open_file_in_dir`. /// /// Passing "." as the name results in opening the `parent_dir` a second time. - pub fn open_dir( + pub async fn open_dir( &self, parent_dir: RawDirectory, name: N, @@ -288,7 +293,7 @@ where &mut data.block_cache, &data.open_dirs[parent_dir_idx], &short_file_name, - )?, + ).await?, }; debug!("Found dir entry: {:?}", dir_entry); @@ -333,7 +338,7 @@ where /// Close a volume /// /// You can't close it if there are any files or directories open on it. - pub fn close_volume(&self, volume: RawVolume) -> Result<(), Error> { + pub async fn close_volume(&self, volume: RawVolume) -> Result<(), Error> { let mut data = self.data.try_borrow_mut().map_err(|_| Error::LockError)?; let data = data.deref_mut(); @@ -353,7 +358,7 @@ where match &mut data.open_volumes[volume_idx].volume_type { VolumeType::Fat(fat) => { - fat.update_info_sector(&mut data.block_cache)?; + fat.update_info_sector(&mut data.block_cache).await?; } } @@ -363,7 +368,7 @@ where } /// Look in a directory for a named file. - pub fn find_directory_entry( + pub async fn find_directory_entry( &self, directory: RawDirectory, name: N, @@ -383,7 +388,7 @@ where &mut data.block_cache, &data.open_dirs[directory_idx], &sfn, - ) + ).await } } } @@ -399,7 +404,7 @@ where /// object is already locked in order to do the iteration. /// /// - pub fn iterate_dir( + pub async fn iterate_dir( &self, directory: RawDirectory, mut func: F, @@ -423,7 +428,7 @@ where func(de); } }, - ) + ).await } } } @@ -443,7 +448,7 @@ where /// object is already locked in order to do the iteration. /// /// - pub fn iterate_dir_lfn( + pub async fn iterate_dir_lfn( &self, directory: RawDirectory, lfn_buffer: &mut LfnBuffer<'_>, @@ -466,13 +471,13 @@ where lfn_buffer, &data.open_dirs[directory_idx], func, - ) + ).await } } } /// Open a file with the given full path. A file can only be opened once. - pub fn open_file_in_dir( + pub async fn open_file_in_dir( &self, directory: RawDirectory, name: N, @@ -500,7 +505,7 @@ where &mut data.block_cache, &data.open_dirs[directory_idx], &sfn, - ), + ).await, }; let dir_entry = match dir_entry { @@ -547,7 +552,7 @@ where cluster, sfn, att, - )?, + ).await?, }; let file_id = RawFile(data.id_generator.generate()); @@ -627,13 +632,13 @@ where VolumeType::Fat(fat) => fat.truncate_cluster_chain( &mut data.block_cache, file.entry.cluster, - )?, + ).await?, }; file.update_length(0); match &data.open_volumes[volume_idx].volume_type { VolumeType::Fat(fat) => { file.entry.mtime = self.time_source.get_timestamp(); - fat.write_entry_to_disk(&mut data.block_cache, &file.entry)?; + fat.write_entry_to_disk(&mut data.block_cache, &file.entry).await?; } }; @@ -653,7 +658,7 @@ where } /// Delete a closed file with the given filename, if it exists. - pub fn delete_file_in_dir( + pub async fn delete_file_in_dir( &self, directory: RawDirectory, name: N, @@ -670,7 +675,7 @@ where let sfn = name.to_short_filename().map_err(Error::FilenameError)?; let dir_entry = match &data.open_volumes[volume_idx].volume_type { - VolumeType::Fat(fat) => fat.find_directory_entry(&mut data.block_cache, dir_info, &sfn), + VolumeType::Fat(fat) => fat.find_directory_entry(&mut data.block_cache, dir_info, &sfn).await, }?; if dir_entry.attributes.is_directory() { @@ -684,7 +689,7 @@ where let volume_idx = data.get_volume_by_id(dir_info.raw_volume)?; match &data.open_volumes[volume_idx].volume_type { VolumeType::Fat(fat) => { - fat.delete_directory_entry(&mut data.block_cache, dir_info, &sfn)? + fat.delete_directory_entry(&mut data.block_cache, dir_info, &sfn).await? } } @@ -695,10 +700,10 @@ where /// /// Will look in the BPB for a volume label, and if nothing is found, will /// search the root directory for a volume label. - pub fn get_root_volume_label( + pub async fn get_root_volume_label( &self, raw_volume: RawVolume, - ) -> Result, Error> { + ) -> Result, Error> { debug!("Reading volume label for {:?}", raw_volume); // prefer the one in the BPB - it's easier to get let data = self.data.try_borrow().map_err(|_| Error::LockError)?; @@ -725,7 +730,7 @@ where { maybe_volume_name = Some(unsafe { de.name.clone().to_volume_label() }) } - })?; + }).await?; debug!( "Got volume label {:?} for {:?} from root", @@ -736,7 +741,7 @@ where } /// Read from an open file. - pub fn read(&self, file: RawFile, buffer: &mut [u8]) -> Result> { + pub async fn read(&self, file: RawFile, buffer: &mut [u8]) -> Result> { let mut data = self.data.try_borrow_mut().map_err(|_| Error::LockError)?; let data = data.deref_mut(); @@ -755,12 +760,12 @@ where &mut current_cluster, data.open_files[file_idx].entry.cluster, data.open_files[file_idx].current_offset, - )?; + ).await?; data.open_files[file_idx].current_cluster = current_cluster; trace!("Reading file ID {:?}", file); let block = data .block_cache - .read(block_idx) + .read(block_idx).await .map_err(Error::DeviceError)?; let to_copy = block_avail .min(space) @@ -778,7 +783,7 @@ where } /// Write to a open file. - pub fn write(&self, file: RawFile, buffer: &[u8]) -> Result<(), Error> { + pub async fn write(&self, file: RawFile, buffer: &[u8]) -> Result<(), Error> { #[cfg(feature = "defmt-log")] debug!("write(file={:?}, buffer={:x}", file, buffer); @@ -804,7 +809,7 @@ where data.open_files[file_idx].entry.cluster = match data.open_volumes[volume_idx].volume_type { VolumeType::Fat(ref mut fat) => { - fat.alloc_cluster(&mut data.block_cache, None, false)? + fat.alloc_cluster(&mut data.block_cache, None, false).await? } }; debug!( @@ -839,7 +844,7 @@ where &mut current_cluster, data.open_files[file_idx].entry.cluster, current_offset, - ) { + ).await { Ok(vars) => { debug!( "Found block_idx={:?}, block_offset={:?}, block_avail={}", @@ -856,7 +861,7 @@ where &mut data.block_cache, Some(current_cluster.1), false, - ) + ).await .is_err() { return Err(Error::DiskFull); @@ -869,6 +874,7 @@ where data.open_files[file_idx].entry.cluster, data.open_files[file_idx].current_offset, ) + .await .map_err(|_| Error::AllocationError)?; debug!("New offset {:?}", new_offset); new_offset @@ -881,7 +887,7 @@ where let block = if block_offset != 0 { debug!("Reading for partial block write"); data.block_cache - .read_mut(block_idx) + .read_mut(block_idx).await .map_err(Error::DeviceError)? } else { data.block_cache.blank_mut(block_idx) @@ -889,7 +895,7 @@ where block[block_offset..block_offset + to_copy] .copy_from_slice(&buffer[written..written + to_copy]); debug!("Writing block {:?}", block_idx); - data.block_cache.write_back()?; + data.block_cache.write_back().await?; written += to_copy; data.open_files[file_idx].current_cluster = current_cluster; @@ -910,8 +916,8 @@ where } /// Close a file with the given raw file handle. - pub fn close_file(&self, file: RawFile) -> Result<(), Error> { - let flush_result = self.flush_file(file); + pub async fn close_file(&self, file: RawFile) -> Result<(), Error> { + let flush_result = self.flush_file(file).await; let mut data = self.data.try_borrow_mut().map_err(|_| Error::LockError)?; let file_idx = data.get_file_by_id(file)?; data.open_files.swap_remove(file_idx); @@ -919,7 +925,7 @@ where } /// Flush (update the entry) for a file with the given raw file handle. - pub fn flush_file(&self, file: RawFile) -> Result<(), Error> { + pub async fn flush_file(&self, file: RawFile) -> Result<(), Error> { let mut data = self.data.try_borrow_mut().map_err(|_| Error::LockError)?; let data = data.deref_mut(); @@ -930,7 +936,7 @@ where match &mut data.open_volumes[volume_idx].volume_type { VolumeType::Fat(fat) => { debug!("Updating FAT info sector"); - fat.update_info_sector(&mut data.block_cache)?; + fat.update_info_sector(&mut data.block_cache).await?; debug!("Updating dir entry {:?}", data.open_files[file_id].entry); if data.open_files[file_id].entry.size != 0 { // If you have a length, you must have a cluster @@ -939,7 +945,7 @@ where fat.write_entry_to_disk( &mut data.block_cache, &data.open_files[file_id].entry, - )?; + ).await?; } }; } @@ -1014,7 +1020,7 @@ where } /// Create a directory in a given directory. - pub fn make_dir_in_dir( + pub async fn make_dir_in_dir( &self, directory: RawDirectory, name: N, @@ -1046,7 +1052,7 @@ where // Does an entry exist with this name? let maybe_dir_entry = match &volume_info.volume_type { VolumeType::Fat(fat) => { - fat.find_directory_entry(&mut data.block_cache, parent_directory_info, &sfn) + fat.find_directory_entry(&mut data.block_cache, parent_directory_info, &sfn).await } }; @@ -1078,7 +1084,7 @@ where parent_directory_info.cluster, sfn, att, - )?; + ).await?; } }; @@ -1106,6 +1112,7 @@ struct VolumeManagerData< open_files: Vec, } +#[bisync] impl VolumeManagerData where @@ -1171,7 +1178,7 @@ where /// * the index for the block on the disk that contains the data we want, /// * the byte offset into that block for the data we want, and /// * how many bytes remain in that block. - fn find_data_on_disk( + async fn find_data_on_disk( &mut self, volume_idx: usize, start: &mut (u32, ClusterId), @@ -1197,7 +1204,7 @@ where let num_clusters = offset_from_cluster / bytes_per_cluster; for _ in 0..num_clusters { start.1 = match &self.open_volumes[volume_idx].volume_type { - VolumeType::Fat(fat) => fat.next_cluster(&mut self.block_cache, start.1)?, + VolumeType::Fat(fat) => fat.next_cluster(&mut self.block_cache, start.1).await?, }; start.0 += bytes_per_cluster; } @@ -1241,10 +1248,12 @@ fn solve_mode_variant(mode: Mode, dir_entry_is_some: bool) -> Mode { // **************************************************************************** #[cfg(test)] +#[allow(unused_imports)] +#[allow(dead_code)] mod tests { + use super::super::super::only_sync; + use super::super::{filesystem::Handle, FatVolume, Timestamp}; use super::*; - use crate::filesystem::Handle; - use crate::Timestamp; struct DummyBlockDevice; @@ -1269,6 +1278,7 @@ mod tests { } } + #[only_sync] impl BlockDevice for DummyBlockDevice { type Error = Error; @@ -1476,6 +1486,7 @@ mod tests { } } + #[only_sync] #[test] fn partition0() { let c: VolumeManager = @@ -1489,7 +1500,7 @@ mod tests { &VolumeInfo { raw_volume: expected_id, idx: VolumeIdx(0), - volume_type: VolumeType::Fat(crate::FatVolume { + volume_type: VolumeType::Fat(FatVolume { lba_start: BlockIdx(1), num_blocks: BlockCount(0x0011_2233), blocks_per_cluster: 8, diff --git a/src/lib.rs b/src/lib.rs index c6af4e95..ec612944 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,6 +8,12 @@ //! `alloc` or `collections` to keep the memory footprint low. In the first //! instance it is designed for readability and simplicity over performance. //! +//! This crate supports both blocking and asynchronous usage via their respective +//! modules. The APIs within the modules are identical except that the asynchronous +//! module uses `async fn` where applicable. The `blocking` module uses traits from +//! the embedded_hal & embedded_io crates whereas the `asynchronous module uses +//! traits from the embedded_hal_async and embedded_io_async crates. +//! //! ## Using the crate //! //! You will need something that implements the `BlockDevice` trait, which can @@ -17,7 +23,7 @@ //! suitable for reading SD and SDHC cards over SPI. //! //! ```rust -//! use embedded_sdmmc::{Error, Mode, SdCard, SdCardError, TimeSource, VolumeIdx, VolumeManager}; +//! use embedded_sdmmc::blocking::{Error, Mode, SdCard, SdCardError, TimeSource, VolumeIdx, VolumeManager}; //! //! fn example(spi: S, delay: D, ts: T) -> Result<(), Error> //! where @@ -46,7 +52,7 @@ //! For writing files: //! //! ```rust -//! use embedded_sdmmc::{BlockDevice, Directory, Error, Mode, TimeSource}; +//! use embedded_sdmmc::blocking::{BlockDevice, Directory, Error, Mode, TimeSource}; //! fn write_file( //! root_dir: &mut Directory, //! ) -> Result<(), Error> @@ -72,13 +78,6 @@ //! You cannot enable both the `log` feature and the `defmt-log` feature. #![cfg_attr(not(test), no_std)] -#![deny(missing_docs)] - -// **************************************************************************** -// -// Imports -// -// **************************************************************************** #[cfg(test)] #[macro_use] @@ -87,38 +86,25 @@ extern crate hex_literal; #[macro_use] mod structure; -pub mod blockdevice; -pub mod fat; -pub mod filesystem; -pub mod sdcard; - -use core::fmt::Debug; -use embedded_io::ErrorKind; -use filesystem::Handle; - -#[doc(inline)] -pub use crate::blockdevice::{Block, BlockCache, BlockCount, BlockDevice, BlockIdx}; - -#[doc(inline)] -pub use crate::fat::{FatVolume, VolumeName}; - -#[doc(inline)] -pub use crate::filesystem::{ - Attributes, ClusterId, DirEntry, Directory, File, FilenameError, LfnBuffer, Mode, RawDirectory, - RawFile, ShortFileName, TimeSource, Timestamp, MAX_FILE_SIZE, -}; - -use filesystem::DirectoryInfo; - -#[doc(inline)] -pub use crate::sdcard::Error as SdCardError; - -#[doc(inline)] -pub use crate::sdcard::SdCard; +/// Blocking implementation of this crate. Uses traits from embedded_hal & embedded_io crates. +#[path = "."] +pub mod blocking { + use bisync::synchronous::*; + use embedded_hal::{spi::SpiDevice, delay::DelayNs}; + use embedded_io::{ErrorType, Read, Seek, SeekFrom, Write}; + mod inner; + pub use inner::*; +} -mod volume_mgr; -#[doc(inline)] -pub use volume_mgr::VolumeManager; +/// Async implementation of this crate. Uses traits from embedded_hal_async & embedded_io_async crates. +#[path = "."] +pub mod asynchronous { + use bisync::asynchronous::*; + use embedded_hal_async::{spi::SpiDevice, delay::DelayNs}; + use embedded_io_async::{ErrorType, Read, Seek, SeekFrom, Write}; + mod inner; + pub use inner::*; +} #[cfg(all(feature = "defmt-log", feature = "log"))] compile_error!("Cannot enable both log and defmt-log"); @@ -149,313 +135,3 @@ macro_rules! trace { macro_rules! warn { ($($arg:tt)+) => {}; } - -// **************************************************************************** -// -// Public Types -// -// **************************************************************************** - -/// All the ways the functions in this crate can fail. -#[cfg_attr(feature = "defmt-log", derive(defmt::Format))] -#[derive(Debug, Clone)] -pub enum Error -where - E: core::fmt::Debug, -{ - /// The underlying block device threw an error. - DeviceError(E), - /// The filesystem is badly formatted (or this code is buggy). - FormatError(&'static str), - /// The given `VolumeIdx` was bad, - NoSuchVolume, - /// The given filename was bad - FilenameError(FilenameError), - /// Out of memory opening volumes - TooManyOpenVolumes, - /// Out of memory opening directories - TooManyOpenDirs, - /// Out of memory opening files - TooManyOpenFiles, - /// Bad handle given - BadHandle, - /// That file or directory doesn't exist - NotFound, - /// You can't open a file twice or delete an open file - FileAlreadyOpen, - /// You can't open a directory twice - DirAlreadyOpen, - /// You can't open a directory as a file - OpenedDirAsFile, - /// You can't open a file as a directory - OpenedFileAsDir, - /// You can't delete a directory as a file - DeleteDirAsFile, - /// You can't close a volume with open files or directories - VolumeStillInUse, - /// You can't open a volume twice - VolumeAlreadyOpen, - /// We can't do that yet - Unsupported, - /// Tried to read beyond end of file - EndOfFile, - /// Found a bad cluster - BadCluster, - /// Error while converting types - ConversionError, - /// The device does not have enough space for the operation - NotEnoughSpace, - /// Cluster was not properly allocated by the library - AllocationError, - /// Jumped to free space during FAT traversing - UnterminatedFatChain, - /// Tried to open Read-Only file with write mode - ReadOnly, - /// Tried to create an existing file - FileAlreadyExists, - /// Bad block size - only 512 byte blocks supported - BadBlockSize(u16), - /// Bad offset given when seeking - InvalidOffset, - /// Disk is full - DiskFull, - /// A directory with that name already exists - DirAlreadyExists, - /// The filesystem tried to gain a lock whilst already locked. - /// - /// This is either a bug in the filesystem, or you tried to access the - /// filesystem API from inside a directory iterator (that isn't allowed). - LockError, -} - -impl embedded_io::Error for Error { - fn kind(&self) -> ErrorKind { - match self { - Error::DeviceError(_) - | Error::FormatError(_) - | Error::FileAlreadyOpen - | Error::DirAlreadyOpen - | Error::VolumeStillInUse - | Error::VolumeAlreadyOpen - | Error::EndOfFile - | Error::DiskFull - | Error::NotEnoughSpace - | Error::AllocationError - | Error::LockError => ErrorKind::Other, - Error::NoSuchVolume - | Error::FilenameError(_) - | Error::BadHandle - | Error::InvalidOffset => ErrorKind::InvalidInput, - Error::TooManyOpenVolumes | Error::TooManyOpenDirs | Error::TooManyOpenFiles => { - ErrorKind::OutOfMemory - } - Error::NotFound => ErrorKind::NotFound, - Error::OpenedDirAsFile - | Error::OpenedFileAsDir - | Error::DeleteDirAsFile - | Error::BadCluster - | Error::ConversionError - | Error::UnterminatedFatChain => ErrorKind::InvalidData, - Error::Unsupported | Error::BadBlockSize(_) => ErrorKind::Unsupported, - Error::ReadOnly => ErrorKind::PermissionDenied, - Error::FileAlreadyExists | Error::DirAlreadyExists => ErrorKind::AlreadyExists, - } - } -} - -impl From for Error -where - E: core::fmt::Debug, -{ - fn from(value: E) -> Error { - Error::DeviceError(value) - } -} - -/// A handle to a volume. -/// -/// A volume is a partition with a filesystem within it. -/// -/// Do NOT drop this object! It doesn't hold a reference to the Volume Manager -/// it was created from and the VolumeManager will think you still have the -/// volume open if you just drop it, and it won't let you open the file again. -/// -/// Instead you must pass it to [`crate::VolumeManager::close_volume`] to close -/// it cleanly. -#[cfg_attr(feature = "defmt-log", derive(defmt::Format))] -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub struct RawVolume(Handle); - -impl RawVolume { - /// Convert a raw volume into a droppable [`Volume`] - pub fn to_volume< - D, - T, - const MAX_DIRS: usize, - const MAX_FILES: usize, - const MAX_VOLUMES: usize, - >( - self, - volume_mgr: &VolumeManager, - ) -> Volume - where - D: crate::BlockDevice, - T: crate::TimeSource, - { - Volume::new(self, volume_mgr) - } -} - -/// A handle for an open volume on disk, which closes on drop. -/// -/// In contrast to a `RawVolume`, a `Volume` holds a mutable reference to its -/// parent `VolumeManager`, which restricts which operations you can perform. -/// -/// If you drop a value of this type, it closes the volume automatically, but -/// any error that may occur will be ignored. To handle potential errors, use -/// the [`Volume::close`] method. -pub struct Volume<'a, D, T, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize> -where - D: crate::BlockDevice, - T: crate::TimeSource, -{ - raw_volume: RawVolume, - volume_mgr: &'a VolumeManager, -} - -impl<'a, D, T, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize> - Volume<'a, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES> -where - D: crate::BlockDevice, - T: crate::TimeSource, -{ - /// Create a new `Volume` from a `RawVolume` - pub fn new( - raw_volume: RawVolume, - volume_mgr: &'a VolumeManager, - ) -> Volume<'a, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES> { - Volume { - raw_volume, - volume_mgr, - } - } - - /// Open the volume's root directory. - /// - /// You can then read the directory entries with `iterate_dir`, or you can - /// use `open_file_in_dir`. - pub fn open_root_dir( - &self, - ) -> Result, Error> { - let d = self.volume_mgr.open_root_dir(self.raw_volume)?; - Ok(d.to_directory(self.volume_mgr)) - } - - /// Convert back to a raw volume - pub fn to_raw_volume(self) -> RawVolume { - let v = self.raw_volume; - core::mem::forget(self); - v - } - - /// Consume the `Volume` handle and close it. The behavior of this is similar - /// to using [`core::mem::drop`] or letting the `Volume` go out of scope, - /// except this lets the user handle any errors that may occur in the process, - /// whereas when using drop, any errors will be discarded silently. - pub fn close(self) -> Result<(), Error> { - let result = self.volume_mgr.close_volume(self.raw_volume); - core::mem::forget(self); - result - } -} - -impl<'a, D, T, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize> Drop - for Volume<'a, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES> -where - D: crate::BlockDevice, - T: crate::TimeSource, -{ - fn drop(&mut self) { - _ = self.volume_mgr.close_volume(self.raw_volume) - } -} - -impl<'a, D, T, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize> - core::fmt::Debug for Volume<'a, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES> -where - D: crate::BlockDevice, - T: crate::TimeSource, -{ - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - write!(f, "Volume({})", self.raw_volume.0 .0) - } -} - -#[cfg(feature = "defmt-log")] -impl<'a, D, T, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize> - defmt::Format for Volume<'a, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES> -where - D: crate::BlockDevice, - T: crate::TimeSource, -{ - fn format(&self, fmt: defmt::Formatter) { - defmt::write!(fmt, "Volume({})", self.raw_volume.0 .0) - } -} - -/// Internal information about a Volume -#[cfg_attr(feature = "defmt-log", derive(defmt::Format))] -#[derive(Debug, PartialEq, Eq)] -pub(crate) struct VolumeInfo { - /// Handle for this volume. - raw_volume: RawVolume, - /// Which volume (i.e. partition) we opened on the disk - idx: VolumeIdx, - /// What kind of volume this is - volume_type: VolumeType, -} - -/// This enum holds the data for the various different types of filesystems we -/// support. -#[cfg_attr(feature = "defmt-log", derive(defmt::Format))] -#[derive(Debug, PartialEq, Eq)] -pub enum VolumeType { - /// FAT16/FAT32 formatted volumes. - Fat(FatVolume), -} - -/// A number which identifies a volume (or partition) on a disk. -/// -/// `VolumeIdx(0)` is the first primary partition on an MBR partitioned disk. -#[cfg_attr(feature = "defmt-log", derive(defmt::Format))] -#[derive(Debug, PartialEq, Eq, Copy, Clone)] -pub struct VolumeIdx(pub usize); - -/// Marker for a FAT32 partition. Sometimes also use for FAT16 formatted -/// partitions. -const PARTITION_ID_FAT32_LBA: u8 = 0x0C; -/// Marker for a FAT16 partition with LBA. Seen on a Raspberry Pi SD card. -const PARTITION_ID_FAT16_LBA: u8 = 0x0E; -/// Marker for a FAT16 partition. Seen on a card formatted with the official -/// SD-Card formatter. -const PARTITION_ID_FAT16: u8 = 0x06; -/// Marker for a FAT16 partition smaller than 32MB. Seen on the wowki simulated -/// microsd card -const PARTITION_ID_FAT16_SMALL: u8 = 0x04; -/// Marker for a FAT32 partition. What Macosx disk utility (and also SD-Card formatter?) -/// use. -const PARTITION_ID_FAT32_CHS_LBA: u8 = 0x0B; - -// **************************************************************************** -// -// Unit Tests -// -// **************************************************************************** - -// None - -// **************************************************************************** -// -// End Of File -// -// **************************************************************************** diff --git a/tests/directories.rs b/tests/directories.rs index e10a9018..5bbe2a71 100644 --- a/tests/directories.rs +++ b/tests/directories.rs @@ -1,6 +1,8 @@ //! Directory related tests -use embedded_sdmmc::{LfnBuffer, Mode, ShortFileName}; +use embedded_sdmmc::blocking::{ + DirEntry, Error, LfnBuffer, Mode, ShortFileName, VolumeIdx, VolumeManager, +}; mod utils; @@ -13,8 +15,8 @@ struct ExpectedDirEntry { is_dir: bool, } -impl PartialEq for ExpectedDirEntry { - fn eq(&self, other: &embedded_sdmmc::DirEntry) -> bool { +impl PartialEq for ExpectedDirEntry { + fn eq(&self, other: &DirEntry) -> bool { if other.name.to_string() != self.name { return false; } @@ -38,10 +40,10 @@ impl PartialEq for ExpectedDirEntry { fn fat16_root_directory_listing() { let time_source = utils::make_time_source(); let disk = utils::make_block_device(utils::DISK_SOURCE).unwrap(); - let volume_mgr = embedded_sdmmc::VolumeManager::new(disk, time_source); + let volume_mgr = VolumeManager::new(disk, time_source); let fat16_volume = volume_mgr - .open_raw_volume(embedded_sdmmc::VolumeIdx(0)) + .open_raw_volume(VolumeIdx(0)) .expect("open volume 0"); let root_dir = volume_mgr .open_root_dir(fat16_volume) @@ -145,10 +147,10 @@ fn fat16_root_directory_listing() { fn fat16_sub_directory_listing() { let time_source = utils::make_time_source(); let disk = utils::make_block_device(utils::DISK_SOURCE).unwrap(); - let volume_mgr = embedded_sdmmc::VolumeManager::new(disk, time_source); + let volume_mgr = VolumeManager::new(disk, time_source); let fat16_volume = volume_mgr - .open_raw_volume(embedded_sdmmc::VolumeIdx(0)) + .open_raw_volume(VolumeIdx(0)) .expect("open volume 0"); let root_dir = volume_mgr .open_root_dir(fat16_volume) @@ -216,10 +218,10 @@ fn fat16_sub_directory_listing() { fn fat32_root_directory_listing() { let time_source = utils::make_time_source(); let disk = utils::make_block_device(utils::DISK_SOURCE).unwrap(); - let volume_mgr = embedded_sdmmc::VolumeManager::new(disk, time_source); + let volume_mgr = VolumeManager::new(disk, time_source); let fat32_volume = volume_mgr - .open_raw_volume(embedded_sdmmc::VolumeIdx(1)) + .open_raw_volume(VolumeIdx(1)) .expect("open volume 1"); let root_dir = volume_mgr .open_root_dir(fat32_volume) @@ -343,10 +345,10 @@ fn fat32_root_directory_listing() { fn open_dir_twice() { let time_source = utils::make_time_source(); let disk = utils::make_block_device(utils::DISK_SOURCE).unwrap(); - let volume_mgr = embedded_sdmmc::VolumeManager::new(disk, time_source); + let volume_mgr = VolumeManager::new(disk, time_source); let fat32_volume = volume_mgr - .open_raw_volume(embedded_sdmmc::VolumeIdx(1)) + .open_raw_volume(VolumeIdx(1)) .expect("open volume 1"); let root_dir = volume_mgr @@ -359,7 +361,7 @@ fn open_dir_twice() { assert!(matches!( volume_mgr.open_dir(root_dir, "README.TXT"), - Err(embedded_sdmmc::Error::OpenedFileAsDir) + Err(Error::OpenedFileAsDir) )); let test_dir = volume_mgr @@ -375,7 +377,7 @@ fn open_dir_twice() { assert!(matches!( volume_mgr.close_dir(test_dir), - Err(embedded_sdmmc::Error::BadHandle) + Err(Error::BadHandle) )); } @@ -383,16 +385,16 @@ fn open_dir_twice() { fn open_too_many_dirs() { let time_source = utils::make_time_source(); let disk = utils::make_block_device(utils::DISK_SOURCE).unwrap(); - let volume_mgr: embedded_sdmmc::VolumeManager< + let volume_mgr: VolumeManager< utils::RamDisk>, utils::TestTimeSource, 1, 4, 2, - > = embedded_sdmmc::VolumeManager::new_with_limits(disk, time_source, 0x1000_0000); + > = VolumeManager::new_with_limits(disk, time_source, 0x1000_0000); let fat32_volume = volume_mgr - .open_raw_volume(embedded_sdmmc::VolumeIdx(1)) + .open_raw_volume(VolumeIdx(1)) .expect("open volume 1"); let root_dir = volume_mgr .open_root_dir(fat32_volume) @@ -400,7 +402,7 @@ fn open_too_many_dirs() { assert!(matches!( volume_mgr.open_dir(root_dir, "TEST"), - Err(embedded_sdmmc::Error::TooManyOpenDirs) + Err(Error::TooManyOpenDirs) )); } @@ -408,10 +410,10 @@ fn open_too_many_dirs() { fn find_dir_entry() { let time_source = utils::make_time_source(); let disk = utils::make_block_device(utils::DISK_SOURCE).unwrap(); - let volume_mgr = embedded_sdmmc::VolumeManager::new(disk, time_source); + let volume_mgr = VolumeManager::new(disk, time_source); let fat32_volume = volume_mgr - .open_raw_volume(embedded_sdmmc::VolumeIdx(1)) + .open_raw_volume(VolumeIdx(1)) .expect("open volume 1"); let root_dir = volume_mgr @@ -430,7 +432,7 @@ fn find_dir_entry() { assert!(matches!( volume_mgr.find_directory_entry(root_dir, "README.TXS"), - Err(embedded_sdmmc::Error::NotFound) + Err(Error::NotFound) )); } @@ -438,10 +440,10 @@ fn find_dir_entry() { fn delete_file() { let time_source = utils::make_time_source(); let disk = utils::make_block_device(utils::DISK_SOURCE).unwrap(); - let volume_mgr = embedded_sdmmc::VolumeManager::new(disk, time_source); + let volume_mgr = VolumeManager::new(disk, time_source); let fat32_volume = volume_mgr - .open_raw_volume(embedded_sdmmc::VolumeIdx(1)) + .open_raw_volume(VolumeIdx(1)) .expect("open volume 1"); let root_dir = volume_mgr @@ -454,12 +456,12 @@ fn delete_file() { assert!(matches!( volume_mgr.delete_file_in_dir(root_dir, "README.TXT"), - Err(embedded_sdmmc::Error::FileAlreadyOpen) + Err(Error::FileAlreadyOpen) )); assert!(matches!( volume_mgr.delete_file_in_dir(root_dir, "README2.TXT"), - Err(embedded_sdmmc::Error::NotFound) + Err(Error::NotFound) )); volume_mgr.close_file(file).unwrap(); @@ -470,12 +472,12 @@ fn delete_file() { assert!(matches!( volume_mgr.delete_file_in_dir(root_dir, "README.TXT"), - Err(embedded_sdmmc::Error::NotFound) + Err(Error::NotFound) )); assert!(matches!( volume_mgr.open_file_in_dir(root_dir, "README.TXT", Mode::ReadOnly), - Err(embedded_sdmmc::Error::NotFound) + Err(Error::NotFound) )); } @@ -483,10 +485,10 @@ fn delete_file() { fn make_directory() { let time_source = utils::make_time_source(); let disk = utils::make_block_device(utils::DISK_SOURCE).unwrap(); - let volume_mgr = embedded_sdmmc::VolumeManager::new(disk, time_source); + let volume_mgr = VolumeManager::new(disk, time_source); let fat32_volume = volume_mgr - .open_raw_volume(embedded_sdmmc::VolumeIdx(1)) + .open_raw_volume(VolumeIdx(1)) .expect("open volume 1"); let root_dir = volume_mgr @@ -530,7 +532,7 @@ fn make_directory() { .open_file_in_dir( new_dir, &test_file_name, - embedded_sdmmc::Mode::ReadWriteCreate, + Mode::ReadWriteCreate, ) .expect("open new file"); volume_mgr @@ -585,7 +587,7 @@ fn make_directory() { .open_dir(root_dir, &test_dir_name) .expect("find new dir"); let new_file = volume_mgr - .open_file_in_dir(new_dir, &test_file_name, embedded_sdmmc::Mode::ReadOnly) + .open_file_in_dir(new_dir, &test_file_name, Mode::ReadOnly) .expect("re-open new file"); volume_mgr.close_dir(root_dir).expect("close root"); volume_mgr.close_dir(new_dir).expect("close new dir"); diff --git a/tests/open_files.rs b/tests/open_files.rs index 6b927bc0..b35b96f8 100644 --- a/tests/open_files.rs +++ b/tests/open_files.rs @@ -1,6 +1,6 @@ //! File opening related tests -use embedded_sdmmc::{Error, Mode, VolumeIdx, VolumeManager}; +use embedded_sdmmc::blocking::{Error, Mode, VolumeIdx, VolumeManager}; mod utils; diff --git a/tests/read_file.rs b/tests/read_file.rs index 904964f3..6cc384b5 100644 --- a/tests/read_file.rs +++ b/tests/read_file.rs @@ -2,6 +2,8 @@ use sha2::Digest; +use embedded_sdmmc::blocking::{Mode, VolumeIdx, VolumeManager}; + mod utils; static TEST_DAT_SHA256_SUM: &[u8] = @@ -11,10 +13,10 @@ static TEST_DAT_SHA256_SUM: &[u8] = fn read_file_512_blocks() { let time_source = utils::make_time_source(); let disk = utils::make_block_device(utils::DISK_SOURCE).unwrap(); - let volume_mgr = embedded_sdmmc::VolumeManager::new(disk, time_source); + let volume_mgr = VolumeManager::new(disk, time_source); let fat16_volume = volume_mgr - .open_raw_volume(embedded_sdmmc::VolumeIdx(0)) + .open_raw_volume(VolumeIdx(0)) .expect("open volume 0"); let root_dir = volume_mgr .open_root_dir(fat16_volume) @@ -24,7 +26,7 @@ fn read_file_512_blocks() { .expect("Open test dir"); let test_file = volume_mgr - .open_file_in_dir(test_dir, "TEST.DAT", embedded_sdmmc::Mode::ReadOnly) + .open_file_in_dir(test_dir, "TEST.DAT", Mode::ReadOnly) .expect("open test file"); let mut contents = Vec::new(); @@ -53,10 +55,10 @@ fn read_file_512_blocks() { fn read_file_all() { let time_source = utils::make_time_source(); let disk = utils::make_block_device(utils::DISK_SOURCE).unwrap(); - let volume_mgr = embedded_sdmmc::VolumeManager::new(disk, time_source); + let volume_mgr = VolumeManager::new(disk, time_source); let fat16_volume = volume_mgr - .open_raw_volume(embedded_sdmmc::VolumeIdx(0)) + .open_raw_volume(VolumeIdx(0)) .expect("open volume 0"); let root_dir = volume_mgr .open_root_dir(fat16_volume) @@ -66,7 +68,7 @@ fn read_file_all() { .expect("Open test dir"); let test_file = volume_mgr - .open_file_in_dir(test_dir, "TEST.DAT", embedded_sdmmc::Mode::ReadOnly) + .open_file_in_dir(test_dir, "TEST.DAT", Mode::ReadOnly) .expect("open test file"); let mut contents = vec![0u8; 4096]; @@ -87,10 +89,10 @@ fn read_file_all() { fn read_file_prime_blocks() { let time_source = utils::make_time_source(); let disk = utils::make_block_device(utils::DISK_SOURCE).unwrap(); - let volume_mgr = embedded_sdmmc::VolumeManager::new(disk, time_source); + let volume_mgr = VolumeManager::new(disk, time_source); let fat16_volume = volume_mgr - .open_raw_volume(embedded_sdmmc::VolumeIdx(0)) + .open_raw_volume(VolumeIdx(0)) .expect("open volume 0"); let root_dir = volume_mgr .open_root_dir(fat16_volume) @@ -100,7 +102,7 @@ fn read_file_prime_blocks() { .expect("Open test dir"); let test_file = volume_mgr - .open_file_in_dir(test_dir, "TEST.DAT", embedded_sdmmc::Mode::ReadOnly) + .open_file_in_dir(test_dir, "TEST.DAT", Mode::ReadOnly) .expect("open test file"); let mut contents = Vec::new(); @@ -130,10 +132,10 @@ fn read_file_prime_blocks() { fn read_file_backwards() { let time_source = utils::make_time_source(); let disk = utils::make_block_device(utils::DISK_SOURCE).unwrap(); - let volume_mgr = embedded_sdmmc::VolumeManager::new(disk, time_source); + let volume_mgr = VolumeManager::new(disk, time_source); let fat16_volume = volume_mgr - .open_raw_volume(embedded_sdmmc::VolumeIdx(0)) + .open_raw_volume(VolumeIdx(0)) .expect("open volume 0"); let root_dir = volume_mgr .open_root_dir(fat16_volume) @@ -143,7 +145,7 @@ fn read_file_backwards() { .expect("Open test dir"); let test_file = volume_mgr - .open_file_in_dir(test_dir, "TEST.DAT", embedded_sdmmc::Mode::ReadOnly) + .open_file_in_dir(test_dir, "TEST.DAT", Mode::ReadOnly) .expect("open test file"); let mut contents = std::collections::VecDeque::new(); @@ -188,14 +190,14 @@ fn read_file_backwards() { fn read_file_with_odd_seek() { let time_source = utils::make_time_source(); let disk = utils::make_block_device(utils::DISK_SOURCE).unwrap(); - let volume_mgr = embedded_sdmmc::VolumeManager::new(disk, time_source); + let volume_mgr = VolumeManager::new(disk, time_source); let volume = volume_mgr - .open_volume(embedded_sdmmc::VolumeIdx(0)) + .open_volume(VolumeIdx(0)) .unwrap(); let root_dir = volume.open_root_dir().unwrap(); let f = root_dir - .open_file_in_dir("64MB.DAT", embedded_sdmmc::Mode::ReadOnly) + .open_file_in_dir("64MB.DAT", Mode::ReadOnly) .unwrap(); f.seek_from_start(0x2c).unwrap(); while f.offset() < 1000000 { diff --git a/tests/utils/mod.rs b/tests/utils/mod.rs index 3f89a0ea..b984a237 100644 --- a/tests/utils/mod.rs +++ b/tests/utils/mod.rs @@ -2,7 +2,7 @@ use std::io::prelude::*; -use embedded_sdmmc::{Block, BlockCount, BlockDevice, BlockIdx}; +use embedded_sdmmc::blocking::{Block, BlockCount, BlockDevice, BlockIdx, TimeSource, Timestamp}; /// This file contains: /// @@ -90,8 +90,8 @@ where let contents: &[u8] = borrow.as_ref(); let mut block_idx = start_block_idx; for block in blocks.iter_mut() { - let start_offset = block_idx.0 as usize * embedded_sdmmc::Block::LEN; - let end_offset = start_offset + embedded_sdmmc::Block::LEN; + let start_offset = block_idx.0 as usize * Block::LEN; + let end_offset = start_offset + Block::LEN; if end_offset > contents.len() { return Err(Error::OutOfBounds(block_idx)); } @@ -108,8 +108,8 @@ where let contents: &mut [u8] = borrow.as_mut(); let mut block_idx = start_block_idx; for block in blocks.iter() { - let start_offset = block_idx.0 as usize * embedded_sdmmc::Block::LEN; - let end_offset = start_offset + embedded_sdmmc::Block::LEN; + let start_offset = block_idx.0 as usize * Block::LEN; + let end_offset = start_offset + Block::LEN; if end_offset > contents.len() { return Err(Error::OutOfBounds(block_idx)); } @@ -122,7 +122,7 @@ where fn num_blocks(&self) -> Result { let borrow = self.contents.borrow(); let contents: &[u8] = borrow.as_ref(); - let len_blocks = contents.len() / embedded_sdmmc::Block::LEN; + let len_blocks = contents.len() / Block::LEN; if len_blocks > u32::MAX as usize { panic!("Test disk too large! Only 2**32 blocks allowed"); } @@ -146,11 +146,11 @@ pub fn make_block_device(gzip_bytes: &[u8]) -> Result>, Error> { } pub struct TestTimeSource { - fixed: embedded_sdmmc::Timestamp, + fixed: Timestamp, } -impl embedded_sdmmc::TimeSource for TestTimeSource { - fn get_timestamp(&self) -> embedded_sdmmc::Timestamp { +impl TimeSource for TestTimeSource { + fn get_timestamp(&self) -> Timestamp { self.fixed } } @@ -164,7 +164,7 @@ impl embedded_sdmmc::TimeSource for TestTimeSource { /// in 1981. pub fn make_time_source() -> TestTimeSource { TestTimeSource { - fixed: embedded_sdmmc::Timestamp { + fixed: Timestamp { year_since_1970: 33, zero_indexed_month: 3, zero_indexed_day: 3, diff --git a/tests/volume.rs b/tests/volume.rs index c2ea49b9..768ae64a 100644 --- a/tests/volume.rs +++ b/tests/volume.rs @@ -1,52 +1,54 @@ //! Volume related tests +use embedded_sdmmc::blocking::{Error, Mode, VolumeIdx, VolumeManager}; + mod utils; #[test] fn open_all_volumes() { let time_source = utils::make_time_source(); let disk = utils::make_block_device(utils::DISK_SOURCE).unwrap(); - let volume_mgr: embedded_sdmmc::VolumeManager< + let volume_mgr: VolumeManager< utils::RamDisk>, utils::TestTimeSource, 4, 4, 2, - > = embedded_sdmmc::VolumeManager::new_with_limits(disk, time_source, 0x1000_0000); + > = VolumeManager::new_with_limits(disk, time_source, 0x1000_0000); // Open Volume 0 let fat16_volume = volume_mgr - .open_raw_volume(embedded_sdmmc::VolumeIdx(0)) + .open_raw_volume(VolumeIdx(0)) .expect("open volume 0"); // Fail to Open Volume 0 again assert!(matches!( - volume_mgr.open_raw_volume(embedded_sdmmc::VolumeIdx(0)), - Err(embedded_sdmmc::Error::VolumeAlreadyOpen) + volume_mgr.open_raw_volume(VolumeIdx(0)), + Err(Error::VolumeAlreadyOpen) )); volume_mgr.close_volume(fat16_volume).expect("close fat16"); // Open Volume 1 let fat32_volume = volume_mgr - .open_raw_volume(embedded_sdmmc::VolumeIdx(1)) + .open_raw_volume(VolumeIdx(1)) .expect("open volume 1"); // Fail to Volume 1 again assert!(matches!( - volume_mgr.open_raw_volume(embedded_sdmmc::VolumeIdx(1)), - Err(embedded_sdmmc::Error::VolumeAlreadyOpen) + volume_mgr.open_raw_volume(VolumeIdx(1)), + Err(Error::VolumeAlreadyOpen) )); // Open Volume 0 again let fat16_volume = volume_mgr - .open_raw_volume(embedded_sdmmc::VolumeIdx(0)) + .open_raw_volume(VolumeIdx(0)) .expect("open volume 0"); // Open any volume - too many volumes (0 and 1 are open) assert!(matches!( - volume_mgr.open_raw_volume(embedded_sdmmc::VolumeIdx(0)), - Err(embedded_sdmmc::Error::TooManyOpenVolumes) + volume_mgr.open_raw_volume(VolumeIdx(0)), + Err(Error::TooManyOpenVolumes) )); volume_mgr.close_volume(fat16_volume).expect("close fat16"); @@ -54,27 +56,27 @@ fn open_all_volumes() { // This isn't a valid volume assert!(matches!( - volume_mgr.open_raw_volume(embedded_sdmmc::VolumeIdx(2)), - Err(embedded_sdmmc::Error::FormatError(_e)) + volume_mgr.open_raw_volume(VolumeIdx(2)), + Err(Error::FormatError(_e)) )); // This isn't a valid volume assert!(matches!( - volume_mgr.open_raw_volume(embedded_sdmmc::VolumeIdx(3)), - Err(embedded_sdmmc::Error::FormatError(_e)) + volume_mgr.open_raw_volume(VolumeIdx(3)), + Err(Error::FormatError(_e)) )); // This isn't a valid volume assert!(matches!( - volume_mgr.open_raw_volume(embedded_sdmmc::VolumeIdx(9)), - Err(embedded_sdmmc::Error::NoSuchVolume) + volume_mgr.open_raw_volume(VolumeIdx(9)), + Err(Error::NoSuchVolume) )); let _root_dir = volume_mgr.open_root_dir(fat32_volume).expect("Open dir"); assert!(matches!( volume_mgr.close_volume(fat32_volume), - Err(embedded_sdmmc::Error::VolumeStillInUse) + Err(Error::VolumeStillInUse) )); } @@ -82,21 +84,21 @@ fn open_all_volumes() { fn close_volume_too_early() { let time_source = utils::make_time_source(); let disk = utils::make_block_device(utils::DISK_SOURCE).unwrap(); - let volume_mgr = embedded_sdmmc::VolumeManager::new(disk, time_source); + let volume_mgr = VolumeManager::new(disk, time_source); let volume = volume_mgr - .open_raw_volume(embedded_sdmmc::VolumeIdx(0)) + .open_raw_volume(VolumeIdx(0)) .expect("open volume 0"); let root_dir = volume_mgr.open_root_dir(volume).expect("open root dir"); // Dir open assert!(matches!( volume_mgr.close_volume(volume), - Err(embedded_sdmmc::Error::VolumeStillInUse) + Err(Error::VolumeStillInUse) )); let _test_file = volume_mgr - .open_file_in_dir(root_dir, "64MB.DAT", embedded_sdmmc::Mode::ReadOnly) + .open_file_in_dir(root_dir, "64MB.DAT", Mode::ReadOnly) .expect("open test file"); volume_mgr.close_dir(root_dir).unwrap(); @@ -104,7 +106,7 @@ fn close_volume_too_early() { // File open, not dir open assert!(matches!( volume_mgr.close_volume(volume), - Err(embedded_sdmmc::Error::VolumeStillInUse) + Err(Error::VolumeStillInUse) )); } diff --git a/tests/write_file.rs b/tests/write_file.rs index af178145..0ac1fb99 100644 --- a/tests/write_file.rs +++ b/tests/write_file.rs @@ -1,6 +1,6 @@ //! File opening related tests -use embedded_sdmmc::{Mode, VolumeIdx, VolumeManager}; +use embedded_sdmmc::blocking::{Mode, VolumeIdx, VolumeManager}; mod utils; From 0c24c580c131e6884f74abc0419adc379ee4c1e8 Mon Sep 17 00:00:00 2001 From: Be Date: Thu, 6 Feb 2025 01:05:02 -0600 Subject: [PATCH 2/5] move shared code for blocking & async to new module --- src/{inner => common}/filesystem/attributes.rs | 0 src/{inner => common}/filesystem/cluster.rs | 0 src/common/filesystem/mod.rs | 3 +++ src/{inner => common}/filesystem/timestamp.rs | 0 src/common/mod.rs | 4 ++++ src/common/sdcard/mod.rs | 1 + src/{inner => common}/sdcard/proto.rs | 0 src/inner/filesystem/mod.rs | 9 +++------ src/inner/sdcard/mod.rs | 2 +- src/lib.rs | 2 ++ 10 files changed, 14 insertions(+), 7 deletions(-) rename src/{inner => common}/filesystem/attributes.rs (100%) rename src/{inner => common}/filesystem/cluster.rs (100%) create mode 100644 src/common/filesystem/mod.rs rename src/{inner => common}/filesystem/timestamp.rs (100%) create mode 100644 src/common/mod.rs create mode 100644 src/common/sdcard/mod.rs rename src/{inner => common}/sdcard/proto.rs (100%) diff --git a/src/inner/filesystem/attributes.rs b/src/common/filesystem/attributes.rs similarity index 100% rename from src/inner/filesystem/attributes.rs rename to src/common/filesystem/attributes.rs diff --git a/src/inner/filesystem/cluster.rs b/src/common/filesystem/cluster.rs similarity index 100% rename from src/inner/filesystem/cluster.rs rename to src/common/filesystem/cluster.rs diff --git a/src/common/filesystem/mod.rs b/src/common/filesystem/mod.rs new file mode 100644 index 00000000..bd2afcc5 --- /dev/null +++ b/src/common/filesystem/mod.rs @@ -0,0 +1,3 @@ +pub(crate) mod attributes; +pub(crate) mod cluster; +pub(crate) mod timestamp; diff --git a/src/inner/filesystem/timestamp.rs b/src/common/filesystem/timestamp.rs similarity index 100% rename from src/inner/filesystem/timestamp.rs rename to src/common/filesystem/timestamp.rs diff --git a/src/common/mod.rs b/src/common/mod.rs new file mode 100644 index 00000000..e71e3ed4 --- /dev/null +++ b/src/common/mod.rs @@ -0,0 +1,4 @@ +//! Common modules that have no difference between blocking and asynchronous code + +pub(crate) mod filesystem; +pub(crate) mod sdcard; diff --git a/src/common/sdcard/mod.rs b/src/common/sdcard/mod.rs new file mode 100644 index 00000000..febacec6 --- /dev/null +++ b/src/common/sdcard/mod.rs @@ -0,0 +1 @@ +pub mod proto; diff --git a/src/inner/sdcard/proto.rs b/src/common/sdcard/proto.rs similarity index 100% rename from src/inner/sdcard/proto.rs rename to src/common/sdcard/proto.rs diff --git a/src/inner/filesystem/mod.rs b/src/inner/filesystem/mod.rs index 668ac86b..6994d52d 100644 --- a/src/inner/filesystem/mod.rs +++ b/src/inner/filesystem/mod.rs @@ -6,21 +6,18 @@ /// Maximum file size supported by this library pub const MAX_FILE_SIZE: u32 = u32::MAX; -mod attributes; -mod cluster; mod directory; mod filename; mod files; mod handles; -mod timestamp; -pub use self::attributes::Attributes; -pub use self::cluster::ClusterId; +pub use crate::common::filesystem::attributes::Attributes; +pub use crate::common::filesystem::cluster::ClusterId; pub use self::directory::{DirEntry, Directory, RawDirectory}; pub use self::filename::{FilenameError, LfnBuffer, ShortFileName, ToShortFileName}; pub use self::files::{File, FileError, Mode, RawFile}; pub use self::handles::{Handle, HandleGenerator}; -pub use self::timestamp::{TimeSource, Timestamp}; +pub use crate::common::filesystem::timestamp::{TimeSource, Timestamp}; pub(crate) use self::directory::DirectoryInfo; pub(crate) use self::files::FileInfo; diff --git a/src/inner/sdcard/mod.rs b/src/inner/sdcard/mod.rs index 0985ba42..a6daba03 100644 --- a/src/inner/sdcard/mod.rs +++ b/src/inner/sdcard/mod.rs @@ -3,7 +3,7 @@ //! This is currently optimised for readability and debugability, not //! performance. -pub mod proto; +pub use crate::common::sdcard::proto; use super::super::{bisync, DelayNs, SpiDevice}; diff --git a/src/lib.rs b/src/lib.rs index ec612944..4dd62962 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -86,6 +86,8 @@ extern crate hex_literal; #[macro_use] mod structure; +mod common; + /// Blocking implementation of this crate. Uses traits from embedded_hal & embedded_io crates. #[path = "."] pub mod blocking { From ce1be5f7569639fa75d8ab6bede4cb7919f7804c Mon Sep 17 00:00:00 2001 From: Be Date: Thu, 6 Feb 2025 14:35:58 -0600 Subject: [PATCH 3/5] add comment explaining #[allow(async_fn_in_trait)] --- src/inner/mod.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/inner/mod.rs b/src/inner/mod.rs index de1410be..3f3d92f0 100644 --- a/src/inner/mod.rs +++ b/src/inner/mod.rs @@ -5,6 +5,9 @@ // **************************************************************************** #![deny(missing_docs)] +// The compiler warning for `async fn` in public traits isn't relevant for embedded, +// so silence it. +// https://github.com/rust-embedded/embedded-hal/pull/515#issuecomment-1763525962 #![allow(async_fn_in_trait)] pub mod blockdevice; From e623266de9481fd43d8e256d6485e5b6ae9b97ac Mon Sep 17 00:00:00 2001 From: Be Date: Fri, 7 Feb 2025 21:14:15 -0600 Subject: [PATCH 4/5] document that async File & Volume must be explicitly closed --- src/inner/filesystem/files.rs | 3 +++ src/inner/mod.rs | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/inner/filesystem/files.rs b/src/inner/filesystem/files.rs index ff9e0222..594e4177 100644 --- a/src/inner/filesystem/files.rs +++ b/src/inner/filesystem/files.rs @@ -48,6 +48,9 @@ impl RawFile { /// If you drop a value of this type, it closes the file automatically, and but /// error that may occur will be ignored. To handle potential errors, use /// the [`File::close`] method. +/// +/// For async Files, async drop does not exist in Rust, so you *must* call +/// [`File::close`] when you are done with a File. pub struct File<'a, D, T, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize> where D: BlockDevice, diff --git a/src/inner/mod.rs b/src/inner/mod.rs index 3f3d92f0..3cf46313 100644 --- a/src/inner/mod.rs +++ b/src/inner/mod.rs @@ -209,6 +209,9 @@ impl RawVolume { /// If you drop a value of this type, it closes the volume automatically, but /// any error that may occur will be ignored. To handle potential errors, use /// the [`Volume::close`] method. +/// +/// For async Volumes, async drop does not exist in Rust, so you *must* call +/// [`Volume::close`] when you are done with a Volume. pub struct Volume<'a, D, T, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize> where D: BlockDevice, From 7b15cf594ed038df271ad47e2bfc11277defe731 Mon Sep 17 00:00:00 2001 From: Be Date: Wed, 12 Feb 2025 17:01:18 -0600 Subject: [PATCH 5/5] impl Drop for async File & Volume with embassy_futures::block_on --- Cargo.toml | 1 + src/inner/filesystem/files.rs | 21 +++++++++++++++++---- src/inner/mod.rs | 21 +++++++++++++++++---- 3 files changed, 35 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 120bfc37..a759a1be 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,7 @@ rust-version = "1.76" bisync = "0.3.0" byteorder = {version = "1", default-features = false} defmt = {version = "0.3", optional = true} +embassy-futures = "0.1.1" embedded-hal = "1.0.0" embedded-hal-async = "1.0.0" embedded-io = "0.6.1" diff --git a/src/inner/filesystem/files.rs b/src/inner/filesystem/files.rs index 594e4177..731741dd 100644 --- a/src/inner/filesystem/files.rs +++ b/src/inner/filesystem/files.rs @@ -1,4 +1,4 @@ -use super::super::super::{bisync, only_sync}; +use super::super::super::{bisync, only_sync, only_async}; use super::super::super::{ErrorType, Read, Seek, SeekFrom, Write}; use super::super::{ filesystem::{ClusterId, DirEntry, Handle}, @@ -49,8 +49,10 @@ impl RawFile { /// error that may occur will be ignored. To handle potential errors, use /// the [`File::close`] method. /// -/// For async Files, async drop does not exist in Rust, so you *must* call -/// [`File::close`] when you are done with a File. +/// For async `Files`, the implementation of [`Drop`] blocks with [`embassy_futures::block_on`] +/// because there is no way to `.await` inside [`Drop::drop`]. If you would prefer +/// to rely on the async executor you are already using, call [`File::close`] +/// manually instead of letting the `File` drop. pub struct File<'a, D, T, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize> where D: BlockDevice, @@ -150,7 +152,6 @@ where } } -// async drop does not yet exist :( #[only_sync] impl<'a, D, T, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize> Drop for File<'a, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES> @@ -163,6 +164,18 @@ where } } +#[only_async] +impl<'a, D, T, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize> Drop + for File<'a, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES> +where + D: BlockDevice, + T: TimeSource, +{ + fn drop(&mut self) { + _ = embassy_futures::block_on(self.volume_mgr.close_file(self.raw_file)); + } +} + impl<'a, D, T, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize> core::fmt::Debug for File<'a, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES> where diff --git a/src/inner/mod.rs b/src/inner/mod.rs index 3cf46313..812068ec 100644 --- a/src/inner/mod.rs +++ b/src/inner/mod.rs @@ -19,7 +19,7 @@ use core::fmt::Debug; use embedded_io::ErrorKind; use filesystem::Handle; -use super::{bisync, only_sync}; +use super::{bisync, only_sync, only_async}; #[doc(inline)] pub use blockdevice::{Block, BlockCache, BlockCount, BlockDevice, BlockIdx}; @@ -210,8 +210,10 @@ impl RawVolume { /// any error that may occur will be ignored. To handle potential errors, use /// the [`Volume::close`] method. /// -/// For async Volumes, async drop does not exist in Rust, so you *must* call -/// [`Volume::close`] when you are done with a Volume. +/// For async `Volumes`, the implementation of [`Drop`] blocks with [`embassy_futures::block_on`] +/// because there is no way to `.await` inside [`Drop::drop`]. If you would prefer +/// to rely on the async executor you are already using, call [`Volume::close`] +/// manually instead of letting the `Volume` drop. pub struct Volume<'a, D, T, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize> where D: BlockDevice, @@ -268,7 +270,6 @@ where } } -// async drop does not yet exist :( #[only_sync] impl<'a, D, T, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize> Drop for Volume<'a, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES> @@ -281,6 +282,18 @@ where } } +#[only_async] +impl<'a, D, T, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize> Drop + for Volume<'a, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES> +where + D: BlockDevice, + T: TimeSource, +{ + fn drop(&mut self) { + _ = embassy_futures::block_on(self.volume_mgr.close_volume(self.raw_volume)); + } +} + impl<'a, D, T, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize> core::fmt::Debug for Volume<'a, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES> where