use crate::adapter::{Adapter, AdapterBaud, AdapterError};
use crate::frame::{CanFrame, Frame};
use core::cell::RefCell;
use std::os::fd::AsRawFd;
use std::os::unix::io::BorrowedFd;
use std::result::Result;
use std::time::Duration;
use nix::poll::{PollFd, PollFlags, PollTimeout, poll};
use socketcan::{
BlockingCan, CanFrame as SocketCanFrame, CanInterface, CanSocket, Socket,
SocketOptions,
};
pub struct SocketCanAdapter {
rx: RefCell<CanSocket>,
tx: RefCell<CanSocket>,
}
impl SocketCanAdapter {
pub fn new(
ifname: &str,
baud: AdapterBaud,
) -> Result<Self, Box<dyn std::error::Error>> {
let iface = CanInterface::open(ifname)?;
if let Some(bit_rate) = iface.bit_rate()?
&& bit_rate as AdapterBaud != baud
{
return Err(Box::new(AdapterError::UnsupportedBaud));
}
let loopback = ifname.starts_with("vcan");
let rx = CanSocket::open(ifname)?;
rx.set_loopback(loopback)?;
let tx = CanSocket::open(ifname)?;
tx.set_loopback(loopback)?;
Ok(Self {
rx: RefCell::new(rx),
tx: RefCell::new(tx),
})
}
}
impl Adapter for SocketCanAdapter {
fn send(&self, frame: &CanFrame) -> Result<(), Box<AdapterError>> {
let frame = SocketCanFrame::new(frame.id(), frame.data())
.ok_or(AdapterError::WriteFailed)?;
self.tx
.borrow_mut()
.transmit(&frame)
.or(Err(AdapterError::WriteFailed))?;
Ok(())
}
fn recv(
&self,
timeout: Option<Duration>,
) -> Result<CanFrame, Box<AdapterError>> {
if let Some(timeout) = timeout {
let fd = unsafe {
let socket = self.rx.borrow();
BorrowedFd::borrow_raw(socket.as_raw_fd())
};
let pollfd = PollFd::new(fd, PollFlags::POLLIN);
let poll_timeout = timeout.try_into().unwrap_or(PollTimeout::MAX);
match poll(&mut [pollfd], poll_timeout) {
Ok(1) => {}
Ok(0) => {
return Err(Box::new(AdapterError::ReadTimeout));
}
_ => return Err(Box::new(AdapterError::ReadFailed)),
}
}
match self.rx.borrow_mut().receive() {
Ok(frame) => {
let frame = CanFrame::new(frame.id(), frame.data())
.ok_or(Box::new(AdapterError::ReadFailed))?;
Ok(frame)
}
_ => Err(Box::new(AdapterError::ReadFailed)),
}
}
}