@@ -35,6 +35,107 @@ where
3535 Word : Copy + ' static ,
3636= impl Future < Output = Result < ( ) , T :: Error > > ;
3737
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+
38139/// SPI device trait
39140///
40141/// `SpiDevice` represents ownership over a single SPI device on a (possibly shared) bus, selected
@@ -59,6 +160,10 @@ pub unsafe trait SpiDevice: ErrorType {
59160
60161 /// Perform a transaction against the device.
61162 ///
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+ ///
62167 /// - Locks the bus
63168 /// - Asserts the CS (Chip Select) pin.
64169 /// - 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 {
95200 Self :: Bus : SpiBusRead < Word > ,
96201 Word : Copy + ' static ,
97202 {
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 } )
103204 }
104205
105206 /// Do a write within a transaction.
@@ -112,11 +213,7 @@ pub unsafe trait SpiDevice: ErrorType {
112213 Self :: Bus : SpiBusWrite < Word > ,
113214 Word : Copy + ' static ,
114215 {
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 } )
120217 }
121218
122219 /// Do a transfer within a transaction.
@@ -133,11 +230,10 @@ pub unsafe trait SpiDevice: ErrorType {
133230 Self :: Bus : SpiBus < Word > ,
134231 Word : Copy + ' static ,
135232 {
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+ )
141237 }
142238
143239 /// Do an in-place transfer within a transaction.
@@ -153,11 +249,10 @@ pub unsafe trait SpiDevice: ErrorType {
153249 Self :: Bus : SpiBus < Word > ,
154250 Word : Copy + ' static ,
155251 {
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+ )
161256 }
162257}
163258
0 commit comments