第一次提交

This commit is contained in:
2026-04-09 10:14:20 +08:00
commit e325a77b42
584 changed files with 279711 additions and 0 deletions

View File

@@ -0,0 +1,754 @@
#include "app_mozen_handler.h"
#include "mozen_tunnel.h"
#include "mx_log.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "app_calibration.h"
#include "main.h"
#include "cJSON.h"
// Define logs if not using MX_LOG directly
#ifndef MX_LOGD
#define MX_LOGD MOZEN_LOGD
#define MX_LOGI MOZEN_LOGI
#define MX_LOGW MOZEN_LOGW
#define MX_LOGE MOZEN_LOGE
#endif
#define MX_PTO_TAG "MOZEN_APP"
volatile uint16_t g_sensor_frames_to_send = 0;
volatile uint8_t g_sensor_data_type = SENSOR_BIT_16;
static mozen_protocol_t *s_prot_instance = NULL;
static mozen_tunnel_t s_tunnel_instance;
// --- Tunnel Stream State ---
static struct
{
bool is_streaming_write;
uint8_t* write_ptr;
uint8_t target_cmd;
uint8_t target_sub_class;
} s_stream_state;
// --- Response Utilities ---
#pragma pack(1)
typedef struct
{
uint8_t target_id;
uint8_t sub_class;
uint8_t operation;
} mozen_cmd_header_t;
#pragma pack()
typedef void (*cmd_sub_handler_t)(const mozen_cmd_header_t* header, const uint8_t* payload, uint16_t payload_len);
typedef struct
{
uint8_t target_id;
cmd_sub_handler_t handler;
} cmd_handler_entry_t;
// --- Forward Declarations ---
static void param_handle_report_mode(const mozen_cmd_header_t* header, const uint8_t* payload, uint16_t payload_len);
static void param_handle_device_info(const mozen_cmd_header_t* header, const uint8_t* payload, uint16_t payload_len);
static void param_handle_cali_display(const mozen_cmd_header_t* header, const uint8_t* payload, uint16_t payload_len);
static void prod_handle_sys_cmd(const mozen_cmd_header_t* header, const uint8_t* payload, uint16_t payload_len);
static void prod_handle_map_cali_cmd(const mozen_cmd_header_t* header, const uint8_t* payload, uint16_t payload_len);
// --- param setters ---
static const cmd_handler_entry_t s_param_handlers[] =
{
{MOZEN_PARAM_TGT_REPORT_MODE, param_handle_report_mode}, // 0x01
{MOZEN_PARAM_TGT_DEVICE_INFO, param_handle_device_info}, // 0x02
{MOZEN_PARAM_TGT_CALI_DISPLAY, param_handle_cali_display}, // 0x03
};
// --- Production Test Targets ---
static const cmd_handler_entry_t s_prod_handlers[] =
{
{MOZEN_PROD_TGT_SYS, prod_handle_sys_cmd},
{MOZEN_PROD_TGT_MAP_CALI, prod_handle_map_cali_cmd},
};
// --- Init ---
mozen_protocol_t g_mozen_prot; // Global mozen protocol instance
#define RX_BUFFER_SIZE 1024 // RX buffer size
#define TX_BUFFER_SIZE 1024 // TX buffer size
static uint8_t s_rx_buf[RX_BUFFER_SIZE]; // RX buffer
static uint8_t s_tx_buf_main[TX_BUFFER_SIZE]; // TX buffer
static uint8_t s_tx_buf_irq[TX_BUFFER_SIZE]; // TX buffer
// --- Legacy Compatibility Functions ---
static uint8_t is_legacy_config_payload(const uint8_t *data, uint16_t len)
{
if ((data == NULL) || (len < 3U)) return 0U;
if ((data[0] != 0x3DU) && (data[0] != 0x3FU)) return 0U;
if ((data[1] != '{') || (data[len - 1U] != '}')) return 0U;
return 1U;
}
static int handle_old_protocol_device_status(uint8_t *data, uint16_t length)
{
if ((data == NULL) || (length < 2U)) return -1;
if (data[0] == 0x3D) // "=" <20><><EFBFBD>ò<EFBFBD><C3B2><EFBFBD>
{
cJSON *root = cJSON_Parse((char *)&data[1]);
if (!root)
{
MX_LOGE(MX_PTO_TAG, "old protocol json parse failed");
return -1;
}
MX_LOGD(MX_PTO_TAG, "old protocol json parsed");
// 1. <20><>Ӧ<EFBFBD>ò<EFBFBD><C3B2><EFBFBD>ȡ<EFBFBD><C8A1>ǰ״̬<D7B4><CCAC><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>и<EFBFBD><D0B8><EFBFBD>
app_device_status_t new_status = *app_device_status_get();
// 2. <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֶ<EFBFBD>
cJSON *led_sw = cJSON_GetObjectItem(root, "led_sw");
if (led_sw) new_status.led_sw = (uint8_t)led_sw->valueint;
cJSON *tx_sw = cJSON_GetObjectItem(root, "tx_sw");
if (tx_sw)
{
int tx_sw_val = tx_sw->valueint;
if (new_status.sensor_tx_sw == 1 && tx_sw_val == 0)
{
new_status.tx_stopping = 1;
}
new_status.sensor_tx_sw = (uint8_t)tx_sw_val;
}
cJSON *pick_sw = cJSON_GetObjectItem(root, "pick_sw");
if (pick_sw) new_status.output_pick = (uint8_t)pick_sw->valueint;
// printf("pick_sw: %d\n", new_status.output_pick);
// 3. <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ѧ<EFBFBD><EFBFBD><EAB6A8><EFBFBD><EFBFBD> (math / math_full)
cJSON *math_arr = cJSON_GetObjectItem(root, "math");
cJSON *extra_flag = cJSON_GetObjectItem(root, "math_full");
if (math_arr && cJSON_IsArray(math_arr))
{
int size = cJSON_GetArraySize(math_arr);
app_math_cali_t temp_params;
if (app_math_get_temp_params(&temp_params))
{
if (size >= 1) {
temp_params.max_trigger_res_value = (uint16_t)cJSON_GetArrayItem(math_arr, 0)->valueint;
}
if (size >= 2) {
temp_params.min_trigger_res_value = (uint16_t)cJSON_GetArrayItem(math_arr, 1)->valueint;
}
if (extra_flag && extra_flag->valueint && size >= 5) {
temp_params.max_display_value = (uint16_t)cJSON_GetArrayItem(math_arr, 4)->valueint;
}
app_math_set_temp_params(&temp_params);
}
}
// 4. <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
cJSON *creep_strength = cJSON_GetObjectItem(root, "creep_strength");
cJSON *creep_level = cJSON_GetObjectItem(root, "creep_level");
if (creep_strength || creep_level)
{
app_creep_params temp_creep;
app_creep_read_params(&temp_creep);
if (creep_strength) {
temp_creep.creep_strength = (uint8_t)creep_strength->valueint;
}
if (creep_level) {
temp_creep.creep_level = (uint8_t)creep_level->valueint;
}
app_creep_write_temp_params(&temp_creep);
}
// 5. Flash <20>־û<D6BE><C3BB><EFBFBD><EFBFBD><EFBFBD>
cJSON *flash_cmd = cJSON_GetObjectItem(root, "flash");
if(flash_cmd && cJSON_IsString(flash_cmd))
{
if (strcmp(flash_cmd->valuestring, "creep_write") == 0)
{
app_creep_params temp_creep;
app_creep_read_params(&temp_creep);
app_creep_save_params(&temp_creep);
}
}
// 6. <20><><EFBFBD><EFBFBD><EFBFBD>º<EFBFBD><C2BA><EFBFBD> Legacy ״̬<D7B4><CCAC>д<EFBFBD><D0B4>Ӧ<EFBFBD>ò<EFBFBD>
app_device_status_set(&new_status);
cJSON_Delete(root);
}
return 0;
}
static bool app_mozen_raw_frame_handler(void *context, const uint8_t *raw_frame, uint16_t length)
{
mozen_frame_header_t header;
if (length < sizeof(mozen_frame_header_t) + 2) return false;
memcpy(&header, raw_frame, sizeof(mozen_frame_header_t));
if (header.command_id == MOZEN_CMD_ID_PARAM_CONFIG) // 0x02
{
uint16_t full_frame_len = header.frame_length;
uint16_t data_len = full_frame_len - sizeof(mozen_frame_header_t);
const uint8_t *data_ptr = raw_frame + sizeof(mozen_frame_header_t);
uint16_t received_crc;
memcpy(&received_crc, raw_frame + full_frame_len, sizeof(uint16_t));
uint16_t calc_crc = mozen_protocol_checksum(raw_frame, full_frame_len);
if ((calc_crc == received_crc) || is_legacy_config_payload(data_ptr, data_len))
{
handle_old_protocol_device_status((uint8_t *)data_ptr, data_len);
return true; // Handled
}
}
return false; // Not handled, fallback to standard parsing
}
// --- Command Handlers ---
/*
* @brief handle sensor data
* @param context: context
* @param cmd_id: command id
* @param data: data
* @param length: length
* @retval none
*/
static void handle_sensor_data(void *context, uint8_t cmd_id, const uint8_t *data, uint16_t length)
{
if (length < 3)
{
MX_LOGE(MX_PTO_TAG, "sensor command length invalid: %u", (unsigned int)length);
return;
}
uint8_t new_data_type = data[0];
if (new_data_type != g_sensor_data_type)
{
g_sensor_data_type = new_data_type;
app_math_cali_t temp_params;
bool got_params = false;
got_params = app_math_get_temp_params(&temp_params);
if (got_params)
{
temp_params.max_display_value = 4095;
temp_params.sensor_data_type = g_sensor_data_type;
app_math_set_temp_params(&temp_params);
}
}
g_sensor_frames_to_send = *(uint16_t*)(&data[1]);
}
// --- Response Utilities ---
/*
* @brief send mozen command response
* @param cmd_id: command id
* @param req_header: request header
* @param payload: payload
* @param payload_len: payload length
* @retval none
*/
static void send_mozen_cmd_response(uint8_t cmd_id, const mozen_cmd_header_t* req_header, const uint8_t* payload, uint16_t payload_len)
{
static uint8_t response_buf[1024];
mozen_cmd_header_t* res_header = (mozen_cmd_header_t*)response_buf;
res_header->target_id = req_header->target_id;
res_header->sub_class = req_header->sub_class;
res_header->operation = req_header->operation;
if (payload && payload_len > 0)
{
memcpy(response_buf + sizeof(mozen_cmd_header_t), payload, payload_len);
}
mozen_protocol_send_frame(s_prot_instance, cmd_id, response_buf, sizeof(mozen_cmd_header_t) + payload_len, false);
}
/*
* @brief send mozen command ok or ng
* @param cmd_id: command id
* @param req_header: request header
* @param success: success or not
* @retval none
*/
static inline void send_mozen_cmd_ok_ng(uint8_t cmd_id, const mozen_cmd_header_t* req_header, bool success)
{
uint8_t payload[2] = { success ? 'o' : 'N', success ? 'k' : 'G' };
send_mozen_cmd_response(cmd_id, req_header, payload, 2);
}
// --- Parameter Config Handlers (Cmd ID: 0x02) ---
/*
* @brief handle report mode
* @param header: header
* @param payload: payload
* @param payload_len: payload length
* @retval none
*/
static void param_handle_report_mode(const mozen_cmd_header_t* header, const uint8_t* payload, uint16_t payload_len)
{
if (header->sub_class == 0x01 && header->operation == 0x01 && payload_len >= 1)
{
if (payload[0] == 0x00) g_is_active_reporting = false;
else if (payload[0] == 0x01) g_is_active_reporting = true;
send_mozen_cmd_ok_ng(MOZEN_CMD_ID_PARAM_CONFIG, header, true);
}
}
/*
* @brief handle device info
* @param header: header
* @param payload: payload
* @param payload_len: payload length
* @retval none
*/
static void param_handle_device_info(const mozen_cmd_header_t* header, const uint8_t* payload, uint16_t payload_len)
{
if (header->operation == 0x00)
{
app_dev_info dev_info;
char sensor_size_text[sizeof(dev_info.sensor_size) + 1U] = {0};
char protocol_ver_text[sizeof(dev_info.protocol_ver) + 1U] = {0};
if (app_device_get_info(&dev_info))
{
memcpy(sensor_size_text, dev_info.sensor_size, sizeof(dev_info.sensor_size));
memcpy(protocol_ver_text, dev_info.protocol_ver, sizeof(dev_info.protocol_ver));
uint8_t resp_payload[256];
resp_payload[0] = 'o';
resp_payload[1] = 'k';
int json_len = snprintf((char *)(resp_payload + 2),
sizeof(resp_payload) - 2,
"{\"PN\":\"%s\",\"SN\":\"%s\",\"SV\":\"%s\",\"HVI\":\"%s\",\"XY\":\"%s\",\"Protocol\":\"%s\"}",
dev_info.pn, dev_info.sn, dev_info.sw_ver, dev_info.hw_ver, sensor_size_text, protocol_ver_text);
if (json_len > 0) {
send_mozen_cmd_response(MOZEN_CMD_ID_PARAM_CONFIG, header, resp_payload, 2 + (uint16_t)json_len);
} else {
send_mozen_cmd_ok_ng(MOZEN_CMD_ID_PARAM_CONFIG, header, false);
}
}
else
{
send_mozen_cmd_ok_ng(MOZEN_CMD_ID_PARAM_CONFIG, header, false);
}
}
}
/*
* @brief handle calibration display
* @param header: header
* @param payload: payload
* @param payload_len: payload length
* @retval none
*/
static void param_handle_cali_display(const mozen_cmd_header_t* header, const uint8_t* payload, uint16_t payload_len)
{
if (header->operation == 0x00)
{
uint8_t success = 0;
uint16_t data_len = 0;
uint8_t resp_payload[256];
if (header->sub_class == 0x01)
{
success = app_math_read_temp_params(resp_payload + 2, &data_len);
}
else if (header->sub_class == 0x02)
{
success = app_math_read_solidified_params(resp_payload + 2, &data_len);
}
if (success)
{
resp_payload[0] = 'o'; resp_payload[1] = 'k';
send_mozen_cmd_response(MOZEN_CMD_ID_PARAM_CONFIG, header, resp_payload, 2 + data_len);
}
else
{
send_mozen_cmd_ok_ng(MOZEN_CMD_ID_PARAM_CONFIG, header, false);
}
}
else if (header->operation == 0x01)
{
uint8_t success = 0;
if (header->sub_class == 0x01)
{
success = app_math_write_temp_params(payload, payload_len);
}
else if (header->sub_class == 0x02)
{
success = app_math_save_params(payload, payload_len);
}
else if (header->sub_class == 0x03)
{
success = app_math_clear_params();
app_calibration_invalidate_runtime();
}
send_mozen_cmd_ok_ng(MOZEN_CMD_ID_PARAM_CONFIG, header, success);
}
}
/*
* @brief handle param config
* @param context: context
* @param cmd_id: cmd id
* @param data: data
* @param length: length
* @retval none
*/
static void handle_param_config(void *context, uint8_t cmd_id, const uint8_t *data, uint16_t length)
{
if (length < sizeof(mozen_cmd_header_t)) return;
const mozen_cmd_header_t* cmd_header = (const mozen_cmd_header_t*)data;
const uint8_t* cmd_payload = data + sizeof(mozen_cmd_header_t);
uint16_t payload_len = length - sizeof(mozen_cmd_header_t);
for (size_t i = 0; i < sizeof(s_param_handlers) / sizeof(s_param_handlers[0]); i++)
{
if (s_param_handlers[i].target_id == cmd_header->target_id)
{
s_param_handlers[i].handler(cmd_header, cmd_payload, payload_len);
break;
}
}
}
// --- Production Test Handlers (Cmd ID: 0xAA) ---
/*
* @brief handle system command
* @param header: header
* @param payload: payload
* @param payload_len: payload length
* @retval none
*/
static void prod_handle_sys_cmd(const mozen_cmd_header_t* header, const uint8_t* payload, uint16_t payload_len)
{
if (header->operation == 0x01)
{
if (header->sub_class == 0x02)
{
if(payload_len > 0)
{
g_is_creep_enable = (payload[0] == 0x01);
send_mozen_cmd_ok_ng(MOZEN_CMD_ID_PROD_TEST, header, true);
}
}
else if (header->sub_class == 0x01)
{
app_dev_info dev_info;
if (payload_len == 0U || payload_len >= sizeof(dev_info.sn))
{
send_mozen_cmd_ok_ng(MOZEN_CMD_ID_PROD_TEST, header, false);
return;
}
if (!app_device_get_info(&dev_info))
{
send_mozen_cmd_ok_ng(MOZEN_CMD_ID_PROD_TEST, header, false);
return;
}
memset(dev_info.sn, 0, sizeof(dev_info.sn));
memcpy(dev_info.sn, payload, payload_len);
if (!app_device_set_info(&dev_info))
{
send_mozen_cmd_ok_ng(MOZEN_CMD_ID_PROD_TEST, header, false);
return;
}
send_mozen_cmd_ok_ng(MOZEN_CMD_ID_PROD_TEST, header, true);
}
}
}
/*
* @brief handle map calibration
* @param header: header
* @param payload: payload
* @param payload_len: payload length
* @retval none
*/
static void prod_handle_map_cali_cmd(const mozen_cmd_header_t* header, const uint8_t* payload, uint16_t payload_len)
{
uint8_t resp_payload[260];
// First, handle map temp params (operation 0x01, sub_class 0x3F / 0x3D) which return no response
// First, handle map temp params (operation 0x01, sub_class 0x3F / 0x3D) which return no response
if (header->operation == 0x01)
{
if (header->sub_class == 0x3F)
{
app_map_write_temp_params(payload, payload_len);
return;
}
else if (header->sub_class == 0x3D)
{
app_map_save_params(payload, payload_len);
return;
}
}
switch (header->sub_class)
{
case 0x4F:
if (header->operation == 0x00)
{
uint16_t d_len;
if(app_pressure_read_temp_params(resp_payload + 2, &d_len))
{
resp_payload[0] = 'o'; resp_payload[1] = 'k';
send_mozen_cmd_response(MOZEN_CMD_ID_PROD_TEST, header, resp_payload, 2 + d_len);
} else {
send_mozen_cmd_ok_ng(MOZEN_CMD_ID_PROD_TEST, header, false);
}
} else if (header->operation == 0x01) {
bool success = app_pressure_write_temp_params(payload, payload_len);
send_mozen_cmd_ok_ng(MOZEN_CMD_ID_PROD_TEST, header, success);
}
break;
case 0x4D:
if (header->operation == 0x00)
{
uint16_t d_len;
if(app_pressure_read_solidified_params(resp_payload + 2, &d_len))
{
resp_payload[0] = 'o'; resp_payload[1] = 'k';
send_mozen_cmd_response(MOZEN_CMD_ID_PROD_TEST, header, resp_payload, 2 + d_len);
} else {
send_mozen_cmd_ok_ng(MOZEN_CMD_ID_PROD_TEST, header, false);
}
} else if (header->operation == 0x01)
{
bool success = app_pressure_save_params(payload, payload_len);
send_mozen_cmd_ok_ng(MOZEN_CMD_ID_PROD_TEST, header, success);
}
break;
case 0x3F:
case 0x3D:
if (header->operation == 0x00)
{
uint32_t total_data_len = app_map_data_size();
uint16_t total_packets = (total_data_len + 240 - 1) / 240;
memcpy(resp_payload, &total_data_len, sizeof(uint32_t));
memcpy(resp_payload + 4, &total_packets, sizeof(uint16_t));
send_mozen_cmd_response(MOZEN_CMD_ID_PROD_TEST, header, resp_payload, 6);
}
else if (header->operation == 0x02)
{
if (payload_len < 2)
{
send_mozen_cmd_ok_ng(MOZEN_CMD_ID_PROD_TEST, header, false);
break;
}
uint16_t packet_index;
memcpy(&packet_index, payload, sizeof(uint16_t));
uint32_t total_data_len = app_map_data_size();
uint32_t offset = (packet_index - 1) * 240;
if (offset >= total_data_len)
{
send_mozen_cmd_ok_ng(MOZEN_CMD_ID_PROD_TEST, header, false);
break;
}
uint16_t chunk_len = 240;
if (offset + chunk_len > total_data_len) chunk_len = total_data_len - offset;
memcpy(resp_payload, &packet_index, sizeof(uint16_t));
memcpy(resp_payload + 2, app_map_data_ptr() + offset, chunk_len);
send_mozen_cmd_response(MOZEN_CMD_ID_PROD_TEST, header, resp_payload, 2 + chunk_len);
}
break;
case 0x00:
if (header->operation == 0x01)
{
bool success = app_pressure_clear_params();
success = success && app_map_clear_params();
send_mozen_cmd_ok_ng(MOZEN_CMD_ID_PROD_TEST, header, success);
app_calibration_invalidate_runtime();
}
break;
}
}
/*
* This function handles the production test command.
* It checks the target ID and calls the appropriate handler function.
* @param context The context pointer.
* @param cmd_id The command ID.
* @param data The command data.
* @param length The length of the command data.
*/
static void handle_production_test(void *context, uint8_t cmd_id, const uint8_t *data, uint16_t length)
{
if (length < sizeof(mozen_cmd_header_t)) return;
const mozen_cmd_header_t* cmd_header = (const mozen_cmd_header_t*)data;
const uint8_t* cmd_payload = data + sizeof(mozen_cmd_header_t);
uint16_t payload_len = length - sizeof(mozen_cmd_header_t);
for (size_t i = 0; i < sizeof(s_prod_handlers) / sizeof(s_prod_handlers[0]); i++)
{
if (s_prod_handlers[i].target_id == cmd_header->target_id) {
s_prod_handlers[i].handler(cmd_header, cmd_payload, payload_len);
break;
}
}
}
/*
* This function handles the tunnel command.
* It calls the tunnel handle command function with the command data.
* @param context The context pointer.
* @param cmd_id The command ID.
* @param data The command data.
* @param length The length of the command data.
*/
static void handle_tunnel_command(void *context, uint8_t cmd_id, const uint8_t *data, uint16_t length)
{
mozen_tunnel_handle_command(&s_tunnel_instance, data, length);
}
// --- Tunnel Callbacks ---
/*
* This function is called when a stream starts.
* It checks if the target command is production test and the sub class is 0x3F or 0x3D.
* If so, it sets the stream state to streaming write and initializes the write pointer.
* @param context The context pointer.
* @param target_cmd The target command.
* @param first_chunk_data The first chunk of data.
* @param chunk_len The length of the chunk.
* @return true if the stream is started, false otherwise.
*/
static bool tunnel_on_stream_start(void *context, uint8_t target_cmd, const uint8_t *first_chunk_data, uint16_t chunk_len)
{
if (target_cmd == MOZEN_CMD_ID_PROD_TEST) {
const mozen_cmd_header_t* pt_header = (const mozen_cmd_header_t*)first_chunk_data;
if (chunk_len >= sizeof(mozen_cmd_header_t) && pt_header->target_id == MOZEN_PROD_TGT_MAP_CALI && pt_header->operation == 0x01)
{
if (pt_header->sub_class == 0x3F || pt_header->sub_class == 0x3D)
{
s_stream_state.is_streaming_write = true;
s_stream_state.target_sub_class = pt_header->sub_class;
s_stream_state.target_cmd = target_cmd;
s_stream_state.write_ptr = app_map_data_ptr();
uint16_t data_offset = sizeof(mozen_cmd_header_t);
uint16_t data_to_write = chunk_len - data_offset;
uint8_t* end_ptr = (app_map_data_ptr() + app_map_data_size());
if (s_stream_state.write_ptr + data_to_write > end_ptr) {
data_to_write = end_ptr - s_stream_state.write_ptr;
}
if (data_to_write > 0) {
memcpy(s_stream_state.write_ptr, first_chunk_data + data_offset, data_to_write);
s_stream_state.write_ptr += data_to_write;
}
return true;
}
}
}
return false;
}
/*
* This function is called when data is received during a stream.
* It checks if the stream is in streaming write mode and writes the data to the map data buffer.
* @param context The context pointer.
* @param data The data.
* @param len The length of the data.
* @return true if the data is handled, false otherwise.
*/
static bool tunnel_on_stream_data(void *context, const uint8_t *data, uint16_t len)
{
if (s_stream_state.is_streaming_write) {
uint8_t* end_ptr = (app_map_data_ptr() + app_map_data_size());
uint16_t data_to_write = len;
if (s_stream_state.write_ptr >= end_ptr) {
data_to_write = 0;
} else if (s_stream_state.write_ptr + data_to_write > end_ptr) {
data_to_write = end_ptr - s_stream_state.write_ptr;
}
if (data_to_write > 0) {
memcpy(s_stream_state.write_ptr, data, data_to_write);
s_stream_state.write_ptr += data_to_write;
}
return true;
}
return false;
}
/*
* This function is called when a stream finishes.
* It checks if the stream is in streaming write mode and saves the map parameters if the target command is production test and the sub class is 0x3F or 0x3D.
* @param context The context pointer.
* @param success The success flag.
* @return None.
*/
static void tunnel_on_stream_finish(void *context, bool success)
{
if (s_stream_state.is_streaming_write && success)
{
uint32_t expected_bytes = app_map_data_size();
uint32_t written_bytes = (uint32_t)(s_stream_state.write_ptr - app_map_data_ptr());
if (written_bytes >= expected_bytes)
{
if (s_stream_state.target_cmd == MOZEN_CMD_ID_PROD_TEST && s_stream_state.target_sub_class == 0x3D)
{
app_map_save_params(app_map_data_ptr(), (uint16_t)expected_bytes);
app_calibration_request_reset_effect();
}
}
}
s_stream_state.is_streaming_write = false;
s_stream_state.write_ptr = NULL;
}
// --- Init ---
/*
* @brief: Initialize the mozen handler.
* @param tx_fn: The function to send data.
* @return None.
*/
void app_mozen_init(mozen_tx_fn_t tx_fn)
{
mozen_protocol_init(&g_mozen_prot, s_rx_buf, sizeof(s_rx_buf),
s_tx_buf_main, s_tx_buf_irq, sizeof(s_tx_buf_main),
tx_fn, NULL);
app_mozen_handler_init(&g_mozen_prot);
}
/*
* @brief: Handle raw frame from mozen protocol
* @param: prot - Protocol instance
* @return: None
*/
void app_mozen_handler_init(mozen_protocol_t *prot)
{
s_prot_instance = prot;
// Register commands
mozen_protocol_register_handler(prot, MOZEN_CMD_ID_SENSOR_DATA, handle_sensor_data);
mozen_protocol_register_handler(prot, MOZEN_CMD_ID_PARAM_CONFIG, handle_param_config);
mozen_protocol_register_handler(prot, MOZEN_CMD_ID_PROD_TEST, handle_production_test);
mozen_protocol_register_handler(prot, MOZEN_TUNNEL_COMMAND_ID, handle_tunnel_command);
// Legacy handler
mozen_protocol_set_raw_handler(prot, app_mozen_raw_frame_handler);
// Init tunnel
mozen_tunnel_init(&s_tunnel_instance, prot, 240, NULL);
mozen_tunnel_set_callbacks(&s_tunnel_instance, tunnel_on_stream_start, tunnel_on_stream_data, tunnel_on_stream_finish);
}

