capsules_core/virtualizers/
virtual_flash.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//! Virtualize writing flash.
6//!
7//! `MuxFlash` provides shared access to a flash interface from multiple clients
8//! in the kernel. For instance, a board may wish to expose the internal MCU
9//! flash for multiple uses, like allowing userland apps to write their own
10//! flash space, and to provide a "scratch space" as the end of flash for all
11//! apps to use. Each of these requires a capsule to support the operation, and
12//! must use a `FlashUser` instance to contain the per-user state for the
13//! virtualization.
14//!
15//! Usage
16//! -----
17//!
18//! ```rust,ignore
19//! # use kernel::{hil, static_init};
20//!
21//! // Create the mux.
22//! let mux_flash = static_init!(
23//!     capsules_core::virtual_flash::MuxFlash<'static, sam4l::flashcalw::FLASHCALW>,
24//!     capsules_core::virtual_flash::MuxFlash::new(&sam4l::flashcalw::FLASH_CONTROLLER));
25//! hil::flash::HasClient::set_client(&sam4l::flashcalw::FLASH_CONTROLLER, mux_flash);
26//!
27//! // Everything that then uses the virtualized flash must use one of these.
28//! let virtual_flash = static_init!(
29//!     capsules_core::virtual_flash::FlashUser<'static, sam4l::flashcalw::FLASHCALW>,
30//!     capsules_core::virtual_flash::FlashUser::new(mux_flash));
31//! ```
32
33use core::cell::Cell;
34
35use kernel::collections::list::{List, ListLink, ListNode};
36use kernel::hil;
37use kernel::utilities::cells::{OptionalCell, TakeCell};
38use kernel::ErrorCode;
39
40/// Handle keeping a list of active users of flash hardware and serialize their
41/// requests.
42///
43/// After each completed request the list is checked to see if there
44/// is another flash user with an outstanding read, write, or erase
45/// request.
46pub struct MuxFlash<'a, F: hil::flash::Flash + 'static> {
47    flash: &'a F,
48    users: List<'a, FlashUser<'a, F>>,
49    inflight: OptionalCell<&'a FlashUser<'a, F>>,
50}
51
52impl<F: hil::flash::Flash> hil::flash::Client<F> for MuxFlash<'_, F> {
53    fn read_complete(
54        &self,
55        pagebuffer: &'static mut F::Page,
56        result: Result<(), hil::flash::Error>,
57    ) {
58        self.inflight.take().map(move |user| {
59            user.read_complete(pagebuffer, result);
60        });
61        self.do_next_op();
62    }
63
64    fn write_complete(
65        &self,
66        pagebuffer: &'static mut F::Page,
67        result: Result<(), hil::flash::Error>,
68    ) {
69        self.inflight.take().map(move |user| {
70            user.write_complete(pagebuffer, result);
71        });
72        self.do_next_op();
73    }
74
75    fn erase_complete(&self, result: Result<(), hil::flash::Error>) {
76        self.inflight.take().map(move |user| {
77            user.erase_complete(result);
78        });
79        self.do_next_op();
80    }
81}
82
83impl<'a, F: hil::flash::Flash> MuxFlash<'a, F> {
84    pub const fn new(flash: &'a F) -> MuxFlash<'a, F> {
85        MuxFlash {
86            flash,
87            users: List::new(),
88            inflight: OptionalCell::empty(),
89        }
90    }
91
92    /// Scan the list of users and find the first user that has a pending
93    /// request, then issue that request to the flash hardware.
94    fn do_next_op(&self) {
95        if self.inflight.is_none() {
96            let mnode = self
97                .users
98                .iter()
99                .find(|node| node.operation.get() != Op::Idle);
100            mnode.map(|node| {
101                node.buffer.take().map_or_else(
102                    || {
103                        // Don't need a buffer for erase.
104                        match node.operation.get() {
105                            Op::Erase(page_number) => {
106                                let _ = self.flash.erase_page(page_number);
107                            }
108                            _ => {}
109                        }
110                    },
111                    |buf| {
112                        match node.operation.get() {
113                            Op::Write(page_number) => {
114                                if let Err((_, buf)) = self.flash.write_page(page_number, buf) {
115                                    node.buffer.replace(buf);
116                                }
117                            }
118                            Op::Read(page_number) => {
119                                if let Err((_, buf)) = self.flash.read_page(page_number, buf) {
120                                    node.buffer.replace(buf);
121                                }
122                            }
123                            Op::Erase(page_number) => {
124                                let _ = self.flash.erase_page(page_number);
125                            }
126                            Op::Idle => {} // Can't get here...
127                        }
128                    },
129                );
130                node.operation.set(Op::Idle);
131                self.inflight.set(node);
132            });
133        }
134    }
135}
136
137#[derive(Copy, Clone, PartialEq)]
138enum Op {
139    Idle,
140    Write(usize),
141    Read(usize),
142    Erase(usize),
143}
144
145/// Keeps state for each flash user.
146///
147/// All uses of the virtualized flash interface need to create one of
148/// these to be a user of the flash. The `new()` function handles most
149/// of the work, a user only has to pass in a reference to the
150/// MuxFlash object.
151pub struct FlashUser<'a, F: hil::flash::Flash + 'static> {
152    mux: &'a MuxFlash<'a, F>,
153    buffer: TakeCell<'static, F::Page>,
154    operation: Cell<Op>,
155    next: ListLink<'a, FlashUser<'a, F>>,
156    client: OptionalCell<&'a dyn hil::flash::Client<FlashUser<'a, F>>>,
157}
158
159impl<'a, F: hil::flash::Flash> FlashUser<'a, F> {
160    pub fn new(mux: &'a MuxFlash<'a, F>) -> FlashUser<'a, F> {
161        FlashUser {
162            mux,
163            buffer: TakeCell::empty(),
164            operation: Cell::new(Op::Idle),
165            next: ListLink::empty(),
166            client: OptionalCell::empty(),
167        }
168    }
169}
170
171impl<'a, F: hil::flash::Flash, C: hil::flash::Client<Self>> hil::flash::HasClient<'a, C>
172    for FlashUser<'a, F>
173{
174    fn set_client(&'a self, client: &'a C) {
175        self.mux.users.push_head(self);
176        self.client.set(client);
177    }
178}
179
180impl<F: hil::flash::Flash> hil::flash::Client<F> for FlashUser<'_, F> {
181    fn read_complete(
182        &self,
183        pagebuffer: &'static mut F::Page,
184        result: Result<(), hil::flash::Error>,
185    ) {
186        self.client.map(move |client| {
187            client.read_complete(pagebuffer, result);
188        });
189    }
190
191    fn write_complete(
192        &self,
193        pagebuffer: &'static mut F::Page,
194        result: Result<(), hil::flash::Error>,
195    ) {
196        self.client.map(move |client| {
197            client.write_complete(pagebuffer, result);
198        });
199    }
200
201    fn erase_complete(&self, result: Result<(), hil::flash::Error>) {
202        self.client.map(move |client| {
203            client.erase_complete(result);
204        });
205    }
206}
207
208impl<'a, F: hil::flash::Flash> ListNode<'a, FlashUser<'a, F>> for FlashUser<'a, F> {
209    fn next(&'a self) -> &'a ListLink<'a, FlashUser<'a, F>> {
210        &self.next
211    }
212}
213
214impl<F: hil::flash::Flash> hil::flash::Flash for FlashUser<'_, F> {
215    type Page = F::Page;
216
217    fn read_page(
218        &self,
219        page_number: usize,
220        buf: &'static mut Self::Page,
221    ) -> Result<(), (ErrorCode, &'static mut Self::Page)> {
222        self.buffer.replace(buf);
223        self.operation.set(Op::Read(page_number));
224        self.mux.do_next_op();
225        Ok(())
226    }
227
228    fn write_page(
229        &self,
230        page_number: usize,
231        buf: &'static mut Self::Page,
232    ) -> Result<(), (ErrorCode, &'static mut Self::Page)> {
233        self.buffer.replace(buf);
234        self.operation.set(Op::Write(page_number));
235        self.mux.do_next_op();
236        Ok(())
237    }
238
239    fn erase_page(&self, page_number: usize) -> Result<(), ErrorCode> {
240        self.operation.set(Op::Erase(page_number));
241        self.mux.do_next_op();
242        Ok(())
243    }
244}