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 30, 2024
1 parent 52e0e4f commit ee3c688
Show file tree
Hide file tree
Showing 11 changed files with 403 additions and 450 deletions.
3 changes: 2 additions & 1 deletion 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", default-features = false }
heapless = "^0.8"
log = {version = "0.4", default-features = false, optional = true}

Expand All @@ -28,5 +29,5 @@ sha2 = "0.10"

[features]
default = ["log"]
defmt-log = ["dep:defmt"]
defmt-log = ["dep:defmt", "embedded-storage/defmt"]
log = ["dep:log"]
26 changes: 16 additions & 10 deletions examples/linux/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
//! Helpers for using embedded-sdmmc on Linux

use chrono::Timelike;
use embedded_sdmmc::{Block, BlockCount, BlockDevice, BlockIdx, TimeSource, Timestamp};
use embedded_sdmmc::{
blockdevice::BLOCK_LEN_U64, Block, BlockCount, BlockDevice, BlockIdx, TimeSource, Timestamp,
};
use std::cell::RefCell;
use std::fs::{File, OpenOptions};
use std::io::prelude::*;
Expand Down Expand Up @@ -34,35 +36,39 @@ 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,
blocks: &mut [Block],
first_block_index: BlockIdx,
) -> Result<(), Self::Error> {
self.file
.borrow_mut()
.seek(SeekFrom::Start(start_block_idx.into_bytes()))?;
.seek(SeekFrom::Start(first_block_index.0 * 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, blocks: &[Block], first_block_index: BlockIdx) -> Result<(), Self::Error> {
self.file
.borrow_mut()
.seek(SeekFrom::Start(start_block_idx.into_bytes()))?;
.seek(SeekFrom::Start(first_block_index.0 * 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<BlockCount, Self::Error> {
let num_blocks = self.file.borrow().metadata().unwrap().len() / 512;
Ok(BlockCount(num_blocks as u32))
Ok(BlockCount(num_blocks))
}
}

Expand Down
215 changes: 39 additions & 176 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::block::{BlockCount, BlockDevice, BlockIdx};

/// 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],
}

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;

/// Sometimes we want `LEN` as a `u32` and the casts don't look nice.
pub const LEN_U32: u32 = 512;
pub type Block = [u8; BLOCK_LEN];

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

impl core::ops::Deref for Block {
type Target = [u8; 512];
fn deref(&self) -> &[u8; 512] {
&self.contents
}
}
/// Sometimes we want `LEN` as a `u32` and the casts don't look nice.
pub const BLOCK_LEN_U32: u32 = 512;

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 @@ -104,7 +43,7 @@ where
pub fn new(block_device: D) -> BlockCache<D> {
BlockCache {
block_device,
block: [Block::new()],
block: [[0; BLOCK_LEN]],
block_idx: None,
}
}
Expand Down Expand Up @@ -160,112 +99,36 @@ where
}
}

/// The linear numeric address of a block (or sector).
///
/// The first block on a disk gets `BlockIdx(0)` (which usually contains the
/// Master Boot Record).
#[cfg_attr(feature = "defmt-log", derive(defmt::Format))]
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct BlockIdx(pub u32);

impl BlockIdx {
/// Convert a block index into a 64-bit byte offset from the start of the
/// 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)
}

/// Create an iterator from the current `BlockIdx` through the given
/// number of blocks.
pub fn range(self, num: BlockCount) -> BlockIter {
BlockIter::new(self, self + BlockCount(num.0))
}
}

impl core::ops::Add<BlockCount> for BlockIdx {
type Output = BlockIdx;
fn add(self, rhs: BlockCount) -> BlockIdx {
BlockIdx(self.0 + rhs.0)
}
}

impl core::ops::AddAssign<BlockCount> for BlockIdx {
fn add_assign(&mut self, rhs: BlockCount) {
self.0 += rhs.0
}
}

