@@ -7,9 +7,8 @@ mod usb_protocol;
77pub use error:: Error ;
88use std:: collections:: VecDeque ;
99
10- use crate :: can:: AsyncCanAdapter ;
11- use crate :: can:: CanAdapter ;
12- use crate :: can:: Frame ;
10+ use crate :: can:: bitrate:: { AdapterTimingConst , BitTimingConst , BitrateConfig } ;
11+ use crate :: can:: { AsyncCanAdapter , CanAdapter , Frame } ;
1312use crate :: panda:: constants:: { Endpoint , HwType , SafetyModel } ;
1413use crate :: Result ;
1514use tracing:: { info, warn} ;
@@ -19,6 +18,34 @@ const USB_PIDS: &[u16] = &[0xddee, 0xddcc];
1918const EXPECTED_CAN_PACKET_VERSION : u8 = 4 ;
2019const MAX_BULK_SIZE : usize = 16384 ;
2120const PANDA_BUS_CNT : usize = 3 ;
21+ const PANDA_NOMINAL_SAMPLE_POINT : f64 = 0.8 ;
22+ const SUPPORTED_CAN_BITRATES_BPS : & [ u32 ] = & [
23+ 10_000 , 20_000 , 50_000 , 100_000 , 125_000 , 250_000 , 500_000 , 1_000_000 ,
24+ ] ;
25+ const SUPPORTED_CAN_DATA_BITRATES_BPS : & [ u32 ] = & [
26+ 10_000 , 20_000 , 50_000 , 100_000 , 125_000 , 250_000 , 500_000 , 1_000_000 , 2_000_000 , 5_000_000 ,
27+ ] ;
28+ const PANDA_BIT_TIMING_CONST : BitTimingConst = BitTimingConst {
29+ clock_hz : 80_000_000 ,
30+ tseg1_min : 1 ,
31+ tseg1_max : 1 << 8 ,
32+ tseg2_min : 1 ,
33+ tseg2_max : 1 << 7 ,
34+ sjw_max : 1 << 7 ,
35+ brp_min : 1 ,
36+ brp_max : 1 << 10 ,
37+ brp_inc : 1 ,
38+ } ;
39+ const PANDA_TIMING_CONST : AdapterTimingConst = AdapterTimingConst {
40+ nominal : PANDA_BIT_TIMING_CONST ,
41+ data : Some ( PANDA_BIT_TIMING_CONST ) ,
42+ } ;
43+
44+ #[ derive( Debug , Clone , Copy , PartialEq , Eq ) ]
45+ struct PandaBitrateConfig {
46+ nominal_kbps : u16 ,
47+ data_kbps : Option < u16 > ,
48+ }
2249
2350/// Blocking implementation of the panda CAN adapter
2451pub struct Panda {
@@ -38,13 +65,16 @@ unsafe impl Send for Panda {}
3865
3966impl Panda {
4067 /// Convenience function to create a new panda adapter and wrap in an [`AsyncCanAdapter`]
41- pub fn new_async ( ) -> Result < AsyncCanAdapter > {
42- let panda = Panda :: new ( ) ?;
68+ pub fn new_async ( bitrate_cfg : BitrateConfig ) -> Result < AsyncCanAdapter > {
69+ let panda = Panda :: new ( bitrate_cfg ) ?;
4370 Ok ( AsyncCanAdapter :: new ( panda) )
4471 }
4572
4673 /// Connect to the first available panda. This function will set the safety mode to ALL_OUTPUT and clear all buffers.
47- pub fn new ( ) -> Result < Panda > {
74+ pub fn new ( bitrate_cfg : BitrateConfig ) -> Result < Panda > {
75+ let resolved_bitrate_cfg = resolve_bitrate_config ( & bitrate_cfg) ?;
76+ warn_if_non_default_sample_points ( & bitrate_cfg) ;
77+
4878 for device in rusb:: devices ( ) . unwrap ( ) . iter ( ) {
4979 let device_desc = device. device_descriptor ( ) . unwrap ( ) ;
5080
@@ -69,19 +99,25 @@ impl Panda {
6999 return Err ( Error :: WrongFirmwareVersion . into ( ) ) ;
70100 }
71101
102+ let hw_type = panda. get_hw_type ( ) ?;
103+ warn_if_fd_unsupported ( hw_type, resolved_bitrate_cfg. data_kbps . is_some ( ) ) ;
104+
72105 panda. set_safety_model ( SafetyModel :: AllOutput ) ?;
73106 panda. set_power_save ( false ) ?;
74107 panda. set_heartbeat_disabled ( ) ?;
75108 panda. can_reset_communications ( ) ?;
76109
77110 for i in 0 ..PANDA_BUS_CNT {
111+ panda. set_can_speed_kbps ( i, resolved_bitrate_cfg. nominal_kbps ) ?;
112+ if let Some ( data_kbps) = resolved_bitrate_cfg. data_kbps {
113+ panda. set_can_data_speed_kbps ( i, data_kbps) ?;
114+ }
78115 panda. set_canfd_auto ( i, false ) ?;
79116 }
80117
81118 // can_reset_communications() doesn't work properly, flush manually
82119 panda. flush_rx ( ) ?;
83120
84- let hw_type = panda. get_hw_type ( ) ?;
85121 info ! ( "Connected to Panda ({:?})" , hw_type) ;
86122
87123 return Ok ( panda) ;
@@ -125,6 +161,20 @@ impl Panda {
125161 self . usb_write_control ( Endpoint :: CanFDAuto , bus as u16 , auto as u16 )
126162 }
127163
164+ fn set_can_speed_kbps ( & self , bus : usize , speed_kbps : u16 ) -> Result < ( ) > {
165+ if bus >= PANDA_BUS_CNT {
166+ return Err ( crate :: Error :: NotSupported ) ;
167+ }
168+ self . usb_write_control ( Endpoint :: CanSpeed , bus as u16 , speed_kbps * 10 )
169+ }
170+
171+ fn set_can_data_speed_kbps ( & self , bus : usize , speed_kbps : u16 ) -> Result < ( ) > {
172+ if bus >= PANDA_BUS_CNT {
173+ return Err ( crate :: Error :: NotSupported ) ;
174+ }
175+ self . usb_write_control ( Endpoint :: CanDataSpeed , bus as u16 , speed_kbps * 10 )
176+ }
177+
128178 /// Get the hardware type of the panda. Usefull to detect if it supports CAN-FD.
129179 pub fn get_hw_type ( & self ) -> Result < HwType > {
130180 let hw_type = self . usb_read_control ( Endpoint :: HwType , 1 ) ?;
@@ -217,4 +267,98 @@ impl CanAdapter for Panda {
217267 }
218268 }
219269 }
270+
271+ fn timing_const ( ) -> AdapterTimingConst
272+ where
273+ Self : Sized ,
274+ {
275+ PANDA_TIMING_CONST
276+ }
277+ }
278+
279+ fn resolve_bitrate_config ( bitrate_cfg : & BitrateConfig ) -> Result < PandaBitrateConfig > {
280+ Ok ( PandaBitrateConfig {
281+ nominal_kbps : validate_supported_bitrate (
282+ bitrate_cfg. nominal . bitrate ,
283+ SUPPORTED_CAN_BITRATES_BPS ,
284+ "Panda arbitration bitrate" ,
285+ ) ?,
286+ data_kbps : bitrate_cfg
287+ . data
288+ . map ( |data| {
289+ validate_supported_bitrate (
290+ data. bitrate ,
291+ SUPPORTED_CAN_DATA_BITRATES_BPS ,
292+ "Panda data bitrate" ,
293+ )
294+ } )
295+ . transpose ( ) ?,
296+ } )
297+ }
298+
299+ fn validate_supported_bitrate ( bitrate_bps : u32 , supported : & [ u32 ] , label : & str ) -> Result < u16 > {
300+ if !supported. contains ( & bitrate_bps) {
301+ return Err ( crate :: Error :: InvalidBitrate ( format ! (
302+ "{label} {bitrate_bps} bps is unsupported; expected one of {supported:?}"
303+ ) ) ) ;
304+ }
305+
306+ Ok ( ( bitrate_bps / 1000 ) as u16 )
307+ }
308+
309+ fn warn_if_non_default_sample_points ( bitrate_cfg : & BitrateConfig ) {
310+ if !sample_point_matches ( bitrate_cfg. nominal . sample_point , PANDA_NOMINAL_SAMPLE_POINT ) {
311+ warn ! (
312+ bitrate = bitrate_cfg. nominal. bitrate,
313+ requested_sample_point = bitrate_cfg. nominal. sample_point,
314+ panda_sample_point = PANDA_NOMINAL_SAMPLE_POINT ,
315+ "Panda ignores the requested nominal sample point"
316+ ) ;
317+ }
318+
319+ if let Some ( data) = bitrate_cfg. data {
320+ let panda_sample_point = default_data_sample_point ( data. bitrate ) ;
321+ if !sample_point_matches ( data. sample_point , panda_sample_point) {
322+ warn ! (
323+ bitrate = data. bitrate,
324+ requested_sample_point = data. sample_point,
325+ panda_sample_point,
326+ "Panda ignores the requested CAN-FD data sample point"
327+ ) ;
328+ }
329+ }
330+ }
331+
332+ fn warn_if_fd_unsupported ( hw_type : HwType , using_fd_bitrate : bool ) {
333+ if using_fd_bitrate && !supports_can_fd ( hw_type) {
334+ warn ! (
335+ ?hw_type,
336+ "Configured CAN-FD bitrate on panda hardware that does not support CAN-FD"
337+ ) ;
338+ }
339+ }
340+
341+ fn supports_can_fd ( hw_type : HwType ) -> bool {
342+ !matches ! (
343+ hw_type,
344+ HwType :: Unknown
345+ | HwType :: WhitePanda
346+ | HwType :: GreyPanda
347+ | HwType :: BlackPanda
348+ | HwType :: Pedal
349+ | HwType :: Uno
350+ | HwType :: Dos
351+ )
352+ }
353+
354+ fn default_data_sample_point ( data_bitrate : u32 ) -> f64 {
355+ if data_bitrate == 5_000_000 {
356+ 0.75
357+ } else {
358+ 0.8
359+ }
360+ }
361+
362+ fn sample_point_matches ( actual : f64 , expected : f64 ) -> bool {
363+ ( actual - expected) . abs ( ) < 1e-9
220364}
0 commit comments