1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2022.
45//! Map arbitrary nonvolatile reads and writes to page operations.
6//!
7//! This splits non-page-aligned reads and writes into a series of page level
8//! reads and writes. While it is handling a read or write it returns `BUSY` to
9//! all additional requests.
10//!
11//! This module is designed to be used on top of any flash storage and below any
12//! user of `NonvolatileStorage`. This module handles different sized pages.
13//!
14//! ```plain
15//! hil::nonvolatile_storage::NonvolatileStorage
16//! ┌─────────────┐
17//! │ │
18//! │ This module │
19//! │ │
20//! └─────────────┘
21//! hil::flash::Flash
22//! ```
23//!
24//! Usage
25//! -----
26//!
27//! ```rust,ignore
28//! # use kernel::{hil, static_init};
2930//! sam4l::flashcalw::FLASH_CONTROLLER.configure();
31//! let page_buffer = static_init!(
32//! sam4l::flashcalw::Sam4lPage,
33//! sam4l::flashcalw::Sam4lPage::default()
34//! );
35//! let nv_to_page = static_init!(
36//! capsules::nonvolatile_to_pages::NonvolatileToPages<'static, sam4l::flashcalw::FLASHCALW>,
37//! capsules::nonvolatile_to_pages::NonvolatileToPages::new(
38//! &mut sam4l::flashcalw::FLASH_CONTROLLER,
39//! page_buffer));
40//! hil::flash::HasClient::set_client(&sam4l::flashcalw::FLASH_CONTROLLER, nv_to_page);
41//! ```
4243use core::cell::Cell;
44use core::cmp;
45use kernel::hil;
46use kernel::utilities::cells::NumericCellExt;
47use kernel::utilities::cells::{OptionalCell, TakeCell};
48use kernel::ErrorCode;
4950/// This module is either waiting to do something, or handling a read/write.
51#[derive(Clone, Copy, Debug, PartialEq)]
52enum State {
53 Idle,
54 Read,
55 Write,
56}
5758pub struct NonvolatileToPages<'a, F: hil::flash::Flash + 'static> {
59/// The module providing a `Flash` interface.
60driver: &'a F,
61/// Callback to the user of this capsule.
62client: OptionalCell<&'a dyn hil::nonvolatile_storage::NonvolatileStorageClient>,
63/// Buffer correctly sized for the underlying flash page size.
64pagebuffer: TakeCell<'static, F::Page>,
65/// Current state of this capsule.
66state: Cell<State>,
67/// Temporary holding place for the user's buffer.
68buffer: TakeCell<'static, [u8]>,
69/// Absolute address of where we are reading or writing. This gets updated
70 /// as the operation proceeds across pages.
71address: Cell<usize>,
72/// Total length to read or write. We need to store this to return it to the
73 /// client.
74length: Cell<usize>,
75/// How many bytes are left to read or write.
76remaining_length: Cell<usize>,
77/// Where we are in the user buffer.
78buffer_index: Cell<usize>,
79}
8081impl<'a, F: hil::flash::Flash> NonvolatileToPages<'a, F> {
82pub fn new(driver: &'a F, buffer: &'static mut F::Page) -> NonvolatileToPages<'a, F> {
83 NonvolatileToPages {
84 driver,
85 client: OptionalCell::empty(),
86 pagebuffer: TakeCell::new(buffer),
87 state: Cell::new(State::Idle),
88 buffer: TakeCell::empty(),
89 address: Cell::new(0),
90 length: Cell::new(0),
91 remaining_length: Cell::new(0),
92 buffer_index: Cell::new(0),
93 }
94 }
95}
9697impl<'a, F: hil::flash::Flash> hil::nonvolatile_storage::NonvolatileStorage<'a>
98for NonvolatileToPages<'a, F>
99{
100fn set_client(&self, client: &'a dyn hil::nonvolatile_storage::NonvolatileStorageClient) {
101self.client.set(client);
102 }
103104fn read(
105&self,
106 buffer: &'static mut [u8],
107 address: usize,
108 length: usize,
109 ) -> Result<(), ErrorCode> {
110if self.state.get() != State::Idle {
111return Err(ErrorCode::BUSY);
112 }
113114self.pagebuffer
115 .take()
116 .map_or(Err(ErrorCode::RESERVE), move |pagebuffer| {
117let page_size = pagebuffer.as_mut().len();
118119// Just start reading. We'll worry about how much of the page we
120 // want later.
121self.state.set(State::Read);
122self.buffer.replace(buffer);
123self.address.set(address);
124self.length.set(length);
125self.remaining_length.set(length);
126self.buffer_index.set(0);
127128match self.driver.read_page(address / page_size, pagebuffer) {
129Ok(()) => Ok(()),
130Err((error_code, pagebuffer)) => {
131self.pagebuffer.replace(pagebuffer);
132Err(error_code)
133 }
134 }
135 })
136 }
137138fn write(
139&self,
140 buffer: &'static mut [u8],
141 address: usize,
142 length: usize,
143 ) -> Result<(), ErrorCode> {
144if self.state.get() != State::Idle {
145return Err(ErrorCode::BUSY);
146 }
147148self.pagebuffer
149 .take()
150 .map_or(Err(ErrorCode::RESERVE), move |pagebuffer| {
151let page_size = pagebuffer.as_mut().len();
152153self.state.set(State::Write);
154self.length.set(length);
155156if address % page_size == 0 && length >= page_size {
157// This write is aligned to a page and we are writing an entire
158 // page or more.
159160 // Copy data into page buffer.
161pagebuffer.as_mut()[..page_size].copy_from_slice(&buffer[..page_size]);
162163self.buffer.replace(buffer);
164self.address.set(address + page_size);
165self.remaining_length.set(length - page_size);
166self.buffer_index.set(page_size);
167168match self.driver.write_page(address / page_size, pagebuffer) {
169Ok(()) => Ok(()),
170Err((error_code, pagebuffer)) => {
171self.pagebuffer.replace(pagebuffer);
172Err(error_code)
173 }
174 }
175 } else {
176// Need to do a read first.
177self.buffer.replace(buffer);
178self.address.set(address);
179self.remaining_length.set(length);
180self.buffer_index.set(0);
181182match self.driver.read_page(address / page_size, pagebuffer) {
183Ok(()) => Ok(()),
184Err((error_code, pagebuffer)) => {
185self.pagebuffer.replace(pagebuffer);
186Err(error_code)
187 }
188 }
189 }
190 })
191 }
192}
193194impl<F: hil::flash::Flash> hil::flash::Client<F> for NonvolatileToPages<'_, F> {
195fn read_complete(
196&self,
197 pagebuffer: &'static mut F::Page,
198 _result: Result<(), hil::flash::Error>,
199 ) {
200match self.state.get() {
201 State::Read => {
202// OK we got a page from flash. Copy what we actually want from it
203 // out of it.
204self.buffer.take().map(move |buffer| {
205let page_size = pagebuffer.as_mut().len();
206// This will get us our offset into the page.
207let page_index = self.address.get() % page_size;
208// Length is either the rest of the page or how much we have left.
209let len = cmp::min(page_size - page_index, self.remaining_length.get());
210// And where we left off in the user buffer.
211let buffer_index = self.buffer_index.get();
212213// Copy what we read from the page buffer to the user buffer.
214buffer[buffer_index..(len + buffer_index)]
215 .copy_from_slice(&pagebuffer.as_mut()[page_index..(len + page_index)]);
216217// Decide if we are done.
218let new_len = self.remaining_length.get() - len;
219if new_len == 0 {
220// Nothing more to do. Put things back and issue callback.
221self.pagebuffer.replace(pagebuffer);
222self.state.set(State::Idle);
223self.client
224 .map(move |client| client.read_done(buffer, self.length.get()));
225 } else {
226// More to do!
227self.buffer.replace(buffer);
228// Increment all buffer pointers and state.
229self.remaining_length.subtract(len);
230self.address.add(len);
231self.buffer_index.set(buffer_index + len);
232233if let Err((_, pagebuffer)) = self
234.driver
235 .read_page(self.address.get() / page_size, pagebuffer)
236 {
237self.pagebuffer.replace(pagebuffer);
238 }
239 }
240 });
241 }
242 State::Write => {
243// We did a read because we're not page aligned on either or
244 // both ends.
245self.buffer.take().map(move |buffer| {
246let page_size = pagebuffer.as_mut().len();
247// This will get us our offset into the page.
248let page_index = self.address.get() % page_size;
249// Length is either the rest of the page or how much we have left.
250let len = cmp::min(page_size - page_index, self.remaining_length.get());
251// And where we left off in the user buffer.
252let buffer_index = self.buffer_index.get();
253// Which page we read and which we are going to write back to.
254let page_number = self.address.get() / page_size;
255256// Copy what we read from the page buffer to the user buffer.
257pagebuffer.as_mut()[page_index..(len + page_index)]
258 .copy_from_slice(&buffer[buffer_index..(len + buffer_index)]);
259260// Do the write.
261self.buffer.replace(buffer);
262self.remaining_length.subtract(len);
263self.address.add(len);
264self.buffer_index.set(buffer_index + len);
265if let Err((_, pagebuffer)) = self.driver.write_page(page_number, pagebuffer) {
266self.pagebuffer.replace(pagebuffer);
267 }
268 });
269 }
270_ => {}
271 }
272 }
273274fn write_complete(
275&self,
276 pagebuffer: &'static mut F::Page,
277 _result: Result<(), hil::flash::Error>,
278 ) {
279// After a write we could be done, need to do another write, or need to
280 // do a read.
281self.buffer.take().map(move |buffer| {
282let page_size = pagebuffer.as_mut().len();
283284if self.remaining_length.get() == 0 {
285// Done!
286self.pagebuffer.replace(pagebuffer);
287self.state.set(State::Idle);
288self.client
289 .map(move |client| client.write_done(buffer, self.length.get()));
290 } else if self.remaining_length.get() >= page_size {
291// Write an entire page!
292let buffer_index = self.buffer_index.get();
293let page_number = self.address.get() / page_size;
294295// Copy data into page buffer.
296pagebuffer.as_mut()[..page_size]
297 .copy_from_slice(&buffer[buffer_index..(page_size + buffer_index)]);
298299self.buffer.replace(buffer);
300self.remaining_length.subtract(page_size);
301self.address.add(page_size);
302self.buffer_index.set(buffer_index + page_size);
303if let Err((_, pagebuffer)) = self.driver.write_page(page_number, pagebuffer) {
304self.pagebuffer.replace(pagebuffer);
305 }
306 } else {
307// Write a partial page!
308self.buffer.replace(buffer);
309if let Err((_, pagebuffer)) = self
310.driver
311 .read_page(self.address.get() / page_size, pagebuffer)
312 {
313self.pagebuffer.replace(pagebuffer);
314 }
315 }
316 });
317 }
318319fn erase_complete(&self, _result: Result<(), hil::flash::Error>) {}
320}