feat(proto): 添加设备通信协议v1修订版及统一帧格式

- 新增docs/device_communication_protocol_v1.md文档,定义V1修订版协议
- CDC和BLE GATT均改为直接传输业务消息,去掉外层协议封装
- BLE改为使用NUS(Nordic UART Service)替代原有GATT服务
- 统一键盘位图为29字节格式,FunctionKeyEvent改为上报全键盘位图
- 顶层消息增加msg_id和reply_to字段用于请求响应匹配
- Ack和Error合并为统一Response消息类型
- CDC和NUS均增加统一外层帧格式:magic(2) + len(1) + protobuf
- 添加Proto frame常量定义及长度验证逻辑
- 更新proto文件定义,包含DeviceMessage统一信封和ResponseCode枚举

- 重构hid_flowctrl_module.c中的上下文访问方式,统一使用ctx前缀
This commit is contained in:
2026-04-24 10:54:14 +08:00
parent 48968e7880
commit 3971d7c4b2
10 changed files with 703 additions and 321 deletions

View File

@@ -28,8 +28,7 @@ LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF);
#define USB_CDC_RX_RING_BUF_SIZE 256
#define USB_CDC_TX_RING_BUF_SIZE 256
#define USB_CDC_RX_CHUNK_SIZE 32
#define USB_CDC_PROTO_RX_BUF_SIZE 128
#define USB_CDC_PROTO_RX_FLUSH_DELAY K_MSEC(3)
#define USB_CDC_PROTO_RX_BUF_SIZE PROTO_MAX_FRAME_LEN
#define USB_CDC_EXPECTED_BAUDRATE 115200U
enum usb_cdc_business_state {
@@ -47,7 +46,6 @@ struct usb_cdc_ctx {
struct ring_buf rx_ringbuf;
struct ring_buf tx_ringbuf;
struct k_work rx_work;
struct k_work_delayable rx_flush_work;
uint8_t proto_rx_buf[USB_CDC_PROTO_RX_BUF_SIZE];
bool usb_active;
size_t proto_rx_len;
@@ -79,8 +77,6 @@ static struct usb_cdc_ctx ctx = {
.proto_rx_len = 0U,
};
#define proto_rx_buf ctx.proto_rx_buf
static void validate_line_coding(void);
static const char *usb_cdc_business_state_name(enum usb_cdc_business_state state)
@@ -137,7 +133,6 @@ static void disable_uart_io(void)
uart_irq_rx_disable(ctx.cdc_dev);
uart_irq_tx_disable(ctx.cdc_dev);
ctx.proto_rx_len = 0U;
k_work_cancel_delayable(&ctx.rx_flush_work);
reset_ring_buffers();
}
@@ -280,6 +275,46 @@ static void validate_line_coding(void)
#endif
}
static bool try_extract_frame(void)
{
uint8_t *buf = ctx.proto_rx_buf;
size_t frame_len;
uint16_t magic;
if (ctx.proto_rx_len < PROTO_FRAME_HEADER_SIZE) {
return false;
}
magic = (uint16_t)buf[0] | ((uint16_t)buf[1] << 8);
if (magic != PROTO_FRAME_MAGIC) {
LOG_WRN("CDC invalid frame magic 0x%04x", magic);
memmove(buf, &buf[1], ctx.proto_rx_len - 1U);
ctx.proto_rx_len--;
return true;
}
if (buf[2] > PROTO_MAX_PAYLOAD_LEN) {
LOG_WRN("CDC invalid frame len:%u", buf[2]);
memmove(buf, &buf[1], ctx.proto_rx_len - 1U);
ctx.proto_rx_len--;
return true;
}
frame_len = PROTO_FRAME_HEADER_SIZE + buf[2];
if (ctx.proto_rx_len < frame_len) {
return false;
}
LOG_INF("CDC submit framed proto_rx len:%u", (unsigned int)frame_len);
(void)submit_proto_rx_event(PROTO_TRANSPORT_USB_CDC, buf, frame_len);
if (ctx.proto_rx_len > frame_len) {
memmove(buf, &buf[frame_len], ctx.proto_rx_len - frame_len);
}
ctx.proto_rx_len -= frame_len;
return true;
}
static void rx_work_handler(struct k_work *work)
{
uint8_t buffer[USB_CDC_RX_CHUNK_SIZE];
@@ -290,47 +325,30 @@ static void rx_work_handler(struct k_work *work)
uint32_t len;
unsigned int key = irq_lock();
len = ring_buf_get(&ctx.rx_ringbuf, buffer, sizeof(buffer));
len = ring_buf_get(&ctx.rx_ringbuf, buffer, sizeof(buffer));
irq_unlock(key);
if (len == 0U) {
return;
break;
}
LOG_INF("CDC rx_work pulled %u bytes from RX ring (proto_rx_len:%u)",
(unsigned int)len, (unsigned int)ctx.proto_rx_len);
if ((ctx.proto_rx_len + len) > sizeof(proto_rx_buf)) {
LOG_WRN("Drop oversized CDC protobuf message len:%u",
if ((ctx.proto_rx_len + len) > sizeof(ctx.proto_rx_buf)) {
LOG_WRN("Drop oversized CDC framed data len:%u",
(uint32_t)(ctx.proto_rx_len + len));
ctx.proto_rx_len = 0U;
}
if (len > 0U) {
memcpy(&proto_rx_buf[ctx.proto_rx_len], buffer, len);
memcpy(&ctx.proto_rx_buf[ctx.proto_rx_len], buffer, len);
ctx.proto_rx_len += len;
k_work_reschedule(&ctx.rx_flush_work, USB_CDC_PROTO_RX_FLUSH_DELAY);
}
}
}
static void rx_flush_work_handler(struct k_work *work)
{
ARG_UNUSED(work);
if ((transport_link_state_get() != PROTO_TRANSPORT_LINK_READY) ||
(ctx.proto_rx_len == 0U)) {
LOG_INF("CDC rx_flush skipped: link=%s proto_rx_len:%u business:%s",
proto_link_state_name(transport_link_state_get()),
(unsigned int)ctx.proto_rx_len,
usb_cdc_business_state_name(ctx.business));
return;
while (try_extract_frame()) {
}
LOG_INF("CDC rx_flush submit proto_rx len:%u", (unsigned int)ctx.proto_rx_len);
(void)submit_proto_rx_event(PROTO_TRANSPORT_USB_CDC, proto_rx_buf,
ctx.proto_rx_len);
ctx.proto_rx_len = 0U;
}
static void cdc_interrupt_handler(const struct device *dev, void *user_data)
@@ -408,7 +426,6 @@ static int do_init(void)
reset_ring_buffers();
k_work_init(&ctx.rx_work, rx_work_handler);
k_work_init_delayable(&ctx.rx_flush_work, rx_flush_work_handler);
uart_irq_callback_set(ctx.cdc_dev, cdc_interrupt_handler);
ctx.business = USB_CDC_BUS_OFFLINE;
ctx.usb_active = false;