Skip to content

Commit a536359

Browse files
authored
Merge pull request #192 from rust-embedded-community/release-v0.9.0
Release v0.9.0
2 parents ea8cd4c + fe6d98c commit a536359

35 files changed

+2399
-1428
lines changed

.github/workflows/rust.yml

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,28 @@ jobs:
66
formatting:
77
runs-on: ubuntu-latest
88
steps:
9-
- uses: actions/checkout@v1
9+
- uses: actions/checkout@v4
10+
- uses: dtolnay/rust-toolchain@stable
11+
with:
12+
components: rustfmt
1013
- name: Check formatting
1114
run: cargo fmt -- --check
1215

1316
build-test:
1417
runs-on: ubuntu-latest
1518
strategy:
1619
matrix:
20+
# Always run MSRV too!
21+
rust: ["stable", "1.76"]
1722
features: ['log', 'defmt-log', '""']
1823
steps:
19-
- uses: actions/checkout@v1
24+
- uses: actions/checkout@v4
25+
- uses: dtolnay/rust-toolchain@master
26+
with:
27+
toolchain: ${{ matrix.rust }}
2028
- name: Build
2129
run: cargo build --no-default-features --features ${{matrix.features}} --verbose
30+
env:
31+
DEFMT_LOG: debug
2232
- name: Run Tests
2333
run: cargo test --no-default-features --features ${{matrix.features}} --verbose