View File

@@ -0,0 +1,48 @@
#ifndef __APP_MOZEN_HANDLER_H
#define __APP_MOZEN_HANDLER_H
#include <stdint.h>
#include "mozen_protocol.h"
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ͷ<EFBFBD><CDB6><EFBFBD>
enum
{
SENSOR_BIT_8 = 0x01,
SENSOR_BIT_16,
};
// ȫ<><C8AB>״̬<D7B4><CCAC><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ԭ<EFBFBD><D4AD> mozen_protocol.h <20>ж<EFBFBD><D0B6><EFBFBD><E5A3AC><EFBFBD><EFBFBD>Ӧ<EFBFBD>ò<EFBFBD><C3B2><EFBFBD><EFBFBD><EFBFBD>
extern volatile uint16_t g_sensor_frames_to_send;
extern volatile uint8_t g_sensor_data_type;
// --- Protocol Command IDs ---
#define MOZEN_CMD_ID_SENSOR_DATA 0x01 // --- <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> ---
#define MOZEN_CMD_ID_PARAM_CONFIG 0x02 // --- <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> ---
#define MOZEN_CMD_ID_PROD_TEST 0xAA // --- <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> ---
// --- Param Config Target IDs (Command 0x02) ---
#define MOZEN_PARAM_TGT_REPORT_MODE 0x01
#define MOZEN_PARAM_TGT_CALI_DISPLAY 0x3D
#define MOZEN_PARAM_TGT_DEVICE_INFO 0x3F
// --- Production Test Target IDs (Command 0xAA) ---
#define MOZEN_PROD_TGT_SYS 0x01
#define MOZEN_PROD_TGT_MAP_CALI 0x03
// Global protocol instance for application
extern mozen_protocol_t g_mozen_prot;
/**
* @brief Initialize the Mozen application handlers.
* This will register all necessary command handlers to the protocol instance.
* @param prot Protocol instance to bind to.
*/
void app_mozen_handler_init(mozen_protocol_t *prot);
/**
* @brief Top level initialization for Mozen protocol and application handlers.
* @param tx_fn The transmit callback function.
*/
void app_mozen_init(mozen_tx_fn_t tx_fn);
#endif // __APP_MOZEN_HANDLER_H

