Skip to main content

ace_can/ext/
fd.rs

1use crate::constants::{
2    BRS_FLAG, CAN_FD_MAX_DLC, CAN_FD_MIN_LEN, DLC_OFFSET, EDL_FLAG, EFF_FLAG, ERR_FLAG, ESI_FLAG,
3    EXT_ID_MASK, FLAGS_OFFSET, RRS_FLAG, STD_ID_MASK,
4};
5use crate::isotp::address::IsoTpAddressingMode;
6use crate::{constants::DATA_OFFSET, error::CanError};
7use ace_proto::can::frame::fd::{CanFdFrame, CanFdFrameMut};
8
9// region: Offsets
10
11/// Byte layout of a raw CAN FD frame:
12///
13/// [0..=3] - 32-bit CAN ID word (big-endian)
14///           bit 31: extended frame flag (EFF)
15///           bit 30: RRS (replaces RTR in CAN FD, must be 0)
16///           bit 29: error frame flag (ERR)
17///           bits 28-0: CAN ID
18/// [4]     - flags byte
19///           bit 2: EDL (Extended Data Length) - must be 1 for CAN FD
20///           bit 1: BRS (Bit Rate Switch)
21///           bit 0: ESI (Error State Indicator)
22/// [5]     - DLC (0–15, maps to 0–64 bytes via dlc_to_len)
23/// [6..]   - data bytes (0–64 bytes)
24
25// endregion: Offsets
26
27// region: DLC helpers
28
29/// Maps a CAN FD DLC value (0–15) to the actual data length in bytes.
30pub fn dlc_to_len(dlc: u8) -> usize {
31    match dlc {
32        0..=8 => dlc as usize,
33        9 => 12,
34        10 => 16,
35        11 => 20,
36        12 => 24,
37        13 => 32,
38        14 => 48,
39        15 => 64,
40        _ => 64,
41    }
42}
43
44/// Maps a data length in bytes to the smallest valid CAN FD DLC.
45pub fn len_to_dlc(len: usize) -> Option<u8> {
46    match len {
47        0..=8 => Some(len as u8),
48        9..=12 => Some(9),
49        13..=16 => Some(10),
50        17..=20 => Some(11),
51        21..=24 => Some(12),
52        25..=32 => Some(13),
53        33..=48 => Some(14),
54        49..=64 => Some(15),
55        _ => None,
56    }
57}
58
59// endregion: DLC helpers
60
61// region: CanFdFrameExt
62
63pub trait CanFdFrameExt {
64    fn as_bytes(&self) -> &[u8];
65
66    // region: Raw field accessors
67
68    fn id_word(&self) -> Option<u32> {
69        let b = self.as_bytes();
70        if b.len() < DATA_OFFSET {
71            return None;
72        }
73        Some(u32::from_be_bytes([b[0], b[1], b[2], b[3]]))
74    }
75
76    fn flags_byte(&self) -> Option<u8> {
77        self.as_bytes().get(FLAGS_OFFSET).copied()
78    }
79
80    fn dlc(&self) -> Option<u8> {
81        self.as_bytes().get(DLC_OFFSET).copied()
82    }
83
84    fn data_bytes(&self) -> Option<&[u8]> {
85        let b = self.as_bytes();
86        let dlc = self.dlc()?;
87        let len = dlc_to_len(dlc);
88        let end = DATA_OFFSET + len;
89        if b.len() < end {
90            return None;
91        }
92        Some(&b[DATA_OFFSET..end])
93    }
94
95    // endregion: Raw field accessors
96
97    // region: Flags
98
99    fn is_extended_frame(&self) -> bool {
100        self.id_word().map_or(false, |w| w & EFF_FLAG != 0)
101    }
102
103    fn is_rrs(&self) -> bool {
104        self.id_word().map_or(false, |w| w & RRS_FLAG != 0)
105    }
106
107    fn is_error_frame(&self) -> bool {
108        self.id_word().map_or(false, |w| w & ERR_FLAG != 0)
109    }
110
111    fn is_edl(&self) -> bool {
112        self.flags_byte().map_or(false, |f| f & EDL_FLAG != 0)
113    }
114
115    fn is_brs(&self) -> bool {
116        self.flags_byte().map_or(false, |f| f & BRS_FLAG != 0)
117    }
118
119    fn is_esi(&self) -> bool {
120        self.flags_byte().map_or(false, |f| f & ESI_FLAG != 0)
121    }
122
123    // endregion: Flags
124
125    // region: Typed accessors
126
127    fn can_id(&self) -> Option<Result<ace_proto::can::id::CanId, CanError>> {
128        let word = self.id_word()?;
129        if word & EFF_FLAG != 0 {
130            let raw = word & EXT_ID_MASK;
131            Some(
132                ace_proto::can::id::ExtendedCanId::new(raw)
133                    .map(ace_proto::can::id::CanId::Extended)
134                    .map_err(CanError::from),
135            )
136        } else {
137            let raw = (word & STD_ID_MASK) as u16;
138            Some(
139                ace_proto::can::id::StandardCanId::new(raw)
140                    .map(ace_proto::can::id::CanId::Standard)
141                    .map_err(CanError::from),
142            )
143        }
144    }
145
146    // endregion: Typed accessors
147
148    // region: Validation
149
150    fn validate(&self) -> Result<(), CanError> {
151        let b = self.as_bytes();
152
153        if b.len() < CAN_FD_MIN_LEN {
154            return Err(CanError::BufferTooShort {
155                expected: CAN_FD_MIN_LEN,
156                actual: b.len(),
157            });
158        }
159
160        let flags = b[FLAGS_OFFSET];
161
162        // EDL must be set on all CAN FD frames
163        if flags & EDL_FLAG == 0 {
164            return Err(CanError::InvalidFlags);
165        }
166
167        // BRS requires EDL - already guaranteed above, but ESI without EDL is illegal
168        if flags & ESI_FLAG != 0 && flags & EDL_FLAG == 0 {
169            return Err(CanError::InvalidFlags);
170        }
171
172        // RRS must be 0 on CAN FD frames
173        let word = u32::from_be_bytes([b[0], b[1], b[2], b[3]]);
174        if word & RRS_FLAG != 0 {
175            return Err(CanError::InvalidFlags);
176        }
177
178        let dlc = b[DLC_OFFSET];
179        if dlc > CAN_FD_MAX_DLC {
180            return Err(CanError::InvalidDlc(dlc));
181        }
182
183        let expected_len = DATA_OFFSET + dlc_to_len(dlc);
184        if b.len() < expected_len {
185            return Err(CanError::BufferTooShort {
186                expected: expected_len,
187                actual: b.len(),
188            });
189        }
190
191        Ok(())
192    }
193
194    fn is_valid(&self) -> bool {
195        self.validate().is_ok()
196    }
197
198    // endregion: Validation
199
200    // region: ISO-TP helpers
201
202    fn isotp_bytes(&self, mode: &IsoTpAddressingMode) -> Option<&[u8]> {
203        let data = self.data_bytes()?;
204        let offset = mode.pci_offset();
205        if data.len() <= offset {
206            return None;
207        }
208        Some(&data[offset..])
209    }
210
211    // endregion: ISO-TP helpers
212}
213
214impl<'a> CanFdFrameExt for CanFdFrame<'a> {
215    fn as_bytes(&self) -> &[u8] {
216        ace_proto::common::RawFrame::as_bytes(self)
217    }
218}
219
220impl<'a> CanFdFrameExt for CanFdFrameMut<'a> {
221    fn as_bytes(&self) -> &[u8] {
222        ace_proto::common::RawFrame::as_bytes(self)
223    }
224}
225
226// endregion: CanFdFrameExt
227
228// region: CanFdFrameMutExt
229
230pub trait CanFdFrameMutExt: CanFdFrameExt {
231    fn as_bytes_mut(&mut self) -> &mut [u8];
232
233    fn set_id_word(&mut self, word: u32) -> Result<(), CanError> {
234        let b = self.as_bytes_mut();
235        if b.len() < DATA_OFFSET {
236            return Err(CanError::BufferTooShort {
237                expected: DATA_OFFSET,
238                actual: b.len(),
239            });
240        }
241        b[0..4].copy_from_slice(&word.to_be_bytes());
242        Ok(())
243    }
244
245    fn set_standard_id(&mut self, id: &ace_proto::can::id::StandardCanId) -> Result<(), CanError> {
246        let word = id.value() as u32 & STD_ID_MASK;
247        self.set_id_word(word)
248    }
249
250    fn set_extended_id(&mut self, id: &ace_proto::can::id::ExtendedCanId) -> Result<(), CanError> {
251        let word = (id.value() & EXT_ID_MASK) | EFF_FLAG;
252        self.set_id_word(word)
253    }
254
255    fn set_flags(&mut self, edl: bool, brs: bool, esi: bool) -> Result<(), CanError> {
256        let b = self.as_bytes_mut();
257        if b.len() <= FLAGS_OFFSET {
258            return Err(CanError::BufferTooShort {
259                expected: FLAGS_OFFSET + 1,
260                actual: b.len(),
261            });
262        }
263        let mut flags = EDL_FLAG; // EDL always set for CAN FD
264        if brs {
265            flags |= BRS_FLAG;
266        }
267        if esi {
268            flags |= ESI_FLAG;
269        }
270        if !edl {
271            return Err(CanError::InvalidFlags);
272        }
273        b[FLAGS_OFFSET] = flags;
274        Ok(())
275    }
276
277    fn set_dlc(&mut self, dlc: u8) -> Result<(), CanError> {
278        if dlc > CAN_FD_MAX_DLC {
279            return Err(CanError::InvalidDlc(dlc));
280        }
281        let b = self.as_bytes_mut();
282        if b.len() <= DLC_OFFSET {
283            return Err(CanError::BufferTooShort {
284                expected: DLC_OFFSET + 1,
285                actual: b.len(),
286            });
287        }
288        b[DLC_OFFSET] = dlc;
289        Ok(())
290    }
291
292    fn write_data(&mut self, data: &[u8]) -> Result<(), CanError> {
293        let dlc = len_to_dlc(data.len()).ok_or(CanError::InvalidDlc(data.len() as u8))?;
294        let b = self.as_bytes_mut();
295        let end = DATA_OFFSET + data.len();
296        if b.len() < end {
297            return Err(CanError::BufferTooShort {
298                expected: end,
299                actual: b.len(),
300            });
301        }
302        b[DATA_OFFSET..DATA_OFFSET + data.len()].copy_from_slice(data);
303        b[DLC_OFFSET] = dlc;
304        Ok(())
305    }
306}
307
308impl<'a> CanFdFrameMutExt for CanFdFrameMut<'a> {
309    fn as_bytes_mut(&mut self) -> &mut [u8] {
310        ace_proto::common::RawFrameMut::as_bytes_mut(self)
311    }
312}
313
314// endregion: CanFdFrameMutExt