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