embedded_sdmmc/
blockdevice.rs

1//! Traits and types for working with Block Devices.
2//!
3//! Generic code for handling block devices, such as types for identifying
4//! a particular block on a block device by its index.
5
6/// A standard 512 byte block (also known as a sector).
7///
8/// IBM PC formatted 5.25" and 3.5" floppy disks, IDE/SATA Hard Drives up to
9/// about 2 TiB, and almost all SD/MMC cards have 512 byte blocks.
10///
11/// This library does not support devices with a block size other than 512
12/// bytes.
13#[derive(Clone)]
14pub struct Block {
15    /// The 512 bytes in this block (or sector).
16    pub contents: [u8; Block::LEN],
17}
18
19/// The linear numeric address of a block (or sector).
20///
21/// The first block on a disk gets `BlockIdx(0)` (which usually contains the
22/// Master Boot Record).
23#[cfg_attr(feature = "defmt-log", derive(defmt::Format))]
24#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
25pub struct BlockIdx(pub u32);
26
27/// The a number of blocks (or sectors).
28///
29/// Add this to a `BlockIdx` to get an actual address on disk.
30#[cfg_attr(feature = "defmt-log", derive(defmt::Format))]
31#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
32pub struct BlockCount(pub u32);
33
34/// An iterator returned from `Block::range`.
35pub struct BlockIter {
36    inclusive_end: BlockIdx,
37    current: BlockIdx,
38}
39
40/// A block device - a device which can read and write blocks (or
41/// sectors). Only supports devices which are <= 2 TiB in size.
42pub trait BlockDevice {
43    /// The errors that the `BlockDevice` can return. Must be debug formattable.
44    type Error: core::fmt::Debug;
45    /// Read one or more blocks, starting at the given block index.
46    fn read(
47        &self,
48        blocks: &mut [Block],
49        start_block_idx: BlockIdx,
50        reason: &str,
51    ) -> Result<(), Self::Error>;
52    /// Write one or more blocks, starting at the given block index.
53    fn write(&self, blocks: &[Block], start_block_idx: BlockIdx) -> Result<(), Self::Error>;
54    /// Determine how many blocks this device can hold.
55    fn num_blocks(&self) -> Result<BlockCount, Self::Error>;
56}
57
58impl Block {
59    /// All our blocks are a fixed length of 512 bytes. We do not support
60    /// 'Advanced Format' Hard Drives with 4 KiB blocks, nor weird old
61    /// pre-3.5-inch floppy disk formats.
62    pub const LEN: usize = 512;
63
64    /// Sometimes we want `LEN` as a `u32` and the casts don't look nice.
65    pub const LEN_U32: u32 = 512;
66
67    /// Create a new block full of zeros.
68    pub fn new() -> Block {
69        Block {
70            contents: [0u8; Self::LEN],
71        }
72    }
73}
74
75impl Default for Block {
76    fn default() -> Self {
77        Self::new()
78    }
79}
80
81impl core::ops::Add<BlockCount> for BlockIdx {
82    type Output = BlockIdx;
83    fn add(self, rhs: BlockCount) -> BlockIdx {
84        BlockIdx(self.0 + rhs.0)
85    }
86}
87
88impl core::ops::AddAssign<BlockCount> for BlockIdx {
89    fn add_assign(&mut self, rhs: BlockCount) {
90        self.0 += rhs.0
91    }
92}
93
94impl core::ops::Add<BlockCount> for BlockCount {
95    type Output = BlockCount;
96    fn add(self, rhs: BlockCount) -> BlockCount {
97        BlockCount(self.0 + rhs.0)
98    }
99}
100
101impl core::ops::AddAssign<BlockCount> for BlockCount {
102    fn add_assign(&mut self, rhs: BlockCount) {
103        self.0 += rhs.0
104    }
105}
106
107impl core::ops::Sub<BlockCount> for BlockIdx {
108    type Output = BlockIdx;
109    fn sub(self, rhs: BlockCount) -> BlockIdx {
110        BlockIdx(self.0 - rhs.0)
111    }
112}
113
114impl core::ops::SubAssign<BlockCount> for BlockIdx {
115    fn sub_assign(&mut self, rhs: BlockCount) {
116        self.0 -= rhs.0
117    }
118}
119
120impl core::ops::Sub<BlockCount> for BlockCount {
121    type Output = BlockCount;
122    fn sub(self, rhs: BlockCount) -> BlockCount {
123        BlockCount(self.0 - rhs.0)
124    }
125}
126
127impl core::ops::SubAssign<BlockCount> for BlockCount {
128    fn sub_assign(&mut self, rhs: BlockCount) {
129        self.0 -= rhs.0
130    }
131}
132
133impl core::ops::Deref for Block {
134    type Target = [u8; 512];
135    fn deref(&self) -> &[u8; 512] {
136        &self.contents
137    }
138}
139
140impl core::ops::DerefMut for Block {
141    fn deref_mut(&mut self) -> &mut [u8; 512] {
142        &mut self.contents
143    }
144}
145
146impl core::fmt::Debug for Block {
147    fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
148        writeln!(fmt, "Block:")?;
149        for line in self.contents.chunks(32) {
150            for b in line {
151                write!(fmt, "{:02x}", b)?;
152            }
153            write!(fmt, " ")?;
154            for &b in line {
155                if (0x20..=0x7F).contains(&b) {
156                    write!(fmt, "{}", b as char)?;
157                } else {
158                    write!(fmt, ".")?;
159                }
160            }
161            writeln!(fmt)?;
162        }
163        Ok(())
164    }
165}
166
167impl BlockIdx {
168    /// Convert a block index into a 64-bit byte offset from the start of the
169    /// volume. Useful if your underlying block device actually works in
170    /// bytes, like `open("/dev/mmcblk0")` does on Linux.
171    pub fn into_bytes(self) -> u64 {
172        (u64::from(self.0)) * (Block::LEN as u64)
173    }
174
175    /// Create an iterator from the current `BlockIdx` through the given
176    /// number of blocks.
177    pub fn range(self, num: BlockCount) -> BlockIter {
178        BlockIter::new(self, self + BlockCount(num.0))
179    }
180}
181
182impl BlockCount {
183    /// How many blocks are required to hold this many bytes.
184    ///
185    /// ```
186    /// # use embedded_sdmmc::BlockCount;
187    /// assert_eq!(BlockCount::from_bytes(511), BlockCount(1));
188    /// assert_eq!(BlockCount::from_bytes(512), BlockCount(1));
189    /// assert_eq!(BlockCount::from_bytes(513), BlockCount(2));
190    /// assert_eq!(BlockCount::from_bytes(1024), BlockCount(2));
191    /// assert_eq!(BlockCount::from_bytes(1025), BlockCount(3));
192    /// ```
193    pub const fn from_bytes(byte_count: u32) -> BlockCount {
194        let mut count = byte_count / Block::LEN_U32;
195        if (count * Block::LEN_U32) != byte_count {
196            count += 1;
197        }
198        BlockCount(count)
199    }
200
201    /// Take a number of blocks and increment by the integer number of blocks
202    /// required to get to the block that holds the byte at the given offset.
203    pub fn offset_bytes(self, offset: u32) -> Self {
204        BlockCount(self.0 + (offset / Block::LEN_U32))
205    }
206}
207
208impl BlockIter {
209    /// Create a new `BlockIter`, from the given start block, through (and
210    /// including) the given end block.
211    pub const fn new(start: BlockIdx, inclusive_end: BlockIdx) -> BlockIter {
212        BlockIter {
213            inclusive_end,
214            current: start,
215        }
216    }
217}
218
219impl core::iter::Iterator for BlockIter {
220    type Item = BlockIdx;
221    fn next(&mut self) -> Option<Self::Item> {
222        if self.current.0 >= self.inclusive_end.0 {
223            None
224        } else {
225            let this = self.current;
226            self.current += BlockCount(1);
227            Some(this)
228        }
229    }
230}
231
232// ****************************************************************************
233//
234// End Of File
235//
236// ****************************************************************************