1 | use core::cell::RefCell; |
2 | |
3 | use embassy_sync::blocking_mutex::raw::RawMutex; |
4 | use embassy_sync::blocking_mutex::Mutex; |
5 | use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash}; |
6 | |
7 | use super::Error; |
8 | |
9 | /// A logical partition of an underlying shared flash |
10 | /// |
11 | /// A partition holds an offset and a size of the flash, |
12 | /// and is restricted to operate with that range. |
13 | /// There is no guarantee that muliple partitions on the same flash |
14 | /// operate on mutually exclusive ranges - such a separation is up to |
15 | /// the user to guarantee. |
16 | pub struct BlockingPartition<'a, M: RawMutex, T: NorFlash> { |
17 | flash: &'a Mutex<M, RefCell<T>>, |
18 | offset: u32, |
19 | size: u32, |
20 | } |
21 | |
22 | impl<'a, M: RawMutex, T: NorFlash> Clone for BlockingPartition<'a, M, T> { |
23 | fn clone(&self) -> Self { |
24 | Self { |
25 | flash: self.flash, |
26 | offset: self.offset, |
27 | size: self.size, |
28 | } |
29 | } |
30 | } |
31 | |
32 | impl<'a, M: RawMutex, T: NorFlash> BlockingPartition<'a, M, T> { |
33 | /// Create a new partition |
34 | pub const fn new(flash: &'a Mutex<M, RefCell<T>>, offset: u32, size: u32) -> Self { |
35 | if offset % T::READ_SIZE as u32 != 0 || offset % T::WRITE_SIZE as u32 != 0 || offset % T::ERASE_SIZE as u32 != 0 |
36 | { |
37 | panic!("Partition offset must be a multiple of read, write and erase size" ); |
38 | } |
39 | if size % T::READ_SIZE as u32 != 0 || size % T::WRITE_SIZE as u32 != 0 || size % T::ERASE_SIZE as u32 != 0 { |
40 | panic!("Partition size must be a multiple of read, write and erase size" ); |
41 | } |
42 | Self { flash, offset, size } |
43 | } |
44 | |
45 | /// Get the partition offset within the flash |
46 | pub const fn offset(&self) -> u32 { |
47 | self.offset |
48 | } |
49 | |
50 | /// Get the partition size |
51 | pub const fn size(&self) -> u32 { |
52 | self.size |
53 | } |
54 | } |
55 | |
56 | impl<M: RawMutex, T: NorFlash> ErrorType for BlockingPartition<'_, M, T> { |
57 | type Error = Error<T::Error>; |
58 | } |
59 | |
60 | impl<M: RawMutex, T: NorFlash> ReadNorFlash for BlockingPartition<'_, M, T> { |
61 | const READ_SIZE: usize = T::READ_SIZE; |
62 | |
63 | fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { |
64 | if offset + bytes.len() as u32 > self.size { |
65 | return Err(Error::OutOfBounds); |
66 | } |
67 | |
68 | self.flash.lock(|flash: &RefCell| { |
69 | flash |
70 | .borrow_mut() |
71 | .read(self.offset + offset, bytes) |
72 | .map_err(op:Error::Flash) |
73 | }) |
74 | } |
75 | |
76 | fn capacity(&self) -> usize { |
77 | self.size as usize |
78 | } |
79 | } |
80 | |
81 | impl<M: RawMutex, T: NorFlash> NorFlash for BlockingPartition<'_, M, T> { |
82 | const WRITE_SIZE: usize = T::WRITE_SIZE; |
83 | const ERASE_SIZE: usize = T::ERASE_SIZE; |
84 | |
85 | fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { |
86 | if offset + bytes.len() as u32 > self.size { |
87 | return Err(Error::OutOfBounds); |
88 | } |
89 | |
90 | self.flash.lock(|flash| { |
91 | flash |
92 | .borrow_mut() |
93 | .write(self.offset + offset, bytes) |
94 | .map_err(Error::Flash) |
95 | }) |
96 | } |
97 | |
98 | fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { |
99 | if to > self.size { |
100 | return Err(Error::OutOfBounds); |
101 | } |
102 | |
103 | self.flash.lock(|flash| { |
104 | flash |
105 | .borrow_mut() |
106 | .erase(self.offset + from, self.offset + to) |
107 | .map_err(Error::Flash) |
108 | }) |
109 | } |
110 | } |
111 | |
112 | #[cfg (test)] |
113 | mod tests { |
114 | use embassy_sync::blocking_mutex::raw::NoopRawMutex; |
115 | |
116 | use super::*; |
117 | use crate::flash::mem_flash::MemFlash; |
118 | |
119 | #[test ] |
120 | fn can_read() { |
121 | let mut flash = MemFlash::<1024, 128, 4>::default(); |
122 | flash.mem[132..132 + 8].fill(0xAA); |
123 | |
124 | let flash = Mutex::<NoopRawMutex, _>::new(RefCell::new(flash)); |
125 | let mut partition = BlockingPartition::new(&flash, 128, 256); |
126 | |
127 | let mut read_buf = [0; 8]; |
128 | partition.read(4, &mut read_buf).unwrap(); |
129 | |
130 | assert!(read_buf.iter().position(|&x| x != 0xAA).is_none()); |
131 | } |
132 | |
133 | #[test ] |
134 | fn can_write() { |
135 | let flash = MemFlash::<1024, 128, 4>::default(); |
136 | |
137 | let flash = Mutex::<NoopRawMutex, _>::new(RefCell::new(flash)); |
138 | let mut partition = BlockingPartition::new(&flash, 128, 256); |
139 | |
140 | let write_buf = [0xAA; 8]; |
141 | partition.write(4, &write_buf).unwrap(); |
142 | |
143 | let flash = flash.into_inner().take(); |
144 | assert!(flash.mem[132..132 + 8].iter().position(|&x| x != 0xAA).is_none()); |
145 | } |
146 | |
147 | #[test ] |
148 | fn can_erase() { |
149 | let flash = MemFlash::<1024, 128, 4>::new(0x00); |
150 | |
151 | let flash = Mutex::<NoopRawMutex, _>::new(RefCell::new(flash)); |
152 | let mut partition = BlockingPartition::new(&flash, 128, 256); |
153 | |
154 | partition.erase(0, 128).unwrap(); |
155 | |
156 | let flash = flash.into_inner().take(); |
157 | assert!(flash.mem[128..256].iter().position(|&x| x != 0xFF).is_none()); |
158 | } |
159 | } |
160 | |