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}