feat(keyboard): 添加完整的HID键盘功能模块

- 新增keyboard_module.c实现完整的键盘HID功能,包括按键映射、
  协议处理和报告生成
- 添加hid_protocol_event和hid_report_event事件系统支持
- 实现键盘和consumer类型的HID报告处理
- 支持Boot协议和Report协议两种模式
- 添加hid_keymap_def.h定义键盘映射表

refactor(ble_hid): 重构HIDS模块为BLE HID模块

- 将hids_module.c重命名为ble_hid_module.c
- 集成新的hid_protocol_event和hid_report_event事件处理
- 改进协议切换逻辑,添加协议事件发布功能
- 优化BLE HID报告发送机制

refactor(CMakeLists): 更新构建配置

- 添加新的事件模块源文件到构建列表
- 添加keyboard_module.c替换原有的button_map_module.c
- 添加ble_hid_module.c替换原有的hids_module.c
- 配置HID密钥映射定义路径编译选项

refactor(events): 简化USB HID事件结构

- 移除USB HID事件中的冗余状态字段
- 更新事件日志和分析器字段定义

docs(hid): 添加HID报告描述符文档

- 定义REPORT_ID_KEYBOARD和REPORT_ID_CONSUMER枚举值
- 整理HID报告相关的常量定义
This commit is contained in:
2026-03-14 18:00:14 +08:00
parent a3196ef162
commit cd8101428d
13 changed files with 833 additions and 148 deletions

View File

