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(any(
feature = "embedded-hal-0",
feature = "embedded-hal",
feature = "embedded-hal-nb"
))]
mod hal;
mod ioctl;
mod segment;
pub use self::segment::Segment;
#[cfg(any(
feature = "embedded-hal-0",
feature = "embedded-hal",
feature = "embedded-hal-nb"
))]
pub use hal::SimpleHalSpiDevice;
#[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>;
#[inline(always)]
pub fn reverse_bits(buffer: &mut [u8]) {
for byte in buffer {
*byte = byte.reverse_bits();
}
}
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum Bus {
Spi0 = 0,
Spi1 = 1,
Spi2 = 2,
Spi3 = 3,
Spi4 = 4,
Spi5 = 5,
Spi6 = 6,
}
impl fmt::Display for Bus {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Bus::Spi0 => write!(f, "Spi0"),
Bus::Spi1 => write!(f, "Spi1"),
Bus::Spi2 => write!(f, "Spi2"),
Bus::Spi3 => write!(f, "Spi3"),
Bus::Spi4 => write!(f, "Spi4"),
Bus::Spi5 => write!(f, "Spi5"),
Bus::Spi6 => write!(f, "Spi6"),
}
}
}
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum SlaveSelect {
Ss0 = 0,
Ss1 = 1,
Ss2 = 2,
Ss3 = 3,
Ss4 = 4,
Ss5 = 5,
Ss6 = 6,
Ss7 = 7,
Ss8 = 8,
Ss9 = 9,
Ss10 = 10,
Ss11 = 11,
Ss12 = 12,
Ss13 = 13,
Ss14 = 14,
Ss15 = 15,
}
impl fmt::Display for SlaveSelect {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
SlaveSelect::Ss0 => write!(f, "Ss0"),
SlaveSelect::Ss1 => write!(f, "Ss1"),
SlaveSelect::Ss2 => write!(f, "Ss2"),
SlaveSelect::Ss3 => write!(f, "Ss3"),
SlaveSelect::Ss4 => write!(f, "Ss4"),
SlaveSelect::Ss5 => write!(f, "Ss5"),
SlaveSelect::Ss6 => write!(f, "Ss6"),
SlaveSelect::Ss7 => write!(f, "Ss7"),
SlaveSelect::Ss8 => write!(f, "Ss8"),
SlaveSelect::Ss9 => write!(f, "Ss9"),
SlaveSelect::Ss10 => write!(f, "Ss10"),
SlaveSelect::Ss11 => write!(f, "Ss11"),
SlaveSelect::Ss12 => write!(f, "Ss12"),
SlaveSelect::Ss13 => write!(f, "Ss13"),
SlaveSelect::Ss14 => write!(f, "Ss14"),
SlaveSelect::Ss15 => write!(f, "Ss15"),
}
}
}
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum Polarity {
ActiveLow = 0,
ActiveHigh = 1,
}
impl fmt::Display for Polarity {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Polarity::ActiveLow => write!(f, "ActiveLow"),
Polarity::ActiveHigh => write!(f, "ActiveHigh"),
}
}
}
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum Mode {
Mode0 = 0,
Mode1 = 1,
Mode2 = 2,
Mode3 = 3,
}
impl fmt::Display for Mode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Mode::Mode0 => write!(f, "Mode0"),
Mode::Mode1 => write!(f, "Mode1"),
Mode::Mode2 => write!(f, "Mode2"),
Mode::Mode3 => write!(f, "Mode3"),
}
}
}
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum BitOrder {
MsbFirst = 0,
LsbFirst = 1,
}
impl fmt::Display for BitOrder {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
BitOrder::MsbFirst => write!(f, "MsbFirst"),
BitOrder::LsbFirst => write!(f, "LsbFirst"),
}
}
}
pub struct Spi {
spidev: File,
#[cfg(any(feature = "embedded-hal-0", feature = "embedded-hal-nb"))]
last_read: Option<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(any(feature = "embedded-hal-0", feature = "embedded-hal-nb"))]
last_read: None,
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()
}
}