W5波+N-19收口:FTP鉴权落地、Trap25三连定案修复(N-30)、N-20/N-23/N-26

N-19 FTP 鉴权:
- lftpd_auth(USER/PASS 状态机 + 连续5次失败锁60s)+ SHA-256
  (user||pwd) 口令哈希;config.ini [ftp] 段 user/pwd_hash/write,
  出厂 admin/admin 在用有启动告警;lftpd_log.c 删除并入 lftpd_auth
- ftp_cfg 配置读写;台架 ftp_auth_bench.py 13/13

N-30 / Trap 25(CH395F_Trap_Records.md 四次定案全记录):
- 修复1 closed-with-data accept:客户端发完正常 FIN 后芯片按
  FUN_PARA(0x09) §5.52② 保留 Socket+RX 缓冲,旧代码把 CLOSED 一律
  relisten 自毁缓冲 → STOR 必挂 425;改为接受"已关但可读"连接排空
- 修复2 丢 CONNECT 中断探针 net_lost_connect_probe + last_peer_port
  幽灵甄别(拆链窗口握手完成但中断丢失,双信源去抖补 accept)
- 修复3 send_list/send_nlst 批量单发:背靠背 ~60B 小段毒化芯片 TX
  (芯片报 SEND_OK 但对端全丢),整份目录一次性写入
- 证伪存档:固定沉降/Socket 轮换冷却/listen 门控(台架 v7 矩阵)
- 台架工具 test/board/ch395f_reuse_bench.{c,h} + host 脚本存档
  (不注册编译;实验分支 n30-chip-bench 保留)

N-20:Error_Handler 死循环降级(记录+复位路径)
N-23:scripts/check_registration.py 双端注册反向校验(pre-commit)
N-26:GD5F NAND 直 DMA 调用方缓冲窗口断言

