Files
BLE-USB_Dongle/apps/usr_le_code/usr_usb_jb.c
T

759 lines
23 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/******************************************************************************
* @file usr_usb_jb.c
* @brief USB CDC 见宝帧解析:校验后写入产品码并重广播
* @author cyWu <1917507415@qq.com>
* @date 2026.07.24
* @version V1.0.2
* @history
* - V1.0.2, 2026.07.28, cyWu, 非产品码命令整帧透传 BLE REPORTCMD_V2
* - V1.0.1, 2026.07.24, cyWu, CDC 中断内禁止 syscfg_write,改延时到任务上下文
* - V1.0.0, 2026.07.24, cyWu, 首次发布
******************************************************************************/
#include "system/includes.h"
#include "app_config.h"
#include "vm.h"
#include "usr_usb_jb.h"
#include "bleproto_api.h"
#include "usr_dengle_sched.h"
#define LOG_TAG_CONST APP
#define LOG_TAG "[USB_JB]"
#define LOG_ERROR_ENABLE
#define LOG_DEBUG_ENABLE
#define LOG_INFO_ENABLE
#define LOG_CLI_ENABLE
#include "debug.h"
#if TCFG_USB_SLAVE_CDC_ENABLE
#include "usb/device/cdc.h"
#endif
/*============================================================================*/
/* 协议常量(对齐 jb_protocol / jb_common */
/*============================================================================*/
#define PKT_HEAD_0 0x55
#define PKT_HEAD_1 0xAA
#define MAX_PACKAGE_LEN 128
#define JB_RB_CAPACITY 256
#define JB_RSP_BUF_SZ 800 /* Dengle 查询应答:result + 最多 10 槽 */
#define USR_PROD_MAGIC 0x5A
#define USR_JB_BLE_CMD_REPORT 100 /* 0x07/0x34/0x38 → 主动上报类 */
#define USR_JB_BLE_CMD_ACK 110 /* 0x04/0x06/0x31/0x37 → 应答类 */
#define USR_JB_BLE_TX_SZ 384
#ifndef CFG_USER_PROD_INFO
#define CFG_USER_PROD_INFO 28
#endif
extern void usr_ble_adv_init(void);
extern void usr_ble_send_data(u8 *data, u16 len);
extern u16 usr_get_conn_service(void);
/*============================================================================*/
/* 类型 */
/*============================================================================*/
typedef struct {
uint8_t magic;
uint8_t prodcode[USR_PRODCODE_LEN];
uint8_t pcode[USR_PCODE_LEN];
} usr_prod_info_t;
typedef struct {
uint8_t buf[JB_RB_CAPACITY];
uint16_t write;
uint16_t read;
uint16_t count;
} usr_jb_rb_t;
/*============================================================================*/
/* 静态变量 */
/*============================================================================*/
static const uint8_t s_default_prodcode[USR_PRODCODE_LEN] = {'C', 'B', 'C', '0'};
static const uint8_t s_default_pcode[USR_PCODE_LEN] = {'P', 'I', 'C', 'B', 'C', '0'};
static uint8_t s_prodcode[USR_PRODCODE_LEN];
static uint8_t s_pcode[USR_PCODE_LEN];
static usr_jb_rb_t s_rb;
/* 待写 flash / 回 ACK 的缓存(ISR 只填,定时器里执行) */
static uint8_t s_pending_save;
static uint8_t s_pending_cmd;
static uint8_t s_pending_sn;
static uint8_t s_pending_prod[USR_PRODCODE_LEN];
static uint8_t s_pending_pcode[USR_PCODE_LEN];
static int s_process_timer;
static uint8_t s_ble_txbuf[USR_JB_BLE_TX_SZ];
static uint8_t s_last_ble_msgid; /* 最近一次 BLE RX 的 msgid */
static uint8_t s_rsp_buf[JB_RSP_BUF_SZ];
static uint8_t usr_jb_checksum(const uint8_t *frame, uint16_t total_len);
static uint8_t usr_jb_need_com_offline_auto_ack(uint8_t cmd);
void usr_ble_rx_msgid_save(uint8_t msgid)
{
s_last_ble_msgid = msgid & 0x0F;
printf("usr_ble rx msgid save=%u\n", s_last_ble_msgid);
}
uint8_t usr_ble_rx_msgid_get(void)
{
return s_last_ble_msgid;
}
/**
* @brief 按 USB JB 命令映射 ble_proto cmd / gateway_int
* @param jb_cmd USB 帧 CMDframe[4]
* @param pkt 待填充的 ble_proto 包
* @return 0 可透传, -1 不支持的命令
*/
static int usr_jb_map_ble_pkt(uint8_t jb_cmd, ble_proto_packet_t *pkt)
{
if (!pkt) {
return -1;
}
memset(pkt, 0, sizeof(*pkt));
switch (jb_cmd) {
case 0x07: /* REPORT */
case 0x34: /* OTA_COMPLETE */
case 0x38: /* OTA_STOP_MCU */
pkt->cmd = USR_JB_BLE_CMD_REPORT;
pkt->gateway_int_cnt = 0;
break;
case 0x04: /* CONTROL_ACK */
case 0x06: /* READ_ACK */
case 0x31: /* OTA_NOTIFY_ACK */
case 0x33: /* OTA_DATA_ACK */
case 0x37: /* OTA_STOP_BLE_ACK */
pkt->cmd = USR_JB_BLE_CMD_ACK;
pkt->gateway_int_cnt = 2;
pkt->gateway_int[0] = 0;
pkt->gateway_int[1] = 0;
break;
default:
return -1;
}
return 0;
}
/**
* @brief 将完整 JB 帧封装为 REPORTCMD_V2 后经 BLE 发出
* @param frame 完整 55 AA 帧
* @param len 帧长度
*/
static void usr_jb_forward_to_ble(const uint8_t *frame, uint16_t len)
{
bleproto_appdata_desc_t encode_desc = {0};
ble_proto_packet_t *pkt;
uint8_t jb_cmd;
int elen;
if (!frame || len < 7 || len > MAX_PACKAGE_LEN) {
return;
}
if (usr_get_conn_service() == 0) {
printf("usr_jb ble forward skip: not connected\n");
return;
}
jb_cmd = frame[4];
pkt = &encode_desc.datast.reportcmd_v2_req;
if (usr_jb_map_ble_pkt(jb_cmd, pkt) != 0) {
printf("usr_jb ble forward ignore jb_cmd=0x%02x\n", jb_cmd);
return;
}
encode_desc.header.version = 0;
encode_desc.header.datafmt = BLEPROTO_APPDATA_DATAFMT_JSON;
/* 100 上报用 REQ110 应答用 RSP */
encode_desc.header.msgtype = (pkt->cmd == USR_JB_BLE_CMD_ACK)
? BLEPROTO_APPDATA_MSGTYPE_RSP
: BLEPROTO_APPDATA_MSGTYPE_REQ;
encode_desc.header.msgid = usr_ble_rx_msgid_get();
encode_desc.header.encrypt = 0;
encode_desc.header.serviceid = (pkt->cmd == USR_JB_BLE_CMD_ACK)
? E_BLEPROTO_SERVICE_ID_RUNCMD_V2
: E_BLEPROTO_SERVICE_ID_REPORTCMD_V2;
pkt->reserved = 0;
pkt->device_int_len = 0;
pkt->device_str_len = 0;
pkt->device_bytes_len = len;
pkt->bytes_data = frame;
elen = bleproto_appdata_encode(s_ble_txbuf, sizeof(s_ble_txbuf),
sizeof(s_ble_txbuf), &encode_desc);
printf("usr_jb ble forward jb_cmd=0x%02x ble_cmd=%u msgtype=%u msgid=%u gw_cnt=%u elen=%d\n",
jb_cmd, pkt->cmd, encode_desc.header.msgtype,
encode_desc.header.msgid, pkt->gateway_int_cnt, elen);
if (elen > 0) {
usr_ble_send_data(s_ble_txbuf, (u16)elen);
printf_buf(s_ble_txbuf, elen);
} else {
printf("usr_jb ble encode fail\n");
}
}
/**
* @brief 是否属于 COM 离线时需本地自动应答的命令
*/
static uint8_t usr_jb_need_com_offline_auto_ack(uint8_t cmd)
{
switch (cmd) {
case USR_JB_CMD_CTRL: /* 0x03 → ACK 0x04,映射表已支持 */
case USR_JB_CMD_READ: /* 0x05 → ACK 0x06,映射表已支持 */
case USR_JB_CMD_OTA_NOTIFY: /* 0x30 → ACK 0x31 */
case USR_JB_CMD_OTA_DATA: /* 0x32 → ACK 0x33 */
case USR_JB_CMD_OTA_STOP_BLE: /* 0x36 → ACK 0x37 */
return 1;
default:
return 0;
}
}
/**
* @brief 组简单 JB 应答帧(cmd+1 / 同 SN / result),再走 forward 发 BLE
*/
static void usr_jb_send_simple_ack_frame(uint8_t req_cmd, uint8_t sn, uint8_t result)
{
uint8_t frame[8];
uint16_t len_field = 4; /* CMD+SN+result+CKSUM */
uint16_t total = len_field + 4;
frame[0] = PKT_HEAD_0;
frame[1] = PKT_HEAD_1;
frame[2] = (uint8_t)(len_field >> 8);
frame[3] = (uint8_t)(len_field & 0xFF);
frame[4] = (uint8_t)(req_cmd + 1);
frame[5] = sn;
frame[6] = result;
frame[7] = usr_jb_checksum(frame, total);
printf("usr_jb auto-ack req=0x%02x rsp=0x%02x sn=%u result=0x%02x\n",
req_cmd, frame[4], sn, result);
usr_jb_forward_to_ble(frame, total);
}
/*============================================================================*/
/* 环形缓冲 / 校验 */
/*============================================================================*/
static void usr_jb_rb_init(usr_jb_rb_t *rb)
{
memset(rb, 0, sizeof(*rb));
}
static void usr_jb_rb_push(usr_jb_rb_t *rb, uint8_t byte)
{
if (rb->count >= JB_RB_CAPACITY) {
rb->read = (rb->read + 1) % JB_RB_CAPACITY;
rb->count--;
}
rb->buf[rb->write] = byte;
rb->write = (rb->write + 1) % JB_RB_CAPACITY;
rb->count++;
}
static uint8_t usr_jb_rb_pop(usr_jb_rb_t *rb)
{
uint8_t b = rb->buf[rb->read];
rb->read = (rb->read + 1) % JB_RB_CAPACITY;
rb->count--;
return b;
}
static uint16_t usr_jb_rb_available(const usr_jb_rb_t *rb)
{
return rb->count;
}
static uint16_t usr_jb_read_u16_be(const uint8_t *buf)
{
return ((uint16_t)buf[0] << 8) | buf[1];
}
/**
* @brief 校验和:sum(LEN_H .. payload_last) & 0xFF
*/
static uint8_t usr_jb_checksum(const uint8_t *frame, uint16_t total_len)
{
uint16_t sum = 0;
uint16_t i;
if (!frame || total_len < 4) {
return 0;
}
for (i = 2; i < total_len - 1; i++) {
sum += frame[i];
}
return (uint8_t)(sum & 0xFF);
}
/**
* @brief 从环形缓冲提取完整帧(逻辑对齐 jbGetOnePacket
* @return 0 成功, 1 需要更多数据, -2 校验失败
*/
static int8_t usr_jb_get_one_packet(uint8_t *out, uint16_t *out_len)
{
static uint8_t state = 0;
static uint16_t expect = 0;
static uint16_t idx = 0;
static uint8_t prev = 0;
while (usr_jb_rb_available(&s_rb)) {
uint8_t b = usr_jb_rb_pop(&s_rb);
if (state == 0) {
if (prev == PKT_HEAD_0 && b == PKT_HEAD_1) {
out[0] = PKT_HEAD_0;
out[1] = PKT_HEAD_1;
idx = 2;
state = 1;
}
} else if (state == 1) {
out[idx++] = b;
if (idx == 6) {
expect = 4 + usr_jb_read_u16_be(&out[2]);
if (expect > MAX_PACKAGE_LEN || expect < 7) {
state = 0;
} else {
state = 2;
}
}
} else {
out[idx++] = b;
if (idx >= expect) {
state = 0;
if (out[expect - 1] == usr_jb_checksum(out, expect)) {
*out_len = expect;
return 0;
}
printf("usr_jb checksum fail expect=0x%02x calc=0x%02x\n",
out[expect - 1], usr_jb_checksum(out, expect));
return -2;
}
}
prev = b;
}
return 1;
}
/*============================================================================*/
/* 产品码 flash */
/*============================================================================*/
static void usr_prodcode_set_default(void)
{
memcpy(s_prodcode, s_default_prodcode, USR_PRODCODE_LEN);
memcpy(s_pcode, s_default_pcode, USR_PCODE_LEN);
}
void usr_prodcode_init(void)
{
usr_prod_info_t info;
int ret;
usr_jb_rb_init(&s_rb);
s_pending_save = 0;
s_process_timer = 0;
usr_prodcode_set_default();
memset(&info, 0, sizeof(info));
ret = syscfg_read(CFG_USER_PROD_INFO, (u8 *)&info, sizeof(info));
if (ret > 0 && info.magic == USR_PROD_MAGIC) {
memcpy(s_prodcode, info.prodcode, USR_PRODCODE_LEN);
memcpy(s_pcode, info.pcode, USR_PCODE_LEN);
printf("prodcode from flash: %c%c%c%c / %c%c%c%c%c%c\n",
s_prodcode[0], s_prodcode[1], s_prodcode[2], s_prodcode[3],
s_pcode[0], s_pcode[1], s_pcode[2], s_pcode[3], s_pcode[4], s_pcode[5]);
} else {
printf("prodcode use default: %c%c%c%c / %c%c%c%c%c%c\n",
s_prodcode[0], s_prodcode[1], s_prodcode[2], s_prodcode[3],
s_pcode[0], s_pcode[1], s_pcode[2], s_pcode[3], s_pcode[4], s_pcode[5]);
}
}
void usr_prodcode_get(uint8_t *prodcode, uint8_t *pcode)
{
if (prodcode) {
memcpy(prodcode, s_prodcode, USR_PRODCODE_LEN);
}
if (pcode) {
memcpy(pcode, s_pcode, USR_PCODE_LEN);
}
}
/**
* @brief 写 VM(必须在任务/定时器上下文调用,禁止在 CDC 中断里调用)
*/
static int usr_prodcode_save(const uint8_t *prodcode, const uint8_t *pcode)
{
usr_prod_info_t info;
int ret;
if (!prodcode || !pcode) {
return -1;
}
memset(&info, 0, sizeof(info));
info.magic = USR_PROD_MAGIC;
memcpy(info.prodcode, prodcode, USR_PRODCODE_LEN);
memcpy(info.pcode, pcode, USR_PCODE_LEN);
memcpy(s_prodcode, prodcode, USR_PRODCODE_LEN);
memcpy(s_pcode, pcode, USR_PCODE_LEN);
ret = syscfg_write(CFG_USER_PROD_INFO, (u8 *)&info, sizeof(info));
if (ret <= 0) {
printf("prodcode flash write fail ret=%d\n", ret);
return -1;
}
printf("prodcode saved: %c%c%c%c / %c%c%c%c%c%c ret=%d\n",
s_prodcode[0], s_prodcode[1], s_prodcode[2], s_prodcode[3],
s_pcode[0], s_pcode[1], s_pcode[2], s_pcode[3], s_pcode[4], s_pcode[5],
ret);
return 0;
}
/**
* @brief 解析产品码 payload(仅 TLV
* 格式: [len=4][ProdtCode 4B][len=6][productCode 6B]
* @return 0 成功, -1 失败
*/
static int usr_jb_parse_prod_payload(const uint8_t *payload, uint16_t payload_len,
uint8_t *prodcode, uint8_t *pcode)
{
if (!payload || !prodcode || !pcode) {
return -1;
}
/* TLV: 04 xx xx xx xx 06 yy yy yy yy yy yy */
if (payload_len < (1 + USR_PRODCODE_LEN + 1 + USR_PCODE_LEN)) {
return -1;
}
if (payload[0] != USR_PRODCODE_LEN) {
return -1;
}
if (payload[1 + USR_PRODCODE_LEN] != USR_PCODE_LEN) {
return -1;
}
memcpy(prodcode, &payload[1], USR_PRODCODE_LEN);
memcpy(pcode, &payload[1 + USR_PRODCODE_LEN + 1], USR_PCODE_LEN);
return 0;
}
#if TCFG_USB_SLAVE_CDC_ENABLE
/**
* @brief 回 ACKCMD = 请求 CMD+1payload = result
*/
static void usr_jb_send_ack(uint8_t cmd, uint8_t sn, uint8_t result)
{
uint8_t buf[8];
uint16_t len_field = 4; /* CMD+SN+result+CKSUM */
uint16_t total = len_field + 4;
buf[0] = PKT_HEAD_0;
buf[1] = PKT_HEAD_1;
buf[2] = (uint8_t)(len_field >> 8);
buf[3] = (uint8_t)(len_field & 0xFF);
buf[4] = (uint8_t)(cmd + 1);
buf[5] = sn;
buf[6] = result;
buf[7] = usr_jb_checksum(buf, total);
cdc_write_data(0, buf, total);
}
/**
* @brief 回带 payload 的应答:CMD = 请求 CMD+1
* @param cmd 请求命令
* @param sn 流水号
* @param payload 应答负载(可为 NULL)
* @param payload_len 负载长度
*/
static void usr_jb_send_rsp(uint8_t cmd, uint8_t sn,
const uint8_t *payload, uint16_t payload_len)
{
uint16_t len_field;
uint16_t total;
/* LEN = CMD + SN + payload + CKSUM */
len_field = (uint16_t)(2 + payload_len + 1);
total = (uint16_t)(len_field + 4);
if (total > JB_RSP_BUF_SZ) {
printf("usr_jb rsp too long %u\n", total);
usr_jb_send_ack(cmd, sn, 0x01);
return;
}
s_rsp_buf[0] = PKT_HEAD_0;
s_rsp_buf[1] = PKT_HEAD_1;
s_rsp_buf[2] = (uint8_t)(len_field >> 8);
s_rsp_buf[3] = (uint8_t)(len_field & 0xFF);
s_rsp_buf[4] = (uint8_t)(cmd + 1);
s_rsp_buf[5] = sn;
if (payload && payload_len) {
memcpy(&s_rsp_buf[6], payload, payload_len);
}
s_rsp_buf[total - 1] = usr_jb_checksum(s_rsp_buf, total);
cdc_write_data(0, s_rsp_buf, total);
printf("usr_jb rsp cmd=0x%02x plen=%u total=%u\n", cmd + 1, payload_len, total);
}
/**
* @brief Dengle 查询:空/0xFF=全部;单字节槽位=查一条
* @note 应答 0x75
* 查全部 → [result][slot_rec x 10]slot_rec 与 0x70 payload 同格式
* 查单槽 → [result][slot_rec x 1]
*/
static void usr_jb_handle_dengle_query(uint8_t sn,
const uint8_t *payload,
uint16_t payload_len)
{
static uint8_t s_body[JB_RSP_BUF_SZ - 8];
int n;
uint8_t slot_id;
uint8_t i;
/* 查单槽 */
if (payload_len >= 1 && payload[0] < DENGLE_SLOT_MAX) {
slot_id = payload[0];
s_body[0] = 0x00; /* result ok */
n = dengle_sched_pack_slot(slot_id, &s_body[1], sizeof(s_body) - 1);
if (n <= 0) {
usr_jb_send_ack(USR_JB_CMD_DENGLE_QRY, sn, 0x01);
return;
}
usr_jb_send_rsp(USR_JB_CMD_DENGLE_QRY, sn, s_body, (uint16_t)(1 + n));
return;
}
/* 查全部:payload 空 / 0xFF / 其它 */
s_body[0] = 0x00;
n = dengle_sched_pack_all(&s_body[1], sizeof(s_body) - 1);
if (n > 0) {
usr_jb_send_rsp(USR_JB_CMD_DENGLE_QRY, sn, s_body, (uint16_t)(1 + n));
return;
}
/* 缓冲不够时退化为逐槽上报 */
printf("dengle qry pack_all fail, fallback per-slot\n");
for (i = 0; i < DENGLE_SLOT_MAX; i++) {
s_body[0] = 0x00;
n = dengle_sched_pack_slot(i, &s_body[1], sizeof(s_body) - 1);
if (n <= 0) {
usr_jb_send_ack(USR_JB_CMD_DENGLE_QRY, sn, 0x01);
return;
}
usr_jb_send_rsp(USR_JB_CMD_DENGLE_QRY, sn, s_body, (uint16_t)(1 + n));
}
}
/**
* @brief 查询产品码:应答格式与 0x64 设置 payload 对齐(带 result
* RSP 0x67 payload:
* [result:1][04][ProdtCode 4B][06][productCode 6B]
*/
static void usr_jb_handle_prodcode_query(uint8_t sn)
{
uint8_t body[1 + 1 + USR_PRODCODE_LEN + 1 + USR_PCODE_LEN];
uint8_t prod[USR_PRODCODE_LEN];
uint8_t pcode[USR_PCODE_LEN];
usr_prodcode_get(prod, pcode);
body[0] = 0x00; /* result ok */
body[1] = USR_PRODCODE_LEN;
memcpy(&body[2], prod, USR_PRODCODE_LEN);
body[2 + USR_PRODCODE_LEN] = USR_PCODE_LEN;
memcpy(&body[2 + USR_PRODCODE_LEN + 1], pcode, USR_PCODE_LEN);
printf("prodcode qry: %c%c%c%c / %c%c%c%c%c%c\n",
prod[0], prod[1], prod[2], prod[3],
pcode[0], pcode[1], pcode[2], pcode[3], pcode[4], pcode[5]);
usr_jb_send_rsp(USR_JB_CMD_GET_PRODCODE, sn, body, sizeof(body));
}
#endif
/**
* @brief 任务上下文:解析帧 / 写 flash / ACK / 重建广播
*/
static void usr_jb_process_timeout(void *priv)
{
uint8_t pkt[MAX_PACKAGE_LEN];
uint16_t pkt_len;
int8_t ret;
uint8_t cmd;
uint8_t sn;
uint16_t payload_len;
(void)priv;
s_process_timer = 0;
/* 先把环形缓冲里的完整帧抽完 */
do {
pkt_len = 0;
ret = usr_jb_get_one_packet(pkt, &pkt_len);
if (ret != 0) {
break;
}
cmd = pkt[4];
sn = pkt[5];
payload_len = (pkt_len >= 7) ? (pkt_len - 7) : 0;
printf("usr_jb cmd=0x%02x sn=%u plen=%u\n", cmd, sn, payload_len);
printf_buf(pkt, pkt_len);
switch (cmd) {
case USR_JB_CMD_SET_PRODCODE:
if (usr_jb_parse_prod_payload(&pkt[6], payload_len,
s_pending_prod, s_pending_pcode) != 0) {
printf("usr_jb set prodcode payload invalid\n");
#if TCFG_USB_SLAVE_CDC_ENABLE
usr_jb_send_ack(cmd, sn, 0x01);
#endif
break;
}
/* 缓存后统一落盘,避免同一周期多次写 flash */
s_pending_cmd = cmd;
s_pending_sn = sn;
s_pending_save = 1;
break;
case USR_JB_CMD_GET_PRODCODE:
#if TCFG_USB_SLAVE_CDC_ENABLE
usr_jb_handle_prodcode_query(sn);
#endif
break;
case USR_JB_CMD_DENGLE_SET: {
Dengle_Ret_t dr = dengle_sched_usb_set(&pkt[6], payload_len);
printf("dengle sched usb set ret=%d\n", dr);
#if TCFG_USB_SLAVE_CDC_ENABLE
usr_jb_send_ack(cmd, sn, (dr == DENGLE_OK) ? 0x00 : 0x01);
#endif
break;
}
case USR_JB_CMD_DENGLE_CLR: {
Dengle_Ret_t dr = dengle_sched_clear_all();
printf("dengle sched clear all ret=%d\n", dr);
#if TCFG_USB_SLAVE_CDC_ENABLE
usr_jb_send_ack(cmd, sn, (dr == DENGLE_OK) ? 0x00 : 0x01);
#endif
break;
}
case USR_JB_CMD_DENGLE_QRY:
#if TCFG_USB_SLAVE_CDC_ENABLE
usr_jb_handle_dengle_query(sn, &pkt[6], payload_len);
#else
(void)payload_len;
#endif
break;
default:
/*
* COM 未打开:0x03/0x05 等需本地自动应答回 BLE;
* COM 已打开或其它命令:按原逻辑透传(由映射表决定能否发出)
*/
#if TCFG_USB_SLAVE_CDC_ENABLE
if (!cdc_is_com_open() && usr_jb_need_com_offline_auto_ack(cmd)) {
usr_jb_send_simple_ack_frame(cmd, sn, 0x00);
break;
}
#else
if (usr_jb_need_com_offline_auto_ack(cmd)) {
usr_jb_send_simple_ack_frame(cmd, sn, 0x00);
break;
}
#endif
usr_jb_forward_to_ble(pkt, pkt_len);
break;
}
} while (1);
if (!s_pending_save) {
return;
}
s_pending_save = 0;
/* code / prodcode 与当前完全一致:ACK 成功,不写 Flash、不重建广播 */
if (memcmp(s_prodcode, s_pending_prod, USR_PRODCODE_LEN) == 0 &&
memcmp(s_pcode, s_pending_pcode, USR_PCODE_LEN) == 0) {
#if TCFG_USB_SLAVE_CDC_ENABLE
usr_jb_send_ack(s_pending_cmd, s_pending_sn, 0x00);
#endif
printf("prodcode unchanged, skip adv rebuild\n");
return;
}
if (usr_prodcode_save(s_pending_prod, s_pending_pcode) == 0) {
#if TCFG_USB_SLAVE_CDC_ENABLE
usr_jb_send_ack(s_pending_cmd, s_pending_sn, 0x00);
#endif
extern void usr_goto_pair_mode(void);
usr_goto_pair_mode();
printf("prodcode applied, adv rebuilt\n");
} else {
#if TCFG_USB_SLAVE_CDC_ENABLE
usr_jb_send_ack(s_pending_cmd, s_pending_sn, 0x05);
#endif
}
}
/**
* @brief USB CDC 喂数:仅压入环形缓冲并挂定时器(可在中断调用)
*/
void usr_usb_jb_feed(uint8_t *buf, uint32_t len)
{
uint32_t i;
if (!buf || len == 0) {
return;
}
for (i = 0; i < len; i++) {
usr_jb_rb_push(&s_rb, buf[i]);
}
/* 延时到系统定时器任务再解析/写 flash,避免中断里擦写导致复位 */
if (s_process_timer == 0) {
s_process_timer = sys_timeout_add(NULL, usr_jb_process_timeout, 20);
}
}
/**
* @brief BLE RUNCMD_V2 bytesCOM 开则透传 CDC,关则走 feed 统一解析/自动应答
*/
void usr_jb_on_runcmd_bytes(const uint8_t *data, uint16_t len)
{
if (!data || len == 0) {
return;
}
#if TCFG_USB_SLAVE_CDC_ENABLE
if (cdc_is_com_open()) {
u32 wlen = cdc_write_data(0, (u8 *)data, len);
printf("usr_jb runcmd -> CDC %u/%u\n", wlen, (u32)len);
return;
}
#endif
/* COM 未打开:与 USB 入环同一路径,由 process 里 get_one_packet + auto-ack 处理 */
printf("usr_jb runcmd COM closed, feed for local auto-ack len=%u\n", len);
usr_usb_jb_feed((uint8_t *)data, len);
}