1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Board interface.
//!
//! A board provides hierarchy of interfaces under the root [`Api`] trait. Some of these interfaces
//! support triggering [events][Event].

#![no_std]
#![feature(doc_auto_cfg)]
#![feature(never_type)]

extern crate alloc;

use core::marker::PhantomData;
use core::ops::Deref;

use derivative::Derivative;
pub use wasefire_error::Error;

#[cfg(feature = "api-button")]
pub mod button;
#[cfg(feature = "internal-api-crypto")]
pub mod crypto;
pub mod debug;
#[cfg(feature = "api-gpio")]
pub mod gpio;
#[cfg(feature = "api-led")]
pub mod led;
#[cfg(feature = "internal-api-platform")]
pub mod platform;
#[cfg(feature = "internal-api-radio")]
pub mod radio;
#[cfg(feature = "api-rng")]
pub mod rng;
#[cfg(feature = "api-timer")]
pub mod timer;
#[cfg(feature = "api-uart")]
pub mod uart;
#[cfg(feature = "internal-api-usb")]
pub mod usb;

/// Board interface.
///
/// This is essentially a type hierarchy. The implementation is responsible for handling a possible
/// explicit global state. The type implementing this API may be equivalent to the never type (e.g.
/// an empty enum) because it is never used, i.e. there are no functions which take `self`.
pub trait Api: Send + 'static {
    /// Returns the oldest triggered event, if any.
    ///
    /// This function is non-blocking. See [`Self::wait_event()`] for a blocking version.
    fn try_event() -> Option<Event<Self>>;

    /// Returns the oldest triggered event, possibly waiting until one triggers.
    ///
    /// This function is non-blocking if an event already triggered. However, if there are no event
    /// available, this function blocks and enters a power-saving state until an event triggers.
    fn wait_event() -> Event<Self>;

    /// Board-specific syscalls.
    ///
    /// Those calls are directly forwarded from the applet by the scheduler. The default
    /// implementation traps by returning `None`. The platform will panic if `Some(Ok(x))` is
    /// returned when `x as i32` would be negative.
    fn syscall(_x1: u32, _x2: u32, _x3: u32, _x4: u32) -> Option<Result<u32, Error>> {
        None
    }

    #[cfg(feature = "api-button")]
    type Button: button::Api;
    #[cfg(feature = "internal-api-crypto")]
    type Crypto: crypto::Api;
    type Debug: debug::Api;
    #[cfg(feature = "api-gpio")]
    type Gpio: gpio::Api;
    #[cfg(feature = "api-led")]
    type Led: led::Api;
    #[cfg(feature = "internal-api-platform")]
    type Platform: platform::Api;
    #[cfg(feature = "internal-api-radio")]
    type Radio: radio::Api;
    #[cfg(feature = "api-rng")]
    type Rng: rng::Api;
    #[cfg(feature = "api-storage")]
    type Storage: Singleton + wasefire_store::Storage + Send;
    #[cfg(feature = "api-timer")]
    type Timer: timer::Api;
    #[cfg(feature = "api-uart")]
    type Uart: uart::Api;
    #[cfg(feature = "internal-api-usb")]
    type Usb: usb::Api;
}

/// Describes how an API is supported.
///
/// The `Value` type parameter is usually `bool`. It may also be `usize` for APIs that have multiple
/// similar things like buttons, leds, and timers.
pub trait Support<Value> {
    const SUPPORT: Value;
}

/// Marker trait for supported API.
pub trait Supported {}

/// Provides access to a singleton API.
pub trait Singleton: Sized {
    /// Returns the singleton.
    ///
    /// Returns `None` if the API is not supported. Returns `None` if called more than once.
    fn take() -> Option<Self>;
}

/// Events that interfaces may trigger.
///
/// Events are de-duplicated if the previous one was not processed yet, because some events may
/// trigger repeatedly.
#[derive(Derivative)]
#[derivative(Debug(bound = ""), PartialEq(bound = ""), Eq(bound = ""))]
pub enum Event<B: Api + ?Sized> {
    /// Button event.
    #[cfg(feature = "api-button")]
    Button(button::Event<B>),

