#![no_std]
pub mod uds29 {
use embedded_can::{ExtendedId, Id as CanId};
use embedded_can_interface::{Id, IdMask, IdMaskFilter};
pub const EXT_ID_MAX: u32 = 0x1FFF_FFFF;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Uds29Kind {
Physical,
Functional,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Uds29Id {
pub kind: Uds29Kind,
pub target: u8,
pub source: u8,
}
pub const PHYS_BASE: u32 = 0x18DA_0000;
pub const FUNC_BASE: u32 = 0x18DB_0000;
pub const BASE_MASK: u32 = 0x1FFF_0000;
pub const TARGET_MASK: u32 = 0x1FFF_FF00;
#[inline]
pub const fn encode_id_raw(kind: Uds29Kind, target: u8, source: u8) -> u32 {
let base = match kind {
Uds29Kind::Physical => PHYS_BASE,
Uds29Kind::Functional => FUNC_BASE,
};
base | ((target as u32) << 8) | (source as u32)
}
#[inline]
pub const fn decode_id_raw(raw: u32) -> Option<Uds29Id> {
if (raw & !EXT_ID_MAX) != 0 {
return None;
}
let base = raw & BASE_MASK;
let kind = if base == PHYS_BASE {
Uds29Kind::Physical
} else if base == FUNC_BASE {
Uds29Kind::Functional
} else {
return None;
};
let target = ((raw >> 8) & 0xFF) as u8;
let source = (raw & 0xFF) as u8;
Some(Uds29Id {
kind,
target,
source,
})
}
#[inline]
pub fn encode_id(kind: Uds29Kind, target: u8, source: u8) -> Id {
let raw = encode_id_raw(kind, target, source);
Id::Extended(ExtendedId::new(raw).expect("UDS 29-bit ID must fit in 29 bits"))
}
#[inline]
pub fn decode_id(id: CanId) -> Option<Uds29Id> {
match id {
CanId::Extended(ext) => decode_id_raw(ext.as_raw()),
CanId::Standard(_) => None,
}
}
#[inline]
pub const fn encode_phys_id_raw(target: u8, source: u8) -> u32 {
encode_id_raw(Uds29Kind::Physical, target, source)
}
#[inline]
pub const fn encode_func_id_raw(target: u8, source: u8) -> u32 {
encode_id_raw(Uds29Kind::Functional, target, source)
}
#[inline]
pub const fn decode_phys_id_raw(raw: u32) -> Option<(u8, u8)> {
match decode_id_raw(raw) {
Some(Uds29Id {
kind: Uds29Kind::Physical,
target,
source,
}) => Some((target, source)),
_ => None,
}
}
#[inline]
pub const fn decode_func_id_raw(raw: u32) -> Option<(u8, u8)> {
match decode_id_raw(raw) {
Some(Uds29Id {
kind: Uds29Kind::Functional,
target,
source,
}) => Some((target, source)),
_ => None,
}
}
#[inline]
pub fn encode_phys_id(target: u8, source: u8) -> Id {
encode_id(Uds29Kind::Physical, target, source)
}
#[inline]
pub fn encode_func_id(target: u8, source: u8) -> Id {
encode_id(Uds29Kind::Functional, target, source)
}
#[inline]
pub fn decode_phys_id(id: CanId) -> Option<(u8, u8)> {
decode_id(id).and_then(|v| match v.kind {
Uds29Kind::Physical => Some((v.target, v.source)),
Uds29Kind::Functional => None,
})
}
#[inline]
pub fn decode_func_id(id: CanId) -> Option<(u8, u8)> {
decode_id(id).and_then(|v| match v.kind {
Uds29Kind::Functional => Some((v.target, v.source)),
Uds29Kind::Physical => None,
})
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct AcceptanceFilters {
filters: [IdMaskFilter; 2],
len: usize,
}
impl AcceptanceFilters {
#[inline]
pub fn as_slice(&self) -> &[IdMaskFilter] {
&self.filters[..self.len]
}
}
fn filter_for_target_kind(kind: Uds29Kind, target: u8) -> IdMaskFilter {
let base = match kind {
Uds29Kind::Physical => PHYS_BASE,
Uds29Kind::Functional => FUNC_BASE,
};
let raw = base | ((target as u32) << 8);
IdMaskFilter {
id: Id::Extended(ExtendedId::new(raw).expect("UDS base+target must fit in 29 bits")),
mask: IdMask::Extended(TARGET_MASK),
}
}
#[inline]
pub fn filter_phys_for_target(target: u8) -> IdMaskFilter {
filter_for_target_kind(Uds29Kind::Physical, target)
}
#[inline]
pub fn filter_func_for_target(target: u8) -> IdMaskFilter {
filter_for_target_kind(Uds29Kind::Functional, target)
}
#[inline]
pub fn filters_for_targets(phys_target: u8, func_target: Option<u8>) -> AcceptanceFilters {
let phys = filter_phys_for_target(phys_target);
if let Some(ft) = func_target {
let func = filter_func_for_target(ft);
AcceptanceFilters {
filters: [phys, func],
len: 2,
}
} else {
AcceptanceFilters {
filters: [phys, phys],
len: 1,
}
}
}
}
#[cfg(test)]
mod tests {
use super::uds29;
use embedded_can::{ExtendedId, Id as CanId};
#[test]
fn uds29_round_trip_raw() {
let (ta, sa) = (0xF1u8, 0x33u8);
let raw = uds29::encode_phys_id_raw(ta, sa);
assert_eq!(raw, 0x18DA_F133);
assert_eq!(uds29::decode_phys_id_raw(raw), Some((ta, sa)));
}
#[test]
fn uds29_functional_round_trip_raw() {
let (ta, sa) = (0x33u8, 0xF1u8);
let raw = uds29::encode_func_id_raw(ta, sa);
assert_eq!(raw, 0x18DB_33F1);
assert_eq!(uds29::decode_func_id_raw(raw), Some((ta, sa)));
}
#[test]
fn uds29_decode_rejects_non_extended_range() {
let raw = 0xFFFF_FFFF;
assert_eq!(uds29::decode_phys_id_raw(raw), None);
}
#[test]
fn uds29_decode_rejects_wrong_base() {
let raw = 0x18DB_0000 | (0x12u32 << 8) | 0x34;
assert_eq!(uds29::decode_phys_id_raw(raw), None);
assert_eq!(uds29::decode_func_id_raw(raw), Some((0x12, 0x34)));
}
#[test]
fn uds29_decode_from_embedded_can_id() {
let raw = uds29::encode_phys_id_raw(0xAA, 0x55);
let id = CanId::Extended(ExtendedId::new(raw).unwrap());
assert_eq!(uds29::decode_phys_id(id), Some((0xAA, 0x55)));
}
}