socketcan-alt 0.1.0

Rust binding for SocketCAN
Documentation
use crate::{sys, Frame};
use std::ffi::CStr;
use std::io::{Error, Result};
use std::mem::{self, size_of, size_of_val, MaybeUninit};
use std::os::raw::c_int;
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};

pub struct Socket(RawFd);

impl Socket {
    pub fn bind<I>(ifname: I) -> Result<Self>
    where
        I: AsRef<CStr>,
    {
        let ifindex = unsafe { libc::if_nametoindex(ifname.as_ref().as_ptr()) };
        if ifindex == 0 {
            return Err(Error::last_os_error());
        }

        let fd = unsafe { libc::socket(libc::PF_CAN, libc::SOCK_RAW, sys::CAN_RAW as _) };
        if fd == -1 {
            return Err(Error::last_os_error());
        }
        let socket = Self(fd);

        let mut address = MaybeUninit::<sys::sockaddr_can>::zeroed();
        let address = unsafe {
            (*address.as_mut_ptr()).can_family = libc::AF_CAN as _;
            (*address.as_mut_ptr()).can_ifindex = ifindex as _;
            address.assume_init()
        };
        if unsafe {
            libc::bind(
                socket.as_raw_fd(),
                &address as *const _ as _,
                size_of_val(&address) as _,
            ) != 0
        } {
            return Err(Error::last_os_error());
        }
        Ok(socket)
    }

    pub fn set_nonblocking(&self, nonblocking: bool) -> Result<()> {
        if unsafe { libc::ioctl(self.as_raw_fd(), libc::FIONBIO, &(nonblocking as c_int)) } != 0 {
            return Err(Error::last_os_error());
        }
        Ok(())
    }

    unsafe fn setsockopt<T>(&self, name: c_int, value: &T) -> Result<()> {
        if libc::setsockopt(
            self.as_raw_fd(),
            sys::SOL_CAN_RAW as _,
            name,
            value as *const _ as _,
            size_of_val(value) as _,
        ) != 0
        {
            return Err(Error::last_os_error());
        }
        Ok(())
    }

    pub fn set_recv_own_msgs(&self, enable: bool) -> Result<()> {
        unsafe { self.setsockopt(sys::CAN_RAW_RECV_OWN_MSGS as _, &(enable as c_int)) }
    }

    pub fn set_fd_frames(&self, enable: bool) -> Result<()> {
        unsafe { self.setsockopt(sys::CAN_RAW_FD_FRAMES as _, &(enable as c_int)) }
    }

    pub fn recv(&self) -> Result<Frame> {
        #[repr(C)]
        union Inner {
            can: sys::can_frame,
            canfd: sys::canfd_frame,
        }
        let mut inner = MaybeUninit::<Inner>::uninit();
        let size = unsafe {
            libc::read(
                self.as_raw_fd(),
                inner.as_mut_ptr() as _,
                size_of::<Inner>(),
            )
        } as usize;
        if size == size_of::<sys::can_frame>() {
            Ok(Frame::from_can_frame(unsafe { inner.assume_init().can }))
        } else if size == size_of::<sys::canfd_frame>() {
            Ok(Frame::from_canfd_frame(unsafe {
                inner.assume_init().canfd
            }))
        } else {
            Err(Error::last_os_error())
        }
    }

    pub fn send(&self, frame: &Frame) -> Result<()> {
        if unsafe { libc::write(self.as_raw_fd(), frame.as_ptr(), frame.size()) } as usize
            != frame.size()
        {
            return Err(Error::last_os_error());
        }
        Ok(())
    }
}

impl Drop for Socket {
    fn drop(&mut self) {
        unsafe { libc::close(self.as_raw_fd()) };
    }
}

impl AsRawFd for Socket {
    fn as_raw_fd(&self) -> RawFd {
        self.0
    }
}

impl FromRawFd for Socket {
    unsafe fn from_raw_fd(fd: RawFd) -> Self {
        Self(fd)
    }
}

impl IntoRawFd for Socket {
    fn into_raw_fd(self) -> RawFd {
        let fd = self.0;
        mem::forget(self);
        fd
    }
}

#[cfg(test)]
mod tests;