peak_can/socket/
usb.rs

1//!
2//!
3//!
4
5use std::ffi::CString;
6
7use crate::bus::UsbBus;
8use crate::channel::Channel;
9use crate::df::{
10    HasAcceptanceFilter11Bit, HasAcceptanceFilter29Bit, HasAllowEchoFrames, HasAllowErrorFrames,
11    HasAllowRTRFrames, HasAllowStatusFrames, HasMessageFilter, HasReceiveStatus,
12    HasSetAcceptanceFilter11Bit, HasSetAcceptanceFilter29Bit, HasSetAllowEchoFrames,
13    HasSetAllowErrorFrames, HasSetAllowRTRFrames, HasSetAllowStatusFrames, HasSetMessageFilter,
14    HasSetReceiveStatus,
15};
16use crate::error::{CanError, CanOkError};
17use crate::hw::{
18    HasChannelIdentifying, HasControllerNumber, HasDeviceId, HasDevicePartNumber, HasHardwareName,
19    HasSetControllerNumber, HasSetDeviceId,
20};
21use crate::info::{
22    HasBitrateInfo, HasChannelFeatures, HasChannelVersion, HasDataBusSpeed, HasFirmwareVersion,
23    HasNominalBusSpeed,
24};
25use crate::io::{
26    HasAnalogValue, HasDigitalConfiguration, HasDigitalValue, HasSetDigitalClear,
27    HasSetDigitalConfiguration, HasSetDigitalSet, HasSetDigitalValue,
28};
29use crate::peak_lib;
30use crate::socket::{Baudrate, CanBitTiming, CanFdBitTiming, HasRecvCan, HasRecvCanFd, HasSendCan, HasSendCanFd, Socket};
31use crate::special::{
32    HasBusOffAutoreset, HasFiveVoltsPower, HasInterframeDelay, HasListenOnly,
33    HasSetBusOffAutoreset, HasSetFiveVoltsPower, HasSetInterframeDelay, HasSetListenOnly,
34};
35use crate::trace::{
36    HasSetTraceConfigure, HasSetTraceLocation, HasSetTraceSize, HasSetTraceStatus,
37    HasTraceConfigure, HasTraceLocation, HasTraceSize, HasTraceStatus,
38};
39
40/// CAN FD controller clock frequency in Hz for PEAK USB devices.
41/// 
42/// This represents the base clock frequency (80 MHz) used by the CAN FD controller
43/// on PEAK USB hardware. This value is used when configuring custom bit timing
44/// parameters for CAN FD communication.
45const CANFD_CLOCK_HZ: u32 = 80_000_000;
46
47/// Helper function to calculate BTR0BTR1 value
48fn calculate_btr0btr1(timing: &CanBitTiming) -> u16 {
49    ((((timing.tseg2 - 1) & 0x07) as u16) << 4)
50        | (((timing.tseg1 - 1) & 0x0F) as u16)
51        | ((((timing.prescaler - 1) & 0x3F) as u16) << 8)
52        | ((((timing.sjw - 1) & 0x03) as u16) << 14)
53}
54
55/// Helper function to build timing string
56fn build_timing_string(timing: &CanFdBitTiming) -> String {
57    format!(
58        "f_clock={},nom_brp={},nom_tseg1={},nom_tseg2={},nom_sjw={},data_brp={},data_tseg1={},data_tseg2={},data_sjw={}",
59        CANFD_CLOCK_HZ,
60        timing.nom_prescaler,
61        timing.nom_tseg1,
62        timing.nom_tseg2,
63        timing.nom_sjw,
64        timing.data_prescaler,
65        timing.data_tseg1,
66        timing.data_tseg2,
67        timing.data_sjw,
68    )
69}
70
71#[derive(Debug, PartialEq)]
72pub struct UsbCanSocket {
73    handle: u16,
74}
75
76impl UsbCanSocket {
77    /// Opens a CAN socket with a standard baudrate.
78    ///
79    /// # Arguments
80    ///
81    /// * `bus` - USB bus channel (USB1-USB16)
82    /// * `baud` - Standard baudrate (e.g., Baud125K, Baud500K)
83    ///
84    /// # Examples
85    ///
86    /// ```no_run
87    /// # use peak_can::socket::usb::UsbCanSocket;
88    /// # use peak_can::socket::Baudrate;
89    /// # use peak_can::bus::UsbBus;
90    /// let socket = UsbCanSocket::open(UsbBus::USB1, Baudrate::Baud500K)?;
91    /// # Ok::<(), peak_can::error::CanError>(())
92    /// ```
93    pub fn open(bus: UsbBus, baud: Baudrate) -> Result<UsbCanSocket, CanError> {
94        let handle = bus.into();
95        let code = unsafe { peak_lib()?.CAN_Initialize(handle, baud.into(), 0, 0, 0) };
96
97        match CanOkError::try_from(code) {
98            Ok(CanOkError::Ok) => Ok(UsbCanSocket { handle }),
99            Ok(CanOkError::Err(err)) => Err(err),
100            Err(_) => Err(CanError::Unknown),
101        }
102    }
103
104    pub fn open_with_usb_bus(bus: UsbBus) -> UsbCanSocket {
105        let handle = bus.into();
106        UsbCanSocket { handle }
107    }
108
109    /// Opens a CAN socket with custom bit timing.
110    ///
111    /// Use [`CAN_TIMING_BOUNDARIES`](crate::socket::CAN_TIMING_BOUNDARIES) for valid ranges.
112    ///
113    /// # Examples
114    ///
115    /// ```no_run
116    /// # use peak_can::socket::usb::UsbCanSocket;
117    /// # use peak_can::socket::CanBitTiming;
118    /// # use peak_can::bus::UsbBus;
119    /// let timing = CanBitTiming::new(8, 1, 13, 2)?;  // 500 kbit/s
120    /// let socket = UsbCanSocket::open_with_timing(UsbBus::USB1, &timing)?;
121    /// # Ok::<(), Box<dyn std::error::Error>>(())
122    /// ```
123    pub fn open_with_timing(bus: UsbBus, timing: &CanBitTiming) -> Result<UsbCanSocket, CanError> {
124        let handle = bus.into();
125        let btr0btr1 = calculate_btr0btr1(timing);
126        let code = unsafe { peak_lib()?.CAN_Initialize(handle, btr0btr1, 0, 0, 0) };
127
128        match CanOkError::try_from(code) {
129            Ok(CanOkError::Ok) => Ok(UsbCanSocket { handle }),
130            Ok(CanOkError::Err(err)) => Err(err),
131            Err(_) => Err(CanError::Unknown),
132        }
133    }
134
135    /// Opens a CAN FD socket with custom timing for nominal and data phases.
136    ///
137    /// # Examples
138    ///
139    /// ```no_run
140    /// # use peak_can::socket::usb::UsbCanSocket;
141    /// # use peak_can::socket::CanFdBitTiming;
142    /// # use peak_can::bus::UsbBus;
143    /// let timing = CanFdBitTiming::new(
144    ///     10, 4, 13, 2,  // Nominal phase
145    ///     5, 2, 6, 1     // Data phase
146    /// )?;
147    /// let socket = UsbCanSocket::open_fd_with_timing(UsbBus::USB1, &timing)?;
148    /// # Ok::<(), Box<dyn std::error::Error>>(())
149    /// ```
150    pub fn open_fd_with_timing(bus: UsbBus, timing: &CanFdBitTiming) -> Result<UsbCanSocket, CanError> {
151        let handle = bus.into();
152        let timing_str = build_timing_string(timing);
153
154        let mut timing_bytes = CString::new(timing_str)
155            .map_err(|_| CanError::Unknown)?
156            .into_bytes_with_nul();
157
158        let code = unsafe { peak_lib()?.CAN_InitializeFD(handle, timing_bytes.as_mut_ptr().cast()) };
159
160        match CanOkError::try_from(code) {
161            Ok(CanOkError::Ok) => Ok(UsbCanSocket { handle }),
162            Ok(CanOkError::Err(err)) => Err(err),
163            Err(_) => Err(CanError::Unknown),
164        }
165    }
166}
167
168/* Drop trait implementation */
169
170impl Drop for UsbCanSocket {
171    fn drop(&mut self) {
172        let Ok(peak_lib) = peak_lib() else {
173            return;
174        };
175        unsafe { peak_lib.CAN_Uninitialize(self.handle) };
176    }
177}
178
179/* Socket trait implementation */
180
181impl Socket for UsbCanSocket {
182    fn handle(&self) -> u16 {
183        self.handle
184    }
185}
186
187/* Channel trait implementation */
188
189impl Channel for UsbCanSocket {
190    fn channel(&self) -> u16 {
191        self.handle
192    }
193}
194
195/* CAN trait implementations */
196
197impl HasRecvCan for UsbCanSocket {}
198impl HasSendCan for UsbCanSocket {}
199
200impl HasRecvCanFd for UsbCanSocket {}
201impl HasSendCanFd for UsbCanSocket {}
202
203/* HARDWARE IDENTIFICATION */
204
205impl HasChannelIdentifying for UsbCanSocket {}
206
207impl HasDeviceId for UsbCanSocket {}
208impl HasSetDeviceId for UsbCanSocket {}
209
210impl HasHardwareName for UsbCanSocket {}
211
212impl HasControllerNumber for UsbCanSocket {}
213impl HasSetControllerNumber for UsbCanSocket {}
214
215impl HasDevicePartNumber for UsbCanSocket {}
216
217/* INFORMATIONAL PARAMETER */
218
219impl HasChannelVersion for UsbCanSocket {}
220
221impl HasChannelFeatures for UsbCanSocket {}
222
223impl HasBitrateInfo for UsbCanSocket {}
224
225impl HasNominalBusSpeed for UsbCanSocket {}
226
227impl HasDataBusSpeed for UsbCanSocket {}
228
229impl HasFirmwareVersion for UsbCanSocket {}
230
231/* SPECIAL BEHAVIOR */
232
233impl HasFiveVoltsPower for UsbCanSocket {}
234impl HasSetFiveVoltsPower for UsbCanSocket {}
235
236impl HasBusOffAutoreset for UsbCanSocket {}
237impl HasSetBusOffAutoreset for UsbCanSocket {}
238
239impl HasListenOnly for UsbCanSocket {}
240impl HasSetListenOnly for UsbCanSocket {}
241
242impl HasInterframeDelay for UsbCanSocket {}
243impl HasSetInterframeDelay for UsbCanSocket {}
244
245/* CONTROLLING DATA FLOW */
246
247impl HasMessageFilter for UsbCanSocket {}
248impl HasSetMessageFilter for UsbCanSocket {}
249
250impl HasReceiveStatus for UsbCanSocket {}
251impl HasSetReceiveStatus for UsbCanSocket {}
252
253impl HasAllowStatusFrames for UsbCanSocket {}
254impl HasSetAllowStatusFrames for UsbCanSocket {}
255
256impl HasAllowRTRFrames for UsbCanSocket {}
257impl HasSetAllowRTRFrames for UsbCanSocket {}
258
259impl HasAllowErrorFrames for UsbCanSocket {}
260impl HasSetAllowErrorFrames for UsbCanSocket {}
261
262impl HasAllowEchoFrames for UsbCanSocket {}
263impl HasSetAllowEchoFrames for UsbCanSocket {}
264
265impl HasAcceptanceFilter11Bit for UsbCanSocket {}
266impl HasSetAcceptanceFilter11Bit for UsbCanSocket {}
267
268impl HasAcceptanceFilter29Bit for UsbCanSocket {}
269impl HasSetAcceptanceFilter29Bit for UsbCanSocket {}
270
271/* TRACING PARAMETERS */
272
273impl HasTraceLocation for UsbCanSocket {}
274impl HasSetTraceLocation for UsbCanSocket {}
275
276impl HasTraceStatus for UsbCanSocket {}
277impl HasSetTraceStatus for UsbCanSocket {}
278
279impl HasTraceSize for UsbCanSocket {}
280impl HasSetTraceSize for UsbCanSocket {}
281
282impl HasTraceConfigure for UsbCanSocket {}
283impl HasSetTraceConfigure for UsbCanSocket {}
284
285/* ELECTRONIC CIRCUITS PARAMETERS */
286
287impl HasDigitalConfiguration for UsbCanSocket {}
288impl HasSetDigitalConfiguration for UsbCanSocket {}
289
290impl HasDigitalValue for UsbCanSocket {}
291impl HasSetDigitalValue for UsbCanSocket {}
292
293impl HasSetDigitalSet for UsbCanSocket {}
294
295impl HasSetDigitalClear for UsbCanSocket {}
296
297impl HasAnalogValue for UsbCanSocket {}
298#[cfg(test)]
299mod tests {
300    use super::*;
301
302    #[test]
303    fn btr0btr1_encoding() {
304        // Test boundary values and common configurations
305        let test_cases = vec![
306            ((1, 1, 1, 1), 0x0000),      // Minimum values
307            ((64, 4, 16, 8), 0xFF7F),    // Maximum values
308            ((8, 1, 13, 2), 0x071C),     // 500k config
309            ((32, 1, 13, 2), 0x1F1C),    // 125k config
310            ((4, 1, 13, 2), 0x031C),     // 1M config
311        ];
312
313        for ((prescaler, sjw, tseg1, tseg2), expected) in test_cases {
314            let timing = CanBitTiming::new(prescaler, sjw, tseg1, tseg2).unwrap();
315            let btr0btr1 = calculate_btr0btr1(&timing);
316            assert_eq!(btr0btr1, expected, 
317                "Failed for prescaler={}, sjw={}, tseg1={}, tseg2={}", 
318                prescaler, sjw, tseg1, tseg2);
319        }
320
321        // Verify bit field masking doesn't overflow
322        let timing = CanBitTiming::new(64, 4, 16, 8).unwrap();
323        let btr0btr1 = calculate_btr0btr1(&timing);
324        assert_eq!(btr0btr1 & 0x000F, 15);  // tseg1
325        assert_eq!((btr0btr1 >> 4) & 0x07, 7);  // tseg2
326        assert_eq!((btr0btr1 >> 8) & 0x3F, 63);  // prescaler
327        assert_eq!((btr0btr1 >> 14) & 0x03, 3);  // sjw
328    }
329
330    #[test]
331    fn fd_timing_string_format() {
332        // Test boundary values and common configurations
333        let test_cases = vec![
334            (
335                (1, 1, 1, 1, 1, 1, 1, 1),
336                "f_clock=80000000,nom_brp=1,nom_tseg1=1,nom_tseg2=1,nom_sjw=1,data_brp=1,data_tseg1=1,data_tseg2=1,data_sjw=1"
337            ),
338            (
339                (1024, 128, 256, 128, 1024, 16, 32, 16),
340                "f_clock=80000000,nom_brp=1024,nom_tseg1=256,nom_tseg2=128,nom_sjw=128,data_brp=1024,data_tseg1=32,data_tseg2=16,data_sjw=16"
341            ),
342            (
343                (10, 4, 13, 2, 5, 2, 6, 1),  // 500k nominal / 1M data
344                "f_clock=80000000,nom_brp=10,nom_tseg1=13,nom_tseg2=2,nom_sjw=4,data_brp=5,data_tseg1=6,data_tseg2=1,data_sjw=2"
345            ),
346            (
347                (5, 2, 13, 2, 2, 2, 13, 2),  // 1M nominal / 2M data
348                "f_clock=80000000,nom_brp=5,nom_tseg1=13,nom_tseg2=2,nom_sjw=2,data_brp=2,data_tseg1=13,data_tseg2=2,data_sjw=2"
349            ),
350        ];
351
352        for ((nom_brp, nom_sjw, nom_tseg1, nom_tseg2, data_brp, data_sjw, data_tseg1, data_tseg2), expected) in test_cases {
353            let timing = CanFdBitTiming::new(nom_brp, nom_sjw, nom_tseg1, nom_tseg2, data_brp, data_sjw, data_tseg1, data_tseg2).unwrap();
354            let actual = build_timing_string(&timing);
355            assert_eq!(actual, expected);
356        }
357    }
358
359    #[test]
360    fn fd_timing_string_structure() {
361        let timing = CanFdBitTiming::new(10, 4, 13, 2, 5, 2, 6, 1).unwrap();
362        let timing_str = build_timing_string(&timing);
363        
364        // Verify structure: 9 parameters, no spaces, proper delimiters
365        assert_eq!(timing_str.matches(',').count(), 8);
366        assert!(!timing_str.contains(' '));
367        assert!(timing_str.starts_with("f_clock=80000000,"));
368        assert!(timing_str.ends_with("data_sjw=2"));
369        assert!(!timing_str.contains(|c: char| c.is_control()));
370        
371        // Verify parameter order
372        let parts: Vec<&str> = timing_str.split(',').collect();
373        assert_eq!(parts.len(), 9);
374        assert!(parts[0].starts_with("f_clock="));
375        assert!(parts[1].starts_with("nom_brp="));
376        assert!(parts[4].starts_with("nom_sjw="));
377        assert!(parts[5].starts_with("data_brp="));
378        assert!(parts[8].starts_with("data_sjw="));
379    }
380}