use std::mem;
use std::io;
use std::ops::Index;
use std::{os::raw::{c_char, c_int, c_void}};
use chrono::Duration;
#[allow(invalid_value)]
const AF_CAN: c_int = 29;
const PF_CAN: c_int = 29;
pub const EFF_FLAG: u32 = 0x80000000;
pub const RTR_FLAG: u32 = 0x40000000;
pub const ERR_FLAG: u32 = 0x20000000;
pub const SFF_MASK: u32 = 0x000007ff;
pub const EFF_MASK: u32 = 0x1fffffff;
pub const ERR_MASK: u32 = 0x1fffffff;
pub const ERR_MASK_ALL: u32 = ERR_MASK;
pub const ERR_MASK_NONE: u32 = 0;
pub struct Can {
fd: c_int,
}
impl Can
{
pub fn open(ifname: &str) -> Result<Can, io::Error> {
unsafe {
if ifname.len() > 16 {
return Err(io::Error::new(io::ErrorKind::Other, "No such device"));
}
let fd = libc::socket(PF_CAN, libc::SOCK_RAW, libc::CAN_RAW);
let mut uaddr = mem::MaybeUninit::<libc::sockaddr_can>::uninit();
let mut addr = uaddr.as_mut_ptr();
(*addr).can_family = AF_CAN as u16;
let mut cifname = [0 as c_char; 17];
for (i, ch) in ifname.chars().enumerate() {
cifname[i] = ch as c_char;
}
(*addr).can_ifindex = libc::if_nametoindex(&cifname as *const c_char) as i32;
if (*addr).can_ifindex == 0 {
return Err(io::Error::last_os_error());
}
let can = Can { fd: fd };
{
let timestamp_on: c_int = 1;
if libc::setsockopt(can.fd, libc::SOL_SOCKET, libc::SO_TIMESTAMP, ×tamp_on as *const c_int as *const c_void, mem::size_of::<c_int>() as u32 + 2) < 0 {
return Err(io::Error::last_os_error());
}
}
{
let opt_on: c_int = 1;
libc::setsockopt(can.fd, libc::SOL_CAN_RAW, libc::CAN_RAW_FD_FRAMES, &opt_on as *const c_int as *const c_void, mem::size_of::<c_int>() as u32);
}
if libc::bind(can.fd, addr as *const libc::sockaddr_can as *const libc::sockaddr, mem::size_of::<libc::sockaddr_can>() as u32) != 0 {
return Err(io::Error::last_os_error());
}
Ok(can)
}
}
pub fn recv(&self, msg: &mut Msg) -> Result<(), io::Error> {
unsafe {
msg.reset();
let nbytes = libc::recvmsg(self.fd, mem::transmute(&msg.msg), 0);
if nbytes < 0 {
return Err(io::Error::last_os_error());
}
}
Ok(())
}
}
impl Drop for Can {
fn drop(&mut self) {
unsafe {
if self.fd != 0 {
libc::close(self.fd);
}
self.fd = 0;
}
}
}
struct CanData<T> {
can: Can,
user_data: T,
}
pub struct Msg {
msg: libc::msghdr,
addr: libc::sockaddr_can,
iov: libc::iovec,
frame: libc::canfd_frame,
ctrlmsg: [u8; unsafe { libc::CMSG_SPACE(mem::size_of::<libc::timeval>() as u32) +
libc::CMSG_SPACE(3 * mem::size_of::<libc::timespec>() as u32) +
libc::CMSG_SPACE(mem::size_of::<u32>() as u32) } as usize],
}
impl Msg {
pub fn new() -> Box<Msg> {
unsafe {
let ucm = mem::MaybeUninit::<Msg>::uninit();
let mut msg = Box::<Msg>::new(mem::transmute(ucm));
msg.msg.msg_iovlen = 1;
msg.iov.iov_base = mem::transmute(&(*msg).frame);
msg.msg.msg_name = mem::transmute(&(*msg).addr);
msg.msg.msg_iov = mem::transmute(&(*msg).iov);
msg.msg.msg_control = mem::transmute(&(*msg).ctrlmsg[0]);
msg.reset();
msg
}
}
fn reset(&mut self) {
self.msg.msg_iovlen = 1;
self.iov.iov_len = mem::size_of::<libc::canfd_frame>();
self.msg.msg_namelen = mem::size_of::<libc::sockaddr_can>() as u32;
self.msg.msg_controllen = mem::size_of_val(&self.ctrlmsg);
self.msg.msg_flags = 0;
}
pub fn can_id(&self) -> u32 {
self.frame.can_id
}
pub fn len(&self) -> u8 {
self.frame.len
}
pub fn flags(&self) -> u8 {
self.frame.flags
}
pub fn timestamp(&self) -> io::Result<Duration> {
unsafe {
let mut cmsg = libc::CMSG_FIRSTHDR(&self.msg);
loop {
if cmsg.is_null() || (*cmsg).cmsg_level != libc::SOL_SOCKET {
break
}
match (*cmsg).cmsg_type {
libc::SO_TIMESTAMP => {
let tv = libc::CMSG_DATA(cmsg) as *const libc::timeval;
return Ok(Duration::milliseconds(((*tv).tv_sec * 1000000000 + (*tv).tv_usec * 1000) as i64));
}
libc::SO_TIMESTAMPING => {
let ts = libc::CMSG_DATA(cmsg) as *const libc::timespec;
return Ok(Duration::milliseconds(((*ts).tv_sec * 1000000000 + (*ts).tv_nsec) as i64));
}
_ => {
}
};
cmsg = libc::CMSG_NXTHDR(&self.msg, cmsg);
}
}
Err(io::Error::new(io::ErrorKind::Unsupported, "timestamps aren't supported"))
}
}
impl Index<usize> for Msg {
type Output = u8;
fn index(&self, index: usize) -> &u8 {
&self.frame.data[index]
}
}
pub struct CanGroup<T> {
fd_epoll: c_int,
cans: Vec<CanData<T>>,
events: Vec<libc::epoll_event>,
msg: Box<Msg>,
}
impl<T> CanGroup<T> {
pub fn new() -> CanGroup<T> {
unsafe {
CanGroup::<T> {
fd_epoll: libc::epoll_create(1),
cans: Vec::new(),
events: Vec::new(),
msg: Msg::new(),
}
}
}
pub fn add(&mut self, can: Can, user_data: T) -> io::Result<()> {
unsafe {
for i in 0..self.events.len() {
if libc::epoll_ctl(self.fd_epoll, libc::EPOLL_CTL_DEL, self.cans[i].can.fd, 0 as *mut libc::epoll_event) != 0 {
return Err(io::Error::last_os_error());
}
}
self.cans.push(CanData { can: can, user_data: user_data });
self.events.push(mem::transmute(mem::MaybeUninit::<libc::epoll_event>::uninit()));
for i in 0..self.events.len() {
self.events[i].events = libc::EPOLLIN as u32;
self.events[i].u64 = self.cans.last().unwrap() as *const _ as u64;
let eptr = self.events.as_mut_ptr();
if libc::epoll_ctl(self.fd_epoll, libc::EPOLL_CTL_ADD, self.cans[i].can.fd, eptr.add(i)) != 0 {
return Err(io::Error::last_os_error());
}
}
}
Ok(())
}
pub fn next(&mut self, timeout: Duration, on_recv: fn(&Box<Msg>, &T)) -> Result<bool, io::Error> {
unsafe {
let mut no_timeout = false;
let num_events = libc::epoll_wait(self.fd_epoll, self.events.as_mut_ptr(), self.events.len() as i32, timeout.num_milliseconds() as i32);
if num_events == -1 {
return Err(io::Error::last_os_error());
}
if num_events > 0 {
no_timeout = true;
for i in 0..num_events {
let can_data = self.events[i as usize].u64 as *mut CanData<T>;
(*can_data).can.recv(&mut self.msg)?;
on_recv(&self.msg, &(*can_data).user_data);
}
}
Ok(no_timeout)
}
}
}
impl<T> Drop for CanGroup<T> {
fn drop(&mut self) {
unsafe {
if self.fd_epoll != 0 {
libc::close(self.fd_epoll);
}
self.fd_epoll = 0;
}
}
}
#[cfg(test)]
fn on_recv(msg: &Box<Msg>, _user_data: &u64) {
println!("timestamp: {:?}", msg.timestamp());
print!("received CAN frame (id: {}): ", msg.can_id());
for i in 0..msg.len() {
print!("{} ", msg[i as usize]);
}
println!("");
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_works() {
let can = Can::open("vcan0").unwrap();
let mut cg = CanGroup::<u64>::new();
cg.add(can, 0).unwrap();
match cg.next(Duration::milliseconds(-1), on_recv) {
Ok(no_timeout) => if !no_timeout { panic!("timeout"); },
Err(_) => panic!("error"),
}
}
}