Skip to content

Commit

Permalink
[SocketCAN] read IFF_ECHO flag from interface
Browse files Browse the repository at this point in the history
  • Loading branch information
pd0wm committed Apr 1, 2024
1 parent 307041d commit d236271
Show file tree
Hide file tree
Showing 5 changed files with 51 additions and 27 deletions.
2 changes: 1 addition & 1 deletion src/can/adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ pub fn get_adapter() -> Result<crate::can::AsyncCanAdapter, crate::error::Error>
#[cfg(target_os = "linux")]
{
// TODO: iterate over all available SocketCAN adapters to also find things like vcan0
if let Ok(socket) = crate::socketcan::SocketCan::new_async_from_name("can0") {
if let Ok(socket) = crate::socketcan::SocketCan::new_async("can0") {
return Ok(socket);
}
}
Expand Down
58 changes: 42 additions & 16 deletions src/socketcan/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,45 @@ use crate::Result;
use socketcan::socket::Socket;
use socketcan::SocketOptions;
use std::collections::VecDeque;
use tracing::info;

mod frame;

const IFF_ECHO: u64 = 1 << 18; // include/uapi/linux/if.h

/// Aadapter for a [`socketcan::CanFdSocket`].
pub struct SocketCan {
socket: socketcan::CanFdSocket,

/// If the IFF_ECHO flag is set on the interface, it will implement proper ACK logic. If the flag is not set, the kernel will emulate this.
#[allow(dead_code)]
iff_echo: bool,
}

fn read_iff_echo(if_name: &str) -> Option<bool> {
// Check IFF_ECHO. SIOCGIFADDR only returns the lower 16 bits of the interface flags,
// so we read the value from sysfs instead. Alternatively, we could use netlink, but
// that is probably not worth the complexity.

let flags = std::fs::read_to_string(format!("/sys/class/net/{}/flags", if_name)).ok()?;
let flags = flags.trim();
let flags = flags.strip_prefix("0x")?;
let flags = u64::from_str_radix(flags, 16).ok()?;

Some(flags & IFF_ECHO != 0)
}

impl SocketCan {
pub fn new(socket: socketcan::CanFdSocket) -> Self {
pub fn new_async(name: &str) -> Result<AsyncCanAdapter> {
let socket = SocketCan::new(name)?;
Ok(AsyncCanAdapter::new(socket))
}

pub fn new(name: &str) -> Result<SocketCan> {
let socket = match socketcan::CanFdSocket::open(name) {
Ok(socket) => socket,
Err(_) => return Err(crate::error::Error::NotFound),
};

socket.set_nonblocking(true).unwrap();
socket.set_loopback(true).unwrap();
socket.set_recv_own_msgs(true).unwrap();
Expand All @@ -25,25 +53,23 @@ impl SocketCan {
socket.as_raw_socket().set_recv_buffer_size(1_000_000).ok();

if let Ok(sz) = socket.as_raw_socket().recv_buffer_size() {
info!("SocketCAN receive buffer size {}", sz);
tracing::info!("SocketCAN receive buffer size {}", sz);
}

Self { socket }
}
// Read IFF_ECHO flag from interface
let iff_echo = match read_iff_echo(name) {
Some(iff_echo) => iff_echo,
None => {
tracing::warn!("Could not read flags for interface. Assuming IFF_ECHO is not set.");
false
}
};

pub fn new_async_from_name(name: &str) -> Result<AsyncCanAdapter> {
if let Ok(socket) = socketcan::CanFdSocket::open(name) {
SocketCan::new_async(socket)
} else {
Err(crate::error::Error::NotFound)
if !iff_echo {
tracing::warn!("IFF_ECHO is not set on the interface. ACK support is emulated by the Linux Kernel.");
}
}

pub fn new_async(socket: socketcan::CanFdSocket) -> Result<AsyncCanAdapter> {
let socket = SocketCan::new(socket);

info!("Connected to SocketCan");
Ok(AsyncCanAdapter::new(socket))
Ok(SocketCan { socket, iff_echo })
}
}

Expand Down
14 changes: 6 additions & 8 deletions tests/adapter_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,24 +82,23 @@ async fn panda_bulk_send_async() {
#[serial_test::serial]
fn socketcan_bulk_send_sync() {
use socketcan::Socket;
let socket = socketcan::CanFdSocket::open("can0").unwrap();
let mut adapter = automotive::socketcan::SocketCan::new(socket);
let mut adapter = automotive::socketcan::SocketCan::new("can0").unwrap();
bulk_send_sync(&mut adapter);
}

#[cfg(feature = "test_socketcan")]
#[tokio::test]
#[serial_test::serial]
async fn socketcan_bulk_send_async() {
let adapter = automotive::socketcan::SocketCan::new_async_from_name("can0").unwrap();
let adapter = automotive::socketcan::SocketCan::new_async("can0").unwrap();
bulk_send(&adapter).await;
}

// #[cfg(feature = "test_socketcan")]
// #[tokio::test]
// #[serial_test::serial]
// async fn vcan_bulk_send_fd() {
// let adapter = automotive::socketcan::SocketCan::new_async_from_name("can0").unwrap();
// let adapter = automotive::socketcan::SocketCan::new_async("can0").unwrap();
// adapter.send(&Frame::new(0, 0x123.into(), &[0u8; 64])).await;
// }

Expand All @@ -108,24 +107,23 @@ async fn socketcan_bulk_send_async() {
#[serial_test::serial]
fn vcan_bulk_send_sync() {
use socketcan::Socket;
let socket = socketcan::CanFdSocket::open("vcan0").unwrap();
let mut adapter = automotive::socketcan::SocketCan::new(socket);
let mut adapter = automotive::socketcan::SocketCan::new("vcan0").unwrap();
bulk_send_sync(&mut adapter);
}

#[cfg(feature = "test_vcan")]
#[tokio::test]
#[serial_test::serial]
async fn vcan_bulk_send_async() {
let adapter = automotive::socketcan::SocketCan::new_async_from_name("vcan0").unwrap();
let adapter = automotive::socketcan::SocketCan::new_async("vcan0").unwrap();
bulk_send(&adapter).await;
}

#[cfg(feature = "test_vcan")]
#[tokio::test]
#[serial_test::serial]
async fn vcan_bulk_send_fd() {
let adapter = automotive::socketcan::SocketCan::new_async_from_name("vcan0").unwrap();
let adapter = automotive::socketcan::SocketCan::new_async("vcan0").unwrap();
adapter
.send(&Frame::new(0, 0x123.into(), &[0u8; 64]).unwrap())
.await;
Expand Down
2 changes: 1 addition & 1 deletion tests/isotp_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ async fn vecu_spawn(adapter: &AsyncCanAdapter, config: VECUConfig) -> ChildGuard
}

async fn isotp_test_echo(msg_len: usize, config: VECUConfig) {
let adapter = automotive::socketcan::SocketCan::new_async_from_name("vcan0").unwrap();
let adapter = automotive::socketcan::SocketCan::new_async("vcan0").unwrap();
let _vecu = vecu_spawn(&adapter, config).await;

let mut isotp_config = IsoTPConfig::new(0, Identifier::Standard(0x7a1));
Expand Down
2 changes: 1 addition & 1 deletion tests/uds_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ async fn vecu_spawn(adapter: &AsyncCanAdapter) -> ChildGuard {
#[tokio::test]
#[serial_test::serial]
async fn uds_test_sids() {
let adapter = automotive::socketcan::SocketCan::new_async_from_name("vcan0").unwrap();
let adapter = automotive::socketcan::SocketCan::new_async("vcan0").unwrap();
let _vecu = vecu_spawn(&adapter).await;

let mut isotp_config = IsoTPConfig::new(0, Identifier::Standard(0x7a1));
Expand Down

0 comments on commit d236271

Please sign in to comment.