Skip to content

feat(aic8800): AIC8800DC SoftAP for SG2002 — boot AP, SSH + HTTP, client reconnect#1318

Merged
ZR233 merged 10 commits into
rcore-os:devfrom
BattiestStone4:feat/aic8800dc-port
Jun 22, 2026
Merged

feat(aic8800): AIC8800DC SoftAP for SG2002 — boot AP, SSH + HTTP, client reconnect#1318
ZR233 merged 10 commits into
rcore-os:devfrom
BattiestStone4:feat/aic8800dc-port

Conversation

@BattiestStone4

@BattiestStone4 BattiestStone4 commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

概述

在已合入的 AIC8800 SoftAP(#1185)与 D80 模式切换/EAPOL 修复(#1266#1276)基础上,把 SoftAP 路径移植并稳定到 AIC8800DC(DC-H)芯片 + CV1800 SDIO host。真机(LicheeRV Nano / SG2002)上 boot AP 可广播、客户端可关联、数据通(SSH + HTTP 均验证),且客户端断开后可重连。

DC 与已支持的 D80/8801 的关键差异在固件 bring-up(固件常驻 ROM、分步打 patch、RF 校准)与 SDIO 邮箱路由(命令走 func2、数据走 func1),握手层与变体无关。

改动内容

1. DC bring-up 与数据面路由(boot AP 可用)

  • TX 数据面(MGMT + DATA 802.11 帧)路由到 func1(data_func),而非 func2。func2 仅命令/CFM,func1 是 TX/RX 的数据平面——此前走错 function 导致客户端永远无法关联。
  • MGMT 帧字段修正:两个帧构造器 byte[2]=0x01、ac=3(VO)。
  • AIC8800DC 固件镜像/初始化路径(fw/firmware/dc.rs + chip variant)。

2. SoftAP 稳定性(SSH + HTTP + 重连)

  • TX poll kicker:wifi-tx 是边沿触发 poll 任务,在 tx_process 之后才注册 waker;落在该窗口的 wake() 会丢失(PollSet 无 sticky),叠加 pending_flag 队头阻塞会冻结 TX 数秒。RX 早有 10ms kicker 兜底,此处为 TX 补上对称的,使丢失的唤醒 ~10ms 内被救醒。
  • ME_STA_ADD 去重 + 控制端口自愈:固件对已注册 MAC 的重复 ME_STA_ADD 不回 CFM,手机重传 AssocReq 会让 AP worker 阻塞 5s 超时。改为按 (mac, sta_idx, ctrl_port_open) 跟踪 STA,已知 MAC 跳过 ME_STA_ADD;控制端口在重关联时重试直到真正成功。
  • Deauth/Disassoc 处理 + MM_STA_DEL 槽位回收:断开的 STA 从去重表移除并发 MM_STA_DEL_REQ 释放固件槽位,使重连能复用低位 sta_idx(DC 下行单一 sta_idx 路由只对低位槽位可靠)。
  • 热路径逐帧日志 info→debug:单核协作调度下每条 info 日志是一次阻塞式 UART 写,Auth 洪泛时会饿死 TX/kicker 线程;降级后关联明显变快。

3. 芯片变体门控(回归护栏)

  • SdioTransport 恢复保留完整 ChipVariant(此前被塌缩成 is_v3/cmd_func 两个派生 bool),新增语义化 is_dual_pipe() 谓词。
  • 上述 §2 的 5 项 AP 生命周期改动(STA 去重 / MM_STA_DEL 队列 / 控制端口周期对账 / TX·RX poll kicker / Deauth 清表)全部门控到仅 DC/DW 启用。D80/8801 走与 upstream/dev 逐字节一致的原有路径(已对照 baseline handle_assoc_req 核验),不受 DC 适配影响。

4. style

  • cargo fmt + clippy 干净,无功能改动。

测试

真机验证:

  • AIC8800DC(LicheeRV Nano / SG2002):boot AP 广播、客户端(iPhone)快速关联、SSH + HTTP 互通、断开后重连、AP↔STA 运行时模式切换(wifi_switch sta → WPA2 关联 + DHCP 获址联网)
  • AIC8801(LicheeRV Nano):boot AP + 客户端关联 + ping + SSH 全通,门控后无回归(走 baseline 路径)
  • aic8800 crate 对 dev 顶端编译通过,cargo fmt --checkcargo clippy -D warnings 均通过

已知问题 / 待核实

  • AIC8801 在某第三方板上 SSH 大包阶段会卡,已定位为该板 boot firmware(fip.bin)阶段的 SDIO 引脚/PHY 配置问题,与本 PR 的内核改动无关:① SDIO 时钟写死在驱动且恒为 25MHz(不读 DTB);② sdhci-cv1800 驱动本 PR 零改动;③ probe 时 dump 的 PHY/MSHC 寄存器在"能跑的板"与"卡的板"上逐位相同——差异在内核之外的 fip 阶段。该第三方板属新硬件 bringup,不在本 PR 回归范围。
  • D80 暂无板实测;通过 §3 芯片门控使其走 upstream/dev 已验证路径作为护栏。
  • AP 多客户端能力受既有 aid=1 + 全局 sta_idx 设计限制(改动前即存在,本 PR 未改善也未恶化)。

备注

  • AIC8800DC 的运行时 AP↔STA 模式切换本 PR 已验证可用(见测试)。

Port the AIC8800DC firmware/driver path so the boot-time SoftAP brings up,
broadcasts, accepts client association, and carries data (SSH verified) on
the LicheeRV Nano (SG2002).

Key fixes that made the AP functional:
- TX data-plane (MGMT + DATA 802.11 frames) routed on func1 (data_func),
  not func2 (command mailbox). func2 is command/CFM only, func1 is the
  data plane for both TX and RX. This was why no client could ever associate.
- MGMT frame field fixes: byte[2]=0x01 on both frame builders, ac=3 (VO).
- AIC8800DC firmware image/init path (fw/firmware/dc.rs + chip variant).
- Diagnostic logging at info for CMD sent / CFM received / MGMT sent, to
  make the command->CFM round-trip observable on hardware.

Runtime AP<->STA mode switching is not yet functional and is tracked
separately; the boot AP path is the verified milestone here.
Builds on the boot-AP milestone (6518369) to make the AIC8800DC SoftAP
reliable enough for sustained client traffic and reconnection. Verified on
SG2002 (LicheeRV Nano): client associates fast, SSH and HTTP both work, and
a client can reconnect after disconnecting.

Four independent fixes, each rooted in an observed failure on real hardware:

- TX poll kicker (tx.rs): wifi-tx is an edge-triggered poll task that
  registers its waker after tx_process; a wake() landing in that window is
  lost (PollSet has no sticky bit) and, combined with the pending_flag
  head-of-line block, freezes all TX for seconds. RX already had a 10ms
  kicker for the same hazard; add the symmetric one for TX so any lost wake
  is recovered within ~10ms.

- ME_STA_ADD dedup with control-port self-heal (ap.rs, bus.rs): the firmware
  does not CFM a repeated ME_STA_ADD for an already-registered MAC, so a
  phone retransmitting AssocReq made the AP worker block 5s and timeout.
  Track registered STAs as (mac, sta_idx, ctrl_port_open); skip ME_STA_ADD
  for known MACs, and retry opening the control port on re-assoc until it
  actually succeeds (a timed-out control-port open previously left data
  frames dropped by the firmware → no ping/SSH).

- Deauth/Disassoc handling (rx.rs): these were silently ignored, so a
  disconnected STA stayed in the dedup table and reconnects were skipped
  entirely. Remove the MAC from the table on 0xC/0xA so a reconnect fully
  re-registers.

- Demote per-frame hot-path logs info→debug (rx.rs, tx.rs): on the single-
  core cooperative scheduler each info log is a blocking UART write; during
  an Auth flood these starve the TX/kicker tasks. Demoting them markedly
  speeds up association. Per-connection events (registered, control port
  OPENED, deauth) stay at info.
- cargo fmt across the AIC8800DC SoftAP changes (import wrapping, multi-line
  log argument formatting).
- Replace `kicks % 100 == 0` with `kicks.is_multiple_of(100)` in the RX poll
  kicker to satisfy clippy::manual_is_multiple_of under -D warnings.

No functional change.

@mai-team-app mai-team-app Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review — PR #1318 (AIC8800DC SoftAP)

整体方向清晰,DC 固件 bring-up 和 SoftAP 稳定性改进的思路合理,代码注释充分。以下是阻塞和非阻塞发现。

🔴 阻塞问题

1. 与 dev 分支存在合并冲突

PR 当前 mergeStateStatus=dirty,3 个文件与 dev 有冲突:

  • components/aic8800/src/fdrv/protocol/cmd.rs
  • components/aic8800/src/fdrv/thread/rx.rs
  • components/aic8800/src/fdrv/thread/tx.rs

请 rebase 到最新 dev 顶端后解决冲突。

2. DIAG 诊断日志不应使用 log::info!

init.rspoll_for_response 添加了每 500 次重试扫描 func1/func2 block_cnt 的 [DIAG] 日志,级别为 info。单核协作调度下 info 级别每条都是一次阻塞式 UART 写入,会显著拖慢轮询路径。请降级为 log::debug!log::trace!。具体位置:

  • init.rs ~L178-192: poll_for_response 中的 [DIAG] retry=... 日志
  • init.rs ~L295-300: polling_send_cmd 中的 pre-send block_cnt f1/f2 日志

3. AIC8801/D80 回归风险未隔离

作者自己在 PR body 里提到 AIC8801 实测已有回归。以下改动无 ChipVariant 门控,对所有变体生效:

  • rx.rs: drain_func(bus, 2) + drain_func(bus, 1) — D80/8801 本来只读 func1,现在额外读 func2
  • tx.rs: byte[2]=0x01 变更、ac=3(VO) 变更
  • rx.rs: Auth/AssocResp 的 log 级别从 info→debug 对所有变体生效

建议对 DC-only 的行为差异加 matches!(chip, Aic8800DC | Aic8800DW) 门控,或至少在 PR body 的回归状态更新后再合入。

🟡 非阻塞建议

4. 生产环境诊断计数器

rx.rs 中的 RX_KICK_COUNTRX_POLL_COUNTRX_LAST_BLOCKCNTRX_NONZERO_READS 静态变量及其 trace 输出是调试用的。建议用 #[cfg(debug_assertions)] 包裹,或在确认 DC 稳定后移除。

5. registered_stas 无容量上限

ApState::registered_stasVec,只在 deauth/disassoc 时 remove。虽然当前 aid=1 限制实际不会增长太多,但建议加一个合理上限(如 16)或换用 BoundedVec

6. CI 无 check runs

此 PR 的 head SHA 9f23f98 没有任何 GitHub Actions check runs。请确认 CI 工作流是否正常触发。


总结: 方向正确、注释详尽,但需解决合并冲突并将 DIAG 日志降级后再合入。同时建议隔离对 D80/8801 的行为变更。

Powered by mimo-v2.5-pro

Comment thread components/aic8800/src/fdrv/core/init.rs
Comment thread components/aic8800/src/fdrv/core/init.rs
Comment thread components/aic8800/src/fdrv/thread/rx.rs
# Conflicts:
#	components/aic8800/src/fdrv/protocol/cmd.rs
#	components/aic8800/src/fdrv/thread/rx.rs
#	components/aic8800/src/fdrv/thread/tx.rs
After merging upstream/dev's func-agnostic RX refactor, process_rx_frames
drained both func2 and func1 unconditionally. Only DC/DW route the command
CFM/indication mailbox to func2; 8801/D80 keep command+data on func1, so
polling func2 there issued a wasted CMD52 every RX cycle. Gate the func2
drain on cmd_func()==2 to keep the three variants on their correct path.
…STA table

Review feedback from mai-team-app:
- init.rs: demote [DIAG] retry scan and cmd-send/pre-send block_cnt logs
  from info to debug/trace; gate the extra register reads behind
  log_enabled!(Trace) so the production polling path issues no extra CMD52.
- rx.rs: drop orphaned diagnostic counters (RX_POLL_COUNT /
  RX_LAST_BLOCKCNT / RX_NONZERO_READS); cfg(debug_assertions)-gate the
  remaining RX_KICK_COUNT heartbeat.
- ap.rs: bound registered_stas at MAX_REGISTERED_STAS (16) to prevent
  unbounded growth from AssocReq without matching deauth.
- tx.rs: mgmt-frame ac=VO is now gated to DC/DW (dual_pipe); 8801/D80
  keep vendor ac=BK to match upstream/dev and avoid regression. byte[2]
  reverted to the shared SDIO_TYPE_DATA constant (== 0x01 on both sides,
  so it was never a behavior change).

@mai-team-app mai-team-app Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

二次审查 — PR #1318 (AIC8800DC SoftAP)

本审查基于最新 head 928daa40a,已在本地完成代码审读和验证。

改动概述

将 SoftAP 路径移植到 AIC8800DC(DC-H) 芯片 + CV1800 SDIO host。主要改动:

  • SDIO 命令邮箱路由:DC/DW 命令走 func2,数据走 func1
  • TX poll kicker 修复边沿触发丢失唤醒问题
  • ME_STA_ADD 去重 + 控制端口自愈
  • Deauth/Disassoc 处理(从注册表移除使重连完整)
  • 热路径日志降级 info→debug
  • 新增 DC-H 专用固件及 RF 校准路径

🔴 阻塞问题

1. CI 失败:Run clippy / run_host 构建 panic

build.rs 在尝试下载 remote_path: "" 的固件时 panic:

failed to GET https://raw.githubusercontent.com/lxowalle/aic8800-sdio-firmware/c56f910044cc854d6c553bcb9a644f3bca5a4c38/: http status: 400

7 个新增固件文件(fmacfw_patch_8800dc_h_u02.binfmacfw_patch_tbl_8800dc_h_u02.binfmacfw_calib_8800dc_h_u02.bindc_ldpc_cfg_ram.bindc_agc_cfg_ram.bindc_txgain_map.bindc_txgain_map_h.bin)设置了 remote_path: "",注释说明为 "In-tree only"。但 components/aic8800/firmware/ 目录为空(仅有 .gitignoreREADME.md),导致 build script 尝试下载时拼出 URL 以 c8/ 结尾(commit hash + 空路径),HTTP 400 报错。

修复方向

  • 将 DC-H 固件二进制文件提交到 components/aic8800/firmware/ 目录;或
  • 为这些文件设置有效的 remote_path(上传到上游固件仓库);或
  • download() 函数中对空 remote_path 发出更明确的错误信息而非 panic。

上一次审查(mai-team-app[bot])当时此问题尚未出现(审查针对的是 9f23f98 旧 head),新 push 928daa40a 引入了这些 in-tree-only 文件但未提交实际固件。

2. 所有变体行为变更未隔离

作者在 PR body 中已承认 AIC8801 实测存在回归。以下改动对所有芯片变体生效,无 ChipVariant 门控:

  • rx.rsprocess_rx_framesdrain_func(bus, 2) — 对 D80/8801 多排空了一次 func2
  • tx.rs 中 MGMT 帧构造器的 byte[2]=0x01、ac=3(VO) 变更
  • rx.rs Auth/AssocResp 日志级别 info→debug 对所有变体生效

建议对 DC-only 的行为差异加 matches!(chip, Aic8800DC | Aic8800DW) 门控,或至少在 D80/8801 回归验证通过前不合并。

🟡 非阻塞建议

3. init.rs DIAG 诊断日志已降级为 trace

上次审查建议将 poll_for_response 中的寄存器扫描和 polling_send_cmd 的 pre-send 读数从 info 降级。当前代码已将 DIAG 日志改为 log::trace! 并加了 log::log_enabled! 守卫。✅ 已解决。

4. registered_stas 已添加容量上限

consts.rs 新增 MAX_REGISTERED_STAS: usize = 16ap.rs 中在注册前检查容量。✅ 上次建议已采纳。

5. 生产环境诊断计数器

rx.rs 中的 RX_KICK_COUNTRX_POLL_COUNT 等静态变量及 trace 输出已用 #[cfg(debug_assertions)] 包裹。✅ 已处理。

CI 状态分析

CI workflow 对当前 head 928daa40a 的检查结果:

  • Check formatting / run_host → SUCCESS ✅
  • Run sync-lint / run_container → SUCCESS ✅
  • Run clippy / run_host → FAILURE ❌(构建 panic,见上)
  • Test axvisor x86_64 svm hosted / run_host → SUCCESS ✅
  • 其余测试因 clippy 失败被 CANCELLED(预期行为)

本地验证:cargo fmt --check 通过 ✅。clippy 无法在本地完成验证(同样因 firmware 缺失触发 build.rs panic)。

重复/重叠分析

未发现其他 open PR 与此 PR 重叠。PR #1185#1266#1276 已合入 dev,是本 PR 的前置基础。

审查结论

请求变更:CI clippy 失败为 PR 直接导致的阻塞问题(firmware 文件缺失),且对所有变体的行为变更未隔离,作者已确认 AIC8801 存在回归。请修复 build.rs panic 后重新提交,并对 DC-only 差异加变体门控或完成 D80/8801 回归验证。

Powered by mimo-v2.5-pro

Comment thread components/aic8800/build.rs
Comment thread components/aic8800/src/fdrv/thread/rx.rs
Two AP-mode stability fixes for "associates + gets IP but ping/SSH dead":

1. MM_STA_DEL on disconnect. Downlink data frames route by a single global
   sta_idx (hostdesc staid); firmware delivers reliably only to low slots.
   The deauth/disassoc handler cleared the driver table but never sent
   MM_STA_DEL_REQ, so the firmware kept the old slot and the NEXT station
   got a higher sta_idx (0->1->2...) whose downlink never arrived — the
   client kept re-ARPing and ping/SSH failed. Now the RX handler enqueues
   the freed sta_idx (can't send_cmd on the RX thread — CFM is processed
   there, would deadlock; same reason AssocReq is queued), and the AP
   worker drains the del-queue BEFORE the assoc-queue so a reconnecting
   STA reuses the known-good low slot.

2. Control-port reconcile. Opening the control port (authorize) could
   time out under single-core UART starvation; the old code only retried
   on AssocReq re-transmission, which a settled client never sends, so the
   data plane stayed dead. The AP worker now periodically re-issues
   SET_CONTROL_PORT for any unauthorized STA (capped at
   CONTROL_PORT_MAX_RETRY) until it succeeds.

Still single-concurrent-STA by design (one global sta_idx, one DHCP lease).

@mai-team-app mai-team-app Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR #1318 Review — AIC8800DC SoftAP for SG2002

总体评价: PR 结构清晰、commit 历史规整(7 commits)、描述详尽,DC 固件 bring-up 和 SoftAP 稳定性修复的技术设计合理。之前两轮 inline 评论均已回应(DIAG 日志降级 ✅、func2 drain 门控 ✅)。以下是复审发现,有一项阻塞合入。


🔴 阻塞:AIC8801 回归未解决

PR body 已明确写到:

⚠️ AIC8801 上板实测:当前镜像无法连入 boot AP、无法模式切换……本 PR 改动均无 is_v3/ChipVariant 门控、对所有变体生效,故不排除影响;D80 回归状态待复测。合入前需在 D80/8801 上回归验证

这说明作者自己认为回归未解决。以下改动对所有芯片生效,任何一项都可能引起回归:

  • registered_stas 去重逻辑(ap.rs handle_assoc_req):D80/8801 固件是否也对重复 MAC 的 ME_STA_ADD 不回 CFM?如果不是,去重会跳过合法注册。
  • MM_STA_DEL_REQ(apm.rs):D80/8801 是否支持此消息?虽然 send_mm_sta_del_req 失败只 warn 不 panic,但未知固件行为可能引起副作用。
  • 控制端口对账(reconcile_control_ports):50ms 周期自唤醒 + CONTROL_PORT_MAX_RETRY=20,在 D80/8801 上的行为未验证。
  • TX/RX poll kicker:新增两个 10ms 周期任务,独占 ax_task 线程,在 D80/8801 上是否影响调度?

建议: 至少在 D80 上完成 SoftAP 回归测试后再合入。如果短期无法测试,建议对 registered_stas 去重、sta_del_queue 处理、控制端口对账等逻辑加 ChipVariant 门控(仅 DC/DW 启用),避免影响既有芯片。


🟡 建议:build.rs 空 remote_path 会 panic

DC-H 固件和 RF cfg blob 共 7 个文件的 remote_path 为空字符串:

FirmwareFile {
    name: "fmacfw_patch_8800dc_h_u02.bin",
    remote_path: "",  // ← download() 会拼出无效 URL 并 panic
    sha256: "e3257b...",
},

download() 函数构造 https://raw.githubusercontent.com/.../{remote_path},空路径会导致 404 并 panic!

当前 components/aic8800/firmware/ 目录为空(只有 .gitignoreREADME.md),这些文件不在 in-tree 目录中。如果 $AIC8800_FIRMWARE_DIR 也没设置,build 会直接失败。

建议: 在 download() 开头加 guard:

fn download(file: &FirmwareFile) -> Vec<u8> {
    if file.remote_path.is_empty() {
        panic!("firmware {} is in-tree only but not found in firmware/ dir or $AIC8800_FIRMWARE_DIR", file.name);
    }
    // ...
}

或者将 DC-H 固件文件提交到 in-tree firmware/ 目录。


🟢 代码质量小结

  1. registered_stas 用裸 tuple (mac, idx, ctrl_open, retries) 访问(ap.rs):e.2 = truee.3.saturating_add(1) 可读性差。建议改为命名 struct:

    struct RegisteredSta {
        mac: [u8; 6],
        sta_idx: u8,
        ctrl_open: bool,
        retries: u8,
    }

    bus.rs 中已有 pub type RegisteredSta = ([u8; 6], u8, bool, u8); 的 type alias,改为 struct 更安全。

  2. cmd_func 逻辑三处重复init.rs::cmd_func()SdioTransport::cmd_func()IpcTransport::cmd_func()。建议统一到一处(如 ChipVariant 上的方法),避免新增芯片变体时遗漏。

  3. MM_STA_DEL_REQ timeout=0(ap.rs send_mm_sta_del_req(&bus, idx, 0)):fire-and-forget 设计合理(避免 AP worker 阻塞),但建议加一行注释说明为何 timeout=0。

  4. data_func() 永远返回 1(sdio_transport.rs):对 DC/DW 正确(数据面在 func1),但建议加注释说明这是 by-design 而非临时 hardcode。


✅ 正面评价

  • DC 固件 bring-up 序列(dc.rs)移植自 vendor BSP,步骤完整、注释详尽
  • SDIO 双管道架构(cmd_func vs data_func)设计清晰
  • TX/RX poll kicker 解决 PollSet 无 sticky 的根本问题,对称且优雅
  • STA 去重 + 控制端口对账 + Deauth 清理构成完整的 AP 生命周期管理
  • 热路径日志降级(info→debug/trace)对单核调度性能有实质改善
  • PR 描述坦诚记录已知问题和回归风险

结论: request_changes。核心阻塞项是 AIC8801 回归(作者自己也标记为合入前必验)。建议先做 D80/8801 回归测试,或对新逻辑加芯片门控。build.rs 空路径问题建议一并修复。

Powered by mimo-v2.5-pro

CI `Run clippy / run_host` panicked in build.rs: the 7 AIC8800DC-H
firmware/RF-config blobs have remote_path "" (no upstream mirror — they're
extracted from the vendor BSP/rootfs), but firmware/.gitignore ignored
*.bin, so they were absent from a fresh checkout. build.rs then fell
through to download("") and hit HTTP 400.

- Allow-list the 7 in-tree-only blobs in firmware/.gitignore (downloadable
  blobs stay ignored and keep coming from the upstream mirror).
- Commit the blobs; their SHA-256 already match the digests in build.rs.
- Harden download(): assert remote_path is non-empty with an actionable
  message instead of emitting a confusing 400.

@mai-team-app mai-team-app Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

第四轮审查 — PR #1318 (AIC8800DC SoftAP for SG2002)

审查基于: head 0a501f8649 (最新 push,含 vendor firmware 修复)


改动概述

将 SoftAP 路径移植到 AIC8800DC(DC-H) + CV1800 SDIO host。核心改动:

  • SDIO 双管道架构:cmd_func/data_func 分离(DC/DW 命令走 func2,数据走 func1)
  • TX/RX poll kicker 修复边沿触发丢失唤醒
  • ME_STA_ADD 去重 + 控制端口自愈
  • Deauth/Disassoc 处理 + MM_STA_DEL_REQ
  • 热路径日志降级 info→debug/trace
  • DC-H 固件内嵌 + build.rs 空路径 assert

已解决的前轮问题 ✅

  1. DIAG 日志降级(review #1)→ 已改为 log::trace! + log::log_enabled! 守卫 ✅
  2. build.rs 空 remote_path panic(review #2)→ 已加 assert!(!file.remote_path.is_empty()) 并 vendor 固件二进制 ✅
  3. drain_func 门控(review #1/#2)→ 已用 if bus.transport.cmd_func() == 2 { drain_func(bus, 2); } 门控 ✅
  4. registered_stas 容量上限(review #2)→ 已加 MAX_REGISTERED_STAS = 16
  5. 诊断计数器(review #2)→ 已用 #[cfg(debug_assertions)] 包裹 ✅
  6. CMD/数据 FIFO func 路由 → 已用 transport.cmd_func() / transport.data_func()
  7. reinit_sdio_func DC 专用路径 → 已用 is_dc flag 门控 ✅

CI 状态

检查 结果
Check formatting / run_host ✅ SUCCESS
Run sync-lint / run_container ✅ SUCCESS
Run clippy / run_host ✅ SUCCESS
Test axvisor riscv64 qemu / run_host ✅ SUCCESS
Test axvisor self-hosted x86_64 UEFI / run_host ✅ SUCCESS
Test axvisor loongarch64 qemu / run_container ✅ SUCCESS
Test axvisor self-hosted board orangepi-5-plus-linux ✅ SUCCESS
Test arceos aarch64/x86_64/loongarch64 qemu ✅ SUCCESS
Test axvisor self-hosted x86_64 / run_host (smoke-vmx) ❌ FAILURE
其余 starry 测试 CANCELLED(被上者取消)

smoke-vmx 失败是 axvisor x86_64 VMX 测试,与 aic8800 WiFi 驱动无关联(PR 仅改 components/aic8800/),且同机 axvisor x86_64 UEFI 测试通过。判定为无关 CI 失败


🔴 阻塞:AIC8801/D80 回归未验证

PR body 已明确声明:

⚠️ AIC8801 上板实测:当前镜像无法连入 boot AP、无法模式切换……本 PR 改动均无 is_v3/ChipVariant 门控、对所有变体生效,故不排除影响;D80 回归状态待复测。合入前需在 D80/8801 上回归验证

经审读最新代码,以下改动对所有芯片变体生效且无 ChipVariant 门控:

  1. STA 注册去重(ap.rs handle_assoc_req):已注册 MAC 跳过 ME_STA_ADD。D80/8801 固件是否也对重复 MAC 不回 CFM?如果不是,去重会跳过合法重新注册,导致重连失败。

  2. MM_STA_DEL_REQ(apm.rs + ap.rs sta_del_queue):D80/8801 固件是否支持 MM_STA_DEL_REQ 消息?虽然失败仅 warn,但未知固件行为可能引起副作用(如固件内部状态不一致)。

  3. 控制端口对账(ap.rs reconcile_control_ports):50ms 周期自唤醒 + 最多 20 次重试。D80/8801 上的行为未验证,额外的周期性命令可能干扰已有正常流程。

  4. TX/RX poll kicker(tx.rs + rx.rs):各独占一个 ax_task 线程做 10ms 周期唤醒。D80/8801 上是否影响调度或造成不必要的 SDIO 竞争?

  5. Deauth/Disassoc 处理(rx.rs):新增 0xC/0xA 帧处理分支。

作者自己确认这是合入前必验项。当前无 D80/8801 回归测试证据。

建议方向(二选一):

  • 方案 A:在 D80 上完成 SoftAP 回归测试(boot AP → 客户端关联 → ping/SSH → 断开重连),提供日志证据。
  • 方案 B:对上述 1-5 项加 if matches!(chip, Aic8800DC | Aic8800DW) 门控,仅 DC/DW 启用新行为,D80/8801 走原有路径。方案 B 工作量小且安全。

🟢 代码质量评价

  • DC SDIO 双管道架构设计清晰,cmd_func/data_func 抽象合理
  • TX/RX poll kicker 解决 PollSet 无 sticky 的根本问题,对称优雅
  • STA 去重 + 控制端口对账 + Deauth 清理构成完整的 AP 生命周期管理
  • 固件 bring-up 序列(dc.rs)注释详尽
  • registered_stas 用裸 tuple (mac, idx, ctrl_open, retries) 可读性稍差,建议后续改为命名 struct

重复/重叠分析

未发现其他 open PR 与本 PR 重叠。PR #1185#1266#1276 已合入 dev,是本 PR 的前置基础。


结论:方向正确、代码质量好、前轮问题均已修复。请求变更:核心阻塞项是 AIC8801/D80 回归(作者自己标记为合入前必验)。建议先做 D80 回归测试,或对通用行为加芯片门控。

Powered by mimo-v2.5-pro

Comment thread components/aic8800/src/fdrv/thread/ap.rs
Comment thread components/aic8800/src/fdrv/thread/ap.rs
Comment thread components/aic8800/src/fdrv/thread/tx.rs
Reviewer flagged 5 AP-lifecycle changes from the DC port that applied to
all chip variants without gating, risking regressions on the existing
AIC8801/D80 support (which lacked re-verification).

Add ChipVariant back to SdioTransport (it was collapsed into is_v3/cmd_func
proxies) plus a semantic `is_dual_pipe()` predicate, and gate all 5 behind
it so D80/8801 follow the upstream/dev path verbatim:

1. STA registration dedup + registered_stas table maintenance
2. MM_STA_DEL_REQ slot reclaim queue
3. Periodic control-port reconciliation (50ms retry loop)
4. TX/RX poll kickers (10ms wakeup tasks)
5. Deauth/Disassoc de-registration

Non-DC branches verified byte-equivalent to baseline handle_assoc_req.

Verified on hardware:
- AIC8800DC (newboard): SoftAP + ping/SSH + AP<->STA mode switch OK
- AIC8801 (LicheeRV Nano): SoftAP + ping/SSH OK (no regression)
@BattiestStone4

Copy link
Copy Markdown
Contributor Author

已按方案 B 加芯片门控 + 回归验证 (commit f6fb19b)

感谢细致 review。核心阻塞项「AIC8801/D80 回归」已处理,采用建议的方案 B

门控落地

SdioTransport 恢复保留完整 ChipVariant(此前被塌缩成 is_v3/cmd_func),新增 is_dual_pipe() 谓词。点名的 5 项全部门控到仅 DC/DW:

# 改动 DC/DW D80/8801
1 STA 注册去重 + 注册表维护 启用 跳过(每次 ME_STA_ADD,不入表)
2 MM_STA_DEL 槽位回收 启用 队列恒空,跳过
3 控制端口周期对账 启用 关联时一次性 authorize
4 TX/RX poll kicker 启用 纯事件驱动
5 Deauth/Disassoc 清表 启用 早返回(不动 STA 表)

非 DC 分支已逐项与 baseline handle_assoc_req 核对,逐字节等价走 upstream/dev 路径。

回归验证(真机)

  • AIC8801(LicheeRV Nano):boot AP + 关联 + ping + SSH 全通,门控后无回归 ✅
  • AIC8800DC(newboard):SoftAP + ping/SSH + AP↔STA 模式切换(wifi_switch sta → WPA2 + DHCP 联网)全通 ✅
  • D80:暂无板实测,通过门控走已验证 dev 路径作为护栏

关于 8801 在第三方板的 SSH 卡顿

排查确认与本 PR 内核改动无关,属该板 fip.bin(boot firmware)阶段的 SDIO 引脚/PHY 配置:① SDIO 时钟写死且恒 25MHz(不读 DTB);② sdhci-cv1800 本 PR 零改动;③ probe 时 dump 的 PHY/MSHC 寄存器在能跑的板与卡的板上逐位相同 → 差异在内核之外。该板属新硬件 bringup,已从 PR body 的回归项移除。

CI

smoke-vmx 失败是 axvisor x86_64 VMX 测试(本 PR 仅改 components/aic8800/),同机 x86_64 UEFI 测试通过,判定为无关失败。

@mai-team-app mai-team-app Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

第五轮审查 — PR #1318 (AIC8800DC SoftAP for SG2002)

审查基于: head f6fb19b(最新 push)

改动概述

将 SoftAP 路径移植到 AIC8800DC(DC-H) + CV1800 SDIO host。核心改动:

  • SDIO 双管道架构:cmd_func/data_func 分离(DC/DW 命令走 func2,数据走 func1)
  • TX/RX poll kicker 修复边沿触发丢失唤醒
  • ME_STA_ADD 去重 + 控制端口自愈
  • Deauth/Disassoc 处理 + MM_STA_DEL_REQ 槽位回收
  • 热路径日志降级 info→debug/trace
  • DC-H 固件内嵌 + build.rs 空路径 assert
  • 所有 5 项 AP 生命周期改动全部用 is_dual_pipe() 门控到仅 DC/DW

前轮问题解决状态

问题 状态
合并冲突 ✅ 已解决
DIAG 日志降级 ✅ 改为 log::trace! + log::log_enabled! 守卫
build.rs 空 remote_path panic ✅ 加 assert! + 固件二进制已 vendor 到 in-tree
drain_func 门控 cmd_func() == 2 门控
registered_stas 容量上限 MAX_REGISTERED_STAS = 16
AIC8801/D80 回归 ✅ 5 项全部 is_dual_pipe() 门控 + AIC8801 真机验证无回归
诊断计数器 #[cfg(debug_assertions)] 包裹

芯片门控验证

逐一确认代码中 5 项 AP 生命周期改动的门控:

  1. STA 注册去重ap.rs:100 let dual = bus.transport.is_dual_pipe()
  2. MM_STA_DEL 槽位回收ap.rs:35 if bus.transport.is_dual_pipe()
  3. 控制端口周期对账ap.rs:62 if bus.transport.is_dual_pipe()
  4. TX poll kickertx.rs:50 if bus.transport.is_dual_pipe()
  5. Deauth/Disassoc 清表rx.rs:444 if !bus.transport.is_dual_pipe() { return; }

非 DC/DW 分支走与 upstream/dev 逐字节一致的原有路径,不受 DC 适配影响。

CI 状态

CI workflow 对当前 head f6fb19b 的整体结论为 SUCCESS。各测试矩阵 job 因路径过滤器(PR 仅改 components/aic8800/)而 skip,属于预期行为。前轮 smoke-vmx 失败为 axvisor x86_64 VMX 测试,与本 PR 的 aic8800 驱动无关。

本地验证:cargo fmt --check -p aic8800 通过 ✅。无 [patch.crates-io] 引入。

回归测试

作者已提供真机验证:

  • AIC8800DC(LicheeRV Nano / SG2002):SoftAP 广播、客户端关联、SSH + HTTP 互通、断开重连、AP↔STA 模式切换 ✅
  • AIC8801(LicheeRV Nano):boot AP + 客户端关联 + ping + SSH 全通,门控后无回归 ✅
  • D80:暂无板实测,通过门控走已验证 dev 路径作为护栏

重复/重叠分析

未发现其他 open PR 与本 PR 重叠。PR #1185#1266#1276 已合入 dev,是本 PR 的前置基础。

审查结论

批准合入。经过 4 轮审查,所有阻塞问题均已解决:合并冲突已修复、日志级别已降级、固件已 vendor、所有行为差异已门控到 DC/DW。AIC8801 真机验证无回归。代码设计合理(双管道 SDIO 架构、PollSet 唤醒修复、STA 生命周期管理),注释详尽,CI 通过。

Powered by mimo-v2.5-pro

@ZR233 ZR233 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

当前 head:f6fb19b29aa8ec2a52a9eeba18eac8e71fbe8446

复审通过。此前的阻塞项已经逐项处理:合并状态为 CLEAN;DIAG 日志已降级并加 trace 守卫;in-tree only 固件已经提交且 build.rs 对空 remote_path 给出明确断言;func2 drain 以及 AP 生命周期相关逻辑均已用 is_dual_pipe()/cmd_func() 门控到 DC/DW 路径,避免影响 AIC8801/D80 的既有路径。

本地验证:

  • cargo fmt --check -p aic8800
  • cargo xtask clippy --package aic8800
  • git diff --check origin/dev...HEAD
  • 校验 components/aic8800/firmware/*.bin 的 sha256 与 build.rs 清单一致

CI 当前为通过/按路径过滤跳过的预期状态。真机验证依赖 SG2002/AIC8800DC/AIC8801 硬件,本机无法复跑;作者已在 PR 中补充 AIC8800DC 与 AIC8801 的实测结果。当前仍显示的旧 bot unresolved threads 对应问题在代码中已处理,建议合入前在 GitHub 上做行政性 resolve。

@ZR233 ZR233 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

请清理一下 bin 文件

Per review, remove all .bin blobs from components/aic8800/firmware/.
The 7 AIC8800DC-H blobs were the only ones vendored in-tree; the rest
were already fetched at build time. They split into two kinds:

- 3 firmware images (fmacfw_patch / fmacfw_patch_tbl / fmacfw_calib
  _8800dc_h_u02) are now fetched from the pinned upstream mirror
  (lxowalle/aic8800-sdio-firmware @ c56f910), same as every other blob,
  with SHA-256 verification. Board-tested on AIC8800DC: SoftAP + SSH +
  AP<->STA mode switch all work with these upstream versions.

- 4 RF config tables (ldpc / agc / txgain / txgain_h) are NOT firmware
  images and have no upstream mirror -- they are little-endian u32 arrays
  from the vendor BSP source aic8800dc_compat.c. Inlined as Rust byte
  arrays in src/fw/firmware/dc_rf_cfg.rs (byte-identical to the removed
  blobs), so no .bin is kept for them either.

components/aic8800/firmware/ now contains zero .bin files. .gitignore
drops the per-file allow-list; README documents both sources.
@BattiestStone4

Copy link
Copy Markdown
Contributor Author

已清理 bin 文件 (commit 90bdc07)。

components/aic8800/firmware/ 现在零 .bin。7 个 DC-H blob 分两类处理:

  • 3 个固件镜像fmacfw_patch / fmacfw_patch_tbl / fmacfw_calib_8800dc_h_u02)→ 改为从 pinned 上游 lxowalle/aic8800-sdio-firmware@c56f910 下载 + SHA-256 校验,与其余固件走完全相同的机制。AIC8800DC 真机实测:SoftAP + SSH + AP↔STA 模式切换全部正常。

  • 4 个 RF 配置表(ldpc / agc / txgain / txgain_h)不是固件镜像、无上游镜像——它们是 vendor BSP 源码 aic8800dc_compat.c 里的小端 u32 数组,已内联为 Rust 字节数组(src/fw/firmware/dc_rf_cfg.rs,与原 bin 逐字节一致),故也不保留 .bin

.gitignore 去掉了逐文件 allow-list,README 记录了两类来源。

三块板(AIC8801 / AIC8800DC / AIC8800D80)均已真机验证 SoftAP + 模式切换正常。本地发布检查全过:cargo fmt --all --checkcargo xtask clippy --package aic8800cargo xtask sync-lint

@mai-team-app mai-team-app Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

第六轮审查 — PR #1318 (AIC8800DC SoftAP for SG2002)

审查基于: head 90bdc079a(最新 push:stop vendoring DC-H firmware blobs in-tree)

改动概述

将 SoftAP 路径移植到 AIC8800DC(DC-H) + CV1800 SDIO host。核心改动:

  • SDIO 双管道架构:cmd_func/data_func 分离(DC/DW 命令走 func2,数据走 func1)
  • TX/RX poll kicker 修复边沿触发丢失唤醒
  • ME_STA_ADD 去重 + 控制端口自愈
  • Deauth/Disassoc 处理 + MM_STA_DEL_REQ 槽位回收
  • 热路径日志降级 info→debug/trace
  • DC-H 固件改为从上游下载(不再 in-tree vendor)+ RF cfg 内联为 Rust 字节数组
  • 所有 5 项 AP 生命周期改动全部用 is_dual_pipe() 门控到仅 DC/DW

前轮问题解决状态

问题 状态
DIAG 日志降级 log::trace! + log::log_enabled! 守卫
build.rs 空 remote_path ✅ DC-H 固件改上游下载,assert guard 保留
drain_func 门控 cmd_func() == 2 门控
registered_stas 容量上限 MAX_REGISTERED_STAS = 16
5 项 AP 生命周期门控 ✅ 全部 is_dual_pipe() 门控
AIC8801/D80 回归 ✅ AIC8801 真机验证无回归
诊断计数器 #[cfg(debug_assertions)] 包裹
bin 文件清理(ZR233 阻塞项) ✅ firmware/ 仅剩 README.md,3 个固件镜像走上游下载,4 个 RF cfg 内联为 Rust 字节数组

芯片门控验证

逐一确认代码中门控:

  1. STA 注册去重ap.rs let dual = bus.transport.is_dual_pipe()
  2. MM_STA_DEL 槽位回收ap.rs if bus.transport.is_dual_pipe()
  3. 控制端口周期对账ap.rs if bus.transport.is_dual_pipe()
  4. TX poll kickertx.rs if bus.transport.is_dual_pipe()
  5. RX poll kickerrx.rs if bus.transport.is_dual_pipe()
  6. Deauth/Disassoc 清表rx.rs if !bus.transport.is_dual_pipe() { return; }
  7. drain_func(func2)rx.rs if bus.transport.cmd_func() == 2
  8. MGMT frame ac 字段tx.rs hd[22] = if dual_pipe { 3 } else { 0 }

非 DC/DW 分支走与 upstream/dev 逐字节一致的原有路径。

CI 状态

CI workflow 对当前 head 90bdc079a 的所有 check runs 均为 skipped(路径过滤器跳过 aic8800-only 改动),无失败。前轮 smoke-vmx 失败为 axvisor x86_64 VMX 测试,与本 PR 的 aic8800 驱动无关。

本地验证:cargo fmt --check -p aic8800 ✅、git diff --check ✅。

重复/重叠分析

未发现其他 open PR 与本 PR 重叠。PR #1185#1266#1276 已合入 dev,是本 PR 的前置基础。

审查结论

批准合入。经过 6 轮审查,所有阻塞问题均已解决:

  • ZR233 的「清理 bin 文件」要求已满足(固件走上游下载 + RF cfg 内联)
  • 合并状态为 CLEAN
  • 所有行为差异已门控到 DC/DW,AIC8801 真机验证无回归
  • CI 无失败(路径过滤器跳过)
  • 代码设计合理(双管道 SDIO、PollSet 唤醒修复、STA 生命周期管理),注释详尽

Powered by mimo-v2.5-pro

@ZR233 ZR233 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

本轮按 current head 90bdc079a628b1ac5803836cd7a8f48c924ab578 复查,前轮阻塞项已经解决,旧的未解决线程也已关闭。

确认结果:

  • components/aic8800/firmware/ 现在只剩 README.md.gitignore,没有 .bin;DC-H 3 个固件镜像改为从 pinned 上游 lxowalle/aic8800-sdio-firmware@c56f910044cc854d6c553bcb9a644f3bca5a4c38 下载并校验 SHA-256。
  • 4 个 RF 配置表已内联到 src/fw/firmware/dc_rf_cfg.rs,不再作为 in-tree 二进制 blob 存放。
  • 原先的 remote_path = "" 问题已消除;build.rs 中 DC-H 固件 remote_path 有效,且保留了空路径 assert guard。
  • func2 drain 已按 cmd_func() == 2 门控;TX/RX poll kicker、STA 去重、MM_STA_DEL、控制端口重试、deauth/disassoc 清表等 DC/DW 行为均通过 is_dual_pipe() 门控,避免影响 D80/8801 baseline 路径。
  • 热路径诊断日志已降为 debug/trace,并用 log_enabled!(Trace) 避免默认路径额外读寄存器。
  • open PR 搜索未发现另一个 AIC8800DC SoftAP 替代实现;#1336 是 SDIO host abstraction,不是本 PR 的重复实现。

验证:

  • git diff --check origin/dev...HEAD:通过。
  • cargo fmt --check:通过。
  • rg -n '\[patch\.crates-io\]' -g 'Cargo.toml' .:无结果。
  • cargo xtask clippy --package aic8800:通过。
  • 当前 head CI 中 format、sync-lint、clippy、std、Starry/ArceOS/Axvisor QEMU、自托管/板卡相关检查均为 success 或预期 skipped,无失败项。

作者补充的 AIC8800DC / AIC8801 / D80 真机验证覆盖了本 PR 的关键硬件风险。LGTM。

@ZR233
ZR233 merged commit e941219 into rcore-os:dev Jun 22, 2026
52 checks passed
@github-actions github-actions Bot mentioned this pull request Jun 22, 2026
@BattiestStone4
BattiestStone4 deleted the feat/aic8800dc-port branch June 23, 2026 03:14
Antareske pushed a commit to Antareske/tgoskits that referenced this pull request Jun 27, 2026
…ent reconnect (rcore-os#1318)

* feat(aic8800): AIC8800DC SoftAP working — boot AP + SSH stable on SG2002

Port the AIC8800DC firmware/driver path so the boot-time SoftAP brings up,
broadcasts, accepts client association, and carries data (SSH verified) on
the LicheeRV Nano (SG2002).

Key fixes that made the AP functional:
- TX data-plane (MGMT + DATA 802.11 frames) routed on func1 (data_func),
  not func2 (command mailbox). func2 is command/CFM only, func1 is the
  data plane for both TX and RX. This was why no client could ever associate.
- MGMT frame field fixes: byte[2]=0x01 on both frame builders, ac=3 (VO).
- AIC8800DC firmware image/init path (fw/firmware/dc.rs + chip variant).
- Diagnostic logging at info for CMD sent / CFM received / MGMT sent, to
  make the command->CFM round-trip observable on hardware.

Runtime AP<->STA mode switching is not yet functional and is tracked
separately; the boot AP path is the verified milestone here.

* fix(aic8800): stabilize AIC8800DC SoftAP — SSH + HTTP, client reconnect

Builds on the boot-AP milestone (6518369) to make the AIC8800DC SoftAP
reliable enough for sustained client traffic and reconnection. Verified on
SG2002 (LicheeRV Nano): client associates fast, SSH and HTTP both work, and
a client can reconnect after disconnecting.

Four independent fixes, each rooted in an observed failure on real hardware:

- TX poll kicker (tx.rs): wifi-tx is an edge-triggered poll task that
  registers its waker after tx_process; a wake() landing in that window is
  lost (PollSet has no sticky bit) and, combined with the pending_flag
  head-of-line block, freezes all TX for seconds. RX already had a 10ms
  kicker for the same hazard; add the symmetric one for TX so any lost wake
  is recovered within ~10ms.

- ME_STA_ADD dedup with control-port self-heal (ap.rs, bus.rs): the firmware
  does not CFM a repeated ME_STA_ADD for an already-registered MAC, so a
  phone retransmitting AssocReq made the AP worker block 5s and timeout.
  Track registered STAs as (mac, sta_idx, ctrl_port_open); skip ME_STA_ADD
  for known MACs, and retry opening the control port on re-assoc until it
  actually succeeds (a timed-out control-port open previously left data
  frames dropped by the firmware → no ping/SSH).

- Deauth/Disassoc handling (rx.rs): these were silently ignored, so a
  disconnected STA stayed in the dedup table and reconnects were skipped
  entirely. Remove the MAC from the table on 0xC/0xA so a reconnect fully
  re-registers.

- Demote per-frame hot-path logs info→debug (rx.rs, tx.rs): on the single-
  core cooperative scheduler each info log is a blocking UART write; during
  an Auth flood these starve the TX/kicker tasks. Demoting them markedly
  speeds up association. Per-connection events (registered, control port
  OPENED, deauth) stay at info.

* style(aic8800): apply rustfmt and fix clippy manual_is_multiple_of

- cargo fmt across the AIC8800DC SoftAP changes (import wrapping, multi-line
  log argument formatting).
- Replace `kicks % 100 == 0` with `kicks.is_multiple_of(100)` in the RX poll
  kicker to satisfy clippy::manual_is_multiple_of under -D warnings.

No functional change.

* fix(wifi): gate func2 RX drain to DC/DW dual-pipe chips

After merging upstream/dev's func-agnostic RX refactor, process_rx_frames
drained both func2 and func1 unconditionally. Only DC/DW route the command
CFM/indication mailbox to func2; 8801/D80 keep command+data on func1, so
polling func2 there issued a wasted CMD52 every RX cycle. Gate the func2
drain on cmd_func()==2 to keep the three variants on their correct path.

* fix(wifi): address PR rcore-os#1318 review — demote DIAG logs, bound STA table

Review feedback from mai-team-app:
- init.rs: demote [DIAG] retry scan and cmd-send/pre-send block_cnt logs
  from info to debug/trace; gate the extra register reads behind
  log_enabled!(Trace) so the production polling path issues no extra CMD52.
- rx.rs: drop orphaned diagnostic counters (RX_POLL_COUNT /
  RX_LAST_BLOCKCNT / RX_NONZERO_READS); cfg(debug_assertions)-gate the
  remaining RX_KICK_COUNT heartbeat.
- ap.rs: bound registered_stas at MAX_REGISTERED_STAS (16) to prevent
  unbounded growth from AssocReq without matching deauth.
- tx.rs: mgmt-frame ac=VO is now gated to DC/DW (dual_pipe); 8801/D80
  keep vendor ac=BK to match upstream/dev and avoid regression. byte[2]
  reverted to the shared SDIO_TYPE_DATA constant (== 0x01 on both sides,
  so it was never a behavior change).

* fix(wifi): free firmware STA slot on deauth; reconcile control port

Two AP-mode stability fixes for "associates + gets IP but ping/SSH dead":

1. MM_STA_DEL on disconnect. Downlink data frames route by a single global
   sta_idx (hostdesc staid); firmware delivers reliably only to low slots.
   The deauth/disassoc handler cleared the driver table but never sent
   MM_STA_DEL_REQ, so the firmware kept the old slot and the NEXT station
   got a higher sta_idx (0->1->2...) whose downlink never arrived — the
   client kept re-ARPing and ping/SSH failed. Now the RX handler enqueues
   the freed sta_idx (can't send_cmd on the RX thread — CFM is processed
   there, would deadlock; same reason AssocReq is queued), and the AP
   worker drains the del-queue BEFORE the assoc-queue so a reconnecting
   STA reuses the known-good low slot.

2. Control-port reconcile. Opening the control port (authorize) could
   time out under single-core UART starvation; the old code only retried
   on AssocReq re-transmission, which a settled client never sends, so the
   data plane stayed dead. The AP worker now periodically re-issues
   SET_CONTROL_PORT for any unauthorized STA (capped at
   CONTROL_PORT_MAX_RETRY) until it succeeds.

Still single-concurrent-STA by design (one global sta_idx, one DHCP lease).

* fix(aic8800): vendor in-tree-only DC-H firmware so CI build resolves

CI `Run clippy / run_host` panicked in build.rs: the 7 AIC8800DC-H
firmware/RF-config blobs have remote_path "" (no upstream mirror — they're
extracted from the vendor BSP/rootfs), but firmware/.gitignore ignored
*.bin, so they were absent from a fresh checkout. build.rs then fell
through to download("") and hit HTTP 400.

- Allow-list the 7 in-tree-only blobs in firmware/.gitignore (downloadable
  blobs stay ignored and keep coming from the upstream mirror).
- Commit the blobs; their SHA-256 already match the digests in build.rs.
- Harden download(): assert remote_path is non-empty with an actionable
  message instead of emitting a confusing 400.

* feat(aic8800): gate DC/DW-specific AP lifecycle behavior by chip variant

Reviewer flagged 5 AP-lifecycle changes from the DC port that applied to
all chip variants without gating, risking regressions on the existing
AIC8801/D80 support (which lacked re-verification).

Add ChipVariant back to SdioTransport (it was collapsed into is_v3/cmd_func
proxies) plus a semantic `is_dual_pipe()` predicate, and gate all 5 behind
it so D80/8801 follow the upstream/dev path verbatim:

1. STA registration dedup + registered_stas table maintenance
2. MM_STA_DEL_REQ slot reclaim queue
3. Periodic control-port reconciliation (50ms retry loop)
4. TX/RX poll kickers (10ms wakeup tasks)
5. Deauth/Disassoc de-registration

Non-DC branches verified byte-equivalent to baseline handle_assoc_req.

Verified on hardware:
- AIC8800DC (newboard): SoftAP + ping/SSH + AP<->STA mode switch OK
- AIC8801 (LicheeRV Nano): SoftAP + ping/SSH OK (no regression)

* chore(aic8800): stop vendoring DC-H firmware blobs in-tree

Per review, remove all .bin blobs from components/aic8800/firmware/.
The 7 AIC8800DC-H blobs were the only ones vendored in-tree; the rest
were already fetched at build time. They split into two kinds:

- 3 firmware images (fmacfw_patch / fmacfw_patch_tbl / fmacfw_calib
  _8800dc_h_u02) are now fetched from the pinned upstream mirror
  (lxowalle/aic8800-sdio-firmware @ c56f910), same as every other blob,
  with SHA-256 verification. Board-tested on AIC8800DC: SoftAP + SSH +
  AP<->STA mode switch all work with these upstream versions.

- 4 RF config tables (ldpc / agc / txgain / txgain_h) are NOT firmware
  images and have no upstream mirror -- they are little-endian u32 arrays
  from the vendor BSP source aic8800dc_compat.c. Inlined as Rust byte
  arrays in src/fw/firmware/dc_rf_cfg.rs (byte-identical to the removed
  blobs), so no .bin is kept for them either.

components/aic8800/firmware/ now contains zero .bin files. .gitignore
drops the per-file allow-list; README documents both sources.
Antareske pushed a commit to Antareske/tgoskits that referenced this pull request Jun 27, 2026
…ent reconnect (rcore-os#1318)

* feat(aic8800): AIC8800DC SoftAP working — boot AP + SSH stable on SG2002

Port the AIC8800DC firmware/driver path so the boot-time SoftAP brings up,
broadcasts, accepts client association, and carries data (SSH verified) on
the LicheeRV Nano (SG2002).

Key fixes that made the AP functional:
- TX data-plane (MGMT + DATA 802.11 frames) routed on func1 (data_func),
  not func2 (command mailbox). func2 is command/CFM only, func1 is the
  data plane for both TX and RX. This was why no client could ever associate.
- MGMT frame field fixes: byte[2]=0x01 on both frame builders, ac=3 (VO).
- AIC8800DC firmware image/init path (fw/firmware/dc.rs + chip variant).
- Diagnostic logging at info for CMD sent / CFM received / MGMT sent, to
  make the command->CFM round-trip observable on hardware.

Runtime AP<->STA mode switching is not yet functional and is tracked
separately; the boot AP path is the verified milestone here.

* fix(aic8800): stabilize AIC8800DC SoftAP — SSH + HTTP, client reconnect

Builds on the boot-AP milestone (6518369) to make the AIC8800DC SoftAP
reliable enough for sustained client traffic and reconnection. Verified on
SG2002 (LicheeRV Nano): client associates fast, SSH and HTTP both work, and
a client can reconnect after disconnecting.

Four independent fixes, each rooted in an observed failure on real hardware:

- TX poll kicker (tx.rs): wifi-tx is an edge-triggered poll task that
  registers its waker after tx_process; a wake() landing in that window is
  lost (PollSet has no sticky bit) and, combined with the pending_flag
  head-of-line block, freezes all TX for seconds. RX already had a 10ms
  kicker for the same hazard; add the symmetric one for TX so any lost wake
  is recovered within ~10ms.

- ME_STA_ADD dedup with control-port self-heal (ap.rs, bus.rs): the firmware
  does not CFM a repeated ME_STA_ADD for an already-registered MAC, so a
  phone retransmitting AssocReq made the AP worker block 5s and timeout.
  Track registered STAs as (mac, sta_idx, ctrl_port_open); skip ME_STA_ADD
  for known MACs, and retry opening the control port on re-assoc until it
  actually succeeds (a timed-out control-port open previously left data
  frames dropped by the firmware → no ping/SSH).

- Deauth/Disassoc handling (rx.rs): these were silently ignored, so a
  disconnected STA stayed in the dedup table and reconnects were skipped
  entirely. Remove the MAC from the table on 0xC/0xA so a reconnect fully
  re-registers.

- Demote per-frame hot-path logs info→debug (rx.rs, tx.rs): on the single-
  core cooperative scheduler each info log is a blocking UART write; during
  an Auth flood these starve the TX/kicker tasks. Demoting them markedly
  speeds up association. Per-connection events (registered, control port
  OPENED, deauth) stay at info.

* style(aic8800): apply rustfmt and fix clippy manual_is_multiple_of

- cargo fmt across the AIC8800DC SoftAP changes (import wrapping, multi-line
  log argument formatting).
- Replace `kicks % 100 == 0` with `kicks.is_multiple_of(100)` in the RX poll
  kicker to satisfy clippy::manual_is_multiple_of under -D warnings.

No functional change.

* fix(wifi): gate func2 RX drain to DC/DW dual-pipe chips

After merging upstream/dev's func-agnostic RX refactor, process_rx_frames
drained both func2 and func1 unconditionally. Only DC/DW route the command
CFM/indication mailbox to func2; 8801/D80 keep command+data on func1, so
polling func2 there issued a wasted CMD52 every RX cycle. Gate the func2
drain on cmd_func()==2 to keep the three variants on their correct path.

* fix(wifi): address PR rcore-os#1318 review — demote DIAG logs, bound STA table

Review feedback from mai-team-app:
- init.rs: demote [DIAG] retry scan and cmd-send/pre-send block_cnt logs
  from info to debug/trace; gate the extra register reads behind
  log_enabled!(Trace) so the production polling path issues no extra CMD52.
- rx.rs: drop orphaned diagnostic counters (RX_POLL_COUNT /
  RX_LAST_BLOCKCNT / RX_NONZERO_READS); cfg(debug_assertions)-gate the
  remaining RX_KICK_COUNT heartbeat.
- ap.rs: bound registered_stas at MAX_REGISTERED_STAS (16) to prevent
  unbounded growth from AssocReq without matching deauth.
- tx.rs: mgmt-frame ac=VO is now gated to DC/DW (dual_pipe); 8801/D80
  keep vendor ac=BK to match upstream/dev and avoid regression. byte[2]
  reverted to the shared SDIO_TYPE_DATA constant (== 0x01 on both sides,
  so it was never a behavior change).

* fix(wifi): free firmware STA slot on deauth; reconcile control port

Two AP-mode stability fixes for "associates + gets IP but ping/SSH dead":

1. MM_STA_DEL on disconnect. Downlink data frames route by a single global
   sta_idx (hostdesc staid); firmware delivers reliably only to low slots.
   The deauth/disassoc handler cleared the driver table but never sent
   MM_STA_DEL_REQ, so the firmware kept the old slot and the NEXT station
   got a higher sta_idx (0->1->2...) whose downlink never arrived — the
   client kept re-ARPing and ping/SSH failed. Now the RX handler enqueues
   the freed sta_idx (can't send_cmd on the RX thread — CFM is processed
   there, would deadlock; same reason AssocReq is queued), and the AP
   worker drains the del-queue BEFORE the assoc-queue so a reconnecting
   STA reuses the known-good low slot.

2. Control-port reconcile. Opening the control port (authorize) could
   time out under single-core UART starvation; the old code only retried
   on AssocReq re-transmission, which a settled client never sends, so the
   data plane stayed dead. The AP worker now periodically re-issues
   SET_CONTROL_PORT for any unauthorized STA (capped at
   CONTROL_PORT_MAX_RETRY) until it succeeds.

Still single-concurrent-STA by design (one global sta_idx, one DHCP lease).

* fix(aic8800): vendor in-tree-only DC-H firmware so CI build resolves

CI `Run clippy / run_host` panicked in build.rs: the 7 AIC8800DC-H
firmware/RF-config blobs have remote_path "" (no upstream mirror — they're
extracted from the vendor BSP/rootfs), but firmware/.gitignore ignored
*.bin, so they were absent from a fresh checkout. build.rs then fell
through to download("") and hit HTTP 400.

- Allow-list the 7 in-tree-only blobs in firmware/.gitignore (downloadable
  blobs stay ignored and keep coming from the upstream mirror).
- Commit the blobs; their SHA-256 already match the digests in build.rs.
- Harden download(): assert remote_path is non-empty with an actionable
  message instead of emitting a confusing 400.

* feat(aic8800): gate DC/DW-specific AP lifecycle behavior by chip variant

Reviewer flagged 5 AP-lifecycle changes from the DC port that applied to
all chip variants without gating, risking regressions on the existing
AIC8801/D80 support (which lacked re-verification).

Add ChipVariant back to SdioTransport (it was collapsed into is_v3/cmd_func
proxies) plus a semantic `is_dual_pipe()` predicate, and gate all 5 behind
it so D80/8801 follow the upstream/dev path verbatim:

1. STA registration dedup + registered_stas table maintenance
2. MM_STA_DEL_REQ slot reclaim queue
3. Periodic control-port reconciliation (50ms retry loop)
4. TX/RX poll kickers (10ms wakeup tasks)
5. Deauth/Disassoc de-registration

Non-DC branches verified byte-equivalent to baseline handle_assoc_req.

Verified on hardware:
- AIC8800DC (newboard): SoftAP + ping/SSH + AP<->STA mode switch OK
- AIC8801 (LicheeRV Nano): SoftAP + ping/SSH OK (no regression)

* chore(aic8800): stop vendoring DC-H firmware blobs in-tree

Per review, remove all .bin blobs from components/aic8800/firmware/.
The 7 AIC8800DC-H blobs were the only ones vendored in-tree; the rest
were already fetched at build time. They split into two kinds:

- 3 firmware images (fmacfw_patch / fmacfw_patch_tbl / fmacfw_calib
  _8800dc_h_u02) are now fetched from the pinned upstream mirror
  (lxowalle/aic8800-sdio-firmware @ c56f910), same as every other blob,
  with SHA-256 verification. Board-tested on AIC8800DC: SoftAP + SSH +
  AP<->STA mode switch all work with these upstream versions.

- 4 RF config tables (ldpc / agc / txgain / txgain_h) are NOT firmware
  images and have no upstream mirror -- they are little-endian u32 arrays
  from the vendor BSP source aic8800dc_compat.c. Inlined as Rust byte
  arrays in src/fw/firmware/dc_rf_cfg.rs (byte-identical to the removed
  blobs), so no .bin is kept for them either.

components/aic8800/firmware/ now contains zero .bin files. .gitignore
drops the per-file allow-list; README documents both sources.
luodeb pushed a commit that referenced this pull request Jun 30, 2026
…ent reconnect (#1318)

* feat(aic8800): AIC8800DC SoftAP working — boot AP + SSH stable on SG2002

Port the AIC8800DC firmware/driver path so the boot-time SoftAP brings up,
broadcasts, accepts client association, and carries data (SSH verified) on
the LicheeRV Nano (SG2002).

Key fixes that made the AP functional:
- TX data-plane (MGMT + DATA 802.11 frames) routed on func1 (data_func),
  not func2 (command mailbox). func2 is command/CFM only, func1 is the
  data plane for both TX and RX. This was why no client could ever associate.
- MGMT frame field fixes: byte[2]=0x01 on both frame builders, ac=3 (VO).
- AIC8800DC firmware image/init path (fw/firmware/dc.rs + chip variant).
- Diagnostic logging at info for CMD sent / CFM received / MGMT sent, to
  make the command->CFM round-trip observable on hardware.

Runtime AP<->STA mode switching is not yet functional and is tracked
separately; the boot AP path is the verified milestone here.

* fix(aic8800): stabilize AIC8800DC SoftAP — SSH + HTTP, client reconnect

Builds on the boot-AP milestone (6518369) to make the AIC8800DC SoftAP
reliable enough for sustained client traffic and reconnection. Verified on
SG2002 (LicheeRV Nano): client associates fast, SSH and HTTP both work, and
a client can reconnect after disconnecting.

Four independent fixes, each rooted in an observed failure on real hardware:

- TX poll kicker (tx.rs): wifi-tx is an edge-triggered poll task that
  registers its waker after tx_process; a wake() landing in that window is
  lost (PollSet has no sticky bit) and, combined with the pending_flag
  head-of-line block, freezes all TX for seconds. RX already had a 10ms
  kicker for the same hazard; add the symmetric one for TX so any lost wake
  is recovered within ~10ms.

- ME_STA_ADD dedup with control-port self-heal (ap.rs, bus.rs): the firmware
  does not CFM a repeated ME_STA_ADD for an already-registered MAC, so a
  phone retransmitting AssocReq made the AP worker block 5s and timeout.
  Track registered STAs as (mac, sta_idx, ctrl_port_open); skip ME_STA_ADD
  for known MACs, and retry opening the control port on re-assoc until it
  actually succeeds (a timed-out control-port open previously left data
  frames dropped by the firmware → no ping/SSH).

- Deauth/Disassoc handling (rx.rs): these were silently ignored, so a
  disconnected STA stayed in the dedup table and reconnects were skipped
  entirely. Remove the MAC from the table on 0xC/0xA so a reconnect fully
  re-registers.

- Demote per-frame hot-path logs info→debug (rx.rs, tx.rs): on the single-
  core cooperative scheduler each info log is a blocking UART write; during
  an Auth flood these starve the TX/kicker tasks. Demoting them markedly
  speeds up association. Per-connection events (registered, control port
  OPENED, deauth) stay at info.

* style(aic8800): apply rustfmt and fix clippy manual_is_multiple_of

- cargo fmt across the AIC8800DC SoftAP changes (import wrapping, multi-line
  log argument formatting).
- Replace `kicks % 100 == 0` with `kicks.is_multiple_of(100)` in the RX poll
  kicker to satisfy clippy::manual_is_multiple_of under -D warnings.

No functional change.

* fix(wifi): gate func2 RX drain to DC/DW dual-pipe chips

After merging upstream/dev's func-agnostic RX refactor, process_rx_frames
drained both func2 and func1 unconditionally. Only DC/DW route the command
CFM/indication mailbox to func2; 8801/D80 keep command+data on func1, so
polling func2 there issued a wasted CMD52 every RX cycle. Gate the func2
drain on cmd_func()==2 to keep the three variants on their correct path.

* fix(wifi): address PR #1318 review — demote DIAG logs, bound STA table

Review feedback from mai-team-app:
- init.rs: demote [DIAG] retry scan and cmd-send/pre-send block_cnt logs
  from info to debug/trace; gate the extra register reads behind
  log_enabled!(Trace) so the production polling path issues no extra CMD52.
- rx.rs: drop orphaned diagnostic counters (RX_POLL_COUNT /
  RX_LAST_BLOCKCNT / RX_NONZERO_READS); cfg(debug_assertions)-gate the
  remaining RX_KICK_COUNT heartbeat.
- ap.rs: bound registered_stas at MAX_REGISTERED_STAS (16) to prevent
  unbounded growth from AssocReq without matching deauth.
- tx.rs: mgmt-frame ac=VO is now gated to DC/DW (dual_pipe); 8801/D80
  keep vendor ac=BK to match upstream/dev and avoid regression. byte[2]
  reverted to the shared SDIO_TYPE_DATA constant (== 0x01 on both sides,
  so it was never a behavior change).

* fix(wifi): free firmware STA slot on deauth; reconcile control port

Two AP-mode stability fixes for "associates + gets IP but ping/SSH dead":

1. MM_STA_DEL on disconnect. Downlink data frames route by a single global
   sta_idx (hostdesc staid); firmware delivers reliably only to low slots.
   The deauth/disassoc handler cleared the driver table but never sent
   MM_STA_DEL_REQ, so the firmware kept the old slot and the NEXT station
   got a higher sta_idx (0->1->2...) whose downlink never arrived — the
   client kept re-ARPing and ping/SSH failed. Now the RX handler enqueues
   the freed sta_idx (can't send_cmd on the RX thread — CFM is processed
   there, would deadlock; same reason AssocReq is queued), and the AP
   worker drains the del-queue BEFORE the assoc-queue so a reconnecting
   STA reuses the known-good low slot.

2. Control-port reconcile. Opening the control port (authorize) could
   time out under single-core UART starvation; the old code only retried
   on AssocReq re-transmission, which a settled client never sends, so the
   data plane stayed dead. The AP worker now periodically re-issues
   SET_CONTROL_PORT for any unauthorized STA (capped at
   CONTROL_PORT_MAX_RETRY) until it succeeds.

Still single-concurrent-STA by design (one global sta_idx, one DHCP lease).

* fix(aic8800): vendor in-tree-only DC-H firmware so CI build resolves

CI `Run clippy / run_host` panicked in build.rs: the 7 AIC8800DC-H
firmware/RF-config blobs have remote_path "" (no upstream mirror — they're
extracted from the vendor BSP/rootfs), but firmware/.gitignore ignored
*.bin, so they were absent from a fresh checkout. build.rs then fell
through to download("") and hit HTTP 400.

- Allow-list the 7 in-tree-only blobs in firmware/.gitignore (downloadable
  blobs stay ignored and keep coming from the upstream mirror).
- Commit the blobs; their SHA-256 already match the digests in build.rs.
- Harden download(): assert remote_path is non-empty with an actionable
  message instead of emitting a confusing 400.

* feat(aic8800): gate DC/DW-specific AP lifecycle behavior by chip variant

Reviewer flagged 5 AP-lifecycle changes from the DC port that applied to
all chip variants without gating, risking regressions on the existing
AIC8801/D80 support (which lacked re-verification).

Add ChipVariant back to SdioTransport (it was collapsed into is_v3/cmd_func
proxies) plus a semantic `is_dual_pipe()` predicate, and gate all 5 behind
it so D80/8801 follow the upstream/dev path verbatim:

1. STA registration dedup + registered_stas table maintenance
2. MM_STA_DEL_REQ slot reclaim queue
3. Periodic control-port reconciliation (50ms retry loop)
4. TX/RX poll kickers (10ms wakeup tasks)
5. Deauth/Disassoc de-registration

Non-DC branches verified byte-equivalent to baseline handle_assoc_req.

Verified on hardware:
- AIC8800DC (newboard): SoftAP + ping/SSH + AP<->STA mode switch OK
- AIC8801 (LicheeRV Nano): SoftAP + ping/SSH OK (no regression)

* chore(aic8800): stop vendoring DC-H firmware blobs in-tree

Per review, remove all .bin blobs from components/aic8800/firmware/.
The 7 AIC8800DC-H blobs were the only ones vendored in-tree; the rest
were already fetched at build time. They split into two kinds:

- 3 firmware images (fmacfw_patch / fmacfw_patch_tbl / fmacfw_calib
  _8800dc_h_u02) are now fetched from the pinned upstream mirror
  (lxowalle/aic8800-sdio-firmware @ c56f910), same as every other blob,
  with SHA-256 verification. Board-tested on AIC8800DC: SoftAP + SSH +
  AP<->STA mode switch all work with these upstream versions.

- 4 RF config tables (ldpc / agc / txgain / txgain_h) are NOT firmware
  images and have no upstream mirror -- they are little-endian u32 arrays
  from the vendor BSP source aic8800dc_compat.c. Inlined as Rust byte
  arrays in src/fw/firmware/dc_rf_cfg.rs (byte-identical to the removed
  blobs), so no .bin is kept for them either.

components/aic8800/firmware/ now contains zero .bin files. .gitignore
drops the per-file allow-list; README documents both sources.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants