lin_bus/
frame.rs

1//! LIN bus frame definitions
2
3use crate::ldf::NodeAttributes;
4use bitfield::BitRange;
5use byteorder::{ByteOrder, LittleEndian};
6use core::mem::size_of;
7use num_traits::{PrimInt, Unsigned};
8
9/// Protected ID which is a 6 bit ID with two parity bits
10#[derive(Copy, Clone, Debug, PartialEq, Eq)]
11#[repr(transparent)]
12pub struct PID(u8);
13
14impl PID {
15    /// Creates a new PID object with given PID
16    pub const fn new(pid: u8) -> PID {
17        // check that the given PID has valid parity bits
18        let correct_pid = PID::from_id(pid & 0b0011_1111);
19        assert!(correct_pid.0 == pid, "Invalid PID");
20        correct_pid
21    }
22
23    /// Calculate the PID from an ID.
24    /// P0 = ID0 ⊕ ID1 ⊕ ID2 ⊕ ID4
25    /// P1 = ¬(ID1 ⊕ ID3 ⊕ ID4 ⊕ ID5)
26    pub const fn from_id(id: u8) -> PID {
27        assert!(id < 64, "ID must be less than 64");
28        // count parity bits and check if they are even odd
29        let p0 = (id & 0b1_0111).count_ones() as u8 & 0b1;
30        let p1 = ((id & 0b11_1010).count_ones() as u8 + 1) & 0b1;
31        PID(id | (p0 << 6u8) | (p1 << 7u8))
32    }
33
34    /// Return the contained PID
35    pub const fn get(self) -> u8 {
36        self.0
37    }
38
39    /// Return the contained ID
40    pub const fn get_id(self) -> u8 {
41        self.0 & 0b0011_1111
42    }
43
44    /// Return if the associated frame uses the classic checksum (diagnostic IDs 60 and 61 or
45    /// special use IDs 62, 63)
46    pub const fn uses_classic_checksum(self) -> bool {
47        self.get_id() >= 60
48    }
49}
50
51/// Calculate the LIN V2.1 "enhanced" checksum. It is defined as "The inverted eight bit sum with
52/// carry. Eight bit sum with carry is equivalent to sum all values and subtract 255 every time the
53/// sum is greater or equal to 256"
54pub fn checksum(pid: PID, data: &[u8]) -> u8 {
55    let sum = data.iter().fold(u16::from(pid.0), |sum, v| {
56        let sum = sum + u16::from(*v);
57        if sum >= 256 {
58            sum - 255
59        } else {
60            sum
61        }
62    });
63    !(sum as u8)
64}
65
66/// Calculate the LIN V1.3 "classic" checksum. It is defined as "Checksum calculation over the data
67/// bytes only"
68pub fn classic_checksum(data: &[u8]) -> u8 {
69    checksum(PID(0u8), data)
70}
71
72#[derive(Debug, Eq, PartialEq)]
73pub struct Frame {
74    pub(crate) pid: PID,
75    pub(crate) buffer: [u8; 9],
76    pub(crate) data_length: usize,
77}
78
79impl Frame {
80    /// Creates a LIN frame from the PID and data. Calculates and adds checksum accordingly
81    pub fn from_data(pid: PID, data: &[u8]) -> Frame {
82        assert!(data.len() <= 8, "Maximum data is 8 bytes");
83        let mut buffer = [0u8; 9];
84        buffer[0..data.len()].clone_from_slice(data);
85        buffer[data.len()] = {
86            if pid.uses_classic_checksum() {
87                classic_checksum(&buffer[0..data.len()])
88            } else {
89                checksum(pid, &buffer[0..data.len()])
90            }
91        };
92        Frame {
93            pid,
94            buffer,
95            data_length: data.len(),
96        }
97    }
98
99    /// Access the data from the frame
100    pub fn get_data(&self) -> &[u8] {
101        &self.buffer[0..self.data_length]
102    }
103
104    /// Decode frame data
105    pub fn decode<T>(&self, offset: usize, length: usize) -> T
106    where
107        T: PrimInt + Unsigned,
108        u64: BitRange<T>,
109    {
110        assert!(
111            (offset + length) <= self.data_length * 8,
112            "Not enough data available"
113        );
114        assert!(length <= size_of::<T>() * 8, "Output type not big enough");
115
116        let num = LittleEndian::read_u64(&self.buffer[0..8]);
117        num.bit_range(offset + length - 1, offset)
118    }
119
120    /// Get the checksum from the frame
121    pub fn get_checksum(&self) -> u8 {
122        self.buffer[self.data_length]
123    }
124
125    /// Get the PID from the frame
126    pub fn get_pid(&self) -> PID {
127        self.pid
128    }
129
130    /// Get the serialized bytes to write to the driver
131    pub fn get_data_with_checksum(&self) -> &[u8] {
132        &self.buffer[0..=self.data_length]
133    }
134}
135
136/// Implements the transport layer of LIN. The units that are transported in a transport layer
137/// frame are called PDU (Packet Data Unit)
138pub mod transport {
139    use super::{Frame, PID};
140
141    /// NAD is the address of the slave node being addressed in a request, i.e. only slave nodes
142    /// have an address. NAD is also used to indicate the source of a response.
143    #[derive(Copy, Clone, Debug, PartialEq, Eq)]
144    #[repr(transparent)]
145    pub struct NAD(pub u8);
146
147    /// The PCI (Protocol Control Information) contains the transport layer flow control
148    /// information.
149    #[derive(Copy, Clone, Debug, PartialEq, Eq)]
150    #[repr(transparent)]
151    pub struct PCI(u8);
152
153    /// Type of the `PCI` byte
154    #[derive(Copy, Clone, Debug, PartialEq, Eq)]
155    pub enum PCIType {
156        /// Single Frame
157        SF = 0,
158        /// First Frame. Start of a multi frame message.
159        FF = 1,
160        /// Consecutive Frame.
161        CF = 2,
162        /// Invalid PCIType
163        Invalid,
164    }
165
166    impl PCI {
167        /// Create a `PCI` with type `PCIType::SF` and the given length
168        pub const fn new_sf(length: u8) -> PCI {
169            assert!(length <= 6, "Maximum length for single frame is 6");
170            PCI(length)
171        }
172
173        /// Get the `PCIType` of the PCI
174        pub const fn get_type(self) -> PCIType {
175            match self.0 >> 4 {
176                0 => PCIType::SF,
177                1 => PCIType::FF,
178                2 => PCIType::CF,
179                _ => PCIType::Invalid,
180            }
181        }
182
183        /// Get the length field of the `PCI`
184        pub const fn get_length(self) -> u8 {
185            self.0 & 0x0F
186        }
187    }
188
189    /// The Service Identifier (SID) specifies the request that shall be performed by the slave
190    /// node addressed.
191    #[derive(Copy, Clone, Debug, PartialEq, Eq)]
192    #[repr(transparent)]
193    pub struct SID(pub u8);
194
195    /// The Response Service Identifier (RSID) specifies the contents of the response.
196    #[derive(Copy, Clone, Debug, PartialEq, Eq)]
197    #[repr(transparent)]
198    pub struct RSID(pub u8);
199
200    /// Create a single frame (CF) PDU
201    pub fn create_single_frame(pid: PID, nad: NAD, sid: SID, data: &[u8]) -> Frame {
202        assert!(
203            !data.is_empty() && data.len() <= 5,
204            "A single frame must contain between 0 and 5 bytes"
205        );
206        // If a PDU is not completely filled the unused bytes shall be filled with 0xFF.
207        let mut frame_data = [0xFFu8; 8];
208        frame_data[0] = nad.0;
209        frame_data[1] = PCI::new_sf(data.len() as u8 + 1).0;
210        frame_data[2] = sid.0;
211        frame_data[3..data.len() + 3].clone_from_slice(data);
212        Frame::from_data(pid, &frame_data)
213    }
214}
215
216/// Implements the LIN diagnostics methods.
217pub mod diagnostic {
218    use super::transport::{create_single_frame, NAD, SID};
219    use super::{ByteOrder, Frame, LittleEndian, PID};
220
221    pub const MASTER_REQUEST_FRAME_ID: u8 = 0x3C;
222    pub const SLAVE_RESPONSE_FRAME_ID: u8 = 0x3D;
223
224    pub const MASTER_REQUEST_FRAME_PID: PID = PID::from_id(0x3C);
225    pub const SLAVE_RESPONSE_FRAME_PID: PID = PID::from_id(0x3D);
226
227    pub const READ_BY_IDENTIFIER_SID: SID = SID(0xB2);
228
229    #[repr(u8)]
230    /// Identifiers used for the Read by identifer
231    pub enum Identifier {
232        /// See also `ProductId`
233        LINProductIdentification,
234        /// See also `SerialNumber`
235        SerialNumber,
236        /// User defined range 32..=63
237        UserDefined(u8),
238        /// Reserved range 2..=31 and 64..=255
239        Reserved(u8),
240    }
241
242    impl From<u8> for Identifier {
243        fn from(byte: u8) -> Identifier {
244            match byte {
245                0 => Identifier::LINProductIdentification,
246                1 => Identifier::SerialNumber,
247                b @ 32..=63 => Identifier::UserDefined(b),
248                b => Identifier::Reserved(b),
249            }
250        }
251    }
252
253    impl From<Identifier> for u8 {
254        fn from(identifier: Identifier) -> u8 {
255            match identifier {
256                Identifier::LINProductIdentification => 0,
257                Identifier::SerialNumber => 1,
258                Identifier::UserDefined(b) => b,
259                Identifier::Reserved(b) => b,
260            }
261        }
262    }
263
264    /// Holds the LIN slave node product identification
265    #[derive(Copy, Clone, Debug, PartialEq, Eq)]
266    pub struct ProductId {
267        pub supplier_id: u16,
268        pub function_id: u16,
269        pub variant: u8,
270    }
271
272    impl From<&[u8]> for ProductId {
273        fn from(data: &[u8]) -> ProductId {
274            assert!(data.len() >= 5, "We require at least 4 data bytes");
275            ProductId {
276                supplier_id: LittleEndian::read_u16(&data[0..2]),
277                function_id: LittleEndian::read_u16(&data[2..4]),
278                variant: data[4],
279            }
280        }
281    }
282
283    #[derive(Copy, Clone, Debug, PartialEq, Eq)]
284    #[repr(transparent)]
285    pub struct SerialNumber(pub u32);
286
287    impl From<&[u8]> for SerialNumber {
288        fn from(data: &[u8]) -> SerialNumber {
289            assert!(data.len() >= 4, "We require at least 4 data bytes");
290            SerialNumber(LittleEndian::read_u32(data))
291        }
292    }
293
294    /// Create a read by identifier `Frame` from `NodeAttributes`
295    pub fn create_read_by_identifier_frame_from_node_attributes(
296        node_attributes: super::NodeAttributes,
297        identifier: Identifier,
298    ) -> Frame {
299        create_read_by_identifier_frame(
300            node_attributes.initial_nad,
301            identifier,
302            node_attributes.product_id.supplier_id,
303            node_attributes.product_id.function_id,
304        )
305    }
306
307    /// Create a read by identifier frame
308    pub fn create_read_by_identifier_frame(
309        nad: NAD,
310        identifier: Identifier,
311        supplier_id: u16,
312        function_id: u16,
313    ) -> Frame {
314        create_single_frame(
315            MASTER_REQUEST_FRAME_PID,
316            nad,
317            READ_BY_IDENTIFIER_SID,
318            &[
319                identifier.into(),
320                (supplier_id & 0xFF) as u8,
321                (supplier_id >> 8) as u8,
322                (function_id & 0xFF) as u8,
323                (function_id >> 8) as u8,
324            ],
325        )
326    }
327
328    pub fn create_read_lin_product_identification_frame(
329        node_attributes: super::NodeAttributes,
330    ) -> Frame {
331        create_read_by_identifier_frame_from_node_attributes(
332            node_attributes,
333            Identifier::LINProductIdentification,
334        )
335    }
336
337    pub fn create_read_serial_number_frame(node_attributes: super::NodeAttributes) -> Frame {
338        create_read_by_identifier_frame_from_node_attributes(
339            node_attributes,
340            Identifier::SerialNumber,
341        )
342    }
343}
344
345#[cfg(test)]
346mod tests {
347    use super::diagnostic::*;
348    use super::transport::*;
349    use super::*;
350
351    struct CheckSumTestData<'a> {
352        pid: PID,
353        data: &'a [u8],
354        checksum: u8,
355    }
356
357    #[test]
358    fn test_enhanced_checksum() {
359        let test_data = [
360            CheckSumTestData {
361                pid: PID(0xDD),
362                data: &[0x01],
363                checksum: 0x21,
364            },
365            CheckSumTestData {
366                pid: PID(0x4A),
367                data: &[0x55, 0x93, 0xE5],
368                checksum: 0xE6,
369            },
370            CheckSumTestData {
371                pid: PID(0xBF),
372                data: &[0x40, 0xFF],
373                checksum: 0x00,
374            },
375        ];
376        for d in &test_data {
377            assert_eq!(d.checksum, checksum(d.pid, d.data));
378        }
379    }
380
381    #[test]
382    fn test_classic_checksum() {
383        let test_data = [
384            CheckSumTestData {
385                pid: PID::from_id(0x3C),
386                data: &[0x01],
387                checksum: 0xFE,
388            },
389            CheckSumTestData {
390                pid: PID::from_id(0x3D),
391                data: &[0x01],
392                checksum: 0xFE,
393            },
394            CheckSumTestData {
395                pid: PID::from_id(0x3d),
396                data: &[0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08],
397                checksum: 0xDB,
398            },
399        ];
400        for d in &test_data {
401            assert_eq!(d.checksum, classic_checksum(d.data));
402        }
403    }
404
405    #[test]
406    fn test_pid_new() {
407        let test_data = [
408            (0x64, PID::new(0x64)),
409            (0xCA, PID::new(0xCA)),
410            (0x80, PID::new(0x80)),
411            (0xC1, PID::new(0xC1)),
412            (0x47, PID::new(0x47)),
413            (0x61, PID::new(0x61)),
414        ];
415
416        for d in &test_data {
417            assert_eq!(d.0, d.1.get());
418        }
419    }
420
421    #[test]
422    #[should_panic]
423    fn test_invalid_pid_new() {
424        PID::new(0x07);
425    }
426
427    #[test]
428    fn test_pid_from_id() {
429        let test_data = [
430            (0, PID(0x80)),
431            (1, PID(0xC1)),
432            (2, PID(0x42)),
433            (25, PID(0x99)),
434            (27, PID(0x5B)),
435            (29, PID(0xDD)),
436        ];
437
438        for d in &test_data {
439            let pid = PID::from_id(d.0);
440            assert_eq!(pid, d.1);
441            assert_eq!(pid.get_id(), d.0);
442        }
443    }
444
445    #[test]
446    fn test_id_uses_classic_checksum() {
447        let test_ids: &[u8] = &[0, 1, 59, 60, 63];
448
449        for i in test_ids {
450            assert_eq!(PID::from_id(*i).uses_classic_checksum(), *i >= 60);
451        }
452    }
453
454    #[test]
455    #[should_panic]
456    fn test_pid_from_id_panic() {
457        PID::from_id(64);
458    }
459
460    #[test]
461    fn test_pci() {
462        let pci = PCI::new_sf(5);
463        assert_eq!(pci.get_type(), PCIType::SF);
464        assert_eq!(pci.get_length(), 5);
465    }
466
467    #[test]
468    fn test_transport_frame() {
469        struct TestData {
470            pid: PID,
471            nad: transport::NAD,
472            sid: transport::SID,
473            data: &'static [u8],
474            frame_data: [u8; 8],
475        }
476        let test_data = [
477            TestData {
478                pid: diagnostic::MASTER_REQUEST_FRAME_PID,
479                nad: transport::NAD(0x10),
480                sid: transport::SID(0xB2),
481                data: &[0x01, 0xB3, 0x00, 0x01, 0x10],
482                frame_data: [0x10, 0x06, 0xB2, 0x01, 0xB3, 0x00, 0x01, 0x10],
483            },
484            TestData {
485                pid: diagnostic::SLAVE_RESPONSE_FRAME_PID,
486                nad: transport::NAD(0x10),
487                sid: transport::SID(0xB2),
488                data: &[0x01],
489                frame_data: [0x10, 0x02, 0xB2, 0x01, 0xFF, 0xFF, 0xFF, 0xFF],
490            },
491        ];
492
493        for d in &test_data {
494            let frame = transport::create_single_frame(d.pid, d.nad, d.sid, d.data);
495            assert_eq!(frame.get_pid(), d.pid);
496            assert_eq!(frame.get_data(), d.frame_data);
497            assert_eq!(frame.data_length, 8);
498        }
499    }
500
501    #[test]
502    #[should_panic]
503    fn test_transport_frame_without_data() {
504        transport::create_single_frame(
505            PID::from_id(0x1),
506            transport::NAD(0x2),
507            transport::SID(0x03),
508            &[],
509        );
510    }
511
512    #[test]
513    #[should_panic]
514    fn test_transport_frame_with_too_much_data() {
515        transport::create_single_frame(
516            PID::from_id(0x1),
517            transport::NAD(0x2),
518            transport::SID(0x03),
519            &[1, 2, 3, 4, 5, 6],
520        );
521    }
522
523    #[test]
524    fn test_create_read_by_identifier_frame() {
525        const LIN_ID_SERIAL_REQ_PAYLOAD: &[u8] = &[0x10, 0x06, 0xB2, 0x01, 0xB3, 0x00, 0x01, 0x10];
526
527        let frame = diagnostic::create_read_by_identifier_frame(
528            transport::NAD(0x10),
529            diagnostic::Identifier::SerialNumber,
530            0x00B3,
531            0x1001,
532        );
533
534        assert_eq!(frame.get_pid(), diagnostic::MASTER_REQUEST_FRAME_PID);
535        assert_eq!(frame.get_data(), LIN_ID_SERIAL_REQ_PAYLOAD);
536        assert_eq!(frame.data_length, 8);
537    }
538
539    #[test]
540    fn test_create_read_by_identifier_frame_from_node_attributes() {
541        const LIN_ID_SERIAL_REQ_PAYLOAD: &[u8] = &[0x10, 0x06, 0xB2, 0x01, 0xB3, 0x00, 0x01, 0x10];
542        let node_attributes = NodeAttributes::with_default_timing(
543            transport::NAD(0x10),
544            transport::NAD(0x10),
545            diagnostic::ProductId {
546                supplier_id: 0x00B3,
547                function_id: 0x1001,
548                variant: 0x00,
549            },
550        );
551
552        let frame = diagnostic::create_read_by_identifier_frame_from_node_attributes(
553            node_attributes,
554            diagnostic::Identifier::SerialNumber,
555        );
556        assert_eq!(frame.get_pid(), diagnostic::MASTER_REQUEST_FRAME_PID);
557        assert_eq!(frame.get_data(), LIN_ID_SERIAL_REQ_PAYLOAD);
558        assert_eq!(frame.data_length, 8);
559    }
560
561    #[test]
562    fn test_decode_product_id() {
563        let product_id = ProductId {
564            supplier_id: 0x00B3,
565            function_id: 0x1001,
566            variant: 0x01,
567        };
568        let data = [0xB3, 0x00, 0x01, 0x10, 0x01];
569
570        assert_eq!(product_id, ProductId::from(&data[..]));
571    }
572
573    #[test]
574    fn test_decode_serial_number() {
575        let serial_number = SerialNumber(190200009);
576        let data = [0xC9, 0x38, 0x56, 0x0B];
577        assert_eq!(serial_number, SerialNumber::from(&data[..]));
578    }
579}