use std::error;
use std::fmt;
use std::fs::{File, OpenOptions};
use std::io;
use std::io::{Read, Write};
use std::marker::PhantomData;
use std::os::unix::io::AsRawFd;
use std::result;
#[cfg(feature = "hal")]
mod hal;
mod ioctl;
mod segment;
pub use self::segment::Segment;
#[derive(Debug)]
pub enum Error {
Io(io::Error),
BitsPerWordNotSupported(u8),
BitOrderNotSupported(BitOrder),
ClockSpeedNotSupported(u32),
ModeNotSupported(Mode),
PolarityNotSupported(Polarity),
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Error::Io(ref err) => write!(f, "I/O error: {}", err),
Error::BitsPerWordNotSupported(bits_per_word) => {
write!(f, "Bits per word value not supported: {}", bits_per_word)
}
Error::BitOrderNotSupported(bit_order) => {
write!(f, "Bit order value not supported: {:?}", bit_order)
}
Error::ClockSpeedNotSupported(clock_speed) => {
write!(f, "Clock speed value not supported: {}", clock_speed)
}
Error::ModeNotSupported(mode) => write!(f, "Mode value not supported: {:?}", mode),
Error::PolarityNotSupported(polarity) => {
write!(f, "Polarity value not supported: {:?}", polarity)
}
}
}
}
impl error::Error for Error {}
impl From<io::Error> for Error {
fn from(err: io::Error) -> Error {
Error::Io(err)
}
}
pub type Result<T> = result::Result<T, Error>;
const LOOKUP_REVERSE_BITS: [u8; 256] = [
0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC,
0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2,
0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6,
0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9,
0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3,
0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF,
];
#[inline(always)]
pub fn reverse_bits(buffer: &mut [u8]) {
for byte in buffer {
*byte = LOOKUP_REVERSE_BITS[*byte as usize];
}
}
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum Bus {
Spi0 = 0,
Spi1 = 1,
Spi2 = 2,
}
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum SlaveSelect {
Ss0 = 0,
Ss1 = 1,
Ss2 = 2,
}
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum Polarity {
ActiveLow = 0,
ActiveHigh = 1,
}
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum Mode {
Mode0 = 0,
Mode1 = 1,
Mode2 = 2,
Mode3 = 3,
}
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum BitOrder {
MsbFirst = 0,
LsbFirst = 1,
}
pub struct Spi {
spidev: File,
#[cfg(feature = "hal")]
last_read: u8,
not_sync: PhantomData<*const ()>,
}
impl Spi {
pub fn new(bus: Bus, slave_select: SlaveSelect, clock_speed: u32, mode: Mode) -> Result<Spi> {
let spidev = OpenOptions::new()
.read(true)
.write(true)
.open(format!("/dev/spidev{}.{}", bus as u8, slave_select as u8))?;
if let Err(e) = ioctl::set_mode32(spidev.as_raw_fd(), mode as u32) {
if e.kind() == io::ErrorKind::InvalidInput {
return Err(Error::ModeNotSupported(mode));
} else {
return Err(Error::Io(e));
}
}
let spi = Spi {
spidev,
#[cfg(feature = "hal")]
last_read: 0,
not_sync: PhantomData,
};
spi.set_bits_per_word(8)?;
spi.set_clock_speed(clock_speed)?;
Ok(spi)
}
pub fn bit_order(&self) -> Result<BitOrder> {
let mut bit_order: u8 = 0;
ioctl::lsb_first(self.spidev.as_raw_fd(), &mut bit_order)?;
Ok(match bit_order {
0 => BitOrder::MsbFirst,
_ => BitOrder::LsbFirst,
})
}
pub fn set_bit_order(&self, bit_order: BitOrder) -> Result<()> {
match ioctl::set_lsb_first(self.spidev.as_raw_fd(), bit_order as u8) {
Ok(_) => Ok(()),
Err(ref e) if e.kind() == io::ErrorKind::InvalidInput => {
Err(Error::BitOrderNotSupported(bit_order))
}
Err(e) => Err(Error::Io(e)),
}
}
pub fn bits_per_word(&self) -> Result<u8> {
let mut bits_per_word: u8 = 0;
ioctl::bits_per_word(self.spidev.as_raw_fd(), &mut bits_per_word)?;
Ok(bits_per_word)
}
pub fn set_bits_per_word(&self, bits_per_word: u8) -> Result<()> {
match ioctl::set_bits_per_word(self.spidev.as_raw_fd(), bits_per_word) {
Ok(_) => Ok(()),
Err(ref e) if e.kind() == io::ErrorKind::InvalidInput => {
Err(Error::BitsPerWordNotSupported(bits_per_word))
}
Err(e) => Err(Error::Io(e)),
}
}
pub fn clock_speed(&self) -> Result<u32> {
let mut clock_speed: u32 = 0;
ioctl::clock_speed(self.spidev.as_raw_fd(), &mut clock_speed)?;
Ok(clock_speed)
}
pub fn set_clock_speed(&self, clock_speed: u32) -> Result<()> {
match ioctl::set_clock_speed(self.spidev.as_raw_fd(), clock_speed) {
Ok(_) => Ok(()),
Err(ref e) if e.kind() == io::ErrorKind::InvalidInput => {
Err(Error::ClockSpeedNotSupported(clock_speed))
}
Err(e) => Err(Error::Io(e)),
}
}
pub fn mode(&self) -> Result<Mode> {
let mut mode: u8 = 0;
ioctl::mode(self.spidev.as_raw_fd(), &mut mode)?;
Ok(match mode & 0x03 {
0x01 => Mode::Mode1,
0x02 => Mode::Mode2,
0x03 => Mode::Mode3,
_ => Mode::Mode0,
})
}
pub fn set_mode(&self, mode: Mode) -> Result<()> {
let mut new_mode: u8 = 0;
ioctl::mode(self.spidev.as_raw_fd(), &mut new_mode)?;
new_mode = (new_mode & !0x03) | (mode as u8);
match ioctl::set_mode(self.spidev.as_raw_fd(), new_mode) {
Ok(_) => Ok(()),
Err(ref e) if e.kind() == io::ErrorKind::InvalidInput => {
Err(Error::ModeNotSupported(mode))
}
Err(e) => Err(Error::Io(e)),
}
}
pub fn ss_polarity(&self) -> Result<Polarity> {
let mut mode: u8 = 0;
ioctl::mode(self.spidev.as_raw_fd(), &mut mode)?;
if (mode & ioctl::MODE_CS_HIGH) == 0 {
Ok(Polarity::ActiveLow)
} else {
Ok(Polarity::ActiveHigh)
}
}
pub fn set_ss_polarity(&self, polarity: Polarity) -> Result<()> {
let mut new_mode: u8 = 0;
ioctl::mode(self.spidev.as_raw_fd(), &mut new_mode)?;
if polarity == Polarity::ActiveHigh {
new_mode |= ioctl::MODE_CS_HIGH;
} else {
new_mode &= !ioctl::MODE_CS_HIGH;
}
match ioctl::set_mode(self.spidev.as_raw_fd(), new_mode) {
Ok(_) => Ok(()),
Err(ref e) if e.kind() == io::ErrorKind::InvalidInput => {
Err(Error::PolarityNotSupported(polarity))
}
Err(e) => Err(Error::Io(e)),
}
}
pub fn read(&mut self, buffer: &mut [u8]) -> Result<usize> {
Ok(self.spidev.read(buffer)?)
}
pub fn write(&mut self, buffer: &[u8]) -> Result<usize> {
Ok(self.spidev.write(buffer)?)
}
pub fn transfer(&self, read_buffer: &mut [u8], write_buffer: &[u8]) -> Result<usize> {
let segment = Segment::new(read_buffer, write_buffer);
ioctl::transfer(self.spidev.as_raw_fd(), &[segment])?;
Ok(segment.len())
}
pub fn transfer_segments(&self, segments: &[Segment<'_, '_>]) -> Result<()> {
ioctl::transfer(self.spidev.as_raw_fd(), segments)?;
Ok(())
}
}
unsafe impl Send for Spi {}
impl fmt::Debug for Spi {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Spi").field("spidev", &self.spidev).finish()
}
}