impl core::ops::Sub<BlockCount> for BlockIdx {
type Output = BlockIdx;
fn sub(self, rhs: BlockCount) -> BlockIdx {
BlockIdx(self.0 - rhs.0)
}
/// Convert a block index into a 64-bit byte offset from the start of the
/// volume. Useful if your underlying block device actually works in
/// bytes, like `open("/dev/mmcblk0")` does on Linux.
pub fn block_index_into_bytes(block_index: BlockIdx) -> u64 {
block_index.0 * BLOCK_LEN_U64
}

impl core::ops::SubAssign<BlockCount> for BlockIdx {
fn sub_assign(&mut self, rhs: BlockCount) {
self.0 -= rhs.0
}
}

/// The a number of blocks (or sectors).
/// How many blocks are required to hold this many bytes.
///
/// Add this to a `BlockIdx` to get an actual address on disk.
#[cfg_attr(feature = "defmt-log", derive(defmt::Format))]
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct BlockCount(pub u32);

impl core::ops::Add<BlockCount> for BlockCount {
type Output = BlockCount;
fn add(self, rhs: BlockCount) -> BlockCount {
BlockCount(self.0 + rhs.0)
}
}

impl core::ops::AddAssign<BlockCount> for BlockCount {
fn add_assign(&mut self, rhs: BlockCount) {
self.0 += rhs.0
}
}

impl core::ops::Sub<BlockCount> for BlockCount {
type Output = BlockCount;
fn sub(self, rhs: BlockCount) -> BlockCount {
BlockCount(self.0 - rhs.0)
}
}

impl core::ops::SubAssign<BlockCount> for BlockCount {
fn sub_assign(&mut self, rhs: BlockCount) {
self.0 -= rhs.0
}
}

impl BlockCount {
/// How many blocks are required to hold this many bytes.
///
/// ```
/// # use embedded_sdmmc::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));
/// assert_eq!(BlockCount::from_bytes(1024), BlockCount(2));
/// 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 {
count += 1;
}
BlockCount(count)
}

/// 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))
}
/// ```
/// # use embedded_sdmmc::blockdevice::{block_count_from_bytes};
/// # use embedded_storage::block::BlockCount;
/// assert_eq!(block_count_from_bytes(511), BlockCount(1));
/// assert_eq!(block_count_from_bytes(512), BlockCount(1));
/// assert_eq!(block_count_from_bytes(513), BlockCount(2));
/// assert_eq!(block_count_from_bytes(1024), BlockCount(2));
/// assert_eq!(block_count_from_bytes(1025), BlockCount(3));
/// ```
pub const fn block_count_from_bytes(byte_count: u64) -> BlockCount {
let mut count = byte_count / BLOCK_LEN_U64;
if (count * BLOCK_LEN_U64) != byte_count {
count += 1;
}
BlockCount(count)
}

/// 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 block_count_offset_bytes(base: BlockCount, offset: u64) -> BlockCount {
BlockCount(base.0 + (offset / BLOCK_LEN_U64))
}

/// An iterator returned from `Block::range`.
Expand Down
11 changes: 7 additions & 4 deletions src/fat/bpb.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
//! Boot Parameter Block

use crate::{
blockdevice::BlockCount,
blockdevice::block_count_from_bytes,
fat::{FatType, OnDiskDirEntry},
};
use byteorder::{ByteOrder, LittleEndian};
use embedded_storage::block::BlockCount;

/// A Boot Parameter Block.
///
Expand All @@ -30,8 +31,10 @@ impl<'a> Bpb<'a> {
return Err("Bad BPB footer");
}

let root_dir_blocks =
BlockCount::from_bytes(u32::from(bpb.root_entries_count()) * OnDiskDirEntry::LEN_U32).0;
let root_dir_blocks = block_count_from_bytes(
u64::from(bpb.root_entries_count()) * u64::from(OnDiskDirEntry::LEN_U32),
)
.0 as u32;
let non_data_blocks = u32::from(bpb.reserved_block_count())
+ (u32::from(bpb.num_fats()) * bpb.fat_size())
+ root_dir_blocks;
Expand Down Expand Up @@ -101,7 +104,7 @@ impl<'a> Bpb<'a> {
pub fn fs_info_block(&self) -> Option<BlockCount> {
match self.fat_type {
FatType::Fat16 => None,
FatType::Fat32 => Some(BlockCount(u32::from(self.fs_info()))),
FatType::Fat32 => Some(BlockCount(self.fs_info().into())),
}
}

Expand Down
Loading

0 comments on commit ee3c688

Please sign in to comment.