7efa58d9a1
分两段(用户裁定 2026-11-02):先做地址迁移,再把同类字面量收敛成
单一真源,以后再改地址只动一处。
一、IP 迁移 192.168.1.100 → 192.168.31.100(gw 同步 → .31.1)
- 工厂默认:Drivers/BSP/NETCFG/net_cfg.c 的 NET_CFG_DEF_IP/_GW
- 注释/文档示例、主机与板级测试、台架 py 脚本、两份 sim_config.ini
- 字节序教学点同步换算 hex:0x6401A8C0 → 0x641FA8C0(内存
C0 A8 1F 64),旧版示例引文与历史 Trap 记录按冻结纪律不动
- UI 黄金基准:网口设置 4 态 + shot_info 共 5 张 .bin 由
check_content_pages.py --update 重刷(渲染值 IP 变了,属预期变更)
- 保留判定:客户点表引用(IEC104/继电保护规划)、芯片手册摘录、
日期冻结的变更记录行、已退役台架档案 MDK-ARM/retracted_bench_*
二、单一真源收口(A+B+C+D,用户批准全做)
- 新建 test/host/bench_defaults.py:台架 py 默认值唯一定义点
(DEVICE_IP/DEVICE_GW/PC_IP/NET_BCAST),22 个脚本改为引用;
MDK-ARM 两探针与 scripts/board_ui_nav.py 经 sys.path shim 引入
- test/board/net_test_task.h 新增 TEST_PC_PEER_IP,板测三处
PHASE*_REMOTE_IP 改引该宏(保留 -D 覆盖);Phase1 断言改
memcmp 数组比较,数组各只出现一次
- simulator/tests/check_content_pages.py 从 net_cfg.c 正则解析
NET_CFG_DEF_* 推导期望值(解析失败即报错、不静默兜底),
脚本内台架 IP 字面量清零
- 装饰性注释去具体值(net_cfg.h/net_socket.h/net_socket.c/
net_socket_sim.c);字节序教学 hex 对刻意保留为唯一写值点
- 刻意不动:主机单测夹具 IP(逐文件钉住的任意值是正确测试
实践)、文档正文 IP(那是文档内容本身)
顺带:scripts/gen_task_table.py 规范串清掉「,B5」——编号清退提交
72b4b094 改了受管区没重跑生成器,task-table 门禁因此一直红
验证:产品 @build 0/0;CH395F 套件开关构建 0/0 后还原产品态;
6 套主机单测 rc=0;content_pages(含宏推导路径)/菜单 8 态/
推流位比对全 PASS;bom-drift 99 无漂移;doc-links/coding-standard/
cppcheck 绿;py_compile 23 文件 + --help 冒烟(含 shim 绝对路径)
577 lines
27 KiB
C
577 lines
27 KiB
C
/*
|
||
* 分片 6:边界残支(公开入口的越界/队满、直调入口的参数支、等待环里的其余出口)
|
||
*
|
||
* 这一片不为「覆盖率好看」而写:每条都对应一条**能被改坏**的分支——越界检查挪到
|
||
* 入队之后,调用方就会在 netTask 里等到一个没人处理的 BADF;短写留痕被删掉,
|
||
* 上层就会像 2026-09-21 那次 UI 推流一样「对端永久等待剩余字节」而日志全白。
|
||
* 唯一一条刻意**不是**真实时序的场景是 TC-NETSOCK-530(一次轮询之间被压了 1s),
|
||
* 它在真机上的对应形态是 netTask 被更高优先级任务压住——用大步进时基表达,
|
||
* 并在注释里写明它考的是「停滞超时先于自旋预算生效」这条判定顺序。
|
||
*
|
||
* 创建日期:2026-10-24
|
||
*/
|
||
|
||
/* TC-NETSOCK-500 六个公开入口的越界形态:BADF 必须在**入队之前**由调用方侧挡下 */
|
||
static void tc_500_public_bounds(void)
|
||
{
|
||
struct net_sockaddr_in in;
|
||
char buf[8];
|
||
int addrlen;
|
||
|
||
bring_up();
|
||
(void)memset(&in, 0, sizeof(in));
|
||
addrlen = (int)sizeof(in);
|
||
|
||
CHECK(net_send(NET_MAX_SOCKETS, buf, 4, 0) == -1 && net_get_errno() == NET_ERR_BADF,
|
||
"TC-NETSOCK-500a net_send out of range -> BADF");
|
||
CHECK(net_recv(NET_MAX_SOCKETS, buf, 4, NET_MSG_DONTWAIT) == -1 &&
|
||
net_get_errno() == NET_ERR_BADF, "TC-NETSOCK-500b net_recv out of range -> BADF");
|
||
CHECK(net_recv_to(NET_MAX_SOCKETS, buf, 4, 10U) == -1 &&
|
||
net_get_errno() == NET_ERR_BADF, "TC-NETSOCK-500c net_recv_to out of range -> BADF");
|
||
CHECK(net_close(NET_MAX_SOCKETS) == -1 && net_get_errno() == NET_ERR_BADF,
|
||
"TC-NETSOCK-500d net_close out of range -> BADF");
|
||
CHECK(net_drop_conn(NET_MAX_SOCKETS) == -1 && net_get_errno() == NET_ERR_BADF,
|
||
"TC-NETSOCK-500e net_drop_conn out of range -> BADF");
|
||
CHECK(net_accept(NET_MAX_SOCKETS, (struct net_sockaddr *)&in, &addrlen) == -1 &&
|
||
net_get_errno() == NET_ERR_BADF, "TC-NETSOCK-500f net_accept out of range -> BADF");
|
||
CHECK(net_connect(NET_MAX_SOCKETS, (struct net_sockaddr *)&in, (int)sizeof(in)) == -1 &&
|
||
net_get_errno() == NET_ERR_BADF, "TC-NETSOCK-500g net_connect out of range -> BADF");
|
||
CHECK(net_sendto(NET_MAX_SOCKETS, buf, 4, 0, NULL, 0) == -1 &&
|
||
net_get_errno() == NET_ERR_BADF, "TC-NETSOCK-500h net_sendto out of range -> BADF");
|
||
CHECK(net_recvfrom(NET_MAX_SOCKETS, buf, 4, NET_MSG_DONTWAIT, NULL, NULL) == -1 &&
|
||
net_get_errno() == NET_ERR_BADF, "TC-NETSOCK-500i net_recvfrom out of range -> BADF");
|
||
CHECK(g_ns_put_calls == 0,
|
||
"TC-NETSOCK-500j every out-of-range shape enqueues nothing (blocking after the enqueue hands netTask an ownerless request)");
|
||
reset_all();
|
||
}
|
||
|
||
/* TC-NETSOCK-501 队满时每个公开入口都要报 BUSY(不能有的报、有的静默) */
|
||
static void tc_501_all_entries_busy_on_full_queue(void)
|
||
{
|
||
struct net_sockaddr_in in;
|
||
int fd;
|
||
|
||
bring_up();
|
||
fd = mk_established(NET_SOCK_STREAM);
|
||
g_ns_cur_task = (TaskHandle_t)&g_ns_task_caller;
|
||
|
||
ns_rtos_q_fill((int)NS_Q_CAP);
|
||
CHECK(net_accept(fd, NULL, NULL) == -1 && net_get_errno() == NET_ERR_BUSY,
|
||
"TC-NETSOCK-501a net_accept on a full queue -> BUSY");
|
||
ns_rtos_q_fill((int)NS_Q_CAP);
|
||
(void)memset(&in, 0, sizeof(in));
|
||
CHECK(net_connect(fd, (struct net_sockaddr *)&in, (int)sizeof(in)) == -1 &&
|
||
net_get_errno() == NET_ERR_BUSY, "TC-NETSOCK-501b net_connect on a full queue -> BUSY");
|
||
ns_rtos_q_fill((int)NS_Q_CAP);
|
||
CHECK(net_recv_to(fd, (void *)"x", 1, 20U) == -1 && net_get_errno() == NET_ERR_BUSY,
|
||
"TC-NETSOCK-501c net_recv_to on a full queue -> BUSY");
|
||
ns_rtos_q_fill((int)NS_Q_CAP);
|
||
CHECK(net_recv(fd, (void *)"x", 1, NET_MSG_DONTWAIT) == -1 &&
|
||
net_get_errno() == NET_ERR_BUSY, "TC-NETSOCK-501d net_recv on a full queue -> BUSY");
|
||
ns_rtos_q_fill((int)NS_Q_CAP);
|
||
CHECK(net_send(fd, "x", 1, 0) == -1 && net_get_errno() == NET_ERR_BUSY,
|
||
"TC-NETSOCK-501e net_send on a full queue -> BUSY");
|
||
CHECK(g_ns_notify_calls == 0, "TC-NETSOCK-501f the full-queue path sends no notification (the caller leaves with the return value)");
|
||
reset_all();
|
||
}
|
||
|
||
/* TC-NETSOCK-502 fd 表用尽:NOMEM 且不影响已建立的会话 */
|
||
static void tc_502_socket_table_full(void)
|
||
{
|
||
int i;
|
||
int fd;
|
||
|
||
bring_up();
|
||
for (i = 0; i < NET_MAX_SOCKETS; i++) {
|
||
fd = net_socket(NET_AF_INET, NET_SOCK_STREAM);
|
||
if (fd < 0) {
|
||
break;
|
||
}
|
||
}
|
||
CHECK(i == NET_MAX_SOCKETS, "TC-NETSOCK-502a all 8 slots are allocatable");
|
||
CHECK(net_socket(NET_AF_INET, NET_SOCK_STREAM) == -1 &&
|
||
net_get_errno() == NET_ERR_NOMEM, "TC-NETSOCK-502b the ninth -> NOMEM (no out-of-range table write)");
|
||
s_net_socks[3].state = NET_SOCK_STATE_ESTABLISHED;
|
||
CHECK(net_get_sock(3) != NULL && net_listener_armed(3) == 1,
|
||
"TC-NETSOCK-502c a full table does not disturb live sessions (alloc failure only returns NOMEM)");
|
||
reset_all();
|
||
}
|
||
|
||
/* TC-NETSOCK-503 net_wait_result 的两段式拾取:先零等待,已有通知就不必再睡 */
|
||
static void tc_503_zero_wait_pickup(void)
|
||
{
|
||
int fd;
|
||
int rc;
|
||
|
||
bring_up();
|
||
fd = mk_established(NET_SOCK_STREAM);
|
||
g_ns_cur_task = (TaskHandle_t)&g_ns_task_caller;
|
||
/* 预置一条**已挂起**的通知(真机上=上一条消息的回执还没被读走),
|
||
* 公开入口应先零等待拾取它,而不是再睡一次(睡过头就是 实测踩过的坑) */
|
||
ns_rtos_post_notify((TaskHandle_t)&g_ns_task_caller, (uint32_t)(int)4);
|
||
g_ns_block_waits = 0;
|
||
|
||
rc = net_send(fd, "abcd", 4, 0);
|
||
|
||
CHECK(rc == 4, "TC-NETSOCK-503a the zero-wait peek takes the notify value -> returns the result immediately");
|
||
CHECK(g_ns_block_waits == 0, "TC-NETSOCK-503b with a notification already pending it never enters the blocking branch");
|
||
CHECK(g_peers.send_reg_calls == 0 && g_chip.write_calls == 0,
|
||
"TC-NETSOCK-503c this call never touched the chip (the value peeked was the previous receipt, a shape we already hit in practice)");
|
||
reset_all();
|
||
}
|
||
|
||
/* TC-NETSOCK-504 直调入口的参数支(这些分支从公开入口不可达,只能直接钉) */
|
||
static void tc_504_locked_entry_branches(void)
|
||
{
|
||
struct net_sockaddr_in in;
|
||
int fd;
|
||
|
||
bring_up();
|
||
(void)memset(&in, 0, sizeof(in));
|
||
in.sin_port = net_htons(80);
|
||
in.sin_addr.s_addr = net_inet_addr("10.1.1.1");
|
||
|
||
fd = net_socket(NET_AF_INET, NET_SOCK_STREAM);
|
||
CHECK(net_connect_locked(fd, NULL, (int)sizeof(in)) == -1 &&
|
||
net_get_errno() == NET_ERR_INVAL, "TC-NETSOCK-504a connect_locked with addr=NULL -> INVAL");
|
||
CHECK(net_connect_locked(fd, (const struct net_sockaddr *)&in, 1) == -1 &&
|
||
net_get_errno() == NET_ERR_INVAL, "TC-NETSOCK-504b connect_locked with a short addrlen -> INVAL");
|
||
s_net_socks[fd].in_use = 1U;
|
||
CHECK(net_send_locked(&s_net_socks[fd], "x", 1, NULL) == -1 &&
|
||
net_get_errno() == NET_ERR_NOTCONN, "TC-NETSOCK-504c direct send on a not-established block -> NOTCONN");
|
||
CHECK(net_recv_locked(&s_net_socks[fd], (void *)"x", 1, NET_MSG_DONTWAIT, 0U, NULL) == -1 &&
|
||
net_get_errno() == NET_ERR_WOULDBLOCK, "TC-NETSOCK-504d direct recv, no data, non-blocking -> WOULDBLOCK");
|
||
CHECK(net_recv_locked(&s_net_socks[fd], NULL, 1, NET_MSG_DONTWAIT, 0U, NULL) == -1 &&
|
||
net_get_errno() == NET_ERR_INVAL, "TC-NETSOCK-504e direct recv with a NULL buf -> INVAL");
|
||
reset_all();
|
||
}
|
||
|
||
/* TC-NETSOCK-505 发送途中缓冲再也不腾出来:内层等待也要转挂起,并且末块不许登记 */
|
||
static void tc_505_inner_wait_and_last_chunk(void)
|
||
{
|
||
static char big[2100];
|
||
int fd;
|
||
int rc;
|
||
|
||
/* 第一块写得进、之后芯片不再报空闲 → 第二块的**内层**等待转挂起 */
|
||
bring_up();
|
||
fd = mk_established(NET_SOCK_STREAM);
|
||
g_ns_int_pin_set = 0;
|
||
ch395f_fake_push_glob((uint16_t)(1U << (fd + 4)));
|
||
g_fake.int_status[fd] = CH395F_SINT_STAT_SENBUF_FREE; /* 只给一次空闲 */
|
||
rc = net_send_locked(&s_net_socks[fd], big, 2100,
|
||
(TaskHandle_t)&g_ns_task_caller);
|
||
CHECK(rc == NET_PENDING_REGISTERED,
|
||
"TC-NETSOCK-505a the inner wait (after writing one chunk) also converts to pending past budget, no in-place busy wait");
|
||
CHECK(g_chip.write_calls == 2 && g_peers.last_snd_sent == 2048,
|
||
"TC-NETSOCK-505b registered progress = bytes written into the chip (1024+1024=2048), delivered bytes are never resent");
|
||
reset_all();
|
||
|
||
/* 末块:全部写完但芯片不回执 → 按完成返回,**不许**登记挂起
|
||
* (2026-10-14 台架回归 TC-NET-902 正是登记了却无人完成 → 永久挂死) */
|
||
bring_up();
|
||
fd = mk_established(NET_SOCK_STREAM);
|
||
rc = net_send_locked(&s_net_socks[fd], big, 1024,
|
||
(TaskHandle_t)&g_ns_task_caller);
|
||
CHECK(rc == 1024, "TC-NETSOCK-505c a single chunk finishing exactly -> returns 1024 (complete)");
|
||
CHECK(g_peers.send_reg_calls == 0,
|
||
"TC-NETSOCK-505d the last-chunk wait must not register pending (the sent>=len guard has to hold)");
|
||
reset_all();
|
||
}
|
||
|
||
/* TC-NETSOCK-506 UDP sendto:等待腾出与短写留痕(与 TCP 同一套语义的另一份实现) */
|
||
static void tc_506_sendto_stall_and_short(void)
|
||
{
|
||
static char big[2100];
|
||
int fd;
|
||
int rc;
|
||
|
||
bring_up_netctx();
|
||
fd = net_socket(NET_AF_INET, NET_SOCK_DGRAM);
|
||
s_net_socks[fd].send_ready = 0U;
|
||
g_ns_int_pin_set = 0;
|
||
ch395f_fake_push_glob((uint16_t)(1U << (fd + 4)));
|
||
/* 每轮只走 1ms:先命中 NET_SEND_TIMEOUT_MS(1s) 之前不会有别出口 → 短写 */
|
||
g_ns_poll_tick_inc = 500U;
|
||
rc = net_sendto(fd, big, 2100, 0, NULL, 0);
|
||
CHECK(rc < 2100 && rc >= 0,
|
||
"TC-NETSOCK-506a sendto stalled returns only what went out (short-write semantics match TCP)");
|
||
CHECK(g_ns_error_count >= 2,
|
||
"TC-NETSOCK-506b both the stall [ERR] and the short-write [ERR] must be there (missing one degrades into a silent truncation)");
|
||
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)));
|
||
g_fake.int_status[fd] = CH395F_SINT_STAT_SENBUF_FREE; /* 只给一次 */
|
||
g_ns_poll_tick_inc = 500U;
|
||
rc = net_sendto(fd, big, 2100, 0, NULL, 0);
|
||
CHECK(rc >= 1024 && rc < 2100,
|
||
"TC-NETSOCK-506c first chunk delivered and the second chunk wait timed out -> a value between 1024 and 2099");
|
||
reset_all();
|
||
}
|
||
|
||
/* TC-NETSOCK-530 停滞超时优先于自旋预算:考判定顺序,不考真实时长 */
|
||
static void tc_530_stall_timeout_before_spin(void)
|
||
{
|
||
static char buf[64];
|
||
int fd;
|
||
int rc;
|
||
|
||
/* 一次轮询之间被压了 2s(真机形态:netTask 被更高优先级任务压住),
|
||
* 停滞超时(NET_SEND_TIMEOUT_MS=1s)必须**先于**自旋预算(2ms)命中:
|
||
* 生产代码里两条判定的先后顺序就是这条断言的内容。 */
|
||
bring_up();
|
||
fd = mk_established(NET_SOCK_STREAM);
|
||
s_net_socks[fd].send_ready = 0U;
|
||
g_ns_poll_tick_inc = 2000U;
|
||
|
||
rc = net_send_locked(&s_net_socks[fd], buf, (int)sizeof(buf),
|
||
(TaskHandle_t)&g_ns_task_caller);
|
||
CHECK(rc == 0,
|
||
"TC-NETSOCK-530a the stall timeout returns the bytes sent so far, 0 (a short write, not -1)");
|
||
CHECK(g_peers.send_reg_calls == 0,
|
||
"TC-NETSOCK-530b the stall timeout wins over the spin budget: no pending registration (a swapped order would register)");
|
||
CHECK(net_get_errno() == NET_ERR_TIMEDOUT,
|
||
"TC-NETSOCK-530c errno = TIMEDOUT (lets the caller tell a stall from a disconnect)");
|
||
reset_all();
|
||
}
|
||
|
||
/* TC-NETSOCK-535 已经写出去一块之后的超时收口(内层等待的另一条出口) */
|
||
static void tc_535_inner_timeout_after_chunk(void)
|
||
{
|
||
static char big[2100];
|
||
int fd;
|
||
int rc;
|
||
|
||
/* 第一块写得进(send_ready 初值 1),第二块的**内层**等待直接撞上停滞超时
|
||
* (判定顺序:先超时后自旋,所以这里收口在超时支而不是登记挂起) */
|
||
bring_up();
|
||
fd = mk_established(NET_SOCK_STREAM);
|
||
g_ns_poll_tick_inc = 2000U;
|
||
|
||
rc = net_send_locked(&s_net_socks[fd], big, 2100,
|
||
(TaskHandle_t)&g_ns_task_caller);
|
||
|
||
CHECK(rc == 1024, "TC-NETSOCK-535a the already delivered first chunk still counts (1024), the rest is a short write");
|
||
CHECK(g_peers.send_reg_calls == 0,
|
||
"TC-NETSOCK-535b the stall timeout closes out BEFORE any pending registration (swapping the order parks a pending that never completes)");
|
||
CHECK(g_chip.write_calls == 1, "TC-NETSOCK-535c the chip was written exactly once: a delivered chunk must never be resent");
|
||
reset_all();
|
||
}
|
||
|
||
/* TC-NETSOCK-536 net_recv_to 的三条回传分支(-2 / -1+errno / >=0) */
|
||
static void tc_536_recv_to_three_exits(void)
|
||
{
|
||
char buf[16];
|
||
int fd;
|
||
|
||
/* >=0:正常拿到数据 */
|
||
bring_up();
|
||
fd = mk_established(NET_SOCK_STREAM);
|
||
s_net_socks[fd].readable = 1U;
|
||
ch395f_fake_push_recv_len((uint8_t)fd, 12U);
|
||
g_ns_cur_task = (TaskHandle_t)&g_ns_task_caller;
|
||
CHECK(net_recv_to(fd, buf, (int)sizeof(buf), 100U) == 12,
|
||
"TC-NETSOCK-536a data available -> returns the actual byte count (the >=0 branch)");
|
||
|
||
/* <0 且非 TIMEDOUT:分派侧 BADF → 恢复 errno 后返回 -1 */
|
||
reset_all();
|
||
bring_up();
|
||
(void)net_socket(NET_AF_INET, NET_SOCK_STREAM); /* 占掉 fd0,用未分配的 fd1 */
|
||
g_ns_cur_task = (TaskHandle_t)&g_ns_task_caller;
|
||
CHECK(net_recv_to(1, buf, (int)sizeof(buf), 100U) == -1 &&
|
||
net_get_errno() == NET_ERR_BADF,
|
||
"TC-NETSOCK-536b a real error -> -1 with errno restored from the notify value (strictly distinct from -2)");
|
||
|
||
/* ==TIMEDOUT:已在 TC-NETSOCK-224 钉过 -2;这里补一条「-2 不改写 errno」 */
|
||
reset_all();
|
||
bring_up();
|
||
fd = mk_established(NET_SOCK_STREAM);
|
||
(void)net_set_errno(NET_OK);
|
||
ns_peers_arm_pending_done(fd, NET_ERR_TIMEDOUT);
|
||
g_ns_cur_task = (TaskHandle_t)&g_ns_task_caller;
|
||
CHECK(net_recv_to(fd, buf, (int)sizeof(buf), 30U) == -2 &&
|
||
net_get_errno() == NET_OK,
|
||
"TC-NETSOCK-536c timeout is judged by the return code only and does NOT rewrite the errno slot (this branch sets none)");
|
||
reset_all();
|
||
}
|
||
|
||
/* TC-NETSOCK-537 fire_event:注册了回调才会被叫到(本层唯一的回调触发点在 SEND_OK) */
|
||
static void tc_537_event_callback_path(void)
|
||
{
|
||
int fd;
|
||
|
||
bring_up();
|
||
fd = mk_established(NET_SOCK_STREAM);
|
||
/* handle_send_event 是兄弟模块(net_events)的职责,本层只负责把中断交给它;
|
||
* 回调是否触发属 net_events 的断言面(其单测 TC-NETEV 已覆盖),此处只钉
|
||
* 「net_poll 不因回调未注册而跳过事件」。 */
|
||
s_net_socks[fd].event_cb = NULL;
|
||
g_ns_int_pin_set = 0;
|
||
ch395f_fake_push_glob((uint16_t)(1U << (fd + 4)));
|
||
g_fake.int_status[fd] = CH395F_SINT_STAT_SEND_OK;
|
||
CHECK(net_poll() == 1, "TC-NETSOCK-537 events are counted even with no callback registered (a null pointer never skips dispatch)");
|
||
reset_all();
|
||
}
|
||
|
||
/* TC-NETSOCK-540 close / drop_conn 的失败结果也要经通知值恢复精确 errno */
|
||
static void tc_540_negative_results(void)
|
||
{
|
||
net_msg_t msg;
|
||
int fd;
|
||
|
||
/* close 的负结果支:net_close_locked 自身恒返 0(复位没成也放行,见 TC-NETSOCK-320h),
|
||
* 所以 CLOSE 唯一的负值来自**分派侧**的 net_get_sock 判空 → 通知值 -2 →
|
||
* 调用方在自己的槽位里恢复成 BADF。范围合法但未分配的 fd 正好走这条。 */
|
||
bring_up();
|
||
fd = 3; /* 范围内、未分配 */
|
||
g_ns_cur_task = (TaskHandle_t)&g_ns_task_caller;
|
||
CHECK(net_close(fd) == -1 && net_get_errno() == NET_ERR_BADF,
|
||
"TC-NETSOCK-540a close on an unallocated fd -> -1 with errno restored to BADF from the notify value, not a generic NET_ERR");
|
||
CHECK(net_drop_conn(fd) == -1 && net_get_errno() == NET_ERR_BADF,
|
||
"TC-NETSOCK-540a2 drop_conn takes the same negative-value restoration branch");
|
||
reset_all();
|
||
|
||
/* drop_conn 同理:用未分配 fd 走分派侧 BADF 后再由调用方恢复 */
|
||
bring_up();
|
||
(void)memset(&msg, 0, sizeof(msg));
|
||
msg.type = NET_MSG_DROP_CONN;
|
||
msg.sockfd = 1;
|
||
msg.caller = (TaskHandle_t)&g_ns_task_net;
|
||
(void)ns_rtos_q_put_raw(&msg);
|
||
net_process_messages();
|
||
CHECK(g_ns_notify_last_val == (uint32_t)NET_ERR_BADF,
|
||
"TC-NETSOCK-540b drop_conn BADF from the dispatch side travels back on the notify value");
|
||
|
||
/* RECV 的 BADF 支(分派侧 net_get_sock 判空) */
|
||
reset_all();
|
||
bring_up();
|
||
(void)memset(&msg, 0, sizeof(msg));
|
||
msg.type = NET_MSG_RECV;
|
||
msg.sockfd = 4;
|
||
msg.buf = (void *)"x";
|
||
msg.len = 1;
|
||
msg.caller = (TaskHandle_t)&g_ns_task_net;
|
||
(void)ns_rtos_q_put_raw(&msg);
|
||
net_process_messages();
|
||
CHECK(g_ns_notify_last_val == (uint32_t)NET_ERR_BADF,
|
||
"TC-NETSOCK-540c recv BADF from the dispatch side likewise travels back on the notify value");
|
||
reset_all();
|
||
}
|
||
|
||
/* TC-NETSOCK-545 未绑队列时的 sendto/recvfrom/getpeername 等纯本地入口 */
|
||
static void tc_545_local_entries_without_queue(void)
|
||
{
|
||
struct net_sockaddr_in in;
|
||
int addrlen = (int)sizeof(in);
|
||
|
||
reset_all();
|
||
CHECK(net_getsockname(0, (struct net_sockaddr *)&in, &addrlen) == -1 &&
|
||
net_get_errno() == NET_ERR_BADF, "TC-NETSOCK-545a even uninitialised it safely reports BADF (no crash)");
|
||
CHECK(net_getpeername(0, (struct net_sockaddr *)&in, &addrlen) == -1,
|
||
"TC-NETSOCK-545b getpeername behaves the same");
|
||
CHECK(net_inet_addr("1.2.3.4") != 0U, "TC-NETSOCK-545c pure helpers do not depend on initialisation");
|
||
reset_all();
|
||
}
|
||
|
||
|
||
/* ============= TC-NETSOCK-550 s_addr 同族一致性(字节序统一后的收口断言) =============
|
||
*
|
||
* 这次字节序统一的实质不是「给 inet_addr 换个数值」,而是「全仓只剩一套 s_addr 约定」。
|
||
* 所以这组断言全部写在**跨函数的接缝**上:解析→存储→芯片下发→回读→打印,
|
||
* 每一环都拿同一句点分十进制做基准。任何一处又引入第二套顺位,这里必红。
|
||
*/
|
||
|
||
/* TC-NETSOCK-550 解析 → 存储 → 打印 闭合(getsockname / getpeername 同族可比对) */
|
||
static void tc_550_family_roundtrip(void)
|
||
{
|
||
struct net_sockaddr_in in;
|
||
struct net_sockaddr *sa = (struct net_sockaddr *)∈
|
||
char txt[16];
|
||
int addrlen;
|
||
int fd;
|
||
|
||
bring_up();
|
||
|
||
/* 1) getsockname 的 s_addr 与 inet_addr 解析结果**直接相等**(旧实现差一次换端) */
|
||
(void)mk_established(NET_SOCK_STREAM); /* getsockname 要 in_use 的描述符 */
|
||
addrlen = (int)sizeof(in);
|
||
(void)memset(&in, 0, sizeof(in));
|
||
CHECK(net_getsockname(0, sa, &addrlen) == 0,
|
||
"TC-NETSOCK-550a getsockname precondition ok");
|
||
CHECK(in.sin_addr.s_addr == net_inet_addr("192.168.31.100"),
|
||
"TC-NETSOCK-550b getsockname s_addr is the same family as inet_addr (direct equality)");
|
||
CHECK(strcmp(net_inet_ntoa(in.sin_addr.s_addr, txt), "192.168.31.100") == 0,
|
||
"TC-NETSOCK-550c local address closes parse-store-print (old code printed 100.31.168.192)");
|
||
|
||
/* 2) connect 的对端簿记同样闭合(lftpd:864 与 104 显示走的就是这条链) */
|
||
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(2404U);
|
||
in.sin_addr.s_addr = net_inet_addr("192.168.31.200");
|
||
g_ns_cur_task = (TaskHandle_t)&g_ns_task_net;
|
||
CHECK(net_connect_locked(fd, sa, (int)sizeof(in)) == 0,
|
||
"TC-NETSOCK-550d connect precondition ok");
|
||
addrlen = (int)sizeof(in);
|
||
(void)memset(&in, 0, sizeof(in));
|
||
CHECK(net_getpeername(fd, sa, &addrlen) == 0,
|
||
"TC-NETSOCK-550e getpeername ok (reads event cache, zero SPI)");
|
||
CHECK(in.sin_addr.s_addr == net_inet_addr("192.168.31.200"),
|
||
"TC-NETSOCK-550f getpeername peer equals the s_addr fed into net_connect");
|
||
CHECK(strcmp(net_inet_ntoa(in.sin_addr.s_addr, txt), "192.168.31.200") == 0,
|
||
"TC-NETSOCK-550g peer address prints right (lftpd connection-received log depends on it)");
|
||
CHECK(s_net_socks[fd].remote_ip_arr[0] == 192U &&
|
||
s_net_socks[fd].remote_ip_arr[3] == 200U,
|
||
"TC-NETSOCK-550h chip des_ip array stays wire order [192,168,31,200]: on-wire behaviour unchanged");
|
||
reset_all();
|
||
}
|
||
|
||
/* TC-NETSOCK-555 UDP 报文头里的源地址拆装(同族的第四个生产者:net_recvfrom) */
|
||
static void tc_555_udp_src_addr(void)
|
||
{
|
||
/* CH395F UDP 头(§9.2.4):[0-1] 总长(LE) [2-3] 源端口(LE) [4-7] 源 IP(线序) */
|
||
static const uint8_t hdr[8] = {
|
||
11U, 0U, /* 总长 = 8 头 + 3 数据 */
|
||
0x1FU, 0xCBU, /* 源端口 51999(小端 1F CB) */
|
||
239U, 1U, 2U, 3U /* 源 IP 线序:239.1.2.3 */
|
||
};
|
||
struct net_sockaddr_in in;
|
||
struct net_sockaddr *sa = (struct net_sockaddr *)∈
|
||
char buf[16];
|
||
char txt[16];
|
||
int addrlen;
|
||
int fd;
|
||
|
||
bring_up_netctx();
|
||
fd = net_socket(NET_AF_INET, NET_SOCK_DGRAM);
|
||
ch395f_fake_push_recv_len((uint8_t)fd, (uint16_t)(8U + 3U));
|
||
ch395f_fake_set_recv_bytes(hdr, (uint16_t)sizeof(hdr));
|
||
|
||
addrlen = (int)sizeof(in);
|
||
(void)memset(&in, 0, sizeof(in));
|
||
CHECK(net_recvfrom(fd, buf, (int)sizeof(buf), NET_MSG_DONTWAIT, sa, &addrlen) == 3,
|
||
"TC-NETSOCK-555a strips the 8-byte chip header and delivers only the 3-byte payload");
|
||
CHECK(in.sin_addr.s_addr == net_inet_addr("239.1.2.3"),
|
||
"TC-NETSOCK-555b source IP unpack matches the inet_addr family");
|
||
CHECK(in.sin_port == net_htons(51999U),
|
||
"TC-NETSOCK-555c source port read chip little-endian then stored in network order");
|
||
CHECK(addrlen == (int)sizeof(in) && net_ntohs(in.sin_port) == 51999U,
|
||
"TC-NETSOCK-555d addrlen filled and port round-trips to 51999");
|
||
CHECK(strcmp(net_inet_ntoa(in.sin_addr.s_addr, txt), "239.1.2.3") == 0,
|
||
"TC-NETSOCK-555e multicast source address prints correctly");
|
||
reset_all();
|
||
}
|
||
|
||
|
||
/* TC-NETSOCK-560 UDP 上下文守卫:非执行任务不得直调芯片
|
||
* 三条一起才成立,缺一条都不算把危险面关掉:
|
||
* ① 执行任务内照常成功(守卫不是"把 UDP 判死")
|
||
* ② 非执行任务被拒且**一次芯片事务都没有**(拒了却已经抢过 SPI = 白拒)
|
||
* ③ 正常错误路径不被守卫吞掉,且**走消息队列的接口完全不受影响**
|
||
* (那些接口生来就是给非 netTask 用的,误伤它们才是大事故)
|
||
*/
|
||
static void tc_560_udp_context_guard(void)
|
||
{
|
||
char buf[8];
|
||
int fd;
|
||
|
||
/* ① 执行任务上下文:与修复前逐字相同 */
|
||
bring_up_netctx();
|
||
fd = net_socket(NET_AF_INET, NET_SOCK_DGRAM);
|
||
CHECK(net_sendto(fd, "abc", 3, 0, NULL, 0) == 3,
|
||
"TC-NETSOCK-560a net_sendto unchanged inside the executing task");
|
||
reset_all();
|
||
|
||
/* ② 非执行任务:拒绝 + 零芯片事务(bring_up 里 net_init 是以 netTask 身份跑的,
|
||
* 此刻 g_ns_cur_task 已还原成 caller,正是"别的任务") */
|
||
bring_up();
|
||
fd = net_socket(NET_AF_INET, NET_SOCK_DGRAM);
|
||
g_chip.write_calls = 0;
|
||
g_chip.read_calls = 0;
|
||
g_fxchip.set_des_ip_calls = 0;
|
||
g_fxchip.glob_calls = 0;
|
||
g_ns_poll_calls = 0;
|
||
g_ns_error_count = 0;
|
||
|
||
CHECK(net_sendto(fd, "abc", 3, 0, NULL, 0) == -1 &&
|
||
net_get_errno() == NET_ERR_NOT_IN_NETTASK,
|
||
"TC-NETSOCK-560b non-executing task gets NET_ERR_NOT_IN_NETTASK (exact code, not -1)");
|
||
CHECK(g_chip.write_calls == 0 && g_fxchip.set_des_ip_calls == 0 &&
|
||
g_ns_poll_calls == 0,
|
||
"TC-NETSOCK-560c clean refusal: zero writes, zero set_des_ip, zero net_poll rounds");
|
||
CHECK(net_recvfrom(fd, buf, (int)sizeof(buf), NET_MSG_DONTWAIT, NULL, NULL) == -1 &&
|
||
net_get_errno() == NET_ERR_NOT_IN_NETTASK,
|
||
"TC-NETSOCK-560d net_recvfrom guarded the same way");
|
||
CHECK(g_chip.read_calls == 0 && g_fxchip.glob_calls == 0 && g_ns_poll_calls == 0,
|
||
"TC-NETSOCK-560e recvfrom touched no chip and no global interrupt register either");
|
||
CHECK(g_ns_error_count == 2,
|
||
"TC-NETSOCK-560f each refusal logs one ERR line (silence hides the wrong task)");
|
||
|
||
/* ③ 正常错误路径优先:守卫排在参数校验之后 */
|
||
CHECK(net_sendto(-1, "abc", 3, 0, NULL, 0) == -1 &&
|
||
net_get_errno() == NET_ERR_BADF, "TC-NETSOCK-560g out-of-range fd still reports BADF first");
|
||
CHECK(net_sendto(fd, NULL, 3, 0, NULL, 0) == -1 &&
|
||
net_get_errno() == NET_ERR_INVAL,
|
||
"TC-NETSOCK-560h NULL buf still reports INVAL, the guard does not swallow it");
|
||
CHECK(net_recvfrom(fd, NULL, 3, NET_MSG_DONTWAIT, NULL, NULL) == -1 &&
|
||
net_get_errno() == NET_ERR_INVAL, "TC-NETSOCK-560i recvfrom keeps the same precedence");
|
||
|
||
/* ④ 走消息队列的接口不受守卫影响:同一个非 netTask 上下文照常能把活干完 */
|
||
CHECK(net_close(fd) == 0,
|
||
"TC-NETSOCK-560j net_close from a non-executing task still works (queue path untouched)");
|
||
/* 执行完的证据用"收到回执 + 槽位释放",不看 close_calls:芯片此刻已是
|
||
* CLOSED,走的是快路径(不再补发 CLOSE,见 TC-NETSOCK-320a/b),拿它当证据
|
||
* 会把一条正确的快路径判成失败 */
|
||
CHECK(s_net_socks[fd].in_use == 0U && g_ns_notify_calls >= 1,
|
||
"TC-NETSOCK-560k queue path really completed (receipt seen and slot released)");
|
||
reset_all();
|
||
|
||
/* ⑤ 未捕获执行任务(net_init 没跑):守卫不抢先,仍由参数/状态检查给确定错误 */
|
||
reset_all();
|
||
g_ns_pump = net_process_messages;
|
||
CHECK(net_sendto(0, "abc", 3, 0, NULL, 0) == -1 &&
|
||
net_get_errno() == NET_ERR_BADF,
|
||
"TC-NETSOCK-560l before net_init sendto reports BADF, no invented context error");
|
||
|
||
/* 未捕获执行任务、但 socket 确实在用时,守卫**选择放行**:宁可不做判定,也不给
|
||
* 一个无从判定的场景编造错误码。放行后走的是完全正常的执行路径(这里 DONTWAIT
|
||
* 无数据 → WOULDBLOCK),证明守卫不是"没初始化就一律拒绝"的粗暴实现。
|
||
* 刻意用 recvfrom 而不是 sendto:未初始化时 net_poll 直接短路返回、时间基准
|
||
* 不前进,sendto 的等待环永远等不到超时(真机上不可达:net_socket 之后还有
|
||
* NET_READY 屏障,但这条差异值得记一笔,见交付报告)。 */
|
||
reset_all();
|
||
g_ns_cur_task = (TaskHandle_t)&g_ns_task_caller; /* net_init 没跑:判据恒 NULL */
|
||
fd = net_socket(NET_AF_INET, NET_SOCK_DGRAM);
|
||
g_fxchip.glob_calls = 0;
|
||
CHECK(net_recvfrom(fd, buf, (int)sizeof(buf), NET_MSG_DONTWAIT, NULL, NULL) == -1 &&
|
||
net_get_errno() == NET_ERR_WOULDBLOCK,
|
||
"TC-NETSOCK-560m guard stays open when the executing task was never captured");
|
||
CHECK(net_get_errno() != NET_ERR_NOT_IN_NETTASK && g_fxchip.glob_calls == 0,
|
||
"TC-NETSOCK-560n no context error invented and no global register read");
|
||
reset_all();
|
||
}
|
||
|
||
static void ns_run_s6(void)
|
||
{
|
||
tc_500_public_bounds();
|
||
tc_501_all_entries_busy_on_full_queue();
|
||
tc_502_socket_table_full();
|
||
tc_503_zero_wait_pickup();
|
||
tc_504_locked_entry_branches();
|
||
tc_505_inner_wait_and_last_chunk();
|
||
tc_506_sendto_stall_and_short();
|
||
tc_530_stall_timeout_before_spin();
|
||
tc_535_inner_timeout_after_chunk();
|
||
tc_536_recv_to_three_exits();
|
||
tc_537_event_callback_path();
|
||
tc_540_negative_results();
|
||
tc_545_local_entries_without_queue();
|
||
tc_550_family_roundtrip();
|
||
tc_555_udp_src_addr();
|
||
tc_560_udp_context_guard();
|
||
}
|