use std::cmp::Ordering;
use std::fmt;
use std::io;
use std::ops;
use std::path::Path;
pub struct SpidevDevice(pub spidev::Spidev);
pub struct SpidevBus(pub spidev::Spidev);
impl SpidevDevice {
pub fn open<P>(path: P) -> Result<Self, SPIError>
where
P: AsRef<Path>,
{
spidev::Spidev::open(path)
.map(SpidevDevice)
.map_err(|e| e.into())
}
}
impl SpidevBus {
pub fn open<P>(path: P) -> Result<Self, SPIError>
where
P: AsRef<Path>,
{
spidev::Spidev::open(path)
.map(SpidevBus)
.map_err(|e| e.into())
}
}
impl ops::Deref for SpidevDevice {
type Target = spidev::Spidev;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl ops::DerefMut for SpidevDevice {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl ops::Deref for SpidevBus {
type Target = spidev::Spidev;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl ops::DerefMut for SpidevBus {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
mod embedded_hal_impl {
use super::*;
use embedded_hal::spi::ErrorType;
use embedded_hal::spi::{Operation as SpiOperation, SpiBus, SpiDevice};
use spidev::SpidevTransfer;
use std::convert::TryInto;
use std::io::{Read, Write};
impl ErrorType for SpidevDevice {
type Error = SPIError;
}
impl ErrorType for SpidevBus {
type Error = SPIError;
}
impl SpiBus<u8> for SpidevBus {
fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
self.0.read_exact(words).map_err(|err| SPIError { err })
}
fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
self.0.write_all(words).map_err(|err| SPIError { err })
}
fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {
let read_len = read.len();
match read_len.cmp(&write.len()) {
Ordering::Less => self.0.transfer_multiple(&mut [
SpidevTransfer::read_write(&write[..read_len], read),
SpidevTransfer::write(&write[read_len..]),
]),
Ordering::Equal => self
.0
.transfer(&mut SpidevTransfer::read_write(write, read)),
Ordering::Greater => {
let (read1, read2) = read.split_at_mut(write.len());
self.0.transfer_multiple(&mut [
SpidevTransfer::read_write(write, read1),
SpidevTransfer::read(read2),
])
}
}
.map_err(|err| SPIError { err })
}
fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
self.0
.transfer(&mut SpidevTransfer::read_write_in_place(words))
.map_err(|err| SPIError { err })
}
fn flush(&mut self) -> Result<(), Self::Error> {
self.0.flush().map_err(|err| SPIError { err })
}
}
impl SpiDevice for SpidevDevice {
fn transaction(
&mut self,
operations: &mut [SpiOperation<'_, u8>],
) -> Result<(), Self::Error> {
let mut transfers = Vec::with_capacity(operations.len());
for op in operations {
match op {
SpiOperation::Read(buf) => transfers.push(SpidevTransfer::read(buf)),
SpiOperation::Write(buf) => transfers.push(SpidevTransfer::write(buf)),
SpiOperation::Transfer(read, write) => match read.len().cmp(&write.len()) {
Ordering::Less => {
let n = read.len();
transfers.push(SpidevTransfer::read_write(&write[..n], read));
transfers.push(SpidevTransfer::write(&write[n..]));
}
Ordering::Equal => transfers.push(SpidevTransfer::read_write(write, read)),
Ordering::Greater => {
let (read1, read2) = read.split_at_mut(write.len());
transfers.push(SpidevTransfer::read_write(write, read1));
transfers.push(SpidevTransfer::read(read2));
}
},
SpiOperation::TransferInPlace(buf) => {
transfers.push(SpidevTransfer::read_write_in_place(buf));
}
SpiOperation::DelayNs(ns) => {
let us = {
if *ns == 0 {
0
} else {
let us = *ns / 1000;
if us == 0 {
1
} else {
(us).try_into().unwrap_or(u16::MAX)
}
}
};
transfers.push(SpidevTransfer::delay(us));
}
}
}
self.0
.transfer_multiple(&mut transfers)
.map_err(|err| SPIError { err })?;
self.flush()?;
Ok(())
}
}
}
#[derive(Debug)]
pub struct SPIError {
err: io::Error,
}
impl SPIError {
pub fn inner(&self) -> &io::Error {
&self.err
}
}
impl From<io::Error> for SPIError {
fn from(err: io::Error) -> Self {
Self { err }
}
}
impl embedded_hal::spi::Error for SPIError {
#[allow(clippy::match_single_binding)]
fn kind(&self) -> embedded_hal::spi::ErrorKind {
use embedded_hal::spi::ErrorKind;
ErrorKind::Other
}
}
impl fmt::Display for SPIError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.err)
}
}
impl std::error::Error for SPIError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
Some(&self.err)
}
}