spidev/
lib.rs

1// Copyright 2015, Paul Osborne <osbpau@gmail.com>
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/license/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option.  This file may not be copied, modified, or distributed
7// except according to those terms.
8
9//! # Spidev
10//!
11//! The `spidev` crate provides access to Linux spidev devices
12//! from rust.  The wrapping of the interface is pretty direct
13//! and shouldn't cause any surprises.
14//!
15//! Additional information on the interface may be found in
16//! [the kernel documentation
17//! for spidev](https://www.kernel.org/doc/Documentation/spi/spidev).
18//!
19//! # Examples
20//!
21//! ```no_run
22//! extern crate spidev;
23//! use std::io;
24//! use std::io::prelude::*;
25//! use spidev::{Spidev, SpidevOptions, SpidevTransfer, SpiModeFlags};
26//!
27//! fn create_spi() -> io::Result<Spidev> {
28//!     let mut spi = Spidev::open("/dev/spidev0.0")?;
29//!     let options = SpidevOptions::new()
30//!          .bits_per_word(8)
31//!          .max_speed_hz(20_000)
32//!          .mode(SpiModeFlags::SPI_MODE_0)
33//!          .build();
34//!     spi.configure(&options)?;
35//!     Ok(spi)
36//! }
37//!
38//! /// perform half duplex operations using Read and Write traits
39//! fn half_duplex(spi: &mut Spidev) -> io::Result<()> {
40//!     let mut rx_buf = [0_u8; 10];
41//!     spi.write(&[0x01, 0x02, 0x03])?;
42//!     spi.read(&mut rx_buf)?;
43//!     println!("{:?}", rx_buf);
44//!     Ok(())
45//! }
46//!
47//! /// Perform full duplex operations using Ioctl
48//! fn full_duplex(spi: &mut Spidev) -> io::Result<()> {
49//!     // "write" transfers are also reads at the same time with
50//!     // the read having the same length as the write
51//!     let tx_buf = [0x01, 0x02, 0x03];
52//!     let mut rx_buf = [0; 3];
53//!     {
54//!         let mut transfer = SpidevTransfer::read_write(&tx_buf, &mut rx_buf);
55//!         spi.transfer(&mut transfer)?;
56//!     }
57//!     println!("{:?}", rx_buf);
58//!     Ok(())
59//! }
60//!
61//! fn main() {
62//!     let mut spi = create_spi().unwrap();
63//!     println!("{:?}", half_duplex(&mut spi).unwrap());
64//!     println!("{:?}", full_duplex(&mut spi).unwrap());
65//! }
66//! ```
67
68pub mod spidevioctl;
69pub use crate::spidevioctl::SpidevTransfer;
70
71use bitflags::bitflags;
72use std::fs::{File, OpenOptions};
73use std::io;
74use std::io::prelude::*;
75use std::os::unix::prelude::*;
76use std::path::Path;
77
78// Constants extracted from linux/spi/spidev.h
79bitflags! {
80    #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
81    pub struct SpiModeFlags: u32 {
82        /// Clock Phase
83        const SPI_CPHA = 0x01;
84        /// Clock Polarity
85        const SPI_CPOL = 0x02;
86        /// Chipselect Active High?
87        const SPI_CS_HIGH = 0x04;
88        /// Per-word Bits On Wire
89        const SPI_LSB_FIRST = 0x08;
90        /// SI/SO Signals Shared
91        const SPI_3WIRE = 0x10;
92        /// Loopback Mode
93        const SPI_LOOP = 0x20;
94        /// 1 dev/bus; no chipselect
95        const SPI_NO_CS = 0x40;
96        /// Slave pulls low to pause
97        const SPI_READY = 0x80;
98
99        // Common Configurations
100        const SPI_MODE_0 = 0x00;
101        const SPI_MODE_1 = Self::SPI_CPHA.bits();
102        const SPI_MODE_2 = Self::SPI_CPOL.bits();
103        const SPI_MODE_3 = (Self::SPI_CPOL.bits() | Self::SPI_CPHA.bits());
104
105        // == Only Supported with 32-bits ==
106
107        /// Transmit with 2 wires
108        const SPI_TX_DUAL = 0x100;
109        /// Transmit with 4 wires
110        const SPI_TX_QUAD = 0x200;
111        /// Receive with 2 wires
112        const SPI_RX_DUAL = 0x400;
113        /// Receive with 4 wires
114        const SPI_RX_QUAD = 0x800;
115    }
116}
117
118/// Provide high-level access to Linux Spidev Driver
119#[derive(Debug)]
120pub struct Spidev {
121    devfile: File,
122}
123
124/// Options that control defaults for communication on a device
125///
126/// Individual settings may be overridden via parameters that
127/// are specified as part of any individual SpiTransfer when
128/// using `transfer` or `transfer_multiple`.
129///
130/// Options that are not configured with one of the builder
131/// functions will not be modified in the kernel when
132/// `configure` is called.
133#[derive(Debug, Default, Clone, Copy, PartialEq)]
134pub struct SpidevOptions {
135    pub bits_per_word: Option<u8>,
136    pub max_speed_hz: Option<u32>,
137    pub lsb_first: Option<bool>,
138    pub spi_mode: Option<SpiModeFlags>,
139}
140
141impl SpidevOptions {
142    /// Create a new, empty set of options
143    pub fn new() -> SpidevOptions {
144        SpidevOptions::default()
145    }
146
147    /// The number of bits in each SPI transfer word
148    ///
149    /// The value zero signifies eight bits.
150    pub fn bits_per_word(&mut self, bits_per_word: u8) -> &mut Self {
151        self.bits_per_word = Some(bits_per_word);
152        self
153    }
154
155    /// The maximum SPI transfer speed, in Hz
156    ///
157    /// The controller can't necessarily assign that specific clock speed.
158    pub fn max_speed_hz(&mut self, max_speed_hz: u32) -> &mut Self {
159        self.max_speed_hz = Some(max_speed_hz);
160        self
161    }
162
163    /// The bit justification used to transfer SPI words
164    ///
165    /// Zero indicates MSB-first; other values indicate the less common
166    /// LSB-first encoding.  In both cases the specified value is
167    /// right-justified in each word, so that unused (TX) or undefined (RX)
168    /// bits are in the MSBs.
169    pub fn lsb_first(&mut self, lsb_first: bool) -> &mut Self {
170        self.lsb_first = Some(lsb_first);
171        self
172    }
173
174    /// Set the SPI Transfer Mode
175    ///
176    /// Use the constants SPI_MODE_0..SPI_MODE_3; or if you prefer
177    /// you can combine SPI_CPOL (clock polarity, idle high iff this
178    /// is set) or SPI_CPHA (clock phase, sample on trailing edge
179    /// iff this is set) flags.
180    ///
181    /// Note that this API will always prefer to use SPI_IOC_WR_MODE
182    /// rathern than the 32-bit one to target the greatest number of
183    /// kernels.  SPI_IOC_WR_MODE32 is only present in 3.15+ kernels.
184    /// SPI_IOC_WR_MODE32 will be used iff bits higher than those in
185    /// 8bits are provided (e.g. Dual/Quad Tx/Rx).
186    pub fn mode(&mut self, mode: SpiModeFlags) -> &mut Self {
187        self.spi_mode = Some(mode);
188        self
189    }
190
191    /// Finalize and build the SpidevOptions
192    pub fn build(&self) -> Self {
193        *self
194    }
195}
196
197impl Spidev {
198    /// Wrap an already opened [`File`] for use as an spidev
199    pub fn new(devfile: File) -> Self {
200        Self { devfile }
201    }
202
203    /// Open the spidev device with the provided path
204    ///
205    /// Typically, the path will be something like `"/dev/spidev0.0"`
206    /// where the first number if the bus and the second number
207    /// is the chip select on that bus for the device being targeted.
208    pub fn open<P: AsRef<Path>>(path: P) -> io::Result<Spidev> {
209        let devfile = OpenOptions::new()
210            .read(true)
211            .write(true)
212            .create(false)
213            .open(path)?;
214        Ok(Self::new(devfile))
215    }
216
217    /// Get a reference to the underlying [`File`] object
218    pub fn inner(&self) -> &File {
219        &self.devfile
220    }
221
222    /// Consume the object and get the underlying [`File`] object
223    pub fn into_inner(self) -> File {
224        self.devfile
225    }
226
227    /// Write the provided configuration to this device
228    pub fn configure(&mut self, options: &SpidevOptions) -> io::Result<()> {
229        // write out each present option to the device.  Options
230        // that are None are left as-is, in order to reduce
231        // overhead
232        let fd = self.devfile.as_raw_fd();
233        if let Some(bpw) = options.bits_per_word {
234            spidevioctl::set_bits_per_word(fd, bpw)?;
235        }
236        if let Some(speed) = options.max_speed_hz {
237            spidevioctl::set_max_speed_hz(fd, speed)?;
238        }
239        if let Some(lsb_first) = options.lsb_first {
240            spidevioctl::set_lsb_first(fd, lsb_first)?;
241        }
242        if let Some(spi_mode_flags) = options.spi_mode {
243            spidevioctl::set_mode(fd, spi_mode_flags)?;
244        }
245        Ok(())
246    }
247
248    /// Perform a single transfer
249    pub fn transfer(&self, transfer: &mut SpidevTransfer) -> io::Result<()> {
250        spidevioctl::transfer(self.devfile.as_raw_fd(), transfer)
251    }
252
253    /// Perform multiple transfers in a single system call to the kernel
254    ///
255    /// Chaining together multiple requests like this can reduce latency
256    /// and be used for conveniently and efficient implementing some
257    /// protocols without extra round trips back to userspace.
258    pub fn transfer_multiple(&self, transfers: &mut [SpidevTransfer]) -> io::Result<()> {
259        spidevioctl::transfer_multiple(self.devfile.as_raw_fd(), transfers)
260    }
261}
262
263impl Read for Spidev {
264    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
265        self.devfile.read(buf)
266    }
267}
268
269impl Write for Spidev {
270    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
271        self.devfile.write(buf)
272    }
273
274    fn flush(&mut self) -> io::Result<()> {
275        self.devfile.flush()
276    }
277}
278
279impl AsRawFd for Spidev {
280    fn as_raw_fd(&self) -> RawFd {
281        self.devfile.as_raw_fd()
282    }
283}
284
285#[cfg(test)]
286mod test {
287    use super::{SpiModeFlags, SpidevOptions};
288
289    #[test]
290    fn test_spidev_options_all() {
291        let options = SpidevOptions::new()
292            .bits_per_word(8)
293            .max_speed_hz(20_000)
294            .lsb_first(false)
295            .mode(SpiModeFlags::SPI_MODE_0)
296            .build();
297        assert_eq!(options.bits_per_word, Some(8));
298        assert_eq!(options.max_speed_hz, Some(20_000));
299        assert_eq!(options.lsb_first, Some(false));
300        assert_eq!(options.spi_mode, Some(SpiModeFlags::SPI_MODE_0));
301    }
302
303    #[test]
304    fn test_spidev_options_some() {
305        let mut options = SpidevOptions::new();
306        options.bits_per_word(10);
307        options.lsb_first(true);
308        assert_eq!(options.bits_per_word, Some(10));
309        assert_eq!(options.max_speed_hz, None);
310        assert_eq!(options.lsb_first, Some(true));
311        assert_eq!(options.spi_mode, None);
312    }
313}