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            self.0
177                .transfer(&mut SpidevTransfer::read_write_in_place(words))
178                .map_err(|err| SPIError { err })
179        }
180
181        fn flush(&mut self) -> Result<(), Self::Error> {
182            self.0.flush().map_err(|err| SPIError { err })
183        }
184    }
185
186    impl SpiDevice for SpidevDevice {
187        /// Perform a transaction against the device. [Read more][transaction]
188        ///
189        /// [Delay operations][delay] are capped to 65535 microseconds.
190        ///
191        /// [transaction]: SpiDevice::transaction
192        /// [delay]: SpiOperation::DelayUs
193        fn transaction(
194            &mut self,
195            operations: &mut [SpiOperation<'_, u8>],
196        ) -> Result<(), Self::Error> {
197            let mut transfers = Vec::with_capacity(operations.len());
198            for op in operations {
199                match op {
200                    SpiOperation::Read(buf) => transfers.push(SpidevTransfer::read(buf)),
201                    SpiOperation::Write(buf) => transfers.push(SpidevTransfer::write(buf)),
202                    SpiOperation::Transfer(read, write) => match read.len().cmp(&write.len()) {
203                        Ordering::Less => {
204                            let n = read.len();
205                            transfers.push(SpidevTransfer::read_write(&write[..n], read));
206                            transfers.push(SpidevTransfer::write(&write[n..]));
207                        }
208                        Ordering::Equal => transfers.push(SpidevTransfer::read_write(write, read)),
209                        Ordering::Greater => {
210                            let (read1, read2) = read.split_at_mut(write.len());
211                            transfers.push(SpidevTransfer::read_write(write, read1));
212                            transfers.push(SpidevTransfer::read(read2));
213                        }
214                    },
215                    SpiOperation::TransferInPlace(buf) => {
216                        transfers.push(SpidevTransfer::read_write_in_place(buf));
217                    }
218                    SpiOperation::DelayNs(ns) => {
219                        let us = {
220                            if *ns == 0 {
221                                0
222                            } else {
223                                let us = *ns / 1000;
224                                if us == 0 {
225                                    1
226                                } else {
227                                    (us).try_into().unwrap_or(u16::MAX)
228                                }
229                            }
230                        };
231                        transfers.push(SpidevTransfer::delay(us));
232                    }
233                }
234            }
235            self.0
236                .transfer_multiple(&mut transfers)
237                .map_err(|err| SPIError { err })?;
238            self.flush()?;
239            Ok(())
240        }
241    }
242}
243
244/// Error type wrapping [io::Error](io::Error) to implement [embedded_hal::spi::ErrorKind]
245#[derive(Debug)]
246pub struct SPIError {
247    err: io::Error,
248}
249
250impl SPIError {
251    /// Fetch inner (concrete) [`LinuxI2CError`]
252    pub fn inner(&self) -> &io::Error {
253        &self.err
254    }
255}
256
257impl From<io::Error> for SPIError {
258    fn from(err: io::Error) -> Self {
259        Self { err }
260    }
261}
262
263impl embedded_hal::spi::Error for SPIError {
264    #[allow(clippy::match_single_binding)]
265    fn kind(&self) -> embedded_hal::spi::ErrorKind {
266        use embedded_hal::spi::ErrorKind;
267        // TODO: match any errors here if we can find any that are relevant
268        ErrorKind::Other
269    }
270}
271
272impl fmt::Display for SPIError {
273    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
274        write!(f, "{}", self.err)
275    }
276}
277
278impl std::error::Error for SPIError {
279    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
280        Some(&self.err)
281    }
282}