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