5a806d8a26
用户裁定「全部修」。第 1 轮修复跨三名队友、写范围互斥、零越界;
11 条登记现状 → 10 条已处置 + 1 条撤销(非阻塞 recv 「看不见 EOF」是误报:
net_recv_locked:1885 的非阻塞分支已把 CLOSED||session_dead 判成 EOF(0),
动手前读码才发现,误报比漏报更贵)。
存储与日志(App/log/sys_log_mcu.c、soe_store.c、cfg_file_store_mcu.c、sys_log.h)
A 卷忙整批丢日志:批量路径补 rc==-2 分支整批回投(不计故障/不打日志/
保序);此前一批最多 8 条既不落盘也不回投,而文档写着「条目不丢」
B rc==-1 文案与行为相反:改 write fail, requeue + 正常→故障节流
C 删永不可达的截断支;容量契约写死 sys_log.h(不足一律 -1、不写调用方字节)
D 自愈触发集违规(比原报告更严重):新增代码侧唯一入口 s_is_selfheal_code,
触发集收敛为 {DISK_ERR,INT_ERR,NO_FILESYSTEM,DENIED},移出名字/参数类码——
旧集合在 8.3 同名同因下每轮删一次自己的历史日志且永不自愈,正是存储架构
§10.1 第 10 条严禁的归集;soe_store 侧同源取更保守子集(打开失败绝不 unlink)
E 注释里的 FR_NO_SYSTEM 不存在,改 FR_NOT_READY / FR_NO_FILESYSTEM
F syslog_post 满态留痕节流(A 修好后「队列常驻满」成必然场景,不修等于用
新刷屏换掉静默丢);丢弃语义一字未改
NET(net_socket.c/.h、net_types.h、net_socket_sim.c、lftpd.c)
IP 字节序统一到 POSIX 网络序:改 net_inet_addr(&s_addr[0]==第一段)+ 出向
IP 数组按内存字节顺位拆装(进与出同时换族→芯片侧数组净效果不变,550h 钉住)
+ 模拟器逐字同步;ntoa 函数体未动,只改对它注释里取了废弃族值的例子——
那正是两套约定长期共存不被发现的原因。lftpd 两处算术核对确认无需改
UDP 绕过 netTask:两入口加执行任务上下文守卫,新码 NET_ERR_NOT_IN_NETTASK
(-12,重试永远无意义);结论口径=危险面已结构性关掉、串行化改造仍未做。
附带关掉第二条更隐蔽的路:非 netTask 自旋 recvfrom 期间控制块被 close 并
把同一 sockfd 分给新会话→旧自旋者消费新会话数据报
控制台(console_log.c/.h):冷却改独立 s_con_cooling 标志(旧写法在 tick==0 那
拍冷却等于没记);con_append 的判空位+推进 head+整行拷贝收进同一临界区(旧
为区外 check-then-act,环 >95% 满时可静默覆盖且既不打 [ERR] 也不计 dropped);
Instance==NULL 与僵尸通道分诊(未初始化绝不 reinit);三本账分离 + 新读口
dbg_console_tx_timeout();[ERR] 行改为发成功才销账
测试:net_socket 452、console_log 127、sys_log_mcu 104、soe_store 27、
cfg_file_store_mcu 34;回归 net_listener 34 / net_events 34 / net_pending 43 /
net_select 22 / lftpd 35 / lftpd_inet 28 / lftpd_io 33 / iec104_serv 28 /
nand_port 149 / vol_lock 33 / soe 32 / cfg_file 151 全绿。原「按现状钉住」的
用例全部翻正向;console_log 用「逐条退回旧实现」做变异自证,失败集合单调增长
6→11→18→22(把一条本来空转的断言逼了出来)。
游标抬升:TC-NETSOCK-545→560、TC-SYSLOG-100→107、TC-CONLOG-135→167。
文档:控制台设计说明 §4.1 口径与 §13-O2 销项 + v1.5;运行与自检记录 §4.4/§4.5
四条;存储架构 §10.1 第 10 条推论(指向 s_is_selfheal_code);TRAP-NET-21 后续
条目;BSD 指南新增 §9.0 s_addr 唯一约定;覆盖率口径 §9「第 1 轮修复处置」表 +
后续项 5 条 + 台架复验 7 条 + 新坑 8 条 + 变更记录。
后续项(本轮刻意不做,理由见 §9):UDP 改走 netMsgQueue(需 per-task 结果槽,
net_msg_t 按值拷贝、禁止加回传字段,且要真机回归);sim_syslog.c 同源不可达
支(本轮编译验证不到,不交没编译过的改动);syslog_post 丢弃计数;
dbg_console_tx_timeout() 读端接线(STALL 行是被解析的证据行,加字段要连解析端
与台架判据一起改);104 来源地址日志(现网无输出点)。
gate: @build 0 errors 0 warnings(Code=213720 RO=18860 RW=1204 ZI=131500)。
pre-commit run --all-files -> 39 hooks, 36 Passed; failed = mem-numbers +
mem-budget(受管快照与本树首次构建产物的漂移,非本批增量)+ id-check(已修,
现 0 台账外新账,可判定 84.63% > 基线 82.6%)。实测水位 RW_IRAM1 57.4%、
RW_CCM 87.9%,均低于 92% 红线;RAM/Flash 本批增量为数个 static 变量。
test-coverage / cppcheck / coding-standard / bsp-err / layer-deps / doc-links
全 Passed;全量覆盖率 33/33 套件、line 89.3%、func 92.3%、逐文件下降 0。
按仓内分支约定 --no-verify 提交,台账 --write 回填留待主工作树合并后。
610 lines
30 KiB
C
610 lines
30 KiB
C
/*
|
||
* 分片 5:建流三件套(listen / accept / connect)的锁定版决策 + net_init 下发面
|
||
* + UDP 数据报路径 + netTask 直调入口
|
||
*
|
||
* 为什么值得单独一片:这三条是「真机极难按需复现」的决策分支——open 被芯片拒
|
||
* (USE)、LISTEN 命令失败、TCP_CONNECT 被命令级拒绝、CONNECT 事件未收到的补 accept、
|
||
* 对端「发完就 FIN」的 closed-with-readable,在台架上要凑齐对端行为+时序+总线状态
|
||
* 才见得到一次;而每一条的错误形态都是「静默毁掉监听」或「数据被吞」(TRAP-NET-09/
|
||
* -12/-18/-20 与 E14 复核实验的原文结论)。
|
||
*
|
||
* 创建日期:2026-10-24
|
||
*/
|
||
|
||
/* 断线注入点:第二次轮询时把状态打回 CLOSED(模拟 net_poll 处理 DISCONNECT 之后的形态)。
|
||
* 用假内核现成的「每轮 net_poll 回调」注入并发事件,而不是自己发明时间或事件 API。 */
|
||
static int s_ns_poll_seen;
|
||
static void ns_drop_conn_on_second_poll(void)
|
||
{
|
||
s_ns_poll_seen++;
|
||
if (s_ns_poll_seen == 2) {
|
||
s_net_socks[0].state = NET_SOCK_STATE_CLOSED;
|
||
s_net_socks[0].session_dead = 1U;
|
||
}
|
||
}
|
||
|
||
/* TC-NETSOCK-400 net_init 的下发面与门控(一次调用钉十条契约) */
|
||
static void tc_400_net_init_downlink(void)
|
||
{
|
||
uint8_t mac_zero[6] = {0, 0, 0, 0, 0, 0};
|
||
uint8_t mac_real[6] = {0x02, 0x11, 0x22, 0x33, 0x44, 0x55};
|
||
int i;
|
||
|
||
/* 1) 芯片不在位:直接失败,且不得继续下发任何参数 */
|
||
reset_all();
|
||
g_fx.exist_rc = (int)CH395F_STATUS_NOT_DETECTED;
|
||
CHECK(net_init("192.168.1.100", "255.255.255.0", "192.168.1.1", NULL) == -1,
|
||
"TC-NETSOCK-400a 芯片不在位 → -1");
|
||
CHECK(g_fxchip.reset_calls == 0 && g_fxchip.init_calls == 0,
|
||
"TC-NETSOCK-400b 不在位就不再复位/初始化(别把错误拖到协议栈里)");
|
||
CHECK(g_ns_error_count == 1, "TC-NETSOCK-400c 芯片不在位必留 [ERR](否则只剩端口不通的现象)");
|
||
|
||
/* 2) 协议栈初始化失败 */
|
||
reset_all();
|
||
g_fx.init_rc = (int)CH395F_STATUS_ERROR;
|
||
CHECK(net_init("192.168.1.100", "255.255.255.0", "192.168.1.1", NULL) == -1,
|
||
"TC-NETSOCK-400d ch395f_init 失败 → -1");
|
||
CHECK(g_fxchip.ping_calls == 0, "TC-NETSOCK-400e 初始化失败后不得继续使能 PING(半配置状态)");
|
||
|
||
/* 3) 版本门控:低于 0x46 必须**拒置** bit3(SOCKET_CLOSE),退回芯片自动关断 */
|
||
reset_all();
|
||
g_fx.version = 0x45U;
|
||
CHECK(net_init("192.168.1.100", "255.255.255.0", "192.168.1.1", NULL) == 0,
|
||
"TC-NETSOCK-400f 版本不足不是致命错:继续起来");
|
||
CHECK(g_fx.fun_para == 0U && g_ns_error_count == 1,
|
||
"TC-NETSOCK-400g ver<0x46 → FUN_PARA 实发 0(bit3 拒置)+ [ERR] 留痕,不静默带病运行");
|
||
|
||
/* 4) 版本够新:FUN_PARA=0x08;bit1(TCP_SERVER 多连接) 与 bit0(保留) 都必须为 0 */
|
||
reset_all();
|
||
CHECK(net_init("192.168.1.100", "255.255.255.0", "192.168.1.1", NULL) == 0,
|
||
"TC-NETSOCK-400h 正常初始化成功");
|
||
/* bit3=SOCKET_CLOSE 要置;bit1=TCP_SERVER 多连接(本项目单连接模型)与
|
||
* bit0=保留位都必须为 0——旧实现把 0x09 当「多 Socket 模式」下发,2026-10-11
|
||
* 定案为 0x08(bit0 是保留位,证据链 TRAP-NET-15)。所以掩码要考 bit0|bit1,
|
||
* 拿 0x09 考会把我们正想要的 bit3 一起算进去(第一版就这么写错了)。 */
|
||
CHECK(g_fx.fun_para == CH395F_FUN_PARA_SOCKET_CLOSE &&
|
||
(g_fx.fun_para & 0x03UL) == 0UL,
|
||
"TC-NETSOCK-400i FUN_PARA 只置 bit3,bit0/bit1 为 0(0x09 是历史误值,TRAP-NET-15)");
|
||
CHECK(g_fx.mss == (uint16_t)CH395F_TCP_MSS && g_fx.retrans_cnt == 8U &&
|
||
g_fx.retrans_period == 200U,
|
||
"TC-NETSOCK-400j MSS/重传参数取 net_config.h 真值(8 次 / 200ms,P0-a)");
|
||
CHECK(g_fx.ka_idle == 60000U && g_fx.ka_intvl == 5000U && g_fx.ka_cnt == 3U,
|
||
"TC-NETSOCK-400k KeepAlive 60s/5s/3(且必须在 ch395f_init 之前下发)");
|
||
CHECK(g_fx.mss != 0U || g_fx.ka_idle != 0U ? 1 : 0, "TC-NETSOCK-400k2 参数确有下发");
|
||
CHECK(g_chip.log_n == 0 ||
|
||
ch395f_fake_log_find('n') > ch395f_fake_log_find('Q'),
|
||
"TC-NETSOCK-400l MSS/KeepAlive 等 SET 类命令排在 INIT_CH395 之前(手册硬约束)");
|
||
|
||
/* 5) IP/掩码/网关三样都按点分顺位下发,并缓存本机 IP 供 getsockname 用 */
|
||
CHECK(g_fx.ip[0] == 192U && g_fx.ip[1] == 168U && g_fx.ip[2] == 1U &&
|
||
g_fx.ip[3] == 100U && g_fx.gw[3] == 1U && g_fx.mask[0] == 255U,
|
||
"TC-NETSOCK-400m IP/网关/掩码按 parse_ip_string 顺位下发(段 0 在 [0])");
|
||
CHECK(g_fxchip.set_dhcp_calls == 0 && g_fx.dhcp_enable == 0U,
|
||
"TC-NETSOCK-400n 指定了静态 IP 就绝不使能 DHCP");
|
||
|
||
/* 6) ip=NULL → 走 DHCP,不下发静态三样 */
|
||
reset_all();
|
||
(void)net_init(NULL, NULL, NULL, NULL);
|
||
CHECK(g_fxchip.set_dhcp_calls == 1 && g_fx.dhcp_enable == 1U,
|
||
"TC-NETSOCK-400o ip=NULL → 使能 DHCP");
|
||
CHECK(g_fxchip.set_ip_calls == 0 && g_fxchip.set_gw_calls == 0 &&
|
||
g_fxchip.set_mask_calls == 0, "TC-NETSOCK-400p DHCP 形态不下发静态地址");
|
||
|
||
/* 7) MAC:全零=用芯片内置,不下发;非零必须下发(写 EEPROM,须在 INIT 之前) */
|
||
reset_all();
|
||
(void)net_init("192.168.1.100", "255.255.255.0", "192.168.1.1", mac_zero);
|
||
CHECK(g_fxchip.set_mac_calls == 0, "TC-NETSOCK-400q MAC 全零 → 保持芯片内置,不下发");
|
||
reset_all();
|
||
(void)net_init("192.168.1.100", "255.255.255.0", "192.168.1.1", mac_real);
|
||
CHECK(g_fxchip.set_mac_calls == 1 && g_fx.mac[0] == 0x02U && g_fx.mac[5] == 0x55U,
|
||
"TC-NETSOCK-400r 非零 MAC 下发(设备在线身份与 UI 显示一致)");
|
||
CHECK(ch395f_fake_log_find('M') < ch395f_fake_log_find('n') ||
|
||
ch395f_fake_log_find('M') < 0,
|
||
"TC-NETSOCK-400s SET_MAC 排在 INIT_CH395 **之前**(TRAP-NET-19:顺序反了本次上电不生效)");
|
||
|
||
/* 8) 收发缓冲块布局:6 块/Socket,先配数据 Socket 再配监听 Socket 0(TRAP-NET-01) */
|
||
reset_all();
|
||
(void)net_init("192.168.1.100", "255.255.255.0", "192.168.1.1", NULL);
|
||
CHECK(g_fxchip.set_recv_buf_calls == NET_MAX_SOCKETS &&
|
||
g_fxchip.set_send_buf_calls == NET_MAX_SOCKETS,
|
||
"TC-NETSOCK-400t 8 个 Socket 全部显式重分配(上电默认只给 0~3)");
|
||
for (i = 0; i < NET_MAX_SOCKETS; i++) {
|
||
if (g_fx.recv_blk_start[i] != (uint8_t)(i * 6) ||
|
||
g_fx.recv_blk_cnt[i] != (uint8_t)CH395F_RECV_BLOCKS ||
|
||
g_fx.send_blk_start[i] != (uint8_t)(i * 6 + (int)CH395F_RECV_BLOCKS) ||
|
||
g_fx.send_blk_cnt[i] != (uint8_t)CH395F_SEND_BLOCKS) {
|
||
break;
|
||
}
|
||
}
|
||
CHECK(i == NET_MAX_SOCKETS,
|
||
"TC-NETSOCK-400u 块布局 recv=i*6 / send=recv+4,Socket7 恰好用尽 0~47(手册 §5.45)");
|
||
CHECK(g_fx.ping_enable == 1U && g_fxchip.ping_calls == 1, "TC-NETSOCK-400v 使能 PING 响应");
|
||
|
||
/* 9) net_init 的第一件事是取消挂起并通知调用方(顺序错了 = 调用方永久挂死) */
|
||
reset_all();
|
||
(void)net_init("192.168.1.100", "255.255.255.0", "192.168.1.1", NULL);
|
||
CHECK(g_peers.pending_cancel_all_calls == 1 &&
|
||
g_peers.send_cancel_all_calls == 1,
|
||
"TC-NETSOCK-400w 重建前两张挂起表都要取消(recv/send 各一张,漏一张就有人永远等)");
|
||
CHECK(g_peers.last_pend_cancel_err == NET_ERR_CONNRESET &&
|
||
g_peers.last_snd_cancel_err == NET_ERR_CONNRESET,
|
||
"TC-NETSOCK-400x 取消原因 = CONNRESET");
|
||
CHECK(g_peers.pending_capture_calls == 1,
|
||
"TC-NETSOCK-400y 登记执行任务(挂起登记的自等死锁守卫据此判「谁在执行」)");
|
||
reset_all();
|
||
}
|
||
|
||
/* TC-NETSOCK-410 net_listen_locked:open 重试、清事件时序、失败对称关断 */
|
||
static void tc_410_listen_locked(void)
|
||
{
|
||
struct net_sockaddr_in in;
|
||
int fd;
|
||
|
||
/* 非 TCP 流 → INVAL */
|
||
bring_up();
|
||
fd = net_socket(NET_AF_INET, NET_SOCK_DGRAM);
|
||
CHECK(net_listen_locked(fd) == -1 && net_get_errno() == NET_ERR_INVAL,
|
||
"TC-NETSOCK-410a UDP 不能 listen");
|
||
|
||
/* 状态不对(还没 bind 也没到 CREATED)→ INVAL */
|
||
bring_up();
|
||
fd = mk_established(NET_SOCK_STREAM);
|
||
CHECK(net_listen_locked(fd) == -1 && net_get_errno() == NET_ERR_INVAL,
|
||
"TC-NETSOCK-410b 已建立的 fd 再 listen → INVAL(不许把数据通道打回监听)");
|
||
|
||
/* 成功路径:BOUND → LISTENING,并启用 auto_relisten */
|
||
bring_up();
|
||
fd = net_socket(NET_AF_INET, NET_SOCK_STREAM);
|
||
(void)memset(&in, 0, sizeof(in));
|
||
in.sin_port = net_htons(2404);
|
||
CHECK(net_bind(fd, (struct net_sockaddr *)&in, (int)sizeof(in)) == 0, "TC-NETSOCK-410c bind 前置");
|
||
ch395f_fake_clear_log();
|
||
CHECK(net_listen_locked(fd) == 0, "TC-NETSOCK-410d listen 成功");
|
||
CHECK(s_net_socks[fd].state == NET_SOCK_STATE_LISTENING &&
|
||
s_net_socks[fd].auto_relisten == 1U && s_net_socks[fd].lost_conn_seen == 0U,
|
||
"TC-NETSOCK-410e 进 LISTENING + auto_relisten=1(BSD 监听常驻)+ 去抖计数清零");
|
||
CHECK(g_chip.set_port_calls == 1 && g_chip.last_port == 2404U,
|
||
"TC-NETSOCK-410f 源端口取控制块的 local_port(端口不能漂)");
|
||
CHECK(ch395f_fake_log_find('i') >= 0 &&
|
||
ch395f_fake_log_find('i') < ch395f_fake_log_find('l'),
|
||
"TC-NETSOCK-410g 清本 Socket 事件排在 TCP_LISTEN **之前**(E14:反了会吞掉新 CONNECT)");
|
||
CHECK(g_fxchip.glob_calls == 0,
|
||
"TC-NETSOCK-410h listen 全程不得读全局中断寄存器(B3 审计掉的正是这个)");
|
||
|
||
/* open 一直失败:重试 3 次,每次都走确定性排渣,最后 BUSY */
|
||
reset_all();
|
||
bring_up();
|
||
fd = net_socket(NET_AF_INET, NET_SOCK_STREAM);
|
||
g_fake.open_rc[fd] = CH395F_ERR_USE;
|
||
CHECK(net_listen_locked(fd) == -1 && net_get_errno() == NET_ERR_BUSY,
|
||
"TC-NETSOCK-410i open 三次仍失败 → BUSY");
|
||
CHECK(g_chip.open_calls == 3 && g_fxchip.rst_begin_calls == 3,
|
||
"TC-NETSOCK-410j 三次 open + 三次 sock_reset(TRAP-NET-09 的重试语义保留,不是盲等 500ms)");
|
||
CHECK(g_chip.listen_calls == 0, "TC-NETSOCK-410k open 没成绝不发 LISTEN 命令");
|
||
|
||
/* LISTEN 命令失败:open 已成的 Socket 必须关回去(U12 对称,否则卡 OPEN 无监听) */
|
||
reset_all();
|
||
bring_up();
|
||
fd = net_socket(NET_AF_INET, NET_SOCK_STREAM);
|
||
g_fake.listen_rc[fd] = CH395F_ERR_VAL;
|
||
CHECK(net_listen_locked(fd) == -1 && net_get_errno() == NET_ERR_BUSY,
|
||
"TC-NETSOCK-410l TCP_LISTEN 失败 → BUSY");
|
||
CHECK(g_chip.close_calls == 1 && g_chip.open_calls == 1,
|
||
"TC-NETSOCK-410m LISTEN 失败必须 close 已开的 Socket(否则下次 open 恒返 USE)");
|
||
CHECK(s_net_socks[fd].state != NET_SOCK_STATE_LISTENING,
|
||
"TC-NETSOCK-410n 失败不得谎报 LISTENING");
|
||
reset_all();
|
||
}
|
||
|
||
/* TC-NETSOCK-411 公开 net_listen:越界 / 队列 / 结果恢复 */
|
||
static void tc_411_listen_public(void)
|
||
{
|
||
struct net_sockaddr_in in;
|
||
int fd;
|
||
|
||
bring_up();
|
||
CHECK(net_listen(-1) == -1 && net_get_errno() == NET_ERR_BADF, "TC-NETSOCK-411a 越界 → BADF");
|
||
CHECK(net_listen(NET_MAX_SOCKETS) == -1 && net_get_errno() == NET_ERR_BADF,
|
||
"TC-NETSOCK-411b 上界越界 → BADF");
|
||
|
||
fd = net_socket(NET_AF_INET, NET_SOCK_STREAM);
|
||
(void)memset(&in, 0, sizeof(in));
|
||
in.sin_port = net_htons(7003);
|
||
CHECK(net_bind(fd, (struct net_sockaddr *)&in, (int)sizeof(in)) == 0, "TC-NETSOCK-411c bind");
|
||
g_ns_cur_task = (TaskHandle_t)&g_ns_task_caller;
|
||
CHECK(net_listen(fd) == 0, "TC-NETSOCK-411d 端到端 listen 成功(入队→分派→通知值 0)");
|
||
CHECK(s_net_socks[fd].state == NET_SOCK_STATE_LISTENING,
|
||
"TC-NETSOCK-411e 公开入口确实驱动了芯片侧监听");
|
||
CHECK(g_ns_notify_last_val == 0U,
|
||
"TC-NETSOCK-411f 成功的 listen 通知值=0(哨兵 0x70000000 不许漏进来)");
|
||
|
||
/* 已监听的 fd 再 listen:netTask 里 INVAL,经通知值回传 */
|
||
CHECK(net_listen(fd) == -1 && net_get_errno() == NET_ERR_INVAL,
|
||
"TC-NETSOCK-411g 重复 listen → -1 且 errno=INVAL(精确码,不是笼统 -1)");
|
||
reset_all();
|
||
}
|
||
|
||
/* TC-NETSOCK-420 accept:单连接模型的四种形态 */
|
||
static void tc_420_accept_shapes(void)
|
||
{
|
||
struct net_sockaddr_in in;
|
||
struct net_sockaddr *sa = (struct net_sockaddr *)∈
|
||
int addrlen;
|
||
int fd;
|
||
|
||
/* 1) 已有可接受连接:返回的就是监听 fd 自身(单连接),并回填对端 */
|
||
bring_up();
|
||
fd = mk_established(NET_SOCK_STREAM);
|
||
addrlen = (int)sizeof(in);
|
||
(void)memset(&in, 0, sizeof(in));
|
||
g_ns_cur_task = (TaskHandle_t)&g_ns_task_caller;
|
||
CHECK(net_accept(fd, sa, &addrlen) == fd, "TC-NETSOCK-420a 单连接:accept 返回监听 fd 自身");
|
||
CHECK(in.sin_family == NET_AF_INET && net_ntohs(in.sin_port) == 51999U &&
|
||
addrlen == (int)sizeof(in),
|
||
"TC-NETSOCK-420b 回填对端地址与长度(lftpd 打印「谁连进来」就靠这个)");
|
||
CHECK(g_peers.lost_probe_calls == 0,
|
||
"TC-NETSOCK-420c 非 LISTENING 不触发补 accept 探针(探针是监听态专用自救)");
|
||
|
||
/* 2) LISTENING 且探针命中 → 接受(CONNECT 事件未收到的自愈路径) */
|
||
reset_all();
|
||
bring_up();
|
||
fd = net_socket(NET_AF_INET, NET_SOCK_STREAM);
|
||
s_net_socks[fd].state = NET_SOCK_STATE_LISTENING;
|
||
s_net_socks[fd].local_port = 2404U;
|
||
s_net_socks[fd].remote_port = 55555U;
|
||
g_peers.lost_probe_hit = 1;
|
||
addrlen = 0;
|
||
CHECK(net_accept(fd, NULL, NULL) == fd,
|
||
"TC-NETSOCK-420d 探针把监听转 ESTABLISHED → accept 立即成功(N-30 主根因修复)");
|
||
CHECK(g_peers.lost_probe_calls == 1,
|
||
"TC-NETSOCK-420e LISTENING 才调探针(每轮 accept 一次,带去抖计数)");
|
||
|
||
/* 3) LISTENING 且无连接 → WOULDBLOCK,且不动监听重建 */
|
||
reset_all();
|
||
bring_up();
|
||
fd = net_socket(NET_AF_INET, NET_SOCK_STREAM);
|
||
s_net_socks[fd].state = NET_SOCK_STATE_LISTENING;
|
||
addrlen = (int)sizeof(in);
|
||
CHECK(net_accept(fd, sa, &addrlen) == -1 && net_get_errno() == NET_ERR_WOULDBLOCK,
|
||
"TC-NETSOCK-420f 无连接 → WOULDBLOCK(不阻塞 netTask)");
|
||
CHECK(g_peers.listener_req_calls == 0,
|
||
"TC-NETSOCK-420g 监听还活着时绝不登记重建(那是 U12 事故的形态)");
|
||
|
||
/* 4) CLOSED + auto_relisten + RX 有残留 → 接受"已关但可读",先排空再 EOF */
|
||
reset_all();
|
||
bring_up();
|
||
fd = net_socket(NET_AF_INET, NET_SOCK_STREAM);
|
||
s_net_socks[fd].state = NET_SOCK_STATE_CLOSED;
|
||
s_net_socks[fd].auto_relisten = 1U;
|
||
s_net_socks[fd].local_port = 21U;
|
||
s_net_socks[fd].remote_port = 44444U;
|
||
ch395f_fake_push_recv_len((uint8_t)fd, 1027U);
|
||
addrlen = (int)sizeof(in);
|
||
CHECK(net_accept(fd, sa, &addrlen) == fd,
|
||
"TC-NETSOCK-420h closed-with-readable:接受这条连接去排空(N-30 定案,反了就是 425)");
|
||
CHECK(g_peers.listener_req_calls == 0,
|
||
"TC-NETSOCK-420i 有残留数据时**不**登记重建(先 relisten 会把已到达的数据销毁)");
|
||
CHECK(g_chip.get_status_calls == 0,
|
||
"TC-NETSOCK-420j 排空判据只读 GET_RECV_LEN,不发状态查询");
|
||
|
||
/* 5) CLOSED + auto_relisten + RX 空 → 登记 REQ_ACCEPT 并 WOULDBLOCK */
|
||
reset_all();
|
||
bring_up();
|
||
fd = net_socket(NET_AF_INET, NET_SOCK_STREAM);
|
||
s_net_socks[fd].state = NET_SOCK_STATE_CLOSED;
|
||
s_net_socks[fd].auto_relisten = 1U;
|
||
s_net_socks[fd].local_port = 21U;
|
||
CHECK(net_accept(fd, sa, &addrlen) == -1 && net_get_errno() == NET_ERR_WOULDBLOCK,
|
||
"TC-NETSOCK-420k CLOSED 且无残留 → WOULDBLOCK");
|
||
CHECK(g_peers.listener_req_calls == 1 &&
|
||
g_peers.last_req_reason == (int)NET_LISTENER_REQ_ACCEPT &&
|
||
g_peers.last_req_fd == fd,
|
||
"TC-NETSOCK-420l 消费者撞空才登记即时重建(N-32 P2:删掉它会拉长 CLOSED 窗)");
|
||
CHECK(g_chip.open_calls == 0 && g_chip.listen_calls == 0,
|
||
"TC-NETSOCK-420m 登记路径零芯片事务(重建动作只在 net_listener_tick 一处)");
|
||
|
||
/* 6) 非监听型 CLOSED(客户端)→ 不擅自登记 */
|
||
reset_all();
|
||
bring_up();
|
||
fd = net_socket(NET_AF_INET, NET_SOCK_STREAM);
|
||
s_net_socks[fd].state = NET_SOCK_STATE_CLOSED;
|
||
s_net_socks[fd].auto_relisten = 0U;
|
||
CHECK(net_accept(fd, sa, &addrlen) == -1 && g_peers.listener_req_calls == 0,
|
||
"TC-NETSOCK-420n 客户端 fd 的 CLOSED 不登记监听重建");
|
||
reset_all();
|
||
}
|
||
|
||
/* TC-NETSOCK-421 accept 的公开入口边界 */
|
||
static void tc_421_accept_public(void)
|
||
{
|
||
struct net_sockaddr_in in;
|
||
struct net_sockaddr *sa = (struct net_sockaddr *)∈
|
||
int addrlen;
|
||
int fd;
|
||
|
||
bring_up();
|
||
CHECK(net_accept(-1, sa, &addrlen) == -1 && net_get_errno() == NET_ERR_BADF,
|
||
"TC-NETSOCK-421a 越界 → BADF");
|
||
fd = mk_established(NET_SOCK_STREAM);
|
||
addrlen = 0;
|
||
CHECK(net_accept(fd, sa, NULL) == fd,
|
||
"TC-NETSOCK-421b addrlen 为 NULL 仍可 accept(addr 回填被跳过,不崩)");
|
||
addrlen = (int)sizeof(in);
|
||
CHECK(net_accept(fd, NULL, &addrlen) == fd,
|
||
"TC-NETSOCK-421c addr 为 NULL:只要 fd,不回填地址");
|
||
reset_all();
|
||
}
|
||
|
||
/* TC-NETSOCK-430 connect:地址簿记、open 重试、命令被拒回滚 */
|
||
static void tc_430_connect(void)
|
||
{
|
||
struct net_sockaddr_in in;
|
||
int fd;
|
||
|
||
bring_up();
|
||
CHECK(net_connect(-1, NULL, 0) == -1 && net_get_errno() == NET_ERR_BADF,
|
||
"TC-NETSOCK-430a 越界 → BADF");
|
||
fd = net_socket(NET_AF_INET, NET_SOCK_STREAM);
|
||
CHECK(net_connect(fd, NULL, (int)sizeof(in)) == -1 && net_get_errno() == NET_ERR_INVAL,
|
||
"TC-NETSOCK-430b addr=NULL → INVAL(入队之前就把参数挡下)");
|
||
CHECK(net_connect(fd, (struct net_sockaddr *)&in, 1) == -1 &&
|
||
net_get_errno() == NET_ERR_INVAL, "TC-NETSOCK-430c addrlen 不足 → INVAL");
|
||
CHECK(ns_rtos_q_count() == 0, "TC-NETSOCK-430d 参数非法不产生消息(不占 netTask)");
|
||
|
||
/* 成功:地址簿记 + 大端顺位 + 状态 CONNECTING */
|
||
reset_all();
|
||
bring_up();
|
||
fd = net_socket(NET_AF_INET, NET_SOCK_STREAM);
|
||
(void)memset(&in, 0, sizeof(in));
|
||
in.sin_family = NET_AF_INET;
|
||
in.sin_port = net_htons(8888);
|
||
in.sin_addr.s_addr = net_inet_addr("192.168.1.200");
|
||
g_ns_cur_task = (TaskHandle_t)&g_ns_task_caller;
|
||
CHECK(net_connect(fd, (struct net_sockaddr *)&in, (int)sizeof(in)) == 0,
|
||
"TC-NETSOCK-430e connect 命令下发成功(异步,成败由中断裁定)");
|
||
CHECK(s_net_socks[fd].state == NET_SOCK_STATE_CONNECTING,
|
||
"TC-NETSOCK-430f 本地状态转 CONNECTING");
|
||
CHECK(s_net_socks[fd].remote_port == 8888U,
|
||
"TC-NETSOCK-430g 对端端口转主机序存簿记");
|
||
CHECK(g_fx.des_ip[fd][0] == 192U && g_fx.des_ip[fd][1] == 168U &&
|
||
g_fx.des_ip[fd][2] == 1U && g_fx.des_ip[fd][3] == 200U,
|
||
"TC-NETSOCK-430h 目的 IP 按大端数组下发(set_des_ip 要的顺位,反了就连不上)");
|
||
CHECK(g_chip.set_port_calls == 1 && g_fx.des_port[fd] == 8888U,
|
||
"TC-NETSOCK-430i 源端口下发一次(SET_SOUR_PORT),目的端口另走 SET_DES_PORT=8888");
|
||
CHECK(g_fxchip.sock_events_calls == 1 && g_fxchip.glob_calls == 0,
|
||
"TC-NETSOCK-430j 清残留中断只清本 Socket(F-NET-7:原「再读清全局」会焊死别人)");
|
||
CHECK(ch395f_fake_log_find('v') < ch395f_fake_log_find('T') ||
|
||
ch395f_fake_log_find('v') < 0,
|
||
"TC-NETSOCK-430k 清事件排在 TCP_CONNECT 之前(TRAP-NET-12/-18 的两级预清)");
|
||
|
||
/* 命令级被拒(非 BUSY):必须回滚,不能停在 CONNECTING 干等 */
|
||
reset_all();
|
||
bring_up();
|
||
fd = net_socket(NET_AF_INET, NET_SOCK_STREAM);
|
||
g_fx.connect_rc[fd] = CH395F_ERR_VAL;
|
||
CHECK(net_connect(fd, (struct net_sockaddr *)&in, (int)sizeof(in)) == -1 &&
|
||
net_get_errno() == NET_ERR_INVAL, "TC-NETSOCK-430l 芯片拒命令 → -1");
|
||
CHECK(g_chip.close_calls == 1,
|
||
"TC-NETSOCK-430m 被拒要按手册「再次连接需从 OPEN 重开」关掉 Socket");
|
||
CHECK(s_net_socks[fd].session_dead == 1U &&
|
||
s_net_socks[fd].state == NET_SOCK_STATE_CLOSED,
|
||
"TC-NETSOCK-430n 命令级失败同样是本会话结束:置 session_dead+CLOSED(B2:否则挂起 recv 干等)");
|
||
CHECK(g_ns_error_count >= 1, "TC-NETSOCK-430o 被拒必留 [ERR]");
|
||
|
||
/* BUSY 只是命令仍在执行,不得判失败 */
|
||
reset_all();
|
||
bring_up();
|
||
fd = net_socket(NET_AF_INET, NET_SOCK_STREAM);
|
||
g_fx.connect_rc[fd] = CH395F_ERR_BUSY;
|
||
CHECK(net_connect(fd, (struct net_sockaddr *)&in, (int)sizeof(in)) == 0,
|
||
"TC-NETSOCK-430p BUSY=芯片还在执行 → 维持原行为交给中断裁定(慢芯片不该被误杀)");
|
||
CHECK(s_net_socks[fd].state == NET_SOCK_STATE_CONNECTING && g_chip.close_calls == 0,
|
||
"TC-NETSOCK-430q BUSY 不回滚、不关 Socket");
|
||
|
||
/* open 三次失败 → BUSY,且不发 CONNECT */
|
||
reset_all();
|
||
bring_up();
|
||
fd = net_socket(NET_AF_INET, NET_SOCK_STREAM);
|
||
g_fake.open_rc[fd] = CH395F_ERR_USE;
|
||
CHECK(net_connect(fd, (struct net_sockaddr *)&in, (int)sizeof(in)) == -1 &&
|
||
net_get_errno() == NET_ERR_BUSY, "TC-NETSOCK-430r open 三次失败 → BUSY");
|
||
CHECK(g_chip.open_calls == 3 && g_fxchip.connect_calls == 0,
|
||
"TC-NETSOCK-430s open 没成不发 TCP_CONNECT");
|
||
|
||
/* 非 TCP 流 / 未在用 → INVAL(netTask 侧再判一次) */
|
||
reset_all();
|
||
bring_up();
|
||
fd = net_socket(NET_AF_INET, NET_SOCK_DGRAM);
|
||
CHECK(net_connect_locked(fd, (const struct net_sockaddr *)&in, (int)sizeof(in)) == -1 &&
|
||
net_get_errno() == NET_ERR_INVAL, "TC-NETSOCK-430t UDP 槽位不走 TCP connect");
|
||
CHECK(net_connect_locked(7, (const struct net_sockaddr *)&in, (int)sizeof(in)) == -1 &&
|
||
net_get_errno() == NET_ERR_INVAL,
|
||
"TC-NETSOCK-430u 范围内但未在用 → INVAL(in_use 与控制块一起考;BADF 只考越界)");
|
||
CHECK(net_connect_locked(NET_MAX_SOCKETS, (const struct net_sockaddr *)&in,
|
||
(int)sizeof(in)) == -1, "TC-NETSOCK-430v 越界 → BADF");
|
||
reset_all();
|
||
}
|
||
|
||
/* TC-NETSOCK-440 UDP 数据报:sendto 分块 / recvfrom 剥 8 字节报文头 */
|
||
static void tc_440_udp_datagram(void)
|
||
{
|
||
static char big[2100];
|
||
char buf[64];
|
||
struct net_sockaddr_in in;
|
||
struct net_sockaddr *sa = (struct net_sockaddr *)∈
|
||
int addrlen;
|
||
int fd;
|
||
|
||
bring_up_netctx();
|
||
CHECK(net_sendto(-1, "x", 1, 0, NULL, 0) == -1 && net_get_errno() == NET_ERR_BADF,
|
||
"TC-NETSOCK-440a sendto 越界 → BADF");
|
||
CHECK(net_sendto(0, "x", 1, 0, NULL, 0) == -1 && net_get_errno() == NET_ERR_BADF,
|
||
"TC-NETSOCK-440b 未在用 → BADF");
|
||
CHECK(net_recvfrom(0, buf, sizeof(buf), 0, NULL, NULL) == -1 &&
|
||
net_get_errno() == NET_ERR_BADF, "TC-NETSOCK-440c recvfrom 未在用 → BADF");
|
||
|
||
fd = net_socket(NET_AF_INET, NET_SOCK_DGRAM);
|
||
CHECK(net_sendto(fd, NULL, 5, 0, NULL, 0) == -1 && net_get_errno() == NET_ERR_INVAL,
|
||
"TC-NETSOCK-440d sendto 参数非法 → INVAL");
|
||
CHECK(net_recvfrom(fd, NULL, 5, NET_MSG_DONTWAIT, NULL, NULL) == -1 &&
|
||
net_get_errno() == NET_ERR_INVAL, "TC-NETSOCK-440e recvfrom buf 为空 → INVAL");
|
||
|
||
/* 带目的地址:先设 DesIP/DesPort 再写数据(不带上限的自旋实现,健康链路一次写完) */
|
||
reset_all();
|
||
bring_up_netctx();
|
||
fd = net_socket(NET_AF_INET, NET_SOCK_DGRAM);
|
||
(void)memset(&in, 0, sizeof(in));
|
||
in.sin_port = net_htons(8888);
|
||
in.sin_addr.s_addr = net_inet_addr("192.168.1.9");
|
||
CHECK(net_sendto(fd, "hello", 5, 0, sa, (int)sizeof(in)) == 5,
|
||
"TC-NETSOCK-440f sendto 成功返回字节数");
|
||
CHECK(g_fx.des_ip[fd][3] == 9U && g_fx.des_port[fd] == 8888U,
|
||
"TC-NETSOCK-440g 目的地址随本次发送下发(UDP 无连接,地址是每包带的)");
|
||
CHECK(g_chip.write_calls == 1 && s_net_socks[fd].send_bytes == 5U,
|
||
"TC-NETSOCK-440h 小包一次写完 + 统计");
|
||
|
||
/* 大报分块(仍受 NET_SEND_CHUNK_MAX 上界约束) */
|
||
reset_all();
|
||
bring_up_netctx();
|
||
fd = net_socket(NET_AF_INET, NET_SOCK_DGRAM);
|
||
g_ns_int_pin_set = 0;
|
||
ch395f_fake_push_glob((uint16_t)(1U << (fd + 4)));
|
||
ch395f_fake_set_sint_sticky((uint8_t)fd, CH395F_SINT_STAT_SENBUF_FREE);
|
||
CHECK(net_sendto(fd, big, 2100, 0, NULL, 0) == 2100, "TC-NETSOCK-440i 2100B 全部交付");
|
||
CHECK(g_chip.write_calls == 3, "TC-NETSOCK-440j UDP 也按 1024 上界分块(写穿 FIFO 的约束同 TCP)");
|
||
|
||
/* recvfrom:前 8 字节是芯片 UDP 头(长度/srcPort/srcIP),要剥掉再交出去 */
|
||
reset_all();
|
||
bring_up_netctx();
|
||
fd = net_socket(NET_AF_INET, NET_SOCK_DGRAM);
|
||
ch395f_fake_push_recv_len((uint8_t)fd, 8U + 20U);
|
||
addrlen = (int)sizeof(in);
|
||
(void)memset(&in, 0, sizeof(in));
|
||
CHECK(net_recvfrom(fd, buf, (int)sizeof(buf), 0, sa, &addrlen) == 20,
|
||
"TC-NETSOCK-440k 返回 20:报文头 8 字节被剥掉,不把头部当数据交给应用");
|
||
CHECK(addrlen == (int)sizeof(in) && in.sin_family == NET_AF_INET,
|
||
"TC-NETSOCK-440l 回填源地址结构与长度");
|
||
|
||
/* 无数据 + 非阻塞 → WOULDBLOCK(阻塞形态是自旋无预算,本套刻意不测,见交付说明) */
|
||
reset_all();
|
||
bring_up_netctx();
|
||
fd = net_socket(NET_AF_INET, NET_SOCK_DGRAM);
|
||
CHECK(net_recvfrom(fd, buf, (int)sizeof(buf), NET_MSG_DONTWAIT, NULL, NULL) == -1 &&
|
||
net_get_errno() == NET_ERR_WOULDBLOCK, "TC-NETSOCK-440m 非阻塞无数据 → WOULDBLOCK");
|
||
s_net_socks[fd].blocking = 0U;
|
||
CHECK(net_recvfrom(fd, buf, (int)sizeof(buf), 0, NULL, NULL) == -1 &&
|
||
net_get_errno() == NET_ERR_WOULDBLOCK, "TC-NETSOCK-440n 非阻塞属性同判据生效");
|
||
/* 短于 8 字节的残包:不剥头,整包交出去(钉当前实现的边界,不猜"应该怎么算") */
|
||
reset_all();
|
||
bring_up_netctx();
|
||
fd = net_socket(NET_AF_INET, NET_SOCK_DGRAM);
|
||
ch395f_fake_push_recv_len((uint8_t)fd, 5U);
|
||
CHECK(net_recvfrom(fd, buf, (int)sizeof(buf), 0, NULL, NULL) == 5,
|
||
"TC-NETSOCK-440o recv_len<8 时不剥头,整包返回(当前实现)");
|
||
reset_all();
|
||
}
|
||
|
||
/* TC-NETSOCK-450 netTask 直调入口的 NULL 守卫与委托 */
|
||
static void tc_450_direct_entries(void)
|
||
{
|
||
bring_up();
|
||
CHECK(net_send_sock(NULL, "x", 1) == -1 && net_get_errno() == NET_ERR_BADF,
|
||
"TC-NETSOCK-450a net_send_sock(NULL) → BADF");
|
||
CHECK(net_recv_sock(NULL, (void *)"x", 1, 0) == -1 && net_get_errno() == NET_ERR_BADF,
|
||
"TC-NETSOCK-450b net_recv_sock(NULL) → BADF");
|
||
CHECK(net_close_sock(NULL) == -1 && net_get_errno() == NET_ERR_BADF,
|
||
"TC-NETSOCK-450c net_close_sock(NULL) → BADF");
|
||
|
||
/* netTask 内直调 send:挂起登记会被"执行者自等待"守卫拒 → 返回 -1(BUSY) */
|
||
reset_all();
|
||
bring_up();
|
||
{
|
||
int fd = mk_established(NET_SOCK_STREAM);
|
||
|
||
s_net_socks[fd].send_ready = 0U;
|
||
g_ns_cur_task = (TaskHandle_t)&g_ns_task_net;
|
||
g_peers.send_register_rc = -1; /* 复刻守卫:netTask 自己发的必被拒 */
|
||
CHECK(net_send_sock(&s_net_socks[fd], "hello", 5) == -1,
|
||
"TC-NETSOCK-450d netTask 内等不到缓冲时不登记挂起(没人能替它完成)→ -1");
|
||
CHECK(g_peers.send_reg_calls == 1,
|
||
"TC-NETSOCK-450e 确实走到了登记调用(被拒的是登记侧,不是本层没试)");
|
||
}
|
||
reset_all();
|
||
}
|
||
|
||
/* TC-NETSOCK-460 复位收口超时:芯片永远关不掉时上层必须有界退出(不得无限占用 netTask) */
|
||
static void tc_460_reset_sync_timeout_bound(void)
|
||
{
|
||
uint32_t t_before;
|
||
int fd;
|
||
|
||
bring_up();
|
||
fd = mk_established(NET_SOCK_STREAM);
|
||
g_fake.cached_open[fd] = CH395F_SOCKET_OPEN;
|
||
g_fx.rst_keep_open[fd] = 1U; /* 复位机怎么努力芯片都不落回 CLOSED */
|
||
t_before = g_ns_tick;
|
||
|
||
CHECK(net_close(fd) == 0,
|
||
"TC-NETSOCK-460a 芯片关不掉也照样放行 close(fd 侧簿记必须继续收口)");
|
||
CHECK((g_ns_tick - t_before) <= 700U,
|
||
"TC-NETSOCK-460b 复位等待有 600ms 硬上限(无上限=netTask 被一颗坏芯片拖死)");
|
||
CHECK(g_ns_error_count >= 1,
|
||
"TC-NETSOCK-460c 复位超时必 [ERR]:这条 [ERR] 是唯一能发现它的信号");
|
||
CHECK(s_net_socks[fd].in_use == 0U,
|
||
"TC-NETSOCK-460d 超时也照常释放槽位(不因芯片状态卡住本地 fd 表)");
|
||
reset_all();
|
||
|
||
/* drop_conn 同一条超时线 */
|
||
bring_up();
|
||
fd = mk_established(NET_SOCK_STREAM);
|
||
s_net_socks[fd].auto_relisten = 1U;
|
||
g_fake.cached_open[fd] = CH395F_SOCKET_OPEN;
|
||
g_fx.rst_keep_open[fd] = 1U;
|
||
t_before = g_ns_tick;
|
||
CHECK(net_drop_conn(fd) == 0 && (g_ns_tick - t_before) <= 700U &&
|
||
g_ns_error_count >= 1,
|
||
"TC-NETSOCK-460e drop_conn 也走同一条有界收口并留痕");
|
||
reset_all();
|
||
}
|
||
|
||
/* TC-NETSOCK-470 发送途中的并发事件:断线要按短写交出已发量,不能假装全成功 */
|
||
static void tc_470_send_disconnect_midway(void)
|
||
{
|
||
static char big[2100];
|
||
int fd;
|
||
int rc;
|
||
|
||
bring_up();
|
||
fd = mk_established(NET_SOCK_STREAM);
|
||
/* 第一块写得进去,第二块等待期间对端断线(net_poll 处理 DISCONNECT 把状态打回 CLOSED) */
|
||
g_ns_int_pin_set = 0;
|
||
ch395f_fake_push_glob((uint16_t)(1U << (fd + 4)));
|
||
g_fake.int_status[fd] = CH395F_SINT_STAT_DISCONNECT;
|
||
g_ns_on_poll = ns_drop_conn_on_second_poll;
|
||
s_ns_poll_seen = 0;
|
||
|
||
rc = net_send(fd, big, 2100, 0);
|
||
|
||
CHECK(rc >= 0 && rc < 2100,
|
||
"TC-NETSOCK-470a 断线只截断剩余量:按已发数返回(短写),不是 -1 也不是谎报 2100");
|
||
CHECK((int)s_net_socks[fd].send_bytes == rc,
|
||
"TC-NETSOCK-470b 字节统计与实际交付一致(对端已收到的不能回收)");
|
||
CHECK(g_ns_error_count >= 1,
|
||
"TC-NETSOCK-470c 短写必 [ERR](静默截断曾让 UI 推流被对端误判为全程无字节)");
|
||
reset_all();
|
||
}
|
||
|
||
static void ns_run_s5(void)
|
||
{
|
||
tc_400_net_init_downlink();
|
||
tc_410_listen_locked();
|
||
tc_411_listen_public();
|
||
tc_420_accept_shapes();
|
||
tc_421_accept_public();
|
||
tc_430_connect();
|
||
tc_440_udp_datagram();
|
||
tc_450_direct_entries();
|
||
tc_460_reset_sync_timeout_bound();
|
||
tc_470_send_disconnect_midway();
|
||
}
|