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}