Add full automotive RTOS project
Add kernel (Cortex-M0/M3/M4, Tricore, S32K, RISC-V ports), drivers, middleware (CAN stack, diagnostics, safety), applications, board support, build/test tooling, and documentation.
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
/**
|
||||
* @file can_nm.h
|
||||
* @brief CAN Network Management (AUTOSAR-like) implementation
|
||||
*/
|
||||
|
||||
#ifndef CAN_NM_H
|
||||
#define CAN_NM_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "kernel.h"
|
||||
#include "can_driver.h"
|
||||
|
||||
/* CAN NM Configuration */
|
||||
#define CAN_NM_MAX_NODES 16
|
||||
#define CAN_NM_MAX_NETWORKS 4
|
||||
#define CAN_NM_MESSAGE_ID_BASE 0x400 /* Base ID for NM messages */
|
||||
#define CAN_NM_DEFAULT_TIMEOUT_MS 2000
|
||||
|
||||
/* CAN NM States */
|
||||
typedef enum {
|
||||
CAN_NM_BUS_SLEEP = 0,
|
||||
CAN_NM_PREPARE_BUS_SLEEP = 1,
|
||||
CAN_NM_READY_SLEEP = 2,
|
||||
CAN_NM_NORMAL_OPERATION = 3,
|
||||
CAN_NM_REPEAT_MESSAGE = 4,
|
||||
CAN_NM_SYNCHRONIZE = 5,
|
||||
CAN_NM_OFFLINE = 6
|
||||
} CanNmState_t;
|
||||
|
||||
/* CAN NM Message Types */
|
||||
typedef enum {
|
||||
CAN_NM_MSG_ALIVE = 0,
|
||||
CAN_NM_MSG_RING = 1,
|
||||
CAN_NM_MSG_LIMPHOME = 2,
|
||||
CAN_NM_MSG_SLEEP_ACK = 3,
|
||||
CAN_NM_MSG_SLEEP_CONF = 4
|
||||
} CanNmMessageType_t;
|
||||
|
||||
/* CAN NM Configuration */
|
||||
typedef struct {
|
||||
uint8_t node_id;
|
||||
uint8_t network_id;
|
||||
uint32_t message_id;
|
||||
uint32_t timeout_ms;
|
||||
uint32_t repeat_message_time_ms;
|
||||
bool is_coordinator;
|
||||
uint8_t sleep_ack_timeout_ms;
|
||||
} CanNmConfig_t;
|
||||
|
||||
/* CAN NM Node Information */
|
||||
typedef struct {
|
||||
uint8_t node_id;
|
||||
bool is_present;
|
||||
bool is_awake;
|
||||
uint32_t last_message_time;
|
||||
CanNmState_t state;
|
||||
} CanNmNodeInfo_t;
|
||||
|
||||
/* CAN NM Callbacks */
|
||||
typedef void (*CanNmNetworkStateChangedCallback_t)(CanNmState_t new_state);
|
||||
typedef void (*CanNmNodeStateChangedCallback_t)(uint8_t node_id, bool is_awake);
|
||||
typedef void (*CanNmBusSleepRequestCallback_t)(void);
|
||||
typedef void (*CanNmWakeupIndicationCallback_t)(void);
|
||||
|
||||
/* CAN NM Functions */
|
||||
KernelStatus_t can_nm_init(const CanNmConfig_t* config);
|
||||
KernelStatus_t can_nm_deinit(void);
|
||||
KernelStatus_t can_nm_start(void);
|
||||
KernelStatus_t can_nm_stop(void);
|
||||
KernelStatus_t can_nm_request_bus_sleep(void);
|
||||
KernelStatus_t can_nm_network_release(void);
|
||||
KernelStatus_t can_nm_network_request(void);
|
||||
CanNmState_t can_nm_get_state(void);
|
||||
KernelStatus_t can_nm_get_node_info(uint8_t node_id, CanNmNodeInfo_t* info);
|
||||
KernelStatus_t can_nm_register_callbacks(
|
||||
CanNmNetworkStateChangedCallback_t network_callback,
|
||||
CanNmNodeStateChangedCallback_t node_callback,
|
||||
CanNmBusSleepRequestCallback_t sleep_callback,
|
||||
CanNmWakeupIndicationCallback_t wakeup_callback);
|
||||
void can_nm_process_rx_message(const CanMessage_t* message);
|
||||
void can_nm_main_function(void); /* Periodic processing */
|
||||
bool can_nm_is_bus_awake(void);
|
||||
|
||||
#endif /* CAN_NM_H */
|
||||
@@ -0,0 +1,115 @@
|
||||
/**
|
||||
* @file can_tp.h
|
||||
* @brief CAN Transport Protocol (ISO 15765-2) implementation
|
||||
*/
|
||||
|
||||
#ifndef CAN_TP_H
|
||||
#define CAN_TP_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "kernel.h"
|
||||
#include "can_driver.h"
|
||||
|
||||
/* CAN TP Configuration */
|
||||
#define CAN_TP_MAX_CONNECTIONS 8
|
||||
#define CAN_TP_MAX_PAYLOAD_SIZE 4096
|
||||
#define CAN_TP_DEFAULT_TIMEOUT_MS 1000
|
||||
#define CAN_TP_STMIN_DEFAULT 10 /* Minimum separation time in ms */
|
||||
#define CAN_TP_BS_DEFAULT 8 /* Block size */
|
||||
|
||||
/* CAN TP Addressing Formats */
|
||||
typedef enum {
|
||||
CAN_TP_ADDRESSING_NORMAL = 0, /* Standard 11-bit or 29-bit */
|
||||
CAN_TP_ADDRESSING_NORMAL_FIXED = 1, /* Fixed addressing */
|
||||
CAN_TP_ADDRESSING_EXTENDED = 2, /* Extended addressing */
|
||||
CAN_TP_ADDRESSING_MIXED = 3 /* Mixed addressing */
|
||||
} CanTpAddressingFormat_t;
|
||||
|
||||
/* CAN TP Frame Types */
|
||||
typedef enum {
|
||||
CAN_TP_FRAME_SINGLE = 0, /* Single Frame */
|
||||
CAN_TP_FRAME_FIRST = 1, /* First Frame */
|
||||
CAN_TP_FRAME_CONSECUTIVE = 2, /* Consecutive Frame */
|
||||
CAN_TP_FRAME_FLOW_CONTROL = 3 /* Flow Control */
|
||||
} CanTpFrameType_t;
|
||||
|
||||
/* CAN TP Flow Control Status */
|
||||
typedef enum {
|
||||
CAN_TP_FC_CONTINUE = 0, /* Continue to send */
|
||||
CAN_TP_FC_WAIT = 1, /* Wait */
|
||||
CAN_TP_FC_OVERFLOW = 2 /* Overflow/Abort */
|
||||
} CanTpFlowControlStatus_t;
|
||||
|
||||
/* CAN TP States */
|
||||
typedef enum {
|
||||
CAN_TP_IDLE = 0,
|
||||
CAN_TP_SEND_IN_PROGRESS = 1,
|
||||
CAN_TP_RECEIVE_IN_PROGRESS = 2,
|
||||
CAN_TP_WAIT_FLOW_CONTROL = 3,
|
||||
CAN_TP_WAIT_CONSECUTIVE = 4,
|
||||
CAN_TP_TIMEOUT = 5,
|
||||
CAN_TP_ERROR = 6
|
||||
} CanTpState_t;
|
||||
|
||||
/* CAN TP Message Structure */
|
||||
typedef struct {
|
||||
uint32_t message_id;
|
||||
uint8_t* data;
|
||||
uint16_t length;
|
||||
uint8_t addressing_format;
|
||||
uint32_t source_address;
|
||||
uint32_t target_address;
|
||||
} CanTpMessage_t;
|
||||
|
||||
/* CAN TP Connection */
|
||||
typedef struct {
|
||||
uint8_t connection_id;
|
||||
CanTpState_t state;
|
||||
CanTpMessage_t current_message;
|
||||
uint16_t current_index;
|
||||
uint8_t sequence_number;
|
||||
uint8_t block_counter;
|
||||
uint32_t timeout_timer;
|
||||
uint8_t stmin;
|
||||
uint8_t block_size;
|
||||
bool is_sender;
|
||||
Semaphore_t flow_control_semaphore;
|
||||
Semaphore_t complete_semaphore;
|
||||
Mutex_t connection_mutex;
|
||||
} CanTpConnection_t;
|
||||
|
||||
/* CAN TP Configuration */
|
||||
typedef struct {
|
||||
CanTpAddressingFormat_t addressing_format;
|
||||
uint32_t source_address;
|
||||
uint32_t target_address;
|
||||
uint32_t timeout_ms;
|
||||
uint8_t stmin;
|
||||
uint8_t block_size;
|
||||
bool padding_enabled;
|
||||
uint8_t padding_byte;
|
||||
} CanTpConfig_t;
|
||||
|
||||
/* CAN TP Callbacks */
|
||||
typedef void (*CanTpMessageReceivedCallback_t)(const CanTpMessage_t* message);
|
||||
typedef void (*CanTpMessageSentCallback_t)(uint8_t connection_id, bool success);
|
||||
typedef void (*CanTpErrorCallback_t)(uint8_t connection_id, uint32_t error_code);
|
||||
|
||||
/* CAN TP Functions */
|
||||
KernelStatus_t can_tp_init(const CanTpConfig_t* config);
|
||||
KernelStatus_t can_tp_deinit(void);
|
||||
KernelStatus_t can_tp_send_message(const CanTpMessage_t* message,
|
||||
uint32_t timeout_ms);
|
||||
KernelStatus_t can_tp_receive_message(CanTpMessage_t* message,
|
||||
uint32_t timeout_ms);
|
||||
KernelStatus_t can_tp_register_callbacks(
|
||||
CanTpMessageReceivedCallback_t rx_callback,
|
||||
CanTpMessageSentCallback_t tx_callback,
|
||||
CanTpErrorCallback_t error_callback);
|
||||
void can_tp_process_rx_indication(const CanMessage_t* can_message);
|
||||
void can_tp_process_tx_confirmation(uint8_t mailbox);
|
||||
void can_tp_main_function(void); /* Periodic processing */
|
||||
CanTpState_t can_tp_get_state(uint8_t connection_id);
|
||||
|
||||
#endif /* CAN_TP_H */
|
||||
@@ -0,0 +1,134 @@
|
||||
/**
|
||||
* @file uds.h
|
||||
* @brief Unified Diagnostic Services (ISO 14229) implementation
|
||||
*/
|
||||
|
||||
#ifndef UDS_H
|
||||
#define UDS_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "kernel.h"
|
||||
#include "can_tp.h"
|
||||
|
||||
/* UDS Configuration */
|
||||
#define UDS_MAX_SESSIONS 8
|
||||
#define UDS_MAX_SECURITY_LEVELS 5
|
||||
#define UDS_MAX_DTCS 50
|
||||
#define UDS_MAX_DATA_SIZE 4096
|
||||
|
||||
/* UDS Service IDs */
|
||||
typedef enum {
|
||||
UDS_SID_DIAGNOSTIC_SESSION_CONTROL = 0x10,
|
||||
UDS_SID_ECU_RESET = 0x11,
|
||||
UDS_SID_SECURITY_ACCESS = 0x27,
|
||||
UDS_SID_COMMUNICATION_CONTROL = 0x28,
|
||||
UDS_SID_READ_DATA_BY_IDENTIFIER = 0x22,
|
||||
UDS_SID_WRITE_DATA_BY_IDENTIFIER = 0x2E,
|
||||
UDS_SID_IO_CONTROL_BY_IDENTIFIER = 0x2F,
|
||||
UDS_SID_ROUTINE_CONTROL = 0x31,
|
||||
UDS_SID_REQUEST_DOWNLOAD = 0x34,
|
||||
UDS_SID_REQUEST_UPLOAD = 0x35,
|
||||
UDS_SID_TRANSFER_DATA = 0x36,
|
||||
UDS_SID_REQUEST_TRANSFER_EXIT = 0x37,
|
||||
UDS_SID_READ_DTC_INFORMATION = 0x19,
|
||||
UDS_SID_CLEAR_DTC_INFORMATION = 0x14,
|
||||
UDS_SID_READ_DATA_BY_PERIODIC_IDENTIFIER = 0x2A,
|
||||
UDS_SID_DYNAMICALLY_DEFINE_DATA_IDENTIFIER = 0x2C,
|
||||
UDS_SID_TESTER_PRESENT = 0x3E,
|
||||
UDS_SID_CONTROL_DTC_SETTING = 0x85
|
||||
} UdsServiceId_t;
|
||||
|
||||
/* UDS Response Codes */
|
||||
typedef enum {
|
||||
UDS_RESPONSE_POSITIVE = 0x00,
|
||||
UDS_RESPONSE_GENERAL_REJECT = 0x10,
|
||||
UDS_RESPONSE_SERVICE_NOT_SUPPORTED = 0x11,
|
||||
UDS_RESPONSE_SUBFUNCTION_NOT_SUPPORTED = 0x12,
|
||||
UDS_RESPONSE_INCORRECT_MESSAGE_LENGTH = 0x13,
|
||||
UDS_RESPONSE_CONDITIONS_NOT_CORRECT = 0x22,
|
||||
UDS_RESPONSE_REQUEST_SEQUENCE_ERROR = 0x24,
|
||||
UDS_RESPONSE_REQUEST_OUT_OF_RANGE = 0x31,
|
||||
UDS_RESPONSE_SECURITY_ACCESS_DENIED = 0x33,
|
||||
UDS_RESPONSE_INVALID_KEY = 0x35,
|
||||
UDS_RESPONSE_EXCEED_NUMBER_OF_ATTEMPTS = 0x36,
|
||||
UDS_RESPONSE_REQUIRED_TIME_DELAY_NOT_EXPIRED = 0x37,
|
||||
UDS_RESPONSE_UPLOAD_DOWNLOAD_NOT_ACCEPTED = 0x70,
|
||||
UDS_RESPONSE_TRANSFER_DATA_SUSPENDED = 0x71,
|
||||
UDS_RESPONSE_GENERAL_PROGRAMMING_FAILURE = 0x72,
|
||||
UDS_RESPONSE_WRONG_BLOCK_SEQUENCE_COUNTER = 0x73,
|
||||
UDS_RESPONSE_RESPONSE_PENDING = 0x78,
|
||||
UDS_RESPONSE_SUBFUNCTION_NOT_SUPPORTED_IN_ACTIVE_SESSION = 0x7E,
|
||||
UDS_RESPONSE_SERVICE_NOT_SUPPORTED_IN_ACTIVE_SESSION = 0x7F
|
||||
} UdsResponseCode_t;
|
||||
|
||||
/* UDS Sessions */
|
||||
typedef enum {
|
||||
UDS_SESSION_DEFAULT = 0x01,
|
||||
UDS_SESSION_PROGRAMMING = 0x02,
|
||||
UDS_SESSION_EXTENDED = 0x03,
|
||||
UDS_SESSION_SAFETY_SYSTEM = 0x04
|
||||
} UdsSessionType_t;
|
||||
|
||||
/* UDS Security Levels */
|
||||
typedef enum {
|
||||
UDS_SECURITY_LOCKED = 0x00,
|
||||
UDS_SECURITY_LEVEL_1 = 0x01,
|
||||
UDS_SECURITY_LEVEL_2 = 0x02,
|
||||
UDS_SECURITY_LEVEL_3 = 0x03,
|
||||
UDS_SECURITY_LEVEL_4 = 0x04,
|
||||
UDS_SECURITY_LEVEL_5 = 0x05
|
||||
} UdsSecurityLevel_t;
|
||||
|
||||
/* UDS Message Structure */
|
||||
typedef struct {
|
||||
uint8_t service_id;
|
||||
uint8_t sub_function;
|
||||
uint8_t* data;
|
||||
uint16_t length;
|
||||
uint32_t data_identifier;
|
||||
UdsSessionType_t session_type;
|
||||
UdsSecurityLevel_t security_level;
|
||||
} UdsMessage_t;
|
||||
|
||||
/* UDS Configuration */
|
||||
typedef struct {
|
||||
uint32_t source_address;
|
||||
uint32_t target_address;
|
||||
uint32_t timeout_ms;
|
||||
UdsSessionType_t current_session;
|
||||
UdsSecurityLevel_t current_security_level;
|
||||
bool security_access_enabled;
|
||||
uint32_t p2_server_max_ms;
|
||||
uint32_t p2_star_server_max_ms;
|
||||
} UdsConfig_t;
|
||||
|
||||
/* UDS Callbacks */
|
||||
typedef void (*UdsServiceCallback_t)(const UdsMessage_t* request,
|
||||
UdsMessage_t* response);
|
||||
typedef void (*UdsSecurityAccessCallback_t)(uint8_t security_level,
|
||||
const uint8_t* seed,
|
||||
uint8_t* key,
|
||||
uint8_t length,
|
||||
bool* access_granted);
|
||||
typedef void (*UdsSessionChangedCallback_t)(UdsSessionType_t old_session,
|
||||
UdsSessionType_t new_session);
|
||||
|
||||
/* UDS Functions */
|
||||
KernelStatus_t uds_init(const UdsConfig_t* config);
|
||||
KernelStatus_t uds_deinit(void);
|
||||
KernelStatus_t uds_process_message(const UdsMessage_t* request,
|
||||
UdsMessage_t* response);
|
||||
KernelStatus_t uds_register_service_callback(UdsServiceId_t service_id,
|
||||
UdsServiceCallback_t callback);
|
||||
KernelStatus_t uds_register_security_callback(
|
||||
UdsSecurityAccessCallback_t callback);
|
||||
KernelStatus_t uds_register_session_callback(
|
||||
UdsSessionChangedCallback_t callback);
|
||||
KernelStatus_t uds_set_session(UdsSessionType_t session);
|
||||
UdsSessionType_t uds_get_session(void);
|
||||
KernelStatus_t uds_set_security_level(UdsSecurityLevel_t level);
|
||||
UdsSecurityLevel_t uds_get_security_level(void);
|
||||
void uds_main_function(void); /* Periodic processing */
|
||||
|
||||
#endif /* UDS_H */
|
||||
@@ -0,0 +1,330 @@
|
||||
/**
|
||||
* @file can_nm.c
|
||||
* @brief CAN Network Management implementation
|
||||
*/
|
||||
|
||||
#include "can_nm.h"
|
||||
#include <string.h>
|
||||
|
||||
/* CAN NM State */
|
||||
typedef struct {
|
||||
bool initialized;
|
||||
CanNmConfig_t config;
|
||||
CanNmState_t current_state;
|
||||
CanNmNodeInfo_t nodes[CAN_NM_MAX_NODES];
|
||||
CanNmNetworkStateChangedCallback_t network_callback;
|
||||
CanNmNodeStateChangedCallback_t node_callback;
|
||||
CanNmBusSleepRequestCallback_t sleep_callback;
|
||||
CanNmWakeupIndicationCallback_t wakeup_callback;
|
||||
uint32_t state_timer;
|
||||
uint32_t repeat_message_timer;
|
||||
uint8_t repeat_message_count;
|
||||
Mutex_t mutex;
|
||||
} CanNmState_t;
|
||||
|
||||
static CanNmState_t can_nm_state;
|
||||
|
||||
/* Initialize CAN NM */
|
||||
KernelStatus_t can_nm_init(const CanNmConfig_t* config) {
|
||||
if (config == NULL || can_nm_state.initialized) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
/* Copy configuration */
|
||||
memcpy(&can_nm_state.config, config, sizeof(CanNmConfig_t));
|
||||
|
||||
/* Initialize state */
|
||||
can_nm_state.current_state = CAN_NM_OFFLINE;
|
||||
can_nm_state.state_timer = 0;
|
||||
can_nm_state.repeat_message_timer = 0;
|
||||
can_nm_state.repeat_message_count = 0;
|
||||
|
||||
/* Initialize nodes */
|
||||
for (int i = 0; i < CAN_NM_MAX_NODES; i++) {
|
||||
can_nm_state.nodes[i].node_id = i;
|
||||
can_nm_state.nodes[i].is_present = false;
|
||||
can_nm_state.nodes[i].is_awake = false;
|
||||
can_nm_state.nodes[i].last_message_time = 0;
|
||||
can_nm_state.nodes[i].state = CAN_NM_OFFLINE;
|
||||
}
|
||||
|
||||
/* Create mutex */
|
||||
mutex_create(&can_nm_state.mutex, false);
|
||||
|
||||
can_nm_state.initialized = true;
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Start CAN NM */
|
||||
KernelStatus_t can_nm_start(void) {
|
||||
if (!can_nm_state.initialized) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
mutex_lock(&can_nm_state.mutex, 100);
|
||||
|
||||
can_nm_state.current_state = CAN_NM_REPEAT_MESSAGE;
|
||||
can_nm_state.repeat_message_count = 0;
|
||||
can_nm_state.repeat_message_timer = kernel_get_tick_count();
|
||||
|
||||
mutex_unlock(&can_nm_state.mutex);
|
||||
|
||||
/* Send first alive message */
|
||||
can_nm_send_message(CAN_NM_MSG_ALIVE);
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Stop CAN NM */
|
||||
KernelStatus_t can_nm_stop(void) {
|
||||
if (!can_nm_state.initialized) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
mutex_lock(&can_nm_state.mutex, 100);
|
||||
can_nm_state.current_state = CAN_NM_OFFLINE;
|
||||
mutex_unlock(&can_nm_state.mutex);
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Request Bus Sleep */
|
||||
KernelStatus_t can_nm_request_bus_sleep(void) {
|
||||
if (!can_nm_state.initialized) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
mutex_lock(&can_nm_state.mutex, 100);
|
||||
|
||||
/* Send sleep indication */
|
||||
can_nm_send_message(CAN_NM_MSG_SLEEP_ACK);
|
||||
|
||||
can_nm_state.current_state = CAN_NM_READY_SLEEP;
|
||||
|
||||
mutex_unlock(&can_nm_state.mutex);
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Network Release */
|
||||
KernelStatus_t can_nm_network_release(void) {
|
||||
if (!can_nm_state.initialized) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
mutex_lock(&can_nm_state.mutex, 100);
|
||||
|
||||
/* Check if all nodes are ready to sleep */
|
||||
bool all_ready = true;
|
||||
for (int i = 0; i < CAN_NM_MAX_NODES; i++) {
|
||||
if (can_nm_state.nodes[i].is_present &&
|
||||
can_nm_state.nodes[i].state != CAN_NM_READY_SLEEP) {
|
||||
all_ready = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (all_ready || can_nm_state.config.is_coordinator) {
|
||||
can_nm_state.current_state = CAN_NM_PREPARE_BUS_SLEEP;
|
||||
|
||||
/* Send sleep confirmation */
|
||||
can_nm_send_message(CAN_NM_MSG_SLEEP_CONF);
|
||||
|
||||
/* Call sleep callback */
|
||||
if (can_nm_state.sleep_callback != NULL) {
|
||||
can_nm_state.sleep_callback();
|
||||
}
|
||||
|
||||
can_nm_state.current_state = CAN_NM_BUS_SLEEP;
|
||||
|
||||
/* Call network state changed callback */
|
||||
if (can_nm_state.network_callback != NULL) {
|
||||
can_nm_state.network_callback(CAN_NM_BUS_SLEEP);
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&can_nm_state.mutex);
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Network Request */
|
||||
KernelStatus_t can_nm_network_request(void) {
|
||||
if (!can_nm_state.initialized) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
mutex_lock(&can_nm_state.mutex, 100);
|
||||
|
||||
if (can_nm_state.current_state == CAN_NM_BUS_SLEEP) {
|
||||
can_nm_state.current_state = CAN_NM_REPEAT_MESSAGE;
|
||||
can_nm_state.repeat_message_count = 0;
|
||||
|
||||
/* Send wakeup indication */
|
||||
can_nm_send_message(CAN_NM_MSG_ALIVE);
|
||||
|
||||
/* Call wakeup callback */
|
||||
if (can_nm_state.wakeup_callback != NULL) {
|
||||
can_nm_state.wakeup_callback();
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&can_nm_state.mutex);
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Process Received NM Message */
|
||||
void can_nm_process_rx_message(const CanMessage_t* message) {
|
||||
if (!can_nm_state.initialized || message == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check if NM message */
|
||||
if (message->id.id < CAN_NM_MESSAGE_ID_BASE ||
|
||||
message->id.id >= CAN_NM_MESSAGE_ID_BASE + CAN_NM_MAX_NODES) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Extract node ID and message type */
|
||||
uint8_t node_id = message->id.id - CAN_NM_MESSAGE_ID_BASE;
|
||||
uint8_t message_type = message->data[0] & 0x0F;
|
||||
|
||||
mutex_lock(&can_nm_state.mutex, 100);
|
||||
|
||||
/* Update node information */
|
||||
can_nm_state.nodes[node_id].is_present = true;
|
||||
can_nm_state.nodes[node_id].last_message_time = kernel_get_tick_count();
|
||||
|
||||
/* Process message type */
|
||||
switch (message_type) {
|
||||
case CAN_NM_MSG_ALIVE:
|
||||
can_nm_state.nodes[node_id].is_awake = true;
|
||||
can_nm_state.nodes[node_id].state = CAN_NM_NORMAL_OPERATION;
|
||||
|
||||
if (can_nm_state.node_callback != NULL) {
|
||||
can_nm_state.node_callback(node_id, true);
|
||||
}
|
||||
break;
|
||||
|
||||
case CAN_NM_MSG_RING:
|
||||
/* Ring message - keep awake */
|
||||
can_nm_state.nodes[node_id].is_awake = true;
|
||||
break;
|
||||
|
||||
case CAN_NM_MSG_SLEEP_ACK:
|
||||
can_nm_state.nodes[node_id].state = CAN_NM_READY_SLEEP;
|
||||
break;
|
||||
|
||||
case CAN_NM_MSG_SLEEP_CONF:
|
||||
can_nm_state.nodes[node_id].state = CAN_NM_BUS_SLEEP;
|
||||
can_nm_state.nodes[node_id].is_awake = false;
|
||||
|
||||
if (can_nm_state.node_callback != NULL) {
|
||||
can_nm_state.node_callback(node_id, false);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
mutex_unlock(&can_nm_state.mutex);
|
||||
}
|
||||
|
||||
/* CAN NM Main Function */
|
||||
void can_nm_main_function(void) {
|
||||
if (!can_nm_state.initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t current_time = kernel_get_tick_count();
|
||||
|
||||
mutex_lock(&can_nm_state.mutex, 100);
|
||||
|
||||
switch (can_nm_state.current_state) {
|
||||
case CAN_NM_REPEAT_MESSAGE:
|
||||
/* Send repeat messages */
|
||||
if ((current_time - can_nm_state.repeat_message_timer) >=
|
||||
can_nm_state.config.repeat_message_time_ms) {
|
||||
can_nm_send_message(CAN_NM_MSG_RING);
|
||||
can_nm_state.repeat_message_timer = current_time;
|
||||
can_nm_state.repeat_message_count++;
|
||||
|
||||
/* Transition to normal operation after repeat messages */
|
||||
if (can_nm_state.repeat_message_count >= 3) {
|
||||
can_nm_state.current_state = CAN_NM_NORMAL_OPERATION;
|
||||
|
||||
if (can_nm_state.network_callback != NULL) {
|
||||
can_nm_state.network_callback(CAN_NM_NORMAL_OPERATION);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case CAN_NM_NORMAL_OPERATION:
|
||||
/* Send periodic ring messages */
|
||||
if ((current_time - can_nm_state.state_timer) >=
|
||||
can_nm_state.config.timeout_ms / 2) {
|
||||
can_nm_send_message(CAN_NM_MSG_RING);
|
||||
can_nm_state.state_timer = current_time;
|
||||
}
|
||||
break;
|
||||
|
||||
case CAN_NM_READY_SLEEP:
|
||||
/* Check if still ready to sleep */
|
||||
if ((current_time - can_nm_state.state_timer) >=
|
||||
can_nm_state.config.sleep_ack_timeout_ms) {
|
||||
can_nm_state.current_state = CAN_NM_NORMAL_OPERATION;
|
||||
}
|
||||
break;
|
||||
|
||||
case CAN_NM_PREPARE_BUS_SLEEP:
|
||||
/* Transition to bus sleep */
|
||||
if ((current_time - can_nm_state.state_timer) >=
|
||||
can_nm_state.config.timeout_ms) {
|
||||
can_nm_state.current_state = CAN_NM_BUS_SLEEP;
|
||||
|
||||
if (can_nm_state.network_callback != NULL) {
|
||||
can_nm_state.network_callback(CAN_NM_BUS_SLEEP);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* Check node timeouts */
|
||||
for (int i = 0; i < CAN_NM_MAX_NODES; i++) {
|
||||
if (can_nm_state.nodes[i].is_present &&
|
||||
can_nm_state.nodes[i].is_awake) {
|
||||
if ((current_time - can_nm_state.nodes[i].last_message_time) >
|
||||
can_nm_state.config.timeout_ms) {
|
||||
/* Node timeout */
|
||||
can_nm_state.nodes[i].is_present = false;
|
||||
can_nm_state.nodes[i].is_awake = false;
|
||||
|
||||
if (can_nm_state.node_callback != NULL) {
|
||||
can_nm_state.node_callback(i, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&can_nm_state.mutex);
|
||||
}
|
||||
|
||||
/* Send NM Message (internal) */
|
||||
static void can_nm_send_message(uint8_t message_type) {
|
||||
CanMessage_t message;
|
||||
message.id.id = can_nm_state.config.message_id +
|
||||
can_nm_state.config.node_id;
|
||||
message.id.is_extended = false;
|
||||
message.length = 8;
|
||||
message.data[0] = message_type;
|
||||
message.data[1] = can_nm_state.config.node_id;
|
||||
message.data[2] = can_nm_state.current_state;
|
||||
|
||||
can_send_message(&message, 100);
|
||||
}
|
||||
@@ -0,0 +1,367 @@
|
||||
/**
|
||||
* @file can_tp.c
|
||||
* @brief CAN Transport Protocol implementation
|
||||
*/
|
||||
|
||||
#include "can_tp.h"
|
||||
#include <string.h>
|
||||
|
||||
/* CAN TP State */
|
||||
typedef struct {
|
||||
bool initialized;
|
||||
CanTpConfig_t config;
|
||||
CanTpConnection_t connections[CAN_TP_MAX_CONNECTIONS];
|
||||
CanTpMessageReceivedCallback_t rx_callback;
|
||||
CanTpMessageSentCallback_t tx_callback;
|
||||
CanTpErrorCallback_t error_callback;
|
||||
Mutex_t global_mutex;
|
||||
} CanTpState_t;
|
||||
|
||||
static CanTpState_t can_tp_state;
|
||||
|
||||
/* Initialize CAN TP */
|
||||
KernelStatus_t can_tp_init(const CanTpConfig_t* config) {
|
||||
if (config == NULL || can_tp_state.initialized) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
/* Copy configuration */
|
||||
memcpy(&can_tp_state.config, config, sizeof(CanTpConfig_t));
|
||||
|
||||
/* Initialize connections */
|
||||
for (int i = 0; i < CAN_TP_MAX_CONNECTIONS; i++) {
|
||||
CanTpConnection_t* conn = &can_tp_state.connections[i];
|
||||
conn->connection_id = i;
|
||||
conn->state = CAN_TP_IDLE;
|
||||
conn->stmin = config->stmin;
|
||||
conn->block_size = config->block_size;
|
||||
|
||||
semaphore_create(&conn->flow_control_semaphore, SEMAPHORE_BINARY, 0, 1);
|
||||
semaphore_create(&conn->complete_semaphore, SEMAPHORE_BINARY, 0, 1);
|
||||
mutex_create(&conn->connection_mutex, false);
|
||||
}
|
||||
|
||||
/* Create global mutex */
|
||||
mutex_create(&can_tp_state.global_mutex, false);
|
||||
|
||||
can_tp_state.initialized = true;
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Send CAN TP Message */
|
||||
KernelStatus_t can_tp_send_message(const CanTpMessage_t* message,
|
||||
uint32_t timeout_ms) {
|
||||
if (!can_tp_state.initialized || message == NULL || message->data == NULL) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
/* Find free connection */
|
||||
CanTpConnection_t* conn = NULL;
|
||||
for (int i = 0; i < CAN_TP_MAX_CONNECTIONS; i++) {
|
||||
if (can_tp_state.connections[i].state == CAN_TP_IDLE) {
|
||||
conn = &can_tp_state.connections[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (conn == NULL) {
|
||||
return KERNEL_RESOURCE_BUSY;
|
||||
}
|
||||
|
||||
/* Lock connection */
|
||||
if (mutex_lock(&conn->connection_mutex, timeout_ms) != KERNEL_OK) {
|
||||
return KERNEL_TIMEOUT;
|
||||
}
|
||||
|
||||
/* Set up connection */
|
||||
conn->current_message = *message;
|
||||
conn->current_index = 0;
|
||||
conn->sequence_number = 0;
|
||||
conn->block_counter = 0;
|
||||
conn->is_sender = true;
|
||||
conn->state = CAN_TP_SEND_IN_PROGRESS;
|
||||
|
||||
/* Send single frame or first frame */
|
||||
CanMessage_t can_message;
|
||||
memset(&can_message, 0, sizeof(CanMessage_t));
|
||||
|
||||
if (message->length <= 7) {
|
||||
/* Single Frame */
|
||||
can_message.id.id = message->message_id;
|
||||
can_message.id.is_extended = true;
|
||||
can_message.length = message->length + 1;
|
||||
can_message.data[0] = (CAN_TP_FRAME_SINGLE << 4) | message->length;
|
||||
memcpy(&can_message.data[1], message->data, message->length);
|
||||
|
||||
/* Send message */
|
||||
if (can_send_message(&can_message, timeout_ms) != KERNEL_OK) {
|
||||
conn->state = CAN_TP_ERROR;
|
||||
mutex_unlock(&conn->connection_mutex);
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
conn->state = CAN_TP_IDLE;
|
||||
mutex_unlock(&conn->connection_mutex);
|
||||
|
||||
/* Signal completion */
|
||||
if (can_tp_state.tx_callback != NULL) {
|
||||
can_tp_state.tx_callback(conn->connection_id, true);
|
||||
}
|
||||
|
||||
return KERNEL_OK;
|
||||
} else {
|
||||
/* First Frame */
|
||||
can_message.id.id = message->message_id;
|
||||
can_message.id.is_extended = true;
|
||||
can_message.length = 8;
|
||||
can_message.data[0] = (CAN_TP_FRAME_FIRST << 4) | ((message->length >> 8) & 0x0F);
|
||||
can_message.data[1] = message->length & 0xFF;
|
||||
memcpy(&can_message.data[2], &message->data[0], 6);
|
||||
|
||||
/* Send first frame */
|
||||
if (can_send_message(&can_message, timeout_ms) != KERNEL_OK) {
|
||||
conn->state = CAN_TP_ERROR;
|
||||
mutex_unlock(&conn->connection_mutex);
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
conn->current_index = 6;
|
||||
conn->state = CAN_TP_WAIT_FLOW_CONTROL;
|
||||
}
|
||||
|
||||
/* Wait for flow control */
|
||||
if (semaphore_take(&conn->flow_control_semaphore, timeout_ms) != KERNEL_OK) {
|
||||
conn->state = CAN_TP_TIMEOUT;
|
||||
mutex_unlock(&conn->connection_mutex);
|
||||
return KERNEL_TIMEOUT;
|
||||
}
|
||||
|
||||
/* Send consecutive frames */
|
||||
while (conn->current_index < message->length) {
|
||||
/* Check block size */
|
||||
if (conn->block_counter >= conn->block_size && conn->block_size > 0) {
|
||||
/* Wait for another flow control */
|
||||
conn->block_counter = 0;
|
||||
if (semaphore_take(&conn->flow_control_semaphore, timeout_ms) != KERNEL_OK) {
|
||||
conn->state = CAN_TP_TIMEOUT;
|
||||
mutex_unlock(&conn->connection_mutex);
|
||||
return KERNEL_TIMEOUT;
|
||||
}
|
||||
}
|
||||
|
||||
/* Send consecutive frame */
|
||||
CanMessage_t consecutive_frame;
|
||||
consecutive_frame.id.id = message->message_id;
|
||||
consecutive_frame.id.is_extended = true;
|
||||
|
||||
uint16_t remaining = message->length - conn->current_index;
|
||||
uint8_t frame_length = (remaining > 7) ? 7 : remaining;
|
||||
|
||||
consecutive_frame.length = frame_length + 1;
|
||||
consecutive_frame.data[0] = (CAN_TP_FRAME_CONSECUTIVE << 4) |
|
||||
(conn->sequence_number & 0x0F);
|
||||
memcpy(&consecutive_frame.data[1],
|
||||
&message->data[conn->current_index], frame_length);
|
||||
|
||||
/* Send consecutive frame */
|
||||
if (can_send_message(&consecutive_frame, timeout_ms) != KERNEL_OK) {
|
||||
conn->state = CAN_TP_ERROR;
|
||||
mutex_unlock(&conn->connection_mutex);
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
conn->current_index += frame_length;
|
||||
conn->sequence_number = (conn->sequence_number + 1) & 0x0F;
|
||||
conn->block_counter++;
|
||||
|
||||
/* Wait for STMin */
|
||||
if (conn->stmin > 0) {
|
||||
kernel_delay(conn->stmin);
|
||||
}
|
||||
}
|
||||
|
||||
/* Message sent successfully */
|
||||
conn->state = CAN_TP_IDLE;
|
||||
mutex_unlock(&conn->connection_mutex);
|
||||
|
||||
/* Signal completion */
|
||||
if (can_tp_state.tx_callback != NULL) {
|
||||
can_tp_state.tx_callback(conn->connection_id, true);
|
||||
}
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Process Received CAN Message */
|
||||
void can_tp_process_rx_indication(const CanMessage_t* can_message) {
|
||||
if (!can_tp_state.initialized || can_message == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Parse frame type */
|
||||
uint8_t frame_type = (can_message->data[0] >> 4) & 0x0F;
|
||||
|
||||
switch (frame_type) {
|
||||
case CAN_TP_FRAME_SINGLE: {
|
||||
/* Single frame - complete message */
|
||||
uint8_t length = can_message->data[0] & 0x0F;
|
||||
|
||||
CanTpMessage_t tp_message;
|
||||
tp_message.message_id = can_message->id.id;
|
||||
tp_message.length = length;
|
||||
tp_message.data = (uint8_t*)&can_message->data[1];
|
||||
|
||||
/* Call callback */
|
||||
if (can_tp_state.rx_callback != NULL) {
|
||||
can_tp_state.rx_callback(&tp_message);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case CAN_TP_FRAME_FIRST: {
|
||||
/* First frame - start receiving multi-frame message */
|
||||
uint16_t total_length = ((can_message->data[0] & 0x0F) << 8) |
|
||||
can_message->data[1];
|
||||
|
||||
/* Find connection for receiving */
|
||||
for (int i = 0; i < CAN_TP_MAX_CONNECTIONS; i++) {
|
||||
CanTpConnection_t* conn = &can_tp_state.connections[i];
|
||||
|
||||
if (conn->state == CAN_TP_IDLE) {
|
||||
conn->state = CAN_TP_RECEIVE_IN_PROGRESS;
|
||||
conn->is_sender = false;
|
||||
conn->current_message.message_id = can_message->id.id;
|
||||
conn->current_message.length = total_length;
|
||||
conn->current_message.data = (uint8_t*)malloc(total_length);
|
||||
conn->current_index = 0;
|
||||
conn->sequence_number = 0;
|
||||
conn->block_counter = 0;
|
||||
|
||||
/* Copy first 6 bytes */
|
||||
memcpy(conn->current_message.data, &can_message->data[2], 6);
|
||||
conn->current_index = 6;
|
||||
|
||||
/* Send flow control */
|
||||
CanMessage_t fc_message;
|
||||
fc_message.id.id = can_message->id.id;
|
||||
fc_message.id.is_extended = true;
|
||||
fc_message.length = 8;
|
||||
fc_message.data[0] = (CAN_TP_FRAME_FLOW_CONTROL << 4) |
|
||||
CAN_TP_FC_CONTINUE;
|
||||
fc_message.data[1] = conn->block_size;
|
||||
fc_message.data[2] = conn->stmin;
|
||||
|
||||
can_send_message(&fc_message, CAN_TP_DEFAULT_TIMEOUT_MS);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case CAN_TP_FRAME_CONSECUTIVE: {
|
||||
/* Consecutive frame - part of multi-frame message */
|
||||
uint8_t sequence_number = can_message->data[0] & 0x0F;
|
||||
|
||||
/* Find active receiving connection */
|
||||
for (int i = 0; i < CAN_TP_MAX_CONNECTIONS; i++) {
|
||||
CanTpConnection_t* conn = &can_tp_state.connections[i];
|
||||
|
||||
if (conn->state == CAN_TP_RECEIVE_IN_PROGRESS && !conn->is_sender) {
|
||||
if (sequence_number == conn->sequence_number) {
|
||||
/* Copy data */
|
||||
uint8_t frame_length = can_message->length - 1;
|
||||
memcpy(&conn->current_message.data[conn->current_index],
|
||||
&can_message->data[1], frame_length);
|
||||
conn->current_index += frame_length;
|
||||
conn->sequence_number = (conn->sequence_number + 1) & 0x0F;
|
||||
conn->block_counter++;
|
||||
|
||||
/* Check if complete */
|
||||
if (conn->current_index >= conn->current_message.length) {
|
||||
/* Message complete */
|
||||
if (can_tp_state.rx_callback != NULL) {
|
||||
can_tp_state.rx_callback(&conn->current_message);
|
||||
}
|
||||
|
||||
/* Free data */
|
||||
free(conn->current_message.data);
|
||||
conn->state = CAN_TP_IDLE;
|
||||
} else if (conn->block_counter >= conn->block_size) {
|
||||
/* Send another flow control */
|
||||
CanMessage_t fc_message;
|
||||
fc_message.id.id = conn->current_message.message_id;
|
||||
fc_message.id.is_extended = true;
|
||||
fc_message.length = 8;
|
||||
fc_message.data[0] = (CAN_TP_FRAME_FLOW_CONTROL << 4) |
|
||||
CAN_TP_FC_CONTINUE;
|
||||
fc_message.data[1] = conn->block_size;
|
||||
fc_message.data[2] = conn->stmin;
|
||||
|
||||
can_send_message(&fc_message, CAN_TP_DEFAULT_TIMEOUT_MS);
|
||||
conn->block_counter = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case CAN_TP_FRAME_FLOW_CONTROL: {
|
||||
/* Flow control - update sending connection */
|
||||
uint8_t flow_status = can_message->data[0] & 0x0F;
|
||||
|
||||
for (int i = 0; i < CAN_TP_MAX_CONNECTIONS; i++) {
|
||||
CanTpConnection_t* conn = &can_tp_state.connections[i];
|
||||
|
||||
if (conn->state == CAN_TP_WAIT_FLOW_CONTROL && conn->is_sender) {
|
||||
if (flow_status == CAN_TP_FC_CONTINUE) {
|
||||
conn->block_size = can_message->data[1];
|
||||
conn->stmin = can_message->data[2];
|
||||
conn->block_counter = 0;
|
||||
|
||||
/* Signal flow control received */
|
||||
semaphore_give(&conn->flow_control_semaphore);
|
||||
} else if (flow_status == CAN_TP_FC_OVERFLOW) {
|
||||
conn->state = CAN_TP_ERROR;
|
||||
if (can_tp_state.error_callback != NULL) {
|
||||
can_tp_state.error_callback(conn->connection_id,
|
||||
CAN_TP_FC_OVERFLOW);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* CAN TP Main Function */
|
||||
void can_tp_main_function(void) {
|
||||
if (!can_tp_state.initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check timeouts */
|
||||
uint32_t current_time = kernel_get_tick_count();
|
||||
|
||||
for (int i = 0; i < CAN_TP_MAX_CONNECTIONS; i++) {
|
||||
CanTpConnection_t* conn = &can_tp_state.connections[i];
|
||||
|
||||
if (conn->state != CAN_TP_IDLE && conn->state != CAN_TP_ERROR) {
|
||||
if ((current_time - conn->timeout_timer) > CAN_TP_DEFAULT_TIMEOUT_MS) {
|
||||
/* Timeout occurred */
|
||||
conn->state = CAN_TP_TIMEOUT;
|
||||
|
||||
if (conn->current_message.data != NULL && !conn->is_sender) {
|
||||
free(conn->current_message.data);
|
||||
}
|
||||
|
||||
if (can_tp_state.error_callback != NULL) {
|
||||
can_tp_state.error_callback(conn->connection_id, CAN_TP_TIMEOUT);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,357 @@
|
||||
/**
|
||||
* @file uds.c
|
||||
* @brief Unified Diagnostic Services implementation
|
||||
*/
|
||||
|
||||
#include "uds.h"
|
||||
#include "dtc_manager.h"
|
||||
#include <string.h>
|
||||
|
||||
/* UDS State */
|
||||
typedef struct {
|
||||
bool initialized;
|
||||
UdsConfig_t config;
|
||||
UdsServiceCallback_t service_callbacks[0x100];
|
||||
UdsSecurityAccessCallback_t security_callback;
|
||||
UdsSessionChangedCallback_t session_callback;
|
||||
uint32_t security_attempt_count;
|
||||
uint32_t security_delay_timer;
|
||||
Mutex_t mutex;
|
||||
} UdsState_t;
|
||||
|
||||
static UdsState_t uds_state;
|
||||
|
||||
/* Initialize UDS */
|
||||
KernelStatus_t uds_init(const UdsConfig_t* config) {
|
||||
if (config == NULL || uds_state.initialized) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
/* Copy configuration */
|
||||
memcpy(&uds_state.config, config, sizeof(UdsConfig_t));
|
||||
|
||||
/* Initialize state */
|
||||
uds_state.security_attempt_count = 0;
|
||||
uds_state.security_delay_timer = 0;
|
||||
|
||||
/* Clear service callbacks */
|
||||
memset(uds_state.service_callbacks, 0, sizeof(uds_state.service_callbacks));
|
||||
|
||||
/* Create mutex */
|
||||
mutex_create(&uds_state.mutex, false);
|
||||
|
||||
uds_state.initialized = true;
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Process UDS Message */
|
||||
KernelStatus_t uds_process_message(const UdsMessage_t* request,
|
||||
UdsMessage_t* response) {
|
||||
if (!uds_state.initialized || request == NULL || response == NULL) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
mutex_lock(&uds_state.mutex, uds_state.config.timeout_ms);
|
||||
|
||||
/* Initialize response */
|
||||
response->service_id = request->service_id + 0x40; /* Positive response */
|
||||
response->length = 0;
|
||||
|
||||
/* Check if service is supported */
|
||||
if (uds_state.service_callbacks[request->service_id] == NULL) {
|
||||
response->service_id = 0x7F; /* Negative response */
|
||||
response->data[0] = request->service_id;
|
||||
response->data[1] = UDS_RESPONSE_SERVICE_NOT_SUPPORTED;
|
||||
response->length = 2;
|
||||
mutex_unlock(&uds_state.mutex);
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Check security access */
|
||||
if (request->service_id == UDS_SID_SECURITY_ACCESS) {
|
||||
/* Handle security access separately */
|
||||
if (uds_process_security_access(request, response) != KERNEL_OK) {
|
||||
mutex_unlock(&uds_state.mutex);
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
} else if (request->service_id == UDS_SID_DIAGNOSTIC_SESSION_CONTROL) {
|
||||
/* Handle session control */
|
||||
if (uds_process_session_control(request, response) != KERNEL_OK) {
|
||||
mutex_unlock(&uds_state.mutex);
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
} else if (request->service_id == UDS_SID_READ_DTC_INFORMATION) {
|
||||
/* Handle DTC reading */
|
||||
if (uds_process_read_dtc(request, response) != KERNEL_OK) {
|
||||
mutex_unlock(&uds_state.mutex);
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
} else if (request->service_id == UDS_SID_CLEAR_DTC_INFORMATION) {
|
||||
/* Handle DTC clearing */
|
||||
if (uds_process_clear_dtc(request, response) != KERNEL_OK) {
|
||||
mutex_unlock(&uds_state.mutex);
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
} else if (request->service_id == UDS_SID_TESTER_PRESENT) {
|
||||
/* Handle tester present */
|
||||
response->data[0] = request->sub_function;
|
||||
response->length = 1;
|
||||
} else {
|
||||
/* Call registered service callback */
|
||||
uds_state.service_callbacks[request->service_id](request, response);
|
||||
}
|
||||
|
||||
mutex_unlock(&uds_state.mutex);
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Process Security Access */
|
||||
static KernelStatus_t uds_process_security_access(const UdsMessage_t* request,
|
||||
UdsMessage_t* response) {
|
||||
uint8_t security_level = request->sub_function;
|
||||
|
||||
/* Check if delay timer is active */
|
||||
if (uds_state.security_delay_timer > 0) {
|
||||
uint32_t current_time = kernel_get_tick_count();
|
||||
if (current_time < uds_state.security_delay_timer) {
|
||||
response->service_id = 0x7F;
|
||||
response->data[0] = request->service_id;
|
||||
response->data[1] = UDS_RESPONSE_REQUIRED_TIME_DELAY_NOT_EXPIRED;
|
||||
response->length = 2;
|
||||
return KERNEL_OK;
|
||||
}
|
||||
}
|
||||
|
||||
if (security_level % 2 == 1) {
|
||||
/* Request seed */
|
||||
if (uds_state.security_callback != NULL) {
|
||||
uint8_t seed[16];
|
||||
uint8_t seed_length = 0;
|
||||
|
||||
/* Generate seed */
|
||||
for (int i = 0; i < 16; i++) {
|
||||
seed[i] = rand() & 0xFF;
|
||||
seed_length++;
|
||||
}
|
||||
|
||||
/* Set response */
|
||||
response->data[0] = security_level;
|
||||
memcpy(&response->data[1], seed, seed_length);
|
||||
response->length = seed_length + 1;
|
||||
}
|
||||
} else {
|
||||
/* Send key */
|
||||
if (uds_state.security_callback != NULL) {
|
||||
bool access_granted = false;
|
||||
uint8_t key[16];
|
||||
uint8_t key_length = request->length - 1;
|
||||
|
||||
memcpy(key, &request->data[1], key_length);
|
||||
|
||||
/* Verify key */
|
||||
uds_state.security_callback(security_level - 1, NULL, key,
|
||||
key_length, &access_granted);
|
||||
|
||||
if (access_granted) {
|
||||
uds_state.config.current_security_level = security_level - 1;
|
||||
uds_state.security_attempt_count = 0;
|
||||
|
||||
response->data[0] = security_level;
|
||||
response->length = 1;
|
||||
} else {
|
||||
uds_state.security_attempt_count++;
|
||||
|
||||
if (uds_state.security_attempt_count >= 3) {
|
||||
/* Set delay timer */
|
||||
uds_state.security_delay_timer = kernel_get_tick_count() + 10000;
|
||||
uds_state.security_attempt_count = 0;
|
||||
|
||||
response->service_id = 0x7F;
|
||||
response->data[0] = request->service_id;
|
||||
response->data[1] = UDS_RESPONSE_EXCEED_NUMBER_OF_ATTEMPTS;
|
||||
response->length = 2;
|
||||
} else {
|
||||
response->service_id = 0x7F;
|
||||
response->data[0] = request->service_id;
|
||||
response->data[1] = UDS_RESPONSE_INVALID_KEY;
|
||||
response->length = 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Process Session Control */
|
||||
static KernelStatus_t uds_process_session_control(const UdsMessage_t* request,
|
||||
UdsMessage_t* response) {
|
||||
UdsSessionType_t new_session = (UdsSessionType_t)request->sub_function;
|
||||
|
||||
/* Check if session is supported */
|
||||
if (new_session != UDS_SESSION_DEFAULT &&
|
||||
new_session != UDS_SESSION_PROGRAMMING &&
|
||||
new_session != UDS_SESSION_EXTENDED &&
|
||||
new_session != UDS_SESSION_SAFETY_SYSTEM) {
|
||||
response->service_id = 0x7F;
|
||||
response->data[0] = request->service_id;
|
||||
response->data[1] = UDS_RESPONSE_SUBFUNCTION_NOT_SUPPORTED;
|
||||
response->length = 2;
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Store old session */
|
||||
UdsSessionType_t old_session = uds_state.config.current_session;
|
||||
|
||||
/* Update session */
|
||||
uds_state.config.current_session = new_session;
|
||||
|
||||
/* Reset security level for non-default sessions */
|
||||
if (new_session != UDS_SESSION_DEFAULT) {
|
||||
uds_state.config.current_security_level = UDS_SECURITY_LOCKED;
|
||||
}
|
||||
|
||||
/* Call session changed callback */
|
||||
if (uds_state.session_callback != NULL) {
|
||||
uds_state.session_callback(old_session, new_session);
|
||||
}
|
||||
|
||||
/* Set response */
|
||||
response->data[0] = request->sub_function;
|
||||
response->data[1] = 0x00; /* P2 server max high byte */
|
||||
response->data[2] = 0x32; /* P2 server max low byte (50ms) */
|
||||
response->data[3] = 0x01; /* P2* server max high byte */
|
||||
response->data[4] = 0xF4; /* P2* server max low byte (500ms) */
|
||||
response->length = 5;
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Process Read DTC */
|
||||
static KernelStatus_t uds_process_read_dtc(const UdsMessage_t* request,
|
||||
UdsMessage_t* response) {
|
||||
uint8_t sub_function = request->sub_function;
|
||||
|
||||
switch (sub_function) {
|
||||
case 0x01: /* Report number of DTC by status mask */
|
||||
case 0x02: /* Report DTC by status mask */
|
||||
case 0x04: /* Report DTC snapshot identification */
|
||||
case 0x06: /* Report DTC extended data */
|
||||
case 0x0A: /* Report supported DTCs */
|
||||
break;
|
||||
default:
|
||||
response->service_id = 0x7F;
|
||||
response->data[0] = request->service_id;
|
||||
response->data[1] = UDS_RESPONSE_SUBFUNCTION_NOT_SUPPORTED;
|
||||
response->length = 2;
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Get DTC information */
|
||||
response->data[0] = 0x01; /* Availability mask */
|
||||
response->data[1] = 0x00; /* DTC format identifier */
|
||||
|
||||
/* Get number of DTCs */
|
||||
uint16_t dtc_count = dtc_manager_get_count();
|
||||
response->data[2] = (dtc_count >> 8) & 0xFF;
|
||||
response->data[3] = dtc_count & 0xFF;
|
||||
|
||||
response->length = 4;
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Process Clear DTC */
|
||||
static KernelStatus_t uds_process_clear_dtc(const UdsMessage_t* request,
|
||||
UdsMessage_t* response) {
|
||||
/* Clear all DTCs */
|
||||
dtc_manager_clear_all();
|
||||
|
||||
response->length = 0;
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Register Service Callback */
|
||||
KernelStatus_t uds_register_service_callback(UdsServiceId_t service_id,
|
||||
UdsServiceCallback_t callback) {
|
||||
if (!uds_state.initialized || callback == NULL) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
uds_state.service_callbacks[service_id] = callback;
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Register Security Callback */
|
||||
KernelStatus_t uds_register_security_callback(UdsSecurityAccessCallback_t callback) {
|
||||
if (!uds_state.initialized || callback == NULL) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
uds_state.security_callback = callback;
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Register Session Callback */
|
||||
KernelStatus_t uds_register_session_callback(UdsSessionChangedCallback_t callback) {
|
||||
if (!uds_state.initialized || callback == NULL) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
uds_state.session_callback = callback;
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Set Session */
|
||||
KernelStatus_t uds_set_session(UdsSessionType_t session) {
|
||||
if (!uds_state.initialized) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
uds_state.config.current_session = session;
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Get Session */
|
||||
UdsSessionType_t uds_get_session(void) {
|
||||
if (!uds_state.initialized) {
|
||||
return UDS_SESSION_DEFAULT;
|
||||
}
|
||||
|
||||
return uds_state.config.current_session;
|
||||
}
|
||||
|
||||
/* Set Security Level */
|
||||
KernelStatus_t uds_set_security_level(UdsSecurityLevel_t level) {
|
||||
if (!uds_state.initialized) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
uds_state.config.current_security_level = level;
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Get Security Level */
|
||||
UdsSecurityLevel_t uds_get_security_level(void) {
|
||||
if (!uds_state.initialized) {
|
||||
return UDS_SECURITY_LOCKED;
|
||||
}
|
||||
|
||||
return uds_state.config.current_security_level;
|
||||
}
|
||||
|
||||
/* UDS Main Function */
|
||||
void uds_main_function(void) {
|
||||
if (!uds_state.initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check security delay timer */
|
||||
if (uds_state.security_delay_timer > 0) {
|
||||
if (kernel_get_tick_count() >= uds_state.security_delay_timer) {
|
||||
uds_state.security_delay_timer = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/**
|
||||
* @file dtc_manager.h
|
||||
* @brief Diagnostic Trouble Code Manager
|
||||
*/
|
||||
|
||||
#ifndef DTC_MANAGER_H
|
||||
#define DTC_MANAGER_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "kernel.h"
|
||||
|
||||
/* DTC Configuration */
|
||||
#define DTC_MAX_COUNT 100
|
||||
#define DTC_MAX_SNAPSHOT_RECORDS 5
|
||||
#define DTC_MAX_EXTENDED_DATA 10
|
||||
|
||||
/* DTC Status Bits */
|
||||
typedef struct {
|
||||
uint8_t test_failed : 1;
|
||||
uint8_t test_failed_this_operation_cycle : 1;
|
||||
uint8_t pending_dtc : 1;
|
||||
uint8_t confirmed_dtc : 1;
|
||||
uint8_t test_not_completed_since_last_clear : 1;
|
||||
uint8_t test_failed_since_last_clear : 1;
|
||||
uint8_t test_not_completed_this_operation_cycle : 1;
|
||||
uint8_t warning_indicator_requested : 1;
|
||||
} DtcStatus_t;
|
||||
|
||||
/* DTC Format */
|
||||
typedef struct {
|
||||
uint8_t high_byte;
|
||||
uint8_t middle_byte;
|
||||
uint8_t low_byte;
|
||||
} DtcCode_t;
|
||||
|
||||
/* DTC Snapshot Record */
|
||||
typedef struct {
|
||||
uint16_t record_number;
|
||||
uint8_t* data;
|
||||
uint16_t length;
|
||||
uint32_t timestamp;
|
||||
} DtcSnapshotRecord_t;
|
||||
|
||||
/* DTC Extended Data */
|
||||
typedef struct {
|
||||
uint16_t record_number;
|
||||
uint8_t* data;
|
||||
uint16_t length;
|
||||
} DtcExtendedData_t;
|
||||
|
||||
/* DTC Entry */
|
||||
typedef struct {
|
||||
DtcCode_t code;
|
||||
DtcStatus_t status;
|
||||
uint8_t severity;
|
||||
uint8_t functional_unit;
|
||||
uint32_t occurrence_counter;
|
||||
uint32_t aging_counter;
|
||||
uint32_t first_occurrence_time;
|
||||
uint32_t last_occurrence_time;
|
||||
DtcSnapshotRecord_t snapshots[DTC_MAX_SNAPSHOT_RECORDS];
|
||||
uint8_t snapshot_count;
|
||||
DtcExtendedData_t extended_data[DTC_MAX_EXTENDED_DATA];
|
||||
uint8_t extended_data_count;
|
||||
bool is_active;
|
||||
} DtcEntry_t;
|
||||
|
||||
/* DTC Manager Functions */
|
||||
KernelStatus_t dtc_manager_init(void);
|
||||
KernelStatus_t dtc_manager_deinit(void);
|
||||
KernelStatus_t dtc_manager_add_dtc(const DtcCode_t* code, uint8_t severity);
|
||||
KernelStatus_t dtc_manager_remove_dtc(const DtcCode_t* code);
|
||||
KernelStatus_t dtc_manager_set_status(const DtcCode_t* code, DtcStatus_t status);
|
||||
KernelStatus_t dtc_manager_get_status(const DtcCode_t* code, DtcStatus_t* status);
|
||||
KernelStatus_t dtc_manager_add_snapshot(const DtcCode_t* code,
|
||||
const uint8_t* data, uint16_t length);
|
||||
KernelStatus_t dtc_manager_add_extended_data(const DtcCode_t* code,
|
||||
const uint8_t* data, uint16_t length);
|
||||
KernelStatus_t dtc_manager_clear_all(void);
|
||||
uint16_t dtc_manager_get_count(void);
|
||||
KernelStatus_t dtc_manager_get_dtc(uint16_t index, DtcEntry_t* entry);
|
||||
KernelStatus_t dtc_manager_increment_occurrence(const DtcCode_t* code);
|
||||
void dtc_manager_main_function(void); /* Periodic processing */
|
||||
|
||||
#endif /* DTC_MANAGER_H */
|
||||
@@ -0,0 +1,91 @@
|
||||
/**
|
||||
* @file obd_ii.h
|
||||
* @brief OBD-II (On-Board Diagnostics) implementation
|
||||
*/
|
||||
|
||||
#ifndef OBD_II_H
|
||||
#define OBD_II_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "kernel.h"
|
||||
#include "can_driver.h"
|
||||
|
||||
/* OBD-II Configuration */
|
||||
#define OBD_II_MAX_PIDS 0xFF
|
||||
#define OBD_II_CAN_ID_REQUEST 0x7DF
|
||||
#define OBD_II_CAN_ID_RESPONSE 0x7E8
|
||||
#define OBD_II_CAN_ID_RESPONSE_2 0x7E9
|
||||
|
||||
/* OBD-II Service Modes */
|
||||
typedef enum {
|
||||
OBD_II_SERVICE_SHOW_CURRENT_DATA = 0x01,
|
||||
OBD_II_SERVICE_SHOW_FREEZE_FRAME = 0x02,
|
||||
OBD_II_SERVICE_SHOW_STORED_DTCS = 0x03,
|
||||
OBD_II_SERVICE_CLEAR_DTCS = 0x04,
|
||||
OBD_II_SERVICE_TEST_RESULTS_OXYGEN = 0x05,
|
||||
OBD_II_SERVICE_TEST_RESULTS_NON_CONTINUOUS = 0x06,
|
||||
OBD_II_SERVICE_SHOW_PENDING_DTCS = 0x07,
|
||||
OBD_II_SERVICE_CONTROL_ONBOARD_SYSTEM = 0x08,
|
||||
OBD_II_SERVICE_REQUEST_VEHICLE_INFO = 0x09,
|
||||
OBD_II_SERVICE_PERMANENT_DTCS = 0x0A
|
||||
} ObdIIServiceMode_t;
|
||||
|
||||
/* OBD-II PID Definitions */
|
||||
typedef enum {
|
||||
OBD_II_PID_SUPPORTED_00_20 = 0x00,
|
||||
OBD_II_PID_MONITOR_STATUS = 0x01,
|
||||
OBD_II_PID_FREEZE_DTC = 0x02,
|
||||
OBD_II_PID_FUEL_SYSTEM_STATUS = 0x03,
|
||||
OBD_II_PID_CALCULATED_ENGINE_LOAD = 0x04,
|
||||
OBD_II_PID_ENGINE_COOLANT_TEMP = 0x05,
|
||||
OBD_II_PID_SHORT_TERM_FUEL_TRIM_BANK1 = 0x06,
|
||||
OBD_II_PID_LONG_TERM_FUEL_TRIM_BANK1 = 0x07,
|
||||
OBD_II_PID_INTAKE_MANIFOLD_PRESSURE = 0x0B,
|
||||
OBD_II_PID_ENGINE_RPM = 0x0C,
|
||||
OBD_II_PID_VEHICLE_SPEED = 0x0D,
|
||||
OBD_II_PID_TIMING_ADVANCE = 0x0E,
|
||||
OBD_II_PID_INTAKE_AIR_TEMP = 0x0F,
|
||||
OBD_II_PID_MAF_AIR_FLOW_RATE = 0x10,
|
||||
OBD_II_PID_THROTTLE_POSITION = 0x11,
|
||||
OBD_II_PID_OXYGEN_SENSOR_PRESENT = 0x13,
|
||||
OBD_II_PID_OXYGEN_SENSOR_1 = 0x14,
|
||||
OBD_II_PID_OXYGEN_SENSOR_2 = 0x15,
|
||||
OBD_II_PID_RUN_TIME_SINCE_START = 0x1F,
|
||||
OBD_II_PID_SUPPORTED_21_40 = 0x20,
|
||||
OBD_II_PID_DISTANCE_WITH_MIL = 0x21,
|
||||
OBD_II_PID_FUEL_RAIL_PRESSURE = 0x22,
|
||||
OBD_II_PID_FUEL_RAIL_GAUGE_PRESSURE = 0x23,
|
||||
OBD_II_PID_OXYGEN_SENSOR_3 = 0x24,
|
||||
OBD_II_PID_OXYGEN_SENSOR_4 = 0x25,
|
||||
OBD_II_PID_SUPPORTED_41_60 = 0x40,
|
||||
OBD_II_PID_CONTROL_MODULE_VOLTAGE = 0x42,
|
||||
OBD_II_PID_ABSOLUTE_LOAD_VALUE = 0x43,
|
||||
OBD_II_PID_AMBIENT_AIR_TEMP = 0x46,
|
||||
OBD_II_PID_THROTTLE_POSITION_B = 0x47,
|
||||
OBD_II_PID_ACCELERATOR_PEDAL_POSITION_D = 0x49,
|
||||
OBD_II_PID_ACCELERATOR_PEDAL_POSITION_E = 0x4A
|
||||
} ObdIIPid_t;
|
||||
|
||||
/* OBD-II PID Value */
|
||||
typedef struct {
|
||||
ObdIIPid_t pid;
|
||||
uint8_t data[4];
|
||||
uint8_t length;
|
||||
bool is_supported;
|
||||
} ObdIIPidValue_t;
|
||||
|
||||
/* OBD-II Callbacks */
|
||||
typedef bool (*ObdIIPidRequestCallback_t)(ObdIIPid_t pid, uint8_t* data,
|
||||
uint8_t* length);
|
||||
typedef void (*ObdIIDtcRequestCallback_t)(uint8_t* dtc_data, uint16_t* length);
|
||||
|
||||
/* OBD-II Functions */
|
||||
KernelStatus_t obd_ii_init(void);
|
||||
KernelStatus_t obd_ii_deinit(void);
|
||||
KernelStatus_t obd_ii_register_pid_callback(ObdIIPidRequestCallback_t callback);
|
||||
KernelStatus_t obd_ii_register_dtc_callback(ObdIIDtcRequestCallback_t callback);
|
||||
void obd_ii_process_request(const CanMessage_t* message);
|
||||
void obd_ii_main_function(void);
|
||||
|
||||
#endif /* OBD_II_H */
|
||||
@@ -0,0 +1,248 @@
|
||||
/**
|
||||
* @file dtc_manager.c
|
||||
* @brief DTC Manager implementation
|
||||
*/
|
||||
|
||||
#include "dtc_manager.h"
|
||||
#include <string.h>
|
||||
|
||||
/* DTC Manager State */
|
||||
typedef struct {
|
||||
bool initialized;
|
||||
DtcEntry_t dtc_entries[DTC_MAX_COUNT];
|
||||
uint16_t dtc_count;
|
||||
Mutex_t mutex;
|
||||
} DtcManagerState_t;
|
||||
|
||||
static DtcManagerState_t dtc_manager;
|
||||
|
||||
/* Initialize DTC Manager */
|
||||
KernelStatus_t dtc_manager_init(void) {
|
||||
if (dtc_manager.initialized) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
memset(&dtc_manager, 0, sizeof(DtcManagerState_t));
|
||||
mutex_create(&dtc_manager.mutex, false);
|
||||
|
||||
dtc_manager.initialized = true;
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Add DTC */
|
||||
KernelStatus_t dtc_manager_add_dtc(const DtcCode_t* code, uint8_t severity) {
|
||||
if (!dtc_manager.initialized || code == NULL) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
mutex_lock(&dtc_manager.mutex, 100);
|
||||
|
||||
/* Check if DTC already exists */
|
||||
for (uint16_t i = 0; i < dtc_manager.dtc_count; i++) {
|
||||
if (dtc_manager.dtc_entries[i].code.high_byte == code->high_byte &&
|
||||
dtc_manager.dtc_entries[i].code.middle_byte == code->middle_byte &&
|
||||
dtc_manager.dtc_entries[i].code.low_byte == code->low_byte) {
|
||||
mutex_unlock(&dtc_manager.mutex);
|
||||
return KERNEL_OK; /* DTC already exists */
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if maximum DTCs reached */
|
||||
if (dtc_manager.dtc_count >= DTC_MAX_COUNT) {
|
||||
mutex_unlock(&dtc_manager.mutex);
|
||||
return KERNEL_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
/* Add new DTC */
|
||||
DtcEntry_t* entry = &dtc_manager.dtc_entries[dtc_manager.dtc_count];
|
||||
entry->code = *code;
|
||||
entry->status.test_failed = 1;
|
||||
entry->status.test_failed_this_operation_cycle = 1;
|
||||
entry->severity = severity;
|
||||
entry->occurrence_counter = 1;
|
||||
entry->first_occurrence_time = kernel_get_tick_count();
|
||||
entry->last_occurrence_time = entry->first_occurrence_time;
|
||||
entry->is_active = true;
|
||||
|
||||
dtc_manager.dtc_count++;
|
||||
|
||||
mutex_unlock(&dtc_manager.mutex);
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Remove DTC */
|
||||
KernelStatus_t dtc_manager_remove_dtc(const DtcCode_t* code) {
|
||||
if (!dtc_manager.initialized || code == NULL) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
mutex_lock(&dtc_manager.mutex, 100);
|
||||
|
||||
for (uint16_t i = 0; i < dtc_manager.dtc_count; i++) {
|
||||
if (dtc_manager.dtc_entries[i].code.high_byte == code->high_byte &&
|
||||
dtc_manager.dtc_entries[i].code.middle_byte == code->middle_byte &&
|
||||
dtc_manager.dtc_entries[i].code.low_byte == code->low_byte) {
|
||||
/* Remove DTC */
|
||||
for (uint16_t j = i; j < dtc_manager.dtc_count - 1; j++) {
|
||||
dtc_manager.dtc_entries[j] = dtc_manager.dtc_entries[j + 1];
|
||||
}
|
||||
dtc_manager.dtc_count--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&dtc_manager.mutex);
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Set DTC Status */
|
||||
KernelStatus_t dtc_manager_set_status(const DtcCode_t* code, DtcStatus_t status) {
|
||||
if (!dtc_manager.initialized || code == NULL) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
mutex_lock(&dtc_manager.mutex, 100);
|
||||
|
||||
for (uint16_t i = 0; i < dtc_manager.dtc_count; i++) {
|
||||
if (dtc_manager.dtc_entries[i].code.high_byte == code->high_byte &&
|
||||
dtc_manager.dtc_entries[i].code.middle_byte == code->middle_byte &&
|
||||
dtc_manager.dtc_entries[i].code.low_byte == code->low_byte) {
|
||||
dtc_manager.dtc_entries[i].status = status;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&dtc_manager.mutex);
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Add Snapshot */
|
||||
KernelStatus_t dtc_manager_add_snapshot(const DtcCode_t* code,
|
||||
const uint8_t* data, uint16_t length) {
|
||||
if (!dtc_manager.initialized || code == NULL || data == NULL) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
mutex_lock(&dtc_manager.mutex, 100);
|
||||
|
||||
for (uint16_t i = 0; i < dtc_manager.dtc_count; i++) {
|
||||
if (dtc_manager.dtc_entries[i].code.high_byte == code->high_byte &&
|
||||
dtc_manager.dtc_entries[i].code.middle_byte == code->middle_byte &&
|
||||
dtc_manager.dtc_entries[i].code.low_byte == code->low_byte) {
|
||||
|
||||
DtcEntry_t* entry = &dtc_manager.dtc_entries[i];
|
||||
|
||||
if (entry->snapshot_count < DTC_MAX_SNAPSHOT_RECORDS) {
|
||||
DtcSnapshotRecord_t* snapshot =
|
||||
&entry->snapshots[entry->snapshot_count];
|
||||
|
||||
snapshot->record_number = entry->snapshot_count + 1;
|
||||
snapshot->data = (uint8_t*)malloc(length);
|
||||
memcpy(snapshot->data, data, length);
|
||||
snapshot->length = length;
|
||||
snapshot->timestamp = kernel_get_tick_count();
|
||||
|
||||
entry->snapshot_count++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&dtc_manager.mutex);
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Clear All DTCs */
|
||||
KernelStatus_t dtc_manager_clear_all(void) {
|
||||
if (!dtc_manager.initialized) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
mutex_lock(&dtc_manager.mutex, 100);
|
||||
|
||||
/* Free snapshot data */
|
||||
for (uint16_t i = 0; i < dtc_manager.dtc_count; i++) {
|
||||
DtcEntry_t* entry = &dtc_manager.dtc_entries[i];
|
||||
|
||||
for (uint8_t j = 0; j < entry->snapshot_count; j++) {
|
||||
if (entry->snapshots[j].data != NULL) {
|
||||
free(entry->snapshots[j].data);
|
||||
}
|
||||
}
|
||||
|
||||
for (uint8_t j = 0; j < entry->extended_data_count; j++) {
|
||||
if (entry->extended_data[j].data != NULL) {
|
||||
free(entry->extended_data[j].data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Clear all DTCs */
|
||||
dtc_manager.dtc_count = 0;
|
||||
|
||||
mutex_unlock(&dtc_manager.mutex);
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Get DTC Count */
|
||||
uint16_t dtc_manager_get_count(void) {
|
||||
return dtc_manager.dtc_count;
|
||||
}
|
||||
|
||||
/* Increment Occurrence Counter */
|
||||
KernelStatus_t dtc_manager_increment_occurrence(const DtcCode_t* code) {
|
||||
if (!dtc_manager.initialized || code == NULL) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
mutex_lock(&dtc_manager.mutex, 100);
|
||||
|
||||
for (uint16_t i = 0; i < dtc_manager.dtc_count; i++) {
|
||||
if (dtc_manager.dtc_entries[i].code.high_byte == code->high_byte &&
|
||||
dtc_manager.dtc_entries[i].code.middle_byte == code->middle_byte &&
|
||||
dtc_manager.dtc_entries[i].code.low_byte == code->low_byte) {
|
||||
|
||||
dtc_manager.dtc_entries[i].occurrence_counter++;
|
||||
dtc_manager.dtc_entries[i].last_occurrence_time = kernel_get_tick_count();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&dtc_manager.mutex);
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* DTC Manager Main Function */
|
||||
void dtc_manager_main_function(void) {
|
||||
if (!dtc_manager.initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
mutex_lock(&dtc_manager.mutex, 100);
|
||||
|
||||
/* Update DTC aging */
|
||||
for (uint16_t i = 0; i < dtc_manager.dtc_count; i++) {
|
||||
DtcEntry_t* entry = &dtc_manager.dtc_entries[i];
|
||||
|
||||
/* Update aging counter */
|
||||
if (entry->status.test_failed) {
|
||||
entry->aging_counter++;
|
||||
} else {
|
||||
entry->aging_counter = 0;
|
||||
}
|
||||
|
||||
/* Update status based on aging */
|
||||
if (entry->aging_counter > 40) {
|
||||
entry->status.confirmed_dtc = 1;
|
||||
entry->status.pending_dtc = 0;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&dtc_manager.mutex);
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
/**
|
||||
* @file obd_ii.c
|
||||
* @brief OBD-II implementation
|
||||
*/
|
||||
|
||||
#include "obd_ii.h"
|
||||
#include "dtc_manager.h"
|
||||
#include <string.h>
|
||||
|
||||
/* OBD-II State */
|
||||
typedef struct {
|
||||
bool initialized;
|
||||
ObdIIPidRequestCallback_t pid_callback;
|
||||
ObdIIDtcRequestCallback_t dtc_callback;
|
||||
Mutex_t mutex;
|
||||
} ObdIIState_t;
|
||||
|
||||
static ObdIIState_t obd_ii_state;
|
||||
|
||||
/* Initialize OBD-II */
|
||||
KernelStatus_t obd_ii_init(void) {
|
||||
if (obd_ii_state.initialized) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
memset(&obd_ii_state, 0, sizeof(ObdIIState_t));
|
||||
mutex_create(&obd_ii_state.mutex, false);
|
||||
|
||||
obd_ii_state.initialized = true;
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Register PID Callback */
|
||||
KernelStatus_t obd_ii_register_pid_callback(ObdIIPidRequestCallback_t callback) {
|
||||
if (!obd_ii_state.initialized || callback == NULL) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
obd_ii_state.pid_callback = callback;
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Register DTC Callback */
|
||||
KernelStatus_t obd_ii_register_dtc_callback(ObdIIDtcRequestCallback_t callback) {
|
||||
if (!obd_ii_state.initialized || callback == NULL) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
obd_ii_state.dtc_callback = callback;
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Process OBD-II Request */
|
||||
void obd_ii_process_request(const CanMessage_t* message) {
|
||||
if (!obd_ii_state.initialized || message == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check if OBD-II request */
|
||||
if (message->id.id != OBD_II_CAN_ID_REQUEST) {
|
||||
return;
|
||||
}
|
||||
|
||||
mutex_lock(&obd_ii_state.mutex, 100);
|
||||
|
||||
/* Parse request */
|
||||
uint8_t mode = message->data[1];
|
||||
uint8_t pid = message->data[2];
|
||||
|
||||
CanMessage_t response;
|
||||
response.id.id = OBD_II_CAN_ID_RESPONSE;
|
||||
response.id.is_extended = false;
|
||||
|
||||
switch (mode) {
|
||||
case OBD_II_SERVICE_SHOW_CURRENT_DATA:
|
||||
/* Handle PID request */
|
||||
if (obd_ii_state.pid_callback != NULL) {
|
||||
uint8_t data[4];
|
||||
uint8_t length = 0;
|
||||
|
||||
if (obd_ii_state.pid_callback((ObdIIPid_t)pid, data, &length)) {
|
||||
response.length = 5 + length;
|
||||
response.data[0] = 0x04; /* Number of additional bytes */
|
||||
response.data[1] = 0x41; /* Mode 1 response */
|
||||
response.data[2] = pid;
|
||||
memcpy(&response.data[3], data, length);
|
||||
} else {
|
||||
/* PID not supported */
|
||||
response.length = 3;
|
||||
response.data[0] = 0x02;
|
||||
response.data[1] = 0x7F;
|
||||
response.data[2] = 0x11; /* Service not supported */
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case OBD_II_SERVICE_SHOW_STORED_DTCS:
|
||||
/* Handle DTC request */
|
||||
if (obd_ii_state.dtc_callback != NULL) {
|
||||
uint8_t dtc_data[100];
|
||||
uint16_t dtc_length = 0;
|
||||
|
||||
obd_ii_state.dtc_callback(dtc_data, &dtc_length);
|
||||
|
||||
response.length = dtc_length + 3;
|
||||
response.data[0] = dtc_length + 2;
|
||||
response.data[1] = 0x43; /* Mode 3 response */
|
||||
memcpy(&response.data[2], dtc_data, dtc_length);
|
||||
}
|
||||
break;
|
||||
|
||||
case OBD_II_SERVICE_CLEAR_DTCS:
|
||||
/* Clear DTCs */
|
||||
dtc_manager_clear_all();
|
||||
|
||||
response.length = 2;
|
||||
response.data[0] = 0x01;
|
||||
response.data[1] = 0x44; /* Mode 4 response */
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Service not supported */
|
||||
response.length = 3;
|
||||
response.data[0] = 0x02;
|
||||
response.data[1] = 0x7F;
|
||||
response.data[2] = 0x11;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Send response */
|
||||
can_send_message(&response, 100);
|
||||
|
||||
mutex_unlock(&obd_ii_state.mutex);
|
||||
}
|
||||
@@ -0,0 +1,280 @@
|
||||
/**
|
||||
* @file e2e_protection.c
|
||||
* @brief End-to-End Protection for safety-critical communication
|
||||
*/
|
||||
|
||||
#include "kernel.h"
|
||||
#include "can_driver.h"
|
||||
#include <string.h>
|
||||
|
||||
/* E2E Protection Configuration */
|
||||
#define E2E_MAX_PROFILES 8
|
||||
#define E2E_MAX_DATA_LENGTH 64
|
||||
#define E2E_CRC_POLYNOMIAL 0x2F /* CRC-8 polynomial */
|
||||
|
||||
/* E2E Profile Types */
|
||||
typedef enum {
|
||||
E2E_PROFILE_1 = 1, /* CRC-8 */
|
||||
E2E_PROFILE_2 = 2, /* CRC-16 */
|
||||
E2E_PROFILE_4 = 4, /* CRC-32 */
|
||||
E2E_PROFILE_5 = 5, /* Custom */
|
||||
E2E_PROFILE_7 = 7 /* Counter + CRC */
|
||||
} E2eProfileType_t;
|
||||
|
||||
/* E2E Configuration */
|
||||
typedef struct {
|
||||
E2eProfileType_t profile_type;
|
||||
uint16_t data_id;
|
||||
uint8_t data_length;
|
||||
uint8_t counter_offset;
|
||||
uint8_t crc_offset;
|
||||
uint8_t timeout_ms;
|
||||
uint32_t max_delta_counter;
|
||||
} E2eConfig_t;
|
||||
|
||||
/* E2E State */
|
||||
typedef struct {
|
||||
bool initialized;
|
||||
E2eConfig_t profiles[E2E_MAX_PROFILES];
|
||||
uint8_t profile_count;
|
||||
uint8_t counters[E2E_MAX_PROFILES];
|
||||
uint32_t last_receive_time[E2E_MAX_PROFILES];
|
||||
uint32_t error_counters[E2E_MAX_PROFILES];
|
||||
Mutex_t mutex;
|
||||
} E2eState_t;
|
||||
|
||||
static E2eState_t e2e_state;
|
||||
|
||||
/* Initialize E2E Protection */
|
||||
KernelStatus_t e2e_protection_init(void) {
|
||||
if (e2e_state.initialized) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
memset(&e2e_state, 0, sizeof(E2eState_t));
|
||||
mutex_create(&e2e_state.mutex, false);
|
||||
|
||||
e2e_state.initialized = true;
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Register E2E Profile */
|
||||
KernelStatus_t e2e_register_profile(const E2eConfig_t* config) {
|
||||
if (!e2e_state.initialized || config == NULL) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
if (e2e_state.profile_count >= E2E_MAX_PROFILES) {
|
||||
return KERNEL_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
mutex_lock(&e2e_state.mutex, 100);
|
||||
|
||||
e2e_state.profiles[e2e_state.profile_count] = *config;
|
||||
e2e_state.counters[e2e_state.profile_count] = 0;
|
||||
e2e_state.last_receive_time[e2e_state.profile_count] = 0;
|
||||
e2e_state.error_counters[e2e_state.profile_count] = 0;
|
||||
|
||||
e2e_state.profile_count++;
|
||||
|
||||
mutex_unlock(&e2e_state.mutex);
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Protect Message (Add E2E header) */
|
||||
KernelStatus_t e2e_protect_message(uint8_t profile_id, CanMessage_t* message) {
|
||||
if (!e2e_state.initialized || message == NULL) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
if (profile_id >= e2e_state.profile_count) {
|
||||
return KERNEL_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
mutex_lock(&e2e_state.mutex, 100);
|
||||
|
||||
E2eConfig_t* config = &e2e_state.profiles[profile_id];
|
||||
|
||||
/* Add counter */
|
||||
message->data[config->counter_offset] = e2e_state.counters[profile_id];
|
||||
|
||||
/* Calculate CRC */
|
||||
uint8_t crc = e2e_calculate_crc8(message->data, config->data_length);
|
||||
|
||||
/* Add CRC */
|
||||
message->data[config->crc_offset] = crc;
|
||||
|
||||
/* Increment counter */
|
||||
e2e_state.counters[profile_id]++;
|
||||
|
||||
mutex_unlock(&e2e_state.mutex);
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Check Message (Verify E2E header) */
|
||||
KernelStatus_t e2e_check_message(uint8_t profile_id, const CanMessage_t* message,
|
||||
bool* is_valid) {
|
||||
if (!e2e_state.initialized || message == NULL || is_valid == NULL) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
if (profile_id >= e2e_state.profile_count) {
|
||||
return KERNEL_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
mutex_lock(&e2e_state.mutex, 100);
|
||||
|
||||
E2eConfig_t* config = &e2e_state.profiles[profile_id];
|
||||
*is_valid = false;
|
||||
|
||||
/* Check counter */
|
||||
uint8_t received_counter = message->data[config->counter_offset];
|
||||
uint8_t expected_counter = e2e_state.counters[profile_id];
|
||||
|
||||
uint32_t delta = (received_counter - expected_counter) & 0xFF;
|
||||
|
||||
if (delta > config->max_delta_counter) {
|
||||
/* Counter error */
|
||||
e2e_state.error_counters[profile_id]++;
|
||||
mutex_unlock(&e2e_state.mutex);
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
/* Check CRC */
|
||||
uint8_t calculated_crc = e2e_calculate_crc8(message->data,
|
||||
config->data_length);
|
||||
uint8_t received_crc = message->data[config->crc_offset];
|
||||
|
||||
if (calculated_crc != received_crc) {
|
||||
/* CRC error */
|
||||
e2e_state.error_counters[profile_id]++;
|
||||
mutex_unlock(&e2e_state.mutex);
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
/* Update state */
|
||||
e2e_state.counters[profile_id] = received_counter;
|
||||
e2e_state.last_receive_time[profile_id] = kernel_get_tick_count();
|
||||
|
||||
*is_valid = true;
|
||||
|
||||
mutex_unlock(&e2e_state.mutex);
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Calculate CRC-8 */
|
||||
static uint8_t e2e_calculate_crc8(const uint8_t* data, uint8_t length) {
|
||||
uint8_t crc = 0xFF; /* Initial value */
|
||||
|
||||
for (uint8_t i = 0; i < length; i++) {
|
||||
crc ^= data[i];
|
||||
|
||||
for (uint8_t j = 0; j < 8; j++) {
|
||||
if (crc & 0x80) {
|
||||
crc = (crc << 1) ^ E2E_CRC_POLYNOMIAL;
|
||||
} else {
|
||||
crc <<= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
/* Calculate CRC-16 */
|
||||
static uint16_t e2e_calculate_crc16(const uint8_t* data, uint8_t length) {
|
||||
uint16_t crc = 0xFFFF;
|
||||
|
||||
for (uint8_t i = 0; i < length; i++) {
|
||||
crc ^= (data[i] << 8);
|
||||
|
||||
for (uint8_t j = 0; j < 8; j++) {
|
||||
if (crc & 0x8000) {
|
||||
crc = (crc << 1) ^ 0x1021;
|
||||
} else {
|
||||
crc <<= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
/* Calculate CRC-32 */
|
||||
static uint32_t e2e_calculate_crc32(const uint8_t* data, uint8_t length) {
|
||||
uint32_t crc = 0xFFFFFFFF;
|
||||
|
||||
for (uint8_t i = 0; i < length; i++) {
|
||||
crc ^= data[i];
|
||||
|
||||
for (uint8_t j = 0; j < 8; j++) {
|
||||
if (crc & 1) {
|
||||
crc = (crc >> 1) ^ 0xEDB88320;
|
||||
} else {
|
||||
crc >>= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ~crc;
|
||||
}
|
||||
|
||||
/* Get E2E Error Counter */
|
||||
uint32_t e2e_get_error_count(uint8_t profile_id) {
|
||||
if (profile_id >= e2e_state.profile_count) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return e2e_state.error_counters[profile_id];
|
||||
}
|
||||
|
||||
/* Reset E2E State */
|
||||
KernelStatus_t e2e_reset(uint8_t profile_id) {
|
||||
if (!e2e_state.initialized) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
if (profile_id >= e2e_state.profile_count) {
|
||||
return KERNEL_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
mutex_lock(&e2e_state.mutex, 100);
|
||||
|
||||
e2e_state.counters[profile_id] = 0;
|
||||
e2e_state.last_receive_time[profile_id] = 0;
|
||||
e2e_state.error_counters[profile_id] = 0;
|
||||
|
||||
mutex_unlock(&e2e_state.mutex);
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* E2E Main Function */
|
||||
void e2e_protection_main_function(void) {
|
||||
if (!e2e_state.initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t current_time = kernel_get_tick_count();
|
||||
|
||||
mutex_lock(&e2e_state.mutex, 100);
|
||||
|
||||
/* Check timeouts */
|
||||
for (uint8_t i = 0; i < e2e_state.profile_count; i++) {
|
||||
E2eConfig_t* config = &e2e_state.profiles[i];
|
||||
|
||||
if (e2e_state.last_receive_time[i] > 0) {
|
||||
if ((current_time - e2e_state.last_receive_time[i]) >
|
||||
config->timeout_ms) {
|
||||
/* Timeout error */
|
||||
e2e_state.error_counters[i]++;
|
||||
e2e_state.last_receive_time[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&e2e_state.mutex);
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
/**
|
||||
* @file memory_protection.c
|
||||
* @brief Memory Protection for safety-critical applications
|
||||
*/
|
||||
|
||||
#include "kernel.h"
|
||||
#include "task.h"
|
||||
#include <string.h>
|
||||
|
||||
/* Memory Protection Configuration */
|
||||
#define MPU_MAX_REGIONS 8
|
||||
#define MEMORY_PROTECTION_ALIGNMENT 32 /* 32 bytes minimum */
|
||||
|
||||
/* Memory Region Attributes */
|
||||
typedef struct {
|
||||
uint32_t base_address;
|
||||
uint32_t size;
|
||||
uint8_t permissions;
|
||||
bool executable;
|
||||
bool cacheable;
|
||||
bool bufferable;
|
||||
} MemoryRegion_t;
|
||||
|
||||
/* Memory Protection State */
|
||||
typedef struct {
|
||||
bool initialized;
|
||||
bool enabled;
|
||||
MemoryRegion_t regions[MPU_MAX_REGIONS];
|
||||
uint8_t region_count;
|
||||
TaskHandle_t current_task;
|
||||
Mutex_t mutex;
|
||||
} MemoryProtectionState_t;
|
||||
|
||||
static MemoryProtectionState_t memory_protection;
|
||||
|
||||
/* Initialize Memory Protection */
|
||||
KernelStatus_t memory_protection_init(void) {
|
||||
if (memory_protection.initialized) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
memset(&memory_protection, 0, sizeof(MemoryProtectionState_t));
|
||||
memory_protection.enabled = false;
|
||||
memory_protection.region_count = 0;
|
||||
|
||||
mutex_create(&memory_protection.mutex, false);
|
||||
|
||||
memory_protection.initialized = true;
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Configure Memory Region */
|
||||
KernelStatus_t memory_protection_configure_region(uint32_t base_address,
|
||||
uint32_t size,
|
||||
uint8_t permissions,
|
||||
bool executable) {
|
||||
if (!memory_protection.initialized) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
if (memory_protection.region_count >= MPU_MAX_REGIONS) {
|
||||
return KERNEL_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
/* Validate alignment */
|
||||
if ((base_address % MEMORY_PROTECTION_ALIGNMENT) != 0 ||
|
||||
(size % MEMORY_PROTECTION_ALIGNMENT) != 0) {
|
||||
return KERNEL_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
mutex_lock(&memory_protection.mutex, 100);
|
||||
|
||||
MemoryRegion_t* region = &memory_protection.regions[memory_protection.region_count];
|
||||
region->base_address = base_address;
|
||||
region->size = size;
|
||||
region->permissions = permissions;
|
||||
region->executable = executable;
|
||||
|
||||
memory_protection.region_count++;
|
||||
|
||||
mutex_unlock(&memory_protection.mutex);
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Enable Memory Protection */
|
||||
KernelStatus_t memory_protection_enable(void) {
|
||||
if (!memory_protection.initialized) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
/* Configure MPU hardware */
|
||||
hal_mpu_enable();
|
||||
|
||||
/* Configure regions */
|
||||
for (uint8_t i = 0; i < memory_protection.region_count; i++) {
|
||||
MemoryRegion_t* region = &memory_protection.regions[i];
|
||||
hal_mpu_configure_region(i, region->base_address, region->size,
|
||||
region->permissions, region->executable);
|
||||
}
|
||||
|
||||
memory_protection.enabled = true;
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Disable Memory Protection */
|
||||
KernelStatus_t memory_protection_disable(void) {
|
||||
if (!memory_protection.initialized) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
hal_mpu_disable();
|
||||
memory_protection.enabled = false;
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Set Task Memory Region */
|
||||
KernelStatus_t memory_protection_set_task_region(TaskHandle_t task,
|
||||
uint32_t base_address,
|
||||
uint32_t size) {
|
||||
if (!memory_protection.initialized || task == NULL) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
/* Configure task-specific memory region */
|
||||
hal_mpu_configure_task_region(task, base_address, size);
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Check Memory Access */
|
||||
bool memory_protection_check_access(uint32_t address, uint8_t access_type) {
|
||||
if (!memory_protection.initialized || !memory_protection.enabled) {
|
||||
return true; /* Protection disabled */
|
||||
}
|
||||
|
||||
/* Check if address is within any protected region */
|
||||
for (uint8_t i = 0; i < memory_protection.region_count; i++) {
|
||||
MemoryRegion_t* region = &memory_protection.regions[i];
|
||||
|
||||
if (address >= region->base_address &&
|
||||
address < (region->base_address + region->size)) {
|
||||
/* Check permissions */
|
||||
if ((region->permissions & access_type) == 0) {
|
||||
return false; /* Access denied */
|
||||
}
|
||||
return true; /* Access allowed */
|
||||
}
|
||||
}
|
||||
|
||||
return false; /* Address not in any region */
|
||||
}
|
||||
|
||||
/* Memory Protection Fault Handler */
|
||||
void memory_protection_fault_handler(uint32_t fault_address) {
|
||||
/* Log fault */
|
||||
fault_handler_process(2, fault_address, 0);
|
||||
|
||||
/* Enter safe state */
|
||||
while (1) {
|
||||
/* Wait for watchdog reset */
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,212 @@
|
||||
/**
|
||||
* @file watchdog_manager.c
|
||||
* @brief Watchdog Manager for safety-critical applications
|
||||
*/
|
||||
|
||||
#include "kernel.h"
|
||||
#include "task.h"
|
||||
#include <string.h>
|
||||
|
||||
/* Watchdog Configuration */
|
||||
#define WATCHDOG_MAX_TASKS 16
|
||||
#define WATCHDOG_DEFAULT_TIMEOUT 100 /* ms */
|
||||
#define WATCHDOG_MAX_ALIVE_COUNT 5
|
||||
|
||||
/* Watchdog Task Status */
|
||||
typedef enum {
|
||||
WATCHDOG_TASK_ALIVE = 0,
|
||||
WATCHDOG_TASK_TIMEOUT = 1,
|
||||
WATCHDOG_TASK_SUSPENDED = 2
|
||||
} WatchdogTaskStatus_t;
|
||||
|
||||
/* Watchdog Task Entry */
|
||||
typedef struct {
|
||||
TaskHandle_t task;
|
||||
char task_name[16];
|
||||
uint32_t timeout_ms;
|
||||
uint32_t last_alive_time;
|
||||
uint32_t alive_count;
|
||||
WatchdogTaskStatus_t status;
|
||||
bool is_supervised;
|
||||
} WatchdogTaskEntry_t;
|
||||
|
||||
/* Watchdog Manager State */
|
||||
typedef struct {
|
||||
bool initialized;
|
||||
bool enabled;
|
||||
WatchdogTaskEntry_t tasks[WATCHDOG_MAX_TASKS];
|
||||
uint8_t task_count;
|
||||
uint32_t global_timeout_ms;
|
||||
uint32_t last_service_time;
|
||||
Mutex_t mutex;
|
||||
void (*system_reset_callback)(void);
|
||||
void (*task_timeout_callback)(TaskHandle_t task);
|
||||
} WatchdogManagerState_t;
|
||||
|
||||
static WatchdogManagerState_t watchdog_manager;
|
||||
|
||||
/* Initialize Watchdog Manager */
|
||||
KernelStatus_t watchdog_manager_init(uint32_t global_timeout_ms) {
|
||||
if (watchdog_manager.initialized) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
memset(&watchdog_manager, 0, sizeof(WatchdogManagerState_t));
|
||||
watchdog_manager.global_timeout_ms = global_timeout_ms;
|
||||
watchdog_manager.enabled = false;
|
||||
watchdog_manager.last_service_time = kernel_get_tick_count();
|
||||
|
||||
mutex_create(&watchdog_manager.mutex, false);
|
||||
|
||||
watchdog_manager.initialized = true;
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Register Task for Supervision */
|
||||
KernelStatus_t watchdog_register_task(TaskHandle_t task, const char* name,
|
||||
uint32_t timeout_ms) {
|
||||
if (!watchdog_manager.initialized || task == NULL) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
if (watchdog_manager.task_count >= WATCHDOG_MAX_TASKS) {
|
||||
return KERNEL_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
mutex_lock(&watchdog_manager.mutex, 100);
|
||||
|
||||
WatchdogTaskEntry_t* entry = &watchdog_manager.tasks[watchdog_manager.task_count];
|
||||
entry->task = task;
|
||||
strncpy(entry->task_name, name, sizeof(entry->task_name) - 1);
|
||||
entry->timeout_ms = timeout_ms;
|
||||
entry->last_alive_time = kernel_get_tick_count();
|
||||
entry->alive_count = 0;
|
||||
entry->status = WATCHDOG_TASK_ALIVE;
|
||||
entry->is_supervised = true;
|
||||
|
||||
watchdog_manager.task_count++;
|
||||
|
||||
mutex_unlock(&watchdog_manager.mutex);
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Task Alive Indication */
|
||||
KernelStatus_t watchdog_task_alive(TaskHandle_t task) {
|
||||
if (!watchdog_manager.initialized || task == NULL) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
mutex_lock(&watchdog_manager.mutex, 100);
|
||||
|
||||
for (uint8_t i = 0; i < watchdog_manager.task_count; i++) {
|
||||
if (watchdog_manager.tasks[i].task == task) {
|
||||
watchdog_manager.tasks[i].last_alive_time = kernel_get_tick_count();
|
||||
watchdog_manager.tasks[i].alive_count++;
|
||||
watchdog_manager.tasks[i].status = WATCHDOG_TASK_ALIVE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&watchdog_manager.mutex);
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Enable Watchdog */
|
||||
KernelStatus_t watchdog_enable(void) {
|
||||
if (!watchdog_manager.initialized) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
watchdog_manager.enabled = true;
|
||||
watchdog_manager.last_service_time = kernel_get_tick_count();
|
||||
|
||||
/* Enable hardware watchdog */
|
||||
hal_watchdog_enable(watchdog_manager.global_timeout_ms);
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Disable Watchdog */
|
||||
KernelStatus_t watchdog_disable(void) {
|
||||
if (!watchdog_manager.initialized) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
watchdog_manager.enabled = false;
|
||||
|
||||
/* Disable hardware watchdog */
|
||||
hal_watchdog_disable();
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Service Watchdog */
|
||||
KernelStatus_t watchdog_service(void) {
|
||||
if (!watchdog_manager.initialized || !watchdog_manager.enabled) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
/* Check all supervised tasks */
|
||||
uint32_t current_time = kernel_get_tick_count();
|
||||
bool all_tasks_alive = true;
|
||||
|
||||
mutex_lock(&watchdog_manager.mutex, 100);
|
||||
|
||||
for (uint8_t i = 0; i < watchdog_manager.task_count; i++) {
|
||||
WatchdogTaskEntry_t* entry = &watchdog_manager.tasks[i];
|
||||
|
||||
if (!entry->is_supervised) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Check if task is alive */
|
||||
if ((current_time - entry->last_alive_time) > entry->timeout_ms) {
|
||||
entry->status = WATCHDOG_TASK_TIMEOUT;
|
||||
all_tasks_alive = false;
|
||||
|
||||
/* Call task timeout callback */
|
||||
if (watchdog_manager.task_timeout_callback != NULL) {
|
||||
watchdog_manager.task_timeout_callback(entry->task);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&watchdog_manager.mutex);
|
||||
|
||||
if (all_tasks_alive) {
|
||||
/* Service hardware watchdog */
|
||||
hal_watchdog_service();
|
||||
watchdog_manager.last_service_time = current_time;
|
||||
return KERNEL_OK;
|
||||
} else {
|
||||
/* Don't service watchdog - will trigger reset */
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
/* Register Callbacks */
|
||||
KernelStatus_t watchdog_register_callbacks(
|
||||
void (*system_reset_callback)(void),
|
||||
void (*task_timeout_callback)(TaskHandle_t task)) {
|
||||
|
||||
if (!watchdog_manager.initialized) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
watchdog_manager.system_reset_callback = system_reset_callback;
|
||||
watchdog_manager.task_timeout_callback = task_timeout_callback;
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Watchdog Main Function */
|
||||
void watchdog_manager_main_function(void) {
|
||||
if (!watchdog_manager.initialized || !watchdog_manager.enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Service watchdog periodically */
|
||||
watchdog_service();
|
||||
}
|
||||
Reference in New Issue
Block a user