9ceb218f80
Co-authored-by: Cursor <cursoragent@cursor.com>
1799 lines
51 KiB
C
1799 lines
51 KiB
C
/**
|
||
* @file fc41d_ble.c
|
||
* @brief FC41D 蓝牙调试完整实现:AT 收发、模组电源、BLE 状态机、DIP 选路、comm_link 后端
|
||
*
|
||
* 模块内部分为四段(均为 static 或本文件内可见):
|
||
* 1. AT 命令层 — UART7 与 FC41D 对话,解析 +QBLESTAT URC
|
||
* 2. 硬件控制 — PE6(FC41D_RST) 复位脉冲与释放,非电源使能
|
||
* 3. BLE 状态机 — 外设初始化、等待连接、QBLETRANMODE 透传数据交互
|
||
* 4. 对外接口 — ble_link_* / comm_link_ble_* 供 tcp_ser_task、comm_link 调用
|
||
*/
|
||
|
||
#include "publicdata/public_define.h"
|
||
|
||
#if BLE_DEBUG_EN
|
||
|
||
#include "fc41d_ble.h"
|
||
#include "fc41d_ble_priv.h"
|
||
#include "debug_link.h"
|
||
#if TCP_DEBUG_EN
|
||
#include "debug_files.h"
|
||
#include "tcp_protocol.h"
|
||
#endif
|
||
#include "gpio.h"
|
||
#include "usart.h"
|
||
#include "sys_drv_init.h"
|
||
#include "publicdata/publicdata.h"
|
||
#include "app_init/app_init.h"
|
||
#include "sub_comm/sub_comm_task.h"
|
||
#include "wdt_task/wdt_task.h"
|
||
#include "FreeRTOS.h"
|
||
#include "gd32h7xx.h"
|
||
|
||
#include <string.h>
|
||
#include <stdio.h>
|
||
|
||
/* UART7(FC41D AT)专用:不扩展公共 usart 驱动,逻辑限定在本文件 */
|
||
static void s_fc41d_uart_init(void)
|
||
{
|
||
rcu_periph_clock_enable(RCU_GPIOE);
|
||
rcu_periph_clock_enable(RCU_UART7);
|
||
|
||
gpio_af_set(GPIOE, GPIO_AF_8, GPIO_PIN_1);
|
||
gpio_mode_set(GPIOE, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_1);
|
||
gpio_output_options_set(GPIOE, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_1);
|
||
|
||
gpio_af_set(GPIOE, GPIO_AF_8, GPIO_PIN_0);
|
||
gpio_mode_set(GPIOE, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_0);
|
||
gpio_output_options_set(GPIOE, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_0);
|
||
|
||
usart_deinit(UART7);
|
||
usart_word_length_set(UART7, USART_WL_8BIT);
|
||
usart_stop_bit_set(UART7, USART_STB_1BIT);
|
||
usart_parity_config(UART7, USART_PM_NONE);
|
||
usart_baudrate_set(UART7, FC41D_UART_BAUD);
|
||
usart_hardware_flow_rts_config(UART7, USART_RTS_DISABLE);
|
||
usart_hardware_flow_cts_config(UART7, USART_CTS_DISABLE);
|
||
usart_receive_config(UART7, USART_RECEIVE_ENABLE);
|
||
usart_transmit_config(UART7, USART_TRANSMIT_ENABLE);
|
||
usart_interrupt_enable(UART7, USART_INT_RBNE);
|
||
usart_enable(UART7);
|
||
}
|
||
|
||
static void s_fc41d_uart_discard(void)
|
||
{
|
||
uint8_t scrap[64];
|
||
|
||
while (SET == usart_flag_get(UART7, USART_FLAG_RBNE)) {
|
||
(void)usart_data_receive(UART7);
|
||
}
|
||
for (;;) {
|
||
int n = v_usart_read_rx_buffer(FC41D_UART_HW_NUM, scrap, (uint16_t)sizeof(scrap));
|
||
if (n <= 0) {
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
static uint16_t s_fc41d_uart_drain_rx(uint8_t *buf, uint16_t cap)
|
||
{
|
||
uint16_t total = 0U;
|
||
|
||
if (buf == NULL || cap == 0U) {
|
||
return 0U;
|
||
}
|
||
while (total < cap && (SET == usart_flag_get(UART7, USART_FLAG_RBNE))) {
|
||
buf[total++] = (uint8_t)usart_data_receive(UART7);
|
||
}
|
||
{
|
||
int got = v_usart_read_rx_buffer(FC41D_UART_HW_NUM, &buf[total], (uint16_t)(cap - total));
|
||
if (got > 0) {
|
||
total += (uint16_t)got;
|
||
}
|
||
}
|
||
return total;
|
||
}
|
||
|
||
#define FC41D_LINK_CAP() (BLE->io.link_rx_acc_cap)
|
||
#define FC41D_APP_CAP() (BLE->io.app_rx_cap)
|
||
|
||
/* =============================================================================
|
||
* 模块常量与上下文宏(状态封装在 comm_link_ctx.backend.ble / session)
|
||
* ============================================================================= */
|
||
|
||
/** 单次 QBLEGATTSNTFY 最大载荷(字节) */
|
||
#define FC41D_GATT_TX_CHUNK 400U
|
||
|
||
#define SESS (comm_link_session())
|
||
#define BLE (&(comm_link_ctx_get()->backend.ble))
|
||
|
||
/* =============================================================================
|
||
* AT 命令层 — 静态函数
|
||
* ============================================================================= */
|
||
|
||
static void v_fc41d_parse_qble_recv(uint8_t *buf, uint16_t *p_len);
|
||
static void v_at_uart_read_all(void);
|
||
static void v_at_uart_discard(void);
|
||
static void v_at_uart_drain_payload(void);
|
||
static int s_fc41d_resp_has_ok(const uint8_t *buf, uint16_t len);
|
||
static int s_fc41d_link_or_at_has_ok(void);
|
||
static void s_ble_drain_uart_before_gatt(void);
|
||
#if TCP_DEBUG_EN
|
||
static void s_ble_log_json_io(const char *dir, const uint8_t *data, uint32_t len);
|
||
#endif
|
||
static void v_at_set_transparent(uint8_t on);
|
||
static uint8_t u8_at_is_transparent(void);
|
||
static int s_ble_enter_transparent_blocking(void);
|
||
static int s_ble_exit_transparent_blocking(void);
|
||
/**
|
||
* @brief 将 UART 读到的原始字节追加到 BLE->at.rx_acc
|
||
* @param data 数据指针
|
||
* @param len 字节数;为 0 或 data 为 NULL 时不操作
|
||
* @note 缓冲满时从头覆盖,避免长时间运行撑爆内存
|
||
*/
|
||
static void v_at_append_rx(const uint8_t *data, uint16_t len)
|
||
{
|
||
uint16_t i;
|
||
|
||
if ((data == NULL) || (len == 0U)) {
|
||
return;
|
||
}
|
||
for (i = 0U; i < len; i++) {
|
||
if (BLE->at.rx_acc_len >= (FC41D_AT_RX_ACC_SIZE - 1U)) {
|
||
BLE->at.rx_acc_len = 0U;
|
||
}
|
||
BLE->at.rx_acc[BLE->at.rx_acc_len++] = (char)data[i];
|
||
BLE->at.rx_acc[BLE->at.rx_acc_len] = '\0';
|
||
}
|
||
}
|
||
|
||
static void v_fc41d_shift_rx(uint8_t *buf, uint16_t *p_len, uint16_t consume)
|
||
{
|
||
uint16_t len = *p_len;
|
||
|
||
if (consume >= len) {
|
||
*p_len = 0U;
|
||
return;
|
||
}
|
||
memmove(buf, &buf[consume], (size_t)(len - consume));
|
||
*p_len = (uint16_t)(len - consume);
|
||
}
|
||
|
||
static void s_app_rx_clear(void)
|
||
{
|
||
BLE->io.app_rx_wr = 0U;
|
||
BLE->io.app_rx_rd = 0U;
|
||
BLE->io.qble_payload_need = 0U;
|
||
BLE->io.link_rx_acc_len = 0U;
|
||
}
|
||
|
||
static void s_ble_io_free(void)
|
||
{
|
||
if (BLE->io.link_rx_acc != NULL) {
|
||
vPortFree(BLE->io.link_rx_acc);
|
||
BLE->io.link_rx_acc = NULL;
|
||
}
|
||
if (BLE->io.app_rx != NULL) {
|
||
vPortFree(BLE->io.app_rx);
|
||
BLE->io.app_rx = NULL;
|
||
}
|
||
BLE->io.link_rx_acc_cap = 0U;
|
||
BLE->io.app_rx_cap = 0U;
|
||
s_app_rx_clear();
|
||
}
|
||
|
||
static int s_ble_io_set_caps(uint16_t link_cap, uint16_t app_cap)
|
||
{
|
||
if ((link_cap == 0U) || (app_cap == 0U)) {
|
||
return -1;
|
||
}
|
||
if ((BLE->io.link_rx_acc != NULL) && (BLE->io.link_rx_acc_cap == link_cap) &&
|
||
(BLE->io.app_rx != NULL) && (BLE->io.app_rx_cap == app_cap)) {
|
||
s_app_rx_clear();
|
||
return 0;
|
||
}
|
||
|
||
s_ble_io_free();
|
||
|
||
BLE->io.link_rx_acc = (uint8_t *)pvPortMalloc((size_t)link_cap);
|
||
BLE->io.app_rx = (uint8_t *)pvPortMalloc((size_t)app_cap);
|
||
if ((BLE->io.link_rx_acc == NULL) || (BLE->io.app_rx == NULL)) {
|
||
s_ble_io_free();
|
||
BT_PRINT("BLE RX buf alloc fail link=%u app=%u\r\n",
|
||
(unsigned)link_cap, (unsigned)app_cap);
|
||
return -1;
|
||
}
|
||
|
||
BLE->io.link_rx_acc_cap = link_cap;
|
||
BLE->io.app_rx_cap = app_cap;
|
||
s_app_rx_clear();
|
||
return 0;
|
||
}
|
||
|
||
#if TCP_DEBUG_EN
|
||
static int s_ble_diag_trace(void)
|
||
{
|
||
return (system_filetransfer_is_active() != 0) ? 1 : 0;
|
||
}
|
||
|
||
int comm_link_ble_diag_trace(void)
|
||
{
|
||
return s_ble_diag_trace();
|
||
}
|
||
|
||
static void s_ble_log_json_io(const char *dir, const uint8_t *data, uint32_t len)
|
||
{
|
||
uint32_t off = 0U;
|
||
enum { kChunk = 256U };
|
||
|
||
if ((dir == NULL) || (data == NULL) || (len == 0U)) {
|
||
return;
|
||
}
|
||
printf("[BLE-JSON] %s (%u): ", dir, (unsigned)len);
|
||
if (len <= kChunk) {
|
||
printf("%.*s\r\n", (int)len, (const char *)data);
|
||
return;
|
||
}
|
||
while (off < len) {
|
||
uint32_t n = len - off;
|
||
|
||
if (n > kChunk) {
|
||
n = kChunk;
|
||
}
|
||
printf("%.*s", (int)n, (const char *)(data + off));
|
||
off += n;
|
||
}
|
||
printf("\r\n");
|
||
}
|
||
|
||
static uint16_t s_app_rx_pending(void)
|
||
{
|
||
uint16_t cap = FC41D_APP_CAP();
|
||
uint16_t rd = BLE->io.app_rx_rd;
|
||
uint16_t wr = BLE->io.app_rx_wr;
|
||
|
||
if (cap == 0U) {
|
||
return 0U;
|
||
}
|
||
if (wr >= rd) {
|
||
return (uint16_t)(wr - rd);
|
||
}
|
||
return (uint16_t)(cap - rd + wr);
|
||
}
|
||
|
||
static void s_fw_log_link_cache(const char *when)
|
||
{
|
||
uint16_t len;
|
||
uint16_t app_pending;
|
||
|
||
if (s_ble_diag_trace() == 0) {
|
||
return;
|
||
}
|
||
if (BLE->io.link_rx_acc == NULL) {
|
||
return;
|
||
}
|
||
|
||
len = BLE->io.link_rx_acc_len;
|
||
app_pending = s_app_rx_pending();
|
||
if ((len == 0U) && (app_pending == 0U) && (BLE->io.qble_payload_need == 0U)) {
|
||
return;
|
||
}
|
||
printf("[FW_UPGRADE] LINK-CACHE@%s link_len=%u need=%u link_cap=%u app_pending=%u\r\n",
|
||
when,
|
||
(unsigned)len,
|
||
(unsigned)BLE->io.qble_payload_need,
|
||
(unsigned)FC41D_LINK_CAP(),
|
||
(unsigned)app_pending);
|
||
if (len > 0U) {
|
||
tcp_firmware_upgrade_log_buf("LINK-CACHE-DATA", BLE->io.link_rx_acc, (uint32_t)len);
|
||
}
|
||
}
|
||
#endif
|
||
|
||
static int s_app_rx_push(const uint8_t *data, uint16_t len)
|
||
{
|
||
uint16_t i;
|
||
uint16_t cap = FC41D_APP_CAP();
|
||
|
||
if ((data == NULL) || (len == 0U) || (BLE->io.app_rx == NULL) || (cap == 0U)) {
|
||
return 0;
|
||
}
|
||
for (i = 0U; i < len; i++) {
|
||
uint16_t next = (uint16_t)((BLE->io.app_rx_wr + 1U) % cap);
|
||
if (next == BLE->io.app_rx_rd) {
|
||
BT_PRINT("app_rx overflow, lost %u bytes\r\n",
|
||
(unsigned)(len - i));
|
||
break;
|
||
}
|
||
BLE->io.app_rx[BLE->io.app_rx_wr] = data[i];
|
||
BLE->io.app_rx_wr = next;
|
||
}
|
||
#if TCP_DEBUG_EN
|
||
if ((s_ble_diag_trace() != 0) && (i > 0U)) {
|
||
tcp_firmware_upgrade_log_buf("APP-PUSH", data, (uint32_t)i);
|
||
s_fw_log_link_cache("after-app-push");
|
||
}
|
||
#endif
|
||
return (int)i;
|
||
}
|
||
|
||
static int s_app_rx_pop(uint8_t *buf, uint32_t cap_out)
|
||
{
|
||
uint32_t n = 0U;
|
||
uint16_t cap = FC41D_APP_CAP();
|
||
|
||
if ((buf == NULL) || (cap_out == 0U) || (BLE->io.app_rx == NULL) || (cap == 0U)) {
|
||
return 0;
|
||
}
|
||
while ((n < cap_out) && (BLE->io.app_rx_rd != BLE->io.app_rx_wr)) {
|
||
buf[n++] = BLE->io.app_rx[BLE->io.app_rx_rd];
|
||
BLE->io.app_rx_rd = (uint16_t)((BLE->io.app_rx_rd + 1U) % cap);
|
||
}
|
||
return (int)n;
|
||
}
|
||
|
||
static void v_at_set_transparent(uint8_t on)
|
||
{
|
||
BLE->sm.transparent = (on != 0U) ? 1U : 0U;
|
||
if (on != 0U) {
|
||
v_at_uart_discard();
|
||
s_app_rx_clear();
|
||
BT_PRINT("Enter QBLETRANMODE uart passthrough\r\n");
|
||
} else {
|
||
BT_PRINT("Leave uart passthrough\r\n");
|
||
}
|
||
}
|
||
|
||
static uint8_t u8_at_is_transparent(void)
|
||
{
|
||
return BLE->sm.transparent;
|
||
}
|
||
|
||
/** 透传模式:UART 载荷直接写入 app_rx 环 */
|
||
static void v_at_uart_drain_payload(void)
|
||
{
|
||
uint8_t chunk[128];
|
||
uint16_t n;
|
||
|
||
if (u8_at_is_transparent() == 0U) {
|
||
return;
|
||
}
|
||
do {
|
||
n = s_fc41d_uart_drain_rx(chunk, sizeof(chunk));
|
||
if (n > 0U) {
|
||
#if TCP_DEBUG_EN
|
||
if (s_ble_diag_trace() != 0) {
|
||
tcp_firmware_upgrade_log_buf("UART-RX", chunk, (uint32_t)n);
|
||
}
|
||
#endif
|
||
(void)s_app_rx_push(chunk, n);
|
||
}
|
||
} while (n == (uint16_t)sizeof(chunk));
|
||
}
|
||
|
||
/**
|
||
* @brief 排空 UART 到 link 解析器(保留 +QBLERECV 应用数据,不清硬件 FIFO 中业务载荷)
|
||
*/
|
||
static void v_at_uart_drain_link(void)
|
||
{
|
||
uint8_t round;
|
||
|
||
for (round = 0U; round < 4U; round++) {
|
||
v_at_uart_read_all();
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief 在 link 缓冲内对齐到下一帧 +QBLERECV(仅帧头对齐,禁止 strstr 全缓冲搜索)
|
||
*/
|
||
static void v_fc41d_qble_resync(uint8_t *buf, uint16_t *p_len)
|
||
{
|
||
uint16_t len = *p_len;
|
||
uint16_t i;
|
||
|
||
if (len < 10U) {
|
||
return;
|
||
}
|
||
if (strncmp((char *)buf, "+QBLERECV:", 10) == 0) {
|
||
return;
|
||
}
|
||
for (i = 1U; i + 10U <= len; i++) {
|
||
if (strncmp((char *)&buf[i], "+QBLERECV:", 10) == 0) {
|
||
v_fc41d_shift_rx(buf, p_len, i);
|
||
return;
|
||
}
|
||
}
|
||
if (len > (FC41D_LINK_CAP() / 2U)) {
|
||
v_fc41d_shift_rx(buf, p_len, (uint16_t)(len - 64U));
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief 在缓冲区内查找 +QBLERECV 帧头(续接载荷时避免吞掉下一帧)
|
||
* @return 帧头偏移,未找到 -1
|
||
*/
|
||
static int s_fc41d_qble_header_at(const uint8_t *buf, uint16_t len)
|
||
{
|
||
uint16_t i;
|
||
|
||
if ((buf == NULL) || (len < 10U)) {
|
||
return -1;
|
||
}
|
||
for (i = 0U; i + 10U <= len; i++) {
|
||
if (strncmp((char *)&buf[i], "+QBLERECV:", 10) == 0) {
|
||
return (int)i;
|
||
}
|
||
}
|
||
return -1;
|
||
}
|
||
|
||
/** @return 1 缓冲开头为 +QBLERECV: 的前缀(UART 分片头行) */
|
||
static int s_fc41d_is_qble_header_prefix(const uint8_t *buf, uint16_t len)
|
||
{
|
||
static const char k_hdr[] = "+QBLERECV:";
|
||
uint16_t i;
|
||
|
||
if ((buf == NULL) || (len == 0U)) {
|
||
return 0;
|
||
}
|
||
for (i = 0U; i < len; i++) {
|
||
if (i >= (sizeof(k_hdr) - 1U)) {
|
||
return 0;
|
||
}
|
||
if (buf[i] != (uint8_t)k_hdr[i]) {
|
||
return 0;
|
||
}
|
||
}
|
||
return 1;
|
||
}
|
||
|
||
/** @return 1 缓冲区内疑似 +QBLERECV AT 头行(含分片,禁止写入 app_rx) */
|
||
static int s_fc41d_looks_like_qble_at_text(const uint8_t *buf, uint16_t len)
|
||
{
|
||
uint16_t scan;
|
||
|
||
if ((buf == NULL) || (len == 0U)) {
|
||
return 0;
|
||
}
|
||
if (s_fc41d_is_qble_header_prefix(buf, len)) {
|
||
return 1;
|
||
}
|
||
if (len >= 7U && memcmp(buf, "BLERECV", 7) == 0) {
|
||
return 1;
|
||
}
|
||
if (len >= 6U && memcmp(buf, "LERECV", 6) == 0) {
|
||
return 1;
|
||
}
|
||
if (len >= 5U && memcmp(buf, "RECV:", 5) == 0) {
|
||
return 1;
|
||
}
|
||
if (len >= 4U && memcmp(buf, "ECV:", 4) == 0) {
|
||
return 1;
|
||
}
|
||
if (len >= 2U && buf[0] == (uint8_t)'V' && buf[1] == (uint8_t)':') {
|
||
return 1;
|
||
}
|
||
for (scan = 0U; (scan + 7U) <= len && scan < 64U; scan++) {
|
||
if (memcmp(&buf[scan], "BLERECV", 7) == 0) {
|
||
return 1;
|
||
}
|
||
if ((scan + 5U) <= len && memcmp(&buf[scan], "FFF2,", 5) == 0) {
|
||
return 1;
|
||
}
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/** @return 1 缓冲含 AT 头行 CRLF(GATT JSON 载荷内不应出现裸 CRLF) */
|
||
static int s_fc41d_buf_has_at_crlf(const uint8_t *buf, uint16_t len)
|
||
{
|
||
uint16_t i;
|
||
|
||
if ((buf == NULL) || (len < 2U)) {
|
||
return 0;
|
||
}
|
||
for (i = 0U; i + 1U < len && i < 80U; i++) {
|
||
if (buf[i] == (uint8_t)'\r' && buf[i + 1U] == (uint8_t)'\n') {
|
||
return 1;
|
||
}
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/** @return 1 无 +QBLERECV 包裹的 JSON/Base64 续接块(UART 拆包遗留) */
|
||
static int s_fc41d_is_orphan_app_payload(const uint8_t *buf, uint16_t len)
|
||
{
|
||
uint8_t c;
|
||
|
||
if ((buf == NULL) || (len == 0U)) {
|
||
return 0;
|
||
}
|
||
c = buf[0];
|
||
if (c == (uint8_t)'{') {
|
||
return 1;
|
||
}
|
||
if ((c >= (uint8_t)'A' && c <= (uint8_t)'Z') ||
|
||
(c >= (uint8_t)'a' && c <= (uint8_t)'z') ||
|
||
(c >= (uint8_t)'0' && c <= (uint8_t)'9') ||
|
||
(c == (uint8_t)'=') || (c == (uint8_t)'+') || (c == (uint8_t)'/')) {
|
||
return (s_fc41d_looks_like_qble_at_text(buf, len) == 0) ? 1 : 0;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
static void v_fc41d_drain_orphan_app_payload(uint8_t *buf, uint16_t *p_len)
|
||
{
|
||
uint16_t len = *p_len;
|
||
|
||
if (s_ble_diag_trace() == 0) {
|
||
return;
|
||
}
|
||
if ((len == 0U) || (BLE->io.qble_payload_need > 0U)) {
|
||
return;
|
||
}
|
||
if (buf[0] == (uint8_t)'+') {
|
||
return;
|
||
}
|
||
if (s_fc41d_looks_like_qble_at_text(buf, len)) {
|
||
return;
|
||
}
|
||
if (s_fc41d_qble_header_at(buf, len) >= 0) {
|
||
return;
|
||
}
|
||
if (s_fc41d_is_orphan_app_payload(buf, len) == 0) {
|
||
return;
|
||
}
|
||
(void)s_app_rx_push(buf, len);
|
||
v_fc41d_shift_rx(buf, p_len, len);
|
||
}
|
||
|
||
/**
|
||
* @brief 续接上一帧未收齐的 +QBLERECV 二进制载荷
|
||
*/
|
||
static void v_fc41d_qble_payload_continue(uint8_t *buf, uint16_t *p_len)
|
||
{
|
||
uint16_t len = *p_len;
|
||
uint16_t take;
|
||
int hdr;
|
||
|
||
if (BLE->io.qble_payload_need == 0U || len == 0U) {
|
||
return;
|
||
}
|
||
|
||
hdr = s_fc41d_qble_header_at(buf, len);
|
||
if (hdr == 0) {
|
||
/* 下一帧 +QBLERECV 已到(多次 Write 或 UART 对齐),放弃残缺续接 */
|
||
BLE->io.qble_payload_need = 0U;
|
||
return;
|
||
}
|
||
|
||
if (hdr < 0) {
|
||
/* UART 分片到达 +QBLERECV 头行时勿当作 GATT 载荷 */
|
||
if (s_fc41d_looks_like_qble_at_text(buf, len) ||
|
||
s_fc41d_buf_has_at_crlf(buf, len)) {
|
||
BLE->io.qble_payload_need = 0U;
|
||
return;
|
||
}
|
||
}
|
||
|
||
take = len;
|
||
if (hdr > 0) {
|
||
take = (uint16_t)hdr;
|
||
}
|
||
if (take > BLE->io.qble_payload_need) {
|
||
take = BLE->io.qble_payload_need;
|
||
}
|
||
if (take == 0U) {
|
||
return;
|
||
}
|
||
|
||
(void)s_app_rx_push(buf, take);
|
||
BLE->io.qble_payload_need = (uint16_t)(BLE->io.qble_payload_need - take);
|
||
v_fc41d_shift_rx(buf, p_len, take);
|
||
}
|
||
|
||
/**
|
||
* @brief 解析缓冲开头的 +QBLERECV 帧(头行 + 定长载荷);禁止在 Base64 载荷中误匹配
|
||
* @return 1 消费了一帧或坏头行,0 等待更多数据
|
||
*/
|
||
static int v_fc41d_try_parse_qble_recv_at_start(uint8_t *buf, uint16_t *p_len)
|
||
{
|
||
char *base = (char *)buf;
|
||
char *eol = NULL;
|
||
int role = 0;
|
||
int data_len = 0;
|
||
char uuid[12] = {0};
|
||
uint16_t len = *p_len;
|
||
uint16_t payload_off;
|
||
uint16_t payload_in_buf;
|
||
|
||
if (len < 12U || strncmp(base, "+QBLERECV:", 10) != 0) {
|
||
return 0;
|
||
}
|
||
|
||
for (uint16_t i = 10U; i + 1U < len; i++) {
|
||
if (base[i] == '\r' && base[i + 1U] == '\n') {
|
||
eol = &base[i];
|
||
break;
|
||
}
|
||
}
|
||
if (eol == NULL) {
|
||
return 0;
|
||
}
|
||
|
||
if (sscanf(base, "+QBLERECV:%d,%11[^,],%d", &role, uuid, &data_len) < 3 ||
|
||
data_len <= 0) {
|
||
v_fc41d_shift_rx(buf, p_len, (uint16_t)((eol + 2) - base));
|
||
return 1;
|
||
}
|
||
|
||
payload_off = (uint16_t)((eol + 2) - base);
|
||
payload_in_buf = (len > payload_off) ? (uint16_t)(len - payload_off) : 0U;
|
||
|
||
if (payload_in_buf >= (uint16_t)data_len) {
|
||
(void)s_app_rx_push((const uint8_t *)&buf[payload_off], (uint16_t)data_len);
|
||
v_fc41d_shift_rx(buf, p_len, (uint16_t)(payload_off + (uint16_t)data_len));
|
||
BLE->io.qble_payload_need = 0U;
|
||
#if TCP_DEBUG_EN
|
||
if (s_ble_diag_trace() != 0) {
|
||
printf("[FW_UPGRADE] QBLE full uuid=%s data_len=%d pushed=%d\r\n",
|
||
uuid, data_len, data_len);
|
||
}
|
||
#endif
|
||
} else {
|
||
if (payload_in_buf > 0U) {
|
||
(void)s_app_rx_push((const uint8_t *)&buf[payload_off], payload_in_buf);
|
||
}
|
||
if ((payload_off + payload_in_buf) < len &&
|
||
s_fc41d_qble_header_at((const uint8_t *)&buf[payload_off + payload_in_buf],
|
||
(uint16_t)(len - payload_off - payload_in_buf)) >= 0) {
|
||
/* 下一 +QBLERECV 已到达:按多帧拼接 JSON,勿等待本帧 data_len 填满 */
|
||
BLE->io.qble_payload_need = 0U;
|
||
v_fc41d_shift_rx(buf, p_len, (uint16_t)(payload_off + payload_in_buf));
|
||
} else if (payload_in_buf == 0U) {
|
||
/* 仅收到头行,等待后续纯二进制载荷 */
|
||
BLE->io.qble_payload_need = (uint16_t)data_len;
|
||
v_fc41d_shift_rx(buf, p_len, payload_off);
|
||
#if TCP_DEBUG_EN
|
||
if (s_ble_diag_trace() != 0) {
|
||
printf("[FW_UPGRADE] QBLE hdr-only uuid=%s data_len=%d need=%u\r\n",
|
||
uuid, data_len, (unsigned)BLE->io.qble_payload_need);
|
||
}
|
||
#endif
|
||
} else {
|
||
/* 同一 +QBLERECV 帧 UART 分片,续接剩余 payload */
|
||
BLE->io.qble_payload_need = (uint16_t)((uint16_t)data_len - payload_in_buf);
|
||
v_fc41d_shift_rx(buf, p_len, len);
|
||
#if TCP_DEBUG_EN
|
||
if (s_ble_diag_trace() != 0) {
|
||
printf("[FW_UPGRADE] QBLE uart-split uuid=%s data_len=%d in_buf=%u need=%u\r\n",
|
||
uuid, data_len, (unsigned)payload_in_buf,
|
||
(unsigned)BLE->io.qble_payload_need);
|
||
}
|
||
#endif
|
||
}
|
||
}
|
||
(void)role;
|
||
return 1;
|
||
}
|
||
|
||
static void v_fc41d_parse_qble_recv(uint8_t *buf, uint16_t *p_len)
|
||
{
|
||
while ((BLE->io.qble_payload_need == 0U) &&
|
||
(*p_len > 0U) &&
|
||
(v_fc41d_try_parse_qble_recv_at_start(buf, p_len) != 0)) {
|
||
}
|
||
}
|
||
|
||
static void v_fc41d_parse_link_rx(void)
|
||
{
|
||
uint16_t len;
|
||
uint16_t pos = 0U;
|
||
|
||
if ((BLE->io.link_rx_acc == NULL) || (FC41D_LINK_CAP() == 0U)) {
|
||
return;
|
||
}
|
||
|
||
if (BLE->io.qble_payload_need == 0U) {
|
||
v_fc41d_qble_resync(BLE->io.link_rx_acc, &BLE->io.link_rx_acc_len);
|
||
} else if (s_fc41d_looks_like_qble_at_text(BLE->io.link_rx_acc, BLE->io.link_rx_acc_len)) {
|
||
BLE->io.qble_payload_need = 0U;
|
||
}
|
||
|
||
v_fc41d_parse_qble_recv(BLE->io.link_rx_acc, &BLE->io.link_rx_acc_len);
|
||
|
||
v_fc41d_qble_payload_continue(BLE->io.link_rx_acc, &BLE->io.link_rx_acc_len);
|
||
if (BLE->io.qble_payload_need > 0U) {
|
||
if (s_fc41d_looks_like_qble_at_text(BLE->io.link_rx_acc, BLE->io.link_rx_acc_len)) {
|
||
BLE->io.qble_payload_need = 0U;
|
||
v_fc41d_parse_qble_recv(BLE->io.link_rx_acc, &BLE->io.link_rx_acc_len);
|
||
} else {
|
||
#if TCP_DEBUG_EN
|
||
if (s_ble_diag_trace() != 0) {
|
||
printf("[FW_UPGRADE] LINK-WAIT need=%u len=%u\r\n",
|
||
(unsigned)BLE->io.qble_payload_need,
|
||
(unsigned)BLE->io.link_rx_acc_len);
|
||
s_fw_log_link_cache("link-wait");
|
||
}
|
||
#endif
|
||
return;
|
||
}
|
||
}
|
||
|
||
len = BLE->io.link_rx_acc_len;
|
||
pos = 0U;
|
||
|
||
while (pos < len) {
|
||
uint16_t line_end = pos;
|
||
int found_eol = 0;
|
||
|
||
while (line_end + 1U < len) {
|
||
if ((BLE->io.link_rx_acc[line_end] == '\r') &&
|
||
(BLE->io.link_rx_acc[line_end + 1U] == '\n')) {
|
||
found_eol = 1;
|
||
break;
|
||
}
|
||
line_end++;
|
||
}
|
||
if (found_eol == 0) {
|
||
break;
|
||
}
|
||
|
||
{
|
||
char line[192];
|
||
uint16_t line_len = (uint16_t)(line_end - pos);
|
||
|
||
if (line_len >= (uint16_t)sizeof(line)) {
|
||
line_len = (uint16_t)(sizeof(line) - 1U);
|
||
}
|
||
memcpy(line, &BLE->io.link_rx_acc[pos], line_len);
|
||
line[line_len] = '\0';
|
||
|
||
if (strncmp(line, "+QBLERECV:", 10) == 0) {
|
||
break;
|
||
}
|
||
|
||
if (strncmp(line, "+QBLESTAT", 9) == 0) {
|
||
if (strstr(line, "DISCONNECTED") != NULL) {
|
||
BLE->at.pending_urc = FC41D_BLE_URC_DISCONNECTED;
|
||
BT_PRINT("URC: BLE DISCONNECTED (%s)\r\n", line);
|
||
} else if (strstr(line, "CONNECTED") != NULL) {
|
||
BLE->at.pending_urc = FC41D_BLE_URC_CONNECTED;
|
||
BT_PRINT("URC: BLE CONNECTED (%s)\r\n", line);
|
||
}
|
||
} else if (strncmp(line, "+QBLEPEER", 9) == 0) {
|
||
BLE->at.pending_urc = FC41D_BLE_URC_CONNECTED;
|
||
BT_PRINT("URC: BLE CONNECTED (%s)\r\n", line);
|
||
}
|
||
|
||
v_at_append_rx((const uint8_t *)line, line_len);
|
||
v_at_append_rx((const uint8_t *)"\r\n", 2U);
|
||
}
|
||
|
||
pos = (uint16_t)(line_end + 2U);
|
||
}
|
||
|
||
if (pos > 0U) {
|
||
v_fc41d_shift_rx(BLE->io.link_rx_acc, &BLE->io.link_rx_acc_len, pos);
|
||
}
|
||
|
||
if (system_filetransfer_is_active() != 0) {
|
||
v_fc41d_drain_orphan_app_payload(BLE->io.link_rx_acc, &BLE->io.link_rx_acc_len);
|
||
}
|
||
v_fc41d_parse_qble_recv(BLE->io.link_rx_acc, &BLE->io.link_rx_acc_len);
|
||
#if TCP_DEBUG_EN
|
||
if (s_ble_diag_trace() != 0) {
|
||
s_fw_log_link_cache("parse-done");
|
||
}
|
||
#endif
|
||
}
|
||
|
||
static void v_fc41d_link_rx_feed(const uint8_t *data, uint16_t len)
|
||
{
|
||
uint16_t i;
|
||
uint16_t link_cap = FC41D_LINK_CAP();
|
||
|
||
if ((data == NULL) || (len == 0U) || (BLE->io.link_rx_acc == NULL) || (link_cap == 0U)) {
|
||
return;
|
||
}
|
||
|
||
for (i = 0U; i < len; i++) {
|
||
if (BLE->io.link_rx_acc_len >= link_cap) {
|
||
v_fc41d_parse_link_rx();
|
||
link_cap = FC41D_LINK_CAP();
|
||
if (BLE->io.link_rx_acc_len >= link_cap) {
|
||
if (BLE->io.qble_payload_need > 0U) {
|
||
BT_PRINT("link_rx full, reset qble_payload_need\r\n");
|
||
BLE->io.qble_payload_need = 0U;
|
||
v_fc41d_qble_resync(BLE->io.link_rx_acc, &BLE->io.link_rx_acc_len);
|
||
} else {
|
||
v_fc41d_shift_rx(BLE->io.link_rx_acc, &BLE->io.link_rx_acc_len,
|
||
(uint16_t)(link_cap / 2U));
|
||
v_fc41d_qble_resync(BLE->io.link_rx_acc, &BLE->io.link_rx_acc_len);
|
||
}
|
||
link_cap = FC41D_LINK_CAP();
|
||
}
|
||
}
|
||
BLE->io.link_rx_acc[BLE->io.link_rx_acc_len++] = data[i];
|
||
}
|
||
|
||
v_fc41d_parse_link_rx();
|
||
}
|
||
|
||
static void v_at_uart_drain_link(void)
|
||
{
|
||
if (u8_at_is_transparent() != 0U) {
|
||
v_at_uart_drain_payload();
|
||
return;
|
||
}
|
||
v_at_uart_read_all();
|
||
}
|
||
|
||
/**
|
||
* @brief 排空 UART7 接收(AT 阶段解析 URC;透传阶段写入 app_rx)
|
||
*/
|
||
static void v_at_uart_read_all(void)
|
||
{
|
||
uint8_t chunk[128];
|
||
uint16_t n;
|
||
|
||
if (u8_at_is_transparent() != 0U) {
|
||
v_at_uart_drain_payload();
|
||
return;
|
||
}
|
||
|
||
do {
|
||
n = s_fc41d_uart_drain_rx(chunk, sizeof(chunk));
|
||
if (n > 0U) {
|
||
#if TCP_DEBUG_EN
|
||
if (s_ble_diag_trace() != 0) {
|
||
tcp_firmware_upgrade_log_buf("UART-RX", chunk, (uint32_t)n);
|
||
}
|
||
#endif
|
||
v_fc41d_link_rx_feed(chunk, n);
|
||
}
|
||
} while (n == (uint16_t)sizeof(chunk));
|
||
}
|
||
|
||
/**
|
||
* @brief 复位 AT 层接收状态与 URC 队列(模组重启或停止 BLE 时调用)
|
||
*/
|
||
static void v_at_init(void)
|
||
{
|
||
BLE->at.rx_acc_len = 0U;
|
||
BLE->at.rx_acc[0] = '\0';
|
||
BLE->io.link_rx_acc_len = 0U;
|
||
BLE->io.qble_payload_need = 0U;
|
||
BLE->at.pending_urc = FC41D_BLE_URC_NONE;
|
||
BLE->sm.transparent = 0U;
|
||
BLE->sm.gatt_fallback = 0U;
|
||
s_app_rx_clear();
|
||
}
|
||
|
||
/**
|
||
* @brief 清空 UART 硬件 FIFO 及 BLE->at.rx_acc,避免旧应答干扰下一条 AT
|
||
* @note 最多丢弃 4096 字节,防止死循环
|
||
*/
|
||
static void v_at_uart_discard(void)
|
||
{
|
||
uint8_t scrap[64];
|
||
uint32_t total = 0U;
|
||
|
||
s_fc41d_uart_discard();
|
||
while (total < 4096U) {
|
||
uint16_t n = s_fc41d_uart_drain_rx(scrap, sizeof(scrap));
|
||
if (n == 0U) {
|
||
break;
|
||
}
|
||
total += n;
|
||
}
|
||
BLE->at.rx_acc_len = 0U;
|
||
BLE->at.rx_acc[0] = '\0';
|
||
BLE->io.link_rx_acc_len = 0U;
|
||
}
|
||
|
||
/**
|
||
* @brief 将 AT 命令行格式化为完整发送串(含 AT 前缀与 \\r\\n)
|
||
* @return 格式化长度,失败返回 <=0
|
||
*/
|
||
static int s_at_format_cmd(const char *at_line, char *out, size_t out_sz)
|
||
{
|
||
int n;
|
||
|
||
if ((at_line == NULL) || (out == NULL) || (out_sz == 0U)) {
|
||
return -1;
|
||
}
|
||
if (strncmp(at_line, "AT", 2) == 0) {
|
||
n = snprintf(out, out_sz, "%s\r\n", at_line);
|
||
} else if (at_line[0] == '+') {
|
||
n = snprintf(out, out_sz, "AT%s\r\n", at_line);
|
||
} else {
|
||
n = snprintf(out, out_sz, "AT+%s\r\n", at_line);
|
||
}
|
||
if ((n <= 0) || ((size_t)n >= out_sz)) {
|
||
return -1;
|
||
}
|
||
return n;
|
||
}
|
||
|
||
/**
|
||
* @brief 阻塞等待当前 AT 应答 OK(同时解析 +QBLERECV 写入应用缓冲)
|
||
*/
|
||
static int s_at_blocking_wait_ok(uint32_t wait_ms)
|
||
{
|
||
uint64_t deadline_ms = u64_get_current_millis() + (uint64_t)wait_ms;
|
||
uint32_t poll_ms = 20U;
|
||
|
||
#if TCP_DEBUG_EN
|
||
if (system_filetransfer_is_active() != 0) {
|
||
poll_ms = 5U;
|
||
}
|
||
#endif
|
||
|
||
while (u64_get_current_millis() < deadline_ms) {
|
||
uint8_t chunk[128];
|
||
uint16_t got = s_fc41d_uart_drain_rx(chunk, sizeof(chunk));
|
||
|
||
if (got > 0U) {
|
||
#if TCP_DEBUG_EN
|
||
if (s_ble_diag_trace() != 0) {
|
||
tcp_firmware_upgrade_log_buf("UART-RX-AT", chunk, (uint32_t)got);
|
||
}
|
||
#endif
|
||
v_fc41d_link_rx_feed(chunk, got);
|
||
}
|
||
if (s_fc41d_link_or_at_has_ok() != 0) {
|
||
return 0;
|
||
}
|
||
if (strstr(BLE->at.rx_acc, "+QBLEMTU:") != NULL) {
|
||
return 0;
|
||
}
|
||
if (strstr(BLE->at.rx_acc, "ERROR") != NULL) {
|
||
return -1;
|
||
}
|
||
mSleep(poll_ms);
|
||
v_wdt_setFlag(TASK_ID_TcpSer);
|
||
}
|
||
return -1;
|
||
}
|
||
|
||
/**
|
||
* @brief 阻塞执行一条 AT 命令
|
||
* @return 0 成功,-1 失败
|
||
*/
|
||
static int s_at_exec_blocking_line(const char *at_line, uint32_t wait_ms)
|
||
{
|
||
char cmd[FC41D_AT_LINE_MAX];
|
||
int cmd_len;
|
||
|
||
if (at_line == NULL) {
|
||
return -1;
|
||
}
|
||
|
||
cmd_len = s_at_format_cmd(at_line, cmd, sizeof(cmd));
|
||
if (cmd_len <= 0) {
|
||
return -1;
|
||
}
|
||
|
||
BLE->at.rx_acc_len = 0U;
|
||
BLE->at.rx_acc[0] = '\0';
|
||
v_at_uart_drain_link();
|
||
BT_PRINT("AT>> %s", cmd);
|
||
if (u16_usart_send(FC41D_USART_ID, (uint8_t *)cmd, (uint16_t)cmd_len) == 0U) {
|
||
BT_PRINT("AT send failed\r\n");
|
||
return -1;
|
||
}
|
||
|
||
if (s_at_blocking_wait_ok(wait_ms) != 0) {
|
||
BT_PRINT("AT timeout blocking (rx_len=%u)\r\n", (unsigned)BLE->at.rx_acc_len);
|
||
if (BLE->at.rx_acc_len > 0U) {
|
||
BT_PRINT("AT rx: %s\r\n", BLE->at.rx_acc);
|
||
}
|
||
return -1;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/**
|
||
* @brief 取走并清零一条待处理的 BLE URC(CONNECTED/DISCONNECTED)
|
||
* @note 顺带读 UART 并解析 URC / +QBLERECV
|
||
*/
|
||
static fc41d_ble_urc_t s_at_poll_urc(void)
|
||
{
|
||
fc41d_ble_urc_t ev;
|
||
|
||
if (u8_at_is_transparent() == 0U) {
|
||
v_at_uart_read_all();
|
||
}
|
||
|
||
ev = BLE->at.pending_urc;
|
||
BLE->at.pending_urc = FC41D_BLE_URC_NONE;
|
||
return ev;
|
||
}
|
||
|
||
/**
|
||
* @brief 阻塞执行一条较长 AT(GATT notify 含 hex 载荷)
|
||
*/
|
||
static int s_at_exec_blocking_gatt(const char *at_line, uint32_t wait_ms)
|
||
{
|
||
int cmd_len;
|
||
|
||
if (at_line == NULL) {
|
||
return -1;
|
||
}
|
||
|
||
cmd_len = s_at_format_cmd(at_line, BLE->at.gatt_at_cmd, sizeof(BLE->at.gatt_at_cmd));
|
||
if (cmd_len <= 0) {
|
||
return -1;
|
||
}
|
||
|
||
BLE->at.rx_acc_len = 0U;
|
||
BLE->at.rx_acc[0] = '\0';
|
||
s_ble_drain_uart_before_gatt();
|
||
if (u16_usart_send(FC41D_USART_ID, (uint8_t *)BLE->at.gatt_at_cmd, (uint16_t)cmd_len) == 0U) {
|
||
BT_PRINT("AT send failed\r\n");
|
||
return -1;
|
||
}
|
||
|
||
if (s_at_blocking_wait_ok(wait_ms) != 0) {
|
||
s_ble_drain_uart_before_gatt();
|
||
if (s_fc41d_link_or_at_has_ok() != 0) {
|
||
return 0;
|
||
}
|
||
if (s_at_blocking_wait_ok(400U) != 0) {
|
||
BT_PRINT("AT GATT timeout (rx_len=%u)\r\n", (unsigned)BLE->at.rx_acc_len);
|
||
return -1;
|
||
}
|
||
return 0;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/**
|
||
* @brief 经 QBLEGATTSNTFY 向手机 Notify 特征发送一段载荷(hex 编码)
|
||
*/
|
||
static int s_ble_gatt_notify_chunk(const uint8_t *data, uint16_t len)
|
||
{
|
||
static const char k_hex[] = "0123456789ABCDEF";
|
||
char line[FC41D_AT_CMD_MAX];
|
||
char *hex_out;
|
||
uint16_t i;
|
||
int n;
|
||
|
||
if ((data == NULL) || (len == 0U)) {
|
||
return -1;
|
||
}
|
||
if ((size_t)len * 2U + 48U >= sizeof(line)) {
|
||
return -1;
|
||
}
|
||
|
||
n = snprintf(line, sizeof(line), "QBLEGATTSNTFY=%s,%u,",
|
||
FC41D_CHAR_NOTIFY_UUID, (unsigned)len);
|
||
if (n <= 0) {
|
||
return -1;
|
||
}
|
||
|
||
hex_out = &line[n];
|
||
for (i = 0U; i < len; i++) {
|
||
hex_out[i * 2U] = k_hex[(data[i] >> 4) & 0x0FU];
|
||
hex_out[i * 2U + 1U] = k_hex[data[i] & 0x0FU];
|
||
}
|
||
hex_out[len * 2U] = '\0';
|
||
|
||
return s_at_exec_blocking_gatt(line, FC41D_GATT_AT_WAIT_MS);
|
||
}
|
||
|
||
/**
|
||
* @brief GATT 非透传发送(自动分片),与 WiFi/TCP 上层载荷格式一致
|
||
* @return 实际发送字节数,失败 -1
|
||
*/
|
||
static int s_ble_gatt_notify_send(const uint8_t *data, uint16_t len)
|
||
{
|
||
uint16_t off = 0U;
|
||
int total = 0;
|
||
|
||
if ((data == NULL) || (len == 0U)) {
|
||
return -1;
|
||
}
|
||
|
||
while (off < len) {
|
||
uint16_t chunk = (uint16_t)(len - off);
|
||
|
||
if (chunk > FC41D_GATT_TX_CHUNK) {
|
||
chunk = FC41D_GATT_TX_CHUNK;
|
||
}
|
||
if (s_ble_gatt_notify_chunk(&data[off], chunk) != 0) {
|
||
return (total > 0) ? total : -1;
|
||
}
|
||
off = (uint16_t)(off + chunk);
|
||
total += (int)chunk;
|
||
if (off < len) {
|
||
mSleep(30U);
|
||
}
|
||
}
|
||
return total;
|
||
}
|
||
|
||
/**
|
||
* @brief 判断 FC41D 应答中是否含独立 OK(排除 +Qxxx 字段内的 ok)
|
||
*/
|
||
static int s_fc41d_resp_has_ok(const uint8_t *buf, uint16_t len)
|
||
{
|
||
uint16_t i;
|
||
|
||
if (buf == NULL || len < 2U) {
|
||
return 0;
|
||
}
|
||
for (i = 0U; i + 1U < len; ++i) {
|
||
if ((buf[i] == 'O' || buf[i] == 'o') && (buf[i + 1U] == 'K' || buf[i + 1U] == 'k')) {
|
||
if (i > 0U && buf[i - 1U] == '+') {
|
||
continue;
|
||
}
|
||
return 1;
|
||
}
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/**
|
||
* @brief 在 AT 累积区或 link 解析缓冲中查找模组 OK(升级时 OK 常混在 +QBLERECV 流里)
|
||
*/
|
||
static int s_fc41d_link_or_at_has_ok(void)
|
||
{
|
||
uint16_t i;
|
||
uint16_t n;
|
||
|
||
if (s_fc41d_resp_has_ok((const uint8_t *)BLE->at.rx_acc, BLE->at.rx_acc_len) != 0) {
|
||
return 1;
|
||
}
|
||
|
||
n = BLE->io.link_rx_acc_len;
|
||
if (n < 4U) {
|
||
return 0;
|
||
}
|
||
|
||
for (i = 0U; i + 3U < n; i++) {
|
||
if ((BLE->io.link_rx_acc[i] == '\r') && (BLE->io.link_rx_acc[i + 1U] == '\n') &&
|
||
(BLE->io.link_rx_acc[i + 2U] == 'O') && (BLE->io.link_rx_acc[i + 3U] == 'K')) {
|
||
return 1;
|
||
}
|
||
if ((i == 0U) && (BLE->io.link_rx_acc[0] == 'O') && (BLE->io.link_rx_acc[1] == 'K') &&
|
||
(BLE->io.link_rx_acc[2] == '\r') && (BLE->io.link_rx_acc[3] == '\n')) {
|
||
return 1;
|
||
}
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/**
|
||
* @brief GATT 发送前尽量排空入站数据,降低与 +QBLERECV 并发冲突
|
||
*/
|
||
static void s_ble_drain_uart_before_gatt(void)
|
||
{
|
||
#if TCP_DEBUG_EN
|
||
if (system_filetransfer_is_active() != 0) {
|
||
uint8_t round;
|
||
|
||
for (round = 0U; round < 6U; round++) {
|
||
v_at_uart_read_all();
|
||
}
|
||
return;
|
||
}
|
||
#endif
|
||
v_at_uart_drain_link();
|
||
}
|
||
|
||
static int s_ble_is_periodic_report_json(const uint8_t *data, uint32_t len)
|
||
{
|
||
if ((data == NULL) || (len < 16U)) {
|
||
return 0;
|
||
}
|
||
if (strstr((const char *)data, "\"command\":\"SystemInfo\"") != NULL) {
|
||
return 1;
|
||
}
|
||
if (strstr((const char *)data, "\"command\":\"GunInfo\"") != NULL) {
|
||
return 1;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
static void s_ble_set_peer_info(void)
|
||
{
|
||
(void)snprintf(SESS->peer_info, sizeof(SESS->peer_info), "BLE:%s", FC41D_BLE_NAME);
|
||
}
|
||
|
||
/* =============================================================================
|
||
* 硬件控制 — 静态函数(PE6 = FC41D_RST,高电平复位,常态低)
|
||
* ============================================================================= */
|
||
|
||
/**
|
||
* @brief FC41D 硬件复位脉冲:高 200ms 后释放为低,并清空 UART7 接收
|
||
*/
|
||
static void v_hw_fc41d_reset_pulse(void)
|
||
{
|
||
FC41D_RST(1);
|
||
mSleep(FC41D_RST_PULSE_MS);
|
||
FC41D_RST(0);
|
||
s_fc41d_uart_init();
|
||
s_fc41d_uart_discard();
|
||
BT_PRINT("FC41D reset pulse done (PE6 RST)\r\n");
|
||
}
|
||
|
||
/**
|
||
* @brief 拉高 PE6 使模组进入复位
|
||
*/
|
||
static void v_hw_fc41d_hold_reset(void)
|
||
{
|
||
FC41D_RST(1);
|
||
BT_PRINT("FC41D held in reset\r\n");
|
||
}
|
||
|
||
/* =============================================================================
|
||
* BLE 状态机 — 静态函数
|
||
* ============================================================================= */
|
||
|
||
/**
|
||
* @brief 阻塞执行 BLE 外设初始化 AT 序列
|
||
* @return 0 成功,-1 失败
|
||
*/
|
||
static int s_ble_setup_sequence_blocking(void)
|
||
{
|
||
if (s_at_exec_blocking_line("QBLEINIT=2", FC41D_AT_INIT_WAIT_MS) != 0) {
|
||
return -1;
|
||
}
|
||
|
||
(void)snprintf(BLE->at.ble_cmd_buf, sizeof(BLE->at.ble_cmd_buf), "QBLENAME=%s", FC41D_BLE_NAME);
|
||
if (s_at_exec_blocking_line(BLE->at.ble_cmd_buf, FC41D_AT_CMD_WAIT_MS) != 0) {
|
||
return -1;
|
||
}
|
||
|
||
if (s_at_exec_blocking_line("QBLEURC=2", FC41D_AT_CMD_WAIT_MS) != 0) {
|
||
return -1;
|
||
}
|
||
|
||
(void)snprintf(BLE->at.ble_cmd_buf, sizeof(BLE->at.ble_cmd_buf), "QBLEGATTSSRV=%s", FC41D_SRV_UUID);
|
||
if (s_at_exec_blocking_line(BLE->at.ble_cmd_buf, FC41D_AT_CMD_WAIT_MS) != 0) {
|
||
return -1;
|
||
}
|
||
|
||
(void)snprintf(BLE->at.ble_cmd_buf, sizeof(BLE->at.ble_cmd_buf), "QBLEGATTSCHAR=%s", FC41D_CHAR_WRITE_UUID);
|
||
if (s_at_exec_blocking_line(BLE->at.ble_cmd_buf, FC41D_AT_CMD_WAIT_MS) != 0) {
|
||
return -1;
|
||
}
|
||
|
||
(void)snprintf(BLE->at.ble_cmd_buf, sizeof(BLE->at.ble_cmd_buf), "QBLEGATTSCHAR=%s", FC41D_CHAR_NOTIFY_UUID);
|
||
if (s_at_exec_blocking_line(BLE->at.ble_cmd_buf, FC41D_AT_CMD_WAIT_MS) != 0) {
|
||
return -1;
|
||
}
|
||
|
||
if (s_at_exec_blocking_line("QBLEADVPARAM=150,150", FC41D_AT_CMD_WAIT_MS) != 0) {
|
||
return -1;
|
||
}
|
||
|
||
if (s_at_exec_blocking_line("QBLEADVSTART", FC41D_AT_CMD_WAIT_MS) != 0) {
|
||
return -1;
|
||
}
|
||
|
||
BT_PRINT("BLE advertising started, name=%s\r\n", FC41D_BLE_NAME);
|
||
return 0;
|
||
}
|
||
|
||
/**
|
||
* @brief 阻塞查询 QBLESTAT,判断是否已连接(10s 兜底,非每周期轮询)
|
||
* @return 1 已连接,0 未连接或查询失败
|
||
*/
|
||
static int s_fc41d_stat_query_connected(void)
|
||
{
|
||
static const char k_cmd[] = "AT+QBLESTAT\r\n";
|
||
uint8_t rx[320];
|
||
uint64_t deadline_ms;
|
||
uint16_t rx_len;
|
||
|
||
s_fc41d_uart_discard();
|
||
if (u16_usart_send(FC41D_USART_ID, (uint8_t *)k_cmd,
|
||
(uint16_t)(sizeof(k_cmd) - 1U)) == 0U) {
|
||
return 0;
|
||
}
|
||
|
||
rx_len = 0U;
|
||
rx[0] = '\0';
|
||
deadline_ms = u64_get_current_millis() + (uint64_t)FC41D_AT_CMD_WAIT_MS;
|
||
while (u64_get_current_millis() < deadline_ms) {
|
||
uint16_t got;
|
||
|
||
got = s_fc41d_uart_drain_rx(&rx[rx_len],
|
||
(uint16_t)(sizeof(rx) - rx_len));
|
||
if (got > 0U) {
|
||
rx_len += got;
|
||
rx[rx_len] = '\0';
|
||
}
|
||
if (strstr((char *)rx, "ERROR") != NULL) {
|
||
return 0;
|
||
}
|
||
if (s_fc41d_resp_has_ok(rx, rx_len) != 0) {
|
||
if (strstr((char *)rx, "CONNECTED") != NULL &&
|
||
strstr((char *)rx, "DISCONNECTED") == NULL) {
|
||
return 1;
|
||
}
|
||
return 0;
|
||
}
|
||
mSleep(20U);
|
||
v_wdt_setFlag(TASK_ID_TcpSer);
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/**
|
||
* @brief 连接后进入 QBLETRANMODE 透传(手册 AT+QBLETRANMODE)
|
||
*/
|
||
static int s_ble_enter_transparent_blocking(void)
|
||
{
|
||
#if !FC41D_BLE_USE_TRANSPARENT
|
||
if (s_at_exec_blocking_line("QBLECFGMTU=512", FC41D_AT_CMD_WAIT_MS) != 0) {
|
||
BT_PRINT("QBLECFGMTU failed (continue)\r\n");
|
||
}
|
||
BLE->sm.gatt_fallback = 1U;
|
||
return 0;
|
||
#else
|
||
static const char *const k_tran_cmds[] = {
|
||
"QBLETRANMODE=" FC41D_TRAN_UUID ",1",
|
||
"QBLETRANMODE=" FC41D_CHAR_NOTIFY_UUID ",1",
|
||
"QBLETRANMODE=" FC41D_CHAR_WRITE_UUID ",1",
|
||
"QBLETRANMODE=" FC41D_TRAN_UUID ",0",
|
||
"QBLETRANMODE=" FC41D_CHAR_NOTIFY_UUID ",0",
|
||
};
|
||
size_t i;
|
||
|
||
if (s_at_exec_blocking_line("QBLECFGMTU=512", FC41D_AT_CMD_WAIT_MS) != 0) {
|
||
BT_PRINT("QBLECFGMTU failed (continue)\r\n");
|
||
}
|
||
|
||
for (i = 0U; i < (sizeof(k_tran_cmds) / sizeof(k_tran_cmds[0])); i++) {
|
||
if (s_at_exec_blocking_line(k_tran_cmds[i], FC41D_AT_CMD_WAIT_MS) == 0) {
|
||
v_at_set_transparent(1U);
|
||
BLE->sm.gatt_fallback = 0U;
|
||
BT_PRINT("Transparent mode OK (%s)\r\n", k_tran_cmds[i]);
|
||
return 0;
|
||
}
|
||
BT_PRINT("QBLETRANMODE failed: %s\r\n", k_tran_cmds[i]);
|
||
}
|
||
|
||
BLE->sm.gatt_fallback = 1U;
|
||
v_at_set_transparent(0U);
|
||
BT_PRINT("QBLETRANMODE all failed, GATT fallback (+QBLERECV)\r\n");
|
||
return 0;
|
||
#endif
|
||
}
|
||
|
||
static int s_ble_exit_transparent_blocking(void)
|
||
{
|
||
#if FC41D_BLE_USE_TRANSPARENT
|
||
if (u8_at_is_transparent() == 0U) {
|
||
return 0;
|
||
}
|
||
|
||
v_at_set_transparent(0U);
|
||
(void)snprintf(BLE->at.ble_cmd_buf, sizeof(BLE->at.ble_cmd_buf),
|
||
"QBLETRANMODE=%s,0", FC41D_CHAR_NOTIFY_UUID);
|
||
if (s_at_exec_blocking_line(BLE->at.ble_cmd_buf, FC41D_AT_CMD_WAIT_MS) != 0) {
|
||
BT_PRINT("QBLETRANMODE exit failed\r\n");
|
||
return -1;
|
||
}
|
||
#endif
|
||
return 0;
|
||
}
|
||
|
||
/**
|
||
* @brief 丢弃 UART 残留并清空 AT 接收缓冲(断连/停止前调用)
|
||
*/
|
||
static void v_at_tx_reset(void)
|
||
{
|
||
v_at_uart_discard();
|
||
}
|
||
|
||
/**
|
||
* @brief 复位 BLE 会话全部软件状态(不断电)
|
||
*/
|
||
static void v_ble_session_reset(void)
|
||
{
|
||
BLE->sm.state = FC41D_BLE_ST_IDLE;
|
||
SESS->client_connected = 0U;
|
||
BLE->sm.connect_deadline_ms = 0U;
|
||
BLE->sm.power_on_deadline_ms = 0U;
|
||
BLE->sm.last_stat_poll_ms = 0U;
|
||
BLE->sm.gatt_fallback = 0U;
|
||
SESS->peer_info[0] = '\0';
|
||
v_at_init();
|
||
s_ble_io_free();
|
||
v_at_tx_reset();
|
||
}
|
||
|
||
/**
|
||
* @brief 上电并开始 BLE 初始化状态机(异步,不阻塞 tcp_ser 任务)
|
||
* @return 0 成功
|
||
*/
|
||
static int s_ble_session_start(void)
|
||
{
|
||
v_ble_session_reset();
|
||
if (s_ble_io_set_caps(FC41D_LINK_RX_NORMAL, FC41D_APP_RX_NORMAL) != 0) {
|
||
BT_PRINT("BLE RX normal buf alloc failed\r\n");
|
||
return -1;
|
||
}
|
||
v_hw_fc41d_reset_pulse();
|
||
BLE->sm.power_on_deadline_ms = u64_get_current_millis() + (uint64_t)FC41D_POWER_ON_DELAY_MS;
|
||
BLE->sm.state = FC41D_BLE_ST_POWER_ON;
|
||
BT_PRINT("BLE init started (DIP1=%u)\r\n", (unsigned)u8_sub_comm_yx_get(E_SUB_DIP_1));
|
||
return 0;
|
||
}
|
||
|
||
/**
|
||
* @brief 驱动 BLE 状态机(需在 comm_link_ble_process / 任务循环中周期调用)
|
||
*/
|
||
static void v_ble_session_process(void)
|
||
{
|
||
fc41d_ble_urc_t urc;
|
||
uint64_t now_ms;
|
||
|
||
if (BLE->sm.state == FC41D_BLE_ST_IDLE || BLE->sm.state == FC41D_BLE_ST_ERROR) {
|
||
return;
|
||
}
|
||
|
||
now_ms = u64_get_current_millis();
|
||
urc = s_at_poll_urc();
|
||
|
||
if (BLE->sm.state == FC41D_BLE_ST_POWER_ON) {
|
||
if (u64_get_current_millis() < BLE->sm.power_on_deadline_ms) {
|
||
return;
|
||
}
|
||
if (s_ble_setup_sequence_blocking() != 0) {
|
||
BT_PRINT("BLE setup failed\r\n");
|
||
BLE->sm.state = FC41D_BLE_ST_ERROR;
|
||
return;
|
||
}
|
||
BLE->sm.state = FC41D_BLE_ST_WAIT_CONNECT;
|
||
BLE->sm.connect_deadline_ms = u64_get_current_millis() + (uint64_t)FC41D_CONNECT_WAIT_MS;
|
||
BLE->sm.last_stat_poll_ms = u64_get_current_millis();
|
||
BT_PRINT("Waiting for BLE connection...\r\n");
|
||
return;
|
||
}
|
||
|
||
if (BLE->sm.state == FC41D_BLE_ST_WAIT_CONNECT) {
|
||
if (urc == FC41D_BLE_URC_CONNECTED) {
|
||
BT_PRINT("BLE link connected (URC)\r\n");
|
||
SESS->client_connected = 1U;
|
||
s_ble_set_peer_info();
|
||
(void)s_ble_enter_transparent_blocking();
|
||
BLE->sm.state = FC41D_BLE_ST_CONNECTED;
|
||
if (u8_at_is_transparent() != 0U) {
|
||
BT_PRINT("BLE connected, name=%s\r\n", FC41D_BLE_NAME);
|
||
BT_PRINT("BLE transparent ready for JSON\r\n");
|
||
} else {
|
||
BT_PRINT("BLE connected (GATT), name=%s\r\n", FC41D_BLE_NAME);
|
||
}
|
||
return;
|
||
}
|
||
if ((now_ms - BLE->sm.last_stat_poll_ms) >= (uint64_t)FC41D_STAT_POLL_MS) {
|
||
BLE->sm.last_stat_poll_ms = now_ms;
|
||
if (s_fc41d_stat_query_connected() != 0) {
|
||
BT_PRINT("BLE link connected (QBLESTAT)\r\n");
|
||
SESS->client_connected = 1U;
|
||
s_ble_set_peer_info();
|
||
(void)s_ble_enter_transparent_blocking();
|
||
BLE->sm.state = FC41D_BLE_ST_CONNECTED;
|
||
if (u8_at_is_transparent() != 0U) {
|
||
BT_PRINT("BLE connected, name=%s\r\n", FC41D_BLE_NAME);
|
||
BT_PRINT("BLE transparent ready for JSON\r\n");
|
||
} else {
|
||
BT_PRINT("BLE connected (GATT), name=%s\r\n", FC41D_BLE_NAME);
|
||
}
|
||
return;
|
||
}
|
||
}
|
||
if (now_ms >= BLE->sm.connect_deadline_ms) {
|
||
/* 已在广播中,勿重复 QBLEADVSTART(模组会回 ERROR) */
|
||
BT_PRINT("BLE still advertising, waiting for phone connect...\r\n");
|
||
BLE->sm.connect_deadline_ms = now_ms + (uint64_t)FC41D_CONNECT_WAIT_MS;
|
||
BLE->sm.last_stat_poll_ms = now_ms;
|
||
}
|
||
return;
|
||
}
|
||
|
||
if (BLE->sm.state == FC41D_BLE_ST_CONNECTED) {
|
||
if (u8_at_is_transparent() != 0U) {
|
||
v_at_uart_drain_payload();
|
||
} else {
|
||
#if TCP_DEBUG_EN
|
||
if (system_filetransfer_is_active() != 0) {
|
||
uint8_t drain;
|
||
|
||
for (drain = 0U; drain < 16U; drain++) {
|
||
v_at_uart_drain_link();
|
||
}
|
||
} else
|
||
#endif
|
||
{
|
||
v_at_uart_drain_link();
|
||
}
|
||
}
|
||
if (urc == FC41D_BLE_URC_DISCONNECTED) {
|
||
SESS->client_connected = 0U;
|
||
SESS->peer_info[0] = '\0';
|
||
(void)s_ble_exit_transparent_blocking();
|
||
BLE->sm.gatt_fallback = 0U;
|
||
s_app_rx_clear();
|
||
BLE->sm.state = FC41D_BLE_ST_WAIT_CONNECT;
|
||
BLE->sm.connect_deadline_ms = now_ms + (uint64_t)FC41D_CONNECT_WAIT_MS;
|
||
BLE->sm.last_stat_poll_ms = now_ms;
|
||
(void)s_at_exec_blocking_line("QBLEADVSTOP", FC41D_AT_CMD_WAIT_MS);
|
||
if (s_at_exec_blocking_line("QBLEADVSTART", FC41D_AT_CMD_WAIT_MS) != 0) {
|
||
BT_PRINT("BLE re-advertise failed\r\n");
|
||
} else {
|
||
BT_PRINT("BLE disconnected, re-advertising\r\n");
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief 是否已有 BLE 逻辑连接(不含透传是否就绪)
|
||
*/
|
||
static uint8_t u8_ble_is_connected(void)
|
||
{
|
||
return SESS->client_connected;
|
||
}
|
||
|
||
/* =============================================================================
|
||
* 对外接口 — DIP 拨码
|
||
* ============================================================================= */
|
||
|
||
/**
|
||
* @brief 上电后等待一段时间,确保副板 sub_comm 已上报 DIP 状态
|
||
* @param timeout_ms 阻塞等待毫秒数(当前实现为固定 delay,便于日志打印 DIP1)
|
||
* @return 固定 0
|
||
*/
|
||
int ble_link_wait_dip_ready(uint32_t timeout_ms)
|
||
{
|
||
uint64_t deadline = u64_get_current_millis() + (uint64_t)timeout_ms;
|
||
|
||
while (u64_get_current_millis() < deadline) {
|
||
mSleep(50U);
|
||
}
|
||
BT_PRINT("DIP wait %ums, DIP1=%u\r\n",
|
||
(unsigned)timeout_ms, (unsigned)u8_sub_comm_yx_get(E_SUB_DIP_1));
|
||
return 0;
|
||
}
|
||
|
||
/**
|
||
* @brief 根据 DIP1 判断当前是否应选择蓝牙调试链路
|
||
* @return 1 DIP1=1(蓝牙),0 DIP1=0(以太网 TCP)
|
||
*/
|
||
uint8_t ble_link_is_bt_mode(void)
|
||
{
|
||
return (u8_sub_comm_yx_get(E_SUB_DIP_1) != 0U) ? 1U : 0U;
|
||
}
|
||
|
||
/* =============================================================================
|
||
* 对外接口 — comm_link 后端(与 tcp_server JSON 协议对接)
|
||
* ============================================================================= */
|
||
|
||
/**
|
||
* @brief 启动 BLE 后端:模组上电并开始 AT 初始化序列
|
||
* @return 0 成功
|
||
*/
|
||
int comm_link_ble_start(void)
|
||
{
|
||
return s_ble_session_start();
|
||
}
|
||
|
||
/**
|
||
* @brief 停止 BLE:复位状态机、模组下电
|
||
*/
|
||
void comm_link_ble_stop(void)
|
||
{
|
||
(void)s_ble_exit_transparent_blocking();
|
||
v_at_tx_reset();
|
||
v_ble_session_reset();
|
||
v_hw_fc41d_hold_reset();
|
||
}
|
||
|
||
/**
|
||
* @brief 周期处理:推进 BLE 状态机(广播、连接、断线重广播)
|
||
*/
|
||
void comm_link_ble_process(void)
|
||
{
|
||
v_ble_session_process();
|
||
}
|
||
|
||
/**
|
||
* @brief 是否有上位机连接且已进入透传(tcp_server 可收发 JSON)
|
||
* @return 1 已连接且透传就绪,0 否
|
||
*/
|
||
int comm_link_ble_is_client_connected(void)
|
||
{
|
||
if (u8_ble_is_connected() == 0U) {
|
||
return 0;
|
||
}
|
||
if (BLE->sm.state != FC41D_BLE_ST_CONNECTED) {
|
||
return 0;
|
||
}
|
||
#if FC41D_BLE_USE_TRANSPARENT
|
||
return (u8_at_is_transparent() != 0U || BLE->sm.gatt_fallback != 0U) ? 1 : 0;
|
||
#else
|
||
return 1;
|
||
#endif
|
||
}
|
||
|
||
/**
|
||
* @brief 主动断开当前 BLE 连接(发送 QBLEDISCONN)
|
||
*/
|
||
void comm_link_ble_close_client(void)
|
||
{
|
||
(void)s_ble_exit_transparent_blocking();
|
||
BLE->sm.gatt_fallback = 0U;
|
||
v_at_tx_reset();
|
||
s_app_rx_clear();
|
||
(void)s_at_exec_blocking_line("QBLEDISCONN", FC41D_AT_CMD_WAIT_MS);
|
||
}
|
||
|
||
/**
|
||
* @brief 从 BLE 透传通道读取数据(QBLETRANMODE 后 UART 直传 JSON)
|
||
* @return >0 字节数,0 无数据,-1 错误
|
||
*/
|
||
int comm_link_ble_receive_data(uint8_t *buffer, uint32_t buffer_size)
|
||
{
|
||
if ((buffer == NULL) || (buffer_size == 0U)) {
|
||
return -1;
|
||
}
|
||
if (BLE->sm.state != FC41D_BLE_ST_CONNECTED) {
|
||
return 0;
|
||
}
|
||
#if FC41D_BLE_USE_TRANSPARENT
|
||
if ((u8_at_is_transparent() == 0U) && (BLE->sm.gatt_fallback == 0U)) {
|
||
return 0;
|
||
}
|
||
#endif
|
||
|
||
#if TCP_DEBUG_EN
|
||
if (system_filetransfer_is_active() != 0) {
|
||
uint8_t pass;
|
||
|
||
for (pass = 0U; pass < 16U; pass++) {
|
||
v_at_uart_read_all();
|
||
}
|
||
} else
|
||
#endif
|
||
{
|
||
v_at_uart_read_all();
|
||
}
|
||
{
|
||
int n = s_app_rx_pop(buffer, buffer_size);
|
||
|
||
#if TCP_DEBUG_EN
|
||
if (n > 0) {
|
||
s_ble_log_json_io("RX", buffer, (uint32_t)n);
|
||
}
|
||
if ((s_ble_diag_trace() != 0) && (n > 0)) {
|
||
tcp_firmware_upgrade_log_buf("APP-POP", buffer, (uint32_t)n);
|
||
printf("[FW_UPGRADE] APP-POP done n=%d app_remain=%u\r\n",
|
||
n, (unsigned)s_app_rx_pending());
|
||
} else if ((s_ble_diag_trace() != 0) && (s_app_rx_pending() > 0U)) {
|
||
printf("[FW_UPGRADE] APP-POP blocked pending=%u cap_out=%u\r\n",
|
||
(unsigned)s_app_rx_pending(), (unsigned)buffer_size);
|
||
}
|
||
#endif
|
||
return n;
|
||
}
|
||
}
|
||
|
||
int comm_link_ble_rx_upgrade_begin(void)
|
||
{
|
||
BT_PRINT("BLE RX upgrade buf (link=%u app=%u)\r\n",
|
||
(unsigned)FC41D_LINK_RX_UPGRADE, (unsigned)FC41D_APP_RX_UPGRADE);
|
||
if (s_ble_io_set_caps(FC41D_LINK_RX_UPGRADE, FC41D_APP_RX_UPGRADE) != 0) {
|
||
return -1;
|
||
}
|
||
s_app_rx_clear();
|
||
return 0;
|
||
}
|
||
|
||
void comm_link_ble_rx_flush_pending(void)
|
||
{
|
||
s_app_rx_clear();
|
||
if (BLE->io.link_rx_acc != NULL) {
|
||
BLE->io.link_rx_acc_len = 0U;
|
||
BLE->io.qble_payload_need = 0U;
|
||
}
|
||
}
|
||
|
||
int comm_link_ble_rx_upgrade_end(void)
|
||
{
|
||
BT_PRINT("BLE RX restore normal buf (link=%u app=%u)\r\n",
|
||
(unsigned)FC41D_LINK_RX_NORMAL, (unsigned)FC41D_APP_RX_NORMAL);
|
||
return s_ble_io_set_caps(FC41D_LINK_RX_NORMAL, FC41D_APP_RX_NORMAL);
|
||
}
|
||
|
||
/**
|
||
* @brief 经 BLE 透传发送数据(tcp_server 应答 JSON)
|
||
* @return 发送字节数,失败 -1
|
||
*/
|
||
int comm_link_ble_send_data(const uint8_t *data, uint32_t len)
|
||
{
|
||
uint32_t off = 0U;
|
||
int total = 0;
|
||
|
||
if (BLE->sm.state != FC41D_BLE_ST_CONNECTED) {
|
||
return -1;
|
||
}
|
||
if ((data == NULL) || (len == 0U)) {
|
||
return -1;
|
||
}
|
||
if (len > 0xFFFFU) {
|
||
len = 0xFFFFU;
|
||
}
|
||
#if TCP_DEBUG_EN
|
||
if ((system_filetransfer_is_active() != 0) &&
|
||
(s_ble_is_periodic_report_json(data, len) != 0)) {
|
||
return (int)len;
|
||
}
|
||
#endif
|
||
#if FC41D_BLE_USE_TRANSPARENT
|
||
if (u8_at_is_transparent() != 0U) {
|
||
while (off < len) {
|
||
uint16_t chunk = (uint16_t)(len - off);
|
||
|
||
if (chunk > 512U) {
|
||
chunk = 512U;
|
||
}
|
||
if (u16_usart_send(FC41D_USART_ID, (uint8_t *)(data + off), chunk) != chunk) {
|
||
return (total > 0) ? total : -1;
|
||
}
|
||
total += (int)chunk;
|
||
off += (uint32_t)chunk;
|
||
}
|
||
#if TCP_DEBUG_EN
|
||
s_ble_log_json_io("TX", data, len);
|
||
#endif
|
||
return total;
|
||
}
|
||
if (BLE->sm.gatt_fallback == 0U) {
|
||
return -1;
|
||
}
|
||
#endif
|
||
{
|
||
int sent = s_ble_gatt_notify_send(data, (uint16_t)len);
|
||
|
||
#if TCP_DEBUG_EN
|
||
if (sent > 0) {
|
||
s_ble_log_json_io("TX", data, (uint32_t)sent);
|
||
}
|
||
#endif
|
||
return (sent > 0) ? sent : -1;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief 填充对端描述字符串(BLE 无 IP,固定为 "BLE")
|
||
* @return 0 成功,-1 参数无效
|
||
*/
|
||
int comm_link_ble_get_peer_info(char *buf, uint32_t buf_size)
|
||
{
|
||
if ((buf == NULL) || (buf_size == 0U)) {
|
||
return -1;
|
||
}
|
||
if (SESS->peer_info[0] != '\0') {
|
||
(void)snprintf(buf, buf_size, "%s", SESS->peer_info);
|
||
} else {
|
||
(void)snprintf(buf, buf_size, "BLE");
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
#if !TCP_DEBUG_EN
|
||
int comm_link_ble_diag_trace(void)
|
||
{
|
||
return 0;
|
||
}
|
||
#endif
|
||
|
||
#endif /* BLE_DEBUG_EN */
|