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

@@ -98,19 +98,6 @@ static struct hid_flowctrl_module_ctx ctx = {
},
};
#define lifecycle ctx.lc
#define channel_state ctx.channel_state
#define pending_keys ctx.pending_keys
#define pending_consumer_latest ctx.pending_consumer_latest
#define consumer_fifo ctx.consumer_fifo
#define consumer_fifo_head ctx.consumer_fifo_head
#define consumer_fifo_tail ctx.consumer_fifo_tail
#define consumer_fifo_count ctx.consumer_fifo_count
#define in_flight ctx.in_flight
#define current_transport ctx.current_transport
#define next_sequence ctx.next_sequence
#define running module_lifecycle_is_running(&ctx.lc)
static bool current_transport_to_channel(enum keyboard_report_type report_type,
enum hid_send_channel *channel)
{
@@ -118,7 +105,7 @@ static bool current_transport_to_channel(enum keyboard_report_type report_type,
return false;
}
switch (current_transport) {
switch (ctx.current_transport) {
case HID_TRANSPORT_POLICY_USB:
*channel = (report_type == KEYBOARD_REPORT_TYPE_KEYS) ?
HID_SEND_CH_USB_KEYS :
@@ -136,44 +123,47 @@ static bool current_transport_to_channel(enum keyboard_report_type report_type,
static void clear_pending_reports(void)
{
memset(&pending_keys, 0, sizeof(pending_keys));
memset(&pending_consumer_latest, 0, sizeof(pending_consumer_latest));
consumer_fifo_head = 0U;
consumer_fifo_tail = 0U;
consumer_fifo_count = 0U;
memset(&in_flight, 0, sizeof(in_flight));
memset(&ctx.pending_keys, 0, sizeof(ctx.pending_keys));
memset(&ctx.pending_consumer_latest, 0, sizeof(ctx.pending_consumer_latest));
ctx.consumer_fifo_head = 0U;
ctx.consumer_fifo_tail = 0U;
ctx.consumer_fifo_count = 0U;
memset(&ctx.in_flight, 0, sizeof(ctx.in_flight));
}
static void consumer_fifo_push(enum keyboard_report_type report_type,
enum keyboard_protocol_mode protocol_mode,
const uint8_t *data, size_t size)
{
if (consumer_fifo_count == HID_FLOWCTRL_FIFO_DEPTH) {
if (ctx.consumer_fifo_count == HID_FLOWCTRL_FIFO_DEPTH) {
LOG_WRN("Consumer FIFO full, dropping oldest pulse");
consumer_fifo_head = (consumer_fifo_head + 1U) % HID_FLOWCTRL_FIFO_DEPTH;
consumer_fifo_count--;
ctx.consumer_fifo_head =
(ctx.consumer_fifo_head + 1U) % HID_FLOWCTRL_FIFO_DEPTH;
ctx.consumer_fifo_count--;
}
struct queued_report *entry = &consumer_fifo[consumer_fifo_tail];
struct queued_report *entry = &ctx.consumer_fifo[ctx.consumer_fifo_tail];
entry->report_type = report_type;
entry->protocol_mode = protocol_mode;
entry->size = size;
memcpy(entry->data, data, size);
consumer_fifo_tail = (consumer_fifo_tail + 1U) % HID_FLOWCTRL_FIFO_DEPTH;
consumer_fifo_count++;
ctx.consumer_fifo_tail =
(ctx.consumer_fifo_tail + 1U) % HID_FLOWCTRL_FIFO_DEPTH;
ctx.consumer_fifo_count++;
}
static bool consumer_fifo_pop(struct queued_report *entry)
{
if (consumer_fifo_count == 0U) {
if (ctx.consumer_fifo_count == 0U) {
return false;
}
*entry = consumer_fifo[consumer_fifo_head];
consumer_fifo_head = (consumer_fifo_head + 1U) % HID_FLOWCTRL_FIFO_DEPTH;
consumer_fifo_count--;
*entry = ctx.consumer_fifo[ctx.consumer_fifo_head];
ctx.consumer_fifo_head =
(ctx.consumer_fifo_head + 1U) % HID_FLOWCTRL_FIFO_DEPTH;
ctx.consumer_fifo_count--;
return true;
}
@@ -182,9 +172,9 @@ static bool channel_can_send_report(enum hid_send_channel channel,
enum keyboard_report_type report_type,
enum keyboard_protocol_mode protocol_mode)
{
const struct hid_channel_state_data *state = &channel_state[channel];
const struct hid_channel_state_data *state = &ctx.channel_state[channel];
if (in_flight[channel].active) {
if (ctx.in_flight[channel].active) {
return false;
}
@@ -205,7 +195,7 @@ static void try_send_keys(void)
{
enum hid_send_channel channel;
if (!pending_keys.valid) {
if (!ctx.pending_keys.valid) {
return;
}
@@ -213,20 +203,20 @@ static void try_send_keys(void)
return;
}
if (!channel_can_send_report(channel, pending_keys.report_type,
pending_keys.protocol_mode)) {
if (!channel_can_send_report(channel, ctx.pending_keys.report_type,
ctx.pending_keys.protocol_mode)) {
return;
}
in_flight[channel].active = true;
in_flight[channel].channel = channel;
in_flight[channel].report_type = pending_keys.report_type;
in_flight[channel].sequence = next_sequence++;
(void)submit_hid_tx_report_event(channel, pending_keys.report_type,
pending_keys.protocol_mode,
in_flight[channel].sequence,
pending_keys.data, pending_keys.size);
pending_keys.valid = false;
ctx.in_flight[channel].active = true;
ctx.in_flight[channel].channel = channel;
ctx.in_flight[channel].report_type = ctx.pending_keys.report_type;
ctx.in_flight[channel].sequence = ctx.next_sequence++;
(void)submit_hid_tx_report_event(channel, ctx.pending_keys.report_type,
ctx.pending_keys.protocol_mode,
ctx.in_flight[channel].sequence,
ctx.pending_keys.data, ctx.pending_keys.size);
ctx.pending_keys.valid = false;
}
static void try_send_consumer_fifo(void)
@@ -234,7 +224,7 @@ static void try_send_consumer_fifo(void)
struct queued_report queued;
enum hid_send_channel channel;
if (consumer_fifo_count == 0U) {
if (ctx.consumer_fifo_count == 0U) {
return;
}
@@ -248,20 +238,20 @@ static void try_send_consumer_fifo(void)
if (!channel_can_send_report(channel, queued.report_type,
queued.protocol_mode)) {
if (queued.protocol_mode == channel_state[channel].protocol_mode) {
if (queued.protocol_mode == ctx.channel_state[channel].protocol_mode) {
consumer_fifo_push(queued.report_type, queued.protocol_mode,
queued.data, queued.size);
}
return;
}
in_flight[channel].active = true;
in_flight[channel].channel = channel;
in_flight[channel].report_type = queued.report_type;
in_flight[channel].sequence = next_sequence++;
ctx.in_flight[channel].active = true;
ctx.in_flight[channel].channel = channel;
ctx.in_flight[channel].report_type = queued.report_type;
ctx.in_flight[channel].sequence = ctx.next_sequence++;
(void)submit_hid_tx_report_event(channel, queued.report_type,
queued.protocol_mode,
in_flight[channel].sequence,
ctx.in_flight[channel].sequence,
queued.data, queued.size);
}
@@ -269,7 +259,7 @@ static void try_send_consumer_latest(void)
{
enum hid_send_channel channel;
if (!pending_consumer_latest.valid) {
if (!ctx.pending_consumer_latest.valid) {
return;
}
@@ -278,27 +268,28 @@ static void try_send_consumer_latest(void)
}
if (!channel_can_send_report(channel,
pending_consumer_latest.report_type,
pending_consumer_latest.protocol_mode)) {
ctx.pending_consumer_latest.report_type,
ctx.pending_consumer_latest.protocol_mode)) {
return;
}
in_flight[channel].active = true;
in_flight[channel].channel = channel;
in_flight[channel].report_type = pending_consumer_latest.report_type;
in_flight[channel].sequence = next_sequence++;
ctx.in_flight[channel].active = true;
ctx.in_flight[channel].channel = channel;
ctx.in_flight[channel].report_type =
ctx.pending_consumer_latest.report_type;
ctx.in_flight[channel].sequence = ctx.next_sequence++;
(void)submit_hid_tx_report_event(channel,
pending_consumer_latest.report_type,
pending_consumer_latest.protocol_mode,
in_flight[channel].sequence,
pending_consumer_latest.data,
pending_consumer_latest.size);
pending_consumer_latest.valid = false;
ctx.pending_consumer_latest.report_type,
ctx.pending_consumer_latest.protocol_mode,
ctx.in_flight[channel].sequence,
ctx.pending_consumer_latest.data,
ctx.pending_consumer_latest.size);
ctx.pending_consumer_latest.valid = false;
}
static void try_send_next(void)
{
if (!running) {
if (!module_lifecycle_is_running(&ctx.lc)) {
return;
}
@@ -310,7 +301,7 @@ static void try_send_next(void)
static bool handle_keyboard_hid_report_event(
const struct keyboard_hid_report_event *event)
{
if (!running ||
if (!module_lifecycle_is_running(&ctx.lc) ||
((event->mode != MODE_SWITCH_USB) && (event->mode != MODE_SWITCH_BLE))) {
return false;
}
@@ -321,17 +312,18 @@ static bool handle_keyboard_hid_report_event(
event->dyndata.data,
event->dyndata.size);
} else if (event->report_type == KEYBOARD_REPORT_TYPE_KEYS) {
pending_keys.valid = true;
pending_keys.report_type = event->report_type;
pending_keys.protocol_mode = event->protocol_mode;
pending_keys.size = event->dyndata.size;
memcpy(pending_keys.data, event->dyndata.data, event->dyndata.size);
ctx.pending_keys.valid = true;
ctx.pending_keys.report_type = event->report_type;
ctx.pending_keys.protocol_mode = event->protocol_mode;
ctx.pending_keys.size = event->dyndata.size;
memcpy(ctx.pending_keys.data, event->dyndata.data,
event->dyndata.size);
} else {
pending_consumer_latest.valid = true;
pending_consumer_latest.report_type = event->report_type;
pending_consumer_latest.protocol_mode = event->protocol_mode;
pending_consumer_latest.size = event->dyndata.size;
memcpy(pending_consumer_latest.data, event->dyndata.data,
ctx.pending_consumer_latest.valid = true;
ctx.pending_consumer_latest.report_type = event->report_type;
ctx.pending_consumer_latest.protocol_mode = event->protocol_mode;
ctx.pending_consumer_latest.size = event->dyndata.size;
memcpy(ctx.pending_consumer_latest.data, event->dyndata.data,
event->dyndata.size);
}
@@ -346,11 +338,12 @@ static bool handle_hid_channel_state_event(
return false;
}
channel_state[event->channel].report_ready_bm = event->report_ready_bm;
channel_state[event->channel].protocol_mode = event->protocol_mode;
ctx.channel_state[event->channel].report_ready_bm =
event->report_ready_bm;
ctx.channel_state[event->channel].protocol_mode = event->protocol_mode;
if (event->report_ready_bm == 0U) {
in_flight[event->channel].active = false;
ctx.in_flight[event->channel].active = false;
}
try_send_next();
@@ -363,17 +356,17 @@ static bool handle_hid_report_sent_event(const struct hid_report_sent_event *eve
return false;
}
if (!in_flight[event->channel].active) {
if (!ctx.in_flight[event->channel].active) {
return false;
}
if (event->sequence != in_flight[event->channel].sequence) {
if (event->sequence != ctx.in_flight[event->channel].sequence) {
LOG_WRN("Unexpected HID sent sequence %u (expected %u)",
event->sequence, in_flight[event->channel].sequence);
event->sequence, ctx.in_flight[event->channel].sequence);
return false;
}
in_flight[event->channel].active = false;
ctx.in_flight[event->channel].active = false;
if (event->error) {
LOG_WRN("HID report send failed for seq %u", event->sequence);
@@ -386,12 +379,13 @@ static bool handle_hid_report_sent_event(const struct hid_report_sent_event *eve
static bool handle_transport_policy_event(
const struct transport_policy_event *event)
{
bool transport_changed = (current_transport != event->hid_transport);
bool transport_changed =
(ctx.current_transport != event->hid_transport);
current_transport = event->hid_transport;
ctx.current_transport = event->hid_transport;
if (transport_changed ||
(current_transport == HID_TRANSPORT_POLICY_NONE)) {
(ctx.current_transport == HID_TRANSPORT_POLICY_NONE)) {
clear_pending_reports();
}
@@ -402,21 +396,21 @@ static bool handle_transport_policy_event(
static int do_init(void)
{
clear_pending_reports();
current_transport = HID_TRANSPORT_POLICY_USB;
memset(channel_state, 0, sizeof(channel_state));
channel_state[HID_SEND_CH_USB_KEYS].protocol_mode =
ctx.current_transport = HID_TRANSPORT_POLICY_USB;
memset(ctx.channel_state, 0, sizeof(ctx.channel_state));
ctx.channel_state[HID_SEND_CH_USB_KEYS].protocol_mode =
KEYBOARD_PROTOCOL_MODE_REPORT;
channel_state[HID_SEND_CH_USB_CONSUMER].protocol_mode =
ctx.channel_state[HID_SEND_CH_USB_CONSUMER].protocol_mode =
KEYBOARD_PROTOCOL_MODE_REPORT;
channel_state[HID_SEND_CH_BLE_SHARED].protocol_mode =
ctx.channel_state[HID_SEND_CH_BLE_SHARED].protocol_mode =
KEYBOARD_PROTOCOL_MODE_REPORT;
next_sequence = 1U;
ctx.next_sequence = 1U;
return 0;
}
static int do_start(void)
{
if (running) {
if (module_lifecycle_is_running(&ctx.lc)) {
return 0;
}
@@ -426,7 +420,7 @@ static int do_start(void)
static int do_stop(void)
{
if (!running) {
if (!module_lifecycle_is_running(&ctx.lc)) {
return 0;
}
@@ -462,23 +456,23 @@ static bool app_event_handler(const struct app_event_header *aeh)
cast_module_state_event(aeh);
if (check_state(event, MODULE_ID(main), MODULE_STATE_READY)) {
(void)module_set_lifecycle(&lifecycle, LC_RUNNING);
(void)module_set_lifecycle(&ctx.lc, LC_RUNNING);
}
return false;
}
if (is_power_down_event(aeh)) {
if (module_lifecycle_is_initialized(&lifecycle)) {
(void)module_set_lifecycle(&lifecycle, LC_STOPPED);
if (module_lifecycle_is_initialized(&ctx.lc)) {
(void)module_set_lifecycle(&ctx.lc, LC_STOPPED);
}
return false;
}
if (is_wake_up_event(aeh)) {
if (module_lifecycle_is_initialized(&lifecycle)) {
(void)module_set_lifecycle(&lifecycle, LC_RUNNING);
if (module_lifecycle_is_initialized(&ctx.lc)) {
(void)module_set_lifecycle(&ctx.lc, LC_RUNNING);
}
return false;