feat: BLE plain-text cmd, AT probe fix, INI sed persist, log buffering
bt_uart.c: - Reader thread: accept plain-text (non-JSON) lines from BLE app; routes directly to extra_cmd_cb — test commands no longer need JSON wrapping - bt_uart_probe_and_upgrade(): fix AT+BAUD7 sequence — reopen fd at 115200 before sending AT+NAME/AT+RESET (was sending on stale 9600 fd) - bt_uart_send_json(): replace blocking mutex_lock with 200 ms trylock retry to avoid deadlock if writer thread dies holding the queue mutex - Reader select timeout 500 ms → 200 ms for faster plain-text flush buzzer.c: - buzzer_set_pattern(): write s_pattern directly then trylock+signal, so callers never block on the buzzer thread's mutex can_bus.c: - Add debug printf in can_bus_send_control_cmd() (temporary) event_recorder.c: - TEST_MODE_TIMEOUT_SEC 60 → 300 (5 min, enough for full test run) kp_firmware.c: - Persist INI keys via sed in-place instead of iniparser_dump_ini, preserving comments and original file structure - setvbuf stdout/stderr to line-buffered so tee log appears immediately Add GF_AI_Box_Test_Guide (.md/.docx/.pdf) and uclibc_compat.c
This commit is contained in:
parent
fa67a9f225
commit
cd96e33c62
96
CLAUDE.md
Normal file
96
CLAUDE.md
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
# CLAUDE.md
|
||||||
|
|
||||||
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||||
|
|
||||||
|
## Project Overview
|
||||||
|
|
||||||
|
KL630 Host Stream Firmware — a real-time semantic segmentation system running on a Kneron KL630 AI SoC. It processes video from a Sony IMX662 DOL-HDR dual-exposure camera for golf course monitoring (pedestrian/vehicle detection via STDC semantic segmentation). The binary runs on embedded Linux (ARMv7-A, uClibc 1.0.34) and outputs via RTSP streaming, HDMI display, BLE (iPad app), and CAN bus (golf cart control).
|
||||||
|
|
||||||
|
## Build
|
||||||
|
|
||||||
|
Cross-compilation is Docker-based (Ubuntu 20.04 + `arm-linux-gnueabihf-gcc`). **Docker Desktop must be running.**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Install Python dependencies (one-time)
|
||||||
|
pip install -r requirements.txt
|
||||||
|
|
||||||
|
# Compile via web UI (recommended)
|
||||||
|
python web_serve.py # then open http://localhost:8080/ and click "Compile"
|
||||||
|
|
||||||
|
# Compile via CLI
|
||||||
|
python build_and_serve.py # outputs build/kp_firmware_host_stream + build/host_stream.ini
|
||||||
|
```
|
||||||
|
|
||||||
|
`compile.sh` is the script executed inside the Docker container — it performs the actual `arm-linux-gnueabihf-gcc` invocation. The `Dockerfile` defines the build image.
|
||||||
|
|
||||||
|
## Deploy
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Via web UI: click "Deploy to KL630" (requires device IP configured)
|
||||||
|
|
||||||
|
# Manual: telnet into device, then pull from HTTP server on host:
|
||||||
|
wget http://<HOST_IP>:8080/deploy.sh -O /tmp/deploy.sh && sh /tmp/deploy.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Device paths:
|
||||||
|
- Binary: `/mnt/flash/vienna/kp_firmware_host_stream`
|
||||||
|
- Config: `/mnt/flash/plus/kp_firmware/kp_firmware_0/kp_firmware/bin/ini/host_stream.ini`
|
||||||
|
- Model (NEF): `/mnt/flash/plus/kp_firmware/kp_firmware_0/kp_firmware/bin/nef/`
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
No automated test framework. Manual verification:
|
||||||
|
- **Web UI** (`web_serve.py`): live RTSP preview, terminal, compile/deploy
|
||||||
|
- **Mock server** (`tools/mock_server/server.py`): simulates device events without hardware
|
||||||
|
- **On-device**: `ps | grep firmware`, `cat /tmp/fw.log`, `curl http://localhost:8080/api/stdc/stats`
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
**Video pipeline:**
|
||||||
|
```
|
||||||
|
IMX662 (1920×1080, DOL-HDR)
|
||||||
|
→ KL630 ISP (YUV420)
|
||||||
|
→ VMF (Vatics Media Framework)
|
||||||
|
├─ Stream 0 (1920×1080) → H.264 encoder → RTSP
|
||||||
|
└─ Stream 1 (724×362) → NPU (STDC inference)
|
||||||
|
→ Post-processing
|
||||||
|
→ HDMI overlay / BLE / CAN events
|
||||||
|
```
|
||||||
|
|
||||||
|
**Threading model** (all threads in `kp_firmware.c`):
|
||||||
|
- **Image thread**: reads ISP output from SSM → enqueues into FifoQ
|
||||||
|
- **Inference thread**: FifoQ dispatcher → NPU (STDC model, ModelId=32769)
|
||||||
|
- **Results thread**: FifoQ dequeue → post-processing → event dispatch
|
||||||
|
- **Video/VOC thread**: H.264 encoding + HDMI draw-box overlay
|
||||||
|
|
||||||
|
**Key source directories:**
|
||||||
|
- `src/host_stream/` — main firmware logic, BLE/UART (`bt_uart.c`), CAN bus (`can_bus.c`, `mcp2515.c`), event recording, FEC API, GPIO
|
||||||
|
- `src/app_flow/` — NPU inference orchestration (`stdc_inf_single_model.c`)
|
||||||
|
- `src/pre_post_proc/` — STDC post-processing: motion detection, class distribution, ROI analysis
|
||||||
|
- `include/common/` — shared data structures (KP_STRUCT, model types)
|
||||||
|
- `include/fake/` — stub headers for SDK components unavailable at compile time
|
||||||
|
- `ini/host_stream.ini` — runtime config template (model, ISP, FEC, RTSP, event thresholds)
|
||||||
|
- `tools/` — deploy scripts, mock server, Python control scripts
|
||||||
|
|
||||||
|
**Communication interfaces:**
|
||||||
|
- **BLE**: JSON over UART at 115200 baud (`/dev/ttyS1`) via DX-BT24 module → iPad app
|
||||||
|
- **CAN**: Standard CAN frames at 250 kbps via MCP2515 SPI → golf cart
|
||||||
|
- **RTSP**: H.264 from VMF encoder
|
||||||
|
- **Web API**: Flask HTTP server (`web_serve.py`) exposes compile/deploy/stats endpoints
|
||||||
|
|
||||||
|
## Code Conventions
|
||||||
|
|
||||||
|
- Global variables: `g_` prefix (e.g., `g_dwVocEnable`, `g_stdc_seg_map`)
|
||||||
|
- Boolean flags: `_bl` suffix; uint32_t: `_dw` suffix; struct pointers: `pt` prefix
|
||||||
|
- Config driven by INI file (iniparser library); sections: `[output]`, `[event]`, `[isp]`, `[fec]`
|
||||||
|
- Return codes: `KP_SUCCESS = 0`, negative values for errors
|
||||||
|
- Thread safety: mutex `g_stdc_seg_mutex` guards the shared segmentation map
|
||||||
|
- Logging: stdout/stderr + `/tmp/fw.log` on device; set `verbose_log=1` in INI for per-frame output
|
||||||
|
|
||||||
|
## Important Runtime Notes
|
||||||
|
|
||||||
|
- **ISP one-time setup**: DOL-HDR requires running firmware with `--setup` flag on first boot
|
||||||
|
- **Host IP**: configured in `.web_config.json` or via web UI; device uses it for `wget` downloads
|
||||||
|
- **FEC mode**: fish-eye correction mode 0–5 in INI; Mode 4 recommended for ceiling-mount
|
||||||
|
- **Model switching**: change `ModelId`/`JobId` in INI and redeploy
|
||||||
|
- **Startup mode**: `demo_rtsp.sh`, `demo_hdmi.sh`, or `demo_rtsp_hdmi.sh` on device sets output mode
|
||||||
BIN
GF_AI_Box_Test_Guide.docx
Normal file
BIN
GF_AI_Box_Test_Guide.docx
Normal file
Binary file not shown.
309
GF_AI_Box_Test_Guide.md
Normal file
309
GF_AI_Box_Test_Guide.md
Normal file
@ -0,0 +1,309 @@
|
|||||||
|
# GF AI Box — 測試人員操作指南
|
||||||
|
|
||||||
|
> 文件版本:v2.0
|
||||||
|
> 適用韌體:gf_ai_box_v4
|
||||||
|
> 更新日期:2026-06-14
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 一、測試環境準備
|
||||||
|
|
||||||
|
### 所需工具
|
||||||
|
- 藍芽串口工具(iOS:LightBlue / Serial Bluetooth Terminal;Android:Serial Bluetooth Terminal)
|
||||||
|
- CAN bus 分析儀(選配,用於驗證 CAN 訊號)
|
||||||
|
- 油門踏板(CAN 測試時需要)
|
||||||
|
|
||||||
|
### 連線方式
|
||||||
|
1. 開啟藍芽工具,掃描並連線到 AI Box 的 BLE 裝置
|
||||||
|
2. 完成握手後即可送出指令
|
||||||
|
3. 指令為**純文字**,大小寫不分,直接輸入後送出
|
||||||
|
|
||||||
|
### 重要說明
|
||||||
|
- **硬體 Reset 後預設為正常模式**,必須先送 `test_enter` 才能執行測試指令
|
||||||
|
- 進入測試模式後,AI Box 的 AI 推論結果暫停處理(NPU 繼續運算但不派發事件)
|
||||||
|
- 測試模式下如果 **60 秒無任何指令**,系統會自動退出測試模式並恢復正常
|
||||||
|
- 若 TEST_ALL 系列正在執行,請勿重複送出測試指令
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 二、基本控制指令
|
||||||
|
|
||||||
|
| 指令 | 說明 | 預期回覆 |
|
||||||
|
|------|------|---------|
|
||||||
|
| `test_enter` | 進入測試模式 | `TEST_ACK ENTER` |
|
||||||
|
| `test_exit` | 退出測試模式,恢復正常 | `TEST_EXIT_OK` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 三、大項一:BLE JSON 測試
|
||||||
|
|
||||||
|
**測試目的**:驗證各警示事件觸發時,藍芽送出的 JSON 格式與內容正確。
|
||||||
|
**注意**:只送出 BLE JSON,**不觸發** CAN bus 指令與 Buzzer。
|
||||||
|
|
||||||
|
### 3.1 單項測試指令
|
||||||
|
|
||||||
|
#### Violation(草地違規)
|
||||||
|
|
||||||
|
| 指令 | 說明 | 預期 BLE 回傳 JSON |
|
||||||
|
|------|------|-----------------|
|
||||||
|
| `test_ble violation 1` | 觸發 violation level=1 | `{"response_type":"violation","content":{"id":"...","date":"...","type":"lane","level":1}}` |
|
||||||
|
| `test_ble violation 0` | 解除 violation | `{"response_type":"violation","content":{"id":"...","date":"...","type":"lane","level":0}}` |
|
||||||
|
|
||||||
|
#### Collision(碰撞警示)
|
||||||
|
|
||||||
|
| 指令 | 說明 | 預期 BLE 回傳 JSON |
|
||||||
|
|------|------|-----------------|
|
||||||
|
| `test_ble collision 1` | 觸發 collision level=1(type=vehicle)| `{"response_type":"collision_warning","content":{"level":1,"type":"vehicle"}}` |
|
||||||
|
| `test_ble collision 0` | 解除 collision | `{"response_type":"collision_warning","content":{"level":0,"type":null}}` |
|
||||||
|
|
||||||
|
#### Alert(方向警示)
|
||||||
|
|
||||||
|
| 指令 | 說明 | 預期 BLE 回傳 JSON |
|
||||||
|
|------|------|-----------------|
|
||||||
|
| `test_ble alert 1 tree 0 null` | 左側 tree 警示,右側無 | `{"response_type":"alert","content":{"left":{"level":1,"type":"tree"},"right":{"level":0,"type":null}}}` |
|
||||||
|
| `test_ble alert 0 null 1 person` | 左側無,右側 person | `{"response_type":"alert","content":{"left":{"level":0,"type":null},"right":{"level":1,"type":"person"}}}` |
|
||||||
|
| `test_ble alert 1 tree 1 vehicle` | 左右都有警示 | `{"response_type":"alert","content":{"left":{"level":1,"type":"tree"},"right":{"level":1,"type":"vehicle"}}}` |
|
||||||
|
| `test_ble alert 0 null 0 null` | 解除所有 alert | `{"response_type":"alert","content":{"left":{"level":0,"type":null},"right":{"level":0,"type":null}}}` |
|
||||||
|
|
||||||
|
> **Alert 指令格式**:`test_ble alert <左level> <左type> <右level> <右type>`
|
||||||
|
> type 可以是:`tree` / `person` / `vehicle` / `null`
|
||||||
|
|
||||||
|
### 3.2 BLE 全項自動測試
|
||||||
|
|
||||||
|
| 指令 | 說明 |
|
||||||
|
|------|------|
|
||||||
|
| `test_ble_all` | 自動跑完所有 BLE 測項(約 20 秒)|
|
||||||
|
|
||||||
|
**TEST_BLE_ALL 執行順序:**
|
||||||
|
|
||||||
|
```
|
||||||
|
[00s] violation level=1 → 等 3 秒
|
||||||
|
[03s] violation level=0 → 等 2 秒
|
||||||
|
[05s] collision level=1 → 等 3 秒
|
||||||
|
[08s] collision level=0 → 等 2 秒
|
||||||
|
[10s] alert left=tree, right=無 → 等 3 秒
|
||||||
|
[13s] alert clear → 等 2 秒
|
||||||
|
[15s] alert left=tree, right=vehicle → 等 3 秒
|
||||||
|
[18s] alert clear → 等 2 秒
|
||||||
|
[20s] TEST_BLE_ALL_DONE,自動退出測試模式
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 四、大項二:CAN bus 測試
|
||||||
|
|
||||||
|
**測試目的**:驗證各警示事件觸發時,CAN bus 送出正確速度指令。
|
||||||
|
**注意**:只觸發 CAN bus 速度指令,**不送出** BLE JSON,**不啟動** Buzzer。
|
||||||
|
|
||||||
|
### 速度對照表
|
||||||
|
|
||||||
|
| 事件 | CAN 速度值 |
|
||||||
|
|------|-----------|
|
||||||
|
| 正常行駛 | 240 |
|
||||||
|
| Violation / Alert | 35 |
|
||||||
|
| Collision | 10 |
|
||||||
|
|
||||||
|
### 4.1 單項測試指令
|
||||||
|
|
||||||
|
#### Violation CAN 測試
|
||||||
|
|
||||||
|
| 指令 | CAN 速度 | 預期回覆 |
|
||||||
|
|------|---------|---------|
|
||||||
|
| `test_can violation 1` | 35 | `TEST_ACK CAN VIOLATION 1 speed=35` |
|
||||||
|
| `test_can violation 0` | 240 | `TEST_ACK CAN VIOLATION 0 speed=240` |
|
||||||
|
|
||||||
|
#### Collision CAN 測試
|
||||||
|
|
||||||
|
| 指令 | CAN 速度 | 預期回覆 |
|
||||||
|
|------|---------|---------|
|
||||||
|
| `test_can collision 1` | 10 | `TEST_ACK CAN COLLISION 1 speed=10` |
|
||||||
|
| `test_can collision 0` | 240 | `TEST_ACK CAN COLLISION 0 speed=240` |
|
||||||
|
|
||||||
|
#### Alert CAN 測試
|
||||||
|
|
||||||
|
| 指令 | CAN 速度 | 預期回覆 |
|
||||||
|
|------|---------|---------|
|
||||||
|
| `test_can alert 1` | 35 | `TEST_ACK CAN ALERT 1 speed=35` |
|
||||||
|
| `test_can alert 0` | 240 | `TEST_ACK CAN ALERT 0 speed=240` |
|
||||||
|
|
||||||
|
### 4.2 CAN 全項自動測試
|
||||||
|
|
||||||
|
| 指令 | 說明 |
|
||||||
|
|------|------|
|
||||||
|
| `test_can_all` | 自動跑完所有 CAN 測項(預設觀察 10 秒,約 39 秒)|
|
||||||
|
| `test_can_all 15` | 每個測項觀察時間改為 15 秒 |
|
||||||
|
|
||||||
|
**TEST_CAN_ALL 執行順序(預設觀察 10 秒):**
|
||||||
|
|
||||||
|
```
|
||||||
|
[00s] violation level=1 → speed=35
|
||||||
|
↓ 觀察 10 秒(踩油門,確認轉速被限制)
|
||||||
|
[10s] violation level=0 → speed=240
|
||||||
|
↓ 等 3 秒(確認恢復正常)
|
||||||
|
[13s] collision level=1 → speed=10
|
||||||
|
↓ 觀察 10 秒(踩油門,確認轉速被限制到最低)
|
||||||
|
[23s] collision level=0 → speed=240
|
||||||
|
↓ 等 3 秒
|
||||||
|
[26s] alert level=1 → speed=35
|
||||||
|
↓ 觀察 10 秒
|
||||||
|
[36s] alert level=0 → speed=240
|
||||||
|
↓ 等 3 秒
|
||||||
|
[39s] TEST_CAN_ALL_DONE,自動退出測試模式
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 五、大項三:Buzzer 測試
|
||||||
|
|
||||||
|
**測試目的**:驗證各警示事件觸發時,Buzzer 發出正確嗶聲節奏。
|
||||||
|
**注意**:只觸發 Buzzer,**不送出** BLE JSON,**不發** CAN bus 指令。
|
||||||
|
|
||||||
|
### Buzzer Pattern 對照表
|
||||||
|
|
||||||
|
| Pattern | 節奏 |
|
||||||
|
|---------|------|
|
||||||
|
| OFF(靜音)| 無聲 |
|
||||||
|
| GRASS(Violation 草地)| 慢速:500ms 響 / 500ms 停 |
|
||||||
|
| ALERT(Alert 方向警示)| 中速:300ms 響 / 200ms 停 |
|
||||||
|
| COLLISION(碰撞)| 急促:100ms 響 / 100ms 停 |
|
||||||
|
|
||||||
|
### 5.1 單項測試指令
|
||||||
|
|
||||||
|
| 指令 | Buzzer 動作 | 預期回覆 |
|
||||||
|
|------|-----------|---------|
|
||||||
|
| `test_buzzer grass` | 慢速嗶(500ms 響 / 500ms 停)| `TEST_ACK BUZZER GRASS OK` |
|
||||||
|
| `test_buzzer alert` | 中速嗶(300ms 響 / 200ms 停)| `TEST_ACK BUZZER ALERT OK` |
|
||||||
|
| `test_buzzer collision` | 急促嗶(100ms 響 / 100ms 停)| `TEST_ACK BUZZER COLLISION OK` |
|
||||||
|
| `test_buzzer off` | 靜音 | `TEST_ACK BUZZER OFF OK` |
|
||||||
|
|
||||||
|
> Buzzer 指令送出後立即生效,持續到下一個指令為止。
|
||||||
|
|
||||||
|
### 5.2 Buzzer 全項自動測試
|
||||||
|
|
||||||
|
| 指令 | 說明 |
|
||||||
|
|------|------|
|
||||||
|
| `test_buzzer_all` | 自動跑完所有 Buzzer 測項(約 12 秒)|
|
||||||
|
|
||||||
|
**TEST_BUZZER_ALL 執行順序:**
|
||||||
|
|
||||||
|
```
|
||||||
|
[00s] GRASS 慢速嗶 → 等 4 秒
|
||||||
|
[04s] ALERT 中速嗶 → 等 4 秒
|
||||||
|
[08s] COLLISION 急促嗶 → 等 4 秒
|
||||||
|
[12s] OFF 靜音
|
||||||
|
[12s] TEST_BUZZER_ALL_DONE,自動退出測試模式
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 六、全功能自動測試
|
||||||
|
|
||||||
|
| 指令 | 說明 |
|
||||||
|
|------|------|
|
||||||
|
| `test_all` | 依序執行 BLE → CAN → Buzzer 三大群組(預設觀察 10 秒,約 77 秒)|
|
||||||
|
| `test_all 15` | CAN 觀察時間改為 15 秒(約 92 秒)|
|
||||||
|
|
||||||
|
**執行順序(預設觀察 10 秒):**
|
||||||
|
|
||||||
|
1. **BLE 群組**(約 20 秒)— 驗證 BLE JSON
|
||||||
|
2. 休息 3 秒
|
||||||
|
3. **CAN 群組**(約 39 秒)— 驗證 CAN bus 速度指令
|
||||||
|
4. 休息 3 秒
|
||||||
|
5. **Buzzer 群組**(約 12 秒)— 驗證 Buzzer 嗶聲節奏
|
||||||
|
6. 送出 `TEST_ALL_DONE`,自動退出測試模式
|
||||||
|
|
||||||
|
**進度訊息:**
|
||||||
|
|
||||||
|
| 回傳訊息 | 說明 |
|
||||||
|
|---------|------|
|
||||||
|
| `TEST_ALL_START` | 全功能測試開始 |
|
||||||
|
| `TEST_ALL_BLE_GROUP_START` | BLE 群組開始 |
|
||||||
|
| `TEST_ALL_BLE_GROUP_DONE` | BLE 群組完成 |
|
||||||
|
| `TEST_ALL_CAN_GROUP_START` | CAN 群組開始 |
|
||||||
|
| `TEST_ALL_CAN_GROUP_DONE` | CAN 群組完成 |
|
||||||
|
| `TEST_ALL_BUZZER_GROUP_START` | Buzzer 群組開始 |
|
||||||
|
| `TEST_ALL_BUZZER_GROUP_DONE` | Buzzer 群組完成 |
|
||||||
|
| `TEST_ALL_DONE` | 全部完成 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 七、錯誤回覆說明
|
||||||
|
|
||||||
|
| 回傳訊息 | 說明 |
|
||||||
|
|---------|------|
|
||||||
|
| `TEST_ERR NOT_IN_TEST_MODE` | 尚未執行 `test_enter` |
|
||||||
|
| `TEST_ERR ALL_ALREADY_RUNNING` | TEST_ALL 系列仍在執行中,請等待完成 |
|
||||||
|
| `TEST_ERR UNKNOWN_CMD` | 指令不正確,請確認格式 |
|
||||||
|
| `TEST_ACK ALREADY_IN_TEST` | 已在測試模式,不需重複 `test_enter` |
|
||||||
|
| `TEST_TIMEOUT_EXIT` | 60 秒逾時,自動退出測試模式 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 八、快速測試流程範例
|
||||||
|
|
||||||
|
### 範例 A:只測試藍芽 JSON
|
||||||
|
|
||||||
|
```
|
||||||
|
test_enter
|
||||||
|
test_ble collision 1
|
||||||
|
(確認收到 collision_warning JSON)
|
||||||
|
test_ble collision 0
|
||||||
|
test_exit
|
||||||
|
```
|
||||||
|
|
||||||
|
### 範例 B:只測試 CAN 限速
|
||||||
|
|
||||||
|
```
|
||||||
|
test_enter
|
||||||
|
test_can collision 1
|
||||||
|
(踩油門 → 確認轉速被限制 + 確認 ACK speed=10)
|
||||||
|
test_can collision 0
|
||||||
|
(確認轉速恢復正常)
|
||||||
|
test_exit
|
||||||
|
```
|
||||||
|
|
||||||
|
### 範例 C:只測試 Buzzer
|
||||||
|
|
||||||
|
```
|
||||||
|
test_enter
|
||||||
|
test_buzzer collision
|
||||||
|
(聽到急促嗶聲)
|
||||||
|
test_buzzer off
|
||||||
|
(靜音)
|
||||||
|
test_exit
|
||||||
|
```
|
||||||
|
|
||||||
|
### 範例 D:全自動測試(觀察 15 秒)
|
||||||
|
|
||||||
|
```
|
||||||
|
test_enter
|
||||||
|
test_all 15
|
||||||
|
(等待約 92 秒,觀察各測項)
|
||||||
|
(收到 TEST_ALL_DONE 後自動退出測試模式)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 附錄:指令速查表
|
||||||
|
|
||||||
|
```
|
||||||
|
test_enter
|
||||||
|
test_exit
|
||||||
|
|
||||||
|
test_ble violation <level> level = 0 或 1
|
||||||
|
test_ble collision <level> level = 0 或 1
|
||||||
|
test_ble alert <左level> <左type> <右level> <右type>
|
||||||
|
test_ble_all
|
||||||
|
|
||||||
|
test_can violation <level> level = 0 或 1
|
||||||
|
test_can collision <level> level = 0 或 1
|
||||||
|
test_can alert <level> level = 0 或 1
|
||||||
|
test_can_all [觀察秒數] 預設 10 秒
|
||||||
|
|
||||||
|
test_buzzer grass
|
||||||
|
test_buzzer alert
|
||||||
|
test_buzzer collision
|
||||||
|
test_buzzer off
|
||||||
|
test_buzzer_all
|
||||||
|
|
||||||
|
te
|
||||||
BIN
GF_AI_Box_Test_Guide.pdf
Normal file
BIN
GF_AI_Box_Test_Guide.pdf
Normal file
Binary file not shown.
@ -165,21 +165,73 @@ static void *bt_reader_thread(void *arg)
|
|||||||
char buf[512];
|
char buf[512];
|
||||||
int pos = 0, depth = 0;
|
int pos = 0, depth = 0;
|
||||||
int in_str = 0, escaped = 0;
|
int in_str = 0, escaped = 0;
|
||||||
|
int is_text = 0; /* 1 = accumulating plain-text (non-JSON) line */
|
||||||
|
|
||||||
while (s_running) {
|
while (s_running) {
|
||||||
if (s_bt_fd < 0) { usleep(100000); continue; }
|
if (s_bt_fd < 0) { usleep(100000); continue; }
|
||||||
|
|
||||||
fd_set fds;
|
fd_set fds;
|
||||||
struct timeval tv = {0, 500000}; /* 500 ms — lets us recheck s_running */
|
struct timeval tv = {0, 200000}; /* 200 ms — flush plain-text if no newline */
|
||||||
FD_ZERO(&fds);
|
FD_ZERO(&fds);
|
||||||
FD_SET(s_bt_fd, &fds);
|
FD_SET(s_bt_fd, &fds);
|
||||||
if (select(s_bt_fd + 1, &fds, NULL, NULL, &tv) <= 0)
|
if (select(s_bt_fd + 1, &fds, NULL, NULL, &tv) <= 0) {
|
||||||
|
/* timeout: flush any pending plain-text (no newline from app) */
|
||||||
|
if (is_text && pos > 0) {
|
||||||
|
buf[pos] = '\0';
|
||||||
|
printf("[BT RX TEXT timeout-flush] %s\n", buf);
|
||||||
|
if (s_extra_cmd_cb) s_extra_cmd_cb(buf);
|
||||||
|
pos = 0; is_text = 0;
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
unsigned char c;
|
unsigned char c;
|
||||||
if (read(s_bt_fd, &c, 1) != 1) continue;
|
int n = (int)read(s_bt_fd, &c, 1);
|
||||||
|
if (n != 1) {
|
||||||
|
/* read timeout / error: flush any pending plain-text (app sent no newline) */
|
||||||
|
if (is_text && pos > 0) {
|
||||||
|
buf[pos] = '\0';
|
||||||
|
printf("[BT RX TEXT flush] %s\n", buf);
|
||||||
|
if (s_extra_cmd_cb) s_extra_cmd_cb(buf);
|
||||||
|
pos = 0; is_text = 0;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
/* JSON brace / string-literal state machine */
|
printf("[BT RX BYTE] 0x%02X '%c'\n", c, (c >= 0x20 && c < 0x7F) ? c : '.');
|
||||||
|
|
||||||
|
/* ── Plain-text line mode ──────────────────────────────────────────
|
||||||
|
* Entered when the first non-whitespace character at depth==0 is not '{'.
|
||||||
|
* Used for test-mode commands (test_enter, test_exit, etc.).
|
||||||
|
* Routes directly to s_extra_cmd_cb, bypassing JSON parser and
|
||||||
|
* handshake so test commands work with any BLE app without pairing.
|
||||||
|
* ─────────────────────────────────────────────────────────────── */
|
||||||
|
if (is_text) {
|
||||||
|
if (c == '\r' || c == '\n') {
|
||||||
|
if (pos > 0) {
|
||||||
|
buf[pos] = '\0';
|
||||||
|
printf("[BT RX TEXT] %s\n", buf);
|
||||||
|
if (s_extra_cmd_cb) s_extra_cmd_cb(buf);
|
||||||
|
pos = 0;
|
||||||
|
}
|
||||||
|
is_text = 0;
|
||||||
|
} else if (pos < (int)sizeof(buf) - 1) {
|
||||||
|
buf[pos++] = (char)c;
|
||||||
|
} else {
|
||||||
|
printf("[BT RX] text overflow — discarding\n");
|
||||||
|
pos = 0; is_text = 0;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Detect start of plain-text line (not JSON) ───────────────── */
|
||||||
|
if (depth == 0 && pos == 0 && c != '{' && c != '\r' && c != '\n' && c > ' ') {
|
||||||
|
is_text = 1;
|
||||||
|
buf[pos++] = (char)c;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── JSON brace / string-literal state machine ────────────────── */
|
||||||
if (!in_str) {
|
if (!in_str) {
|
||||||
if (c == '"') { in_str = 1; }
|
if (c == '"') { in_str = 1; }
|
||||||
else if (c == '{') { depth++; }
|
else if (c == '{') { depth++; }
|
||||||
@ -206,7 +258,7 @@ static void *bt_reader_thread(void *arg)
|
|||||||
/* Discard overlong garbage */
|
/* Discard overlong garbage */
|
||||||
if (pos >= (int)sizeof(buf) - 1) {
|
if (pos >= (int)sizeof(buf) - 1) {
|
||||||
printf("[BT RX] buffer overflow — discarding\n");
|
printf("[BT RX] buffer overflow — discarding\n");
|
||||||
pos = 0; depth = 0; in_str = 0; escaped = 0;
|
pos = 0; depth = 0; in_str = 0; escaped = 0; is_text = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -310,10 +362,13 @@ static void bt_set_module_name(int fd, const char *name)
|
|||||||
int n;
|
int n;
|
||||||
snprintf(cmd, sizeof(cmd), "AT+NAME%s\r\n", name);
|
snprintf(cmd, sizeof(cmd), "AT+NAME%s\r\n", name);
|
||||||
write(fd, cmd, strlen(cmd));
|
write(fd, cmd, strlen(cmd));
|
||||||
usleep(100000); /* 100 ms — module needs time to process */
|
usleep(300000); /* 300 ms — module needs time to process (longer after baud switch) */
|
||||||
n = read(fd, buf, sizeof(buf) - 1);
|
n = read(fd, buf, sizeof(buf) - 1);
|
||||||
buf[n > 0 ? n : 0] = '\0';
|
buf[n > 0 ? n : 0] = '\0';
|
||||||
printf("[BT] AT+NAME%s => %d byte(s): %s\n", name, n, n > 0 ? buf : "(none)");
|
printf("[BT] AT+NAME%s => %d byte(s): %s\n", name, n, n > 0 ? buf : "(none)");
|
||||||
|
/* Note: some DX-BT24 firmware versions accept AT+NAME silently (no response).
|
||||||
|
* Success is confirmed by scanning BLE after AT+RESET — the broadcast name
|
||||||
|
* should reflect the new value. */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── One-time AT baud setup (call only when bt_at_probe = 1 in INI) ─────── */
|
/* ── One-time AT baud setup (call only when bt_at_probe = 1 in INI) ─────── */
|
||||||
@ -329,12 +384,20 @@ static void bt_uart_probe_and_upgrade(const char *dev)
|
|||||||
n = read(fd, buf, sizeof(buf) - 1);
|
n = read(fd, buf, sizeof(buf) - 1);
|
||||||
buf[n > 0 ? n : 0] = '\0';
|
buf[n > 0 ? n : 0] = '\0';
|
||||||
printf("[BT] AT probe @ 115200 => %d byte(s): %s\n", n, n > 0 ? buf : "(none)");
|
printf("[BT] AT probe @ 115200 => %d byte(s): %s\n", n, n > 0 ? buf : "(none)");
|
||||||
close(fd);
|
|
||||||
|
|
||||||
if (n > 0) {
|
if (n > 0) {
|
||||||
printf("[BT] module already at 115200 — no upgrade needed\n");
|
/* Module already at 115200 — still set the BT name in case it changed */
|
||||||
|
printf("[BT] module already at 115200 — setting BT name\n");
|
||||||
|
bt_set_module_name(fd, s_bt_name);
|
||||||
|
write(fd, "AT+RESET\r\n", 10);
|
||||||
|
usleep(100000);
|
||||||
|
read(fd, buf, sizeof(buf) - 1);
|
||||||
|
close(fd);
|
||||||
|
usleep(500000); /* 500 ms: wait for module to reboot */
|
||||||
|
usleep(300000); /* 300 ms: let module stabilise */
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
close(fd);
|
||||||
|
|
||||||
/* No response at 115200 — try 9600 */
|
/* No response at 115200 — try 9600 */
|
||||||
fd = open_uart(dev, B9600, 0, 10);
|
fd = open_uart(dev, B9600, 0, 10);
|
||||||
@ -353,25 +416,30 @@ static void bt_uart_probe_and_upgrade(const char *dev)
|
|||||||
|
|
||||||
/* Module is at 9600 — send AT+BAUD7 to switch to 115200.
|
/* Module is at 9600 — send AT+BAUD7 to switch to 115200.
|
||||||
* The module switches baud immediately after responding; close this fd
|
* The module switches baud immediately after responding; close this fd
|
||||||
* before sending any further commands. */
|
* then reopen at 115200 before sending any further AT commands. */
|
||||||
printf("[BT] upgrading module from 9600 to 115200\n");
|
printf("[BT] upgrading module from 9600 to 115200\n");
|
||||||
memset(buf, 0, sizeof(buf));
|
memset(buf, 0, sizeof(buf));
|
||||||
write(fd, "AT+BAUD7\r\n", 10);
|
write(fd, "AT+BAUD7\r\n", 10);
|
||||||
usleep(100000); /* 100 ms: wait for ack + baud switch */
|
usleep(100000); /* 100 ms: wait for ack + baud switch */
|
||||||
n = read(fd, buf, sizeof(buf) - 1);
|
n = read(fd, buf, sizeof(buf) - 1);
|
||||||
printf("[BT] AT+BAUD7 => %d byte(s): %s\n", n, n > 0 ? buf : "(none)");
|
printf("[BT] AT+BAUD7 => %d byte(s): %s\n", n, n > 0 ? buf : "(none)");
|
||||||
bt_set_module_name(fd, s_bt_name); /* set BT broadcast name before reset */
|
close(fd);
|
||||||
|
usleep(300000); /* 300 ms: let module stabilise at 115200 */
|
||||||
|
|
||||||
|
/* Reopen at 115200 to send AT+NAME and AT+RESET at the correct baud */
|
||||||
|
fd = open_uart(dev, B115200, 0, 10);
|
||||||
|
if (fd < 0) {
|
||||||
|
printf("[BT] WARNING: reopen at 115200 failed after BAUD7\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
bt_set_module_name(fd, s_bt_name); /* set BT broadcast name */
|
||||||
write(fd, "AT+RESET\r\n", 10);
|
write(fd, "AT+RESET\r\n", 10);
|
||||||
usleep(100000);
|
usleep(100000);
|
||||||
read(fd, buf, sizeof(buf) - 1);
|
read(fd, buf, sizeof(buf) - 1);
|
||||||
close(fd);
|
close(fd);
|
||||||
usleep(500000); /* 500 ms: wait for module to reboot */
|
usleep(500000); /* 500 ms: wait for module to reboot */
|
||||||
close(fd);
|
usleep(300000); /* 300 ms: let module stabilise */
|
||||||
usleep(300000); /* 300 ms: let module stabilise at 115200 */
|
|
||||||
|
|
||||||
/* Reopen at 115200 and reset so the new baud persists across power cycles */
|
|
||||||
fd = open_uart(dev, B115200, 0, 10);
|
|
||||||
if (fd < 0) return;
|
|
||||||
printf("[BT] upgrade complete — set bt_at_probe = 0 in INI to skip AT on next boot\n");
|
printf("[BT] upgrade complete — set bt_at_probe = 0 in INI to skip AT on next boot\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -453,7 +521,25 @@ void bt_uart_send_json(const char *json)
|
|||||||
msg->next = NULL;
|
msg->next = NULL;
|
||||||
if (!msg->json) { free(msg); return; }
|
if (!msg->json) { free(msg); return; }
|
||||||
|
|
||||||
pthread_mutex_lock(&s_q_mtx);
|
/* Use trylock with retry instead of blocking lock.
|
||||||
|
* The bt_writer_thread may have died while holding s_q_mtx (e.g. due to
|
||||||
|
* SIGPIPE / SIGSEGV on a bad UART write), leaving the mutex permanently
|
||||||
|
* locked. A blocking pthread_mutex_lock would then stall the bt_reader
|
||||||
|
* thread forever. We retry for up to 200 ms; if the lock is still
|
||||||
|
* unavailable after that, we drop the message and log a warning so the
|
||||||
|
* reader thread can continue handling subsequent commands. */
|
||||||
|
int locked = 0;
|
||||||
|
for (int i = 0; i < 200; i++) {
|
||||||
|
if (pthread_mutex_trylock(&s_q_mtx) == 0) { locked = 1; break; }
|
||||||
|
usleep(1000); /* 1 ms per retry */
|
||||||
|
}
|
||||||
|
if (!locked) {
|
||||||
|
printf("[BT] WARN: send_json: s_q_mtx stuck (200 ms timeout), dropping: %.48s\n", json);
|
||||||
|
free(msg->json);
|
||||||
|
free(msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (s_q_tail) {
|
if (s_q_tail) {
|
||||||
s_q_tail->next = msg;
|
s_q_tail->next = msg;
|
||||||
s_q_tail = msg;
|
s_q_tail = msg;
|
||||||
|
|||||||
@ -132,12 +132,16 @@ int buzzer_init(void)
|
|||||||
|
|
||||||
void buzzer_set_pattern(buzzer_pattern_t pattern)
|
void buzzer_set_pattern(buzzer_pattern_t pattern)
|
||||||
{
|
{
|
||||||
pthread_mutex_lock(&s_mtx);
|
/* Write s_pattern directly (volatile, 32-bit ARM = atomic word write).
|
||||||
if (s_pattern != pattern) {
|
* Use trylock so callers never block even if the buzzer thread holds
|
||||||
s_pattern = pattern;
|
* s_mtx briefly between its own pthread_mutex_lock and cond_wait.
|
||||||
|
* The thread will pick up the new pattern on its next cycle even
|
||||||
|
* without the signal, because s_pattern is checked after every sleep. */
|
||||||
|
s_pattern = pattern;
|
||||||
|
if (pthread_mutex_trylock(&s_mtx) == 0) {
|
||||||
pthread_cond_signal(&s_cond);
|
pthread_cond_signal(&s_cond);
|
||||||
|
pthread_mutex_unlock(&s_mtx);
|
||||||
}
|
}
|
||||||
pthread_mutex_unlock(&s_mtx);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void buzzer_close(void)
|
void buzzer_close(void)
|
||||||
|
|||||||
@ -665,7 +665,7 @@ static void handle_test_cmd(const char *raw)
|
|||||||
} else if (strncmp(cmd, "TEST_CAN_ALL", 12) == 0) {
|
} else if (strncmp(cmd, "TEST_CAN_ALL", 12) == 0) {
|
||||||
if (s_test_all_running) { test_reply("TEST_ERR ALL_ALREADY_RUNNING"); return; }
|
if (s_test_all_running) { test_reply("TEST_ERR ALL_ALREADY_RUNNING"); return; }
|
||||||
int obs = TEST_OBS_DEFAULT_SEC;
|
int obs = TEST_OBS_DEFAULT_SEC;
|
||||||
sscanf(cmd + 12, " %d", &obs);
|
int lv = atoi(obs + 12);
|
||||||
if (obs <= 0 || obs > 60) obs = TEST_OBS_DEFAULT_SEC;
|
if (obs <= 0 || obs > 60) obs = TEST_OBS_DEFAULT_SEC;
|
||||||
s_test_all_running = 1;
|
s_test_all_running = 1;
|
||||||
pthread_t tid; pthread_create(&tid, NULL, test_can_all_thread, (void*)(intptr_t)obs); pthread_detach(tid);
|
pthread_t tid; pthread_create(&tid, NULL, test_can_all_thread, (void*)(intptr_t)obs); pthread_detach(tid);
|
||||||
|
|||||||
@ -275,15 +275,43 @@ int loadConfig(HOST_STREAM_INIT_OPT_T* pHostStreamInit)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (need_ini_save) {
|
if (need_ini_save) {
|
||||||
FILE *fini = fopen(HOST_STREAM_CONFIG_PATH, "w");
|
/* Use sed in-place replacement to update ONLY the specific keys we need to
|
||||||
if (fini) {
|
* persist. This preserves the original file structure, all other sections,
|
||||||
iniparser_dump_ini(ini, fini);
|
* inline comments, and avoids iniparser_dump_ini stripping all comments and
|
||||||
fclose(fini);
|
* potentially producing an unreadable flat-format output on some iniparser
|
||||||
printf("[BT] INI persisted: bt_name=%s device_id=%s bt_at_probe=%d\n",
|
* versions.
|
||||||
bt_name, device_id, bt_at_probe ? 0 : bt_at_probe);
|
*
|
||||||
} else {
|
* Pattern: match the key at the start of a line (with optional spaces around
|
||||||
printf("[BT] WARNING: cannot write INI back (%s)\n", HOST_STREAM_CONFIG_PATH);
|
* '='), replace the value part. The sed expression targets the [event] block
|
||||||
|
* values by their key names — safe because these keys only appear once. */
|
||||||
|
char cmd[512];
|
||||||
|
int rc = 0;
|
||||||
|
|
||||||
|
/* device_id */
|
||||||
|
snprintf(cmd, sizeof(cmd),
|
||||||
|
"sed -i 's|^device_id[[:space:]]*=.*|device_id = %s|' %s",
|
||||||
|
device_id, HOST_STREAM_CONFIG_PATH);
|
||||||
|
rc |= system(cmd);
|
||||||
|
|
||||||
|
/* bt_name */
|
||||||
|
snprintf(cmd, sizeof(cmd),
|
||||||
|
"sed -i 's|^bt_name[[:space:]]*=.*|bt_name = %s|' %s",
|
||||||
|
bt_name, HOST_STREAM_CONFIG_PATH);
|
||||||
|
rc |= system(cmd);
|
||||||
|
|
||||||
|
/* bt_at_probe → 0 (only when we just ran the AT probe) */
|
||||||
|
if (bt_at_probe) {
|
||||||
|
snprintf(cmd, sizeof(cmd),
|
||||||
|
"sed -i 's|^bt_at_probe[[:space:]]*=.*|bt_at_probe = 0|' %s",
|
||||||
|
HOST_STREAM_CONFIG_PATH);
|
||||||
|
rc |= system(cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (rc == 0)
|
||||||
|
printf("[BT] INI persisted via sed: bt_name=%s device_id=%s bt_at_probe=%d\n",
|
||||||
|
bt_name, device_id, bt_at_probe ? 0 : bt_at_probe);
|
||||||
|
else
|
||||||
|
printf("[BT] WARNING: sed INI update returned %d (%s)\n", rc, HOST_STREAM_CONFIG_PATH);
|
||||||
}
|
}
|
||||||
|
|
||||||
event_recorder_init(ev_up, ev_sd, ev_max_mb, ev_delay, ev_enable);
|
event_recorder_init(ev_up, ev_sd, ev_max_mb, ev_delay, ev_enable);
|
||||||
@ -440,6 +468,12 @@ void free_stream_init(HOST_STREAM_INIT_OPT_T* pHostStreamInit) {
|
|||||||
|
|
||||||
int main (int argc, char* argv[])
|
int main (int argc, char* argv[])
|
||||||
{
|
{
|
||||||
|
/* Force line-buffered stdout so printf output appears immediately even
|
||||||
|
* when piped to tee (e.g. ./firmware 2>&1 | tee /tmp/fw.log).
|
||||||
|
* Without this, the pipe triggers full-buffering and log appears in chunks. */
|
||||||
|
setvbuf(stdout, NULL, _IOLBF, 0);
|
||||||
|
setvbuf(stderr, NULL, _IOLBF, 0);
|
||||||
|
|
||||||
uint32_t major, minor, patch, build;
|
uint32_t major, minor, patch, build;
|
||||||
pthread_t thread_f = 0;
|
pthread_t thread_f = 0;
|
||||||
|
|
||||||
|
|||||||
72
src/host_stream/uclibc_compat.c
Normal file
72
src/host_stream/uclibc_compat.c
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
/*
|
||||||
|
* uclibc_compat.c — uClibc 相容性 shim
|
||||||
|
*
|
||||||
|
* 問題:cross-compiler 使用 glibc 標頭,-D_GNU_SOURCE 加上 C99/C11 模式會讓
|
||||||
|
* glibc stdio.h 把所有 scanf 系列呼叫改成 __isoc99_* 版本。
|
||||||
|
* 但目標裝置 (KL630) 跑的是 uClibc 1.0.34,沒有這些符號,
|
||||||
|
* 導致執行時出現 "can't resolve symbol" 錯誤。
|
||||||
|
*
|
||||||
|
* 解法:在 binary 本身提供全部 __isoc99_* scanf 系列符號,
|
||||||
|
* 轉呼叫 uClibc 本身有的底層函數。
|
||||||
|
* 由於符號定義在 binary 裡,執行時不需要 uClibc 提供。
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
/* 移除 glibc 所有 scanf 相關 macro,避免遞迴展開 */
|
||||||
|
#undef scanf
|
||||||
|
#undef fscanf
|
||||||
|
#undef sscanf
|
||||||
|
#undef vscanf
|
||||||
|
#undef vfscanf
|
||||||
|
#undef vsscanf
|
||||||
|
|
||||||
|
/* ---- 非 v 系列 ---- */
|
||||||
|
|
||||||
|
int __isoc99_scanf(const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
int ret;
|
||||||
|
va_start(ap, fmt);
|
||||||
|
ret = vscanf(fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int __isoc99_fscanf(FILE *stream, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
int ret;
|
||||||
|
va_start(ap, fmt);
|
||||||
|
ret = vfscanf(stream, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int __isoc99_sscanf(const char *s, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
int ret;
|
||||||
|
va_start(ap, fmt);
|
||||||
|
ret = vsscanf(s, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- v 系列(直接轉呼叫 uClibc 底層) ---- */
|
||||||
|
|
||||||
|
int __isoc99_vscanf(const char *fmt, va_list ap)
|
||||||
|
{
|
||||||
|
return vscanf(fmt, ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
int __isoc99_vfscanf(FILE *stream, const char *fmt, va_list ap)
|
||||||
|
{
|
||||||
|
return vfscanf(stream, fmt, ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
int __isoc99_vsscanf(const char *s, const char *fmt, va_list ap)
|
||||||
|
{
|
||||||
|
return vsscanf(s, fmt, ap);
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user