0f7b5a87fb
登记簿、文档与代码注释里的存量字母加数字编号(N-x / S-x / T-x / E-x / A-x / 卡号) 一律改写为语义短语,净删 2,028 处引用;删除基线与待确认、并行分道方案、排期详述、 采集保护收尾 4 篇(内容已并入 docs/任务规划/README 与登记簿)。 门禁机器与三张账同批重打:check_ids.py 新增变更记录行豁免与第二根护栏 undecidable_total_ceiling;check_test_traceability.py 只加 stdout/stderr UTF-8 reconfigure(TC_RE 未动);check_cubemx_survival.py 的 --write 固定 newline=LF; id_registry / id_aliases(+19 条 retired-to-phrase)/ id_known_backlog 重打。 test/ 56 个文件同步 TC 标签语义化,其中 18 个 TU 改了 CHECK / printf 里的标签文字, 是本批唯一改变构建产物的文件集合。 为什么合成一笔、不拆四笔(实测撞闸,非偏好):pre-commit 跑钩子前会 stash 未 staged 部分,拆开后中间提交必然是「新规则 + 旧语料」——实跑可判定 85.15% < 基线 86.00%、 不可判定 1,453 > 上限 1,166,编号门禁直接拦下。编号识别面与它描述的语料只能同码移动。 实跑基线(工作区全量):754 文件 / 8,230 处编号字面量 / 可判定 7,079 = 86.01% (基线 86.0%,容差 0.1pp)/ 不可判定绝对数 1,151(上限 1,166)/ 校验 1-6 错误级 0 / 告警 = 悬空 2(App/algo/freq.c 的 S2,属 §4.4 同号不同义)+ 孤儿 50。 基线从 84.0 抬到 86.0 的理由见 docs/门禁说明/id-check.md §7.6:本批真删了 2,028 处 引用,可判定面随真实内容上升,不是把分母做小。同批新增排除 docs/硬件平台/ 语料,使 识别面少 264 处——芯片手册位号与网表料号不属编号空间(registry 有 excluded_paths_note)。 ⚠ 余量只剩 0.11pp(约 10 处)与绝对数 15 处:把「可判定的引用」改成散文会立刻撞第二 根护栏。另遗留一项:id_aliases 里 retired 的 N / S / T / A 别名没有 scope(U1~U5 有), 今后有人新写一个 A-4,校验 3 不会响,只能靠人,建议下批补 scope。 固件不变证据:115 个 .c/.h 剥注释并归一字符串后与 HEAD 逐字节全等,残留代码差异 0; App / Src / Drivers 的字符串字面量零改动。@build 0 错误 0 警告;40/40 主机套件、2,889 断言全通过;覆盖率 line 89.2% / func 92.6%;pre-commit 48 钩子 --all-files 全绿; check_doc_links --all 通过(176 文档,章节引用无法判定 0);逐字节卫生核过:14 个带 BOM 文件与 HEAD 一致、0 个 CR、0 个 U+FFFD,字符串字面量纯 ASCII。 .cubemx_survival_baseline.json 的 watchdog-module 哈希 56bf27 变 2bb051 属合法: Src/freertos.c 的 USER CODE 区内只有注释文字随编号改写,代码未变。 同批带上体积同码凭证与 RAM 台账行(@build 之后盖戳):RW_IRAM1 79,008 B / 60.3%, RW_CCM 57,376 B / 87.9%,阈值 92%。
362 lines
17 KiB
C
362 lines
17 KiB
C
/*
|
||
* 分片 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 落到链路缓存(F-DRV-13 接线) */
|
||
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 (F-NET-7)");
|
||
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();
|
||
}
|