capsules_extra/
nonvolatile_to_pages.rs

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.
4
5//! 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};
29
30//! 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//! ```
42
43use core::cell::Cell;
44use core::cmp;
45use kernel::hil;
46use kernel::utilities::cells::NumericCellExt;
47use kernel::utilities::cells::{OptionalCell, TakeCell};
48use kernel::ErrorCode;
49
50/// 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}
57
58pub struct NonvolatileToPages<'a, F: hil::flash::Flash + 'static> {
59    /// The module providing a `Flash` interface.
60    driver: &'a F,
61    /// Callback to the user of this capsule.
62    client: OptionalCell<&'a dyn hil::nonvolatile_storage::NonvolatileStorageClient>,
63    /// Buffer correctly sized for the underlying flash page size.
64    pagebuffer: TakeCell<'static, F::Page>,
65    /// Current state of this capsule.
66    state: Cell<State>,
67    /// Temporary holding place for the user's buffer.
68    buffer: TakeCell<'static, [u8]>,
69    /// Absolute address of where we are reading or writing. This gets updated
70    /// as the operation proceeds across pages.
71    address: Cell<usize>,
72    /// Total length to read or write. We need to store this to return it to the
73    /// client.
74    length: Cell<usize>,
75    /// How many bytes are left to read or write.
76    remaining_length: Cell<usize>,
77    /// Where we are in the user buffer.
78    buffer_index: Cell<usize>,
79}
80
81impl<'a, F: hil::flash::Flash> NonvolatileToPages<'a, F> {
82    pub 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}
96
97impl<'a, F: hil::flash::Flash> hil::nonvolatile_storage::NonvolatileStorage<'a>
98    for NonvolatileToPages<'a, F>
99{
100    fn set_client(&self, client: &'a dyn hil::nonvolatile_storage::NonvolatileStorageClient) {
101        self.client.set(client);
102    }
103
104    fn read(
105        &self,
106        buffer: &'static mut [u8],
107        address: usize,
108        length: usize,
109    ) -> Result<(), ErrorCode> {
110        if self.state.get() != State::Idle {
111            return Err(ErrorCode::BUSY);
112        }
113
114        self.pagebuffer
115            .take()
116            .map_or(Err(ErrorCode::RESERVE), move |pagebuffer| {
117                let page_size = pagebuffer.as_mut().len();
118
119                // Just start reading. We'll worry about how much of the page we
120                // want later.
121                self.state.set(State::Read);
122                self.buffer.replace(buffer);
123                self.address.set(address);
124                self.length.set(length);
125                self.remaining_length.set(length);
126                self.buffer_index.set(0);
127
128                match self.driver.read_page(address / page_size, pagebuffer) {
129                    Ok(()) => Ok(()),
130                    Err((error_code, pagebuffer)) => {
131                        self.pagebuffer.replace(pagebuffer);
132                        Err(error_code)
133                    }
134                }
135            })
136    }
137
138    fn write(
139        &self,
140        buffer: &'static mut [u8],
141        address: usize,
142        length: usize,
143    ) -> Result<(), ErrorCode> {
144        if self.state.get() != State::Idle {
145            return Err(ErrorCode::BUSY);
146        }
147
148        self.pagebuffer
149            .take()
150            .map_or(Err(ErrorCode::RESERVE), move |pagebuffer| {
151                let page_size = pagebuffer.as_mut().len();
152
153                self.state.set(State::Write);
154                self.length.set(length);
155
156                if 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.
159
160                    // Copy data into page buffer.
161                    pagebuffer.as_mut()[..page_size].copy_from_slice(&buffer[..page_size]);
162
163                    self.buffer.replace(buffer);
164                    self.address.set(address + page_size);
165                    self.remaining_length.set(length - page_size);
166                    self.buffer_index.set(page_size);
167
168                    match self.driver.write_page(address / page_size, pagebuffer) {
169                        Ok(()) => Ok(()),
170                        Err((error_code, pagebuffer)) => {
171                            self.pagebuffer.replace(pagebuffer);
172                            Err(error_code)
173                        }
174                    }
175                } else {
176                    // Need to do a read first.
177                    self.buffer.replace(buffer);
178                    self.address.set(address);
179                    self.remaining_length.set(length);
180                    self.buffer_index.set(0);
181
182                    match self.driver.read_page(address / page_size, pagebuffer) {
183                        Ok(()) => Ok(()),
184                        Err((error_code, pagebuffer)) => {
185                            self.pagebuffer.replace(pagebuffer);
186                            Err(error_code)
187                        }
188                    }
189                }
190            })
191    }
192}
193
194impl<F: hil::flash::Flash> hil::flash::Client<F> for NonvolatileToPages<'_, F> {
195    fn read_complete(
196        &self,
197        pagebuffer: &'static mut F::Page,
198        _result: Result<(), hil::flash::Error>,
199    ) {
200        match 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.
204                self.buffer.take().map(move |buffer| {
205                    let page_size = pagebuffer.as_mut().len();
206                    // This will get us our offset into the page.
207                    let page_index = self.address.get() % page_size;
208                    // Length is either the rest of the page or how much we have left.
209                    let len = cmp::min(page_size - page_index, self.remaining_length.get());
210                    // And where we left off in the user buffer.
211                    let buffer_index = self.buffer_index.get();
212
213                    // Copy what we read from the page buffer to the user buffer.
214                    buffer[buffer_index..(len + buffer_index)]
215                        .copy_from_slice(&pagebuffer.as_mut()[page_index..(len + page_index)]);
216
217                    // Decide if we are done.
218                    let new_len = self.remaining_length.get() - len;
219                    if new_len == 0 {
220                        // Nothing more to do. Put things back and issue callback.
221                        self.pagebuffer.replace(pagebuffer);
222                        self.state.set(State::Idle);
223                        self.client
224                            .map(move |client| client.read_done(buffer, self.length.get()));
225                    } else {
226                        // More to do!
227                        self.buffer.replace(buffer);
228                        // Increment all buffer pointers and state.
229                        self.remaining_length.subtract(len);
230                        self.address.add(len);
231                        self.buffer_index.set(buffer_index + len);
232
233                        if let Err((_, pagebuffer)) = self
234                            .driver
235                            .read_page(self.address.get() / page_size, pagebuffer)
236                        {
237                            self.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.
245                self.buffer.take().map(move |buffer| {
246                    let page_size = pagebuffer.as_mut().len();
247                    // This will get us our offset into the page.
248                    let page_index = self.address.get() % page_size;
249                    // Length is either the rest of the page or how much we have left.
250                    let len = cmp::min(page_size - page_index, self.remaining_length.get());
251                    // And where we left off in the user buffer.
252                    let buffer_index = self.buffer_index.get();
253                    // Which page we read and which we are going to write back to.
254                    let page_number = self.address.get() / page_size;
255
256                    // Copy what we read from the page buffer to the user buffer.
257                    pagebuffer.as_mut()[page_index..(len + page_index)]
258                        .copy_from_slice(&buffer[buffer_index..(len + buffer_index)]);
259
260                    // Do the write.
261                    self.buffer.replace(buffer);
262                    self.remaining_length.subtract(len);
263                    self.address.add(len);
264                    self.buffer_index.set(buffer_index + len);
265                    if let Err((_, pagebuffer)) = self.driver.write_page(page_number, pagebuffer) {
266                        self.pagebuffer.replace(pagebuffer);
267                    }
268                });
269            }
270            _ => {}
271        }
272    }
273
274    fn 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.
281        self.buffer.take().map(move |buffer| {
282            let page_size = pagebuffer.as_mut().len();
283
284            if self.remaining_length.get() == 0 {
285                // Done!
286                self.pagebuffer.replace(pagebuffer);
287                self.state.set(State::Idle);
288                self.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!
292                let buffer_index = self.buffer_index.get();
293                let page_number = self.address.get() / page_size;
294
295                // Copy data into page buffer.
296                pagebuffer.as_mut()[..page_size]
297                    .copy_from_slice(&buffer[buffer_index..(page_size + buffer_index)]);
298
299                self.buffer.replace(buffer);
300                self.remaining_length.subtract(page_size);
301                self.address.add(page_size);
302                self.buffer_index.set(buffer_index + page_size);
303                if let Err((_, pagebuffer)) = self.driver.write_page(page_number, pagebuffer) {
304                    self.pagebuffer.replace(pagebuffer);
305                }
306            } else {
307                // Write a partial page!
308                self.buffer.replace(buffer);
309                if let Err((_, pagebuffer)) = self
310                    .driver
311                    .read_page(self.address.get() / page_size, pagebuffer)
312                {
313                    self.pagebuffer.replace(pagebuffer);
314                }
315            }
316        });
317    }
318
319    fn erase_complete(&self, _result: Result<(), hil::flash::Error>) {}
320}