zephyr/device/
gpio.rs

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
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
//! Most devices in Zephyr operate on a `struct device`.  This provides untyped access to
//! devices.  We want to have stronger typing in the Zephyr interfaces, so most of these types
//! will be wrapped in another structure.  This wraps a Gpio device, and provides methods to
//! most of the operations on gpios.
//!
//! Safey: In general, even just using gpio pins is unsafe in Zephyr.  The gpio drivers are used
//! pervasively throughout Zephyr device drivers.  As such, most of the calls in this module are
//! unsafe.

use core::ffi::c_int;

use super::{NoStatic, Unique};
use crate::raw;

#[cfg(feature = "async-drivers")]
mod async_io {
    //! Async operations for gpio drivers.
    //!
    //! For now, we make an assumption that a gpio controller can contain up to 32 gpios, which is
    //! the largest number currently used, although this might change with 64-bit targest in the
    //! future.

    use core::{
        cell::UnsafeCell,
        future::Future,
        mem,
        sync::atomic::Ordering,
        task::{Poll, Waker},
    };

    use embassy_sync::waitqueue::AtomicWaker;
    use zephyr_sys::{
        device, gpio_add_callback, gpio_callback, gpio_init_callback, gpio_pin_interrupt_configure,
        gpio_pin_interrupt_configure_dt, gpio_port_pins_t, GPIO_INT_LEVEL_HIGH, GPIO_INT_LEVEL_LOW,
        ZR_GPIO_INT_MODE_DISABLE_ONLY,
    };

    use crate::sync::atomic::{AtomicBool, AtomicU32};

    use super::{GpioPin, GpioToken};

    pub(crate) struct GpioStatic {
        /// The wakers for each of the gpios.
        wakers: [AtomicWaker; 32],
        /// Indicates when an interrupt has fired.  Used to definitively indicate the event has
        /// happened, so we can wake.
        fired: AtomicU32,
        /// Have we been initialized?
        installed: AtomicBool,
        /// The data for the callback itself.
        callback: UnsafeCell<gpio_callback>,
    }

    unsafe impl Sync for GpioStatic {}

    impl GpioStatic {
        pub(crate) const fn new() -> Self {
            Self {
                wakers: [const { AtomicWaker::new() }; 32],
                fired: AtomicU32::new(0),
                installed: AtomicBool::new(false),
                // SAFETY: `installed` will tell us this need to be installed.
                callback: unsafe { mem::zeroed() },
            }
        }

        /// Ensure that the callback has been installed.
        pub(super) fn fast_install(&self, port: *const device) {
            if !self.installed.load(Ordering::Acquire) {
                self.install(port);
            }
        }

        fn install(&self, port: *const device) {
            critical_section::with(|_| {
                if !self.installed.load(Ordering::Acquire) {
                    let cb = self.callback.get();
                    // SAFETY: We're in a critical section, so there should be no concurrent use,
                    // and there should not be any calls from the driver.
                    unsafe {
                        gpio_init_callback(cb, Some(Self::callback_handler), 0);
                        gpio_add_callback(port, cb);
                    }

                    self.installed.store(true, Ordering::Release);
                }
            })
        }

        /// Register (replacing) a given callback.
        pub(super) fn register(&self, pin: u8, waker: &Waker) {
            self.wakers[pin as usize].register(waker);

            // SAFETY: Inherently unsafe, due to how the Zephyr API is defined.
            // The API appears to assume coherent memory, which although untrue, probably is "close
            // enough" on the supported targets.
            // The main issue is to ensure that any race is resolved in the direction of getting the
            // callback more than needed, rather than missing.  In the context here, ensure the
            // waker is registered (which does use an atomic), before enabling the pin in the
            // callback structure.
            //
            // If it seems that wakes are getting missed, it might be the case that this needs some
            // kind of memory barrier.
            let cb = self.callback.get();
            unsafe {
                (*cb).pin_mask |= 1 << pin;
            }
        }

