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
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
// Licensed under the Apache License, Version 2.0 or the MIT License.
// SPDX-License-Identifier: Apache-2.0 OR MIT
// Copyright Tock Contributors 2022.

//! HIL for General Purpose Input-Output (GPIO) pins.

use core::cell::Cell;

use crate::utilities::cells::OptionalCell;
use crate::ErrorCode;

/// Enum for configuring any pull-up or pull-down resistors on the GPIO pin.
#[derive(Clone, Copy, Debug)]
pub enum FloatingState {
    PullUp,
    PullDown,
    PullNone,
}

/// Enum for selecting which edge to trigger interrupts on.
#[derive(Clone, Copy, Debug)]
pub enum InterruptEdge {
    RisingEdge,
    FallingEdge,
    EitherEdge,
}

/// Enum for which state the pin is in. Some MCUs can support Input/Output pins,
/// so this is a valid option. `Function` means the pin has been configured to
/// a special function. Determining which function it outside the scope of the HIL,
/// and should instead use a chip-specific API.
#[derive(Clone, Copy, Debug)]
pub enum Configuration {
    /// Cannot be read or written or used; effectively inactive.
    LowPower,
    /// Calls to the `Input` trait are valid.
    Input,
    /// Calls to the `Output` trait are valid.
    Output,
    /// Calls to both the `Input` and `Output` traits are valid.
    InputOutput,
    /// Chip-specific, requires chip-specific API for more detail,
    Function,
    /// In a state not covered by other values.
    Other,
}

/// Some GPIOs can be semantically active or not.
/// For example:
/// - a LED is active when emitting light,
/// - a button GPIO is active when pressed.
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum ActivationState {
    Inactive = 0,
    Active = 1,
}

/// Whether a GPIO is in the `ActivationState::Active` when the signal is high
/// or low.
#[derive(Clone, Copy)]
pub enum ActivationMode {
    ActiveHigh,
    ActiveLow,
}

/// The Pin trait allows a pin to be used as either input
/// or output and to be configured.
pub trait Pin: Input + Output + Configure {}

/// The InterruptPin trait allows a pin to be used as either
/// input or output and also to source interrupts.
pub trait InterruptPin<'a>: Pin + Interrupt<'a> {}

/// The InterruptValuePin trait allows a pin to be used as
/// either input or output and also to source interrupts which
/// pass a value.
pub trait InterruptValuePin<'a>: Pin + InterruptWithValue<'a> {}

// Provide blanket implementations for all trait groups
impl<T: Input + Output + Configure> Pin for T {}
impl<'a, T: Pin + Interrupt<'a>> InterruptPin<'a> for T {}
impl<'a, T: Pin + InterruptWithValue<'a>> InterruptValuePin<'a> for T {}

/// Control and configure a GPIO pin.
pub trait Configure {
    /// Return the current pin configuration.
    fn configuration(&self) -> Configuration;

    /// Make the pin an output, returning the current configuration,
    /// which should be either `Configuration::Output` or
    /// `Configuration::InputOutput`.
    fn make_output(&self) -> Configuration;
    /// Disable the pin as an output, returning the current configuration.
    fn disable_output(&self) -> Configuration;

    /// Make the pin an input, returning the current configuration,
    /// which should be ither `Configuration::Input` or
    /// `Configuration::InputOutput`.
    fn make_input(&self) -> Configuration;
    /// Disable the pin as an input, returning the current configuration.
    fn disable_input(&self) -> Configuration;

    /// Put a pin into its lowest power state, with no guarantees on
    /// if it is enabled or not. Implementations are free to use any
    /// state (e.g. input, output, disable, etc.) the hardware pin
    /// supports to ensure the pin is as low power as possible.
    /// Re-enabling the pin requires reconfiguring it (i.e. the state
    /// of its enabled configuration is not stored).
    fn deactivate_to_low_power(&self);

    /// Set the floating state of the pin.
    fn set_floating_state(&self, state: FloatingState);
    /// Return the current floating state of the pin.
    fn floating_state(&self) -> FloatingState;

