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
//! Provides userspace applications with a driver interface to asynchronous GPIO
//! pins.
//!
//! Async GPIO pins are pins that exist on something like a GPIO extender or a
//! radio that has controllable GPIOs.
//!
//! Usage
//! -----
//!
//! ```rust
//! Generate a list of ports to group into one userspace driver.
//! let async_gpio_ports = static_init!(
//!     [&'static capsules::mcp23008::MCP23008; 1],
//!     [mcp23008]);
//!
//! let gpio_async = static_init!(
//!     capsules::gpio_async::GPIOAsync<'static, capsules::mcp23008::MCP23008<'static>>,
//!     capsules::gpio_async::GPIOAsync::new(async_gpio_ports));
//!
//! // Setup the clients correctly.
//! for port in async_gpio_ports.iter() {
//!     port.set_client(gpio_async);
//! }
//! ```

use core::cell::Cell;
use kernel::{AppId, Callback, Driver};
use kernel::ReturnCode;
use kernel::hil;

pub struct GPIOAsync<'a, Port: hil::gpio_async::Port + 'a> {
    ports: &'a [&'a Port],
    callback: Cell<Option<Callback>>,
    interrupt_callback: Cell<Option<Callback>>,
}

impl<'a, Port: hil::gpio_async::Port> GPIOAsync<'a, Port> {
    pub fn new(ports: &'a [&'a Port]) -> GPIOAsync<'a, Port> {
        GPIOAsync {
            ports: ports,
            callback: Cell::new(None),
            interrupt_callback: Cell::new(None),
        }
    }

    fn configure_input_pin(&self, port: usize, pin: usize, config: usize) -> ReturnCode {
        let ports = self.ports.as_ref();
        let mode = match config {
            0 => hil::gpio::InputMode::PullNone,
            1 => hil::gpio::InputMode::PullUp,
            2 => hil::gpio::InputMode::PullDown,
            _ => return ReturnCode::EINVAL,
        };
        ports[port].make_input(pin, mode)
    }

    fn configure_interrupt(&self, port: usize, pin: usize, config: usize) -> ReturnCode {
        let ports = self.ports.as_ref();
        let mode = match config {
            0 => hil::gpio::InterruptMode::EitherEdge,
            1 => hil::gpio::InterruptMode::RisingEdge,
            2 => hil::gpio::InterruptMode::FallingEdge,
            _ => return ReturnCode::EINVAL,
        };
        ports[port].enable_interrupt(pin, mode, port)
    }
}

impl<'a, Port: hil::gpio_async::Port> hil::gpio_async::Client for GPIOAsync<'a, Port> {
    fn fired(&self, pin: usize, identifier: usize) {
        self.interrupt_callback
            .get()
            .map(|mut cb| cb.schedule(identifier, pin, 0));
    }

    fn done(&self, value: usize) {
        self.callback.get().map(|mut cb| cb.schedule(0, value, 0));
    }
}

impl<'a, Port: hil::gpio_async::Port> Driver for GPIOAsync<'a, Port> {
    /// Setup callbacks for gpio_async events.
    ///
    /// ### `subscribe_num`
    ///
    /// - `0`: Setup a callback for when **split-phase operations complete**.
    ///   This callback gets called from the gpio_async `done()` event and
    ///   signals the end of operations like asserting a GPIO pin or configuring
    ///   an interrupt pin. The callback will be called with two valid
    ///   arguments. The first is the callback type, which is currently 0 for
    ///   all `done()` events. The second is a value, which is only useful for
    ///   operations which should return something, like a GPIO read.
    /// - `1`: Setup a callback for when a **GPIO interrupt** occurs. This
    ///   callback will be called with two arguments, the first being the port
    ///   number of the interrupting pin, and the second being the pin number.
    fn subscribe(&self, subscribe_num: usize, callback: Callback) -> ReturnCode {
        match subscribe_num {
            // Set callback for `done()` events
            0 => {
                self.callback.set(Some(callback));
                ReturnCode::SUCCESS
            }

            // Set callback for pin interrupts
            1 => {
                self.interrupt_callback.set(Some(callback));
                ReturnCode::SUCCESS
            }

            // default
            _ => ReturnCode::ENOSUPPORT,
        }
    }

    /// Configure and read GPIO pins.
    ///
    /// `pin` is the index of the pin.
    ///
    /// `data` is a 32 bit value packed with the lowest 16 bits as the port
    /// number, and the remaining upper bits as a command-specific value.
    ///
    /// ### `command_num`
    ///
    /// - `0`: Driver check and get number of GPIO ports supported.
    /// - `1`: Set a pin as an output.
    /// - `2`: Set a pin high by setting it to 1.
    /// - `3`: Clear a pin by setting it to 0.
    /// - `4`: Toggle a pin.
    /// - `5`: Set a pin as an input and configure its pull-up or pull-down
    ///   state. The command-specific field should be set to 0 for a pull-up, 1
    ///   for a pull-down, or 2 for neither.
    /// - `6`: Read a GPIO pin state, and have its value returned in the done()
    ///   callback.
    /// - `7`: Enable an interrupt on a GPIO pin. The command-specific data
    ///   should be 0 for an either-edge interrupt, 1 for a rising edge
    ///   interrupt, and 2 for a falling edge interrupt.
    /// - `8`: Disable an interrupt on a pin.
    /// - `9`: Disable a GPIO pin.
    fn command(&self, command_num: usize, pin: usize, data: usize, _: AppId) -> ReturnCode {
        let port = data & 0xFFFF;
        let other = (data >> 16) & 0xFFFF;
        let ports = self.ports.as_ref();

        // On any command other than 0, we check for ports length.
        if command_num != 0 && port >= ports.len() {
            return ReturnCode::EINVAL;
        }

        match command_num {
            // How many ports
            0 => ReturnCode::SuccessWithValue {
                value: ports.len() as usize,
            },

            // enable output
            1 => ports[port].make_output(pin),

            // set pin
            2 => ports[port].set(pin),

            // clear pin
            3 => ports[port].clear(pin),

            // toggle pin
            4 => ports[port].toggle(pin),

            // enable and configure input
            5 => self.configure_input_pin(port, pin, other & 0xFF),

            // read input
            6 => ports[port].read(pin),

            // enable interrupt on pin
            7 => self.configure_interrupt(port, pin, other & 0xFF),

            // disable interrupt on pin
            8 => ports[port].disable_interrupt(pin),

            // disable pin
            9 => ports[port].disable(pin),

            // default
            _ => ReturnCode::ENOSUPPORT,
        }
    }
}