        extern "C" fn callback_handler(
            port: *const device,
            cb: *mut gpio_callback,
            mut pins: gpio_port_pins_t,
        ) {
            let data = unsafe {
                cb.cast::<u8>()
                    .sub(mem::offset_of!(Self, callback))
                    .cast::<Self>()
            };

            // For each pin we are informed of.
            while pins > 0 {
                let pin = pins.trailing_zeros();

                pins &= !(1 << pin);

                // SAFETY: Handling this correctly is a bit tricky, especially with the
                // un-coordinated 'pin-mask' value.
                //
                // For level-triggered interrupts, not disabling this will result in an interrupt
                // storm.
                unsafe {
                    // Disable the actual interrupt from the controller.
                    gpio_pin_interrupt_configure(port, pin as u8, ZR_GPIO_INT_MODE_DISABLE_ONLY);

                    // Remove the callback bit.  Unclear if this is actually useful.
                    (*cb).pin_mask &= !(1 << pin);

                    // Indicate that we have fired.
                    // AcqRel is sufficient for ordering across a single atomic.
                    (*data).fired.fetch_or(1 << pin, Ordering::AcqRel);

                    // After the interrupt is off, wake the handler.
                    (*data).wakers[pin as usize].wake();
                }
            }
        }

        /// Check if we have fired for a given pin.  Clears the status.
        pub(crate) fn has_fired(&self, pin: u8) -> bool {
            let value = self.fired.fetch_and(!(1 << pin), Ordering::AcqRel);
            value & (1 << pin) != 0
        }
    }

    impl GpioPin {
        /// Asynchronously wait for a gpio pin to become high.
        ///
        /// # Safety
        ///
        /// The `_token` enforces single use of gpios.  Note that this makes it impossible to wait for
        /// more than one GPIO.
        ///
        pub unsafe fn wait_for_high(
            &mut self,
            _token: &mut GpioToken,
        ) -> impl Future<Output = ()> + use<'_> {
            GpioWait::new(self, 1)
        }

        /// Asynchronously wait for a gpio pin to become low.
        ///
        /// # Safety
        ///
        /// The `_token` enforces single use of gpios.  Note that this makes it impossible to wait
        /// for more than one GPIO.
        pub unsafe fn wait_for_low(
            &mut self,
            _token: &mut GpioToken,
        ) -> impl Future<Output = ()> + use<'_> {
            GpioWait::new(self, 0)
        }
    }

    /// A future that waits for a gpio to become high.
    pub struct GpioWait<'a> {
        pin: &'a mut GpioPin,
        level: u8,
    }

    impl<'a> GpioWait<'a> {
        fn new(pin: &'a mut GpioPin, level: u8) -> Self {
            Self { pin, level }
        }
    }

    impl<'a> Future for GpioWait<'a> {
        type Output = ();

        fn poll(
            self: core::pin::Pin<&mut Self>,
            cx: &mut core::task::Context<'_>,
        ) -> core::task::Poll<Self::Output> {
            self.pin.data.fast_install(self.pin.pin.port);

            // Early detection of the event.  Also clears.
            // This should be non-racy as long as only one task at a time waits on the gpio.
            if self.pin.data.has_fired(self.pin.pin.pin) {
                return Poll::Ready(());
            }

            self.pin.data.register(self.pin.pin.pin, cx.waker());

            let mode = match self.level {
                0 => GPIO_INT_LEVEL_LOW,
                1 => GPIO_INT_LEVEL_HIGH,
                _ => unreachable!(),
            };

            unsafe {
                gpio_pin_interrupt_configure_dt(&self.pin.pin, mode);

                // Before sleeping, check if it fired, to avoid having to pend if it already
                // happened.
                if self.pin.data.has_fired(self.pin.pin.pin) {
                    return Poll::Ready(());
                }
            }

            Poll::Pending
        }
    }
}

#[cfg(not(feature = "async-drivers"))]
mod async_io {
    pub(crate) struct GpioStatic;

    impl GpioStatic {
        pub(crate) const fn new() -> Self {
            Self
        }
    }
}

pub(crate) use async_io::*;

/// Global instance to help make gpio in Rust slightly safer.
///
/// # Safety
///
/// To help with safety, the rust types use a global instance of a gpio-token.  Methods will
/// take a mutable reference to this, which will require either a single thread in the
/// application code, or something like a mutex or critical section to manage.  The operation
/// methods are still unsafe, because we have no control over what happens with the gpio
/// operations outside of Rust code, but this will help make the Rust usage at least better.
pub struct GpioToken(());

static GPIO_TOKEN: Unique = Unique::new();

impl GpioToken {
    /// Retrieves the gpio token.
    ///
    /// # Safety
    /// This is unsafe because lots of code in zephyr operates on the gpio drivers.  The user of the
    /// gpio subsystem, in general should either coordinate all gpio access across the system (the
    /// token coordinates this only within Rust code), or verify that the particular gpio driver and
    /// methods are thread safe.
    pub unsafe fn get_instance() -> Option<GpioToken> {
        if !GPIO_TOKEN.once() {
            return None;
        }
        Some(GpioToken(()))
    }
}

