#![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)
}
pub(crate) fn monotonic_ns() -> u64 {
#[cfg(target_vendor = "apple")]
let clock = libc::CLOCK_UPTIME_RAW;
#[cfg(not(target_vendor = "apple"))]
let clock = libc::CLOCK_MONOTONIC;
let mut now = libc::timespec {
tv_sec: 0,
tv_nsec: 0,
};
unsafe { libc::clock_gettime(clock, &mut now) };
now.tv_sec as u64 * 1_000_000_000 + now.tv_nsec as u64
}