Files
wandering1 b7c0889cc7 refactor(编号·批次二): 代码与测试侧 215 处保护功能号改成功能名,零行为改动
用户裁定「不要用字母加数字的编号,语义标识是最好的」后的批次二代码半(道 ui-param 执行,逐文件站点数与
自证见其交付)。唯一口径 = 映射表 ci-artifacts/lprot_map.tsv 的功能名列原文,**零自造同义词**;L2.5 与
L-TEST 经实测各 0 处(不是本族),TC 族一字未动。

固件 22 文件 128 处(adc_task.c 21、phasor.c 22、phasor.h 13…),主机测试 13 文件 87 处(pha
sor_test.c
57…);设计文档指针新增 19 行,另 16 只文件本来就写着规划文档 ⇒ 不重复加。

**两条只有真改过 215 处才会踩到的坑,都写进注释规范实践**:
· 指针**不能接在原句尾部**——那会替原文一个未闭合的半括号封口,实测咬坏 App/algo/sample_ring.h 这类
  跨行句 ⇒ 指针一律**单独成行**;
· 号删掉会留下**叠词**(「自检失败闭锁 闭锁判定」「定值校验和 定值巡检」)⇒ 复扫并收口 4 处,措辞只用
  映射名本身;具体对象需要留住时写成「自检失败闭锁(采样故障闭锁)」。

自证(不靠嘴说):改前把 35 只文件原文备份到仓外 temp,逐文件比 `/*`、`*/` 计数、三类括号计数、行数增量
恰等于指针行数、全角括号增删相等、残留号 0 ⇒ **35/35 通过**;Lead 复核后跑权威 @build **0 错 0 警**,
Program Size `Code=233900 RO=26104 RW=1536 ZI=136808` —— **与三条道各自改前改后测的四数逐
字节同数**,
这就是「只动注释与文本」的机器证据;八套主机单测 exit 0,cppcheck 按钩子参数零发现,coding-standard 0。

文件头「修改记录」里的号**一并换名、不还原**(Lead 裁定):这些号是全仓退役的,留一个查无此号的引用比
留一条改过名的条目更坏——「冻结历史不回改」保护的是**别替历史改口**,不是保护一个再也查不到的字符串;
id-check 对变更记录行本就豁免,两种选择都不影响门禁。
2026-10-27 08:59:26 +08:00

