Files
STM32F4-Base/Drivers/BSP/CH395F/ch395f_test.c
2026-07-22 11:14:22 +08:00

915 lines
30 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* 模块名称CH395F Driver Test Suite
* 模块功能CH395F 驱动各功能模块的测试入口,分阶段组织测试用例,
* 覆盖 SPI 命令路径、Socket 状态机、大文件传输、边界条件等
* 适用平台STM32F4 系列CH395F 以太网芯片)
* 作者:王建锋
* 创建日期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 配置默认值
*/
#ifndef PHASE2_REMOTE_IP
#define PHASE2_REMOTE_IP "192.168.1.2"
#endif
#ifndef PHASE2_REMOTE_PORT
#define PHASE2_REMOTE_PORT 8081
#endif
#ifndef PHASE2_LOCAL_PORT
#define PHASE2_LOCAL_PORT 50000
#endif
#ifndef PHASE2_LOOP_COUNT
#define PHASE2_LOOP_COUNT 3
#endif
#ifndef PHASE2_TIMEOUT_MS
#define PHASE2_TIMEOUT_MS 10000
#endif
/*
* Phase 3 配置默认值
*/
#ifndef PHASE3_REMOTE_IP
#define PHASE3_REMOTE_IP "192.168.1.2"
#endif
#ifndef PHASE3_REMOTE_PORT
#define PHASE3_REMOTE_PORT 8082
#endif
#ifndef PHASE3_LOCAL_PORT
#define PHASE3_LOCAL_PORT 60000
#endif
#ifndef PHASE3_ECHO_COUNT
#define PHASE3_ECHO_COUNT 30
#endif
#ifndef PHASE3_TIMEOUT_MS
#define PHASE3_TIMEOUT_MS 30000
#endif
/*
* 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 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;
int ver = 0;
DBG_INFO("=== CH395F Phase 1 Tests ===");
ver = ch395f_get_version();
TEST_CHECK(ver > 0, "GET_VERSION: 0x%02X (ver=%d)", ver, ver & 0x3F);
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");
uint8_t ip_info[20];
ch395f_get_ip_inf(ip_info);
DBG_INFO("GET_IP_INF:");
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]);
DBG_INFO(" DNS2: %d.%d.%d.%d", ip_info[16], ip_info[17], ip_info[18], ip_info[19]);
TEST_CHECK(ip_info[0]==192 && ip_info[1]==168 && ip_info[2]==1 && ip_info[3]==100,
"IP match");
TEST_CHECK(ip_info[4]==192 && ip_info[5]==168 && ip_info[6]==1 && ip_info[7]==1,
"GW match");
TEST_CHECK(ip_info[8]==255 && ip_info[9]==255 && ip_info[10]==255 && ip_info[11]==0,
"MASK match");
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", gint_all);
uint8_t gint_1b = ch395f_get_glob_int_status();
TEST_CHECK(gint_1b == 0x00,
"GINT_STATUS_1byte: 0x%02X (expect 0x00)", gint_1b);
{
uint8_t st[2];
ch395f_get_socket_status(0, st);
TEST_CHECK(st[0] == 0x05 && st[1] == 0x01,
"SOCK_STATUS(0): sock=0x%02X, tcp=0x%02X", st[0], st[1]);
}
ch395f_set_arp(10, 5);
TEST_CHECK(1, "SET_ARP(period=10*100ms, cnt=5): OK");
ch395f_set_ttl(0, 64);
TEST_CHECK(1, "SET_TTL(sock=0, ttl=64): OK");
ch395f_clear_recv_buf(0);
TEST_CHECK(1, "CLEAR_RECV_BUF(sock=0): OK");
for (i = 0; i < 8; i++) {
uint8_t si = ch395f_get_sock_int_status(i);
TEST_CHECK(si == 0x00, "SOCK_INT(%d): 0x%02X", i, si);
}
TEST_REPORT("Phase 1");
}
/* ============================ Phase 2 ============================ */
void ch395f_phase2_tests(void) {
uint8_t sock = 0;
uint8_t sock_int = 0;
uint8_t rx_buf[300] = {0};
int i = 0;
uint32_t tick = 0;
uint16_t int_st = 0;
int iter_pass = 0;
DBG_INFO("=== CH395F Phase 2 Tests (TCP Client - Direct Driver) ===");
uint8_t ip_arr[4];
{
uint32_t a[4];
sscanf(PHASE2_REMOTE_IP, "%lu.%lu.%lu.%lu", &a[0], &a[1], &a[2], &a[3]);
ip_arr[0] = (uint8_t)a[0];
ip_arr[1] = (uint8_t)a[1];
ip_arr[2] = (uint8_t)a[2];
ip_arr[3] = (uint8_t)a[3];
}
DBG_INFO("Target: %s:%d", PHASE2_REMOTE_IP, PHASE2_REMOTE_PORT);
sock = 0;
ch395f_set_proto_type(sock, CH395F_PROTO_TYPE_TCP);
ch395f_set_des_ip(sock, ip_arr);
ch395f_set_des_port(sock, PHASE2_REMOTE_PORT);
for (i = 0; i < PHASE2_LOOP_COUNT; i++) {
DBG_INFO("--- Iteration %d/%d ---", i + 1, PHASE2_LOOP_COUNT);
iter_pass = 1;
ch395f_set_sour_port(sock, PHASE2_LOCAL_PORT + i);
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; }
tick = HAL_GetTick();
while (HAL_GetTick() - tick < PHASE2_TIMEOUT_MS) {
int_st = ch395f_get_glob_int_status_all();
if (int_st & (1U << (sock + 4))) {
sock_int = ch395f_get_sock_int_status(sock);
if (sock_int & CH395F_SINT_STAT_CONNECT) break;
}
HAL_Delay(10);
}
if (HAL_GetTick() - tick >= PHASE2_TIMEOUT_MS) { TEST_CHECK(0, "connect timeout"); iter_pass = 0; ch395f_close_socket(sock); goto phase2_iter_done; }
HAL_Delay(100);
{
uint8_t test_buf[64] = {0};
const char *msg = "HelloFromCH395F_TCP_Client! This is a 64-byte test message.";
int p, mlen = (int)strlen(msg);
memcpy(test_buf, msg, mlen);
for (p = mlen; p < 64; p++) test_buf[p] = (uint8_t)('0' + (p - mlen) % 10);
ch395f_write_send_buf(sock, test_buf, 64);
}
{
int got_echo = 0;
tick = HAL_GetTick();
while (HAL_GetTick() - tick < 10000) {
uint16_t rlen = ch395f_get_recv_len(sock);
if (rlen > 0) {
uint16_t read_len = (rlen > sizeof(rx_buf)) ? (uint16_t)sizeof(rx_buf) : rlen;
memset(rx_buf, 0, sizeof(rx_buf));
ch395f_read_recv_buf(sock, rx_buf, read_len);
DBG_INFO("recv: %d bytes [%.30s]", read_len, (char *)rx_buf);
ch395f_write_send_buf(sock, rx_buf, read_len);
got_echo = 1;
break;
}
int_st = ch395f_get_glob_int_status_all();
if (int_st & (1U << (sock + 4))) {
sock_int = ch395f_get_sock_int_status(sock);
if (sock_int & CH395F_SINT_STAT_DISCONNECT) { DBG_INFO("DISCONNECT"); break; }
if (sock_int & CH395F_SINT_STAT_SOCK_TIMEOUT) { DBG_INFO("SOCK_TIMEOUT"); break; }
}
HAL_Delay(50);
}
if (!got_echo) { TEST_CHECK(0, "echo recv (iter %d)", i + 1); iter_pass = 0; }
}
phase2_iter_done:
if (iter_pass) TEST_CHECK(1, "Iteration %d PASS", i + 1);
ch395f_close_socket(sock);
{
uint8_t st[2];
uint32_t wait = HAL_GetTick();
while (HAL_GetTick() - wait < 5000) {
ch395f_get_socket_status(sock, st);
if (st[0] == CH395F_SOCKET_CLOSED) break;
HAL_Delay(50);
}
DBG_INFO("closed: sock=0x%02X tcp=0x%02X", st[0], st[1]);
}
HAL_Delay(200);
}
TEST_REPORT("Phase 2");
}
/* ============================ Phase 3 ============================ */
void ch395f_phase3_tests(void) {
uint8_t sock = 0;
uint8_t rx_buf[300] = {0};
uint32_t tick = 0;
int i = 0;
int echo_ok = 0;
DBG_INFO("=== CH395F Phase 3 Tests (UDP) ===");
sock = 0;
ch395f_set_proto_type(sock, CH395F_PROTO_TYPE_UDP);
ch395f_set_sour_port(sock, PHASE3_LOCAL_PORT);
{
uint8_t zero_ip[4] = {0xFF, 0xFF, 0xFF, 0xFF};
ch395f_set_des_ip(sock, zero_ip);
}
ch395f_set_des_port(sock, 0);
TEST_CHECK(ch395f_open_socket(sock) == CH395F_ERR_SUCCESS,
"UDP socket %d opened (port=%d)", sock, PHASE3_LOCAL_PORT);
HAL_Delay(200);
for (i = 0; i < PHASE3_ECHO_COUNT; i++) {
tick = HAL_GetTick();
echo_ok = 0;
while (HAL_GetTick() - tick < PHASE3_TIMEOUT_MS) {
uint16_t rlen = ch395f_get_recv_len(sock);
if (rlen > 8) {
memset(rx_buf, 0, sizeof(rx_buf));
ch395f_read_recv_buf(sock, rx_buf, rlen);
uint16_t payload_len = rlen - 8;
uint16_t src_port = (uint16_t)rx_buf[2] | ((uint16_t)rx_buf[3] << 8);
uint8_t *src_ip = &rx_buf[4];
uint8_t *payload = &rx_buf[8];
DBG_INFO("UDP #%d: %dB from %d.%d.%d.%d:%d [%.32s]",
i + 1, payload_len,
src_ip[0], src_ip[1], src_ip[2], src_ip[3], src_port,
payload_len > 0 ? (char *)payload : "");
ch395f_set_des_ip(sock, src_ip);
ch395f_set_des_port(sock, src_port);
ch395f_write_send_buf(sock, payload, payload_len);
{
uint32_t swait = HAL_GetTick();
while (HAL_GetTick() - swait < 2000) {
uint16_t gs = ch395f_get_glob_int_status_all();
if (gs & (1U << (sock + 4))) {
if (ch395f_get_sock_int_status(sock) & CH395F_SINT_STAT_SENBUF_FREE)
break;
}
HAL_Delay(10);
}
}
echo_ok = 1;
break;
}
HAL_Delay(50);
}
TEST_CHECK(echo_ok, "UDP echo #%d/%d", i + 1, PHASE3_ECHO_COUNT);
if (!echo_ok) break;
}
ch395f_close_socket(sock);
TEST_CHECK(1, "UDP socket closed");
TEST_REPORT("Phase 3");
}
/* ============================ Phase 4 ============================ */
/*
* NET 层 TCP Echo单连接模式
* 在 netTask 中运行,仅操作 Socket 0
* 覆盖64B、1460B(MSS)、4096B(缓冲满) 回显
*/
#ifdef ENABLE_PHASE4_TESTS
static char s_phase4_buf[8192];
static uint32_t s_phase4_echo_count = 0;
#endif
void ch395f_phase4_tests(void) {
#ifdef ENABLE_PHASE4_TESTS
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) {
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 ============================ */
/*
* NET 层大文件传输测试
* 验证超过 CH395F 内部缓冲区大小4KB的连续数据收发
* 覆盖20KB、64KB 分块传输
*/
#ifdef ENABLE_PHASE5_TESTS
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
void ch395f_phase5_init(void) {
#ifdef ENABLE_PHASE5_TESTS
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
}
void ch395f_phase5_tests(void) {
#ifdef ENABLE_PHASE5_TESTS
net_sock_t *sk = net_get_sock(0);
if (sk == NULL || !sk->in_use) {
return;
}
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;
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 ============================ */
#ifndef PHASE6_ANNOUNCE_PORT
#define PHASE6_ANNOUNCE_PORT 60001
#endif
#ifndef PHASE6_ANNOUNCE_SOCK
#define PHASE6_ANNOUNCE_SOCK 6
#endif
void ch395f_phase6_tests(void) {
#ifdef ENABLE_PHASE6_TESTS
uint8_t status = 0;
uint8_t ip_info[20] = {0};
uint32_t tick = 0;
DBG_INFO("=== CH395F Phase 6 Tests (DHCP) ===");
ch395f_set_dhcp(1);
TEST_CHECK(1, "DHCP enabled, waiting for IP...");
HAL_Delay(500);
tick = HAL_GetTick();
while (HAL_GetTick() - tick < 30000) {
uint16_t gint = ch395f_get_glob_int_status_all();
if (gint & CH395F_GINT_STAT_DHCP) {
DBG_INFO("DHCP interrupt");
}
status = ch395f_get_dhcp_status();
if (status == 0x00) {
TEST_CHECK(1, "DHCP: SUCCESS");
break;
}
HAL_Delay(500);
}
TEST_CHECK(HAL_GetTick() - tick < 30000,
"DHCP completed within 30s (status=0x%02X)", status);
if (HAL_GetTick() - tick >= 30000) {
goto phase6_end;
}
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]);
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]);
{
#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};
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);
ch395f_set_sour_port(anno_sock, PHASE6_HELLO_PORT);
ch395f_set_des_ip(anno_sock, bcast_ip);
ch395f_set_des_port(anno_sock, 0);
if (ch395f_open_socket(anno_sock) == CH395F_ERR_SUCCESS) {
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);
{
uint32_t swait = HAL_GetTick();
while (HAL_GetTick() - swait < 2000) {
uint16_t gs = ch395f_get_glob_int_status_all();
if (gs & (1U << (anno_sock + 4))) {
if (ch395f_get_sock_int_status(anno_sock) & CH395F_SINT_STAT_SENBUF_FREE)
break;
}
HAL_Delay(10);
}
}
DBG_INFO("DHCP announcement: IP=%d.%d.%d.%d (port %d->%d)",
ip_info[0], ip_info[1], ip_info[2], ip_info[3],
PHASE6_HELLO_PORT, PHASE6_ANNOUNCE_PORT);
TEST_CHECK(1, "DHCP announcement sent");
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) {
ch395f_get_glob_int_status_all();
uint16_t rlen = ch395f_get_recv_len(anno_sock);
if (rlen > 8) {
memset(rx_buf, 0, sizeof(rx_buf));
ch395f_read_recv_buf(anno_sock, rx_buf, rlen);
uint16_t payload_len = rlen - 8;
uint8_t *payload = &rx_buf[8];
if (payload_len == 5 && memcmp(payload, "HELLO", 5) == 0) {
uint8_t *src_ip = &rx_buf[4];
uint16_t src_port = (uint16_t)rx_buf[2] | ((uint16_t)rx_buf[3] << 8);
ch395f_set_des_ip(anno_sock, src_ip);
ch395f_set_des_port(anno_sock, src_port);
ch395f_write_send_buf(anno_sock, payload, payload_len);
HAL_Delay(100);
DBG_INFO("HELLO response sent to %d.%d.%d.%d:%d",
src_ip[0], src_ip[1], src_ip[2], src_ip[3], src_port);
TEST_CHECK(1, "HELLO echo OK");
hello_ok = 1;
break;
}
}
HAL_Delay(50);
}
TEST_CHECK(hello_ok, "HELLO echo within 5s");
ch395f_close_socket(anno_sock);
} else {
TEST_CHECK(0, "DHCP announcement: open socket failed");
}
}
phase6_end:
TEST_REPORT("Phase 6");
#endif
}
/* ============================ Phase 7 ============================ */
/*
* Select/Poll I/O 多路复用测试(单连接模式)
* 在 netTask 中运行,仅操作 Socket 0
*/
#ifdef ENABLE_PHASE7_TESTS
static char s_phase7_buf[8192];
static uint32_t s_phase7_count = 0;
#endif
void ch395f_phase7_tests(void) {
#ifdef ENABLE_PHASE7_TESTS
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;
NET_FD_ZERO(&readfds);
NET_FD_SET(0, &readfds);
tv.tv_sec = 0;
tv.tv_usec = 50000;
int n = net_select(1, &readfds, NULL, NULL, &tv);
if (n <= 0) return;
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 (%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
}