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
11pub 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#[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 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 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 pub const SPI_CPHA: u8 = 0x01;
241 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 pub const SPI_CS_HIGH: u8 = 0x04;
251 pub const SPI_LSB_FIRST: u8 = 0x08;
253 pub const SPI_3WIRE: u8 = 0x10;
255 pub const SPI_LOOP: u8 = 0x20;
257 pub const SPI_NO_CS: u8 = 0x40;
259 pub const SPI_READY: u8 = 0x80;
261
262 pub const SPI_TX_DUAL: u32 = 0x100;
264 pub const SPI_TX_QUAD: u32 = 0x200;
266 pub const SPI_RX_DUAL: u32 = 0x400;
268 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 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 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 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}