Skip to main content

agent_can/can/
mod.rs

1mod backend;
2pub mod dbc;
3
4use crate::frame::CanFrame;
5use crate::frame::{
6    CAN_FLAG_BRS, CAN_FLAG_ESI, CAN_FLAG_EXTENDED, CAN_FLAG_FD, CAN_FLAG_RESERVED_MASK,
7    CAN_FLAG_RTR,
8};
9
10#[derive(Debug)]
11pub struct CanSocket {
12    inner: backend::PlatformCanSocket,
13}
14
15impl CanSocket {
16    pub fn open(
17        iface: &str,
18        bitrate: u32,
19        bitrate_data: u32,
20        fd_capable: bool,
21    ) -> Result<Self, String> {
22        Ok(Self {
23            inner: backend::PlatformCanSocket::open(iface, bitrate, bitrate_data, fd_capable)?,
24        })
25    }
26
27    pub fn iface(&self) -> &str {
28        self.inner.iface()
29    }
30
31    pub fn recv_all(&self) -> Result<Vec<CanFrame>, String> {
32        self.inner.recv_all()
33    }
34
35    pub fn send(&self, frame: &CanFrame) -> Result<(), String> {
36        self.inner.send(frame)
37    }
38}
39
40pub fn available_adapters() -> Vec<String> {
41    #[cfg(target_os = "windows")]
42    {
43        let mut adapters = Vec::new();
44        for prefix in ["usb", "pci"] {
45            for idx in 1..=16 {
46                adapters.push(format!("{prefix}{idx}"));
47            }
48        }
49        adapters
50    }
51
52    #[cfg(not(target_os = "windows"))]
53    {
54        Vec::new()
55    }
56}
57
58pub fn parse_data_hex(raw: &str) -> Result<Vec<u8>, String> {
59    let compact = raw
60        .chars()
61        .filter(|ch| !ch.is_whitespace() && *ch != '_')
62        .collect::<String>();
63    if compact.len() % 2 != 0 {
64        return Err(format!(
65            "invalid CAN payload hex '{raw}': expected an even number of hex characters"
66        ));
67    }
68    if compact.len() / 2 > 64 {
69        return Err(format!(
70            "invalid CAN payload hex '{raw}': payload exceeds 64 bytes"
71        ));
72    }
73    let mut payload = Vec::with_capacity(compact.len() / 2);
74    let bytes = compact.as_bytes();
75    let mut idx = 0;
76    while idx < bytes.len() {
77        let pair = format!("{}{}", bytes[idx] as char, bytes[idx + 1] as char);
78        let value = u8::from_str_radix(&pair, 16)
79            .map_err(|_| format!("invalid CAN payload hex '{raw}': bad byte '{pair}'"))?;
80        payload.push(value);
81        idx += 2;
82    }
83    Ok(payload)
84}
85
86pub fn frame_from_payload(arb_id: u32, payload: &[u8]) -> CanFrame {
87    let mut data = [0_u8; 64];
88    data[..payload.len()].copy_from_slice(payload);
89    let mut flags = 0_u8;
90    if arb_id > 0x7FF {
91        flags |= CAN_FLAG_EXTENDED;
92    }
93    if payload.len() > 8 {
94        flags |= CAN_FLAG_FD;
95    }
96    CanFrame {
97        arb_id,
98        len: payload.len() as u8,
99        flags,
100        data,
101    }
102}
103
104pub fn validate_frame(fd_capable: bool, frame: &CanFrame) -> Result<(), String> {
105    if (frame.flags & CAN_FLAG_RESERVED_MASK) != 0 {
106        return Err("CAN frame has reserved flag bits set".to_string());
107    }
108    if (frame.flags & CAN_FLAG_EXTENDED) != 0 {
109        if frame.arb_id > 0x1FFF_FFFF {
110            return Err(format!(
111                "CAN frame has invalid extended arbitration id 0x{:X}",
112                frame.arb_id
113            ));
114        }
115    } else if frame.arb_id > 0x7FF {
116        return Err(format!(
117            "CAN frame has invalid standard arbitration id 0x{:X}",
118            frame.arb_id
119        ));
120    }
121    if frame.len > 64 {
122        return Err(format!(
123            "CAN frame has invalid payload length {}",
124            frame.len
125        ));
126    }
127
128    let fd_requested =
129        (frame.flags & CAN_FLAG_FD) != 0 || (frame.flags & (CAN_FLAG_BRS | CAN_FLAG_ESI)) != 0;
130    if fd_requested {
131        if !fd_capable {
132            return Err("classic-only session cannot carry FD frames".to_string());
133        }
134        if !matches!(frame.len, 0..=8 | 12 | 16 | 20 | 24 | 32 | 48 | 64) {
135            return Err(format!(
136                "CAN FD frame has invalid length {}; valid lengths are 0-8,12,16,20,24,32,48,64",
137                frame.len
138            ));
139        }
140        if (frame.flags & CAN_FLAG_RTR) != 0 {
141            return Err("CAN FD frame cannot set RTR flag".to_string());
142        }
143    } else if frame.len > 8 {
144        return Err(format!(
145            "classic CAN frame has invalid length {}",
146            frame.len
147        ));
148    }
149
150    Ok(())
151}