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:: { cell:: UnsafeCell , future:: Future , mem, sync:: atomic:: Ordering , task:: { Poll , Waker } } ;
24
+
25
+ use embassy_sync:: waitqueue:: AtomicWaker ;
26
+ use portable_atomic:: AtomicBool ;
27
+ use zephyr_sys:: { device, gpio_add_callback, gpio_callback, gpio_init_callback, gpio_pin_get, gpio_pin_interrupt_configure, gpio_pin_interrupt_configure_dt, gpio_port_pins_t, GPIO_INT_LEVEL_HIGH , ZR_GPIO_INT_MODE_DISABLE_ONLY } ;
28
+
29
+ use super :: { GpioPin , GpioToken } ;
30
+
31
+ pub ( crate ) struct GpioStatic {
32
+ /// The wakers for each of the gpios.
33
+ wakers : [ AtomicWaker ; 32 ] ,
34
+ /// Have we been initialized?
35
+ installed : AtomicBool ,
36
+ /// The data for the callback itself.
37
+ callback : UnsafeCell < gpio_callback > ,
38
+ }
39
+
40
+ unsafe impl Sync for GpioStatic { }
41
+
42
+ impl GpioStatic {
43
+ pub ( crate ) const fn new ( ) -> Self {
44
+ Self {
45
+ wakers : [ const { AtomicWaker :: new ( ) } ; 32 ] ,
46
+ installed : AtomicBool :: new ( false ) ,
47
+ // SAFETY: `installed` will tell us this need to be installed.
48
+ callback : unsafe { mem:: zeroed ( ) } ,
49
+ }
50
+ }
51
+
52
+ /// Ensure that the callback has been installed.
53
+ pub ( super ) fn fast_install ( & self , port : * const device ) {
54
+ if !self . installed . load ( Ordering :: Acquire ) {
55
+ self . install ( port) ;
56
+ }
57
+ }
58
+
59
+ fn install ( & self , port : * const device ) {
60
+ critical_section:: with ( |_| {
61
+ if !self . installed . load ( Ordering :: Acquire ) {
62
+ let cb = self . callback . get ( ) ;
63
+ // SAFETY: We're in a critical section, so there should be no concurrent use,
64
+ // and there should not be any calls from the driver.
65
+ unsafe {
66
+ gpio_init_callback ( cb, Some ( Self :: callback_handler) , 0 ) ;
67
+ gpio_add_callback ( port, cb) ;
68
+ }
69
+
70
+ self . installed . store ( true , Ordering :: Release ) ;
71
+ }
72
+ } )
73
+ }
74
+
75
+ /// Register (replacing) a given callback.
76
+ pub ( super ) fn register ( & self , pin : u8 , waker : & Waker ) {
77
+ self . wakers [ pin as usize ] . register ( waker) ;
78
+
79
+ // SAFETY: Inherently unsafe, due to how the Zephyr API is defined.
80
+ // The API appears to assume coherent memory, which although untrue, probably is "close
81
+ // enough" on the supported targets.
82
+ // The main issue is to ensure that any race is resolved in the direction of getting the
83
+ // callback more than needed, rather than missing. In the context here, ensure the
84
+ // waker is registered (which does use an atomic), before enabling the pin in the
85
+ // callback structure.
86
+ //
87
+ // If it seems that wakes are getting missed, it might be the case that this needs some
88
+ // kind of memory barrier.
89
+ let cb = self . callback . get ( ) ;
90
+ unsafe {
91
+ ( * cb) . pin_mask |= 1 << pin;
92
+ }
93
+ }
94
+
95
+ extern "C" fn callback_handler ( port : * const device , cb : * mut gpio_callback , mut pins : gpio_port_pins_t ) {
96
+ let data = unsafe { cb
97
+ . cast :: < u8 > ( )
98
+ . sub ( mem:: offset_of!( Self , callback) )
99
+ . cast :: < Self > ( )
100
+ } ;
101
+
102
+ // printkln!("CB called: pins: {pins:#x}");
103
+
104
+ // For each pin we are informed of.
105
+ while pins > 0 {
106
+ let pin = pins. trailing_zeros ( ) ;
107
+
108
+ pins &= !( 1 << pin) ;
109
+
110
+ // SAFETY: Handling this correctly is a bit tricky, especially with the
111
+ // un-coordinated 'pin-mask' value.
112
+ //
113
+ // For level-triggered interrupts, not disabling this will result in an interrupt
114
+ // storm.
115
+ unsafe {
116
+ // Disable the actual interrupt from the controller.
117
+ gpio_pin_interrupt_configure ( port, pin as u8 , ZR_GPIO_INT_MODE_DISABLE_ONLY ) ;
118
+
119
+ // Remove the callback bit. Unclear if this is actually useful.
120
+ // (*cb).pin_mask &= !(1 << pin);
121
+
122
+ // After the interrupt is off, wake the handler.
123
+ ( * data) . wakers [ pin as usize ] . wake ( ) ;
124
+ }
125
+ }
126
+ }
127
+ }
128
+
129
+ impl GpioPin {
130
+ /// Asynchronously wait for a gpio pin to become high.
131
+ ///
132
+ /// # Safety
133
+ ///
134
+ /// The `_token` enforces single use of gpios. Note that this makes it impossible to wait for
135
+ /// more than one GPIO.
136
+ ///
137
+ pub unsafe fn wait_for_high ( & mut self , _token : & mut GpioToken ) -> impl Future < Output = ( ) > + use < ' _ > {
138
+ GpioWait :: new ( self )
139
+ }
140
+ }
141
+
142
+ /// A future that waits for a gpio to become high.
143
+ pub struct GpioWait < ' a > {
144
+ pin : & ' a mut GpioPin ,
145
+ }
146
+
147
+ impl < ' a > GpioWait < ' a > {
148
+ fn new ( pin : & ' a mut GpioPin ) -> Self {
149
+ Self {
150
+ pin,
151
+ }
152
+ }
153
+ }
154
+
155
+ impl < ' a > Future for GpioWait < ' a > {
156
+ type Output = ( ) ;
157
+
158
+ fn poll ( self : core:: pin:: Pin < & mut Self > , cx : & mut core:: task:: Context < ' _ > ) -> core:: task:: Poll < Self :: Output > {
159
+ self . pin . data . fast_install ( self . pin . pin . port ) ;
160
+
161
+ self . pin . data . register ( self . pin . pin . pin , cx. waker ( ) ) ;
162
+
163
+ unsafe {
164
+ gpio_pin_interrupt_configure_dt ( & self . pin . pin , GPIO_INT_LEVEL_HIGH ) ;
165
+
166
+ if gpio_pin_get ( self . pin . pin . port , self . pin . pin . pin ) == 1 {
167
+ // TODO: Need to match with level.
168
+ // Zephyr doesn't have a way to determine if a given interrupt is pending, so the
169
+ // best we can do is just read the pin. This doesn't work for edges though.
170
+ return Poll :: Ready ( ( ) ) ;
171
+ }
172
+ }
173
+
174
+ Poll :: Pending
175
+ }
176
+ }
177
+ }
178
+
179
+ #[ cfg( not( feature = "async-drivers" ) ) ]
180
+ mod async_io {
181
+ pub ( crate ) struct GpioStatic ;
182
+
183
+ impl GpioStatic {
184
+ pub ( crate ) const fn new ( ) -> Self {
185
+ Self
186
+ }
187
+ }
188
+ }
189
+
190
+ pub ( crate ) use async_io:: * ;
191
+
15
192
/// Global instance to help make gpio in Rust slightly safer.
16
193
///
17
194
/// # Safety
@@ -47,6 +224,8 @@ pub struct Gpio {
47
224
/// The underlying device itself.
48
225
#[ allow( dead_code) ]
49
226
pub ( crate ) device : * const raw:: device ,
227
+ /// Our associated data, used for callbacks.
228
+ pub ( crate ) data : & ' static GpioStatic ,
50
229
}
51
230
52
231
// SAFETY: Gpio's can be shared with other threads. Safety is maintained by the Token.
@@ -57,11 +236,11 @@ impl Gpio {
57
236
///
58
237
/// TODO: Guarantee single instancing.
59
238
#[ allow( dead_code) ]
60
- pub ( crate ) unsafe fn new ( unique : & Unique , device : * const raw:: device ) -> Option < Gpio > {
239
+ pub ( crate ) unsafe fn new ( unique : & Unique , data : & ' static GpioStatic , device : * const raw:: device ) -> Option < Gpio > {
61
240
if !unique. once ( ) {
62
241
return None ;
63
242
}
64
- Some ( Gpio { device } )
243
+ Some ( Gpio { device, data } )
65
244
}
66
245
67
246
/// Verify that the device is ready for use. At a minimum, this means the device has been
@@ -79,6 +258,7 @@ impl Gpio {
79
258
#[ allow( dead_code) ]
80
259
pub struct GpioPin {
81
260
pub ( crate ) pin : raw:: gpio_dt_spec ,
261
+ pub ( crate ) data : & ' static GpioStatic ,
82
262
}
83
263
84
264
// SAFETY: GpioPin's can be shared with other threads. Safety is maintained by the Token.
@@ -89,7 +269,9 @@ impl GpioPin {
89
269
#[ allow( dead_code) ]
90
270
pub ( crate ) unsafe fn new (
91
271
unique : & Unique ,
272
+ _static : & NoStatic ,
92
273
device : * const raw:: device ,
274
+ device_static : & ' static GpioStatic ,
93
275
pin : u32 ,
94
276
dt_flags : u32 ,
95
277
) -> Option < GpioPin > {
@@ -102,6 +284,7 @@ impl GpioPin {
102
284
pin : pin as raw:: gpio_pin_t ,
103
285
dt_flags : dt_flags as raw:: gpio_dt_flags_t ,
104
286
} ,
287
+ data : device_static,
105
288
} )
106
289
}
107
290
@@ -115,6 +298,7 @@ impl GpioPin {
115
298
pub fn get_gpio ( & self ) -> Gpio {
116
299
Gpio {
117
300
device : self . pin . port ,
301
+ data : self . data ,
118
302
}
119
303
}
120
304
0 commit comments