/// A single instance of a zephyr device to manage a gpio controller.  A gpio controller
/// represents a set of gpio pins, that are generally operated on by the same hardware block.
pub struct Gpio {
    /// The underlying device itself.
    #[allow(dead_code)]
    pub(crate) device: *const raw::device,
    /// Our associated data, used for callbacks.
    pub(crate) data: &'static GpioStatic,
}

// SAFETY: Gpio's can be shared with other threads.  Safety is maintained by the Token.
unsafe impl Send for Gpio {}

impl Gpio {
    /// Constructor, used by the devicetree generated code.
    ///
    /// TODO: Guarantee single instancing.
    #[allow(dead_code)]
    pub(crate) unsafe fn new(
        unique: &Unique,
        data: &'static GpioStatic,
        device: *const raw::device,
    ) -> Option<Gpio> {
        if !unique.once() {
            return None;
        }
        Some(Gpio { device, data })
    }

    /// Verify that the device is ready for use.  At a minimum, this means the device has been
    /// successfully initialized.
    pub fn is_ready(&self) -> bool {
        unsafe { raw::device_is_ready(self.device) }
    }
}

/// A GpioPin represents a single pin on a gpio device.
///
/// This is a lightweight wrapper around the Zephyr `gpio_dt_spec` structure.  Note that
/// multiple pins may share a gpio controller, and as such, all methods on this are both unsafe,
/// and require a mutable reference to the [`GpioToken`].
#[allow(dead_code)]
pub struct GpioPin {
    pub(crate) pin: raw::gpio_dt_spec,
    pub(crate) data: &'static GpioStatic,
}

// SAFETY: GpioPin's can be shared with other threads.  Safety is maintained by the Token.
unsafe impl Send for GpioPin {}

impl GpioPin {
    /// Constructor, used by the devicetree generated code.
    #[allow(dead_code)]
    pub(crate) unsafe fn new(
        unique: &Unique,
        _static: &NoStatic,
        device: *const raw::device,
        device_static: &'static GpioStatic,
        pin: u32,
        dt_flags: u32,
    ) -> Option<GpioPin> {
        if !unique.once() {
            return None;
        }
        Some(GpioPin {
            pin: raw::gpio_dt_spec {
                port: device,
                pin: pin as raw::gpio_pin_t,
                dt_flags: dt_flags as raw::gpio_dt_flags_t,
            },
            data: device_static,
        })
    }

    /// Verify that the device is ready for use.  At a minimum, this means the device has been
    /// successfully initialized.
    pub fn is_ready(&self) -> bool {
        self.get_gpio().is_ready()
    }

    /// Get the underlying Gpio device.
    pub fn get_gpio(&self) -> Gpio {
        Gpio {
            device: self.pin.port,
            data: self.data,
        }
    }

    /// Configure a single pin.
    ///
    /// # Safety
    ///
    /// The `_token` enforces single threaded use of gpios from Rust code.  However, many drivers
    /// within Zephyr use GPIOs, and to use gpios safely, the caller must ensure that there is
    /// either not simultaneous use, or the gpio driver in question is thread safe.
    pub unsafe fn configure(&mut self, _token: &mut GpioToken, extra_flags: raw::gpio_flags_t) {
        // TODO: Error?
        unsafe {
            raw::gpio_pin_configure(
                self.pin.port,
                self.pin.pin,
                self.pin.dt_flags as raw::gpio_flags_t | extra_flags,
            );
        }
    }

    /// Toggle pin level.
    ///
    /// # Safety
    ///
    /// The `_token` enforces single threaded use of gpios from Rust code.  However, many drivers
    /// within Zephyr use GPIOs, and to use gpios safely, the caller must ensure that there is
    /// either not simultaneous use, or the gpio driver in question is thread safe.
    pub unsafe fn toggle_pin(&mut self, _token: &mut GpioToken) {
        // TODO: Error?
        unsafe {
            raw::gpio_pin_toggle_dt(&self.pin);
        }
    }

    /// Set the logical level of the pin.
    pub unsafe fn set(&mut self, _token: &mut GpioToken, value: bool) {
        raw::gpio_pin_set_dt(&self.pin, value as c_int);
    }

    /// Read the logical level of the pin.
    pub unsafe fn get(&mut self, _token: &mut GpioToken) -> bool {
        match raw::gpio_pin_get_dt(&self.pin) {
            0 => false,
            1 => true,
            _ => panic!("TODO: Handle gpio get error"),
        }
    }
}