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