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:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user