diff --git a/src/can/mod.rs b/src/can/mod.rs index faac40e..b38b57b 100644 --- a/src/can/mod.rs +++ b/src/can/mod.rs @@ -2,12 +2,14 @@ pub mod adapter; pub mod async_can; +pub mod timing; use std::collections::VecDeque; use std::fmt; pub use adapter::get_adapter; pub use async_can::AsyncCanAdapter; +pub use timing::{BitTiming, TimingConfig}; pub static DLC_TO_LEN: &[usize] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 12, 16, 20, 24, 32, 48, 64]; @@ -127,6 +129,11 @@ impl fmt::Debug for Frame { pub trait CanAdapter { fn send(&mut self, frames: &mut VecDeque) -> crate::Result<()>; fn recv(&mut self) -> crate::Result>; + fn config_timing( + &mut self, + bus: usize, + config: &crate::can::timing::TimingConfig, + ) -> crate::Result<()>; } #[cfg(test)] diff --git a/src/can/timing.rs b/src/can/timing.rs new file mode 100644 index 0000000..748923e --- /dev/null +++ b/src/can/timing.rs @@ -0,0 +1,28 @@ +const DEFAULT_BITRATE: u32 = 500_000; +const DEFAULT_DBITRATE: u32 = 2_000_000; // SAE J2284-4 +const DEFAULT_SAMPLE_POINT: f32 = 0.8; // SAE J2284-4 and SAE J2284-5 + +pub struct TimingConfig { + pub classic: BitTiming, + pub fd: Option, +} + +pub struct BitTiming { + pub bitrate: u32, + pub sample_point: f32, +} + +impl Default for TimingConfig { + fn default() -> Self { + TimingConfig { + classic: BitTiming { + bitrate: DEFAULT_BITRATE, + sample_point: DEFAULT_SAMPLE_POINT, + }, + fd: Some(BitTiming { + bitrate: DEFAULT_DBITRATE, + sample_point: DEFAULT_SAMPLE_POINT, + }), + } + } +} diff --git a/src/panda/constants.rs b/src/panda/constants.rs index 6cb56e7..1cea052 100644 --- a/src/panda/constants.rs +++ b/src/panda/constants.rs @@ -21,6 +21,8 @@ pub enum Endpoint { CanWrite = 0x3, HwType = 0xc1, SafetyModel = 0xdc, + CanSpeed = 0xde, + CanDataSpeed = 0xf9, CanResetCommunications = 0xc0, CanRead = 0x81, PacketsVersions = 0xdd, @@ -35,3 +37,10 @@ pub enum SafetyModel { Silent = 0, AllOutput = 17, } + +pub const FD_PANDAS: [HwType; 4] = [ + HwType::RedPanda, + HwType::RedPandaV2, + HwType::Tres, + HwType::Quatro, +]; diff --git a/src/panda/mod.rs b/src/panda/mod.rs index c5e5ce5..dfaeee7 100644 --- a/src/panda/mod.rs +++ b/src/panda/mod.rs @@ -10,7 +10,8 @@ use std::collections::VecDeque; use crate::can::AsyncCanAdapter; use crate::can::CanAdapter; use crate::can::Frame; -use crate::panda::constants::{Endpoint, HwType, SafetyModel}; +use crate::can::TimingConfig; +use crate::panda::constants::{Endpoint, HwType, SafetyModel, FD_PANDAS}; use crate::Result; use tracing::{info, warn}; @@ -19,6 +20,7 @@ const PRODUCT_ID: u16 = 0xddcc; const EXPECTED_CAN_PACKET_VERSION: u8 = 4; const MAX_BULK_SIZE: usize = 16384; const PANDA_BUS_CNT: usize = 3; +const PANDA_DEFAULT_SAMPLE_POINT: f32 = 0.8; // SAE J2284-4 and SAE J2284-5 /// Blocking implementation of the panda CAN adapter pub struct Panda { @@ -55,7 +57,7 @@ impl Panda { continue; } - let panda = Panda { + let mut panda = Panda { dat: vec![], handle: device.open()?, timeout: std::time::Duration::from_millis(100), @@ -74,8 +76,11 @@ impl Panda { panda.set_heartbeat_disabled()?; panda.can_reset_communications()?; + let config = TimingConfig::default(); + for i in 0..PANDA_BUS_CNT { panda.set_canfd_auto(i, false)?; + panda.config_timing(i, &config)?; } // can_reset_communications() doesn't work properly, flush manually @@ -125,6 +130,19 @@ impl Panda { self.usb_write_control(Endpoint::CanFDAuto, bus as u16, auto as u16) } + fn set_can_speed_kbps(&self, bus: usize, speed_kbps: u32) -> Result<()> { + self.usb_write_control(Endpoint::CanSpeed, bus as u16, (speed_kbps * 10) as u16) + } + + fn set_can_data_speed_kbps(&self, bus: usize, speed_kbps: u32) -> Result<()> { + self.usb_write_control(Endpoint::CanDataSpeed, bus as u16, (speed_kbps * 10) as u16) + } + + fn supports_fd(&self) -> Result { + let hw_type = self.get_hw_type()?; + Ok(FD_PANDAS.contains(&hw_type)) + } + /// Get the hardware type of the panda. Usefull to detect if it supports CAN-FD. pub fn get_hw_type(&self) -> Result { let hw_type = self.usb_read_control(Endpoint::HwType, 1)?; @@ -217,4 +235,24 @@ impl CanAdapter for Panda { } } } + + fn config_timing(&mut self, bus: usize, config: &TimingConfig) -> Result<()> { + self.set_can_speed_kbps(bus, config.classic.bitrate / 1000)?; + if config.classic.sample_point != PANDA_DEFAULT_SAMPLE_POINT { + warn!("Setting sample point is not supported on Panda, ignoring"); + } + + if let Some(fd) = &config.fd { + if !self.supports_fd()? { + warn!("CAN-FD not supported by adapter"); + } else { + self.set_can_data_speed_kbps(bus, fd.bitrate / 1000)?; + if fd.sample_point != PANDA_DEFAULT_SAMPLE_POINT { + warn!("Setting sample point is not supported on Panda, ignoring"); + } + } + } + + Ok(()) + } } diff --git a/src/socketcan/mod.rs b/src/socketcan/mod.rs index 53fe9cf..b7f4415 100644 --- a/src/socketcan/mod.rs +++ b/src/socketcan/mod.rs @@ -1,4 +1,6 @@ //! This module provides a [`CanAdapter`] implementation for SocketCAN interfaces +use tracing::warn; + use crate::can::{AsyncCanAdapter, CanAdapter, Frame}; use crate::socketcan::socket::CanFdSocket; use crate::Result; @@ -121,4 +123,14 @@ impl CanAdapter for SocketCan { Ok(frames) } + + fn config_timing( + &mut self, + _bus: usize, + _config: &crate::can::TimingConfig, + ) -> crate::Result<()> { + warn!("Ignoring timing config. Set using ip link"); + // TODO: Report error instead? + Ok(()) + } } diff --git a/src/vector/mod.rs b/src/vector/mod.rs index a8b55c9..667065b 100644 --- a/src/vector/mod.rs +++ b/src/vector/mod.rs @@ -91,4 +91,13 @@ impl CanAdapter for VectorCan { Ok(frames) } + + fn config_timing( + &mut self, + _bus: usize, + _config: &crate::can::TimingConfig, + ) -> crate::Result<()> { + todo!("No yet implemented"); + Ok(()) + } }