3535 Word : Copy + ' static ,
3636= impl Future < Output = Result < ( ) , T :: Error > > ;
3737
38+ #[ macro_export]
39+ /// This macro is a workaround for the workaround in [`SpiDevice::transaction`]
40+ ///
41+ /// # Examples
42+ ///
43+ /// ```
44+ /// use embedded_hal_async::spi::{transaction_helper, SpiBus, SpiBusRead, SpiBusWrite, SpiDevice};
45+ ///
46+ /// pub async fn transaction_example<SPI>(mut device: SPI) -> Result<u32, SPI::Error>
47+ /// where
48+ /// SPI: SpiDevice,
49+ /// SPI::Bus: SpiBus,
50+ /// {
51+ /// transaction_helper!(&mut device, move |bus| async move {
52+ /// // Unlike `SpiDevice::transaction`, we don't need to
53+ /// // manually dereference a pointer in order to use the bus.
54+ /// bus.write(&[42]).await?;
55+ /// let mut data = [0; 4];
56+ /// bus.read(&mut data).await?;
57+ /// Ok(u32::from_be_bytes(data))
58+ /// })
59+ /// .await
60+ /// }
61+ /// ```
62+ macro_rules! spi_transaction_helper {
63+ ( $device: expr, move |$bus: ident| async move $closure_body: expr) => {
64+ $crate:: spi:: SpiDevice :: transaction( $device, move |$bus| async move {
65+ // Safety: Implementers of the `SpiDevice` trait guarantee that the pointer is
66+ // valid and dereferencable for the entire duration of the closure.
67+ let $bus = unsafe { & mut * $bus } ;
68+ let result = $closure_body;
69+ :: core:: mem:: drop( $bus) ; // Ensure that the bus reference was not moved out of the closure
70+ result
71+ } )
72+ } ;
73+ }
74+
75+ #[ doc( inline) ]
76+ pub use spi_transaction_helper as transaction_helper;
77+
3878/// SPI device trait
3979///
4080/// `SpiDevice` represents ownership over a single SPI device on a (possibly shared) bus, selected
@@ -95,11 +135,7 @@ pub unsafe trait SpiDevice: ErrorType {
95135 Self :: Bus : SpiBusRead < Word > ,
96136 Word : Copy + ' static ,
97137 {
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- } )
138+ transaction_helper ! ( self , move |bus| async move { bus. read( buf) . await } )
103139 }
104140
105141 /// Do a write within a transaction.
@@ -112,11 +148,7 @@ pub unsafe trait SpiDevice: ErrorType {
112148 Self :: Bus : SpiBusWrite < Word > ,
113149 Word : Copy + ' static ,
114150 {
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- } )
151+ transaction_helper ! ( self , move |bus| async move { bus. write( buf) . await } )
120152 }
121153
122154 /// Do a transfer within a transaction.
@@ -133,11 +165,10 @@ pub unsafe trait SpiDevice: ErrorType {
133165 Self :: Bus : SpiBus < Word > ,
134166 Word : Copy + ' static ,
135167 {
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- } )
168+ transaction_helper ! (
169+ self ,
170+ move |bus| async move { bus. transfer( read, write) . await }
171+ )
141172 }
142173
143174 /// Do an in-place transfer within a transaction.
@@ -153,11 +184,10 @@ pub unsafe trait SpiDevice: ErrorType {
153184 Self :: Bus : SpiBus < Word > ,
154185 Word : Copy + ' static ,
155186 {
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- } )
187+ transaction_helper ! (
188+ self ,
189+ move |bus| async move { bus. transfer_in_place( buf) . await }
190+ )
161191 }
162192}
163193
0 commit comments