Skip to content

Commit

Permalink
Use embedded-storage BlockDevice trait.
Browse files Browse the repository at this point in the history
This will allow both the SD card driver and the FAT32 implementation to
be used with filesystem or block device drivers respectively.
  • Loading branch information
qwandor committed Oct 29, 2024
1 parent 1b9fa5c commit 936d63f
Show file tree
Hide file tree
Showing 10 changed files with 343 additions and 335 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ byteorder = {version = "1", default-features = false}
defmt = {version = "0.3", optional = true}
embedded-hal = "1.0.0"
embedded-io = "0.6.1"
embedded-storage = { git = "https://github.com/qwandor/embedded-storage", branch = "block" }
heapless = "^0.8"
log = {version = "0.4", default-features = false, optional = true}

Expand Down
20 changes: 10 additions & 10 deletions examples/linux/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Helpers for using embedded-sdmmc on Linux

use chrono::Timelike;
use embedded_sdmmc::{Block, BlockCount, BlockDevice, BlockIdx, TimeSource, Timestamp};
use embedded_sdmmc::{Block, BlockDevice, TimeSource, Timestamp, BLOCK_LEN_U64};
use std::cell::RefCell;
use std::fs::{File, OpenOptions};
use std::io::prelude::*;
Expand Down Expand Up @@ -34,35 +34,35 @@ impl LinuxBlockDevice {
impl BlockDevice for LinuxBlockDevice {
type Error = std::io::Error;

fn read(&self, blocks: &mut [Block], start_block_idx: BlockIdx) -> Result<(), Self::Error> {
fn read(&mut self, first_block_index: u64, blocks: &mut [Block]) -> Result<(), Self::Error> {
self.file
.borrow_mut()
.seek(SeekFrom::Start(start_block_idx.into_bytes()))?;
.seek(SeekFrom::Start(first_block_index * BLOCK_LEN_U64))?;
for block in blocks.iter_mut() {
self.file.borrow_mut().read_exact(&mut block.contents)?;
self.file.borrow_mut().read_exact(block)?;
if self.print_blocks {
println!("Read block {:?}: {:?}", start_block_idx, &block);
println!("Read block {:?}: {:?}", first_block_index, &block);
}
}
Ok(())
}

fn write(&self, blocks: &[Block], start_block_idx: BlockIdx) -> Result<(), Self::Error> {
fn write(&mut self, first_block_index: u64, blocks: &[Block]) -> Result<(), Self::Error> {
self.file
.borrow_mut()
.seek(SeekFrom::Start(start_block_idx.into_bytes()))?;
.seek(SeekFrom::Start(first_block_index * BLOCK_LEN_U64))?;
for block in blocks.iter() {
self.file.borrow_mut().write_all(&block.contents)?;
self.file.borrow_mut().write_all(block)?;
if self.print_blocks {
println!("Wrote: {:?}", &block);
}
}
Ok(())
}

fn num_blocks(&self) -> Result<BlockCount, Self::Error> {
fn block_count(&self) -> Result<u64, Self::Error> {
let num_blocks = self.file.borrow().metadata().unwrap().len() / 512;
Ok(BlockCount(num_blocks as u32))
Ok(num_blocks)
}
}

Expand Down
113 changes: 29 additions & 84 deletions src/blockdevice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,88 +3,27 @@
//! Generic code for handling block devices, such as types for identifying
//! a particular block on a block device by its index.

use embedded_storage::BlockDevice;

/// 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
/// about 2 TiB, and almost all SD/MMC cards have 512 byte blocks.
///
/// This library does not support devices with a block size other than 512
/// bytes.
#[derive(Clone)]
pub struct Block {
/// The 512 bytes in this block (or sector).
pub contents: [u8; Block::LEN],
}
pub type Block = [u8; BLOCK_LEN];

impl Block {
/// All our blocks are a fixed length of 512 bytes. We do not support
/// 'Advanced Format' Hard Drives with 4 KiB blocks, nor weird old
/// pre-3.5-inch floppy disk formats.
pub const LEN: usize = 512;
/// All our blocks are a fixed length of 512 bytes. We do not support
/// 'Advanced Format' Hard Drives with 4 KiB blocks, nor weird old
/// pre-3.5-inch floppy disk formats.
pub const BLOCK_LEN: usize = 512;

/// Sometimes we want `LEN` as a `u32` and the casts don't look nice.
pub const LEN_U32: u32 = 512;
/// Sometimes we want `LEN` as a `u32` and the casts don't look nice.
pub const BLOCK_LEN_U32: u32 = 512;

/// Create a new block full of zeros.
pub fn new() -> Block {
Block {
contents: [0u8; Self::LEN],
}
}
}

impl core::ops::Deref for Block {
type Target = [u8; 512];
fn deref(&self) -> &[u8; 512] {
&self.contents
}
}

impl core::ops::DerefMut for Block {
fn deref_mut(&mut self) -> &mut [u8; 512] {
&mut self.contents
}
}

impl core::fmt::Debug for Block {
fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
writeln!(fmt, "Block:")?;
for line in self.contents.chunks(32) {
for b in line {
write!(fmt, "{:02x}", b)?;
}
write!(fmt, " ")?;
for &b in line {
if (0x20..=0x7F).contains(&b) {
write!(fmt, "{}", b as char)?;
} else {
write!(fmt, ".")?;
}
}
writeln!(fmt)?;
}
Ok(())
}
}

impl Default for Block {
fn default() -> Self {
Self::new()
}
}

/// A block device - a device which can read and write blocks (or
/// sectors). Only supports devices which are <= 2 TiB in size.
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>;
/// Write one or more blocks, starting at the given block index.
fn write(&self, blocks: &[Block], start_block_idx: BlockIdx) -> Result<(), Self::Error>;
/// Determine how many blocks this device can hold.
fn num_blocks(&self) -> Result<BlockCount, Self::Error>;
}
/// Sometimes we want `LEN` as a `u64` and the casts don't look nice.
pub const BLOCK_LEN_U64: u64 = 512;

/// A caching layer for block devices
///
Expand All @@ -93,7 +32,7 @@ pub trait BlockDevice {
pub struct BlockCache<D> {
block_device: D,
block: [Block; 1],
block_idx: Option<BlockIdx>,
block_idx: Option<u64>,
}

impl<D> BlockCache<D>
Expand All @@ -104,26 +43,26 @@ where
pub fn new(block_device: D) -> BlockCache<D> {
BlockCache {
block_device,
block: [Block::new()],
block: [[0; BLOCK_LEN]],
block_idx: None,
}
}

/// Read a block, and return a reference to it.
pub fn read(&mut self, block_idx: BlockIdx) -> Result<&Block, D::Error> {
pub fn read(&mut self, block_idx: u64) -> 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(block_idx, &mut self.block)?;
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 fn read_mut(&mut self, block_idx: u64) -> 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(block_idx, &mut self.block)?;
self.block_idx = Some(block_idx);
}
Ok(&mut self.block[0])
Expand All @@ -132,13 +71,13 @@ where
/// Write back a block you read with [`Self::read_mut`] and then modified.
pub fn write_back(&mut self) -> Result<(), D::Error> {
self.block_device.write(
&self.block,
self.block_idx.expect("write_back with no read"),
&self.block,
)
}

/// Access a blank sector
pub fn blank_mut(&mut self, block_idx: BlockIdx) -> &mut Block {
pub fn blank_mut(&mut self, block_idx: u64) -> &mut Block {
self.block_idx = Some(block_idx);
for b in self.block[0].iter_mut() {
*b = 0;
Expand Down Expand Up @@ -173,7 +112,7 @@ impl BlockIdx {
/// volume. Useful if your underlying block device actually works in
/// bytes, like `open("/dev/mmcblk0")` does on Linux.
pub fn into_bytes(self) -> u64 {
(u64::from(self.0)) * (Block::LEN as u64)
u64::from(self.0) * BLOCK_LEN_U64
}

/// Create an iterator from the current `BlockIdx` through the given
Expand All @@ -183,6 +122,12 @@ impl BlockIdx {
}
}

impl From<BlockIdx> for u64 {
fn from(value: BlockIdx) -> Self {
value.0.into()
}
}

impl core::ops::Add<BlockCount> for BlockIdx {
type Output = BlockIdx;
fn add(self, rhs: BlockCount) -> BlockIdx {
Expand Down Expand Up @@ -254,8 +199,8 @@ impl BlockCount {
/// assert_eq!(BlockCount::from_bytes(1025), BlockCount(3));
/// ```
pub const fn from_bytes(byte_count: u32) -> BlockCount {
let mut count = byte_count / Block::LEN_U32;
if (count * Block::LEN_U32) != byte_count {
let mut count = byte_count / BLOCK_LEN_U32;
if (count * BLOCK_LEN_U32) != byte_count {
count += 1;
}
BlockCount(count)
Expand All @@ -264,7 +209,7 @@ impl BlockCount {
/// Take a number of blocks and increment by the integer number of blocks
/// required to get to the block that holds the byte at the given offset.
pub fn offset_bytes(self, offset: u32) -> Self {
BlockCount(self.0 + (offset / Block::LEN_U32))
BlockCount(self.0 + (offset / BLOCK_LEN_U32))
}
}

Expand Down
Loading

0 comments on commit 936d63f

Please sign in to comment.