#![deny(clippy::all)]
use libc::{
c_int, c_short, c_uint, c_void, close, connect, fcntl, read, sockaddr, socket, suseconds_t,
time_t, timeval, write, F_SETFL, O_NONBLOCK,
};
use bitflags::bitflags;
use core::convert::TryFrom;
use futures::prelude::*;
use futures::ready;
use futures::task::Context;
use mio::event::Evented;
use mio::unix::EventedFd;
use mio::{unix::UnixReady, PollOpt, Ready, Token};
use nix::net::if_::if_nametoindex;
use socketcan::{EFF_FLAG, EFF_MASK, SFF_MASK};
use std::fmt;
use std::io::{Error, ErrorKind};
use std::mem::size_of;
use std::pin::Pin;
use std::task::Poll;
use std::{io, slice, time};
use tokio::io::PollEvented;
pub use socketcan::CANFrame;
#[cfg(test)]
mod tests {
use super::*;
use std::convert::TryFrom;
#[test]
fn eff_with_eff_bit_is_stripped_of_bit() {
let can_id = CANMessageId::try_from(0x98FE_F5EBu32);
assert_eq!(Ok(CANMessageId::EFF(0x18FE_F5EB)), can_id);
}
}
pub const AF_CAN: c_int = 29;
pub const PF_CAN: c_int = AF_CAN;
pub const CAN_BCM: c_int = 2;
pub const SOCK_DGRAM: c_int = 2;
const SFF_MASK_U16: u16 = 0x07ff;
pub const MAX_NFRAMES: u32 = 256;
pub const TX_SETUP: u32 = 1;
pub const TX_DELETE: u32 = 2;
pub const TX_READ: u32 = 3;
pub const TX_SEND: u32 = 4;
pub const RX_SETUP: u32 = 5;
pub const RX_DELETE: u32 = 6;
pub const RX_READ: u32 = 7;
pub const TX_STATUS: u32 = 8;
pub const TX_EXPIRED: u32 = 9;
pub const RX_STATUS: u32 = 10;
pub const RX_TIMEOUT: u32 = 11;
pub const RX_CHANGED: u32 = 12;
pub const SETTIMER: u32 = 0x0001;
pub const STARTTIMER: u32 = 0x0002;
pub const TX_COUNTEVT: u32 = 0x0004;
pub const TX_ANNOUNCE: u32 = 0x0008;
pub const TX_CP_CAN_ID: u32 = 0x0010;
pub const RX_FILTER_ID: u32 = 0x0020;
pub const RX_CHECK_DLC: u32 = 0x0040;
pub const RX_NO_AUTOTIMER: u32 = 0x0080;
pub const RX_ANNOUNCE_RESUM: u32 = 0x0100;
pub const TX_RESET_MULTI_ID: u32 = 0x0200;
pub const RX_RTR_FRAME: u32 = 0x0400;
pub const CAN_FD_FRAME: u32 = 0x0800;
#[repr(C)]
pub struct BcmMsgHead {
_opcode: u32,
_flags: u32,
_count: u32,
_ival1: timeval,
_ival2: timeval,
_can_id: u32,
_nframes: u32,
#[cfg(all(target_pointer_width = "32"))]
_pad: u32,
_frames: [CANFrame; MAX_NFRAMES as usize],
}
impl BcmMsgHead {
pub fn can_id(&self) -> u32 {
self._can_id
}
#[inline]
pub fn frames(&self) -> &[CANFrame] {
unsafe { slice::from_raw_parts(self._frames.as_ptr(), self._nframes as usize) }
}
}
impl fmt::Debug for BcmMsgHead {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "BcmMsgHead {{ _opcode: {}, _flags: {} , _count: {}, _ival1: {:?}, _ival2: {:?}, _can_id: {}, _nframes: {}}}", self._opcode, self._flags, self._count, self._ival1.tv_sec, self._ival2.tv_sec, self._can_id, self._nframes)
}
}
#[repr(C)]
pub struct BcmMsgHeadFrameLess {
_opcode: u32,
_flags: u32,
_count: u32,
_ival1: timeval,
_ival2: timeval,
_can_id: u32,
_nframes: u32,
#[cfg(all(target_pointer_width = "32"))]
_pad: usize,
}
#[repr(C)]
pub struct TxMsg {
_msg_head: BcmMsgHeadFrameLess,
_frames: [CANFrame; MAX_NFRAMES as usize],
}
#[derive(Debug)]
pub struct BCMSocket {
pub fd: c_int,
}
pub struct BcmFrameStream {
io: PollEvented<BCMSocket>,
}
impl BcmFrameStream {
pub fn new(socket: BCMSocket) -> io::Result<BcmFrameStream> {
let io = PollEvented::new(socket)?;
Ok(BcmFrameStream { io })
}
}
impl Stream for BcmFrameStream {
type Item = io::Result<CANFrame>;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Option<Self::Item>> {
let ready = Ready::readable();
ready!(self
.io
.poll_read_ready(cx, Ready::readable() | UnixReady::error()))?;
match self.io.get_ref().read_msg() {
Ok(n) => {
if let Some(frame) = n.frames().iter().next() {
Poll::Ready(Some(Ok(*frame)))
} else {
self.io.clear_read_ready(cx, ready)?;
Poll::Pending
}
}
Err(err) => {
if err.kind() == io::ErrorKind::WouldBlock {
self.io.clear_read_ready(cx, ready)?;
return Poll::Pending;
} else {
Poll::Ready(Some(Err(err)))
}
}
}
}
}
impl Evented for BcmFrameStream {
fn register(
&self,
poll: &mio::Poll,
token: Token,
interest: Ready,
opts: PollOpt,
) -> io::Result<()> {
self.io.get_ref().register(poll, token, interest, opts)
}
fn reregister(
&self,
poll: &mio::Poll,
token: Token,
interest: Ready,
opts: PollOpt,
) -> io::Result<()> {
self.io.get_ref().reregister(poll, token, interest, opts)
}
fn deregister(&self, poll: &mio::Poll) -> io::Result<()> {
self.io.get_ref().deregister(poll)
}
}
impl BCMSocket {
pub fn open_nb(ifname: &str) -> io::Result<BCMSocket> {
let if_index = if_nametoindex(ifname).map_err(|nix_error| {
if let nix::Error::Sys(err_no) = nix_error {
io::Error::from(err_no)
} else {
panic!("unexpected nix error type: {:?}", nix_error)
}
})?;
BCMSocket::open_if_nb(if_index)
}
pub fn open_if_nb(if_index: c_uint) -> io::Result<BCMSocket> {
let sock_fd;
unsafe {
sock_fd = socket(PF_CAN, SOCK_DGRAM, CAN_BCM);
}
if sock_fd == -1 {
return Err(io::Error::last_os_error());
}
let fcntl_resp = unsafe { fcntl(sock_fd, F_SETFL, O_NONBLOCK) };
if fcntl_resp == -1 {
return Err(io::Error::last_os_error());
}
let addr = CANAddr {
_af_can: AF_CAN as c_short,
if_index: if_index as c_int,
rx_id: 0,
tx_id: 0,
};
let sockaddr_ptr = &addr as *const CANAddr;
let connect_res;
unsafe {
connect_res = connect(
sock_fd,
sockaddr_ptr as *const sockaddr,
size_of::<CANAddr>() as u32,
);
}
if connect_res != 0 {
return Err(io::Error::last_os_error());
}
Ok(BCMSocket { fd: sock_fd })
}
fn close(&mut self) -> io::Result<()> {
unsafe {
let rv = close(self.fd);
if rv != -1 {
return Err(io::Error::last_os_error());
}
}
Ok(())
}
pub fn filter_id(
&self,
can_id: CANMessageId,
ival1: time::Duration,
ival2: time::Duration,
) -> io::Result<()> {
let _ival1 = c_timeval_new(ival1);
let _ival2 = c_timeval_new(ival2);
let frames = [CANFrame::new(0x0, &[], false, false).unwrap(); MAX_NFRAMES as usize];
let msg = BcmMsgHeadFrameLess {
_opcode: RX_SETUP,
_flags: SETTIMER | RX_FILTER_ID,
_count: 0,
#[cfg(all(target_pointer_width = "32"))]
_pad: 0,
_ival1,
_ival2,
_can_id: can_id.with_eff_bit(),
_nframes: 0,
};
let tx_msg = &TxMsg {
_msg_head: msg,
_frames: frames,
};
let tx_msg_ptr = tx_msg as *const TxMsg;
let write_rv = unsafe { write(self.fd, tx_msg_ptr as *const c_void, size_of::<TxMsg>()) };
if write_rv < 0 {
return Err(Error::new(ErrorKind::WriteZero, io::Error::last_os_error()));
}
Ok(())
}
pub fn filter_id_incoming_frames(
self,
can_id: CANMessageId,
ival1: time::Duration,
ival2: time::Duration,
) -> io::Result<BcmFrameStream> {
self.filter_id(can_id, ival1, ival2)?;
self.incoming_frames()
}
pub fn incoming_msg(self) -> io::Result<BcmStream> {
BcmStream::from(self)
}
pub fn incoming_frames(self) -> io::Result<BcmFrameStream> {
BcmFrameStream::new(self)
}
pub fn filter_delete(&self, can_id: CANMessageId) -> io::Result<()> {
let frames = [CANFrame::new(0x0, &[], false, false).unwrap(); MAX_NFRAMES as usize];
let msg = &BcmMsgHead {
_opcode: RX_DELETE,
_flags: 0,
_count: 0,
_ival1: c_timeval_new(time::Duration::new(0, 0)),
_ival2: c_timeval_new(time::Duration::new(0, 0)),
_can_id: can_id.with_eff_bit(),
_nframes: 0,
#[cfg(all(target_pointer_width = "32"))]
_pad: 0,
_frames: frames,
};
let msg_ptr = msg as *const BcmMsgHead;
let write_rv = unsafe { write(self.fd, msg_ptr as *const c_void, size_of::<BcmMsgHead>()) };
let expected_size = size_of::<BcmMsgHead>() - size_of::<[CANFrame; MAX_NFRAMES as usize]>();
if write_rv as usize != expected_size {
let msg = format!("Wrote {} but expected {}", write_rv, expected_size);
return Err(Error::new(ErrorKind::WriteZero, msg));
}
Ok(())
}
pub fn read_msg(&self) -> io::Result<BcmMsgHead> {
let ival1 = c_timeval_new(time::Duration::from_millis(0));
let ival2 = c_timeval_new(time::Duration::from_millis(0));
let frames = [CANFrame::new(0x0, &[], false, false).unwrap(); MAX_NFRAMES as usize];
let mut msg = BcmMsgHead {
_opcode: 0,
_flags: 0,
_count: 0,
_ival1: ival1,
_ival2: ival2,
_can_id: 0,
_nframes: 0,
#[cfg(all(target_pointer_width = "32"))]
_pad: 0,
_frames: frames,
};
let msg_ptr = &mut msg as *mut BcmMsgHead;
let count = unsafe { read(self.fd, msg_ptr as *mut c_void, size_of::<BcmMsgHead>()) };
let last_error = io::Error::last_os_error();
if count < 0 {
Err(last_error)
} else {
Ok(msg)
}
}
}
impl Evented for BCMSocket {
fn register(
&self,
poll: &mio::Poll,
token: Token,
interest: Ready,
opts: PollOpt,
) -> io::Result<()> {
EventedFd(&self.fd).register(poll, token, interest, opts)
}
fn reregister(
&self,
poll: &mio::Poll,
token: Token,
interest: Ready,
opts: PollOpt,
) -> io::Result<()> {
EventedFd(&self.fd).reregister(poll, token, interest, opts)
}
fn deregister(&self, poll: &mio::Poll) -> io::Result<()> {
EventedFd(&self.fd).deregister(poll)
}
}
impl Drop for BCMSocket {
fn drop(&mut self) {
self.close().ok();
}
}
pub struct BcmStream {
io: PollEvented<BCMSocket>,
}
pub trait IntoBcmStream {
type Stream: futures::stream::Stream;
type Error;
fn into_bcm(self) -> Result<Self::Stream, Self::Error>;
}
impl BcmStream {
pub fn from(bcm_socket: BCMSocket) -> io::Result<BcmStream> {
let io = PollEvented::new(bcm_socket)?;
Ok(BcmStream { io })
}
}
impl Stream for BcmStream {
type Item = io::Result<BcmMsgHead>;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Option<Self::Item>> {
ready!(self
.io
.poll_read_ready(cx, Ready::readable() | UnixReady::error()))?;
match self.io.get_ref().read_msg() {
Ok(msg) => Poll::Ready(Some(Ok(msg))),
Err(err) => {
if err.kind() == io::ErrorKind::WouldBlock {
self.io.clear_read_ready(cx, Ready::readable())?;
Poll::Pending
} else {
Poll::Ready(Some(Err(err)))
}
}
}
}
}
bitflags! {
#[derive(Default)]
pub struct FrameFlags: u32 {
const EFF_FLAG = 0x8000_0000;
const RTR_FLAG = 0x4000_0000;
const ERR_FLAG = 0x2000_0000;
}
}
#[derive(Debug)]
#[repr(C)]
pub struct CANAddr {
pub _af_can: c_short,
pub if_index: c_int,
pub rx_id: u32,
pub tx_id: u32,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Hash)]
pub enum CANMessageId {
SFF(u16),
EFF(u32),
}
impl fmt::Display for CANMessageId {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
CANMessageId::SFF(id) => write!(f, "{}", id),
CANMessageId::EFF(id) => write!(f, "{}", id),
}
}
}
impl CANMessageId {
pub fn with_eff_bit(self) -> u32 {
match self {
CANMessageId::SFF(id) => u32::from(id),
CANMessageId::EFF(id) => id | FrameFlags::EFF_FLAG.bits(),
}
}
}
impl From<u16> for CANMessageId {
fn from(id: u16) -> CANMessageId {
match id {
0..=SFF_MASK_U16 => CANMessageId::SFF(id),
SFF_MASK_U16..=std::u16::MAX => CANMessageId::EFF(u32::from(id)),
}
}
}
impl TryFrom<u32> for CANMessageId {
type Error = ConstructionError;
fn try_from(id: u32) -> Result<CANMessageId, ConstructionError> {
match id {
0...SFF_MASK => Ok(CANMessageId::SFF(id as u16)),
SFF_MASK...EFF_MASK => Ok(CANMessageId::EFF(id)),
_ => {
if id & EFF_FLAG != 0 {
let without_flag = id & EFF_MASK;
Ok(CANMessageId::EFF(without_flag))
} else {
Err(ConstructionError::IDTooLarge)
}
}
}
}
}
impl From<CANMessageId> for u32 {
fn from(id: CANMessageId) -> u32 {
match id {
CANMessageId::SFF(id) => u32::from(id),
CANMessageId::EFF(id) => id,
}
}
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum ConstructionError {
IDTooLarge,
}
impl fmt::Display for ConstructionError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
ConstructionError::IDTooLarge => write!(f, "CAN ID too large"),
}
}
}
impl std::error::Error for ConstructionError {
fn description(&self) -> &str {
match *self {
ConstructionError::IDTooLarge => "can id too large",
}
}
}
fn c_timeval_new(t: time::Duration) -> timeval {
timeval {
tv_sec: t.as_secs() as time_t,
tv_usec: i64::from(t.subsec_micros()) as suseconds_t,
}
}