use std::error;
use std::fmt;
use std::fs::{self, File, OpenOptions};
use std::io;
use std::io::{Read, Write};
use std::os::unix::fs::OpenOptionsExt;
use std::os::unix::io::{AsRawFd, RawFd};
use std::path::Path;
use std::result;
use std::time::Duration;
use libc::O_NOCTTY;
use crate::gpio::{self, Gpio, IoPin, Mode};
#[cfg(feature = "hal")]
mod hal;
mod termios;
const UART_RTS_GPIO: u8 = 17;
const UART_CTS_GPIO: u8 = 16;
const UART0_RTS_MODE: Mode = Mode::Alt3;
const UART0_CTS_MODE: Mode = Mode::Alt3;
const UART1_RTS_MODE: Mode = Mode::Alt5;
const UART1_CTS_MODE: Mode = Mode::Alt5;
#[derive(Debug)]
pub enum Error {
Io(io::Error),
Gpio(gpio::Error),
InvalidValue,
}
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::Gpio(ref err) => write!(f, "GPIO error: {}", err),
Error::InvalidValue => write!(f, "Invalid value"),
}
}
}
impl error::Error for Error {}
impl From<io::Error> for Error {
fn from(err: io::Error) -> Error {
Error::Io(err)
}
}
impl From<gpio::Error> for Error {
fn from(err: gpio::Error) -> Error {
Error::Gpio(err)
}
}
pub type Result<T> = result::Result<T, Error>;
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum Parity {
None,
Even,
Odd,
Mark,
Space,
}
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum Buffer {
Incoming,
Outgoing,
Both,
}
#[derive(Debug)]
pub struct Uart {
device: File,
fd: RawFd,
rts_cts_mode: Option<(Mode, Mode)>,
rts_cts: Option<(IoPin, IoPin)>,
}
impl Uart {
pub fn new(line_speed: u32, parity: Parity, data_bits: u8, stop_bits: u8) -> Result<Uart> {
Self::with_path("/dev/serial0", line_speed, parity, data_bits, stop_bits)
}
pub fn with_path<P: AsRef<Path>>(
path: P,
line_speed: u32,
parity: Parity,
data_bits: u8,
stop_bits: u8,
) -> Result<Uart> {
let path = fs::canonicalize(path)?;
let rts_cts_mode = if let Some(path_str) = path.to_str() {
match path_str {
"/dev/ttyAMA0" => Some((UART0_RTS_MODE, UART0_CTS_MODE)),
"/dev/ttyS0" => Some((UART1_RTS_MODE, UART1_CTS_MODE)),
_ => None,
}
} else {
None
};
let device = OpenOptions::new()
.read(true)
.write(true)
.custom_flags(O_NOCTTY)
.open(path)?;
let fd = device.as_raw_fd();
termios::set_raw_mode(fd)?;
termios::set_read_mode(fd, 0, Duration::default())?;
termios::ignore_carrier_detect(fd)?;
termios::enable_read(fd)?;
termios::set_software_flow_control(fd, false)?;
termios::set_hardware_flow_control(fd, false)?;
termios::set_line_speed(fd, line_speed)?;
termios::set_parity(fd, parity)?;
termios::set_data_bits(fd, data_bits)?;
termios::set_stop_bits(fd, stop_bits)?;
termios::flush(fd, Buffer::Both)?;
Ok(Uart {
device,
fd,
rts_cts_mode,
rts_cts: None,
})
}
pub fn line_speed(&self) -> Result<u32> {
termios::line_speed(self.fd)
}
pub fn set_line_speed(&self, line_speed: u32) -> Result<()> {
termios::set_line_speed(self.fd, line_speed)
}
pub fn parity(&self) -> Result<Parity> {
termios::parity(self.fd)
}
pub fn set_parity(&self, parity: Parity) -> Result<()> {
termios::set_parity(self.fd, parity)
}
pub fn data_bits(&self) -> Result<u8> {
termios::data_bits(self.fd)
}
pub fn set_data_bits(&self, data_bits: u8) -> Result<()> {
termios::set_data_bits(self.fd, data_bits)
}
pub fn stop_bits(&self) -> Result<u8> {
termios::stop_bits(self.fd)
}
pub fn set_stop_bits(&self, stop_bits: u8) -> Result<()> {
termios::set_stop_bits(self.fd, stop_bits)
}
pub fn hardware_flow_control(&self) -> Result<bool> {
termios::hardware_flow_control(self.fd)
}
pub fn set_hardware_flow_control(&mut self, enabled: bool) -> Result<()> {
if enabled && self.rts_cts.is_none() {
if let Some((rts_mode, cts_mode)) = self.rts_cts_mode {
let gpio = Gpio::new()?;
let pin_rts = gpio.get(UART_RTS_GPIO)?.into_io(rts_mode);
let pin_cts = gpio.get(UART_CTS_GPIO)?.into_io(cts_mode);
self.rts_cts = Some((pin_rts, pin_cts));
}
} else if !enabled {
self.rts_cts = None;
}
termios::set_hardware_flow_control(self.fd, enabled)
}
pub fn cts(&self) -> Result<bool> {
termios::cts(self.fd)
}
pub fn rts(&self) -> Result<bool> {
termios::rts(self.fd)
}
pub fn set_rts(&self, enabled: bool) -> Result<()> {
termios::set_rts(self.fd, enabled)
}
pub fn blocking_mode(&self) -> Result<(usize, Duration)> {
termios::read_mode(self.fd)
}
pub fn set_blocking_mode(&self, min_length: usize, timeout: Duration) -> Result<()> {
termios::set_read_mode(self.fd, min_length, timeout)?;
Ok(())
}
pub fn read(&mut self, buffer: &mut [u8]) -> Result<usize> {
match self.device.read(buffer) {
Ok(bytes_read) => Ok(bytes_read),
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => Ok(0),
Err(e) => Err(Error::Io(e)),
}
}
pub fn write(&mut self, buffer: &[u8]) -> Result<usize> {
match self.device.write(buffer) {
Ok(bytes_written) => Ok(bytes_written),
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => Ok(0),
Err(e) => Err(Error::Io(e)),
}
}
pub fn drain(&self) -> Result<()> {
termios::drain(self.fd)
}
pub fn flush(&self, buffer_type: Buffer) -> Result<()> {
termios::flush(self.fd, buffer_type)
}
}