use crate::error::{AutomotiveError, Result};
use crate::physical::PhysicalLayer;
use crate::transport::TransportLayer;
use crate::types::{Config, Frame};
pub const LIN_SYNC_BYTE: u8 = 0x55;
pub const LIN_ID_MASK: u8 = 0x3F;
pub const LIN_P0_FLAG: u8 = 6;
pub const LIN_P1_FLAG: u8 = 7;
pub const LIN_BREAK_BYTE: u8 = 0x00;
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum LinFrameType {
Classic,
Enhanced,
}
#[derive(Debug, Clone, Copy, PartialEq)]
enum LinState {
Idle,
Break,
Sync,
Id,
Data,
Checksum,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum LinFrameSlot {
Unconditional,
Event,
Sporadic,
Diagnostic,
}
#[derive(Debug, Clone)]
pub struct LinConfig {
pub timeout_ms: u32,
pub frame_type: LinFrameType,
}
impl Config for LinConfig {
fn validate(&self) -> Result<()> {
Ok(())
}
}
impl Default for LinConfig {
fn default() -> Self {
Self {
timeout_ms: 1000,
frame_type: LinFrameType::Classic,
}
}
}
pub struct Lin<P: PhysicalLayer> {
config: LinConfig,
physical: P,
is_open: bool,
state: LinState,
}
impl<P: PhysicalLayer> Lin<P> {
pub fn with_physical(config: LinConfig, physical: P) -> Self {
Self {
config,
physical,
is_open: false,
state: LinState::Idle,
}
}
pub fn send_header(&mut self, pid: u8) -> Result<()> {
if !self.is_open {
return Err(AutomotiveError::NotInitialized);
}
self.physical.send_frame(&Frame {
id: 0,
data: vec![LIN_BREAK_BYTE],
timestamp: 0,
is_extended: false,
is_fd: false,
})?;
self.physical.send_frame(&Frame {
id: 0,
data: vec![LIN_SYNC_BYTE],
timestamp: 0,
is_extended: false,
is_fd: false,
})?;
let parity = calculate_parity(pid);
let pid_with_parity = pid | parity;
self.physical.send_frame(&Frame {
id: 0,
data: vec![pid_with_parity],
timestamp: 0,
is_extended: false,
is_fd: false,
})?;
Ok(())
}
pub fn send_response(&mut self, pid: u8, data: &[u8]) -> Result<()> {
if !self.is_open {
return Err(AutomotiveError::NotInitialized);
}
if data.len() > 8 {
return Err(AutomotiveError::InvalidParameter);
}
self.physical.send_frame(&Frame {
id: 0,
data: data.to_vec(),
timestamp: 0,
is_extended: false,
is_fd: false,
})?;
let checksum = if self.config.frame_type == LinFrameType::Enhanced {
calculate_enhanced_checksum(pid, data)
} else {
calculate_classic_checksum(data)
};
self.physical.send_frame(&Frame {
id: 0,
data: vec![checksum],
timestamp: 0,
is_extended: false,
is_fd: false,
})?;
Ok(())
}
pub fn read_response(&mut self, timeout_ms: u32) -> Result<Vec<u8>> {
if !self.is_open {
return Err(AutomotiveError::NotInitialized);
}
let mut response = Vec::new();
let mut checksum = None;
for _ in 0..8 {
match self.physical.receive_frame() {
Ok(frame) => {
if frame.data.is_empty() {
break;
}
response.extend_from_slice(&frame.data);
}
Err(AutomotiveError::Timeout) => break,
Err(e) => return Err(e),
}
}
match self.physical.receive_frame() {
Ok(frame) => {
if !frame.data.is_empty() {
checksum = Some(frame.data[0]);
}
}
Err(AutomotiveError::Timeout) => {}
Err(e) => return Err(e),
}
if let Some(received_checksum) = checksum {
let expected_checksum = if self.config.frame_type == LinFrameType::Enhanced {
calculate_enhanced_checksum(0, &response) } else {
calculate_classic_checksum(&response)
};
if received_checksum != expected_checksum {
return Err(AutomotiveError::ChecksumError);
}
}
Ok(response)
}
}
impl<P: PhysicalLayer> TransportLayer for Lin<P> {
type Config = LinConfig;
fn new(_config: Self::Config) -> Result<Self> {
Err(AutomotiveError::NotInitialized) }
fn open(&mut self) -> Result<()> {
if self.is_open {
return Ok(());
}
self.physical.set_timeout(self.config.timeout_ms)?;
self.is_open = true;
Ok(())
}
fn close(&mut self) -> Result<()> {
self.is_open = false;
Ok(())
}
fn write_frame(&mut self, frame: &Frame) -> Result<()> {
if !self.is_open {
return Err(AutomotiveError::NotInitialized);
}
self.physical.send_frame(frame)
}
fn read_frame(&mut self) -> Result<Frame> {
if !self.is_open {
return Err(AutomotiveError::NotInitialized);
}
self.physical.receive_frame()
}
fn set_timeout(&mut self, timeout_ms: u32) -> Result<()> {
if !self.is_open {
return Err(AutomotiveError::NotInitialized);
}
self.physical.set_timeout(timeout_ms)
}
}
fn calculate_parity(pid: u8) -> u8 {
let p0 = (pid ^ (pid >> 1) ^ (pid >> 2) ^ (pid >> 4)) & 1;
let p1 = !((pid >> 1) ^ (pid >> 3) ^ (pid >> 4) ^ (pid >> 5)) & 1;
(p0 << 6) | (p1 << 7)
}
fn calculate_classic_checksum(data: &[u8]) -> u8 {
let mut sum: u16 = 0;
for &byte in data {
sum = sum.wrapping_add(byte as u16);
if sum > 0xFF {
sum = (sum & 0xFF) + 1;
}
}
(!sum as u8)
}
fn calculate_enhanced_checksum(pid: u8, data: &[u8]) -> u8 {
let mut sum: u16 = pid as u16;
for &byte in data {
sum = sum.wrapping_add(byte as u16);
if sum > 0xFF {
sum = (sum & 0xFF) + 1;
}
}
(!sum as u8)
}