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