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 std::str;
use libc::{c_int, O_NOCTTY, O_NONBLOCK};
use libc::{TIOCM_CAR, TIOCM_CTS, TIOCM_DSR, TIOCM_DTR, TIOCM_RNG, TIOCM_RTS};
use crate::gpio::{self, Gpio, IoPin, Mode};
use crate::system::{self, DeviceInfo, Model};
#[cfg(feature = "hal")]
mod hal;
mod termios;
const GPIO_RTS: u8 = 17;
const GPIO_CTS: u8 = 16;
const GPIO_RTS_BREV2: u8 = 31;
const GPIO_CTS_BREV2: u8 = 30;
const GPIO_RTS_MODE_UART0: Mode = Mode::Alt3;
const GPIO_CTS_MODE_UART0: Mode = Mode::Alt3;
const GPIO_RTS_MODE_UART1: Mode = Mode::Alt5;
const GPIO_CTS_MODE_UART1: 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 or unsupported 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)
}
}
impl From<system::Error> for Error {
fn from(_err: system::Error) -> Error {
Error::Gpio(gpio::Error::UnknownModel)
}
}
pub type Result<T> = result::Result<T, Error>;
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum Parity {
None,
Even,
Odd,
Mark,
Space,
}
impl fmt::Display for Parity {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Parity::None => write!(f, "None"),
Parity::Even => write!(f, "Even"),
Parity::Odd => write!(f, "Odd"),
Parity::Mark => write!(f, "Mark"),
Parity::Space => write!(f, "Space"),
}
}
}
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum ParityCheck {
None,
Strip,
Replace,
Mark,
}
impl fmt::Display for ParityCheck {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
ParityCheck::None => write!(f, "None"),
ParityCheck::Strip => write!(f, "Strip"),
ParityCheck::Replace => write!(f, "Replace"),
ParityCheck::Mark => write!(f, "Mark"),
}
}
}
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum Queue {
Input,
Output,
Both,
}
impl fmt::Display for Queue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Queue::Input => write!(f, "Input"),
Queue::Output => write!(f, "Output"),
Queue::Both => write!(f, "Both"),
}
}
}
pub struct Status {
tiocm: c_int,
}
impl Status {
pub fn rts(&self) -> bool {
self.tiocm & TIOCM_RTS > 0
}
pub fn cts(&self) -> bool {
self.tiocm & TIOCM_CTS > 0
}
pub fn dtr(&self) -> bool {
self.tiocm & TIOCM_DTR > 0
}
pub fn dsr(&self) -> bool {
self.tiocm & TIOCM_DSR > 0
}
pub fn dcd(&self) -> bool {
self.tiocm & TIOCM_CAR > 0
}
pub fn ri(&self) -> bool {
self.tiocm & TIOCM_RNG > 0
}
}
impl fmt::Debug for Status {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Status")
.field("rts", &self.rts())
.field("cts", &self.cts())
.field("dtr", &self.dtr())
.field("dsr", &self.dsr())
.field("dcd", &self.dcd())
.field("ri", &self.ri())
.finish()
}
}
#[derive(Debug)]
struct UartInner {
device: File,
fd: RawFd,
rtscts_mode: Option<(Mode, Mode)>,
rtscts_pins: Option<(IoPin, IoPin)>,
blocking_read: bool,
blocking_write: bool,
baud_rate: u32,
parity: Parity,
parity_check: ParityCheck,
data_bits: u8,
stop_bits: u8,
software_flow_control: bool,
hardware_flow_control: bool,
}
#[derive(Debug)]
pub struct Uart {
inner: UartInner,
}
impl Uart {
pub fn new(baud_rate: u32, parity: Parity, data_bits: u8, stop_bits: u8) -> Result<Uart> {
Self::with_path("/dev/serial0", baud_rate, parity, data_bits, stop_bits)
}
pub fn new_bt(baud_rate: u32, parity: Parity, data_bits: u8, stop_bits: u8) -> Result<Uart> {
Self::with_path("/dev/rfcomm0", baud_rate, parity, data_bits, stop_bits)
}
pub fn with_path<P: AsRef<Path>>(
path: P,
baud_rate: u32,
parity: Parity,
data_bits: u8,
stop_bits: u8,
) -> Result<Uart> {
let path = fs::canonicalize(path)?;
let rtscts_mode = if let Some(path_str) = path.to_str() {
match path_str {
"/dev/ttyAMA0" => Some((GPIO_RTS_MODE_UART0, GPIO_CTS_MODE_UART0)),
"/dev/ttyS0" => Some((GPIO_RTS_MODE_UART1, GPIO_CTS_MODE_UART1)),
_ => None,
}
} else {
None
};
let device = OpenOptions::new()
.read(true)
.write(true)
.custom_flags(O_NOCTTY | O_NONBLOCK)
.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, false)?;
termios::set_hardware_flow_control(fd, false)?;
termios::set_line_speed(fd, baud_rate)?;
termios::set_parity(fd, parity)?;
termios::set_data_bits(fd, data_bits)?;
termios::set_stop_bits(fd, stop_bits)?;
termios::set_parity_check(fd, ParityCheck::None)?;
termios::flush(fd, Queue::Both)?;
Ok(Uart {
inner: UartInner {
device,
fd,
rtscts_mode,
rtscts_pins: None,
blocking_read: false,
blocking_write: false,
baud_rate,
parity,
parity_check: ParityCheck::None,
data_bits,
stop_bits,
software_flow_control: false,
hardware_flow_control: false,
},
})
}
pub fn baud_rate(&self) -> u32 {
self.inner.baud_rate
}
pub fn set_baud_rate(&mut self, baud_rate: u32) -> Result<()> {
termios::set_line_speed(self.inner.fd, baud_rate)?;
self.inner.baud_rate = baud_rate;
Ok(())
}
pub fn parity(&self) -> Parity {
self.inner.parity
}
pub fn set_parity(&mut self, parity: Parity) -> Result<()> {
termios::set_parity(self.inner.fd, parity)?;
self.inner.parity = parity;
Ok(())
}
pub fn parity_check(&self) -> ParityCheck {
self.inner.parity_check
}
pub fn set_parity_check(&mut self, parity_check: ParityCheck) -> Result<()> {
termios::set_parity_check(self.inner.fd, parity_check)?;
self.inner.parity_check = parity_check;
Ok(())
}
pub fn data_bits(&self) -> u8 {
self.inner.data_bits
}
pub fn set_data_bits(&mut self, data_bits: u8) -> Result<()> {
termios::set_data_bits(self.inner.fd, data_bits)?;
self.inner.data_bits = data_bits;
Ok(())
}
pub fn stop_bits(&self) -> u8 {
self.inner.stop_bits
}
pub fn set_stop_bits(&mut self, stop_bits: u8) -> Result<()> {
termios::set_stop_bits(self.inner.fd, stop_bits)?;
self.inner.stop_bits = stop_bits;
Ok(())
}
pub fn status(&self) -> Result<Status> {
let tiocm = termios::status(self.inner.fd)?;
Ok(Status { tiocm })
}
pub fn set_dtr(&mut self, dtr: bool) -> Result<()> {
termios::set_dtr(self.inner.fd, dtr)
}
pub fn set_rts(&mut self, rts: bool) -> Result<()> {
termios::set_rts(self.inner.fd, rts)
}
pub fn software_flow_control(&self) -> bool {
self.inner.software_flow_control
}
pub fn set_software_flow_control(&mut self, software_flow_control: bool) -> Result<()> {
termios::set_software_flow_control(
self.inner.fd,
software_flow_control,
software_flow_control,
)?;
self.inner.software_flow_control = software_flow_control;
Ok(())
}
pub fn hardware_flow_control(&self) -> bool {
self.inner.hardware_flow_control
}
pub fn set_hardware_flow_control(&mut self, hardware_flow_control: bool) -> Result<()> {
if hardware_flow_control && self.inner.rtscts_pins.is_none() {
if let Some((rts_mode, cts_mode)) = self.inner.rtscts_mode {
let gpio = Gpio::new()?;
let (gpio_rts, gpio_cts) = if DeviceInfo::new()?.model() == Model::RaspberryPiBRev2
{
(GPIO_RTS_BREV2, GPIO_CTS_BREV2)
} else {
(GPIO_RTS, GPIO_CTS)
};
let pin_rts = gpio.get(gpio_rts)?.into_io(rts_mode);
let pin_cts = gpio.get(gpio_cts)?.into_io(cts_mode);
self.inner.rtscts_pins = Some((pin_rts, pin_cts));
}
} else if !hardware_flow_control {
self.inner.rtscts_pins = None;
}
termios::set_hardware_flow_control(self.inner.fd, hardware_flow_control)?;
self.inner.hardware_flow_control = hardware_flow_control;
Ok(())
}
pub fn send_stop(&self) -> Result<()> {
if self.inner.software_flow_control {
termios::send_stop(self.inner.fd)?;
}
if self.inner.hardware_flow_control {
termios::set_rts(self.inner.fd, false)?;
}
Ok(())
}
pub fn send_start(&self) -> Result<()> {
if self.inner.software_flow_control {
termios::send_start(self.inner.fd)?;
}
if self.inner.hardware_flow_control {
termios::set_rts(self.inner.fd, true)?;
}
Ok(())
}
pub fn is_read_blocking(&self) -> bool {
self.inner.blocking_read
}
pub fn is_write_blocking(&self) -> bool {
self.inner.blocking_write
}
pub fn set_read_mode(&mut self, min_length: u8, timeout: Duration) -> Result<()> {
termios::set_read_mode(self.inner.fd, min_length, timeout)?;
self.inner.blocking_read = min_length > 0 || timeout.as_millis() > 0;
if self.inner.blocking_read || self.inner.blocking_write {
unsafe {
libc::fcntl(self.inner.fd, libc::F_SETFL, 0);
}
} else {
unsafe {
libc::fcntl(self.inner.fd, libc::F_SETFL, libc::O_NONBLOCK);
}
}
Ok(())
}
pub fn set_write_mode(&mut self, blocking: bool) -> Result<()> {
self.inner.blocking_write = blocking;
if self.inner.blocking_read || self.inner.blocking_write {
unsafe {
libc::fcntl(self.inner.fd, libc::F_SETFL, 0);
}
} else {
unsafe {
libc::fcntl(self.inner.fd, libc::F_SETFL, libc::O_NONBLOCK);
}
}
Ok(())
}
pub fn input_len(&self) -> Result<usize> {
termios::input_len(self.inner.fd)
}
pub fn output_len(&self) -> Result<usize> {
termios::output_len(self.inner.fd)
}
pub fn read_bytes(&mut self, buffer: &mut [u8]) -> Result<usize> {
self.inner.device.read(buffer).or_else(|e| {
if e.kind() == io::ErrorKind::WouldBlock {
Ok(0)
} else {
Err(Error::Io(e))
}
})
}
pub fn write_bytes(&mut self, buffer: &[u8]) -> Result<usize> {
if self.inner.blocking_read && !self.inner.blocking_write {
unsafe {
libc::fcntl(self.inner.fd, libc::F_SETFL, libc::O_NONBLOCK);
}
}
let result = self.inner.device.write(buffer).or_else(|e| {
if e.kind() == io::ErrorKind::WouldBlock {
Ok(0)
} else {
Err(Error::Io(e))
}
});
if self.inner.blocking_read && !self.inner.blocking_write {
unsafe {
libc::fcntl(self.inner.fd, libc::F_SETFL, 0);
}
}
result
}
pub fn drain(&self) -> Result<()> {
termios::drain(self.inner.fd)
}
pub fn flush(&self, queue_type: Queue) -> Result<()> {
termios::flush(self.inner.fd, queue_type)
}
}
impl Uart {
pub fn set_bt()->Result<Uart>{
Self::with_path("/dev/rfcomm0", 115200, Parity::None, 8, 1)
}
pub fn set()->Result<Uart>{
Self::with_path("/dev/serial0", 115200, Parity::None, 8, 1)
}
pub fn write(&mut self, message : String) -> Result<usize>{
self.write_bytes(message.as_bytes())
}
pub fn read(&mut self) -> Result<String> {
let mut buffer = [0u8; 255];
let k = self.read_bytes(&mut buffer)?;
let out = str::from_utf8(&buffer[0..k]).unwrap().to_string();
Ok(out)
}
pub fn read_until(&mut self,c : char)-> Result<String>{
let mut string = [0u8;255];
let mut end:usize =1;
let mut cond = 0;
while cond ==0 {
let mut buffer = [0u8;255];
let k = self.read_bytes(&mut buffer).expect("Something failed in reading the uart");
if k > 0 && k<255{
for i in 0..k{
if buffer[i] == c as u8 {
cond = 1;
break;
}else{
string[end-1] = buffer[i];
end =end+1;
}
}
}
}
let message :&str = str::from_utf8(&string).expect("Convertion failed");
let out:String =message.trim_matches(char::from('\0')).into();
Ok(out)
}
pub fn read_line(&mut self)-> Result<String>{
self.read_until('\n')
}
pub fn read_csv<T: std::str::FromStr>(&mut self, buffer: &mut [T])->Result<u8>{
let s:String = self.read_until('\n').unwrap();
let v: Vec<&str>= s.split(',').collect();
if v.len() == 0 {
Ok(0u8)
} else {
for i in 0..v.len(){
match v[i].parse::<T>(){
Ok(k) => {buffer[i] = k},
Err(_) => {break;},
}
}
Ok(v.len() as u8)
}
}
}