View File

@@ -0,0 +1,315 @@
#include "mozen_protocol.h"
#include <string.h>
#include "bsp_uart.h"
// --- Helper Functions ---
/*
* Calculate CRC16 for data
* @param data: Pointer to data
* @param length: Length of data
* @return: CRC16 value
* @note: This function is used for Modbus CRC16
*/
uint16_t mozen_protocol_crc16_modbus(const uint8_t *data, uint16_t length)
{
uint16_t crc = 0xFFFF;
for (uint16_t i = 0; i < length; i++)
{
crc ^= data[i];
for (uint8_t j = 0; j < 8; j++)
{
if (crc & 0x0001) crc = (crc >> 1) ^ 0xA001;
else crc >>= 1;
}
}
return crc;
}
/*
* Calculate checksum for data
* @param data: Pointer to data
* @param len: Length of data
* @return: Checksum value
* @note: This function is used for legacy checksum protocols
*/
uint16_t mozen_protocol_checksum(const uint8_t *data, uint32_t len)
{
uint16_t sum = 0;
for (uint32_t i = 0; i < len; ++i)
{
sum += data[i];
}
return sum;
}
// --- Internal Functions ---
/*
* Reset RX state
* @param prot: Pointer to mozen_protocol_t structure
* @note: This function is called automatically by mozen_protocol_process_rx
*/
static void mozen_protocol_reset_rx(mozen_protocol_t *prot)
{
prot->rx_state = MOZEN_RX_STATE_WAIT_SOF1; // Reset to wait for SOF1
prot->rx_index = 0; // Reset index
prot->expected_data_len = 0; // Reset expected data length
}
/*
* Dispatch received frame
* @param prot: Pointer to mozen_protocol_t structure
* @note: This function is called automatically by mozen_protocol_process_rx
* when a complete frame is received
*/
static void mozen_protocol_dispatch(mozen_protocol_t *prot)
{
if (prot->rx_index < sizeof(mozen_frame_header_t) + 2) return; // Too short
uint16_t received_crc; // Extract CRC from frame
mozen_frame_header_t header;
memcpy(&header, prot->rx_buffer, sizeof(mozen_frame_header_t));
if (header.sof != MOZEN_PROTOCOL_SOF) return;
uint16_t full_frame_len = header.frame_length;
uint32_t total_len = (uint32_t)full_frame_len + 2; // +2 for SOF and CRC
if (total_len > prot->rx_index || total_len > prot->rx_buffer_size) return;
uint16_t data_len = full_frame_len - sizeof(mozen_frame_header_t); // Exclude header
const uint8_t *data_ptr = prot->rx_buffer + sizeof(mozen_frame_header_t); // Point to data
memcpy(&received_crc, prot->rx_buffer + full_frame_len, sizeof(uint16_t)); // Extract CRC from frame
// 1. Raw frame handler check (e.g. for legacy checksum protocols)
if (prot->raw_frame_handler)
{
if (prot->raw_frame_handler(prot->user_context, prot->rx_buffer, total_len)) return; // Handled by custom raw handler
}
// 2. Standard CRC16 check
uint16_t calculated_crc = mozen_protocol_crc16_modbus(prot->rx_buffer, full_frame_len);
if (received_crc != calculated_crc) return; // CRC Error
// 3. Dispatch to registered handlers
for (uint8_t i = 0; i < prot->handler_count; i++)
{
if (prot->handlers[i].cmd_id == header.command_id)
{
prot->handlers[i].handler(prot->user_context, header.command_id, data_ptr, data_len);
return;
}
}
// 4. Default handler fallback
if (prot->default_handler)
{
prot->default_handler(prot->user_context, header.command_id, data_ptr, data_len);
}
}
// --- API Functions ---
/*
* Initialize mozen_protocol_t structure
* @param prot: Pointer to mozen_protocol_t structure
* @param rx_buf: Pointer to RX buffer
* @param rx_buf_size: Size of RX buffer
* @param tx_buf_main: Pointer to TX buffer
* @param tx_buf_irq: Pointer to IRQ TX buffer (optional)
* @param tx_buf_size: Size of TX buffer
* @param tx_fn: TX function
* @param user_context: User context for TX function
* @note: This function should be called once before using the protocol
*/
void mozen_protocol_init(mozen_protocol_t *prot,
uint8_t *rx_buf, uint16_t rx_buf_size,
uint8_t *tx_buf_main, uint8_t *tx_buf_irq, uint16_t tx_buf_size,
mozen_tx_fn_t tx_fn, void *user_context)
{
if (!prot) return;
memset(prot, 0, sizeof(mozen_protocol_t));
prot->rx_buffer = rx_buf;
prot->rx_buffer_size = rx_buf_size;
prot->tx_buffer_main = tx_buf_main;
prot->tx_buffer_irq = tx_buf_irq ? tx_buf_irq : tx_buf_main;
prot->tx_buffer_size = tx_buf_size;
prot->tx_fn = tx_fn;
prot->user_context = user_context;
mozen_protocol_reset_rx(prot);
}
/*
* Register a command handler
* @param prot: Pointer to mozen_protocol_t structure
* @param cmd_id: Command ID
* @param handler: Command handler function
* @return: 0 on success, -1 on failure
* @note: This function can be called multiple times to register multiple handlers
*/
int mozen_protocol_register_handler(mozen_protocol_t *prot, uint8_t cmd_id, mozen_cmd_handler_t handler)
{
if (!prot || !handler) return -1;
if (prot->handler_count >= MOZEN_MAX_HANDLERS) return -1;
prot->handlers[prot->handler_count].cmd_id = cmd_id;
prot->handlers[prot->handler_count].handler = handler;
prot->handler_count++;
return 0;
}
/*
* Process RX buffer
* @param prot: Pointer to mozen_protocol_t structure
* @param current_time_ms: Current time in milliseconds
* @note: This function should be called periodically to process incoming data
*/
void mozen_protocol_set_default_handler(mozen_protocol_t *prot, mozen_cmd_handler_t handler)
{
if (prot) prot->default_handler = handler;
}
/*
* Set raw frame handler
* @param prot: Pointer to mozen_protocol_t structure
* @param handler: Raw frame handler function
* @note: This function can be used to handle legacy checksum protocols or custom protocols
*/
void mozen_protocol_set_raw_handler(mozen_protocol_t *prot, mozen_raw_frame_handler_t handler)
{
if (prot) prot->raw_frame_handler = handler;
}
/*
* Feed a byte to the protocol
* @param prot: Pointer to mozen_protocol_t structure
* @param byte: Byte to feed
* @param current_time_ms: Current time in milliseconds
* @note: This function should be called whenever a new byte is received
*/
void mozen_protocol_feed_byte(mozen_protocol_t *prot, uint8_t byte, uint32_t current_time_ms)
{
if (!prot || !prot->rx_buffer) return;
prot->last_rx_time_ms = current_time_ms;
switch (prot->rx_state)
{
case MOZEN_RX_STATE_WAIT_SOF1:
if (byte == (MOZEN_PROTOCOL_SOF & 0xFF))
{
prot->rx_buffer[0] = byte;
prot->rx_index = 1;
prot->rx_state = MOZEN_RX_STATE_WAIT_SOF2;
}
break;
case MOZEN_RX_STATE_WAIT_SOF2:
if (byte == (MOZEN_PROTOCOL_SOF >> 8))
{
prot->rx_buffer[1] = byte;
prot->rx_index = 2;
prot->rx_state = MOZEN_RX_STATE_WAIT_HEADER;
}
else { mozen_protocol_reset_rx(prot);}
break;
case MOZEN_RX_STATE_WAIT_HEADER: // Wait for frame header
prot->rx_buffer[prot->rx_index++] = byte;
if (prot->rx_index >= sizeof(mozen_frame_header_t))
{
mozen_frame_header_t header;
memcpy(&header, prot->rx_buffer, sizeof(mozen_frame_header_t));
prot->expected_data_len = header.frame_length - sizeof(mozen_frame_header_t) + 2; // +2 for CRC
if ((sizeof(mozen_frame_header_t) + prot->expected_data_len) > prot->rx_buffer_size)
{
mozen_protocol_reset_rx(prot);
return;
}
prot->rx_state = MOZEN_RX_STATE_WAIT_DATA;
}
break;
case MOZEN_RX_STATE_WAIT_DATA:
prot->rx_buffer[prot->rx_index++] = byte;
if (prot->rx_index >= (sizeof(mozen_frame_header_t) + prot->expected_data_len))
{
mozen_protocol_dispatch(prot);
mozen_protocol_reset_rx(prot);
} else if (prot->rx_index >= prot->rx_buffer_size) {
// Should not happen due to header check, but safe guard against buffer overflow
mozen_protocol_reset_rx(prot);
}
break;
}
}
/*
* Reset the protocol state
* @param prot: Pointer to mozen_protocol_t structure
* current_time_ms: Current time in milliseconds
* timeout_ms: Timeout in milliseconds
* @note: This function should be called when a timeout occurs or when a frame is received
*/
void mozen_protocol_tick(mozen_protocol_t *prot, uint32_t current_time_ms, uint32_t timeout_ms)
{
if (!prot) return;
if (prot->rx_state != MOZEN_RX_STATE_WAIT_SOF1)
{
// Calculate diff taking into account potential integer overflow/wraparound
uint32_t time_diff = current_time_ms - prot->last_rx_time_ms;
if (time_diff > timeout_ms)
{
mozen_protocol_reset_rx(prot);
}
}
}
/*
* Send a frame
* @param prot: Pointer to mozen_protocol_t structure
* @param command_id: Command ID
* @param data: Data to send
* @param length: Length of data
* @param use_irq_buffer: Use IRQ buffer for sending
* @return: 0 on success, -1 on error
* @note: This function sends a frame using the provided data and command ID
*/
int mozen_protocol_send_frame(mozen_protocol_t *prot, uint8_t command_id, const uint8_t* data, uint16_t length, bool use_irq_buffer)
{
if (!prot || !prot->tx_fn) return -1;
uint16_t frame_length = sizeof(mozen_frame_header_t) + length; //Total length after removing CRC
uint16_t total_length = frame_length + 2; // Total length including CRC
if (total_length > prot->tx_buffer_size) return -1;
// During the transmission stage, double buffering is employed to prevent the data frames of the main loop from overlapping with the interrupt ACKs.
// The sensor data frames and ACKs may be sent concurrently in different contexts.
// The two static buffers can avoid overwriting and competition.
// The ACKs use an independent buffer to prevent interference with large packet transmission.
uint8_t *active_buffer = use_irq_buffer ? prot->tx_buffer_irq : prot->tx_buffer_main;
mozen_frame_header_t header;
header.sof = MOZEN_PROTOCOL_SOF;
header.command_id = command_id;
header.frame_length = frame_length;
memcpy(active_buffer, &header, sizeof(mozen_frame_header_t));
if (data && length > 0)
{
memcpy(active_buffer + sizeof(mozen_frame_header_t), data, length);
}
uint16_t crc = mozen_protocol_crc16_modbus(active_buffer, frame_length);
memcpy(active_buffer + frame_length, &crc, sizeof(uint16_t));
return prot->tx_fn(active_buffer, total_length);
}

