zephyr/device/
gpio.rs

1//! Most devices in Zephyr operate on a `struct device`.  This provides untyped access to
2//! devices.  We want to have stronger typing in the Zephyr interfaces, so most of these types
3//! will be wrapped in another structure.  This wraps a Gpio device, and provides methods to
4//! most of the operations on gpios.
5//!
6//! Safey: In general, even just using gpio pins is unsafe in Zephyr.  The gpio drivers are used
7//! pervasively throughout Zephyr device drivers.  As such, most of the calls in this module are
8//! unsafe.
9
10use core::ffi::c_int;
11
12use super::{NoStatic, Unique};
13use crate::raw;
14
15#[cfg(feature = "async-drivers")]
16mod async_io {
17    //! Async operations for gpio drivers.
18    //!
19    //! For now, we make an assumption that a gpio controller can contain up to 32 gpios, which is
20    //! the largest number currently used, although this might change with 64-bit targest in the
21    //! future.
22
23    use core::{
24        cell::UnsafeCell,
25        future::Future,
26        mem,
27        sync::atomic::Ordering,
28        task::{Poll, Waker},
29    };
30
31    use embassy_sync::waitqueue::AtomicWaker;
32    use zephyr_sys::{
33        device, gpio_add_callback, gpio_callback, gpio_init_callback, gpio_pin_interrupt_configure,
34        gpio_pin_interrupt_configure_dt, gpio_port_pins_t, GPIO_INT_LEVEL_HIGH, GPIO_INT_LEVEL_LOW,
35        ZR_GPIO_INT_MODE_DISABLE_ONLY,
36    };
37
38    use crate::sync::atomic::{AtomicBool, AtomicU32};
39
40    use super::{GpioPin, GpioToken};
41
42    pub(crate) struct GpioStatic {
43        /// The wakers for each of the gpios.
44        wakers: [AtomicWaker; 32],
45        /// Indicates when an interrupt has fired.  Used to definitively indicate the event has
46        /// happened, so we can wake.
47        fired: AtomicU32,
48        /// Have we been initialized?
49        installed: AtomicBool,
50        /// The data for the callback itself.
51        callback: UnsafeCell<gpio_callback>,
52    }
53
54    unsafe impl Sync for GpioStatic {}
55
56    impl GpioStatic {
57        pub(crate) const fn new() -> Self {
58            Self {
59                wakers: [const { AtomicWaker::new() }; 32],
60                fired: AtomicU32::new(0),
61                installed: AtomicBool::new(false),
62                // SAFETY: `installed` will tell us this need to be installed.
63                callback: unsafe { mem::zeroed() },
64            }
65        }
66
67        /// Ensure that the callback has been installed.
68        pub(super) fn fast_install(&self, port: *const device) {
69            if !self.installed.load(Ordering::Acquire) {
70                self.install(port);
71            }
72        }
73
74        fn install(&self, port: *const device) {
75            critical_section::with(|_| {
76                if !self.installed.load(Ordering::Acquire) {
77                    let cb = self.callback.get();
78                    // SAFETY: We're in a critical section, so there should be no concurrent use,
79                    // and there should not be any calls from the driver.
80                    unsafe {
81                        gpio_init_callback(cb, Some(Self::callback_handler), 0);
82                        gpio_add_callback(port, cb);
83                    }
84
85                    self.installed.store(true, Ordering::Release);
86                }
87            })
88        }
89
90        /// Register (replacing) a given callback.
91        pub(super) fn register(&self, pin: u8, waker: &Waker) {
92            self.wakers[pin as usize].register(waker);
93
94            // SAFETY: Inherently unsafe, due to how the Zephyr API is defined.
95            // The API appears to assume coherent memory, which although untrue, probably is "close
96            // enough" on the supported targets.
97            // The main issue is to ensure that any race is resolved in the direction of getting the
98            // callback more than needed, rather than missing.  In the context here, ensure the
99            // waker is registered (which does use an atomic), before enabling the pin in the
100            // callback structure.
101            //
102            // If it seems that wakes are getting missed, it might be the case that this needs some
103            // kind of memory barrier.
104            let cb = self.callback.get();
105            unsafe {
106                (*cb).pin_mask |= 1 << pin;
107            }
108        }
109
110        extern "C" fn callback_handler(
111            port: *const device,
112            cb: *mut gpio_callback,
113            mut pins: gpio_port_pins_t,
114        ) {
115            let data = unsafe {
116                cb.cast::<u8>()
117                    .sub(mem::offset_of!(Self, callback))
118                    .cast::<Self>()
119            };
120
121            // For each pin we are informed of.
122            while pins > 0 {
123                let pin = pins.trailing_zeros();
124
125                pins &= !(1 << pin);
126
127                // SAFETY: Handling this correctly is a bit tricky, especially with the
128                // un-coordinated 'pin-mask' value.
129                //
130                // For level-triggered interrupts, not disabling this will result in an interrupt
131                // storm.
132                unsafe {
133                    // Disable the actual interrupt from the controller.
134                    gpio_pin_interrupt_configure(port, pin as u8, ZR_GPIO_INT_MODE_DISABLE_ONLY);
135
136                    // Remove the callback bit.  Unclear if this is actually useful.
137                    (*cb).pin_mask &= !(1 << pin);
138
139                    // Indicate that we have fired.
140                    // AcqRel is sufficient for ordering across a single atomic.
141                    (*data).fired.fetch_or(1 << pin, Ordering::AcqRel);
142
143                    // After the interrupt is off, wake the handler.
144                    (*data).wakers[pin as usize].wake();
145                }
146            }
147        }
148
149        /// Check if we have fired for a given pin.  Clears the status.
150        pub(crate) fn has_fired(&self, pin: u8) -> bool {
151            let value = self.fired.fetch_and(!(1 << pin), Ordering::AcqRel);
152            value & (1 << pin) != 0
153        }
154    }
155
156    impl GpioPin {
157        /// Asynchronously wait for a gpio pin to become high.
158        ///
159        /// # Safety
160        ///
161        /// The `_token` enforces single use of gpios.  Note that this makes it impossible to wait for
162        /// more than one GPIO.
163        ///
164        pub unsafe fn wait_for_high(
165            &mut self,
166            _token: &mut GpioToken,
167        ) -> impl Future<Output = ()> + use<'_> {
168            GpioWait::new(self, 1)
169        }
170
171        /// Asynchronously wait for a gpio pin to become low.
172        ///
173        /// # Safety
174        ///
175        /// The `_token` enforces single use of gpios.  Note that this makes it impossible to wait
176        /// for more than one GPIO.
177        pub unsafe fn wait_for_low(
178            &mut self,
179            _token: &mut GpioToken,
180        ) -> impl Future<Output = ()> + use<'_> {
181            GpioWait::new(self, 0)
182        }
183    }
184
185    /// A future that waits for a gpio to become high.
186    pub struct GpioWait<'a> {
187        pin: &'a mut GpioPin,
188        level: u8,
189    }
190
191    impl<'a> GpioWait<'a> {
192        fn new(pin: &'a mut GpioPin, level: u8) -> Self {
193            Self { pin, level }
194        }
195    }
196
197    impl<'a> Future for GpioWait<'a> {
198        type Output = ();
199
200        fn poll(
201            self: core::pin::Pin<&mut Self>,
202            cx: &mut core::task::Context<'_>,
203        ) -> core::task::Poll<Self::Output> {
204            self.pin.data.fast_install(self.pin.pin.port);
205
206            // Early detection of the event.  Also clears.
207            // This should be non-racy as long as only one task at a time waits on the gpio.
208            if self.pin.data.has_fired(self.pin.pin.pin) {
209                return Poll::Ready(());
210            }
211
212            self.pin.data.register(self.pin.pin.pin, cx.waker());
213
214            let mode = match self.level {
215                0 => GPIO_INT_LEVEL_LOW,
216                1 => GPIO_INT_LEVEL_HIGH,
217                _ => unreachable!(),
218            };
219
220            unsafe {
221                gpio_pin_interrupt_configure_dt(&self.pin.pin, mode);
222
223                // Before sleeping, check if it fired, to avoid having to pend if it already
224                // happened.
225                if self.pin.data.has_fired(self.pin.pin.pin) {
226                    return Poll::Ready(());
227                }
228            }
229
230            Poll::Pending
231        }
232    }
233}
234
235#[cfg(not(feature = "async-drivers"))]
236mod async_io {
237    pub(crate) struct GpioStatic;
238
239    impl GpioStatic {
240        pub(crate) const fn new() -> Self {
241            Self
242        }
243    }
244}
245
246pub(crate) use async_io::*;
247
248/// Global instance to help make gpio in Rust slightly safer.
249///
250/// # Safety
251///
252/// To help with safety, the rust types use a global instance of a gpio-token.  Methods will
253/// take a mutable reference to this, which will require either a single thread in the
254/// application code, or something like a mutex or critical section to manage.  The operation
255/// methods are still unsafe, because we have no control over what happens with the gpio
256/// operations outside of Rust code, but this will help make the Rust usage at least better.
257pub struct GpioToken(());
258
259static GPIO_TOKEN: Unique = Unique::new();
260
261impl GpioToken {
262    /// Retrieves the gpio token.
263    ///
264    /// # Safety
265    /// This is unsafe because lots of code in zephyr operates on the gpio drivers.  The user of the
266    /// gpio subsystem, in general should either coordinate all gpio access across the system (the
267    /// token coordinates this only within Rust code), or verify that the particular gpio driver and
268    /// methods are thread safe.
269    pub unsafe fn get_instance() -> Option<GpioToken> {
270        if !GPIO_TOKEN.once() {
271            return None;
272        }
273        Some(GpioToken(()))
274    }
275}
276
277/// A single instance of a zephyr device to manage a gpio controller.  A gpio controller
278/// represents a set of gpio pins, that are generally operated on by the same hardware block.
279pub struct Gpio {
280    /// The underlying device itself.
281    #[allow(dead_code)]
282    pub(crate) device: *const raw::device,
283    /// Our associated data, used for callbacks.
284    pub(crate) data: &'static GpioStatic,
285}
286
287// SAFETY: Gpio's can be shared with other threads.  Safety is maintained by the Token.
288unsafe impl Send for Gpio {}
289
290impl Gpio {
291    /// Constructor, used by the devicetree generated code.
292    ///
293    /// TODO: Guarantee single instancing.
294    #[allow(dead_code)]
295    pub(crate) unsafe fn new(
296        unique: &Unique,
297        data: &'static GpioStatic,
298        device: *const raw::device,
299    ) -> Option<Gpio> {
300        if !unique.once() {
301            return None;
302        }
303        Some(Gpio { device, data })
304    }
305
306    /// Verify that the device is ready for use.  At a minimum, this means the device has been
307    /// successfully initialized.
308    pub fn is_ready(&self) -> bool {
309        unsafe { raw::device_is_ready(self.device) }
310    }
311}
312
313/// A GpioPin represents a single pin on a gpio device.
314///
315/// This is a lightweight wrapper around the Zephyr `gpio_dt_spec` structure.  Note that
316/// multiple pins may share a gpio controller, and as such, all methods on this are both unsafe,
317/// and require a mutable reference to the [`GpioToken`].
318#[allow(dead_code)]
319pub struct GpioPin {
320    pub(crate) pin: raw::gpio_dt_spec,
321    pub(crate) data: &'static GpioStatic,
322}
323
324// SAFETY: GpioPin's can be shared with other threads.  Safety is maintained by the Token.
325unsafe impl Send for GpioPin {}
326
327impl GpioPin {
328    /// Constructor, used by the devicetree generated code.
329    #[allow(dead_code)]
330    pub(crate) unsafe fn new(
331        unique: &Unique,
332        _static: &NoStatic,
333        device: *const raw::device,
334        device_static: &'static GpioStatic,
335        pin: u32,
336        dt_flags: u32,
337    ) -> Option<GpioPin> {
338        if !unique.once() {
339            return None;
340        }
341        Some(GpioPin {
342            pin: raw::gpio_dt_spec {
343                port: device,
344                pin: pin as raw::gpio_pin_t,
345                dt_flags: dt_flags as raw::gpio_dt_flags_t,
346            },
347            data: device_static,
348        })
349    }
350
351    /// Verify that the device is ready for use.  At a minimum, this means the device has been
352    /// successfully initialized.
353    pub fn is_ready(&self) -> bool {
354        self.get_gpio().is_ready()
355    }
356
357    /// Get the underlying Gpio device.
358    pub fn get_gpio(&self) -> Gpio {
359        Gpio {
360            device: self.pin.port,
361            data: self.data,
362        }
363    }
364
365    /// Configure a single pin.
366    ///
367    /// # Safety
368    ///
369    /// The `_token` enforces single threaded use of gpios from Rust code.  However, many drivers
370    /// within Zephyr use GPIOs, and to use gpios safely, the caller must ensure that there is
371    /// either not simultaneous use, or the gpio driver in question is thread safe.
372    pub unsafe fn configure(&mut self, _token: &mut GpioToken, extra_flags: raw::gpio_flags_t) {
373        // TODO: Error?
374        unsafe {
375            raw::gpio_pin_configure(
376                self.pin.port,
377                self.pin.pin,
378                self.pin.dt_flags as raw::gpio_flags_t | extra_flags,
379            );
380        }
381    }
382
383    /// Toggle pin level.
384    ///
385    /// # Safety
386    ///
387    /// The `_token` enforces single threaded use of gpios from Rust code.  However, many drivers
388    /// within Zephyr use GPIOs, and to use gpios safely, the caller must ensure that there is
389    /// either not simultaneous use, or the gpio driver in question is thread safe.
390    pub unsafe fn toggle_pin(&mut self, _token: &mut GpioToken) {
391        // TODO: Error?
392        unsafe {
393            raw::gpio_pin_toggle_dt(&self.pin);
394        }
395    }
396
397    /// Set the logical level of the pin.
398    pub unsafe fn set(&mut self, _token: &mut GpioToken, value: bool) {
399        raw::gpio_pin_set_dt(&self.pin, value as c_int);
400    }
401
402    /// Read the logical level of the pin.
403    pub unsafe fn get(&mut self, _token: &mut GpioToken) -> bool {
404        match raw::gpio_pin_get_dt(&self.pin) {
405            0 => false,
406            1 => true,
407            _ => panic!("TODO: Handle gpio get error"),
408        }
409    }
410}