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                        if let Op::Erase(page_number) = node.operation.get() {
105                            let _ = self.flash.erase_page(page_number);
106                        }
107                    },
108                    |buf| {
109                        match node.operation.get() {
110                            Op::Write(page_number) => {
111                                if let Err((_, buf)) = self.flash.write_page(page_number, buf) {
112                                    node.buffer.replace(buf);
113                                }
114                            }
115                            Op::Read(page_number) => {
116                                if let Err((_, buf)) = self.flash.read_page(page_number, buf) {
117                                    node.buffer.replace(buf);
118                                }
119                            }
120                            Op::Erase(page_number) => {
121                                let _ = self.flash.erase_page(page_number);
122                            }
123                            Op::Idle => {} // Can't get here...
124                        }
125                    },
126                );
127                node.operation.set(Op::Idle);
128                self.inflight.set(node);
129            });
130        }
131    }
132}
133
134#[derive(Copy, Clone, PartialEq)]
135enum Op {
136    Idle,
137    Write(usize),
138    Read(usize),
139    Erase(usize),
140}
141
142/// Keeps state for each flash user.
143///
144/// All uses of the virtualized flash interface need to create one of
145/// these to be a user of the flash. The `new()` function handles most
146/// of the work, a user only has to pass in a reference to the
147/// MuxFlash object.
148pub struct FlashUser<'a, F: hil::flash::Flash + 'static> {
149    mux: &'a MuxFlash<'a, F>,
150    buffer: TakeCell<'static, F::Page>,
151    operation: Cell<Op>,
152    next: ListLink<'a, FlashUser<'a, F>>,
153    client: OptionalCell<&'a dyn hil::flash::Client<FlashUser<'a, F>>>,
154}
155
156impl<'a, F: hil::flash::Flash> FlashUser<'a, F> {
157    pub fn new(mux: &'a MuxFlash<'a, F>) -> FlashUser<'a, F> {
158        FlashUser {
159            mux,
160            buffer: TakeCell::empty(),
161            operation: Cell::new(Op::Idle),
162            next: ListLink::empty(),
163            client: OptionalCell::empty(),
164        }
165    }
166}
167
168impl<'a, F: hil::flash::Flash, C: hil::flash::Client<Self>> hil::flash::HasClient<'a, C>
169    for FlashUser<'a, F>
170{
171    fn set_client(&'a self, client: &'a C) {
172        self.mux.users.push_head(self);
173        self.client.set(client);
174    }
175}
176
177impl<F: hil::flash::Flash> hil::flash::Client<F> for FlashUser<'_, F> {
178    fn read_complete(
179        &self,
180        pagebuffer: &'static mut F::Page,
181        result: Result<(), hil::flash::Error>,
182    ) {
183        self.client.map(move |client| {
184            client.read_complete(pagebuffer, result);
185        });
186    }
187
188    fn write_complete(
189        &self,
190        pagebuffer: &'static mut F::Page,
191        result: Result<(), hil::flash::Error>,
192    ) {
193        self.client.map(move |client| {
194            client.write_complete(pagebuffer, result);
195        });
196    }
197
198    fn erase_complete(&self, result: Result<(), hil::flash::Error>) {
199        self.client.map(move |client| {
200            client.erase_complete(result);
201        });
202    }
203}
204
205impl<'a, F: hil::flash::Flash> ListNode<'a, FlashUser<'a, F>> for FlashUser<'a, F> {
206    fn next(&'a self) -> &'a ListLink<'a, FlashUser<'a, F>> {
207        &self.next
208    }
209}
210
211impl<F: hil::flash::Flash> hil::flash::Flash for FlashUser<'_, F> {
212    type Page = F::Page;
213
214    fn read_page(
215        &self,
216        page_number: usize,
217        buf: &'static mut Self::Page,
218    ) -> Result<(), (ErrorCode, &'static mut Self::Page)> {
219        self.buffer.replace(buf);
220        self.operation.set(Op::Read(page_number));
221        self.mux.do_next_op();
222        Ok(())
223    }
224
225    fn write_page(
226        &self,
227        page_number: usize,
228        buf: &'static mut Self::Page,
229    ) -> Result<(), (ErrorCode, &'static mut Self::Page)> {
230        self.buffer.replace(buf);
231        self.operation.set(Op::Write(page_number));
232        self.mux.do_next_op();
233        Ok(())
234    }
235
236    fn erase_page(&self, page_number: usize) -> Result<(), ErrorCode> {
237        self.operation.set(Op::Erase(page_number));
238        self.mux.do_next_op();
239        Ok(())
240    }
241}