    /// Return whether the pin is an input (reading from
    /// the Input trait will return valid results). Returns
    /// true if the pin is in Configuration::Input or
    /// Configuration::InputOutput.
    fn is_input(&self) -> bool {
        match self.configuration() {
            Configuration::Input | Configuration::InputOutput => true,
            _ => false,
        }
    }

    /// Return whether the pin is an output (writing to
    /// the Output trait will change the output of the pin).
    /// Returns true if the pin is in Configuration::Output or
    /// Configuration::InputOutput.
    fn is_output(&self) -> bool {
        match self.configuration() {
            Configuration::Output | Configuration::InputOutput => true,
            _ => false,
        }
    }
}

/// Configuration trait for pins that can be simultaneously
/// input and output. Having this trait allows an implementation
/// to statically verify this is possible.
pub trait ConfigureInputOutput: Configure {
    /// Make the pin a simultaneously input and output; should always
    /// return `Configuration::InputOutput`.
    fn make_input_output(&self) -> Configuration;
    fn is_input_output(&self) -> bool;
}

pub trait Output {
    /// Set the GPIO pin high. If the pin is not an output or
    /// input/output, this call is ignored.
    fn set(&self);

    /// Set the GPIO pin low. If the pin is not an output or
    /// input/output, this call is ignored.
    fn clear(&self);

    /// Toggle the GPIO pin. If the pin was high, set it low. If
    /// the pin was low, set it high. If the pin is not an output or
    /// input/output, this call is ignored. Return the new value
    /// of the pin.
    fn toggle(&self) -> bool;

    /// Activate or deactivate a GPIO pin, for a given activation mode.
    fn write_activation(&self, state: ActivationState, mode: ActivationMode) {
        match (state, mode) {
            (ActivationState::Active, ActivationMode::ActiveHigh)
            | (ActivationState::Inactive, ActivationMode::ActiveLow) => {
                self.set();
            }
            (ActivationState::Active, ActivationMode::ActiveLow)
            | (ActivationState::Inactive, ActivationMode::ActiveHigh) => {
                self.clear();
            }
        }
    }
}

pub trait Input {
    /// Get the current state of an input GPIO pin. For an output
    /// pin, return the output; for an input pin, return the input;
    /// for disabled or function pins the value is undefined.
    fn read(&self) -> bool;

    /// Get the current state of a GPIO pin, for a given activation mode.
    fn read_activation(&self, mode: ActivationMode) -> ActivationState {
        let value = self.read();
        match (mode, value) {
            (ActivationMode::ActiveHigh, true) | (ActivationMode::ActiveLow, false) => {
                ActivationState::Active
            }
            (ActivationMode::ActiveLow, true) | (ActivationMode::ActiveHigh, false) => {
                ActivationState::Inactive
            }
        }
    }
}

pub trait Interrupt<'a>: Input {
    /// Set the client for interrupt events.
    fn set_client(&self, client: &'a dyn Client);

    /// Enable an interrupt on the GPIO pin. This does not
    /// configure the pin except to enable an interrupt: it
    /// should be separately configured as an input, etc.
    fn enable_interrupts(&self, mode: InterruptEdge);

    /// Disable interrupts for the GPIO pin.
    fn disable_interrupts(&self);

    /// Return whether this interrupt is pending
    fn is_pending(&self) -> bool;
}

/// Interface for users of synchronous GPIO interrupts. In order
/// to receive interrupts, the user must implement
/// this `Client` interface.
pub trait Client {
    /// Called when an interrupt occurs. The `identifier` will
    /// be the same value that was passed to `enable_interrupt()`
    /// when the interrupt was configured.
    fn fired(&self);
}