View File

@@ -0,0 +1,120 @@
#ifndef __MOZEN_PROTOCOL_H
#define __MOZEN_PROTOCOL_H
#include <stdint.h>
#include <stdbool.h>
#define MOZEN_PROTOCOL_SOF 0x5AA5 // ֡ͷ
// --- Core Protocol Data Structures ---
#pragma pack(1)
/**
* @brief Represents the header of a Mozen protocol frame.
* The payload data follows this header directly in the buffer.
*/
typedef struct
{
uint16_t sof;
uint8_t command_id;
uint16_t frame_length;
} mozen_frame_header_t;
#pragma pack()
/**
* @brief Command handler callback type.
* @param context User context provided during registration.
* @param cmd_id The command ID of the received frame.
* @param data Pointer to the 'Data' field of the received frame.
* @param length The length of the 'Data' field.
*/
typedef void (*mozen_cmd_handler_t)(void *context, uint8_t cmd_id, const uint8_t *data, uint16_t length);
/**
* @brief Custom raw frame handler, used for legacy protocol compatibility or custom validation.
* @param context User context.
* @param raw_frame The complete raw frame data.
* @param length The length of the complete raw frame.
* @return true if the frame is handled and should not be processed further, false otherwise.
*/
typedef bool (*mozen_raw_frame_handler_t)(void *context, const uint8_t *raw_frame, uint16_t length);
/**
* @brief Transport TX callback type.
* @param frame Complete protocol frame buffer (header + payload + crc).
* @param len Frame length in bytes.
* @return 0 on success, negative value on failure.
*/
typedef int (*mozen_tx_fn_t)(const uint8_t *frame, uint16_t len);
// Max number of registered command handlers
#define MOZEN_MAX_HANDLERS 10
// RX State Machine States
typedef enum
{
MOZEN_RX_STATE_WAIT_SOF1,
MOZEN_RX_STATE_WAIT_SOF2,
MOZEN_RX_STATE_WAIT_HEADER,
MOZEN_RX_STATE_WAIT_DATA,
} mozen_rx_state_t;
/**
* @brief Mozen Protocol Instance Context
* Contains state and configurations for a single protocol instance.
*/
typedef struct
{
// Buffers and configuration
uint8_t *rx_buffer;
uint16_t rx_buffer_size;
uint8_t *tx_buffer_main; // Main TX buffer
uint8_t *tx_buffer_irq; // IRQ TX buffer
uint16_t tx_buffer_size;
// Callbacks
mozen_tx_fn_t tx_fn;
void *user_context;
// Command Handlers
struct
{
uint8_t cmd_id;
mozen_cmd_handler_t handler;
} handlers[MOZEN_MAX_HANDLERS];
uint8_t handler_count;
mozen_cmd_handler_t default_handler;
mozen_raw_frame_handler_t raw_frame_handler;
// RX State Machine
mozen_rx_state_t rx_state;
uint16_t rx_index;
uint16_t expected_data_len;
uint32_t last_rx_time_ms; // For timeout handling
} mozen_protocol_t;
// --- API Functions ---
void mozen_protocol_init(mozen_protocol_t *prot,
uint8_t *rx_buf, uint16_t rx_buf_size,
uint8_t *tx_buf_main, uint8_t *tx_buf_irq, uint16_t tx_buf_size,
mozen_tx_fn_t tx_fn, void *user_context);
int mozen_protocol_register_handler(mozen_protocol_t *prot, uint8_t cmd_id, mozen_cmd_handler_t handler);
void mozen_protocol_set_default_handler(mozen_protocol_t *prot, mozen_cmd_handler_t handler);
void mozen_protocol_set_raw_handler(mozen_protocol_t *prot, mozen_raw_frame_handler_t handler);
void mozen_protocol_feed_byte(mozen_protocol_t *prot, uint8_t byte, uint32_t current_time_ms);
void mozen_protocol_tick(mozen_protocol_t *prot, uint32_t current_time_ms, uint32_t timeout_ms);
int mozen_protocol_send_frame(mozen_protocol_t *prot, uint8_t command_id, const uint8_t* data, uint16_t length, bool use_irq_buffer);
uint16_t mozen_protocol_crc16_modbus(const uint8_t *data, uint16_t length);
uint16_t mozen_protocol_checksum(const uint8_t *data, uint32_t len);
#endif // __MOZEN_PROTOCOL_H

