1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
#![allow(dead_code)]

use std::io;
use std::mem;
use std::result;

use libc::{self, c_int, ioctl};

use super::segment::Segment;

#[cfg(target_env = "gnu")]
type IoctlLong = libc::c_ulong;
#[cfg(target_env = "musl")]
type IoctlLong = c_int;
pub type Result<T> = result::Result<T, io::Error>;

const NRBITS: u8 = 8;
const TYPEBITS: u8 = 8;
const SIZEBITS: u8 = 14;
const DIRBITS: u8 = 2;
const NRSHIFT: u8 = 0;
const TYPESHIFT: u8 = NRSHIFT + NRBITS;
const SIZESHIFT: u8 = TYPESHIFT + TYPEBITS;
const DIRSHIFT: u8 = SIZESHIFT + SIZEBITS;
const NR_MESSAGE: IoctlLong = 0 << NRSHIFT;
const NR_MODE: IoctlLong = 1 << NRSHIFT;
const NR_LSB_FIRST: IoctlLong = 2 << NRSHIFT;
const NR_BITS_PER_WORD: IoctlLong = 3 << NRSHIFT;
const NR_MAX_SPEED_HZ: IoctlLong = 4 << NRSHIFT;
const NR_MODE32: IoctlLong = 5 << NRSHIFT;
const TYPE_SPI: IoctlLong = (b'k' as IoctlLong) << TYPESHIFT;
const SIZE_U8: IoctlLong = (mem::size_of::<u8>() as IoctlLong) << SIZESHIFT;
const SIZE_U32: IoctlLong = (mem::size_of::<u32>() as IoctlLong) << SIZESHIFT;
const DIR_NONE: IoctlLong = 0;
const DIR_WRITE: IoctlLong = 1 << DIRSHIFT;
const DIR_READ: IoctlLong = 2 << DIRSHIFT;
const REQ_RD_MODE: IoctlLong = DIR_READ | TYPE_SPI | NR_MODE | SIZE_U8;
const REQ_RD_LSB_FIRST: IoctlLong = DIR_READ | TYPE_SPI | NR_LSB_FIRST | SIZE_U8;
const REQ_RD_BITS_PER_WORD: IoctlLong = DIR_READ | TYPE_SPI | NR_BITS_PER_WORD | SIZE_U8;
const REQ_RD_MAX_SPEED_HZ: IoctlLong = DIR_READ | TYPE_SPI | NR_MAX_SPEED_HZ | SIZE_U32;
const REQ_RD_MODE_32: IoctlLong = DIR_READ | TYPE_SPI | NR_MODE32 | SIZE_U32;
const REQ_WR_MESSAGE: IoctlLong = DIR_WRITE | TYPE_SPI | NR_MESSAGE;
const REQ_WR_MODE: IoctlLong = DIR_WRITE | TYPE_SPI | NR_MODE | SIZE_U8;
const REQ_WR_LSB_FIRST: IoctlLong = DIR_WRITE | TYPE_SPI | NR_LSB_FIRST | SIZE_U8;
const REQ_WR_BITS_PER_WORD: IoctlLong = DIR_WRITE | TYPE_SPI | NR_BITS_PER_WORD | SIZE_U8;
const REQ_WR_MAX_SPEED_HZ: IoctlLong = DIR_WRITE | TYPE_SPI | NR_MAX_SPEED_HZ | SIZE_U32;
const REQ_WR_MODE_32: IoctlLong = DIR_WRITE | TYPE_SPI | NR_MODE32 | SIZE_U32;
pub const MODE_CPHA: u8 = 0x01;
pub const MODE_CPOL: u8 = 0x02;
pub const MODE_0: u8 = 0;
pub const MODE_1: u8 = MODE_CPHA;
pub const MODE_2: u8 = MODE_CPOL;
pub const MODE_3: u8 = MODE_CPOL | MODE_CPHA;
pub const MODE_CS_HIGH: u8 = 0x04;
// Set SS to active high
pub const MODE_LSB_FIRST: u8 = 0x08; // Set bit order to LSB first
pub const MODE_3WIRE: u8 = 0x10; // Set bidirectional mode
pub const MODE_LOOP: u8 = 0x20; // Set loopback mode
pub const MODE_NO_CS: u8 = 0x40; // Don't assert SS
pub const MODE_READY: u8 = 0x80; // Slave sends a ready signal
pub const MODE_TX_DUAL: u32 = 0x0100; // Send on 2 outgoing lines
pub const MODE_TX_QUAD: u32 = 0x0200; // Send on 4 outgoing lines
pub const MODE_RX_DUAL: u32 = 0x0400; // Receive on 2 incoming lines
pub const MODE_RX_QUAD: u32 = 0x0800; // Receive on 4 incoming lines

pub fn mode(fd: c_int, value: &mut u8) -> Result<i32> {
    parse_retval!(unsafe { ioctl(fd, REQ_RD_MODE, value) })
}

pub fn set_mode(fd: c_int, value: u8) -> Result<i32> {
    parse_retval!(unsafe { ioctl(fd, REQ_WR_MODE, &value) })
}

pub fn lsb_first(fd: c_int, value: &mut u8) -> Result<i32> {
    parse_retval!(unsafe { ioctl(fd, REQ_RD_LSB_FIRST, value) })
}

pub fn set_lsb_first(fd: c_int, value: u8) -> Result<i32> {
    parse_retval!(unsafe { ioctl(fd, REQ_WR_LSB_FIRST, &value) })
}

pub fn bits_per_word(fd: c_int, value: &mut u8) -> Result<i32> {
    parse_retval!(unsafe { ioctl(fd, REQ_RD_BITS_PER_WORD, value) })
}

pub fn set_bits_per_word(fd: c_int, value: u8) -> Result<i32> {
    parse_retval!(unsafe { ioctl(fd, REQ_WR_BITS_PER_WORD, &value) })
}

pub fn clock_speed(fd: c_int, value: &mut u32) -> Result<i32> {
    parse_retval!(unsafe { ioctl(fd, REQ_RD_MAX_SPEED_HZ, value) })
}

pub fn set_clock_speed(fd: c_int, value: u32) -> Result<i32> {
    parse_retval!(unsafe { ioctl(fd, REQ_WR_MAX_SPEED_HZ, &value) })
}

pub fn mode32(fd: c_int, value: &mut u32) -> Result<i32> {
    parse_retval!(unsafe { ioctl(fd, REQ_RD_MODE_32, value) })
}

pub fn set_mode32(fd: c_int, value: u32) -> Result<i32> {
    parse_retval!(unsafe { ioctl(fd, REQ_WR_MODE_32, &value) })
}

pub fn transfer(fd: c_int, segments: &[Segment<'_, '_>]) -> Result<i32> {
    parse_retval!(unsafe {
        ioctl(
            fd,
            REQ_WR_MESSAGE | ((std::mem::size_of_val(segments) as IoctlLong) << SIZESHIFT),
            segments,
        )
    })
}