• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
68 pub mod spidevioctl;
69 pub use crate::spidevioctl::SpidevTransfer;
70 
71 use bitflags::bitflags;
72 use std::fs::{File, OpenOptions};
73 use std::io;
74 use std::io::prelude::*;
75 use std::os::unix::prelude::*;
76 use std::path::Path;
77 
78 // Constants extracted from linux/spi/spidev.h
79 bitflags! {
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)]
120 pub 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)]
134 pub 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 
141 impl SpidevOptions {
142     /// Create a new, empty set of options
new() -> SpidevOptions143     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.
bits_per_word(&mut self, bits_per_word: u8) -> &mut Self150     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.
max_speed_hz(&mut self, max_speed_hz: u32) -> &mut Self158     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.
lsb_first(&mut self, lsb_first: bool) -> &mut Self169     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).
mode(&mut self, mode: SpiModeFlags) -> &mut Self186     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
build(&self) -> Self192     pub fn build(&self) -> Self {
193         *self
194     }
195 }
196 
197 impl Spidev {
198     /// Wrap an already opened [`File`] for use as an spidev
new(devfile: File) -> Self199     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.
open<P: AsRef<Path>>(path: P) -> io::Result<Spidev>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
inner(&self) -> &File218     pub fn inner(&self) -> &File {
219         &self.devfile
220     }
221 
222     /// Consume the object and get the underlying [`File`] object
into_inner(self) -> File223     pub fn into_inner(self) -> File {
224         self.devfile
225     }
226 
227     /// Write the provided configuration to this device
configure(&mut self, options: &SpidevOptions) -> io::Result<()>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     /// Read the current configuration from this device
query_configuration(&self) -> io::Result<SpidevOptions>249     pub fn query_configuration(&self) -> io::Result<SpidevOptions> {
250         let fd = self.devfile.as_raw_fd();
251 
252         let bpw = spidevioctl::get_bits_per_word(fd)?;
253         let speed = spidevioctl::get_max_speed_hz(fd)?;
254         let lsb_first = (spidevioctl::get_lsb_first(fd)?) != 0;
255 
256         // Try to get the mode as 32-bit (`RD_MODE32`). Older kernels may return
257         // `ENOTTY` indicating 32-bit is not supported. In that case we retry in
258         // 8-bit mode.
259         let mode_bits = spidevioctl::get_mode_u32(fd).or_else(|err| {
260             if err.raw_os_error() == Some(libc::ENOTTY) {
261                 spidevioctl::get_mode(fd).map(|value| value as u32)
262             } else {
263                 Err(err)
264             }
265         })?;
266 
267         let mode = SpiModeFlags::from_bits_retain(mode_bits);
268 
269         let options = SpidevOptions::new()
270             .bits_per_word(bpw)
271             .max_speed_hz(speed)
272             .lsb_first(lsb_first)
273             .mode(mode)
274             .build();
275 
276         Ok(options)
277     }
278 
279     /// Perform a single transfer
transfer(&self, transfer: &mut SpidevTransfer) -> io::Result<()>280     pub fn transfer(&self, transfer: &mut SpidevTransfer) -> io::Result<()> {
281         spidevioctl::transfer(self.devfile.as_raw_fd(), transfer)
282     }
283 
284     /// Perform multiple transfers in a single system call to the kernel
285     ///
286     /// Chaining together multiple requests like this can reduce latency
287     /// and be used for conveniently and efficient implementing some
288     /// protocols without extra round trips back to userspace.
transfer_multiple(&self, transfers: &mut [SpidevTransfer]) -> io::Result<()>289     pub fn transfer_multiple(&self, transfers: &mut [SpidevTransfer]) -> io::Result<()> {
290         spidevioctl::transfer_multiple(self.devfile.as_raw_fd(), transfers)
291     }
292 }
293 
294 impl Read for Spidev {
read(&mut self, buf: &mut [u8]) -> io::Result<usize>295     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
296         self.devfile.read(buf)
297     }
298 }
299 
300 impl Write for Spidev {
write(&mut self, buf: &[u8]) -> io::Result<usize>301     fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
302         self.devfile.write(buf)
303     }
304 
flush(&mut self) -> io::Result<()>305     fn flush(&mut self) -> io::Result<()> {
306         self.devfile.flush()
307     }
308 }
309 
310 impl AsRawFd for Spidev {
as_raw_fd(&self) -> RawFd311     fn as_raw_fd(&self) -> RawFd {
312         self.devfile.as_raw_fd()
313     }
314 }
315 
316 #[cfg(test)]
317 mod test {
318     use super::{SpiModeFlags, SpidevOptions};
319 
320     #[test]
test_spidev_options_all()321     fn test_spidev_options_all() {
322         let options = SpidevOptions::new()
323             .bits_per_word(8)
324             .max_speed_hz(20_000)
325             .lsb_first(false)
326             .mode(SpiModeFlags::SPI_MODE_0)
327             .build();
328         assert_eq!(options.bits_per_word, Some(8));
329         assert_eq!(options.max_speed_hz, Some(20_000));
330         assert_eq!(options.lsb_first, Some(false));
331         assert_eq!(options.spi_mode, Some(SpiModeFlags::SPI_MODE_0));
332     }
333 
334     #[test]
test_spidev_options_some()335     fn test_spidev_options_some() {
336         let mut options = SpidevOptions::new();
337         options.bits_per_word(10);
338         options.lsb_first(true);
339         assert_eq!(options.bits_per_word, Some(10));
340         assert_eq!(options.max_speed_hz, None);
341         assert_eq!(options.lsb_first, Some(true));
342         assert_eq!(options.spi_mode, None);
343     }
344 }
345