@@ -1,24 +1,29 @@
/*
* 模块名称: CH395F Driver Test Suite
* 模块功能: CH395F 驱动各功能模块的测试入口,分阶段组织测试用例
* 模块功能: CH395F 驱动各功能模块的测试入口,分阶段组织测试用例,
* 覆盖 SPI 命令路径、Socket 状态机、大文件传输、边界条件等
* 适用平台: STM32F4 系列( CH395F 以太网芯片)
* 作者:王建<EFBFBD> ? * 创建日期<E697A5> ?026-07-19
* 修改记录<EFBFBD> ? * 2026-07-19 Phase 1/2/3 implemented
* 作者:王建锋
* 创建日期: 2026-07-19
* 修改记录:
* 2026-07-19 Phase 1/2/3 implemented
* 2026-07-22 全面重构为单连接模式,增加 Phase 5/8/10
*/
/* 头文件包含区 */
# include <string.h>
# include <stdio.h>
# include "ch395f_test.h"
# include "ch395f.h"
# include "net_socket.h"
# include "net_select.h"
# define DBG_TAG "[CH395T]"
# include "dbg_log.h"
test_stats_t g_test_stats ;
/*
* Phase 2 配置默认<EFBFBD> ? */
* Phase 2 配置默认值
*/
# ifndef PHASE2_REMOTE_IP
# define PHASE2_REMOTE_IP "192.168.1.2"
# endif
@@ -36,7 +41,8 @@ test_stats_t g_test_stats;
# endif
/*
* Phase 3 配置默认<EFBFBD> ? */
* Phase 3 配置默认值
*/
# ifndef PHASE3_REMOTE_IP
# define PHASE3_REMOTE_IP "192.168.1.2"
# endif
@@ -54,24 +60,36 @@ test_stats_t g_test_stats;
# endif
/*
* 函数功能: 恢<EFBFBD> ?TCP Server 多连接模式的数据 Socket 配置
* 入口参数: port - TCP 源端口号
* <20> ?<3F> ?值: <E580BC> ? * 限定条件: CH395F 协议栈已初始<E5889D> ? * 函数说明: 重新设置数<E7BDAE> ?Socket 的协议类型和源端<E6BA90> ? */
static void restore_data_sockets ( uint16_t port ) {
int i = 0 ;
for ( i = NET_TCP_SERVER_DATA_SOCK_START ; i < NET_MAX_SOCKETS ; i + + ) {
ch395f_set_proto_type ( ( uint8_t ) i , CH395F_PROTO_TYPE_TCP ) ;
ch395f_set_sour_port ( ( uint8_t ) i , port ) ;
}
DBG_INFO ( " data socks restored (port %d) " , port ) ;
}
* Phase 5 配置默认值(大文件传输)
*/
# ifndef PHASE5_LOCAL_PORT
# define PHASE5_LOCAL_PORT 60000
# endif
# ifndef PHASE5_FILE_SIZE
# define PHASE5_FILE_SIZE 20480 /* 20KB */
# endif
# ifndef PHASE5_CHUNK_SIZE
# define PHASE5_CHUNK_SIZE 4096 /* 单次 NET 层收发块大小 */
# endif
# ifndef PHASE5_BUF_SIZE
# define PHASE5_BUF_SIZE 65536 /* 接收缓冲最大 64KB */
# endif
/*
* ============================ Phase 1 ============================
* 函数功能:寄存器读写 + 中断一致性基础测试
* 入口参数:无
* <20> ?<3F> ?值: <E580BC> ? * 限定条件: CH395F 芯片正常应答
* 函数说明: 包<EFBC9A> ?GET_VERSION / GET_CMD_STATUS / GET_IP_INF <20> ?9 <20> ? */
* Phase 8 配置默认值(边界条件)
*/
# ifndef PHASE8_LOCAL_PORT
# define PHASE8_LOCAL_PORT 8080
# endif
/*
* Phase 10 配置默认值( KeepAlive)
*/
# ifndef PHASE10_KEEPALIVE_IDLE_MS
# define PHASE10_KEEPALIVE_IDLE_MS 60000 /* 空闲 60 秒触发探测 */
# endif
/* ============================ Phase 1 ============================ */
void ch395f_phase1_tests ( void ) {
uint8_t i = 0 ;
@@ -79,18 +97,15 @@ void ch395f_phase1_tests(void) {
DBG_INFO ( " === CH395F Phase 1 Tests === " ) ;
/* Test 1: GET_VERSION */
ver = ch395f_get_version ( ) ;
TEST_CHECK ( ver > 0 , " GET_VERSION: 0x%02X (ver=%d) " , ver , ver & 0x3F ) ;
/* Test 2: GET_CMD_STATUS */
uint8_t cmd_st = ch395f_get_cmd_status ( ) ;
TEST_CHECK ( cmd_st = = CH395F_ERR_SUCCESS ,
" GET_CMD_STATUS: 0x%02X (%s) " , cmd_st ,
cmd_st = = CH395F_ERR_SUCCESS ? " SUCCESS " :
cmd_st = = CH395F_ERR_BUSY ? " BUSY " : " OTHER " ) ;
/* Test 3: GET_IP_INF */
uint8_t ip_info [ 20 ] ;
ch395f_get_ip_inf ( ip_info ) ;
DBG_INFO ( " GET_IP_INF: " ) ;
@@ -106,34 +121,27 @@ void ch395f_phase1_tests(void) {
TEST_CHECK ( ip_info [ 8 ] = = 255 & & ip_info [ 9 ] = = 255 & & ip_info [ 10 ] = = 255 & & ip_info [ 11 ] = = 0 ,
" MASK match " ) ;
/* Test 4: GINT_STATUS_ALL (2-byte version) */
uint16_t gint_all = ch395f_get_glob_int_status_all ( ) ;
TEST_CHECK ( ( gint_all & 0xFF ) = = 0x04 | | ( gint_all & 0xFF ) = = 0x00 ,
" GINT_STATUS_ALL: 0x%04X (lo=0x%02X hi=0x%02X) " ,
gint_all , gint_all & 0xFF , ( gint_all > > 8 ) & 0xFF ) ;
" GINT_STATUS_ALL: 0x%04X " , gint_all ) ;
/* Test 5: GINT_STATUS (1-byte version) <20> ?read-clear check */
uint8_t gint_1b = ch395f_get_glob_int_status ( ) ;
TEST_CHECK ( gint_1b = = 0x00 ,
" GINT_STATUS_1byte: 0x%02X (expect 0x00 after read-clear ) " , gint_1b ) ;
" GINT_STATUS_1byte: 0x%02X (expect 0x00) " , gint_1b ) ;
/* Test 6: SOCK_STATUS(0) */
{
uint8_t st [ 2 ] ;
ch395f_get_socket_status ( 0 , st ) ;
TEST_CHECK ( st [ 0 ] = = 0x05 & & st [ 1 ] = = 0x01 ,
" SOCK_STATUS(0): sock=0x%02X(LISTEN) , tcp=0x%02X(LISTEN) " , st [ 0 ] , st [ 1 ] ) ;
" SOCK_STATUS(0): sock=0x%02X, tcp=0x%02X " , st [ 0 ] , st [ 1 ] ) ;
}
/* Test 7: SET_ARP */
ch395f_set_arp ( 10 , 5 ) ;
TEST_CHECK ( 1 , " SET_ARP(period=10*100ms, cnt=5): OK " ) ;
/* Test 8: SET_TTL */
ch395f_set_ttl ( 0 , 64 ) ;
TEST_CHECK ( 1 , " SET_TTL(sock=0, ttl=64): OK " ) ;
/* Test 9: CLEAR_RECV_BUF + SOCK_INT poll */
ch395f_clear_recv_buf ( 0 ) ;
TEST_CHECK ( 1 , " CLEAR_RECV_BUF(sock=0): OK " ) ;
for ( i = 0 ; i < 8 ; i + + ) {
@@ -144,10 +152,7 @@ void ch395f_phase1_tests(void) {
TEST_REPORT ( " Phase 1 " ) ;
}
/*
* ============================ Phase 2 ============================
* 函数功能: TCP Client 收发 + 关闭重连 × 3 <20> ? * 入口参数:无
* <20> ?<3F> ?值: <E580BC> ? * 限定条件: PC 端已启动 TCP Server( 默<EFBC88> ?192.168.1.2:8081<38> ? * 函数说明:每轮连接→发送→接收→关闭,连续 3 <20> ? */
/* ============================ Phase 2 ============================ */
void ch395f_phase2_tests ( void ) {
uint8_t sock = 0 ;
@@ -160,7 +165,6 @@ void ch395f_phase2_tests(void) {
DBG_INFO ( " === CH395F Phase 2 Tests (TCP Client - Direct Driver) === " ) ;
/* 解析目标 IP */
uint8_t ip_arr [ 4 ] ;
{
uint32_t a [ 4 ] ;
@@ -172,12 +176,9 @@ void ch395f_phase2_tests(void) {
}
DBG_INFO ( " Target: %s:%d " , PHASE2_REMOTE_IP , PHASE2_REMOTE_PORT ) ;
sock = ( uint8_t ) ( NET_MAX_SOCKETS - 1 ) ;
sock = 0 ;
/* 一次性配<E680A7> ?*/
ch395f_set_proto_type ( sock , CH395F_PROTO_TYPE_TCP ) ;
ch395f_set_send_buf ( sock , 28 , 2 ) ;
ch395f_set_recv_buf ( sock , 30 , 2 ) ;
ch395f_set_des_ip ( sock , ip_arr ) ;
ch395f_set_des_port ( sock , PHASE2_REMOTE_PORT ) ;
@@ -190,7 +191,6 @@ void ch395f_phase2_tests(void) {
if ( ch395f_open_socket ( sock ) ! = CH395F_ERR_SUCCESS ) { TEST_CHECK ( 0 , " open socket " ) ; iter_pass = 0 ; goto phase2_iter_done ; }
if ( ch395f_tcp_connect ( sock ) ! = CH395F_ERR_SUCCESS ) { TEST_CHECK ( 0 , " tcp connect " ) ; iter_pass = 0 ; ch395f_close_socket ( sock ) ; goto phase2_iter_done ; }
/* 等待 CONNECT */
tick = HAL_GetTick ( ) ;
while ( HAL_GetTick ( ) - tick < PHASE2_TIMEOUT_MS ) {
int_st = ch395f_get_glob_int_status_all ( ) ;
@@ -204,7 +204,6 @@ void ch395f_phase2_tests(void) {
HAL_Delay ( 100 ) ;
/* 发送测试数<E8AF95> ?*/
{
uint8_t test_buf [ 64 ] = { 0 } ;
const char * msg = " HelloFromCH395F_TCP_Client! This is a 64-byte test message. " ;
@@ -214,7 +213,6 @@ void ch395f_phase2_tests(void) {
ch395f_write_send_buf ( sock , test_buf , 64 ) ;
}
/* Echo 循环:收数据 <20> ?回发 */
{
int got_echo = 0 ;
tick = HAL_GetTick ( ) ;
@@ -243,7 +241,6 @@ void ch395f_phase2_tests(void) {
phase2_iter_done :
if ( iter_pass ) TEST_CHECK ( 1 , " Iteration %d PASS " , i + 1 ) ;
/* 关闭 socket */
ch395f_close_socket ( sock ) ;
{
uint8_t st [ 2 ] ;
@@ -258,15 +255,10 @@ phase2_iter_done:
HAL_Delay ( 200 ) ;
}
restore_data_sockets ( 8080 ) ;
TEST_REPORT ( " Phase 2 " ) ;
}
/*
* ============================ Phase 3 ============================
* 函数功能: UDP Server Echo + PING + 大包测试
* 入口参数:无
* <20> ?<3F> ?值: <E580BC> ? * 限定条件: PC 端已启动 UDP Client( 默<EFBC88> ?192.168.1.2:8082<38> ? * 函数说明: 包<EFBC9A> ?20 次回显<E59B9E> ? <20> ?PING<4E> ?0 次大包收<E58C85> ? */
/* ============================ Phase 3 ============================ */
void ch395f_phase3_tests ( void ) {
uint8_t sock = 0 ;
@@ -277,12 +269,9 @@ void ch395f_phase3_tests(void) {
DBG_INFO ( " === CH395F Phase 3 Tests (UDP) === " ) ;
sock = ( uint8_t ) ( NET_MAX_SOCKETS - 1 ) ;
sock = 0 ;
/* UDP Server 模式: DesIP=0xFFFFFFFF <20> ?接受任意来源 */
ch395f_set_proto_type ( sock , CH395F_PROTO_TYPE_UDP ) ;
ch395f_set_send_buf ( sock , 28 , 2 ) ;
ch395f_set_recv_buf ( sock , 30 , 2 ) ;
ch395f_set_sour_port ( sock , PHASE3_LOCAL_PORT ) ;
{
uint8_t zero_ip [ 4 ] = { 0xFF , 0xFF , 0xFF , 0xFF } ;
@@ -295,7 +284,6 @@ void ch395f_phase3_tests(void) {
HAL_Delay ( 200 ) ;
/* Echo 循环: 收<EFBC9A> ?<3F> ?解析来源 <20> ?回显 */
for ( i = 0 ; i < PHASE3_ECHO_COUNT ; i + + ) {
tick = HAL_GetTick ( ) ;
echo_ok = 0 ;
@@ -342,110 +330,143 @@ void ch395f_phase3_tests(void) {
ch395f_close_socket ( sock ) ;
TEST_CHECK ( 1 , " UDP socket closed " ) ;
restore_data_sockets ( 8080 ) ;
TEST_REPORT ( " Phase 3 " ) ;
}
/* ============================ Phase 4 ============================ */
/*
* ============================ Phase 4 ============================
* NET <20> ?TCP Echo: 在主循环中调用, 用 net_recv + net_send 回显
* NET 层 TCP Echo( 单连接模式)
* 在 netTask 中运行,仅操作 Socket 0
* 覆盖: 64B、1460B(MSS)、4096B(缓冲满) 回显
*/
# ifdef ENABLE_PHASE4_TESTS
static char s_phase4_buf [ 2048 ] ;
static char s_phase4_buf [ 8192 ] ;
static uint32_t s_phase4_echo_count = 0 ;
# endif
/*
* 函数功能: NET <20> ?TCP Echo 测试
* 入口参数:无
* <20> ?<3F> ?值: <E580BC> ? * 限定条件: net_init 已完<E5B7B2> ? * 函数说明:在 netTask 中运行,轮询 Socket 1~7 回显接收数据
*/
void ch395f_phase4_tests ( void ) {
# ifdef ENABLE_PHASE4_TESTS
for ( int i = NET_TCP_SERVER_DATA_SOCK_START ; i < NET_MAX_SOCKETS ; i + + ) {
net_sock_t * sk = net_get_sock ( i ) ;
if ( sk = = NULL | | ! sk - > in_use | | sk - > state ! = NET_SOCK_STATE_ESTABLISHED ) {
continue ;
}
net_sock_t * sk = net_get_sock ( 0 ) ;
if ( sk = = NULL | | ! sk - > in_use ) {
return ;
}
if ( sk - > state = = NET_SOCK_STATE_ESTABLISHED ) {
int n = net_recv_sock ( sk , s_phase4_buf , sizeof ( s_phase4_buf ) , NET_MSG_DONTWAIT ) ;
if ( n > 0 ) {
net_send_sock ( sk , s_phase4_buf , n ) ;
int sent = net_send_sock ( sk , s_phase4_buf , n ) ;
if ( sent = = n ) {
s_phase4_echo_count + + ;
if ( s_phase4_echo_count < = 10 | | ( s_phase4_echo_count % 100 = = 0 ) ) {
DBG_INFO ( " Phase4: echo #%lu (%d bytes) " , s_phase4_echo_count , n ) ;
}
}
}
}
# endif
}
/* ============================ Phase 5 ============================ */
/*
* ============================ Phase 5 ============================
* NET <20> ?UDP Echo: netTask 中运行, <E8A18C> ?net_recvfrom + net_sendto 回显
* NET 层大文件传输测试
* 验证超过 CH395F 内部缓冲区大小( 4KB) 的连续数据收发
* 覆盖: 20KB、64KB 分块传输
*/
# ifndef PHASE5_LOCAL_PORT
# define PHASE5_LOCAL_PORT 60000
# endif
# ifdef ENABLE_PHASE5_TESTS
static int s_phase5_sock = - 1 ;
static char s_phase5_buf [ 2048 ] ;
static uint32_t s_phase5_count = 0 ;
static int s_phase5_active = 0 ;
static uint32_t s_phase5_total_sent = 0 ;
static uint32_t s_phase5_total_recv = 0 ;
static uint8_t s_phase5_tx_buf [ PHASE5_FILE_SIZE ] ;
static uint8_t s_phase5_rx_buf [ PHASE5_BUF_SIZE ] ;
static void phase5_fill_pattern ( uint8_t * buf , uint32_t size , uint32_t offset ) {
for ( uint32_t i = 0 ; i < size ; i + + ) {
buf [ i ] = ( uint8_t ) ( ( offset + i ) & 0xFF ) ;
}
}
static int phase5_verify_pattern ( uint8_t * buf , uint32_t size , uint32_t offset ) {
for ( uint32_t i = 0 ; i < size ; i + + ) {
if ( buf [ i ] ! = ( uint8_t ) ( ( offset + i ) & 0xFF ) ) {
DBG_ERROR ( " Phase5: verify fail at offset %lu (expected 0x%02X, got 0x%02X) " ,
offset + i , ( uint8_t ) ( ( offset + i ) & 0xFF ) , buf [ i ] ) ;
return 0 ;
}
}
return 1 ;
}
# endif
/*
* 函数功能: NET <20> ?UDP Echo 初始<E5889D> ? * 入口参数:无
* <20> ?<3F> ?值: <E580BC> ? * 限定条件: net_init 已完<E5B7B2> ? * 函数说明: 创<EFBC9A> ?UDP Socket 并绑定本地端<E59CB0> ? */
void ch395f_phase5_init ( void ) {
# ifdef ENABLE_PHASE5_TESTS
struct net_sockaddr_in addr ;
s_phase5_sock = net_socket ( NET_AF_INET , NET_SOCK_DGRAM , 0 ) ;
if ( s_phase5_sock < 0 ) {
DBG_ERROR ( " Phase5: net_socket failed " ) ;
return ;
}
memset ( & addr , 0 , sizeof ( addr ) ) ;
addr . sin_family = NET_AF_INET ;
addr . sin_port = net_htons ( PHASE5_LOCAL_PORT ) ;
if ( net_bind ( s_phase5_sock , ( struct net_sockaddr * ) & addr , sizeof ( addr ) ) < 0 ) {
DBG_ERROR ( " Phase5: net_bind failed " ) ;
net_close_sock ( net_get_sock ( s_phase5_sock ) ) ;
s_phase5_sock = - 1 ;
return ;
}
s_phase5_count = 0 ;
DBG_INFO ( " Phase5: UDP socket %d bound to port %d " , s_phase5_sock , PHASE5_LOCAL_PORT ) ;
s_phase5_active = 0 ;
s_phase5_total_sent = 0 ;
s_phase5_total_recv = 0 ;
DBG_INFO ( " Phase5: file transfer test, size=%d bytes, chunk=%d bytes " ,
PHASE5_FILE_SIZE , PHASE5_CHUNK_SIZE ) ;
# endif
}
/*
* 函数功能: NET <20> ?UDP Echo 测试
* 入口参数:无
* <20> ?<3F> ?值: <E580BC> ? * 限定条件: ch395f_phase5_init 已成功调<E58A9F> ? * 函数说明:在 netTask 中运行,回显 UDP 接收数据
*/
void ch395f_phase5_tests ( void ) {
# ifdef ENABLE_PHASE5_TESTS
if ( s_phase5_sock < 0 ) return ;
net_sock_t * sk = net_get_sock ( 0 ) ;
if ( sk = = NULL | | ! sk - > in_use ) {
return ;
}
struct net_sockaddr_in src ;
int addrlen = sizeof ( src ) ;
if ( ! s_phase5_active ) {
if ( sk - > state = = NET_SOCK_STATE_ESTABLISHED ) {
s_phase5_active = 1 ;
s_phase5_total_sent = 0 ;
s_phase5_total_recv = 0 ;
int n = net_recvfrom ( s_phase5_sock , s_phase5_buf , sizeof ( s_phase5_buf ) ,
NET_MSG_DONTWAIT ,
( struct net_sockaddr * ) & src , & addrlen ) ;
if ( n > 0 ) {
net_sendto ( s_phase5_sock , s_phase5_buf , n , 0 ,
( struct net_sockaddr * ) & src , sizeof ( src ) ) ;
s_phase5_count + + ;
TEST_CHECK ( 1 , " Phase5: UDP echo #%lu (%d bytes) " , s_phase5_count , n ) ;
phase5_fill_pattern ( s_phase5_tx_buf , PHASE5_FILE_SIZE , 0 ) ;
DBG_INFO ( " Phase5: starting %d-byte file transfer (sock=0) " , PHASE5_FILE_SIZE ) ;
}
return ;
}
/* 发送阶段:分块发送,每块 PHASE5_CHUNK_SIZE */
if ( s_phase5_total_sent < PHASE5_FILE_SIZE ) {
uint32_t remaining = PHASE5_FILE_SIZE - s_phase5_total_sent ;
uint32_t chunk = ( remaining > PHASE5_CHUNK_SIZE ) ? PHASE5_CHUNK_SIZE : remaining ;
int sent = net_send_sock ( sk , s_phase5_tx_buf + s_phase5_total_sent , ( int ) chunk ) ;
if ( sent > 0 ) {
s_phase5_total_sent + = ( uint32_t ) sent ;
DBG_INFO ( " Phase5: sent %d/%d bytes " , s_phase5_total_sent , PHASE5_FILE_SIZE ) ;
}
return ;
}
/* 接收阶段:分块接收并验证 */
if ( s_phase5_total_recv < PHASE5_FILE_SIZE ) {
uint32_t remaining = PHASE5_FILE_SIZE - s_phase5_total_recv ;
int max_read = ( int ) ( remaining < NET_TASK_BUF_SIZE ? remaining : NET_TASK_BUF_SIZE ) ;
int n = net_recv_sock ( sk , s_phase5_rx_buf , max_read , NET_MSG_DONTWAIT ) ;
if ( n > 0 ) {
int ok = phase5_verify_pattern ( s_phase5_rx_buf , ( uint32_t ) n , s_phase5_total_recv ) ;
TEST_CHECK ( ok , " Phase5: recv verify at offset %lu (%d/%d bytes) " ,
s_phase5_total_recv , s_phase5_total_recv + ( uint32_t ) n , PHASE5_FILE_SIZE ) ;
s_phase5_total_recv + = ( uint32_t ) n ;
}
return ;
}
/* 完成 */
if ( s_phase5_total_recv > = PHASE5_FILE_SIZE ) {
TEST_CHECK ( 1 , " Phase5: %d-byte file transfer complete (sent=%lu, recv=%lu) " ,
PHASE5_FILE_SIZE , s_phase5_total_sent , s_phase5_total_recv ) ;
s_phase5_active = 0 ;
}
# endif
}
/*
* Phase 6 配置默认<E9BB98> ? */
/* ============================ Phase 6 ============================ */
# ifndef PHASE6_ANNOUNCE_PORT
# define PHASE6_ANNOUNCE_PORT 60001
# endif
@@ -453,14 +474,6 @@ void ch395f_phase5_tests(void) {
# define PHASE6_ANNOUNCE_SOCK 6
# endif
/*
* ============================ Phase 6 ============================
* 函数功能: DHCP 自动获取 IP 测试
* 入口参数:无
* <20> ?<3F> ?值: <E580BC> ? * 限定条件: 局域网内存<E58685> ?DHCP Server
* 函数说明: 启<EFBC9A> ?DHCP <20> ?轮询等待 IP <20> ?打印结果 <20> ?广播宣告 <20> ?HELLO 回显
*/
void ch395f_phase6_tests ( void ) {
# ifdef ENABLE_PHASE6_TESTS
uint8_t status = 0 ;
@@ -469,11 +482,9 @@ void ch395f_phase6_tests(void) {
DBG_INFO ( " === CH395F Phase 6 Tests (DHCP) === " ) ;
/* 1. 启用 DHCP */
ch395f_set_dhcp ( 1 ) ;
TEST_CHECK ( 1 , " DHCP enabled, waiting for IP... " ) ;
/* 2. 轮询等待 DHCP 完成 */
HAL_Delay ( 500 ) ;
tick = HAL_GetTick ( ) ;
while ( HAL_GetTick ( ) - tick < 30000 ) {
@@ -498,24 +509,20 @@ void ch395f_phase6_tests(void) {
goto phase6_end ;
}
/* 3. 读取获取<E88EB7> ?IP */
ch395f_get_ip_inf ( ip_info ) ;
DBG_INFO ( " IP: %d.%d.%d.%d " , ip_info [ 0 ] , ip_info [ 1 ] , ip_info [ 2 ] , ip_info [ 3 ] ) ;
DBG_INFO ( " GW: %d.%d.%d.%d " , ip_info [ 4 ] , ip_info [ 5 ] , ip_info [ 6 ] , ip_info [ 7 ] ) ;
DBG_INFO ( " MASK: %d.%d.%d.%d " , ip_info [ 8 ] , ip_info [ 9 ] , ip_info [ 10 ] , ip_info [ 11 ] ) ;
DBG_INFO ( " DNS1: %d.%d.%d.%d " , ip_info [ 12 ] , ip_info [ 13 ] , ip_info [ 14 ] , ip_info [ 15 ] ) ;
TEST_CHECK ( ip_info [ 0 ] ! = 0 | | ip_info [ 1 ] ! = 0 | | ip_info [ 2 ] ! = 0 | | ip_info [ 3 ] ! = 0 ,
" IP = %d.%d.%d.%d (non-zero) " , ip_info [ 0 ] , ip_info [ 1 ] , ip_info [ 2 ] , ip_info [ 3 ] ) ;
/* 4. 广播宣告 + 等待 PC HELLO 确认 */
{
# define PHASE6_HELLO_PORT 60000
uint8_t anno_sock = PHASE6_ANNOUNCE_SOCK ;
uint8_t bcast_ip [ 4 ] = { 0xFF , 0xFF , 0xFF , 0xFF } ;
uint8_t rx_buf [ 64 ] = { 0 } ;
/* UDP Server 模式: 本地端<E59CB0> ?60000( 接<EFBC88> ?HELLO) , DesIP=0xFFFFFFFF */
ch395f_set_proto_type ( anno_sock , CH395F_PROTO_TYPE_UDP ) ;
ch395f_set_send_buf ( anno_sock , 24 , 2 ) ;
ch395f_set_recv_buf ( anno_sock , 26 , 2 ) ;
@@ -524,12 +531,10 @@ void ch395f_phase6_tests(void) {
ch395f_set_des_port ( anno_sock , 0 ) ;
if ( ch395f_open_socket ( anno_sock ) = = CH395F_ERR_SUCCESS ) {
/* 发送广播宣告(宣告端口 = 60001, 来源端<E6BA90> ?= 60000<30> ?*/
ch395f_set_des_ip ( anno_sock , bcast_ip ) ;
ch395f_set_des_port ( anno_sock , PHASE6_ANNOUNCE_PORT ) ;
ch395f_write_send_buf ( anno_sock , ip_info , 4 ) ;
/* 等待 SENDBUF_FREE( 手册要求: 每次 write_send_buf 后必须等待) */
{
uint32_t swait = HAL_GetTick ( ) ;
while ( HAL_GetTick ( ) - swait < 2000 ) {
@@ -546,14 +551,12 @@ void ch395f_phase6_tests(void) {
PHASE6_HELLO_PORT , PHASE6_ANNOUNCE_PORT ) ;
TEST_CHECK ( 1 , " DHCP announcement sent " ) ;
/* 重置 Server 模式, 等<EFBC8C> ?PC 发<> ?HELLO */
ch395f_set_des_ip ( anno_sock , bcast_ip ) ;
ch395f_set_des_port ( anno_sock , 0 ) ;
uint32_t wait = HAL_GetTick ( ) ;
uint8_t hello_ok = 0 ;
while ( HAL_GetTick ( ) - wait < 5000 ) {
/* 读取全局中断( 刷<EFBC88> ?socket 中断状态) */
ch395f_get_glob_int_status_all ( ) ;
uint16_t rlen = ch395f_get_recv_len ( anno_sock ) ;
if ( rlen > 8 ) {
@@ -591,55 +594,321 @@ phase6_end:
# endif
}
/* ============================ Phase 7 ============================ */
/*
* ============================ Phase 7 ============================
* Select/Poll I/O 多路复用测试 <20> ?net_select() + net_recv/send echo
* Select/Poll I/O 多路复用测试(单连接模式)
* 在 netTask 中运行,仅操作 Socket 0
*/
# include "net_select.h"
# ifdef ENABLE_PHASE7_TESTS
static char s_phase7_buf [ 2048 ] ;
static char s_phase7_buf [ 8192 ] ;
static uint32_t s_phase7_count = 0 ;
# endif
/*
* 函数功能: Select/Poll I/O 多路复用测试
* 入口参数:无
* <20> ?<3F> ?值: <E580BC> ? * 限定条件: net_init 已完成, 多连<E5A49A> ?TCP Server 已启<E5B7B2> ? * 函数说明:在 netTask 中运行,验证 select/poll 正确返回读写事件
*/
void ch395f_phase7_tests ( void ) {
# ifdef ENABLE_PHASE7_TESTS
static uint32_t s_phase7_count = 0 ;
net_sock_t * sk = net_get_sock ( 0 ) ;
if ( sk = = NULL | | ! sk - > in_use | | sk - > state ! = NET_SOCK_STATE_ESTABLISHED ) {
return ;
}
net_fd_set readfds ;
net_timeval tv ;
FD_ZERO
NET_FD_ZERO ( & readfds ) ;
for ( int i = NET_TCP_SERVER_DATA_SOCK_START ; i < NET_MAX_SOCKETS ; i + + ) {
net_sock_t * sk = net_get_sock ( i ) ;
if ( sk & & sk - > in_use & & sk - > state = = NET_SOCK_STATE_ESTABLISHED ) {
NET_FD_SET ( i , & readfds ) ;
}
}
NET_FD_SET ( 0 , & readfds ) ;
tv . tv_sec = 0 ;
tv . tv_usec = 50000 ;
int n = net_select ( NET_MAX_SOCKETS , & readfds , NULL , NULL , & tv ) ;
int n = net_select ( 1 , & readfds , NULL , NULL , & tv ) ;
if ( n < = 0 ) return ;
for ( int i = 0 ; i < NET_MAX_SOCK ETS ; i + + ) {
net_sock_t * sk = net_get_sock ( i ) ;
if ( ! sk | | ! NET_FD_ISSET ( i , & readfds ) ) continue ;
i f ( NET_FD_ISS ET( 0 , & readfds ) ) {
int len = net_recv_sock ( sk , s_phase7_buf , sizeof ( s_phase7_buf ) , NET_MSG_DONTWAIT ) ;
if ( len > 0 ) {
net_send_sock ( sk , s_phase7_buf , len ) ;
s_phase7_count + + ;
TEST_CHECK ( 1 , " Phase7: select echo #%lu (sock=%d, %d bytes) " , s_phase7_count , i , len ) ;
TEST_CHECK ( 1 , " Phase7: select echo #%lu (%d bytes) " , s_phase7_count , len ) ;
}
}
# endif
}
/* ============================ Phase 8 ============================ */
/*
* 边界条件测试( 单连接模式, netTask 中运行)
* 覆盖: 零长度收发、DMA 边界( 4095/4096/4097B) 、
* recv 缓冲区小于数据、连续快速发送、快速重连
*/
# ifdef ENABLE_PHASE8_TESTS
static int s_phase8_stage = 0 ; /* 0=idle, 1=4095B, 2=4096B, 3=4097B, 4=zero, 5=fastsend */
static uint32_t s_phase8_sent = 0 ;
static uint32_t s_phase8_recv = 0 ;
static uint8_t s_phase8_tx [ 8192 ] ;
static uint8_t s_phase8_rx [ 8192 ] ;
static void phase8_fill ( uint8_t * buf , uint32_t size , uint8_t seed ) {
for ( uint32_t i = 0 ; i < size ; i + + ) {
buf [ i ] = ( uint8_t ) ( seed + i ) ;
}
}
static int phase8_verify ( uint8_t * buf , uint32_t size , uint8_t seed ) {
for ( uint32_t i = 0 ; i < size ; i + + ) {
if ( buf [ i ] ! = ( uint8_t ) ( seed + i ) ) {
return 0 ;
}
}
return 1 ;
}
# endif
void ch395f_phase8_init ( void ) {
# ifdef ENABLE_PHASE8_TESTS
s_phase8_stage = 0 ;
DBG_INFO ( " Phase8: boundary tests initialized " ) ;
# endif
}
void ch395f_phase8_tests ( void ) {
# ifdef ENABLE_PHASE8_TESTS
net_sock_t * sk = net_get_sock ( 0 ) ;
if ( sk = = NULL | | ! sk - > in_use ) {
return ;
}
if ( sk - > state = = NET_SOCK_STATE_LISTENING ) {
/* 等待连接 */
return ;
}
if ( sk - > state ! = NET_SOCK_STATE_ESTABLISHED ) {
s_phase8_stage = 0 ;
return ;
}
switch ( s_phase8_stage ) {
case 0 : {
/* Stage 1: 发送 4095 字节( DMA 边界 -1: 单次 SPI 事务可容纳) */
DBG_INFO ( " Phase8: stage 1/5 — send 4095 bytes (DMA boundary -1) " ) ;
phase8_fill ( s_phase8_tx , 4095 , 0x10 ) ;
int sent = net_send_sock ( sk , s_phase8_tx , 4095 ) ;
TEST_CHECK ( sent = = 4095 , " Phase8: send 4095 bytes (sent=%d) " , sent ) ;
s_phase8_sent = ( sent > 0 ) ? ( uint32_t ) sent : 0 ;
s_phase8_recv = 0 ;
s_phase8_stage = 1 ;
break ;
}
case 1 : {
/* 等待接收 4095 字节回显 */
int n = net_recv_sock ( sk , s_phase8_rx + s_phase8_recv ,
( int ) ( 4095 - s_phase8_recv ) , NET_MSG_DONTWAIT ) ;
if ( n > 0 ) {
s_phase8_recv + = ( uint32_t ) n ;
if ( s_phase8_recv > = 4095 ) {
int ok = phase8_verify ( s_phase8_rx , 4095 , 0x10 ) ;
TEST_CHECK ( ok , " Phase8: verify 4095 bytes echo " ) ;
s_phase8_stage = 2 ;
}
}
break ;
}
case 2 : {
/* Stage 2: 发送 4096 字节( DMA 边界:刚好填满 SPI 事务) */
DBG_INFO ( " Phase8: stage 2/5 — send 4096 bytes (DMA boundary exact) " ) ;
phase8_fill ( s_phase8_tx , 4096 , 0x20 ) ;
int sent = net_send_sock ( sk , s_phase8_tx , 4096 ) ;
TEST_CHECK ( sent = = 4096 , " Phase8: send 4096 bytes (sent=%d) " , sent ) ;
s_phase8_sent = ( sent > 0 ) ? ( uint32_t ) sent : 0 ;
s_phase8_recv = 0 ;
s_phase8_stage = 3 ;
break ;
}
case 3 : {
int n = net_recv_sock ( sk , s_phase8_rx + s_phase8_recv ,
( int ) ( 4096 - s_phase8_recv ) , NET_MSG_DONTWAIT ) ;
if ( n > 0 ) {
s_phase8_recv + = ( uint32_t ) n ;
if ( s_phase8_recv > = 4096 ) {
int ok = phase8_verify ( s_phase8_rx , 4096 , 0x20 ) ;
TEST_CHECK ( ok , " Phase8: verify 4096 bytes echo " ) ;
s_phase8_stage = 4 ;
}
}
break ;
}
case 4 : {
/* Stage 3: 发送 4097 字节( DMA 边界 +1: 需两次 SPI 事务) */
DBG_INFO ( " Phase8: stage 3/5 — send 4097 bytes (DMA boundary +1, two SPI xacts) " ) ;
phase8_fill ( s_phase8_tx , 4097 , 0x30 ) ;
int sent = net_send_sock ( sk , s_phase8_tx , 4097 ) ;
TEST_CHECK ( sent = = 4097 , " Phase8: send 4097 bytes (sent=%d) " , sent ) ;
s_phase8_sent = ( sent > 0 ) ? ( uint32_t ) sent : 0 ;
s_phase8_recv = 0 ;
s_phase8_stage = 5 ;
break ;
}
case 5 : {
int n = net_recv_sock ( sk , s_phase8_rx + s_phase8_recv ,
( int ) ( 4097 - s_phase8_recv ) , NET_MSG_DONTWAIT ) ;
if ( n > 0 ) {
s_phase8_recv + = ( uint32_t ) n ;
if ( s_phase8_recv > = 4097 ) {
int ok = phase8_verify ( s_phase8_rx , 4097 , 0x30 ) ;
TEST_CHECK ( ok , " Phase8: verify 4097 bytes echo " ) ;
s_phase8_stage = 6 ;
}
}
break ;
}
case 6 : {
/* Stage 4: 零长度发送(验证错误处理) */
DBG_INFO ( " Phase8: stage 4/5 — zero-length send/recv " ) ;
int sent = net_send_sock ( sk , s_phase8_tx , 0 ) ;
TEST_CHECK ( sent < 0 , " Phase8: send 0 bytes returns %d (expected <0) " , sent ) ;
s_phase8_stage = 7 ;
break ;
}
case 7 : {
/* Stage 5: 连续快速发送 20 个小包(验证 SENDBUF_FREE 等待机制) */
DBG_INFO ( " Phase8: stage 5/5 — rapid 20x 100-byte sends " ) ;
phase8_fill ( s_phase8_tx , 100 , 0x40 ) ;
int all_ok = 1 ;
for ( int i = 0 ; i < 20 ; i + + ) {
int sent = net_send_sock ( sk , s_phase8_tx , 100 ) ;
if ( sent ! = 100 ) {
TEST_CHECK ( 0 , " Phase8: rapid send #%d failed (sent=%d) " , i + 1 , sent ) ;
all_ok = 0 ;
break ;
}
osDelay ( 1 ) ;
}
if ( all_ok ) {
TEST_CHECK ( 1 , " Phase8: 20x rapid sends OK " ) ;
s_phase8_sent = 2000 ;
s_phase8_recv = 0 ;
s_phase8_stage = 8 ;
} else {
s_phase8_stage = 0 ;
}
break ;
}
case 8 : {
/* 接收所有快速发送的回显 */
int n = net_recv_sock ( sk , s_phase8_rx + s_phase8_recv ,
( int ) ( 2000 - s_phase8_recv ) , NET_MSG_DONTWAIT ) ;
if ( n > 0 ) {
s_phase8_recv + = ( uint32_t ) n ;
if ( s_phase8_recv > = 2000 ) {
TEST_CHECK ( 1 , " Phase8: all 2000 bytes echo received " ) ;
TEST_REPORT ( " Phase 8 " ) ;
s_phase8_stage = 0 ;
}
}
break ;
}
default :
s_phase8_stage = 0 ;
break ;
}
# endif
}
/* ============================ Phase 9 ============================ */
/*
* PHY 链路恢复测试(需物理插拔网线)
* 在 main.c 中单独调用,或在 netTask 中通过宏启用
*/
void ch395f_phase9_tests ( void ) {
# ifdef ENABLE_PHASE9_TESTS
DBG_INFO ( " === CH395F Phase 9 Tests (PHY Recovery) === " ) ;
/* TC-NET-901: 拔线后检测 PHY 断开 */
uint32_t wait = HAL_GetTick ( ) ;
uint8_t phy_disconnected = 0 ;
while ( HAL_GetTick ( ) - wait < 15000 ) {
uint16_t gint = ch395f_get_glob_int_status_all ( ) ;
if ( gint & CH395F_GINT_STAT_PHY_CHANGE ) {
uint8_t phy = ch395f_get_phy_status ( ) ;
DBG_INFO ( " PHY_CHANGE: 0x%02X " , phy ) ;
if ( phy = = CH395F_PHY_DISCONN ) {
TEST_CHECK ( 1 , " PHY disconnect detected " ) ;
phy_disconnected = 1 ;
break ;
}
}
HAL_Delay ( 100 ) ;
}
if ( ! phy_disconnected ) {
TEST_CHECK ( 0 , " PHY disconnect NOT detected within 15s (unplug cable?) " ) ;
goto phase9_end ;
}
/* TC-NET-902: 插回网线后检测链路恢复 */
wait = HAL_GetTick ( ) ;
uint8_t phy_reconnected = 0 ;
while ( HAL_GetTick ( ) - wait < 30000 ) {
uint16_t gint = ch395f_get_glob_int_status_all ( ) ;
if ( gint & CH395F_GINT_STAT_PHY_CHANGE ) {
uint8_t phy = ch395f_get_phy_status ( ) ;
DBG_INFO ( " PHY_CHANGE: 0x%02X " , phy ) ;
if ( phy ! = CH395F_PHY_DISCONN ) {
TEST_CHECK ( 1 , " PHY reconnected (0x%02X) " , phy ) ;
phy_reconnected = 1 ;
break ;
}
}
HAL_Delay ( 100 ) ;
}
if ( ! phy_reconnected ) {
TEST_CHECK ( 0 , " PHY reconnect NOT detected within 30s (re-plug cable?) " ) ;
}
phase9_end :
TEST_REPORT ( " Phase 9 " ) ;
# endif
}
/* ============================ Phase 10 ============================ */
/*
* KeepAlive 超时断开测试
* 在 netTask 中运行,监视 Socket 0 的 DISCONNECT/TIMEOUT 事件
* PC 连接后静置(不发数据),验证 KeepAlive 在空闲超时后触发断开
*/
# ifdef ENABLE_PHASE10_TESTS
static int s_phase10_connected = 0 ;
static uint32_t s_phase10_start_tick = 0 ;
# endif
void ch395f_phase10_tests ( void ) {
# ifdef ENABLE_PHASE10_TESTS
net_sock_t * sk = net_get_sock ( 0 ) ;
if ( sk = = NULL | | ! sk - > in_use ) {
return ;
}
if ( ! s_phase10_connected ) {
if ( sk - > state = = NET_SOCK_STATE_ESTABLISHED ) {
s_phase10_connected = 1 ;
s_phase10_start_tick = HAL_GetTick ( ) ;
DBG_INFO ( " Phase10: connection established, waiting for KeepAlive timeout (%dms)... " ,
PHASE10_KEEPALIVE_IDLE_MS ) ;
}
return ;
}
/* 已建立连接, 等待断开( KeepAlive 触发) */
if ( sk - > state = = NET_SOCK_STATE_LISTENING ) {
uint32_t elapsed = HAL_GetTick ( ) - s_phase10_start_tick ;
TEST_CHECK ( elapsed > = PHASE10_KEEPALIVE_IDLE_MS ,
" Phase10: KeepAlive triggered after %lums (idle=%dms) " ,
elapsed , PHASE10_KEEPALIVE_IDLE_MS ) ;
s_phase10_connected = 0 ;
TEST_REPORT ( " Phase 10 " ) ;
}
# endif
}