@@ -13,6 +13,8 @@
#include <caf/events/module_state_event.h>
#include "hid_report_descriptor.h"
#include "hid_protocol_event.h"
#include "hid_report_event.h"
#include "mode_event.h"
#include "usb_hid_event.h"
@@ -48,14 +50,16 @@ struct usb_hid_ctx {
bool boot_iface_ready;
bool nkro_iface_ready;
bool raw_iface_ready;
bool boot_in_flight;
bool nkro_in_flight;
enum usb_hid_usbd_state usbd_state;
enum usb_hid_stack_state hid_state;
enum hid_protocol_type current_protocol;
};
static struct usb_hid_ctx g_usb_hid = {
.usbd_state = USB_HID_USBD_DISCONNECTED,
.hid_state = USB_HID_STACK_OFF,
.current_protocol = HID_PROTO_REPORT,
};
USBD_DEVICE_DEFINE(new_kbd_usbd,
@@ -78,37 +82,13 @@ static void publish_usb_hid_state(void)
event->evt_type = USB_HID_EVT_STATE_REPORT;
event->enable = g_usb_hid.stack_enabled;
event->usbd_state = g_usb_hid.usbd_state;
event->hid_state = g_usb_hid.hid_state;
APP_EVENT_SUBMIT(event);
}
static void recompute_hid_state(void)
{
enum usb_hid_stack_state new_hid_state;
if (g_usb_hid.stack_error) {
new_hid_state = USB_HID_STACK_ERROR;
} else if (g_usb_hid.pm_suspended && g_usb_hid.stack_enabled) {
new_hid_state = USB_HID_STACK_SUSPENDED;
} else if (!g_usb_hid.stack_initialized) {
new_hid_state = USB_HID_STACK_OFF;
} else if (g_usb_hid.stack_enabled &&
(g_usb_hid.boot_iface_ready ||
g_usb_hid.nkro_iface_ready ||
g_usb_hid.raw_iface_ready)) {
new_hid_state = USB_HID_STACK_ACTIVE;
} else {
/*
* 栈已初始化但未进入 ACTIVE例如刚 enable 还未配置、或 mode 切走后 disable
* 用 READY 表示“协议栈可用但当前未承载有效 HID 会话”。
*/
new_hid_state = USB_HID_STACK_READY;
}
if (g_usb_hid.hid_state != new_hid_state) {
g_usb_hid.hid_state = new_hid_state;
publish_usb_hid_state();
}
/* 兼容现有调用点:对外仅发布 enable + usbd 状态。 */
publish_usb_hid_state();
}
static void set_usbd_state(enum usb_hid_usbd_state state)
@@ -160,13 +140,49 @@ static uint32_t hid_stub_get_idle(const struct device *dev, uint8_t id)
static void hid_stub_set_protocol(const struct device *dev, uint8_t proto)
{
ARG_UNUSED(dev);
ARG_UNUSED(proto);
enum hid_protocol_type new_protocol =
(proto == HID_PROTOCOL_BOOT) ? HID_PROTO_BOOT : HID_PROTO_REPORT;
if (g_usb_hid.current_protocol == new_protocol) {
return;
}
g_usb_hid.current_protocol = new_protocol;
/*
* 按需求USB HID 在连接后收到 set_protocol 时上报 hid_protocol_event。
* 这里额外检查接口 ready避免在未枚举完成阶段上报无意义协议切换。
*/
if (g_usb_hid.boot_iface_ready || g_usb_hid.nkro_iface_ready) {
struct hid_protocol_event *event = new_hid_protocol_event();
event->transport = HID_TRANSPORT_USB;
event->protocol = new_protocol;
APP_EVENT_SUBMIT(event);
}
}
static void hid_stub_input_done(const struct device *dev, const uint8_t *report)
{
ARG_UNUSED(dev);
ARG_UNUSED(report);
/*
* 发送完成回调:
* - 仅在这里清除“在途发送”标志,确保“上一包未完成则丢弃新包”的策略可闭环;
* - 若收到未知 dev 的回调,仅记录告警,避免静默状态错乱。
*/
if (dev == g_usb_hid.boot_dev) {
g_usb_hid.boot_in_flight = false;
return;
}
if (dev == g_usb_hid.nkro_dev) {
g_usb_hid.nkro_in_flight = false;
return;
}
LOG_WRN("input_done from unknown HID dev: %p", (void *)dev);
}
static void hid_stub_output_report(const struct device *dev, uint16_t len, const uint8_t *buf)
@@ -180,14 +196,26 @@ static void hid_iface_ready_cb(const struct device *dev, bool ready)
{
if (dev == g_usb_hid.boot_dev) {
g_usb_hid.boot_iface_ready = ready;
if (!ready) {
g_usb_hid.boot_in_flight = false;
}
} else if (dev == g_usb_hid.nkro_dev) {
g_usb_hid.nkro_iface_ready = ready;
if (!ready) {
g_usb_hid.nkro_in_flight = false;
}
} else if (dev == g_usb_hid.raw_dev) {
g_usb_hid.raw_iface_ready = ready;
}
if (ready) {
set_usbd_state(USB_HID_USBD_CONNECTED);
/* 连接可用后同步一次当前协议,让 keyboard_module 与传输侧编码一致。 */
struct hid_protocol_event *event = new_hid_protocol_event();
event->transport = HID_TRANSPORT_USB;
event->protocol = g_usb_hid.current_protocol;
APP_EVENT_SUBMIT(event);
}
recompute_hid_state();
@@ -210,6 +238,7 @@ static const struct hid_device_ops report_hid_ops = {
.set_report = hid_stub_set_report,
.set_idle = hid_stub_set_idle,
.get_idle = hid_stub_get_idle,
.set_protocol = hid_stub_set_protocol,
.input_report_done = hid_stub_input_done,
.output_report = hid_stub_output_report,
};
@@ -435,6 +464,8 @@ static int usb_hid_set_enabled(bool enable)
g_usb_hid.boot_iface_ready = false;
g_usb_hid.nkro_iface_ready = false;
g_usb_hid.raw_iface_ready = false;
g_usb_hid.boot_in_flight = false;
g_usb_hid.nkro_in_flight = false;
set_usbd_state(USB_HID_USBD_DISCONNECTED);
}
@@ -517,6 +548,80 @@ static bool handle_wake_up_event(void)
return false;
}
static bool handle_hid_report_event(const struct hid_report_event *event)
{
/*
* USB 侧仅在 active 条件满足时发送:
* - 当前 mode 为 USB
* - USB HID 栈已启用且对应接口 ready。
*/
if (!g_usb_hid.usb_mode_selected || !g_usb_hid.stack_enabled) {
return false;
}
uint8_t report_id;
if (event->protocol != g_usb_hid.current_protocol) {
return false;
}
if (event->protocol == HID_PROTO_BOOT) {
const uint8_t *payload = event->dyndata.data;
size_t payload_len = event->dyndata.size;
report_id = REPORT_ID_KEYBOARD;
if (!g_usb_hid.boot_iface_ready || !g_usb_hid.boot_dev) {
return false;
}
if (report_id != REPORT_ID_KEYBOARD) {
return false;
}
if (g_usb_hid.boot_in_flight) {
LOG_WRN("Drop boot report: previous report not sent");
return false;
}
int err = hid_device_submit_report(g_usb_hid.boot_dev,
payload_len,
payload);
if (err) {
LOG_WRN("USB boot report send failed err=%d", err);
} else {
g_usb_hid.boot_in_flight = true;
}
return false;
}
if (event->dyndata.size < 1U) {
return false;
}
report_id = event->dyndata.data[0];
if (!g_usb_hid.nkro_iface_ready || !g_usb_hid.nkro_dev) {
return false;
}
if ((report_id != REPORT_ID_KEYBOARD) && (report_id != REPORT_ID_CONSUMER)) {
return false;
}
if (g_usb_hid.nkro_in_flight) {
LOG_WRN("Drop report id=0x%02x: previous report not sent", report_id);
return false;
}
/* Report 协议下 dyndata 是 [report_id|payload],可直接透传。 */
int err = hid_device_submit_report(g_usb_hid.nkro_dev, event->dyndata.size, event->dyndata.data);
if (err) {
LOG_WRN("USB report send failed id=0x%02x err=%d", report_id, err);
} else {
g_usb_hid.nkro_in_flight = true;
}
return false;
}
static bool app_event_handler(const struct app_event_header *aeh)
{
if (is_module_state_event(aeh)) {
@@ -535,6 +640,10 @@ static bool app_event_handler(const struct app_event_header *aeh)
return handle_wake_up_event();
}
if (is_hid_report_event(aeh)) {
return handle_hid_report_event(cast_hid_report_event(aeh));
}
__ASSERT_NO_MSG(false);
return false;
}
@@ -544,3 +653,4 @@ APP_EVENT_SUBSCRIBE(MODULE, module_state_event);
APP_EVENT_SUBSCRIBE(MODULE, mode_event);
APP_EVENT_SUBSCRIBE_EARLY(MODULE, power_down_event);
APP_EVENT_SUBSCRIBE(MODULE, wake_up_event);
APP_EVENT_SUBSCRIBE_EARLY(MODULE, hid_report_event);