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

@@ -125,16 +125,6 @@ static struct keyboard_core_module_ctx ctx = {
},
};
#define lifecycle ctx.lc
#define keyboard_state ctx.keyboard_state
#define reports_cache ctx.reports_cache
#define function_usage_mask ctx.function_usage_mask
#define transport_protocol_modes ctx.transport_protocol_modes
#define current_transport ctx.current_transport
#define mode_valid ctx.mode_valid
#define settings_active ctx.settings_active
#define running module_lifecycle_is_running(&ctx.lc)
static bool policy_to_transport(enum hid_transport_policy policy,
enum hid_transport *transport)
{
@@ -156,8 +146,9 @@ static enum keyboard_protocol_mode active_protocol_mode_get(void)
{
enum hid_transport transport;
if (mode_valid && policy_to_transport(current_transport, &transport)) {
return transport_protocol_modes[transport];
if (ctx.mode_valid &&
policy_to_transport(ctx.current_transport, &transport)) {
return ctx.transport_protocol_modes[transport];
}
return KEYBOARD_PROTOCOL_MODE_REPORT;
@@ -269,38 +260,39 @@ static bool consumer_key_update(uint16_t consumer_id, bool pressed)
return false;
}
bool was_pressed = (keyboard_state.consumer_bits & BIT(consumer_id)) != 0U;
bool was_pressed =
(ctx.keyboard_state.consumer_bits & BIT(consumer_id)) != 0U;
if (was_pressed == pressed) {
return false;
}
WRITE_BIT(keyboard_state.consumer_bits, consumer_id, pressed);
WRITE_BIT(ctx.keyboard_state.consumer_bits, consumer_id, pressed);
return true;
}
static void keyboard_state_clear(void)
{
memset(&keyboard_state, 0, sizeof(keyboard_state));
memset(&ctx.keyboard_state, 0, sizeof(ctx.keyboard_state));
}
static void function_usage_mask_clear(void)
{
memset(function_usage_mask, 0, sizeof(function_usage_mask));
memset(ctx.function_usage_mask, 0, sizeof(ctx.function_usage_mask));
}
static void reports_cache_invalidate(void)
{
reports_cache.boot_valid = false;
reports_cache.nkro_valid = false;
reports_cache.consumer_valid = false;
ctx.reports_cache.boot_valid = false;
ctx.reports_cache.nkro_valid = false;
ctx.reports_cache.consumer_valid = false;
}
static void build_effective_hid_bitmap(uint8_t bitmap[KEYBOARD_PROTOCOL_BITMAP_BYTES])
{
for (size_t i = 0; i < KEYBOARD_PROTOCOL_BITMAP_BYTES; i++) {
bitmap[i] = keyboard_state.pressed_usage_bitmap[i] &
(uint8_t)~keyboard_state.function_pressed_bitmap[i];
bitmap[i] = ctx.keyboard_state.pressed_usage_bitmap[i] &
(uint8_t)~ctx.keyboard_state.function_pressed_bitmap[i];
}
}
@@ -347,7 +339,7 @@ static void build_nkro_report(uint8_t report[KEYBOARD_NKRO_REPORT_SIZE])
static uint16_t active_consumer_usage_get(void)
{
for (uint8_t consumer_id = 0; consumer_id < KEYBOARD_CONSUMER_CTRL_COUNT; consumer_id++) {
if ((keyboard_state.consumer_bits & BIT(consumer_id)) != 0U) {
if ((ctx.keyboard_state.consumer_bits & BIT(consumer_id)) != 0U) {
return consumer_usage_map[consumer_id];
}
}
@@ -366,9 +358,9 @@ static void submit_consumer_fifo_frame(uint16_t usage_id)
enum keyboard_protocol_mode protocol_mode = active_protocol_mode_get();
enum mode_switch_mode mode;
if (!running || !mode_valid ||
settings_active ||
!transport_policy_to_mode(current_transport, &mode) ||
if (!module_lifecycle_is_running(&ctx.lc) || !ctx.mode_valid ||
ctx.settings_active ||
!transport_policy_to_mode(ctx.current_transport, &mode) ||
(protocol_mode == KEYBOARD_PROTOCOL_MODE_BOOT)) {
return;
}
@@ -414,24 +406,25 @@ static void emit_keys_report(bool force)
enum keyboard_protocol_mode protocol_mode = active_protocol_mode_get();
enum mode_switch_mode mode;
if (!mode_valid || !transport_policy_to_mode(current_transport, &mode)) {
if (!ctx.mode_valid ||
!transport_policy_to_mode(ctx.current_transport, &mode)) {
return;
}
if (settings_active) {
if (ctx.settings_active) {
return;
}
if (protocol_mode == KEYBOARD_PROTOCOL_MODE_BOOT) {
build_boot_report(report_buf);
report_size = KEYBOARD_BOOT_REPORT_SIZE;
cache_buf = reports_cache.boot_report;
cache_valid = &reports_cache.boot_valid;
cache_buf = ctx.reports_cache.boot_report;
cache_valid = &ctx.reports_cache.boot_valid;
} else {
build_nkro_report(report_buf);
report_size = KEYBOARD_NKRO_REPORT_SIZE;
cache_buf = reports_cache.nkro_report;
cache_valid = &reports_cache.nkro_valid;
cache_buf = ctx.reports_cache.nkro_report;
cache_valid = &ctx.reports_cache.nkro_valid;
}
if (!force && *cache_valid && (memcmp(cache_buf, report_buf, report_size) == 0)) {
@@ -452,21 +445,23 @@ static void emit_consumer_report(bool force)
enum keyboard_protocol_mode protocol_mode = active_protocol_mode_get();
enum mode_switch_mode mode;
if (!mode_valid || !transport_policy_to_mode(current_transport, &mode) ||
settings_active ||
if (!ctx.mode_valid ||
!transport_policy_to_mode(ctx.current_transport, &mode) ||
ctx.settings_active ||
(protocol_mode == KEYBOARD_PROTOCOL_MODE_BOOT)) {
return;
}
build_consumer_report(report_buf);
if (!force && reports_cache.consumer_valid &&
(memcmp(reports_cache.consumer_report, report_buf,
if (!force && ctx.reports_cache.consumer_valid &&
(memcmp(ctx.reports_cache.consumer_report, report_buf,
KEYBOARD_CONSUMER_REPORT_SIZE) == 0)) {
return;
}
memcpy(reports_cache.consumer_report, report_buf, KEYBOARD_CONSUMER_REPORT_SIZE);
reports_cache.consumer_valid = true;
memcpy(ctx.reports_cache.consumer_report, report_buf,
KEYBOARD_CONSUMER_REPORT_SIZE);
ctx.reports_cache.consumer_valid = true;
(void)submit_keyboard_hid_report_event(
mode, KEYBOARD_REPORT_TYPE_CONSUMER, protocol_mode,
@@ -485,7 +480,8 @@ static void emit_all_reports(bool force)
static void emit_function_state_event(void)
{
(void)submit_function_bitmap_state_event(keyboard_state.pressed_usage_bitmap);
(void)submit_function_bitmap_state_event(
ctx.keyboard_state.pressed_usage_bitmap);
}
static void emit_release_reports(enum hid_transport_policy transport_policy)
@@ -526,11 +522,11 @@ static int do_init(void)
keyboard_state_clear();
reports_cache_invalidate();
function_usage_mask_clear();
mode_valid = false;
settings_active = false;
transport_protocol_modes[HID_TRANSPORT_USB] =
ctx.mode_valid = false;
ctx.settings_active = false;
ctx.transport_protocol_modes[HID_TRANSPORT_USB] =
KEYBOARD_PROTOCOL_MODE_REPORT;
transport_protocol_modes[HID_TRANSPORT_BLE] =
ctx.transport_protocol_modes[HID_TRANSPORT_BLE] =
KEYBOARD_PROTOCOL_MODE_REPORT;
return 0;
@@ -538,7 +534,7 @@ static int do_init(void)
static int do_start(void)
{
if (running) {
if (module_lifecycle_is_running(&ctx.lc)) {
return 0;
}
@@ -547,18 +543,18 @@ static int do_start(void)
static int do_stop(void)
{
if (!running) {
if (!module_lifecycle_is_running(&ctx.lc)) {
return 0;
}
if (mode_valid) {
emit_release_reports(current_transport);
if (ctx.mode_valid) {
emit_release_reports(ctx.current_transport);
}
emit_function_state_event();
keyboard_state_clear();
reports_cache_invalidate();
mode_valid = false;
ctx.mode_valid = false;
return 0;
}
@@ -568,11 +564,11 @@ static bool handle_button_event(const struct button_event *event)
const struct keymap_entry *entry;
bool changed;
if (!running) {
if (!module_lifecycle_is_running(&ctx.lc)) {
return false;
}
if (settings_active) {
if (ctx.settings_active) {
return false;
}
@@ -585,7 +581,7 @@ static bool handle_button_event(const struct button_event *event)
if (entry->usage_type == KEY_USAGE_TYPE_KEYBOARD) {
bool routed_to_function;
changed = usage_bitmap_write(keyboard_state.pressed_usage_bitmap,
changed = usage_bitmap_write(ctx.keyboard_state.pressed_usage_bitmap,
entry->usage_id, event->pressed);
if (!changed) {
return false;
@@ -593,14 +589,14 @@ static bool handle_button_event(const struct button_event *event)
if (event->pressed) {
routed_to_function =
usage_bitmap_test(function_usage_mask, entry->usage_id);
(void)usage_bitmap_write(keyboard_state.function_pressed_bitmap,
usage_bitmap_test(ctx.function_usage_mask, entry->usage_id);
(void)usage_bitmap_write(ctx.keyboard_state.function_pressed_bitmap,
entry->usage_id, routed_to_function);
} else {
routed_to_function =
usage_bitmap_test(keyboard_state.function_pressed_bitmap,
usage_bitmap_test(ctx.keyboard_state.function_pressed_bitmap,
entry->usage_id);
(void)usage_bitmap_write(keyboard_state.function_pressed_bitmap,
(void)usage_bitmap_write(ctx.keyboard_state.function_pressed_bitmap,
entry->usage_id, false);
}
@@ -624,23 +620,24 @@ static bool handle_transport_policy_event(
{
bool transport_changed;
if (!running) {
current_transport = event->hid_transport;
if (!module_lifecycle_is_running(&ctx.lc)) {
ctx.current_transport = event->hid_transport;
return false;
}
transport_changed = mode_valid && (current_transport != event->hid_transport);
transport_changed =
ctx.mode_valid && (ctx.current_transport != event->hid_transport);
if (transport_changed) {
emit_release_reports(current_transport);
emit_release_reports(ctx.current_transport);
emit_function_state_event();
keyboard_state_clear();
reports_cache_invalidate();
}
current_transport = event->hid_transport;
mode_valid = (current_transport != HID_TRANSPORT_POLICY_NONE);
ctx.current_transport = event->hid_transport;
ctx.mode_valid = (ctx.current_transport != HID_TRANSPORT_POLICY_NONE);
if (mode_valid) {
if (ctx.mode_valid) {
emit_all_reports(true);
}
@@ -649,11 +646,11 @@ static bool handle_transport_policy_event(
static bool handle_encoder_event(const struct encoder_event *event)
{
if (!running || !mode_valid) {
if (!module_lifecycle_is_running(&ctx.lc) || !ctx.mode_valid) {
return false;
}
if (settings_active) {
if (ctx.settings_active) {
return false;
}
@@ -671,7 +668,8 @@ static bool handle_encoder_event(const struct encoder_event *event)
static bool handle_function_bitmap_update_event(
const struct function_bitmap_update_event *event)
{
memcpy(function_usage_mask, event->bitmap, sizeof(function_usage_mask));
memcpy(ctx.function_usage_mask, event->bitmap,
sizeof(ctx.function_usage_mask));
return false;
}
@@ -698,11 +696,13 @@ static bool app_event_handler(const struct app_event_header *aeh)
return false;
}
if (transport_protocol_modes[event->transport] != event->protocol_mode) {
transport_protocol_modes[event->transport] = event->protocol_mode;
if (ctx.transport_protocol_modes[event->transport] !=
event->protocol_mode) {
ctx.transport_protocol_modes[event->transport] =
event->protocol_mode;
if (running && mode_valid &&
policy_to_transport(current_transport, &active_transport) &&
if (module_lifecycle_is_running(&ctx.lc) && ctx.mode_valid &&
policy_to_transport(ctx.current_transport, &active_transport) &&
(active_transport == event->transport)) {
reports_cache_invalidate();
emit_keys_report(true);
@@ -725,14 +725,14 @@ static bool app_event_handler(const struct app_event_header *aeh)
const struct settings_mode_event *event =
cast_settings_mode_event(aeh);
if (settings_active == event->active) {
if (ctx.settings_active == event->active) {
return false;
}
settings_active = event->active;
if (settings_active) {
if (mode_valid) {
emit_release_reports(current_transport);
ctx.settings_active = event->active;
if (ctx.settings_active) {
if (ctx.mode_valid) {
emit_release_reports(ctx.current_transport);
}
emit_function_state_event();
keyboard_state_clear();
@@ -746,23 +746,23 @@ static bool app_event_handler(const struct app_event_header *aeh)
const struct module_state_event *event = 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;