use super::ApplicationLayer;
use crate::error::{AutomotiveError, Result};
use crate::transport::TransportLayer;
use crate::types::{Config, Frame};
pub const SID_DIAGNOSTIC_SESSION_CONTROL: u8 = 0x10;
pub const SID_ECU_RESET: u8 = 0x11;
pub const SID_SECURITY_ACCESS: u8 = 0x27;
pub const SID_TESTER_PRESENT: u8 = 0x3E;
pub const SID_READ_DATA_BY_ID: u8 = 0x22;
pub const SID_WRITE_DATA_BY_ID: u8 = 0x2E;
pub const SID_CLEAR_DIAGNOSTIC_INFO: u8 = 0x14;
pub const SID_READ_DTC: u8 = 0x19;
pub const SID_COMMUNICATION_CONTROL: u8 = 0x28;
pub const SID_AUTHENTICATION: u8 = 0x29;
pub const SID_READ_DATA_BY_PERIODIC_ID: u8 = 0x2A;
pub const SID_DYNAMICALLY_DEFINE_DATA_ID: u8 = 0x2C;
pub const SID_READ_MEMORY_BY_ADDRESS: u8 = 0x23;
pub const SID_WRITE_MEMORY_BY_ADDRESS: u8 = 0x3D;
pub const SID_READ_SCALING_DATA_BY_ID: u8 = 0x24;
pub const SID_INPUT_OUTPUT_CONTROL_BY_ID: u8 = 0x2F;
pub const SID_ROUTINE_CONTROL: u8 = 0x31;
pub const SID_REQUEST_DOWNLOAD: u8 = 0x34;
pub const SID_REQUEST_UPLOAD: u8 = 0x35;
pub const SID_TRANSFER_DATA: u8 = 0x36;
pub const SID_REQUEST_TRANSFER_EXIT: u8 = 0x37;
#[derive(Debug, Clone, PartialEq)]
pub enum UdsResponseType {
Positive,
Negative(u8), }
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum UdsSessionType {
Default = 0x01,
Programming = 0x02,
Extended = 0x03,
SafetySystem = 0x04,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum UdsResetType {
HardReset = 0x01,
KeyOffOnReset = 0x02,
SoftReset = 0x03,
EnableRapidPowerShutdown = 0x04,
DisableRapidPowerShutdown = 0x05,
}
pub const NRC_GENERAL_REJECT: u8 = 0x10;
pub const NRC_SERVICE_NOT_SUPPORTED: u8 = 0x11;
pub const NRC_SUB_FUNCTION_NOT_SUPPORTED: u8 = 0x12;
pub const NRC_INCORRECT_MESSAGE_LENGTH: u8 = 0x13;
pub const NRC_CONDITIONS_NOT_CORRECT: u8 = 0x22;
pub const NRC_REQUEST_SEQUENCE_ERROR: u8 = 0x24;
pub const NRC_REQUEST_OUT_OF_RANGE: u8 = 0x31;
pub const NRC_SECURITY_ACCESS_DENIED: u8 = 0x33;
pub const NRC_INVALID_KEY: u8 = 0x35;
pub const NRC_EXCEEDED_NUMBER_OF_ATTEMPTS: u8 = 0x36;
pub const NRC_RESPONSE_PENDING: u8 = 0x78;
#[derive(Debug, Clone)]
pub struct UdsRequest {
pub service_id: u8,
pub parameters: Vec<u8>,
}
#[derive(Debug, Clone)]
pub struct UdsResponse {
pub service_id: u8,
pub data: Vec<u8>,
}
#[derive(Debug, Clone)]
pub struct SessionStatus {
pub session_type: UdsSessionType,
pub security_level: u8,
pub last_activity: std::time::Instant,
pub tester_present_sent: bool,
}
impl Default for SessionStatus {
fn default() -> Self {
Self {
session_type: UdsSessionType::Default,
security_level: 0,
last_activity: std::time::Instant::now(),
tester_present_sent: false,
}
}
}
#[derive(Debug, Clone)]
pub struct UdsConfig {
pub timeout_ms: u32,
pub p2_timeout_ms: u32,
pub p2_star_timeout_ms: u32,
pub s3_client_timeout_ms: u32,
pub tester_present_interval_ms: u32,
}
impl Config for UdsConfig {
fn validate(&self) -> Result<()> {
Ok(())
}
}
impl Default for UdsConfig {
fn default() -> Self {
Self {
timeout_ms: 1000,
p2_timeout_ms: 5000,
p2_star_timeout_ms: 5000,
s3_client_timeout_ms: 5000,
tester_present_interval_ms: 2000,
}
}
}
pub struct Uds<T: TransportLayer> {
config: UdsConfig,
transport: T,
pub status: SessionStatus, is_open: bool,
handling_session_timing: bool, }
impl<T: TransportLayer> Uds<T> {
pub fn with_transport(config: UdsConfig, transport: T) -> Self {
Self {
config,
transport,
status: SessionStatus::default(),
is_open: false,
handling_session_timing: false,
}
}
pub fn change_session(&mut self, session_type: UdsSessionType) -> Result<()> {
let request = UdsRequest {
service_id: SID_DIAGNOSTIC_SESSION_CONTROL,
parameters: vec![session_type as u8],
};
let response = self.send_request(&request)?;
if response.data.is_empty() {
Err(AutomotiveError::InvalidParameter)
} else {
self.status.session_type = session_type;
self.status.last_activity = std::time::Instant::now();
Ok(())
}
}
pub fn ecu_reset(&mut self, reset_type: UdsResetType) -> Result<()> {
let request = UdsRequest {
service_id: SID_ECU_RESET,
parameters: vec![reset_type as u8],
};
let response = self.send_request(&request)?;
if response.data.is_empty() {
Ok(())
} else {
Err(AutomotiveError::UdsError("Failed to reset ECU".into()))
}
}
pub fn read_data_by_id(&mut self, did: u16) -> Result<Vec<u8>> {
let request = UdsRequest {
service_id: SID_READ_DATA_BY_ID,
parameters: vec![(did >> 8) as u8, did as u8],
};
let response = self.send_request(&request)?;
if response.data.is_empty() {
Err(AutomotiveError::UdsError("Failed to read data".into()))
} else {
Ok(response.data)
}
}
pub fn write_data_by_id(&mut self, did: u16, data: &[u8]) -> Result<()> {
let mut request_data = vec![(did >> 8) as u8, did as u8];
request_data.extend_from_slice(data);
let request = UdsRequest {
service_id: SID_WRITE_DATA_BY_ID,
parameters: request_data,
};
let response = self.send_request(&request)?;
if response.data.is_empty() {
Ok(())
} else {
Err(AutomotiveError::UdsError("Failed to write data".into()))
}
}
pub fn tester_present(&mut self) -> Result<()> {
let request = UdsRequest {
service_id: SID_TESTER_PRESENT,
parameters: vec![],
};
let response = self.send_request(&request)?;
if response.data.is_empty() {
self.status.tester_present_sent = true;
Ok(())
} else {
Err(AutomotiveError::UdsError(
"Failed to send tester present".into(),
))
}
}
pub fn security_access(&mut self, level: u8, key_fn: impl Fn(&[u8]) -> Vec<u8>) -> Result<()> {
let request = UdsRequest {
service_id: SID_SECURITY_ACCESS,
parameters: vec![2 * level - 1],
};
let response = self.send_request(&request)?;
if response.data.is_empty() {
Err(AutomotiveError::UdsError("Failed to get seed".into()))
} else {
let key = key_fn(&response.data);
let request = UdsRequest {
service_id: SID_SECURITY_ACCESS,
parameters: key,
};
let response = self.send_request(&request)?;
if response.data.is_empty() {
self.status.security_level = level;
self.status.last_activity = std::time::Instant::now();
Ok(())
} else {
Err(AutomotiveError::UdsError("Invalid key".into()))
}
}
}
pub fn routine_control(
&mut self,
_routine_type: u8,
routine_id: u16,
data: &[u8],
) -> Result<Vec<u8>> {
let mut request_data = vec![(routine_id >> 8) as u8, routine_id as u8];
request_data.extend_from_slice(data);
let request = UdsRequest {
service_id: SID_ROUTINE_CONTROL,
parameters: request_data,
};
let response = self.send_request(&request)?;
if response.data.is_empty() {
Err(AutomotiveError::UdsError("Routine control failed".into()))
} else {
Ok(response.data[2..].to_vec())
}
}
pub fn io_control(
&mut self,
did: u16,
control_param: u8,
control_state: &[u8],
) -> Result<Vec<u8>> {
let mut request_data = vec![(did >> 8) as u8, did as u8, control_param];
request_data.extend_from_slice(control_state);
let request = UdsRequest {
service_id: SID_INPUT_OUTPUT_CONTROL_BY_ID,
parameters: request_data,
};
let response = self.send_request(&request)?;
if response.data.is_empty() {
Ok(vec![])
} else {
Ok(response.data[3..].to_vec())
}
}
pub fn read_memory(&mut self, address: u32, size: u16) -> Result<Vec<u8>> {
let request_data = vec![
4, 2, (address >> 24) as u8,
(address >> 16) as u8,
(address >> 8) as u8,
address as u8,
(size >> 8) as u8,
size as u8,
];
let request = UdsRequest {
service_id: SID_READ_MEMORY_BY_ADDRESS,
parameters: request_data,
};
let response = self.send_request(&request)?;
if response.data.is_empty() {
Err(AutomotiveError::UdsError("Memory read failed".into()))
} else {
Ok(response.data)
}
}
pub fn write_memory(&mut self, address: u32, data: &[u8]) -> Result<()> {
let mut request_data = vec![
4, (data.len() as u16 >> 8) as u8,
data.len() as u8,
(address >> 24) as u8,
(address >> 16) as u8,
(address >> 8) as u8,
address as u8,
];
request_data.extend_from_slice(data);
let request = UdsRequest {
service_id: SID_WRITE_MEMORY_BY_ADDRESS,
parameters: request_data,
};
let response = self.send_request(&request)?;
if response.data.is_empty() {
Ok(())
} else {
Err(AutomotiveError::UdsError("Memory write failed".into()))
}
}
fn handle_session_timing(&mut self) -> Result<()> {
if self.handling_session_timing {
return Ok(());
}
self.handling_session_timing = true;
let now = std::time::Instant::now();
if self.status.session_type != UdsSessionType::Default
&& now.duration_since(self.status.last_activity).as_millis()
> self.config.timeout_ms as u128
{
self.status = SessionStatus::default();
self.handling_session_timing = false;
return Ok(());
}
if self.status.session_type != UdsSessionType::Default
&& (!self.status.tester_present_sent
|| now.duration_since(self.status.last_activity).as_millis()
> self.config.timeout_ms as u128)
{
let request = UdsRequest {
service_id: SID_TESTER_PRESENT,
parameters: vec![],
};
self.transport.write_frame(&Frame {
id: 0,
data: vec![request.service_id],
timestamp: 0,
is_extended: false,
is_fd: false,
})?;
let response = self.transport.read_frame()?;
if response.data.len() >= 1 && response.data[0] == request.service_id + 0x40 {
self.status.tester_present_sent = true;
}
}
self.handling_session_timing = false;
Ok(())
}
}
impl<T: TransportLayer> ApplicationLayer for Uds<T> {
type Config = UdsConfig;
type Request = UdsRequest;
type Response = UdsResponse;
fn new(config: Self::Config) -> Result<Self> {
Err(AutomotiveError::NotInitialized) }
fn open(&mut self) -> Result<()> {
if self.is_open {
return Ok(());
}
self.transport.open()?;
self.is_open = true;
Ok(())
}
fn close(&mut self) -> Result<()> {
self.is_open = false;
Ok(())
}
fn send_request(&mut self, request: &Self::Request) -> Result<Self::Response> {
if !self.is_open {
return Err(AutomotiveError::NotInitialized);
}
let mut data = vec![request.service_id];
data.extend_from_slice(&request.parameters);
self.transport.write_frame(&Frame {
id: 0,
data,
timestamp: 0,
is_extended: false,
is_fd: false,
})?;
let response = self.transport.read_frame()?;
if response.data.is_empty() {
return Err(AutomotiveError::InvalidParameter);
}
Ok(UdsResponse {
service_id: response.data[0],
data: response.data[1..].to_vec(),
})
}
fn set_timeout(&mut self, timeout_ms: u32) -> Result<()> {
if !self.is_open {
return Err(AutomotiveError::NotInitialized);
}
self.transport.set_timeout(timeout_ms)
}
}