Skip to main content

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, ZR_GPIO_INT_LEVEL_HIGH,
35        ZR_GPIO_INT_LEVEL_LOW, ZR_GPIO_INT_MODE_DISABLE_ONLY,
36    };
37
38    use crate::sync::atomic::{AtomicBool, AtomicU32};
39
40    use super::GpioPin;
41
42    /// Per-controller static data for GPIO async operations.
43    pub struct GpioStatic {
44        /// The wakers for each of the gpios.
45        wakers: [AtomicWaker; 32],
46        /// Indicates when an interrupt has fired.  Used to definitively indicate the event has
47        /// happened, so we can wake.
48        fired: AtomicU32,
49        /// Have we been initialized?
50        installed: AtomicBool,
51        /// The data for the callback itself.
52        callback: UnsafeCell<gpio_callback>,
53    }
54
55    unsafe impl Sync for GpioStatic {}
56
57    impl GpioStatic {
58        pub(crate) const fn new() -> Self {
59            Self {
60                wakers: [const { AtomicWaker::new() }; 32],
61                fired: AtomicU32::new(0),
62                installed: AtomicBool::new(false),
63                // SAFETY: `installed` will tell us this need to be installed.
64                callback: unsafe { mem::zeroed() },
65            }
66        }
67
68        /// Ensure that the callback has been installed.
69        pub(super) fn fast_install(&self, port: *const device) {
70            if !self.installed.load(Ordering::Acquire) {
71                self.install(port);
72            }
73        }
74
75        fn install(&self, port: *const device) {
76            critical_section::with(|_| {
77                if !self.installed.load(Ordering::Acquire) {
78                    let cb = self.callback.get();
79                    // SAFETY: We're in a critical section, so there should be no concurrent use,
80                    // and there should not be any calls from the driver.
81                    unsafe {
82                        gpio_init_callback(cb, Some(Self::callback_handler), 0);
83                        gpio_add_callback(port, cb);
84                    }
85
86                    self.installed.store(true, Ordering::Release);
87                }
88            })
89        }
90
91        /// Register (replacing) a given callback.
92        pub(super) fn register(&self, pin: u8, waker: &Waker) {
93            self.wakers[pin as usize].register(waker);
94
95            // SAFETY: Inherently unsafe, due to how the Zephyr API is defined.
96            // The API appears to assume coherent memory, which although untrue, probably is "close
97            // enough" on the supported targets.
98            // The main issue is to ensure that any race is resolved in the direction of getting the
99            // callback more than needed, rather than missing.  In the context here, ensure the
100            // waker is registered (which does use an atomic), before enabling the pin in the
101            // callback structure.
102            //
103            // If it seems that wakes are getting missed, it might be the case that this needs some
104            // kind of memory barrier.
105            let cb = self.callback.get();
106            unsafe {
107                (*cb).pin_mask |= 1 << pin;
108            }
109        }
110
111        extern "C" fn callback_handler(
112            port: *const device,
113            cb: *mut gpio_callback,
114            mut pins: gpio_port_pins_t,
115        ) {
116            let data = unsafe {
117                cb.cast::<u8>()
118                    .sub(mem::offset_of!(Self, callback))
119                    .cast::<Self>()
120            };
121
122            // For each pin we are informed of.
123            while pins > 0 {
124                let pin = pins.trailing_zeros();
125
126                pins &= !(1 << pin);
127
128                // SAFETY: Handling this correctly is a bit tricky, especially with the
129                // un-coordinated 'pin-mask' value.
130                //
131                // For level-triggered interrupts, not disabling this will result in an interrupt
132                // storm.
133                unsafe {
134                    // Disable the actual interrupt from the controller.
135                    gpio_pin_interrupt_configure(port, pin as u8, ZR_GPIO_INT_MODE_DISABLE_ONLY);
136
137                    // Remove the callback bit.  Unclear if this is actually useful.
138                    (*cb).pin_mask &= !(1 << pin);
139
140                    // Indicate that we have fired.
141                    // AcqRel is sufficient for ordering across a single atomic.
142                    (*data).fired.fetch_or(1 << pin, Ordering::AcqRel);
143
144                    // After the interrupt is off, wake the handler.
145                    (*data).wakers[pin as usize].wake();
146                }
147            }
148        }
149
150        /// Check if we have fired for a given pin.  Clears the status.
151        pub(crate) fn has_fired(&self, pin: u8) -> bool {
152            let value = self.fired.fetch_and(!(1 << pin), Ordering::AcqRel);
153            value & (1 << pin) != 0
154        }
155    }
156
157    impl GpioPin {
158        /// Asynchronously wait for a gpio pin to become high.
159        ///
160        /// # Safety
161        ///
162        /// Safety of multiple GPIOs depends on the underlying controller.
163        pub unsafe fn wait_for_high(&mut self) -> impl Future<Output = ()> + use<'_> {
164            GpioWait::new(self, 1)
165        }
166
167        /// Asynchronously wait for a gpio pin to become low.
168        ///
169        /// # Safety
170        ///
171        /// Safety of multiple GPIOs depends on the underlying controller.
172        pub unsafe fn wait_for_low(&mut self) -> impl Future<Output = ()> + use<'_> {
173            GpioWait::new(self, 0)
174        }
175    }
176
177    /// A future that waits for a gpio to become high.
178    pub struct GpioWait<'a> {
179        pin: &'a mut GpioPin,
180        level: u8,
181    }
182
183    impl<'a> GpioWait<'a> {
184        fn new(pin: &'a mut GpioPin, level: u8) -> Self {
185            Self { pin, level }
186        }
187    }
188
189    impl<'a> Future for GpioWait<'a> {
190        type Output = ();
191
192        fn poll(
193            self: core::pin::Pin<&mut Self>,
194            cx: &mut core::task::Context<'_>,
195        ) -> core::task::Poll<Self::Output> {
196            self.pin.data.fast_install(self.pin.pin.port);
197
198            // Early detection of the event.  Also clears.
199            // This should be non-racy as long as only one task at a time waits on the gpio.
200            if self.pin.data.has_fired(self.pin.pin.pin) {
201                return Poll::Ready(());
202            }
203
204            self.pin.data.register(self.pin.pin.pin, cx.waker());
205
206            let mode = match self.level {
207                0 => ZR_GPIO_INT_LEVEL_LOW,
208                1 => ZR_GPIO_INT_LEVEL_HIGH,
209                _ => unreachable!(),
210            };
211
212            unsafe {
213                gpio_pin_interrupt_configure_dt(&self.pin.pin, mode);
214
215                // Before sleeping, check if it fired, to avoid having to pend if it already
216                // happened.
217                if self.pin.data.has_fired(self.pin.pin.pin) {
218                    return Poll::Ready(());
219                }
220            }
221
222            Poll::Pending
223        }
224    }
225}
226
227#[cfg(not(feature = "async-drivers"))]
228mod async_io {
229    /// Per-controller static data for GPIO (stub when async-drivers is disabled).
230    pub struct GpioStatic;
231
232    impl GpioStatic {
233        pub(crate) const fn new() -> Self {
234            Self
235        }
236    }
237}
238
239pub use async_io::*;
240
241/// A single instance of a zephyr device to manage a gpio controller.  A gpio controller
242/// represents a set of gpio pins, that are generally operated on by the same hardware block.
243pub struct Gpio {
244    /// The underlying device itself.
245    #[allow(dead_code)]
246    pub(crate) device: *const raw::device,
247    /// Our associated data, used for callbacks.
248    pub(crate) data: &'static GpioStatic,
249}
250
251// SAFETY: Gpio's can be shared with other threads.  Safety is maintained by the Token.
252unsafe impl Send for Gpio {}
253
254impl Gpio {
255    /// Constructor, used by the devicetree generated code.
256    ///
257    /// TODO: Guarantee single instancing.
258    #[allow(dead_code)]
259    pub(crate) unsafe fn new(
260        unique: &Unique,
261        data: &'static GpioStatic,
262        device: *const raw::device,
263    ) -> Option<Gpio> {
264        if !unique.once() {
265            return None;
266        }
267        Some(Gpio { device, data })
268    }
269
270    /// Verify that the device is ready for use.  At a minimum, this means the device has been
271    /// successfully initialized.
272    pub fn is_ready(&self) -> bool {
273        unsafe { raw::device_is_ready(self.device) }
274    }
275}
276
277/// A GpioPin represents a single pin on a gpio device.
278///
279/// This is a lightweight wrapper around the Zephyr `gpio_dt_spec` structure.
280#[allow(dead_code)]
281pub struct GpioPin {
282    pub(crate) pin: raw::gpio_dt_spec,
283    pub(crate) data: &'static GpioStatic,
284}
285
286impl core::fmt::Debug for GpioPin {
287    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
288        f.debug_struct("GpioPin")
289            .field("port", &self.pin.port)
290            .field("pin", &self.pin.pin)
291            .field("dt_flags", &self.pin.dt_flags)
292            .finish()
293    }
294}
295
296// SAFETY: GpioPin's can be shared with other threads.  Safety is maintained by the Token.
297unsafe impl Send for GpioPin {}
298
299impl GpioPin {
300    /// Constructor, used by the devicetree generated code.
301    #[allow(dead_code)]
302    pub(crate) unsafe fn new(
303        unique: &Unique,
304        _static: &NoStatic,
305        device: *const raw::device,
306        device_static: &'static GpioStatic,
307        pin: u32,
308        dt_flags: u32,
309    ) -> Option<GpioPin> {
310        if !unique.once() {
311            return None;
312        }
313        Some(GpioPin {
314            pin: raw::gpio_dt_spec {
315                port: device,
316                pin: pin as raw::gpio_pin_t,
317                dt_flags: dt_flags as raw::gpio_dt_flags_t,
318            },
319            data: device_static,
320        })
321    }
322
323    /// An unsafe constructor intended for use outside of this crate.  Does not
324    /// guarantee any uniqueness (which isn't guaranteed or needed by the Gpio
325    /// device anyway).
326    ///
327    /// # Safety
328    ///
329    /// - `device` must be a valid pointer to a Zephyr GPIO controller device
330    ///   that remains valid for the lifetime of the returned `GpioPin`.
331    /// - `device_static` must be the `GpioStatic` associated with that same
332    ///   controller.
333    /// - `pin` and `dt_flags` must fit within Zephyr's `gpio_pin_t` and
334    ///   `gpio_dt_flags_t` types respectively (values are truncated on cast).
335    pub unsafe fn raw_new(
336        device: *const raw::device,
337        device_static: &'static GpioStatic,
338        pin: u32,
339        dt_flags: u32,
340    ) -> GpioPin {
341        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    /// Concurrency safety is determined by the underlying driver.
370    pub fn configure(&mut self, extra_flags: raw::gpio_flags_t) {
371        // TODO: Error?
372        unsafe {
373            raw::gpio_pin_configure(
374                self.pin.port,
375                self.pin.pin,
376                self.pin.dt_flags as raw::gpio_flags_t | extra_flags,
377            );
378        }
379    }
380
381    /// Toggle pin level.
382    ///
383    /// # Safety
384    ///
385    /// Concurrency safety is determined by the underlying driver.
386    pub fn toggle_pin(&mut self) {
387        // TODO: Error?
388        unsafe {
389            raw::gpio_pin_toggle_dt(&self.pin);
390        }
391    }
392
393    /// Set the logical level of the pin.
394    pub fn set(&mut self, value: bool) {
395        unsafe {
396            raw::gpio_pin_set_dt(&self.pin, value as c_int);
397        }
398    }
399
400    /// Read the logical level of the pin.
401    pub fn get(&mut self) -> bool {
402        unsafe {
403            match raw::gpio_pin_get_dt(&self.pin) {
404                0 => false,
405                1 => true,
406                _ => panic!("TODO: Handle gpio get error"),
407            }
408        }
409    }
410}