CHANGELOG.md

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,26 @@ The format is based on [Keep a Changelog] and this project adheres to [Semantic
66

77
## [Unreleased]
88

9+
## [Version 0.9.0] - 2025-06-08
10+
911
### Changed
1012

11-
- None
13+
- __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.
14+
- __Breaking Change__: The `VolumeManager`, `File`, `Directory` and `Volume` no longer implement `Send` or `Sync`.
15+
- `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.
16+
- __Breaking Change__: The `VolumeManager::device` method now takes a callback rather than giving you a reference to the underlying `BlockDevice`
17+
- __Breaking Change__: `Error:LockError` variant added.
18+
- __Breaking Change__: `SearchId` was renamed to `Handle`
19+
- Fixed writing at block start mid-file (previously overwrote subsequent file data with zeros up to the end of the block)
1220

1321
### Added
1422

15-
- None
23+
- `File` now implements the `embedded-io` `Read`, `Write` and `Seek` traits.
24+
- New `iterate_dir_lfn` method on `VolumeManager` and `Directory` - provides decoded Long File Names as `Option<&str>`
1625

1726
### Removed
1827

19-
- None
28+
- __Breaking Change__: Removed the `reason: &str` argument from `BlockDevice`
2029

2130
## [Version 0.8.2] - 2025-06-07
2231

@@ -180,7 +189,8 @@ The format is based on [Keep a Changelog] and this project adheres to [Semantic
180189

181190
[Keep a Changelog]: http://keepachangelog.com/en/1.0.0/
182191
[Semantic Versioning]: http://semver.org/spec/v2.0.0.html
183-
[Unreleased]: https://github.com/rust-embedded-community/embedded-sdmmc-rs/compare/v0.8.2...develop
192+
[Unreleased]: https://github.com/rust-embedded-community/embedded-sdmmc-rs/compare/v0.9.0...develop
193+
[Version 0.9.0]: https://github.com/rust-embedded-community/embedded-sdmmc-rs/compare/v0.8.2...v0.9.0
184194
[Version 0.8.2]: https://github.com/rust-embedded-community/embedded-sdmmc-rs/compare/v0.8.1...v0.8.2
185195
[Version 0.8.1]: https://github.com/rust-embedded-community/embedded-sdmmc-rs/compare/v0.8.0...v0.8.1
186196
[Version 0.8.0]: https://github.com/rust-embedded-community/embedded-sdmmc-rs/compare/v0.7.0...v0.8.0

Cargo.toml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,16 @@ license = "MIT OR Apache-2.0"
88
name = "embedded-sdmmc"
99
readme = "README.md"
1010
repository = "https://github.com/rust-embedded-community/embedded-sdmmc-rs"
11-
version = "0.8.2"
11+
version = "0.9.0"
12+
13+
# Make sure to update the CI too!
14+
rust-version = "1.76"
1215

1316
[dependencies]
1417
byteorder = {version = "1", default-features = false}
1518
defmt = {version = "0.3", optional = true}
1619
embedded-hal = "1.0.0"
20+
embedded-io = "0.6.1"
1721
heapless = "^0.8"
1822
log = {version = "0.4", default-features = false, optional = true}
1923

README.md

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,22 +11,23 @@ designed for readability and simplicity over performance.
1111
You will need something that implements the `BlockDevice` trait, which can read and write the 512-byte blocks (or sectors) from your card. If you were to implement this over USB Mass Storage, there's no reason this crate couldn't work with a USB Thumb Drive, but we only supply a `BlockDevice` suitable for reading SD and SDHC cards over SPI.
1212

1313
```rust
14+
use embedded_sdmmc::{SdCard, VolumeManager, Mode, VolumeIdx};
1415
// Build an SD Card interface out of an SPI device, a chip-select pin and the delay object
15-
let sdcard = embedded_sdmmc::SdCard::new(sdmmc_spi, delay);
16+
let sdcard = SdCard::new(sdmmc_spi, delay);
1617
// Get the card size (this also triggers card initialisation because it's not been done yet)
1718
println!("Card size is {} bytes", sdcard.num_bytes()?);
1819
// Now let's look for volumes (also known as partitions) on our block device.
1920
// To do this we need a Volume Manager. It will take ownership of the block device.
20-
let mut volume_mgr = embedded_sdmmc::VolumeManager::new(sdcard, time_source);
21+
let volume_mgr = VolumeManager::new(sdcard, time_source);
2122
// Try and access Volume 0 (i.e. the first partition).
2223
// The volume object holds information about the filesystem on that volume.
23-
let mut volume0 = volume_mgr.open_volume(embedded_sdmmc::VolumeIdx(0))?;
24+
let volume0 = volume_mgr.open_volume(VolumeIdx(0))?;
2425
println!("Volume 0: {:?}", volume0);
2526
// Open the root directory (mutably borrows from the volume).
26-
let mut root_dir = volume0.open_root_dir()?;
27+
let root_dir = volume0.open_root_dir()?;
2728
// Open a file called "MY_FILE.TXT" in the root directory
2829
// This mutably borrows the directory.
29-
let mut my_file = root_dir.open_file_in_dir("MY_FILE.TXT", embedded_sdmmc::Mode::ReadOnly)?;
30+
let my_file = root_dir.open_file_in_dir("MY_FILE.TXT", Mode::ReadOnly)?;
3031
// Print the contents of the file, assuming it's in ISO-8859-1 encoding
3132
while !my_file.is_eof() {
3233
let mut buffer = [0u8; 32];
@@ -37,13 +38,26 @@ while !my_file.is_eof() {
3738
}
3839
```
3940

41+
For writing files:
42+
43+
```rust
44+
let my_other_file = root_dir.open_file_in_dir("MY_DATA.CSV", embedded_sdmmc::Mode::ReadWriteCreateOrAppend)?;
45+
my_other_file.write(b"Timestamp,Signal,Value\n")?;
46+
my_other_file.write(b"2025-01-01T00:00:00Z,TEMP,25.0\n")?;
47+
my_other_file.write(b"2025-01-01T00:00:01Z,TEMP,25.1\n")?;
48+
my_other_file.write(b"2025-01-01T00:00:02Z,TEMP,25.2\n")?;
49+
50+
// Don't forget to flush the file so that the directory entry is updated
51+
my_other_file.flush()?;
52+
```
53+
4054
### Open directories and files
4155

4256
By default the `VolumeManager` will initialize with a maximum number of `4` open directories, files and volumes. This can be customized by specifying the `MAX_DIR`, `MAX_FILES` and `MAX_VOLUMES` generic consts of the `VolumeManager`:
4357

4458
```rust
4559
// Create a volume manager with a maximum of 6 open directories, 12 open files, and 4 volumes (or partitions)
46-
let mut cont: VolumeManager<_, _, 6, 12, 4> = VolumeManager::new_with_limits(block, time_source);
60+
let cont: VolumeManager<_, _, 6, 12, 4> = VolumeManager::new_with_limits(block, time_source);
4761
```
4862

4963
## Supported features

examples/append_file.rs

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,37 +5,35 @@
55
//! $ cargo run --example append_file -- /dev/mmcblk0
66
//! ```
77
//!
8-
//! If you pass a block device it should be unmounted. No testing has been
9-
//! performed with Windows raw block devices - please report back if you try
10-
//! this! There is a gzipped example disk image which you can gunzip and test
11-
//! with if you don't have a suitable block device.
8+
//! If you pass a block device it should be unmounted. There is a gzipped
9+
//! example disk image which you can gunzip and test with if you don't have a
10+
//! suitable block device.
1211
//!
1312
//! ```bash
1413
//! zcat ./tests/disk.img.gz > ./disk.img
1514
//! $ cargo run --example append_file -- ./disk.img
1615
//! ```
1716
18-
extern crate embedded_sdmmc;
19-
2017
mod linux;
2118
use linux::*;
2219

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

25-
use embedded_sdmmc::{Error, Mode, VolumeIdx, VolumeManager};
22+
use embedded_sdmmc::{Error, Mode, VolumeIdx};
23+
24+
type VolumeManager = embedded_sdmmc::VolumeManager<LinuxBlockDevice, Clock, 8, 4, 4>;
2625

27-
fn main() -> Result<(), embedded_sdmmc::Error<std::io::Error>> {
26+
fn main() -> Result<(), Error<std::io::Error>> {
2827
env_logger::init();
2928
let mut args = std::env::args().skip(1);
3029
let filename = args.next().unwrap_or_else(|| "/dev/mmcblk0".into());
3130
let print_blocks = args.find(|x| x == "-v").map(|_| true).unwrap_or(false);
3231
let lbd = LinuxBlockDevice::new(filename, print_blocks).map_err(Error::DeviceError)?;
33-
let mut volume_mgr: VolumeManager<LinuxBlockDevice, Clock, 8, 8, 4> =
34-
VolumeManager::new_with_limits(lbd, Clock, 0xAA00_0000);
35-
let mut volume = volume_mgr.open_volume(VolumeIdx(0))?;
36-
let mut root_dir = volume.open_root_dir()?;
32+
let volume_mgr: VolumeManager = VolumeManager::new_with_limits(lbd, Clock, 0xAA00_0000);
33+
let volume = volume_mgr.open_volume(VolumeIdx(0))?;
34+
let root_dir = volume.open_root_dir()?;
3735
println!("\nCreating file {}...", FILE_TO_APPEND);
38-
let mut f = root_dir.open_file_in_dir(FILE_TO_APPEND, Mode::ReadWriteAppend)?;
36+
let f = root_dir.open_file_in_dir(FILE_TO_APPEND, Mode::ReadWriteAppend)?;
3937
f.write(b"\r\n\r\nThis has been added to your file.\r\n")?;
4038
Ok(())
4139
}

examples/big_dir.rs

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,48 @@
1-
extern crate embedded_sdmmc;
1+
//! Big Directory Example.
2+
//!
3+
//! Attempts to create an infinite number of files in the root directory of the
4+
//! first volume of the given block device. This is basically to see what
5+
//! happens when the root directory runs out of space.
6+
//!
7+
//! ```bash
8+
//! $ cargo run --example big_dir -- ./disk.img
9+
//! $ cargo run --example big_dir -- /dev/mmcblk0
10+
//! ```
11+
//!
12+
//! If you pass a block device it should be unmounted. There is a gzipped
13+
//! example disk image which you can gunzip and test with if you don't have a
14+
//! suitable block device.
15+
//!
16+
//! ```bash
17+
//! zcat ./tests/disk.img.gz > ./disk.img
18+
//! $ cargo run --example big_dir -- ./disk.img
19+
//! ```
220
321
mod linux;
422
use linux::*;
523

6-
use embedded_sdmmc::{Error, VolumeManager};
24+
use embedded_sdmmc::{Error, Mode, VolumeIdx};
725

8-
fn main() -> Result<(), embedded_sdmmc::Error<std::io::Error>> {
26+
type VolumeManager = embedded_sdmmc::VolumeManager<LinuxBlockDevice, Clock, 8, 4, 4>;
27+
28+
fn main() -> Result<(), Error<std::io::Error>> {
929
env_logger::init();
1030
let mut args = std::env::args().skip(1);
1131
let filename = args.next().unwrap_or_else(|| "/dev/mmcblk0".into());
1232
let print_blocks = args.find(|x| x == "-v").map(|_| true).unwrap_or(false);
1333
let lbd = LinuxBlockDevice::new(filename, print_blocks).map_err(Error::DeviceError)?;
14-
let mut volume_mgr: VolumeManager<LinuxBlockDevice, Clock, 8, 8, 4> =
15-
VolumeManager::new_with_limits(lbd, Clock, 0xAA00_0000);
16-
let mut volume = volume_mgr
17-
.open_volume(embedded_sdmmc::VolumeIdx(1))
18-
.unwrap();
34+
let volume_mgr: VolumeManager = VolumeManager::new_with_limits(lbd, Clock, 0xAA00_0000);
35+
let volume = volume_mgr.open_volume(VolumeIdx(0)).unwrap();
1936
println!("Volume: {:?}", volume);
20-
let mut root_dir = volume.open_root_dir().unwrap();
37+
let root_dir = volume.open_root_dir().unwrap();
2138

2239
let mut file_num = 0;
2340
loop {
2441
file_num += 1;
2542
let file_name = format!("{}.da", file_num);
2643
println!("opening file {file_name} for writing");
27-
let mut file = root_dir
28-
.open_file_in_dir(
29-
file_name.as_str(),
30-
embedded_sdmmc::Mode::ReadWriteCreateOrTruncate,
31-
)
44+
let file = root_dir
45+
.open_file_in_dir(file_name.as_str(), Mode::ReadWriteCreateOrTruncate)
3246
.unwrap();
3347
let buf = b"hello world, from rust";
3448
println!("writing to file");

examples/create_file.rs

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,40 +5,38 @@
55
//! $ cargo run --example create_file -- /dev/mmcblk0
66
//! ```
77
//!
8-
//! If you pass a block device it should be unmounted. No testing has been
9-
//! performed with Windows raw block devices - please report back if you try
10-
//! this! There is a gzipped example disk image which you can gunzip and test
11-
//! with if you don't have a suitable block device.
8+
//! If you pass a block device it should be unmounted. There is a gzipped
9+
//! example disk image which you can gunzip and test with if you don't have a
10+
//! suitable block device.
1211
//!
1312
//! ```bash
1413
//! zcat ./tests/disk.img.gz > ./disk.img
1514
//! $ cargo run --example create_file -- ./disk.img
1615
//! ```
1716
18-
extern crate embedded_sdmmc;
19-
2017
mod linux;
2118
use linux::*;
2219

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

25-
use embedded_sdmmc::{Error, Mode, VolumeIdx, VolumeManager};
22+
use embedded_sdmmc::{Error, Mode, VolumeIdx};
23+
24+
type VolumeManager = embedded_sdmmc::VolumeManager<LinuxBlockDevice, Clock, 8, 4, 4>;
2625

27-
fn main() -> Result<(), embedded_sdmmc::Error<std::io::Error>> {
26+
fn main() -> Result<(), Error<std::io::Error>> {
2827
env_logger::init();
2928
let mut args = std::env::args().skip(1);
3029
let filename = args.next().unwrap_or_else(|| "/dev/mmcblk0".into());
3130
let print_blocks = args.find(|x| x == "-v").map(|_| true).unwrap_or(false);
3231
let lbd = LinuxBlockDevice::new(filename, print_blocks).map_err(Error::DeviceError)?;
33-
let mut volume_mgr: VolumeManager<LinuxBlockDevice, Clock, 8, 8, 4> =
34-
VolumeManager::new_with_limits(lbd, Clock, 0xAA00_0000);
35-
let mut volume = volume_mgr.open_volume(VolumeIdx(0))?;
36-
let mut root_dir = volume.open_root_dir()?;
32+
let volume_mgr: VolumeManager = VolumeManager::new_with_limits(lbd, Clock, 0xAA00_0000);
33+
let volume = volume_mgr.open_volume(VolumeIdx(0))?;
34+
let root_dir = volume.open_root_dir()?;
3735
println!("\nCreating file {}...", FILE_TO_CREATE);
3836
// This will panic if the file already exists: use ReadWriteCreateOrAppend
3937
// or ReadWriteCreateOrTruncate instead if you want to modify an existing
4038
// file.
41-
let mut f = root_dir.open_file_in_dir(FILE_TO_CREATE, Mode::ReadWriteCreate)?;
39+
let f = root_dir.open_file_in_dir(FILE_TO_CREATE, Mode::ReadWriteCreate)?;
4240
f.write(b"Hello, this is a new file on disk\r\n")?;
4341
Ok(())
4442
}

examples/delete_file.rs

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,35 +8,33 @@
88
//! NOTE: THIS EXAMPLE DELETES A FILE CALLED README.TXT. IF YOU DO NOT WANT THAT
99
//! FILE DELETED FROM YOUR DISK IMAGE, DO NOT RUN THIS EXAMPLE.
1010
//!
11-
//! If you pass a block device it should be unmounted. No testing has been
12-
//! performed with Windows raw block devices - please report back if you try
13-
//! this! There is a gzipped example disk image which you can gunzip and test
14-
//! with if you don't have a suitable block device.
11+
//! If you pass a block device it should be unmounted. There is a gzipped
12+
//! example disk image which you can gunzip and test with if you don't have a
13+
//! suitable block device.
1514
//!
1615
//! ```bash
1716
//! zcat ./tests/disk.img.gz > ./disk.img
1817
//! $ cargo run --example delete_file -- ./disk.img
1918
//! ```
2019
21-
extern crate embedded_sdmmc;
22-
2320
mod linux;
2421
use linux::*;
2522

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

28-
use embedded_sdmmc::{Error, VolumeIdx, VolumeManager};
25+
use embedded_sdmmc::{Error, VolumeIdx};
26+
27+
type VolumeManager = embedded_sdmmc::VolumeManager<LinuxBlockDevice, Clock, 8, 4, 4>;
2928

30-
fn main() -> Result<(), embedded_sdmmc::Error<std::io::Error>> {
29+
fn main() -> Result<(), Error<std::io::Error>> {
3130
env_logger::init();
3231
let mut args = std::env::args().skip(1);
3332
let filename = args.next().unwrap_or_else(|| "/dev/mmcblk0".into());
3433
let print_blocks = args.find(|x| x == "-v").map(|_| true).unwrap_or(false);
3534
let lbd = LinuxBlockDevice::new(filename, print_blocks).map_err(Error::DeviceError)?;
36-
let mut volume_mgr: VolumeManager<LinuxBlockDevice, Clock, 8, 8, 4> =
37-
VolumeManager::new_with_limits(lbd, Clock, 0xAA00_0000);
38-
let mut volume = volume_mgr.open_volume(VolumeIdx(0))?;
39-
let mut root_dir = volume.open_root_dir()?;
35+
let volume_mgr: VolumeManager = VolumeManager::new_with_limits(lbd, Clock, 0xAA00_0000);
36+
let volume = volume_mgr.open_volume(VolumeIdx(0))?;
37+
let root_dir = volume.open_root_dir()?;
4038
println!("Deleting file {}...", FILE_TO_DELETE);
4139
root_dir.delete_file_in_dir(FILE_TO_DELETE)?;
4240
println!("Deleted!");

examples/linux/mod.rs

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,22 +34,14 @@ impl LinuxBlockDevice {
3434
impl BlockDevice for LinuxBlockDevice {
3535
type Error = std::io::Error;
3636

37-
fn read(
38-
&self,
39-
blocks: &mut [Block],
40-
start_block_idx: BlockIdx,
41-
reason: &str,
42-
) -> Result<(), Self::Error> {
37+
fn read(&self, blocks: &mut [Block], start_block_idx: BlockIdx) -> Result<(), Self::Error> {
4338
self.file
4439
.borrow_mut()
4540
.seek(SeekFrom::Start(start_block_idx.into_bytes()))?;
4641
for block in blocks.iter_mut() {
4742
self.file.borrow_mut().read_exact(&mut block.contents)?;
4843
if self.print_blocks {
49-
println!(
50-
"Read block ({}) {:?}: {:?}",
51-
reason, start_block_idx, &block
52-
);
44+
println!("Read block {:?}: {:?}", start_block_idx, &block);
5345
}
5446
}
5547
Ok(())

0 commit comments

Comments
 (0)