linux_embedded_hal/spi.rs
1//! Implementation of [`embedded-hal`] SPI traits
2//!
3//! [`embedded-hal`]: https://docs.rs/embedded-hal
4//!
5
6use std::cmp::Ordering;
7use std::fmt;
8use std::io;
9use std::ops;
10use std::path::Path;
11
12/// Spidev wrapper providing the embedded-hal [`SpiDevice`] trait.
13///
14/// Use this struct when you want a single spidev device, using a Linux-managed CS (chip-select) pin,
15/// which is already defined in your device tree. Linux will handle sharing the bus
16/// between different SPI devices, even between different processes.
17///
18/// You get an object that implements [`SpiDevice`], which is what most drivers require,
19/// but does not implement [`SpiBus`]. In some rare cases, you may require [`SpiBus`]
20/// instead; for that refer to [`SpidevBus`] below. You may also want to use [`SpiBus`]
21/// if you want to handle all the CS pins yourself using GPIO.
22///
23/// This struct wraps a [`spidev::Spidev`] struct, so it can be constructed directly
24/// and the inner struct accessed if needed, for example to (re)configure the SPI settings.
25///
26/// Note that [delay operations] on this device are capped to 65535 microseconds.
27///
28/// [`SpiDevice`]: embedded_hal::spi::SpiDevice
29/// [`SpiBus`]: embedded_hal::spi::SpiBus
30/// [`spidev::Spidev`]: spidev::Spidev
31/// [delay operations]: embedded_hal::spi::Operation::DelayUs
32pub struct SpidevDevice(pub spidev::Spidev);
33
34/// Spidev wrapper providing the embedded-hal [`SpiBus`] trait.
35///
36/// Use this struct when you require direct access to the underlying SPI bus, for
37/// example when you want to use GPIOs as software-controlled CS (chip-select) pins to share the
38/// bus with multiple devices, or because a driver requires the entire bus (for
39/// example to drive smart LEDs).
40///
41/// Do not use this struct if you're accessing SPI devices that already appear in your
42/// device tree; you will not be able to drive CS pins that are already used by `spidev`
43/// as GPIOs. Instead use [`SpidevDevice`].
44///
45/// This struct must still be created from a [`spidev::Spidev`] device, but there are two
46/// important notes:
47///
48/// 1. The CS pin associated with this `spidev` device will be driven whenever any device accesses
49/// this bus, so it should be an unconnected or unused pin.
50/// 2. No other `spidev` device on the same bus may be used as long as this `SpidevBus` exists,
51/// as Linux will _not_ do anything to ensure this bus has exclusive access.
52///
53/// It is recommended to use a dummy `spidev` device associated with an unused CS pin, and then use
54/// regular GPIOs as CS pins if required. If you are planning to share this bus using GPIOs, the
55/// [`embedded-hal-bus`] crate may be of interest.
56///
57/// If necessary, you can [configure] the underlying [`spidev::Spidev`] instance with the
58/// [`SPI_NO_CS`] flag set to prevent any CS pin activity.
59///
60/// [`SpiDevice`]: embedded_hal::spi::SpiDevice
61/// [`SpiBus`]: embedded_hal::spi::SpiBus
62/// [`embedded-hal-bus`]: https://docs.rs/embedded-hal-bus/
63/// [`spidev::Spidev`]: spidev::Spidev
64/// [delay operations]: embedded_hal::spi::Operation::DelayUs
65/// [configure]: spidev::Spidev::configure
66/// [`SPI_NO_CS`]: spidev::SpiModeFlags::SPI_NO_CS
67pub struct SpidevBus(pub spidev::Spidev);
68
69impl SpidevDevice {
70 /// See [`spidev::Spidev::open`] for details.
71 ///
72 /// The provided `path` is for the specific device you wish to access.
73 /// Access to the bus is shared with other devices via the Linux kernel.
74 pub fn open<P>(path: P) -> Result<Self, SPIError>
75 where
76 P: AsRef<Path>,
77 {
78 spidev::Spidev::open(path)
79 .map(SpidevDevice)
80 .map_err(|e| e.into())
81 }
82}
83
84impl SpidevBus {
85 /// See [`spidev::Spidev::open`] for details.
86 ///
87 /// The provided `path` must be the _only_ device in use on its bus,
88 /// and note its own CS pin will be asserted for all device access,
89 /// so the path should be to a dummy device used only to access
90 /// the underlying bus.
91 pub fn open<P>(path: P) -> Result<Self, SPIError>
92 where
93 P: AsRef<Path>,
94 {
95 spidev::Spidev::open(path)
96 .map(SpidevBus)
97 .map_err(|e| e.into())
98 }
99}
100
101impl ops::Deref for SpidevDevice {
102 type Target = spidev::Spidev;
103
104 fn deref(&self) -> &Self::Target {
105 &self.0
106 }
107}
108
109impl ops::DerefMut for SpidevDevice {
110 fn deref_mut(&mut self) -> &mut Self::Target {
111 &mut self.0
112 }
113}
114
115impl ops::Deref for SpidevBus {
116 type Target = spidev::Spidev;
117
118 fn deref(&self) -> &Self::Target {
119 &self.0
120 }
121}
122
123impl ops::DerefMut for SpidevBus {
124 fn deref_mut(&mut self) -> &mut Self::Target {
125 &mut self.0
126 }
127}
128
129mod embedded_hal_impl {
130 use super::*;
131 use embedded_hal::spi::ErrorType;
132 use embedded_hal::spi::{Operation as SpiOperation, SpiBus, SpiDevice};
133 use spidev::SpidevTransfer;
134 use std::convert::TryInto;
135 use std::io::{Read, Write};
136
137 impl ErrorType for SpidevDevice {
138 type Error = SPIError;
139 }
140
141 impl ErrorType for SpidevBus {
142 type Error = SPIError;
143 }
144
145 impl SpiBus<u8> for SpidevBus {
146 fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
147 self.0.read_exact(words).map_err(|err| SPIError { err })
148 }
149
150 fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
151 self.0.write_all(words).map_err(|err| SPIError { err })
152 }
153
154 fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {
155 let read_len = read.len();
156 match read_len.cmp(&write.len()) {
157 Ordering::Less => self.0.transfer_multiple(&mut [
158 SpidevTransfer::read_write(&write[..read_len], read),
159 SpidevTransfer::write(&write[read_len..]),
160 ]),
161 Ordering::Equal => self
162 .0
163 .transfer(&mut SpidevTransfer::read_write(write, read)),
164 Ordering::Greater => {
165 let (read1, read2) = read.split_at_mut(write.len());
166 self.0.transfer_multiple(&mut [
167 SpidevTransfer::read_write(write, read1),
168 SpidevTransfer::read(read2),
169 ])
170 }
171 }
172 .map_err(|err| SPIError { err })
173 }
174
175 fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
176 let tx = words.to_owned();
177 self.0
178 .transfer(&mut SpidevTransfer::read_write(&tx, words))
179 .map_err(|err| SPIError { err })
180 }
181
182 fn flush(&mut self) -> Result<(), Self::Error> {
183 self.0.flush().map_err(|err| SPIError { err })
184 }
185 }
186
187 impl SpiDevice for SpidevDevice {
188 /// Perform a transaction against the device. [Read more][transaction]
189 ///
190 /// [Delay operations][delay] are capped to 65535 microseconds.
191 ///
192 /// [transaction]: SpiDevice::transaction
193 /// [delay]: SpiOperation::DelayUs
194 fn transaction(
195 &mut self,
196 operations: &mut [SpiOperation<'_, u8>],
197 ) -> Result<(), Self::Error> {
198 let mut transfers = Vec::with_capacity(operations.len());
199 for op in operations {
200 match op {
201 SpiOperation::Read(buf) => transfers.push(SpidevTransfer::read(buf)),
202 SpiOperation::Write(buf) => transfers.push(SpidevTransfer::write(buf)),
203 SpiOperation::Transfer(read, write) => match read.len().cmp(&write.len()) {
204 Ordering::Less => {
205 let n = read.len();
206 transfers.push(SpidevTransfer::read_write(&write[..n], read));
207 transfers.push(SpidevTransfer::write(&write[n..]));
208 }
209 Ordering::Equal => transfers.push(SpidevTransfer::read_write(write, read)),
210 Ordering::Greater => {
211 let (read1, read2) = read.split_at_mut(write.len());
212 transfers.push(SpidevTransfer::read_write(write, read1));
213 transfers.push(SpidevTransfer::read(read2));
214 }
215 },
216 SpiOperation::TransferInPlace(buf) => {
217 let tx = unsafe {
218 let p = buf.as_ptr();
219 std::slice::from_raw_parts(p, buf.len())
220 };
221 transfers.push(SpidevTransfer::read_write(tx, buf));
222 }
223 SpiOperation::DelayNs(ns) => {
224 let us = {
225 if *ns == 0 {
226 0
227 } else {
228 let us = *ns / 1000;
229 if us == 0 {
230 1
231 } else {
232 (us).try_into().unwrap_or(u16::MAX)
233 }
234 }
235 };
236 transfers.push(SpidevTransfer::delay(us));
237 }
238 }
239 }
240 self.0
241 .transfer_multiple(&mut transfers)
242 .map_err(|err| SPIError { err })?;
243 self.flush()?;
244 Ok(())
245 }
246 }
247}
248
249/// Error type wrapping [io::Error](io::Error) to implement [embedded_hal::spi::ErrorKind]
250#[derive(Debug)]
251pub struct SPIError {
252 err: io::Error,
253}
254
255impl SPIError {
256 /// Fetch inner (concrete) [`LinuxI2CError`]
257 pub fn inner(&self) -> &io::Error {
258 &self.err
259 }
260}
261
262impl From<io::Error> for SPIError {
263 fn from(err: io::Error) -> Self {
264 Self { err }
265 }
266}
267
268impl embedded_hal::spi::Error for SPIError {
269 #[allow(clippy::match_single_binding)]
270 fn kind(&self) -> embedded_hal::spi::ErrorKind {
271 use embedded_hal::spi::ErrorKind;
272 // TODO: match any errors here if we can find any that are relevant
273 ErrorKind::Other
274 }
275}
276
277impl fmt::Display for SPIError {
278 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
279 write!(f, "{}", self.err)
280 }
281}
282
283impl std::error::Error for SPIError {
284 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
285 Some(&self.err)
286 }
287}