9
9
10
10
use core:: ffi:: c_int;
11
11
12
- use super :: Unique ;
12
+ use super :: { NoStatic , Unique } ;
13
13
use crate :: raw;
14
14
15
+ #[ cfg( feature = "async-drivers" ) ]
16
+ mod 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" ) ) ]
236
+ mod async_io {
237
+ pub ( crate ) struct GpioStatic ;
238
+
239
+ impl GpioStatic {
240
+ pub ( crate ) const fn new ( ) -> Self {
241
+ Self
242
+ }
243
+ }
244
+ }
245
+
246
+ pub ( crate ) use async_io:: * ;
247
+
15
248
/// Global instance to help make gpio in Rust slightly safer.
16
249
///
17
250
/// # Safety
@@ -47,6 +280,8 @@ pub struct Gpio {
47
280
/// The underlying device itself.
48
281
#[ allow( dead_code) ]
49
282
pub ( crate ) device : * const raw:: device ,
283
+ /// Our associated data, used for callbacks.
284
+ pub ( crate ) data : & ' static GpioStatic ,
50
285
}
51
286
52
287
// SAFETY: Gpio's can be shared with other threads. Safety is maintained by the Token.
@@ -57,11 +292,15 @@ impl Gpio {
57
292
///
58
293
/// TODO: Guarantee single instancing.
59
294
#[ allow( dead_code) ]
60
- pub ( crate ) unsafe fn new ( unique : & Unique , device : * const raw:: device ) -> Option < Gpio > {
295
+ pub ( crate ) unsafe fn new (
296
+ unique : & Unique ,
297
+ data : & ' static GpioStatic ,
298
+ device : * const raw:: device ,
299
+ ) -> Option < Gpio > {
61
300
if !unique. once ( ) {
62
301
return None ;
63
302
}
64
- Some ( Gpio { device } )
303
+ Some ( Gpio { device, data } )
65
304
}
66
305
67
306
/// Verify that the device is ready for use. At a minimum, this means the device has been
@@ -79,6 +318,7 @@ impl Gpio {
79
318
#[ allow( dead_code) ]
80
319
pub struct GpioPin {
81
320
pub ( crate ) pin : raw:: gpio_dt_spec ,
321
+ pub ( crate ) data : & ' static GpioStatic ,
82
322
}
83
323
84
324
// SAFETY: GpioPin's can be shared with other threads. Safety is maintained by the Token.
@@ -89,7 +329,9 @@ impl GpioPin {
89
329
#[ allow( dead_code) ]
90
330
pub ( crate ) unsafe fn new (
91
331
unique : & Unique ,
332
+ _static : & NoStatic ,
92
333
device : * const raw:: device ,
334
+ device_static : & ' static GpioStatic ,
93
335
pin : u32 ,
94
336
dt_flags : u32 ,
95
337
) -> Option < GpioPin > {
@@ -102,6 +344,7 @@ impl GpioPin {
102
344
pin : pin as raw:: gpio_pin_t ,
103
345
dt_flags : dt_flags as raw:: gpio_dt_flags_t ,
104
346
} ,
347
+ data : device_static,
105
348
} )
106
349
}
107
350
@@ -115,6 +358,7 @@ impl GpioPin {
115
358
pub fn get_gpio ( & self ) -> Gpio {
116
359
Gpio {
117
360
device : self . pin . port ,
361
+ data : self . data ,
118
362
}
119
363
}
120
364
0 commit comments