#![allow(unsafe_code)]
use std::io;
use std::os::fd::AsRawFd;
use std::path::Path;
use serial2::{SerialPort, Settings};
use super::{Port, READ_SLICE};
use crate::backend::WRITE_DEADLINE;
pub(super) fn open(path: &Path, baud: u32) -> io::Result<Box<dyn Port>> {
let mut port = SerialPort::open(path, |mut settings: Settings| {
settings.set_raw();
settings.set_baud_rate(baud)?;
Ok(settings)
})?;
let fd = port.as_raw_fd();
unsafe {
if libc::flock(fd, libc::LOCK_EX | libc::LOCK_NB) != 0 {
return Err(io::Error::last_os_error());
}
if libc::ioctl(fd, libc::TIOCEXCL) != 0 {
return Err(io::Error::last_os_error());
}
}
port.set_read_timeout(READ_SLICE)?;
port.set_write_timeout(WRITE_DEADLINE)?;
Ok(Box::new(UnixPort(port)))
}
struct UnixPort(SerialPort);
impl Port for UnixPort {
fn read(&self, buffer: &mut [u8]) -> io::Result<usize> {
self.0.read(buffer)
}
fn write_all(&self, bytes: &[u8]) -> io::Result<()> {
self.0.write_all(bytes)
}
fn wake(&self) {}
fn retire(&self) {}
}
pub(crate) fn lock_port(path: &Path) -> io::Result<std::fs::File> {
use std::os::unix::fs::OpenOptionsExt;
let file = std::fs::OpenOptions::new()
.read(true)
.write(true)
.custom_flags(libc::O_NOCTTY | libc::O_NONBLOCK)
.open(path)?;
if unsafe { libc::flock(file.as_raw_fd(), libc::LOCK_EX | libc::LOCK_NB) } != 0 {
return Err(io::Error::last_os_error());
}
Ok(file)
}