wasefire_board_api/
lib.rs

1// Copyright 2022 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Board interface.
16//!
17//! A board provides hierarchy of interfaces under the root [`Api`] trait. Some of these interfaces
18//! support triggering [events][Event].
19
20#![no_std]
21#![feature(doc_auto_cfg)]
22#![feature(never_type)]
23
24extern crate alloc;
25
26use core::marker::PhantomData;
27use core::ops::Deref;
28
29use derive_where::derive_where;
30use wasefire_error::Code;
31pub use wasefire_error::Error;
32
33pub mod applet;
34#[cfg(feature = "api-button")]
35pub mod button;
36#[cfg(feature = "internal-api-crypto")]
37pub mod crypto;
38pub mod debug;
39#[cfg(feature = "api-gpio")]
40pub mod gpio;
41#[cfg(feature = "api-led")]
42pub mod led;
43pub mod platform;
44#[cfg(feature = "internal-api-radio")]
45pub mod radio;
46#[cfg(feature = "api-rng")]
47pub mod rng;
48#[cfg(feature = "api-timer")]
49pub mod timer;
50#[cfg(feature = "api-uart")]
51pub mod uart;
52#[cfg(feature = "internal-api-usb")]
53pub mod usb;
54
55/// Board interface.
56///
57/// This is essentially a type hierarchy. The implementation is responsible for handling a possible
58/// explicit global state. The type implementing this API may be equivalent to the never type (e.g.
59/// an empty enum) because it is never used, i.e. there are no functions which take `self`.
60pub trait Api: Send + 'static {
61    /// Returns the oldest triggered event, if any.
62    ///
63    /// This function is non-blocking. See [`Self::wait_event()`] for a blocking version.
64    fn try_event() -> Option<Event<Self>>;
65
66    /// Returns the oldest triggered event, possibly waiting until one triggers.
67    ///
68    /// This function is non-blocking if an event already triggered. However, if there are no event
69    /// available, this function blocks and enters a power-saving state until an event triggers.
70    fn wait_event() -> Event<Self>;
71
72    /// Board-specific syscalls.
73    ///
74    /// Those calls are directly forwarded from the applet by the scheduler. The default
75    /// implementation traps by returning `None`. The platform will panic if `Some(Ok(x))` is
76    /// returned when `x as i32` would be negative.
77    fn syscall(_x1: u32, _x2: u32, _x3: u32, _x4: u32) -> Option<Result<u32, Error>> {
78        None
79    }
80
81    /// Applet interface.
82    type Applet: applet::Api;
83
84    /// Button interface.
85    #[cfg(feature = "api-button")]
86    type Button: button::Api;
87
88    /// Cryptography interface.
89    #[cfg(feature = "internal-api-crypto")]
90    type Crypto: crypto::Api;
91
92    /// Debugging and testing interface.
93    type Debug: debug::Api;
94
95    /// Low-level GPIO interface.
96    #[cfg(feature = "api-gpio")]
97    type Gpio: gpio::Api;
98
99    /// LED interface.
100    #[cfg(feature = "api-led")]
101    type Led: led::Api;
102
103    /// Platform interface.
104    type Platform: platform::Api;
105
106    /// Radio interface.
107    #[cfg(feature = "internal-api-radio")]
108    type Radio: radio::Api;
109
110    /// Random number generator interface.
111    #[cfg(feature = "api-rng")]
112    type Rng: rng::Api;
113
114    /// Persistent storage interface.
115    #[cfg(feature = "api-storage")]
116    type Storage: Singleton + wasefire_store::Storage + Send;
117
118    /// Timer interface.
119    #[cfg(feature = "api-timer")]
120    type Timer: timer::Api;
121
122    /// UART interface.
123    #[cfg(feature = "api-uart")]
124    type Uart: uart::Api;
125
126    /// USB interface.
127    #[cfg(feature = "internal-api-usb")]
128    type Usb: usb::Api;
129}
130
131/// Describes how an API is supported.
132///
133/// The `Value` type parameter is usually `bool`. It may also be `usize` for APIs that have multiple
134/// similar things like buttons, leds, and timers.
135pub trait Support<Value> {
136    /// Whether and how the API is supported.
137    const SUPPORT: Value;
138}
139
140/// Marker trait for supported API.
141pub trait Supported {}
142
143/// Provides access to a singleton API.
144pub trait Singleton: Sized {
145    /// Returns the singleton.
146    ///
147    /// Returns `None` if the API is not supported. Returns `None` if called more than once.
148    fn take() -> Option<Self>;
149}
150
151/// Events that interfaces may trigger.
152///
153/// Events are de-duplicated if the previous one was not processed yet, because some events may
154/// trigger repeatedly.
155#[cfg_attr(feature = "defmt", derive(defmt::Format))]
156#[derive_where(Debug, PartialEq, Eq)]
157pub enum Event<B: Api + ?Sized> {
158    /// Button event.
159    #[cfg(feature = "api-button")]
160    Button(button::Event<B>),
161
162    /// Platform event.
163    Platform(platform::Event),
164
165    /// Radio event.
166    #[cfg(feature = "internal-api-radio")]
167    Radio(radio::Event),
168
169    /// Timer event.
170    #[cfg(feature = "api-timer")]
171    Timer(timer::Event<B>),
172
173    /// UART event.
174    #[cfg(feature = "api-uart")]
175    Uart(uart::Event<B>),
176
177    /// USB event.
178    #[cfg(feature = "internal-api-usb")]
179    Usb(usb::Event),
180
181    /// Dummy event for typing purposes.
182    Impossible(Impossible<B>),
183}
184
185/// Impossible type with board parameter.
186///
187/// This type is useful when the type parameter `B` needs to be mentioned in an enum. This type can
188/// be destructed by calling its unreachable method.
189#[derive_where(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
190pub struct Impossible<B: Api + ?Sized>(!, PhantomData<B>);
191
192#[cfg(feature = "defmt")]
193impl<B: Api + ?Sized> defmt::Format for Impossible<B> {
194    fn format(&self, fmt: defmt::Formatter) {
195        defmt::write!(fmt, "Impossible");
196    }
197}
198
199impl<B: Api + ?Sized> Impossible<B> {
200    /// Provides a static proof of dead code.
201    pub fn unreachable(&self) -> ! {
202        match self.0 {}
203    }
204}
205
206/// Applet interface.
207pub type Applet<B> = <B as Api>::Applet;
208
209/// Button interface.
210#[cfg(feature = "api-button")]
211pub type Button<B> = <B as Api>::Button;
212
213/// Cryptography interface.
214#[cfg(feature = "internal-api-crypto")]
215pub type Crypto<B> = <B as Api>::Crypto;
216
217/// Debugging and testing interface.
218pub type Debug<B> = <B as Api>::Debug;
219
220/// Low-level GPIO interface.
221#[cfg(feature = "api-gpio")]
222pub type Gpio<B> = <B as Api>::Gpio;
223
224/// LED interface.
225#[cfg(feature = "api-led")]
226pub type Led<B> = <B as Api>::Led;
227
228/// Platform interface.
229pub type Platform<B> = <B as Api>::Platform;
230
231/// Radio interface.
232#[cfg(feature = "internal-api-radio")]
233pub type Radio<B> = <B as Api>::Radio;
234
235/// Random number generator interface.
236#[cfg(feature = "api-rng")]
237pub type Rng<B> = <B as Api>::Rng;
238
239/// Persistent storage interface.
240#[cfg(feature = "api-storage")]
241pub type Storage<B> = <B as Api>::Storage;
242
243/// Timer interface.
244#[cfg(feature = "api-timer")]
245pub type Timer<B> = <B as Api>::Timer;
246
247/// UART interface.
248#[cfg(feature = "api-uart")]
249pub type Uart<B> = <B as Api>::Uart;
250
251/// USB interface.
252#[cfg(feature = "internal-api-usb")]
253pub type Usb<B> = <B as Api>::Usb;
254
255/// Valid identifier for a countable API.
256#[derive_where(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
257pub struct Id<T: Support<usize> + ?Sized> {
258    // Invariant: value < T::SUPPORT
259    value: usize,
260    count: PhantomData<T>,
261}
262
263#[cfg(feature = "defmt")]
264impl<T: Support<usize> + ?Sized> defmt::Format for Id<T> {
265    fn format(&self, fmt: defmt::Formatter) {
266        self.value.format(fmt)
267    }
268}
269
270impl<T: Support<usize>> Id<T> {
271    /// Creates a safe identifier for an API with countable support.
272    pub fn new(value: usize) -> Result<Self, Error> {
273        match value < T::SUPPORT {
274            true => Ok(Self { value, count: PhantomData }),
275            false => Err(Error::user(Code::OutOfBounds)),
276        }
277    }
278}
279
280impl<T: Support<usize>> Deref for Id<T> {
281    type Target = usize;
282
283    fn deref(&self) -> &Self::Target {
284        &self.value
285    }
286}
287
288impl<T: Supported> Support<bool> for T {
289    const SUPPORT: bool = true;
290}