socketcan2/
lib.rs

1//! SocketCAN support.
2//! # Features
3//! * Receive can frames
4//! * Accurate timestamps (timestamps also support multi threading in contrast to receiving the TIMESTAMP via an ioctl call, which does not support mt)
5//! * epoll-support (allows to wait on multiple CAN devices in the same thread)
6//! * Send CAN frames (not implemented yet)
7//! * Filter CAN frames (not implemented yet)
8//! # Usage example
9//! ```
10//! #[cfg(test)]
11//! fn on_recv(msg: &Box<Msg>, _user_data: &u64) {
12//!   println!("timestamp: {:?}", msg.timestamp());
13//!   print!("received CAN frame (id: {}): ", msg.can_id());
14//!   for i in 0..msg.len() {
15//!     print!("{} ", msg[i as usize]);
16//!   }
17//!   println!("");
18//! }
19//! 
20//! #[cfg(test)]
21//! mod tests {
22//!   use super::*;
23//!   #[test]
24//!   fn it_works() {
25//!     let can = Can::open("vcan0").unwrap();
26//!     let mut cg = CanGroup::<u64>::new();
27//!     cg.add(can, 0).unwrap();
28//!     match cg.next(Duration::milliseconds(-1), on_recv) {
29//!       Ok(no_timeout) => if !no_timeout { panic!("timeout"); },
30//!       Err(_) => panic!("error"),
31//!     }
32//!   }
33//! }
34//! ```
35
36use std::mem;
37use std::io;
38use std::ops::Index;
39use std::{os::raw::{c_char, c_int, c_void}};
40
41use chrono::Duration;
42
43#[allow(invalid_value)]
44
45// Constants stolen from C headers
46const AF_CAN: c_int = 29;
47const PF_CAN: c_int = 29;
48// Unused yet
49// const CAN_RAW: c_int = 1;
50// const SOL_CAN_BASE: c_int = 100;
51// const SOL_CAN_RAW: c_int = SOL_CAN_BASE + CAN_RAW;
52// const CAN_RAW_FILTER: c_int = 1;
53// const CAN_RAW_ERR_FILTER: c_int = 2;
54// const CAN_RAW_LOOPBACK: c_int = 3;
55// const CAN_RAW_RECV_OWN_MSGS: c_int = 4;
56// const CAN_RAW_FD_FRAMES: c_int = 5;
57// const CAN_RAW_JOIN_FILTERS: c_int = 6;
58// const SIOCGSTAMP: c_int = 0x8906;
59// const SIOCGSTAMPNS: c_int = 0x8907;
60/// if set, indicate 29 bit extended format
61pub const EFF_FLAG: u32 = 0x80000000;
62/// remote transmission request flag
63pub const RTR_FLAG: u32 = 0x40000000;
64/// error flag
65pub const ERR_FLAG: u32 = 0x20000000;
66/// valid bits in standard frame id
67pub const SFF_MASK: u32 = 0x000007ff;
68/// valid bits in extended frame id
69pub const EFF_MASK: u32 = 0x1fffffff;
70/// valid bits in error frame
71pub const ERR_MASK: u32 = 0x1fffffff;
72/// an error mask that will cause SocketCAN to report all errors
73pub const ERR_MASK_ALL: u32 = ERR_MASK;
74/// an error mask that will cause SocketCAN to silently drop all errors
75pub const ERR_MASK_NONE: u32 = 0;
76
77/// CAN socket
78///
79/// Provides standard functionallity for sending and receiving CAN frames.
80pub struct Can {
81  fd: c_int,
82}
83impl Can
84{
85  /// Open CAN device by netdev name.
86  pub fn open(ifname: &str) -> Result<Can, io::Error> {
87    unsafe {
88      if ifname.len() > 16 {
89        return Err(io::Error::new(io::ErrorKind::Other, "No such device"));
90      }
91      let fd = libc::socket(PF_CAN, libc::SOCK_RAW, libc::CAN_RAW);
92      let mut uaddr = mem::MaybeUninit::<libc::sockaddr_can>::uninit();
93      let mut addr = uaddr.as_mut_ptr();
94      (*addr).can_family = AF_CAN as u16;
95      let mut cifname = [0 as c_char; 17];
96      for (i, ch) in ifname.chars().enumerate() {
97        cifname[i] = ch as c_char;
98      }
99      (*addr).can_ifindex = libc::if_nametoindex(&cifname as *const c_char) as i32;
100      if (*addr).can_ifindex == 0 {
101        return Err(io::Error::last_os_error());
102      }
103      let can = Can { fd: fd };
104      {
105        let timestamp_on: c_int = 1;
106        if libc::setsockopt(can.fd, libc::SOL_SOCKET, libc::SO_TIMESTAMP, &timestamp_on as *const c_int as *const c_void, mem::size_of::<c_int>() as u32 + 2) < 0 {
107          return Err(io::Error::last_os_error());
108        }
109      }
110      {
111        let opt_on: c_int = 1;
112        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);
113      }
114      if libc::bind(can.fd, addr as *const libc::sockaddr_can as *const libc::sockaddr, mem::size_of::<libc::sockaddr_can>() as u32) != 0 {
115        return Err(io::Error::last_os_error());
116      }
117      Ok(can)
118    }
119  }
120  /// Receives a CAN message.
121  /// Blocks until frame is received or the iface is down.
122  pub fn recv(&self, msg: &mut Msg) -> Result<(), io::Error> {
123    unsafe {
124      msg.reset();
125      let nbytes = libc::recvmsg(self.fd, mem::transmute(&msg.msg), 0);
126      if nbytes < 0 {
127        return Err(io::Error::last_os_error());
128      }
129    }
130    Ok(())
131  }
132}
133impl Drop for Can {
134  fn drop(&mut self) {
135    unsafe {
136      if self.fd != 0 {
137        libc::close(self.fd);
138      }
139      self.fd = 0;
140    }
141  }
142}
143struct CanData<T> {
144  can: Can, 
145  user_data: T,
146}
147/// CAN message type.
148pub struct Msg {
149  msg: libc::msghdr,
150  addr: libc::sockaddr_can,
151  iov: libc::iovec,
152  frame: libc::canfd_frame,
153  ctrlmsg: [u8; unsafe { libc::CMSG_SPACE(mem::size_of::<libc::timeval>() as u32) + 
154                         libc::CMSG_SPACE(3 * mem::size_of::<libc::timespec>() as u32) +
155                         libc::CMSG_SPACE(mem::size_of::<u32>() as u32) } as usize],
156}
157impl Msg {
158  /// Return initialized empty message object.
159  /// The return type is Box, to avoid dangling pointers
160  /// when the object is moved.
161  pub fn new() -> Box<Msg> {
162    unsafe {
163      let ucm             = mem::MaybeUninit::<Msg>::uninit();
164      let mut msg         = Box::<Msg>::new(mem::transmute(ucm));
165      msg.msg.msg_iovlen  = 1;
166      msg.iov.iov_base    = mem::transmute(&(*msg).frame);
167      msg.msg.msg_name    = mem::transmute(&(*msg).addr);
168      msg.msg.msg_iov     = mem::transmute(&(*msg).iov);
169      msg.msg.msg_control = mem::transmute(&(*msg).ctrlmsg[0]);
170      msg.reset();
171      msg
172    }
173  }
174  fn reset(&mut self) {
175    self.msg.msg_iovlen     = 1;
176    self.iov.iov_len        = mem::size_of::<libc::canfd_frame>();
177    self.msg.msg_namelen    = mem::size_of::<libc::sockaddr_can>() as u32;
178    self.msg.msg_controllen = mem::size_of_val(&self.ctrlmsg);
179    self.msg.msg_flags      = 0;
180  }
181  /// Get CAN ID.
182  pub fn can_id(&self) -> u32 {
183    self.frame.can_id
184  }
185  /// Get DLC.
186  pub fn len(&self) -> u8 {
187    self.frame.len
188  }
189  /// Get CAN FD flags.
190  pub fn flags(&self) -> u8 {
191    self.frame.flags
192  }
193  /// Get the frame timestamp.
194  pub fn timestamp(&self) -> io::Result<Duration> {
195    unsafe {
196      let mut cmsg = libc::CMSG_FIRSTHDR(&self.msg);
197      loop {
198        if cmsg.is_null() || (*cmsg).cmsg_level != libc::SOL_SOCKET {
199          break
200        }
201        match (*cmsg).cmsg_type {
202          libc::SO_TIMESTAMP => {
203            let tv = libc::CMSG_DATA(cmsg) as *const libc::timeval;            
204            return Ok(Duration::milliseconds(((*tv).tv_sec * 1000000000 + (*tv).tv_usec * 1000) as i64));
205          }
206          libc::SO_TIMESTAMPING => {
207            let ts = libc::CMSG_DATA(cmsg) as *const libc::timespec; 
208            return Ok(Duration::milliseconds(((*ts).tv_sec * 1000000000 + (*ts).tv_nsec) as i64));
209          }
210          _ => {
211          }
212        };
213        cmsg = libc::CMSG_NXTHDR(&self.msg, cmsg);
214      }
215    }
216    Err(io::Error::new(io::ErrorKind::Unsupported, "timestamps aren't supported"))
217  }
218}
219impl Index<usize> for Msg {
220  type Output = u8;
221  fn index(&self, index: usize) -> &u8 {
222    &self.frame.data[index]
223  }
224}
225/// Type for receiving data from multiple CAN devices. This type also supports timeouts.
226pub struct CanGroup<T> {
227  fd_epoll: c_int,
228  cans: Vec<CanData<T>>,
229  events: Vec<libc::epoll_event>,
230  msg: Box<Msg>,
231}
232impl<T> CanGroup<T> {
233  /// Creates an empty CanGroup instance.
234  pub fn new() -> CanGroup<T> {
235    unsafe {
236      CanGroup::<T> {
237        fd_epoll: libc::epoll_create(1),
238        cans:     Vec::new(),
239        events:   Vec::new(),
240        msg:      Msg::new(),
241      }
242    }
243  }
244  /// Adds a can device to the group
245  pub fn add(&mut self, can: Can, user_data: T) -> io::Result<()> {
246    unsafe {
247      for i in 0..self.events.len() {
248        if libc::epoll_ctl(self.fd_epoll, libc::EPOLL_CTL_DEL, self.cans[i].can.fd, 0 as *mut libc::epoll_event) != 0 {
249          return Err(io::Error::last_os_error());
250        }
251      }
252      self.cans.push(CanData { can: can, user_data: user_data });
253      self.events.push(mem::transmute(mem::MaybeUninit::<libc::epoll_event>::uninit()));
254      for i in 0..self.events.len() {
255        self.events[i].events = libc::EPOLLIN as u32;
256        self.events[i].u64 = self.cans.last().unwrap() as *const _ as u64;
257        let eptr = self.events.as_mut_ptr();
258        if libc::epoll_ctl(self.fd_epoll, libc::EPOLL_CTL_ADD, self.cans[i].can.fd, eptr.add(i)) != 0 {
259          return Err(io::Error::last_os_error());
260        }
261      }
262    }
263    Ok(())
264  }
265  /// Receives the the next CAN frame which is avaiable and calls the provided callback. The callback
266  /// may be called n times, in which n is the number of added CAN devices. The function blocks either until
267  /// at least one CAN devices has new data available, until timeout is reached or until an error happend.
268  /// The timeout uses ms granularity. Duration::from_milliseconds(-1) can be passed to this function
269  /// to disable the timeout functionallity. The function either returns true, if no timeout happened,
270  /// false if a timeout happend or io::Error if an error happend.
271  pub fn next(&mut self, timeout: Duration, on_recv: fn(&Box<Msg>, &T)) -> Result<bool, io::Error> {
272    unsafe {
273      let mut no_timeout = false;
274      let num_events = libc::epoll_wait(self.fd_epoll, self.events.as_mut_ptr(), self.events.len() as i32, timeout.num_milliseconds() as i32);
275      if num_events == -1 {
276        return Err(io::Error::last_os_error());
277      }
278      if num_events > 0 {
279        no_timeout = true;
280        for i in 0..num_events {
281          let can_data = self.events[i as usize].u64 as *mut CanData<T>;
282          (*can_data).can.recv(&mut self.msg)?;
283          on_recv(&self.msg, &(*can_data).user_data);
284        }
285      }
286      Ok(no_timeout)
287    }
288  }
289}
290impl<T> Drop for CanGroup<T> {
291  fn drop(&mut self) {
292    unsafe {
293      if self.fd_epoll != 0 {
294        libc::close(self.fd_epoll);
295      }
296      self.fd_epoll = 0;
297    }
298  }
299}
300
301#[cfg(test)]
302fn on_recv(msg: &Box<Msg>, _user_data: &u64) {
303  println!("timestamp: {:?}", msg.timestamp());
304  print!("received CAN frame (id: {}): ", msg.can_id());
305  for i in 0..msg.len() {
306    print!("{} ", msg[i as usize]);
307  }
308  println!("");
309}
310
311#[cfg(test)]
312mod tests {
313  use super::*;
314  #[test]
315  fn it_works() {
316    let can = Can::open("vcan0").unwrap();
317    let mut cg = CanGroup::<u64>::new();
318    cg.add(can, 0).unwrap();
319    match cg.next(Duration::milliseconds(-1), on_recv) {
320      Ok(no_timeout) => if !no_timeout { panic!("timeout"); },
321      Err(_) => panic!("error"),
322    }
323  }
324}
325