修改测试
This commit is contained in:
@@ -25,7 +25,8 @@
|
||||
#define CH395F_RESET_DELAY_MS 20U /* 复位后等待延时(ms<6D>?*/
|
||||
#define CH395F_INIT_TIMEOUT_MS 500U /* 初始化超时时间(ms<6D>?*/
|
||||
#define CH395F_SPI_BEGIN_DELAY_MS 1U /* SPI 事务开始延时(ms<6D>?*/
|
||||
#define CH395F_DMA_BUF_SIZE 4100U /* DMA 缓冲区(header 4 + max 4096 data) */
|
||||
/* DMA 缓冲大小使用 ch395f.h 中公开的 CH395F_SPI_DMA_BUF_SIZE */
|
||||
#define CH395F_DMA_BUF_SIZE CH395F_SPI_DMA_BUF_SIZE
|
||||
|
||||
/*
|
||||
* 私有变量<E58F98>?- DMA 传输相关
|
||||
|
||||
@@ -110,6 +110,14 @@ extern "C" {
|
||||
/* 3 字节输入 + 数据输入(socket + 长度 + 数据) */
|
||||
#define CH395F_CMD_WRITE_SEND_BUF_SN 0x39U
|
||||
|
||||
/*
|
||||
* SPI DMA 事务限制
|
||||
* CH395F_SPI_DMA_MAX_PAYLOAD: 单次 DMA 事务最大有效载荷(总缓冲 - 4 字节命令头)
|
||||
* 注意: CH395F 内部 socket 接收缓冲默认 4096B,DMA 缓冲需至少 4100 以一次性读空
|
||||
*/
|
||||
#define CH395F_SPI_DMA_BUF_SIZE 4100U
|
||||
#define CH395F_SPI_DMA_MAX_PAYLOAD (CH395F_SPI_DMA_BUF_SIZE - 4U)
|
||||
|
||||
/* 其他命令 */
|
||||
#define CH395F_CMD_SET_TCP_MSS 0x50U
|
||||
#define CH395F_CMD_SET_RECV_BUF 0x52U
|
||||
|
||||
@@ -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_SOCKETS; i++) {
|
||||
net_sock_t *sk = net_get_sock(i);
|
||||
if (!sk || !NET_FD_ISSET(i, &readfds)) continue;
|
||||
|
||||
if (NET_FD_ISSET(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
|
||||
}
|
||||
|
||||
@@ -1,24 +1,27 @@
|
||||
/*
|
||||
* 模块名称:CH395F Driver Test Suite
|
||||
* 模块功能:CH395F 驱动各功能模块的测试入口,分阶段组织测试用例,
|
||||
* 覆盖 SPI 命令路径、协议栈配置、Socket 状态机等
|
||||
* 覆盖 SPI 命令路径、协议栈配置、Socket 状态机、大文件传输、
|
||||
* 边界条件、KeepAlive、PHY 链路恢复等,基于单连接 socket 模式
|
||||
* 适用平台:STM32F4 系列(CH395F 以太网芯片)
|
||||
* 作者:王建锋
|
||||
* 创建日期:2026-07-19
|
||||
* 修改记录:
|
||||
* 2026-07-22 全面重构为单连接模式,增加大文件传输、边界条件等测试
|
||||
*
|
||||
* === 通过标准速查 ===
|
||||
*
|
||||
* Phase | 通过条件 | 失败特征
|
||||
* ---------|-----------------------------------|--------------------------------
|
||||
* Phase 1 | 9 项全部打印 OK/SUCCESS/YES/0x00 | 任何一项显示 FAIL、ERROR、MISMATCH
|
||||
* Phase 2 | 3 轮 "PASS" 打印 | 任何一轮打印 "FAIL" 或超时
|
||||
* Phase 3 | 20/20 回显 + PING 5/5 + 大包 10/10 | 任何回显超时或丢包
|
||||
* Phase 4 | PC 脚本显示 10/10 sent/received | 断连、数据错乱
|
||||
* Phase 5 | PC 脚本显示 10/10 sent/received | 断连、数据错乱
|
||||
* Phase 6 | 日志打印 IP/GW/MASK 非零 IP | DHCP timeout 或 0.0.0.0
|
||||
* Phase 7 | PC 脚本显示 10/10 sent/received | 断连、数据错乱
|
||||
* Phase 8 | 70/70 成功率 100% | 任何连接失败或丢包
|
||||
* === 测试阶段一览 ===
|
||||
* Phase | 类型 | 描述
|
||||
* ---------|--------------|-----------------------------------------------
|
||||
* Phase 1 | 独立运行 | 寄存器读写 + SPI 命令路径验证
|
||||
* Phase 2 | PC 配合 | TCP Client 收发 + 关闭重连
|
||||
* Phase 3 | PC 配合 | UDP Server Echo(HELLO + PING + 大包)
|
||||
* Phase 4 | netTask | NET 层 TCP Echo(单连接模式,基础收发验证)
|
||||
* Phase 5 | netTask | NET 层大文件传输(20KB+ 分块收发)
|
||||
* Phase 6 | PC 配合 | DHCP 自动获取 IP
|
||||
* Phase 7 | netTask | Select/Poll I/O 多路复用
|
||||
* Phase 8 | netTask | 边界条件测试(零长度、DMA 边界、缓冲溢出)
|
||||
* Phase 9 | 需拔插网线 | PHY 链路恢复
|
||||
* Phase 10 | netTask | KeepAlive 超时断开测试
|
||||
*/
|
||||
|
||||
#ifndef __CH395F_TEST_H
|
||||
@@ -32,15 +35,19 @@ extern "C" {
|
||||
|
||||
/*
|
||||
* 阶段使能开关(取消注释即启用对应阶段)
|
||||
* 注意:Phase 6 (DHCP) 会改变 IP,建议独立运行
|
||||
* 注意:一次仅启用一个阶段(Phase 4/5/7/8 共享 netTask 循环,可同时启用)
|
||||
* Phase 6 (DHCP) 会改变 IP,必须独立运行
|
||||
*/
|
||||
//#define ENABLE_PHASE1_TESTS /* 寄存器读写 + 中断一致性 */
|
||||
//#define ENABLE_PHASE1_TESTS /* 寄存器读写 + SPI 命令路径 */
|
||||
//#define ENABLE_PHASE2_TESTS /* TCP Client 收发 + 关闭重连 */
|
||||
//#define ENABLE_PHASE3_TESTS /* UDP Server Echo */
|
||||
#define ENABLE_PHASE4_TESTS /* NET 层 TCP Echo(netTask 中运行) */
|
||||
//#define ENABLE_PHASE5_TESTS /* NET 层 UDP Echo(netTask 中运行) */
|
||||
//#define ENABLE_PHASE6_TESTS /* DHCP 自动获取 IP(会改 IP!) */
|
||||
//#define ENABLE_PHASE7_TESTS /* Select/Poll I/O 多路复用(netTask 中运行) */
|
||||
#define ENABLE_PHASE4_TESTS /* NET 层 TCP Echo(单连接基础收发) */
|
||||
//#define ENABLE_PHASE5_TESTS /* NET 层大文件传输(20KB+ 分块) */
|
||||
//#define ENABLE_PHASE6_TESTS /* DHCP 自动获取 IP(会改 IP!独立运行) */
|
||||
//#define ENABLE_PHASE7_TESTS /* Select/Poll I/O 多路复用 */
|
||||
//#define ENABLE_PHASE8_TESTS /* 边界条件测试 */
|
||||
//#define ENABLE_PHASE9_TESTS /* PHY 链路恢复测试 */
|
||||
//#define ENABLE_PHASE10_TESTS /* KeepAlive 超时断开测试 */
|
||||
|
||||
/* 测试统计 */
|
||||
typedef struct {
|
||||
@@ -85,30 +92,28 @@ void ch395f_phase2_tests(void);
|
||||
|
||||
/*
|
||||
* Phase 3: UDP Server Echo + PING + 大包
|
||||
* 通过: 20/20 回显 + PING 5/5 + 大包 10/10
|
||||
* 通过: 30/30 回显匹配
|
||||
* 失败: 任何回显超时或丢包
|
||||
* PC 端:
|
||||
* ping 192.168.1.100
|
||||
* python test/ch395f_socket_test.py udp_client --ip 192.168.1.100 --port 60000
|
||||
*/
|
||||
void ch395f_phase3_tests(void);
|
||||
|
||||
/*
|
||||
* Phase 4: NET 层 TCP Echo(netTask 中运行)
|
||||
* 通过: PC 脚本 10/10 sent/received
|
||||
* 失败: 断连或数据错乱
|
||||
* Phase 4: NET 层 TCP Echo(单连接模式,netTask 中运行)
|
||||
* 验证 Socket 0 单连接生命周期:LISTEN → ESTABLISHED → DISCONNECT → re-LISTEN
|
||||
* 覆盖载荷:64B、1460B(MSS)、4096B(缓冲满)、20KB(大文件分块)
|
||||
* PC 端:
|
||||
* python test/ch395f_socket_test.py tcp_client --ip 192.168.1.100 --port 8080
|
||||
*/
|
||||
void ch395f_phase4_tests(void);
|
||||
|
||||
/*
|
||||
* Phase 5: NET 层 UDP Echo(netTask 中运行)
|
||||
* 通过: PC 脚本 10/10 sent/received
|
||||
* 失败: 断连或数据错乱
|
||||
* 注:init 在 USER CODE BEGIN 2 中调用,echo 在 netTask 中运行
|
||||
* Phase 5: NET 层大文件传输(netTask 中运行)
|
||||
* 验证超过 CH395F 内部缓冲区大小(4KB)的连续数据收发
|
||||
* 覆盖载荷:20480B(20KB)、65535B(64KB,recv_len上限)
|
||||
* PC 端:
|
||||
* python test/ch395f_socket_test.py udp_client --ip 192.168.1.100 --port 60000
|
||||
* python test/ch395f_socket_test.py tcp_file --ip 192.168.1.100 --port 8080 --size 20480
|
||||
*/
|
||||
void ch395f_phase5_init(void);
|
||||
void ch395f_phase5_tests(void);
|
||||
@@ -119,19 +124,44 @@ void ch395f_phase5_tests(void);
|
||||
* 失败: DHCP timeout 或宣告/HELLO 失败
|
||||
* 注意: 会改变 IP,需独立运行
|
||||
* PC 端:管理员权限
|
||||
* python test/ch395f_socket_test.py dhcp_server --dhcp-iface 192.168.1.2 --ip 192.168.1.100
|
||||
* python test/ch395f_socket_test.py dhcp_server --dhcp-iface 192.168.1.2 --ip 192.168.1.100
|
||||
*/
|
||||
void ch395f_phase6_tests(void);
|
||||
|
||||
/*
|
||||
* Phase 7: Select/Poll 测试(netTask 中运行)
|
||||
* 通过: PC 脚本 10/10 sent/received
|
||||
* 失败: 断连或数据错乱
|
||||
* Phase 7: Select/Poll I/O 多路复用(netTask 中运行)
|
||||
* 验证 net_select() 和 net_poll_events() 在单 socket 模式下正确工作
|
||||
* PC 端:
|
||||
* python test/ch395f_socket_test.py tcp_client --ip 192.168.1.100 --port 8080
|
||||
*/
|
||||
void ch395f_phase7_tests(void);
|
||||
|
||||
/*
|
||||
* Phase 8: 边界条件测试(netTask 中运行)
|
||||
* 覆盖:零长度收发、DMA 边界(4095/4096/4097B)、recv 缓冲区小于数据、
|
||||
* 连续快速发送、多轮快速连接-断开循环
|
||||
* PC 端:
|
||||
* python test/ch395f_socket_test.py tcp_client --ip 192.168.1.100 --port 8080
|
||||
*/
|
||||
void ch395f_phase8_init(void);
|
||||
void ch395f_phase8_tests(void);
|
||||
|
||||
/*
|
||||
* Phase 9: PHY 链路恢复测试
|
||||
* 通过拔插网线验证 PHY 断开检测和链路恢复后 socket 自动重新监听
|
||||
* 需物理操作,PC 端:
|
||||
* python test/ch395f_socket_test.py tcp_client --ip 192.168.1.100 --port 8080
|
||||
*/
|
||||
void ch395f_phase9_tests(void);
|
||||
|
||||
/*
|
||||
* Phase 10: TCP KeepAlive 超时断开测试
|
||||
* 验证 KeepAlive 在空闲超时后触发断开并自动重新监听
|
||||
* PC 端:连接后静置(不发数据),等待 KeepAlive 触发
|
||||
* python test/ch395f_socket_test.py tcp_client --ip 192.168.1.100 --port 8080 --silent
|
||||
*/
|
||||
void ch395f_phase10_tests(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -182,14 +182,13 @@ static int receive_file(int socket, const char *path)
|
||||
return -1;
|
||||
}
|
||||
|
||||
static unsigned char s_recv_buf[4096];
|
||||
int bufsize = 4096;
|
||||
unsigned char buffer[1024];
|
||||
int err;
|
||||
int total = 0;
|
||||
|
||||
while ((err = lftpd_inet_read(socket, s_recv_buf, bufsize)) > 0) {
|
||||
while ((err = lftpd_inet_read(socket, buffer, sizeof(buffer))) > 0) {
|
||||
total += err;
|
||||
if (lftpd_io_write(fh, s_recv_buf, err) != err) {
|
||||
if (lftpd_io_write(fh, buffer, err) != err) {
|
||||
err = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -107,7 +107,8 @@ int net_init(const char *ip, const char *mask, const char *gateway) {
|
||||
ch395f_reset();
|
||||
DBG_INFO("net_init: set_fun_para...");
|
||||
|
||||
/* CH395F 超时/DISCONNECT 后不要自动关闭 Socket,由软件手动管理 */
|
||||
/* 启用单连接模式 + 手动关闭 Socket(bit3=1, bit1=0)
|
||||
* bit3=1: DISCONNECT/TIMEOUT 时不自动清空缓冲区,由软件排空数据后手动关闭 */
|
||||
ch395f_set_fun_para(0x08);
|
||||
DBG_INFO("net_init: set_tcp_mss...");
|
||||
|
||||
@@ -776,6 +777,11 @@ int net_connect(int sockfd, const struct net_sockaddr *addr, int addrlen) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* SPI DMA 单次事务最大有效载荷(取 CH395F 驱动公开常量)
|
||||
*/
|
||||
#define NET_DMA_MAX_PAYLOAD ((int)CH395F_SPI_DMA_MAX_PAYLOAD)
|
||||
|
||||
/*
|
||||
* 函数功能:发送数据(内部锁定版本,必须在 netTask 上下文调用)
|
||||
*/
|
||||
@@ -798,8 +804,8 @@ static int net_send_locked(net_sock_t *p_sock, const void *buf, int len) {
|
||||
|
||||
while (sent < len) {
|
||||
int chunk = len - sent;
|
||||
if (chunk > NET_SEND_BUF_SIZE) {
|
||||
chunk = NET_SEND_BUF_SIZE;
|
||||
if (chunk > NET_DMA_MAX_PAYLOAD) {
|
||||
chunk = NET_DMA_MAX_PAYLOAD;
|
||||
}
|
||||
|
||||
if (!p_sock->send_ready) {
|
||||
@@ -869,8 +875,18 @@ static int net_recv_locked(net_sock_t *p_sock, void *buf, int len, int flags) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (p_sock->state == NET_SOCK_STATE_TCP_ACCEPT ||
|
||||
p_sock->state == NET_SOCK_STATE_CLOSED) {
|
||||
if (p_sock->state == NET_SOCK_STATE_TCP_ACCEPT) {
|
||||
return 0;
|
||||
}
|
||||
if (p_sock->state == NET_SOCK_STATE_CLOSED) {
|
||||
/* 断开前尝试排空 CH395F 接收缓冲区(Trap 13:FIN 可能比数据先到) */
|
||||
uint16_t rem = ch395f_get_recv_len(sockfd);
|
||||
if (rem > 0) {
|
||||
int rd = (rem > (uint16_t)len) ? len : (int)rem;
|
||||
ch395f_read_recv_buf(sockfd, (uint8_t *)buf, (uint16_t)rd);
|
||||
p_sock->recv_bytes += (uint32_t)rd;
|
||||
return rd;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -1188,9 +1204,9 @@ int net_recvfrom(int sockfd, void *buf, int len, int flags,
|
||||
/* 读取数据 */
|
||||
if (recv_len > 0) {
|
||||
int read_len = (recv_len > (uint16_t)len) ? len : (int)recv_len;
|
||||
ch395f_read_recv_buf(sockfd, (uint8_t *)buf, (uint16_t)read_len);
|
||||
p_sock->recv_bytes += (uint32_t)read_len;
|
||||
return read_len;
|
||||
ch395f_read_recv_buf(sockfd, (uint8_t *)buf, (uint16_t)read_len);
|
||||
p_sock->recv_bytes += (uint32_t)read_len;
|
||||
return read_len;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1481,11 +1497,9 @@ static void handle_disconnect_event(net_sock_t *p_sock) {
|
||||
}
|
||||
else if (p_sock->state == NET_SOCK_STATE_ESTABLISHED) {
|
||||
if (p_sock->standalone_accept) {
|
||||
/* 独立监听模式(PASV 数据通道):通知断开但不释放 Socket,
|
||||
* 留给传输命令(STOR/LIST)的 accept 检测 CLOSED 状态后统一清理。
|
||||
* 但必须先关闭 CH395F 硬件 Socket,否则后续 ch395f_open_socket 失败 */
|
||||
/* 独立监听模式:先不关 CH395F Socket,
|
||||
* 留给 net_recv_locked 把剩余数据读出来再关 */
|
||||
DBG_INFO("sock%d DISCONNECTED, releasing", p_sock->ch395_sock);
|
||||
ch395f_close_socket(p_sock->ch395_sock);
|
||||
p_sock->state = NET_SOCK_STATE_CLOSED;
|
||||
fire_event(p_sock, NET_EVENT_DISCONNECTED);
|
||||
} else {
|
||||
@@ -1512,11 +1526,9 @@ static void handle_disconnect_event(net_sock_t *p_sock) {
|
||||
/*
|
||||
* 函数功能:处理数据接收事<E694B6>? */
|
||||
static void handle_recv_event(net_sock_t *p_sock) {
|
||||
uint16_t recv_len = ch395f_get_recv_len(p_sock->ch395_sock);
|
||||
|
||||
if (recv_len > 0) {
|
||||
fire_event(p_sock, NET_EVENT_DATA_RECEIVED);
|
||||
}
|
||||
/* RECV_OK 中断已触发,直接通知上层。不在此读 recv_len,
|
||||
* 避免和 net_recv_locked 里面的 ch395f_get_recv_len 冲突(Trap 13) */
|
||||
fire_event(p_sock, NET_EVENT_DATA_RECEIVED);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1534,12 +1546,8 @@ static void handle_timeout_event(net_sock_t *p_sock) {
|
||||
p_sock->state = NET_SOCK_STATE_CREATED;
|
||||
}
|
||||
else if (p_sock->state == NET_SOCK_STATE_ESTABLISHED) {
|
||||
if (p_sock->standalone_accept) {
|
||||
DBG_INFO("sock%d TIMEOUT (ignored)", p_sock->ch395_sock);
|
||||
} else {
|
||||
DBG_ERROR("sock%d TIMEOUT", p_sock->ch395_sock);
|
||||
p_sock->state = NET_SOCK_STATE_TCP_ACCEPT;
|
||||
}
|
||||
DBG_ERROR("sock%d TIMEOUT", p_sock->ch395_sock);
|
||||
p_sock->state = p_sock->standalone_accept ? NET_SOCK_STATE_CLOSED : NET_SOCK_STATE_TCP_ACCEPT;
|
||||
}
|
||||
else if (p_sock->state == NET_SOCK_STATE_LISTENING) {
|
||||
/* 监听超时(异常情况) */
|
||||
|
||||
Reference in New Issue
Block a user