    /// Radio event.
    #[cfg(feature = "internal-api-radio")]
    Radio(radio::Event),

    /// Timer event.
    #[cfg(feature = "api-timer")]
    Timer(timer::Event<B>),

    /// UART event.
    #[cfg(feature = "api-uart")]
    Uart(uart::Event<B>),

    /// USB event.
    #[cfg(feature = "internal-api-usb")]
    Usb(usb::Event),

    /// Dummy event for typing purposes.
    Impossible(Impossible<B>),
}

/// Impossible type with board parameter.
///
/// This type is useful when the type parameter `B` needs to be mentioned in an enum. This type can
/// be destructed by calling its unreachable method.
#[derive(Derivative)]
#[derivative(Debug(bound = ""), Copy(bound = ""), Hash(bound = ""))]
#[derivative(PartialEq(bound = ""), Eq(bound = ""), Ord(bound = ""))]
#[derivative(Ord = "feature_allow_slow_enum")]
pub struct Impossible<B: Api + ?Sized>(Void, PhantomData<B>);

// TODO(https://github.com/mcarton/rust-derivative/issues/112): Use Clone(bound = "") instead.
impl<B: Api + ?Sized> Clone for Impossible<B> {
    fn clone(&self) -> Self {
        *self
    }
}

// TODO(https://github.com/mcarton/rust-derivative/issues/112): Use PartialOrd(bound = "") instead.
impl<B: Api + ?Sized> PartialOrd for Impossible<B> {
    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
        Some(self.cmp(other))
    }
}

impl<B: Api + ?Sized> Impossible<B> {
    pub fn unreachable(&self) -> ! {
        match self.0 {}
    }
}

#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
enum Void {}

#[cfg(feature = "api-button")]
pub type Button<B> = <B as Api>::Button;
#[cfg(feature = "internal-api-crypto")]
pub type Crypto<B> = <B as Api>::Crypto;
pub type Debug<B> = <B as Api>::Debug;
#[cfg(feature = "api-gpio")]
pub type Gpio<B> = <B as Api>::Gpio;
#[cfg(feature = "api-led")]
pub type Led<B> = <B as Api>::Led;
#[cfg(feature = "internal-api-platform")]
pub type Platform<B> = <B as Api>::Platform;
#[cfg(feature = "internal-api-radio")]
pub type Radio<B> = <B as Api>::Radio;
#[cfg(feature = "api-rng")]
pub type Rng<B> = <B as Api>::Rng;
#[cfg(feature = "api-storage")]
pub type Storage<B> = <B as Api>::Storage;
#[cfg(feature = "api-timer")]
pub type Timer<B> = <B as Api>::Timer;
#[cfg(feature = "api-uart")]
pub type Uart<B> = <B as Api>::Uart;
#[cfg(feature = "internal-api-usb")]
pub type Usb<B> = <B as Api>::Usb;

/// Valid identifier for a countable API.
#[derive(Derivative)]
#[derivative(Debug(bound = ""), Copy(bound = ""), Hash(bound = ""))]
#[derivative(PartialEq(bound = ""), Eq(bound = ""), Ord(bound = ""))]
pub struct Id<T: Support<usize> + ?Sized> {
    // Invariant: value < T::SUPPORT
    value: usize,
    count: PhantomData<T>,
}

// TODO(https://github.com/mcarton/rust-derivative/issues/112): Use Clone(bound = "") instead.
impl<T: Support<usize> + ?Sized> Clone for Id<T> {
    fn clone(&self) -> Self {
        *self
    }
}

// TODO(https://github.com/mcarton/rust-derivative/issues/112): Use PartialOrd(bound = "") instead.
impl<T: Support<usize> + ?Sized> PartialOrd for Id<T> {
    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
        Some(self.cmp(other))
    }
}

impl<T: Support<usize>> Id<T> {
    pub fn new(value: usize) -> Option<Self> {
        (value < T::SUPPORT).then_some(Self { value, count: PhantomData })
    }
}

impl<T: Support<usize>> Deref for Id<T> {
    type Target = usize;

    fn deref(&self) -> &Self::Target {
        &self.value
    }
}

impl<T: Supported> Support<bool> for T {
    const SUPPORT: bool = true;
}