fff961d803
前置来自 #57 SNTP 调研:旧 net_sendto / net_recvfrom 由调用方直调 SPI 且无限自旋收包,既违约「netTask 是芯片唯一访问者」,又把 IWDG 预算吃在自旋里。 改造面 - 新增 Drivers/BSP/NET/net_udp.c/.h(真机专属,模拟器不编译): bind/set_peer/sendto/recvfrom 四条执行体 + 挂起收包表 + tick; net_msg_type_t 加四型,公开 API 一律「入队 → 睡在通知上」。 - 源地址出参不搭 net_msg_t(按值拷贝,AGENTS 禁加回传字段):改住 net_udp.c 的 per-fd 槽,netTask 就地写回调用方内存,通知值只带 结果码;每条出口都清槽,泄漏 = 该 fd 永久 BUSY。 - 发送复用 net_send_locked,不写第二份分块逻辑;「会话是否已死」 收成一条判据 net_send_state_down()(TCP 看 ESTABLISHED、UDP 看 UDP_OPEN),原先 4 处各写一遍。 - 顺带修掉真缺陷(新条 TRAP-NET-22):旧 sendto 把 DesIP 写成具体 IP,芯片静默切 UDP Client,而旧 recvfrom 无条件剥 8 字节 → SNTP 这类 48 B 应答丢载荷前 8 字节并交出垃圾源地址。加 udp_mode(塞进 原有 padding,sizeof 不变)、bind 恒 SERVER、显式对端 sendto 切 CLIENT、set_peer(fd, NULL) 轻量回 SERVER,仅 SERVER 且 avail >= 8 才剥头。 - 截断定案:一次调用交付一个数据报,buf 装不下的尾巴用 64 B 栈上 scratch 排空(不用 clear_recv_buf——它连后面已到达的包一起丢)。 - 取消点接齐:net_init 重建 cancel_all、net_socket 分配 fd clear、 net_close settle_close + clear、重 bind 前先结算等包者, net_process_messages 第 5 个 tick 位挂 net_udp_tick。 - 收紧两处契约(原实现无生产调用者):UDP 未 bind 一律 -5; net_recv / _nb / _to 拒 DGRAM(绕过剥头就是静默吃 8 字节); NET_ERR_NOT_IN_NETTASK(-12) 退役为历史常量并写明原因。 验证 - 主机单测 test/host/net_socket 全套 616/616 绿(改造前 470):新 分片 ns_s7_udp.c 20 组,改写 440 / 555 / 560 组;覆盖率 net_udp.c 234/236(99.2%)、net_socket.c 956/969(98.7%), scripts/test_coverage_baseline.json 随本提交登记新文件。 - 测试抓到 4 个被测码真 bug:自等守卫误用调用侧谓词、芯片长度被读 两次、槽指针取在 fd 合法性检查之前、重 bind 静默作废等待者。 - Keil ARMCC 0 Error(s) 0 Warning(s);net_udp.o Code 1,358 / ZI 224 (8 fd × 28 B,其余模块零增长),全仓 RW_IRAM1 72.2%;内存台账 受管区按门禁提示用 --write / --update 机器回填,未手改数字。 - pre-commit 全量钩子(含 doc-links、coding-standard、mem-budget、 generated-fresh、bsp-err、registration、nested-comment)全绿。 限度 - 本轮无板子:真机回归 5 条(两端互通、set_peer(NULL) 能否恢复收 广播、截断排空后下一包完整性、挂起 recv 期间 IWDG 余量、bind 写 DesIP 与 open 先后的真芯片效果)已写进覆盖率口径「台架必验」。 - scripts/stack_report.py 退出码 1 是 10-26 起挂在账上的 iec104Task 栈债(margin 1,016 < 1,024,见 docs/代码规范/pre-commit使用说明.md: 122);本轮只让 net_bind 的 UDP 分支多一层静态帧(1,544 → 1,600), netTask margin 1,888 与 timeSyncTask 1,152 不受影响。抬栈要过 mem-budget 台账,不在本分支自行处理。 - 登记簿销项按仓库约定留给主工作树,本分支不动 docs/任务规划/**。
374 lines
21 KiB
C
374 lines
21 KiB
C
/*
|
||
* 模块名称:假 CH395F 芯片(NET 域主机侧单测共享件)
|
||
* 模块功能:给 NET 域的主机侧单测一个**可脚本化的假芯片**。真实 ch395f.h 拉 HAL 与
|
||
* SPI 寄存器定义,PC 上编不动;而 net_listener / net_pending / net_events
|
||
* 的行为恰恰完全由"芯片此刻报什么状态"决定,必须有假件才能测。
|
||
*
|
||
* ⚠ 仅供 test/host/** 使用,不参与 MCU 构建。
|
||
*
|
||
* 为什么值得共享一份(而不是每模块自带桩):三份假芯片各自漂移时,"假件与被测代码
|
||
* 同时错"这类问题再也不会报出来。各模块的 stub/ch395f.h 只做一件事——include 本文件,
|
||
* 于是签名与常量只有一份定义。
|
||
*
|
||
* 常量与类型必须与 Drivers/BSP/CH395F/ch395f.h 逐字一致(2026-10-23 从真实头抄录):
|
||
* CH395F_ERR_SUCCESS 0x00 / CH395F_PROTO_TYPE_TCP 0x03 / CH395F_SOCKET_CLOSED 0x00
|
||
* CH395F_TCP_CLOSED 0x00 / CH395F_TCP_LISTEN 0x01;ch395f_sock_state_t 为
|
||
* { uint8_t open; uint8_t tcp_state; } 两字节。
|
||
*
|
||
* 必须复刻的两条芯片怪癖(不复刻就等于把被测代码的坑假件掉):
|
||
* 1. ch395f_get_sock_int_status() 是**整体读即清**、芯片没有按位清除手段。本假件
|
||
* 返回该 socket 的挂起位图并同步清零。AGENTS 模块约定里 B3 的实证坑("顺手
|
||
* 全量清一遍"把别的在用 Socket 的门焊死)靠这条才照得出来。
|
||
* 2. *_cached 系列返回的是**缓存值**而不是现场值:open 状态与 TCP 状态分开、且
|
||
* "监听还活着"的判据是 (open != CLOSED && tcp_state == LISTEN)。测试据此能构造
|
||
* "缓存陈旧"的形态。
|
||
*
|
||
* 脚本化模型(沿用本仓既有假件风格:nand_port 的真头+假 diskio、lftpd_io 的可注入假堆):
|
||
* - 每个读函数一条按调用序号取的返回队列,队列耗尽后回落到**最后一个值**(便于
|
||
* 写"一直可读""一直关不掉"这类持续状态);
|
||
* - 每个写/控制函数一条调用记录(次数 + 最近一次入参),另给逐函数调用计数,
|
||
* 让"这一轮根本不该碰芯片"成为可断言的事;
|
||
* - g_chip.log[] 记录**调用顺序**(短名),用于断言"先清接收、再关、再读即清"这类
|
||
* 时序契约;
|
||
* - ch395f_fake_reset() 一次全清。
|
||
*
|
||
* 创建日期:2026-10-23
|
||
*/
|
||
|
||
#ifndef CH395F_FAKE_H
|
||
#define CH395F_FAKE_H
|
||
|
||
#include <stdint.h>
|
||
|
||
/* —— 与真实 ch395f.h 逐字一致的常量(值抄自真实头,勿"顺手改")—— */
|
||
#define CH395F_ERR_SUCCESS 0x00U
|
||
#define CH395F_PROTO_TYPE_TCP 0x03U
|
||
#define CH395F_SOCKET_CLOSED 0x00U
|
||
#define CH395F_SOCKET_OPEN 0x05U /* 真头 ch395f.h:220 = 0x05U(05H SOCKET_OPEN,docs/芯片手册/CH395F.md:527)*/
|
||
#define CH395F_TCP_CLOSED 0x00U
|
||
#define CH395F_TCP_LISTEN 0x01U
|
||
#define CH395F_TCP_ESTABLISHED 0x04U /* 真头 ch395f.h:229 = 0x04U(04H TCP_ESTABLISHED,docs/芯片手册/CH395F.md:539)*/
|
||
|
||
/* 发送/接收缓冲尺寸宏链:**与真实 ch395f.h 同构转抄**(2026-10-23 逐条核对)。
|
||
* net_pending.h 里的 NET_SEND_CHUNK_MAX 由这两个宏推导,是"一次最多往芯片写多少"
|
||
* 的真实上界,假件不能自己发明数字(否则分块测试就是在测假件的常量):
|
||
* RAM_BLOCK_SIZE 512 / SEND_BLOCKS 2 → SEND_BUF 1024
|
||
* RECV_BLOCKS 4 → RECV_BUF 2048 → SPI_DMA_BUF 2052 → SPI_DMA_MAX_PAYLOAD 2048
|
||
* ⇒ NET_SEND_CHUNK_MAX = min(1024, 2048) = 1024 */
|
||
#define CH395F_RAM_BLOCK_SIZE 512U
|
||
#define CH395F_SEND_BLOCKS 2U
|
||
#define CH395F_SEND_BUF_SIZE_BYTES (CH395F_SEND_BLOCKS * CH395F_RAM_BLOCK_SIZE)
|
||
#define CH395F_RECV_BLOCKS 4U
|
||
#define CH395F_RECV_BUF_SIZE_BYTES (CH395F_RECV_BLOCKS * CH395F_RAM_BLOCK_SIZE)
|
||
#define CH395F_SPI_DMA_BUF_SIZE (CH395F_RECV_BUF_SIZE_BYTES + 4U)
|
||
#define CH395F_SPI_DMA_MAX_PAYLOAD (CH395F_SPI_DMA_BUF_SIZE - 4U)
|
||
|
||
#define CH395F_FAKE_SOCK_MAX 8 /* 与 NET_MAX_SOCKETS 同量级,测试自用 */
|
||
#define CH395F_FAKE_QUEUE_LEN 24
|
||
#define CH395F_FAKE_LOG_LEN 96
|
||
#define CH395F_FAKE_GLOB_QUEUE 24
|
||
/* 写内容捕获容量(测试自用,真实头无此宏 → 不参与假芯片常量对齐门):
|
||
* 256 够覆盖"一个小数据报全文"与"分块发送首块的前 256 字节"两类断言。 */
|
||
#define CH395F_FAKE_WRITE_CAP 256
|
||
|
||
/* ===== 扩展区(2026-10-24,net_socket 主机单测批次;**纯新增**,上面既有宏/字段/函数
|
||
* 一字未动,net_listener / net_events / net_pending 三套的语义不受影响)=====
|
||
*
|
||
* 追加的是 net_socket.c 这一层会调、而前三个模块不调的芯片原语(初始化下发类 +
|
||
* 全局中断 + sock_reset 复位机 + cmd_done 判据)。签名/常量同样**逐字抄自真实头**
|
||
* Drivers/BSP/CH395F/ch395f.h,改真头即编译期/断言期暴露漂移。
|
||
*
|
||
* 必须复刻的第三条芯片怪癖(前两套测不到、net_socket.c 却是主路):
|
||
* 3. GET_GLOB_INT_STATUS_ALL(0x19,2 字节)**整体读即清**,且它是全局寄存器
|
||
* (含全部 Socket 事件位)。AGENTS 模块约定 B3:唯一读者只能是 net_poll。
|
||
* 假件按调用序号返回预置值,并单独计数,让"别处偷偷读全局"成为可断言的事。
|
||
* 4. sock_reset 复位机(E4 定案四步:读空 RX → CLOSE → 排空事件(quiescent) →
|
||
* 等 byte0=00H,含 LISTEN 态二段 CLOSE 补发)。这里做**契约等价**实现:
|
||
* 阶段推进与最短等待(GRACEFUL 100ms / QUARANTINE 400ms)照抄驱动,
|
||
* 但不复制驱动的超时数值细节(那属于驱动自己的单测面)。
|
||
*/
|
||
#define CH395F_ERR_BUSY 0x10U /* 真头 ch395f.h:170 */
|
||
#define CH395F_ERR_MEM 0x11U /* 真头 ch395f.h:171 */
|
||
#define CH395F_ERR_TIMEOUT 0x13U /* 真头 ch395f.h:173 */
|
||
#define CH395F_ERR_VAL 0x19U /* 真头 ch395f.h:179 */
|
||
#define CH395F_ERR_USE 0x1BU /* 真头 ch395f.h:181(open 已被占用)*/
|
||
#define CH395F_PROTO_TYPE_UDP 0x02U /* 真头 ch395f.h:213 */
|
||
#define CH395F_FUN_PARA_SOCKET_CLOSE (1UL << 3) /* 真头 ch395f.h:205 */
|
||
#define CH395F_TCP_MSS 1024U /* 真头 ch395f.h:124 */
|
||
#define CH395F_PHY_DISCONN 0x01U /* 真头 ch395f.h:191 */
|
||
#define CH395F_GINT_STAT_PHY_CHANGE 0x04U /* 真头 ch395f.h:245 */
|
||
#define CH395F_SOCK_STATE_NONE 0xFFU /* 真头 ch395f.h:762 */
|
||
|
||
#define CH395F_SINT_STAT_SENBUF_FREE 0x01U /* 真头 ch395f.h:263 */
|
||
#define CH395F_SINT_STAT_SEND_OK 0x02U /* 真头 ch395f.h:264 */
|
||
#define CH395F_SINT_STAT_RECV_OK 0x04U /* 真头 ch395f.h:265 */
|
||
#define CH395F_SINT_STAT_CONNECT 0x08U /* 真头 ch395f.h:266 */
|
||
#define CH395F_SINT_STAT_DISCONNECT 0x10U /* 真头 ch395f.h:267 */
|
||
#define CH395F_SINT_STAT_SOCK_TIMEOUT 0x40U /* 真头 ch395f.h:269 */
|
||
|
||
typedef enum {
|
||
CH395F_STATUS_OK = 0, /* 真头 ch395f.h:275 */
|
||
CH395F_STATUS_ERROR = -1,
|
||
CH395F_STATUS_TIMEOUT = -2,
|
||
CH395F_STATUS_NOT_DETECTED = -3
|
||
} ch395f_status_t;
|
||
|
||
typedef enum {
|
||
CH395F_RESET_GRACEFUL = 0, /* 真头 :803:最短等待 100ms */
|
||
CH395F_RESET_QUARANTINE /* 真头 :804:最短等待 400ms */
|
||
} ch395f_reset_mode_t;
|
||
|
||
typedef enum {
|
||
CH395F_DONE_OPEN = 0, /* 真头 :826:byte0 变 05H */
|
||
CH395F_DONE_LISTEN, /* 真头 :827:byte0=05H 且 TCP=01H */
|
||
CH395F_DONE_CLOSE /* 真头 :828:byte0=00H */
|
||
} ch395f_done_what_t;
|
||
|
||
/* 复位机阶段(与驱动同名状态的**契约等价**简化,不代表驱动内部实现) */
|
||
#define CH395F_FAKE_RST_IDLE 0
|
||
#define CH395F_FAKE_RST_DRAINED 1 /* 已读空 RX 并发过 CLOSE */
|
||
#define CH395F_FAKE_RST_MINWAIT 2 /* 等最短沉降窗(100/400ms) */
|
||
#define CH395F_FAKE_RST_QUIET 3 /* 排空本 Socket 事件(连续 2 轮无位) */
|
||
#define CH395F_FAKE_RST_WAITCLS 4 /* 等 byte0=00H(含二段 CLOSE 补发) */
|
||
#define CH395F_FAKE_RST_QUIESCENT 2U
|
||
|
||
typedef struct {
|
||
uint8_t open;
|
||
uint8_t tcp_state;
|
||
} ch395f_sock_state_t;
|
||
|
||
/* —— 可预置的芯片侧世界 —— */
|
||
typedef struct {
|
||
uint8_t cached_open[CH395F_FAKE_SOCK_MAX]; /* sock_state_cached().open */
|
||
uint8_t cached_tcp[CH395F_FAKE_SOCK_MAX]; /* sock_state_cached().tcp_state */
|
||
uint8_t status_byte0[CH395F_FAKE_SOCK_MAX]; /* get_socket_status 的 sock 状态 */
|
||
uint8_t status_byte1[CH395F_FAKE_SOCK_MAX]; /* get_socket_status 的 TCP 状态 */
|
||
uint8_t int_status[CH395F_FAKE_SOCK_MAX]; /* 读即清的挂起位图 */
|
||
uint16_t recv_len_q[CH395F_FAKE_SOCK_MAX][CH395F_FAKE_QUEUE_LEN];
|
||
int recv_len_n[CH395F_FAKE_SOCK_MAX];
|
||
int recv_len_i[CH395F_FAKE_SOCK_MAX];
|
||
uint8_t remote[CH395F_FAKE_SOCK_MAX][6]; /* get_remot_ipp 的 6 字节:ip[4]+port(小端) */
|
||
uint16_t link_change; /* link_change_count(链路变更计数) */
|
||
uint8_t link_up; /* link_up_cached */
|
||
uint8_t open_rc[CH395F_FAKE_SOCK_MAX]; /* open_socket 返回码 */
|
||
uint8_t listen_rc[CH395F_FAKE_SOCK_MAX]; /* tcp_listen 返回码 */
|
||
} ch395f_fake_world_t;
|
||
|
||
typedef struct {
|
||
/* 调用计数("不该碰芯片"的断言面) */
|
||
int open_calls;
|
||
int close_calls;
|
||
int listen_calls;
|
||
int int_status_calls;
|
||
int get_status_calls;
|
||
int read_calls;
|
||
int write_calls;
|
||
int set_proto_calls;
|
||
int set_port_calls;
|
||
int cached_calls;
|
||
/* 最近一次入参 */
|
||
uint8_t last_sock;
|
||
uint16_t last_read_len;
|
||
uint16_t last_port;
|
||
uint8_t last_proto;
|
||
uint8_t last_keepalive;
|
||
int keepalive_calls;
|
||
/* 调用顺序日志(短名首字母):
|
||
* 'c'=close 'i'=int_status(读即清) 's'=get_socket_status 'r'=read_recv_buf
|
||
* 'R'=get_recv_len 'o'=open_socket 'l'=tcp_listen 'p'=set_sour_port
|
||
* 'P'=set_proto_type 'w'=write_send_buf
|
||
* 扩展区新增(net_socket 批次):'G'=读全局中断 'v'=sock_events(读即清)
|
||
* 'X'=sock_reset_begin 'x'=sock_reset_poll 'z'=cmd_done 'T'=tcp_connect
|
||
* 'I'=set_ip_addr 'N'=set_gwip 'K'=set_mask 'M'=set_mac 'D'=set_dhcp
|
||
* 'F'=set_fun_para 'Q'=set_tcp_mss 'b'=set_send_buf 'u'=set_recv_buf
|
||
* 'e'=set_des_ip 'q'=set_des_port 'g'=get_phy_status 'E'=check_exist
|
||
* 'Z'=reset 'n'=init */
|
||
char log[CH395F_FAKE_LOG_LEN];
|
||
int log_n;
|
||
} ch395f_fake_obs_t;
|
||
|
||
extern ch395f_fake_world_t g_fake;
|
||
extern ch395f_fake_obs_t g_chip;
|
||
|
||
void ch395f_fake_reset(void); /* 全清并把所有 socket 设为"已关闭" */
|
||
void ch395f_fake_push_recv_len(uint8_t sock, uint16_t v);
|
||
void ch395f_fake_set_listening(uint8_t sock);
|
||
void ch395f_fake_set_peer(uint8_t sock, uint8_t b0, uint8_t b1, uint8_t b2,
|
||
uint8_t b3, uint16_t peer_port); /* 预置对端六元组 */ /* 缓存:open!=CLOSED 且 tcp=LISTEN */
|
||
|
||
/* —— 假芯片实现(签名与真实 ch395f.h 一字不改)—— */
|
||
ch395f_sock_state_t ch395f_sock_state_cached(uint8_t sn);
|
||
ch395f_sock_state_t ch395f_get_sock_state(uint8_t sn);
|
||
void ch395f_get_socket_status(uint8_t sock, uint8_t *status);
|
||
uint8_t ch395f_get_sock_int_status(uint8_t sock);
|
||
uint16_t ch395f_get_recv_len(uint8_t sock);
|
||
void ch395f_read_recv_buf(uint8_t sock, uint8_t *p_data, uint16_t len);
|
||
void ch395f_write_send_buf(uint8_t sock, uint8_t *p_data, uint16_t len);
|
||
uint8_t ch395f_open_socket(uint8_t sock);
|
||
uint8_t ch395f_close_socket(uint8_t sock);
|
||
uint8_t ch395f_tcp_listen(uint8_t sock);
|
||
void ch395f_set_proto_type(uint8_t sock, uint8_t proto);
|
||
void ch395f_set_sour_port(uint8_t sock, uint16_t port);
|
||
void ch395f_get_remot_ipp(uint8_t sock, uint8_t *p_buf);
|
||
void ch395f_set_keepalive_enable(uint8_t sock, uint8_t enable);
|
||
uint8_t ch395f_link_up_cached(void);
|
||
uint16_t ch395f_link_change_count(void);
|
||
|
||
/* ===== 扩展区第二部分:新增状态世界 + 新增假芯片原型 =====
|
||
* ⚠ 刻意**不往既有 ch395f_fake_world_t / ch395f_fake_obs_t 里加字段**,而是另开一份
|
||
* ch395f_fake_ext_t / ch395f_fake_ext_obs_t:既有三套单测的复位与断言面一字不动,
|
||
* 新增状态由 ch395f_fake_reset() 调 ch395f_fake_ext_reset() 一并清零(场景间不残留)。
|
||
*/
|
||
typedef struct {
|
||
/* —— 初始化/参数下发面(net_init 主路)—— */
|
||
int exist_rc; /* ch395f_check_exist 返回(ch395f_status_t)*/
|
||
int reset_rc; /* ch395f_reset 返回 */
|
||
int init_rc; /* ch395f_init 返回 */
|
||
uint8_t version; /* ch395f_get_version(版本门控 0x46 的用点位)*/
|
||
uint8_t dhcp_enable; /* ch395f_set_dhcp */
|
||
uint8_t ping_enable; /* ch395f_ping_enable */
|
||
uint8_t ip[4]; /* ch395f_set_ip_addr */
|
||
uint8_t gw[4]; /* ch395f_set_gwip_addr */
|
||
uint8_t mask[4]; /* ch395f_set_mask_addr */
|
||
uint8_t mac[6]; /* ch395f_set_mac_addr */
|
||
uint32_t fun_para; /* ch395f_set_fun_para 最近一次实发值 */
|
||
uint16_t mss; /* ch395f_set_tcp_mss */
|
||
uint8_t retrans_cnt;
|
||
uint16_t retrans_period;
|
||
uint32_t ka_idle;
|
||
uint32_t ka_intvl;
|
||
uint8_t ka_cnt;
|
||
uint8_t send_blk_start[CH395F_FAKE_SOCK_MAX]; /* ch395f_set_send_buf */
|
||
uint8_t send_blk_cnt[CH395F_FAKE_SOCK_MAX];
|
||
uint8_t recv_blk_start[CH395F_FAKE_SOCK_MAX]; /* ch395f_set_recv_buf */
|
||
uint8_t recv_blk_cnt[CH395F_FAKE_SOCK_MAX];
|
||
uint8_t des_ip[CH395F_FAKE_SOCK_MAX][4]; /* ch395f_set_des_ip */
|
||
uint16_t des_port[CH395F_FAKE_SOCK_MAX]; /* ch395f_set_des_port */
|
||
uint8_t connect_rc[CH395F_FAKE_SOCK_MAX]; /* ch395f_tcp_connect 返回 */
|
||
/* —— 全局中断(0x19,整体读即清;唯一读者应为 net_poll)—— */
|
||
uint16_t glob_q[CH395F_FAKE_GLOB_QUEUE];
|
||
int glob_n;
|
||
int glob_i;
|
||
uint8_t phy_status; /* ch395f_get_phy_status(喂 glob_event_feed)*/
|
||
/* —— sock_reset 复位机(契约等价,见文件头注释 4)—— */
|
||
uint8_t rst_state[CH395F_FAKE_SOCK_MAX];
|
||
uint8_t rst_mode[CH395F_FAKE_SOCK_MAX];
|
||
uint32_t rst_t0[CH395F_FAKE_SOCK_MAX];
|
||
uint8_t rst_quiet[CH395F_FAKE_SOCK_MAX]; /* 连续无事件轮数 */
|
||
/* 1=复位机**不**替芯片把 cached_open 落回 CLOSED(造"永远关不掉"的芯片形态,
|
||
* 用来量上层的复位超时收口)。默认 0 → 与既有行为一致。 */
|
||
uint8_t rst_keep_open[CH395F_FAKE_SOCK_MAX];
|
||
/* GET_RECV_BUF 的**脚本化内容**(默认长度 0 → 与既有"全零交付"逐字相同)。
|
||
* UDP 报文头(长度/源端口/源 IP)只有内容可脚本化才能被断言:net_recvfrom 要
|
||
* 从芯片头里拆出对端地址,全零内容下"拆对了"与"拆反了"看起来一模一样。 */
|
||
uint8_t recv_bytes[128];
|
||
uint16_t recv_bytes_n;
|
||
/* SINT 的**电平型**来源(可脚本化):读即清之后立刻把这个掩码放回 int_status。
|
||
* 默认 0 → 与既有假件行为逐字相同(读一次就没了)。真实芯片的 RECV_OK /
|
||
* SENBUF_FREE 本就是"条件位":只要条件还在(RX 仍非空 / 发送缓冲仍空),下一轮
|
||
* 就还会再报一次。net_socket.c 的多块发送要的就是这个稳态,没有它就只能
|
||
* 造"写完一块就再也等不到空闲"的假死形态。 */
|
||
uint8_t sint_sticky[CH395F_FAKE_SOCK_MAX];
|
||
/* —— 2026-11-03 UDP 队列化改造追加(默认全 0 → 既有行为逐字不变)—— */
|
||
/* GET_RECV_BUF 的**流式读**开关:UDP 一个数据报 = 8 字节信息头 + 载荷,
|
||
* 生产代码是**两次**读(先剥头再读载荷),而 set_recv_bytes 每次都从预置
|
||
* 内容第 0 字节交付——那样"剥掉头"与"把包头当载荷"在断言里长得一样。
|
||
* 本开关让交付内容带游标:第二次读拿到的是第 8 字节往后的载荷。 */
|
||
uint8_t recv_stream_en;
|
||
uint16_t recv_pos; /* 游标:已交付字节数(截断排空要断言它) */
|
||
/* WRITE_SEND_BUF 的**内容捕获**开关:假芯片原本忽略 p_data(既有用例只数次数),
|
||
* 但 UDP 发送要钉"写进芯片的是不是调用方那份字节、分块边界对不对"。
|
||
* 默认 0 → 既有"内容不可断言"的行为一字不变。 */
|
||
uint8_t write_cap_en;
|
||
uint8_t write_cap[CH395F_FAKE_WRITE_CAP]; /* 按写入顺序拼接的字节(够 2 块 1KB 的前 256B) */
|
||
uint16_t write_cap_n; /* 已捕获字节数(超出容量后只计数不存) */
|
||
uint32_t write_bytes; /* 芯片收到的总字节数(含未捕获部分) */
|
||
} ch395f_fake_ext_t;
|
||
|
||
typedef struct {
|
||
int exist_calls;
|
||
int reset_calls;
|
||
int init_calls;
|
||
int set_ip_calls;
|
||
int set_gw_calls;
|
||
int set_mask_calls;
|
||
int set_mac_calls;
|
||
int set_dhcp_calls;
|
||
int ping_calls;
|
||
int set_fun_para_calls;
|
||
int set_mss_calls;
|
||
int set_retrans_calls;
|
||
int set_ka_calls;
|
||
int set_send_buf_calls;
|
||
int set_recv_buf_calls;
|
||
int set_des_ip_calls;
|
||
int set_des_port_calls;
|
||
int connect_calls;
|
||
int glob_calls; /* 读全局中断次数(B3:除 net_poll 外应为 0)*/
|
||
int glob_bits; /* 最近一次读到的 16 位全局位图 */
|
||
int sock_events_calls; /* ch395f_sock_events(只碰本 Socket 的护栏原语)*/
|
||
int rst_begin_calls;
|
||
int rst_begin_reject; /* begin 被拒次数(复位机已在运行)*/
|
||
int rst_poll_calls;
|
||
int rst_done[CH395F_FAKE_SOCK_MAX]; /* 复位机完成计数 */
|
||
int rst_fail[CH395F_FAKE_SOCK_MAX]; /* 复位机失败计数 */
|
||
int cmd_done_calls;
|
||
uint8_t last_cmd_done_what;
|
||
int rst_close_calls; /* 复位机内部发出的 CLOSE 命令(含二段补发)*/
|
||
int rst_rx_discard_calls; /* 复位机 RX 排空事务次数 */
|
||
} ch395f_fake_ext_obs_t;
|
||
|
||
extern ch395f_fake_ext_t g_fx;
|
||
extern ch395f_fake_ext_obs_t g_fxchip;
|
||
|
||
/* 由 ch395f_fake_reset() 自动调用;单独暴露是为了让测试能只清扩展面 */
|
||
void ch395f_fake_ext_reset(void);
|
||
void ch395f_fake_push_glob(uint16_t v); /* 预置全局中断返回序列 */
|
||
void ch395f_fake_set_sint_sticky(uint8_t sock, uint8_t mask);
|
||
/* 预置 GET_RECV_BUF 交付的字节(n=0 撤销预置);p 可为 NULL(表示填 n 个 0xA5) */
|
||
void ch395f_fake_set_recv_bytes(const uint8_t *p, uint16_t n);
|
||
/* 同上并**开启流式读**(游标归零):UDP 用例要的是"头 8 字节 + 载荷"两次读
|
||
* 各自拿到正确片段,而不是每次都从第 0 字节重交。 */
|
||
void ch395f_fake_set_recv_stream(const uint8_t *p, uint16_t n);
|
||
/* 开启 WRITE_SEND_BUF 内容捕获(g_fx.write_cap / write_cap_n / write_bytes):
|
||
* 默认关 → 既有"内容不可断言"的行为一字不变。 */
|
||
void ch395f_fake_enable_write_capture(void);
|
||
/* 顺序断言辅助:清空调用日志 / 找某个短名第一次出现的位置(-1=没调过)。
|
||
* 刻意提供"清零日志",否则一次长轮询(如 sock_reset 的几十轮 'x')会把 96 格
|
||
* 环形外的顺序信息挤掉,让后续场景的顺序断言失真。 */
|
||
void ch395f_fake_clear_log(void);
|
||
int ch395f_fake_log_find(char c);
|
||
|
||
/* —— 新增假芯片实现(签名与真实 ch395f.h 一字不改)—— */
|
||
ch395f_status_t ch395f_check_exist(void);
|
||
ch395f_status_t ch395f_reset(void);
|
||
ch395f_status_t ch395f_init(void);
|
||
uint8_t ch395f_get_version(void);
|
||
void ch395f_set_fun_para(uint32_t para);
|
||
void ch395f_set_tcp_mss(uint16_t mss);
|
||
void ch395f_set_retrans_count(uint8_t cnt);
|
||
void ch395f_set_retrans_period(uint16_t period);
|
||
void ch395f_set_keepalive_idle(uint32_t time);
|
||
void ch395f_set_keepalive_intvl(uint32_t time);
|
||
void ch395f_set_keepalive_cnt(uint8_t cnt);
|
||
void ch395f_set_dhcp(uint8_t enable);
|
||
void ch395f_ping_enable(uint8_t enable);
|
||
void ch395f_set_ip_addr(uint8_t *p_ip);
|
||
void ch395f_set_gwip_addr(uint8_t *p_gwip);
|
||
void ch395f_set_mask_addr(uint8_t *p_mask);
|
||
void ch395f_set_mac_addr(uint8_t *p_mac);
|
||
void ch395f_set_send_buf(uint8_t sock, uint8_t start_block, uint8_t block_count);
|
||
void ch395f_set_recv_buf(uint8_t sock, uint8_t start_block, uint8_t block_count);
|
||
void ch395f_set_des_ip(uint8_t sock, uint8_t *p_ip);
|
||
void ch395f_set_des_port(uint8_t sock, uint16_t port);
|
||
uint8_t ch395f_tcp_connect(uint8_t sock);
|
||
uint8_t ch395f_get_phy_status(void);
|
||
uint16_t ch395f_get_glob_int_status_all(void);
|
||
void ch395f_glob_event_feed(uint16_t glob_bits);
|
||
uint8_t ch395f_sock_events(uint8_t sn);
|
||
int ch395f_sock_reset_begin(uint8_t sn, ch395f_reset_mode_t mode);
|
||
int ch395f_sock_reset_poll(uint8_t sn);
|
||
int ch395f_cmd_done(uint8_t sn, ch395f_done_what_t what);
|
||
|
||
#endif /* CH395F_FAKE_H */
|