use crate::{
c_timeval_new, raw_open_socket, raw_write_frame, set_fd_mode, set_socket_option,
set_socket_option_mult, CanAddr, CanAnyFrame, SocketCanFrame,
};
use libc::{
can_filter, can_frame, canfd_frame, canxl_frame, fcntl, read, CAN_RAW_ERR_FILTER,
CAN_RAW_FILTER, CAN_RAW_JOIN_FILTERS, CAN_RAW_LOOPBACK, CAN_RAW_RECV_OWN_MSGS, EINPROGRESS,
F_GETFL, F_SETFL, O_NONBLOCK, SOL_CAN_RAW, SOL_SOCKET, SO_RCVTIMEO, SO_SNDTIMEO,
};
use rs_can::{CanDirection, CanError, CanFilter, CanFrame, CanResult, ERR_MASK};
use std::{
collections::HashMap,
io,
os::{
fd::{AsRawFd, BorrowedFd, FromRawFd, OwnedFd},
raw::{c_int, c_void},
},
sync::Arc,
time::{Duration, Instant},
};
pub(crate) const FRAME_SIZE: usize = std::mem::size_of::<can_frame>();
pub(crate) const FD_FRAME_SIZE: usize = std::mem::size_of::<canfd_frame>();
pub(crate) const XL_FRAME_SIZE: usize = std::mem::size_of::<canxl_frame>();
#[derive(Debug, Clone)]
pub struct SocketCan {
pub(crate) sockets: Arc<HashMap<String, OwnedFd>>,
}
impl SocketCan {
pub fn new() -> Self {
Self {
sockets: Default::default(),
}
}
pub fn init_channel(&mut self, channel: &str, canfd: bool) -> CanResult<()> {
let addr =
CanAddr::from_iface(channel).map_err(|e| CanError::InitializeError(e.to_string()))?;
let _ = raw_open_socket(&addr)
.and_then(|fd| set_fd_mode(fd, canfd))
.and_then(|fd| {
Arc::get_mut(&mut self.sockets)
.ok_or(io::Error::last_os_error())?
.insert(channel.to_owned(), unsafe { OwnedFd::from_raw_fd(fd) });
Ok(())
})
.map_err(|_| CanError::InitializeError("device open failed".into()));
Ok(())
}
pub fn read(&self, channel: &str) -> CanResult<SocketCanFrame> {
match self.sockets.get(channel) {
Some(s) => {
let mut buffer = [0; XL_FRAME_SIZE];
let rd = unsafe {
read(
s.as_raw_fd(),
&mut buffer as *mut _ as *mut c_void,
XL_FRAME_SIZE,
)
};
match rd as usize {
FRAME_SIZE => {
let frame = unsafe { *(&buffer as *const _ as *const can_frame) };
let mut frame = SocketCanFrame::try_from(CanAnyFrame::from(frame))?;
frame.set_direction(CanDirection::Receive);
Ok(frame)
}
FD_FRAME_SIZE => {
let frame = unsafe { *(&buffer as *const _ as *const canfd_frame) };
let mut frame = SocketCanFrame::try_from(CanAnyFrame::from(frame))?;
frame.set_direction(CanDirection::Receive);
Ok(frame)
}
XL_FRAME_SIZE => {
let frame = unsafe { *(&buffer as *const _ as *const canxl_frame) };
let mut frame = SocketCanFrame::try_from(CanAnyFrame::from(frame))?;
frame.set_direction(CanDirection::Receive);
Ok(frame)
}
_ => Err(CanError::OperationError(
io::Error::last_os_error().to_string(),
)),
}
}
None => Err(CanError::channel_not_opened(channel)),
}
}
pub fn read_timeout(&self, channel: &str, timeout: Duration) -> CanResult<SocketCanFrame> {
match self.sockets.get(channel) {
Some(s) => {
use nix::poll::{poll, PollFd, PollFlags};
let borrowed_fd = unsafe { BorrowedFd::borrow_raw(s.as_raw_fd()) };
let pollfd = PollFd::new(borrowed_fd, PollFlags::POLLIN);
match poll::<u16>(&mut [pollfd], timeout.as_millis() as u16)
.map_err(|e| CanError::OperationError(e.to_string()))?
{
0 => Err(CanError::channel_timeout(channel)),
_ => self.read(channel),
}
}
None => Err(CanError::channel_not_opened(channel)),
}
}
pub fn write(&self, msg: SocketCanFrame) -> CanResult<()> {
let channel = msg.channel();
match self.sockets.get(&channel) {
Some(s) => {
let frame: CanAnyFrame = msg.into();
match frame {
CanAnyFrame::Normal(f) | CanAnyFrame::Remote(f) | CanAnyFrame::Error(f) => {
raw_write_frame(s.as_raw_fd(), &f, frame.size())
.map_err(|e| CanError::OtherError(e.to_string()))
}
CanAnyFrame::FD(f) => raw_write_frame(s.as_raw_fd(), &f, frame.size())
.map_err(|e| CanError::OtherError(e.to_string())),
CanAnyFrame::XL(f) => raw_write_frame(s.as_raw_fd(), &f, frame.size())
.map_err(|e| CanError::OtherError(e.to_string())),
}
}
None => Err(CanError::channel_not_opened(channel)),
}
}
pub fn write_timeout(&self, msg: SocketCanFrame, timeout: Duration) -> CanResult<()> {
let channel = msg.channel();
let frame: CanAnyFrame = msg.into();
let start = Instant::now();
while start.elapsed() < timeout {
match self.sockets.get(&channel) {
Some(s) => {
if let Err(e) = match frame {
CanAnyFrame::Normal(f) | CanAnyFrame::Remote(f) | CanAnyFrame::Error(f) => {
raw_write_frame(s.as_raw_fd(), &f, frame.size())
}
CanAnyFrame::FD(f) => raw_write_frame(s.as_raw_fd(), &f, frame.size()),
CanAnyFrame::XL(f) => raw_write_frame(s.as_raw_fd(), &f, frame.size()),
} {
match e.kind() {
io::ErrorKind::WouldBlock => {}
io::ErrorKind::Other => {
if !matches!(e.raw_os_error(), Some(errno) if errno == EINPROGRESS)
{
return Err(CanError::OperationError(e.to_string()));
}
}
_ => return Err(CanError::OperationError(e.to_string())),
}
} else {
return Ok(());
}
}
None => return Err(CanError::channel_not_opened(channel)),
}
}
Err(CanError::channel_timeout(channel))
}
pub fn set_nonblocking(&self, channel: &str, nonblocking: bool) -> CanResult<()> {
match self.sockets.get(channel) {
Some(s) => {
let oldfl = unsafe { fcntl(s.as_raw_fd(), F_GETFL) };
if oldfl == -1 {
return Err(CanError::OperationError(
io::Error::last_os_error().to_string(),
));
}
let newfl = if nonblocking {
oldfl | O_NONBLOCK
} else {
oldfl & !O_NONBLOCK
};
let ret = unsafe { fcntl(s.as_raw_fd(), F_SETFL, newfl) };
if ret != 0 {
Err(CanError::OperationError(
io::Error::last_os_error().to_string(),
))
} else {
Ok(())
}
}
None => Err(CanError::channel_not_opened(channel)),
}
}
pub fn set_read_timeout(&self, channel: &str, duration: Duration) -> CanResult<()> {
match self.sockets.get(channel) {
Some(s) => set_socket_option(
s.as_raw_fd(),
SOL_SOCKET,
SO_RCVTIMEO,
&c_timeval_new(duration),
)
.map_err(|e| CanError::OperationError(e.to_string())),
None => Err(CanError::channel_not_opened(channel)),
}
}
pub fn set_write_timeout(&self, channel: &str, duration: Duration) -> CanResult<()> {
match self.sockets.get(channel) {
Some(s) => set_socket_option(
s.as_raw_fd(),
SOL_SOCKET,
SO_SNDTIMEO,
&c_timeval_new(duration),
)
.map_err(|e| CanError::OperationError(e.to_string())),
None => Err(CanError::channel_not_opened(channel)),
}
}
}
impl SocketCan {
pub fn set_filters(&self, channel: &str, filters: &[CanFilter]) -> CanResult<()> {
match self.sockets.get(channel) {
Some(s) => {
let filters: Vec<can_filter> = filters
.iter()
.map(|&f| match f {
CanFilter::Standard { id, mask } => can_filter {
can_id: id.as_raw() as u32,
can_mask: mask as u32,
},
CanFilter::Extended { id, mask } => can_filter {
can_id: id.as_raw(),
can_mask: mask,
},
})
.collect();
set_socket_option_mult(s.as_raw_fd(), SOL_CAN_RAW, CAN_RAW_FILTER, &filters)
.map_err(|e| CanError::OperationError(e.to_string()))
}
None => Err(CanError::channel_not_opened(channel)),
}
}
pub fn set_filter_drop_all(&self, channel: &str) -> CanResult<()> {
match self.sockets.get(channel) {
Some(s) => {
let filters: &[CanFilter] = &[];
set_socket_option_mult(s.as_raw_fd(), SOL_CAN_RAW, CAN_RAW_FILTER, filters)
.map_err(|e| CanError::OperationError(e.to_string()))
}
None => Err(CanError::channel_not_opened(channel)),
}
}
#[inline(always)]
pub fn set_filter_accept_all(&self, channel: &str) -> CanResult<()> {
self.set_filters(channel, &[CanFilter::default()])
}
pub fn set_error_filter(&self, channel: &str, mask: u32) -> CanResult<()> {
match self.sockets.get(channel) {
Some(s) => set_socket_option(s.as_raw_fd(), SOL_CAN_RAW, CAN_RAW_ERR_FILTER, &mask)
.map_err(|e| CanError::OperationError(e.to_string())),
None => Err(CanError::channel_not_opened(channel)),
}
}
#[inline(always)]
pub fn set_error_filter_drop_all(&self, channel: &str) -> CanResult<()> {
self.set_error_filter(channel, 0)
}
#[inline(always)]
pub fn set_error_filter_accept_all(&self, channel: &str) -> CanResult<()> {
self.set_error_filter(channel, ERR_MASK)
}
pub fn set_loopback(&self, channel: &str, enabled: bool) -> CanResult<()> {
match self.sockets.get(channel) {
Some(s) => {
let loopback = c_int::from(enabled);
set_socket_option(s.as_raw_fd(), SOL_CAN_RAW, CAN_RAW_LOOPBACK, &loopback)
.map_err(|e| CanError::OperationError(e.to_string()))
}
None => Err(CanError::channel_not_opened(channel)),
}
}
pub fn set_recv_own_msgs(&self, channel: &str, enabled: bool) -> CanResult<()> {
match self.sockets.get(channel) {
Some(s) => {
let recv_own_msgs = c_int::from(enabled);
set_socket_option(
s.as_raw_fd(),
SOL_CAN_RAW,
CAN_RAW_RECV_OWN_MSGS,
&recv_own_msgs,
)
.map_err(|e| CanError::OperationError(e.to_string()))
}
None => Err(CanError::channel_not_opened(channel)),
}
}
pub fn set_join_filters(&self, channel: &str, enabled: bool) -> CanResult<()> {
match self.sockets.get(channel) {
Some(s) => {
let join_filters = c_int::from(enabled);
set_socket_option(
s.as_raw_fd(),
SOL_CAN_RAW,
CAN_RAW_JOIN_FILTERS,
&join_filters,
)
.map_err(|e| CanError::OperationError(e.to_string()))
}
None => Err(CanError::channel_not_opened(channel)),
}
}
}