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}