View File

@@ -0,0 +1,197 @@
#include "mozen_tunnel.h"
#include <string.h>
// <20>ְ<EFBFBD>
/*
* Initialize tunnel
* tunnel: tunnel context
* prot: protocol context
* mtu: maximum transmission unit
* user_context: user context
* returns: none
*/
void mozen_tunnel_init(mozen_tunnel_t *tunnel, mozen_protocol_t *prot, uint16_t mtu, void *user_context)
{
if (!tunnel) return;
memset(tunnel, 0, sizeof(mozen_tunnel_t));
tunnel->prot = prot;
tunnel->mtu = mtu;
tunnel->user_context = user_context;
}
/*
* Send a response to the tunnel command
* tunnel: tunnel context
* target_cmd: command id
* is_ok: true if the command was successful, false otherwise
* returns: none
*/
void mozen_tunnel_set_callbacks(mozen_tunnel_t *tunnel,
mozen_tunnel_on_stream_start_t on_start,
mozen_tunnel_on_stream_data_t on_data,
mozen_tunnel_on_stream_finish_t on_finish)
{
if (!tunnel) return;
tunnel->on_start = on_start;
tunnel->on_data = on_data;
tunnel->on_finish = on_finish;
}
/*
* Send a response to the tunnel command
* tunnel: tunnel context
* target_cmd: command id
* is_ok: true if the command was successful, false otherwise
* returns: none
*/
static void mozen_tunnel_send_response(mozen_tunnel_t *tunnel, uint8_t target_cmd, bool is_ok)
{
if (!tunnel || !tunnel->prot) return;
uint8_t res_buf[sizeof(mozen_tunnel_header_t) + 2];
mozen_tunnel_header_t *res_header = (mozen_tunnel_header_t *)res_buf;
res_header->tunnel_type = TUNNEL_TYPE_DATA_TRANSFER;
res_header->target_cmd = target_cmd;
res_buf[sizeof(mozen_tunnel_header_t)] = is_ok ? 'o' : 'N';
res_buf[sizeof(mozen_tunnel_header_t) + 1] = is_ok ? 'k' : 'G';
// tunnel messages go through IRQ buffer context to avoid disrupting main buffer?
// The previous implementation used the default send context.
mozen_protocol_send_frame(tunnel->prot, MOZEN_TUNNEL_COMMAND_ID, res_buf, sizeof(res_buf), true);
}
/*
* Handle a received tunnel frame
* tunnel: tunnel context
* data: received data
* length: received data length
* returns: none
*/
static void mozen_tunnel_abort(mozen_tunnel_t *tunnel)
{
if (tunnel->is_active)
{
if (tunnel->on_finish)
{
tunnel->on_finish(tunnel->user_context, false);
}
tunnel->is_active = false;
}
}
/*
* Handle a received tunnel frame
* tunnel: tunnel context
* data: received data
* length: received data length
* returns: none
*/
void mozen_tunnel_handle_command(mozen_tunnel_t *tunnel, const uint8_t *data, uint16_t length)
{
if (!tunnel || length < sizeof(mozen_tunnel_header_t)) return;
const mozen_tunnel_header_t *tunnel_header = (const mozen_tunnel_header_t *)data;
const uint8_t *payload = data + sizeof(mozen_tunnel_header_t);
uint16_t payload_len = length - sizeof(mozen_tunnel_header_t);
switch (tunnel_header->tunnel_type)
{
case TUNNEL_TYPE_HANDSHAKE: // handshake request
{
if (payload_len < sizeof(uint32_t) + sizeof(uint16_t)) return;
mozen_tunnel_abort(tunnel); // Abort any ongoing stream
tunnel->is_active = true;
tunnel->target_cmd = tunnel_header->target_cmd;
memcpy(&tunnel->total_data_len, payload, sizeof(uint32_t));
memcpy(&tunnel->total_packets, payload + sizeof(uint32_t), sizeof(uint16_t));
tunnel->next_packet_index = 1;
tunnel->received_data_len = 0;
// Prepare and send handshake response
uint8_t res_buf[sizeof(mozen_tunnel_handshake_res_t)];
mozen_tunnel_handshake_res_t *res = (mozen_tunnel_handshake_res_t *)res_buf;
res->header.tunnel_type = TUNNEL_TYPE_RESPONSE;
res->header.target_cmd = tunnel_header->target_cmd;
res->status = 0x6B6F; // 'ok' (little-endian: 'o' is 0x6F, 'k' is 0x6B)
res->mtu = tunnel->mtu;
if (tunnel->prot)
{
mozen_protocol_send_frame(tunnel->prot, MOZEN_TUNNEL_COMMAND_ID, (uint8_t *)res, sizeof(mozen_tunnel_handshake_res_t), true);
}
break;
}
case TUNNEL_TYPE_DATA_TRANSFER: // data transfer
{
if (!tunnel->is_active || tunnel_header->target_cmd != tunnel->target_cmd) return;
if (payload_len < sizeof(uint16_t)) return;
uint16_t packet_index;
memcpy(&packet_index, payload, sizeof(uint16_t));
if (packet_index != tunnel->next_packet_index)
{
mozen_tunnel_send_response(tunnel, tunnel->target_cmd, false);
mozen_tunnel_abort(tunnel);
return;
}
const uint8_t *chunk_data = payload + sizeof(uint16_t);
uint16_t chunk_len = payload_len - sizeof(uint16_t);
bool accepted = true;
if (packet_index == 1)
{
if (tunnel->on_start)
{
accepted = tunnel->on_start(tunnel->user_context, tunnel->target_cmd, chunk_data, chunk_len);
}
}
else
{
if (tunnel->on_data)
{
accepted = tunnel->on_data(tunnel->user_context, chunk_data, chunk_len);
}
}
if (!accepted)
{
mozen_tunnel_send_response(tunnel, tunnel->target_cmd, false);
mozen_tunnel_abort(tunnel);
return;
}
tunnel->received_data_len += chunk_len;
tunnel->next_packet_index++;
mozen_tunnel_send_response(tunnel, tunnel->target_cmd, true);
if (tunnel->next_packet_index > tunnel->total_packets)
{
if (tunnel->received_data_len == tunnel->total_data_len)
{
if (tunnel->on_finish)
{
tunnel->on_finish(tunnel->user_context, true);
}
}
else
{
if (tunnel->on_finish)
{
tunnel->on_finish(tunnel->user_context, false);
}
}
tunnel->is_active = false;
}
break;
}
}
}

