2、新增USB查询产品码(0x66)和查询Dengle配置(0x74)命令 3、修复bleproto组包栈溢出导致设置Dengle后复位的问题,并完善CDC的DTR状态判断
679 lines
20 KiB
C
679 lines
20 KiB
C
/******************************************************************************
|
||
* @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];
|
||
|
||
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 帧 CMD(frame[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 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 上报用 REQ,110 应答用 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");
|
||
}
|
||
}
|
||
|
||
/*============================================================================*/
|
||
/* 环形缓冲 / 校验 */
|
||
/*============================================================================*/
|
||
|
||
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 回 ACK:CMD = 请求 CMD+1,payload = 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:
|
||
/* 其它命令:整帧透传到 BLE(按 JB cmd 映射 ble_proto cmd) */
|
||
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);
|
||
}
|
||
}
|