/// Interface that wraps an interrupt to pass a value when it
/// triggers. The standard use case for this trait is when several
/// interrupts call the same callback function and it needs to
/// distinguish which one is calling it by giving each one a unique
/// value.
pub trait InterruptWithValue<'a>: Input {
    /// Set the client for interrupt events.
    fn set_client(&self, client: &'a dyn ClientWithValue);

    /// Enable an interrupt on the GPIO pin. This does not
    /// configure the pin except to enable an interrupt: it
    /// should be separately configured as an input, etc.
    /// Returns:
    ///    Ok(()) - the interrupt was set up properly
    ///    FAIL    - the interrupt was not set up properly; this is due to
    ///              not having an underlying interrupt source yet, i.e.
    ///              the struct is not yet fully initialized.
    fn enable_interrupts(&self, mode: InterruptEdge) -> Result<(), ErrorCode>;

    /// Disable interrupts for the GPIO pin.
    fn disable_interrupts(&self);

    /// Return whether this interrupt is pending
    fn is_pending(&self) -> bool;

    /// Set the value that will be passed to clients on an
    /// interrupt.
    fn set_value(&self, value: u32);

    /// Return the value that is passed to clients on an
    /// interrupt.
    fn value(&self) -> u32;
}

/// Interfaces for users of GPIO interrupts who handle many interrupts
/// with the same function. The value passed in the callback allows the
/// callback to distinguish which interrupt fired.
pub trait ClientWithValue {
    fn fired(&self, value: u32);
}

/// Standard implementation of InterruptWithValue: handles an
/// `gpio::Client::fired` and passes it up as a
/// `gpio::ClientWithValue::fired`.
pub struct InterruptValueWrapper<'a, IP: InterruptPin<'a>> {
    value: Cell<u32>,
    client: OptionalCell<&'a dyn ClientWithValue>,
    source: &'a IP,
}

impl<'a, IP: InterruptPin<'a>> InterruptValueWrapper<'a, IP> {
    pub fn new(pin: &'a IP) -> Self {
        Self {
            value: Cell::new(0),
            client: OptionalCell::empty(),
            source: pin,
        }
    }

    pub fn finalize(&'static self) -> &'static Self {
        self.source.set_client(self);
        self
    }
}

impl<'a, IP: InterruptPin<'a>> InterruptWithValue<'a> for InterruptValueWrapper<'a, IP> {
    fn set_value(&self, value: u32) {
        self.value.set(value);
    }

    fn value(&self) -> u32 {
        self.value.get()
    }

    fn set_client(&self, client: &'a dyn ClientWithValue) {
        self.client.replace(client);
    }

    fn is_pending(&self) -> bool {
        self.source.is_pending()
    }

    fn enable_interrupts(&self, edge: InterruptEdge) -> Result<(), ErrorCode> {
        self.source.enable_interrupts(edge);
        Ok(())
    }

    fn disable_interrupts(&self) {
        self.source.disable_interrupts();
    }
}

impl<'a, IP: InterruptPin<'a>> Input for InterruptValueWrapper<'a, IP> {
    fn read(&self) -> bool {
        self.source.read()
    }
}

impl<'a, IP: InterruptPin<'a>> Configure for InterruptValueWrapper<'a, IP> {
    fn configuration(&self) -> Configuration {
        self.source.configuration()
    }

    fn make_output(&self) -> Configuration {
        self.source.make_output()
    }

    fn disable_output(&self) -> Configuration {
        self.source.disable_output()
    }

    fn make_input(&self) -> Configuration {
        self.source.make_input()
    }

    fn disable_input(&self) -> Configuration {
        self.source.disable_input()
    }

    fn deactivate_to_low_power(&self) {
        self.source.deactivate_to_low_power();
    }

    fn set_floating_state(&self, state: FloatingState) {
        self.source.set_floating_state(state);
    }

    fn floating_state(&self) -> FloatingState {
        self.source.floating_state()
    }

    fn is_input(&self) -> bool {
        self.source.is_input()
    }

    fn is_output(&self) -> bool {
        self.source.is_output()
    }
}

impl<'a, IP: InterruptPin<'a>> Output for InterruptValueWrapper<'a, IP> {
    fn set(&self) {
        self.source.set();
    }

    fn clear(&self) {
        self.source.clear();
    }

    fn toggle(&self) -> bool {
        self.source.toggle()
    }
}

impl<'a, IP: InterruptPin<'a>> Client for InterruptValueWrapper<'a, IP> {
    fn fired(&self) {
        self.client.map(|c| c.fired(self.value()));
    }
}