feat(hid): 添加Vendor报告类型支持键盘掩码功能
- 新增REPORT_ID_VENDOR报告ID和相关常量定义 - 在HID报告描述符中添加Vendor页面的输入输出集合定义 - 更新BLE HID服务配置以支持3个输入报告和2个输出报告 - 实现hid_vendor_mask_event事件用于处理Vendor掩码数据 - 修改keyboard模块以支持物理状态和掩码状态分离 - 添加vendor_output_report_handler处理来自主机的掩码更新 - 更新CMakeLists.txt包含新的事件源文件 - 修改hid_tx_manager以支持Vendor报告的发送管理
This commit is contained in:
@@ -24,6 +24,7 @@ target_sources(app PRIVATE
|
|||||||
src/events/hid_report_event.c
|
src/events/hid_report_event.c
|
||||||
src/events/hid_tx_done_event.c
|
src/events/hid_tx_done_event.c
|
||||||
src/events/hid_tx_event.c
|
src/events/hid_tx_event.c
|
||||||
|
src/events/hid_vendor_mask_event.c
|
||||||
src/events/keyboard_led_event.c
|
src/events/keyboard_led_event.c
|
||||||
src/events/mode_event.c
|
src/events/mode_event.c
|
||||||
src/events/qdec_step_event.c
|
src/events/qdec_step_event.c
|
||||||
|
|||||||
@@ -7,8 +7,20 @@
|
|||||||
enum {
|
enum {
|
||||||
REPORT_ID_KEYBOARD = 1,
|
REPORT_ID_KEYBOARD = 1,
|
||||||
REPORT_ID_CONSUMER = 3,
|
REPORT_ID_CONSUMER = 3,
|
||||||
|
REPORT_ID_VENDOR = 4,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define HID_KBD_USAGE_MAX 0x00E7U
|
||||||
|
#define HID_KBD_MOD_COUNT 8U
|
||||||
|
#define HID_KBD_BITMAP_BITS (HID_KBD_USAGE_MAX + 1U)
|
||||||
|
#define HID_KBD_BITMAP_SIZE ((HID_KBD_BITMAP_BITS + 7U) / 8U)
|
||||||
|
#define HID_KBD_PAYLOAD_SIZE (1U + HID_KBD_BITMAP_SIZE)
|
||||||
|
#define HID_BOOT_KBD_PAYLOAD_SIZE 8U
|
||||||
|
#define HID_CONSUMER_PAYLOAD_SIZE 2U
|
||||||
|
#define HID_VENDOR_PAYLOAD_SIZE HID_KBD_PAYLOAD_SIZE
|
||||||
|
#define HID_KBD_LED_PAYLOAD_SIZE 1U
|
||||||
|
#define HID_FULL_REPORT_SIZE(payload) (1U + (payload))
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* HID_USAGE_PAGE() 只支持 1 字节 Usage Page。
|
* HID_USAGE_PAGE() 只支持 1 字节 Usage Page。
|
||||||
* Vendor Defined Page(0xFF00) 需要 2 字节编码,因此在本地补一个 16 位版本,
|
* Vendor Defined Page(0xFF00) 需要 2 字节编码,因此在本地补一个 16 位版本,
|
||||||
@@ -18,9 +30,12 @@ enum {
|
|||||||
HID_ITEM(HID_ITEM_TAG_USAGE_PAGE, HID_ITEM_TYPE_GLOBAL, 2), page_lsb, page_msb
|
HID_ITEM(HID_ITEM_TAG_USAGE_PAGE, HID_ITEM_TYPE_GLOBAL, 2), page_lsb, page_msb
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 键盘(NKRO) + Consumer 的复合 Report 描述符:
|
* 键盘(NKRO) + Consumer + Vendor 的复合 Report 描述符:
|
||||||
* - USB Report 接口和 BLE HIDS Report Map 统一使用这份定义,
|
* - USB Report 接口和 BLE HIDS Report Map 统一使用这份定义,
|
||||||
* 避免两边手写常量后长期演进出现不一致。
|
* 避免两边手写常量后长期演进出现不一致。
|
||||||
|
* - Vendor Report 复用与 NKRO 键盘状态相同的 payload 结构:
|
||||||
|
* [modifier(1B) | usage_bitmap(29B)]。
|
||||||
|
* 这样主机可以下发“屏蔽遮罩”,设备也可以上报“真实键盘状态”。
|
||||||
*/
|
*/
|
||||||
#define HID_DESC_KEYBOARD_NKRO_CONSUMER() \
|
#define HID_DESC_KEYBOARD_NKRO_CONSUMER() \
|
||||||
{ \
|
{ \
|
||||||
@@ -44,7 +59,7 @@ enum {
|
|||||||
HID_LOGICAL_MIN8(0), \
|
HID_LOGICAL_MIN8(0), \
|
||||||
HID_LOGICAL_MAX8(1), \
|
HID_LOGICAL_MAX8(1), \
|
||||||
HID_REPORT_SIZE(1), \
|
HID_REPORT_SIZE(1), \
|
||||||
HID_REPORT_COUNT(0xE7 + 1), \
|
HID_REPORT_COUNT(HID_KBD_BITMAP_BITS), \
|
||||||
HID_INPUT(0x02), \
|
HID_INPUT(0x02), \
|
||||||
\
|
\
|
||||||
/* Report 协议下键盘 LED 输出(NumLock/CapsLock/ScrollLock/Compose/Kana)。 */ \
|
/* Report 协议下键盘 LED 输出(NumLock/CapsLock/ScrollLock/Compose/Kana)。 */ \
|
||||||
@@ -74,6 +89,22 @@ enum {
|
|||||||
HID_REPORT_SIZE(16), \
|
HID_REPORT_SIZE(16), \
|
||||||
HID_REPORT_COUNT(1), \
|
HID_REPORT_COUNT(1), \
|
||||||
HID_INPUT(0x00), \
|
HID_INPUT(0x00), \
|
||||||
|
HID_END_COLLECTION, \
|
||||||
|
\
|
||||||
|
/* Vendor 页:双向传输完整键盘状态/屏蔽遮罩,payload 结构与 NKRO 一致。 */ \
|
||||||
|
HID_USAGE_PAGE16(0x00, 0xFF), \
|
||||||
|
HID_USAGE(0x02U), \
|
||||||
|
HID_COLLECTION(HID_COLLECTION_APPLICATION), \
|
||||||
|
HID_REPORT_ID(REPORT_ID_VENDOR), \
|
||||||
|
HID_LOGICAL_MIN8(0), \
|
||||||
|
HID_LOGICAL_MAX16(0xFF, 0x00), \
|
||||||
|
HID_REPORT_SIZE(8), \
|
||||||
|
HID_REPORT_COUNT(HID_VENDOR_PAYLOAD_SIZE), \
|
||||||
|
HID_USAGE(0x02U), \
|
||||||
|
HID_INPUT(0x02), \
|
||||||
|
HID_REPORT_COUNT(HID_VENDOR_PAYLOAD_SIZE), \
|
||||||
|
HID_USAGE(0x02U), \
|
||||||
|
HID_OUTPUT(0x02), \
|
||||||
HID_END_COLLECTION, \
|
HID_END_COLLECTION, \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
6
prj.conf
6
prj.conf
@@ -45,9 +45,9 @@ CONFIG_BT_CONN_CTX=y
|
|||||||
CONFIG_BT_GATT_POOL=y
|
CONFIG_BT_GATT_POOL=y
|
||||||
CONFIG_BT_GATT_CHRC_POOL_SIZE=16
|
CONFIG_BT_GATT_CHRC_POOL_SIZE=16
|
||||||
CONFIG_BT_GATT_UUID16_POOL_SIZE=24
|
CONFIG_BT_GATT_UUID16_POOL_SIZE=24
|
||||||
CONFIG_BT_HIDS_ATTR_MAX=32
|
CONFIG_BT_HIDS_ATTR_MAX=40
|
||||||
CONFIG_BT_HIDS_INPUT_REP_MAX=2
|
CONFIG_BT_HIDS_INPUT_REP_MAX=3
|
||||||
CONFIG_BT_HIDS_OUTPUT_REP_MAX=1
|
CONFIG_BT_HIDS_OUTPUT_REP_MAX=2
|
||||||
CONFIG_BT_HIDS_FEATURE_REP_MAX=0
|
CONFIG_BT_HIDS_FEATURE_REP_MAX=0
|
||||||
|
|
||||||
CONFIG_USB_DEVICE_STACK_NEXT=y
|
CONFIG_USB_DEVICE_STACK_NEXT=y
|
||||||
|
|||||||
26
src/events/hid_vendor_mask_event.c
Normal file
26
src/events/hid_vendor_mask_event.c
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
#include "hid_vendor_mask_event.h"
|
||||||
|
|
||||||
|
static void log_hid_vendor_mask_event(const struct app_event_header *aeh)
|
||||||
|
{
|
||||||
|
const struct hid_vendor_mask_event *event = cast_hid_vendor_mask_event(aeh);
|
||||||
|
|
||||||
|
APP_EVENT_MANAGER_LOG(aeh, "len=%u", event->dyndata.size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void profile_hid_vendor_mask_event(struct log_event_buf *buf,
|
||||||
|
const struct app_event_header *aeh)
|
||||||
|
{
|
||||||
|
const struct hid_vendor_mask_event *event = cast_hid_vendor_mask_event(aeh);
|
||||||
|
|
||||||
|
nrf_profiler_log_encode_uint16(buf, event->dyndata.size);
|
||||||
|
}
|
||||||
|
|
||||||
|
APP_EVENT_INFO_DEFINE(hid_vendor_mask_event,
|
||||||
|
ENCODE(NRF_PROFILER_ARG_U16),
|
||||||
|
ENCODE("len"),
|
||||||
|
profile_hid_vendor_mask_event);
|
||||||
|
|
||||||
|
APP_EVENT_TYPE_DEFINE(hid_vendor_mask_event,
|
||||||
|
log_hid_vendor_mask_event,
|
||||||
|
&hid_vendor_mask_event_info,
|
||||||
|
APP_EVENT_FLAGS_CREATE(APP_EVENT_TYPE_FLAGS_INIT_LOG_ENABLE));
|
||||||
39
src/events/hid_vendor_mask_event.h
Normal file
39
src/events/hid_vendor_mask_event.h
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
#ifndef HID_VENDOR_MASK_EVENT_H__
|
||||||
|
#define HID_VENDOR_MASK_EVENT_H__
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <app_event_manager.h>
|
||||||
|
#include <app_event_manager_profiler_tracer.h>
|
||||||
|
|
||||||
|
struct hid_vendor_mask_event {
|
||||||
|
struct app_event_header header;
|
||||||
|
struct event_dyndata dyndata;
|
||||||
|
};
|
||||||
|
|
||||||
|
APP_EVENT_TYPE_DYNDATA_DECLARE(hid_vendor_mask_event);
|
||||||
|
|
||||||
|
static inline void hid_vendor_mask_event_submit(const uint8_t *data, size_t size)
|
||||||
|
{
|
||||||
|
struct hid_vendor_mask_event *event = new_hid_vendor_mask_event(size);
|
||||||
|
|
||||||
|
if ((size > 0U) && (data != NULL)) {
|
||||||
|
memcpy(event->dyndata.data, data, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
APP_EVENT_SUBMIT(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline const uint8_t *hid_vendor_mask_event_get_data(const struct hid_vendor_mask_event *event)
|
||||||
|
{
|
||||||
|
return event->dyndata.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline size_t hid_vendor_mask_event_get_size(const struct hid_vendor_mask_event *event)
|
||||||
|
{
|
||||||
|
return event->dyndata.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* HID_VENDOR_MASK_EVENT_H__ */
|
||||||
@@ -11,18 +11,15 @@
|
|||||||
#include "hid_report_descriptor.h"
|
#include "hid_report_descriptor.h"
|
||||||
#include "hid_tx_done_event.h"
|
#include "hid_tx_done_event.h"
|
||||||
#include "hid_tx_event.h"
|
#include "hid_tx_event.h"
|
||||||
|
#include "hid_vendor_mask_event.h"
|
||||||
#include "keyboard_led_event.h"
|
#include "keyboard_led_event.h"
|
||||||
#include "mode_event.h"
|
#include "mode_event.h"
|
||||||
|
|
||||||
#include <zephyr/logging/log.h>
|
#include <zephyr/logging/log.h>
|
||||||
LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF);
|
LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF);
|
||||||
|
|
||||||
#define INPUT_REPORT_COUNT 2
|
#define INPUT_REPORT_COUNT 3
|
||||||
#define OUTPUT_REPORT_COUNT 1
|
#define OUTPUT_REPORT_COUNT 2
|
||||||
#define KEYBOARD_REPORT_LEN 30
|
|
||||||
#define CONSUMER_REPORT_LEN 2
|
|
||||||
#define KEYBOARD_LED_REPORT_LEN 1
|
|
||||||
#define BOOT_KEYBOARD_REPORT_LEN 8
|
|
||||||
|
|
||||||
BT_HIDS_DEF(hids_obj, INPUT_REPORT_COUNT, OUTPUT_REPORT_COUNT, 0);
|
BT_HIDS_DEF(hids_obj, INPUT_REPORT_COUNT, OUTPUT_REPORT_COUNT, 0);
|
||||||
|
|
||||||
@@ -142,7 +139,7 @@ static void keyboard_output_report_handler(struct bt_hids_rep *rep,
|
|||||||
{
|
{
|
||||||
ARG_UNUSED(conn);
|
ARG_UNUSED(conn);
|
||||||
|
|
||||||
if (!write || !rep || !rep->data || (rep->size < KEYBOARD_LED_REPORT_LEN)) {
|
if (!write || !rep || !rep->data || (rep->size < HID_KBD_LED_PAYLOAD_SIZE)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,6 +147,20 @@ static void keyboard_output_report_handler(struct bt_hids_rep *rep,
|
|||||||
LOG_DBG("Report KB out report 0x%02x", rep->data[0]);
|
LOG_DBG("Report KB out report 0x%02x", rep->data[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void vendor_output_report_handler(struct bt_hids_rep *rep,
|
||||||
|
struct bt_conn *conn,
|
||||||
|
bool write)
|
||||||
|
{
|
||||||
|
ARG_UNUSED(conn);
|
||||||
|
|
||||||
|
if (!write || !rep || !rep->data || (rep->size != HID_VENDOR_PAYLOAD_SIZE)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
hid_vendor_mask_event_submit(rep->data, rep->size);
|
||||||
|
LOG_INF("Vendor mask updated over BLE len=%u", rep->size);
|
||||||
|
}
|
||||||
|
|
||||||
static int hids_service_init(void)
|
static int hids_service_init(void)
|
||||||
{
|
{
|
||||||
static const uint8_t report_map[] = HID_DESC_KEYBOARD_NKRO_CONSUMER();
|
static const uint8_t report_map[] = HID_DESC_KEYBOARD_NKRO_CONSUMER();
|
||||||
@@ -165,17 +176,25 @@ static int hids_service_init(void)
|
|||||||
init_param.rep_map.size = sizeof(report_map);
|
init_param.rep_map.size = sizeof(report_map);
|
||||||
|
|
||||||
input_report[0].id = REPORT_ID_KEYBOARD;
|
input_report[0].id = REPORT_ID_KEYBOARD;
|
||||||
input_report[0].size = KEYBOARD_REPORT_LEN;
|
input_report[0].size = HID_KBD_PAYLOAD_SIZE;
|
||||||
input_report[0].handler = report_notify_handler;
|
input_report[0].handler = report_notify_handler;
|
||||||
|
|
||||||
input_report[1].id = REPORT_ID_CONSUMER;
|
input_report[1].id = REPORT_ID_CONSUMER;
|
||||||
input_report[1].size = CONSUMER_REPORT_LEN;
|
input_report[1].size = HID_CONSUMER_PAYLOAD_SIZE;
|
||||||
input_report[1].handler = report_notify_handler;
|
input_report[1].handler = report_notify_handler;
|
||||||
|
|
||||||
|
input_report[2].id = REPORT_ID_VENDOR;
|
||||||
|
input_report[2].size = HID_VENDOR_PAYLOAD_SIZE;
|
||||||
|
input_report[2].handler = report_notify_handler;
|
||||||
|
|
||||||
output_report[0].id = REPORT_ID_KEYBOARD;
|
output_report[0].id = REPORT_ID_KEYBOARD;
|
||||||
output_report[0].size = KEYBOARD_LED_REPORT_LEN;
|
output_report[0].size = HID_KBD_LED_PAYLOAD_SIZE;
|
||||||
output_report[0].handler = keyboard_output_report_handler;
|
output_report[0].handler = keyboard_output_report_handler;
|
||||||
|
|
||||||
|
output_report[1].id = REPORT_ID_VENDOR;
|
||||||
|
output_report[1].size = HID_VENDOR_PAYLOAD_SIZE;
|
||||||
|
output_report[1].handler = vendor_output_report_handler;
|
||||||
|
|
||||||
init_param.inp_rep_group_init.cnt = INPUT_REPORT_COUNT;
|
init_param.inp_rep_group_init.cnt = INPUT_REPORT_COUNT;
|
||||||
init_param.outp_rep_group_init.cnt = OUTPUT_REPORT_COUNT;
|
init_param.outp_rep_group_init.cnt = OUTPUT_REPORT_COUNT;
|
||||||
init_param.pm_evt_handler = pm_evt_handler;
|
init_param.pm_evt_handler = pm_evt_handler;
|
||||||
@@ -226,7 +245,7 @@ static bool handle_hid_tx_event(const struct hid_tx_event *event)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (payload_len != BOOT_KEYBOARD_REPORT_LEN) {
|
if (payload_len != HID_BOOT_KBD_PAYLOAD_SIZE) {
|
||||||
LOG_WRN("Invalid boot keyboard payload len=%u", payload_len);
|
LOG_WRN("Invalid boot keyboard payload len=%u", payload_len);
|
||||||
hid_tx_done_event_submit(HID_TX_KIND_BOOT, false);
|
hid_tx_done_event_submit(HID_TX_KIND_BOOT, false);
|
||||||
return false;
|
return false;
|
||||||
@@ -268,6 +287,8 @@ static bool handle_hid_tx_event(const struct hid_tx_event *event)
|
|||||||
rep_index = 0U;
|
rep_index = 0U;
|
||||||
} else if (report_id == REPORT_ID_CONSUMER) {
|
} else if (report_id == REPORT_ID_CONSUMER) {
|
||||||
rep_index = 1U;
|
rep_index = 1U;
|
||||||
|
} else if (report_id == REPORT_ID_VENDOR) {
|
||||||
|
rep_index = 2U;
|
||||||
} else {
|
} else {
|
||||||
hid_tx_done_event_submit(HID_TX_KIND_REPORT, false);
|
hid_tx_done_event_submit(HID_TX_KIND_REPORT, false);
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -28,6 +28,8 @@ enum hid_tx_flag {
|
|||||||
HID_TX_FLAG_BOOT_DIRTY,
|
HID_TX_FLAG_BOOT_DIRTY,
|
||||||
HID_TX_FLAG_NKRO_VALID,
|
HID_TX_FLAG_NKRO_VALID,
|
||||||
HID_TX_FLAG_NKRO_DIRTY,
|
HID_TX_FLAG_NKRO_DIRTY,
|
||||||
|
HID_TX_FLAG_VENDOR_VALID,
|
||||||
|
HID_TX_FLAG_VENDOR_DIRTY,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct hid_tx_item {
|
struct hid_tx_item {
|
||||||
@@ -40,6 +42,7 @@ struct hid_tx_ctx {
|
|||||||
atomic_t flags;
|
atomic_t flags;
|
||||||
struct hid_tx_item boot_state;
|
struct hid_tx_item boot_state;
|
||||||
struct hid_tx_item nkro_state;
|
struct hid_tx_item nkro_state;
|
||||||
|
struct hid_tx_item vendor_state;
|
||||||
struct hid_tx_item inflight_item;
|
struct hid_tx_item inflight_item;
|
||||||
mode_type_t active_mode;
|
mode_type_t active_mode;
|
||||||
};
|
};
|
||||||
@@ -122,6 +125,14 @@ static void dispatch_next_if_possible(void)
|
|||||||
|
|
||||||
if (!k_msgq_get(&hid_tx_queue_msgq, &item, K_NO_WAIT)) {
|
if (!k_msgq_get(&hid_tx_queue_msgq, &item, K_NO_WAIT)) {
|
||||||
(void)hid_tx_dispatch_item(&item);
|
(void)hid_tx_dispatch_item(&item);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((tx.active_mode == MODE_TYPE_BLE) &&
|
||||||
|
atomic_test_bit(&tx.flags, HID_TX_FLAG_VENDOR_DIRTY) &&
|
||||||
|
atomic_test_bit(&tx.flags, HID_TX_FLAG_VENDOR_VALID)) {
|
||||||
|
atomic_clear_bit(&tx.flags, HID_TX_FLAG_VENDOR_DIRTY);
|
||||||
|
(void)hid_tx_dispatch_item(&tx.vendor_state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,6 +178,10 @@ static bool handle_hid_report_request_event(const struct hid_report_event *event
|
|||||||
(void)hid_tx_item_store(&tx.nkro_state, HID_TX_KIND_REPORT, data, len);
|
(void)hid_tx_item_store(&tx.nkro_state, HID_TX_KIND_REPORT, data, len);
|
||||||
atomic_set_bit(&tx.flags, HID_TX_FLAG_NKRO_VALID);
|
atomic_set_bit(&tx.flags, HID_TX_FLAG_NKRO_VALID);
|
||||||
atomic_set_bit(&tx.flags, HID_TX_FLAG_NKRO_DIRTY);
|
atomic_set_bit(&tx.flags, HID_TX_FLAG_NKRO_DIRTY);
|
||||||
|
} else if ((len > 0U) && (data[0] == REPORT_ID_VENDOR)) {
|
||||||
|
(void)hid_tx_item_store(&tx.vendor_state, HID_TX_KIND_REPORT, data, len);
|
||||||
|
atomic_set_bit(&tx.flags, HID_TX_FLAG_VENDOR_VALID);
|
||||||
|
atomic_set_bit(&tx.flags, HID_TX_FLAG_VENDOR_DIRTY);
|
||||||
} else {
|
} else {
|
||||||
(void)hid_tx_queue_push(HID_TX_KIND_REPORT, data, len);
|
(void)hid_tx_queue_push(HID_TX_KIND_REPORT, data, len);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
#include "hid_boot_event.h"
|
#include "hid_boot_event.h"
|
||||||
#include "hid_protocol_event.h"
|
#include "hid_protocol_event.h"
|
||||||
#include "hid_report_event.h"
|
#include "hid_report_event.h"
|
||||||
|
#include "hid_vendor_mask_event.h"
|
||||||
#include "qdec_step_event.h"
|
#include "qdec_step_event.h"
|
||||||
|
|
||||||
#include <zephyr/sys/util.h>
|
#include <zephyr/sys/util.h>
|
||||||
@@ -91,19 +92,14 @@ static const struct hid_keymap *hid_keymap_get_local(uint16_t key_id)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Report 协议键盘 payload: modifier(1) + usage bitset(0..0xE7 => 29B)。 */
|
/* Report 协议键盘 payload: modifier(1) + usage bitset(0..0xE7 => 29B)。 */
|
||||||
#define KEYBOARD_USAGE_MAX 0x00E7
|
#define KEYBOARD_USAGE_MAX HID_KBD_USAGE_MAX
|
||||||
#define KEYBOARD_BITMAP_SIZE DIV_ROUND_UP(KEYBOARD_USAGE_MAX + 1, 8)
|
#define KEYBOARD_BITMAP_SIZE HID_KBD_BITMAP_SIZE
|
||||||
#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 {
|
struct keyboard_state {
|
||||||
uint8_t modifier_bm;
|
uint8_t physical_modifier_bm;
|
||||||
uint8_t usage_bm[KEYBOARD_BITMAP_SIZE];
|
uint8_t physical_usage_bm[KEYBOARD_BITMAP_SIZE];
|
||||||
|
uint8_t mask_modifier_bm;
|
||||||
|
uint8_t mask_bm[KEYBOARD_BITMAP_SIZE];
|
||||||
enum hid_protocol_type current_protocol;
|
enum hid_protocol_type current_protocol;
|
||||||
uint16_t consumer_usage;
|
uint16_t consumer_usage;
|
||||||
};
|
};
|
||||||
@@ -119,6 +115,17 @@ static enum hid_protocol_type active_protocol_get(void)
|
|||||||
return ks.current_protocol;
|
return ks.current_protocol;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void submit_hid_report(enum hid_protocol_type protocol,
|
||||||
|
uint8_t report_id,
|
||||||
|
const uint8_t *payload,
|
||||||
|
size_t payload_len);
|
||||||
|
|
||||||
|
static void keyboard_mask_init(void)
|
||||||
|
{
|
||||||
|
ks.mask_modifier_bm = 0xFF;
|
||||||
|
memset(ks.mask_bm, 0xFF, sizeof(ks.mask_bm));
|
||||||
|
}
|
||||||
|
|
||||||
/* 查询某 usage 位在当前键盘位图里是否处于按下状态。 */
|
/* 查询某 usage 位在当前键盘位图里是否处于按下状态。 */
|
||||||
static bool usage_pressed(uint16_t usage)
|
static bool usage_pressed(uint16_t usage)
|
||||||
{
|
{
|
||||||
@@ -126,7 +133,7 @@ static bool usage_pressed(uint16_t usage)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (ks.usage_bm[usage / 8] & BIT(usage % 8)) != 0U;
|
return (ks.physical_usage_bm[usage / 8] & BIT(usage % 8)) != 0U;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -145,13 +152,13 @@ static bool keyboard_usage_update(uint16_t usage_id, bool pressed)
|
|||||||
bool changed = false;
|
bool changed = false;
|
||||||
|
|
||||||
if (pressed) {
|
if (pressed) {
|
||||||
if ((ks.usage_bm[idx] & mask) == 0U) {
|
if ((ks.physical_usage_bm[idx] & mask) == 0U) {
|
||||||
ks.usage_bm[idx] |= mask;
|
ks.physical_usage_bm[idx] |= mask;
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if ((ks.usage_bm[idx] & mask) != 0U) {
|
if ((ks.physical_usage_bm[idx] & mask) != 0U) {
|
||||||
ks.usage_bm[idx] &= (uint8_t)~mask;
|
ks.physical_usage_bm[idx] &= (uint8_t)~mask;
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -160,15 +167,42 @@ static bool keyboard_usage_update(uint16_t usage_id, bool pressed)
|
|||||||
if ((usage_id >= 0x00E0) && (usage_id <= 0x00E7)) {
|
if ((usage_id >= 0x00E0) && (usage_id <= 0x00E7)) {
|
||||||
uint8_t mod_mask = BIT(usage_id - 0x00E0);
|
uint8_t mod_mask = BIT(usage_id - 0x00E0);
|
||||||
if (pressed) {
|
if (pressed) {
|
||||||
ks.modifier_bm |= mod_mask;
|
ks.physical_modifier_bm |= mod_mask;
|
||||||
} else {
|
} else {
|
||||||
ks.modifier_bm &= (uint8_t)~mod_mask;
|
ks.physical_modifier_bm &= (uint8_t)~mod_mask;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return changed;
|
return changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static uint8_t masked_modifier_get(void)
|
||||||
|
{
|
||||||
|
return ks.physical_modifier_bm & ks.mask_modifier_bm;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void build_masked_keyboard_payload(uint8_t payload[HID_KBD_PAYLOAD_SIZE])
|
||||||
|
{
|
||||||
|
payload[0] = masked_modifier_get();
|
||||||
|
|
||||||
|
for (size_t i = 0; i < KEYBOARD_BITMAP_SIZE; i++) {
|
||||||
|
payload[1U + i] = ks.physical_usage_bm[i] & ks.mask_bm[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void submit_vendor_report_payload(void)
|
||||||
|
{
|
||||||
|
uint8_t payload[HID_VENDOR_PAYLOAD_SIZE];
|
||||||
|
|
||||||
|
if (active_protocol_get() != HID_PROTO_REPORT) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
payload[0] = ks.physical_modifier_bm;
|
||||||
|
memcpy(&payload[1], ks.physical_usage_bm, sizeof(ks.physical_usage_bm));
|
||||||
|
submit_hid_report(HID_PROTO_REPORT, REPORT_ID_VENDOR, payload, sizeof(payload));
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 提交 HID 报告事件:
|
* 提交 HID 报告事件:
|
||||||
* - Report 协议编码为 [report_id | payload]
|
* - Report 协议编码为 [report_id | payload]
|
||||||
@@ -179,7 +213,7 @@ static void submit_hid_report(enum hid_protocol_type protocol,
|
|||||||
const uint8_t *payload,
|
const uint8_t *payload,
|
||||||
size_t payload_len)
|
size_t payload_len)
|
||||||
{
|
{
|
||||||
uint8_t report_buf[KEYBOARD_REPORT_PAYLOAD + 1U];
|
uint8_t report_buf[HID_FULL_REPORT_SIZE(HID_KBD_PAYLOAD_SIZE)];
|
||||||
|
|
||||||
if (protocol == HID_PROTO_REPORT) {
|
if (protocol == HID_PROTO_REPORT) {
|
||||||
size_t report_len = payload_len + 1U;
|
size_t report_len = payload_len + 1U;
|
||||||
@@ -200,10 +234,9 @@ static void submit_hid_report(enum hid_protocol_type protocol,
|
|||||||
static void submit_keyboard_report_payload(enum hid_protocol_type protocol)
|
static void submit_keyboard_report_payload(enum hid_protocol_type protocol)
|
||||||
{
|
{
|
||||||
if (protocol == HID_PROTO_REPORT) {
|
if (protocol == HID_PROTO_REPORT) {
|
||||||
uint8_t payload[KEYBOARD_REPORT_PAYLOAD];
|
uint8_t payload[HID_KBD_PAYLOAD_SIZE];
|
||||||
|
|
||||||
payload[0] = ks.modifier_bm;
|
build_masked_keyboard_payload(payload);
|
||||||
memcpy(&payload[1], ks.usage_bm, sizeof(ks.usage_bm));
|
|
||||||
submit_hid_report(HID_PROTO_REPORT, REPORT_ID_KEYBOARD,
|
submit_hid_report(HID_PROTO_REPORT, REPORT_ID_KEYBOARD,
|
||||||
payload, sizeof(payload));
|
payload, sizeof(payload));
|
||||||
return;
|
return;
|
||||||
@@ -213,10 +246,10 @@ static void submit_keyboard_report_payload(enum hid_protocol_type protocol)
|
|||||||
* Boot 协议只支持 6KRO。
|
* Boot 协议只支持 6KRO。
|
||||||
* 从 usage 位图中按升序提取最多 6 个普通键,modifier 走独立字节。
|
* 从 usage 位图中按升序提取最多 6 个普通键,modifier 走独立字节。
|
||||||
*/
|
*/
|
||||||
uint8_t payload[BOOT_KEYBOARD_PAYLOAD] = { 0 };
|
uint8_t payload[HID_BOOT_KBD_PAYLOAD_SIZE] = { 0 };
|
||||||
size_t key_pos = 2;
|
size_t key_pos = 2;
|
||||||
|
|
||||||
payload[0] = ks.modifier_bm;
|
payload[0] = masked_modifier_get();
|
||||||
|
|
||||||
for (uint16_t usage = 0x04; usage <= 0x65; usage++) {
|
for (uint16_t usage = 0x04; usage <= 0x65; usage++) {
|
||||||
if (!usage_pressed(usage)) {
|
if (!usage_pressed(usage)) {
|
||||||
@@ -235,7 +268,7 @@ static void submit_keyboard_report_payload(enum hid_protocol_type protocol)
|
|||||||
/* 组包并提交 consumer 报告(16-bit usage)。 */
|
/* 组包并提交 consumer 报告(16-bit usage)。 */
|
||||||
static void submit_consumer_report_payload(void)
|
static void submit_consumer_report_payload(void)
|
||||||
{
|
{
|
||||||
uint8_t payload[CONSUMER_PAYLOAD];
|
uint8_t payload[HID_CONSUMER_PAYLOAD_SIZE];
|
||||||
|
|
||||||
payload[0] = ks.consumer_usage & 0xFF;
|
payload[0] = ks.consumer_usage & 0xFF;
|
||||||
payload[1] = (ks.consumer_usage >> 8) & 0xFF;
|
payload[1] = (ks.consumer_usage >> 8) & 0xFF;
|
||||||
@@ -244,7 +277,7 @@ static void submit_consumer_report_payload(void)
|
|||||||
|
|
||||||
static void submit_consumer_click_usage(uint16_t usage_id)
|
static void submit_consumer_click_usage(uint16_t usage_id)
|
||||||
{
|
{
|
||||||
uint8_t payload[CONSUMER_PAYLOAD];
|
uint8_t payload[HID_CONSUMER_PAYLOAD_SIZE];
|
||||||
|
|
||||||
if (active_protocol_get() == HID_PROTO_BOOT) {
|
if (active_protocol_get() == HID_PROTO_BOOT) {
|
||||||
return;
|
return;
|
||||||
@@ -269,6 +302,7 @@ static bool handle_keyboard_usage_event(const struct hid_keymap *map, bool press
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
submit_keyboard_report_payload(active_protocol_get());
|
submit_keyboard_report_payload(active_protocol_get());
|
||||||
|
submit_vendor_report_payload();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -329,6 +363,24 @@ static bool handle_hid_protocol_event(const struct hid_protocol_event *event)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool handle_hid_vendor_mask_event(const struct hid_vendor_mask_event *event)
|
||||||
|
{
|
||||||
|
const uint8_t *data = hid_vendor_mask_event_get_data(event);
|
||||||
|
size_t size = hid_vendor_mask_event_get_size(event);
|
||||||
|
|
||||||
|
if (size != HID_VENDOR_PAYLOAD_SIZE) {
|
||||||
|
LOG_WRN("Ignore vendor mask len=%u expect=%u",
|
||||||
|
size, HID_VENDOR_PAYLOAD_SIZE);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ks.mask_modifier_bm = data[0];
|
||||||
|
memcpy(ks.mask_bm, &data[1], sizeof(ks.mask_bm));
|
||||||
|
submit_keyboard_report_payload(active_protocol_get());
|
||||||
|
submit_vendor_report_payload();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
static bool handle_qdec_step_event(const struct qdec_step_event *event)
|
static bool handle_qdec_step_event(const struct qdec_step_event *event)
|
||||||
{
|
{
|
||||||
int8_t step = qdec_step_event_get_step(event);
|
int8_t step = qdec_step_event_get_step(event);
|
||||||
@@ -359,12 +411,17 @@ static bool app_event_handler(const struct app_event_header *aeh)
|
|||||||
return handle_qdec_step_event(cast_qdec_step_event(aeh));
|
return handle_qdec_step_event(cast_qdec_step_event(aeh));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (is_hid_vendor_mask_event(aeh)) {
|
||||||
|
return handle_hid_vendor_mask_event(cast_hid_vendor_mask_event(aeh));
|
||||||
|
}
|
||||||
|
|
||||||
if (is_module_state_event(aeh)) {
|
if (is_module_state_event(aeh)) {
|
||||||
const struct module_state_event *event = cast_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)) {
|
if (check_state(event, MODULE_ID(main), MODULE_STATE_READY)) {
|
||||||
/* 主模块 ready 后做一次 keymap 结构校验。 */
|
/* 主模块 ready 后做一次 keymap 结构校验。 */
|
||||||
hid_keymap_init_local();
|
hid_keymap_init_local();
|
||||||
|
keyboard_mask_init();
|
||||||
module_set_state(MODULE_STATE_READY);
|
module_set_state(MODULE_STATE_READY);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -378,5 +435,6 @@ static bool app_event_handler(const struct app_event_header *aeh)
|
|||||||
APP_EVENT_LISTENER(MODULE, app_event_handler);
|
APP_EVENT_LISTENER(MODULE, app_event_handler);
|
||||||
APP_EVENT_SUBSCRIBE(MODULE, button_event);
|
APP_EVENT_SUBSCRIBE(MODULE, button_event);
|
||||||
APP_EVENT_SUBSCRIBE(MODULE, hid_protocol_event);
|
APP_EVENT_SUBSCRIBE(MODULE, hid_protocol_event);
|
||||||
|
APP_EVENT_SUBSCRIBE(MODULE, hid_vendor_mask_event);
|
||||||
APP_EVENT_SUBSCRIBE(MODULE, qdec_step_event);
|
APP_EVENT_SUBSCRIBE(MODULE, qdec_step_event);
|
||||||
APP_EVENT_SUBSCRIBE(MODULE, module_state_event);
|
APP_EVENT_SUBSCRIBE(MODULE, module_state_event);
|
||||||
|
|||||||
Reference in New Issue
Block a user