Skip to content

Commit

Permalink
[SocketCAN] Emulate ACK if IFF_ECHO not set
Browse files Browse the repository at this point in the history
  • Loading branch information
pd0wm committed Apr 1, 2024
1 parent 536427c commit 138417d
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 12 deletions.
37 changes: 28 additions & 9 deletions src/socketcan/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
//! This module provides a [`CanAdapter`] implementation for the [`socketcan`] crate.
use crate::can::AsyncCanAdapter;
use crate::can::CanAdapter;
use crate::can::{AsyncCanAdapter, CanAdapter, Frame};
use crate::Result;

use socketcan::socket::Socket;
Expand All @@ -16,8 +15,9 @@ 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,

loopback_queue: VecDeque<Frame>,
}

fn read_iff_echo(if_name: &str) -> Option<bool> {
Expand Down Expand Up @@ -47,7 +47,6 @@ impl SocketCan {

socket.set_nonblocking(true).unwrap();
socket.set_loopback(true).unwrap();
socket.set_recv_own_msgs(true).unwrap();

// Attempt to increase the buffer receive size to 1MB
socket.as_raw_socket().set_recv_buffer_size(1_000_000).ok();
Expand All @@ -65,37 +64,57 @@ impl SocketCan {
}
};

if !iff_echo {
tracing::warn!("IFF_ECHO is not set on the interface. ACK support is emulated by the Linux Kernel.");
if iff_echo {
socket.set_recv_own_msgs(true).unwrap();
} else {
tracing::warn!("IFF_ECHO is not set on the interface. ACK support is emulated.");
}

Ok(SocketCan { socket, iff_echo })
Ok(SocketCan {
socket,
iff_echo,
loopback_queue: VecDeque::new(),
})
}
}

impl CanAdapter for SocketCan {
fn send(&mut self, frames: &mut VecDeque<crate::can::Frame>) -> Result<()> {
fn send(&mut self, frames: &mut VecDeque<Frame>) -> Result<()> {
while let Some(frame) = frames.pop_front() {
let to_send: socketcan::frame::CanAnyFrame = frame.clone().into();

if self.socket.write_frame(&to_send).is_err() {
// Failed to send frame, push it back to the front of the queue for next send call
frames.push_front(frame);
break;
} else if !self.iff_echo {
// If IFF_ECHO is not set, we need to emulate the ACK logic.
let mut frame = frame.clone();
frame.loopback = true;
self.loopback_queue.push_back(frame);
}
}

Ok(())
}

fn recv(&mut self) -> Result<Vec<crate::can::Frame>> {
fn recv(&mut self) -> Result<Vec<Frame>> {
let mut frames = vec![];

while let Ok((frame, meta)) = self.socket.read_frame_with_meta() {
let mut frame: crate::can::Frame = frame.into();
frame.loopback = meta.loopback;

// If IFF_ECHO is not set, we emulate the ACK logic ourself.
if frame.loopback {
assert!(self.iff_echo);
}

frames.push(frame);
}

frames.extend(self.loopback_queue.drain(..));

Ok(frames)
}
}
7 changes: 4 additions & 3 deletions tests/adapter_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ use automotive::panda::Panda;
use std::collections::VecDeque;
use std::time::Duration;

static BULK_NUM_FRAMES: u64 = 0x100;
static BULK_NUM_FRAMES_SYNC: u64 = 0x100;
static BULK_NUM_FRAMES_ASYNC: u64 = 0x1000;
static BULK_TIMEOUT_MS: u64 = 1000;

/// Sends a large number of frames to a "blocking" adapter, and then reads back all sent messages.
Expand All @@ -14,7 +15,7 @@ static BULK_TIMEOUT_MS: u64 = 1000;
fn bulk_send_sync<T: CanAdapter>(adapter: &mut T) {
let mut frames = vec![];

for i in 0..BULK_NUM_FRAMES {
for i in 0..BULK_NUM_FRAMES_SYNC {
frames.push(Frame::new(0, 0x123.into(), &i.to_be_bytes()).unwrap());
}

Expand Down Expand Up @@ -48,7 +49,7 @@ fn bulk_send_sync<T: CanAdapter>(adapter: &mut T) {
async fn bulk_send(adapter: &AsyncCanAdapter) {
let mut frames = vec![];

for i in 0..BULK_NUM_FRAMES {
for i in 0..BULK_NUM_FRAMES_ASYNC {
frames.push(Frame::new(0, 0x123.into(), &i.to_be_bytes()).unwrap());
}

Expand Down

0 comments on commit 138417d

Please sign in to comment.