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}