@@ -35,6 +35,107 @@ where
35
35
Word : Copy + ' static ,
36
36
= impl Future < Output = Result < ( ) , T :: Error > > ;
37
37
38
+ #[ macro_export]
39
+ /// Do an SPI transaction on a bus.
40
+ /// This is a safe wrapper for [SpiDevice::transaction], which handles dereferencing the raw pointer for you.
41
+ ///
42
+ /// # Examples
43
+ ///
44
+ /// ```
45
+ /// use embedded_hal_async::spi::{transaction, SpiBus, SpiBusRead, SpiBusWrite, SpiDevice};
46
+ ///
47
+ /// pub async fn transaction_example<SPI>(mut device: SPI) -> Result<u32, SPI::Error>
48
+ /// where
49
+ /// SPI: SpiDevice,
50
+ /// SPI::Bus: SpiBus,
51
+ /// {
52
+ /// transaction!(&mut device, move |bus| async move {
53
+ /// // Unlike `SpiDevice::transaction`, we don't need to
54
+ /// // manually dereference a pointer in order to use the bus.
55
+ /// bus.write(&[42]).await?;
56
+ /// let mut data = [0; 4];
57
+ /// bus.read(&mut data).await?;
58
+ /// Ok(u32::from_be_bytes(data))
59
+ /// })
60
+ /// .await
61
+ /// }
62
+ /// ```
63
+ ///
64
+ /// Note that the compiler will prevent you from moving the bus reference outside of the closure
65
+ /// ```compile_fail
66
+ /// # use embedded_hal_async::spi::{transaction, SpiBus, SpiBusRead, SpiBusWrite, SpiDevice};
67
+ /// #
68
+ /// # pub async fn smuggle_test<SPI>(mut device: SPI) -> Result<(), SPI::Error>
69
+ /// # where
70
+ /// # SPI: SpiDevice,
71
+ /// # SPI::Bus: SpiBus,
72
+ /// # {
73
+ /// let mut bus_smuggler: Option<&mut SPI::Bus> = None;
74
+ /// transaction!(&mut device, move |bus| async move {
75
+ /// bus_smuggler = Some(bus);
76
+ /// Ok(())
77
+ /// })
78
+ /// .await
79
+ /// # }
80
+ /// ```
81
+ macro_rules! spi_transaction {
82
+ ( $device: expr, move |$bus: ident| async move $closure_body: expr) => {
83
+ $crate:: spi:: SpiDevice :: transaction( $device, move |$bus| {
84
+ // Safety: Implementers of the `SpiDevice` trait guarantee that the pointer is
85
+ // valid and dereferencable for the entire duration of the closure.
86
+ let $bus = unsafe { & mut * $bus } ;
87
+ async move {
88
+ let result = $closure_body;
89
+ let $bus = $bus; // Ensure that the bus reference was not moved out of the closure
90
+ let _ = $bus; // Silence the "unused variable" warning from prevous line
91
+ result
92
+ }
93
+ } )
94
+ } ;
95
+ ( $device: expr, move |$bus: ident| async $closure_body: expr) => {
96
+ $crate:: spi:: SpiDevice :: transaction( $device, move |$bus| {
97
+ // Safety: Implementers of the `SpiDevice` trait guarantee that the pointer is
98
+ // valid and dereferencable for the entire duration of the closure.
99
+ let $bus = unsafe { & mut * $bus } ;
100
+ async {
101
+ let result = $closure_body;
102
+ let $bus = $bus; // Ensure that the bus reference was not moved out of the closure
103
+ let _ = $bus; // Silence the "unused variable" warning from prevous line
104
+ result
105
+ }
106
+ } )
107
+ } ;
108
+ ( $device: expr, |$bus: ident| async move $closure_body: expr) => {
109
+ $crate:: spi:: SpiDevice :: transaction( $device, |$bus| {
110
+ // Safety: Implementers of the `SpiDevice` trait guarantee that the pointer is
111
+ // valid and dereferencable for the entire duration of the closure.
112
+ let $bus = unsafe { & mut * $bus } ;
113
+ async move {
114
+ let result = $closure_body;
115
+ let $bus = $bus; // Ensure that the bus reference was not moved out of the closure
116
+ let _ = $bus; // Silence the "unused variable" warning from prevous line
117
+ result
118
+ }
119
+ } )
120
+ } ;
121
+ ( $device: expr, |$bus: ident| async $closure_body: expr) => {
122
+ $crate:: spi:: SpiDevice :: transaction( $device, |$bus| {
123
+ // Safety: Implementers of the `SpiDevice` trait guarantee that the pointer is
124
+ // valid and dereferencable for the entire duration of the closure.
125
+ let $bus = unsafe { & mut * $bus } ;
126
+ async {
127
+ let result = $closure_body;
128
+ let $bus = $bus; // Ensure that the bus reference was not moved out of the closure
129
+ let _ = $bus; // Silence the "unused variable" warning from prevous line
130
+ result
131
+ }
132
+ } )
133
+ } ;
134
+ }
135
+
136
+ #[ doc( inline) ]
137
+ pub use spi_transaction as transaction;
138
+
38
139
/// SPI device trait
39
140
///
40
141
/// `SpiDevice` represents ownership over a single SPI device on a (possibly shared) bus, selected
@@ -59,6 +160,10 @@ pub unsafe trait SpiDevice: ErrorType {
59
160
60
161
/// Perform a transaction against the device.
61
162
///
163
+ /// **NOTE:**
164
+ /// It is not recommended to use this method directly, because it requires `unsafe` code to dereference the raw pointer.
165
+ /// Instead, the [`transaction!`] macro should be used, which handles this safely inside the macro.
166
+ ///
62
167
/// - Locks the bus
63
168
/// - Asserts the CS (Chip Select) pin.
64
169
/// - Calls `f` with an exclusive reference to the bus, which can then be used to do transfers against the device.
@@ -95,11 +200,7 @@ pub unsafe trait SpiDevice: ErrorType {
95
200
Self :: Bus : SpiBusRead < Word > ,
96
201
Word : Copy + ' static ,
97
202
{
98
- self . transaction ( move |bus| async move {
99
- // safety: `bus` is a valid pointer we're allowed to use for the duration of the closure.
100
- let bus = unsafe { & mut * bus } ;
101
- bus. read ( buf) . await
102
- } )
203
+ transaction ! ( self , move |bus| async move { bus. read( buf) . await } )
103
204
}
104
205
105
206
/// Do a write within a transaction.
@@ -112,11 +213,7 @@ pub unsafe trait SpiDevice: ErrorType {
112
213
Self :: Bus : SpiBusWrite < Word > ,
113
214
Word : Copy + ' static ,
114
215
{
115
- self . transaction ( move |bus| async move {
116
- // safety: `bus` is a valid pointer we're allowed to use for the duration of the closure.
117
- let bus = unsafe { & mut * bus } ;
118
- bus. write ( buf) . await
119
- } )
216
+ transaction ! ( self , move |bus| async move { bus. write( buf) . await } )
120
217
}
121
218
122
219
/// Do a transfer within a transaction.
@@ -133,11 +230,10 @@ pub unsafe trait SpiDevice: ErrorType {
133
230
Self :: Bus : SpiBus < Word > ,
134
231
Word : Copy + ' static ,
135
232
{
136
- self . transaction ( move |bus| async move {
137
- // safety: `bus` is a valid pointer we're allowed to use for the duration of the closure.
138
- let bus = unsafe { & mut * bus } ;
139
- bus. transfer ( read, write) . await
140
- } )
233
+ transaction ! (
234
+ self ,
235
+ move |bus| async move { bus. transfer( read, write) . await }
236
+ )
141
237
}
142
238
143
239
/// Do an in-place transfer within a transaction.
@@ -153,11 +249,10 @@ pub unsafe trait SpiDevice: ErrorType {
153
249
Self :: Bus : SpiBus < Word > ,
154
250
Word : Copy + ' static ,
155
251
{
156
- self . transaction ( move |bus| async move {
157
- // safety: `bus` is a valid pointer we're allowed to use for the duration of the closure.
158
- let bus = unsafe { & mut * bus } ;
159
- bus. transfer_in_place ( buf) . await
160
- } )
252
+ transaction ! (
253
+ self ,
254
+ move |bus| async move { bus. transfer_in_place( buf) . await }
255
+ )
161
256
}
162
257
}
163
258
0 commit comments