Skip to content

Commit

Permalink
panda: Support FD flag in USB protocol (#76)
Browse files Browse the repository at this point in the history
* panda: support FD flag in USB protocol

* update readme

* ensure auto mode is off
  • Loading branch information
pd0wm authored Nov 26, 2024
1 parent f6154e8 commit 73b0236
Show file tree
Hide file tree
Showing 5 changed files with 24 additions and 12 deletions.
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@ This library supports awaiting a sent frame and waiting for the ACK on the CAN b
- neoVI/ValueCAN: Use of Intrepid Control System's devices is not recommended due to issues in their SocketCAN driver. If many frames are transmitted simultaneously it will cause the whole system/kernel to hang. [intrepid-socketcan-kernel-module#20](https://github.com/intrepidcs/intrepid-socketcan-kernel-module/issues/20) tracks this issue.
- comma.ai panda
- The panda does not retry frames that are not ACKed, and drops them instead. This can cause panics in some internal parts of the library when frames are dropped. [panda#1922](https://github.com/commaai/panda/issues/1922) tracks this issue.
- The CAN-FD flag on a frame is ignored, if the hardware is configured for CAN-FD all frames will be interpreted as FD regardless of the FD frame bit (r0 bit).
- Vector Devices are supported through the Vector XL Driver Library, and support can be enabled using the `vector-xl` feature. Make sure to distribute `vxlapi64.dll` alongside your application.


Expand Down
2 changes: 0 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@
//! - neoVI/ValueCAN: Use of Intrepid Control System's devices is not recommended due to issues in their SocketCAN driver. If many frames are transmitted simultaneously it will cause the whole system/kernel to hang. [intrepid-socketcan-kernel-module#20](https://github.com/intrepidcs/intrepid-socketcan-kernel-module/issues/20) tracks this issue.
//! - comma.ai panda
//! - The panda does not retry frames that are not ACKed, and drops them instead. This can cause panics in some internal parts of the library when frames are dropped. [panda#1922](https://github.com/commaai/panda/issues/1922) tracks this issue.
//! - The CAN-FD flag on a frame is ignored, if the hardware is configured for CAN-FD all frames will be interpreted as FD regardless of the FD frame bit (r0 bit).
//! - Vector Devices are supported through the Vector XL Driver Library, and support can be enabled using the `vector-xl` feature. Make sure to distribute `vxlapi64.dll` alongside your application.
//!
//!
Expand All @@ -73,7 +72,6 @@
//! - The hardware or driver is free to prioritize sending frames with a lower Arbitration ID to prevent priority inversion. However frames with the same Arbitration ID need to be send out on the CAN bus in the same order as they were queued. This assumption is needed to match a received ACK to the correct frame.
//! - Once a frame is ACKed it should be put in the receive queue with the `loopback` flag set. The `AsyncCanAdapter` wrapper will take care of matching it against the right transmit frame and resolving the Future. If this is not supported by the underlying hardware, this can be faked by looping back all transmitted frames immediately.

pub mod can;
mod error;
pub mod isotp;
Expand Down
1 change: 1 addition & 0 deletions src/panda/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ pub enum Endpoint {
CanRead = 0x81,
PacketsVersions = 0xdd,
PowerSave = 0xe7,
CanFDAuto = 0xe8,
HeartbeatDisabled = 0xf8,
}

Expand Down
12 changes: 12 additions & 0 deletions src/panda/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const VENDOR_ID: u16 = 0xbbaa;
const PRODUCT_ID: u16 = 0xddcc;
const EXPECTED_CAN_PACKET_VERSION: u8 = 4;
const MAX_BULK_SIZE: usize = 16384;
const PANDA_BUS_CNT: usize = 3;

/// Blocking implementation of the panda CAN adapter
pub struct Panda {
Expand Down Expand Up @@ -73,6 +74,10 @@ impl Panda {
panda.set_heartbeat_disabled()?;
panda.can_reset_communications()?;

for i in 0..PANDA_BUS_CNT {
panda.set_canfd_auto(i, false)?;
}

// can_reset_communications() doesn't work properly, flush manually
panda.flush_rx()?;

Expand Down Expand Up @@ -113,6 +118,13 @@ impl Panda {
self.usb_write_control(Endpoint::PowerSave, power_save_enabled as u16, 0)
}

fn set_canfd_auto(&self, bus: usize, auto: bool) -> Result<()> {
if bus >= PANDA_BUS_CNT {
return Err(crate::Error::NotSupported);
}
self.usb_write_control(Endpoint::CanFDAuto, bus as u16, auto as u16)
}

/// Get the hardware type of the panda. Usefull to detect if it supports CAN-FD.
pub fn get_hw_type(&self) -> Result<HwType> {
let hw_type = self.usb_read_control(Endpoint::HwType, 1)?;
Expand Down
20 changes: 11 additions & 9 deletions src/panda/usb_protocol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const CANPACKET_MAX_CHUNK_SIZE: usize = 256;
// Header layout

// byte 0
// unsigned char reserved : 1;
// unsigned char fd : 1;
// unsigned char bus : 3;
// unsigned char data_len_code : 4; // lookup length with dlc_to_len

Expand Down Expand Up @@ -45,17 +45,15 @@ pub fn pack_can_buffer(frames: &[Frame]) -> Result<Vec<Vec<u8>>, Error> {
return Err(Error::MalformedFrame);
}

if frame.fd {
return Err(Error::NotSupported);
}
let fd = frame.fd as u8;

let dlc = DLC_TO_LEN.iter().position(|&x| x == frame.data.len());
let dlc = dlc.ok_or(Error::MalformedFrame)? as u8;

let word_4b: u32 = (id << 3) | (extended << 2);

let header: [u8; CANPACKET_HEAD_SIZE - 1] = [
(dlc << 4) | (frame.bus << 1),
(dlc << 4) | (frame.bus << 1) | fd,
(word_4b & 0xff) as u8,
((word_4b >> 8) & 0xff) as u8,
((word_4b >> 16) & 0xff) as u8,
Expand All @@ -82,6 +80,7 @@ pub fn unpack_can_buffer(dat: &mut Vec<u8>) -> Result<Vec<Frame>, Error> {
while dat.len() >= CANPACKET_HEAD_SIZE {
let bus = (dat[0] >> 1) & 0b111;
let dlc = (dat[0] >> 4) & 0b1111;
let fd = (dat[0] & 0b1) != 0;
let id: u32 = ((dat[4] as u32) << 24
| (dat[3] as u32) << 16
| (dat[2] as u32) << 8
Expand Down Expand Up @@ -113,10 +112,6 @@ pub fn unpack_can_buffer(dat: &mut Vec<u8>) -> Result<Vec<Frame>, Error> {
));
}

// Panda doesn't properly communicate if a frame was FD or not.
// We'll assume it was FD when the data length is > 8.
let fd = data_len > 8;

ret.push(Frame {
id,
bus,
Expand Down Expand Up @@ -187,6 +182,13 @@ mod tests {
loopback: false,
fd: false,
},
Frame {
bus: 1,
id: Identifier::Extended(0x123),
data: vec![1, 2, 3, 4],
loopback: false,
fd: true,
},
];

let buffer = pack_can_buffer(&frames).unwrap();
Expand Down

0 comments on commit 73b0236

Please sign in to comment.