periphery/sys/
spi.rs

1#![allow(dead_code)]
2#![allow(unused_imports)]
3#![allow(unused_macros)]
4
5use std::io::{self, Read, Write};
6use std::fs::{File, OpenOptions};
7use std::marker::PhantomData;
8use std::fmt;
9use std::os::unix::io::{AsRawFd, RawFd};
10
11// 125.0 MHz   125000000
12// 62.5 MHz    62500000
13// 31.2 MHz    31200000
14// 15.6 MHz    15600000
15// 7.8 MHz     7800000
16// 3.9 MHz     3900000
17// 1953 kHz    1953000
18// 976 kHz     976000
19// 488 kHz     488000
20// 244 kHz     244000
21// 122 kHz     122000
22// 61 kHz  61000
23// 30.5 kHz    30500
24// 15.2 kHz    15200
25// 7629 Hz     7629
26
27pub struct SPI {
28    file: File,
29    _not_sync: PhantomData<*const ()>
30}
31
32#[derive(Debug, PartialEq, Eq, Copy, Clone)]
33#[repr(u8)]
34pub enum Mode {
35    Mode0 = 0,
36    Mode1 = 1,
37    Mode2 = 2,
38    Mode3 = 3
39}
40
41#[derive(Debug, PartialEq, Eq, Copy, Clone)]
42pub enum BitOrder {
43    MsbFirst = 0,
44    LsbFirst = 1
45}
46
47/// Slave Select polarities.
48#[derive(Debug, PartialEq, Eq, Copy, Clone)]
49pub enum Polarity {
50    ActiveLow = 0,
51    ActiveHigh = 1,
52}
53
54impl fmt::Display for Polarity {
55    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
56        match *self {
57            Polarity::ActiveLow => write!(f, "ActiveLow"),
58            Polarity::ActiveHigh => write!(f, "ActiveHigh"),
59        }
60    }
61}
62
63impl fmt::Display for BitOrder {
64    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
65        match *self {
66            BitOrder::MsbFirst => write!(f, "MsbFirst"),
67            BitOrder::LsbFirst => write!(f, "LsbFirst")
68        }
69    }
70}
71
72pub type SpidevTransfer<'a, 'b> = private::spi_ioc_transfer<'a, 'b>;
73
74impl SPI {
75    pub fn new(bus: u8, slave: u8, speed_hz: u32, mode: Mode) -> io::Result<SPI> {
76        let file = OpenOptions::new()
77            .read(true)
78            .write(true)
79            .open(format!("/dev/spidev{}.{}", bus, slave))?;
80
81        let spi = SPI {
82            file,
83             _not_sync: PhantomData
84        };
85
86        spi.set_mode(mode)?;
87        spi.set_bits_per_word(8)?;
88        spi.set_speed_hz(speed_hz)?;
89
90        Ok(spi)
91    }
92
93    pub fn mode(&self) -> io::Result<Mode> {
94        let mut mode: u8 = 0;
95
96        private::get_mode_u8(self.file.as_raw_fd(), &mut mode)?;
97
98        Ok(match mode & 0x03 {
99            0x01 => Mode::Mode1,
100            0x02 => Mode::Mode2,
101            0x03 => Mode::Mode3,
102            _ => Mode::Mode0,
103        })
104    }
105
106    pub fn set_mode(&self, mode: Mode) -> io::Result<()> {
107        let old_mode = self.mode()?;
108
109        // Make sure we only replace the CPOL/CPHA bits
110        let new_mode = ((old_mode as u8) & !0x03) | (mode as u8);
111
112        private::set_mode_u8(self.file.as_raw_fd(), &new_mode)?;
113
114        Ok(())
115    }
116
117    pub fn speed_hz(&self) -> io::Result<u32> {
118        let mut speed_hz: u32 = 0;
119
120        private::get_max_speed_hz(self.file.as_raw_fd(), &mut speed_hz)?;
121
122        Ok(speed_hz)
123    }
124
125    pub fn set_speed_hz(&self, speed_hz: u32) -> io::Result<()> {
126        private::set_max_speed_hz(self.file.as_raw_fd(), &speed_hz)?;
127
128        Ok(())
129    }
130
131    pub fn bits_per_word(&self) -> io::Result<u8> {
132        let mut bits_per_word: u8 = 0;
133
134        private::get_bits_per_word(self.file.as_raw_fd(), &mut bits_per_word)?;
135
136        Ok(bits_per_word)
137    }
138
139    pub fn set_bits_per_word(&self, size: u8) -> io::Result<()> {
140        private::set_bits_per_word(self.file.as_raw_fd(), &size)?;
141
142        Ok(())
143    }
144
145    pub fn bit_order(&self) -> io::Result<BitOrder> {
146        let mut bit_order: u8 = 0;
147
148        private::get_lsb_first(self.file.as_raw_fd(), &mut bit_order)?;
149
150        Ok(match bit_order {
151            0 => BitOrder::MsbFirst,
152            _ => BitOrder::LsbFirst,
153        })
154    }
155
156    pub fn set_bit_order(&self, bit_order: BitOrder) -> io::Result<()> {
157        private::set_lsb_first(self.file.as_raw_fd(), &(bit_order as u8))?;
158
159        Ok(())
160    }
161
162    pub fn ss_polarity(&self) -> io::Result<Polarity> {
163        let mut mode: u8 = 0;
164
165        private::get_mode_u8(self.file.as_raw_fd(), &mut mode)?;
166
167        if (mode & private::SPI_CS_HIGH) == 0 {
168            return Ok(Polarity::ActiveLow)
169        }
170
171        Ok(Polarity::ActiveHigh)
172    }
173
174    pub fn set_ss_polarity(&self, polarity: Polarity) -> io::Result<()> {
175        let mut mode: u8 = 0;
176
177        private::get_mode_u8(self.file.as_raw_fd(), &mut mode)?;
178
179        if polarity == Polarity::ActiveHigh {
180            mode |= private::SPI_CS_HIGH;
181        } else {
182            mode &= !private::SPI_CS_HIGH;
183        }
184
185        private::set_mode_u8(self.file.as_raw_fd(), &mode)?;
186
187        Ok(())
188    }
189
190    pub fn read(&mut self, buffer: &mut [u8]) -> io::Result<usize> {
191        Ok(self.file.read(buffer)?)
192    }
193
194    pub fn write(&mut self, buffer: &[u8]) -> io::Result<usize> {
195        Ok(self.file.write(buffer)?)
196    }
197
198    pub fn transfer(&self, transfer: &mut SpidevTransfer) -> io::Result<()> {
199        // The kernel will directly modify the rx_buf of the SpidevTransfer
200        // rx_buf if present, so there is no need to do any additional work
201        private::spidev_transfer(self.file.as_raw_fd(), transfer)?;
202
203        Ok(())
204    }
205
206    pub fn transfer_multiple(&self, transfers: &mut [SpidevTransfer]) -> io::Result<()> {
207        private::spidev_transfer_buf(self.file.as_raw_fd(), transfers)?;
208
209        Ok(())
210    }
211}
212
213impl AsRawFd for SPI {
214    fn as_raw_fd(&self) -> RawFd {
215        self.file.as_raw_fd()
216    }
217}
218
219impl fmt::Debug for SPI {
220    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
221        f.debug_struct("SPI")
222            .field("mode", &self.mode())
223            .field("speed_hz", &self.speed_hz())
224            .field("bits_per_word", &self.bits_per_word())
225            .field("bit_order", &self.bit_order())
226            .field("ss_polarity", &self.ss_polarity())
227            .finish()
228    }
229}
230
231mod private {
232    use std::os::raw::{c_int, c_ulong};
233
234    #[cfg(target_env = "gnu")]
235    type IoctlNumType = c_ulong;
236    #[cfg(target_env = "musl")]
237    type IoctlNumType = c_int;
238
239    /// Clock Phase
240    pub const SPI_CPHA: u8 = 0x01;
241    /// Clock Polarity
242    pub const SPI_CPOL: u8 = 0x02;
243
244    pub const SPI_MODE_0: u8 = 0;
245    pub const SPI_MODE_1: u8 = SPI_CPHA;
246    pub const SPI_MODE_2: u8 = SPI_CPOL;
247    pub const SPI_MODE_3: u8 = SPI_CPOL | SPI_CPHA;
248
249    /// Chipselect Active High?
250    pub const SPI_CS_HIGH: u8 = 0x04;
251    /// Per-word Bits On Wire
252    pub const SPI_LSB_FIRST: u8 = 0x08;
253    /// SI/SO Signals Shared
254    pub const SPI_3WIRE: u8 = 0x10;
255    /// Loopback Mode
256    pub const SPI_LOOP: u8 = 0x20;
257    /// 1 dev/bus; no chipselect
258    pub const SPI_NO_CS: u8 = 0x40;
259    /// Slave pulls low to pause
260    pub const SPI_READY: u8 = 0x80;
261
262    /// Transmit with 2 wires
263    pub const SPI_TX_DUAL: u32 = 0x100;
264    /// Transmit with 4 wires
265    pub const SPI_TX_QUAD: u32 = 0x200;
266    /// Receive with 2 wires
267    pub const SPI_RX_DUAL: u32 = 0x400;
268    /// Receive with 4 wires
269    pub const SPI_RX_QUAD: u32 = 0x800;
270
271    const SPI_IOC_MAGIC: u8 = 'k' as u8;
272    const SPI_IOC_NR_TRANSFER: u8 = 0;
273    const SPI_IOC_NR_MODE: u8 = 1;
274    const SPI_IOC_NR_LSB_FIRST: u8 = 2;
275    const SPI_IOC_NR_BITS_PER_WORD: u8 = 3;
276    const SPI_IOC_NR_MAX_SPEED_HZ: u8 = 4;
277    const SPI_IOC_NR_MODE32: u8 = 5;
278
279    const NONE: u8 = 0;
280    const READ: u8 = 2;
281    const WRITE: u8 = 1;
282    const SIZEBITS: u8 = 14;
283    const DIRBITS: u8 = 2;
284
285    const NRBITS: IoctlNumType = 8;
286    const TYPEBITS: IoctlNumType = 8;
287
288    const NRSHIFT: IoctlNumType = 0;
289    const TYPESHIFT: IoctlNumType = NRSHIFT + NRBITS as IoctlNumType;
290    const SIZESHIFT: IoctlNumType = TYPESHIFT + TYPEBITS as IoctlNumType;
291    const DIRSHIFT: IoctlNumType = SIZESHIFT + SIZEBITS as IoctlNumType;
292
293    const NRMASK: IoctlNumType = (1 << NRBITS) - 1;
294    const TYPEMASK: IoctlNumType = (1 << TYPEBITS) - 1;
295    const SIZEMASK: IoctlNumType = (1 << SIZEBITS) - 1;
296    const DIRMASK: IoctlNumType = (1 << DIRBITS) - 1;
297
298    macro_rules! ioc {
299        ($dir:expr, $ty:expr, $nr:expr, $sz:expr) => (
300            (($dir as IoctlNumType & DIRMASK) << DIRSHIFT) |
301            (($ty as IoctlNumType & TYPEMASK) << TYPESHIFT) |
302            (($nr as IoctlNumType & NRMASK) << NRSHIFT) |
303            (($sz as IoctlNumType & SIZEMASK) << SIZESHIFT))
304    }
305
306    // const SPI_IOC_RD_MODE: IoctlNumType = ioc!(READ, SPI_IOC_MAGIC, SPI_IOC_NR_MODE, mem::size_of::<u8>());
307
308    macro_rules! request_code_none {
309        ($ty:expr, $nr:expr) => (ioc!(NONE, $ty, $nr, 0))
310    }
311
312    macro_rules! request_code_read {
313        ($ty:expr, $nr:expr, $sz:expr) => (ioc!(READ, $ty, $nr, $sz))
314    }
315
316    macro_rules! request_code_write {
317        ($ty:expr, $nr:expr, $sz:expr) => (ioc!(WRITE, $ty, $nr, $sz))
318    }
319
320    macro_rules! request_code_readwrite {
321        ($ty:expr, $nr:expr, $sz:expr) => (ioc!(READ | WRITE, $ty, $nr, $sz))
322    }
323
324    macro_rules! ioctl_read {
325        ($(#[$attr:meta])* $name:ident, $ioty:expr, $nr:expr, $ty:ty) => (
326            $(#[$attr])*
327            pub fn $name(fd: std::os::raw::c_int, data: *mut $ty) -> std::io::Result<std::os::raw::c_int> {
328                syscall!(ioctl(fd, request_code_read!($ioty, $nr, std::mem::size_of::<$ty>()) as IoctlNumType, data))
329            }
330        )
331    }
332
333    macro_rules! ioctl_write_ptr {
334        ($(#[$attr:meta])* $name:ident, $ioty:expr, $nr:expr, $ty:ty) => (
335            $(#[$attr])*
336            pub fn $name(fd: std::os::raw::c_int, data: *const $ty) -> std::io::Result<std::os::raw::c_int> {
337                syscall!(ioctl(fd, request_code_write!($ioty, $nr, std::mem::size_of::<$ty>()) as IoctlNumType, data))
338            }
339        )
340    }
341
342    macro_rules! ioctl_read_buf {
343        ($(#[$attr:meta])* $name:ident, $ioty:expr, $nr:expr, $ty:ty) => (
344            $(#[$attr])*
345            pub fn $name(fd: std::os::raw::c_int,
346                                data: &mut [$ty])
347                                -> std::io::Result<std::os::raw::c_int> {
348                syscall!(ioctl(fd, request_code_read!($ioty, $nr, data.len() * ::std::mem::size_of::<$ty>()) as IoctlNumType, data))
349            }
350        )
351    }
352
353    macro_rules! ioctl_write_buf {
354        ($(#[$attr:meta])* $name:ident, $ioty:expr, $nr:expr, $ty:ty) => (
355            $(#[$attr])*
356            pub fn $name(fd: std::os::raw::c_int, data: &[$ty]) -> std::io::Result<std::os::raw::c_int> {
357                syscall!(ioctl(fd, request_code_write!($ioty, $nr, data.len() * ::std::mem::size_of::<$ty>()) as IoctlNumType, data))
358            }
359        )
360    }
361
362    macro_rules! ioctl_readwrite_buf {
363        ($(#[$attr:meta])* $name:ident, $ioty:expr, $nr:expr, $ty:ty) => (
364            $(#[$attr])*
365            pub fn $name(fd: std::os::raw::c_int,
366                                data: &mut [$ty])
367                                -> std::io::Result<std::os::raw::c_int> {
368                syscall!(ioctl(fd, request_code_readwrite!($ioty, $nr, data.len() * ::std::mem::size_of::<$ty>()) as IoctlNumType, data))
369            }
370        )
371    }
372
373    ioctl_read!(get_mode_u8, SPI_IOC_MAGIC, SPI_IOC_NR_MODE, u8);
374    ioctl_write_ptr!(set_mode_u8, SPI_IOC_MAGIC, SPI_IOC_NR_MODE, u8);
375    ioctl_read!(get_mode_u32, SPI_IOC_MAGIC, SPI_IOC_NR_MODE32, u32);
376    ioctl_write_ptr!(set_mode32, SPI_IOC_MAGIC, SPI_IOC_NR_MODE32, u32);
377
378    ioctl_read!(get_lsb_first, SPI_IOC_MAGIC, SPI_IOC_NR_LSB_FIRST, u8);
379    ioctl_write_ptr!(set_lsb_first, SPI_IOC_MAGIC, SPI_IOC_NR_LSB_FIRST, u8);
380
381    ioctl_read!(get_bits_per_word, SPI_IOC_MAGIC, SPI_IOC_NR_BITS_PER_WORD, u8);
382    ioctl_write_ptr!(set_bits_per_word, SPI_IOC_MAGIC, SPI_IOC_NR_BITS_PER_WORD, u8);
383
384    ioctl_read!(get_max_speed_hz, SPI_IOC_MAGIC, SPI_IOC_NR_MAX_SPEED_HZ, u32);
385    ioctl_write_ptr!(set_max_speed_hz, SPI_IOC_MAGIC, SPI_IOC_NR_MAX_SPEED_HZ, u32);
386
387    #[allow(non_camel_case_types)]
388    #[derive(Debug, Default)]
389    #[repr(C)]
390    pub struct spi_ioc_transfer<'a, 'b> {
391        tx_buf: u64,
392        rx_buf: u64,
393        len: u32,
394
395        // optional overrides
396        pub speed_hz: u32,
397        pub delay_usecs: u16,
398        pub bits_per_word: u8,
399        pub cs_change: u8,
400        pub pad: u32,
401
402        tx_buf_ref: std::marker::PhantomData<&'a [u8]>,
403        rx_buf_ref: std::marker::PhantomData<&'b mut [u8]>,
404    }
405
406    impl<'a, 'b> spi_ioc_transfer<'a, 'b> {
407        pub fn read(buff: &'b mut [u8]) -> Self {
408            spi_ioc_transfer {
409                rx_buf: buff.as_ptr() as *const () as usize as u64,
410                len: buff.len() as u32,
411                ..Default::default()
412            }
413        }
414
415        pub fn write(buff: &'a [u8]) -> Self {
416            spi_ioc_transfer {
417                tx_buf: buff.as_ptr() as *const () as usize as u64,
418                len: buff.len() as u32,
419                ..Default::default()
420            }
421        }
422
423        /// The `tx_buf` and `rx_buf` must be the same length.
424        pub fn read_write(tx_buf: &'a [u8], rx_buf: &'b mut [u8]) -> Self {
425            assert_eq!(tx_buf.len(), rx_buf.len());
426            spi_ioc_transfer {
427                rx_buf: rx_buf.as_ptr() as *const () as usize as u64,
428                tx_buf: tx_buf.as_ptr() as *const () as usize as u64,
429                len: tx_buf.len() as u32,
430                ..Default::default()
431            }
432        }
433    }
434
435
436    ioctl_write_ptr!(spidev_transfer, SPI_IOC_MAGIC, SPI_IOC_NR_TRANSFER, spi_ioc_transfer);
437    ioctl_write_buf!(spidev_transfer_buf, SPI_IOC_MAGIC, SPI_IOC_NR_TRANSFER, spi_ioc_transfer);
438}