362 lines
17 KiB
C
Raw Permalink 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.
/*
* 分片 4:net_poll 中断分派、监听/可读闸门、close 与 drop_conn 的拆除序列
*
* 这一片钉的是 AGENTS 模块约定里**没有文档就无法发现**的那条芯片约束:
* 所有 CH395F 中断在 net_poll() 内经 GET_GLOB_INT_STATUS 集中读取并清除,不得在
* ISR 或其他地方单独处理。全局寄存器整体读即清、芯片无按位清除手段,而 net_poll
* 只在全局位为 1 时才读该 Socket 的 SINT → 顺手全量清一遍等于把别的在用 Socket 的
* 门焊死(B3 实证,详见 docs/网络/网络自愈架构设计.md §4.10)。
*
* 断言手法(刻意不用「读了几次」这种弱口径):GET_SOCK_INT_STATUS 是**读即清**,
* 所以给每个 Socket 预置互不相同的挂起位图,跑一轮 net_poll 后看哪些位图被清掉了
* ——被清=被读过。这比数调用次数更硬:它直接照出「门有没有被焊死」。
*
* 创建日期:2026-10-24
*/
/* 预置:给每个 Socket 挂一个只有自己才有的 SINT 位图(只用低 6 位,避开保留位) */
static void prime_sint_masks(void)
{
int i;
for (i = 0; i < NET_MAX_SOCKETS; i++) {
g_fake.int_status[i] = (uint8_t)(0x01U << (i % 6));
}
g_chip.int_status_calls = 0;
}
/* TC-NETSOCK-300 net_poll 的前置闸门 */
static void tc_300_poll_gates(void)
{
/* 1) 未初始化:直接 0,连引脚都不该读 */
reset_all();
g_ns_pump = net_process_messages;
CHECK(net_poll() == 0, "TC-NETSOCK-300a uninitialized -> 0 events");
CHECK(g_ns_poll_calls == 0U && g_fxchip.glob_calls == 0,
"TC-NETSOCK-300b uninitialized must not read the INT# pin nor the global interrupt register (saves SPI)");
/* 2) 初始化了但 INT# 为高(无中断待处理):不读全局寄存器 */
bring_up();
g_ns_int_pin_set = 1;
CHECK(net_poll() == 0, "TC-NETSOCK-300c INT# high -> 0 events");
CHECK(g_fxchip.glob_calls == 0,
"TC-NETSOCK-300d with INT# high no global interrupt read may be issued (pin polling exists to save that step)");
/* 3) INT# 低但全局状态为 0:读了全局一次,且不进 Socket 循环 */
bring_up();
g_ns_int_pin_set = 0;
ch395f_fake_push_glob(0U);
CHECK(net_poll() == 0, "TC-NETSOCK-300e all global bits clear -> 0 events");
CHECK(g_fxchip.glob_calls == 1 && g_chip.int_status_calls == 0,
"TC-NETSOCK-300f global register read exactly once and it stops on 0 (no per-socket sweep)");
reset_all();
}
/* TC-NETSOCK-301 门控精度:只读全局位置位的那个 Socket 的 SINT(B3 的正面表述) */
static void tc_301_poll_only_gated_sockets(void)
{
int i;
int a;
int b;
bring_up();
a = mk_established(NET_SOCK_STREAM);
b = mk_established(NET_SOCK_STREAM);
(void)mk_established(NET_SOCK_STREAM);
CHECK(a == 0 && b == 1, "TC-NETSOCK-301a precondition: three fds in use");
prime_sint_masks();
g_ns_int_pin_set = 0;
ch395f_fake_push_glob((uint16_t)(1U << (1 + 4))); /* Socket n 的位 = bit(n+4) */
(void)net_poll();
CHECK(g_fake.int_status[0] == (uint8_t)(0x01U << 0) &&
g_fake.int_status[2] == (uint8_t)(0x01U << 2),
"TC-NETSOCK-301b pending bitmaps of unlit sockets 0/2 stay uncleared (read-clear means read, still there means not read)");
CHECK(g_fake.int_status[1] == 0U, "TC-NETSOCK-301c the lit socket 1 got read (bitmap cleared)");
CHECK(g_chip.int_status_calls == 1, "TC-NETSOCK-301d the whole round issued exactly one SINT read");
/* 缓存刷新环是另一回事:它对所有在用 fd 都要刷(单一事实源 I1) */
CHECK(g_fxchip.rst_poll_calls == 3,
"TC-NETSOCK-301e every round steps sock_reset for all 3 in-use fds (IDLE slots cost zero SPI)");
(void)net_poll();
CHECK(g_fxchip.rst_poll_calls == 6,
"TC-NETSOCK-301f another round adds 3 more: the refresh loop is not gated by global bits (events vs state split)");
reset_all();
/* 高位字节:Socket 4~7 的全局位在 bit8~bit11 */
bring_up();
for (i = 0; i < 5; i++) {
(void)mk_established(NET_SOCK_STREAM);
}
prime_sint_masks();
g_ns_int_pin_set = 0;
ch395f_fake_push_glob((uint16_t)(1U << (4 + 4)));
(void)net_poll();
CHECK(g_fake.int_status[4] == 0U && g_fake.int_status[3] == (uint8_t)(0x01U << 3),
"TC-NETSOCK-301g sockets 4-7 are located in the high byte bit(n+4)=bit8..11 (a wrong shift never sees their events)");
reset_all();
}
/* TC-NETSOCK-302 六种 SINT 位各自的分派与事件计数 */
static void tc_302_poll_event_dispatch(void)
{
static const uint8_t bits[6] = {
CH395F_SINT_STAT_CONNECT, CH395F_SINT_STAT_DISCONNECT, CH395F_SINT_STAT_RECV_OK,
CH395F_SINT_STAT_SEND_OK, CH395F_SINT_STAT_SOCK_TIMEOUT, CH395F_SINT_STAT_SENBUF_FREE
};
int k;
int fd;
for (k = 0; k < 6; k++) {
int expect = (bits[k] == CH395F_SINT_STAT_SENBUF_FREE) ? 0 : 1;
int n;
reset_all();
bring_up();
fd = mk_established(NET_SOCK_STREAM);
s_net_socks[fd].send_ready = 0U;
g_ns_int_pin_set = 0;
g_fake.int_status[fd] = bits[k];
ch395f_fake_push_glob((uint16_t)(1U << (fd + 4)));
n = net_poll();
CHECK(n == expect, "TC-NETSOCK-302a event counting distinguishes bit kinds (SENBUF_FREE is a state bit, not an event)");
switch (bits[k]) {
case CH395F_SINT_STAT_CONNECT:
CHECK(g_peers.evt_connect_calls == 1, "TC-NETSOCK-302b CONNECT -> handle_connect_event");
break;
case CH395F_SINT_STAT_DISCONNECT:
CHECK(g_peers.evt_disconnect_calls == 1, "TC-NETSOCK-302c DISCONNECT -> handle_disconnect_event");
break;
case CH395F_SINT_STAT_RECV_OK:
CHECK(g_peers.evt_recv_calls == 1 && s_net_socks[fd].readable == 1U,
"TC-NETSOCK-302d RECV_OK -> handle_recv_event sets the readable level");
break;
case CH395F_SINT_STAT_SEND_OK:
CHECK(g_peers.evt_send_calls == 1, "TC-NETSOCK-302e SEND_OK -> handle_send_event");
break;
case CH395F_SINT_STAT_SOCK_TIMEOUT:
CHECK(g_peers.evt_timeout_calls == 1, "TC-NETSOCK-302f TIMEOUT -> handle_timeout_event");
break;
default:
CHECK(s_net_socks[fd].send_ready == 1U && g_peers.evt_send_calls == 0,
"TC-NETSOCK-302g SENBUF_FREE only sets send_ready (no event, never mistaken for SEND_OK)");
break;
}
CHECK(g_peers.evt_connect_calls + g_peers.evt_disconnect_calls +
g_peers.evt_recv_calls + g_peers.evt_send_calls +
g_peers.evt_timeout_calls <= 1,
"TC-NETSOCK-302h one incoming bit drives exactly one handler (no cross-talk)");
}
/* 同一次 SINT 里多个位 → 多个 handler 都被叫到,事件数叠加 */
reset_all();
bring_up();
fd = mk_established(NET_SOCK_STREAM);
g_ns_int_pin_set = 0;
g_fake.int_status[fd] = (uint8_t)(CH395F_SINT_STAT_RECV_OK | CH395F_SINT_STAT_DISCONNECT);
ch395f_fake_push_glob((uint16_t)(1U << (fd + 4)));
CHECK(net_poll() == 2, "TC-NETSOCK-302i multiple event bits in one batch: each counted (the later bit is not swallowed)");
CHECK(g_peers.evt_recv_calls == 1 && g_peers.evt_disconnect_calls == 1,
"TC-NETSOCK-302j two bits each reach their own handler");
reset_all();
}
/* TC-NETSOCK-303 PHY 变更位经驱动 feed 落到链路缓存(链路事件投喂接线(网络自愈 §7)) */
static void tc_303_phy_feed(void)
{
bring_up();
g_ns_int_pin_set = 0;
g_fx.phy_status = CH395F_PHY_DISCONN; /* 下一次 feed 读到断开 */
ch395f_fake_push_glob(CH395F_GINT_STAT_PHY_CHANGE);
(void)net_poll();
CHECK(g_fake.link_up == 0U && g_fake.link_change == 1U,
"TC-NETSOCK-303 PHY_CHANGE -> driver re-reads PHY and counts one link change (cable pull is no longer silent)");
reset_all();
}
/* TC-NETSOCK-310 监听门控 net_listener_armed 的三支放行与全部挡下形态 */
static void tc_310_listener_armed(void)
{
int fd;
reset_all();
CHECK(net_listener_armed(-1) == 0 && net_listener_armed(NET_MAX_SOCKETS) == 0,
"TC-NETSOCK-310a out of range fd -> 0 (pure RAM read, no RPC)");
CHECK(net_listener_armed(0) == 0, "TC-NETSOCK-310b unallocated fd -> 0");
bring_up();
fd = mk_established(NET_SOCK_STREAM);
CHECK(net_listener_armed(fd) == 1,
"TC-NETSOCK-310c ESTABLISHED (pending-accept level) must pass: i104 bench proved starvation without it");
s_net_socks[fd].state = NET_SOCK_STATE_LISTENING;
CHECK(net_listener_armed(fd) == 1,
"TC-NETSOCK-310d LISTENING must pass: the missed-accept probe lives inside net_accept_locked, blocking it never runs");
s_net_socks[fd].state = NET_SOCK_STATE_CLOSED;
s_net_socks[fd].auto_relisten = 1U;
s_net_socks[fd].local_port = 7003U;
s_net_socks[fd].readable = 1U;
CHECK(net_listener_armed(fd) == 1,
"TC-NETSOCK-310e CLOSED plus auto_relisten plus leftover data -> pass (keeps closed-with-readable accept alive)");
s_net_socks[fd].readable = 0U;
CHECK(net_listener_armed(fd) == 0,
"TC-NETSOCK-310f CLOSED with no leftover must be blocked (else every round in that window sends an RPC and the watchdog starves)");
s_net_socks[fd].readable = 1U;
s_net_socks[fd].auto_relisten = 0U;
CHECK(net_listener_armed(fd) == 0, "TC-NETSOCK-310g CLOSED on a non-listener fd does not pass");
s_net_socks[fd].auto_relisten = 1U;
s_net_socks[fd].local_port = 0U;
CHECK(net_listener_armed(fd) == 0, "TC-NETSOCK-310h a half-built listener without a bound port does not pass");
s_net_socks[fd].state = NET_SOCK_STATE_CONNECTING;
CHECK(net_listener_armed(fd) == 0, "TC-NETSOCK-310i CONNECTING is not a listening state -> 0");
reset_all();
}
/* TC-NETSOCK-311 recv 门控 net_can_read:非 ESTABLISHED 一律放行 */
static void tc_311_can_read(void)
{
int fd;
reset_all();
CHECK(net_can_read(0) == 1,
"TC-NETSOCK-311a dead descriptor -> 1: let recv report BADF instead of hiding a missing connection");
bring_up();
fd = mk_established(NET_SOCK_STREAM);
s_net_socks[fd].readable = 0U;
CHECK(net_can_read(fd) == 0, "TC-NETSOCK-311b ESTABLISHED with nothing readable -> 0");
s_net_socks[fd].readable = 1U;
CHECK(net_can_read(fd) == 1, "TC-NETSOCK-311c readable set -> 1");
s_net_socks[fd].state = NET_SOCK_STATE_CLOSED;
s_net_socks[fd].readable = 0U;
CHECK(net_can_read(fd) == 1,
"TC-NETSOCK-311d connection no longer ESTABLISHED -> recv must pass once to fetch EOF or the error");
reset_all();
}
/* TC-NETSOCK-320 close 的拆除序列:快路径(芯片已 CLOSED)与慢路径(走复位机) */
static void tc_320_close_two_paths(void)
{
int fd;
/* 快路径:芯片已 CLOSED → 只清本 Socket 残留事件,不发复位 */
bring_up();
fd = mk_established(NET_SOCK_STREAM);
g_fake.cached_open[fd] = CH395F_SOCKET_CLOSED;
CHECK(net_close(fd) == 0, "TC-NETSOCK-320a close succeeds (fast path)");
CHECK(g_fxchip.cmd_done_calls >= 1 && g_fxchip.rst_begin_calls == 0,
"TC-NETSOCK-320b already CLOSED -> reset skipped (saves a pointless 100-600ms occupation)");
CHECK(g_fxchip.sock_events_calls >= 1,
"TC-NETSOCK-320c the fast path still clears this socket leftover events (else the next connection sees a false immediate drop)");
CHECK(g_fxchip.glob_calls == 0, "TC-NETSOCK-320d zero global reads throughout (sole reader of global reg)");
reset_all();
/* 慢路径:芯片还 OPEN → 走 sock_reset,且必须是 GRACEFUL */
bring_up();
fd = mk_established(NET_SOCK_STREAM);
g_fake.cached_open[fd] = CH395F_SOCKET_OPEN;
CHECK(net_close(fd) == 0, "TC-NETSOCK-320e close succeeds (slow path)");
CHECK(g_fxchip.rst_begin_calls == 1 && g_fx.rst_mode[fd] == 0U,
"TC-NETSOCK-320f slow path uses GRACEFUL (100ms floor for a normal release), not QUARANTINE");
CHECK(g_fx.rst_state[fd] == (uint8_t)CH395F_FAKE_RST_IDLE,
"TC-NETSOCK-320g the reset machine ends back at IDLE (slot reusable, never stranded)");
reset_all();
/* 复位启动被拒(槽位已在运行)→ close 仍放行,只留痕 */
bring_up();
fd = mk_established(NET_SOCK_STREAM);
g_fake.cached_open[fd] = CH395F_SOCKET_OPEN;
g_fx.rst_state[fd] = (uint8_t)CH395F_FAKE_RST_DRAINED;
CHECK(net_close(fd) == 0, "TC-NETSOCK-320h close still succeeds when starting the reset fails (do not block the caller)");
CHECK(g_fxchip.rst_begin_reject == 1 && g_ns_error_count >= 1,
"TC-NETSOCK-320i a failed reset must leave an [ERR] line for the reconciler/pump to take over");
reset_all();
}
/* TC-NETSOCK-321 drop_conn:监听型 = 登记式(settle QUARANTINE + REQ_DROP,
* 探针窗口心跳缺口修复起 netTask 零占用);非监听 client fd 保留同步排渣;两者都不动监听簿记 */
static void tc_321_drop_conn_settle(void)
{
int fd;
bring_up();
fd = mk_established(NET_SOCK_STREAM);
s_net_socks[fd].auto_relisten = 1U;
s_net_socks[fd].readable = 1U;
s_net_socks[fd].lost_conn_seen = 3U;
g_fake.cached_open[fd] = CH395F_SOCKET_OPEN;
CHECK(net_drop_conn(fd) == 0, "TC-NETSOCK-321a drop_conn succeeds");
CHECK(g_peers.listener_settle_calls == 1 && g_peers.last_settle_fd == fd &&
g_peers.last_settle_level == (int)NET_LISTENER_SETTLE_QUARANTINE,
"TC-NETSOCK-321b listener fd: abnormal-release isolation REGISTERED for the tick machine (TRAP-NET-18 QUARANTINE semantics survive, executed by net_listener_tick)");
CHECK(g_fxchip.rst_begin_calls == 0 && g_ns_error_count == 0,
"TC-NETSOCK-321b2 heartbeat gap fix: the handler starts no synchronous reset and logs nothing - netTask keeps its ~1ms beat");
CHECK(s_net_socks[fd].lost_conn_seen == 0U,
"TC-NETSOCK-321c new session start: the missed-event debounce counter is cleared (relisten settle)");
CHECK(s_net_socks[fd].local_port == 2404U && s_net_socks[fd].auto_relisten == 1U &&
s_net_socks[fd].in_use == 1U,
"TC-NETSOCK-321d port, listener attribute and fd all survive: this is drop-the-connection-not-the-listener");
CHECK(g_peers.listener_req_calls == 1 &&
g_peers.last_req_reason == (int)NET_LISTENER_REQ_DROP,
"TC-NETSOCK-321e registers REQ_DROP; the rebuild action lives only in net_listener_tick");
CHECK(s_net_socks[fd].readable == 0U && s_net_socks[fd].state == NET_SOCK_STATE_CLOSED,
"TC-NETSOCK-321f session bookkeeping closes out: clear readable and move to CLOSED");
reset_all();
/* 非监听 client fd:无监听可保,同步排渣形态原样保留(心跳缺口修复只改监听型) */
bring_up();
fd = mk_established(NET_SOCK_STREAM);
g_fake.cached_open[fd] = CH395F_SOCKET_OPEN;
CHECK(net_drop_conn(fd) == 0, "TC-NETSOCK-321g client-fd drop still succeeds");
CHECK(g_peers.listener_settle_calls == 0 && g_peers.listener_req_calls == 0,
"TC-NETSOCK-321h client fd registers no settle and no REQ_DROP: nothing to rebuild");
CHECK(g_fxchip.rst_begin_calls == 1 && g_fx.rst_mode[fd] == 1U,
"TC-NETSOCK-321i client fd keeps the synchronous QUARANTINE close-out (bounded 600ms, legacy shape)");
reset_all();
}
/* TC-NETSOCK-322 close 前必先把挂起结算掉,再动芯片(顺序不可换) */
static void tc_322_close_before_chip(void)
{
int fd;
int close_at;
int settle_at;
char rxbuf[4];
bring_up();
fd = mk_established(NET_SOCK_STREAM);
g_fake.cached_open[fd] = CH395F_SOCKET_OPEN;
/* 先登记一条挂起 recv:走 netTask 直调版 recv(无数据 + 阻塞 → 登记挂起) */
g_ns_pump = NULL; /* 本场景不靠泵,直接看登记结果 */
CHECK(net_recv_sock(&s_net_socks[fd], rxbuf, (int)sizeof(rxbuf), 0)
== NET_PENDING_REGISTERED,
"TC-NETSOCK-322p precondition: blocking recv registered a pending request (returns the sentinel)");
ch395f_fake_clear_log();
settle_at = g_peers.pending_settle_calls;
close_at = g_chip.close_calls;
CHECK(net_close_sock(&s_net_socks[fd]) == 0, "TC-NETSOCK-322a netTask-direct close succeeds");
CHECK(g_peers.pending_settle_calls == settle_at + 1,
"TC-NETSOCK-322b close must first settle this fd pending recv");
CHECK(g_chip.close_calls > close_at || g_fxchip.rst_close_calls > 0,
"TC-NETSOCK-322c the chip only sees CLOSE after the settlement");
CHECK(g_peers.pending_notified == 1,
"TC-NETSOCK-322d settlement always notifies (cancel notifies too, else the waiter hangs forever)");
reset_all();
}
static void ns_run_s4(void)
{
tc_300_poll_gates();
tc_301_poll_only_gated_sockets();
tc_302_poll_event_dispatch();
tc_303_phy_feed();
tc_310_listener_armed();
tc_311_can_read();
tc_320_close_two_paths();
tc_321_drop_conn_settle();
tc_322_close_before_chip();
}