From 5aad0de67c9f482777e6d9b3734875a28b732925 Mon Sep 17 00:00:00 2001 From: Austin Gill Date: Fri, 4 Aug 2023 10:12:12 -0500 Subject: [PATCH] Add socketcan driver --- Cargo.toml | 5 ++ src/driver/mod.rs | 6 ++ src/driver/socketcan.rs | 119 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 130 insertions(+) create mode 100644 src/driver/socketcan.rs diff --git a/Cargo.toml b/Cargo.toml index ff905f0..24cd3c1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,3 +7,8 @@ description = "A Free ISO-11783 and J1939 CAN Stack" keywords = ["agriculture", "can", "canbus", "isobus", "j1939", "agritech", "smart-farming", "iso11783"] [dependencies] +socketcan = { version = "2.0.0", optional = true } + +[features] +default = ["socketcan"] +socketcan = ["dep:socketcan"] diff --git a/src/driver/mod.rs b/src/driver/mod.rs index 864562d..c18f744 100644 --- a/src/driver/mod.rs +++ b/src/driver/mod.rs @@ -12,8 +12,14 @@ mod driver; mod frame; mod pgn; +#[cfg(feature = "socketcan")] +mod socketcan; + pub use address::Address; pub use can_id::{CanId, Priority, Type}; pub use driver::{Driver, DriverCloseError, DriverOpenError, DriverReadError, DriverWriteError}; pub use frame::Frame; pub use pgn::Pgn; + +#[cfg(feature = "socketcan")] +pub use self::socketcan::SocketcanDriver; diff --git a/src/driver/socketcan.rs b/src/driver/socketcan.rs new file mode 100644 index 0000000..c611fc3 --- /dev/null +++ b/src/driver/socketcan.rs @@ -0,0 +1,119 @@ +use socketcan::{CanSocket, Socket}; + +use crate::driver::{ + Driver, DriverCloseError, DriverOpenError, DriverReadError, DriverWriteError, Frame, +}; + +impl From for DriverReadError { + fn from(e: socketcan::Error) -> DriverReadError { + match e { + socketcan::Error::Can(_) => DriverReadError::ErrorFrame(), + socketcan::Error::Io(e) => DriverReadError::IoError(e), + } + } +} + +impl From for DriverWriteError { + fn from(e: socketcan::Error) -> DriverWriteError { + match e { + socketcan::Error::Can(_) => DriverWriteError::BusError(), + socketcan::Error::Io(e) => DriverWriteError::IoError(e), + } + } +} + +enum SocketcanIface { + Name(String), + Index(u32), +} + +pub struct SocketcanDriver { + iface: SocketcanIface, + sock: Option, +} + +impl SocketcanDriver { + pub fn new_by_name(if_name: &str) -> Self { + Self { + iface: SocketcanIface::Name(if_name.to_string()), + sock: None, + } + } + + pub fn new_by_index(if_index: u32) -> Self { + Self { + iface: SocketcanIface::Index(if_index), + sock: None, + } + } +} + +impl Driver for SocketcanDriver { + fn is_valid(&self) -> bool { + self.sock.is_some() + } + fn open(&mut self) -> Result<(), DriverOpenError> { + match &self.iface { + SocketcanIface::Name(s) => self.sock = Some(CanSocket::open(s)?), + SocketcanIface::Index(i) => self.sock = Some(CanSocket::open_iface(*i)?), + } + // NOTE: To get any kind of non-blocking behavior, EVEN if using NonBlockingCan::receive() + // you MUST set this flag. But setting this flag causes even BlockingCan::receive() to + // return immediately with an error if there is no frame ready. + // NOTE: unwrap() is safe, because we return a DriverOpenError if we fail to create it. + self.sock.as_ref().unwrap().set_nonblocking(true)?; + Ok(()) + } + fn close(&mut self) -> Result<(), DriverCloseError> { + self.sock = None; + Ok(()) + } + + fn read_blocking(&mut self, _frame: &mut Frame) -> Result<(), DriverReadError> { + let Some(sock) = self.sock.as_mut() else { + return Err(DriverReadError::DriverClosed); + }; + + loop { + // Use a timeout so that we're not calling the non-blocking read_frame() over and over + // as fast as possible. + match sock.read_frame_timeout(std::time::Duration::from_millis(100)) { + Ok(_frame) => { + // TODO: convert socketcan CanFrame to Frame + return Ok(()); + } + Err(e) => match e.kind() { + std::io::ErrorKind::TimedOut => {} + _ => return Err(e.into()), + }, + } + } + } + fn read_nonblocking(&mut self, _frame: &mut Frame) -> Result<(), DriverReadError> { + let Some(sock) = self.sock.as_mut() else { + return Err(DriverReadError::DriverClosed); + }; + let _frame = sock.read_frame()?; + // TODO: Convert socketcan CanFrame to Frame. + Ok(()) + } + + fn write_blocking(&mut self, _frame: &Frame) -> Result<(), DriverWriteError> { + let Some(sock) = self.sock.as_mut() else { + return Err(DriverWriteError::DriverClosed); + }; + // TODO: Convert Frame to socketcan CanFrame + let socketcan_frame = socketcan::CanFrame::default(); + sock.write_frame_insist(&socketcan_frame)?; + Ok(()) + } + fn write_nonblocking(&mut self, _frame: &Frame) -> Result<(), DriverWriteError> { + let Some(sock) = self.sock.as_mut() else { + return Err(DriverWriteError::DriverClosed); + }; + // TODO: Convert Frame to socketcan CanFrame + let socketcan_frame = socketcan::CanFrame::default(); + sock.write_frame(&socketcan_frame)?; + Ok(()) + } +}