From 78db590c4cb0f48645a8270ae03009de1bf64539 Mon Sep 17 00:00:00 2001 From: =?utf8?q?=E5=AD=99=E6=98=A5=E9=98=B3?= Date: Tue, 30 Sep 2025 15:23:38 +0800 Subject: [PATCH] =?utf8?q?pppc=E6=B5=8B=E8=AF=95=E7=9B=B8=E5=85=B3?= =?utf8?q?=E6=9B=B4=E6=96=B0=EF=BC=8C=E7=9B=AE=E5=89=8D=E4=B8=BB=E8=A6=81?= =?utf8?q?=E9=97=AE=E9=A2=98=E5=9C=A8=E4=B8=B2=E5=8F=A3=E6=95=B0=E6=8D=AE?= =?utf8?q?=E6=8E=A5=E6=94=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- .../src/include/lwip/lwipopts_default.h | 32 +- .../src/include/netif/ppp/pppdebug.h | 2 +- .../zhihui/ai_stub/src/ppp_stub_functions.c | 54 -- code/sdk/zhihui/ppp/CMakeLists.txt | 23 +- code/sdk/zhihui/ppp/example/ppp_at_cmd.c | 2 +- code/sdk/zhihui/ppp/include/ppp_config.h | 21 +- code/sdk/zhihui/ppp/include/ppp_interrupt.h | 93 ++++ code/sdk/zhihui/ppp/include/ppp_lwip.h | 17 + code/sdk/zhihui/ppp/include/ppp_serial.h | 36 +- .../ppp/ppp_dec_test/performance_test.c | 318 +++++++++++ .../zhihui/ppp/ppp_dec_test/ppp_analyzer.py | 514 +++++++++++++++++ .../zhihui/ppp/ppp_dec_test/ppp_file_reader.c | 330 +++++++++++ .../ppp/ppp_dec_test/ppp_packet_analyzer.c | 519 ++++++++++++++++++ .../ppp/ppp_dec_test/ppp_packet_analyzer.h | 236 ++++++++ code/sdk/zhihui/ppp/ppp_dec_test/ppp_parser.c | 383 +++++++++++++ code/sdk/zhihui/ppp/ppp_dec_test/ppp_parser.h | 158 ++++++ .../ppp_dec_test/ppp_parser_configurable.h | 158 ++++++ .../ppp/ppp_dec_test/ppp_parser_dynamic.c | 272 +++++++++ .../ppp/ppp_dec_test/ppp_parser_dynamic.h | 125 +++++ .../ppp/ppp_dec_test/ppp_parser_streaming.h | 164 ++++++ .../ppp/ppp_dec_test/ppp_stream_parser.c | 397 ++++++++++++++ .../ppp/ppp_dec_test/ppp_stream_parser.h | 206 +++++++ .../zhihui/ppp/ppp_dec_test/ppp_stream_test | Bin 0 -> 132656 bytes .../zhihui/ppp/ppp_dec_test/ppp_stream_test.c | 376 +++++++++++++ code/sdk/zhihui/ppp/src/ppp_api.c | 47 +- code/sdk/zhihui/ppp/src/ppp_interrupt.c | 257 +++++++++ code/sdk/zhihui/ppp/src/ppp_lwip.c | 104 +++- code/sdk/zhihui/ppp/src/ppp_lwip_thread.c | 504 +++++++++-------- code/sdk/zhihui/ppp/src/ppp_serial.c | 146 +++-- .../zhihui/tcptest/include/zhihui_tcp_test.h | 2 +- code/sdk/zhihui/tcptest/src/zhihui_tcp_test.c | 32 +- 31 files changed, 5128 insertions(+), 400 deletions(-) create mode 100644 code/sdk/zhihui/ppp/include/ppp_interrupt.h create mode 100644 code/sdk/zhihui/ppp/ppp_dec_test/performance_test.c create mode 100644 code/sdk/zhihui/ppp/ppp_dec_test/ppp_analyzer.py create mode 100644 code/sdk/zhihui/ppp/ppp_dec_test/ppp_file_reader.c create mode 100644 code/sdk/zhihui/ppp/ppp_dec_test/ppp_packet_analyzer.c create mode 100644 code/sdk/zhihui/ppp/ppp_dec_test/ppp_packet_analyzer.h create mode 100644 code/sdk/zhihui/ppp/ppp_dec_test/ppp_parser.c create mode 100644 code/sdk/zhihui/ppp/ppp_dec_test/ppp_parser.h create mode 100644 code/sdk/zhihui/ppp/ppp_dec_test/ppp_parser_configurable.h create mode 100644 code/sdk/zhihui/ppp/ppp_dec_test/ppp_parser_dynamic.c create mode 100644 code/sdk/zhihui/ppp/ppp_dec_test/ppp_parser_dynamic.h create mode 100644 code/sdk/zhihui/ppp/ppp_dec_test/ppp_parser_streaming.h create mode 100644 code/sdk/zhihui/ppp/ppp_dec_test/ppp_stream_parser.c create mode 100644 code/sdk/zhihui/ppp/ppp_dec_test/ppp_stream_parser.h create mode 100644 code/sdk/zhihui/ppp/ppp_dec_test/ppp_stream_test create mode 100644 code/sdk/zhihui/ppp/ppp_dec_test/ppp_stream_test.c create mode 100644 code/sdk/zhihui/ppp/src/ppp_interrupt.c diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_adapter/liteos/src/include/lwip/lwipopts_default.h b/code/sdk/open_source/openharmony/third_party/lwip/lwip_adapter/liteos/src/include/lwip/lwipopts_default.h index f48813ef8..0ec4bb52f 100644 --- a/code/sdk/open_source/openharmony/third_party/lwip/lwip_adapter/liteos/src/include/lwip/lwipopts_default.h +++ b/code/sdk/open_source/openharmony/third_party/lwip/lwip_adapter/liteos/src/include/lwip/lwipopts_default.h @@ -55,9 +55,16 @@ extern "C" { #ifndef LWIP_DISABLE_TCP_SANITY_CHECKS #define LWIP_DISABLE_TCP_SANITY_CHECKS 1 #endif -#define LWIP_DEBUG 0 -#define LWIP_DBG_TYPES_ON LWIP_DBG_OFF -#define SOCKETS_DEBUG LWIP_DBG_OFF + +#if 0 +#define LWIP_DEBUG LWIP_DBG_ON +#define LWIP_DBG_TYPES_ON LWIP_DBG_ON +#define SOCKETS_DEBUG LWIP_DBG_ON +#define TCP_DEBUG LWIP_DBG_ON +#define TCP_INPUT_DEBUG LWIP_DBG_ON +#define TCP_OUTPUT_DEBUG LWIP_DBG_ON +#define TCP_INPKT_DEBUG LWIP_DBG_ON +#endif #ifndef LWIP_SLIP #define LWIP_SLIP 0 @@ -136,6 +143,17 @@ extern "C" { #define PPP_INPROC_IRQ_SAFE 0 #endif +/* PPP MRU配置控制 - 用于防止发送分片 */ +#ifndef PPP_ENABLE_LARGE_MRU +#define PPP_ENABLE_LARGE_MRU 0 // 0=禁用1500字节MRU(防止分片), 1=启用1500字节MRU +#endif + +#if PPP_ENABLE_LARGE_MRU +#define PPP_MRU_SIZE 1500 // PPP MRU大小 - 启用大MRU +#else +#define PPP_MRU_SIZE 1500 // PPP MRU大小 - 限制为128字节防止分片 +#endif + #elif defined(SLIP_MODULE_ENABLED) && !defined(PPP_MODULE_ENABLED) /* SLIP模式配置 */ #ifndef LWIP_SLIP @@ -1342,11 +1360,11 @@ extern "C" { * debug messages of certain types. */ #ifndef LWIP_DBG_TYPES_ON -#define LWIP_DBG_TYPES_ON LWIP_DBG_OFF +#define LWIP_DBG_TYPES_ON LWIP_DBG_ON #endif #ifndef IP_DEBUG -#define IP_DEBUG LWIP_DBG_OFF +#define IP_DEBUG LWIP_DBG_ON #endif #ifndef ETHARP_DEBUG @@ -1354,7 +1372,7 @@ extern "C" { #endif #ifndef NETIF_DEBUG -#define NETIF_DEBUG LWIP_DBG_OFF +#define NETIF_DEBUG LWIP_DBG_ON #endif #ifndef L3_EVENT_MSG_DEBUG @@ -2122,7 +2140,7 @@ upto 255 interfaces . New versions donot have these restrictions but we stick to * messages are written. */ #ifndef LWIP_DBG_MIN_LEVEL -#define LWIP_DBG_MIN_LEVEL LWIP_DBG_LEVEL_OFF +#define LWIP_DBG_MIN_LEVEL LWIP_DBG_LEVEL_ALL #endif #ifndef LWIP_SOCKET_POLL diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/include/netif/ppp/pppdebug.h b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/include/netif/ppp/pppdebug.h index 36ee4f9bf..1a1c6bd56 100644 --- a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/include/netif/ppp/pppdebug.h +++ b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/include/netif/ppp/pppdebug.h @@ -49,7 +49,7 @@ extern "C" { #define LOG_ERR (PPP_DEBUG | LWIP_DBG_LEVEL_SEVERE) #define LOG_NOTICE (PPP_DEBUG | LWIP_DBG_LEVEL_WARNING) #define LOG_WARNING (PPP_DEBUG | LWIP_DBG_LEVEL_WARNING) -#define LOG_INFO (PPP_DEBUG) +#define LOG_INFO (PPP_DEBUG | LWIP_DBG_LEVEL_WARNING) #define LOG_DETAIL (PPP_DEBUG) #define LOG_DEBUG (PPP_DEBUG) diff --git a/code/sdk/zhihui/ai_stub/src/ppp_stub_functions.c b/code/sdk/zhihui/ai_stub/src/ppp_stub_functions.c index cc4caa5be..4ac37f2b3 100644 --- a/code/sdk/zhihui/ai_stub/src/ppp_stub_functions.c +++ b/code/sdk/zhihui/ai_stub/src/ppp_stub_functions.c @@ -28,63 +28,9 @@ int httpc_get_file_dns_to_disk(const char *url, const char *file_path, int timeo return -1; } #endif -/** - * @brief AI流式录音发送桩函数 - * @param data 音频数据 - * @param len 数据长度 - * @param session_id 会话ID - * @return 总是返回失败(-1) - */ -int streamingRecordAndSend_AI(void *data, int len, const char *session_id) -{ - (void)data; - (void)len; - (void)session_id; - - // 桩函数 - 不实现实际功能 - return -1; -} -/** - * @brief AI消息队列发送桩函数 - * @param msg_data 消息数据 - * @param data_len 数据长度 - * @param type 消息类型 - * @return 总是返回失败(-1) - */ -int send_msg_data_into_Ai_msg_queue(void *msg_data, uint16_t data_len, uint16_t type) -{ - (void)msg_data; - (void)data_len; - (void)type; - - // 桩函数 - 不实现实际功能 - return -1; -} -/** - * @brief 蓝牙设置AI回答字符串桩函数 - * @param answer_str AI回答字符串 - * @param type 消息类型 - */ -void ble_set_ai_Answer_str(char *answer_str, uint8_t type) -{ - (void)answer_str; - (void)type; - - // 桩函数 - 不实现实际功能 -} -/** - * @brief 蓝牙设置AI问题字符串桩函数 - * @param question_str AI问题字符串 - */ -void ble_set_ai_Question_str(char *question_str) -{ - (void)question_str; - - // 桩函数 - 不实现实际功能 -} #ifdef SUPPORT_CAT1_SERVICE diff --git a/code/sdk/zhihui/ppp/CMakeLists.txt b/code/sdk/zhihui/ppp/CMakeLists.txt index bd03b9385..80950319c 100644 --- a/code/sdk/zhihui/ppp/CMakeLists.txt +++ b/code/sdk/zhihui/ppp/CMakeLists.txt @@ -13,15 +13,16 @@ set(PPP_AT_CMD_SOURCE ) # 源文件列表 set(SOURCES - src/ppp_api.c - src/ppp_at_handler.c - src/ppp_serial.c - src/ppp_lwip.c - src/ppp_lwip_thread.c - src/ppp_debug.c - src/ppp_debug_manager.c - src/ppp_power_ctl.c - src/ppp_cat1_api.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/ppp_api.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/ppp_at_handler.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/ppp_serial.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/ppp_lwip.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/ppp_lwip_thread.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/ppp_debug.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/ppp_debug_manager.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/ppp_power_ctl.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/ppp_cat1_api.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/ppp_interrupt.c ${PPP_AT_CMD_SOURCE} ) @@ -62,7 +63,7 @@ set(PUBLIC_DEFINES PPP_USE_PBUF_RAM=1 PPP_FCS_TABLE=1 PRINTPKT_SUPPORT=1 - PPP_DEBUG=LWIP_DBG_ON + PPP_DEBUG=0x80U PPP_INPROC_IRQ_SAFE=0 # 添加IPCP相关配置 IPCP_SUPPORT=1 @@ -70,7 +71,7 @@ set(PUBLIC_DEFINES LWIP_DNS=1 DNS_TABLE_SIZE=2 # 启用IPCP调试 - IPCP_DEBUG=LWIP_DBG_ON + IPCP_DEBUG=0x80U # 确保IPCP功能完全启用 PPP_IPV4_SUPPORT=1 # 启用LCP相关功能 diff --git a/code/sdk/zhihui/ppp/example/ppp_at_cmd.c b/code/sdk/zhihui/ppp/example/ppp_at_cmd.c index 8442a88fd..e8aff5d63 100644 --- a/code/sdk/zhihui/ppp/example/ppp_at_cmd.c +++ b/code/sdk/zhihui/ppp/example/ppp_at_cmd.c @@ -1796,7 +1796,7 @@ static errcode_t at_ppp_tcp_performance_test(uint8_t *para, uint32_t paraLen) config.test_duration = (uint32_t)test_time; config.connection_count = (uint16_t)connections; config.verbose = true; // AT命令模式不使用详细输出 - config.send_interval_ms = 100; // 100ms间隔,适合AT命令模式 + config.send_interval_ms = 500; // 500ms间隔,给其他进程更多CPU时间 config.data_min_size = 1024; // 较小的数据包 config.data_max_size = 4096; // 适中的最大包 config.recv_timeout_ms = (uint32_t)recv_timeout_ms; // 可配置的接收超时 diff --git a/code/sdk/zhihui/ppp/include/ppp_config.h b/code/sdk/zhihui/ppp/include/ppp_config.h index d4c15cf66..1bad8038a 100644 --- a/code/sdk/zhihui/ppp/include/ppp_config.h +++ b/code/sdk/zhihui/ppp/include/ppp_config.h @@ -49,6 +49,23 @@ extern "C" { #define PPP_DIAL_RETRY_COUNT 3 // 拨号重试次数 #define PPP_KEEPALIVE_INTERVAL 30 // 保活间隔(秒) +/* PPP接收模式配置 */ +#ifndef PPP_USE_INTERRUPT_MODE +#define PPP_USE_INTERRUPT_MODE 1 // 1=中断模式(UART中断接收), 0=轮询模式(线程轮询) +#endif + +/* PPP线程模式配置(仅轮询模式有效) */ +#ifndef PPP_USE_DUAL_THREAD +#define PPP_USE_DUAL_THREAD 1 // 1=双线程模式(接收+处理), 0=单线程模式(接收直接处理) +#endif + +/* PPP性能优化配置 - 解决接收阻塞问题 */ +#define PPP_LWIP_PBUF_POOL_SIZE 32 // 增加pbuf池大小,默认16太小 +#define PPP_LWIP_TCPIP_MSG_INPKT 64 // 增加输入消息数量,默认32太小 +#define PPP_LWIP_TCPIP_MBOX_SIZE 128 // 增加TCPIP邮箱大小,默认64太小 +#define PPP_LWIP_TCP_RECVMBOX_SIZE 256 // 增加TCP接收邮箱大小,默认128太小 +#define PPP_LWIP_TCP_WND_SIZE 65536 // 增加TCP接收窗口,默认32KB太小 + /* 缓冲区大小配置 */ #define PPP_RX_BUFFER_SIZE 2048 // 接收缓冲区大小 #define PPP_TX_BUFFER_SIZE 2048 // 发送缓冲区大小 @@ -57,10 +74,6 @@ extern "C" { #define PPP_RX_THREAD_STACK_SIZE 4096 // 接收线程栈大小 #define PPP_RX_THREAD_PRIORITY osPriorityNormal // 接收线程优先级 -/* PPP网络配置 */ -#define PPP_MTU_SIZE 1500 // PPP MTU大小 -#define PPP_MRU_SIZE 1500 // PPP MRU大小 - /* 调试配置 */ #define PPP_DEBUG_ENABLE 1 // 启用调试输出 #define PPP_DEBUG_LEVEL PPP_DEBUG_LEVEL_INFO // 默认只显示警告和错误 (1=ERROR, 2=WARN, 3=INFO, 4=DEBUG) diff --git a/code/sdk/zhihui/ppp/include/ppp_interrupt.h b/code/sdk/zhihui/ppp/include/ppp_interrupt.h new file mode 100644 index 000000000..19bdd8f6b --- /dev/null +++ b/code/sdk/zhihui/ppp/include/ppp_interrupt.h @@ -0,0 +1,93 @@ +/** + * @file ppp_interrupt.h + * @brief PPP UART中断接收处理头文件 + */ + +#ifndef PPP_INTERRUPT_H +#define PPP_INTERRUPT_H + +#include "ppp_types.h" +#include "uart.h" +#include "pinctrl.h" +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief PPP中断统计信息 + */ +typedef struct { + uint32_t total_interrupts; // 总中断次数 + uint32_t success_count; // 成功处理次数 + uint32_t error_count; // 错误次数 + uint32_t lwip_error_count; // lwIP错误次数 + + uint32_t total_bytes_received; // 总接收字节数 + uint32_t total_bytes_sent_to_lwip; // 发送到lwIP的字节数 + + uint32_t dropped_not_initialized; // 因PPP未初始化丢弃的字节数 + uint32_t dropped_lwip_error; // 因lwIP错误丢弃的字节数 +} ppp_interrupt_stats_t; + +/** + * @brief UART接收中断回调函数(由UART驱动调用) + * @param buffer 接收数据缓冲区 + * @param length 数据长度 + * @param error 是否有错误 + */ +void ppp_uart_rx_interrupt_handler(const void *buffer, uint16_t length, bool error); + +/** + * @brief 获取UART硬件缓冲区配置(供外部UART初始化使用) + * @param rx_buffer 返回接收缓冲区指针 + * @param buffer_size 返回缓冲区大小 + */ +void ppp_interrupt_get_buffer_config(void **rx_buffer, uint32_t *buffer_size); + +/** + * @brief 启用PPP UART中断接收 + * @return PPP_ERR_OK 成功,其他值失败 + * + * @note 调用此函数前: + * 1. ppp_serial必须已初始化(UART总线号从ppp_serial_get_uart_bus()获取) + * 2. UART必须已经初始化并配置了硬件缓冲区 + * 3. 使用 ppp_interrupt_get_buffer_config() 获取缓冲区配置 + */ +ppp_error_t ppp_interrupt_enable(void); + +/** + * @brief 禁用PPP UART中断接收 + * @return PPP_ERR_OK 成功,其他值失败 + */ +ppp_error_t ppp_interrupt_disable(void); + +/** + * @brief 检查中断是否已启用 + * @return true 已启用,false 未启用 + */ +bool ppp_interrupt_is_enabled(void); + +/** + * @brief 获取中断统计信息 + * @param stats 统计信息输出缓冲区 + */ +void ppp_interrupt_get_stats(ppp_interrupt_stats_t *stats); + +/** + * @brief 重置中断统计信息 + */ +void ppp_interrupt_reset_stats(void); + +/** + * @brief 打印中断统计信息 + */ +void ppp_interrupt_print_stats(void); + +#ifdef __cplusplus +} +#endif + +#endif /* PPP_INTERRUPT_H */ \ No newline at end of file diff --git a/code/sdk/zhihui/ppp/include/ppp_lwip.h b/code/sdk/zhihui/ppp/include/ppp_lwip.h index 8c91bd8b7..40acb8ec5 100644 --- a/code/sdk/zhihui/ppp/include/ppp_lwip.h +++ b/code/sdk/zhihui/ppp/include/ppp_lwip.h @@ -159,6 +159,23 @@ ppp_error_t ppp_lwip_trigger_keepalive(void); */ ppp_error_t ppp_lwip_get_output_stats(ppp_lwip_output_stats_t *stats); +// /** +// * @brief 获取PPP控制块(供中断处理模块使用) +// * @return PPP控制块指针,如果未初始化则返回NULL +// */ +// ppp_pcb* ppp_lwip_get_pcb(void); + +/** + * @brief 检查PPP lwIP是否已初始化(供中断处理模块使用) + * @return true 已初始化,false 未初始化 + */ +bool ppp_lwip_is_initialized(void); + +/** + * @brief 打印lwIP内存使用情况(用于诊断) + */ +void ppp_lwip_print_memory_stats(void); + #ifdef __cplusplus } #endif diff --git a/code/sdk/zhihui/ppp/include/ppp_serial.h b/code/sdk/zhihui/ppp/include/ppp_serial.h index 876339622..2d7f2b538 100644 --- a/code/sdk/zhihui/ppp/include/ppp_serial.h +++ b/code/sdk/zhihui/ppp/include/ppp_serial.h @@ -99,6 +99,15 @@ ppp_error_t ppp_serial_write(const uint8_t *data, uint32_t length); */ ppp_error_t ppp_serial_read(uint8_t *buffer, uint32_t buffer_size, uint32_t *bytes_read); +/** + * @brief PPP快速尝试读取 - 类似SLIP的sio_tryread,使用1ms超时 + * @param buffer 接收缓冲区 + * @param buffer_size 缓冲区大小 + * @param bytes_read 实际读取字节数 + * @return 错误码 + */ +ppp_error_t ppp_serial_tryread(uint8_t *buffer, uint32_t buffer_size, uint32_t *bytes_read); + /** * @brief 等待数据接收 * @param timeout_ms 超时时间(毫秒) @@ -150,8 +159,33 @@ ppp_error_t ppp_serial_set_rx_callback(ppp_serial_rx_callback_t callback, void * /** * @brief 获取串口统计信息 + * @param stats 统计信息结构指针 + * @return 错误码 + */ +ppp_error_t ppp_serial_get_stats(ppp_serial_stats_t *stats); + +/** + * @brief 获取UART总线号 + * @return UART总线号,如果未初始化返回UART_BUS_0 + */ +uint8_t ppp_serial_get_uart_bus(void); + +/** + * @brief PPP中断接收回调函数 - 用于高效数据接收 + * @param buffer 接收到的数据缓冲区 + * @param length 数据长度 + * @param error 是否有错误 + * @note 此函数应该在UART中断回调中调用,类似SLIP的uart_read_data_int_handler + */ +void ppp_uart_rx_interrupt_handler(const void *buffer, uint16_t length, bool error); + +/** + * @brief 获取中断接收队列统计信息 + * @param queue_count 队列中的数据块数量 + * @param bytes_queued 队列中的字节数 + * @param chunks_dropped 丢弃的数据块数量 */ -uint32_t ppp_serial_get_stats(ppp_serial_stats_t *stats); +void ppp_get_interrupt_rx_stats(uint32_t *queue_count, uint32_t *bytes_queued, uint32_t *chunks_dropped); /** * @brief 重置串口状态 diff --git a/code/sdk/zhihui/ppp/ppp_dec_test/performance_test.c b/code/sdk/zhihui/ppp/ppp_dec_test/performance_test.c new file mode 100644 index 000000000..64972aa8e --- /dev/null +++ b/code/sdk/zhihui/ppp/ppp_dec_test/performance_test.c @@ -0,0 +1,318 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "ppp_parser.h" +#include "ppp_packet_analyzer.h" +#include "ppp_stream_parser.h" + +/* 配置常量 */ +#define MAX_FILE_SIZE (1024 * 1024) /* 最大文件大小1MB */ +#define MAX_FRAMES 10000 /* 最大帧数量 */ +#define CHUNK_SIZE 256 /* 流式处理块大小 */ + +/* 全局变量 */ +static uint8_t g_hex_data[MAX_FILE_SIZE]; /* 十六进制数据缓冲区 */ +static ppp_frame_t g_frames[MAX_FRAMES]; /* PPP帧数组 */ +static packet_analysis_result_t g_results[MAX_FRAMES]; /* 分析结果数组 */ + +/** + * 获取当前时间(微秒) + */ +static uint64_t get_time_us(void) +{ + struct timeval tv; + gettimeofday(&tv, NULL); + return (uint64_t)tv.tv_sec * 1000000 + tv.tv_usec; +} + +/** + * 从十六进制字符串转换为字节值 + */ +static int hex_char_to_byte(char hex_char) +{ + if (hex_char >= '0' && hex_char <= '9') { + return hex_char - '0'; + } else if (hex_char >= 'A' && hex_char <= 'F') { + return hex_char - 'A' + 10; + } else if (hex_char >= 'a' && hex_char <= 'f') { + return hex_char - 'a' + 10; + } + return -1; +} + +/** + * 从文件中读取十六进制数据 + */ +static uint32_t read_hex_file(const char *file_path, uint8_t *data, uint32_t max_size) +{ + if (!file_path || !data || max_size == 0) { + return 0; + } + + FILE *file = fopen(file_path, "r"); + if (!file) { + printf("错误: 无法打开文件 %s: %s\n", file_path, strerror(errno)); + return 0; + } + + char line[256]; + uint32_t data_len = 0; + + while (fgets(line, sizeof(line), file) && data_len < max_size) { + /* 移除行尾的换行符和空白字符 */ + char *end = line + strlen(line) - 1; + while (end >= line && isspace(*end)) { + *end-- = '\0'; + } + + /* 跳过空行 */ + if (strlen(line) == 0) { + continue; + } + + /* 处理每行的十六进制字符串 */ + char *ptr = line; + while (*ptr && data_len < max_size) { + /* 跳过空白字符 */ + while (*ptr && isspace(*ptr)) { + ptr++; + } + + if (!*ptr) break; + + /* 读取两个十六进制字符组成一个字节 */ + if (strlen(ptr) >= 2) { + int high = hex_char_to_byte(ptr[0]); + int low = hex_char_to_byte(ptr[1]); + + if (high >= 0 && low >= 0) { + data[data_len++] = (high << 4) | low; + ptr += 2; + } else { + ptr += 2; + } + } else if (strlen(ptr) == 1) { + /* 单个十六进制字符,当作一个字节处理 */ + int value = hex_char_to_byte(ptr[0]); + if (value >= 0) { + data[data_len++] = value; + ptr++; + } else { + ptr++; + } + } + } + } + + fclose(file); + return data_len; +} + +/** + * 传统解析器性能测试 + */ +static void test_traditional_parser(const char *file_path, uint32_t data_len) +{ + printf("\n=== 传统解析器性能测试 ===\n"); + + uint64_t start_time = get_time_us(); + + /* 初始化解析器 */ + ppp_parser_t parser; + ppp_parser_init(&parser); + + uint64_t init_time = get_time_us(); + + /* 解析PPP帧 */ + uint32_t frame_count = ppp_parser_process_data(&parser, g_hex_data, data_len, + g_frames, MAX_FRAMES); + + uint64_t parse_time = get_time_us(); + + /* 包内容分析 */ + packet_stats_t packet_stats; + uint32_t analyzed_count = analyze_ppp_frames(g_frames, frame_count, + g_results, &packet_stats); + + uint64_t analyze_time = get_time_us(); + + /* 打印性能统计 */ + printf("文件: %s\n", file_path); + printf("数据大小: %u 字节\n", data_len); + printf("解析帧数: %u\n", frame_count); + printf("分析帧数: %u\n", analyzed_count); + printf("时间统计:\n"); + printf(" 初始化时间: %lu 微秒\n", init_time - start_time); + printf(" 解析时间: %lu 微秒\n", parse_time - init_time); + printf(" 分析时间: %lu 微秒\n", analyze_time - parse_time); + printf(" 总时间: %lu 微秒\n", analyze_time - start_time); + printf("性能指标:\n"); + printf(" 解析速度: %.2f MB/s\n", + (double)data_len / (parse_time - init_time) * 1000000 / (1024 * 1024)); + printf(" 分析速度: %.2f 帧/秒\n", + (double)frame_count / (analyze_time - parse_time) * 1000000); + printf(" 总体速度: %.2f MB/s\n", + (double)data_len / (analyze_time - start_time) * 1000000 / (1024 * 1024)); + + /* 内存使用估算 */ + size_t memory_usage = sizeof(ppp_parser_t) + + sizeof(ppp_frame_t) * MAX_FRAMES + + sizeof(packet_analysis_result_t) * MAX_FRAMES; + printf("内存使用: %.2f KB (估算)\n", (double)memory_usage / 1024); +} + +/** + * 流式解析器性能测试 + */ +static void test_streaming_parser(const char *file_path, uint32_t data_len) +{ + printf("\n=== 流式解析器性能测试 ===\n"); + + uint64_t start_time = get_time_us(); + + /* 初始化帧收集器 */ + ppp_frame_collector_t collector; + if (!ppp_frame_collector_init(&collector, MAX_FRAMES)) { + printf("错误: 无法初始化帧收集器\n"); + return; + } + + /* 初始化流式解析器 */ + ppp_stream_parser_t parser; + if (!ppp_stream_parser_init(&parser, + 0, /* 使用默认最大帧大小 */ + ppp_collector_start_callback, + ppp_collector_data_callback, + ppp_collector_complete_callback, + ppp_collector_error_callback, + &collector)) { + printf("错误: 无法初始化流式解析器\n"); + ppp_frame_collector_free(&collector); + return; + } + + uint64_t init_time = get_time_us(); + + /* 分块处理数据 */ + uint32_t total_processed = 0; + uint32_t chunk_count = 0; + + for (uint32_t offset = 0; offset < data_len; offset += CHUNK_SIZE) { + uint32_t chunk_size = (offset + CHUNK_SIZE > data_len) ? + (data_len - offset) : CHUNK_SIZE; + + chunk_count++; + uint32_t processed = ppp_stream_parser_process(&parser, + g_hex_data + offset, + chunk_size); + total_processed += processed; + } + + uint64_t parse_time = get_time_us(); + + /* 分析收集到的帧 */ + uint32_t analyzed_count = 0; + for (uint32_t i = 0; i < collector.frame_count; i++) { + const uint8_t *frame_data; + uint32_t frame_len; + + if (ppp_frame_collector_get_frame(&collector, i, &frame_data, &frame_len)) { + ppp_frame_t temp_frame; + temp_frame.payload_len = frame_len; + memcpy(temp_frame.payload, frame_data, + (frame_len > PPP_MAX_FRAME_SIZE) ? PPP_MAX_FRAME_SIZE : frame_len); + + packet_analysis_result_t result; + if (analyze_ppp_frame(&temp_frame, &result)) { + analyzed_count++; + } + } + } + + uint64_t analyze_time = get_time_us(); + + /* 打印性能统计 */ + printf("文件: %s\n", file_path); + printf("数据大小: %u 字节\n", data_len); + printf("处理块数: %u (块大小: %d 字节)\n", chunk_count, CHUNK_SIZE); + printf("解析帧数: %u\n", collector.frame_count); + printf("分析帧数: %u\n", analyzed_count); + printf("时间统计:\n"); + printf(" 初始化时间: %lu 微秒\n", init_time - start_time); + printf(" 解析时间: %lu 微秒\n", parse_time - init_time); + printf(" 分析时间: %lu 微秒\n", analyze_time - parse_time); + printf(" 总时间: %lu 微秒\n", analyze_time - start_time); + printf("性能指标:\n"); + printf(" 解析速度: %.2f MB/s\n", + (double)data_len / (parse_time - init_time) * 1000000 / (1024 * 1024)); + printf(" 分析速度: %.2f 帧/秒\n", + (double)collector.frame_count / (analyze_time - parse_time) * 1000000); + printf(" 总体速度: %.2f MB/s\n", + (double)data_len / (analyze_time - start_time) * 1000000 / (1024 * 1024)); + + /* 内存使用估算 */ + size_t base_memory = sizeof(ppp_stream_parser_t) + sizeof(ppp_frame_collector_t); + size_t dynamic_memory = 0; + for (uint32_t i = 0; i < collector.frame_count; i++) { + dynamic_memory += collector.frame_lengths[i]; + } + printf("内存使用: %.2f KB (基础) + %.2f KB (动态) = %.2f KB (总计)\n", + (double)base_memory / 1024, + (double)dynamic_memory / 1024, + (double)(base_memory + dynamic_memory) / 1024); + + /* 清理资源 */ + ppp_frame_collector_free(&collector); +} + +/** + * 主函数 + */ +int main(int argc, char *argv[]) +{ + printf("PPP解析器性能测试工具\n"); + printf("对比传统解析器和流式解析器的性能\n\n"); + + /* 确定要测试的文件 */ + const char *test_file = (argc > 1) ? argv[1] : "out0.txt"; + + /* 读取测试数据 */ + printf("读取测试文件: %s\n", test_file); + uint32_t data_len = read_hex_file(test_file, g_hex_data, MAX_FILE_SIZE); + if (data_len == 0) { + printf("错误: 无法读取测试文件\n"); + return 1; + } + + printf("成功读取 %u 字节数据\n", data_len); + + /* 运行性能测试 */ + test_traditional_parser(test_file, data_len); + test_streaming_parser(test_file, data_len); + + /* 打印对比总结 */ + printf("\n=== 性能对比总结 ===\n"); + printf("传统解析器特点:\n"); + printf(" - 一次性加载所有数据到内存\n"); + printf(" - 固定大小的缓冲区\n"); + printf(" - 简单的线性处理\n"); + printf(" - 内存使用可预测\n\n"); + + printf("流式解析器特点:\n"); + printf(" - 分块处理数据,内存占用固定\n"); + printf(" - 支持任意大小的PPP帧\n"); + printf(" - 回调机制,灵活的数据处理\n"); + printf(" - 适合实时数据流处理\n"); + printf(" - 动态内存分配,按需使用\n\n"); + + printf("推荐使用场景:\n"); + printf(" 传统解析器: 小文件、批处理、内存充足\n"); + printf(" 流式解析器: 大文件、实时处理、内存受限、嵌入式系统\n"); + + return 0; +} \ No newline at end of file diff --git a/code/sdk/zhihui/ppp/ppp_dec_test/ppp_analyzer.py b/code/sdk/zhihui/ppp/ppp_dec_test/ppp_analyzer.py new file mode 100644 index 000000000..6d11c3a5c --- /dev/null +++ b/code/sdk/zhihui/ppp/ppp_dec_test/ppp_analyzer.py @@ -0,0 +1,514 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +PPP协议解析工具 +用于解析out.txt和in.txt中的十六进制数据,识别TCP/UDP包并提取基本信息 +""" +import struct +import os +import binascii + +class PPPFrameParser: + """PPP帧解析器,处理帧边界检测、字节填充和数据验证""" + def __init__(self): + self.buffer = bytearray() # 帧数据缓冲区 + self.in_frame = False # 是否处于帧接收状态 + self.dropped_data = [] # 记录丢弃的数据 + self.last_dropped_position = -1 # 上一次丢弃数据的位置 + self.fragmented_frames = {} # 存储分片的PPP帧 {frame_id: fragmented_data} + + def process_byte(self, byte, position=None): + """处理单个字节,返回完整PPP帧(包含头尾0x7E)""" + if byte == 0x7E: # PPP帧标志位 + if self.in_frame and len(self.buffer) > 0: + # 帧结束:添加结束标志并处理缓冲区数据 + self.buffer.append(byte) # 添加结束0x7E + frame = self._parse_buffer_with_flags() + self.buffer.clear() + self.in_frame = False + return frame + else: + # 帧开始:初始化缓冲区并添加开始标志 + # 如果有未处理的缓冲区数据,记录为丢弃数据 + if self.in_frame and len(self.buffer) > 0: + self._record_dropped_data(self.buffer, position - len(self.buffer)) + self.in_frame = True + self.buffer.clear() + self.buffer.append(byte) # 添加开始0x7E + else: + if self.in_frame: + self.buffer.append(byte) + return None + + def _parse_buffer_with_flags(self): + """处理包含头尾0x7E的完整PPP帧,返回原始帧数据""" + # 验证帧头尾标志 + if len(self.buffer) < 6: # 最小:0x7E + 地址1 + 控制1 + 协议2 + 0x7E = 6字节 + self._record_dropped_data(self.buffer, None) + return None + + if self.buffer[0] != 0x7E or self.buffer[-1] != 0x7E: + self._record_dropped_data(self.buffer, None) + return None + + # 返回完整帧数据(包含头尾0x7E),转义字符处理将在分析阶段进行 + return bytes(self.buffer) + + def _unescape_ppp_frame(self, frame_content): + """处理PPP转义字符,输入是去掉头尾0x7E的帧内容""" + processed = bytearray() + i = 0 + while i < len(frame_content): + if frame_content[i] == 0x7D: # 转义字符 + if i + 1 < len(frame_content): + processed.append(frame_content[i+1] ^ 0x20) + i += 2 + else: + # 不完整的转义序列 + return None + else: + processed.append(frame_content[i]) + i += 1 + return processed + + def _parse_buffer_simple(self): + """简化版缓冲区处理,只还原字节填充,不进行FCS校验""" + # 还原字节填充(0x7D后字节异或0x20) + processed = bytearray() + i = 0 + while i < len(self.buffer): + if self.buffer[i] == 0x7D: # 转义字符 + if i + 1 < len(self.buffer): + processed.append(self.buffer[i+1] ^ 0x20) + i += 2 + else: + # 不完整的转义序列,记录为丢弃数据 + self.dropped_data.append({ + 'reason': '不完整的转义序列', + 'data': bytes(self.buffer), + 'position': self.last_dropped_position + }) + return None + else: + processed.append(self.buffer[i]) + i += 1 + + # 校验帧长度(最小:地址1 + 控制1 + 协议2 = 4字节) + if len(processed) < 4: + # 帧长度不足,记录为丢弃数据 + self.dropped_data.append({ + 'reason': '帧长度不足4字节', + 'data': bytes(self.buffer), + 'position': self.last_dropped_position + }) + return None + + # 返回处理后的数据 + return processed + + def _record_dropped_data(self, data, position): + """记录丢弃的数据""" + self.dropped_data.append({ + 'reason': '帧同步丢失或格式错误', + 'data': bytes(data), + 'position': position + }) + self.last_dropped_position = position + + def get_dropped_data_count(self): + """获取丢弃的数据数量""" + return len(self.dropped_data) + + def print_dropped_data(self): + """打印所有丢弃的数据""" + if not self.dropped_data: + print(" 没有丢弃的数据") + return + + print(f" 丢弃的数据总数: {len(self.dropped_data)}") + # 只打印前5条丢弃的数据,避免输出过多 + for i, dropped in enumerate(self.dropped_data[:5]): + print(f" 丢弃数据 #{i+1}:") + print(f" 原因: {dropped['reason']}") + print(f" 位置: {dropped['position']}") + print(f" 数据: {dropped['data'].hex().upper()}") + + if len(self.dropped_data) > 5: + print(f" ... 还有{len(self.dropped_data) - 5}条丢弃的数据未显示") + + def reassemble_fragment(self, frame): + """尝试重组分片的PPP帧 + 在PPP中,分片是通过MP(多链路PPP)协议实现的,但这里我们使用简化的方式 + 实际应用中可能需要根据具体的PPP扩展协议实现更复杂的重组逻辑 + """ + # 这个简化版本假设所有帧都是完整的,不处理分片 + # 如果需要处理实际的PPP分片,需要根据具体的PPP扩展协议实现 + return frame + + def get_fragmented_frame_count(self): + """获取当前缓存的分片帧数量""" + return len(self.fragmented_frames) + + +def calculate_ip_checksum(ip_header): + """计算IP头部校验和""" + # 将IP头部视为16位字的序列 + words = [struct.unpack('!H', ip_header[i:i+2])[0] for i in range(0, len(ip_header), 2)] + + # 如果头部长度为奇数,最后一个字节补零 + if len(ip_header) % 2 == 1: + words.append(ip_header[-1] << 8) + + # 计算校验和 + checksum = sum(words) + # 将溢出的部分加到低16位 + checksum = (checksum >> 16) + (checksum & 0xffff) + checksum += (checksum >> 16) # 再次处理溢出 + # 取反得到最终校验和 + return ~checksum & 0xffff + + +def verify_ip_packet(ip_data): + """验证IP数据包的完整性和正确性""" + # 检查最小长度 + if len(ip_data) < 20: + return False, "IP数据包长度不足20字节" + + # 验证版本号(应为4) + version = (ip_data[0] >> 4) & 0x0F + if version != 4: + return False, f"非IPv4数据包,版本号: {version}" + + # 验证头部长度 + ihl = ip_data[0] & 0x0F + if ihl < 5 or ihl > 15: + return False, f"无效的IP头部长度: {ihl}" + + # 检查总长度是否合理 + total_length = (ip_data[2] << 8) | ip_data[3] + # 允许实际长度比声明长度多2字节(可能是PPP填充) + if not (total_length == len(ip_data) or total_length == len(ip_data) - 2): + return False, f"IP数据包长度不匹配: 头部声明{total_length}字节,实际{len(ip_data)}字节" + + # 保存原始校验和 + original_checksum = (ip_data[10] << 8) | ip_data[11] + + # 清零校验和字段后重新计算 + ip_header_copy = bytearray(ip_data[:ihl*4]) + ip_header_copy[10] = 0 + ip_header_copy[11] = 0 + + # 计算校验和 + calculated_checksum = calculate_ip_checksum(ip_header_copy) + + # 验证校验和 + if original_checksum != calculated_checksum: + return False, f"IP校验和错误: 接收{original_checksum:04X},计算{calculated_checksum:04X}" + + return True, "IP数据包验证通过" + + +def parse_ip_packet(ip_data): + """解析IP数据包,提取基本信息""" + if len(ip_data) < 20: # IP头部最小长度为20字节 + return None + + # 解析IP头部(不考虑选项字段) + ip_header = struct.unpack('!BBHHHBBH4s4s', ip_data[:20]) + version_ihl = ip_header[0] + protocol = ip_header[6] # 协议类型 + src_ip = '.'.join(map(str, ip_header[8])) # 源IP地址 + dst_ip = '.'.join(map(str, ip_header[9])) # 目标IP地址 + + # 计算IP数据部分的偏移量 + ihl = (version_ihl & 0x0F) * 4 + payload = ip_data[ihl:] + + result = { + 'src_ip': src_ip, + 'dst_ip': dst_ip, + 'protocol': protocol + } + + # 根据协议类型解析传输层数据 + if protocol == 6: # TCP协议 + result['protocol_name'] = 'TCP' + tcp_info = parse_tcp_packet(payload) + if tcp_info: + result.update(tcp_info) + elif protocol == 17: # UDP协议 + result['protocol_name'] = 'UDP' + udp_info = parse_udp_packet(payload) + if udp_info: + result.update(udp_info) + else: + result['protocol_name'] = f'Unknown({protocol})' + + return result + +def parse_tcp_packet(tcp_data): + """解析TCP数据包,提取端口信息""" + if len(tcp_data) < 20: # TCP头部最小长度为20字节 + return None + + # 解析TCP头部 + tcp_header = struct.unpack('!HHLLBBHHH', tcp_data[:20]) + src_port = tcp_header[0] # 源端口 + dst_port = tcp_header[1] # 目标端口 + + return { + 'src_port': src_port, + 'dst_port': dst_port + } + +def parse_udp_packet(udp_data): + """解析UDP数据包,提取端口信息""" + if len(udp_data) < 8: # UDP头部固定为8字节 + return None + + # 解析UDP头部 + udp_header = struct.unpack('!HHHH', udp_data[:8]) + src_port = udp_header[0] # 源端口 + dst_port = udp_header[1] # 目标端口 + + return { + 'src_port': src_port, + 'dst_port': dst_port + } + +def read_hex_file(file_path): + """从文件中读取十六进制数据""" + try: + with open(file_path, 'r', encoding='utf-8') as f: + lines = f.readlines() + # 移除每行的换行符并转换为字节 + bytes_data = [] + for i, line in enumerate(lines): + stripped_line = line.strip() + if stripped_line: + try: + # 转换十六进制字符串为整数 + byte_value = int(stripped_line, 16) + if 0 <= byte_value <= 255: + bytes_data.append(byte_value) + else: + print(f"警告: 文件{file_path}第{i+1}行值{stripped_line}超出字节范围") + except ValueError: + print(f"警告: 文件{file_path}第{i+1}行'{stripped_line}'不是有效的十六进制字符串") + print(f"成功读取文件{file_path}: {len(bytes_data)}字节") + return bytearray(bytes_data) + except Exception as e: + print(f"读取文件{file_path}错误: {e}") + return None + + +def group_ppp_frames(frames): + """对PPP帧进行组包处理,目前主要是简单的收集,后续可以扩展更复杂的组包逻辑""" + # 按协议类型对帧进行分组 + grouped_frames = {} + for frame in frames: + if len(frame) >= 4: + protocol = (frame[2] << 8) | frame[3] + if protocol not in grouped_frames: + grouped_frames[protocol] = [] + grouped_frames[protocol].append(frame) + else: + # 帧长度不足,无法解析协议 + if 'invalid' not in grouped_frames: + grouped_frames['invalid'] = [] + grouped_frames['invalid'].append(frame) + + return grouped_frames + +def analyze_ppp_file(file_path): + """分析PPP文件并输出TCP/UDP包信息""" + print(f"\n开始分析文件: {file_path}") + + # 读取文件数据 + data = read_hex_file(file_path) + if not data: + return + + parser = PPPFrameParser() + tcp_count = 0 + udp_count = 0 + other_count = 0 + ppp_frame_count = 0 + reassembled_count = 0 # 重组的帧数量 + protocol_counts = {} + frames = [] # 存储所有解析出的PPP帧 + ip_validation_results = [] # 存储IP帧验证结果 + + # 处理每个字节 + for i, byte in enumerate(data): + frame = parser.process_byte(byte, position=i) + if frame: + # frame现在包含完整PPP帧(包含头尾0x7E) + # 尝试重组分片帧(暂时跳过重组,直接处理单个帧) + raw_frame = frame # 完整帧数据(包含头尾0x7E) + frames.append(raw_frame) + ppp_frame_count += 1 + + # 打印PPP帧信息(显示完整帧长度) + print(f"PPP帧 #{ppp_frame_count}: 长度={len(raw_frame)}字节, 起始位置={i-len(raw_frame)+1}") + print(f" 内容: {raw_frame.hex().upper()}") + + # 验证帧头尾标志 + if len(raw_frame) >= 6 and raw_frame[0] == 0x7E and raw_frame[-1] == 0x7E: + # 提取帧内容(去掉头尾0x7E) + frame_content = raw_frame[1:-1] + + # 处理转义字符 + processed_frame = parser._unescape_ppp_frame(frame_content) + if processed_frame and len(processed_frame) >= 4: + # 解析PPP头部 + address = processed_frame[0] + control = processed_frame[1] + # 协议字段是大端模式的两字节 + protocol = (processed_frame[2] << 8) | processed_frame[3] + + # 更新协议计数 + if protocol not in protocol_counts: + protocol_counts[protocol] = 0 + protocol_counts[protocol] += 1 + + payload = processed_frame[4:] + + print(f" 协议: 0x{protocol:04X}, 地址: 0x{address:02X}, 控制: 0x{control:02X}, 负载长度: {len(payload)}字节") + + # 检查是否为IP协议或其他常见PPP协议 + if protocol == 0x0021: # IP协议 + # 验证IP数据包完整性 + is_valid, validation_msg = verify_ip_packet(payload) + ip_validation_results.append({ + 'frame_number': ppp_frame_count, + 'is_valid': is_valid, + 'message': validation_msg + }) + + print(f" IP验证: {'通过' if is_valid else '失败'} - {validation_msg}") + + if is_valid: + # 解析IP数据包 + ip_info = parse_ip_packet(payload) + if ip_info: + # 输出TCP/UDP包信息 + if ip_info['protocol'] == 6: # TCP + tcp_count += 1 + print(f" TCP #{tcp_count}: {ip_info['src_ip']}:{ip_info['src_port']} -> {ip_info['dst_ip']}:{ip_info['dst_port']}") + elif ip_info['protocol'] == 17: # UDP + udp_count += 1 + print(f" UDP #{udp_count}: {ip_info['src_ip']}:{ip_info['src_port']} -> {ip_info['dst_ip']}:{ip_info['dst_port']}") + else: + other_count += 1 + else: + other_count += 1 + else: + other_count += 1 + elif protocol == 0xC021: # LCP协议 + # LCP协议不需要进一步分析 + print(f" LCP协议帧") + elif protocol == 0x8021: # IPCP协议 + print(f" IPCP协议帧") + else: + print(f" 其他PPP协议: 0x{protocol:04X}") + else: + print(f" 警告: 帧长度不足4字节,无法解析PPP头部") + + # 打印PPP帧组包结果 + if frames: + print(f"\nPPP帧组包结果:") + grouped_frames = group_ppp_frames(frames) + for proto, proto_frames in grouped_frames.items(): + if proto == 'invalid': + print(f" 无效帧: {len(proto_frames)}个") + else: + protocol_name = "未知" + if proto == 0x0021: + protocol_name = "IP (IPv4)" + elif proto == 0xC021: + protocol_name = "LCP (链路控制协议)" + elif proto == 0x8021: + protocol_name = "IPCP (IP控制协议)" + print(f" 0x{proto:04X} ({protocol_name}): {len(proto_frames)}个帧") + + # 验证组包结果是否正确 + total_grouped = sum(len(frames) for frames in grouped_frames.values()) + if total_grouped == len(frames): + print(f" 组包验证: 成功,所有{total_grouped}个帧都已正确分组") + else: + print(f" 组包验证: 警告,分组总数({total_grouped})与原始帧总数({len(frames)})不匹配") + + # 打印分片重组统计 + fragmented_count = parser.get_fragmented_frame_count() + print(f"\nPPP帧分片重组统计:") + print(f" 解析的原始帧数量: {ppp_frame_count}") + print(f" 成功重组的帧数量: {reassembled_count}") + print(f" 当前缓存的分片数量: {fragmented_count}") + + # 打印IP帧验证统计 + if ip_validation_results: + valid_ip_count = sum(1 for result in ip_validation_results if result['is_valid']) + print(f"\nIP帧验证统计:") + print(f" 总共验证IP帧: {len(ip_validation_results)}") + print(f" 验证通过: {valid_ip_count}") + print(f" 验证失败: {len(ip_validation_results) - valid_ip_count}") + + # 打印前几个验证失败的详细信息 + failed_results = [r for r in ip_validation_results if not r['is_valid']][:3] + for result in failed_results: + print(f" 帧 #{result['frame_number']} 验证失败: {result['message']}") + + # 打印丢弃的数据信息 + print(f"\n丢弃的数据信息:") + parser.print_dropped_data() + + # 打印协议统计信息 + print(f"\n文件分析完成: {file_path}") + print(f"总共发现 PPP 帧: {ppp_frame_count}") + print(f"发现 TCP 包: {tcp_count}") + print(f"发现 UDP 包: {udp_count}") + print(f"其他/未知包: {other_count}") + + # 打印协议分布 + print("\nPPP协议分布:") + for proto, count in sorted(protocol_counts.items()): + protocol_name = "未知" + if proto == 0x0021: + protocol_name = "IP (IPv4)" + elif proto == 0xC021: + protocol_name = "LCP (链路控制协议)" + elif proto == 0x8021: + protocol_name = "IPCP (IP控制协议)" + print(f" 0x{proto:04X} ({protocol_name}): {count}个帧") + + +def main(): + """主函数""" + import sys + + # 检查是否有命令行参数指定要分析的文件 + if len(sys.argv) > 1: + # 使用命令行参数中的文件列表 + files_to_analyze = sys.argv[1:] + else: + # 默认分析out.txt和in.txt + files_to_analyze = ['out.txt', 'in.txt'] + + # 遍历并分析每个文件 + for file_name in files_to_analyze: + # 构建完整的文件路径 + if os.path.isabs(file_name): + file_path = file_name + else: + file_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), file_name) + + if os.path.exists(file_path): + print(f"\n=== 开始分析文件: {file_path} ===") + analyze_ppp_file(file_path) + else: + print(f"文件不存在: {file_path}") + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/code/sdk/zhihui/ppp/ppp_dec_test/ppp_file_reader.c b/code/sdk/zhihui/ppp/ppp_dec_test/ppp_file_reader.c new file mode 100644 index 000000000..ae01ea0eb --- /dev/null +++ b/code/sdk/zhihui/ppp/ppp_dec_test/ppp_file_reader.c @@ -0,0 +1,330 @@ +#include +#include +#include +#include +#include +#include "ppp_parser.h" +#include "ppp_packet_analyzer.h" + +/* 配置常量 */ +#define MAX_FILE_SIZE (1024 * 1024) /* 最大文件大小1MB */ +#define MAX_FRAMES 1000 /* 最大帧数量 */ +#define MAX_RESULTS 1000 /* 最大分析结果数量 */ +#define HEX_BUFFER_SIZE 16384 /* 十六进制读取缓冲区大小 */ +#define MAX_LINE_LENGTH 256 /* 最大行长度 */ + +/* 全局变量 */ +static uint8_t g_hex_data[MAX_FILE_SIZE]; /* 十六进制数据缓冲区 */ +static ppp_frame_t g_frames[MAX_FRAMES]; /* PPP帧数组 */ +static packet_analysis_result_t g_results[MAX_RESULTS]; /* 分析结果数组 */ + +/** + * 从十六进制字符串转换为字节值 + * @param hex_char 十六进制字符 + * @return 字节值,失败返回-1 + */ +static int hex_char_to_byte(char hex_char) +{ + if (hex_char >= '0' && hex_char <= '9') { + return hex_char - '0'; + } else if (hex_char >= 'A' && hex_char <= 'F') { + return hex_char - 'A' + 10; + } else if (hex_char >= 'a' && hex_char <= 'f') { + return hex_char - 'a' + 10; + } + return -1; +} + +/** + * 从文件中读取十六进制数据 + * @param file_path 文件路径 + * @param data 输出数据缓冲区 + * @param max_size 最大数据大小 + * @return 实际读取的字节数,失败返回0 + */ +static uint32_t read_hex_file(const char *file_path, uint8_t *data, uint32_t max_size) +{ + if (!file_path || !data || max_size == 0) { + printf("错误: 无效的参数\n"); + return 0; + } + + FILE *file = fopen(file_path, "r"); + if (!file) { + printf("错误: 无法打开文件 %s: %s\n", file_path, strerror(errno)); + return 0; + } + + char line[MAX_LINE_LENGTH]; + uint32_t data_len = 0; + uint32_t line_number = 0; + + printf("开始读取文件: %s\n", file_path); + + while (fgets(line, sizeof(line), file) && data_len < max_size) { + line_number++; + + /* 移除行尾的换行符和空白字符 */ + char *end = line + strlen(line) - 1; + while (end >= line && isspace(*end)) { + *end-- = '\0'; + } + + /* 跳过空行 */ + if (strlen(line) == 0) { + continue; + } + + /* 处理每行的十六进制字符串 */ + char *ptr = line; + while (*ptr && data_len < max_size) { + /* 跳过空白字符 */ + while (*ptr && isspace(*ptr)) { + ptr++; + } + + if (!*ptr) break; + + /* 读取两个十六进制字符组成一个字节 */ + if (strlen(ptr) >= 2) { + int high = hex_char_to_byte(ptr[0]); + int low = hex_char_to_byte(ptr[1]); + + if (high >= 0 && low >= 0) { + data[data_len++] = (high << 4) | low; + ptr += 2; + } else { + printf("警告: 文件%s第%u行包含无效的十六进制字符: %c%c\n", + file_path, line_number, ptr[0], ptr[1]); + ptr += 2; + } + } else if (strlen(ptr) == 1) { + /* 单个十六进制字符,当作一个字节处理 */ + int value = hex_char_to_byte(ptr[0]); + if (value >= 0) { + data[data_len++] = value; + ptr++; + } else { + printf("警告: 文件%s第%u行包含无效的十六进制字符: %c\n", + file_path, line_number, ptr[0]); + ptr++; + } + } + } + } + + fclose(file); + + if (data_len >= max_size) { + printf("警告: 文件%s数据量超过缓冲区大小,只读取了前%u字节\n", file_path, max_size); + } + + printf("成功读取文件%s: %u字节\n", file_path, data_len); + return data_len; +} + +/** + * 分析PPP文件并输出详细信息 + * @param file_path 文件路径 + */ +static void analyze_ppp_file(const char *file_path) +{ + printf("\n=== 开始分析文件: %s ===\n", file_path); + + /* 读取文件数据 */ + uint32_t data_len = read_hex_file(file_path, g_hex_data, MAX_FILE_SIZE); + if (data_len == 0) { + printf("文件读取失败,跳过分析\n"); + return; + } + + /* 初始化PPP解析器 */ + ppp_parser_t parser; + ppp_parser_init(&parser); + + /* 解析PPP帧 */ + printf("\n--- PPP帧解析阶段 ---\n"); + uint32_t frame_count = ppp_parser_process_data(&parser, g_hex_data, data_len, + g_frames, MAX_FRAMES); + + printf("解析完成,共发现 %u 个PPP帧\n", frame_count); + + /* 打印解析器统计信息 */ + ppp_parser_print_stats(&parser); + ppp_parser_print_dropped_data(&parser); + + if (frame_count == 0) { + printf("未发现有效的PPP帧\n"); + return; + } + + /* 打印前几个PPP帧的详细信息 */ + printf("\n--- PPP帧详细信息 ---\n"); + uint32_t print_count = (frame_count > 5) ? 5 : frame_count; + for (uint32_t i = 0; i < print_count; i++) { + ppp_print_frame_info(&g_frames[i], i + 1); + } + + if (frame_count > 5) { + printf("... 还有%u个帧未显示详细信息\n", frame_count - 5); + } + + /* 包内容分析 */ + printf("\n--- 包内容分析阶段 ---\n"); + packet_stats_t packet_stats; + uint32_t analyzed_count = analyze_ppp_frames(g_frames, frame_count, + g_results, &packet_stats); + + /* PPP帧组包分析(在analyzer处理后进行,这样协议信息已经解析) */ + printf("\n--- PPP帧组包分析 ---\n"); + ppp_group_result_t group_result; + ppp_classify_frames(g_frames, frame_count, &group_result); + ppp_print_group_result(&group_result); + + printf("包分析完成,成功分析 %u 个包\n", analyzed_count); + + /* 打印包统计信息 */ + print_packet_stats(&packet_stats); + + /* 打印前几个包的详细分析结果 */ + printf("\n--- 包详细分析结果 ---\n"); + uint32_t detail_count = (analyzed_count > 3) ? 3 : analyzed_count; + for (uint32_t i = 0; i < detail_count; i++) { + if (g_results[i].has_ip) { + print_packet_analysis_result(&g_results[i], i + 1); + } + } + + if (analyzed_count > 3) { + printf("... 还有%u个包未显示详细分析结果\n", analyzed_count - 3); + } + + /* 简化的TCP/UDP包统计输出 */ + printf("\n--- TCP/UDP包简要统计 ---\n"); + uint32_t tcp_count = 0, udp_count = 0; + for (uint32_t i = 0; i < analyzed_count; i++) { + if (g_results[i].has_tcp) { + tcp_count++; + if (tcp_count <= 10) { /* 只显示前10个 */ + char src_ip[16], dst_ip[16]; + format_ip_address(&g_results[i].ip_info.src_ip, src_ip, sizeof(src_ip)); + format_ip_address(&g_results[i].ip_info.dst_ip, dst_ip, sizeof(dst_ip)); + printf(" TCP #%u: %s:%u -> %s:%u\n", tcp_count, + src_ip, g_results[i].tcp_info.src_port, + dst_ip, g_results[i].tcp_info.dst_port); + } + } else if (g_results[i].has_udp) { + udp_count++; + if (udp_count <= 10) { /* 只显示前10个 */ + char src_ip[16], dst_ip[16]; + format_ip_address(&g_results[i].ip_info.src_ip, src_ip, sizeof(src_ip)); + format_ip_address(&g_results[i].ip_info.dst_ip, dst_ip, sizeof(dst_ip)); + printf(" UDP #%u: %s:%u -> %s:%u\n", udp_count, + src_ip, g_results[i].udp_info.src_port, + dst_ip, g_results[i].udp_info.dst_port); + } + } + } + + if (tcp_count > 10) { + printf(" ... 还有%u个TCP包未显示\n", tcp_count - 10); + } + if (udp_count > 10) { + printf(" ... 还有%u个UDP包未显示\n", udp_count - 10); + } + + printf("\n=== 文件分析完成: %s ===\n", file_path); + printf("总结: 发现PPP帧%u个, TCP包%u个, UDP包%u个, 其他包%u个\n", + frame_count, packet_stats.tcp_packets, packet_stats.udp_packets, + packet_stats.other_packets); +} + +/** + * 打印使用说明 + */ +static void print_usage(const char *program_name) +{ + printf("PPP协议解析工具 (C语言版本)\n"); + printf("用于解析十六进制文件中的PPP协议数据,识别TCP/UDP包并提取基本信息\n\n"); + printf("使用方法:\n"); + printf(" %s [文件1] [文件2] ...\n\n", program_name); + printf("参数说明:\n"); + printf(" 文件1, 文件2, ... : 要分析的十六进制数据文件\n"); + printf(" 如果不指定文件,默认分析 out.txt 和 in.txt\n\n"); + printf("文件格式:\n"); + printf(" 每行包含十六进制数据,可以是单个字节或多个字节\n"); + printf(" 例如: 7E FF 03 00 21 45 00 00 1C 7E\n"); + printf(" 或者: 7E\n"); + printf(" FF\n"); + printf(" 03\n\n"); + printf("示例:\n"); + printf(" %s out.txt in.txt\n", program_name); + printf(" %s data1.hex data2.hex data3.hex\n", program_name); +} + +/** + * 主函数 + */ +int main(int argc, char *argv[]) +{ + printf("PPP协议解析工具 (C语言版本) - 启动\n"); + printf("版本: 1.0\n"); + printf("功能: PPP帧解析、IP包分析、TCP/UDP包识别\n\n"); + + /* 检查命令行参数 */ + if (argc > 1 && (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0)) { + print_usage(argv[0]); + return 0; + } + + /* 确定要分析的文件列表 */ + const char **files_to_analyze; + int file_count; + const char *default_files[] = {"out.txt", "in.txt"}; + + if (argc > 1) { + /* 使用命令行参数中的文件列表 */ + files_to_analyze = (const char **)&argv[1]; + file_count = argc - 1; + } else { + /* 使用默认文件列表 */ + files_to_analyze = default_files; + file_count = 2; + printf("未指定文件,使用默认文件: out.txt, in.txt\n"); + } + + /* 遍历并分析每个文件 */ + int success_count = 0; + for (int i = 0; i < file_count; i++) { + const char *file_path = files_to_analyze[i]; + + /* 检查文件是否存在 */ + FILE *test_file = fopen(file_path, "r"); + if (test_file) { + fclose(test_file); + analyze_ppp_file(file_path); + success_count++; + } else { + printf("\n错误: 文件不存在或无法访问: %s\n", file_path); + } + } + + /* 打印总结信息 */ + printf("\n============================================================\n"); + printf("分析总结:\n"); + printf(" 指定文件数量: %d\n", file_count); + printf(" 成功分析文件: %d\n", success_count); + printf(" 失败文件数量: %d\n", file_count - success_count); + + if (success_count == 0) { + printf("\n警告: 没有成功分析任何文件\n"); + printf("请检查:\n"); + printf("1. 文件是否存在\n"); + printf("2. 文件格式是否正确(十六进制数据)\n"); + printf("3. 文件权限是否允许读取\n"); + return 1; + } + + printf("\nPPP协议解析工具运行完成\n"); + return 0; +} \ No newline at end of file diff --git a/code/sdk/zhihui/ppp/ppp_dec_test/ppp_packet_analyzer.c b/code/sdk/zhihui/ppp/ppp_dec_test/ppp_packet_analyzer.c new file mode 100644 index 000000000..28500ceeb --- /dev/null +++ b/code/sdk/zhihui/ppp/ppp_dec_test/ppp_packet_analyzer.c @@ -0,0 +1,519 @@ +#include "ppp_packet_analyzer.h" +#include +#include +#include + +/* 内部辅助函数声明 */ +static bool ppp_unescape_frame(const uint8_t *raw_data, uint16_t raw_len, + uint8_t *processed_data, uint16_t *processed_len); + +/** + * PPP转义字符处理:还原字节填充 + * @param raw_data 原始帧数据(包含转义字符) + * @param raw_len 原始数据长度 + * @param processed_data 输出的处理后数据 + * @param processed_len 输出的处理后数据长度 + * @return 处理是否成功 + */ +static bool ppp_unescape_frame(const uint8_t *raw_data, uint16_t raw_len, + uint8_t *processed_data, uint16_t *processed_len) +{ + if (!raw_data || !processed_data || !processed_len || raw_len == 0) { + return false; + } + + uint16_t out_len = 0; + + /* 还原字节填充(0x7D后字节异或0x20) */ + for (uint16_t i = 0; i < raw_len; i++) { + if (raw_data[i] == 0x7D) { /* PPP转义字符 */ + /* 转义字符 */ + if (i + 1 < raw_len) { + processed_data[out_len++] = raw_data[i + 1] ^ 0x20; + i++; /* 跳过下一个字节 */ + } else { + /* 不完整的转义序列 */ + return false; + } + } else { + processed_data[out_len++] = raw_data[i]; + } + + /* 防止输出缓冲区溢出 */ + if (out_len >= PPP_MAX_FRAME_SIZE) { + return false; + } + } + + *processed_len = out_len; + return true; +} + +/** + * 计算IP头部校验和 + */ +uint16_t calculate_ip_checksum(const uint8_t *ip_header, uint8_t header_len) +{ + if (!ip_header || header_len < IP_MIN_HEADER_SIZE || header_len % 2 != 0) { + return 0; + } + + uint32_t sum = 0; + + /* 将IP头部视为16位字的序列进行累加 */ + for (uint8_t i = 0; i < header_len; i += 2) { + uint16_t word = (ip_header[i] << 8) | ip_header[i + 1]; + sum += word; + } + + /* 处理进位 */ + while (sum >> 16) { + sum = (sum & 0xFFFF) + (sum >> 16); + } + + /* 取反得到校验和 */ + return ~sum & 0xFFFF; +} + +/** + * 验证IP数据包的完整性和正确性 + */ +bool verify_ip_packet(const uint8_t *ip_data, uint16_t data_len, ip_validation_result_t *result) +{ + if (!ip_data || !result) { + *result = IP_ERROR_TOO_SHORT; + return false; + } + + /* 检查最小长度 */ + if (data_len < IP_MIN_HEADER_SIZE) { + *result = IP_ERROR_TOO_SHORT; + return false; + } + + /* 验证版本号(应为4) */ + uint8_t version = (ip_data[0] >> 4) & 0x0F; + if (version != IP_VERSION_4) { + *result = IP_ERROR_INVALID_VERSION; + return false; + } + + /* 验证头部长度 */ + uint8_t ihl = (ip_data[0] & 0x0F) * 4; + if (ihl < IP_MIN_HEADER_SIZE || ihl > IP_MAX_HEADER_SIZE || ihl > data_len) { + *result = IP_ERROR_INVALID_HEADER_LEN; + return false; + } + + /* 检查总长度是否合理 */ + uint16_t total_length = (ip_data[2] << 8) | ip_data[3]; + /* 允许实际长度比声明长度多几个字节(可能是PPP填充),或者实际长度大于等于声明长度 */ + if (total_length > data_len || (data_len - total_length) > 4) { + *result = IP_ERROR_LENGTH_MISMATCH; + return false; + } + + /* 验证校验和 */ + uint16_t original_checksum = (ip_data[10] << 8) | ip_data[11]; + + /* 创建头部副本并清零校验和字段 */ + uint8_t header_copy[IP_MAX_HEADER_SIZE]; + memcpy(header_copy, ip_data, ihl); + header_copy[10] = 0; + header_copy[11] = 0; + + uint16_t calculated_checksum = calculate_ip_checksum(header_copy, ihl); + + if (original_checksum != calculated_checksum) { + *result = IP_ERROR_CHECKSUM_FAILED; + return false; + } + + *result = IP_VALID; + return true; +} + +/** + * 解析IP数据包,提取基本信息 + */ +bool parse_ip_packet(const uint8_t *ip_data, uint16_t data_len, ip_packet_info_t *ip_info) +{ + if (!ip_data || !ip_info || data_len < IP_MIN_HEADER_SIZE) { + return false; + } + + memset(ip_info, 0, sizeof(ip_packet_info_t)); + + /* 解析IP头部字段 */ + ip_info->version = (ip_data[0] >> 4) & 0x0F; + ip_info->header_len = (ip_data[0] & 0x0F) * 4; + ip_info->tos = ip_data[1]; + ip_info->total_len = (ip_data[2] << 8) | ip_data[3]; + ip_info->id = (ip_data[4] << 8) | ip_data[5]; + ip_info->flags_frag = (ip_data[6] << 8) | ip_data[7]; + ip_info->ttl = ip_data[8]; + ip_info->protocol = ip_data[9]; + ip_info->checksum = (ip_data[10] << 8) | ip_data[11]; + + /* 解析IP地址 */ + memcpy(ip_info->src_ip.addr, &ip_data[12], 4); + memcpy(ip_info->dst_ip.addr, &ip_data[16], 4); + + /* 计算负载信息 */ + if (ip_info->header_len <= data_len) { + ip_info->payload = ip_data + ip_info->header_len; + ip_info->payload_len = data_len - ip_info->header_len; + } + + /* 验证IP包 */ + bool is_valid = verify_ip_packet(ip_data, data_len, &ip_info->validation); + ip_info->validation_msg = get_ip_validation_message(ip_info->validation); + + return true; /* 总是返回true,即使验证失败也要解析IP包信息 */ +} + +/** + * 解析TCP数据包,提取端口和基本信息 + */ +bool parse_tcp_packet(const uint8_t *tcp_data, uint16_t data_len, tcp_packet_info_t *tcp_info) +{ + if (!tcp_data || !tcp_info || data_len < TCP_MIN_HEADER_SIZE) { + return false; + } + + memset(tcp_info, 0, sizeof(tcp_packet_info_t)); + + /* 解析TCP头部字段 */ + tcp_info->src_port = (tcp_data[0] << 8) | tcp_data[1]; + tcp_info->dst_port = (tcp_data[2] << 8) | tcp_data[3]; + tcp_info->seq_num = (tcp_data[4] << 24) | (tcp_data[5] << 16) | + (tcp_data[6] << 8) | tcp_data[7]; + tcp_info->ack_num = (tcp_data[8] << 24) | (tcp_data[9] << 16) | + (tcp_data[10] << 8) | tcp_data[11]; + + uint8_t data_offset = (tcp_data[12] >> 4) & 0x0F; + tcp_info->header_len = data_offset * 4; + tcp_info->flags = tcp_data[13]; + tcp_info->window = (tcp_data[14] << 8) | tcp_data[15]; + tcp_info->checksum = (tcp_data[16] << 8) | tcp_data[17]; + tcp_info->urgent_ptr = (tcp_data[18] << 8) | tcp_data[19]; + + /* 计算负载信息 */ + if (tcp_info->header_len <= data_len) { + tcp_info->payload = tcp_data + tcp_info->header_len; + tcp_info->payload_len = data_len - tcp_info->header_len; + } + + return true; +} + +/** + * 解析UDP数据包,提取端口和基本信息 + */ +bool parse_udp_packet(const uint8_t *udp_data, uint16_t data_len, udp_packet_info_t *udp_info) +{ + if (!udp_data || !udp_info || data_len < UDP_HEADER_SIZE) { + return false; + } + + memset(udp_info, 0, sizeof(udp_packet_info_t)); + + /* 解析UDP头部字段 */ + udp_info->src_port = (udp_data[0] << 8) | udp_data[1]; + udp_info->dst_port = (udp_data[2] << 8) | udp_data[3]; + udp_info->length = (udp_data[4] << 8) | udp_data[5]; + udp_info->checksum = (udp_data[6] << 8) | udp_data[7]; + + /* 计算负载信息 */ + udp_info->payload = udp_data + UDP_HEADER_SIZE; + udp_info->payload_len = data_len - UDP_HEADER_SIZE; + + return true; +} + +/** + * 分析PPP帧内容,解析其中的IP/TCP/UDP包 + */ +bool analyze_ppp_frame(const ppp_frame_t *frame, packet_analysis_result_t *result) +{ + if (!frame || !result) { + return false; + } + + memset(result, 0, sizeof(packet_analysis_result_t)); + result->ppp_frame = frame; + + /* 检查原始帧长度 */ + if (frame->raw_frame_len < 6) { /* 最小:0x7E + 地址1 + 控制1 + 协议2 + 0x7E = 6字节 */ + result->protocol_name = "PPP Frame Too Short"; + return false; + } + + /* 验证帧头尾标志 */ + if (frame->raw_frame[0] != PPP_FLAG_BYTE || + frame->raw_frame[frame->raw_frame_len - 1] != PPP_FLAG_BYTE) { + result->protocol_name = "Invalid PPP Frame Flags"; + return false; + } + + /* 提取帧内容(去掉头尾0x7E) */ + const uint8_t *frame_content = &frame->raw_frame[1]; + uint16_t frame_content_len = frame->raw_frame_len - 2; + + /* 处理PPP转义字符 */ + uint8_t processed_data[PPP_MAX_FRAME_SIZE]; + uint16_t processed_len; + + if (!ppp_unescape_frame(frame_content, frame_content_len, processed_data, &processed_len)) { + result->protocol_name = "Invalid PPP Frame"; + return false; + } + + /* 检查处理后的帧长度 */ + if (processed_len < 4) { /* 最小:地址1 + 控制1 + 协议2 = 4字节 */ + result->protocol_name = "PPP Frame Too Short"; + return false; + } + + /* 解析PPP帧头部 */ + uint8_t address = processed_data[0]; + uint8_t control = processed_data[1]; + uint16_t protocol = (processed_data[2] << 8) | processed_data[3]; + + /* 更新frame结构中的协议信息(用于组包统计) */ + ((ppp_frame_t*)frame)->address = address; + ((ppp_frame_t*)frame)->control = control; + ((ppp_frame_t*)frame)->protocol = protocol; + + /* 复制处理后的负载数据 */ + ((ppp_frame_t*)frame)->payload_len = processed_len; + memcpy(((ppp_frame_t*)frame)->payload, processed_data, processed_len); + + /* 获取IP负载数据 */ + const uint8_t *ip_payload = &processed_data[4]; + uint16_t ip_payload_len = processed_len - 4; + + /* 检查是否为IP协议 */ + if (protocol == PPP_PROTOCOL_IP && ip_payload_len > 0) { + /* 解析IP包 */ + result->has_ip = parse_ip_packet(ip_payload, ip_payload_len, &result->ip_info); + + if (result->has_ip) { + if (result->ip_info.validation == IP_VALID) { + /* 根据IP协议类型解析传输层 */ + if (result->ip_info.protocol == IP_PROTOCOL_TCP && result->ip_info.payload_len > 0) { + result->has_tcp = parse_tcp_packet(result->ip_info.payload, + result->ip_info.payload_len, &result->tcp_info); + result->protocol_name = "TCP"; + } else if (result->ip_info.protocol == IP_PROTOCOL_UDP && result->ip_info.payload_len > 0) { + result->has_udp = parse_udp_packet(result->ip_info.payload, + result->ip_info.payload_len, &result->udp_info); + result->protocol_name = "UDP"; + } else if (result->ip_info.protocol == IP_PROTOCOL_ICMP) { + result->protocol_name = "ICMP"; + } else { + result->protocol_name = "Other IP"; + } + } else { + result->protocol_name = "Invalid IP"; + } + } + } else { + /* 非IP协议 */ + result->protocol_name = ppp_get_protocol_name(protocol); + } + + return true; +} + +/** + * 批量分析PPP帧 + */ +uint32_t analyze_ppp_frames(const ppp_frame_t *frames, uint32_t frame_count, + packet_analysis_result_t *results, packet_stats_t *stats) +{ + if (!frames || !results || !stats || frame_count == 0) { + return 0; + } + + init_packet_stats(stats); + uint32_t success_count = 0; + + for (uint32_t i = 0; i < frame_count; i++) { + if (analyze_ppp_frame(&frames[i], &results[i])) { + success_count++; + stats->total_packets++; + + /* 更新统计信息 */ + if (results[i].has_ip) { + stats->ip_packets++; + + /* 更新IP验证统计 */ + ip_validation_result_t validation = results[i].ip_info.validation; + if (validation < 6) { + stats->ip_validation_counts[validation]++; + } + + if (validation != IP_VALID) { + stats->invalid_ip_packets++; + } + + /* 更新传输层统计 */ + if (results[i].has_tcp) { + stats->tcp_packets++; + } else if (results[i].has_udp) { + stats->udp_packets++; + } else { + stats->other_packets++; + } + } else { + stats->other_packets++; + } + } + } + + return success_count; +} + +/** + * 获取IP验证结果的描述字符串 + */ +const char* get_ip_validation_message(ip_validation_result_t result) +{ + switch (result) { + case IP_VALID: + return "IP数据包验证通过"; + case IP_ERROR_TOO_SHORT: + return "IP数据包长度不足20字节"; + case IP_ERROR_INVALID_VERSION: + return "非IPv4数据包"; + case IP_ERROR_INVALID_HEADER_LEN: + return "无效的IP头部长度"; + case IP_ERROR_LENGTH_MISMATCH: + return "IP数据包长度不匹配"; + case IP_ERROR_CHECKSUM_FAILED: + return "IP校验和错误"; + default: + return "未知错误"; + } +} + +/** + * 格式化IP地址为字符串 + */ +void format_ip_address(const ip_addr_t *ip_addr, char *buffer, size_t buffer_size) +{ + if (!ip_addr || !buffer || buffer_size < 16) { + return; + } + + snprintf(buffer, buffer_size, "%u.%u.%u.%u", + ip_addr->addr[0], ip_addr->addr[1], + ip_addr->addr[2], ip_addr->addr[3]); +} + +/** + * 打印IP包信息 + */ +void print_ip_packet_info(const ip_packet_info_t *ip_info) +{ + if (!ip_info) return; + + char src_ip_str[16], dst_ip_str[16]; + format_ip_address(&ip_info->src_ip, src_ip_str, sizeof(src_ip_str)); + format_ip_address(&ip_info->dst_ip, dst_ip_str, sizeof(dst_ip_str)); + + printf(" IP验证: %s - %s\n", + (ip_info->validation == IP_VALID) ? "通过" : "失败", + ip_info->validation_msg); + + if (ip_info->validation == IP_VALID) { + printf(" IP信息: %s -> %s, 协议=%u, 长度=%u, TTL=%u\n", + src_ip_str, dst_ip_str, ip_info->protocol, + ip_info->total_len, ip_info->ttl); + } +} + +/** + * 打印TCP包信息 + */ +void print_tcp_packet_info(const tcp_packet_info_t *tcp_info) +{ + if (!tcp_info) return; + + printf(" TCP: 端口 %u -> %u, 序列号=%u, 确认号=%u, 标志=0x%02X\n", + tcp_info->src_port, tcp_info->dst_port, + tcp_info->seq_num, tcp_info->ack_num, tcp_info->flags); +} + +/** + * 打印UDP包信息 + */ +void print_udp_packet_info(const udp_packet_info_t *udp_info) +{ + if (!udp_info) return; + + printf(" UDP: 端口 %u -> %u, 长度=%u\n", + udp_info->src_port, udp_info->dst_port, udp_info->length); +} + +/** + * 打印包分析结果 + */ +void print_packet_analysis_result(const packet_analysis_result_t *result, uint32_t packet_number) +{ + if (!result || !result->ppp_frame) return; + + printf("\n=== 包 #%u 分析结果 ===\n", packet_number); + + /* 打印PPP帧基本信息 */ + printf("PPP帧: 协议=0x%04X (%s), 负载长度=%u字节\n", + result->ppp_frame->protocol, result->protocol_name, + result->ppp_frame->payload_len); + + /* 打印IP包信息 */ + if (result->has_ip) { + print_ip_packet_info(&result->ip_info); + + /* 打印传输层信息 */ + if (result->has_tcp) { + print_tcp_packet_info(&result->tcp_info); + } else if (result->has_udp) { + print_udp_packet_info(&result->udp_info); + } + } +} + +/** + * 打印包统计信息 + */ +void print_packet_stats(const packet_stats_t *stats) +{ + if (!stats) return; + + printf("\n=== 包分析统计信息 ===\n"); + printf("总包数: %u\n", stats->total_packets); + printf("IP包数: %u\n", stats->ip_packets); + printf("TCP包数: %u\n", stats->tcp_packets); + printf("UDP包数: %u\n", stats->udp_packets); + printf("其他包数: %u\n", stats->other_packets); + printf("无效IP包数: %u\n", stats->invalid_ip_packets); + + printf("\nIP验证结果统计:\n"); + printf(" 验证通过: %u\n", stats->ip_validation_counts[IP_VALID]); + printf(" 长度不足: %u\n", stats->ip_validation_counts[IP_ERROR_TOO_SHORT]); + printf(" 版本错误: %u\n", stats->ip_validation_counts[IP_ERROR_INVALID_VERSION]); + printf(" 头部长度错误: %u\n", stats->ip_validation_counts[IP_ERROR_INVALID_HEADER_LEN]); + printf(" 长度不匹配: %u\n", stats->ip_validation_counts[IP_ERROR_LENGTH_MISMATCH]); + printf(" 校验和错误: %u\n", stats->ip_validation_counts[IP_ERROR_CHECKSUM_FAILED]); +} + +/** + * 初始化包统计信息 + */ +void init_packet_stats(packet_stats_t *stats) +{ + if (!stats) return; + + memset(stats, 0, sizeof(packet_stats_t)); +} \ No newline at end of file diff --git a/code/sdk/zhihui/ppp/ppp_dec_test/ppp_packet_analyzer.h b/code/sdk/zhihui/ppp/ppp_dec_test/ppp_packet_analyzer.h new file mode 100644 index 000000000..b09b24ede --- /dev/null +++ b/code/sdk/zhihui/ppp/ppp_dec_test/ppp_packet_analyzer.h @@ -0,0 +1,236 @@ +#ifndef PPP_PACKET_ANALYZER_H +#define PPP_PACKET_ANALYZER_H + +#include +#include +#include "ppp_parser.h" + +/* IP协议常量定义 */ +#define IP_VERSION_4 4 /* IPv4版本号 */ +#define IP_MIN_HEADER_SIZE 20 /* IP头部最小长度 */ +#define IP_MAX_HEADER_SIZE 60 /* IP头部最大长度 */ +#define IP_PROTOCOL_TCP 6 /* TCP协议 */ +#define IP_PROTOCOL_UDP 17 /* UDP协议 */ +#define IP_PROTOCOL_ICMP 1 /* ICMP协议 */ + +/* TCP/UDP常量定义 */ +#define TCP_MIN_HEADER_SIZE 20 /* TCP头部最小长度 */ +#define UDP_HEADER_SIZE 8 /* UDP头部固定长度 */ + +/* IP验证结果枚举 */ +typedef enum { + IP_VALID = 0, /* IP包有效 */ + IP_ERROR_TOO_SHORT, /* 包长度不足 */ + IP_ERROR_INVALID_VERSION, /* 版本号错误 */ + IP_ERROR_INVALID_HEADER_LEN, /* 头部长度错误 */ + IP_ERROR_LENGTH_MISMATCH, /* 长度不匹配 */ + IP_ERROR_CHECKSUM_FAILED /* 校验和错误 */ +} ip_validation_result_t; + +/* IP地址结构 */ +typedef struct { + uint8_t addr[4]; /* IP地址字节数组 */ +} ip_addr_t; + +/* IP包信息结构 */ +typedef struct { + uint8_t version; /* IP版本 */ + uint8_t header_len; /* 头部长度(字节) */ + uint8_t tos; /* 服务类型 */ + uint16_t total_len; /* 总长度 */ + uint16_t id; /* 标识符 */ + uint16_t flags_frag; /* 标志和片偏移 */ + uint8_t ttl; /* 生存时间 */ + uint8_t protocol; /* 协议类型 */ + uint16_t checksum; /* 头部校验和 */ + ip_addr_t src_ip; /* 源IP地址 */ + ip_addr_t dst_ip; /* 目标IP地址 */ + + /* 负载信息 */ + const uint8_t *payload; /* 负载数据指针 */ + uint16_t payload_len; /* 负载长度 */ + + /* 验证结果 */ + ip_validation_result_t validation; /* 验证结果 */ + const char *validation_msg; /* 验证消息 */ +} ip_packet_info_t; + +/* TCP包信息结构 */ +typedef struct { + uint16_t src_port; /* 源端口 */ + uint16_t dst_port; /* 目标端口 */ + uint32_t seq_num; /* 序列号 */ + uint32_t ack_num; /* 确认号 */ + uint8_t header_len; /* 头部长度(字节) */ + uint8_t flags; /* 控制标志 */ + uint16_t window; /* 窗口大小 */ + uint16_t checksum; /* 校验和 */ + uint16_t urgent_ptr; /* 紧急指针 */ + + /* 负载信息 */ + const uint8_t *payload; /* 负载数据指针 */ + uint16_t payload_len; /* 负载长度 */ +} tcp_packet_info_t; + +/* UDP包信息结构 */ +typedef struct { + uint16_t src_port; /* 源端口 */ + uint16_t dst_port; /* 目标端口 */ + uint16_t length; /* UDP长度 */ + uint16_t checksum; /* 校验和 */ + + /* 负载信息 */ + const uint8_t *payload; /* 负载数据指针 */ + uint16_t payload_len; /* 负载长度 */ +} udp_packet_info_t; + +/* 包分析结果结构 */ +typedef struct { + /* PPP帧信息 */ + const ppp_frame_t *ppp_frame; /* PPP帧指针 */ + + /* IP包信息 */ + bool has_ip; /* 是否包含IP包 */ + ip_packet_info_t ip_info; /* IP包信息 */ + + /* 传输层信息 */ + bool has_tcp; /* 是否包含TCP包 */ + bool has_udp; /* 是否包含UDP包 */ + tcp_packet_info_t tcp_info; /* TCP包信息 */ + udp_packet_info_t udp_info; /* UDP包信息 */ + + /* 协议名称 */ + const char *protocol_name; /* 协议名称字符串 */ +} packet_analysis_result_t; + +/* 包统计信息结构 */ +typedef struct { + uint32_t total_packets; /* 总包数 */ + uint32_t ip_packets; /* IP包数 */ + uint32_t tcp_packets; /* TCP包数 */ + uint32_t udp_packets; /* UDP包数 */ + uint32_t other_packets; /* 其他包数 */ + uint32_t invalid_ip_packets; /* 无效IP包数 */ + + /* IP验证统计 */ + uint32_t ip_validation_counts[6]; /* 各种验证结果的计数 */ +} packet_stats_t; + +/* 函数声明 */ + +/** + * 计算IP头部校验和 + * @param ip_header IP头部数据 + * @param header_len 头部长度 + * @return 计算出的校验和 + */ +uint16_t calculate_ip_checksum(const uint8_t *ip_header, uint8_t header_len); + +/** + * 验证IP数据包的完整性和正确性 + * @param ip_data IP数据包 + * @param data_len 数据长度 + * @param result 验证结果 + * @return 验证是否通过 + */ +bool verify_ip_packet(const uint8_t *ip_data, uint16_t data_len, ip_validation_result_t *result); + +/** + * 解析IP数据包,提取基本信息 + * @param ip_data IP数据包 + * @param data_len 数据长度 + * @param ip_info 输出的IP包信息 + * @return 解析是否成功 + */ +bool parse_ip_packet(const uint8_t *ip_data, uint16_t data_len, ip_packet_info_t *ip_info); + +/** + * 解析TCP数据包,提取端口和基本信息 + * @param tcp_data TCP数据包 + * @param data_len 数据长度 + * @param tcp_info 输出的TCP包信息 + * @return 解析是否成功 + */ +bool parse_tcp_packet(const uint8_t *tcp_data, uint16_t data_len, tcp_packet_info_t *tcp_info); + +/** + * 解析UDP数据包,提取端口和基本信息 + * @param udp_data UDP数据包 + * @param data_len 数据长度 + * @param udp_info 输出的UDP包信息 + * @return 解析是否成功 + */ +bool parse_udp_packet(const uint8_t *udp_data, uint16_t data_len, udp_packet_info_t *udp_info); + +/** + * 分析PPP帧内容,解析其中的IP/TCP/UDP包 + * @param frame PPP帧 + * @param result 分析结果 + * @return 分析是否成功 + */ +bool analyze_ppp_frame(const ppp_frame_t *frame, packet_analysis_result_t *result); + +/** + * 批量分析PPP帧 + * @param frames PPP帧数组 + * @param frame_count 帧数量 + * @param results 分析结果数组 + * @param stats 统计信息 + * @return 成功分析的帧数量 + */ +uint32_t analyze_ppp_frames(const ppp_frame_t *frames, uint32_t frame_count, + packet_analysis_result_t *results, packet_stats_t *stats); + +/** + * 获取IP验证结果的描述字符串 + * @param result 验证结果 + * @return 描述字符串 + */ +const char* get_ip_validation_message(ip_validation_result_t result); + +/** + * 格式化IP地址为字符串 + * @param ip_addr IP地址结构 + * @param buffer 输出缓冲区 + * @param buffer_size 缓冲区大小 + */ +void format_ip_address(const ip_addr_t *ip_addr, char *buffer, size_t buffer_size); + +/** + * 打印IP包信息 + * @param ip_info IP包信息 + */ +void print_ip_packet_info(const ip_packet_info_t *ip_info); + +/** + * 打印TCP包信息 + * @param tcp_info TCP包信息 + */ +void print_tcp_packet_info(const tcp_packet_info_t *tcp_info); + +/** + * 打印UDP包信息 + * @param udp_info UDP包信息 + */ +void print_udp_packet_info(const udp_packet_info_t *udp_info); + +/** + * 打印包分析结果 + * @param result 分析结果 + * @param packet_number 包序号 + */ +void print_packet_analysis_result(const packet_analysis_result_t *result, uint32_t packet_number); + +/** + * 打印包统计信息 + * @param stats 统计信息 + */ +void print_packet_stats(const packet_stats_t *stats); + +/** + * 初始化包统计信息 + * @param stats 统计信息结构 + */ +void init_packet_stats(packet_stats_t *stats); + +#endif /* PPP_PACKET_ANALYZER_H */ \ No newline at end of file diff --git a/code/sdk/zhihui/ppp/ppp_dec_test/ppp_parser.c b/code/sdk/zhihui/ppp/ppp_dec_test/ppp_parser.c new file mode 100644 index 000000000..a2f3c54ee --- /dev/null +++ b/code/sdk/zhihui/ppp/ppp_dec_test/ppp_parser.c @@ -0,0 +1,383 @@ +#include "ppp_parser.h" +#include +#include +#include + +/* 内部辅助函数声明 */ +static bool ppp_parse_buffer(ppp_parser_t *parser, ppp_frame_t *frame); +static void ppp_record_dropped_data(ppp_parser_t *parser, ppp_drop_reason_t reason, + const uint8_t *data, uint16_t data_len); +static const char* ppp_get_drop_reason_string(ppp_drop_reason_t reason); + +/** + * 初始化PPP解析器 + */ +void ppp_parser_init(ppp_parser_t *parser) +{ + if (!parser) return; + + memset(parser, 0, sizeof(ppp_parser_t)); + parser->in_frame = false; + parser->buffer_len = 0; + parser->current_position = 0; + parser->dropped_data_head = 0; + parser->dropped_data_tail = 0; +} + +/** + * 重置PPP解析器状态 + */ +void ppp_parser_reset(ppp_parser_t *parser) +{ + if (!parser) return; + + parser->buffer_len = 0; + parser->in_frame = false; + /* 保留统计信息,只重置解析状态 */ +} + +/** + * 处理单个字节,返回是否解析出完整PPP帧 + */ +bool ppp_parser_process_byte(ppp_parser_t *parser, uint8_t byte, ppp_frame_t *frame) +{ + if (!parser || !frame) return false; + + parser->total_bytes_processed++; + parser->current_position++; + + if (byte == PPP_FLAG_BYTE) { + /* PPP帧标志位 */ + if (parser->in_frame && parser->buffer_len > 0) { + /* 帧结束:添加结束标志并处理缓冲区数据 */ + if (parser->buffer_len < PPP_MAX_BUFFER_SIZE) { + parser->buffer[parser->buffer_len++] = byte; /* 添加结束0x7E */ + } + bool result = ppp_parse_buffer(parser, frame); + parser->buffer_len = 0; + parser->in_frame = false; + return result; + } else { + /* 帧开始:初始化缓冲区并添加开始标志 */ + if (parser->in_frame && parser->buffer_len > 0) { + /* 记录丢弃的数据 */ + ppp_record_dropped_data(parser, PPP_DROP_REASON_FRAME_SYNC_LOST, + parser->buffer, parser->buffer_len); + } + parser->in_frame = true; + parser->buffer_len = 0; + parser->buffer[parser->buffer_len++] = byte; /* 添加开始0x7E */ + } + } else { + if (parser->in_frame) { + /* 检查缓冲区溢出 */ + if (parser->buffer_len >= PPP_MAX_BUFFER_SIZE) { + ppp_record_dropped_data(parser, PPP_DROP_REASON_BUFFER_OVERFLOW, + parser->buffer, parser->buffer_len); + parser->buffer_len = 0; + parser->in_frame = false; + return false; + } + parser->buffer[parser->buffer_len++] = byte; + } + } + + return false; +} + +/** + * 批量处理字节数据 + */ +uint32_t ppp_parser_process_data(ppp_parser_t *parser, const uint8_t *data, + uint32_t data_len, ppp_frame_t *frames, uint32_t max_frames) +{ + if (!parser || !data || !frames || max_frames == 0) return 0; + + uint32_t frame_count = 0; + + for (uint32_t i = 0; i < data_len && frame_count < max_frames; i++) { + if (ppp_parser_process_byte(parser, data[i], &frames[frame_count])) { + frame_count++; + } + } + + return frame_count; +} + +/** + * 解析缓冲区数据,保留完整PPP帧(包含头尾0x7E),不处理转义字符 + */ +static bool ppp_parse_buffer(ppp_parser_t *parser, ppp_frame_t *frame) +{ + if (!parser || !frame || parser->buffer_len == 0) return false; + + /* 校验帧长度(最小:0x7E + 地址1 + 控制1 + 协议2 + 0x7E = 6字节) */ + if (parser->buffer_len < (PPP_MIN_FRAME_SIZE + 2)) { + ppp_record_dropped_data(parser, PPP_DROP_REASON_FRAME_TOO_SHORT, + parser->buffer, parser->buffer_len); + return false; + } + + /* 检查帧长度是否超过最大限制 */ + if (parser->buffer_len > (PPP_MAX_FRAME_SIZE + 2)) { + ppp_record_dropped_data(parser, PPP_DROP_REASON_FRAME_TOO_LONG, + parser->buffer, parser->buffer_len); + return false; + } + + /* 验证帧头尾标志 */ + if (parser->buffer[0] != PPP_FLAG_BYTE || + parser->buffer[parser->buffer_len - 1] != PPP_FLAG_BYTE) { + ppp_record_dropped_data(parser, PPP_DROP_REASON_FRAME_SYNC_LOST, + parser->buffer, parser->buffer_len); + return false; + } + + /* 复制完整PPP帧数据(包含头尾0x7E和转义字符) */ + frame->raw_frame_len = parser->buffer_len; + memcpy(frame->raw_frame, parser->buffer, parser->buffer_len); + + /* 暂时将其他字段设为0,由analyzer处理转义后再解析 */ + frame->address = 0; + frame->control = 0; + frame->protocol = 0; + frame->payload_len = 0; + + parser->total_frames_parsed++; + return true; +} + +/** + * 记录丢弃的数据 + */ +static void ppp_record_dropped_data(ppp_parser_t *parser, ppp_drop_reason_t reason, + const uint8_t *data, uint16_t data_len) +{ + if (!parser || !data) return; + + /* 计算下一个写入位置 */ + uint8_t next_tail = (parser->dropped_data_tail + 1) % 32; + + /* 如果缓冲区满了,覆盖最旧的数据 */ + if (next_tail == parser->dropped_data_head) { + parser->dropped_data_head = (parser->dropped_data_head + 1) % 32; + } + + /* 记录丢弃数据 */ + ppp_dropped_data_t *dropped = &parser->dropped_data[parser->dropped_data_tail]; + dropped->reason = reason; + dropped->position = parser->current_position; + dropped->data_len = (data_len > 256) ? 256 : data_len; + memcpy(dropped->data, data, dropped->data_len); + + parser->dropped_data_tail = next_tail; + parser->dropped_data_count++; +} + +/** + * 对PPP帧进行组包处理 + */ +void ppp_classify_frames(const ppp_frame_t *frames, uint32_t frame_count, ppp_group_result_t *result) +{ + if (!frames || !result || frame_count == 0) return; + + memset(result, 0, sizeof(ppp_group_result_t)); + result->total_frames = frame_count; + + for (uint32_t i = 0; i < frame_count; i++) { + const ppp_frame_t *frame = &frames[i]; + + /* 查找是否已存在该协议的统计 */ + int protocol_index = -1; + for (int j = 0; j < result->protocol_count; j++) { + if (result->protocols[j].protocol == frame->protocol) { + protocol_index = j; + break; + } + } + + if (protocol_index >= 0) { + /* 更新现有协议统计 */ + result->protocols[protocol_index].frame_count++; + } else if (result->protocol_count < 16) { + /* 添加新协议统计 */ + protocol_index = result->protocol_count++; + result->protocols[protocol_index].protocol = frame->protocol; + result->protocols[protocol_index].frame_count = 1; + result->protocols[protocol_index].protocol_name = ppp_get_protocol_name(frame->protocol); + } else { + /* 协议种类超过限制,计入无效帧 */ + result->invalid_frames++; + } + } +} + +/** + * 获取协议名称 + */ +const char* ppp_get_protocol_name(uint16_t protocol) +{ + switch (protocol) { + case PPP_PROTOCOL_IP: + return "IP (IPv4)"; + case PPP_PROTOCOL_LCP: + return "LCP (链路控制协议)"; + case PPP_PROTOCOL_IPCP: + return "IPCP (IP控制协议)"; + default: + return "未知协议"; + } +} + +/** + * 获取丢弃原因字符串 + */ +static const char* ppp_get_drop_reason_string(ppp_drop_reason_t reason) +{ + switch (reason) { + case PPP_DROP_REASON_FRAME_SYNC_LOST: + return "帧同步丢失或格式错误"; + case PPP_DROP_REASON_INCOMPLETE_ESCAPE: + return "不完整的转义序列"; + case PPP_DROP_REASON_FRAME_TOO_SHORT: + return "帧长度不足4字节"; + case PPP_DROP_REASON_FRAME_TOO_LONG: + return "帧长度过长"; + case PPP_DROP_REASON_BUFFER_OVERFLOW: + return "缓冲区溢出"; + default: + return "未知原因"; + } +} + +/** + * 打印PPP解析器统计信息 + */ +void ppp_parser_print_stats(const ppp_parser_t *parser) +{ + if (!parser) return; + + printf("\nPPP解析器统计信息:\n"); + printf(" 处理的总字节数: %u\n", parser->total_bytes_processed); + printf(" 解析的总帧数: %u\n", parser->total_frames_parsed); + printf(" 丢弃数据次数: %u\n", parser->dropped_data_count); +} + +/** + * 打印丢弃数据信息 + */ +void ppp_parser_print_dropped_data(const ppp_parser_t *parser) +{ + if (!parser) return; + + printf("\n丢弃的数据信息:\n"); + + if (parser->dropped_data_count == 0) { + printf(" 没有丢弃的数据\n"); + return; + } + + printf(" 丢弃的数据总数: %u\n", parser->dropped_data_count); + + /* 计算要显示的数据数量(最多5条) */ + uint8_t count = 0; + uint8_t current = parser->dropped_data_head; + + while (current != parser->dropped_data_tail && count < 5) { + const ppp_dropped_data_t *dropped = &parser->dropped_data[current]; + + printf(" 丢弃数据 #%d:\n", count + 1); + printf(" 原因: %s\n", ppp_get_drop_reason_string(dropped->reason)); + printf(" 位置: %u\n", dropped->position); + printf(" 数据: "); + for (uint16_t i = 0; i < dropped->data_len && i < 32; i++) { + printf("%02X", dropped->data[i]); + } + if (dropped->data_len > 32) { + printf("..."); + } + printf("\n"); + + current = (current + 1) % 32; + count++; + } + + if (parser->dropped_data_count > 5) { + printf(" ... 还有%u条丢弃的数据未显示\n", parser->dropped_data_count - 5); + } +} + +/** + * 打印PPP帧信息 + */ +void ppp_print_frame_info(const ppp_frame_t *frame, uint32_t frame_number) +{ + if (!frame) return; + + printf("PPP帧 #%u: 完整长度=%u字节 (包含头尾0x7E)\n", frame_number, frame->raw_frame_len); + + /* 显示完整PPP帧数据 */ + printf(" 完整帧数据: "); + for (uint16_t i = 0; i < frame->raw_frame_len && i < 32; i++) { + printf("%02X", frame->raw_frame[i]); + } + if (frame->raw_frame_len > 32) { + printf("..."); + } + printf("\n"); + + /* 如果已经解析过,显示解析后的信息 */ + if (frame->payload_len > 0) { + printf(" 协议: 0x%04X, 地址: 0x%02X, 控制: 0x%02X\n", + frame->protocol, frame->address, frame->control); + printf(" 处理后负载长度: %u字节 (已处理转义字符)\n", frame->payload_len); + + /* 显示处理后的负载数据(最多显示前32字节) */ + printf(" 处理后负载: "); + uint16_t print_len = (frame->payload_len > 32) ? 32 : frame->payload_len; + for (uint16_t i = 0; i < print_len; i++) { + printf("%02X", frame->payload[i]); + } + if (frame->payload_len > 32) { + printf("..."); + } + printf("\n"); + } else { + printf(" (未解析 - 需要通过analyzer处理转义字符)\n"); + } +} + +/** + * 打印组包结果 + */ +void ppp_print_group_result(const ppp_group_result_t *result) +{ + if (!result) return; + + printf("\nPPP帧组包结果:\n"); + printf(" 总帧数: %u\n", result->total_frames); + printf(" 协议种类: %u\n", result->protocol_count); + + for (uint8_t i = 0; i < result->protocol_count; i++) { + const ppp_protocol_stats_t *stats = &result->protocols[i]; + printf(" 0x%04X (%s): %u个帧\n", + stats->protocol, stats->protocol_name, stats->frame_count); + } + + if (result->invalid_frames > 0) { + printf(" 无效帧: %u个\n", result->invalid_frames); + } + + /* 验证组包结果 */ + uint32_t total_grouped = result->invalid_frames; + for (uint8_t i = 0; i < result->protocol_count; i++) { + total_grouped += result->protocols[i].frame_count; + } + + if (total_grouped == result->total_frames) { + printf(" 组包验证: 成功,所有%u个帧都已正确分组\n", total_grouped); + } else { + printf(" 组包验证: 警告,分组总数(%u)与原始帧总数(%u)不匹配\n", + total_grouped, result->total_frames); + } +} \ No newline at end of file diff --git a/code/sdk/zhihui/ppp/ppp_dec_test/ppp_parser.h b/code/sdk/zhihui/ppp/ppp_dec_test/ppp_parser.h new file mode 100644 index 000000000..0453da52e --- /dev/null +++ b/code/sdk/zhihui/ppp/ppp_dec_test/ppp_parser.h @@ -0,0 +1,158 @@ +#ifndef PPP_PARSER_H +#define PPP_PARSER_H + +#include +#include +#include + +/* PPP协议常量定义 */ +#define PPP_FLAG_BYTE 0x7E /* PPP帧标志位 */ +#define PPP_ESCAPE_BYTE 0x7D /* PPP转义字符 */ +#define PPP_ESCAPE_XOR 0x20 /* PPP转义异或值 */ +#define PPP_MIN_FRAME_SIZE 4 /* PPP帧最小长度(地址+控制+协议) */ +#define PPP_MAX_FRAME_SIZE 1500 /* PPP帧最大长度 */ +#define PPP_MAX_BUFFER_SIZE 2048 /* 解析缓冲区最大长度 */ + +/* PPP协议类型定义 */ +#define PPP_PROTOCOL_IP 0x0021 /* IPv4协议 */ +#define PPP_PROTOCOL_LCP 0xC021 /* 链路控制协议 */ +#define PPP_PROTOCOL_IPCP 0x8021 /* IP控制协议 */ + +/* 丢弃数据原因枚举 */ +typedef enum { + PPP_DROP_REASON_NONE = 0, + PPP_DROP_REASON_FRAME_SYNC_LOST, /* 帧同步丢失 */ + PPP_DROP_REASON_INCOMPLETE_ESCAPE, /* 不完整的转义序列 */ + PPP_DROP_REASON_FRAME_TOO_SHORT, /* 帧长度不足 */ + PPP_DROP_REASON_FRAME_TOO_LONG, /* 帧长度过长 */ + PPP_DROP_REASON_BUFFER_OVERFLOW /* 缓冲区溢出 */ +} ppp_drop_reason_t; + +/* 丢弃数据记录结构 */ +typedef struct { + ppp_drop_reason_t reason; /* 丢弃原因 */ + uint8_t data[256]; /* 丢弃的数据(最多256字节) */ + uint16_t data_len; /* 丢弃数据长度 */ + uint32_t position; /* 丢弃位置 */ +} ppp_dropped_data_t; + +/* PPP帧结构 */ +typedef struct { + uint8_t address; /* 地址字段 */ + uint8_t control; /* 控制字段 */ + uint16_t protocol; /* 协议字段 */ + uint8_t raw_frame[PPP_MAX_FRAME_SIZE + 2]; /* 完整PPP帧数据(包含头尾0x7E) */ + uint16_t raw_frame_len; /* 完整帧长度 */ + uint8_t payload[PPP_MAX_FRAME_SIZE]; /* 负载数据(处理转义后) */ + uint16_t payload_len; /* 负载长度 */ +} ppp_frame_t; + +/* PPP帧解析器状态结构 */ +typedef struct { + uint8_t buffer[PPP_MAX_BUFFER_SIZE]; /* 帧数据缓冲区 */ + uint16_t buffer_len; /* 缓冲区当前长度 */ + bool in_frame; /* 是否处于帧接收状态 */ + uint32_t current_position; /* 当前处理位置 */ + + /* 统计信息 */ + uint32_t total_frames_parsed; /* 解析的总帧数 */ + uint32_t total_bytes_processed; /* 处理的总字节数 */ + uint32_t dropped_data_count; /* 丢弃数据次数 */ + + /* 丢弃数据记录(环形缓冲区) */ + ppp_dropped_data_t dropped_data[32]; /* 最多记录32条丢弃数据 */ + uint8_t dropped_data_head; /* 丢弃数据头指针 */ + uint8_t dropped_data_tail; /* 丢弃数据尾指针 */ +} ppp_parser_t; + +/* PPP协议统计结构 */ +typedef struct { + uint16_t protocol; /* 协议类型 */ + uint32_t frame_count; /* 帧数量 */ + const char *protocol_name; /* 协议名称 */ +} ppp_protocol_stats_t; + +/* PPP帧组包结果结构 */ +typedef struct { + ppp_protocol_stats_t protocols[16]; /* 最多支持16种协议统计 */ + uint8_t protocol_count; /* 协议种类数量 */ + uint32_t total_frames; /* 总帧数 */ + uint32_t invalid_frames; /* 无效帧数 */ +} ppp_group_result_t; + +/* 函数声明 */ + +/** + * 初始化PPP解析器 + * @param parser PPP解析器指针 + */ +void ppp_parser_init(ppp_parser_t *parser); + +/** + * 重置PPP解析器状态 + * @param parser PPP解析器指针 + */ +void ppp_parser_reset(ppp_parser_t *parser); + +/** + * 处理单个字节,返回是否解析出完整PPP帧 + * @param parser PPP解析器指针 + * @param byte 输入字节 + * @param frame 输出的PPP帧指针 + * @return true表示解析出完整帧,false表示需要更多数据 + */ +bool ppp_parser_process_byte(ppp_parser_t *parser, uint8_t byte, ppp_frame_t *frame); + +/** + * 批量处理字节数据 + * @param parser PPP解析器指针 + * @param data 输入数据指针 + * @param data_len 输入数据长度 + * @param frames 输出帧数组 + * @param max_frames 最大帧数量 + * @return 实际解析出的帧数量 + */ +uint32_t ppp_parser_process_data(ppp_parser_t *parser, const uint8_t *data, + uint32_t data_len, ppp_frame_t *frames, uint32_t max_frames); + +/** + * 对PPP帧进行组包处理 + * @param frames PPP帧数组 + * @param frame_count 帧数量 + * @param result 组包结果 + */ +void ppp_classify_frames(const ppp_frame_t *frames, uint32_t frame_count, ppp_group_result_t *result); + +/** + * 获取协议名称 + * @param protocol 协议类型 + * @return 协议名称字符串 + */ +const char* ppp_get_protocol_name(uint16_t protocol); + +/** + * 打印PPP解析器统计信息 + * @param parser PPP解析器指针 + */ +void ppp_parser_print_stats(const ppp_parser_t *parser); + +/** + * 打印丢弃数据信息 + * @param parser PPP解析器指针 + */ +void ppp_parser_print_dropped_data(const ppp_parser_t *parser); + +/** + * 打印PPP帧信息 + * @param frame PPP帧指针 + * @param frame_number 帧序号 + */ +void ppp_print_frame_info(const ppp_frame_t *frame, uint32_t frame_number); + +/** + * 打印组包结果 + * @param result 组包结果指针 + */ +void ppp_print_group_result(const ppp_group_result_t *result); + +#endif /* PPP_PARSER_H */ \ No newline at end of file diff --git a/code/sdk/zhihui/ppp/ppp_dec_test/ppp_parser_configurable.h b/code/sdk/zhihui/ppp/ppp_dec_test/ppp_parser_configurable.h new file mode 100644 index 000000000..79450da31 --- /dev/null +++ b/code/sdk/zhihui/ppp/ppp_dec_test/ppp_parser_configurable.h @@ -0,0 +1,158 @@ +#ifndef PPP_PARSER_CONFIGURABLE_H +#define PPP_PARSER_CONFIGURABLE_H + +#include +#include +#include + +/* PPP协议常量定义 */ +#define PPP_FLAG_BYTE 0x7E /* PPP帧标志位 */ +#define PPP_ESCAPE_BYTE 0x7D /* PPP转义字符 */ +#define PPP_ESCAPE_XOR 0x20 /* PPP转义异或值 */ +#define PPP_MIN_FRAME_SIZE 4 /* PPP帧最小长度(地址+控制+协议) */ + +/* 可配置的缓冲区大小 */ +#ifndef PPP_MAX_BUFFER_SIZE +#define PPP_MAX_BUFFER_SIZE 2048 /* 默认解析缓冲区最大长度 */ +#endif + +#ifndef PPP_MAX_FRAME_SIZE +#define PPP_MAX_FRAME_SIZE 1500 /* 默认PPP帧最大长度 */ +#endif + +/* 编译时检查 */ +#if PPP_MAX_BUFFER_SIZE < PPP_MIN_FRAME_SIZE +#error "PPP_MAX_BUFFER_SIZE must be at least PPP_MIN_FRAME_SIZE" +#endif + +#if PPP_MAX_FRAME_SIZE > PPP_MAX_BUFFER_SIZE +#error "PPP_MAX_FRAME_SIZE cannot be larger than PPP_MAX_BUFFER_SIZE" +#endif + +/* 运行时配置结构 */ +typedef struct { + uint32_t max_buffer_size; /* 最大缓冲区大小 */ + uint32_t max_frame_size; /* 最大帧大小 */ + bool enable_size_warnings; /* 启用大小警告 */ + bool auto_drop_oversized; /* 自动丢弃超大帧 */ +} ppp_parser_config_t; + +/* PPP帧结构(可配置版本) */ +typedef struct { + uint8_t address; /* 地址字段 */ + uint8_t control; /* 控制字段 */ + uint16_t protocol; /* 协议字段 */ + uint8_t *payload; /* 负载数据指针 */ + uint32_t payload_len; /* 负载长度 */ + uint32_t payload_capacity; /* 负载容量 */ +} ppp_frame_configurable_t; + +/* PPP解析器状态结构(可配置版本) */ +typedef struct { + /* 配置信息 */ + ppp_parser_config_t config; + + /* 缓冲区 */ + uint8_t *buffer; /* 动态分配的缓冲区 */ + uint32_t buffer_len; /* 缓冲区当前长度 */ + uint32_t buffer_capacity; /* 缓冲区容量 */ + bool in_frame; /* 是否处于帧接收状态 */ + uint32_t current_position; /* 当前处理位置 */ + + /* 统计信息 */ + uint32_t total_frames_parsed; /* 解析的总帧数 */ + uint32_t total_bytes_processed; /* 处理的总字节数 */ + uint32_t dropped_data_count; /* 丢弃数据次数 */ + uint32_t oversized_frames_dropped; /* 丢弃的超大帧数 */ + uint32_t max_frame_size_seen; /* 见过的最大帧大小 */ +} ppp_parser_configurable_t; + +/* 默认配置 */ +extern const ppp_parser_config_t PPP_DEFAULT_CONFIG; + +/* 大缓冲区配置(用于处理大帧) */ +extern const ppp_parser_config_t PPP_LARGE_BUFFER_CONFIG; + +/* 嵌入式配置(内存受限) */ +extern const ppp_parser_config_t PPP_EMBEDDED_CONFIG; + +/* 函数声明 */ + +/** + * 创建配置 + * @param max_buffer_size 最大缓冲区大小 + * @param max_frame_size 最大帧大小 + * @param enable_warnings 启用警告 + * @param auto_drop 自动丢弃超大帧 + * @return 配置结构 + */ +ppp_parser_config_t ppp_create_config(uint32_t max_buffer_size, uint32_t max_frame_size, + bool enable_warnings, bool auto_drop); + +/** + * 初始化可配置PPP解析器 + * @param parser PPP解析器指针 + * @param config 配置指针(NULL使用默认配置) + * @return 成功返回true + */ +bool ppp_parser_configurable_init(ppp_parser_configurable_t *parser, + const ppp_parser_config_t *config); + +/** + * 释放可配置PPP解析器 + * @param parser PPP解析器指针 + */ +void ppp_parser_configurable_free(ppp_parser_configurable_t *parser); + +/** + * 处理单个字节(可配置版本) + * @param parser PPP解析器指针 + * @param byte 输入字节 + * @param frame 输出的PPP帧指针 + * @return true表示解析出完整帧 + */ +bool ppp_parser_configurable_process_byte(ppp_parser_configurable_t *parser, uint8_t byte, + ppp_frame_configurable_t *frame); + +/** + * 批量处理字节数据(可配置版本) + * @param parser PPP解析器指针 + * @param data 输入数据指针 + * @param data_len 输入数据长度 + * @param frames 输出帧数组 + * @param max_frames 最大帧数量 + * @return 实际解析出的帧数量 + */ +uint32_t ppp_parser_configurable_process_data(ppp_parser_configurable_t *parser, + const uint8_t *data, uint32_t data_len, + ppp_frame_configurable_t *frames, + uint32_t max_frames); + +/** + * 释放PPP帧资源(可配置版本) + * @param frame PPP帧指针 + */ +void ppp_frame_configurable_free(ppp_frame_configurable_t *frame); + +/** + * 打印配置信息 + * @param config 配置指针 + */ +void ppp_print_config(const ppp_parser_config_t *config); + +/** + * 打印解析器统计信息(可配置版本) + * @param parser 解析器指针 + */ +void ppp_parser_configurable_print_stats(const ppp_parser_configurable_t *parser); + +/* 便利宏定义 */ +#define PPP_CONFIG_DEFAULT() (&PPP_DEFAULT_CONFIG) +#define PPP_CONFIG_LARGE_BUFFER() (&PPP_LARGE_BUFFER_CONFIG) +#define PPP_CONFIG_EMBEDDED() (&PPP_EMBEDDED_CONFIG) + +/* 自定义配置宏 */ +#define PPP_CONFIG_CUSTOM(buf_size, frame_size, warnings, auto_drop) \ + ppp_create_config(buf_size, frame_size, warnings, auto_drop) + +#endif /* PPP_PARSER_CONFIGURABLE_H */ \ No newline at end of file diff --git a/code/sdk/zhihui/ppp/ppp_dec_test/ppp_parser_dynamic.c b/code/sdk/zhihui/ppp/ppp_dec_test/ppp_parser_dynamic.c new file mode 100644 index 000000000..f7cae640b --- /dev/null +++ b/code/sdk/zhihui/ppp/ppp_dec_test/ppp_parser_dynamic.c @@ -0,0 +1,272 @@ +#include "ppp_parser_dynamic.h" +#include +#include +#include + +/** + * 初始化动态缓冲区 + */ +bool ppp_dynamic_buffer_init(ppp_dynamic_buffer_t *buffer, uint32_t initial_size) +{ + if (!buffer) return false; + + memset(buffer, 0, sizeof(ppp_dynamic_buffer_t)); + + if (initial_size == 0) { + initial_size = PPP_INITIAL_BUFFER_SIZE; + } + + buffer->data = malloc(initial_size); + if (!buffer->data) { + return false; + } + + buffer->capacity = initial_size; + buffer->len = 0; + buffer->size = initial_size; + + return true; +} + +/** + * 释放动态缓冲区 + */ +void ppp_dynamic_buffer_free(ppp_dynamic_buffer_t *buffer) +{ + if (!buffer) return; + + if (buffer->data) { + free(buffer->data); + buffer->data = NULL; + } + + buffer->capacity = 0; + buffer->len = 0; + buffer->size = 0; +} + +/** + * 确保缓冲区有足够空间 + */ +bool ppp_dynamic_buffer_ensure_space(ppp_dynamic_buffer_t *buffer, uint32_t required_size) +{ + if (!buffer) return false; + + if (buffer->capacity >= required_size) { + return true; /* 已有足够空间 */ + } + + /* 检查是否超过最大限制 */ + if (required_size > PPP_MAX_BUFFER_SIZE) { + printf("错误: 请求的缓冲区大小(%u)超过最大限制(%u)\n", + required_size, PPP_MAX_BUFFER_SIZE); + return false; + } + + /* 计算新的容量(按步长增长) */ + uint32_t new_capacity = buffer->capacity; + while (new_capacity < required_size) { + new_capacity += PPP_BUFFER_GROW_STEP; + if (new_capacity > PPP_MAX_BUFFER_SIZE) { + new_capacity = PPP_MAX_BUFFER_SIZE; + break; + } + } + + /* 重新分配内存 */ + uint8_t *new_data = realloc(buffer->data, new_capacity); + if (!new_data) { + printf("错误: 无法重新分配缓冲区内存(大小: %u)\n", new_capacity); + return false; + } + + buffer->data = new_data; + buffer->capacity = new_capacity; + + printf("缓冲区扩展: %u -> %u 字节\n", buffer->size, new_capacity); + buffer->size = new_capacity; + + return true; +} + +/** + * 向缓冲区添加数据 + */ +bool ppp_dynamic_buffer_append(ppp_dynamic_buffer_t *buffer, const uint8_t *data, uint32_t len) +{ + if (!buffer || !data || len == 0) return false; + + if (!ppp_dynamic_buffer_ensure_space(buffer, buffer->len + len)) { + return false; + } + + memcpy(buffer->data + buffer->len, data, len); + buffer->len += len; + + return true; +} + +/** + * 向缓冲区添加单个字节 + */ +bool ppp_dynamic_buffer_append_byte(ppp_dynamic_buffer_t *buffer, uint8_t byte) +{ + return ppp_dynamic_buffer_append(buffer, &byte, 1); +} + +/** + * 清空缓冲区(不释放内存) + */ +void ppp_dynamic_buffer_clear(ppp_dynamic_buffer_t *buffer) +{ + if (!buffer) return; + buffer->len = 0; +} + +/** + * 初始化动态PPP解析器 + */ +bool ppp_parser_dynamic_init(ppp_parser_dynamic_t *parser) +{ + if (!parser) return false; + + memset(parser, 0, sizeof(ppp_parser_dynamic_t)); + + if (!ppp_dynamic_buffer_init(&parser->buffer, PPP_INITIAL_BUFFER_SIZE)) { + return false; + } + + parser->in_frame = false; + parser->current_position = 0; + + return true; +} + +/** + * 释放动态PPP解析器 + */ +void ppp_parser_dynamic_free(ppp_parser_dynamic_t *parser) +{ + if (!parser) return; + + ppp_dynamic_buffer_free(&parser->buffer); + memset(parser, 0, sizeof(ppp_parser_dynamic_t)); +} + +/** + * 处理单个字节(动态版本) + */ +bool ppp_parser_dynamic_process_byte(ppp_parser_dynamic_t *parser, uint8_t byte, + ppp_frame_dynamic_t *frame) +{ + if (!parser || !frame) return false; + + parser->total_bytes_processed++; + parser->current_position++; + + if (byte == PPP_FLAG_BYTE) { + /* PPP帧标志位 */ + if (parser->in_frame && parser->buffer.len > 0) { + /* 帧结束:处理缓冲区数据 */ + bool result = ppp_parse_buffer_dynamic(parser, frame); + ppp_dynamic_buffer_clear(&parser->buffer); + parser->in_frame = false; + return result; + } else { + /* 帧开始:初始化缓冲区 */ + if (parser->in_frame && parser->buffer.len > 0) { + /* 记录丢弃的数据 */ + parser->dropped_data_count++; + printf("警告: 丢弃未完成的帧数据(%u字节)\n", parser->buffer.len); + } + parser->in_frame = true; + ppp_dynamic_buffer_clear(&parser->buffer); + } + } else { + if (parser->in_frame) { + /* 添加字节到缓冲区 */ + if (!ppp_dynamic_buffer_append_byte(&parser->buffer, byte)) { + /* 缓冲区添加失败,可能是内存不足 */ + printf("错误: 无法添加字节到缓冲区,丢弃当前帧\n"); + parser->dropped_data_count++; + parser->in_frame = false; + ppp_dynamic_buffer_clear(&parser->buffer); + return false; + } + } + } + + return false; +} + +/** + * 解析缓冲区数据(动态版本) + */ +static bool ppp_parse_buffer_dynamic(ppp_parser_dynamic_t *parser, ppp_frame_dynamic_t *frame) +{ + if (!parser || !frame || parser->buffer.len == 0) return false; + + /* 校验帧长度 */ + if (parser->buffer.len < PPP_MIN_FRAME_SIZE) { + printf("警告: 帧长度不足(%u字节)\n", parser->buffer.len); + parser->dropped_data_count++; + return false; + } + + /* 更新最大帧大小统计 */ + if (parser->buffer.len > parser->max_frame_size_seen) { + parser->max_frame_size_seen = parser->buffer.len; + printf("发现更大的帧: %u字节\n", parser->buffer.len); + } + + /* 初始化帧结构 */ + memset(frame, 0, sizeof(ppp_frame_dynamic_t)); + + /* 初始化动态负载缓冲区 */ + if (!ppp_dynamic_buffer_init(&frame->payload, parser->buffer.len)) { + printf("错误: 无法为帧负载分配内存\n"); + return false; + } + + /* 复制原始帧数据 */ + if (!ppp_dynamic_buffer_append(&frame->payload, parser->buffer.data, parser->buffer.len)) { + printf("错误: 无法复制帧数据\n"); + ppp_dynamic_buffer_free(&frame->payload); + return false; + } + + /* 暂时将协议字段设为0,由analyzer处理 */ + frame->address = 0; + frame->control = 0; + frame->protocol = 0; + + parser->total_frames_parsed++; + return true; +} + +/** + * 释放PPP帧资源 + */ +void ppp_frame_dynamic_free(ppp_frame_dynamic_t *frame) +{ + if (!frame) return; + + ppp_dynamic_buffer_free(&frame->payload); + memset(frame, 0, sizeof(ppp_frame_dynamic_t)); +} + +/** + * 打印动态解析器统计信息 + */ +void ppp_parser_dynamic_print_stats(const ppp_parser_dynamic_t *parser) +{ + if (!parser) return; + + printf("\n动态PPP解析器统计信息:\n"); + printf(" 处理的总字节数: %u\n", parser->total_bytes_processed); + printf(" 解析的总帧数: %u\n", parser->total_frames_parsed); + printf(" 丢弃数据次数: %u\n", parser->dropped_data_count); + printf(" 缓冲区重分配次数: %u\n", parser->buffer_reallocations); + printf(" 最大帧大小: %u字节\n", parser->max_frame_size_seen); + printf(" 当前缓冲区容量: %u字节\n", parser->buffer.capacity); +} \ No newline at end of file diff --git a/code/sdk/zhihui/ppp/ppp_dec_test/ppp_parser_dynamic.h b/code/sdk/zhihui/ppp/ppp_dec_test/ppp_parser_dynamic.h new file mode 100644 index 000000000..c580aaa39 --- /dev/null +++ b/code/sdk/zhihui/ppp/ppp_dec_test/ppp_parser_dynamic.h @@ -0,0 +1,125 @@ +#ifndef PPP_PARSER_DYNAMIC_H +#define PPP_PARSER_DYNAMIC_H + +#include +#include +#include + +/* PPP协议常量定义 */ +#define PPP_FLAG_BYTE 0x7E /* PPP帧标志位 */ +#define PPP_ESCAPE_BYTE 0x7D /* PPP转义字符 */ +#define PPP_ESCAPE_XOR 0x20 /* PPP转义异或值 */ +#define PPP_MIN_FRAME_SIZE 4 /* PPP帧最小长度(地址+控制+协议) */ + +/* 动态缓冲区配置 */ +#define PPP_INITIAL_BUFFER_SIZE 1500 /* 初始缓冲区大小 */ +#define PPP_MAX_BUFFER_SIZE 65536 /* 最大缓冲区大小 */ +#define PPP_BUFFER_GROW_STEP 1024 /* 缓冲区增长步长 */ + +/* 动态缓冲区结构 */ +typedef struct { + uint8_t *data; /* 缓冲区数据指针 */ + uint32_t size; /* 缓冲区总大小 */ + uint32_t len; /* 当前数据长度 */ + uint32_t capacity; /* 当前容量 */ +} ppp_dynamic_buffer_t; + +/* PPP帧结构(动态版本) */ +typedef struct { + uint8_t address; /* 地址字段 */ + uint8_t control; /* 控制字段 */ + uint16_t protocol; /* 协议字段 */ + ppp_dynamic_buffer_t payload; /* 动态负载数据 */ +} ppp_frame_dynamic_t; + +/* PPP解析器状态结构(动态版本) */ +typedef struct { + ppp_dynamic_buffer_t buffer; /* 动态帧数据缓冲区 */ + bool in_frame; /* 是否处于帧接收状态 */ + uint32_t current_position; /* 当前处理位置 */ + + /* 统计信息 */ + uint32_t total_frames_parsed; /* 解析的总帧数 */ + uint32_t total_bytes_processed; /* 处理的总字节数 */ + uint32_t dropped_data_count; /* 丢弃数据次数 */ + uint32_t buffer_reallocations; /* 缓冲区重分配次数 */ + uint32_t max_frame_size_seen; /* 见过的最大帧大小 */ +} ppp_parser_dynamic_t; + +/* 函数声明 */ + +/** + * 初始化动态缓冲区 + * @param buffer 缓冲区指针 + * @param initial_size 初始大小 + * @return 成功返回true + */ +bool ppp_dynamic_buffer_init(ppp_dynamic_buffer_t *buffer, uint32_t initial_size); + +/** + * 释放动态缓冲区 + * @param buffer 缓冲区指针 + */ +void ppp_dynamic_buffer_free(ppp_dynamic_buffer_t *buffer); + +/** + * 确保缓冲区有足够空间 + * @param buffer 缓冲区指针 + * @param required_size 需要的大小 + * @return 成功返回true + */ +bool ppp_dynamic_buffer_ensure_space(ppp_dynamic_buffer_t *buffer, uint32_t required_size); + +/** + * 向缓冲区添加数据 + * @param buffer 缓冲区指针 + * @param data 要添加的数据 + * @param len 数据长度 + * @return 成功返回true + */ +bool ppp_dynamic_buffer_append(ppp_dynamic_buffer_t *buffer, const uint8_t *data, uint32_t len); + +/** + * 向缓冲区添加单个字节 + * @param buffer 缓冲区指针 + * @param byte 要添加的字节 + * @return 成功返回true + */ +bool ppp_dynamic_buffer_append_byte(ppp_dynamic_buffer_t *buffer, uint8_t byte); + +/** + * 清空缓冲区(不释放内存) + * @param buffer 缓冲区指针 + */ +void ppp_dynamic_buffer_clear(ppp_dynamic_buffer_t *buffer); + +/** + * 初始化动态PPP解析器 + * @param parser PPP解析器指针 + * @return 成功返回true + */ +bool ppp_parser_dynamic_init(ppp_parser_dynamic_t *parser); + +/** + * 释放动态PPP解析器 + * @param parser PPP解析器指针 + */ +void ppp_parser_dynamic_free(ppp_parser_dynamic_t *parser); + +/** + * 处理单个字节(动态版本) + * @param parser PPP解析器指针 + * @param byte 输入字节 + * @param frame 输出的PPP帧指针 + * @return true表示解析出完整帧 + */ +bool ppp_parser_dynamic_process_byte(ppp_parser_dynamic_t *parser, uint8_t byte, + ppp_frame_dynamic_t *frame); + +/** + * 释放PPP帧资源 + * @param frame PPP帧指针 + */ +void ppp_frame_dynamic_free(ppp_frame_dynamic_t *frame); + +#endif /* PPP_PARSER_DYNAMIC_H */ \ No newline at end of file diff --git a/code/sdk/zhihui/ppp/ppp_dec_test/ppp_parser_streaming.h b/code/sdk/zhihui/ppp/ppp_dec_test/ppp_parser_streaming.h new file mode 100644 index 000000000..c199213cd --- /dev/null +++ b/code/sdk/zhihui/ppp/ppp_dec_test/ppp_parser_streaming.h @@ -0,0 +1,164 @@ +#ifndef PPP_PARSER_STREAMING_H +#define PPP_PARSER_STREAMING_H + +#include +#include +#include + +/* PPP协议常量定义 */ +#define PPP_FLAG_BYTE 0x7E /* PPP帧标志位 */ +#define PPP_ESCAPE_BYTE 0x7D /* PPP转义字符 */ +#define PPP_ESCAPE_XOR 0x20 /* PPP转义异或值 */ +#define PPP_MIN_FRAME_SIZE 4 /* PPP帧最小长度(地址+控制+协议) */ + +/* 流式处理配置 */ +#define PPP_STREAM_BUFFER_SIZE 512 /* 流式缓冲区大小 */ +#define PPP_MAX_FRAME_SIZE 65536 /* 最大支持的帧大小 */ + +/* 流式处理状态 */ +typedef enum { + PPP_STREAM_STATE_IDLE, /* 空闲状态 */ + PPP_STREAM_STATE_IN_FRAME, /* 帧内状态 */ + PPP_STREAM_STATE_FRAME_COMPLETE, /* 帧完成状态 */ + PPP_STREAM_STATE_FRAME_TOO_LARGE, /* 帧过大状态 */ + PPP_STREAM_STATE_ERROR /* 错误状态 */ +} ppp_stream_state_t; + +/* 流式处理回调函数类型 */ +typedef struct ppp_stream_parser ppp_stream_parser_t; + +/** + * 帧数据回调函数 + * @param parser 解析器指针 + * @param data 帧数据 + * @param len 数据长度 + * @param user_data 用户数据 + * @return 处理是否成功 + */ +typedef bool (*ppp_frame_data_callback_t)(ppp_stream_parser_t *parser, + const uint8_t *data, uint32_t len, + void *user_data); + +/** + * 帧完成回调函数 + * @param parser 解析器指针 + * @param total_len 帧总长度 + * @param user_data 用户数据 + * @return 处理是否成功 + */ +typedef bool (*ppp_frame_complete_callback_t)(ppp_stream_parser_t *parser, + uint32_t total_len, + void *user_data); + +/** + * 错误处理回调函数 + * @param parser 解析器指针 + * @param error_code 错误代码 + * @param user_data 用户数据 + */ +typedef void (*ppp_error_callback_t)(ppp_stream_parser_t *parser, + int error_code, + void *user_data); + +/* 流式PPP解析器结构 */ +struct ppp_stream_parser { + /* 状态信息 */ + ppp_stream_state_t state; /* 当前状态 */ + uint32_t current_frame_len; /* 当前帧长度 */ + uint32_t total_frames_parsed; /* 解析的总帧数 */ + uint32_t total_bytes_processed; /* 处理的总字节数 */ + uint32_t dropped_frames; /* 丢弃的帧数 */ + uint32_t oversized_frames; /* 超大帧数 */ + + /* 缓冲区(用于小块数据暂存) */ + uint8_t buffer[PPP_STREAM_BUFFER_SIZE]; + uint32_t buffer_len; + + /* 回调函数 */ + ppp_frame_data_callback_t data_callback; + ppp_frame_complete_callback_t complete_callback; + ppp_error_callback_t error_callback; + void *user_data; +}; + +/* 错误代码定义 */ +#define PPP_ERROR_FRAME_TOO_LARGE 1 /* 帧过大 */ +#define PPP_ERROR_INVALID_STATE 2 /* 无效状态 */ +#define PPP_ERROR_CALLBACK_FAILED 3 /* 回调函数失败 */ + +/* 函数声明 */ + +/** + * 初始化流式PPP解析器 + * @param parser 解析器指针 + * @param data_callback 数据回调函数 + * @param complete_callback 完成回调函数 + * @param error_callback 错误回调函数 + * @param user_data 用户数据 + * @return 成功返回true + */ +bool ppp_stream_parser_init(ppp_stream_parser_t *parser, + ppp_frame_data_callback_t data_callback, + ppp_frame_complete_callback_t complete_callback, + ppp_error_callback_t error_callback, + void *user_data); + +/** + * 处理输入数据 + * @param parser 解析器指针 + * @param data 输入数据 + * @param len 数据长度 + * @return 处理的字节数 + */ +uint32_t ppp_stream_parser_process(ppp_stream_parser_t *parser, + const uint8_t *data, uint32_t len); + +/** + * 重置解析器状态 + * @param parser 解析器指针 + */ +void ppp_stream_parser_reset(ppp_stream_parser_t *parser); + +/** + * 获取解析器统计信息 + * @param parser 解析器指针 + */ +void ppp_stream_parser_print_stats(const ppp_stream_parser_t *parser); + +/* 示例:文件输出处理器 */ +typedef struct { + FILE *output_file; + uint32_t frame_count; + uint8_t *frame_buffer; + uint32_t frame_buffer_size; + uint32_t frame_buffer_pos; +} ppp_file_output_t; + +/** + * 初始化文件输出处理器 + * @param output 输出处理器 + * @param filename 输出文件名 + * @return 成功返回true + */ +bool ppp_file_output_init(ppp_file_output_t *output, const char *filename); + +/** + * 释放文件输出处理器 + * @param output 输出处理器 + */ +void ppp_file_output_free(ppp_file_output_t *output); + +/* 文件输出回调函数 */ +bool ppp_file_output_data_callback(ppp_stream_parser_t *parser, + const uint8_t *data, uint32_t len, + void *user_data); + +bool ppp_file_output_complete_callback(ppp_stream_parser_t *parser, + uint32_t total_len, + void *user_data); + +void ppp_file_output_error_callback(ppp_stream_parser_t *parser, + int error_code, + void *user_data); + +#endif /* PPP_PARSER_STREAMING_H */ \ No newline at end of file diff --git a/code/sdk/zhihui/ppp/ppp_dec_test/ppp_stream_parser.c b/code/sdk/zhihui/ppp/ppp_dec_test/ppp_stream_parser.c new file mode 100644 index 000000000..8b076d9a4 --- /dev/null +++ b/code/sdk/zhihui/ppp/ppp_dec_test/ppp_stream_parser.c @@ -0,0 +1,397 @@ +#include "ppp_stream_parser.h" +#include +#include +#include + +/** + * 初始化流式PPP解析器 + */ +bool ppp_stream_parser_init(ppp_stream_parser_t *parser, + uint32_t max_frame_size, + ppp_frame_start_callback_t start_callback, + ppp_frame_data_callback_t data_callback, + ppp_frame_complete_callback_t complete_callback, + ppp_error_callback_t error_callback, + void *user_data) +{ + if (!parser || !data_callback || !complete_callback) { + return false; + } + + memset(parser, 0, sizeof(ppp_stream_parser_t)); + + parser->state = PPP_STREAM_STATE_IDLE; + parser->max_frame_size = (max_frame_size > 0) ? max_frame_size : PPP_MAX_FRAME_SIZE; + parser->start_callback = start_callback; + parser->data_callback = data_callback; + parser->complete_callback = complete_callback; + parser->error_callback = error_callback; + parser->user_data = user_data; + + return true; +} + +/** + * 处理输入数据 + */ +uint32_t ppp_stream_parser_process(ppp_stream_parser_t *parser, + const uint8_t *data, uint32_t len) +{ + if (!parser || !data || len == 0) { + return 0; + } + + uint32_t processed = 0; + + for (uint32_t i = 0; i < len; i++) { + uint8_t byte = data[i]; + parser->total_bytes_processed++; + processed++; + + switch (parser->state) { + case PPP_STREAM_STATE_IDLE: + if (byte == PPP_FLAG_BYTE) { + /* 帧开始 */ + parser->state = PPP_STREAM_STATE_IN_FRAME; + parser->current_frame_len = 1; /* 包含开始的0x7E */ + parser->buffer_len = 0; + + /* 将开始标志添加到缓冲区 */ + parser->buffer[parser->buffer_len++] = byte; + + /* 调用帧开始回调 */ + if (parser->start_callback) { + if (!parser->start_callback(parser, parser->user_data)) { + if (parser->error_callback) { + parser->error_callback(parser, PPP_ERROR_CALLBACK_FAILED, + "帧开始回调失败", parser->user_data); + } + parser->state = PPP_STREAM_STATE_ERROR; + } + } + } + /* 其他字节在空闲状态下被忽略 */ + break; + + case PPP_STREAM_STATE_IN_FRAME: + if (byte == PPP_FLAG_BYTE) { + /* 帧结束 - 添加结束标志 */ + parser->current_frame_len++; + + /* 将结束标志添加到缓冲区 */ + parser->buffer[parser->buffer_len++] = byte; + + if (parser->current_frame_len >= (PPP_MIN_FRAME_SIZE + 2)) { /* 包含头尾0x7E */ + /* 处理缓冲区中的剩余数据 */ + if (parser->buffer_len > 0) { + if (!parser->data_callback(parser, parser->buffer, + parser->buffer_len, parser->user_data)) { + if (parser->error_callback) { + parser->error_callback(parser, PPP_ERROR_CALLBACK_FAILED, + "数据回调失败", parser->user_data); + } + parser->state = PPP_STREAM_STATE_ERROR; + break; + } + } + + /* 调用帧完成回调 */ + if (parser->complete_callback(parser, parser->current_frame_len, + parser->user_data)) { + parser->total_frames_parsed++; + parser->state = PPP_STREAM_STATE_FRAME_COMPLETE; + + /* 更新最大帧大小统计 */ + if (parser->current_frame_len > parser->max_frame_size_seen) { + parser->max_frame_size_seen = parser->current_frame_len; + } + } else { + if (parser->error_callback) { + parser->error_callback(parser, PPP_ERROR_CALLBACK_FAILED, + "帧完成回调失败", parser->user_data); + } + parser->state = PPP_STREAM_STATE_ERROR; + } + } else { + /* 帧太短,丢弃 */ + parser->dropped_frames++; + if (parser->error_callback) { + char msg[64]; + snprintf(msg, sizeof(msg), "帧长度不足(%u字节)", parser->current_frame_len); + parser->error_callback(parser, PPP_ERROR_FRAME_TOO_LARGE, msg, parser->user_data); + } + } + + /* 重置状态,准备下一帧 */ + parser->state = PPP_STREAM_STATE_IDLE; + parser->current_frame_len = 0; + parser->buffer_len = 0; + } else { + /* 帧内数据 */ + parser->current_frame_len++; + + /* 检查帧大小限制 */ + if (parser->current_frame_len > parser->max_frame_size) { + parser->oversized_frames++; + parser->dropped_frames++; + if (parser->error_callback) { + char msg[64]; + snprintf(msg, sizeof(msg), "帧过大(%u字节)", parser->current_frame_len); + parser->error_callback(parser, PPP_ERROR_FRAME_TOO_LARGE, msg, parser->user_data); + } + parser->state = PPP_STREAM_STATE_FRAME_TOO_LARGE; + break; + } + + /* 将数据添加到缓冲区 */ + parser->buffer[parser->buffer_len++] = byte; + + /* 如果缓冲区满了,调用数据回调 */ + if (parser->buffer_len >= PPP_STREAM_BUFFER_SIZE) { + if (!parser->data_callback(parser, parser->buffer, + parser->buffer_len, parser->user_data)) { + if (parser->error_callback) { + parser->error_callback(parser, PPP_ERROR_CALLBACK_FAILED, + "数据回调失败", parser->user_data); + } + parser->state = PPP_STREAM_STATE_ERROR; + break; + } + parser->buffer_len = 0; + } + } + break; + + case PPP_STREAM_STATE_FRAME_TOO_LARGE: + /* 等待帧结束标志 */ + if (byte == PPP_FLAG_BYTE) { + parser->state = PPP_STREAM_STATE_IDLE; + parser->current_frame_len = 0; + parser->buffer_len = 0; + } + break; + + case PPP_STREAM_STATE_FRAME_COMPLETE: + /* 帧完成后,重新开始处理 */ + parser->state = PPP_STREAM_STATE_IDLE; + /* 重新处理当前字节 */ + i--; + processed--; + parser->total_bytes_processed--; + break; + + case PPP_STREAM_STATE_ERROR: + /* 错误状态,等待重置 */ + return processed; + } + } + + return processed; +} + +/** + * 重置解析器状态 + */ +void ppp_stream_parser_reset(ppp_stream_parser_t *parser) +{ + if (!parser) return; + + parser->state = PPP_STREAM_STATE_IDLE; + parser->current_frame_len = 0; + parser->buffer_len = 0; +} + +/** + * 获取解析器统计信息 + */ +void ppp_stream_parser_print_stats(const ppp_stream_parser_t *parser) +{ + if (!parser) return; + + printf("\n流式PPP解析器统计信息:\n"); + printf(" 当前状态: %s\n", ppp_stream_state_name(parser->state)); + printf(" 处理的总字节数: %u\n", parser->total_bytes_processed); + printf(" 解析的总帧数: %u\n", parser->total_frames_parsed); + printf(" 丢弃的帧数: %u\n", parser->dropped_frames); + printf(" 超大帧数: %u\n", parser->oversized_frames); + printf(" 最大帧大小: %u字节\n", parser->max_frame_size_seen); + printf(" 最大允许帧大小: %u字节\n", parser->max_frame_size); +} + +/** + * 获取状态名称 + */ +const char* ppp_stream_state_name(ppp_stream_state_t state) +{ + switch (state) { + case PPP_STREAM_STATE_IDLE: + return "空闲"; + case PPP_STREAM_STATE_IN_FRAME: + return "帧内"; + case PPP_STREAM_STATE_FRAME_COMPLETE: + return "帧完成"; + case PPP_STREAM_STATE_FRAME_TOO_LARGE: + return "帧过大"; + case PPP_STREAM_STATE_ERROR: + return "错误"; + default: + return "未知"; + } +} + +/* 帧收集器实现 */ + +/** + * 初始化帧收集器 + */ +bool ppp_frame_collector_init(ppp_frame_collector_t *collector, uint32_t max_frames) +{ + if (!collector || max_frames == 0) { + return false; + } + + memset(collector, 0, sizeof(ppp_frame_collector_t)); + + collector->frames = malloc(max_frames * sizeof(uint8_t*)); + collector->frame_lengths = malloc(max_frames * sizeof(uint32_t)); + + if (!collector->frames || !collector->frame_lengths) { + free(collector->frames); + free(collector->frame_lengths); + return false; + } + + collector->max_frames = max_frames; + collector->frame_count = 0; + + return true; +} + +/** + * 释放帧收集器 + */ +void ppp_frame_collector_free(ppp_frame_collector_t *collector) +{ + if (!collector) return; + + /* 释放所有帧数据 */ + for (uint32_t i = 0; i < collector->frame_count; i++) { + free(collector->frames[i]); + } + + /* 释放当前帧数据 */ + free(collector->current_frame_data); + + /* 释放数组 */ + free(collector->frames); + free(collector->frame_lengths); + + memset(collector, 0, sizeof(ppp_frame_collector_t)); +} + +/** + * 获取收集到的帧数据 + */ +bool ppp_frame_collector_get_frame(const ppp_frame_collector_t *collector, + uint32_t frame_index, + const uint8_t **frame_data, + uint32_t *frame_len) +{ + if (!collector || !frame_data || !frame_len || frame_index >= collector->frame_count) { + return false; + } + + *frame_data = collector->frames[frame_index]; + *frame_len = collector->frame_lengths[frame_index]; + + return true; +} + +/* 帧收集器的回调函数实现 */ + +bool ppp_collector_start_callback(ppp_stream_parser_t *parser, void *user_data) +{ + ppp_frame_collector_t *collector = (ppp_frame_collector_t*)user_data; + + if (!collector) return false; + + /* 检查是否还能收集更多帧 */ + if (collector->frame_count >= collector->max_frames) { + return false; + } + + /* 初始化当前帧 */ + collector->current_frame_capacity = 1024; /* 初始容量 */ + collector->current_frame_data = malloc(collector->current_frame_capacity); + collector->current_frame_len = 0; + + return (collector->current_frame_data != NULL); +} + +bool ppp_collector_data_callback(ppp_stream_parser_t *parser, + const uint8_t *data, uint32_t len, void *user_data) +{ + ppp_frame_collector_t *collector = (ppp_frame_collector_t*)user_data; + + if (!collector || !data || len == 0) return false; + + /* 确保有足够空间 */ + uint32_t required_size = collector->current_frame_len + len; + if (required_size > collector->current_frame_capacity) { + /* 扩展容量 */ + uint32_t new_capacity = collector->current_frame_capacity; + while (new_capacity < required_size) { + new_capacity *= 2; + } + + uint8_t *new_data = realloc(collector->current_frame_data, new_capacity); + if (!new_data) { + return false; + } + + collector->current_frame_data = new_data; + collector->current_frame_capacity = new_capacity; + } + + /* 复制数据 */ + memcpy(collector->current_frame_data + collector->current_frame_len, data, len); + collector->current_frame_len += len; + + return true; +} + +bool ppp_collector_complete_callback(ppp_stream_parser_t *parser, + uint32_t total_len, void *user_data) +{ + ppp_frame_collector_t *collector = (ppp_frame_collector_t*)user_data; + + if (!collector) return false; + + /* 将当前帧添加到收集器 */ + collector->frames[collector->frame_count] = collector->current_frame_data; + collector->frame_lengths[collector->frame_count] = collector->current_frame_len; + collector->frame_count++; + + /* 重置当前帧 */ + collector->current_frame_data = NULL; + collector->current_frame_len = 0; + collector->current_frame_capacity = 0; + + return true; +} + +void ppp_collector_error_callback(ppp_stream_parser_t *parser, + int error_code, const char *error_msg, void *user_data) +{ + ppp_frame_collector_t *collector = (ppp_frame_collector_t*)user_data; + + printf("帧收集器错误 [%d]: %s\n", error_code, error_msg ? error_msg : "未知错误"); + + /* 清理当前帧数据 */ + if (collector && collector->current_frame_data) { + free(collector->current_frame_data); + collector->current_frame_data = NULL; + collector->current_frame_len = 0; + collector->current_frame_capacity = 0; + } +} \ No newline at end of file diff --git a/code/sdk/zhihui/ppp/ppp_dec_test/ppp_stream_parser.h b/code/sdk/zhihui/ppp/ppp_dec_test/ppp_stream_parser.h new file mode 100644 index 000000000..b71009483 --- /dev/null +++ b/code/sdk/zhihui/ppp/ppp_dec_test/ppp_stream_parser.h @@ -0,0 +1,206 @@ +#ifndef PPP_STREAM_PARSER_H +#define PPP_STREAM_PARSER_H + +#include +#include +#include + +/* PPP协议常量定义 */ +#define PPP_FLAG_BYTE 0x7E /* PPP帧标志位 */ +#define PPP_ESCAPE_BYTE 0x7D /* PPP转义字符 */ +#define PPP_ESCAPE_XOR 0x20 /* PPP转义异或值 */ +#define PPP_MIN_FRAME_SIZE 4 /* PPP帧最小长度(地址+控制+协议) */ + +/* 流式处理配置 */ +#define PPP_STREAM_BUFFER_SIZE 1024 /* 流式缓冲区大小 */ +#ifndef PPP_MAX_FRAME_SIZE +#define PPP_MAX_FRAME_SIZE 65536 /* 最大支持的帧大小 */ +#endif + +/* 流式处理状态 */ +typedef enum { + PPP_STREAM_STATE_IDLE, /* 空闲状态 */ + PPP_STREAM_STATE_IN_FRAME, /* 帧内状态 */ + PPP_STREAM_STATE_FRAME_COMPLETE, /* 帧完成状态 */ + PPP_STREAM_STATE_FRAME_TOO_LARGE, /* 帧过大状态 */ + PPP_STREAM_STATE_ERROR /* 错误状态 */ +} ppp_stream_state_t; + +/* 错误代码定义 */ +#define PPP_ERROR_FRAME_TOO_LARGE 1 /* 帧过大 */ +#define PPP_ERROR_INVALID_STATE 2 /* 无效状态 */ +#define PPP_ERROR_CALLBACK_FAILED 3 /* 回调函数失败 */ +#define PPP_ERROR_OUT_OF_MEMORY 4 /* 内存不足 */ + +/* 前向声明 */ +typedef struct ppp_stream_parser ppp_stream_parser_t; + +/* 回调函数类型定义 */ + +/** + * 帧开始回调函数 + * @param parser 解析器指针 + * @param user_data 用户数据 + * @return 处理是否成功 + */ +typedef bool (*ppp_frame_start_callback_t)(ppp_stream_parser_t *parser, void *user_data); + +/** + * 帧数据回调函数 + * @param parser 解析器指针 + * @param data 帧数据块 + * @param len 数据长度 + * @param user_data 用户数据 + * @return 处理是否成功 + */ +typedef bool (*ppp_frame_data_callback_t)(ppp_stream_parser_t *parser, + const uint8_t *data, uint32_t len, + void *user_data); + +/** + * 帧完成回调函数 + * @param parser 解析器指针 + * @param total_len 帧总长度 + * @param user_data 用户数据 + * @return 处理是否成功 + */ +typedef bool (*ppp_frame_complete_callback_t)(ppp_stream_parser_t *parser, + uint32_t total_len, + void *user_data); + +/** + * 错误处理回调函数 + * @param parser 解析器指针 + * @param error_code 错误代码 + * @param error_msg 错误消息 + * @param user_data 用户数据 + */ +typedef void (*ppp_error_callback_t)(ppp_stream_parser_t *parser, + int error_code, + const char *error_msg, + void *user_data); + +/* 流式PPP解析器结构 */ +struct ppp_stream_parser { + /* 状态信息 */ + ppp_stream_state_t state; /* 当前状态 */ + uint32_t current_frame_len; /* 当前帧长度 */ + uint32_t max_frame_size; /* 最大允许帧大小 */ + + /* 统计信息 */ + uint32_t total_frames_parsed; /* 解析的总帧数 */ + uint32_t total_bytes_processed; /* 处理的总字节数 */ + uint32_t dropped_frames; /* 丢弃的帧数 */ + uint32_t oversized_frames; /* 超大帧数 */ + uint32_t max_frame_size_seen; /* 见过的最大帧大小 */ + + /* 小缓冲区(用于处理跨块的数据) */ + uint8_t buffer[PPP_STREAM_BUFFER_SIZE]; + uint32_t buffer_len; + + /* 回调函数 */ + ppp_frame_start_callback_t start_callback; + ppp_frame_data_callback_t data_callback; + ppp_frame_complete_callback_t complete_callback; + ppp_error_callback_t error_callback; + void *user_data; +}; + +/* 函数声明 */ + +/** + * 初始化流式PPP解析器 + * @param parser 解析器指针 + * @param max_frame_size 最大帧大小(0使用默认值) + * @param start_callback 帧开始回调函数 + * @param data_callback 数据回调函数 + * @param complete_callback 完成回调函数 + * @param error_callback 错误回调函数 + * @param user_data 用户数据 + * @return 成功返回true + */ +bool ppp_stream_parser_init(ppp_stream_parser_t *parser, + uint32_t max_frame_size, + ppp_frame_start_callback_t start_callback, + ppp_frame_data_callback_t data_callback, + ppp_frame_complete_callback_t complete_callback, + ppp_error_callback_t error_callback, + void *user_data); + +/** + * 处理输入数据 + * @param parser 解析器指针 + * @param data 输入数据 + * @param len 数据长度 + * @return 处理的字节数 + */ +uint32_t ppp_stream_parser_process(ppp_stream_parser_t *parser, + const uint8_t *data, uint32_t len); + +/** + * 重置解析器状态 + * @param parser 解析器指针 + */ +void ppp_stream_parser_reset(ppp_stream_parser_t *parser); + +/** + * 获取解析器统计信息 + * @param parser 解析器指针 + */ +void ppp_stream_parser_print_stats(const ppp_stream_parser_t *parser); + +/** + * 获取状态名称 + * @param state 状态值 + * @return 状态名称字符串 + */ +const char* ppp_stream_state_name(ppp_stream_state_t state); + +/* 便利函数:创建简单的帧收集器 */ +typedef struct { + uint8_t **frames; /* 帧数据数组 */ + uint32_t *frame_lengths; /* 帧长度数组 */ + uint32_t frame_count; /* 当前帧数量 */ + uint32_t max_frames; /* 最大帧数量 */ + uint32_t current_frame_capacity; /* 当前帧容量 */ + uint8_t *current_frame_data; /* 当前帧数据 */ + uint32_t current_frame_len; /* 当前帧长度 */ +} ppp_frame_collector_t; + +/** + * 初始化帧收集器 + * @param collector 收集器指针 + * @param max_frames 最大帧数量 + * @return 成功返回true + */ +bool ppp_frame_collector_init(ppp_frame_collector_t *collector, uint32_t max_frames); + +/** + * 释放帧收集器 + * @param collector 收集器指针 + */ +void ppp_frame_collector_free(ppp_frame_collector_t *collector); + +/** + * 获取收集到的帧数据 + * @param collector 收集器指针 + * @param frame_index 帧索引 + * @param frame_data 输出帧数据指针 + * @param frame_len 输出帧长度 + * @return 成功返回true + */ +bool ppp_frame_collector_get_frame(const ppp_frame_collector_t *collector, + uint32_t frame_index, + const uint8_t **frame_data, + uint32_t *frame_len); + +/* 帧收集器的回调函数 */ +bool ppp_collector_start_callback(ppp_stream_parser_t *parser, void *user_data); +bool ppp_collector_data_callback(ppp_stream_parser_t *parser, + const uint8_t *data, uint32_t len, void *user_data); +bool ppp_collector_complete_callback(ppp_stream_parser_t *parser, + uint32_t total_len, void *user_data); +void ppp_collector_error_callback(ppp_stream_parser_t *parser, + int error_code, const char *error_msg, void *user_data); + +#endif /* PPP_STREAM_PARSER_H */ \ No newline at end of file diff --git a/code/sdk/zhihui/ppp/ppp_dec_test/ppp_stream_test b/code/sdk/zhihui/ppp/ppp_dec_test/ppp_stream_test new file mode 100644 index 0000000000000000000000000000000000000000..d63d4e01a18e52592c9daf51c7250ead99cd886d GIT binary patch literal 132656 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 Dt8DqW literal 0 HcmV?d00001 diff --git a/code/sdk/zhihui/ppp/ppp_dec_test/ppp_stream_test.c b/code/sdk/zhihui/ppp/ppp_dec_test/ppp_stream_test.c new file mode 100644 index 000000000..b03bb9537 --- /dev/null +++ b/code/sdk/zhihui/ppp/ppp_dec_test/ppp_stream_test.c @@ -0,0 +1,376 @@ +#include +#include +#include +#include +#include +#include "ppp_stream_parser.h" +#include "ppp_packet_analyzer.h" + +/* 配置常量 */ +#define MAX_FILE_SIZE (1024 * 1024) /* 最大文件大小1MB */ +#define MAX_FRAMES 1000 /* 最大帧数量 */ +#define CHUNK_SIZE 256 /* 流式处理块大小 */ + +/* 全局变量 */ +static uint8_t g_hex_data[MAX_FILE_SIZE]; /* 十六进制数据缓冲区 */ + +/** + * 从十六进制字符串转换为字节值 + */ +static int hex_char_to_byte(char hex_char) +{ + if (hex_char >= '0' && hex_char <= '9') { + return hex_char - '0'; + } else if (hex_char >= 'A' && hex_char <= 'F') { + return hex_char - 'A' + 10; + } else if (hex_char >= 'a' && hex_char <= 'f') { + return hex_char - 'a' + 10; + } + return -1; +} + +/** + * 从文件中读取十六进制数据 + */ +static uint32_t read_hex_file(const char *file_path, uint8_t *data, uint32_t max_size) +{ + if (!file_path || !data || max_size == 0) { + printf("错误: 无效的参数\n"); + return 0; + } + + FILE *file = fopen(file_path, "r"); + if (!file) { + printf("错误: 无法打开文件 %s: %s\n", file_path, strerror(errno)); + return 0; + } + + char line[256]; + uint32_t data_len = 0; + uint32_t line_number = 0; + + printf("开始读取文件: %s\n", file_path); + + while (fgets(line, sizeof(line), file) && data_len < max_size) { + line_number++; + + /* 移除行尾的换行符和空白字符 */ + char *end = line + strlen(line) - 1; + while (end >= line && isspace(*end)) { + *end-- = '\0'; + } + + /* 跳过空行 */ + if (strlen(line) == 0) { + continue; + } + + /* 处理每行的十六进制字符串 */ + char *ptr = line; + while (*ptr && data_len < max_size) { + /* 跳过空白字符 */ + while (*ptr && isspace(*ptr)) { + ptr++; + } + + if (!*ptr) break; + + /* 读取两个十六进制字符组成一个字节 */ + if (strlen(ptr) >= 2) { + int high = hex_char_to_byte(ptr[0]); + int low = hex_char_to_byte(ptr[1]); + + if (high >= 0 && low >= 0) { + data[data_len++] = (high << 4) | low; + ptr += 2; + } else { + printf("警告: 文件%s第%u行包含无效的十六进制字符: %c%c\n", + file_path, line_number, ptr[0], ptr[1]); + ptr += 2; + } + } else if (strlen(ptr) == 1) { + /* 单个十六进制字符,当作一个字节处理 */ + int value = hex_char_to_byte(ptr[0]); + if (value >= 0) { + data[data_len++] = value; + ptr++; + } else { + printf("警告: 文件%s第%u行包含无效的十六进制字符: %c\n", + file_path, line_number, ptr[0]); + ptr++; + } + } + } + } + + fclose(file); + + if (data_len >= max_size) { + printf("警告: 文件%s数据量超过缓冲区大小,只读取了前%u字节\n", file_path, max_size); + } + + printf("成功读取文件%s: %u字节\n", file_path, data_len); + return data_len; +} + +/** + * 流式分析PPP文件 + */ +static void analyze_ppp_file_streaming(const char *file_path) +{ + printf("\n=== 流式分析文件: %s ===\n", file_path); + + /* 读取文件数据 */ + uint32_t data_len = read_hex_file(file_path, g_hex_data, MAX_FILE_SIZE); + if (data_len == 0) { + printf("文件读取失败,跳过分析\n"); + return; + } + + /* 初始化帧收集器 */ + ppp_frame_collector_t collector; + if (!ppp_frame_collector_init(&collector, MAX_FRAMES)) { + printf("错误: 无法初始化帧收集器\n"); + return; + } + + /* 初始化流式解析器 */ + ppp_stream_parser_t parser; + if (!ppp_stream_parser_init(&parser, + 0, /* 使用默认最大帧大小 */ + ppp_collector_start_callback, + ppp_collector_data_callback, + ppp_collector_complete_callback, + ppp_collector_error_callback, + &collector)) { + printf("错误: 无法初始化流式解析器\n"); + ppp_frame_collector_free(&collector); + return; + } + + printf("\n--- 流式PPP帧解析阶段 ---\n"); + printf("使用块大小: %d字节\n", CHUNK_SIZE); + + /* 分块处理数据 */ + uint32_t total_processed = 0; + uint32_t chunk_count = 0; + + for (uint32_t offset = 0; offset < data_len; offset += CHUNK_SIZE) { + uint32_t chunk_size = (offset + CHUNK_SIZE > data_len) ? + (data_len - offset) : CHUNK_SIZE; + + chunk_count++; + printf("处理块 #%u: 偏移=%u, 大小=%u字节\n", chunk_count, offset, chunk_size); + + uint32_t processed = ppp_stream_parser_process(&parser, + g_hex_data + offset, + chunk_size); + total_processed += processed; + + if (processed != chunk_size) { + printf("警告: 块 #%u 只处理了 %u/%u 字节\n", chunk_count, processed, chunk_size); + } + } + + printf("流式解析完成,总共处理 %u 字节,分 %u 个块\n", total_processed, chunk_count); + + /* 打印解析器统计信息 */ + ppp_stream_parser_print_stats(&parser); + + /* 处理收集到的帧 */ + printf("\n--- 收集到的PPP帧分析 ---\n"); + printf("总共收集到 %u 个PPP帧\n", collector.frame_count); + + if (collector.frame_count > 0) { + /* 分析前几个帧 */ + uint32_t analyze_count = (collector.frame_count > 5) ? 5 : collector.frame_count; + + for (uint32_t i = 0; i < analyze_count; i++) { + const uint8_t *frame_data; + uint32_t frame_len; + + if (ppp_frame_collector_get_frame(&collector, i, &frame_data, &frame_len)) { + printf("\n--- 帧 #%u 分析 (长度: %u字节) ---\n", i + 1, frame_len); + + /* 打印帧数据(前32字节) */ + printf("帧数据: "); + uint32_t print_len = (frame_len > 32) ? 32 : frame_len; + for (uint32_t j = 0; j < print_len; j++) { + printf("%02X", frame_data[j]); + } + if (frame_len > 32) { + printf("..."); + } + printf("\n"); + + /* 使用packet analyzer分析帧内容 */ + packet_analysis_result_t result; + + /* 创建临时的ppp_frame_t结构 */ + ppp_frame_t temp_frame; + memset(&temp_frame, 0, sizeof(temp_frame)); + + /* 设置原始帧数据(包含头尾0x7E) */ + temp_frame.raw_frame_len = frame_len; + memcpy(temp_frame.raw_frame, frame_data, + (frame_len > (PPP_MAX_FRAME_SIZE + 2)) ? (PPP_MAX_FRAME_SIZE + 2) : frame_len); + + if (analyze_ppp_frame(&temp_frame, &result)) { + printf("协议分析: %s\n", result.protocol_name); + + if (result.has_ip) { + char src_ip[16], dst_ip[16]; + format_ip_address(&result.ip_info.src_ip, src_ip, sizeof(src_ip)); + format_ip_address(&result.ip_info.dst_ip, dst_ip, sizeof(dst_ip)); + + printf("IP: %s -> %s, 验证: %s\n", + src_ip, dst_ip, + (result.ip_info.validation == IP_VALID) ? "通过" : "失败"); + + if (result.has_tcp) { + printf("TCP: 端口 %u -> %u\n", + result.tcp_info.src_port, result.tcp_info.dst_port); + } else if (result.has_udp) { + printf("UDP: 端口 %u -> %u\n", + result.udp_info.src_port, result.udp_info.dst_port); + } + } + } else { + printf("协议分析失败\n"); + } + } + } + + if (collector.frame_count > 5) { + printf("\n... 还有 %u 个帧未显示详细分析\n", collector.frame_count - 5); + } + + /* 统计协议分布 */ + printf("\n--- 协议统计 ---\n"); + uint32_t ip_count = 0, tcp_count = 0, udp_count = 0, lcp_count = 0, other_count = 0; + + for (uint32_t i = 0; i < collector.frame_count; i++) { + const uint8_t *frame_data; + uint32_t frame_len; + + if (ppp_frame_collector_get_frame(&collector, i, &frame_data, &frame_len)) { + ppp_frame_t temp_frame; + memset(&temp_frame, 0, sizeof(temp_frame)); + + /* 设置原始帧数据(包含头尾0x7E) */ + temp_frame.raw_frame_len = frame_len; + memcpy(temp_frame.raw_frame, frame_data, + (frame_len > (PPP_MAX_FRAME_SIZE + 2)) ? (PPP_MAX_FRAME_SIZE + 2) : frame_len); + + packet_analysis_result_t result; + if (analyze_ppp_frame(&temp_frame, &result)) { + if (result.has_ip) { + ip_count++; + if (result.has_tcp) tcp_count++; + else if (result.has_udp) udp_count++; + } else if (strstr(result.protocol_name, "LCP")) { + lcp_count++; + } else { + other_count++; + } + } else { + other_count++; + } + } + } + + printf("IP包: %u\n", ip_count); + printf("TCP包: %u\n", tcp_count); + printf("UDP包: %u\n", udp_count); + printf("LCP包: %u\n", lcp_count); + printf("其他包: %u\n", other_count); + } + + /* 清理资源 */ + ppp_frame_collector_free(&collector); + + printf("\n=== 流式分析完成: %s ===\n", file_path); +} + +/** + * 打印使用说明 + */ +static void print_usage(const char *program_name) +{ + printf("流式PPP协议解析工具\n"); + printf("演示流式处理大型PPP数据文件的能力\n\n"); + printf("使用方法:\n"); + printf(" %s [文件1] [文件2] ...\n\n", program_name); + printf("特点:\n"); + printf(" - 流式处理,内存占用固定\n"); + printf(" - 支持任意大小的PPP帧\n"); + printf(" - 分块处理,模拟实时数据流\n"); + printf(" - 回调机制,灵活的数据处理\n\n"); + printf("示例:\n"); + printf(" %s out0.txt\n", program_name); + printf(" %s out0.txt out1.txt\n", program_name); +} + +/** + * 主函数 + */ +int main(int argc, char *argv[]) +{ + printf("流式PPP协议解析工具 - 启动\n"); + printf("版本: 1.0\n"); + printf("特性: 流式处理、动态内存管理、回调机制\n\n"); + + /* 检查命令行参数 */ + if (argc > 1 && (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0)) { + print_usage(argv[0]); + return 0; + } + + /* 确定要分析的文件列表 */ + const char **files_to_analyze; + int file_count; + const char *default_files[] = {"out0.txt", "out1.txt"}; + + if (argc > 1) { + /* 使用命令行参数中的文件列表 */ + files_to_analyze = (const char **)&argv[1]; + file_count = argc - 1; + } else { + /* 使用默认文件列表 */ + files_to_analyze = default_files; + file_count = 2; + printf("未指定文件,使用默认文件: out0.txt, out1.txt\n"); + } + + /* 遍历并分析每个文件 */ + int success_count = 0; + for (int i = 0; i < file_count; i++) { + const char *file_path = files_to_analyze[i]; + + /* 检查文件是否存在 */ + FILE *test_file = fopen(file_path, "r"); + if (test_file) { + fclose(test_file); + analyze_ppp_file_streaming(file_path); + success_count++; + } else { + printf("\n错误: 文件不存在或无法访问: %s\n", file_path); + } + } + + /* 打印总结信息 */ + printf("\n============================================================\n"); + printf("流式分析总结:\n"); + printf(" 指定文件数量: %d\n", file_count); + printf(" 成功分析文件: %d\n", success_count); + printf(" 失败文件数量: %d\n", file_count - success_count); + printf(" 使用的块大小: %d字节\n", CHUNK_SIZE); + + if (success_count == 0) { + printf("\n警告: 没有成功分析任何文件\n"); + return 1; + } + + printf("\n流式PPP协议解析工具运行完成\n"); + return 0; +} \ No newline at end of file diff --git a/code/sdk/zhihui/ppp/src/ppp_api.c b/code/sdk/zhihui/ppp/src/ppp_api.c index f2ebc5201..4e33027f3 100644 --- a/code/sdk/zhihui/ppp/src/ppp_api.c +++ b/code/sdk/zhihui/ppp/src/ppp_api.c @@ -6,6 +6,7 @@ #include "ppp_serial.h" // 添加串口处理头文件 #include "ppp_lwip.h" #include "ppp_cat1_api.h" +#include "ppp_interrupt.h" // 添加中断处理头文件 #include "soc_osal.h" #include #include // 添加malloc/free支持 @@ -432,12 +433,26 @@ errcode_t ppp_start_dial(void) PPP_DEBUG_PRINT("No immediate data after mode switch - this is normal\n"); } - // 9. 启动PPP数据接收处理(在lwIP连接之前启动) - ret = ppp_lwip_start_receive_thread(); + // 9. 启动PPP数据接收处理(根据配置选择中断或轮询模式) +#if PPP_USE_INTERRUPT_MODE + // 中断模式:注册UART中断回调(UART总线号从ppp_serial_get_uart_bus()获取) + PPP_DEBUG_PRINT("Enabling PPP interrupt mode...\n"); + ret = ppp_interrupt_enable(); + if (ret != PPP_ERR_OK) { + PPP_DEBUG_PRINT("Failed to enable PPP interrupt: %d\n", ret); + goto error_recovery_at_mode; + } + PPP_DEBUG_PRINT("PPP interrupt mode enabled successfully\n"); +#else + // 轮询模式:启动接收线程 + PPP_DEBUG_PRINT("Starting PPP receive thread (polling mode)...\n"); + ret = ppp_lwip_start_receive_thread(); if (ret != PPP_ERR_OK) { PPP_DEBUG_PRINT("Failed to start PPP data receive thread: %d\n", ret); goto error_recovery_at_mode; } + PPP_DEBUG_PRINT("PPP receive thread started successfully\n"); +#endif ppp_lwip_config_t lwip_config = { .username = "", @@ -568,14 +583,11 @@ errcode_t ppp_stop_dial(void) ppp_error_t ret = PPP_ERR_OK; // 声明ret变量 bool at_communication_ok = false; // 声明AT通信状态变量 - // 1. 首先停止PPP数据接收线程(在切换串口模式之前) - PPP_DEBUG_INFO("Stopping PPP receive thread..."); - ppp_lwip_stop_receive_thread(); - PPP_DEBUG_INFO("PPP receive thread stopped"); + - // 2. 强制等待确保线程完全退出 - PPP_DEBUG_INFO("Waiting for thread cleanup..."); - osal_msleep(200); // 给线程足够时间完全退出 + // 2. 强制等待确保完全退出 + PPP_DEBUG_INFO("Waiting for cleanup..."); + osal_msleep(200); // 给足够时间完全退出 // 3. 断开lwIP PPP连接 PPP_DEBUG_INFO("Disconnecting lwIP PPP..."); @@ -591,6 +603,23 @@ errcode_t ppp_stop_dial(void) // 等待确保前面的操作完成 osal_msleep(1000); + + // 1. 停止PPP数据接收(根据配置选择中断或轮询模式) + #if PPP_USE_INTERRUPT_MODE + // 中断模式:禁用UART中断 + PPP_DEBUG_INFO("Disabling PPP interrupt mode..."); + ppp_interrupt_disable(); + PPP_DEBUG_INFO("PPP interrupt mode disabled"); + + // 打印中断统计信息 + ppp_interrupt_print_stats(); + #else + // 轮询模式:停止接收线程 + PPP_DEBUG_INFO("Stopping PPP receive thread..."); + ppp_lwip_stop_receive_thread(); + PPP_DEBUG_INFO("PPP receive thread stopped"); + #endif + // // 方法1:发送+++转义序列切换调制解调器到命令模式 // PPP_DEBUG_INFO("Sending escape sequence (+++) to switch modem to command mode..."); // const char escape_seq[] = "+++"; diff --git a/code/sdk/zhihui/ppp/src/ppp_interrupt.c b/code/sdk/zhihui/ppp/src/ppp_interrupt.c new file mode 100644 index 000000000..1827c178a --- /dev/null +++ b/code/sdk/zhihui/ppp/src/ppp_interrupt.c @@ -0,0 +1,257 @@ +/** + * @file ppp_interrupt.c + * @brief PPP UART中断接收处理 - 类似SLIP但更简单 + * + * 核心优势: + * - 使用512字节UART硬件缓冲区,防止数据丢失 + * - 中断回调直接调用pppos_input_tcpip(),无需自己管理队列 + * - lwIP TCPIP线程自动处理协议解析,无需loop线程 + */ + +#include "ppp_interrupt.h" +#include "ppp_debug.h" +#include "ppp_lwip.h" +#include "ppp_serial.h" +#include "uart.h" +#include "hal_uart.h" +#include "errcode.h" +#include +#include + +/* lwIP相关头文件 */ +#include "lwip/err.h" +#include "netif/ppp/ppp.h" +#include "netif/ppp/pppos.h" + +/* UART硬件缓冲区配置 */ +#define PPP_UART_RX_BUFFER_SIZE 2048 // 硬件缓冲区大小(足够容纳MTU 1500 + PPP开销) + +/* 硬件缓冲区 */ +static uint8_t g_ppp_uart_rx_buffer[PPP_UART_RX_BUFFER_SIZE]; + +/* 中断使能标志 */ +static bool g_ppp_interrupt_enabled = false; + +/* 统计信息 */ +static ppp_interrupt_stats_t g_ppp_interrupt_stats = {0}; + +/* 外部函数声明(在ppp_lwip.c中实现) */ +extern ppp_pcb* ppp_lwip_get_pcb(void); +extern bool ppp_lwip_is_initialized(void); + +/** + * @brief UART接收中断回调函数(中断上下文) + * @note 此函数在中断上下文中调用,必须快速返回 + * + * 数据流: + * 1. UART硬件接收数据 -> 512字节硬件缓冲区 + * 2. 触发中断回调(本函数) + * 3. 直接调用pppos_input_tcpip() -> lwIP自动封装pbuf并发送到TCPIP线程 + * 4. TCPIP线程自动调用pppos_input() -> ppp_input() -> IP层 + * + * 关键:pppos_input_tcpip()是lwIP提供的线程安全接口, + * 专门设计为可以在中断上下文调用! + */ +void ppp_uart_rx_interrupt_handler(const void *buffer, uint16_t length, bool error) +{ + // 快速路径:参数校验 + if (error || !buffer || length == 0) { + if (error) { + g_ppp_interrupt_stats.error_count++; + } + return; + } + + // 更新接收统计 + g_ppp_interrupt_stats.total_interrupts++; + g_ppp_interrupt_stats.total_bytes_received += length; + + // 打印接收数据大小(中断中只打印简要信息) + PPP_DEBUG_INFO("PPP IRQ RX: %u bytes", length); + + // 获取PPP控制块 + ppp_pcb *ppp_pcb_ptr = ppp_lwip_get_pcb(); + if (!ppp_pcb_ptr || !ppp_lwip_is_initialized()) { + // PPP未初始化,丢弃数据 + g_ppp_interrupt_stats.dropped_not_initialized += length; + PPP_DEBUG_WARN("PPP IRQ: PPP not initialized, dropping %u bytes", length); + return; + } + + // 核心操作:直接调用lwIP的线程安全接口 + // 这个函数会: + // 1. 分配pbuf + // 2. 复制数据到pbuf + // 3. 通过tcpip_inpkt()发送到TCPIP线程 + // 4. TCPIP线程自动调用pppos_input()进行协议解析 + err_t result = pppos_input_tcpip(ppp_pcb_ptr, (u8_t *)buffer, length); + + if (result != ERR_OK) { + // 发送失败(可能是内存不足或队列满) + g_ppp_interrupt_stats.dropped_lwip_error += length; + g_ppp_interrupt_stats.lwip_error_count++; + PPP_DEBUG_WARN("PPP IRQ: pppos_input_tcpip failed: %d, dropped %u bytes", result, length); + } else { + // 成功发送到lwIP + g_ppp_interrupt_stats.total_bytes_sent_to_lwip += length; + g_ppp_interrupt_stats.success_count++; + } +} + +/** + * @brief 获取UART硬件缓冲区配置(供外部UART初始化使用) + * @param rx_buffer 返回接收缓冲区指针 + * @param buffer_size 返回缓冲区大小 + */ +void ppp_interrupt_get_buffer_config(void **rx_buffer, uint32_t *buffer_size) +{ + if (rx_buffer) { + *rx_buffer = g_ppp_uart_rx_buffer; + } + if (buffer_size) { + *buffer_size = PPP_UART_RX_BUFFER_SIZE; + } +} + +/** + * @brief 启用PPP UART中断接收 + * @return PPP_ERR_OK 成功,其他值失败 + * + * @note 调用此函数前: + * 1. ppp_serial必须已初始化(UART总线号从ppp_serial_get_uart_bus()获取) + * 2. UART必须已经初始化并配置了硬件缓冲区 + * 3. 使用 ppp_interrupt_get_buffer_config() 获取缓冲区配置 + */ +ppp_error_t ppp_interrupt_enable(void) +{ + if (g_ppp_interrupt_enabled) { + PPP_DEBUG_WARN("PPP interrupt already enabled"); + return PPP_ERR_OK; // 已启用 + } + + // 从ppp_serial获取UART总线号(避免重复定义) + uint8_t uart_bus = ppp_serial_get_uart_bus(); + + PPP_DEBUG_INFO("Enabling PPP UART interrupt (bus=%d)", uart_bus); + + // 注册UART中断回调 + // UART_RX_CONDITION_FULL_OR_SUFFICIENT_DATA_OR_IDLE (0x07): + // - 缓冲区满时触发(>= PPP_UART_RX_BUFFER_SIZE) + // - UART空闲时触发(接收间隔超时,防止小包延迟) + // + // 参数说明(参考SLIP的实现): + // - 第2个参数:触发条件标志(0x07 = 满 OR 空闲) + // - 第3个参数:缓冲区大小(2048字节,足够容纳1500 MTU + PPP开销) + // + // 平衡设计: + // - 2048字节缓冲区:可完整接收大包,避免溢出 + // - 空闲检测:小包(<2048字节)依赖UART空闲超时触发 + // - 参考SLIP使用256字节,PPP使用2048字节应对更大的MTU + errcode_t ret = uapi_uart_register_rx_callback( + uart_bus, + 0x07, // UART_RX_CONDITION_FULL_OR_SUFFICIENT_DATA_OR_IDLE + PPP_UART_RX_BUFFER_SIZE, // 缓冲区大小:2048字节 + ppp_uart_rx_interrupt_handler // 回调函数 + ); + + if (ret != ERRCODE_SUCC) { + PPP_DEBUG_ERROR("Failed to register UART RX callback: 0x%x", ret); + return PPP_ERR_UART; + } + + g_ppp_interrupt_enabled = true; + + PPP_DEBUG_INFO("PPP UART interrupt enabled successfully"); + PPP_DEBUG_INFO(" RX buffer: %d bytes (balanced for MTU 1500)", PPP_UART_RX_BUFFER_SIZE); + PPP_DEBUG_INFO(" Trigger: buffer full OR UART idle"); + PPP_DEBUG_INFO(" Data flow: UART IRQ -> pppos_input_tcpip() -> lwIP TCPIP thread"); + + return PPP_ERR_OK; +} + +/** + * @brief 禁用PPP UART中断接收 + * @return PPP_ERR_OK 成功,其他值失败 + */ +ppp_error_t ppp_interrupt_disable(void) +{ + if (!g_ppp_interrupt_enabled) { + return PPP_ERR_OK; + } + + // 从ppp_serial获取UART总线号 + uint8_t uart_bus = ppp_serial_get_uart_bus(); + + PPP_DEBUG_INFO("Disabling PPP UART interrupt (bus=%d)", uart_bus); + + // 注销中断回调 + uapi_uart_unregister_rx_callback(uart_bus); + + g_ppp_interrupt_enabled = false; + + PPP_DEBUG_INFO("PPP UART interrupt disabled"); + + return PPP_ERR_OK; +} + +/** + * @brief 检查中断是否已启用 + * @return true 已启用,false 未启用 + */ +bool ppp_interrupt_is_enabled(void) +{ + return g_ppp_interrupt_enabled; +} + +/** + * @brief 获取中断统计信息 + * @param stats 统计信息输出缓冲区 + */ +void ppp_interrupt_get_stats(ppp_interrupt_stats_t *stats) +{ + if (stats) { + memcpy(stats, &g_ppp_interrupt_stats, sizeof(ppp_interrupt_stats_t)); + } +} + +/** + * @brief 重置中断统计信息 + */ +void ppp_interrupt_reset_stats(void) +{ + memset(&g_ppp_interrupt_stats, 0, sizeof(ppp_interrupt_stats_t)); + PPP_DEBUG_INFO("PPP interrupt statistics reset"); +} + +/** + * @brief 打印中断统计信息 + */ +void ppp_interrupt_print_stats(void) +{ + PPP_DEBUG_INFO("=== PPP Interrupt Statistics ==="); + PPP_DEBUG_INFO("Interrupt Status: %s", g_ppp_interrupt_enabled ? "ENABLED" : "DISABLED"); + PPP_DEBUG_INFO("Hardware Buffer: %d bytes", PPP_UART_RX_BUFFER_SIZE); + PPP_DEBUG_INFO(""); + PPP_DEBUG_INFO("Total Interrupts: %u", g_ppp_interrupt_stats.total_interrupts); + PPP_DEBUG_INFO(" - Success: %u", g_ppp_interrupt_stats.success_count); + PPP_DEBUG_INFO(" - Error: %u", g_ppp_interrupt_stats.error_count); + PPP_DEBUG_INFO(" - lwIP Error: %u", g_ppp_interrupt_stats.lwip_error_count); + PPP_DEBUG_INFO(""); + PPP_DEBUG_INFO("Total Bytes Received: %u", g_ppp_interrupt_stats.total_bytes_received); + PPP_DEBUG_INFO("Total Bytes Sent to lwIP: %u", g_ppp_interrupt_stats.total_bytes_sent_to_lwip); + PPP_DEBUG_INFO(""); + PPP_DEBUG_INFO("Dropped Bytes:"); + PPP_DEBUG_INFO(" - Not Initialized: %u", g_ppp_interrupt_stats.dropped_not_initialized); + PPP_DEBUG_INFO(" - lwIP Error: %u", g_ppp_interrupt_stats.dropped_lwip_error); + PPP_DEBUG_INFO(""); + + // 计算丢包率 + if (g_ppp_interrupt_stats.total_bytes_received > 0) { + uint32_t total_dropped = g_ppp_interrupt_stats.dropped_not_initialized + + g_ppp_interrupt_stats.dropped_lwip_error; + uint32_t drop_rate = (total_dropped * 10000) / g_ppp_interrupt_stats.total_bytes_received; + PPP_DEBUG_INFO("Drop Rate: %u.%02u%%", drop_rate / 100, drop_rate % 100); + } + + PPP_DEBUG_INFO("================================"); +} \ No newline at end of file diff --git a/code/sdk/zhihui/ppp/src/ppp_lwip.c b/code/sdk/zhihui/ppp/src/ppp_lwip.c index 619b358f3..b57fa1e25 100644 --- a/code/sdk/zhihui/ppp/src/ppp_lwip.c +++ b/code/sdk/zhihui/ppp/src/ppp_lwip.c @@ -64,7 +64,7 @@ typedef struct { static ppp_output_stats_t g_ppp_output_stats = {0}; /* 内部函数声明 */ -static u32_t ppp_lwip_output_callback(ppp_pcb *pcb, u8_t *data, u32_t len, void *ctx); +static uint32_t ppp_lwip_output_callback(ppp_pcb *pcb, uint8_t *data, uint32_t len, void *ctx); static void ppp_lwip_link_status_callback(ppp_pcb *pcb, int err_code, void *ctx); static void ppp_lwip_status_callback(ppp_pcb *pcb, int err_code, void *ctx); static void ppp_lwip_netif_status_callback(struct netif *netif); @@ -293,16 +293,22 @@ ppp_error_t ppp_lwip_init(const ppp_lwip_config_t *config) g_lwip_ctx.ppp_pcb->lcp_wantoptions.neg_magicnumber = 0; g_lwip_ctx.ppp_pcb->lcp_allowoptions.neg_magicnumber = 0; + #if PPP_ENABLE_LARGE_MRU /* 启用并强制MRU协商 - 防止接收超大帧 */ g_lwip_ctx.ppp_pcb->lcp_wantoptions.neg_mru = 1; - g_lwip_ctx.ppp_pcb->lcp_allowoptions.neg_mru = 1; g_lwip_ctx.ppp_pcb->lcp_wantoptions.mru = PPP_MRU_SIZE; // 使用配置的1500字节 + g_lwip_ctx.ppp_pcb->lcp_allowoptions.neg_mru = 1; g_lwip_ctx.ppp_pcb->lcp_allowoptions.mru = PPP_MRU_SIZE; + #else + g_lwip_ctx.ppp_pcb->lcp_wantoptions.neg_mru = 0; + g_lwip_ctx.ppp_pcb->lcp_wantoptions.mru = 0; // 使用配置的1500字节 + g_lwip_ctx.ppp_pcb->lcp_allowoptions.neg_mru = 0; + g_lwip_ctx.ppp_pcb->lcp_allowoptions.mru = 0; + #endif PPP_DEBUG_INFO("MRU negotiation enabled: want=%u, allow=%u", - g_lwip_ctx.ppp_pcb->lcp_wantoptions.mru, - g_lwip_ctx.ppp_pcb->lcp_allowoptions.mru); - - /* 启用IPCP接受选项 (对应 ipcp-accept-local/remote) */ + g_lwip_ctx.ppp_pcb->lcp_wantoptions.mru, + g_lwip_ctx.ppp_pcb->lcp_allowoptions.mru); +/* 启用IPCP接受选项 (对应 ipcp-accept-local/remote) */ g_lwip_ctx.ppp_pcb->ipcp_wantoptions.accept_local = 1; g_lwip_ctx.ppp_pcb->ipcp_wantoptions.accept_remote = 1; @@ -930,13 +936,22 @@ static void ppp_lwip_netif_status_callback(struct netif *netif) */ static u32_t ppp_lwip_output_callback(ppp_pcb *pcb, u8_t *data, u32_t len, void *ctx) { - (void)pcb; - (void)ctx; + /* 严格的参数检查 - 防止空指针访问 */ + if (!pcb) { + PPP_DEBUG_ERROR("CRITICAL: ppp_pcb is NULL in output callback!"); + return 0; + } if (!data || len == 0) { PPP_DEBUG_ERROR("Invalid output data: data=%p, len=%u", data, len); return 0; } + + /* 检查数据长度是否合理(PPP最大帧1500字节 + 头部) */ + if (len > 2048) { + PPP_DEBUG_ERROR("Abnormal packet size: %u bytes (max expected 2048)", len); + return 0; + } /* 记录数据活动 */ ppp_lwip_record_data_activity(); @@ -1309,5 +1324,78 @@ ppp_error_t ppp_lwip_get_output_stats(ppp_lwip_output_stats_t *stats) return PPP_ERR_OK; } +/** + * @brief 获取PPP控制块(供中断处理模块使用) + * @return PPP控制块指针,如果未初始化则返回NULL + */ +ppp_pcb* ppp_lwip_get_pcb(void) +{ +#ifdef LWIP_PPP_SUPPORT + return g_lwip_ctx.ppp_pcb; +#else + return NULL; +#endif +} + +/** + * @brief 检查PPP lwIP是否已初始化(供中断处理模块使用) + * @return true 已初始化,false 未初始化 + */ +bool ppp_lwip_is_initialized(void) +{ + return g_lwip_ctx.initialized; +} + +/** + * @brief 打印lwIP内存使用情况(用于诊断) + */ +void ppp_lwip_print_memory_stats(void) +{ +#ifdef LWIP_PPP_SUPPORT + PPP_DEBUG_PRINT("\n=== lwIP Memory Statistics ===\n"); + + #if MEMP_STATS + // 打印pbuf池统计 + PPP_DEBUG_PRINT("PBUF Pools:\n"); + PPP_DEBUG_PRINT(" PBUF_POOL: used=%u, max=%u, avail=%u\n", + lwip_stats.memp[MEMP_PBUF_POOL]->used, + lwip_stats.memp[MEMP_PBUF_POOL]->max, + lwip_stats.memp[MEMP_PBUF_POOL]->avail); + PPP_DEBUG_PRINT(" PBUF: used=%u, max=%u, avail=%u\n", + lwip_stats.memp[MEMP_PBUF]->used, + lwip_stats.memp[MEMP_PBUF]->max, + lwip_stats.memp[MEMP_PBUF]->avail); + + // 打印TCP相关池统计 + PPP_DEBUG_PRINT("TCP Pools:\n"); + PPP_DEBUG_PRINT(" TCP_PCB: used=%u, max=%u, avail=%u\n", + lwip_stats.memp[MEMP_TCP_PCB]->used, + lwip_stats.memp[MEMP_TCP_PCB]->max, + lwip_stats.memp[MEMP_TCP_PCB]->avail); + PPP_DEBUG_PRINT(" TCP_SEG: used=%u, max=%u, avail=%u\n", + lwip_stats.memp[MEMP_TCP_SEG]->used, + lwip_stats.memp[MEMP_TCP_SEG]->max, + lwip_stats.memp[MEMP_TCP_SEG]->avail); + #else + PPP_DEBUG_PRINT(" MEMP_STATS not enabled in lwIP\n"); + #endif + + #if MEM_STATS + // 打印堆内存统计 + PPP_DEBUG_PRINT("Heap Memory:\n"); + PPP_DEBUG_PRINT(" Used: %u bytes\n", lwip_stats.mem.used); + PPP_DEBUG_PRINT(" Max: %u bytes\n", lwip_stats.mem.max); + PPP_DEBUG_PRINT(" Available: %u bytes\n", lwip_stats.mem.avail); + PPP_DEBUG_PRINT(" Errors: %u\n", lwip_stats.mem.err); + #else + PPP_DEBUG_PRINT(" MEM_STATS not enabled in lwIP\n"); + #endif + + PPP_DEBUG_PRINT("=================================\n\n"); +#else + PPP_DEBUG_PRINT("lwIP PPP support not compiled\n"); +#endif +} + #endif /* LWIP_PPP_SUPPORT */ \ No newline at end of file diff --git a/code/sdk/zhihui/ppp/src/ppp_lwip_thread.c b/code/sdk/zhihui/ppp/src/ppp_lwip_thread.c index bf798adaa..661032289 100644 --- a/code/sdk/zhihui/ppp/src/ppp_lwip_thread.c +++ b/code/sdk/zhihui/ppp/src/ppp_lwip_thread.c @@ -1,6 +1,10 @@ /** * @file ppp_lwip_thread.c - * @brief PPP接收线程实现 - 双线程架构:接收线程拼包 + 处理线程处理 + * @brief PPP接收线程实现 - 支持单线程和双线程模式 + * + * 线程模式配置: + * - PPP_USE_DUAL_THREAD=1: 双线程模式(接收线程拼包 + 处理线程处理) + * - PPP_USE_DUAL_THREAD=0: 单线程模式(接收线程直接处理) */ #include "ppp_api.h" @@ -11,104 +15,100 @@ #include "soc_osal.h" #include "schedule/osal_task.h" #include "msgqueue/osal_msgqueue.h" +#include "custom_utils.h" #include #include -/* PPP帧队列相关 */ +/* PPP接收模式配置(中断相关代码已移除,使用独立的ppp_interrupt模块) */ + +/* PPP线程模式配置 */ +#ifndef PPP_USE_DUAL_THREAD +#define PPP_USE_DUAL_THREAD 0 // 默认使用双线程模式 +#endif + +#if PPP_USE_DUAL_THREAD +#define PPP_THREAD_MODE_STR "DUAL_THREAD" +#else +#define PPP_THREAD_MODE_STR "SINGLE_THREAD" +#endif + +/* PPP帧队列相关(仅双线程模式使用) */ +#if PPP_USE_DUAL_THREAD #define PPP_FRAME_QUEUE_SIZE 32 // 队列大小 #define PPP_MAX_FRAME_SIZE 2048 // 从1500增加到2048 -/* PPP帧结构 */ +/* PPP帧结构(仅双线程模式使用) */ typedef struct { uint8_t *data; // 帧数据 uint32_t length; // 帧长度 } ppp_frame_t; +#endif /* PPP字节丢弃统计结构已在ppp_types.h中定义 */ /* PPP线程相关 */ -static osal_task *g_ppp_rx_thread = NULL; // 接收线程 -static osal_task *g_ppp_process_thread = NULL; // 处理线程 +static uint32_t g_ppp_rx_thread_handle = 0; // 接收线程句柄 +#if PPP_USE_DUAL_THREAD +static uint32_t g_ppp_process_thread_handle = 0; // 处理线程句柄(仅双线程模式) +#endif static volatile bool g_ppp_rx_thread_running = false; +#if PPP_USE_DUAL_THREAD static volatile bool g_ppp_process_thread_running = false; +#endif static volatile bool g_ppp_rx_thread_completed = false; +#if PPP_USE_DUAL_THREAD static volatile bool g_ppp_process_thread_completed = false; +#endif -/* PPP帧队列 */ +/* PPP帧队列(仅双线程模式使用) */ +#if PPP_USE_DUAL_THREAD static unsigned long g_ppp_frame_queue = 0xFFFFFFFFu; - -/* PPP接收缓冲区 */ -static uint8_t g_ppp_rx_buffer[PPP_RX_BUFFER_SIZE * 2]; // 双倍缓冲区用于拼包 -static uint32_t g_buffer_pos = 0; // 当前缓冲区位置 - -/* PPP帧标志 */ -#define PPP_FLAG_CHAR 0x7E // PPP帧标志字符 -#define PPP_ESCAPE_CHAR 0x7D // PPP转义字符 -#define PPP_ESCAPE_MASK 0x20 // PPP转义掩码 +#endif /* PPP丢弃统计 */ static ppp_discard_stats_t g_ppp_discard_stats = {0}; -/* PPP帧拼包状态 */ -typedef struct { - uint8_t *buffer; // 拼包缓冲区 - uint32_t buffer_size; // 缓冲区大小 - uint32_t data_len; // 当前数据长度 - bool in_frame; // 是否在帧内 - bool escaped; // 是否转义状态 -} ppp_frame_assembler_t; - -static ppp_frame_assembler_t g_frame_assembler = {0}; +static ppp_error_t ppp_process_data_block(const uint8_t *data, uint32_t length); /** - * @brief 初始化PPP帧拼包器 + * @brief 处理PPP数据并打印详细信息(单线程模式专用) */ -static ppp_error_t ppp_frame_assembler_init(void) +static ppp_error_t ppp_process_and_print_data(const uint8_t *data, uint32_t length) { - g_frame_assembler.buffer_size = PPP_MAX_FRAME_SIZE; - g_frame_assembler.buffer = (uint8_t *)malloc(g_frame_assembler.buffer_size); - if (!g_frame_assembler.buffer) { - PPP_DEBUG_ERROR("Failed to allocate frame assembler buffer"); - return PPP_ERR_MEMORY; + if (!data || length == 0) { + return PPP_ERR_INVALID_PARAM; } - g_frame_assembler.data_len = 0; - g_frame_assembler.in_frame = false; - g_frame_assembler.escaped = false; + // 打印详细的接收数据(在处理阶段,不影响接收效率) + PPP_DEBUG_INFO("PPP Processing: %u bytes", length); + PPP_DEBUG_PRINT("PPP RX Data: "); + for (uint32_t i = 0; i < length; i++) { + PPP_DEBUG_PRINT("%02X ", data[i]); + // 每16字节换行,便于阅读 + if ((i + 1) % 16 == 0) { + PPP_DEBUG_PRINT("\n "); + } + } + PPP_DEBUG_PRINT("\n"); - PPP_DEBUG_INFO("PPP frame assembler initialized with %u bytes buffer", - g_frame_assembler.buffer_size); - return PPP_ERR_OK; -} - -/** - * @brief 清理PPP帧拼包器 - */ -static void ppp_frame_assembler_cleanup(void) -{ - if (g_frame_assembler.buffer) { - free(g_frame_assembler.buffer); - g_frame_assembler.buffer = NULL; + // 调用lwIP处理 + ppp_error_t lwip_ret = ppp_lwip_input(data, length); + if (lwip_ret != PPP_ERR_OK) { + PPP_DEBUG_WARN("lwIP input failed: %d", lwip_ret); + return lwip_ret; + } else { + PPP_DEBUG_DEBUG("PPP data processed successfully by lwIP"); + return PPP_ERR_OK; } - memset(&g_frame_assembler, 0, sizeof(g_frame_assembler)); -} - -/** - * @brief 重置帧拼包状态 - */ -static void ppp_frame_assembler_reset(void) -{ - g_frame_assembler.data_len = 0; - g_frame_assembler.in_frame = false; - g_frame_assembler.escaped = false; } +#if PPP_USE_DUAL_THREAD /** - * @brief 发送完整PPP帧到队列 + * @brief 处理接收到的数据块(双线程模式) */ -static ppp_error_t ppp_send_frame_to_queue(const uint8_t *data, uint32_t length) +static ppp_error_t ppp_process_data_block(const uint8_t *data, uint32_t length) { - if (!data || length == 0 || length > PPP_MAX_FRAME_SIZE) { + if (!data || length == 0) { return PPP_ERR_INVALID_PARAM; } @@ -118,9 +118,8 @@ static ppp_error_t ppp_send_frame_to_queue(const uint8_t *data, uint32_t length) PPP_DEBUG_ERROR("Failed to allocate frame structure"); // 统计内存分配失败丢弃 - g_ppp_discard_stats.total_frames_discarded++; - g_ppp_discard_stats.malloc_fail_bytes += length; g_ppp_discard_stats.total_bytes_discarded += length; + g_ppp_discard_stats.malloc_fail_bytes += length; return PPP_ERR_MEMORY; } @@ -131,9 +130,8 @@ static ppp_error_t ppp_send_frame_to_queue(const uint8_t *data, uint32_t length) PPP_DEBUG_ERROR("Failed to allocate frame data"); // 统计内存分配失败丢弃 - g_ppp_discard_stats.total_frames_discarded++; - g_ppp_discard_stats.malloc_fail_bytes += length; g_ppp_discard_stats.total_bytes_discarded += length; + g_ppp_discard_stats.malloc_fail_bytes += length; free(frame); return PPP_ERR_MEMORY; @@ -146,100 +144,30 @@ static ppp_error_t ppp_send_frame_to_queue(const uint8_t *data, uint32_t length) // 发送到队列 int ret = osal_msg_queue_write_copy(g_ppp_frame_queue, &frame, sizeof(ppp_frame_t *), 0); if (ret != OSAL_SUCCESS) { - PPP_DEBUG_WARN("Failed to send frame to queue: %d, dropping frame", ret); + PPP_DEBUG_WARN("Failed to send data to queue: %d, dropping data", ret); // 统计队列满丢弃 - g_ppp_discard_stats.total_frames_discarded++; - g_ppp_discard_stats.queue_full_bytes += length; g_ppp_discard_stats.total_bytes_discarded += length; + g_ppp_discard_stats.queue_full_bytes += length; free(frame->data); free(frame); - return PPP_ERR_UNKNOWN; // 使用已定义的错误码 - } - - PPP_DEBUG_DEBUG("Sent complete PPP frame to queue: %u bytes", length); - - // 更新帧统计 - g_ppp_discard_stats.total_frames_received++; - - return PPP_ERR_OK; -} - -/** - * @brief 处理接收到的字节,进行PPP帧拼包(保留完整帧格式) - */ -static ppp_error_t ppp_process_received_byte(uint8_t byte) -{ - // PPP帧标志字符 - if (byte == 0x7E) { - if (g_frame_assembler.in_frame && g_frame_assembler.data_len > 0) { - // 帧结束,添加结束标志并发送完整帧 - if (g_frame_assembler.data_len < g_frame_assembler.buffer_size) { - g_frame_assembler.buffer[g_frame_assembler.data_len++] = 0x7E; - - PPP_DEBUG_DEBUG("Complete PPP frame assembled: %u bytes (with 0x7E flags)", g_frame_assembler.data_len); - - // 发送完整帧(包含首尾0x7E标志) - ppp_send_frame_to_queue(g_frame_assembler.buffer, g_frame_assembler.data_len); - } else { - // 缓冲区满,丢弃帧 - PPP_DEBUG_WARN("Frame buffer full, dropping frame"); - g_ppp_discard_stats.total_frames_discarded++; - g_ppp_discard_stats.oversized_frame_bytes += g_frame_assembler.data_len; - g_ppp_discard_stats.total_bytes_discarded += g_frame_assembler.data_len; - } - } else if (g_frame_assembler.in_frame && g_frame_assembler.data_len == 0) { - // 连续的0x7E标志,这是正常的 - PPP_DEBUG_DEBUG("Consecutive 0x7E flag, continuing"); - } else { - // 不在帧内收到0x7E,开始新帧 - PPP_DEBUG_DEBUG("Starting new frame with 0x7E"); - } - - // 开始新帧,首先添加起始0x7E标志 - ppp_frame_assembler_reset(); - g_frame_assembler.in_frame = true; - g_frame_assembler.buffer[g_frame_assembler.data_len++] = 0x7E; - return PPP_ERR_OK; - } - - // 如果不在帧内,忽略数据 - if (!g_frame_assembler.in_frame) { - // 统计被忽略的数据 - g_ppp_discard_stats.total_bytes_discarded++; - g_ppp_discard_stats.incomplete_frame_bytes++; - PPP_DEBUG_DEBUG("Ignoring byte 0x%02X outside frame", byte); - return PPP_ERR_OK; - } - - // 检查缓冲区空间(为结束0x7E预留1字节) - if (g_frame_assembler.data_len >= (g_frame_assembler.buffer_size - 1)) { - PPP_DEBUG_WARN("Frame too large (%u bytes), resetting assembler", g_frame_assembler.data_len); - - // 统计超大帧丢弃 - g_ppp_discard_stats.total_frames_discarded++; - g_ppp_discard_stats.oversized_frame_bytes += g_frame_assembler.data_len; - g_ppp_discard_stats.total_bytes_discarded += g_frame_assembler.data_len; - - ppp_frame_assembler_reset(); return PPP_ERR_UNKNOWN; } - // 直接添加字节到帧缓冲区(保留原始格式,包括转义字符) - g_frame_assembler.buffer[g_frame_assembler.data_len++] = byte; - + PPP_DEBUG_DEBUG("Sent data block to queue: %u bytes", length); return PPP_ERR_OK; } +#endif /** - * @brief PPP数据接收线程 - 只负责拼包 + * @brief PPP数据接收线程 - 轮询模式(类似SLIP的高效策略) */ static void ppp_lwip_receive_thread(void *arg) { (void)arg; - - PPP_DEBUG_INFO("PPP receive thread started (frame assembly mode)"); + + PPP_DEBUG_INFO("PPP receive thread started (polling mode)"); // 分配串口接收缓冲区 uint8_t *rx_buffer = (uint8_t *)malloc(PPP_RX_BUFFER_SIZE); @@ -250,60 +178,88 @@ static void ppp_lwip_receive_thread(void *arg) } uint32_t bytes_read = 0; - uint32_t total_bytes = 0, total_frames = 0; + uint32_t total_bytes = 0; + uint32_t idle_count = 0; - PPP_DEBUG_DEBUG("PPP receive thread: allocated %d bytes RX buffer", PPP_RX_BUFFER_SIZE); + PPP_DEBUG_INFO("PPP receive thread: allocated %d bytes RX buffer", PPP_RX_BUFFER_SIZE); - // 主循环:从串口读取数据并拼包 + // 主循环:类似SLIP的连续读取策略 while (g_ppp_rx_thread_running) { - // 从串口读取数据 - ppp_error_t ret = ppp_serial_read(rx_buffer, PPP_RX_BUFFER_SIZE, &bytes_read); - - if (ret == PPP_ERR_OK && bytes_read > 0) { - total_bytes += bytes_read; + uint32_t batch_bytes = 0; + uint32_t read_count = 0; + + // 连续读取直到无数据,类似SLIP的slipif_poll + while (g_ppp_rx_thread_running) { + ppp_error_t ret = ppp_serial_tryread(rx_buffer, PPP_RX_BUFFER_SIZE, &bytes_read); - // 更新接收字节统计 - g_ppp_discard_stats.total_bytes_received += bytes_read; - - PPP_DEBUG_DEBUG("PPP RX: Got %u bytes (total: %u bytes)", bytes_read, total_bytes); + if (ret == PPP_ERR_OK && bytes_read > 0) { + total_bytes += bytes_read; + batch_bytes += bytes_read; + read_count++; + + // 更新接收字节统计 + g_ppp_discard_stats.total_bytes_received += bytes_read; + + PPP_DEBUG_DEBUG("PPP RX: Got %u bytes (read #%u)", bytes_read, read_count); - // 逐字节处理,进行PPP帧拼包(保留完整帧格式) - for (uint32_t i = 0; i < bytes_read; i++) { - ppp_error_t process_ret = ppp_process_received_byte(rx_buffer[i]); +#if PPP_USE_DUAL_THREAD + // 双线程模式:发送到队列由处理线程处理 + ppp_error_t process_ret = ppp_process_data_block(rx_buffer, bytes_read); if (process_ret != PPP_ERR_OK) { - // 字节处理失败,统计丢弃 - g_ppp_discard_stats.total_bytes_discarded++; - g_ppp_discard_stats.invalid_frame_bytes++; + PPP_DEBUG_WARN("Failed to process data block: %d", process_ret); } - } - } else if (ret != PPP_ERR_OK) { - // 读取错误 - if (!g_ppp_rx_thread_running) { - PPP_DEBUG_DEBUG("PPP receive thread stopping, ignoring read error"); +#else + // 单线程模式:调用处理函数(包含详细数据打印) + ppp_error_t process_ret = ppp_process_and_print_data(rx_buffer, bytes_read); + if (process_ret != PPP_ERR_OK) { + g_ppp_discard_stats.total_bytes_discarded += bytes_read; + } else { + g_ppp_discard_stats.total_bytes_processed += bytes_read; + } +#endif + } else if (ret != PPP_ERR_OK) { + // 读取错误 + if (!g_ppp_rx_thread_running) { + PPP_DEBUG_DEBUG("PPP receive thread stopping, ignoring read error"); + break; + } + PPP_DEBUG_ERROR("PPP serial tryread error: %d", ret); + break; + } else { + // 无数据,退出连续读取循环 break; } - PPP_DEBUG_ERROR("PPP serial read error: %d", ret); - } else { - // 没有数据,短暂休眠 + } + + if (batch_bytes > 0) { + // 有数据处理,重置空闲计数器 + idle_count = 0; + PPP_DEBUG_DEBUG("PPP batch: %u bytes in %u reads", batch_bytes, read_count); + + // 短暂休眠后继续,保持高响应性 osal_msleep(1); + } else { + // 无数据时使用类似SLIP的休眠策略 + idle_count++; + osal_msleep(20); // 类似SLIP的20ms休眠,不打印避免日志干扰 } - // 定期打印统计信息 - if ((total_frames % 100) == 0 && total_frames > 0) { - PPP_DEBUG_DEBUG("PPP RX: %u frames assembled, %u total bytes", - total_frames, total_bytes); + // 定期打印统计信息(减少频率) + if ((total_bytes % 4096) == 0 && total_bytes > 0) { + PPP_DEBUG_INFO("PPP RX: %u total bytes received", total_bytes); } } // 释放缓冲区 free(rx_buffer); - PPP_DEBUG_INFO("PPP receive thread completed"); + PPP_DEBUG_INFO("PPP receive thread completed (polling mode), processed %u bytes", total_bytes); g_ppp_rx_thread_completed = true; } +#if PPP_USE_DUAL_THREAD /** - * @brief PPP帧处理线程 - 从队列获取完整帧并处理 + * @brief PPP数据处理线程 - 从队列获取数据块并处理(仅双线程模式) */ static void ppp_lwip_process_thread(void *arg) { @@ -311,45 +267,47 @@ static void ppp_lwip_process_thread(void *arg) PPP_DEBUG_INFO("PPP process thread started"); - uint32_t processed_frames = 0; + uint32_t processed_blocks = 0; uint32_t processed_bytes = 0; - // 主循环:从队列获取完整PPP帧并处理 + // 主循环:从队列获取数据块并处理 while (g_ppp_process_thread_running) { - ppp_frame_t *frame = NULL; + ppp_frame_t *data_block = NULL; uint32_t msg_size = sizeof(ppp_frame_t *); - // 从队列获取帧(超时100ms) - int ret = osal_msg_queue_read_copy(g_ppp_frame_queue, &frame, &msg_size, OSAL_MSGQ_WAIT_FOREVER); + // 从队列获取数据块(超时100ms,避免无限阻塞) + int ret = osal_msg_queue_read_copy(g_ppp_frame_queue, &data_block, &msg_size, OSAL_WAIT_FOREVER); - if (ret == OSAL_SUCCESS && frame) { - // 处理完整PPP帧 - PPP_DEBUG_DEBUG("Processing PPP frame: %u bytes", frame->length); + if (ret == OSAL_SUCCESS && data_block) { + // 处理数据块 + PPP_DEBUG_DEBUG("Processing data block: %u bytes", data_block->length); - // 调用lwIP处理 - ppp_lwip_input(frame->data, frame->length); + PPP_DEBUG_PRINT("PPP RX Data: "); + for (uint32_t i = 0; i < data_block->length ; i++) { + PPP_DEBUG_PRINT("%02X ", data_block->data[i]); + } + PPP_DEBUG_PRINT("\n"); + // 调用lwIP流式处理 + ppp_error_t process_ret = ppp_process_and_print_data(data_block->data, data_block->length); - // 更新统计 - processed_frames++; - processed_bytes += frame->length; - // 更新处理统计(统计实际传递给lwIP的字节数,但排除我们添加的0x7E) - g_ppp_discard_stats.total_frames_processed++; - // 减去我们添加的额外0x7E标志(每帧最多添加1个) - uint32_t original_bytes = frame->length; - if (frame->length > 0 && frame->data[frame->length-1] == 0x7E) { - original_bytes--; // 减去结束时添加的0x7E + // 更新统计 + processed_blocks++; + processed_bytes += data_block->length; + if (process_ret != PPP_ERR_OK) { + g_ppp_discard_stats.total_bytes_discarded += data_block->length; + } else { + g_ppp_discard_stats.total_bytes_processed += data_block->length; } - g_ppp_discard_stats.total_bytes_processed += original_bytes; - // 释放帧内存 - free(frame->data); - free(frame); + // 释放数据块内存 + free(data_block->data); + free(data_block); // 定期打印统计 - if ((processed_frames % 50) == 0) { - PPP_DEBUG_DEBUG("PPP Process: %u frames, %u bytes processed", - processed_frames, processed_bytes); + if ((processed_blocks % 50) == 0) { + PPP_DEBUG_DEBUG("PPP Process: %u blocks, %u bytes processed", + processed_blocks, processed_bytes); } } else if (ret != OSAL_SUCCESS) { // 超时或其他错误 @@ -357,95 +315,118 @@ static void ppp_lwip_process_thread(void *arg) PPP_DEBUG_DEBUG("PPP process thread stopping"); break; } - // 超时是正常的,其他错误才需要警告 - if (ret != 0) { // 假设超时返回非0值 - PPP_DEBUG_WARN("Failed to read from frame queue: %d", ret); - } + // 超时是正常的,不需要警告。只有真正的错误才需要警告 + // OSAL_FAILURE (-1) 通常表示超时,这是正常的 + // 这里我们不再对超时进行警告,因为这是正常的队列空闲状态 + PPP_DEBUG_DEBUG("Queue read timeout (normal when no data), ret: %d", ret); } // 超时是正常的,继续循环 } - // 清理队列中剩余的帧 - ppp_frame_t *frame = NULL; + // 清理队列中剩余的数据块 + ppp_frame_t *data_block = NULL; uint32_t msg_size = sizeof(ppp_frame_t *); - while (osal_msg_queue_read_copy(g_ppp_frame_queue, &frame, &msg_size, 0) == OSAL_SUCCESS) { - if (frame) { - free(frame->data); - free(frame); + while (osal_msg_queue_read_copy(g_ppp_frame_queue, &data_block, &msg_size, 0) == OSAL_SUCCESS) { + if (data_block) { + free(data_block->data); + free(data_block); } } - PPP_DEBUG_INFO("PPP process thread completed, processed %u frames, %u bytes", - processed_frames, processed_bytes); + PPP_DEBUG_INFO("PPP process thread completed, processed %u blocks, %u bytes", + processed_blocks, processed_bytes); g_ppp_process_thread_completed = true; } +#endif /* PPP_USE_DUAL_THREAD */ /** * @brief 启动PPP接收和处理线程 */ ppp_error_t ppp_lwip_start_receive_thread(void) { - if (g_ppp_rx_thread != NULL || g_ppp_process_thread != NULL) { +#if PPP_USE_DUAL_THREAD + if (g_ppp_rx_thread_handle != 0 || g_ppp_process_thread_handle != 0) { PPP_DEBUG_WARN("PPP threads already running"); return PPP_ERR_INVALID_STATE; } - PPP_DEBUG_INFO("Starting PPP receive and process threads"); - - // 初始化帧拼包器 - ppp_error_t ret = ppp_frame_assembler_init(); - if (ret != PPP_ERR_OK) { - PPP_DEBUG_ERROR("Failed to initialize frame assembler"); - return ret; + PPP_DEBUG_INFO("Starting PPP receive and process threads (" PPP_THREAD_MODE_STR " mode)"); +#else + if (g_ppp_rx_thread_handle != 0) { + PPP_DEBUG_WARN("PPP thread already running"); + return PPP_ERR_INVALID_STATE; } - // 创建帧队列 - int queue_ret = osal_msg_queue_create("ppp_frames", PPP_FRAME_QUEUE_SIZE, - &g_ppp_frame_queue, 0, sizeof(ppp_frame_t *)); - if (queue_ret != OSAL_SUCCESS) { - PPP_DEBUG_ERROR("Failed to create PPP frame queue: %d", queue_ret); - ppp_frame_assembler_cleanup(); - return PPP_ERR_MEMORY; - } + PPP_DEBUG_INFO("Starting PPP receive thread (" PPP_THREAD_MODE_STR " mode)"); +#endif // 重置完成标志 g_ppp_rx_thread_completed = false; + +#if PPP_USE_DUAL_THREAD g_ppp_process_thread_completed = false; + // 创建数据队列(仅双线程模式) + int queue_ret = osal_msg_queue_create("ppp_data", PPP_FRAME_QUEUE_SIZE, + &g_ppp_frame_queue, 0, sizeof(ppp_frame_t *)); + if (queue_ret != OSAL_SUCCESS) { + PPP_DEBUG_ERROR("Failed to create PPP data queue: %d", queue_ret); + return PPP_ERR_MEMORY; + } + // 启动处理线程 g_ppp_process_thread_running = true; - g_ppp_process_thread = osal_kthread_create((osal_kthread_handler)ppp_lwip_process_thread, - NULL, "ppp_process", 4096); - if (!g_ppp_process_thread) { - PPP_DEBUG_ERROR("Failed to create PPP process thread"); + + app_task_definition_t process_task_cfg = {0}; + process_task_cfg.create_queue_handle = NULL; + process_task_cfg.attr.name = "ppp_process"; + process_task_cfg.attr.priority = osPriorityNormal1; + process_task_cfg.attr.stack_size = 4096; + process_task_cfg.func = ppp_lwip_process_thread; + process_task_cfg.task_handle = &g_ppp_process_thread_handle; + process_task_cfg.use_mem = USE_EXTEND_MEM; + + errcode_t process_ret = uapi_create_osThread_custom(&process_task_cfg); + if (process_ret != ERRCODE_SUCC) { + PPP_DEBUG_ERROR("Failed to create PPP process thread: %d", process_ret); g_ppp_process_thread_running = false; osal_msg_queue_delete(g_ppp_frame_queue); g_ppp_frame_queue = 0; - ppp_frame_assembler_cleanup(); return PPP_ERR_MEMORY; } +#endif /* PPP_USE_DUAL_THREAD */ // 启动接收线程 g_ppp_rx_thread_running = true; - g_ppp_rx_thread = osal_kthread_create((osal_kthread_handler)ppp_lwip_receive_thread, - NULL, "ppp_receive", 4096); - if (!g_ppp_rx_thread) { - PPP_DEBUG_ERROR("Failed to create PPP receive thread"); + + app_task_definition_t rx_task_cfg = {0}; + rx_task_cfg.create_queue_handle = NULL; + rx_task_cfg.attr.name = "ppp_receive"; + rx_task_cfg.attr.priority = osPriorityNormal1; + rx_task_cfg.attr.stack_size = 6144; // 单线程模式需要更大栈空间 + rx_task_cfg.func = ppp_lwip_receive_thread; + rx_task_cfg.task_handle = &g_ppp_rx_thread_handle; + rx_task_cfg.use_mem = USE_EXTEND_MEM; + + errcode_t rx_ret = uapi_create_osThread_custom(&rx_task_cfg); + if (rx_ret != ERRCODE_SUCC) { + PPP_DEBUG_ERROR("Failed to create PPP receive thread: %d", rx_ret); g_ppp_rx_thread_running = false; +#if PPP_USE_DUAL_THREAD // 清理处理线程 g_ppp_process_thread_running = false; - osal_kthread_destroy(g_ppp_process_thread, 0); - g_ppp_process_thread = NULL; + osThreadTerminate((osThreadId_t)g_ppp_process_thread_handle); + g_ppp_process_thread_handle = 0; // 清理资源 osal_msg_queue_delete(g_ppp_frame_queue); g_ppp_frame_queue = 0; - ppp_frame_assembler_cleanup(); +#endif return PPP_ERR_MEMORY; } - PPP_DEBUG_INFO("PPP receive and process threads started successfully"); + PPP_DEBUG_INFO("PPP threads started successfully (" PPP_THREAD_MODE_STR " mode)"); return PPP_ERR_OK; } @@ -454,14 +435,20 @@ ppp_error_t ppp_lwip_start_receive_thread(void) */ ppp_error_t ppp_lwip_stop_receive_thread(void) { - if (!g_ppp_rx_thread && !g_ppp_process_thread) { +#if PPP_USE_DUAL_THREAD + if (g_ppp_rx_thread_handle == 0 && g_ppp_process_thread_handle == 0) { + return PPP_ERR_OK; + } +#else + if (g_ppp_rx_thread_handle == 0) { return PPP_ERR_OK; } +#endif - PPP_DEBUG_INFO("Stopping PPP receive and process threads"); + PPP_DEBUG_INFO("Stopping PPP threads (" PPP_THREAD_MODE_STR " mode)"); // 停止接收线程 - if (g_ppp_rx_thread) { + if (g_ppp_rx_thread_handle != 0) { g_ppp_rx_thread_running = false; // 等待接收线程完成 @@ -479,12 +466,13 @@ ppp_error_t ppp_lwip_stop_receive_thread(void) PPP_DEBUG_WARN("PPP receive thread did not complete within timeout"); } - osal_kthread_destroy(g_ppp_rx_thread, 0); - g_ppp_rx_thread = NULL; + osThreadTerminate((osThreadId_t)g_ppp_rx_thread_handle); + g_ppp_rx_thread_handle = 0; } +#if PPP_USE_DUAL_THREAD // 停止处理线程 - if (g_ppp_process_thread) { + if (g_ppp_process_thread_handle != 0) { g_ppp_process_thread_running = false; // 等待处理线程完成 @@ -502,8 +490,8 @@ ppp_error_t ppp_lwip_stop_receive_thread(void) PPP_DEBUG_WARN("PPP process thread did not complete within timeout"); } - osal_kthread_destroy(g_ppp_process_thread, 0); - g_ppp_process_thread = NULL; + osThreadTerminate((osThreadId_t)g_ppp_process_thread_handle); + g_ppp_process_thread_handle = 0; } // 清理队列 @@ -511,15 +499,15 @@ ppp_error_t ppp_lwip_stop_receive_thread(void) osal_msg_queue_delete(g_ppp_frame_queue); g_ppp_frame_queue = 0; } - - // 清理帧拼包器 - ppp_frame_assembler_cleanup(); +#endif // 重置状态标志 g_ppp_rx_thread_running = false; - g_ppp_process_thread_running = false; g_ppp_rx_thread_completed = false; +#if PPP_USE_DUAL_THREAD + g_ppp_process_thread_running = false; g_ppp_process_thread_completed = false; +#endif PPP_DEBUG_INFO("PPP receive and process threads stopped"); return PPP_ERR_OK; @@ -530,8 +518,12 @@ ppp_error_t ppp_lwip_stop_receive_thread(void) */ bool ppp_lwip_is_receive_thread_running(void) { - return g_ppp_rx_thread_running && (g_ppp_rx_thread != NULL) && - g_ppp_process_thread_running && (g_ppp_process_thread != NULL); +#if PPP_USE_DUAL_THREAD + return g_ppp_rx_thread_running && (g_ppp_rx_thread_handle != 0) && + g_ppp_process_thread_running && (g_ppp_process_thread_handle != 0); +#else + return g_ppp_rx_thread_running && (g_ppp_rx_thread_handle != 0); +#endif } /** diff --git a/code/sdk/zhihui/ppp/src/ppp_serial.c b/code/sdk/zhihui/ppp/src/ppp_serial.c index 822706a93..a444b64bd 100644 --- a/code/sdk/zhihui/ppp/src/ppp_serial.c +++ b/code/sdk/zhihui/ppp/src/ppp_serial.c @@ -2,6 +2,7 @@ #include "ppp_config.h" #include "ppp_types.h" #include "ppp_debug.h" +#include "ppp_interrupt.h" // 添加此头文件以访问 ppp_interrupt_get_buffer_config #include "uart.h" // 简化路径 #include "schedule/osal_task.h" #include "semaphore/osal_semaphore.h" @@ -171,14 +172,26 @@ ppp_error_t ppp_serial_init(const ppp_serial_config_t *config) // } // PPP_DEBUG_INFO("UART deinitialized successfully"); - /* 初始化UART */ - PPP_DEBUG_DEBUG("Calling uapi_uart_init..."); - // errcode_t ret = uapi_uart_init(g_serial_ctx.uart_bus, - // &g_serial_ctx.pin_config, - // &g_serial_ctx.uart_attr, - // NULL, - // &g_serial_ctx.buffer_config); - errcode_t ret = uapi_uart_set_attr(g_serial_ctx.uart_bus, &g_serial_ctx.uart_attr); + /* 初始化UART - 使用PPP中断模块提供的硬件缓冲区配置 */ + PPP_DEBUG_DEBUG("Calling uapi_uart_init with buffer config..."); + + // 从ppp_interrupt模块获取硬件缓冲区配置 + void *hw_buffer = NULL; + uint32_t hw_buffer_size = 0; + ppp_interrupt_get_buffer_config(&hw_buffer, &hw_buffer_size); + + uart_buffer_config_t hw_buffer_config = { + .rx_buffer = hw_buffer, + .rx_buffer_size = hw_buffer_size + }; + + PPP_DEBUG_INFO("Using hardware buffer: %u bytes at 0x%p", hw_buffer_size, hw_buffer); + + errcode_t ret = uapi_uart_init(g_serial_ctx.uart_bus, + &g_serial_ctx.pin_config, + &g_serial_ctx.uart_attr, + NULL, + &hw_buffer_config); // 使用硬件缓冲区配置 if (ret != ERRCODE_SUCC) { PPP_DEBUG_ERROR("Failed to initialize UART: error code = %d (0x%x)", ret, ret); PPP_DEBUG_ERROR("UART init failed with bus=%u, tx_pin=%u, rx_pin=%u, baudrate=%u", @@ -191,7 +204,7 @@ ppp_error_t ppp_serial_init(const ppp_serial_config_t *config) return PPP_ERR_UART; } - PPP_DEBUG_INFO("UART initialized successfully (error code = %d)", ret); + PPP_DEBUG_INFO("UART initialized successfully with %u-byte hardware buffer", hw_buffer_size); /* 创建同步对象 */ PPP_DEBUG_DEBUG("Creating synchronization objects..."); @@ -484,33 +497,55 @@ ppp_error_t ppp_serial_read(uint8_t *buffer, uint32_t buffer_size, uint32_t *byt case PPP_SERIAL_MODE_PPP_DATA: /* PPP数据模式:使用优化的直接UART读取,平衡响应性和完整性 */ { - /* 使用适中的超时时间来平衡响应性和帧完整性 */ - // PPP_DEBUG_DEBUG("PPP mode: Attempting UART read (timeout: 200ms)"); - /* 优化PPP数据模式读取超时:使用配置的优化值以提高协商响应速度 */ - int32_t read_result = uapi_uart_read(g_serial_ctx.uart_bus, buffer, buffer_size, PPP_OPTIMIZED_UART_READ_TIMEOUT); - if (read_result > 0) { - *bytes_read = (uint32_t)read_result; - g_serial_ctx.rx_bytes += *bytes_read; + /* 使用类似SLIP的高效读取策略: + * 1. 首次尝试非阻塞读取(1ms超时) + * 2. 如果有数据,继续快速读取直到无数据 + * 3. 这样可以批量读取连续到达的数据,提高效率 + */ + uint32_t total_read = 0; + const uint32_t MAX_BATCH_READ = 8; // 最多批量读取8次 + uint32_t batch_count = 0; + + while (batch_count < MAX_BATCH_READ && total_read < buffer_size) { + uint32_t remaining = buffer_size - total_read; + uint8_t *current_buffer = buffer + total_read; - PPP_DEBUG_INFO("PPP UART Read: Received %u bytes (optimized mode)", *bytes_read); - PPP_DEBUG_PRINT("PPP RX Data: "); - for (uint32_t i = 0; i < *bytes_read && i < 16; i++) { - PPP_DEBUG_PRINT("%02X ", buffer[i]); - } - PPP_DEBUG_PRINT("... (total %u bytes)\n", *bytes_read); + // 使用短超时进行读取,类似sio_tryread的1ms超时 + int32_t read_result = uapi_uart_read(g_serial_ctx.uart_bus, current_buffer, + remaining, batch_count == 0 ? 10 : 1); - return PPP_ERR_OK; - } else if (read_result == 0) { - /* 超时,无数据可读 */ - *bytes_read = 0; - // PPP_DEBUG_DEBUG("PPP UART Read: No data available (timeout)"); + if (read_result > 0) { + total_read += (uint32_t)read_result; + g_serial_ctx.rx_bytes += (uint32_t)read_result; + batch_count++; + + PPP_DEBUG_DEBUG("PPP batch read %u: got %d bytes (total: %u)", + batch_count, read_result, total_read); + + // 如果读取的数据少于请求的数据,说明没有更多数据了 + if ((uint32_t)read_result < remaining) { + break; + } + } else if (read_result == 0) { + // 超时,无更多数据 + break; + } else { + // 读取错误 + if (total_read == 0) { + PPP_DEBUG_ERROR("PPP UART Read: Error %d", read_result); + return PPP_ERR_UART; + } + break; + } + } + + *bytes_read = total_read; + + if (total_read > 0) { + PPP_DEBUG_DEBUG("PPP optimized read: %u bytes in %u batches", total_read, batch_count); return PPP_ERR_OK; } else { - /* UART读取错误 */ - *bytes_read = 0; - g_serial_ctx.rx_errors++; - PPP_DEBUG_ERROR("PPP UART Read: Error %d", read_result); - return PPP_ERR_UART; + return PPP_ERR_OK; // 无数据但无错误 } } break; @@ -549,6 +584,42 @@ ppp_error_t ppp_serial_read(uint8_t *buffer, uint32_t buffer_size, uint32_t *byt return result; } +/** + * @brief PPP快速尝试读取 - 类似SLIP的sio_tryread,使用1ms超时 + * @param buffer 接收缓冲区 + * @param buffer_size 缓冲区大小 + * @param bytes_read 实际读取字节数 + * @return 错误码 + */ +ppp_error_t ppp_serial_tryread(uint8_t *buffer, uint32_t buffer_size, uint32_t *bytes_read) +{ + if (!g_serial_ctx.initialized || !buffer || !bytes_read || buffer_size == 0) { + return PPP_ERR_INVALID_PARAM; + } + + *bytes_read = 0; + + if (g_serial_ctx.current_mode == PPP_SERIAL_MODE_PPP_DATA) { + /* PPP数据模式:使用1ms超时快速读取,类似SLIP的sio_tryread */ + int32_t read_result = uapi_uart_read(g_serial_ctx.uart_bus, buffer, buffer_size, 1); + if (read_result > 0) { + *bytes_read = (uint32_t)read_result; + g_serial_ctx.rx_bytes += *bytes_read; + return PPP_ERR_OK; + } else if (read_result == 0) { + /* 超时,无数据可读 */ + return PPP_ERR_OK; + } else { + /* UART读取错误 */ + PPP_DEBUG_ERROR("PPP UART tryread error: %d", read_result); + return PPP_ERR_UART; + } + } else { + /* AT模式:使用标准读取 */ + return ppp_serial_read(buffer, buffer_size, bytes_read); + } +} + /** * @brief 等待数据接收 */ @@ -786,7 +857,7 @@ ppp_serial_mode_t ppp_serial_get_mode(void) /** * @brief 获取统计信息 */ -uint32_t ppp_serial_get_stats(ppp_serial_stats_t *stats) +ppp_error_t ppp_serial_get_stats(ppp_serial_stats_t *stats) { if (!g_serial_ctx.initialized || !stats) { return PPP_ERR_INVALID_PARAM; @@ -808,6 +879,15 @@ uint32_t ppp_serial_get_stats(ppp_serial_stats_t *stats) osal_mutex_unlock(&g_serial_ctx.tx_mutex); return PPP_ERR_OK; +} + +/** + * @brief 获取UART总线号 + * @return UART总线号,如果未初始化返回UART_BUS_0 + */ +uint8_t ppp_serial_get_uart_bus(void) +{ + return g_serial_ctx.uart_bus; } /* 内部函数实现 */ diff --git a/code/sdk/zhihui/tcptest/include/zhihui_tcp_test.h b/code/sdk/zhihui/tcptest/include/zhihui_tcp_test.h index b3b7cfe38..80ac3726a 100644 --- a/code/sdk/zhihui/tcptest/include/zhihui_tcp_test.h +++ b/code/sdk/zhihui/tcptest/include/zhihui_tcp_test.h @@ -118,7 +118,7 @@ extern "C" { #define TCP_TEST_DEFAULT_RECV_TIMEOUT_MS 1000 // 默认接收超时1000ms /* 新的固定数据模式配置 */ -#define TCP_TEST_FIXED_DATA_SIZE 1024 // 固定数据长度,可通过宏配置 +#define TCP_TEST_FIXED_DATA_SIZE 2048 // 固定数据长度,可通过宏配置 #define TCP_DATA_PATTERN_CYCLE 256 // 数据模式循环:0x00 ~ 0xFF /* TCP测试配置结构体 */ diff --git a/code/sdk/zhihui/tcptest/src/zhihui_tcp_test.c b/code/sdk/zhihui/tcptest/src/zhihui_tcp_test.c index a59c7567f..0f62ad9cd 100644 --- a/code/sdk/zhihui/tcptest/src/zhihui_tcp_test.c +++ b/code/sdk/zhihui/tcptest/src/zhihui_tcp_test.c @@ -350,7 +350,7 @@ static errcode_t tcp_test_create_connection_threads(uint16_t conn_id) } *send_arg = conn_id; - TCP_TASK_CREATE(tcp_test_send_worker, send_arg, "tcp_send", 2048); + TCP_TASK_CREATE(tcp_test_send_worker, send_arg, "tcp_send", 4096); // 创建接收线程 uint16_t *recv_arg = malloc(sizeof(uint16_t)); @@ -361,11 +361,10 @@ static errcode_t tcp_test_create_connection_threads(uint16_t conn_id) } *recv_arg = conn_id; - TCP_TASK_CREATE(tcp_test_recv_worker, recv_arg, "tcp_recv", 2048); + TCP_TASK_CREATE(tcp_test_recv_worker, recv_arg, "tcp_recv", 4096); return ERRCODE_SUCC; } - /** * @brief 发送工作线程 - 只负责发送固定模式数据 */ @@ -379,19 +378,23 @@ static void* tcp_test_send_worker(void *arg) conn->send_thread_running = true; - // 分配发送缓冲区 - 使用动态分配避免栈溢出 - uint8_t *send_buffer = malloc(TCP_TEST_FIXED_DATA_SIZE); - if (!send_buffer) { + // 发送固定大小的数据包 + uint8_t *send_data = (uint8_t *)malloc(TCP_TEST_FIXED_DATA_SIZE); + if (send_data == NULL) { ZHIHUI_TCP_LOG_ERROR("[发送 %03d] 内存分配失败", conn_id); - conn->send_thread_running = false; - return NULL; + goto cleanup; } - uint32_t test_end_time = g_tcp_test_manager.global_stats.start_time + - (config->test_duration * 1000); + // 填充测试数据 + for (int i = 0; i < TCP_TEST_FIXED_DATA_SIZE; i++) { + send_data[i] = (uint8_t)(i & 0xFF); + } ZHIHUI_TCP_LOG_INFO("[发送 %03d] 开始发送数据,固定长度: %d 字节", conn_id, TCP_TEST_FIXED_DATA_SIZE); + uint32_t test_end_time = g_tcp_test_manager.global_stats.start_time + + (config->test_duration * 1000); + uint32_t packet_counter = 0; uint32_t send_interval_ms = config->send_interval_ms; uint32_t last_send_time = 0; @@ -408,7 +411,7 @@ static void* tcp_test_send_worker(void *arg) if (can_send) { // 生成固定模式数据 - errcode_t ret = tcp_test_generate_fixed_pattern_data(current_pattern, send_buffer, TCP_TEST_FIXED_DATA_SIZE); + errcode_t ret = tcp_test_generate_fixed_pattern_data(current_pattern, send_data, TCP_TEST_FIXED_DATA_SIZE); if (ret != ERRCODE_SUCC) { TCP_MUTEX_LOCK(g_tcp_stats_mutex); conn->send_errors++; @@ -419,7 +422,7 @@ static void* tcp_test_send_worker(void *arg) } // 发送数据 - ssize_t bytes_sent = TCP_SEND(conn->socket_fd, send_buffer, TCP_TEST_FIXED_DATA_SIZE, 0); + ssize_t bytes_sent = TCP_SEND(conn->socket_fd, send_data, TCP_TEST_FIXED_DATA_SIZE, 0); if (bytes_sent > 0) { TCP_MUTEX_LOCK(g_tcp_stats_mutex); @@ -459,7 +462,8 @@ static void* tcp_test_send_worker(void *arg) TCP_SLEEP_MS(send_interval_ms ); } - free(send_buffer); + free(send_data); +cleanup: conn->send_thread_running = false; ZHIHUI_TCP_LOG_INFO("[发送 %03d] 发送线程结束,总发送: %d 包", conn_id, packet_counter); @@ -480,7 +484,7 @@ static void* tcp_test_recv_worker(void *arg) conn->recv_thread_running = true; // 分配接收缓冲区 - uint8_t *recv_buffer = malloc(TCP_TEST_FIXED_DATA_SIZE * 4); // 多缓冲一些数据 + uint8_t *recv_buffer = malloc(TCP_TEST_FIXED_DATA_SIZE * 4); // 多缓冲一些数据 if (!recv_buffer) { ZHIHUI_TCP_LOG_ERROR("[接收 %03d] 内存分配失败", conn_id); conn->recv_errors++; -- 2.17.1