1#![no_std]
14
15pub mod uds29 {
16 use embedded_can::{ExtendedId, Id as CanId};
17 use embedded_can_interface::{Id, IdMask, IdMaskFilter};
18
19 pub const EXT_ID_MAX: u32 = 0x1FFF_FFFF;
21
22 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
24 pub enum Uds29Kind {
25 Physical,
27 Functional,
29 }
30
31 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
33 pub struct Uds29Id {
34 pub kind: Uds29Kind,
35 pub target: u8,
36 pub source: u8,
37 }
38
39 pub const PHYS_BASE: u32 = 0x18DA_0000;
41 pub const FUNC_BASE: u32 = 0x18DB_0000;
43
44 pub const BASE_MASK: u32 = 0x1FFF_0000;
46 pub const TARGET_MASK: u32 = 0x1FFF_FF00;
48
49 #[inline]
51 pub const fn encode_id_raw(kind: Uds29Kind, target: u8, source: u8) -> u32 {
52 let base = match kind {
53 Uds29Kind::Physical => PHYS_BASE,
54 Uds29Kind::Functional => FUNC_BASE,
55 };
56 base | ((target as u32) << 8) | (source as u32)
57 }
58
59 #[inline]
61 pub const fn decode_id_raw(raw: u32) -> Option<Uds29Id> {
62 if (raw & !EXT_ID_MAX) != 0 {
63 return None;
64 }
65 let base = raw & BASE_MASK;
66 let kind = if base == PHYS_BASE {
67 Uds29Kind::Physical
68 } else if base == FUNC_BASE {
69 Uds29Kind::Functional
70 } else {
71 return None;
72 };
73
74 let target = ((raw >> 8) & 0xFF) as u8;
75 let source = (raw & 0xFF) as u8;
76 Some(Uds29Id {
77 kind,
78 target,
79 source,
80 })
81 }
82
83 #[inline]
85 pub fn encode_id(kind: Uds29Kind, target: u8, source: u8) -> Id {
86 let raw = encode_id_raw(kind, target, source);
87 Id::Extended(ExtendedId::new(raw).expect("UDS 29-bit ID must fit in 29 bits"))
88 }
89
90 #[inline]
92 pub fn decode_id(id: CanId) -> Option<Uds29Id> {
93 match id {
94 CanId::Extended(ext) => decode_id_raw(ext.as_raw()),
95 CanId::Standard(_) => None,
96 }
97 }
98
99 #[inline]
101 pub const fn encode_phys_id_raw(target: u8, source: u8) -> u32 {
102 encode_id_raw(Uds29Kind::Physical, target, source)
103 }
104
105 #[inline]
107 pub const fn encode_func_id_raw(target: u8, source: u8) -> u32 {
108 encode_id_raw(Uds29Kind::Functional, target, source)
109 }
110
111 #[inline]
113 pub const fn decode_phys_id_raw(raw: u32) -> Option<(u8, u8)> {
114 match decode_id_raw(raw) {
115 Some(Uds29Id {
116 kind: Uds29Kind::Physical,
117 target,
118 source,
119 }) => Some((target, source)),
120 _ => None,
121 }
122 }
123
124 #[inline]
126 pub const fn decode_func_id_raw(raw: u32) -> Option<(u8, u8)> {
127 match decode_id_raw(raw) {
128 Some(Uds29Id {
129 kind: Uds29Kind::Functional,
130 target,
131 source,
132 }) => Some((target, source)),
133 _ => None,
134 }
135 }
136
137 #[inline]
139 pub fn encode_phys_id(target: u8, source: u8) -> Id {
140 encode_id(Uds29Kind::Physical, target, source)
141 }
142
143 #[inline]
145 pub fn encode_func_id(target: u8, source: u8) -> Id {
146 encode_id(Uds29Kind::Functional, target, source)
147 }
148
149 #[inline]
151 pub fn decode_phys_id(id: CanId) -> Option<(u8, u8)> {
152 decode_id(id).and_then(|v| match v.kind {
153 Uds29Kind::Physical => Some((v.target, v.source)),
154 Uds29Kind::Functional => None,
155 })
156 }
157
158 #[inline]
160 pub fn decode_func_id(id: CanId) -> Option<(u8, u8)> {
161 decode_id(id).and_then(|v| match v.kind {
162 Uds29Kind::Functional => Some((v.target, v.source)),
163 Uds29Kind::Physical => None,
164 })
165 }
166
167 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
169 pub struct AcceptanceFilters {
170 filters: [IdMaskFilter; 2],
171 len: usize,
172 }
173
174 impl AcceptanceFilters {
175 #[inline]
176 pub fn as_slice(&self) -> &[IdMaskFilter] {
177 &self.filters[..self.len]
178 }
179 }
180
181 fn filter_for_target_kind(kind: Uds29Kind, target: u8) -> IdMaskFilter {
182 let base = match kind {
183 Uds29Kind::Physical => PHYS_BASE,
184 Uds29Kind::Functional => FUNC_BASE,
185 };
186 let raw = base | ((target as u32) << 8);
187 IdMaskFilter {
188 id: Id::Extended(ExtendedId::new(raw).expect("UDS base+target must fit in 29 bits")),
189 mask: IdMask::Extended(TARGET_MASK),
190 }
191 }
192
193 #[inline]
195 pub fn filter_phys_for_target(target: u8) -> IdMaskFilter {
196 filter_for_target_kind(Uds29Kind::Physical, target)
197 }
198
199 #[inline]
201 pub fn filter_func_for_target(target: u8) -> IdMaskFilter {
202 filter_for_target_kind(Uds29Kind::Functional, target)
203 }
204
205 #[inline]
209 pub fn filters_for_targets(phys_target: u8, func_target: Option<u8>) -> AcceptanceFilters {
210 let phys = filter_phys_for_target(phys_target);
211 if let Some(ft) = func_target {
212 let func = filter_func_for_target(ft);
213 AcceptanceFilters {
214 filters: [phys, func],
215 len: 2,
216 }
217 } else {
218 AcceptanceFilters {
219 filters: [phys, phys],
220 len: 1,
221 }
222 }
223 }
224}
225
226#[cfg(test)]
227mod tests {
228 use super::uds29;
229 use embedded_can::{ExtendedId, Id as CanId};
230
231 #[test]
232 fn uds29_round_trip_raw() {
233 let (ta, sa) = (0xF1u8, 0x33u8);
234 let raw = uds29::encode_phys_id_raw(ta, sa);
235 assert_eq!(raw, 0x18DA_F133);
236 assert_eq!(uds29::decode_phys_id_raw(raw), Some((ta, sa)));
237 }
238
239 #[test]
240 fn uds29_functional_round_trip_raw() {
241 let (ta, sa) = (0x33u8, 0xF1u8);
242 let raw = uds29::encode_func_id_raw(ta, sa);
243 assert_eq!(raw, 0x18DB_33F1);
244 assert_eq!(uds29::decode_func_id_raw(raw), Some((ta, sa)));
245 }
246
247 #[test]
248 fn uds29_decode_rejects_non_extended_range() {
249 let raw = 0xFFFF_FFFF;
250 assert_eq!(uds29::decode_phys_id_raw(raw), None);
251 }
252
253 #[test]
254 fn uds29_decode_rejects_wrong_base() {
255 let raw = 0x18DB_0000 | (0x12u32 << 8) | 0x34;
256 assert_eq!(uds29::decode_phys_id_raw(raw), None);
258 assert_eq!(uds29::decode_func_id_raw(raw), Some((0x12, 0x34)));
259 }
260
261 #[test]
262 fn uds29_decode_from_embedded_can_id() {
263 let raw = uds29::encode_phys_id_raw(0xAA, 0x55);
264 let id = CanId::Extended(ExtendedId::new(raw).unwrap());
265 assert_eq!(uds29::decode_phys_id(id), Some((0xAA, 0x55)));
266 }
267}