use std::io;
use std::io::{Error, ErrorKind};
use spidev::{SPI_MODE_0, SPI_MODE_1, SPI_MODE_2, SPI_MODE_3, Spidev, SpidevOptions, SpidevTransfer};
use globals::{SPI_PATH0, SPI_PATH1};
use std::io::{BufRead, Read, Write};
#[derive(PartialEq)]
pub enum Device {
CE0 = 0,
CE1 = 1,
}
#[derive(Debug, PartialEq)]
#[allow(non_camel_case_types)]
pub enum Speed {
Mhz125_0,
Mhz62_5,
Mhz31_2,
Mhz15_6,
Mhz7_8,
Mhz3_9,
Khz1953,
Khz976,
Khz488,
Khz244,
Khz122,
Khz61,
Khz30_5,
Khz15_2,
Hz7629,
}
impl Speed {
fn to_int(&self) -> u32 {
match *self {
Speed::Mhz125_0 => 125_000_001,
Speed::Mhz62_5 => 62_500_001,
Speed::Mhz31_2 => 31_200_001,
Speed::Mhz15_6 => 15_600_001,
Speed::Mhz7_8 => 7_800_001,
Speed::Mhz3_9 => 3_900_001,
Speed::Khz1953 => 1_935_001,
Speed::Khz976 => 976_001,
Speed::Khz488 => 488_001,
Speed::Khz244 => 244_001,
Speed::Khz122 => 122_001,
Speed::Khz61 => 61_001,
Speed::Khz30_5 => 30_501,
Speed::Khz15_2 => 15_201,
Speed::Hz7629 => 7_630,
}
}
}
#[derive(PartialEq)]
pub enum SpiMode {
Mode0,
Mode1,
Mode2,
Mode3,
}
impl Default for SpiMode {
fn default() -> Self {
SpiMode::Mode0
}
}
#[derive(PartialEq)]
pub enum ComMode {
FullDuplex,
HalfDuplex,
}
impl Default for ComMode {
fn default() -> Self {
ComMode::FullDuplex
}
}
fn spi_open_error() -> Error {
Error::new(
ErrorKind::NotFound,
"Error: Unable to open the spi device. Did you set \"dtparam=spi=on\" in /boot/config.txt?",
)
}
pub struct SerialPi {
device: Spidev,
pub com_mode: ComMode,
read_buffer: Vec<u8>,
}
impl SerialPi {
pub fn new(
device: Device,
speed: Speed,
spi_mode: SpiMode,
communication_mode: ComMode,
) -> io::Result<SerialPi> {
SerialPi::with_capacity(device, speed, spi_mode, communication_mode, 1000)
}
pub fn with_capacity(
device: Device,
speed: Speed,
spi_mode: SpiMode,
communication_mode: ComMode,
buffer_capacity: usize,
) -> io::Result<SerialPi> {
let mut spi = match device {
Device::CE0 => match Spidev::open(SPI_PATH0) {
Err(_) => return Err(spi_open_error()),
Ok(device) => device,
},
Device::CE1 => match Spidev::open(SPI_PATH1) {
Err(_) => return Err(spi_open_error()),
Ok(device) => device,
},
};
let options = SpidevOptions::new()
.bits_per_word(8)
.max_speed_hz(speed.to_int())
.mode(match spi_mode {
SpiMode::Mode0 => SPI_MODE_0,
SpiMode::Mode1 => SPI_MODE_1,
SpiMode::Mode2 => SPI_MODE_2,
SpiMode::Mode3 => SPI_MODE_3,
})
.lsb_first(false)
.build();
try!(spi.configure(&options));
Ok(SerialPi {
device: spi,
com_mode: communication_mode,
read_buffer: Vec::with_capacity(buffer_capacity),
})
}
pub fn buffer_capacity(&self) -> usize {
self.read_buffer.capacity()
}
pub fn try_shrink_to(&mut self, desired_capacity: usize) -> usize {
self.read_buffer.shrink_to_fit();
if self.read_buffer.capacity() < desired_capacity {
let reserve = desired_capacity - self.read_buffer.len();
self.read_buffer.reserve_exact(reserve);
}
self.read_buffer.capacity()
}
}
impl Read for SerialPi {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let mut buffer_read_count = try!(self.read_buffer.as_slice().read(buf));
self.read_buffer = self.read_buffer.split_off(buffer_read_count);
if buffer_read_count < buf.len() {
let (_, rest_buffer) = buf.split_at_mut(buffer_read_count);
buffer_read_count = buffer_read_count + try!(self.device.read(rest_buffer));
}
Ok(buffer_read_count)
}
}
impl BufRead for SerialPi {
fn fill_buf(&mut self) -> io::Result<&[u8]> {
if self.com_mode == ComMode::FullDuplex {
let buffer_length = self.read_buffer.len();
let bytes_read = {
let rest_buffer = {
let capacity = self.read_buffer.capacity();
unsafe {
self.read_buffer.set_len(capacity);
}
let (_, rest_buffer) =
self.read_buffer.as_mut_slice().split_at_mut(buffer_length);
rest_buffer
};
try!(self.device.read(rest_buffer))
};
self.read_buffer.truncate(buffer_length + bytes_read);
}
Ok(self.read_buffer.as_slice())
}
fn consume(&mut self, amt: usize) {
if self.com_mode == ComMode::FullDuplex {
self.read_buffer.drain(0..(amt - 1));
}
}
}
impl Write for SerialPi {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
if self.com_mode == ComMode::HalfDuplex {
self.device.write(buf)
} else {
let mut read_data: Vec<u8> = Vec::with_capacity(buf.len());
read_data.resize(buf.len(), 0 as u8);
{
let mut transfer = SpidevTransfer::read_write(buf, read_data.as_mut_slice());
try!(self.device.transfer(&mut transfer));
}
self.read_buffer.append(&mut read_data);
Ok(buf.len())
}
}
fn flush(&mut self) -> io::Result<()> {
if self.com_mode == ComMode::HalfDuplex {
self.device.flush()
} else {
Ok(())
}
}
}