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:
42
src/events/hid_protocol_event.c
Normal file
42
src/events/hid_protocol_event.c
Normal file
@@ -0,0 +1,42 @@
|
||||
#include "hid_protocol_event.h"
|
||||
|
||||
static const char *const hid_transport_name[] = {
|
||||
[HID_TRANSPORT_BLE] = "BLE",
|
||||
[HID_TRANSPORT_USB] = "USB",
|
||||
};
|
||||
|
||||
static const char *const hid_protocol_name[] = {
|
||||
[HID_PROTO_BOOT] = "BOOT",
|
||||
[HID_PROTO_REPORT] = "REPORT",
|
||||
};
|
||||
|
||||
static void log_hid_protocol_event(const struct app_event_header *aeh)
|
||||
{
|
||||
const struct hid_protocol_event *event = cast_hid_protocol_event(aeh);
|
||||
|
||||
__ASSERT_NO_MSG(event->transport < ARRAY_SIZE(hid_transport_name));
|
||||
__ASSERT_NO_MSG(event->protocol < ARRAY_SIZE(hid_protocol_name));
|
||||
|
||||
APP_EVENT_MANAGER_LOG(aeh, "transport=%s protocol=%s",
|
||||
hid_transport_name[event->transport],
|
||||
hid_protocol_name[event->protocol]);
|
||||
}
|
||||
|
||||
static void profile_hid_protocol_event(struct log_event_buf *buf,
|
||||
const struct app_event_header *aeh)
|
||||
{
|
||||
const struct hid_protocol_event *event = cast_hid_protocol_event(aeh);
|
||||
|
||||
nrf_profiler_log_encode_uint8(buf, (uint8_t)event->transport);
|
||||
nrf_profiler_log_encode_uint8(buf, (uint8_t)event->protocol);
|
||||
}
|
||||
|
||||
APP_EVENT_INFO_DEFINE(hid_protocol_event,
|
||||
ENCODE(NRF_PROFILER_ARG_U8, NRF_PROFILER_ARG_U8),
|
||||
ENCODE("transport", "protocol"),
|
||||
profile_hid_protocol_event);
|
||||
|
||||
APP_EVENT_TYPE_DEFINE(hid_protocol_event,
|
||||
log_hid_protocol_event,
|
||||
&hid_protocol_event_info,
|
||||
APP_EVENT_FLAGS_CREATE(APP_EVENT_TYPE_FLAGS_INIT_LOG_ENABLE));
|
||||
30
src/events/hid_protocol_event.h
Normal file
30
src/events/hid_protocol_event.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#ifndef HID_PROTOCOL_EVENT_H__
|
||||
#define HID_PROTOCOL_EVENT_H__
|
||||
|
||||
#include <app_event_manager.h>
|
||||
#include <app_event_manager_profiler_tracer.h>
|
||||
|
||||
enum hid_transport_type {
|
||||
HID_TRANSPORT_BLE = 0,
|
||||
HID_TRANSPORT_USB,
|
||||
};
|
||||
|
||||
enum hid_protocol_type {
|
||||
HID_PROTO_BOOT = 0,
|
||||
HID_PROTO_REPORT,
|
||||
};
|
||||
|
||||
/*
|
||||
* HID 传输层在收到主机 set_protocol 请求后上报该事件,
|
||||
* keyboard_module 会依据当前 active 传输的协议格式打包 hid_report_event。
|
||||
*/
|
||||
struct hid_protocol_event {
|
||||
struct app_event_header header;
|
||||
|
||||
enum hid_transport_type transport;
|
||||
enum hid_protocol_type protocol;
|
||||
};
|
||||
|
||||
APP_EVENT_TYPE_DECLARE(hid_protocol_event);
|
||||
|
||||
#endif /* HID_PROTOCOL_EVENT_H__ */
|
||||
53
src/events/hid_report_event.c
Normal file
53
src/events/hid_report_event.c
Normal file
@@ -0,0 +1,53 @@
|
||||
#include "hid_report_event.h"
|
||||
|
||||
static const char *const hid_protocol_name[] = {
|
||||
[HID_PROTO_BOOT] = "BOOT",
|
||||
[HID_PROTO_REPORT] = "REPORT",
|
||||
};
|
||||
|
||||
static void log_hid_report_event(const struct app_event_header *aeh)
|
||||
{
|
||||
const struct hid_report_event *event = cast_hid_report_event(aeh);
|
||||
uint8_t report_id = 0x00;
|
||||
uint16_t payload_len = event->dyndata.size;
|
||||
|
||||
__ASSERT_NO_MSG(event->protocol < ARRAY_SIZE(hid_protocol_name));
|
||||
|
||||
if (event->protocol == HID_PROTO_REPORT) {
|
||||
__ASSERT_NO_MSG(event->dyndata.size >= 1U);
|
||||
report_id = event->dyndata.data[0];
|
||||
payload_len = event->dyndata.size - 1U;
|
||||
}
|
||||
|
||||
APP_EVENT_MANAGER_LOG(aeh, "protocol=%s report_id=0x%02x payload_len=%u",
|
||||
hid_protocol_name[event->protocol],
|
||||
report_id,
|
||||
payload_len);
|
||||
}
|
||||
|
||||
static void profile_hid_report_event(struct log_event_buf *buf,
|
||||
const struct app_event_header *aeh)
|
||||
{
|
||||
const struct hid_report_event *event = cast_hid_report_event(aeh);
|
||||
uint8_t report_id = 0x00;
|
||||
|
||||
if ((event->protocol == HID_PROTO_REPORT) && (event->dyndata.size >= 1U)) {
|
||||
report_id = event->dyndata.data[0];
|
||||
}
|
||||
|
||||
nrf_profiler_log_encode_uint8(buf, (uint8_t)event->protocol);
|
||||
nrf_profiler_log_encode_uint8(buf, report_id);
|
||||
nrf_profiler_log_encode_uint16(buf, event->dyndata.size);
|
||||
}
|
||||
|
||||
APP_EVENT_INFO_DEFINE(hid_report_event,
|
||||
ENCODE(NRF_PROFILER_ARG_U8,
|
||||
NRF_PROFILER_ARG_U8,
|
||||
NRF_PROFILER_ARG_U16),
|
||||
ENCODE("protocol", "report_id", "len"),
|
||||
profile_hid_report_event);
|
||||
|
||||
APP_EVENT_TYPE_DEFINE(hid_report_event,
|
||||
log_hid_report_event,
|
||||
&hid_report_event_info,
|
||||
APP_EVENT_FLAGS_CREATE(APP_EVENT_TYPE_FLAGS_INIT_LOG_ENABLE));
|
||||
24
src/events/hid_report_event.h
Normal file
24
src/events/hid_report_event.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#ifndef HID_REPORT_EVENT_H__
|
||||
#define HID_REPORT_EVENT_H__
|
||||
|
||||
#include <app_event_manager.h>
|
||||
#include <app_event_manager_profiler_tracer.h>
|
||||
|
||||
#include "hid_protocol_event.h"
|
||||
|
||||
/*
|
||||
* HID 输入报告统一事件:
|
||||
* - protocol 指示当前 dyndata 编码规则;
|
||||
* - 当 protocol=HID_PROTO_REPORT:dyndata[0]=report_id,dyndata[1..]=payload;
|
||||
* - 当 protocol=HID_PROTO_BOOT:dyndata 仅包含 boot payload(不含 report_id)。
|
||||
*/
|
||||
struct hid_report_event {
|
||||
struct app_event_header header;
|
||||
|
||||
enum hid_protocol_type protocol;
|
||||
struct event_dyndata dyndata;
|
||||
};
|
||||
|
||||
APP_EVENT_TYPE_DYNDATA_DECLARE(hid_report_event);
|
||||
|
||||
#endif /* HID_REPORT_EVENT_H__ */
|
||||
@@ -10,27 +10,17 @@ static const char *const usb_hid_usbd_state_name[] = {
|
||||
[USB_HID_USBD_SUSPENDED] = "SUSPENDED",
|
||||
};
|
||||
|
||||
static const char *const usb_hid_stack_state_name[] = {
|
||||
[USB_HID_STACK_OFF] = "OFF",
|
||||
[USB_HID_STACK_READY] = "READY",
|
||||
[USB_HID_STACK_ACTIVE] = "ACTIVE",
|
||||
[USB_HID_STACK_SUSPENDED] = "SUSPENDED",
|
||||
[USB_HID_STACK_ERROR] = "ERROR",
|
||||
};
|
||||
|
||||
static void log_usb_hid_event(const struct app_event_header *aeh)
|
||||
{
|
||||
const struct usb_hid_event *event = cast_usb_hid_event(aeh);
|
||||
|
||||
__ASSERT_NO_MSG(event->evt_type < ARRAY_SIZE(usb_hid_evt_type_name));
|
||||
__ASSERT_NO_MSG(event->usbd_state < ARRAY_SIZE(usb_hid_usbd_state_name));
|
||||
__ASSERT_NO_MSG(event->hid_state < ARRAY_SIZE(usb_hid_stack_state_name));
|
||||
|
||||
APP_EVENT_MANAGER_LOG(aeh, "type=%s en=%u usbd=%s hid=%s",
|
||||
APP_EVENT_MANAGER_LOG(aeh, "type=%s en=%u usbd=%s",
|
||||
usb_hid_evt_type_name[event->evt_type],
|
||||
event->enable,
|
||||
usb_hid_usbd_state_name[event->usbd_state],
|
||||
usb_hid_stack_state_name[event->hid_state]);
|
||||
usb_hid_usbd_state_name[event->usbd_state]);
|
||||
}
|
||||
|
||||
static void profile_usb_hid_event(struct log_event_buf *buf,
|
||||
@@ -41,15 +31,13 @@ static void profile_usb_hid_event(struct log_event_buf *buf,
|
||||
nrf_profiler_log_encode_uint8(buf, (uint8_t)event->evt_type);
|
||||
nrf_profiler_log_encode_uint8(buf, (uint8_t)event->enable);
|
||||
nrf_profiler_log_encode_uint8(buf, (uint8_t)event->usbd_state);
|
||||
nrf_profiler_log_encode_uint8(buf, (uint8_t)event->hid_state);
|
||||
}
|
||||
|
||||
APP_EVENT_INFO_DEFINE(usb_hid_event,
|
||||
ENCODE(NRF_PROFILER_ARG_U8,
|
||||
NRF_PROFILER_ARG_U8,
|
||||
NRF_PROFILER_ARG_U8,
|
||||
NRF_PROFILER_ARG_U8),
|
||||
ENCODE("evt_type", "enable", "usbd", "hid"),
|
||||
ENCODE("evt_type", "enable", "usbd"),
|
||||
profile_usb_hid_event);
|
||||
|
||||
APP_EVENT_TYPE_DEFINE(usb_hid_event,
|
||||
|
||||
@@ -18,22 +18,12 @@ enum usb_hid_usbd_state {
|
||||
USB_HID_USBD_SUSPENDED,
|
||||
};
|
||||
|
||||
/* HID 协议栈状态(偏“服务是否运行”) */
|
||||
enum usb_hid_stack_state {
|
||||
USB_HID_STACK_OFF = 0,
|
||||
USB_HID_STACK_READY,
|
||||
USB_HID_STACK_ACTIVE,
|
||||
USB_HID_STACK_SUSPENDED,
|
||||
USB_HID_STACK_ERROR,
|
||||
};
|
||||
|
||||
struct usb_hid_event {
|
||||
struct app_event_header header;
|
||||
|
||||
enum usb_hid_event_type evt_type;
|
||||
bool enable;
|
||||
enum usb_hid_usbd_state usbd_state;
|
||||
enum usb_hid_stack_state hid_state;
|
||||
};
|
||||
|
||||
APP_EVENT_TYPE_DECLARE(usb_hid_event);
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
#include <bluetooth/services/hids.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <app_event_manager.h>
|
||||
|
||||
#define MODULE hids
|
||||
#define MODULE ble_hid
|
||||
#include <caf/events/module_state_event.h>
|
||||
#include <caf/events/ble_common_event.h>
|
||||
|
||||
#include "hid_types.h"
|
||||
#include "hid_protocol_event.h"
|
||||
#include "hid_report_event.h"
|
||||
#include "hid_report_descriptor.h"
|
||||
#include "mode_event.h"
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF);
|
||||
@@ -17,13 +20,27 @@ LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF);
|
||||
#define KEYBOARD_REPORT_LEN 30
|
||||
#define CONSUMER_REPORT_LEN 2
|
||||
#define KEYBOARD_LED_REPORT_LEN 1
|
||||
#define BOOT_KEYBOARD_REPORT_LEN 8
|
||||
|
||||
/* 注册 HIDS 实例。此版本聚焦最小可用链路(Boot + Report)。 */
|
||||
BT_HIDS_DEF(hids_obj, INPUT_REPORT_COUNT, OUTPUT_REPORT_COUNT, 0);
|
||||
|
||||
static struct bt_conn *active_conn;
|
||||
static enum bt_hids_pm current_pm = BT_HIDS_PM_REPORT;
|
||||
static bool ble_mode_selected;
|
||||
|
||||
static enum hid_protocol_type pm_to_protocol(enum bt_hids_pm pm)
|
||||
{
|
||||
return (pm == BT_HIDS_PM_BOOT) ? HID_PROTO_BOOT : HID_PROTO_REPORT;
|
||||
}
|
||||
|
||||
static void publish_hid_protocol_event(enum hid_protocol_type protocol)
|
||||
{
|
||||
struct hid_protocol_event *event = new_hid_protocol_event();
|
||||
|
||||
event->transport = HID_TRANSPORT_BLE;
|
||||
event->protocol = protocol;
|
||||
APP_EVENT_SUBMIT(event);
|
||||
}
|
||||
|
||||
static void pm_evt_handler(enum bt_hids_pm_evt evt, struct bt_conn *conn)
|
||||
{
|
||||
@@ -33,10 +50,16 @@ static void pm_evt_handler(enum bt_hids_pm_evt evt, struct bt_conn *conn)
|
||||
case BT_HIDS_PM_EVT_BOOT_MODE_ENTERED:
|
||||
current_pm = BT_HIDS_PM_BOOT;
|
||||
LOG_INF("HIDS protocol: boot");
|
||||
if (active_conn) {
|
||||
publish_hid_protocol_event(HID_PROTO_BOOT);
|
||||
}
|
||||
break;
|
||||
case BT_HIDS_PM_EVT_REPORT_MODE_ENTERED:
|
||||
current_pm = BT_HIDS_PM_REPORT;
|
||||
LOG_INF("HIDS protocol: report");
|
||||
if (active_conn) {
|
||||
publish_hid_protocol_event(HID_PROTO_REPORT);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -59,7 +82,6 @@ static void boot_keyboard_output_report_handler(struct bt_hids_rep *rep,
|
||||
{
|
||||
ARG_UNUSED(conn);
|
||||
|
||||
/* Basic boot protocol support: accept host LED writes and keep state locally. */
|
||||
if (!write || !rep || (rep->size == 0) || !rep->data) {
|
||||
return;
|
||||
}
|
||||
@@ -73,22 +95,11 @@ static void keyboard_output_report_handler(struct bt_hids_rep *rep,
|
||||
{
|
||||
ARG_UNUSED(conn);
|
||||
|
||||
/*
|
||||
* 该回调用于 Report 协议的键盘 LED 输出(NumLock 等)。
|
||||
* 这里仅做最小解析并暴露注册回调,具体业务(例如驱动指示灯)留给上层实现。
|
||||
*/
|
||||
if (!write || !rep || !rep->data || (rep->size < KEYBOARD_LED_REPORT_LEN)) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t leds = rep->data[0];
|
||||
LOG_DBG("Report KB out report 0x%02x", leds);
|
||||
|
||||
/*
|
||||
* 预留:后续在这里把 LED 输出转换为 CAF 事件(例如 NumLock 状态事件),
|
||||
* 由上层模块消费并驱动板级指示灯。
|
||||
*/
|
||||
ARG_UNUSED(leds);
|
||||
LOG_DBG("Report KB out report 0x%02x", rep->data[0]);
|
||||
}
|
||||
|
||||
static int hids_service_init(void)
|
||||
@@ -113,10 +124,6 @@ static int hids_service_init(void)
|
||||
input_report[1].size = CONSUMER_REPORT_LEN;
|
||||
input_report[1].handler = report_notify_handler;
|
||||
|
||||
/*
|
||||
* Report 协议键盘输出报告:
|
||||
* 与 Report Map 中 REPORT_ID_KEYBOARD 下定义的 1 字节 LED Output 对齐。
|
||||
*/
|
||||
output_report[0].id = REPORT_ID_KEYBOARD;
|
||||
output_report[0].size = KEYBOARD_LED_REPORT_LEN;
|
||||
output_report[0].handler = keyboard_output_report_handler;
|
||||
@@ -140,6 +147,8 @@ static void handle_ble_peer_event(const struct ble_peer_event *event)
|
||||
if (bt_hids_connected(&hids_obj, active_conn)) {
|
||||
LOG_WRN("bt_hids_connected failed");
|
||||
}
|
||||
/* 连接建立后按当前协议主动同步一次,避免 keyboard_module 等待下一次 set_protocol。 */
|
||||
publish_hid_protocol_event(pm_to_protocol(current_pm));
|
||||
break;
|
||||
|
||||
case PEER_STATE_DISCONNECTED:
|
||||
@@ -156,6 +165,80 @@ static void handle_ble_peer_event(const struct ble_peer_event *event)
|
||||
}
|
||||
}
|
||||
|
||||
static bool handle_hid_report_event(const struct hid_report_event *event)
|
||||
{
|
||||
if (!ble_mode_selected || !active_conn) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t report_id;
|
||||
const uint8_t *payload;
|
||||
size_t payload_len;
|
||||
|
||||
/*
|
||||
* keyboard_module 已经按照 protocol 打包好了完整 payload。
|
||||
* BLE 模块这里只做协议一致性检查与发送。
|
||||
*/
|
||||
if ((current_pm == BT_HIDS_PM_BOOT) && (event->protocol != HID_PROTO_BOOT)) {
|
||||
return false;
|
||||
}
|
||||
if ((current_pm == BT_HIDS_PM_REPORT) && (event->protocol != HID_PROTO_REPORT)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (event->protocol == HID_PROTO_BOOT) {
|
||||
report_id = REPORT_ID_KEYBOARD;
|
||||
payload = event->dyndata.data;
|
||||
payload_len = event->dyndata.size;
|
||||
} else {
|
||||
if (event->dyndata.size < 1U) {
|
||||
return false;
|
||||
}
|
||||
report_id = event->dyndata.data[0];
|
||||
payload = &event->dyndata.data[1];
|
||||
payload_len = event->dyndata.size - 1U;
|
||||
}
|
||||
|
||||
int err = 0;
|
||||
|
||||
if (event->protocol == HID_PROTO_BOOT) {
|
||||
if (report_id != REPORT_ID_KEYBOARD) {
|
||||
return false;
|
||||
}
|
||||
if (payload_len != BOOT_KEYBOARD_REPORT_LEN) {
|
||||
LOG_WRN("Invalid boot keyboard payload len=%u", payload_len);
|
||||
return false;
|
||||
}
|
||||
|
||||
err = bt_hids_boot_kb_inp_rep_send(&hids_obj, active_conn,
|
||||
payload, payload_len, NULL);
|
||||
} else {
|
||||
uint8_t rep_index;
|
||||
|
||||
if (report_id == REPORT_ID_KEYBOARD) {
|
||||
rep_index = 0U;
|
||||
} else if (report_id == REPORT_ID_CONSUMER) {
|
||||
rep_index = 1U;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (payload_len > UINT8_MAX) {
|
||||
LOG_WRN("Payload too large=%u", payload_len);
|
||||
return false;
|
||||
}
|
||||
|
||||
err = bt_hids_inp_rep_send(&hids_obj, active_conn, rep_index,
|
||||
payload, (uint8_t)payload_len, NULL);
|
||||
}
|
||||
|
||||
if (err) {
|
||||
LOG_WRN("BLE HID send failed report=0x%02x err=%d", report_id, err);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool app_event_handler(const struct app_event_header *aeh)
|
||||
{
|
||||
if (is_module_state_event(aeh)) {
|
||||
@@ -183,11 +266,22 @@ static bool app_event_handler(const struct app_event_header *aeh)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (is_mode_event(aeh)) {
|
||||
const struct mode_event *event = cast_mode_event(aeh);
|
||||
ble_mode_selected = (event->mode_type == MODE_TYPE_BLE);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (is_hid_report_event(aeh)) {
|
||||
return handle_hid_report_event(cast_hid_report_event(aeh));
|
||||
}
|
||||
|
||||
__ASSERT_NO_MSG(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
APP_EVENT_LISTENER(MODULE, app_event_handler);
|
||||
/* Ensure GATT HIDS is registered before BLE is enabled by ble_state. */
|
||||
APP_EVENT_SUBSCRIBE_EARLY(MODULE, module_state_event);
|
||||
APP_EVENT_SUBSCRIBE_EARLY(MODULE, ble_peer_event);
|
||||
APP_EVENT_SUBSCRIBE(MODULE, mode_event);
|
||||
APP_EVENT_SUBSCRIBE(MODULE, hid_report_event);
|
||||
@@ -1,69 +0,0 @@
|
||||
#include <app_event_manager.h>
|
||||
|
||||
#define MODULE button_map
|
||||
#include <caf/events/module_state_event.h>
|
||||
|
||||
#include <caf/events/button_event.h>
|
||||
#include <caf/key_id.h>
|
||||
#include <zephyr/dt-bindings/input/input-event-codes.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
LOG_MODULE_REGISTER(MODULE);
|
||||
|
||||
/*
|
||||
* keymap[row][col] 直接对齐板级 DTS 里的 MATRIX_KEY(row, col, key) 定义。
|
||||
* 值为 Linux input key code;-1 表示该矩阵位置未映射功能键。
|
||||
*/
|
||||
static const int16_t keymap[6][4] = {
|
||||
/* row 0 */ { -1, -1, -1, INPUT_KEY_MUTE },
|
||||
/* row 1 */ { INPUT_KEY_NUMLOCK, INPUT_KEY_KPSLASH, INPUT_KEY_KPASTERISK, INPUT_KEY_KPMINUS },
|
||||
/* row 2 */ { INPUT_KEY_KP7, INPUT_KEY_KP8, INPUT_KEY_KP9, -1 },
|
||||
/* row 3 */ { INPUT_KEY_KP4, INPUT_KEY_KP5, INPUT_KEY_KP6, INPUT_KEY_KPPLUS },
|
||||
/* row 4 */ { INPUT_KEY_KP1, INPUT_KEY_KP2, INPUT_KEY_KP3, -1 },
|
||||
/* row 5 */ { INPUT_KEY_KP0, INPUT_KEY_KPDOT, -1, INPUT_KEY_KPENTER },
|
||||
};
|
||||
|
||||
static bool app_event_handler(const struct app_event_header *aeh)
|
||||
{
|
||||
if (is_button_event(aeh)) {
|
||||
const struct button_event *event = cast_button_event(aeh);
|
||||
uint8_t row = KEY_ROW(event->key_id);
|
||||
uint8_t col = KEY_COL(event->key_id);
|
||||
|
||||
/*
|
||||
* 防御性检查:若行列越界,说明 buttons_def 与实际扫描结果不一致,
|
||||
* 记录错误以便尽快发现硬件矩阵定义或固件配置问题。
|
||||
*/
|
||||
if ((row >= ARRAY_SIZE(keymap)) || (col >= ARRAY_SIZE(keymap[0]))) {
|
||||
LOG_ERR("Unknown key_id=0x%04x (row=%u, col=%u)", event->key_id, row, col);
|
||||
return false;
|
||||
}
|
||||
|
||||
int16_t code = keymap[row][col];
|
||||
if (code < 0) {
|
||||
LOG_INF("Button %s row=%u col=%u unmapped", event->pressed ? "down" : "up", row, col);
|
||||
} else {
|
||||
LOG_INF("Button %s row=%u col=%u keycode=%d", event->pressed ? "down" : "up", row, col, code);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (is_module_state_event(aeh)) {
|
||||
const struct module_state_event *event = cast_module_state_event(aeh);
|
||||
|
||||
if (check_state(event, MODULE_ID(main), MODULE_STATE_READY)) {
|
||||
module_set_state(MODULE_STATE_READY);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* 未处理但已订阅的事件类型不应进入这里。 */
|
||||
__ASSERT_NO_MSG(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
APP_EVENT_LISTENER(MODULE, app_event_handler);
|
||||
APP_EVENT_SUBSCRIBE(MODULE, button_event);
|
||||
APP_EVENT_SUBSCRIBE(MODULE, module_state_event);
|
||||
368
src/modules/keyboard_module.c
Normal file
368
src/modules/keyboard_module.c
Normal file
@@ -0,0 +1,368 @@
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <app_event_manager.h>
|
||||
|
||||
#define MODULE keyboard
|
||||
#include <caf/events/module_state_event.h>
|
||||
|
||||
#include <caf/events/button_event.h>
|
||||
#include <caf/key_id.h>
|
||||
|
||||
#include "hid_report_descriptor.h"
|
||||
#include "hid_protocol_event.h"
|
||||
#include "hid_report_event.h"
|
||||
#include "mode_event.h"
|
||||
|
||||
#include <zephyr/sys/util.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF);
|
||||
|
||||
/*
|
||||
* 参考 nrf_desktop 的表驱动设计:
|
||||
* - key_id -> (usage_id, report_id) 映射定义在外部 hid_keymap_def.h;
|
||||
* - keyboard_module 内部完成映射表校验与查询,不再依赖独立 hid_keymap 模块。
|
||||
*/
|
||||
struct hid_keymap {
|
||||
uint16_t key_id;
|
||||
uint16_t usage_id;
|
||||
uint8_t report_id;
|
||||
};
|
||||
|
||||
#include APP_HID_KEYMAP_DEF_PATH
|
||||
|
||||
static bool hid_keymap_initialized;
|
||||
|
||||
/* 比较函数:供 bsearch 按 key_id 升序查找映射项。 */
|
||||
static int hid_keymap_compare(const void *a, const void *b)
|
||||
{
|
||||
const struct hid_keymap *pa = a;
|
||||
const struct hid_keymap *pb = b;
|
||||
|
||||
return ((int)pa->key_id - (int)pb->key_id);
|
||||
}
|
||||
|
||||
/*
|
||||
* 初始化并校验 hid_keymap:
|
||||
* - 仅在 CONFIG_ASSERT 打开时执行校验,避免 release 构建引入额外开销;
|
||||
* - 校验 key_id 严格升序,确保二分查找行为正确;
|
||||
* - 校验 report_id 只落在当前模块支持的 Keyboard/Consumer 两类。
|
||||
*/
|
||||
static void hid_keymap_init_local(void)
|
||||
{
|
||||
if (!IS_ENABLED(CONFIG_ASSERT) || hid_keymap_initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < ARRAY_SIZE(hid_keymap); i++) {
|
||||
if (i > 0U) {
|
||||
__ASSERT(hid_keymap[i - 1].key_id < hid_keymap[i].key_id,
|
||||
"hid_keymap must be sorted by key_id");
|
||||
}
|
||||
|
||||
__ASSERT((hid_keymap[i].report_id == REPORT_ID_KEYBOARD) ||
|
||||
(hid_keymap[i].report_id == REPORT_ID_CONSUMER),
|
||||
"hid_keymap uses unsupported report_id");
|
||||
}
|
||||
|
||||
hid_keymap_initialized = true;
|
||||
}
|
||||
|
||||
/* 查询指定 key_id 的 HID 映射,查不到返回 NULL。 */
|
||||
static const struct hid_keymap *hid_keymap_get_local(uint16_t key_id)
|
||||
{
|
||||
if (ARRAY_SIZE(hid_keymap) == 0U) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct hid_keymap key = {
|
||||
.key_id = key_id,
|
||||
.usage_id = 0U,
|
||||
.report_id = 0U,
|
||||
};
|
||||
|
||||
return bsearch(&key,
|
||||
hid_keymap,
|
||||
ARRAY_SIZE(hid_keymap),
|
||||
sizeof(hid_keymap[0]),
|
||||
hid_keymap_compare);
|
||||
}
|
||||
|
||||
/* Report 协议键盘 payload: modifier(1) + usage bitset(0..0xE7 => 29B)。 */
|
||||
#define KEYBOARD_USAGE_MAX 0x00E7
|
||||
#define KEYBOARD_BITMAP_SIZE DIV_ROUND_UP(KEYBOARD_USAGE_MAX + 1, 8)
|
||||
#define KEYBOARD_REPORT_PAYLOAD (1 + KEYBOARD_BITMAP_SIZE)
|
||||
|
||||
/* Boot 协议键盘 payload: modifier(1) + reserved(1) + 6 keys。 */
|
||||
#define BOOT_KEYBOARD_PAYLOAD 8
|
||||
|
||||
/* Consumer payload 固定 16-bit usage。 */
|
||||
#define CONSUMER_PAYLOAD 2
|
||||
|
||||
struct keyboard_state {
|
||||
uint8_t modifier_bm;
|
||||
uint8_t usage_bm[KEYBOARD_BITMAP_SIZE];
|
||||
enum hid_protocol_type ble_protocol;
|
||||
enum hid_protocol_type usb_protocol;
|
||||
mode_type_t current_mode;
|
||||
uint16_t consumer_usage;
|
||||
};
|
||||
|
||||
static struct keyboard_state ks = {
|
||||
.ble_protocol = HID_PROTO_REPORT,
|
||||
.usb_protocol = HID_PROTO_REPORT,
|
||||
.current_mode = MODE_TYPE_COUNT,
|
||||
.consumer_usage = 0,
|
||||
};
|
||||
|
||||
/* 依据当前 mode 选择生效的 HID 协议来源(BLE 或 USB)。 */
|
||||
static enum hid_protocol_type active_protocol_get(void)
|
||||
{
|
||||
return (ks.current_mode == MODE_TYPE_USB) ? ks.usb_protocol : ks.ble_protocol;
|
||||
}
|
||||
|
||||
/* 查询某 usage 位在当前键盘位图里是否处于按下状态。 */
|
||||
static bool usage_pressed(uint16_t usage)
|
||||
{
|
||||
if (usage > KEYBOARD_USAGE_MAX) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (ks.usage_bm[usage / 8] & BIT(usage % 8)) != 0U;
|
||||
}
|
||||
|
||||
/*
|
||||
* 更新键盘 usage 位图与 modifier 状态。
|
||||
* 返回 true 表示状态有变化,需要向传输层同步新报告。
|
||||
*/
|
||||
static bool keyboard_usage_update(uint16_t usage_id, bool pressed)
|
||||
{
|
||||
if (usage_id > KEYBOARD_USAGE_MAX) {
|
||||
LOG_WRN("Unsupported usage_id=0x%04x", usage_id);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t idx = usage_id / 8;
|
||||
uint8_t mask = BIT(usage_id % 8);
|
||||
bool changed = false;
|
||||
|
||||
if (pressed) {
|
||||
if ((ks.usage_bm[idx] & mask) == 0U) {
|
||||
ks.usage_bm[idx] |= mask;
|
||||
changed = true;
|
||||
}
|
||||
} else {
|
||||
if ((ks.usage_bm[idx] & mask) != 0U) {
|
||||
ks.usage_bm[idx] &= (uint8_t)~mask;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* modifier(E0~E7) 额外维护一份 bitmask,便于 Boot/Report 复用。 */
|
||||
if ((usage_id >= 0x00E0) && (usage_id <= 0x00E7)) {
|
||||
uint8_t mod_mask = BIT(usage_id - 0x00E0);
|
||||
if (pressed) {
|
||||
ks.modifier_bm |= mod_mask;
|
||||
} else {
|
||||
ks.modifier_bm &= (uint8_t)~mod_mask;
|
||||
}
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
/*
|
||||
* 提交 HID 报告事件:
|
||||
* - Report 协议编码为 [report_id | payload]
|
||||
* - Boot 协议编码为 [payload](不含 report_id)
|
||||
*/
|
||||
static void submit_hid_report(enum hid_protocol_type protocol,
|
||||
uint8_t report_id,
|
||||
const uint8_t *payload,
|
||||
size_t payload_len)
|
||||
{
|
||||
size_t report_len = (protocol == HID_PROTO_REPORT) ? (payload_len + 1U) : payload_len;
|
||||
struct hid_report_event *event = new_hid_report_event(report_len);
|
||||
|
||||
event->protocol = protocol;
|
||||
if (protocol == HID_PROTO_REPORT) {
|
||||
event->dyndata.data[0] = report_id;
|
||||
memcpy(&event->dyndata.data[1], payload, payload_len);
|
||||
} else {
|
||||
memcpy(event->dyndata.data, payload, payload_len);
|
||||
}
|
||||
|
||||
APP_EVENT_SUBMIT(event);
|
||||
}
|
||||
|
||||
/*
|
||||
* 组包并提交键盘报告:
|
||||
* - Report 协议发送 NKRO payload;
|
||||
* - Boot 协议降级为 6KRO 固定 8 字节格式。
|
||||
*/
|
||||
static void submit_keyboard_report_payload(enum hid_protocol_type protocol)
|
||||
{
|
||||
if (protocol == HID_PROTO_REPORT) {
|
||||
uint8_t payload[KEYBOARD_REPORT_PAYLOAD];
|
||||
|
||||
payload[0] = ks.modifier_bm;
|
||||
memcpy(&payload[1], ks.usage_bm, sizeof(ks.usage_bm));
|
||||
submit_hid_report(HID_PROTO_REPORT, REPORT_ID_KEYBOARD,
|
||||
payload, sizeof(payload));
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Boot 协议只支持 6KRO。
|
||||
* 从 usage 位图中按升序提取最多 6 个普通键,modifier 走独立字节。
|
||||
*/
|
||||
uint8_t payload[BOOT_KEYBOARD_PAYLOAD] = { 0 };
|
||||
size_t key_pos = 2;
|
||||
|
||||
payload[0] = ks.modifier_bm;
|
||||
|
||||
for (uint16_t usage = 0x04; usage <= 0x65; usage++) {
|
||||
if (!usage_pressed(usage)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
payload[key_pos++] = (uint8_t)usage;
|
||||
if (key_pos >= ARRAY_SIZE(payload)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
submit_hid_report(HID_PROTO_BOOT, REPORT_ID_KEYBOARD, payload, sizeof(payload));
|
||||
}
|
||||
|
||||
/* 组包并提交 consumer 报告(16-bit usage)。 */
|
||||
static void submit_consumer_report_payload(void)
|
||||
{
|
||||
uint8_t payload[CONSUMER_PAYLOAD];
|
||||
|
||||
payload[0] = ks.consumer_usage & 0xFF;
|
||||
payload[1] = (ks.consumer_usage >> 8) & 0xFF;
|
||||
submit_hid_report(HID_PROTO_REPORT, REPORT_ID_CONSUMER, payload, sizeof(payload));
|
||||
}
|
||||
|
||||
/*
|
||||
* 处理键盘类 usage:
|
||||
* - 仅在按键状态实际变化时提交报告,避免无效重复上报。
|
||||
*/
|
||||
static bool handle_keyboard_usage_event(const struct hid_keymap *map, bool pressed)
|
||||
{
|
||||
if (!keyboard_usage_update(map->usage_id, pressed))
|
||||
return false;
|
||||
|
||||
submit_keyboard_report_payload(active_protocol_get());
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* 处理 consumer 类 usage:
|
||||
* - Boot 协议不发送 consumer 报告;
|
||||
* - 按下时上报 usage,抬起时上报 0 清状态。
|
||||
*/
|
||||
static bool handle_consumer_usage_event(const struct hid_keymap *map, bool pressed)
|
||||
{
|
||||
if (active_protocol_get() == HID_PROTO_BOOT)
|
||||
return false;
|
||||
|
||||
if (pressed) {
|
||||
if (ks.consumer_usage == map->usage_id)
|
||||
return false;
|
||||
|
||||
ks.consumer_usage = map->usage_id;
|
||||
submit_consumer_report_payload();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ks.consumer_usage != map->usage_id)
|
||||
return false;
|
||||
|
||||
ks.consumer_usage = 0U;
|
||||
submit_consumer_report_payload();
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* 处理 button_event:
|
||||
* - 先查 key_id 映射;
|
||||
* - 再按 report_id 分派到键盘/consumer 分支。
|
||||
*/
|
||||
static bool handle_button_event(const struct button_event *event)
|
||||
{
|
||||
const struct hid_keymap *map = hid_keymap_get_local(event->key_id);
|
||||
if (!map) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (map->report_id == REPORT_ID_KEYBOARD)
|
||||
return handle_keyboard_usage_event(map, event->pressed);
|
||||
|
||||
if (map->report_id == REPORT_ID_CONSUMER)
|
||||
return handle_consumer_usage_event(map, event->pressed);
|
||||
|
||||
LOG_WRN("Unsupported report_id=%u key_id=0x%04x", map->report_id, event->key_id);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* 同步 BLE/USB 传输层上报的当前协议。 */
|
||||
static bool handle_hid_protocol_event(const struct hid_protocol_event *event)
|
||||
{
|
||||
if (event->transport == HID_TRANSPORT_BLE) {
|
||||
ks.ble_protocol = event->protocol;
|
||||
} else if (event->transport == HID_TRANSPORT_USB) {
|
||||
ks.usb_protocol = event->protocol;
|
||||
} else {
|
||||
__ASSERT_NO_MSG(false);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* 更新当前激活模式,决定协议来源取 BLE 还是 USB。 */
|
||||
static bool handle_mode_event(const struct mode_event *event)
|
||||
{
|
||||
ks.current_mode = event->mode_type;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* 模块总事件分发入口。 */
|
||||
static bool app_event_handler(const struct app_event_header *aeh)
|
||||
{
|
||||
if (is_button_event(aeh)) {
|
||||
return handle_button_event(cast_button_event(aeh));
|
||||
}
|
||||
|
||||
if (is_hid_protocol_event(aeh)) {
|
||||
return handle_hid_protocol_event(cast_hid_protocol_event(aeh));
|
||||
}
|
||||
|
||||
if (is_mode_event(aeh)) {
|
||||
return handle_mode_event(cast_mode_event(aeh));
|
||||
}
|
||||
|
||||
if (is_module_state_event(aeh)) {
|
||||
const struct module_state_event *event = cast_module_state_event(aeh);
|
||||
|
||||
if (check_state(event, MODULE_ID(main), MODULE_STATE_READY)) {
|
||||
/* 主模块 ready 后做一次 keymap 结构校验。 */
|
||||
hid_keymap_init_local();
|
||||
module_set_state(MODULE_STATE_READY);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
__ASSERT_NO_MSG(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
APP_EVENT_LISTENER(MODULE, app_event_handler);
|
||||
APP_EVENT_SUBSCRIBE(MODULE, button_event);
|
||||
APP_EVENT_SUBSCRIBE(MODULE, hid_protocol_event);
|
||||
APP_EVENT_SUBSCRIBE(MODULE, mode_event);
|
||||
APP_EVENT_SUBSCRIBE(MODULE, module_state_event);
|
||||
@@ -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