Skip to content

Commit

Permalink
Add socketcan driver
Browse files Browse the repository at this point in the history
  • Loading branch information
Notgnoshi committed Aug 8, 2023
1 parent 7c29add commit 5aad0de
Show file tree
Hide file tree
Showing 3 changed files with 130 additions and 0 deletions.
5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
6 changes: 6 additions & 0 deletions src/driver/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
119 changes: 119 additions & 0 deletions src/driver/socketcan.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
use socketcan::{CanSocket, Socket};

use crate::driver::{
Driver, DriverCloseError, DriverOpenError, DriverReadError, DriverWriteError, Frame,
};

impl From<socketcan::Error> for DriverReadError {
fn from(e: socketcan::Error) -> DriverReadError {
match e {
socketcan::Error::Can(_) => DriverReadError::ErrorFrame(),
socketcan::Error::Io(e) => DriverReadError::IoError(e),
}
}
}

impl From<socketcan::Error> 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<CanSocket>,
}

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(())
}
}

0 comments on commit 5aad0de

Please sign in to comment.