• 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 // macros import
10 use super::SpiModeFlags;
11 use nix::{ioctl_read, ioctl_write_buf, ioctl_write_ptr};
12 use std::io;
13 use std::marker::PhantomData;
14 use std::os::unix::prelude::*;
15 
from_nix_result<T>(res: ::nix::Result<T>) -> io::Result<T>16 fn from_nix_result<T>(res: ::nix::Result<T>) -> io::Result<T> {
17     match res {
18         Ok(r) => Ok(r),
19         Err(err) => Err(err.into()),
20     }
21 }
22 
23 /// Structure that is used when performing communication
24 /// with the kernel.
25 ///
26 /// From the kernel documentation:
27 ///
28 /// ```text
29 /// struct spi_ioc_transfer - describes a single SPI transfer
30 /// @tx_buf: Holds pointer to userspace buffer with transmit data, or null.
31 ///   If no data is provided, zeroes are shifted out.
32 /// @rx_buf: Holds pointer to userspace buffer for receive data, or null.
33 /// @len: Length of tx and rx buffers, in bytes.
34 /// @speed_hz: Temporary override of the device's bitrate.
35 /// @bits_per_word: Temporary override of the device's wordsize.
36 /// @delay_usecs: If nonzero, how long to delay after the last bit transfer
37 ///      before optionally deselecting the device before the next transfer.
38 /// @cs_change: True to deselect device before starting the next transfer.
39 ///
40 /// This structure is mapped directly to the kernel spi_transfer structure;
41 /// the fields have the same meanings, except of course that the pointers
42 /// are in a different address space (and may be of different sizes in some
43 /// cases, such as 32-bit i386 userspace over a 64-bit x86_64 kernel).
44 /// Zero-initialize the structure, including currently unused fields, to
45 /// accommodate potential future updates.
46 ///
47 /// SPI_IOC_MESSAGE gives userspace the equivalent of kernel spi_sync().
48 /// Pass it an array of related transfers, they'll execute together.
49 /// Each transfer may be half duplex (either direction) or full duplex.
50 ///
51 ///      struct spi_ioc_transfer mesg[4];
52 ///      ...
53 ///      status = ioctl(fd, SPI_IOC_MESSAGE(4), mesg);
54 ///
55 /// So for example one transfer might send a nine bit command (right aligned
56 /// in a 16-bit word), the next could read a block of 8-bit data before
57 /// terminating that command by temporarily deselecting the chip; the next
58 /// could send a different nine bit command (re-selecting the chip), and the
59 /// last transfer might write some register values.
60 /// ```
61 #[allow(non_camel_case_types)]
62 #[derive(Debug, Default)]
63 #[repr(C)]
64 pub struct spi_ioc_transfer<'a, 'b> {
65     tx_buf: u64,
66     rx_buf: u64,
67     len: u32,
68 
69     // optional overrides
70     pub speed_hz: u32,
71     pub delay_usecs: u16,
72     pub bits_per_word: u8,
73     pub cs_change: u8,
74     pub pad: u32,
75 
76     tx_buf_ref: PhantomData<&'a [u8]>,
77     rx_buf_ref: PhantomData<&'b mut [u8]>,
78 }
79 
80 impl<'a, 'b> spi_ioc_transfer<'a, 'b> {
81     /// Create a read transfer
read(buff: &'b mut [u8]) -> Self82     pub fn read(buff: &'b mut [u8]) -> Self {
83         spi_ioc_transfer {
84             rx_buf: buff.as_ptr() as *const () as usize as u64,
85             len: buff.len() as u32,
86             ..Default::default()
87         }
88     }
89 
90     /// Create a write transfer
write(buff: &'a [u8]) -> Self91     pub fn write(buff: &'a [u8]) -> Self {
92         spi_ioc_transfer {
93             tx_buf: buff.as_ptr() as *const () as usize as u64,
94             len: buff.len() as u32,
95             ..Default::default()
96         }
97     }
98 
99     /// Create a read/write transfer.
100     /// Note that the `tx_buf` and `rx_buf` must be the same length.
read_write(tx_buf: &'a [u8], rx_buf: &'b mut [u8]) -> Self101     pub fn read_write(tx_buf: &'a [u8], rx_buf: &'b mut [u8]) -> Self {
102         assert_eq!(tx_buf.len(), rx_buf.len());
103         spi_ioc_transfer {
104             rx_buf: rx_buf.as_ptr() as *const () as usize as u64,
105             tx_buf: tx_buf.as_ptr() as *const () as usize as u64,
106             len: tx_buf.len() as u32,
107             ..Default::default()
108         }
109     }
110 
111     /// Create a delay transfer of a number of microseconds
delay(microseconds: u16) -> Self112     pub fn delay(microseconds: u16) -> Self {
113         spi_ioc_transfer {
114             delay_usecs: microseconds,
115             len: 0,
116             ..Default::default()
117         }
118     }
119 }
120 
121 mod ioctl {
122     use super::*;
123 
124     const SPI_IOC_MAGIC: u8 = b'k';
125     const SPI_IOC_NR_TRANSFER: u8 = 0;
126     const SPI_IOC_NR_MODE: u8 = 1;
127     const SPI_IOC_NR_LSB_FIRST: u8 = 2;
128     const SPI_IOC_NR_BITS_PER_WORD: u8 = 3;
129     const SPI_IOC_NR_MAX_SPEED_HZ: u8 = 4;
130     const SPI_IOC_NR_MODE32: u8 = 5;
131 
132     ioctl_read!(get_mode_u8, SPI_IOC_MAGIC, SPI_IOC_NR_MODE, u8);
133     ioctl_read!(get_mode_u32, SPI_IOC_MAGIC, SPI_IOC_NR_MODE32, u32);
134     ioctl_write_ptr!(set_mode, SPI_IOC_MAGIC, SPI_IOC_NR_MODE, u8);
135     ioctl_write_ptr!(set_mode32, SPI_IOC_MAGIC, SPI_IOC_NR_MODE32, u32);
136 
137     ioctl_read!(get_lsb_first, SPI_IOC_MAGIC, SPI_IOC_NR_LSB_FIRST, u8);
138     ioctl_write_ptr!(set_lsb_first, SPI_IOC_MAGIC, SPI_IOC_NR_LSB_FIRST, u8);
139 
140     ioctl_read!(
141         get_bits_per_word,
142         SPI_IOC_MAGIC,
143         SPI_IOC_NR_BITS_PER_WORD,
144         u8
145     );
146     ioctl_write_ptr!(
147         set_bits_per_word,
148         SPI_IOC_MAGIC,
149         SPI_IOC_NR_BITS_PER_WORD,
150         u8
151     );
152 
153     ioctl_read!(
154         get_max_speed_hz,
155         SPI_IOC_MAGIC,
156         SPI_IOC_NR_MAX_SPEED_HZ,
157         u32
158     );
159     ioctl_write_ptr!(
160         set_max_speed_hz,
161         SPI_IOC_MAGIC,
162         SPI_IOC_NR_MAX_SPEED_HZ,
163         u32
164     );
165 
166     // NOTE: this macro works for single transfers but cannot properly
167     // calculate size for multi transfer whose length we will not know
168     // until runtime.  We fallback to using the underlying ioctl for that
169     // use case.
170     ioctl_write_ptr!(
171         spidev_transfer,
172         SPI_IOC_MAGIC,
173         SPI_IOC_NR_TRANSFER,
174         spi_ioc_transfer
175     );
176     ioctl_write_buf!(
177         spidev_transfer_buf,
178         SPI_IOC_MAGIC,
179         SPI_IOC_NR_TRANSFER,
180         spi_ioc_transfer
181     );
182 }
183 
184 /// Representation of a spidev transfer that is shared
185 /// with external users
186 pub type SpidevTransfer<'a, 'b> = spi_ioc_transfer<'a, 'b>;
187 
get_mode(fd: RawFd) -> io::Result<u8>188 pub fn get_mode(fd: RawFd) -> io::Result<u8> {
189     let mut mode: u8 = 0;
190     from_nix_result(unsafe { ioctl::get_mode_u8(fd, &mut mode) })?;
191     Ok(mode)
192 }
193 
get_mode_u32(fd: RawFd) -> io::Result<u32>194 pub fn get_mode_u32(fd: RawFd) -> io::Result<u32> {
195     let mut mode: u32 = 0;
196     from_nix_result(unsafe { ioctl::get_mode_u32(fd, &mut mode) })?;
197     Ok(mode)
198 }
199 
set_mode(fd: RawFd, mode: SpiModeFlags) -> io::Result<()>200 pub fn set_mode(fd: RawFd, mode: SpiModeFlags) -> io::Result<()> {
201     // we will always use the 8-bit mode write unless bits not in
202     // the 8-bit mask are used.  This is because WR_MODE32 was not
203     // added until later kernels.  This provides a reasonable story
204     // for forwards and backwards compatibility
205     if (mode.bits() & 0xFFFFFF00) != 0 {
206         from_nix_result(unsafe { ioctl::set_mode32(fd, &mode.bits()) })?;
207     } else {
208         let bits: u8 = mode.bits() as u8;
209         from_nix_result(unsafe { ioctl::set_mode(fd, &bits) })?;
210     }
211     Ok(())
212 }
213 
get_lsb_first(fd: RawFd) -> io::Result<u8>214 pub fn get_lsb_first(fd: RawFd) -> io::Result<u8> {
215     let mut lsb_first: u8 = 0;
216     from_nix_result(unsafe { ioctl::get_lsb_first(fd, &mut lsb_first) })?;
217     Ok(lsb_first)
218 }
219 
set_lsb_first(fd: RawFd, lsb_first: bool) -> io::Result<()>220 pub fn set_lsb_first(fd: RawFd, lsb_first: bool) -> io::Result<()> {
221     let lsb_first_value: u8 = if lsb_first { 1 } else { 0 };
222     from_nix_result(unsafe { ioctl::set_lsb_first(fd, &lsb_first_value) })?;
223     Ok(())
224 }
225 
get_bits_per_word(fd: RawFd) -> io::Result<u8>226 pub fn get_bits_per_word(fd: RawFd) -> io::Result<u8> {
227     let mut bits_per_word: u8 = 0;
228     from_nix_result(unsafe { ioctl::get_bits_per_word(fd, &mut bits_per_word) })?;
229     Ok(bits_per_word)
230 }
231 
set_bits_per_word(fd: RawFd, bits_per_word: u8) -> io::Result<()>232 pub fn set_bits_per_word(fd: RawFd, bits_per_word: u8) -> io::Result<()> {
233     from_nix_result(unsafe { ioctl::set_bits_per_word(fd, &bits_per_word) })?;
234     Ok(())
235 }
236 
get_max_speed_hz(fd: RawFd) -> io::Result<u32>237 pub fn get_max_speed_hz(fd: RawFd) -> io::Result<u32> {
238     let mut max_speed_hz: u32 = 0;
239     from_nix_result(unsafe { ioctl::get_max_speed_hz(fd, &mut max_speed_hz) })?;
240     Ok(max_speed_hz)
241 }
242 
set_max_speed_hz(fd: RawFd, max_speed_hz: u32) -> io::Result<()>243 pub fn set_max_speed_hz(fd: RawFd, max_speed_hz: u32) -> io::Result<()> {
244     from_nix_result(unsafe { ioctl::set_max_speed_hz(fd, &max_speed_hz) })?;
245     Ok(())
246 }
247 
transfer(fd: RawFd, transfer: &mut SpidevTransfer) -> io::Result<()>248 pub fn transfer(fd: RawFd, transfer: &mut SpidevTransfer) -> io::Result<()> {
249     // The kernel will directly modify the rx_buf of the SpidevTransfer
250     // rx_buf if present, so there is no need to do any additional work
251     from_nix_result(unsafe { ioctl::spidev_transfer(fd, transfer) })?;
252     Ok(())
253 }
254 
transfer_multiple(fd: RawFd, transfers: &mut [SpidevTransfer]) -> io::Result<()>255 pub fn transfer_multiple(fd: RawFd, transfers: &mut [SpidevTransfer]) -> io::Result<()> {
256     from_nix_result(unsafe { ioctl::spidev_transfer_buf(fd, transfers) })?;
257     Ok(())
258 }
259