zencan_common/
lss.rs

1//! Core implementation of the LSS protocol
2//!
3//! This module includes definitions of all the relevant constants, and message serialization for
4//! the Layer Setting Services (LSS) protocol. The LSS protocol is used for configuring the node ID
5//! on unconfigured nodes, and for discovering the identity of unconfigured nodes.
6
7use crate::messages::{CanId, CanMessage, MessageError, LSS_REQ_ID, LSS_RESP_ID};
8
9/// Defines all possible values for the LSS command specifier field
10#[derive(Debug, Clone, Copy)]
11pub enum LssCommandSpecifier {
12    /// Used to change the LSS mode for all nodes on the bus
13    SwitchModeGlobal = 0x04,
14    /// Used to set the Node ID of the node(s) currently in *Configuration* mode
15    ConfigureNodeId = 0x11,
16    /// Used to set the bit timing (baud rate) of the node(s) currently in *Configuration* mode
17    ConfigureBitTiming = 0x13,
18    /// Used to command nodes to activate a new bit rate setting
19    ActivateBitTiming = 0x15,
20    /// Used to command nodes to store their config (node ID and bit rate) persistently
21    StoreConfiguration = 0x17,
22    /// Sends Vendor ID for activating an LSS node via its identity
23    SwitchStateVendor = 0x40,
24    /// Sends Product Code for activating an LSS node via its identity
25    SwitchStateProduct = 0x41,
26    /// Sends Revision Number for activating an LSS node via its identity
27    SwitchStateRev = 0x42,
28    /// Sends Serial Number for activating an LSS node via its identity
29    ///
30    /// This command should come last (after vendor, product, rev), as a node which recognizes its
31    /// own identity will respond on receipt of this message
32    SwitchStateSerial = 0x43,
33    /// Response by a node to indicate it has recognized its identity and is entering *Configuration* mode
34    SwitchStateResponse = 0x44,
35    /// Response to a FastScan message
36    IdentifySlave = 0x4F,
37    /// Message used for fast scan protocol to discover unconfigured nodes without knowing their identity
38    FastScan = 0x51,
39    /// Used to inquire the vendor ID of a node in *Configuration* mode
40    InquireVendor = 0x5A,
41    /// Used to inquire the product code of a node in *Configuration* mode
42    InquireProduct = 0x5B,
43    /// Used to inquire the revision number of a node in *Configuration* mode
44    InquireRev = 0x5C,
45    /// Used to inquire the serial number of a node in *Configuration* mode
46    InquireSerial = 0x5D,
47    /// Used to inquire the node ID of a node in *Configuration* mode
48    InquireNodeId = 0x5E,
49}
50
51/// Represents the possible values of the error field returned in response to a ConfigureNodeId
52/// command
53#[derive(Debug, Clone, Copy, PartialEq)]
54#[repr(u8)]
55pub enum LssConfigureError {
56    /// Success
57    Ok = 0,
58    /// The node ID is not value (outside of range 1 to 127)
59    NodeIdOutOfRange = 1,
60    /// A manufacturer specific error is stored in the `spec_error` field
61    Manufacturer = 0xff,
62}
63
64/// Special value for fastscan bit_check field
65pub const LSS_FASTSCAN_CONFIRM: u8 = 0x80;
66
67impl LssCommandSpecifier {
68    /// Attempt to create an [`LssCommandSpecifier`] from a byte code
69    pub fn from_byte(b: u8) -> Result<Self, MessageError> {
70        match b {
71            0x04 => Ok(Self::SwitchModeGlobal),
72            0x11 => Ok(Self::ConfigureNodeId),
73            0x13 => Ok(Self::ConfigureBitTiming),
74            0x15 => Ok(Self::ActivateBitTiming),
75            0x17 => Ok(Self::StoreConfiguration),
76            0x40 => Ok(Self::SwitchStateVendor),
77            0x41 => Ok(Self::SwitchStateProduct),
78            0x42 => Ok(Self::SwitchStateRev),
79            0x43 => Ok(Self::SwitchStateSerial),
80            0x44 => Ok(Self::SwitchStateResponse),
81            0x4F => Ok(Self::IdentifySlave),
82            0x51 => Ok(Self::FastScan),
83            0x5A => Ok(Self::InquireVendor),
84            0x5B => Ok(Self::InquireProduct),
85            0x5C => Ok(Self::InquireRev),
86            0x5D => Ok(Self::InquireSerial),
87            0x5E => Ok(Self::InquireNodeId),
88            _ => Err(MessageError::UnexpectedLssCommand { value: b }),
89        }
90    }
91}
92
93/// An LSS request send by the master to the slave
94#[derive(Clone, Copy, Debug)]
95#[cfg_attr(feature = "defmt", derive(defmt::Format))]
96pub enum LssRequest {
97    /// Switch the mode of all LSS slaves
98    SwitchModeGlobal {
99        /// The mode -- 0 = *Waiting*, 1 = *Configuring*
100        mode: u8,
101    },
102    /// Set the node ID of the node currently in *Configuring* state
103    ConfigureNodeId {
104        /// The new node ID to set
105        node_id: u8,
106    },
107    /// Set the bit time (baud rate) of the node currently in *Configuring* state
108    ConfigureBitTiming {
109        /// Defines what baudrate table is used to lookup the bit timing
110        /// 0 means use the default table
111        /// 1..127 are reserved
112        /// 128..255 are user definable
113        ///
114        /// The default table is:
115        /// - 0: 1MBit/s
116        /// - 1: 800kBit/s
117        /// - 2: 500kBit/s
118        /// - 3: 250kBit/s
119        /// - 4: 125kBit/s
120        /// - 5: 100kBit/s
121        /// - 6: 50kBit/s
122        /// - 7: 20kBit/s
123        /// - 8: 10kBit/s
124        table: u8,
125        /// The index into the baudrate table for the baudrate to select
126        index: u8,
127    },
128    /// Store the currently configured node ID and bit timing to persistent storage
129    StoreConfiguration,
130    /// Command a new bitrate to be activated
131    ActivateBitTiming {
132        /// Duration in ms to delay before activating the new baudrate
133        delay: u16,
134    },
135    /// Send the vendor ID to activate by idenity
136    SwitchStateVendor {
137        /// The vendor ID to match against (32-bit value)
138        vendor_id: u32,
139    },
140    /// Send the producte code to activate by identity
141    SwitchStateProduct {
142        /// The product code to match against (32-bit value)
143        product_code: u32,
144    },
145    /// Send the revision number to active by identity
146    SwitchStateRevision {
147        /// The revision number to match against (32-bit value)
148        revision: u32,
149    },
150    /// Send the serial number to active by identity
151    ///
152    /// This should be sent last, as it triggers the slave to check its identity against the
153    /// recieved values and respond if they match
154    SwitchStateSerial {
155        /// The serial number to match against (32-bit value)
156        serial: u32,
157    },
158    /// Request the vendor ID from a node in LSS Configuring state
159    InquireVendor,
160    /// Request the product code from a node in LSS Configuring state
161    InquireProduct,
162    /// Request the revision from a node in LSS Configuring state
163    InquireRev,
164    /// Request the serial number from a node in LSS Configuring state
165    InquireSerial,
166    /// Request the node ID from a node in LSS Configuring state
167    InquireNodeId,
168
169    /// Send a FastScan query
170    FastScan {
171        /// The ID field
172        id: u32,
173        /// The bit_check field
174        bit_check: u8,
175        /// The sub index of the identity to check
176        /// 0 - Vendor ID
177        /// 1 - Product Code
178        /// 2 - Revision
179        /// 3 - Serial Number
180        sub: u8,
181        /// The sub index of the identity to check on the next FastScan request
182        next: u8,
183    },
184}
185
186impl TryFrom<&[u8]> for LssRequest {
187    type Error = MessageError;
188
189    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
190        if value.is_empty() {
191            return Err(MessageError::MessageTooShort);
192        }
193        let cs = LssCommandSpecifier::from_byte(value[0])?;
194        match cs {
195            LssCommandSpecifier::SwitchModeGlobal => {
196                if value.len() < 2 {
197                    return Err(MessageError::MessageTooShort);
198                }
199                Ok(Self::SwitchModeGlobal { mode: value[1] })
200            }
201            LssCommandSpecifier::ConfigureNodeId => {
202                if value.len() < 2 {
203                    return Err(MessageError::MessageTooShort);
204                }
205                Ok(Self::ConfigureNodeId { node_id: value[1] })
206            }
207            LssCommandSpecifier::ConfigureBitTiming => {
208                if value.len() < 3 {
209                    return Err(MessageError::MessageTooShort);
210                }
211                Ok(Self::ConfigureBitTiming {
212                    table: value[1],
213                    index: value[2],
214                })
215            }
216            LssCommandSpecifier::ActivateBitTiming => {
217                if value.len() < 3 {
218                    return Err(MessageError::MessageTooShort);
219                }
220                Ok(Self::ActivateBitTiming {
221                    delay: u16::from_le_bytes([value[1], value[2]]),
222                })
223            }
224            LssCommandSpecifier::StoreConfiguration => Ok(Self::StoreConfiguration),
225            LssCommandSpecifier::SwitchStateVendor => {
226                if value.len() < 5 {
227                    return Err(MessageError::MessageTooShort);
228                }
229                Ok(Self::SwitchStateVendor {
230                    vendor_id: u32::from_le_bytes(value[1..5].try_into().unwrap()),
231                })
232            }
233            LssCommandSpecifier::SwitchStateProduct => {
234                if value.len() < 5 {
235                    return Err(MessageError::MessageTooShort);
236                }
237                Ok(Self::SwitchStateProduct {
238                    product_code: u32::from_le_bytes(value[1..5].try_into().unwrap()),
239                })
240            }
241            LssCommandSpecifier::SwitchStateRev => {
242                if value.len() < 5 {
243                    return Err(MessageError::MessageTooShort);
244                }
245                Ok(Self::SwitchStateRevision {
246                    revision: u32::from_le_bytes(value[1..5].try_into().unwrap()),
247                })
248            }
249            LssCommandSpecifier::SwitchStateSerial => {
250                if value.len() < 5 {
251                    return Err(MessageError::MessageTooShort);
252                }
253                Ok(Self::SwitchStateSerial {
254                    serial: u32::from_le_bytes(value[1..5].try_into().unwrap()),
255                })
256            }
257            LssCommandSpecifier::SwitchStateResponse => {
258                if value.len() < 5 {
259                    return Err(MessageError::MessageTooShort);
260                }
261                Ok(Self::SwitchStateVendor {
262                    vendor_id: u32::from_le_bytes(value[1..5].try_into().unwrap()),
263                })
264            }
265            // IdentifySlave is only used in a response
266            LssCommandSpecifier::IdentifySlave => {
267                Err(MessageError::UnexpectedLssCommand { value: value[0] })
268            }
269            LssCommandSpecifier::FastScan => {
270                if value.len() < 8 {
271                    return Err(MessageError::MessageTooShort);
272                }
273                Ok(Self::FastScan {
274                    id: u32::from_le_bytes([value[1], value[2], value[3], value[4]]),
275                    bit_check: value[5],
276                    sub: value[6],
277                    next: value[7],
278                })
279            }
280            LssCommandSpecifier::InquireVendor => Ok(LssRequest::InquireVendor),
281            LssCommandSpecifier::InquireProduct => Ok(LssRequest::InquireProduct),
282            LssCommandSpecifier::InquireRev => Ok(LssRequest::InquireRev),
283            LssCommandSpecifier::InquireSerial => Ok(LssRequest::InquireSerial),
284            LssCommandSpecifier::InquireNodeId => Ok(LssRequest::InquireNodeId),
285        }
286    }
287}
288
289impl From<LssRequest> for CanMessage {
290    fn from(value: LssRequest) -> Self {
291        let mut data = [0u8; 8];
292        match value {
293            LssRequest::SwitchModeGlobal { mode } => {
294                data[0] = LssCommandSpecifier::SwitchModeGlobal as u8;
295                data[1] = mode;
296            }
297            LssRequest::ConfigureNodeId { node_id } => {
298                data[0] = LssCommandSpecifier::ConfigureNodeId as u8;
299                data[1] = node_id;
300            }
301            LssRequest::ConfigureBitTiming { table, index } => {
302                data[0] = LssCommandSpecifier::ConfigureBitTiming as u8;
303                data[1] = table;
304                data[2] = index;
305            }
306            LssRequest::StoreConfiguration => {
307                data[0] = LssCommandSpecifier::StoreConfiguration as u8;
308            }
309            LssRequest::ActivateBitTiming { delay } => {
310                data[0] = LssCommandSpecifier::ActivateBitTiming as u8;
311                let delay_bytes = delay.to_le_bytes();
312                data[1] = delay_bytes[0];
313                data[2] = delay_bytes[1];
314            }
315            LssRequest::SwitchStateVendor { vendor_id } => {
316                data[0] = LssCommandSpecifier::SwitchStateVendor as u8;
317                data[1..5].copy_from_slice(&vendor_id.to_le_bytes());
318            }
319            LssRequest::SwitchStateProduct { product_code } => {
320                data[0] = LssCommandSpecifier::SwitchStateProduct as u8;
321                data[1..5].copy_from_slice(&product_code.to_le_bytes());
322            }
323            LssRequest::SwitchStateRevision { revision } => {
324                data[0] = LssCommandSpecifier::SwitchStateRev as u8;
325                data[1..5].copy_from_slice(&revision.to_le_bytes());
326            }
327            LssRequest::SwitchStateSerial { serial } => {
328                data[0] = LssCommandSpecifier::SwitchStateSerial as u8;
329                data[1..5].copy_from_slice(&serial.to_le_bytes());
330            }
331            LssRequest::InquireVendor => {
332                data[0] = LssCommandSpecifier::InquireVendor as u8;
333            }
334            LssRequest::InquireProduct => {
335                data[0] = LssCommandSpecifier::InquireProduct as u8;
336            }
337            LssRequest::InquireRev => {
338                data[0] = LssCommandSpecifier::InquireRev as u8;
339            }
340            LssRequest::InquireSerial => {
341                data[0] = LssCommandSpecifier::InquireSerial as u8;
342            }
343            LssRequest::InquireNodeId => {
344                data[0] = LssCommandSpecifier::InquireNodeId as u8;
345            }
346            LssRequest::FastScan {
347                id,
348                bit_check,
349                sub,
350                next,
351            } => {
352                data[0] = LssCommandSpecifier::FastScan as u8;
353                data[1..5].copy_from_slice(&id.to_le_bytes());
354                data[5] = bit_check;
355                data[6] = sub;
356                data[7] = next;
357            }
358        }
359        CanMessage::new(LSS_REQ_ID, &data)
360    }
361}
362
363/// An LSS response message sent from the Slave to Master
364#[derive(Clone, Copy, Debug, PartialEq)]
365#[cfg_attr(feature = "defmt", derive(defmt::Format))]
366pub enum LssResponse {
367    /// Sent when a slave's identity matches a FastScan request
368    IdentifySlave,
369    /// Sent  in response to a [`LssRequest::SwitchStateSerial`] when it recognizes its
370    /// identity
371    SwitchStateResponse,
372    /// Sent in response to a [`LssRequest::ConfigureNodeId`]
373    ConfigureNodeIdAck {
374        /// The error code
375        error: u8,
376        /// The manufacturer special error code
377        ///
378        /// Valid when error is 255, otherwise it's a don't care
379        spec_error: u8,
380    },
381    /// Sent in response to a [`LssRequest::ConfigureBitTiming`]
382    ConfigureBitTimingAck {
383        /// The error code
384        error: u8,
385        /// The manufacturer special error code
386        ///
387        /// Valid when error is 255, otherwise it's a don't care
388        spec_error: u8,
389    },
390    /// Sent in response to a [`LssRequest::StoreConfiguration`]
391    StoreConfigurationAck {
392        /// The error code
393        error: u8,
394        /// The manufacturer special error code
395        ///
396        /// Valid when error is 255, otherwise it's a don't care
397        spec_error: u8,
398    },
399    /// Sent in response to a [`LssRequest::InquireVendor`]
400    InquireVendorAck {
401        /// The vendor id of the responding node
402        vendor_id: u32,
403    },
404    /// Sent in response to a [`LssRequest::InquireProduct`]
405    InquireProductAck {
406        /// The product code of the responding node
407        product_code: u32,
408    },
409    /// Sent in response to a [`LssRequest::InquireRev`]
410    InquireRevAck {
411        /// The revision number of the responding node
412        revision: u32,
413    },
414    /// Sent in response to a [`LssRequest::InquireSerial`]
415    InquireSerialAck {
416        /// The serial number of the responding node
417        serial_number: u32,
418    },
419    /// Sent in response to a [`LssRequest::InquireNodeId`]
420    InquireNodeIdAck {
421        /// The node ID of the responding node
422        node_id: u8,
423    },
424}
425
426impl TryFrom<&[u8]> for LssResponse {
427    type Error = MessageError;
428
429    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
430        if value.is_empty() {
431            return Err(MessageError::MessageTooShort);
432        }
433        let cs = LssCommandSpecifier::from_byte(value[0])?;
434        match cs {
435            LssCommandSpecifier::IdentifySlave => Ok(Self::IdentifySlave {}),
436            LssCommandSpecifier::SwitchStateResponse => Ok(Self::SwitchStateResponse {}),
437            LssCommandSpecifier::ConfigureNodeId => {
438                if value.len() < 3 {
439                    return Err(MessageError::MessageTooShort);
440                }
441                Ok(Self::ConfigureNodeIdAck {
442                    error: value[1],
443                    spec_error: value[2],
444                })
445            }
446            LssCommandSpecifier::ConfigureBitTiming => {
447                if value.len() < 3 {
448                    return Err(MessageError::MessageTooShort);
449                }
450                Ok(Self::ConfigureBitTimingAck {
451                    error: value[1],
452                    spec_error: value[2],
453                })
454            }
455            LssCommandSpecifier::StoreConfiguration => {
456                if value.len() < 3 {
457                    return Err(MessageError::MessageTooShort);
458                }
459                Ok(Self::StoreConfigurationAck {
460                    error: value[1],
461                    spec_error: value[2],
462                })
463            }
464            LssCommandSpecifier::InquireVendor => {
465                if value.len() < 5 {
466                    return Err(MessageError::MessageTooShort);
467                }
468                Ok(Self::InquireVendorAck {
469                    vendor_id: u32::from_le_bytes([value[1], value[2], value[3], value[4]]),
470                })
471            }
472            LssCommandSpecifier::InquireProduct => {
473                if value.len() < 5 {
474                    return Err(MessageError::MessageTooShort);
475                }
476                Ok(Self::InquireProductAck {
477                    product_code: u32::from_le_bytes([value[1], value[2], value[3], value[4]]),
478                })
479            }
480            LssCommandSpecifier::InquireRev => {
481                if value.len() < 5 {
482                    return Err(MessageError::MessageTooShort);
483                }
484                Ok(Self::InquireRevAck {
485                    revision: u32::from_le_bytes([value[1], value[2], value[3], value[4]]),
486                })
487            }
488            LssCommandSpecifier::InquireSerial => {
489                if value.len() < 5 {
490                    return Err(MessageError::MessageTooShort);
491                }
492                Ok(Self::InquireSerialAck {
493                    serial_number: u32::from_le_bytes([value[1], value[2], value[3], value[4]]),
494                })
495            }
496            LssCommandSpecifier::InquireNodeId => {
497                if value.len() < 2 {
498                    return Err(MessageError::MessageTooShort);
499                }
500                Ok(Self::InquireNodeIdAck { node_id: value[1] })
501            }
502            _ => Err(MessageError::UnexpectedLssCommand { value: value[0] }),
503        }
504    }
505}
506
507impl TryFrom<CanMessage> for LssResponse {
508    type Error = MessageError;
509
510    fn try_from(value: CanMessage) -> Result<Self, Self::Error> {
511        if value.id != LSS_RESP_ID {
512            return Err(MessageError::UnexpectedId {
513                cob_id: value.id,
514                expected: LSS_RESP_ID,
515            });
516        }
517        LssResponse::try_from(&value.data[..])
518    }
519}
520
521impl LssResponse {
522    /// Convert an LssReponse to a CanMessage
523    pub fn to_can_message(self: &LssResponse, id: CanId) -> CanMessage {
524        // LSS messages are required to always be 8 bytes long. For...some reason.
525        let mut msg = CanMessage::new(id, &[0; 8]);
526        match self {
527            LssResponse::IdentifySlave => {
528                msg.data[0] = LssCommandSpecifier::IdentifySlave as u8;
529            }
530            LssResponse::SwitchStateResponse => {
531                msg.data[0] = LssCommandSpecifier::SwitchStateResponse as u8;
532            }
533            LssResponse::ConfigureNodeIdAck { error, spec_error } => {
534                msg.data[0] = LssCommandSpecifier::ConfigureNodeId as u8;
535                msg.data[1] = *error;
536                msg.data[2] = *spec_error;
537            }
538            LssResponse::ConfigureBitTimingAck { error, spec_error } => {
539                msg.data[0] = LssCommandSpecifier::ConfigureBitTiming as u8;
540                msg.data[1] = *error;
541                msg.data[2] = *spec_error;
542            }
543            LssResponse::StoreConfigurationAck { error, spec_error } => {
544                msg.data[0] = LssCommandSpecifier::StoreConfiguration as u8;
545                msg.data[1] = *error;
546                msg.data[2] = *spec_error;
547            }
548            LssResponse::InquireVendorAck { vendor_id } => {
549                msg.data[0] = LssCommandSpecifier::InquireVendor as u8;
550                msg.data[1..5].copy_from_slice(&vendor_id.to_le_bytes());
551            }
552            LssResponse::InquireProductAck { product_code } => {
553                msg.data[0] = LssCommandSpecifier::InquireProduct as u8;
554                msg.data[1..5].copy_from_slice(&product_code.to_le_bytes());
555            }
556            LssResponse::InquireRevAck { revision } => {
557                msg.data[0] = LssCommandSpecifier::InquireRev as u8;
558                msg.data[1..5].copy_from_slice(&revision.to_le_bytes());
559            }
560            LssResponse::InquireSerialAck { serial_number } => {
561                msg.data[0] = LssCommandSpecifier::InquireSerial as u8;
562                msg.data[1..5].copy_from_slice(&serial_number.to_le_bytes());
563            }
564            LssResponse::InquireNodeIdAck { node_id } => {
565                msg.data[0] = LssCommandSpecifier::InquireNodeId as u8;
566                msg.data[1] = *node_id;
567            }
568        }
569        msg
570    }
571}
572
573/// The possible LSS states
574#[derive(Debug, Clone, Copy, PartialEq)]
575#[repr(u8)]
576pub enum LssState {
577    /// The default state of a node.
578    Waiting = 0,
579    /// The state of a node which has been "activated" and is ready for configuring or querying via
580    /// LSS
581    Configuring = 1,
582}
583
584impl LssState {
585    /// Create an LSS state from a mode byte
586    pub fn from_byte(b: u8) -> Result<Self, MessageError> {
587        match b {
588            0x00 => Ok(Self::Waiting),
589            0x01 => Ok(Self::Configuring),
590            _ => Err(MessageError::InvalidField),
591        }
592    }
593}
594
595/// Represents the 128-bit identity in its four components
596///
597/// The node identity is stored in the 0x1018 record object, and it is used for the LSS protocol
598///
599/// The vendor_id, product_code, and revision are configured in the device config file. The serial
600/// number must be set by the application to a unique value. This can be done, e.g., using a UID
601/// register on the MCU, or by loading a previously programmed value from flash. It is important
602/// that each device on the bus have a unique identity.
603#[derive(Debug, Clone, Copy, PartialEq)]
604pub struct LssIdentity {
605    /// A number indicating the vendor of the device
606    pub vendor_id: u32,
607    /// A number indicating a product / model of the device
608    pub product_code: u32,
609    /// A number indicating the revision of the product
610    pub revision: u32,
611    /// A serial number which should be unique among all devices for a given vendor/product/revision
612    /// combination
613    pub serial: u32,
614}
615
616impl LssIdentity {
617    /// Create a new LssIdentity object
618    pub fn new(vendor_id: u32, product_code: u32, revision: u32, serial: u32) -> Self {
619        Self {
620            vendor_id,
621            product_code,
622            revision,
623            serial,
624        }
625    }
626
627    /// Read the LssIdentity by offset as if it were a [u32; 4] array
628    pub fn by_addr(&self, addr: u8) -> u32 {
629        match addr {
630            0 => self.vendor_id,
631            1 => self.product_code,
632            2 => self.revision,
633            3 => self.serial,
634            _ => panic!("Invalid LSS identity address"),
635        }
636    }
637}