aboutsummaryrefslogtreecommitdiff
path: root/embassy-embedded-hal/src/flash/concat_flash.rs
diff options
context:
space:
mode:
Diffstat (limited to 'embassy-embedded-hal/src/flash/concat_flash.rs')
-rw-r--r--embassy-embedded-hal/src/flash/concat_flash.rs228
1 files changed, 228 insertions, 0 deletions
diff --git a/embassy-embedded-hal/src/flash/concat_flash.rs b/embassy-embedded-hal/src/flash/concat_flash.rs
new file mode 100644
index 000000000..1ea84269c
--- /dev/null
+++ b/embassy-embedded-hal/src/flash/concat_flash.rs
@@ -0,0 +1,228 @@
1use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, ReadNorFlash};
2#[cfg(feature = "nightly")]
3use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash};
4
5/// Convenience helper for concatenating two consecutive flashes into one.
6/// This is especially useful if used with "flash regions", where one may
7/// want to concatenate multiple regions into one larger region.
8pub struct ConcatFlash<First, Second>(First, Second);
9
10impl<First, Second> ConcatFlash<First, Second> {
11 /// Create a new flash that concatenates two consecutive flashes.
12 pub fn new(first: First, second: Second) -> Self {
13 Self(first, second)
14 }
15}
16
17const fn get_read_size(first_read_size: usize, second_read_size: usize) -> usize {
18 if first_read_size != second_read_size {
19 panic!("The read size for the concatenated flashes must be the same");
20 }
21 first_read_size
22}
23
24const fn get_write_size(first_write_size: usize, second_write_size: usize) -> usize {
25 if first_write_size != second_write_size {
26 panic!("The write size for the concatenated flashes must be the same");
27 }
28 first_write_size
29}
30
31const fn get_max_erase_size(first_erase_size: usize, second_erase_size: usize) -> usize {
32 let max_erase_size = if first_erase_size > second_erase_size {
33 first_erase_size
34 } else {
35 second_erase_size
36 };
37 if max_erase_size % first_erase_size != 0 || max_erase_size % second_erase_size != 0 {
38 panic!("The erase sizes for the concatenated flashes must have have a gcd equal to the max erase size");
39 }
40 max_erase_size
41}
42
43impl<First, Second, E> ErrorType for ConcatFlash<First, Second>
44where
45 First: ErrorType<Error = E>,
46 Second: ErrorType<Error = E>,
47 E: NorFlashError,
48{
49 type Error = E;
50}
51
52impl<First, Second, E> ReadNorFlash for ConcatFlash<First, Second>
53where
54 First: ReadNorFlash<Error = E>,
55 Second: ReadNorFlash<Error = E>,
56 E: NorFlashError,
57{
58 const READ_SIZE: usize = get_read_size(First::READ_SIZE, Second::READ_SIZE);
59
60 fn read(&mut self, mut offset: u32, mut bytes: &mut [u8]) -> Result<(), E> {
61 if offset < self.0.capacity() as u32 {
62 let len = core::cmp::min(self.0.capacity() - offset as usize, bytes.len());
63 self.0.read(offset, &mut bytes[..len])?;
64 offset += len as u32;
65 bytes = &mut bytes[len..];
66 }
67
68 if !bytes.is_empty() {
69 self.1.read(offset - self.0.capacity() as u32, bytes)?;
70 }
71
72 Ok(())
73 }
74
75 fn capacity(&self) -> usize {
76 self.0.capacity() + self.1.capacity()
77 }
78}
79
80impl<First, Second, E> NorFlash for ConcatFlash<First, Second>
81where
82 First: NorFlash<Error = E>,
83 Second: NorFlash<Error = E>,
84 E: NorFlashError,
85{
86 const WRITE_SIZE: usize = get_write_size(First::WRITE_SIZE, Second::WRITE_SIZE);
87 const ERASE_SIZE: usize = get_max_erase_size(First::ERASE_SIZE, Second::ERASE_SIZE);
88
89 fn write(&mut self, mut offset: u32, mut bytes: &[u8]) -> Result<(), E> {
90 if offset < self.0.capacity() as u32 {
91 let len = core::cmp::min(self.0.capacity() - offset as usize, bytes.len());
92 self.0.write(offset, &bytes[..len])?;
93 offset += len as u32;
94 bytes = &bytes[len..];
95 }
96
97 if !bytes.is_empty() {
98 self.1.write(offset - self.0.capacity() as u32, bytes)?;
99 }
100
101 Ok(())
102 }
103
104 fn erase(&mut self, mut from: u32, to: u32) -> Result<(), E> {
105 if from < self.0.capacity() as u32 {
106 let to = core::cmp::min(self.0.capacity() as u32, to);
107 self.0.erase(from, to)?;
108 from = self.0.capacity() as u32;
109 }
110
111 if from < to {
112 self.1
113 .erase(from - self.0.capacity() as u32, to - self.0.capacity() as u32)?;
114 }
115
116 Ok(())
117 }
118}
119
120#[cfg(feature = "nightly")]
121impl<First, Second, E> AsyncReadNorFlash for ConcatFlash<First, Second>
122where
123 First: AsyncReadNorFlash<Error = E>,
124 Second: AsyncReadNorFlash<Error = E>,
125 E: NorFlashError,
126{
127 const READ_SIZE: usize = get_read_size(First::READ_SIZE, Second::READ_SIZE);
128
129 async fn read(&mut self, mut offset: u32, mut bytes: &mut [u8]) -> Result<(), E> {
130 if offset < self.0.capacity() as u32 {
131 let len = core::cmp::min(self.0.capacity() - offset as usize, bytes.len());
132 self.0.read(offset, &mut bytes[..len]).await?;
133 offset += len as u32;
134 bytes = &mut bytes[len..];
135 }
136
137 if !bytes.is_empty() {
138 self.1.read(offset - self.0.capacity() as u32, bytes).await?;
139 }
140
141 Ok(())
142 }
143
144 fn capacity(&self) -> usize {
145 self.0.capacity() + self.1.capacity()
146 }
147}
148
149#[cfg(feature = "nightly")]
150impl<First, Second, E> AsyncNorFlash for ConcatFlash<First, Second>
151where
152 First: AsyncNorFlash<Error = E>,
153 Second: AsyncNorFlash<Error = E>,
154 E: NorFlashError,
155{
156 const WRITE_SIZE: usize = get_write_size(First::WRITE_SIZE, Second::WRITE_SIZE);
157 const ERASE_SIZE: usize = get_max_erase_size(First::ERASE_SIZE, Second::ERASE_SIZE);
158
159 async fn write(&mut self, mut offset: u32, mut bytes: &[u8]) -> Result<(), E> {
160 if offset < self.0.capacity() as u32 {
161 let len = core::cmp::min(self.0.capacity() - offset as usize, bytes.len());
162 self.0.write(offset, &bytes[..len]).await?;
163 offset += len as u32;
164 bytes = &bytes[len..];
165 }
166
167 if !bytes.is_empty() {
168 self.1.write(offset - self.0.capacity() as u32, bytes).await?;
169 }
170
171 Ok(())
172 }
173
174 async fn erase(&mut self, mut from: u32, to: u32) -> Result<(), E> {
175 if from < self.0.capacity() as u32 {
176 let to = core::cmp::min(self.0.capacity() as u32, to);
177 self.0.erase(from, to).await?;
178 from = self.0.capacity() as u32;
179 }
180
181 if from < to {
182 self.1
183 .erase(from - self.0.capacity() as u32, to - self.0.capacity() as u32)
184 .await?;
185 }
186
187 Ok(())
188 }
189}
190
191#[cfg(test)]
192mod tests {
193 use embedded_storage::nor_flash::{NorFlash, ReadNorFlash};
194
195 use super::ConcatFlash;
196 use crate::flash::mem_flash::MemFlash;
197
198 #[test]
199 fn can_write_and_read_across_flashes() {
200 let first = MemFlash::<64, 16, 4>::default();
201 let second = MemFlash::<64, 64, 4>::default();
202 let mut f = ConcatFlash::new(first, second);
203
204 f.write(60, &[0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88]).unwrap();
205
206 assert_eq!(&[0x11, 0x22, 0x33, 0x44], &f.0.mem[60..]);
207 assert_eq!(&[0x55, 0x66, 0x77, 0x88], &f.1.mem[0..4]);
208
209 let mut read_buf = [0; 8];
210 f.read(60, &mut read_buf).unwrap();
211
212 assert_eq!(&[0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88], &read_buf);
213 }
214
215 #[test]
216 fn can_erase_across_flashes() {
217 let first = MemFlash::<128, 16, 4>::new(0x00);
218 let second = MemFlash::<128, 64, 4>::new(0x00);
219 let mut f = ConcatFlash::new(first, second);
220
221 f.erase(64, 192).unwrap();
222
223 assert_eq!(&[0x00; 64], &f.0.mem[0..64]);
224 assert_eq!(&[0xff; 64], &f.0.mem[64..128]);
225 assert_eq!(&[0xff; 64], &f.1.mem[0..64]);
226 assert_eq!(&[0x00; 64], &f.1.mem[64..128]);
227 }
228}