验证:BUILD_OK 0/0;ftp_regression 连跑 4 轮 11/11(修复前
LIST#4/#7 100% 挂、STOR#2 曾 100% 425);ftp_auth_bench 13/13;
@test 启动标记 PASS;ci_local L0+L0.5 全过;doc-links 通过。
登记簿 N-30 已销项。
This commit is contained in:
2026-10-11 18:50:24 +08:00
parent eddbedeee8
commit d1e6bb4ba7
75 changed files with 3928 additions and 352 deletions
+373
View File
@@ -0,0 +1,373 @@
/**
* @file ch395f_reuse_bench.c
* @brief N-30 芯片级 Socket 复用时序台架(裸机,内核前运行,OS/net/FTP 全剥离)
* @details 直接调用 ch395f_* 命令,循环执行受控时序矩阵(延迟/关闭次序/载荷/Socket 号),
* 每步打印芯片状态与中断寄存器,定位"关→开→监听→连接"复用时序中
* 芯片行为的确切规律。配套主机脚本 test/host/ch395f_reuse_host.py。
* 定案结论与实验矩阵演进 → CH395F_Trap_Records.md Trap 25。
*
* 存档状态:本文件在主分支**不注册编译**(未加入 uvprojx),仅作实验工具存档;
* 运行方法:在实验分支(如 n30-chip-bench)把本 .c 注册进 uvprojx Test 组、
* Src/main.c 的 USER CODE 2 区在 app_main_init() 之前调 ch395f_reuse_bench()
* (内核前裸机循环,永不返回,断电退出),烧录后跑主机脚本对照芯片日志。
* 当前矩阵为定案轮(对照 vs "丢 CONNECT 捕获自 accept"修复组),见 s_vars[]。
*
* close_mode:0=主机先关(芯片等 DISCONNECT);1=芯片先关;2=无数据连上即关
* verify:0=无修复;1=listen 后确认 LISTEN 重试;2=缓冲重分配;
* 3=幽灵耐心等;4=等 CONNECT 期轮询状态+自 accept(定案修复)
*/
#include "ch395f_reuse_bench.h"
#if N30_CHIP_BENCH
#include "ch395f.h"
#include "dbg_log.h"
#include "stm32f4xx_hal.h"
/* 变体表:{sock, close 后复开延迟 ms, TX 字节数, close_mode, verify_listen} */
typedef struct {
uint8_t sock;
uint16_t delay_ms;
uint16_t tx_len;
uint8_t close_mode;
uint8_t verify; /* 1 = listen 后确认 TCP==0x01,否则 close 重试(修复假设) */
} bench_var_t;
static const bench_var_t s_vars[] = {
{ 3, 0, 100, 0, 0 }, /* 0ms 对照 */
{ 3, 300, 100, 0, 0 }, /* 300ms 对照 */
{ 3, 800, 2000, 0, 0 }, /* 2KB 对照 */
{ 3, 0, 100, 0, 4 }, /* 0ms + 丢CONNECT捕获自accept */
{ 3, 300, 100, 0, 4 }, /* 300ms + 丢CONNECT捕获 */
{ 3, 800, 2000, 0, 4 }, /* 2KB + 丢CONNECT捕获 */
};
#define BENCH_N_VARS (sizeof(s_vars) / sizeof(s_vars[0]))
#define BENCH_PORT(i) (5000U + (uint16_t)(i))
/* TX 图案缓冲(静态,SRAM;2KB = 最大变体载荷) */
static uint8_t s_tx_pat[2048];
/* RX 排空缓冲 */
static uint8_t s_rx_buf[2048];
/*
* 函数功能:轮询本 Socket 中断(读全局→读本 Socket,读即清)
* 入口参数:sock - Socket 号 uint8_t
* timeout - 超时 ms uint32_t
* out_bits - 输出最后一次读到的 SINT 位 uint8_t *
* 返回值:1=拿到中断 0=超时 int
*/
static int bench_wait_sock_int(uint8_t sock, uint32_t timeout, uint8_t *out_bits) {
uint32_t t0 = HAL_GetTick();
while ((uint32_t)(HAL_GetTick() - t0) < timeout) {
uint16_t glob = ch395f_get_glob_int_status_all();
if ((glob & (1U << (sock + 4U))) != 0U) {
uint8_t si = ch395f_get_sock_int_status(sock);
if (out_bits != 0) {
*out_bits = si;
}
return 1;
}
HAL_Delay(10);
}
return 0;
}
/* 打印芯片两字节状态(Socket 态 / TCP 态) */
static void bench_log_state(const char *tag, uint8_t sock) {
uint8_t st[2] = { 0, 0 };
ch395f_get_socket_status(sock, st);
DBG_INFO("BENCH %s sock%u st=0x%02X/0x%02X t=%u", tag, (unsigned)sock,
st[0], st[1], (unsigned)HAL_GetTick());
}
/* 芯片级初始化(复刻 net_init 序列,剥离全部内核 API) */
static int bench_chip_init(void) {
if (ch395f_check_exist() != CH395F_STATUS_OK) {
DBG_ERROR("BENCH chip not found");
return -1;
}
ch395f_reset();
/* 等 PHY link(Trap 09:link up 前 open 必失败) */
for (int i = 0; i < 100; i++) {
if (ch395f_get_phy_status() != 0U) {
break;
}
HAL_Delay(100);
}
ch395f_set_fun_para(0x09);
ch395f_set_tcp_mss(CH395F_TCP_MSS);
ch395f_set_retrans_count(3);
ch395f_set_retrans_period(200);
ch395f_set_keepalive_idle(60000);
ch395f_set_keepalive_intvl(5000);
ch395f_set_keepalive_cnt(3);
/* 静态 IP(与生产一致) */
{
uint8_t ip[4] = { 192, 168, 1, 100 };
uint8_t gw[4] = { 192, 168, 1, 1 };
uint8_t msk[4] = { 255, 255, 255, 0 };
ch395f_set_ip_addr(ip);
ch395f_set_gwip_addr(gw);
ch395f_set_mask_addr(msk);
}
if (ch395f_init() != CH395F_STATUS_OK) {
DBG_ERROR("BENCH ch395f_init failed");
return -1;
}
/* 8 Socket 缓冲布局(与生产一致) */
for (int i = 7; i >= 0; i--) {
uint8_t recv_start = (uint8_t)(i * 6);
ch395f_set_send_buf((uint8_t)i, (uint8_t)(recv_start + 4), 2);
ch395f_set_recv_buf((uint8_t)i, recv_start, 4);
}
ch395f_ping_enable(1);
return 0;
}
/* 单轮实验 */
static void bench_round(unsigned idx) {
const bench_var_t *v = &s_vars[idx];
uint8_t sock = v->sock;
uint16_t port = BENCH_PORT(idx);
uint8_t si = 0;
/* 幽灵等待期已见 CONNECT 的标志(跳过后续 CONNECT 等待) */
int pre_connected = 0;
DBG_INFO("BENCH === r%u sock%u port%u delay%u tx%u mode%u t=%u",
idx, (unsigned)sock, (unsigned)port, (unsigned)v->delay_ms,
(unsigned)v->tx_len, (unsigned)v->close_mode, (unsigned)HAL_GetTick());
/* 1. open + listen(Trap 09:close 后需延时重开,失败重试 ×3) */
ch395f_set_proto_type(sock, CH395F_PROTO_TYPE_TCP);
ch395f_set_sour_port(sock, port);
uint8_t ost = CH395F_ERR_SUCCESS;
for (int open_try = 0; open_try < 3; open_try++) {
ost = ch395f_open_socket(sock);
if (ost == CH395F_ERR_SUCCESS) {
break;
}
DBG_INFO("BENCH open retry%d err=0x%02X t=%u", open_try, ost, (unsigned)HAL_GetTick());
ch395f_close_socket(sock);
HAL_Delay(500);
}
if (ost != CH395F_ERR_SUCCESS) {
DBG_ERROR("BENCH open fail 0x%02X", ost);
return;
}
uint8_t lst = ch395f_tcp_listen(sock);
if (lst != CH395F_ERR_SUCCESS) {
DBG_ERROR("BENCH listen fail 0x%02X", lst);
ch395f_close_socket(sock);
return;
}
/* 缓冲重分配变体:listen 后重设收发缓冲(隔离"缓冲管理残留"假设) */
if (v->verify == 2U) {
uint8_t recv_start = (uint8_t)(sock * 6);
ch395f_clear_recv_buf(sock);
ch395f_set_send_buf(sock, (uint8_t)(recv_start + 4), 2);
ch395f_set_recv_buf(sock, recv_start, 4);
DBG_INFO("BENCH realloc done sock%u t=%u", (unsigned)sock, (unsigned)HAL_GetTick());
}
/* 清残留中断(与生产 listen 后一致) */
for (int j = 0; j < 8; j++) {
(void)ch395f_get_sock_int_status((uint8_t)j);
}
(void)ch395f_get_glob_int_status_all();
bench_log_state("post-listen", sock);
/* 修复假设验证:listen 后 TCP 态必须真正 == 0x01(LISTEN);
* mode1:close 快速重试;mode3:耐心等幽灵消退(5s,每 100ms 复查),
* 期间幽灵→LISTEN 转变会补发 CONNECT 中断则直接放行 */
if (v->verify == 1U || v->verify == 3U) {
uint32_t budget = (v->verify == 3U) ? 5000U : 1000U;
int att_max = (v->verify == 3U) ? 2 : 4;
for (int att = 0; att < att_max; att++) {
uint8_t st[2] = { 0, 0 };
uint32_t tw = HAL_GetTick();
int connect_fired = 0;
/* 等 TCP 进 LISTEN(幽灵消退)或 CONNECT 中断补发 */
while ((uint32_t)(HAL_GetTick() - tw) < budget) {
uint16_t glob = ch395f_get_glob_int_status_all();
if ((glob & (1U << (sock + 4U))) != 0U) {
uint8_t si = ch395f_get_sock_int_status(sock);
if ((si & CH395F_SINT_STAT_CONNECT) != 0U) {
connect_fired = 1;
DBG_INFO("BENCH verify CONNECT during ghost-wait att%d t=%u",
att, (unsigned)HAL_GetTick());
break;
}
}
ch395f_get_socket_status(sock, st);
if (st[1] == 0x01U) {
break;
}
HAL_Delay(100);
}
DBG_INFO("BENCH verify att%d st=0x%02X/0x%02X cf=%d t=%u",
att, st[0], st[1], connect_fired, (unsigned)HAL_GetTick());
if (connect_fired) {
/* 幽灵期主机已连上:跳过 CONNECT 等待直接进流程 */
pre_connected = 1;
break;
}
if (st[1] == 0x01U) {
break;
}
/* 幽灵未消:close → 50ms → 重新 open+listen(mode3 只试一次) */
ch395f_close_socket(sock);
HAL_Delay(50);
ch395f_open_socket(sock);
ch395f_tcp_listen(sock);
for (int j = 0; j < 8; j++) {
(void)ch395f_get_sock_int_status((uint8_t)j);
}
(void)ch395f_get_glob_int_status_all();
}
}
/* 2. 等 CONNECT(10s;幽灵等待期已见 CONNECT 则跳过)
* v6 关键探针:等待期每 500ms 读芯片状态;若已 ESTABLISHED(0x04)
* 说明握手完成但 CONNECT 中断丢失 → 补做 accept(对账②模拟) */
int got = pre_connected;
uint32_t t0 = HAL_GetTick();
while (!got && (uint32_t)(HAL_GetTick() - t0) < 10000U) {
if (bench_wait_sock_int(sock, 100, &si)) {
if ((si & CH395F_SINT_STAT_CONNECT) != 0U) {
got = 1;
break;
}
DBG_INFO("BENCH unexpected int=0x%02X while wait", si);
}
if (v->verify == 4U) {
uint8_t st[2] = { 0, 0 };
ch395f_get_socket_status(sock, st);
if ((uint32_t)(HAL_GetTick() - t0) % 1000U < 110U) {
DBG_INFO("BENCH wait-state st=0x%02X/0x%02X t=%u",
st[0], st[1], (unsigned)HAL_GetTick());
}
if (st[1] == 0x04U) {
DBG_INFO("BENCH LOST-CONNECT caught: state ESTABLISHED, self-accept t=%u",
(unsigned)HAL_GetTick());
got = 1;
break;
}
}
}
if (!got) {
DBG_ERROR("BENCH no CONNECT within 10s (host not synced?)");
ch395f_close_socket(sock);
HAL_Delay(v->delay_ms);
return;
}
bench_log_state("post-accept", sock);
/* 对端信息 + RX 残渣检查(旧连接残留的直接证据) */
{
uint8_t rem[6] = { 0 };
ch395f_get_remot_ipp(sock, rem);
uint16_t rl = ch395f_get_recv_len(sock);
DBG_INFO("BENCH remote port=%u recv_len=%u",
(unsigned)(rem[4] | (rem[5] << 8)), (unsigned)rl);
if (rl > 0U) {
uint16_t n = (rl > 2048U) ? 2048U : rl;
ch395f_read_recv_buf(sock, s_rx_buf, n);
DBG_INFO("BENCH drained %u residue bytes", (unsigned)n);
}
}
/* 3. TX 探针(1KB 分块,Trap 20) */
if (v->tx_len > 0U) {
uint16_t sent = 0;
while (sent < v->tx_len) {
uint16_t chunk = (uint16_t)(v->tx_len - sent);
if (chunk > 1024U) {
chunk = 1024U;
}
ch395f_write_send_buf(sock, &s_tx_pat[sent], chunk);
sent += chunk;
/* 等 SENBUF_FREE(500ms) */
if (sent < v->tx_len) {
uint32_t tw = HAL_GetTick();
while ((uint32_t)(HAL_GetTick() - tw) < 500U) {
if (bench_wait_sock_int(sock, 50, &si)) {
if ((si & CH395F_SINT_STAT_SENBUF_FREE) != 0U) {
break;
}
}
}
}
}
DBG_INFO("BENCH tx %u written t=%u", (unsigned)v->tx_len, (unsigned)HAL_GetTick());
/* 连接存活观察窗:500ms 内每 50ms 记状态+中断(抓"秒杀") */
for (int k = 0; k < 10; k++) {
uint16_t glob = ch395f_get_glob_int_status_all();
if ((glob & (1U << (sock + 4U))) != 0U) {
si = ch395f_get_sock_int_status(sock);
DBG_INFO("BENCH int during window=0x%02X t=%u", si, (unsigned)HAL_GetTick());
}
HAL_Delay(50);
bench_log_state("window", sock);
}
}
/* 4. 关闭次序 */
if (v->close_mode == 1U) {
/* 芯片先关 */
HAL_Delay(300);
ch395f_close_socket(sock);
DBG_INFO("BENCH chip closed first t=%u", (unsigned)HAL_GetTick());
} else {
/* 等主机关(DISCONNECT 事件,5s) */
uint32_t tw = HAL_GetTick();
int disc = 0;
while ((uint32_t)(HAL_GetTick() - tw) < 5000U) {
if (bench_wait_sock_int(sock, 100, &si)) {
if ((si & (CH395F_SINT_STAT_DISCONNECT | CH395F_SINT_STAT_SOCK_TIMEOUT)) != 0U) {
disc = 1;
break;
}
}
}
DBG_INFO("BENCH host-close seen=%d t=%u", disc, (unsigned)HAL_GetTick());
ch395f_close_socket(sock);
}
/* 5. 关闭后拆链观察:1s 内每 50ms 记状态 + 中断(Trap 14 滞后中断取证) */
for (int k = 0; k < 20; k++) {
uint16_t glob = ch395f_get_glob_int_status_all();
uint8_t late = 0;
if ((glob & (1U << (sock + 4U))) != 0U) {
late = ch395f_get_sock_int_status(sock);
}
if (late != 0U) {
DBG_INFO("BENCH late-int=0x%02X t=%u", late, (unsigned)HAL_GetTick());
}
if ((k % 4) == 0) {
bench_log_state("post-close", sock);
}
HAL_Delay(50);
}
/* 6. 受控延迟后进入下一轮 */
HAL_Delay(v->delay_ms);
}
void ch395f_reuse_bench(void) {
DBG_INFO("BENCH ===== N30 chip-level reuse bench START =====");
if (bench_chip_init() != 0) {
DBG_ERROR("BENCH init failed, giving up (normal boot continues)");
return;
}
DBG_INFO("BENCH chip ready, cycling %u variants forever", (unsigned)BENCH_N_VARS);
for (unsigned i = 0; i < 2048U; i++) {
s_tx_pat[i] = (uint8_t)(0xA5U + (i % 251U));
}
unsigned idx = 0;
for (;;) {
bench_round(idx);
idx = (idx + 1U) % (unsigned)BENCH_N_VARS;
}
}
#endif /* N30_CHIP_BENCH */
+13
View File
@@ -0,0 +1,13 @@
/*
* ch395f_reuse_bench.h - N-30 芯片级 Socket 复用时序台架(裸机实验,临时分支专用)
*/
#ifndef CH395F_REUSE_BENCH_H
#define CH395F_REUSE_BENCH_H
/* 实验总开关:1 = 内核前进入裸机台架(OS 永不启动,断电退出) */
#define N30_CHIP_BENCH 1
void ch395f_reuse_bench(void);
#endif /* CH395F_REUSE_BENCH_H */