Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fff961d803 |
@@ -383,6 +383,11 @@ int net_udp_sendto_exec(int sockfd, const void *buf, int len,
|
|||||||
* 返回;完成条件由 net_udp_tick 按 1ms 主循环节拍判定。
|
* 返回;完成条件由 net_udp_tick 按 1ms 主循环节拍判定。
|
||||||
* 3. 执行任务自身(caller == netTask)的阻塞等待被登记守卫拒绝——
|
* 3. 执行任务自身(caller == netTask)的阻塞等待被登记守卫拒绝——
|
||||||
* tick 就跑在执行任务里,自等自通知 = 死锁(同 net_pending_register)。
|
* tick 就跑在执行任务里,自等自通知 = 死锁(同 net_pending_register)。
|
||||||
|
* 4. 槽位是调用方在 arm 之后才把消息投进来的,所以本函数**每一条负向
|
||||||
|
* 出口都必须归还槽位**(含参数/形态拒绝):漏一条,这条 fd 就永久
|
||||||
|
* BUSY,且报错还是从下一条 recvfrom 才冒出来(2026-11-03 补测抓到,
|
||||||
|
* 当时 NOTCONN 这条没清)。唯一清不掉的是 fd 越界(p_sock == NULL),
|
||||||
|
* 那时连槽都不存在。
|
||||||
*/
|
*/
|
||||||
int net_udp_recvfrom_exec(int sockfd, void *buf, int len, int flags,
|
int net_udp_recvfrom_exec(int sockfd, void *buf, int len, int flags,
|
||||||
uint32_t to_ms, TaskHandle_t caller) {
|
uint32_t to_ms, TaskHandle_t caller) {
|
||||||
@@ -401,12 +406,16 @@ int net_udp_recvfrom_exec(int sockfd, void *buf, int len, int flags,
|
|||||||
p = &s_net_udp[sockfd];
|
p = &s_net_udp[sockfd];
|
||||||
if (p_sock->type != NET_SOCK_DGRAM) {
|
if (p_sock->type != NET_SOCK_DGRAM) {
|
||||||
/* 面向流的 recv 才是 TCP 的入口 */
|
/* 面向流的 recv 才是 TCP 的入口 */
|
||||||
|
net_udp_clear(sockfd);
|
||||||
return NET_ERR_RETURN_1(NET_ERR_INVAL);
|
return NET_ERR_RETURN_1(NET_ERR_INVAL);
|
||||||
}
|
}
|
||||||
if ((buf == NULL) || (len <= 0)) {
|
if ((buf == NULL) || (len <= 0)) {
|
||||||
|
net_udp_clear(sockfd);
|
||||||
return NET_ERR_RETURN_1(NET_ERR_INVAL);
|
return NET_ERR_RETURN_1(NET_ERR_INVAL);
|
||||||
}
|
}
|
||||||
if (p_sock->state != NET_SOCK_STATE_UDP_OPEN) {
|
if (p_sock->state != NET_SOCK_STATE_UDP_OPEN) {
|
||||||
|
/* 芯片侧 Socket 还没开:先 net_bind */
|
||||||
|
net_udp_clear(sockfd);
|
||||||
return NET_ERR_RETURN_1(NET_ERR_NOTCONN);
|
return NET_ERR_RETURN_1(NET_ERR_NOTCONN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -47,10 +47,10 @@ dhara 失败类 +84;RO 另 +208,全是该失败类名表)→ `ER_IROM1`
|
|||||||
<!-- MEM_SNAPSHOT:BEGIN generated by scripts/check_mem_numbers.py -->
|
<!-- MEM_SNAPSHOT:BEGIN generated by scripts/check_mem_numbers.py -->
|
||||||
| 区域 | 用量 | 容量 | 占比 |
|
| 区域 | 用量 | 容量 | 占比 |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| ER_IROM1 | 278,948 B (272.41 KB) | 1,048,576 B (1024 KB) | 26.6% |
|
| ER_IROM1 | 278,960 B (272.42 KB) | 1,048,576 B (1024 KB) | 26.6% |
|
||||||
| RW_IRAM1 | 94,624 B (92.41 KB) | 131,072 B (128 KB) | 72.2% |
|
| RW_IRAM1 | 94,624 B (92.41 KB) | 131,072 B (128 KB) | 72.2% |
|
||||||
| RW_CCM | 57,376 B (56.03 KB) | 65,280 B (64 KB) | 87.9% |
|
| RW_CCM | 57,376 B (56.03 KB) | 65,280 B (64 KB) | 87.9% |
|
||||||
| **Flash (ROM 合计)** | **281,000 B (274.4 KB)** | 1,048,576 B (1024 KB) | **26.8%** |
|
| **Flash (ROM 合计)** | **281,012 B (274.4 KB)** | 1,048,576 B (1024 KB) | **26.8%** |
|
||||||
<!-- MEM_SNAPSHOT:END -->
|
<!-- MEM_SNAPSHOT:END -->
|
||||||
|
|
||||||
各区的容量与常量来源:
|
各区的容量与常量来源:
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
<!-- MEM_BUDGET:BEGIN generated by scripts/ram_baseline.py -->
|
<!-- MEM_BUDGET:BEGIN generated by scripts/ram_baseline.py -->
|
||||||
| 区域 | 用量 | 容量 | 占比 | 门禁(≤92%) |
|
| 区域 | 用量 | 容量 | 占比 | 门禁(≤92%) |
|
||||||
|---|---|---|---|---|
|
|---|---|---|---|---|
|
||||||
| ER_IROM1 (Flash) | 278,948 B | 1,048,576 B | 26.6% | 参考 |
|
| ER_IROM1 (Flash) | 278,960 B | 1,048,576 B | 26.6% | 参考 |
|
||||||
| RW_CCM | 57,376 B | 65,280 B | 87.9% | PASS |
|
| RW_CCM | 57,376 B | 65,280 B | 87.9% | PASS |
|
||||||
| UNINIT_CCM | 48 B | 256 B | 18.8% | 参考 |
|
| UNINIT_CCM | 48 B | 256 B | 18.8% | 参考 |
|
||||||
| RW_IRAM1 | 94,624 B | 131,072 B | 72.2% | PASS |
|
| RW_IRAM1 | 94,624 B | 131,072 B | 72.2% | PASS |
|
||||||
|
|||||||
@@ -719,11 +719,11 @@ uint16_t ch395f_link_change_count(void);
|
|||||||
- **不写第二份分块逻辑**:UDP 发送复用 `net_send_locked`(1KB 分块 + 挂起 send 都在里面)。为此把"会话是否已死"收成一条判据 `net_send_state_down()`(`net_pending.c`):TCP 看 `ESTABLISHED`、UDP 看 `UDP_OPEN`;原先 `net_send_locked` 两处 + `net_send_pending_tick` + `net_pending_tick` 各写一遍,共 4 处。
|
- **不写第二份分块逻辑**:UDP 发送复用 `net_send_locked`(1KB 分块 + 挂起 send 都在里面)。为此把"会话是否已死"收成一条判据 `net_send_state_down()`(`net_pending.c`):TCP 看 `ESTABLISHED`、UDP 看 `UDP_OPEN`;原先 `net_send_locked` 两处 + `net_send_pending_tick` + `net_pending_tick` 各写一遍,共 4 处。
|
||||||
- **顺带修掉的形态缺陷**(真隐患):`net_sendto` 为发一个包把 DesIP 从 `0xFFFFFFFF` 写成具体 IP,芯片静默切到 UDP Client 形态(收包**不带** 8 字节信息头),而旧 `recvfrom` 无条件剥 8 字节 → 请求-响应式协议(SNTP 48 B 应答)会丢载荷前 8 字节并交出垃圾源地址。**修法**:`net_sock_t` 加 `udp_mode`(塞进原有 2 字节 padding,`sizeof(net_sock_t)` 不变)、`bind` 恒 SERVER、显式对端 `sendto` 切 CLIENT、`net_udp_set_peer(fd, NULL)` 轻量回 SERVER(不重开 Socket 因此不清芯片缓冲)、只在 SERVER 且 `avail >= 8` 时剥头(残包不猜头,TC-NETSOCK-440o 契约不变)。
|
- **顺带修掉的形态缺陷**(真隐患):`net_sendto` 为发一个包把 DesIP 从 `0xFFFFFFFF` 写成具体 IP,芯片静默切到 UDP Client 形态(收包**不带** 8 字节信息头),而旧 `recvfrom` 无条件剥 8 字节 → 请求-响应式协议(SNTP 48 B 应答)会丢载荷前 8 字节并交出垃圾源地址。**修法**:`net_sock_t` 加 `udp_mode`(塞进原有 2 字节 padding,`sizeof(net_sock_t)` 不变)、`bind` 恒 SERVER、显式对端 `sendto` 切 CLIENT、`net_udp_set_peer(fd, NULL)` 轻量回 SERVER(不重开 Socket 因此不清芯片缓冲)、只在 SERVER 且 `avail >= 8` 时剥头(残包不猜头,TC-NETSOCK-440o 契约不变)。
|
||||||
- **截断语义定案**:一次调用交付一个数据报,`buf` 装不下的尾巴**必须排空**——`ch395f_read_recv_buf` 是按长度推进读指针的消费式读,不排空则下一次 `recvfrom` 交出上一个包的残料。排空用 64 B 栈上 scratch 循环读,**不用** `ch395f_clear_recv_buf`:它清整个接收缓冲,会把后面已经到达的包一起丢。
|
- **截断语义定案**:一次调用交付一个数据报,`buf` 装不下的尾巴**必须排空**——`ch395f_read_recv_buf` 是按长度推进读指针的消费式读,不排空则下一次 `recvfrom` 交出上一个包的残料。排空用 64 B 栈上 scratch 循环读,**不用** `ch395f_clear_recv_buf`:它清整个接收缓冲,会把后面已经到达的包一起丢。
|
||||||
- **用例面**:新分片 `test/host/net_socket/ns_s7_udp.c` 共 20 组 UDP-1~20(bind 的 5 条芯片命令**顺序**、未 bind 一律 -5、服务器形态剥头逐字节、客户端形态不剥头、`sendto` 切形态、挂起收包在后续 poll 轮到达、预算超时按调用方 deadline 计、close 与重建结算不许静默丢通知、出参槽互斥与泄漏、截断排空、netTask 自等守卫、`net_init` 重建取消、流式 API 拒 `DGRAM`、非 netTask 调用零芯片事务、执行体与公开入口的参数守卫(越界 fd / `STREAM` / 未 bind / 零长 / 出参容量不足——拒时必须零芯片事务)、"队列未绑定"与"队列满"两条失败出口不许漏槽、`net_udp_set_peer` 轻/重两种回服务器模式(重开丢缓冲 vs 不重开)、挂起 send 由 `net_send_pending` 结算且登记时不许抢发第二份回执、`net_udp_tick` 的会话作废与 stale-readable 兜底、重 bind 必须把还在等包的调用方按 -8 结算);改写 TC-NETSOCK-440(补 bind 前置 + 新增 440p~440u)、555(表头逐字节按形态分路)、560 组("拒在 netTask 外"→"经队列后非 netTask 仍零 SPI")、500h-i / 506 / 545。全套 **616/616 绿**(改造前 470)。
|
- **用例面**:新分片 `test/host/net_socket/ns_s7_udp.c` 共 23 组 UDP-1~23(bind 的 5 条芯片命令**顺序**、未 bind 一律 -5、服务器形态剥头逐字节、客户端形态不剥头、`sendto` 切形态、挂起收包在后续 poll 轮到达、预算超时按调用方 deadline 计、close 与重建结算不许静默丢通知、出参槽互斥与泄漏、截断排空、netTask 自等守卫、`net_init` 重建取消、流式 API 拒 `DGRAM`、非 netTask 调用零芯片事务、执行体与公开入口的参数守卫(越界 fd / `STREAM` / 未 bind / 零长 / 出参容量不足——拒时必须零芯片事务)、"队列未绑定"与"队列满"两条失败出口不许漏槽、`net_udp_set_peer` 轻/重两种回服务器模式(重开丢缓冲 vs 不重开)、挂起 send 由 `net_send_pending` 结算且登记时不许抢发第二份回执、`net_udp_tick` 的会话作废与 stale-readable 兜底、重 bind 必须把还在等包的调用方按 -8 结算、`bind` 遇满队列必须 BUSY 且不给 fd 投毒、**芯片 `open` 回 `ERR_BUSY` 时精确码必须沿通知值回到等待方**(真芯片会这么拒)、**队列里"别人的失败"排在我这条之前时不许污染我的回执**(同 545 在 TCP 侧的钉子,UDP bind 侧补一条)、`net_recvfrom_to` 的"真失败"与"超时"两条出口分开、**netTask 直调路径(板级用例走的那条)的 `set_peer` / `recvfrom` 正例与三种拒绝**;改写 TC-NETSOCK-440(补 bind 前置 + 新增 440p~440u)、555(表头逐字节按形态分路)、560 组("拒在 netTask 外"→"经队列后非 netTask 仍零 SPI")、500h-i / 506 / 545。全套 **647/647 绿**(改造前 470)。
|
||||||
- **假件新增(一律可加不可改)**:`g_fx.wlen[]` + `g_fx.last_write[]` 捕获 `ch395f_write_send_buf` 的**真实字节**(此前假件忽略 `p_data`,UDP 截断与排空无从断言);`ch395f_fake_set_write_delay(fd, n)` 让 SENBUF_FREE 第 n 次才到(钉"排空必须在下一次写之前完成");`g_chip.read_calls` 供"读了几个 scratch 块"断言。`g_fake` / `g_chip` 既有字段一字未动。
|
- **假件新增(一律可加不可改)**:`g_fx.wlen[]` + `g_fx.last_write[]` 捕获 `ch395f_write_send_buf` 的**真实字节**(此前假件忽略 `p_data`,UDP 截断与排空无从断言);`ch395f_fake_set_write_delay(fd, n)` 让 SENBUF_FREE 第 n 次才到(钉"排空必须在下一次写之前完成");`g_chip.read_calls` 供"读了几个 scratch 块"断言。`g_fake` / `g_chip` 既有字段一字未动。
|
||||||
- **两条教训(都是被测码真 bug,不是测法自误)**:① **执行侧自等守卫不能复用调用侧谓词**——`net_udp_recvfrom_exec` 用 `net_ctx_is_owner_task()` 判自等,而它在 netTask 内恒真 → 每一条经队列投进来的正常 recvfrom 都被拒;正确判据是 `caller == xTaskGetCurrentTaskHandle()`(对齐 `net_pending.c` 的 `caller == s_owner_task`)。抓它的是 UDP-8a。② **芯片长度只能读一次并传参**——`net_udp_datagram_read` 内部又调一次 `ch395f_get_recv_len`,于是"剥几字节"与"读几字节"不同源;假件的 recv_len 队列**每次调用弹一个值**,这条设计当场把双读暴露出来。现由调用方读一次 `avail` 传入三个调用点。
|
- **两条教训(都是被测码真 bug,不是测法自误)**:① **执行侧自等守卫不能复用调用侧谓词**——`net_udp_recvfrom_exec` 用 `net_ctx_is_owner_task()` 判自等,而它在 netTask 内恒真 → 每一条经队列投进来的正常 recvfrom 都被拒;正确判据是 `caller == xTaskGetCurrentTaskHandle()`(对齐 `net_pending.c` 的 `caller == s_owner_task`)。抓它的是 UDP-8a。② **芯片长度只能读一次并传参**——`net_udp_datagram_read` 内部又调一次 `ch395f_get_recv_len`,于是"剥几字节"与"读几字节"不同源;假件的 recv_len 队列**每次调用弹一个值**,这条设计当场把双读暴露出来。现由调用方读一次 `avail` 传入三个调用点。
|
||||||
- **补测阶段又抓到两条(同一批缺口分析顺手挖的)**:③ **槽指针必须在 fd 合法性检查之后取**——`net_udp_recvfrom_exec` 原先先算 `&s_net_udp[sockfd]` 再判 `p_sock == NULL`,越界 fd 立刻得到一个越界槽指针(哪怕当下没解引用,后面登记就是野写)。④ **作废不等于可以不吭声**——`net_udp_bind_exec` 重开芯片 Socket 会丢掉缓冲里的包,却只 `net_udp_clear()`:A 任务正阻塞在 `net_recvfrom`、B 任务重 bind 同一个 fd,A 就永远睡死。现在重开之前先按 `NET_ERR_CONNRESET` 结算并通知(UDP-20 钉住)。教训同 `net_pending`:**任何"取消挂起"的动作都必须走 complete 那条路,clear 只是归零**。
|
- **补测阶段又抓到两条(同一批缺口分析顺手挖的)**:③ **槽指针必须在 fd 合法性检查之后取**——`net_udp_recvfrom_exec` 原先先算 `&s_net_udp[sockfd]` 再判 `p_sock == NULL`,越界 fd 立刻得到一个越界槽指针(哪怕当下没解引用,后面登记就是野写)。④ **作废不等于可以不吭声**——`net_udp_bind_exec` 重开芯片 Socket 会丢掉缓冲里的包,却只 `net_udp_clear()`:A 任务正阻塞在 `net_recvfrom`、B 任务重 bind 同一个 fd,A 就永远睡死。现在重开之前先按 `NET_ERR_CONNRESET` 结算并通知(UDP-20 钉住)。教训同 `net_pending`:**任何"取消挂起"的动作都必须走 complete 那条路,clear 只是归零**。
|
||||||
- **本轮覆盖率**:`net_udp.c` 234/236 行(99.2%)、`net_socket.c` 956/969(98.7%)、`net_pending.c` 173/199,全仓 line 8359/9287(90.0%)、47/47 套绿;`net_udp.c` 作为新文件随本轮 `--update-baseline` 登记,未覆盖的两处是 `net_udp.c` 的槽越界兜底(调用方已挡)与 `net_socket.c` 错误码槽重查竞态(真并发才走得到,改造前后同为那 2 行)。
|
- **本轮覆盖率**(收尾那次全仓跑数,`--update-baseline` 已把数字连同本轮提交一起落盘):`net_udp.c` 237/239 行(99.2%)、函数 **14/14**;`net_socket.c` 966/969(99.7%)、函数 **59/59**;`net_pending.c` 173/199(86.9%);全仓 line 8372/9290(90.1%)、47/47 套绿,无回退。剩两处未覆盖:`net_udp.c` 的槽越界兜底(调用方已挡,属防御性代码)与 `net_socket.c` 错误码槽重查竞态(真并发才走得到,改造前后同为那 2 行)。
|
||||||
- **台架必验(本轮无板子,主机测替代不了)**:① 两端真机互通,服务器形态收发 + 客户端形态往返,重点看 48 B 这类小应答**载荷完整**且源地址正确;② `sendto` 切形态后 `net_udp_set_peer(fd, NULL)` 能否真恢复收广播——手册只写 DesIP 语义,没写切换时机;③ 截断包排空后下一个包是否完整(scratch 循环在真芯片读指针上的行为);④ 挂起 recv 等待期间的 IWDG 余量;⑤ bind 在 open **之前**写 DesIP 的顺序在真芯片上的实际效果(TRAP-NET-14 的 UDP 侧对照)。
|
- **台架必验(本轮无板子,主机测替代不了)**:① 两端真机互通,服务器形态收发 + 客户端形态往返,重点看 48 B 这类小应答**载荷完整**且源地址正确;② `sendto` 切形态后 `net_udp_set_peer(fd, NULL)` 能否真恢复收广播——手册只写 DesIP 语义,没写切换时机;③ 截断包排空后下一个包是否完整(scratch 循环在真芯片读指针上的行为);④ 挂起 recv 等待期间的 IWDG 余量;⑤ bind 在 open **之前**写 DesIP 的顺序在真芯片上的实际效果(TRAP-NET-14 的 UDP 侧对照)。
|
||||||
- 本轮改动面:`Drivers/BSP/NET/net_udp.c` 与 `Drivers/BSP/NET/net_udp.h`(新)、`net_socket.c` / `net_socket.h`、`net_pending.c` / `net_pending.h`、`net_types.h`、`MDK-ARM/STM32F407-Demo.uvprojx`、`scripts/check_registration.py`、`test/host/common/ch395f_fake.c` 与 `test/host/common/ch395f_fake.h`、`test/host/net_socket/`(含新分片);文档 → `docs/网络/BSD_Socket_API_使用指南.md` §6.0 / §6.1 / §6.2 / §6.3 / §9.0、`docs/网络/BSD_Socket_API_函数调用关系.md` §4.11、`docs/问题记录/CH395F_Trap_Records.md`(TRAP-NET-14 UDP 段 + 新条)。
|
- 本轮改动面:`Drivers/BSP/NET/net_udp.c` 与 `Drivers/BSP/NET/net_udp.h`(新)、`net_socket.c` / `net_socket.h`、`net_pending.c` / `net_pending.h`、`net_types.h`、`MDK-ARM/STM32F407-Demo.uvprojx`、`scripts/check_registration.py`、`test/host/common/ch395f_fake.c` 与 `test/host/common/ch395f_fake.h`、`test/host/net_socket/`(含新分片);文档 → `docs/网络/BSD_Socket_API_使用指南.md` §6.0 / §6.1 / §6.2 / §6.3 / §9.0、`docs/网络/BSD_Socket_API_函数调用关系.md` §4.11、`docs/问题记录/CH395F_Trap_Records.md`(TRAP-NET-14 UDP 段 + 新条)。
|
||||||
|
|
||||||
|
|||||||
@@ -348,17 +348,17 @@
|
|||||||
"func_rate": 1.0,
|
"func_rate": 1.0,
|
||||||
"funcs_covered": 59,
|
"funcs_covered": 59,
|
||||||
"funcs_valid": 59,
|
"funcs_valid": 59,
|
||||||
"line_rate": 0.9866,
|
"line_rate": 0.9969,
|
||||||
"lines_covered": 956,
|
"lines_covered": 966,
|
||||||
"lines_valid": 969
|
"lines_valid": 969
|
||||||
},
|
},
|
||||||
"Drivers/BSP/NET/net_udp.c": {
|
"Drivers/BSP/NET/net_udp.c": {
|
||||||
"func_rate": 1.0,
|
"func_rate": 1.0,
|
||||||
"funcs_covered": 14,
|
"funcs_covered": 14,
|
||||||
"funcs_valid": 14,
|
"funcs_valid": 14,
|
||||||
"line_rate": 0.9915,
|
"line_rate": 0.9916,
|
||||||
"lines_covered": 234,
|
"lines_covered": 237,
|
||||||
"lines_valid": 236
|
"lines_valid": 239
|
||||||
},
|
},
|
||||||
"Drivers/BSP/NETCFG/ftp_cfg.c": {
|
"Drivers/BSP/NETCFG/ftp_cfg.c": {
|
||||||
"func_rate": 1.0,
|
"func_rate": 1.0,
|
||||||
@@ -596,9 +596,9 @@
|
|||||||
"net_socket": {
|
"net_socket": {
|
||||||
"files": {
|
"files": {
|
||||||
"Drivers/BSP/NET/net_socket.c": 969,
|
"Drivers/BSP/NET/net_socket.c": 969,
|
||||||
"Drivers/BSP/NET/net_udp.c": 236
|
"Drivers/BSP/NET/net_udp.c": 239
|
||||||
},
|
},
|
||||||
"lines_total": 1205,
|
"lines_total": 1208,
|
||||||
"ok": true
|
"ok": true
|
||||||
},
|
},
|
||||||
"phasor": {
|
"phasor": {
|
||||||
@@ -852,9 +852,9 @@
|
|||||||
"func_rate": 0.9266,
|
"func_rate": 0.9266,
|
||||||
"funcs_covered": 619,
|
"funcs_covered": 619,
|
||||||
"funcs_valid": 668,
|
"funcs_valid": 668,
|
||||||
"line_rate": 0.9001,
|
"line_rate": 0.9012,
|
||||||
"lines_covered": 8359,
|
"lines_covered": 8372,
|
||||||
"lines_valid": 9287,
|
"lines_valid": 9290,
|
||||||
"modules_ok": 47,
|
"modules_ok": 47,
|
||||||
"modules_total": 47
|
"modules_total": 47
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1055,6 +1055,205 @@ static void tc_udp20_rebind_settles_waiter(void)
|
|||||||
reset_all();
|
reset_all();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ===== UDP-21 两条"错误不是超时"的出口:bind 满队列 + recvfrom_to 真失败 ===== */
|
||||||
|
|
||||||
|
static void tc_udp21_bind_and_recvfrom_to_exits(void)
|
||||||
|
{
|
||||||
|
struct net_sockaddr_in in;
|
||||||
|
const struct net_sockaddr *sa = (const struct net_sockaddr *)∈
|
||||||
|
char buf[8];
|
||||||
|
int fd;
|
||||||
|
int raw_fd;
|
||||||
|
int chip_fd;
|
||||||
|
|
||||||
|
bring_up(); /* 调用方上下文:公开入口一律走队列 */
|
||||||
|
fd = net_socket(NET_AF_INET, NET_SOCK_DGRAM);
|
||||||
|
(void)memset(&in, 0, sizeof(in));
|
||||||
|
in.sin_family = NET_AF_INET;
|
||||||
|
in.sin_port = net_htons(1234);
|
||||||
|
|
||||||
|
/* 队列被别的请求占满:bind 只能"排队等空位",等不到就 BUSY,
|
||||||
|
* 绝不能把 fd 留在半个打开状态(芯片侧一条命令都没发)。 */
|
||||||
|
ns_rtos_q_fill((int)NS_Q_CAP);
|
||||||
|
CHECK(net_bind(fd, sa, (int)sizeof(in)) == NET_ERR &&
|
||||||
|
net_get_errno() == NET_ERR_BUSY,
|
||||||
|
"UDP-21a bind on a saturated queue is BUSY, and the code is the caller's own -11 (not a bare -1 the caller cannot diagnose)");
|
||||||
|
CHECK(s_net_socks[fd].state != NET_SOCK_STATE_UDP_OPEN && g_chip.open_calls == 0,
|
||||||
|
"UDP-21b a refused bind opens nothing on the chip: the fd is still just created, not half-bound");
|
||||||
|
ns_rtos_q_flush();
|
||||||
|
|
||||||
|
/* 同一 fd 在队列腾空后必须能正常 bind:BUSY 是"这次没排上",不是给 fd 投毒 */
|
||||||
|
CHECK(net_bind(fd, sa, (int)sizeof(in)) == 0 &&
|
||||||
|
s_net_socks[fd].state == NET_SOCK_STATE_UDP_OPEN,
|
||||||
|
"UDP-21c the same fd binds fine once the queue drains (a BUSY refusal poisons nothing)");
|
||||||
|
|
||||||
|
/* net_recvfrom_to 的"真错误"出口:结果 <0 且不是超时 → 原样带精确码回调用方。
|
||||||
|
* 挑一个未 bind 的 DGRAM fd:芯片侧 Socket 还没开,执行体必返 NOTCONN。 */
|
||||||
|
raw_fd = net_socket(NET_AF_INET, NET_SOCK_DGRAM);
|
||||||
|
CHECK(net_recvfrom_to(raw_fd, buf, (int)sizeof(buf), 50U, NULL, NULL) == NET_ERR &&
|
||||||
|
net_get_errno() == NET_ERR_NOTCONN,
|
||||||
|
"UDP-21d net_recvfrom_to maps an execution failure to the precise code (NOTCONN here), and does not disguise it as a timeout");
|
||||||
|
CHECK(net_udp_armed(raw_fd) == 0,
|
||||||
|
"UDP-21e2 the refused attempt hands the out-param slot back (an armed slot left behind by a refusal turns this fd permanently BUSY, and the error only surfaces on the *next* caller)");
|
||||||
|
CHECK(net_recvfrom_to(raw_fd, buf, (int)sizeof(buf), 50U, NULL, NULL) == NET_ERR &&
|
||||||
|
net_get_errno() == NET_ERR_NOTCONN,
|
||||||
|
"UDP-21e the same refusal repeats with the same code (BUSY here would mean the fd was poisoned by its own last failure)");
|
||||||
|
|
||||||
|
/* 正向:预算内已有数据 → 返回字节数,不走 -2 */
|
||||||
|
udp_script_server_datagram("time", 4, 123U, "10.20.30.40");
|
||||||
|
CHECK(net_recvfrom(fd, buf, (int)sizeof(buf), NET_MSG_DONTWAIT, NULL, NULL) == 4,
|
||||||
|
"UDP-21f sanity: the bound fd still delivers a queued datagram byte-for-byte");
|
||||||
|
|
||||||
|
/* 执行体在**执行阶段**失败(不是入队失败)时,精确码要能回到等待方:
|
||||||
|
* 真芯片 open 会回 ERR_BUSY,这条通道是整个"通知值即结果"设计的落点。
|
||||||
|
* 用一个新 fd,别拿已经 bind 成功的那个——那会看不出"失败没把 fd 变成已 bind"。 */
|
||||||
|
chip_fd = net_socket(NET_AF_INET, NET_SOCK_DGRAM);
|
||||||
|
g_fake.open_rc[chip_fd] = CH395F_ERR_BUSY;
|
||||||
|
(void)memset(&in, 0, sizeof(in));
|
||||||
|
in.sin_family = NET_AF_INET;
|
||||||
|
in.sin_port = net_htons(4567);
|
||||||
|
CHECK(net_bind(chip_fd, sa, (int)sizeof(in)) == NET_ERR &&
|
||||||
|
net_get_errno() == NET_ERR_BUSY,
|
||||||
|
"UDP-21g a bind the chip refuses (open → ERR_BUSY) comes back through the notify channel as -1 + the precise BUSY, not a bare -1 and not a timeout");
|
||||||
|
CHECK(s_net_socks[chip_fd].state != NET_SOCK_STATE_UDP_OPEN,
|
||||||
|
"UDP-21h a failed bind does not leave the fd looking bound");
|
||||||
|
g_fake.open_rc[chip_fd] = CH395F_ERR_SUCCESS;
|
||||||
|
CHECK(net_bind(chip_fd, sa, (int)sizeof(in)) == 0 &&
|
||||||
|
s_net_socks[chip_fd].state == NET_SOCK_STATE_UDP_OPEN &&
|
||||||
|
s_net_socks[chip_fd].local_port == 4567U,
|
||||||
|
"UDP-21i the same fd binds normally once the chip stops refusing");
|
||||||
|
|
||||||
|
/* 队列里有"别人的失败"排在我这条之前:两条在同一轮被排空,
|
||||||
|
* 覆盖式通知让我拿到的是**自己那条**的回执(后写覆盖先写)。
|
||||||
|
* 既有 TC-NETSOCK-545 在 TCP 侧钉过同一形态,这里补 UDP bind 这一条。 */
|
||||||
|
{
|
||||||
|
net_msg_t stale;
|
||||||
|
int ghost = 6;
|
||||||
|
|
||||||
|
(void)memset(&stale, 0, sizeof(stale));
|
||||||
|
stale.type = NET_MSG_UDP_BIND;
|
||||||
|
stale.sockfd = ghost; /* 没 net_socket() 过的 fd:执行体必拒 */
|
||||||
|
stale.len = 1234U; /* bind 的本地端口走 len(主机序) */
|
||||||
|
stale.addr = in;
|
||||||
|
stale.addrlen = (int)sizeof(in);
|
||||||
|
stale.caller = (TaskHandle_t)&g_ns_task_caller;
|
||||||
|
CHECK(ns_rtos_q_put_raw(&stale) == 0, "UDP-21j the ghost bind request is queued");
|
||||||
|
|
||||||
|
(void)memset(&in, 0, sizeof(in));
|
||||||
|
in.sin_family = NET_AF_INET;
|
||||||
|
in.sin_port = net_htons(4567);
|
||||||
|
CHECK(net_bind(chip_fd, sa, (int)sizeof(in)) == 0,
|
||||||
|
"UDP-21k a stranger request settling first does not turn my own successful bind into an error (the completion that overwrites the notify value is mine)");
|
||||||
|
ns_rtos_q_flush();
|
||||||
|
}
|
||||||
|
reset_all();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===== UDP-22 netTask 直调路径:set_peer 的三条出口(板级用例走这条) ===== */
|
||||||
|
|
||||||
|
static void tc_udp22_set_peer_direct_ctx(void)
|
||||||
|
{
|
||||||
|
struct net_sockaddr_in peer;
|
||||||
|
int fd;
|
||||||
|
int stream_fd;
|
||||||
|
int puts_before;
|
||||||
|
int set_ip_before;
|
||||||
|
|
||||||
|
bring_up_netctx(); /* 留在 netTask 上下文:owner → 直调,不经队列 */
|
||||||
|
fd = udp_bind(777);
|
||||||
|
puts_before = g_ns_put_calls;
|
||||||
|
|
||||||
|
(void)memset(&peer, 0, sizeof(peer));
|
||||||
|
peer.sin_family = NET_AF_INET;
|
||||||
|
peer.sin_port = net_htons(123);
|
||||||
|
peer.sin_addr.s_addr = net_inet_addr("119.28.220.214");
|
||||||
|
|
||||||
|
CHECK(net_udp_set_peer(fd, &peer) == 0,
|
||||||
|
"UDP-22a set_peer inside netTask runs directly (the board-test path) and succeeds");
|
||||||
|
CHECK(g_ns_put_calls == puts_before,
|
||||||
|
"UDP-22b the direct path does not enqueue a message (a self-addressed queue message would never be drained)");
|
||||||
|
CHECK(net_udp_get_mode(fd) == NET_UDP_MODE_CLIENT &&
|
||||||
|
g_fx.des_ip[fd][0] == 119U && g_fx.des_ip[fd][3] == 214U &&
|
||||||
|
g_fx.des_port[fd] == 123U && g_chip.open_calls == 1,
|
||||||
|
"UDP-22c direct set_peer writes DesIP/DesPort and flips to CLIENT without reopening the socket");
|
||||||
|
|
||||||
|
CHECK(net_udp_set_peer(fd, NULL) == 0 &&
|
||||||
|
net_udp_get_mode(fd) == NET_UDP_MODE_SERVER &&
|
||||||
|
g_fx.des_ip[fd][0] == 0xFFU && g_fx.des_port[fd] == 0U,
|
||||||
|
"UDP-22d NULL peer is the light way back to SERVER: registers restored, still no reopen");
|
||||||
|
|
||||||
|
/* 越界 / 未使用 / 面向流:三种拒绝都必须零芯片事务 */
|
||||||
|
set_ip_before = g_fxchip.set_des_ip_calls;
|
||||||
|
CHECK(net_udp_set_peer(-1, &peer) == NET_ERR && net_get_errno() == NET_ERR_BADF,
|
||||||
|
"UDP-22e negative fd is BADF on the direct path too");
|
||||||
|
CHECK(net_udp_set_peer(NET_MAX_SOCKETS, &peer) == NET_ERR &&
|
||||||
|
net_get_errno() == NET_ERR_BADF,
|
||||||
|
"UDP-22f fd >= NET_MAX_SOCKETS is BADF (the range guard is what keeps s_net_udp[] in bounds)");
|
||||||
|
CHECK(net_udp_set_peer(5, &peer) == NET_ERR && net_get_errno() == NET_ERR_BADF,
|
||||||
|
"UDP-22g an in-range but unused fd is BADF before anything is touched");
|
||||||
|
stream_fd = mk_sock(NET_SOCK_STREAM, NET_SOCK_STATE_ESTABLISHED);
|
||||||
|
CHECK(net_udp_set_peer(stream_fd, &peer) == NET_ERR &&
|
||||||
|
net_get_errno() == NET_ERR_INVAL,
|
||||||
|
"UDP-22h set_peer on a STREAM fd is refused as INVAL (the type check runs before the state check: a TCP socket has no UDP mode to switch at all)");
|
||||||
|
{
|
||||||
|
int unbound = net_socket(NET_AF_INET, NET_SOCK_DGRAM);
|
||||||
|
|
||||||
|
CHECK(net_udp_set_peer(unbound, &peer) == NET_ERR &&
|
||||||
|
net_get_errno() == NET_ERR_NOTCONN,
|
||||||
|
"UDP-22h2 an unbound DGRAM fd is refused as NOTCONN on the direct path too (DesIP written before open gets reset by open)");
|
||||||
|
}
|
||||||
|
CHECK(g_fxchip.set_des_ip_calls == set_ip_before,
|
||||||
|
"UDP-22i every refusal above wrote zero chip registers (a refusal that already moved DesIP would leave the socket pointing at a peer nobody asked for)");
|
||||||
|
reset_all();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===== UDP-23 netTask 直调收包正例 + 槽位被占的 BUSY 出口 ===== */
|
||||||
|
|
||||||
|
static void tc_udp23_recvfrom_direct_ctx(void)
|
||||||
|
{
|
||||||
|
struct net_sockaddr_in from;
|
||||||
|
struct net_sockaddr *sa = (struct net_sockaddr *)&from;
|
||||||
|
int alen = (int)sizeof(from);
|
||||||
|
char buf[16];
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
bring_up_netctx();
|
||||||
|
fd = udp_bind(888);
|
||||||
|
|
||||||
|
/* 数据已在芯片缓冲里:直调路径当场读出并就地经槽位写回源地址 */
|
||||||
|
udp_script_server_datagram("ABCDEFGH", 8, 5353U, "10.1.2.3");
|
||||||
|
CHECK(net_recvfrom(fd, buf, (int)sizeof(buf), 0, sa, &alen) == 8,
|
||||||
|
"UDP-23a netTask-context recvfrom reads the datagram on the spot (board tests call it this way)");
|
||||||
|
CHECK(memcmp(buf, "ABCDEFGH", 8) == 0,
|
||||||
|
"UDP-23b the 8-byte info header is stripped and the payload starts at byte 0");
|
||||||
|
CHECK(alen == (int)sizeof(from) && from.sin_addr.s_addr == net_inet_addr("10.1.2.3") &&
|
||||||
|
from.sin_port == net_htons(5353),
|
||||||
|
"UDP-23c the source address is written through the caller's pointer, on the direct path as well");
|
||||||
|
CHECK(net_udp_armed(fd) == 0,
|
||||||
|
"UDP-23d the slot is handed back by the execution body even on a synchronous hit");
|
||||||
|
|
||||||
|
/* 假件的脚本长度耗尽后会**重复最后一个值**(表达"一直有数据"的持续态),
|
||||||
|
* 这里显式宣布"没包了",否则下面两条会把上一个包再读一遍。 */
|
||||||
|
ch395f_fake_push_recv_len(0, 0U);
|
||||||
|
|
||||||
|
/* 槽位被别人占着:本 fd 上必须 BUSY,且不去碰芯片、也不许动别人的槽 */
|
||||||
|
CHECK(net_udp_outparam_arm(fd, NULL, NULL) == 0, "UDP-23e slot armed by somebody else");
|
||||||
|
CHECK(net_recvfrom(fd, buf, (int)sizeof(buf), NET_MSG_DONTWAIT, sa, &alen) == NET_ERR &&
|
||||||
|
net_get_errno() == NET_ERR_BUSY,
|
||||||
|
"UDP-23f recvfrom while the out-param slot is taken is BUSY rather than writing through a pointer that belongs to another call (公开入口对外仍是 -1 + 精确码)");
|
||||||
|
CHECK(net_udp_armed(fd) != 0,
|
||||||
|
"UDP-23g the refusal does not steal the slot it did not own");
|
||||||
|
(void)net_udp_outparam_disarm(fd);
|
||||||
|
|
||||||
|
/* 直调 + 阻塞 + 无数据 = 自等,被守卫拒成 BUSY(tick 就在自己脚下跑,等谁都不会醒) */
|
||||||
|
CHECK(net_recvfrom(fd, buf, (int)sizeof(buf), 0, NULL, NULL) == NET_ERR &&
|
||||||
|
net_get_errno() == NET_ERR_BUSY,
|
||||||
|
"UDP-23h a blocking recvfrom executed inside netTask refuses to register a pending slot nobody can settle (self-wait guard)");
|
||||||
|
CHECK(net_udp_armed(fd) == 0 && net_udp_pending(fd) == 0,
|
||||||
|
"UDP-23i that refusal leaves neither an armed nor a pending slot behind");
|
||||||
|
reset_all();
|
||||||
|
}
|
||||||
|
|
||||||
/* ===================== 本分片入口 ===================== */
|
/* ===================== 本分片入口 ===================== */
|
||||||
|
|
||||||
static void ns_run_s7(void)
|
static void ns_run_s7(void)
|
||||||
@@ -1079,4 +1278,7 @@ static void ns_run_s7(void)
|
|||||||
tc_udp18_pending_send_completes();
|
tc_udp18_pending_send_completes();
|
||||||
tc_udp19_tick_fallbacks();
|
tc_udp19_tick_fallbacks();
|
||||||
tc_udp20_rebind_settles_waiter();
|
tc_udp20_rebind_settles_waiter();
|
||||||
|
tc_udp21_bind_and_recvfrom_to_exits();
|
||||||
|
tc_udp22_set_peer_direct_ctx();
|
||||||
|
tc_udp23_recvfrom_direct_ctx();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user