View File

@@ -0,0 +1,113 @@
#ifndef __MOZEN_TUNNEL_H
#define __MOZEN_TUNNEL_H
#include <stdint.h>
#include <stdbool.h>
#include "mozen_protocol.h"
// --- Tunnel Protocol Definitions ---
#define MOZEN_TUNNEL_COMMAND_ID 0xFB // Tunnel command ID
typedef enum
{
TUNNEL_TYPE_HANDSHAKE = 0x01, // Handshake request
TUNNEL_TYPE_DATA_TRANSFER = 0x02, // Data transfer
TUNNEL_TYPE_RESPONSE = 0xFF, // Special type for responses
} mozen_tunnel_type_t;
#pragma pack(1)
// <20><><EFBFBD><EFBFBD>Э<EFBFBD><D0AD>ͷ<EFBFBD><CDB7><EFBFBD><EFBFBD>
typedef struct {
uint8_t tunnel_type;
uint8_t target_cmd;
} mozen_tunnel_header_t;
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><E1B9B9>
typedef struct {
mozen_tunnel_header_t header;
uint32_t total_data_length;
uint16_t total_packets;
} mozen_tunnel_handshake_req_t;
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ݰ<EFBFBD><DDB0><EFBFBD><E1B9B9>
typedef struct {
mozen_tunnel_header_t header;
uint16_t status; // 'ok' or 'NG'
uint16_t mtu;
} mozen_tunnel_handshake_res_t;
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ݴ<EFBFBD><DDB4><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><E1B9B9>
typedef struct {
mozen_tunnel_header_t header;
uint16_t packet_index;
uint8_t data_chunk[]; // Flexible array member for data
} mozen_tunnel_data_t;
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ӧ<EFBFBD><EFBFBD><E1B9B9>
typedef struct {
mozen_tunnel_header_t header;
uint16_t status; // 'ok' or 'NG'
} mozen_tunnel_response_t;
#pragma pack()
// --- Tunnel Context and Callbacks ---
/**
* @brief Callback for starting a stream write.
* @param context User context.
* @param target_cmd The command indicating the stream target.
* @param first_chunk_data The first chunk of data, useful for analyzing sub-headers.
* @param chunk_len Length of the first chunk.
* @return True if streaming can begin, false to reject.
*/
typedef bool (*mozen_tunnel_on_stream_start_t)(void *context, uint8_t target_cmd, const uint8_t *first_chunk_data, uint16_t chunk_len);
/**
* @brief Callback for continuing a stream write.
* @param context User context.
* @param data Chunk data.
* @param len Chunk length.
* @return True if chunk accepted, false to abort.
*/
typedef bool (*mozen_tunnel_on_stream_data_t)(void *context, const uint8_t *data, uint16_t len);
/**
* @brief Callback for finishing a stream write.
* @param context User context.
* @param success True if the transfer completed successfully, false if aborted.
*/
typedef void (*mozen_tunnel_on_stream_finish_t)(void *context, bool success);
typedef struct
{
bool is_active;
uint8_t target_cmd;
uint32_t total_data_len;
uint16_t total_packets;
uint16_t next_packet_index;
uint32_t received_data_len;
// Config
uint16_t mtu; // Max packet size
mozen_protocol_t *prot; // For sending ACKs
void *user_context; // For callbacks
// Callbacks
mozen_tunnel_on_stream_start_t on_start;
mozen_tunnel_on_stream_data_t on_data;
mozen_tunnel_on_stream_finish_t on_finish;
} mozen_tunnel_t;
// --- API ---
void mozen_tunnel_init(mozen_tunnel_t *tunnel, mozen_protocol_t *prot, uint16_t mtu, void *user_context);
void mozen_tunnel_set_callbacks(mozen_tunnel_t *tunnel,
mozen_tunnel_on_stream_start_t on_start,
mozen_tunnel_on_stream_data_t on_data,
mozen_tunnel_on_stream_finish_t on_finish);
void mozen_tunnel_handle_command(mozen_tunnel_t *tunnel, const uint8_t *data, uint16_t length);
#endif // __MOZEN_TUNNEL_H