emcyphal_driver/
frame.rs

1//! Transport frame object
2
3use emcyphal_core::{NodeId, Priority, ServiceId, SubjectId};
4
5use crate::time::Instant;
6
7/// A transport-layer maximum transmission unit
8#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
9#[cfg_attr(feature = "defmt", derive(defmt::Format))]
10pub enum Mtu {
11    Classic,
12    Fd,
13}
14
15impl From<Mtu> for usize {
16    fn from(value: Mtu) -> Self {
17        match value {
18            Mtu::Classic => 8,
19            Mtu::Fd => 64,
20        }
21    }
22}
23
24#[derive(Debug)]
25#[cfg_attr(feature = "defmt", derive(defmt::Format))]
26pub struct IncorrectMtu;
27
28impl TryFrom<usize> for Mtu {
29    type Error = IncorrectMtu;
30    fn try_from(value: usize) -> Result<Self, Self::Error> {
31        match value {
32            8 => Ok(Mtu::Classic),
33            64 => Ok(Mtu::Fd),
34            _ => Err(IncorrectMtu),
35        }
36    }
37}
38
39/// Encodes the semantic properties of the data type carried by a transfer and its kind
40#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
41#[cfg_attr(feature = "defmt", derive(defmt::Format))]
42pub enum DataSpecifier {
43    Message(SubjectId),
44    Request(ServiceId),
45    Response(ServiceId),
46}
47
48/// Transport frame data encoded with CAN frame ID
49#[derive(Debug, Clone, Copy, PartialEq, Eq)]
50#[cfg_attr(feature = "defmt", derive(defmt::Format))]
51pub struct Header {
52    pub priority: Priority,
53    pub data_spec: DataSpecifier,
54    pub source: Option<NodeId>,
55    pub destination: Option<NodeId>,
56}
57
58/// Transport frame for both Classic and FD transport
59///
60/// The data length should be limited to the relevant MTU.
61#[derive(Debug, Clone, Copy, PartialEq, Eq)]
62#[cfg_attr(feature = "defmt", derive(defmt::Format))]
63pub struct Frame {
64    pub header: Header,
65    pub data: Data,
66    pub timestamp: Instant,
67    pub loop_back: bool,
68}
69
70/// CAN-FD-compatible data length
71///
72/// Data length code (DLC) of CAN-FD frames supports limited data length options.
73/// Classic CAN frames support a subset of CAN-FD length options limited by MTU.
74#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
75#[cfg_attr(feature = "defmt", derive(defmt::Format))]
76pub struct DataLength(u8);
77
78impl DataLength {
79    pub const MAX: usize = 64;
80
81    pub const fn new(value: usize) -> Option<Self> {
82        let floor = Self::new_floor(value);
83        if floor.as_usize() == value {
84            Some(floor)
85        } else {
86            None
87        }
88    }
89
90    pub const fn new_floor(value: usize) -> Self {
91        let floor = match value {
92            0..8 => value,
93            8..24 => value / 4 * 4,
94            24..32 => value / 8 * 8,
95            32..64 => value / 16 * 16,
96            64.. => 64,
97        };
98        Self(floor as u8)
99    }
100
101    pub const fn new_ceil(value: usize) -> Option<Self> {
102        if value <= Self::MAX {
103            let ceil = match value {
104                0..8 => value,
105                8..24 => value.div_ceil(4) * 4,
106                24..32 => value.div_ceil(8) * 8,
107                32.. => value.div_ceil(16) * 16,
108            };
109            Some(Self(ceil as u8))
110        } else {
111            None
112        }
113    }
114
115    pub const fn as_usize(&self) -> usize {
116        self.0 as usize
117    }
118}
119
120impl From<DataLength> for usize {
121    fn from(value: DataLength) -> Self {
122        value.as_usize()
123    }
124}
125
126#[derive(Debug)]
127#[cfg_attr(feature = "defmt", derive(defmt::Format))]
128pub struct InvalidLength;
129
130/// CAN-FD Frame compatible data vector
131///
132/// Data length code (DLC) of CAN-FD frames supports limited data length options.
133/// Classic CAN frames support a subset of CAN-FD length options limited by MTU.
134#[derive(Debug, Clone, Copy, PartialEq, Eq)]
135#[cfg_attr(feature = "defmt", derive(defmt::Format))]
136pub struct Data {
137    length: DataLength,
138    bytes: [u8; 64],
139}
140
141impl Data {
142    /// Creates a new vector from a slice of compatible length.
143    pub fn new(data: &[u8]) -> Result<Self, InvalidLength> {
144        let length = DataLength::new(data.len()).ok_or(InvalidLength)?;
145        let mut bytes = [0; 64];
146        bytes[..data.len()].copy_from_slice(data);
147
148        Ok(Self { length, bytes })
149    }
150
151    pub fn new_zeros(length: DataLength) -> Self {
152        Self {
153            length,
154            bytes: [0; 64],
155        }
156    }
157
158    pub fn length(&self) -> DataLength {
159        self.length
160    }
161}
162
163impl core::ops::Deref for Data {
164    type Target = [u8];
165
166    fn deref(&self) -> &Self::Target {
167        &self.bytes[..usize::from(self.length)]
168    }
169}
170
171impl core::ops::DerefMut for Data {
172    fn deref_mut(&mut self) -> &mut Self::Target {
173        &mut self.bytes[..usize::from(self.length)]
174    }
175}
176
177#[cfg(test)]
178mod tests {
179    use super::*;
180
181    const VALID_CAN_LENGTH: [usize; 16] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 12, 16, 20, 24, 32, 48, 64];
182
183    fn ceil_length_ref(value: usize) -> Option<usize> {
184        match VALID_CAN_LENGTH.binary_search(&value) {
185            Ok(pos) => Some(VALID_CAN_LENGTH[pos]),
186            Err(pos) => {
187                if pos < VALID_CAN_LENGTH.len() {
188                    Some(VALID_CAN_LENGTH[pos])
189                } else {
190                    None
191                }
192            }
193        }
194    }
195
196    fn floor_length_ref(value: usize) -> usize {
197        match VALID_CAN_LENGTH.binary_search(&value) {
198            Ok(pos) => VALID_CAN_LENGTH[pos],
199            Err(pos) => VALID_CAN_LENGTH[pos - 1],
200        }
201    }
202
203    #[test]
204    fn test_frame_length() {
205        for len in 0usize..100 {
206            assert_eq!(
207                usize::from(DataLength::new_floor(len)),
208                floor_length_ref(len)
209            );
210            assert_eq!(
211                DataLength::new_ceil(len).map(|len| usize::from(len)),
212                ceil_length_ref(len)
213            );
214        }
215    }
216}