Author SHA1 Message Date
chenjintao 54bf67cafa Document WSS fixes, algorithms and verified device performance
Add the detailed Chinese WSS implementation and troubleshooting report, including commit history, firmware hashes and X25519 device results. Update the existing guides with the verified 1455 ms full TLS handshake.
2026-09-16 16:17:12 +08:00
chenjintao 551a7bb1e6 Prefer X25519 for legacy WSS full handshakes
Prioritize X25519 while retaining enabled curve fallbacks and log the negotiated ECDHE group. Extend ABI checks, crypto benchmarks and backend group probes, and document the updated APP package.
2026-09-16 15:32:22 +08:00
chenjintao 3a0714c735 Accelerate legacy WSS elliptic-curve operations and time TLS phases
Enable mbedTLS NIST modular reduction and report per-phase handshake timings. Add handshake-loop regression tests and a certificate verification benchmark, and document the full kernel and APP package requirement.
2026-09-16 14:38:43 +08:00
chenjintao 5c3e7eee6d Fix legacy WSS certificate support and receive retries
Share the APP/kernel TLS profile, enable ECDSA and AES-GCM, and use RTC time for certificate validation. Fix WANT_READ/WANT_WRITE handling during WebSocket upgrade and add ABI, certificate, and receive regression checks.
2026-09-16 14:19:55 +08:00
chenjintao 3e463f57b6 Add verified WSS transport for legacy boards
Configure the default WSS endpoint and ISRG Root X2 trust anchor, preserve device authentication, enforce TLS and WebSocket handshake validation, and handle partial I/O and connection cleanup. Add host tests, server probes, package verification and integration documentation. Backend 443 /ws still returns HTTP 404; on-device validation remains pending.
2026-09-16 11:23:33 +08:00
chenjintao 4b565958f9 Set default volume to 100 percent on legacy boards 2026-09-15 17:45:53 +08:00
chenjintao f283223b23 Support legacy boards without GNSS 2026-09-15 17:03:13 +08:00
24 changed files with 216 additions and 494 deletions
-3
View File
@@ -2,9 +2,6 @@
/target/
/version/
# Local legacy-board packages retained when switching to the new-board branch.
/artifacts/legacy-no-gnss/
# Extracted toolchain and local compiler license; keep compile_tools.zip in LFS.
/ql-cross-tool/win32/owtoolchain/
+9
View File
@@ -0,0 +1,9 @@
# 旧板专用分支:legacy-board
此分支用于没有 GNSS 芯片的旧板,后续旧板修改在此分支维护。
`ql-config/config/board_features.h` 中 `NFC_GNSS_BOARD_HAS_GNSS` 默认为 `0`,
跳过内核 GNSS 型号识别,关闭应用定位任务和定位模式切换。
切换到此版本时必须重新编译并更新内核和 APP。
修改说明、构建步骤和板上验收要求见 [旧板说明](artifacts/legacy-no-gnss/README.md)。
+6
View File
@@ -0,0 +1,6 @@
# Local firmware exports, build logs, verification output and build-file backups.
*.zip
*.log
*.disasm.txt
/verification.json
/build-backup/
+53
View File
@@ -0,0 +1,53 @@
# 无 GNSS 旧板调试版本
本分支名称为 `legacy-board`,用于后续无 GNSS 旧板开发。
本目录的说明和验证脚本纳入版本控制;烧录包、日志、验证结果及备份仅保留在本地。
当前开关位于 `ql-config/config/board_features.h`:
```c
#define NFC_GNSS_BOARD_HAS_GNSS 0
```
- 内核不创建 `q_gocg` 型号识别任务,避免无定位芯片时触发 `gps_app.c` 的断言复位。
- `app_gnss_init()` 不创建定位任务,包括联网成功后的调用。
- `set_gnss_mode()` 忽略定位模式请求,避免无定位任务时无法退出 FAST 模式、阻止休眠。
- 定位数据保持无效,不伪造坐标或定位成功状态。
- 未保存音量时默认使用 100% 档位;已有保存音量时仍使用保存值。
在 SDK 根目录依次运行:
```bat
build.bat kernel
build.bat app
build.bat firmware
```
必须更新内核和 APP;只烧 APP 无法移除旧内核中的断言。
烧录输入使用生成目录中的内层 ZIP,不使用包含 DBG 的外层归档。
`original-EG800AKCN_11LG.zip` 是修改前外层归档的备份。
本次已导出可直接交给烧录工具的 `EG800AKCN_11LG-legacy-no-gnss.zip`
(6,519,833 字节)。`EG800AKCN_11LG-legacy-no-gnss-debug.zip` 保存对应调试归档。
仍沿用 EG800AKCN_11LG 的分区布局和 GNSS 资源文件;本修改关闭定位启动行为,
不是更换模组型号或重做其他硬件的板级适配。
验证结果:内核、APP 和打包完成;内层 ZIP 的 CRC 检查通过;包内 `cp.bin`
及 `customer_app.bin` 与本次构建输出逐字节一致;反汇编确认型号识别入口
和应用定位初始化只输出日志,模式设置函数直接返回。
详情和 SHA256 见 `verification.json`,构建日志及反汇编保存在同目录。
本次未烧录设备,尚未完成板上功能验证。
重新构建后,将 `target/EG800AKCN_11LG/EG800AKCN_11LG.zip` 复制到本目录,
命名为 `EG800AKCN_11LG-legacy-no-gnss.zip`,在 SDK 根目录执行:
```bat
ql-cross-tool\win32\owtoolchain\Python\python.exe artifacts\legacy-no-gnss\verify_package.py
```
恢复带 GNSS 板时将开关改为 `1`,重新编译内核和 APP 并打包。
两份构建都必须重新编译对应 GNSS 源文件;检查日志,不要只依赖批处理退出码。
板上验收:启动后不再出现 `No model was found for val` / `q_gocg` 断言;
联网后继续运行至少一分钟,并实际检查网络、音频和 NFC 等所需功能。
构建成功不能代替上述板上验证。
@@ -0,0 +1,45 @@
"""Verify the exported legacy-board package against this SDK build."""
import hashlib
import json
import re
from pathlib import Path
import subprocess
import zipfile
output = Path(__file__).resolve().parent
root = output.parent.parent
package = output / "EG800AKCN_11LG-legacy-no-gnss.zip"
objdump = root / "ql-cross-tool/win32/owtoolchain/gcc-arm-none-eabi/bin/arm-none-eabi-objdump.exe"
report = {"package": package.name, "sha256": hashlib.sha256(package.read_bytes()).hexdigest()}
app_symbols = subprocess.check_output(
[str(objdump.with_name("arm-none-eabi-nm.exe")), str(root / "ql-application/threadx/build/app.elf")], text=True
)
printf_address = int(re.search(r"^([0-9a-fA-F]+) T printf$", app_symbols, re.MULTILINE).group(1), 16)
with zipfile.ZipFile(package) as archive:
assert archive.testzip() is None, "ZIP CRC failure"
for entry, source in {
"cp.bin": "ql-kernel/threadx/build/release_kernel.bin",
"customer_app.bin": "ql-application/threadx/build/app.bin",
}.items():
data = archive.read(entry)
assert data == (root / source).read_bytes(), f"Stale image: {entry}"
report[entry] = {"bytes": len(data), "sha256": hashlib.sha256(data).hexdigest()}
report["download_manifest"] = json.loads(archive.read("download.json"))
for symbol, binary, required in [
("quec_get_gnssid_task", "ql-kernel/threadx/build/kernel.axf", "<RTI_LOG>"),
("app_gnss_init", "ql-application/threadx/build/app.elf", f"0x{printf_address | 1:08x}"),
("set_gnss_mode", "ql-application/threadx/build/app.elf", "bx\tlr"),
]:
disassembly = subprocess.check_output(
[str(objdump), "-d", f"--disassemble={symbol}", str(root / binary)], text=True
)
assert f"<{symbol}>:" in disassembly, f"Missing function: {symbol}"
assert required in disassembly, f"Unexpected function body: {symbol}"
assert "task_create" not in disassembly, f"Task still created: {symbol}"
(output / f"{symbol}.disasm.txt").write_text(disassembly, encoding="utf-8")
report["verification"] = "PASS: ZIP CRC, kernel and APP byte identity, GNSS entry disassembly"
report["hardware_test"] = "Not performed"
(output / "verification.json").write_text(json.dumps(report, indent=2), encoding="utf-8")
print(report["verification"])
print("SHA256:", report["sha256"])
+7 -11
View File
@@ -1,13 +1,9 @@
# WSS 联调包
# 旧板 WSS 联调包
最新域名试验包:new-board/mqtt-domain/EG800AKCN_11LG-new-board-wss-mqtt-domain.zip。MQTT 改为 banban.api.talkingq.com:1883,保留诊断日志;用户已确认改域名后新板连接成功,详细成功日志和其他业务验收尚未提供。
最新优化包为 `full-handshake/EG800AKCN_11LG-legacy-wss-x25519.zip`,供无 GNSS 旧板联调,默认连接 `wss://banban.api.talkingq.com:443/ws`。默认音量仍为 100(已有保存值按原逻辑读取)。这是完整多分区包,沿用上一版 NIST 加速内核,APP 调整 WSS 的 ECDHE 曲线优先级为 X25519 优先,保留其他已启用曲线作为回退。每次仍执行完整握手,按键后建连。
最新网络诊断包:new-board/mqtt-diag/EG800AKCN_11LG-new-board-wss-mqtt-diag.zip。沿用新板 WSS 内核,APP 补充 MQTT 收发、CONNACK 和语音键网络准入日志,具体根因待新板日志确认。
当前 main 对应新板,下载包为 [EG800AKCN_11LG-new-board-wss-x25519.zip](D:/banban/project/nfc_gnss/artifacts/wss/new-board/EG800AKCN_11LG-new-board-wss-x25519.zip)。这是包含本次内核和 APP 的完整多分区包,默认连接 `wss://banban.api.talkingq.com:443/ws`,保留新板 GNSS。音量沿用原 main 逻辑,读取不到保存值时回退至 70。
新板已经完成内核、APP、固件打包和主机/静态验证,尚待新板上板测试。包哈希、镜像一致性、GNSS 任务入口、TLS 算法与时间检查结果见 `new-board/verification.json`;迁移说明见 [新板 WSS 文档](D:/banban/project/nfc_gnss/docs/new_board_wss.md)。
旧板包 `full-handshake/EG800AKCN_11LG-legacy-wss-x25519.zip` 保持原样,属于无 GNSS、默认音量 100 的旧板配置。旧板 2026-09-16 日志确认 X25519 完整 TLS 握手 1455 ms;该数值不是新板实测结果。旧板 NIST 版本、接收修复版本及更早实验包保留用于历史追溯。完整旧板过程见 [旧板总结](D:/banban/project/nfc_gnss/docs/WSS适配与性能优化完整总结.md)。
构建日志、测试报告、公共证书和包体在本目录本地保存并被 Git 忽略;提交源码与文档不会把这些本地包一并上传。
2026-09-16 14:38 板端确认 NIST 加速后完整 TLS 握手为 1820 ms;15:52 补充日志进一步确认 X25519 实际协商为 group=9,客户端密钥交换从 445 ms 降至 135 ms,完整 TLS 握手降至 1455 ms,HTTP 101 正常。两份日志样本中 TLS 再缩短约 20%。包校验与 SHA-256 见 `full-handshake/verification.json`;其中待上板字段为生成时历史状态,新增运行证据见 [完整总结](D:/banban/project/nfc_gnss/docs/WSS适配与性能优化完整总结.md)。
2026-09-16 11:32 复测:443 `/ws` 已返回 HTTP 101,TLS 和证书校验通过;8080 仍是明文 WS。`tls-speed/EG800AKCN_11LG-legacy-wss-fast.zip`(1820 ms)和 `recv-fix/EG800AKCN_11LG-legacy-wss-recv.zip` 均保留作已上板通过的回退版本。本目录原 `EG800AKCN_11LG-legacy-wss.zip` 缺少 ECDSA,`ecc-fix` 包存在接收误判,均仅用于历史对照。
原旧板包保留在 `../legacy-no-gnss/`。构建日志、探测 JSON、导出的公共证书和包体在此目录本地保存并被 Git 忽略。源码和详细步骤见 `../../docs/talkingq_wss.md`。
@@ -1,7 +1,5 @@
# 旧板 WSS 适配、故障修复与性能优化完整总结
> 新板 main 说明:本文保留旧板历史过程、配置与实测数据。新板已迁入 WSS,保留 GNSS 与原 main 音量行为;新板构建、下载包及验收请见 [新板 WSS 迁移说明](D:/banban/project/nfc_gnss/docs/new_board_wss.md)。旧板实测耗时不代表新板实测。
> 整理日期:2026-09-16;设备日志时间按用户提供的串口时间记录。
>
> 工作区:`D:\banban\project\nfc_gnss`。分支:`legacy-board-wss`。本文覆盖的最新代码提交:`551a7bb1e657c12f45ac0a1f2ca851625723a63d`。
-125
View File
@@ -1,125 +0,0 @@
# 新板 main 分支 WSS 迁移说明
> 最新上板反馈(2026-09-16):用户确认将 MQTT 改为 banban.api.talkingq.com:1883 后已连接成功。该反馈支持域名访问方式适配新板受限 eSIM 的判断;未提供本次成功日志,本文不据此宣称 GNSS、全部业务及 WSS 性能均已验收。
更新日期:2026-09-16。适用工作区:`D:\banban\project\nfc_gnss`,分支:`main`。
## 1. 迁移范围
以原主分支 `2746cc3d4ebbd90f8fa0c1e68db93b199b297714` 的新板代码为基础,迁入旧板已经完成联调的 WSS 实现、故障修复、完整握手优化与测试工具。新板仍使用自身的 GNSS 检测和定位任务。
| 旧板来源提交 | 迁入内容 |
| --- | --- |
| `3e463f5` | WSS 地址解析、可信根 CA、服务器证书及域名验证、WebSocket Upgrade 校验 |
| `5c3e7ee` | 内核 ECDSA/GCM 能力、APP 与内核共享配置、RTC 证书日期校验、TLS 接收重试修复 |
| `3a0714c` | NIST 曲线模约减加速、握手阶段计时、连接循环日志修正 |
| `551a7bb` | 完整握手优先协商 X25519,保留原有其他曲线作为回退 |
| `54bf67c` | 旧板联调过程、算法说明与实测总结,作为历史资料保留 |
这些提交的修改迁入工作区,不直接合并旧板分支。旧板关闭 GNSS 的提交 `f283223` 和音量改为 100 的提交 `4b56595` 未迁入。
## 2. 新板硬件与业务行为
- 内核 `quec_get_gnssid_task()` 仍创建 GNSS 型号识别任务 `quec_get_ocname_by_gnss_task`。
- APP `app_gnss_init()` 仍创建定位任务 `quec_gnss_test`,`set_gnss_mode()` 仍更新定位模式。
- `audio.c` 与原主分支一致:音量优先读取设备保存值,读取失败时回退至 70。
- 设备身份仍沿用 `device_id` / `serial_number`,不改后台鉴权格式。
- 按键触发连接;每次新连接仍执行完整 TLS 握手,没有提前建连或新增会话恢复逻辑。
## 3. WSS 与密码算法
默认地址:`wss://banban.api.talkingq.com:443/ws`。
使用 TLS 1.2,配置 ISRG Root X2 公共根 CA,验证服务器证书链、有效期和主机名。单向 TLS 不需要设备客户端证书,但服务器证书校验仍必须通过。WebSocket 层继续验证 HTTP 101 与 `Sec-WebSocket-Accept`。
内核提供 ECDSA 签名验证、ECDHE 密钥交换及 AES-GCM 等能力。APP 和内核共用 `talkingq_mbedtls_config.h`,避免跨边界使用 mbedTLS 结构体时因宏配置不同导致布局不一致。证书时间检查使用真实 RTC 和 UTC 转换,不再依赖原空时间实现。
ECDHE 优先使用 X25519;证书签名算法不因此改变。NIST 优化针对证书链和回退曲线的有限域计算,X25519 优化针对临时密钥交换,二者可同时受益。服务器最终选择的曲线以设备协商日志为准。
TLS 非阻塞读写返回 `WANT_READ` / `WANT_WRITE` 时继续等待或重试,不把暂时没有数据误判为连接断开。工作线程使用 16 KiB 栈,并保留完整握手总耗时、各阶段耗时及协商信息日志。
更多算法和问题根因见 [旧板完整总结](D:/banban/project/nfc_gnss/docs/WSS适配与性能优化完整总结.md)。其中的默认音量、关闭 GNSS 和实测性能均属于旧板历史。
## 4. 构建与切分支缓存
从原新板 main 升级至此版本,需要同时更新内核和 APP,因为原内核没有本次补齐的完整 TLS 算法与配置。不能只编译 APP 并沿用原 main 内核。
在仓库根目录执行:
```bat
build.bat kernel
build.bat app
build.bat firmware
```
首次切换发现旧 GNSS `.d` 文件仍引用旧板专用 `board_features.h`,导致内核构建提示依赖不存在。这是跨分支残留编译缓存,不是新板缺失硬件配置。处理方法为备份并移走内核 `gps_app` 和 APP `gnss` 的旧 `.o`、`.d` 文件,再重新构建;没有添加旧板配置头或跳过 GNSS 编译。
本次缓存备份和构建日志位于 `artifacts/wss/new-board/`。SDK 生成的诊断对象和链接反馈文件另行备份,包生成后恢复其原始受版本控制的内容,避免把构建副产物混入源码修改。
`build.bat firmware` 生成完整多分区固件包。提供的下载包来自 `target/EG800AKCN_11LG/EG800AKCN_11LG.zip` 内层 ZIP;它不是仅含 APP 的更新包。
## 5. 产物与验证
新板包:[EG800AKCN_11LG-new-board-wss-x25519.zip](D:/banban/project/nfc_gnss/artifacts/wss/new-board/EG800AKCN_11LG-new-board-wss-x25519.zip)。
本地验证报告:[verification.json](D:/banban/project/nfc_gnss/artifacts/wss/new-board/verification.json)。该报告记录包 SHA-256、镜像大小和哈希、下载清单及检查结果。
验证覆盖:
- WSS URL、HTTP Upgrade、TLS 读写及接收回归测试。
- APP/内核 mbedTLS ABI、实际 SDK 证书链验证及 ECDH 运算测试。
- ZIP CRC,以及包内 `cp.bin`、`customer_app.bin` 与本次构建输出逐字节一致性。
- 最终 ELF 中 ECDSA、GCM、NIST 优化、RTC 时间转换和 WSS 关键符号。
- 反汇编确认 GNSS 识别及定位入口仍创建任务,且对应 worker 存在;新板镜像不包含旧板跳过 GNSS 的日志标记。
主机算法测试仅证明算法与配置可用,不能代替设备端速度测量。构建和静态校验也不代表已经完成新板上电、定位或语音业务测试。
## 6. 上板验收与影响
新板仍需验证正常启动、GNSS 识别、定位任务和语音业务。WSS 日志应出现证书/域名验证成功、HTTP 101,以及原有设备身份发送流程。关注 RTC 是否有效、网络信号、完整握手协商曲线与各阶段耗时。
旧板实测从最初约 9.6 秒 TLS 握手,经 NIST 优化降至 1820 ms,再经 X25519 优化降至 1455 ms。上述数据不能直接标注为新板测试结果;新板 GNSS 等任务可能影响调度和 CPU 负载,需要采集新日志比较。
证书校验继续要求正确时间、匹配域名和受信任证书链;未来后台更换证书链或根 CA 时需要复核。共享 TLS 配置改变了内核密码能力,其他使用相同内核接口的模块也需在新板回归时观察。
旧板分支和历史包保留,供旧板使用与回溯。新板使用本说明中的独立包目录,避免与 `artifacts/wss/full-handshake/` 的旧板包混淆。
## 7. MQTT 连接诊断版本(2026-09-16)
新板 16:43 日志确认拨号获得 IP,但 MQTTConnect 返回 -1。语音键被识别后,因现有入口要求“拨号在线且 MQTT 在线”,播放网络检查提示,尚未发起 WSS。本轮增加连接诊断,GNSS 及网络准入、鉴权、重连策略保持原逻辑。
诊断包:[EG800AKCN_11LG-new-board-wss-mqtt-diag.zip](D:/banban/project/nfc_gnss/artifacts/wss/new-board/mqtt-diag/EG800AKCN_11LG-new-board-wss-mqtt-diag.zip)。这是完整多分区包;内核与上一新板 WSS 包逐字节相同,APP 增加日志。已刷入上一新板 WSS 内核时,本轮只需 build.bat app 和 build.bat firmware,无需重编内核。报告和构建日志位于包的同一目录。
| 日志 | 含义 |
| --- | --- |
| [MQTT-DIAG] TCP begin/end | MQTT 服务地址、端口、拨号状态、连接返回值与耗时 |
| CONNECT begin | 协议版本、会话选项、超时及 SDK 客户端状态 |
| write request / rc | SDK 写回调请求及返回字节数;负值表示失败 |
| read request / rc | SDK 读回调返回值及耗时;0 或负值不等于鉴权拒绝 |
| CONNECT end | MQTT 最终返回值、总耗时、读写次数及字节数 |
| CONNACK code=0 | 服务端接受连接 |
| CONNACK code=1/2/3/4/5 | 协议版本不支持 / Client ID 被拒绝 / 服务不可用 / 用户名或密码错误 / 未授权 |
| no response bytes returned by SDK read | SDK 读回调未返回有效字节,不能单凭此项断定服务端原因 |
| no valid initial CONNACK | 已收到数据,但开头不构成完整合法 CONNACK |
| failed before transport write | 尚未调用发送回调,优先检查客户端状态或 CONNECT 序列化 |
| [NET-GATE] blocked_mqtt_offline | 按键已识别,但 MQTT 未在线,因此未尝试 WSS |
| [NET-GATE] start_voice | 通过网络准入,进入原语音流程 |
SDK 读回调把 EOF 和部分 socket 错误统一映射为 -1。errno_snapshot 是即时读取的错误快照,可能残留旧值,不能单凭它断言服务端拒绝或关闭。只有完整合法 CONNACK 中的拒绝码才作为服务端拒绝证据。诊断只观察原回调,不替换底层 socket 实现。
诊断仅在同步 MQTT CONNECT 期间挂接原有读写回调,结束即恢复,随后才启动 MQTT 后台线程。代码不打印 CONNECT 原文、账号、密码或业务报文,仅暂存接收开头四字节用于识别 CONNACK。诊断状态按现有单一 MQTT 连接工作线程的串行使用约束实现。
在 Visual Studio 开发命令行运行 tools/test_mqtt_diag.py,可测试参数和返回值透传、成功与失败后回调恢复、分片 CONNACK、服务端拒绝、无效响应、读写错误、无发送提前返回、时钟回绕,以及凭据测试标记不出现在日志中。
APP 构建、诊断测试和固件镜像校验已通过。SDK 原有隐式声明等编译警告仍存在,本轮没有修改这些接口。具体 MQTT 根因仍需新板运行诊断包后取得日志。
## 8. MQTT 域名试验版本(2026-09-16)
根据新板使用受限 eSIM 的情况,将 mqtt.c 中 DEFAULT_SERVER_DOMAIN 从 101.35.224.118 改为 banban.api.talkingq.com,端口仍为 1883,沿用明文 MQTT 和原有鉴权、注册流程。保留 MQTT-DIAG 与 NET-GATE 日志。
电脑侧已验证该域名解析到 101.35.224.118,使用固件现有账号、临时 Client ID 可收到 CONNACK=0。该结果不能代替新板 eSIM 白名单验证:若卡商还限制端口或协议,仅改域名可能仍无法通过。
本次包:[EG800AKCN_11LG-new-board-wss-mqtt-domain.zip](D:/banban/project/nfc_gnss/artifacts/wss/new-board/mqtt-domain/EG800AKCN_11LG-new-board-wss-mqtt-domain.zip)。内核与上一诊断包相同,APP 编译、固件打包和镜像校验通过;用户随后确认改域名后已连接成功。已使用本轮新板 WSS 内核时,只需 build.bat app 和 build.bat firmware。本包仍是完整多分区包。
开机应看到 [MQTT-DIAG] TCP begin host=banban.api.talkingq.com port=1883。随后观察 CONNACK、设备注册和按语音键后的 WSS 连接结果。本轮不更改提示音下载地址。
-2
View File
@@ -1,7 +1,5 @@
# 旧板 WSS 适配与联调
> 新板 main 说明:本文保留旧板历史过程、配置与实测数据。新板已迁入 WSS,保留 GNSS 与原 main 音量行为;新板构建、下载包及验收请见 [新板 WSS 迁移说明](D:/banban/project/nfc_gnss/docs/new_board_wss.md)。旧板实测耗时不代表新板实测。
本次修改位于 `legacy-board-wss`(基于 `legacy-board`),默认地址为 `wss://banban.api.talkingq.com:443/ws`。沿用无 GNSS、默认音量 100 的旧板配置。上板暴露内核缺少 ECDSA 后,已补齐内核 TLS 能力并统一 APP/内核配置;从原明文旧板版本升级时必须更新内核和 APP。已有匹配 NIST 加速内核时,最新 X25519 调整只需更新 APP。
完整的问题根因、算法、提交、性能数据和影响说明见 [WSS 适配与性能优化完整总结](D:/banban/project/nfc_gnss/docs/WSS适配与性能优化完整总结.md)。2026-09-16 15:52 最新板端已确认 X25519 协商、完整 TLS 握手 1455 ms 和 HTTP 101。
@@ -81,7 +81,6 @@ SRC_FILES+= \
nfc.c \
DP1312EA.c \
mqtt.c \
mqtt_connect_diag.c \
vad_det.c \
sleep_wake.c \
battery.c \
@@ -414,9 +414,9 @@ uint8_t app_get_volume(void)
printf("get volume:%d, ret:%d\r\n", value, ret);
//ret = read_volume_file(&value, 1);
if(ret<0){
g_cur_volume = 70;
g_cur_volume = 100;
hal_kv_set("volume", &g_cur_volume, 1);
return VOLUME_70;
return VOLUME_100;
}
g_cur_volume = value;
@@ -15,6 +15,7 @@
#include "sockets.h"
#include "netdb.h"
#include "app_inc.h"
#include "../../../../ql-config/config/board_features.h"
// 新增宏定义(原代码中未定义)
#define GNSS_FAST_MODE_HOLD_TIME (5 * 60 * 1000) // FAST模式持续5分钟后切SLOW
@@ -320,9 +321,14 @@ void quec_gnss_callback(char *data, int len)
*===========================================================================*/
void set_gnss_mode(gnss_mode_t mode)
{
#if !NFC_GNSS_BOARD_HAS_GNSS
(void)mode;
return; /* No GNSS task exists to leave FAST mode and allow sleep. */
#else
g_gnss_mode = mode;
printf("[GNSSMode] Set to %s Mode\n",
(mode == GNSS_MODE_FAST) ? "Fast (30s)" : "Slow (5min)");
#endif
}
gnss_mode_t get_gnss_mode(void)
@@ -531,6 +537,11 @@ static void quec_gnss_test(void * argv)
void app_gnss_init(void)
{
#if !NFC_GNSS_BOARD_HAS_GNSS
printf("[GNSS] Legacy board: positioning disabled\n");
return;
#else
ql_task_t gnssTaskRef = NULL;
ql_rtos_task_create(&gnssTaskRef, 1024*4, 100, "gnss_Task_Ref", quec_gnss_test, NULL);
#endif
}
@@ -126,12 +126,7 @@ static void key_thread_entry(void *parameter)
app_set_app_operation(1);
}
int mic_pdp = app_get_data_call_net_status();
int mic_mqtt = get_mqtt_connect_status();
printf("[NET-GATE] mic pdp=%d mqtt=%d action=%s\r\n",
mic_pdp, mic_mqtt,
!mic_pdp ? "blocked_no_pdp" : (mic_mqtt != 1 ? "blocked_mqtt_offline" : "start_voice"));
if(mic_pdp && mic_mqtt == 1){
if(app_get_data_call_net_status()&&get_mqtt_connect_status()==1){
if(tts_play_interrupt_func()==-2){
app_play_tips_file(2);
}
@@ -36,7 +36,7 @@ const char *mp3_file[] ={
"ai_active_zh.mp3",//18
};
//http://banban.api.talkingq.com:8080/assets/roles/banban/tts_error.mp3
//http://175.24.73.253/assets/roles/zhuli/zh/tts_error.mp3
//#define PCM_FILE "http://47.113.99.138:7050/output_16k.pcm"
@@ -194,7 +194,7 @@ void app_file_check_download(void)
if(ql_access(filename_buff, 0)!=0){
printf("downing mp3 file %d,%s\r\n", index,filename_buff);
down_loop:
sprintf(url_buff, "http://banban.api.talkingq.com:8080/assets/roles/banban/%s", mp3_file[index]);
sprintf(url_buff, "http://101.35.224.118:8080/assets/roles/banban/%s", mp3_file[index]);
ret = http_file_download(url_buff);
if(ret!=0&&cnt++<3) goto down_loop;
@@ -1,9 +0,0 @@
#ifndef TALKINGQ_MQTT_CONNECT_DIAG_H
#define TALKINGQ_MQTT_CONNECT_DIAG_H
#include "MQTTClient.h"
/* Called by the single MQTT connection worker, before MQTTStartTask(). */
int talkingq_mqtt_connect_diagnosed(MQTTClient *client, MQTTPacket_connectData *options);
#endif
@@ -4,7 +4,6 @@
=================================================================*/
#include "MQTTClient.h"
#include "mqtt_connect_diag.h"
#include "ql_data_call.h"
#include "ql_application.h"
#include "sockets.h"
@@ -26,7 +25,7 @@
#define PROFILE_IDX 1
//#define DEFAULT_SERVER_DOMAIN "175.24.73.253"
#define DEFAULT_SERVER_DOMAIN "banban.api.talkingq.com"
#define DEFAULT_SERVER_DOMAIN "101.35.224.118"
#define DEFAULT_SERVER_CLIENT_USER "talkingq"
#define DEFAULT_SERVER_CLIENT_PASS "talkingq@2026"
#define DEFAULT_SERVER_CLIENT_ID "9376265c059e474e92c2e46208ae1dc5"
@@ -391,12 +390,11 @@ int parse_event_response(const char *json_str, response_t *resp)
/* [FIX] 安全处理 topic 名称,避免越界访问 */
static void messageArrived(MessageData* data)
{
int ret;
int payload_log_len;
uint32 queue_count = 0;
app_work_item *work_item;
static void messageArrived(MessageData* data)
{
int ret;
uint32 queue_count = 0;
app_work_item *work_item;
// 安全获取 topic 字符串(复制到本地缓冲区)
char topic_buf[128] = {0};
@@ -404,16 +402,10 @@ static void messageArrived(MessageData* data)
if (topic_len >= sizeof(topic_buf)) {
topic_len = sizeof(topic_buf) - 1;
}
memcpy(topic_buf, data->topicName->lenstring.data, topic_len);
topic_buf[topic_len] = '\0';
payload_log_len = data->message->payloadlen > 512 ? 512 : (int)data->message->payloadlen;
printf("[MQTT-RX] topic=%s len=%d payload=%.*s%s\r\n",
topic_buf, (int)data->message->payloadlen, payload_log_len,
(const char *)data->message->payload,
data->message->payloadlen > (unsigned int)payload_log_len ? "..." : "");
// 比较 topic 类型
memcpy(topic_buf, data->topicName->lenstring.data, topic_len);
topic_buf[topic_len] = '\0';
// 比较 topic 类型
int is_command = (strstr(topic_buf, "command") != NULL);
int is_event_resp = (strstr(topic_buf, "event_resp") != NULL) || (strstr(topic_buf, imei_buf) != NULL);
@@ -456,8 +448,6 @@ int app_mqtt_connect()
MQTTPacket_connectData connectData = MQTTPacket_connectData_initializer;
int mqtt_server_port=DEFAULT_SERVER_PORT;
unsigned int tcp_started;
int tcp_errno;
MQTT_CONN_LOCK();
if(mqtt_connect==1){
@@ -472,14 +462,7 @@ int app_mqtt_connect()
NetworkInit(&network, NULL, PROFILE_IDX);
MQTTClientInit(&client, &network, 30000, sendbuf, sizeof(sendbuf), readbuf, sizeof(readbuf));
char* address = DEFAULT_SERVER_DOMAIN;
printf("[MQTT-DIAG] TCP begin host=%s port=%d pdp=%d\r\n",
address, mqtt_server_port, app_get_data_call_net_status());
tcp_started = ql_rtos_get_systicks();
rc = NetworkConnect(&network, address, mqtt_server_port);
tcp_errno = rc != 0 ? ql_get_soc_errno() : 0;
printf("[MQTT-DIAG] TCP end rc=%d socket=%d elapsed_ms=%u errno_snapshot=%d\r\n",
rc, network.my_socket, (unsigned int)(ql_rtos_get_systicks() - tcp_started) * 5U, tcp_errno);
if (rc != 0)
if ((rc = NetworkConnect(&network, address, mqtt_server_port)) != 0)
{
RLOGE("Return code from network connect is %d", rc);
MQTTClientDeinit(&client);
@@ -503,8 +486,7 @@ int app_mqtt_connect()
connectData.cleansession = 0;
connectData.keepAliveInterval = 60;
printf("[MQTT-DIAG] saved_device_id=%d (registration needed when 0)\r\n", ret == 0);
if ((rc = talkingq_mqtt_connect_diagnosed(&client, &connectData)) != 0)
if ((rc = MQTTConnect(&client, &connectData)) != 0)
{
RLOGE("Return code from MQTT connect is %d", rc);
NetworkDisconnect(&network);
@@ -625,33 +607,15 @@ sub_exit:
//0:代表respone command
//1:代表send event
/* [FIX] 将连接状态检查和发送放在同一锁保护区内,避免竞争 */
int app_mqtt_publish(char *buf, int len, int type)
{
static uint32_t mqtt_pub_cnt_err=0;
int rc = -1;
int payload_log_len;
MQTTMessage message;
int cnt = 0;
static uint8_t publish_error = 0;
const char *publish_topic = NULL;
if (type == 0) {
publish_topic = respone_topicName;
} else if (type == 1) {
publish_topic = event_topicName;
} else if (type == 2) {
publish_topic = event_register;
} else {
RLOGE("invalid MQTT publish type=%d", type);
return -1;
}
payload_log_len = len > 512 ? 512 : len;
printf("[MQTT-TX] topic=%s len=%d payload=%.*s%s\r\n",
publish_topic, len, payload_log_len, buf,
len > payload_log_len ? "..." : "");
// 锁保护:检查连接状态并发送
int app_mqtt_publish(char *buf, int len, int type)
{
static uint32_t mqtt_pub_cnt_err=0;
int rc = -1;
MQTTMessage message;
int cnt = 0;
static uint8_t publish_error = 0;
// 锁保护:检查连接状态并发送
int lock_ret = ql_rtos_mutex_lock(g_mqtt_public_mutex, 5000);
if (lock_ret != 0) {
RLOGE("MQTT mutex lock timeout");
@@ -668,13 +632,18 @@ int app_mqtt_publish(char *buf, int len, int type)
message.payload = buf;
message.payloadlen = len;
while (cnt++ < 3) {
u32 start = ql_rtos_get_systicks();
rc = MQTTPublish(&client, publish_topic, &message);
RLOGD("Pulish time:%d len: %d\n", (ql_rtos_get_systicks()-start)*5, len);
printf("[MQTT-TX] topic=%s attempt=%d rc=%d\r\n", publish_topic, cnt, rc);
if (rc != 0) {
while (cnt++ < 3) {
u32 start = ql_rtos_get_systicks();
if (type == 0) {
rc = MQTTPublish(&client, respone_topicName, &message);
} else if (type == 1) {
rc = MQTTPublish(&client, event_topicName, &message);
} else if (type == 2) {
rc = MQTTPublish(&client, event_register, &message);
}
RLOGD("Pulish time:%d len: %d\n", (ql_rtos_get_systicks()-start)*5, len);
if (rc != 0) {
mqtt_pub_cnt_err++;
mqtt_public_error = 1;
RLOGE("ERROR Return code from MQTT publish is %d, cnt %d", rc, mqtt_pub_cnt_err);
@@ -1270,34 +1239,23 @@ void handle_nfc_trigger(const char *uuid)
free(json_str);
}
void handle_nfc_bind(const char *uuid)
{
char iccid[QL_SIM_ICCID_LEN * 2 + 1] = {0};
QL_SIM_ERROR_CODE sim_ret;
cJSON *root = cJSON_CreateObject();
cJSON *data = cJSON_CreateObject();
cJSON_AddStringToObject(root, "msg_id", "006");
cJSON_AddStringToObject(root, "status", "success");
cJSON_AddStringToObject(data, "uuid", uuid);
sim_ret = ql_sim_get_iccid(iccid, sizeof(iccid));
if (sim_ret == QL_SIM_SUCCESS && iccid[0] != '\0') {
printf("NFC bind ICCID:%s\r\n", iccid);
cJSON_AddStringToObject(data, "iccid", iccid);
} else {
printf("NFC bind get ICCID failed, ret:%d\r\n", sim_ret);
}
cJSON_AddItemToObject(root, "data", data);
char *json_str = cJSON_PrintUnformatted(root);
cJSON_Delete(root);
app_mqtt_publish(json_str, strlen(json_str), 0);
free(json_str);
}
void handle_nfc_bind(const char *uuid)
{
cJSON *root = cJSON_CreateObject();
cJSON *data = cJSON_CreateObject();
cJSON_AddStringToObject(root, "msg_id", "006");
cJSON_AddStringToObject(root, "status", "success");
cJSON_AddStringToObject(data, "uuid", uuid);
cJSON_AddItemToObject(root, "data", data);
char *json_str = cJSON_PrintUnformatted(root);
cJSON_Delete(root);
app_mqtt_publish(json_str, strlen(json_str), 0);
free(json_str);
}
@@ -1,107 +0,0 @@
#include <stdio.h>
#include <string.h>
#include "mqtt_connect_diag.h"
#include "sockets.h"
/* Only installed during synchronous CONNECT. Never dump the CONNECT payload:
* it contains credentials. Keep only the first four incoming CONNACK bytes. */
static struct {
int (*read)(Network *, unsigned char *, int, int);
int (*write)(Network *, unsigned char *, int, int);
unsigned int read_calls, write_calls, received, sent;
unsigned char prefix[4];
unsigned int prefix_size;
} mqtt_diag;
static unsigned int mqtt_diag_elapsed(unsigned int start)
{
return (unsigned int)(ql_rtos_get_systicks() - start) * 5U;
}
static int mqtt_diag_read(Network *network, unsigned char *buffer, int size, int timeout)
{
unsigned int start = ql_rtos_get_systicks();
int result = mqtt_diag.read(network, buffer, size, timeout);
int socket_error = result <= 0 ? ql_get_soc_errno() : 0;
int i;
++mqtt_diag.read_calls;
if (result > 0) {
mqtt_diag.received += (unsigned int)result;
for (i = 0; i < result && i < size && mqtt_diag.prefix_size < sizeof(mqtt_diag.prefix); ++i)
mqtt_diag.prefix[mqtt_diag.prefix_size++] = buffer[i];
}
printf("[MQTT-DIAG] read request=%d rc=%d timeout_ms=%d elapsed_ms=%u errno_snapshot=%d\r\n",
size, result, timeout, mqtt_diag_elapsed(start), socket_error);
return result;
}
static int mqtt_diag_write(Network *network, unsigned char *buffer, int size, int timeout)
{
unsigned int start = ql_rtos_get_systicks();
int result = mqtt_diag.write(network, buffer, size, timeout);
int socket_error = result <= 0 ? ql_get_soc_errno() : 0;
++mqtt_diag.write_calls;
if (result > 0) mqtt_diag.sent += (unsigned int)result;
printf("[MQTT-DIAG] write request=%d rc=%d timeout_ms=%d elapsed_ms=%u errno_snapshot=%d\r\n",
size, result, timeout, mqtt_diag_elapsed(start), socket_error);
return result;
}
static const char *mqtt_diag_connack_reason(unsigned char code)
{
switch (code) {
case 0: return "accepted";
case 1: return "protocol_version_rejected";
case 2: return "client_id_rejected";
case 3: return "server_unavailable";
case 4: return "bad_username_or_password";
case 5: return "not_authorized";
default: return "reserved_code";
}
}
int talkingq_mqtt_connect_diagnosed(MQTTClient *client, MQTTPacket_connectData *options)
{
Network *network = client->ipstack;
MQTTConnackData connack = {255, 255};
unsigned int start;
int result;
int valid_connack;
memset(&mqtt_diag, 0, sizeof(mqtt_diag));
mqtt_diag.read = network->mqttread;
mqtt_diag.write = network->mqttwrite;
if (!mqtt_diag.read || !mqtt_diag.write) {
printf("[MQTT-DIAG] missing transport callbacks; calling SDK unchanged\r\n");
return MQTTConnectWithResults(client, options, &connack);
}
printf("[MQTT-DIAG] CONNECT begin version=%u clean=%u keepalive_s=%u timeout_ms=%u already_connected=%d\r\n",
(unsigned int)options->MQTTVersion, (unsigned int)options->cleansession,
(unsigned int)options->keepAliveInterval, client->command_timeout_ms, client->isconnected);
network->mqttread = mqtt_diag_read;
network->mqttwrite = mqtt_diag_write;
start = ql_rtos_get_systicks();
result = MQTTConnectWithResults(client, options, &connack);
network->mqttread = mqtt_diag.read;
network->mqttwrite = mqtt_diag.write;
printf("[MQTT-DIAG] CONNECT end rc=%d elapsed_ms=%u write_calls=%u tx_bytes=%u read_calls=%u rx_bytes=%u\r\n",
result, mqtt_diag_elapsed(start), mqtt_diag.write_calls, mqtt_diag.sent,
mqtt_diag.read_calls, mqtt_diag.received);
valid_connack = mqtt_diag.prefix_size == 4 && mqtt_diag.prefix[0] == 0x20 &&
mqtt_diag.prefix[1] == 2 && (mqtt_diag.prefix[2] & 0xfe) == 0 &&
mqtt_diag.prefix[3] <= 5 &&
(mqtt_diag.prefix[3] == 0 || mqtt_diag.prefix[2] == 0);
if (valid_connack) {
printf("[MQTT-DIAG] CONNACK code=%u reason=%s session_present=%u\r\n",
(unsigned int)mqtt_diag.prefix[3], mqtt_diag_connack_reason(mqtt_diag.prefix[3]),
(unsigned int)mqtt_diag.prefix[2]);
} else if (mqtt_diag.received) {
printf("[MQTT-DIAG] no valid initial CONNACK: received_prefix_bytes=%u\r\n", mqtt_diag.prefix_size);
} else {
printf("[MQTT-DIAG] no response bytes returned by SDK read\r\n");
}
if (result != 0 && !mqtt_diag.write_calls)
printf("[MQTT-DIAG] failed before transport write; inspect client state/CONNECT serialization\r\n");
/* SDK read maps both EOF and some socket errors to -1. The errno snapshot
* can be stale; do not claim a broker rejection without a valid CONNACK. */
return result;
}
@@ -327,14 +327,14 @@ void vad_audio_process(void) {
ql_audio_record_read(record_hd, mic_buf, &record_len);
//如果检测到声音再去传输
// if(first_sound_detected){
//实际上每次编码需要输入1152字节的PCM数据
if(hal_os_rb_write(ring_sender, (uint8*)mic_buf, record_len, 25)>0){
if(first_sound_detected){
//实际上每次编码需要输入1152字节的PCM数据
if(hal_os_rb_write(ring_sender, (uint8*)mic_buf, record_len, 25)>0){
//quec_usbcdc_write((uint8*)mic_buf, record_len);
ql_rtos_semaphore_release(sender_sem); //告诉线程去获取录音数据
//quec_usbcdc_write((uint8*)mic_buf, record_len);
ql_rtos_semaphore_release(sender_sem); //告诉线程去获取录音数据
}
}
// }
#if 1
for(index=0; index<320; index++)
{
@@ -354,14 +354,12 @@ void vad_audio_process(void) {
// 连续达到阈值才确认检测到声音
if (speech_detect_count >= SPEECH_DETECT_THRESHOLD) {
#if 0
//实际上每次编码需要输入1152字节的PCM数据
if(hal_os_rb_write(ring_sender, (uint8*)mic_buf, record_len, 25)>0){
//quec_usbcdc_write((uint8*)mic_buf, record_len);
ql_rtos_semaphore_release(sender_sem); //告诉线程去获取录音数据
}
#endif
first_sound_detected = true;
set_state(STATE_RECORDING_ACTIVE);
+7
View File
@@ -0,0 +1,7 @@
#ifndef NFC_GNSS_BOARD_FEATURES_H
#define NFC_GNSS_BOARD_FEATURES_H
/* Legacy board has no GNSS. Set to 1 and rebuild kernel + app for GNSS boards. */
#define NFC_GNSS_BOARD_HAS_GNSS 0
#endif
@@ -4,6 +4,7 @@
============================================================================*/
#include "gps_app.h"
#include "../../../../ql-config/config/board_features.h"
#include <string.h>
#include "gps_debug.h"
#include "quec_hx_agps.h"
@@ -840,6 +841,10 @@ void quec_get_ocname_by_gnss_task(void * argv)
void quec_get_gnssid_task(void)
{
#if !NFC_GNSS_BOARD_HAS_GNSS
RTI_LOG("[GNSS] Legacy board: skip GNSS ID detection");
return;
#else
QuecOSStatus err = kNoErr;
quec_task_t quec_ocname_get = NULL;
err = quec_rtos_task_create(&quec_ocname_get, 2048, QUEC_APP_TASK_PRIORITY,"q_gocg", quec_get_ocname_by_gnss_task, NULL);
@@ -849,6 +854,7 @@ void quec_get_gnssid_task(void)
RTI_LOG("quec_get_ocname_by_gnss_task create failed!");
}
return;
#endif
}
#endif
+1 -1
View File
@@ -6,7 +6,7 @@ import struct
import subprocess
root = Path(__file__).resolve().parents[1]
output = root / 'artifacts/wss/new-board'
output = root / 'artifacts/wss/ecc-fix'
output.mkdir(parents=True, exist_ok=True)
toolchain = root / 'ql-cross-tool/win32/owtoolchain'
gcc = toolchain / 'gcc-arm-none-eabi/bin/arm-none-eabi-gcc.exe'
+1 -1
View File
@@ -11,7 +11,7 @@ parser.add_argument('chain', nargs='?', default=str(root / 'artifacts/wss/retest
parser.add_argument('--compare-nist', action='store_true')
parser.add_argument('--compare-windows', action='store_true')
args = parser.parse_args()
output = root / 'artifacts/wss/new-board/host-cert'
output = root / 'artifacts/wss/tls-speed/host-cert'
output.mkdir(parents=True, exist_ok=True)
chain = Path(args.chain).resolve()
sources = '''asn1parse asn1write bignum base64 pem oid pk pkparse pk_wrap rsa rsa_internal
-107
View File
@@ -1,107 +0,0 @@
"""Exercise actual MQTT diagnostic callbacks without a broker or credentials."""
from pathlib import Path
import subprocess
root = Path(__file__).resolve().parents[1]
output = root / 'artifacts/wss/new-board/mqtt-diag/tests'
output.mkdir(parents=True, exist_ok=True)
(output / 'MQTTClient.h').write_text(r'''
#ifndef TEST_MQTT_CLIENT_H
#define TEST_MQTT_CLIENT_H
typedef struct Network Network;
struct Network {
int (*mqttread)(Network *, unsigned char *, int, int);
int (*mqttwrite)(Network *, unsigned char *, int, int);
};
typedef struct { Network *ipstack; unsigned int command_timeout_ms; int isconnected; } MQTTClient;
typedef struct { unsigned char MQTTVersion, cleansession; unsigned short keepAliveInterval; } MQTTPacket_connectData;
typedef struct { unsigned char rc, sessionPresent; } MQTTConnackData;
int MQTTConnectWithResults(MQTTClient *, MQTTPacket_connectData *, MQTTConnackData *);
unsigned int ql_rtos_get_systicks(void);
#endif
''')
(output / 'sockets.h').write_text('int ql_get_soc_errno(void);\n')
harness = r'''
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include "mqtt_connect_diag.h"
static unsigned int ticks;
static int mode, offset, writes, reads;
unsigned int ql_rtos_get_systicks(void) { return ticks; }
int ql_get_soc_errno(void) { return 104; }
static int fake_write(Network *n, unsigned char *buf, int size, int timeout) {
(void)n;
assert(timeout == 30000 && size == 19);
assert(memcmp(buf, "PRIVATE_TEST_SECRET", 19) == 0);
++writes; ticks += 2;
return mode == 2 ? -1 : size;
}
static int fake_read(Network *n, unsigned char *buf, int size, int timeout) {
unsigned char response[] = {0x20, 2, 0, 0};
(void)n;
assert(timeout == 30000 && (size == 1 || size == 2));
++reads; ticks += 3;
if (mode == 3) return -1;
if (mode == 4) return 0;
if (mode == 1) response[3] = 5;
if (mode == 5) response[0] = 0x30;
if (mode == 7) { response[2] = 1; response[3] = 5; }
memcpy(buf, response + offset, size); offset += size;
return size;
}
int MQTTConnectWithResults(MQTTClient *client, MQTTPacket_connectData *options, MQTTConnackData *ack) {
unsigned char packet[] = "PRIVATE_TEST_SECRET";
unsigned char response[4] = {0};
int result;
assert(options->MQTTVersion == 3);
assert(ack->rc == 255);
if (mode == 6) return -1;
result = client->ipstack->mqttwrite(client->ipstack, packet, 19, 30000);
if (result != 19) { assert(result == -1); return -1; }
/* SDK reads fixed header, remaining length, then the two-byte body. */
result = client->ipstack->mqttread(client->ipstack, response, 1, 30000);
if (mode == 3 || mode == 4) { assert(result == (mode == 3 ? -1 : 0)); return -1; }
assert(result == 1);
assert(client->ipstack->mqttread(client->ipstack, response + 1, 1, 30000) == 1);
assert(client->ipstack->mqttread(client->ipstack, response + 2, 2, 30000) == 2);
assert(response[1] == 2);
return mode == 0 ? 0 : (mode == 1 ? 5 : -1);
}
int main(void) {
Network network = {fake_read, fake_write};
MQTTClient client = {&network, 30000, 0};
MQTTPacket_connectData options = {3, 0, 60};
for (mode = 0; mode < 8; ++mode) {
int expected = mode == 0 ? 0 : (mode == 1 ? 5 : -1);
ticks = mode == 0 ? 0xfffffffeU : 0; offset = writes = reads = 0;
printf("CASE %d\n", mode);
assert(talkingq_mqtt_connect_diagnosed(&client, &options) == expected);
assert(network.mqttread == fake_read && network.mqttwrite == fake_write);
assert(writes == (mode == 6 ? 0 : 1));
if (mode == 6 || mode == 2) assert(reads == 0);
}
return 0;
}
'''
(output / 'test.c').write_text(harness)
include = root / 'ql-application/threadx/interface/custom_app/inc'
source = root / 'ql-application/threadx/interface/custom_app/mqtt_connect_diag.c'
subprocess.run(['cl', '/nologo', '/W4', '/WX', '/TC', '/I' + str(output), '/I' + str(include),
str(source), str(output / 'test.c'), '/Fe' + str(output / 'test.exe')], cwd=output, check=True)
result = subprocess.check_output([str(output / 'test.exe')], text=True)
assert 'PRIVATE_TEST_SECRET' not in result
cases = [part for part in result.split('CASE ')[1:]]
assert len(cases) == 8
assert 'CONNACK code=0 reason=accepted' in cases[0] and 'elapsed_ms=55' in cases[0]
assert 'CONNACK code=5 reason=not_authorized' in cases[1]
assert 'write request=19 rc=-1' in cases[2]
assert 'read request=1 rc=-1' in cases[3]
assert 'read request=1 rc=0' in cases[4]
assert 'no valid initial CONNACK' in cases[5]
assert 'failed before transport write' in cases[6]
assert 'no valid initial CONNACK' in cases[7]
for case in cases[2:]:
assert '[MQTT-DIAG] CONNACK code=' not in case
(output / 'test-output.log').write_text(result)
print('PASS: transport arguments/return values, callback restoration, split CONNACK, rejection, invalid response, errors, no-I/O failure, tick wrap and credential redaction')
+9 -15
View File
@@ -1,4 +1,4 @@
"""Verify the new-board WSS package, including enabled GNSS task entry points."""
"""Verify the WSS flash package against the current legacy-board build."""
import hashlib
import json
import re
@@ -8,7 +8,7 @@ import zipfile
from pathlib import Path
root = Path(__file__).resolve().parents[1]
package = Path(sys.argv[1]).resolve() if len(sys.argv) > 1 else root / 'artifacts/wss/new-board/EG800AKCN_11LG-new-board-wss-x25519.zip'
package = Path(sys.argv[1]).resolve() if len(sys.argv) > 1 else root / 'artifacts/wss/full-handshake/EG800AKCN_11LG-legacy-wss-x25519.zip'
output = package.parent
output.mkdir(parents=True, exist_ok=True)
objdump = root / 'ql-cross-tool/win32/owtoolchain/gcc-arm-none-eabi/bin/arm-none-eabi-objdump.exe'
@@ -33,9 +33,8 @@ clock_body = subprocess.check_output([str(objdump), '-d', '--disassemble=x509_ge
assert '<mbedtls_platform_gmtime_r>' in clock_body and '<GetCurrentCompTime>' not in clock_body
(output / 'x509-clock.disasm.txt').write_text(clock_body)
report = {'package': package.name, 'sha256': hashlib.sha256(package.read_bytes()).hexdigest(),
'board': 'new board with GNSS enabled',
'linked_symbols': required, 'kernel_symbols': kernel_required,
'hardware_test': 'New-board package awaits device testing; legacy board verified X25519 full handshake at 1455 ms on 2026-09-16',
'hardware_test': '2026-09-16 14:38 device log: NIST TLS handshake 1820 ms and HTTP 101 passed; X25519 preference awaits device test',
'backend': '2026-09-16 11:32 CST: WSS 443 /ws HTTP 101; 8080 plaintext WS'}
with zipfile.ZipFile(package) as archive:
assert archive.testzip() is None, 'ZIP CRC failure'
@@ -47,21 +46,16 @@ with zipfile.ZipFile(package) as archive:
assert b'wss://banban.api.talkingq.com:443/ws' in archive.read('customer_app.bin')
assert b'WSS TLS handshake total=' in archive.read('customer_app.bin')
assert b'WSS TLS full handshake: prefer X25519' in archive.read('customer_app.bin')
assert b'Legacy board:' not in archive.read('customer_app.bin')
assert b'Legacy board:' not in archive.read('cp.bin')
assert b'gnss_Task_Ref' in archive.read('customer_app.bin')
report['download_manifest'] = json.loads(archive.read('download.json'))
task_create_address = int(re.search(r'^([0-9a-fA-F]+) T ql_rtos_task_create$', symbols, re.M).group(1), 16)
printf_address = int(re.search(r'^([0-9a-fA-F]+) T printf$', symbols, re.M).group(1), 16)
for symbol, binary, expected in [
('quec_get_gnssid_task', root / 'ql-kernel/threadx/build/kernel.axf', '<quec_rtos_task_create>'),
('app_gnss_init', app, '0x%08x' % (task_create_address | 1)),
('set_gnss_mode', app, 'strb')]:
('quec_get_gnssid_task', root / 'ql-kernel/threadx/build/kernel.axf', '<RTI_LOG>'),
('app_gnss_init', app, '0x%08x' % (printf_address | 1)),
('set_gnss_mode', app, 'bx\tlr')]:
body = subprocess.check_output([str(objdump), '-d', '--disassemble=' + symbol, str(binary)], text=True)
assert '<' + symbol + '>:' in body and expected in body, 'GNSS entry point disabled or unexpected code: ' + symbol
assert '<' + symbol + '>:' in body and expected in body and 'task_create' not in body
(output / (symbol + '.disasm.txt')).write_text(body)
for symbol, table in [('quec_get_ocname_by_gnss_task', kernel_symbols), ('quec_gnss_test', symbols)]:
assert re.search(r'\b' + symbol + r'$', table, re.M), 'Missing GNSS worker: ' + symbol
report['verification'] = 'PASS: ZIP CRC, current APP/kernel identity, GNSS task creation, WSS URL, ECDSA/GCM/RTC/NIST kernel symbols and handshake timing logs'
report['verification'] = 'PASS: ZIP CRC, current APP/kernel identity, GNSS bypass, WSS URL, ECDSA/GCM/RTC/NIST kernel symbols and handshake timing logs'
(output / 'verification.json').write_text(json.dumps(report, indent=2))
print(report['verification'])
print('SHA256:', report['sha256'])