Skip to main content

can_iso_tp/
address.rs

1//! Helpers for ISO-TP addressing modes.
2
3use embedded_can::ExtendedId;
4use embedded_can_interface::Id;
5
6use crate::config::IsoTpConfig;
7use crate::errors::IsoTpError;
8
9/// Address type used in 29-bit fixed/mixed addressing.
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum TargetAddressType {
12    /// 1-to-1 communication (physical addressing).
13    Physical,
14    /// 1-to-n communication (functional addressing).
15    Functional,
16}
17
18/// Transmit addressing parameters (CAN ID + optional first payload byte).
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20pub struct TxAddress {
21    /// CAN identifier used for transmission.
22    pub id: Id,
23    /// Optional ISO-TP addressing byte that is placed at the start of the CAN payload.
24    ///
25    /// When set, the Protocol Control Information (PCI) starts at byte offset 1.
26    pub addr: Option<u8>,
27}
28
29/// Receive addressing parameters (CAN ID + optional expected first payload byte).
30#[derive(Debug, Clone, Copy, PartialEq, Eq)]
31pub struct RxAddress {
32    /// CAN identifier expected when receiving.
33    pub id: Id,
34    /// Optional ISO-TP addressing byte that must match the first payload byte.
35    ///
36    /// When set, the Protocol Control Information (PCI) is expected at byte offset 1.
37    pub addr: Option<u8>,
38}
39
40/// Combine a transmit and receive addressing scheme (asymmetric addressing).
41#[derive(Debug, Clone, Copy, PartialEq, Eq)]
42pub struct AsymmetricAddress {
43    /// Transmit addressing parameters.
44    pub tx: TxAddress,
45    /// Receive addressing parameters.
46    pub rx: RxAddress,
47}
48
49/// Addressing parameters for a single ISO-TP node.
50///
51/// This is a convenient “fully expanded” form that can be converted into an [`IsoTpConfig`] via
52/// `IsoTpConfig::from(IsoTpAddress)`.
53#[derive(Debug, Clone, Copy, PartialEq, Eq)]
54pub struct IsoTpAddress {
55    /// Transmit CAN identifier.
56    pub tx_id: Id,
57    /// Receive CAN identifier.
58    pub rx_id: Id,
59    /// Optional transmit addressing byte.
60    pub tx_addr: Option<u8>,
61    /// Optional receive addressing byte.
62    pub rx_addr: Option<u8>,
63}
64
65fn ext_id(raw: u32) -> Result<Id, IsoTpError<()>> {
66    ExtendedId::new(raw)
67        .map(Id::Extended)
68        .ok_or(IsoTpError::InvalidConfig)
69}
70
71fn fixed_base(target_type: TargetAddressType, physical_base: u32, functional_base: u32) -> u32 {
72    match target_type {
73        TargetAddressType::Physical => physical_base,
74        TargetAddressType::Functional => functional_base,
75    }
76}
77
78impl TxAddress {
79    /// Normal addressing (no extra addressing byte).
80    pub fn normal(id: Id) -> Self {
81        Self { id, addr: None }
82    }
83
84    /// Extended addressing (adds a target address byte when sending).
85    pub fn extended(id: Id, target_address: u8) -> Self {
86        Self {
87            id,
88            addr: Some(target_address),
89        }
90    }
91
92    /// Mixed 11-bit addressing (adds an address extension byte when sending).
93    pub fn mixed_11(id: Id, address_extension: u8) -> Self {
94        Self {
95            id,
96            addr: Some(address_extension),
97        }
98    }
99
100    /// Normal fixed 29-bit addressing.
101    ///
102    /// This constructs a 29-bit CAN ID according to ISO-TP’s fixed addressing scheme.
103    pub fn normal_fixed_29(
104        source_address: u8,
105        target_address: u8,
106        target_type: TargetAddressType,
107    ) -> Result<Self, IsoTpError<()>> {
108        let base = fixed_base(target_type, 0x18DA0000, 0x18DB0000);
109        let raw = base | ((target_address as u32) << 8) | source_address as u32;
110        Ok(Self {
111            id: ext_id(raw)?,
112            addr: None,
113        })
114    }
115
116    /// Mixed 29-bit addressing (fixed CAN ID + address extension byte).
117    pub fn mixed_29(
118        source_address: u8,
119        target_address: u8,
120        address_extension: u8,
121        target_type: TargetAddressType,
122    ) -> Result<Self, IsoTpError<()>> {
123        let base = fixed_base(target_type, 0x18CE0000, 0x18CD0000);
124        let raw = base | ((target_address as u32) << 8) | source_address as u32;
125        Ok(Self {
126            id: ext_id(raw)?,
127            addr: Some(address_extension),
128        })
129    }
130}
131
132impl RxAddress {
133    /// Normal addressing (no extra addressing byte).
134    pub fn normal(id: Id) -> Self {
135        Self { id, addr: None }
136    }
137
138    /// Extended addressing (expects a source address byte when receiving).
139    pub fn extended(id: Id, source_address: u8) -> Self {
140        Self {
141            id,
142            addr: Some(source_address),
143        }
144    }
145
146    /// Mixed 11-bit addressing (expects an address extension byte when receiving).
147    pub fn mixed_11(id: Id, address_extension: u8) -> Self {
148        Self {
149            id,
150            addr: Some(address_extension),
151        }
152    }
153
154    /// Normal fixed 29-bit addressing.
155    ///
156    /// This constructs a 29-bit CAN ID according to ISO-TP’s fixed addressing scheme.
157    pub fn normal_fixed_29(
158        source_address: u8,
159        target_address: u8,
160        target_type: TargetAddressType,
161    ) -> Result<Self, IsoTpError<()>> {
162        let base = fixed_base(target_type, 0x18DA0000, 0x18DB0000);
163        let raw = base | ((source_address as u32) << 8) | target_address as u32;
164        Ok(Self {
165            id: ext_id(raw)?,
166            addr: None,
167        })
168    }
169
170    /// Mixed 29-bit addressing (fixed CAN ID + address extension byte).
171    pub fn mixed_29(
172        source_address: u8,
173        target_address: u8,
174        address_extension: u8,
175        target_type: TargetAddressType,
176    ) -> Result<Self, IsoTpError<()>> {
177        let base = fixed_base(target_type, 0x18CE0000, 0x18CD0000);
178        let raw = base | ((source_address as u32) << 8) | target_address as u32;
179        Ok(Self {
180            id: ext_id(raw)?,
181            addr: Some(address_extension),
182        })
183    }
184}
185
186impl AsymmetricAddress {
187    /// Construct a new asymmetric address pair.
188    pub fn new(tx: TxAddress, rx: RxAddress) -> Self {
189        Self { tx, rx }
190    }
191}
192
193impl From<AsymmetricAddress> for IsoTpAddress {
194    fn from(value: AsymmetricAddress) -> Self {
195        Self {
196            tx_id: value.tx.id,
197            rx_id: value.rx.id,
198            tx_addr: value.tx.addr,
199            rx_addr: value.rx.addr,
200        }
201    }
202}
203
204impl IsoTpAddress {
205    /// Normal addressing with explicit Tx/Rx IDs.
206    pub fn normal(tx_id: Id, rx_id: Id) -> Self {
207        Self {
208            tx_id,
209            rx_id,
210            tx_addr: None,
211            rx_addr: None,
212        }
213    }
214
215    /// Extended addressing with explicit Tx/Rx IDs and explicit source/target address bytes.
216    pub fn extended(tx_id: Id, rx_id: Id, source_address: u8, target_address: u8) -> Self {
217        Self {
218            tx_id,
219            rx_id,
220            tx_addr: Some(target_address),
221            rx_addr: Some(source_address),
222        }
223    }
224
225    /// Mixed 11-bit addressing with explicit Tx/Rx IDs and a shared address extension byte.
226    pub fn mixed_11(tx_id: Id, rx_id: Id, address_extension: u8) -> Self {
227        Self {
228            tx_id,
229            rx_id,
230            tx_addr: Some(address_extension),
231            rx_addr: Some(address_extension),
232        }
233    }
234
235    /// Normal fixed 29-bit addressing.
236    pub fn normal_fixed_29(
237        source_address: u8,
238        target_address: u8,
239        target_type: TargetAddressType,
240    ) -> Result<Self, IsoTpError<()>> {
241        Ok(Self::from(AsymmetricAddress::new(
242            TxAddress::normal_fixed_29(source_address, target_address, target_type)?,
243            RxAddress::normal_fixed_29(source_address, target_address, target_type)?,
244        )))
245    }
246
247    /// Mixed 29-bit addressing.
248    pub fn mixed_29(
249        source_address: u8,
250        target_address: u8,
251        address_extension: u8,
252        target_type: TargetAddressType,
253    ) -> Result<Self, IsoTpError<()>> {
254        Ok(Self::from(AsymmetricAddress::new(
255            TxAddress::mixed_29(
256                source_address,
257                target_address,
258                address_extension,
259                target_type,
260            )?,
261            RxAddress::mixed_29(
262                source_address,
263                target_address,
264                address_extension,
265                target_type,
266            )?,
267        )))
268    }
269}
270
271impl From<IsoTpAddress> for IsoTpConfig {
272    fn from(value: IsoTpAddress) -> Self {
273        Self {
274            tx_id: value.tx_id,
275            rx_id: value.rx_id,
276            tx_addr: value.tx_addr,
277            rx_addr: value.rx_addr,
278            ..IsoTpConfig::default()
279        }
280    }
281}