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
19impl Block {
20    /// All our blocks are a fixed length of 512 bytes. We do not support
21    /// 'Advanced Format' Hard Drives with 4 KiB blocks, nor weird old
22    /// pre-3.5-inch floppy disk formats.
23    pub const LEN: usize = 512;
24
25    /// Sometimes we want `LEN` as a `u32` and the casts don't look nice.
26    pub const LEN_U32: u32 = 512;
27
28    /// Create a new block full of zeros.
29    pub fn new() -> Block {
30        Block {
31            contents: [0u8; Self::LEN],
32        }
33    }
34}
35
36impl core::ops::Deref for Block {
37    type Target = [u8; 512];
38    fn deref(&self) -> &[u8; 512] {
39        &self.contents
40    }
41}
42
43impl core::ops::DerefMut for Block {
44    fn deref_mut(&mut self) -> &mut [u8; 512] {
45        &mut self.contents
46    }
47}
48
49impl core::fmt::Debug for Block {
50    fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
51        writeln!(fmt, "Block:")?;
52        for line in self.contents.chunks(32) {
53            for b in line {
54                write!(fmt, "{:02x}", b)?;
55            }
56            write!(fmt, " ")?;
57            for &b in line {
58                if (0x20..=0x7F).contains(&b) {
59                    write!(fmt, "{}", b as char)?;
60                } else {
61                    write!(fmt, ".")?;
62                }
63            }
64            writeln!(fmt)?;
65        }
66        Ok(())
67    }
68}
69
70impl Default for Block {
71    fn default() -> Self {
72        Self::new()
73    }
74}
75
76/// A block device - a device which can read and write blocks (or
77/// sectors). Only supports devices which are <= 2 TiB in size.
78pub trait BlockDevice {
79    /// The errors that the `BlockDevice` can return. Must be debug formattable.
80    type Error: core::fmt::Debug;
81    /// Read one or more blocks, starting at the given block index.
82    fn read(&self, blocks: &mut [Block], start_block_idx: BlockIdx) -> Result<(), Self::Error>;
83    /// Write one or more blocks, starting at the given block index.
84    fn write(&self, blocks: &[Block], start_block_idx: BlockIdx) -> Result<(), Self::Error>;
85    /// Determine how many blocks this device can hold.
86    fn num_blocks(&self) -> Result<BlockCount, Self::Error>;
87}
88
89/// A caching layer for block devices
90///
91/// Caches a single block.
92#[derive(Debug)]
93pub struct BlockCache<D> {
94    block_device: D,
95    block: [Block; 1],
96    block_idx: Option<BlockIdx>,
97}
98
99impl<D> BlockCache<D>
100where
101    D: BlockDevice,
102{
103    /// Create a new block cache
104    pub fn new(block_device: D) -> BlockCache<D> {
105        BlockCache {
106            block_device,
107            block: [Block::new()],
108            block_idx: None,
109        }
110    }
111
112    /// Read a block, and return a reference to it.
113    pub fn read(&mut self, block_idx: BlockIdx) -> Result<&Block, D::Error> {
114        if self.block_idx != Some(block_idx) {
115            self.block_idx = None;
116            self.block_device.read(&mut self.block, block_idx)?;
117            self.block_idx = Some(block_idx);
118        }
119        Ok(&self.block[0])
120    }
121
122    /// Read a block, and return a reference to it.
123    pub fn read_mut(&mut self, block_idx: BlockIdx) -> Result<&mut Block, D::Error> {
124        if self.block_idx != Some(block_idx) {
125            self.block_idx = None;
126            self.block_device.read(&mut self.block, block_idx)?;
127            self.block_idx = Some(block_idx);
128        }
129        Ok(&mut self.block[0])
130    }
131
132    /// Write back a block you read with [`Self::read_mut`] and then modified.
133    pub fn write_back(&mut self) -> Result<(), D::Error> {
134        self.block_device.write(
135            &self.block,
136            self.block_idx.expect("write_back with no read"),
137        )
138    }
139
140    /// Write back a block you read with [`Self::read_mut`] and then modified, but to two locations.
141    ///
142    /// This is useful for updating two File Allocation Tables.
143    pub fn write_back_with_duplicate(&mut self, duplicate: BlockIdx) -> Result<(), D::Error> {
144        self.block_device.write(
145            &self.block,
146            self.block_idx.expect("write_back with no read"),
147        )?;
148        self.block_device.write(&self.block, duplicate)?;
149        Ok(())
150    }
151
152    /// Access a blank sector
153    pub fn blank_mut(&mut self, block_idx: BlockIdx) -> &mut Block {
154        self.block_idx = Some(block_idx);
155        self.block[0].fill(0);
156        &mut self.block[0]
157    }
158
159    /// Access the block device
160    pub fn block_device(&mut self) -> &mut D {
161        // invalidate the cache
162        self.block_idx = None;
163        // give them the block device
164        &mut self.block_device
165    }
166
167    /// Get the block device back
168    pub fn free(self) -> D {
169        self.block_device
170    }
171}
172
173/// The linear numeric address of a block (or sector).
174///
175/// The first block on a disk gets `BlockIdx(0)` (which usually contains the
176/// Master Boot Record).
177#[cfg_attr(feature = "defmt-log", derive(defmt::Format))]
178#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
179pub struct BlockIdx(pub u32);
180
181impl BlockIdx {
182    /// Convert a block index into a 64-bit byte offset from the start of the
183    /// volume. Useful if your underlying block device actually works in
184    /// bytes, like `open("/dev/mmcblk0")` does on Linux.
185    pub fn into_bytes(self) -> u64 {
186        (u64::from(self.0)) * (Block::LEN as u64)
187    }
188
189    /// Create an iterator from the current `BlockIdx` through the given
190    /// number of blocks.
191    pub fn range(self, num: BlockCount) -> BlockIter {
192        BlockIter::new(self, self + BlockCount(num.0))
193    }
194}
195
196impl core::ops::Add<BlockCount> for BlockIdx {
197    type Output = BlockIdx;
198    fn add(self, rhs: BlockCount) -> BlockIdx {
199        BlockIdx(self.0 + rhs.0)
200    }
201}
202
203impl core::ops::AddAssign<BlockCount> for BlockIdx {
204    fn add_assign(&mut self, rhs: BlockCount) {
205        self.0 += rhs.0
206    }
207}
208
209impl core::ops::Sub<BlockCount> for BlockIdx {
210    type Output = BlockIdx;
211    fn sub(self, rhs: BlockCount) -> BlockIdx {
212        BlockIdx(self.0 - rhs.0)
213    }
214}
215
216impl core::ops::SubAssign<BlockCount> for BlockIdx {
217    fn sub_assign(&mut self, rhs: BlockCount) {
218        self.0 -= rhs.0
219    }
220}
221
222/// The a number of blocks (or sectors).
223///
224/// Add this to a `BlockIdx` to get an actual address on disk.
225#[cfg_attr(feature = "defmt-log", derive(defmt::Format))]
226#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
227pub struct BlockCount(pub u32);
228
229impl core::ops::Add<BlockCount> for BlockCount {
230    type Output = BlockCount;
231    fn add(self, rhs: BlockCount) -> BlockCount {
232        BlockCount(self.0 + rhs.0)
233    }
234}
235
236impl core::ops::AddAssign<BlockCount> for BlockCount {
237    fn add_assign(&mut self, rhs: BlockCount) {
238        self.0 += rhs.0
239    }
240}
241
242impl core::ops::Sub<BlockCount> for BlockCount {
243    type Output = BlockCount;
244    fn sub(self, rhs: BlockCount) -> BlockCount {
245        BlockCount(self.0 - rhs.0)
246    }
247}
248
249impl core::ops::SubAssign<BlockCount> for BlockCount {
250    fn sub_assign(&mut self, rhs: BlockCount) {
251        self.0 -= rhs.0
252    }
253}
254
255impl BlockCount {
256    /// How many blocks are required to hold this many bytes.
257    ///
258    /// ```
259    /// # use embedded_sdmmc::BlockCount;
260    /// assert_eq!(BlockCount::from_bytes(511), BlockCount(1));
261    /// assert_eq!(BlockCount::from_bytes(512), BlockCount(1));
262    /// assert_eq!(BlockCount::from_bytes(513), BlockCount(2));
263    /// assert_eq!(BlockCount::from_bytes(1024), BlockCount(2));
264    /// assert_eq!(BlockCount::from_bytes(1025), BlockCount(3));
265    /// ```
266    pub const fn from_bytes(byte_count: u32) -> BlockCount {
267        let mut count = byte_count / Block::LEN_U32;
268        if (count * Block::LEN_U32) != byte_count {
269            count += 1;
270        }
271        BlockCount(count)
272    }
273
274    /// Take a number of blocks and increment by the integer number of blocks
275    /// required to get to the block that holds the byte at the given offset.
276    pub fn offset_bytes(self, offset: u32) -> Self {
277        BlockCount(self.0 + (offset / Block::LEN_U32))
278    }
279}
280
281/// An iterator returned from `Block::range`.
282pub struct BlockIter {
283    inclusive_end: BlockIdx,
284    current: BlockIdx,
285}
286
287impl BlockIter {
288    /// Create a new `BlockIter`, from the given start block, through (and
289    /// including) the given end block.
290    pub const fn new(start: BlockIdx, inclusive_end: BlockIdx) -> BlockIter {
291        BlockIter {
292            inclusive_end,
293            current: start,
294        }
295    }
296}
297
298impl core::iter::Iterator for BlockIter {
299    type Item = BlockIdx;
300    fn next(&mut self) -> Option<Self::Item> {
301        if self.current.0 >= self.inclusive_end.0 {
302            None
303        } else {
304            let this = self.current;
305            self.current += BlockCount(1);
306            Some(this)
307        }
308    }
309}
310
311// ****************************************************************************
312//
313// End Of File
314//
315// ****************************************************************************