feat(keyboard): 添加蓝牙HID支持和电池状态事件

- 添加BLE相关模块:ble_adv_ctrl_module、ble_adv_uuid16、ble_bas_module、
  ble_hid_module
- 新增电池状态事件(bat_state_event)用于监控电池电量、充电状态
- 在多个事件中添加HID_TRANSPORT_BLE支持,包括hid_led_event、
  set_protocol_event等
- 更新配置文件prj.conf以启用蓝牙功能、HID服务和设置系统
- 修改电池模块以计算并报告电池SOC百分比
- 集成CAF设置加载器以管理蓝牙配对信息
This commit is contained in:
2026-04-10 19:28:20 +08:00
parent b9b7d342f5
commit 39d2962258
21 changed files with 1186 additions and 60 deletions

View File

@@ -13,10 +13,15 @@ add_subdirectory(drivers)
target_sources(app PRIVATE target_sources(app PRIVATE
src/main.c src/main.c
src/battery_module.c src/battery_module.c
src/ble_adv_ctrl_module.c
src/ble_adv_uuid16.c
src/ble_bas_module.c
src/ble_hid_module.c
src/encoder_module.c src/encoder_module.c
src/hid_flowctrl_module.c src/hid_flowctrl_module.c
src/keyboard_core_module.c src/keyboard_core_module.c
src/usb_hid_module.c src/usb_hid_module.c
src/events/bat_state_event.c
src/events/encoder_event.c src/events/encoder_event.c
src/events/hid_led_event.c src/events/hid_led_event.c
src/events/hid_report_sent_event.c src/events/hid_report_sent_event.c

View File

@@ -0,0 +1,27 @@
#ifndef BLINKY_BAT_STATE_EVENT_H_
#define BLINKY_BAT_STATE_EVENT_H_
#include <stdbool.h>
#include <stdint.h>
#include <app_event_manager.h>
#include <app_event_manager_profiler_tracer.h>
#ifdef __cplusplus
extern "C" {
#endif
struct bat_state_event {
struct app_event_header header;
uint8_t soc;
bool charging;
bool full;
};
APP_EVENT_TYPE_DECLARE(bat_state_event);
#ifdef __cplusplus
}
#endif
#endif /* BLINKY_BAT_STATE_EVENT_H_ */

View File

@@ -4,12 +4,15 @@
#include <app_event_manager.h> #include <app_event_manager.h>
#include <app_event_manager_profiler_tracer.h> #include <app_event_manager_profiler_tracer.h>
#include "keyboard_core.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
struct hid_led_event { struct hid_led_event {
struct app_event_header header; struct app_event_header header;
enum hid_transport transport;
uint8_t led_bm; uint8_t led_bm;
}; };

View File

@@ -12,6 +12,7 @@ extern "C" {
struct set_protocol_event { struct set_protocol_event {
struct app_event_header header; struct app_event_header header;
enum hid_transport transport;
enum keyboard_protocol_mode protocol_mode; enum keyboard_protocol_mode protocol_mode;
}; };

View File

@@ -30,6 +30,8 @@ enum hid_queue_policy {
enum hid_transport { enum hid_transport {
HID_TRANSPORT_USB, HID_TRANSPORT_USB,
HID_TRANSPORT_BLE,
HID_TRANSPORT_COUNT,
}; };
enum keyboard_consumer_control { enum keyboard_consumer_control {

15
inc/settings_loader_def.h Normal file
View File

@@ -0,0 +1,15 @@
/*
* Settings must be loaded after HIDS has registered its dynamic GATT
* service and after BLE state is initialized.
*/
const struct {} settings_loader_def_include_once;
#include <caf/events/module_state_event.h>
static inline void get_req_modules(struct module_flags *mf)
{
module_flags_set_bit(mf, MODULE_IDX(main));
module_flags_set_bit(mf, MODULE_IDX(ble_hid_module));
module_flags_set_bit(mf, MODULE_IDX(ble_state));
}

View File

@@ -10,7 +10,13 @@ CONFIG_PINCTRL_DYNAMIC=y
CONFIG_REBOOT=y CONFIG_REBOOT=y
CONFIG_SENSOR=y CONFIG_SENSOR=y
CONFIG_ADC=y CONFIG_ADC=y
CONFIG_HEAP_MEM_POOL_SIZE=2048 CONFIG_SETTINGS=y
CONFIG_SETTINGS_NVS=y
CONFIG_FLASH=y
CONFIG_FLASH_PAGE_LAYOUT=y
CONFIG_FLASH_MAP=y
CONFIG_NVS=y
CONFIG_HEAP_MEM_POOL_SIZE=4096
CONFIG_LOG=y CONFIG_LOG=y
CONFIG_ASSERT=y CONFIG_ASSERT=y
@@ -18,7 +24,62 @@ CONFIG_ASSERT=y
CONFIG_USB_DEVICE_STACK_NEXT=y CONFIG_USB_DEVICE_STACK_NEXT=y
CONFIG_USBD_HID_SUPPORT=y CONFIG_USBD_HID_SUPPORT=y
# BLE
CONFIG_BT=y
CONFIG_BT_PERIPHERAL=y
CONFIG_BT_SMP=y
CONFIG_BT_BONDABLE=y
CONFIG_BT_SETTINGS=y
CONFIG_BT_MAX_CONN=1
CONFIG_BT_MAX_PAIRED=1
CONFIG_BT_ATT_TX_COUNT=5
CONFIG_BT_CONN_CTX=y
CONFIG_BT_DEVICE_NAME="WH Mini Keyboard"
CONFIG_BT_DEVICE_APPEARANCE=961
CONFIG_BT_BAS=y
CONFIG_BT_HIDS=y
CONFIG_BT_HIDS_MAX_CLIENT_COUNT=1
CONFIG_BT_HIDS_DEFAULT_PERM_RW_ENCRYPT=y
CONFIG_BT_HIDS_INPUT_REP_MAX=2
CONFIG_BT_HIDS_OUTPUT_REP_MAX=1
CONFIG_BT_HIDS_FEATURE_REP_MAX=0
CONFIG_BT_GATT_UUID16_POOL_SIZE=40
CONFIG_BT_GATT_CHRC_POOL_SIZE=20
CONFIG_BT_DIS=y
CONFIG_BT_DIS_MANUF_NAME=y
CONFIG_BT_DIS_MANUF_NAME_STR="Atguigu"
CONFIG_BT_DIS_MODEL_NUMBER=y
CONFIG_BT_DIS_MODEL_NUMBER_STR="WH Mini Keyboard"
CONFIG_BT_DIS_PNP=y
CONFIG_BT_DIS_PNP_VID_SRC=2
CONFIG_BT_DIS_PNP_VID=0x1915
CONFIG_BT_DIS_PNP_PID=0x52F0
CONFIG_BT_DIS_PNP_VER=0x0100
# Power manager # Power manager
CONFIG_CAF_POWER_MANAGER=y CONFIG_CAF_POWER_MANAGER=y
CONFIG_CAF_POWER_MANAGER_TIMEOUT=120 CONFIG_CAF_POWER_MANAGER_TIMEOUT=120
# CONFIG_CAF_POWER_MANAGER_STAY_ON=y # CONFIG_CAF_POWER_MANAGER_STAY_ON=y
# CAF BLE
CONFIG_CAF_SETTINGS_LOADER=y
CONFIG_CAF_SETTINGS_LOADER_DEF_PATH="settings_loader_def.h"
CONFIG_CAF_SETTINGS_LOADER_USE_THREAD=y
CONFIG_CAF_SETTINGS_LOADER_THREAD_STACK_SIZE=1792
CONFIG_CAF_BLE_STATE=y
CONFIG_CAF_BLE_STATE_SECURITY_REQ=y
CONFIG_CAF_BLE_STATE_PM=y
CONFIG_CAF_BLE_STATE_MAX_LOCAL_ID_BONDS=1
CONFIG_CAF_BLE_ADV=y
CONFIG_CAF_BLE_ADV_SUSPEND_ON_READY=y
CONFIG_CAF_BLE_ADV_FAST_ADV=y
CONFIG_CAF_BLE_ADV_FILTER_ACCEPT_LIST=y
CONFIG_CAF_BLE_ADV_MODULE_SUSPEND_EVENTS=y
CONFIG_CAF_BLE_BOND=y
CONFIG_CAF_MODULE_SUSPEND_EVENTS=y
CONFIG_BT_ADV_PROV_FLAGS=y
CONFIG_BT_ADV_PROV_GAP_APPEARANCE=y
CONFIG_BT_ADV_PROV_DEVICE_NAME=y
CONFIG_BT_ADV_PROV_DEVICE_NAME_SD=y

View File

@@ -1,5 +1,6 @@
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <string.h>
#include <app_event_manager.h> #include <app_event_manager.h>
@@ -16,11 +17,15 @@
#include <zephyr/logging/log.h> #include <zephyr/logging/log.h>
#include <zephyr/pm/device.h> #include <zephyr/pm/device.h>
#include "bat_state_event.h"
LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF); LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF);
#define VBATT_NODE DT_PATH(vbatt) #define VBATT_NODE DT_PATH(vbatt)
#define IP5306_NODE DT_NODELABEL(ip5306) #define IP5306_NODE DT_NODELABEL(ip5306)
#define BATTERY_SAMPLE_INTERVAL K_SECONDS(1) #define BATTERY_SAMPLE_INTERVAL K_SECONDS(1)
#define BATTERY_SOC_MIN_MV 3300
#define BATTERY_SOC_MAX_MV 4200
BUILD_ASSERT(DT_NODE_HAS_STATUS(VBATT_NODE, okay), BUILD_ASSERT(DT_NODE_HAS_STATUS(VBATT_NODE, okay),
"Missing /vbatt voltage-divider node in devicetree"); "Missing /vbatt voltage-divider node in devicetree");
@@ -30,6 +35,12 @@ BUILD_ASSERT(DT_NODE_HAS_STATUS(IP5306_NODE, okay),
static const struct device *const vbatt_dev = DEVICE_DT_GET(VBATT_NODE); static const struct device *const vbatt_dev = DEVICE_DT_GET(VBATT_NODE);
static const struct device *const ip5306_dev = DEVICE_DT_GET(IP5306_NODE); static const struct device *const ip5306_dev = DEVICE_DT_GET(IP5306_NODE);
static struct k_work_delayable battery_sample_work; static struct k_work_delayable battery_sample_work;
static struct {
bool valid;
uint8_t soc;
bool charging;
bool full;
} last_bat_state;
static bool initialized; static bool initialized;
static bool running; static bool running;
@@ -52,6 +63,48 @@ static int measurement_enable(bool enable)
return 0; return 0;
} }
static uint8_t battery_soc_from_mv(int voltage_mv)
{
const int span_mv = BATTERY_SOC_MAX_MV - BATTERY_SOC_MIN_MV;
int bucket;
if (voltage_mv <= BATTERY_SOC_MIN_MV) {
return 0U;
}
if (voltage_mv >= BATTERY_SOC_MAX_MV) {
return 100U;
}
bucket = ((voltage_mv - BATTERY_SOC_MIN_MV) * 10 + (span_mv / 2)) / span_mv;
return (uint8_t)(bucket * 10);
}
static void submit_bat_state_event(uint8_t soc, bool charging, bool full)
{
struct bat_state_event *event;
if (last_bat_state.valid &&
(last_bat_state.soc == soc) &&
(last_bat_state.charging == charging) &&
(last_bat_state.full == full)) {
return;
}
last_bat_state.valid = true;
last_bat_state.soc = soc;
last_bat_state.charging = charging;
last_bat_state.full = full;
event = new_bat_state_event();
event->soc = soc;
event->charging = charging;
event->full = full;
APP_EVENT_SUBMIT(event);
}
static void battery_sample_fn(struct k_work *work) static void battery_sample_fn(struct k_work *work)
{ {
struct ip5306_status pmic_status; struct ip5306_status pmic_status;
@@ -84,8 +137,9 @@ static void battery_sample_fn(struct k_work *work)
} }
voltage_mv = sensor_value_to_mv(&voltage); voltage_mv = sensor_value_to_mv(&voltage);
// LOG_INF("Battery: %d mV, charging=%d, full=%d", submit_bat_state_event(battery_soc_from_mv(voltage_mv),
// voltage_mv, pmic_status.charging, pmic_status.full); pmic_status.charging,
pmic_status.full);
reschedule: reschedule:
if (running) { if (running) {
@@ -112,6 +166,7 @@ static int module_init(void)
} }
k_work_init_delayable(&battery_sample_work, battery_sample_fn); k_work_init_delayable(&battery_sample_work, battery_sample_fn);
memset(&last_bat_state, 0, sizeof(last_bat_state));
power_manager_restrict(MODULE_IDX(MODULE), POWER_MANAGER_LEVEL_SUSPENDED); power_manager_restrict(MODULE_IDX(MODULE), POWER_MANAGER_LEVEL_SUSPENDED);
return 0; return 0;

115
src/ble_adv_ctrl_module.c Normal file
View File

@@ -0,0 +1,115 @@
#include <stdbool.h>
#include <app_event_manager.h>
#define MODULE ble_adv_ctrl_module
#include <caf/events/module_state_event.h>
#include <caf/events/module_suspend_event.h>
#include <zephyr/logging/log.h>
#include "mode_switch_event.h"
LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF);
static bool initialized;
static bool running;
static bool ble_adv_suspended = true;
static void broadcast_ble_adv_req(bool suspend)
{
if (suspend) {
struct module_suspend_req_event *event = new_module_suspend_req_event();
event->sink_module_id = MODULE_ID(ble_adv);
event->src_module_id = MODULE_ID(MODULE);
APP_EVENT_SUBMIT(event);
} else {
struct module_resume_req_event *event = new_module_resume_req_event();
event->sink_module_id = MODULE_ID(ble_adv);
event->src_module_id = MODULE_ID(MODULE);
APP_EVENT_SUBMIT(event);
}
}
static int module_init(void)
{
ble_adv_suspended = true;
return 0;
}
static int module_start(void)
{
if (running) {
return 0;
}
running = true;
return 0;
}
static void module_pause(void)
{
running = false;
}
static bool handle_mode_switch_event(const struct mode_switch_event *event)
{
bool should_suspend;
if (!running) {
return false;
}
should_suspend = (event->mode != MODE_SWITCH_BLE);
if (should_suspend != ble_adv_suspended) {
ble_adv_suspended = should_suspend;
broadcast_ble_adv_req(should_suspend);
}
return false;
}
static bool app_event_handler(const struct app_event_header *aeh)
{
if (is_mode_switch_event(aeh)) {
return handle_mode_switch_event(cast_mode_switch_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)) {
int err;
if (!initialized) {
err = module_init();
if (err) {
module_set_state(MODULE_STATE_ERROR);
return false;
}
initialized = true;
}
err = module_start();
if (err) {
module_set_state(MODULE_STATE_ERROR);
} else {
module_set_state(MODULE_STATE_READY);
}
}
return false;
}
return false;
}
APP_EVENT_LISTENER(MODULE, app_event_handler);
APP_EVENT_SUBSCRIBE(MODULE, mode_switch_event);
APP_EVENT_SUBSCRIBE(MODULE, module_state_event);

23
src/ble_adv_uuid16.c Normal file
View File

@@ -0,0 +1,23 @@
#include <zephyr/bluetooth/uuid.h>
#include <bluetooth/adv_prov.h>
static int get_data(struct bt_data *sd, const struct bt_le_adv_prov_adv_state *state,
struct bt_le_adv_prov_feedback *fb)
{
static const uint8_t data[] = {
BT_UUID_16_ENCODE(BT_UUID_HIDS_VAL),
BT_UUID_16_ENCODE(BT_UUID_BAS_VAL),
};
ARG_UNUSED(state);
ARG_UNUSED(fb);
sd->type = BT_DATA_UUID16_ALL;
sd->data_len = sizeof(data);
sd->data = data;
return 0;
}
BT_LE_ADV_PROV_SD_PROVIDER_REGISTER(uuid16_all, get_data);

121
src/ble_bas_module.c Normal file
View File

@@ -0,0 +1,121 @@
#include <stdbool.h>
#include <stdint.h>
#include <app_event_manager.h>
#define MODULE ble_bas_module
#include <caf/events/module_state_event.h>
#include <caf/events/ble_common_event.h>
#include <zephyr/bluetooth/services/bas.h>
#include <zephyr/logging/log.h>
#include "bat_state_event.h"
LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF);
static uint8_t current_soc = 100U;
static bool initialized;
static bool running;
static bool ble_ready;
static int module_init(void)
{
return 0;
}
static int module_start(void)
{
int err;
if (running) {
return 0;
}
running = true;
if (!ble_ready) {
return 0;
}
err = bt_bas_set_battery_level(current_soc);
if (err) {
LOG_WRN("bt_bas_set_battery_level failed (%d)", err);
return err;
}
return 0;
}
static void module_pause(void)
{
running = false;
}
static bool handle_bat_state_event(const struct bat_state_event *event)
{
current_soc = event->soc;
if (running && ble_ready) {
int err = bt_bas_set_battery_level(current_soc);
if (err) {
LOG_WRN("bt_bas_set_battery_level failed (%d)", err);
}
}
return false;
}
static bool app_event_handler(const struct app_event_header *aeh)
{
if (is_bat_state_event(aeh)) {
return handle_bat_state_event(cast_bat_state_event(aeh));
}
if (is_module_state_event(aeh)) {
const struct module_state_event *event = cast_module_state_event(aeh);
int err;
if (check_state(event, MODULE_ID(main), MODULE_STATE_READY)) {
if (!initialized) {
err = module_init();
if (err) {
module_set_state(MODULE_STATE_ERROR);
return false;
}
initialized = true;
}
err = module_start();
if (err) {
module_set_state(MODULE_STATE_ERROR);
}
return false;
}
if (check_state(event, MODULE_ID(ble_state), MODULE_STATE_READY)) {
ble_ready = true;
if (running) {
err = bt_bas_set_battery_level(current_soc);
if (err) {
LOG_WRN("bt_bas_set_battery_level failed (%d)", err);
}
}
module_set_state(MODULE_STATE_READY);
return false;
}
return false;
}
return false;
}
APP_EVENT_LISTENER(MODULE, app_event_handler);
APP_EVENT_SUBSCRIBE(MODULE, bat_state_event);
APP_EVENT_SUBSCRIBE(MODULE, module_state_event);

491
src/ble_hid_module.c Normal file
View File

@@ -0,0 +1,491 @@
#include <errno.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include <app_event_manager.h>
#define MODULE ble_hid_module
#include <caf/events/module_state_event.h>
#include <caf/events/power_event.h>
#include <caf/events/ble_common_event.h>
#include <bluetooth/services/hids.h>
#include <zephyr/logging/log.h>
#include "hid_led_event.h"
#include "hid_report_sent_event.h"
#include "hid_transport_state_event.h"
#include "hid_tx_report_event.h"
#include "keyboard_core.h"
#include "set_protocol_event.h"
LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF);
#define BLE_HID_KEYS_REPORT_ID 0x01
#define BLE_HID_CONSUMER_REPORT_ID 0x02
#define BLE_HID_KEYS_REPORT_IDX 0
#define BLE_HID_CONSUMER_REPORT_IDX 1
#define BLE_HID_KEYS_LED_REPORT_SIZE 1U
#define BASE_USB_HID_SPEC_VERSION 0x0101
struct in_flight_report {
bool active;
enum keyboard_report_type report_type;
uint16_t sequence;
};
BT_HIDS_DEF(hids_obj,
KEYBOARD_NKRO_REPORT_SIZE,
KEYBOARD_CONSUMER_REPORT_SIZE,
BLE_HID_KEYS_LED_REPORT_SIZE);
static struct bt_conn *active_conn;
static struct in_flight_report in_flight;
static enum keyboard_protocol_mode protocol_mode = KEYBOARD_PROTOCOL_MODE_REPORT;
static bool initialized;
static bool running;
static bool secured;
static bool keyboard_report_notify_enabled;
static bool consumer_report_notify_enabled;
static bool boot_keyboard_notify_enabled;
static const uint8_t hid_report_desc[] = {
0x05, 0x01, /* Usage Page (Generic Desktop) */
0x09, 0x06, /* Usage (Keyboard) */
0xA1, 0x01, /* Collection (Application) */
0x85, BLE_HID_KEYS_REPORT_ID,
0x05, 0x07, /* Usage Page (Keyboard/Keypad) */
0x19, 0xE0, /* Usage Minimum (0xE0) */
0x29, 0xE7, /* Usage Maximum (0xE7) */
0x15, 0x00, /* Logical Minimum (0) */
0x25, 0x01, /* Logical Maximum (1) */
0x75, 0x01, /* Report Size (1) */
0x95, 0x08, /* Report Count (8) */
0x81, 0x02, /* Input (Data,Var,Abs) */
0x05, 0x07, /* Usage Page (Keyboard/Keypad) */
0x19, 0x00, /* Usage Minimum (0x00) */
0x2A, 0xDF, 0x00, /* Usage Maximum (0x00DF) */
0x15, 0x00, /* Logical Minimum (0) */
0x25, 0x01, /* Logical Maximum (1) */
0x75, 0x01, /* Report Size (1) */
0x96, 0xE0, 0x00, /* Report Count (224) */
0x81, 0x02, /* Input (Data,Var,Abs) */
0x85, BLE_HID_KEYS_REPORT_ID,
0x05, 0x08, /* Usage Page (LEDs) */
0x19, 0x01, /* Usage Minimum (1) */
0x29, 0x05, /* Usage Maximum (5) */
0x15, 0x00, /* Logical Minimum (0) */
0x25, 0x01, /* Logical Maximum (1) */
0x75, 0x01, /* Report Size (1) */
0x95, 0x05, /* Report Count (5) */
0x91, 0x02, /* Output (Data,Var,Abs) */
0x75, 0x03, /* Report Size (3) */
0x95, 0x01, /* Report Count (1) */
0x91, 0x01, /* Output (Const,Array,Abs) */
0xC0, /* End Collection */
0x05, 0x0C, /* Usage Page (Consumer) */
0x09, 0x01, /* Usage (Consumer Control) */
0xA1, 0x01, /* Collection (Application) */
0x85, BLE_HID_CONSUMER_REPORT_ID,
0x15, 0x00, /* Logical Minimum (0) */
0x26, 0xFF, 0x03, /* Logical Maximum (1023) */
0x19, 0x00, /* Usage Minimum (0) */
0x2A, 0xFF, 0x03, /* Usage Maximum (1023) */
0x75, 0x10, /* Report Size (16) */
0x95, 0x01, /* Report Count (1) */
0x81, 0x00, /* Input (Data,Array,Abs) */
0xC0 /* End Collection */
};
static void submit_set_protocol_event(void)
{
struct set_protocol_event *event = new_set_protocol_event();
event->transport = HID_TRANSPORT_BLE;
event->protocol_mode = protocol_mode;
APP_EVENT_SUBMIT(event);
}
static void submit_hid_led_event(uint8_t led_bm)
{
struct hid_led_event *event = new_hid_led_event();
event->transport = HID_TRANSPORT_BLE;
event->led_bm = led_bm;
APP_EVENT_SUBMIT(event);
}
static void submit_transport_state_event(void)
{
struct hid_transport_state_event *event = new_hid_transport_state_event();
bool ready = running && secured && (active_conn != NULL);
event->transport = HID_TRANSPORT_BLE;
event->ready = ready;
event->protocol_mode = protocol_mode;
event->keys_ready = ready &&
((protocol_mode == KEYBOARD_PROTOCOL_MODE_BOOT) ?
boot_keyboard_notify_enabled :
keyboard_report_notify_enabled);
event->consumer_ready = ready &&
(protocol_mode == KEYBOARD_PROTOCOL_MODE_REPORT) &&
consumer_report_notify_enabled;
APP_EVENT_SUBMIT(event);
}
static void submit_hid_report_sent_event(enum keyboard_report_type report_type,
uint16_t sequence, bool error)
{
struct hid_report_sent_event *event = new_hid_report_sent_event();
event->transport = HID_TRANSPORT_BLE;
event->report_type = report_type;
event->sequence = sequence;
event->error = error;
APP_EVENT_SUBMIT(event);
}
static void input_report_notify_handler(uint8_t report_id, enum bt_hids_notify_evt evt)
{
bool enabled = (evt == BT_HIDS_CCCD_EVT_NOTIFY_ENABLED);
if (report_id == BLE_HID_KEYS_REPORT_ID) {
keyboard_report_notify_enabled = enabled;
} else if (report_id == BLE_HID_CONSUMER_REPORT_ID) {
consumer_report_notify_enabled = enabled;
}
submit_transport_state_event();
}
static void boot_keyboard_notify_handler(enum bt_hids_notify_evt evt)
{
boot_keyboard_notify_enabled = (evt == BT_HIDS_CCCD_EVT_NOTIFY_ENABLED);
submit_transport_state_event();
}
static void hid_report_complete_cb(struct bt_conn *conn, void *user_data)
{
ARG_UNUSED(conn);
ARG_UNUSED(user_data);
if (!in_flight.active) {
return;
}
submit_hid_report_sent_event(in_flight.report_type, in_flight.sequence, false);
in_flight.active = false;
}
static void keyboard_led_report_common(struct bt_hids_rep *rep, bool write)
{
if (!write || (rep->data == NULL) || (rep->size < BLE_HID_KEYS_LED_REPORT_SIZE)) {
return;
}
submit_hid_led_event(rep->data[0]);
}
static void keyboard_led_report_handler(struct bt_hids_rep *rep,
struct bt_conn *conn,
bool write)
{
ARG_UNUSED(conn);
keyboard_led_report_common(rep, write);
}
static void boot_keyboard_led_report_handler(struct bt_hids_rep *rep,
struct bt_conn *conn,
bool write)
{
ARG_UNUSED(conn);
keyboard_led_report_common(rep, write);
}
static void pm_evt_handler(enum bt_hids_pm_evt evt, struct bt_conn *conn)
{
ARG_UNUSED(conn);
switch (evt) {
case BT_HIDS_PM_EVT_BOOT_MODE_ENTERED:
protocol_mode = KEYBOARD_PROTOCOL_MODE_BOOT;
break;
case BT_HIDS_PM_EVT_REPORT_MODE_ENTERED:
protocol_mode = KEYBOARD_PROTOCOL_MODE_REPORT;
break;
default:
return;
}
submit_set_protocol_event();
submit_transport_state_event();
}
static int module_init(void)
{
struct bt_hids_init_param hids_init_param = { 0 };
struct bt_hids_inp_rep *input_report;
struct bt_hids_outp_feat_rep *output_report;
hids_init_param.info.bcd_hid = BASE_USB_HID_SPEC_VERSION;
hids_init_param.info.b_country_code = 0x00;
hids_init_param.info.flags = BT_HIDS_REMOTE_WAKE | BT_HIDS_NORMALLY_CONNECTABLE;
hids_init_param.rep_map.data = hid_report_desc;
hids_init_param.rep_map.size = sizeof(hid_report_desc);
hids_init_param.pm_evt_handler = pm_evt_handler;
hids_init_param.is_kb = true;
hids_init_param.boot_kb_notif_handler = boot_keyboard_notify_handler;
hids_init_param.boot_kb_outp_rep_handler = boot_keyboard_led_report_handler;
input_report = &hids_init_param.inp_rep_group_init.reports[BLE_HID_KEYS_REPORT_IDX];
input_report->id = BLE_HID_KEYS_REPORT_ID;
input_report->size = KEYBOARD_NKRO_REPORT_SIZE;
input_report->handler_ext = input_report_notify_handler;
hids_init_param.inp_rep_group_init.cnt++;
input_report = &hids_init_param.inp_rep_group_init.reports[BLE_HID_CONSUMER_REPORT_IDX];
input_report->id = BLE_HID_CONSUMER_REPORT_ID;
input_report->size = KEYBOARD_CONSUMER_REPORT_SIZE;
input_report->handler_ext = input_report_notify_handler;
hids_init_param.inp_rep_group_init.cnt++;
output_report = &hids_init_param.outp_rep_group_init.reports[0];
output_report->id = BLE_HID_KEYS_REPORT_ID;
output_report->size = BLE_HID_KEYS_LED_REPORT_SIZE;
output_report->handler = keyboard_led_report_handler;
hids_init_param.outp_rep_group_init.cnt = 1U;
return bt_hids_init(&hids_obj, &hids_init_param);
}
static int module_start(void)
{
if (running) {
return 0;
}
running = true;
submit_transport_state_event();
return 0;
}
static void module_pause(void)
{
if (!running) {
return;
}
in_flight.active = false;
running = false;
submit_transport_state_event();
}
static void reset_connection_state(void)
{
active_conn = NULL;
secured = false;
keyboard_report_notify_enabled = false;
consumer_report_notify_enabled = false;
boot_keyboard_notify_enabled = false;
protocol_mode = KEYBOARD_PROTOCOL_MODE_REPORT;
in_flight.active = false;
}
static bool handle_ble_peer_event(const struct ble_peer_event *event)
{
int err;
switch (event->state) {
case PEER_STATE_CONNECTED:
if (active_conn != NULL) {
return false;
}
active_conn = event->id;
protocol_mode = KEYBOARD_PROTOCOL_MODE_REPORT;
submit_set_protocol_event();
err = bt_hids_connected(&hids_obj, event->id);
if (err) {
LOG_ERR("bt_hids_connected failed (%d)", err);
}
submit_transport_state_event();
return false;
case PEER_STATE_SECURED:
if (active_conn != event->id) {
return false;
}
secured = true;
submit_transport_state_event();
return false;
case PEER_STATE_DISCONNECTED:
if (active_conn != event->id) {
return false;
}
err = bt_hids_disconnected(&hids_obj, event->id);
if (err) {
LOG_WRN("bt_hids_disconnected failed (%d)", err);
}
reset_connection_state();
submit_transport_state_event();
return false;
default:
return false;
}
}
static bool handle_hid_tx_report_event(const struct hid_tx_report_event *event)
{
int err;
if (!running || (event->transport != HID_TRANSPORT_BLE) || in_flight.active) {
return false;
}
if ((active_conn == NULL) || !secured) {
return false;
}
if (event->report_type == KEYBOARD_REPORT_TYPE_KEYS) {
if (event->protocol_mode != protocol_mode) {
LOG_WRN("Drop BLE keys report due to protocol mismatch");
return false;
}
in_flight.active = true;
in_flight.report_type = event->report_type;
in_flight.sequence = event->sequence;
if (protocol_mode == KEYBOARD_PROTOCOL_MODE_BOOT) {
err = bt_hids_boot_kb_inp_rep_send(&hids_obj,
active_conn,
event->dyndata.data,
(uint8_t)event->dyndata.size,
hid_report_complete_cb);
} else {
err = bt_hids_inp_rep_send(&hids_obj,
active_conn,
BLE_HID_KEYS_REPORT_IDX,
event->dyndata.data,
(uint8_t)event->dyndata.size,
hid_report_complete_cb);
}
if (err) {
in_flight.active = false;
LOG_WRN("BLE keyboard report submit failed (%d)", err);
submit_hid_report_sent_event(KEYBOARD_REPORT_TYPE_KEYS,
event->sequence, true);
}
return false;
}
if (event->report_type == KEYBOARD_REPORT_TYPE_CONSUMER) {
if (protocol_mode != KEYBOARD_PROTOCOL_MODE_REPORT) {
LOG_WRN("Drop BLE consumer report in boot mode");
return false;
}
in_flight.active = true;
in_flight.report_type = event->report_type;
in_flight.sequence = event->sequence;
err = bt_hids_inp_rep_send(&hids_obj,
active_conn,
BLE_HID_CONSUMER_REPORT_IDX,
event->dyndata.data,
(uint8_t)event->dyndata.size,
hid_report_complete_cb);
if (err) {
in_flight.active = false;
LOG_WRN("BLE consumer report submit failed (%d)", err);
submit_hid_report_sent_event(KEYBOARD_REPORT_TYPE_CONSUMER,
event->sequence, true);
}
}
return false;
}
static bool app_event_handler(const struct app_event_header *aeh)
{
if (is_hid_tx_report_event(aeh)) {
return handle_hid_tx_report_event(cast_hid_tx_report_event(aeh));
}
if (is_ble_peer_event(aeh)) {
return handle_ble_peer_event(cast_ble_peer_event(aeh));
}
if (is_module_state_event(aeh)) {
const struct module_state_event *event = cast_module_state_event(aeh);
int err;
if (check_state(event, MODULE_ID(main), MODULE_STATE_READY)) {
if (!initialized) {
err = module_init();
if (err) {
module_set_state(MODULE_STATE_ERROR);
return false;
}
initialized = true;
}
err = module_start();
if (err) {
module_set_state(MODULE_STATE_ERROR);
} else {
module_set_state(MODULE_STATE_READY);
}
}
return false;
}
if (is_power_down_event(aeh)) {
if (initialized) {
module_pause();
module_set_state(MODULE_STATE_STANDBY);
}
return false;
}
if (is_wake_up_event(aeh)) {
if (initialized) {
int err = module_start();
if (err) {
module_set_state(MODULE_STATE_ERROR);
} else {
module_set_state(MODULE_STATE_READY);
}
}
return false;
}
return false;
}
APP_EVENT_LISTENER(MODULE, app_event_handler);
APP_EVENT_SUBSCRIBE_EARLY(MODULE, module_state_event);
APP_EVENT_SUBSCRIBE(MODULE, hid_tx_report_event);
APP_EVENT_SUBSCRIBE_EARLY(MODULE, ble_peer_event);
APP_EVENT_SUBSCRIBE_EARLY(MODULE, power_down_event);
APP_EVENT_SUBSCRIBE(MODULE, wake_up_event);

View File

@@ -0,0 +1,32 @@
#include "bat_state_event.h"
static void log_bat_state_event(const struct app_event_header *aeh)
{
const struct bat_state_event *event = cast_bat_state_event(aeh);
APP_EVENT_MANAGER_LOG(aeh, "soc:%u charging:%u full:%u",
event->soc, event->charging, event->full);
}
static void profile_bat_state_event(struct log_event_buf *buf,
const struct app_event_header *aeh)
{
const struct bat_state_event *event = cast_bat_state_event(aeh);
nrf_profiler_log_encode_uint8(buf, event->soc);
nrf_profiler_log_encode_uint8(buf, event->charging);
nrf_profiler_log_encode_uint8(buf, event->full);
}
APP_EVENT_INFO_DEFINE(bat_state_event,
ENCODE(NRF_PROFILER_ARG_U8,
NRF_PROFILER_ARG_U8,
NRF_PROFILER_ARG_U8),
ENCODE("soc", "charging", "full"),
profile_bat_state_event);
APP_EVENT_TYPE_DEFINE(bat_state_event,
log_bat_state_event,
&bat_state_event_info,
APP_EVENT_FLAGS_CREATE(
APP_EVENT_TYPE_FLAGS_INIT_LOG_ENABLE));

View File

@@ -1,10 +1,23 @@
#include "hid_led_event.h" #include "hid_led_event.h"
static const char *transport_name(enum hid_transport transport)
{
switch (transport) {
case HID_TRANSPORT_USB:
return "USB";
case HID_TRANSPORT_BLE:
return "BLE";
default:
return "?";
}
}
static void log_hid_led_event(const struct app_event_header *aeh) static void log_hid_led_event(const struct app_event_header *aeh)
{ {
const struct hid_led_event *event = cast_hid_led_event(aeh); const struct hid_led_event *event = cast_hid_led_event(aeh);
APP_EVENT_MANAGER_LOG(aeh, "led_bm:0x%02x", event->led_bm); APP_EVENT_MANAGER_LOG(aeh, "transport:%s led_bm:0x%02x",
transport_name(event->transport), event->led_bm);
} }
static void profile_hid_led_event(struct log_event_buf *buf, static void profile_hid_led_event(struct log_event_buf *buf,
@@ -12,12 +25,13 @@ static void profile_hid_led_event(struct log_event_buf *buf,
{ {
const struct hid_led_event *event = cast_hid_led_event(aeh); const struct hid_led_event *event = cast_hid_led_event(aeh);
nrf_profiler_log_encode_uint8(buf, event->transport);
nrf_profiler_log_encode_uint8(buf, event->led_bm); nrf_profiler_log_encode_uint8(buf, event->led_bm);
} }
APP_EVENT_INFO_DEFINE(hid_led_event, APP_EVENT_INFO_DEFINE(hid_led_event,
ENCODE(NRF_PROFILER_ARG_U8), ENCODE(NRF_PROFILER_ARG_U8, NRF_PROFILER_ARG_U8),
ENCODE("led_bm"), ENCODE("transport", "led_bm"),
profile_hid_led_event); profile_hid_led_event);
APP_EVENT_TYPE_DEFINE(hid_led_event, APP_EVENT_TYPE_DEFINE(hid_led_event,

View File

@@ -5,6 +5,8 @@ static const char *transport_name(enum hid_transport transport)
switch (transport) { switch (transport) {
case HID_TRANSPORT_USB: case HID_TRANSPORT_USB:
return "USB"; return "USB";
case HID_TRANSPORT_BLE:
return "BLE";
default: default:
return "?"; return "?";
} }

View File

@@ -5,6 +5,8 @@ static const char *transport_name(enum hid_transport transport)
switch (transport) { switch (transport) {
case HID_TRANSPORT_USB: case HID_TRANSPORT_USB:
return "USB"; return "USB";
case HID_TRANSPORT_BLE:
return "BLE";
default: default:
return "?"; return "?";
} }

View File

@@ -9,6 +9,8 @@ static const char *transport_name(enum hid_transport transport)
switch (transport) { switch (transport) {
case HID_TRANSPORT_USB: case HID_TRANSPORT_USB:
return "USB"; return "USB";
case HID_TRANSPORT_BLE:
return "BLE";
default: default:
return "?"; return "?";
} }

View File

@@ -1,5 +1,17 @@
#include "set_protocol_event.h" #include "set_protocol_event.h"
static const char *transport_name(enum hid_transport transport)
{
switch (transport) {
case HID_TRANSPORT_USB:
return "USB";
case HID_TRANSPORT_BLE:
return "BLE";
default:
return "?";
}
}
static const char *protocol_mode_name(enum keyboard_protocol_mode protocol_mode) static const char *protocol_mode_name(enum keyboard_protocol_mode protocol_mode)
{ {
switch (protocol_mode) { switch (protocol_mode) {
@@ -16,7 +28,8 @@ static void log_set_protocol_event(const struct app_event_header *aeh)
{ {
const struct set_protocol_event *event = cast_set_protocol_event(aeh); const struct set_protocol_event *event = cast_set_protocol_event(aeh);
APP_EVENT_MANAGER_LOG(aeh, "protocol:%s", APP_EVENT_MANAGER_LOG(aeh, "transport:%s protocol:%s",
transport_name(event->transport),
protocol_mode_name(event->protocol_mode)); protocol_mode_name(event->protocol_mode));
} }
@@ -25,12 +38,13 @@ static void profile_set_protocol_event(struct log_event_buf *buf,
{ {
const struct set_protocol_event *event = cast_set_protocol_event(aeh); const struct set_protocol_event *event = cast_set_protocol_event(aeh);
nrf_profiler_log_encode_uint8(buf, event->transport);
nrf_profiler_log_encode_uint8(buf, event->protocol_mode); nrf_profiler_log_encode_uint8(buf, event->protocol_mode);
} }
APP_EVENT_INFO_DEFINE(set_protocol_event, APP_EVENT_INFO_DEFINE(set_protocol_event,
ENCODE(NRF_PROFILER_ARG_U8), ENCODE(NRF_PROFILER_ARG_U8, NRF_PROFILER_ARG_U8),
ENCODE("protocol_mode"), ENCODE("transport", "protocol_mode"),
profile_set_protocol_event); profile_set_protocol_event);
APP_EVENT_TYPE_DEFINE(set_protocol_event, APP_EVENT_TYPE_DEFINE(set_protocol_event,

View File

@@ -46,12 +46,18 @@ struct hid_transport_state_data {
struct in_flight_report { struct in_flight_report {
bool active; bool active;
enum hid_transport transport;
enum keyboard_report_type report_type; enum keyboard_report_type report_type;
uint16_t sequence; uint16_t sequence;
}; };
static struct hid_transport_state_data usb_state = { static struct hid_transport_state_data transport_state[HID_TRANSPORT_COUNT] = {
[HID_TRANSPORT_USB] = {
.protocol_mode = KEYBOARD_PROTOCOL_MODE_REPORT, .protocol_mode = KEYBOARD_PROTOCOL_MODE_REPORT,
},
[HID_TRANSPORT_BLE] = {
.protocol_mode = KEYBOARD_PROTOCOL_MODE_REPORT,
},
}; };
static struct pending_report pending_keys; static struct pending_report pending_keys;
static struct pending_report pending_consumer_latest; static struct pending_report pending_consumer_latest;
@@ -65,6 +71,22 @@ static uint16_t next_sequence;
static bool initialized; static bool initialized;
static bool running; static bool running;
static bool mode_to_transport(enum mode_switch_mode mode, enum hid_transport *transport)
{
switch (mode) {
case MODE_SWITCH_USB:
*transport = HID_TRANSPORT_USB;
return true;
case MODE_SWITCH_BLE:
*transport = HID_TRANSPORT_BLE;
return true;
default:
return false;
}
}
static void clear_pending_reports(void) static void clear_pending_reports(void)
{ {
memset(&pending_keys, 0, sizeof(pending_keys)); memset(&pending_keys, 0, sizeof(pending_keys));
@@ -111,30 +133,41 @@ static bool consumer_fifo_pop(struct queued_report *entry)
static bool transport_can_send_report(enum keyboard_report_type report_type) static bool transport_can_send_report(enum keyboard_report_type report_type)
{ {
if ((current_mode != MODE_SWITCH_USB) || !usb_state.ready || in_flight.active) { enum hid_transport transport;
struct hid_transport_state_data *state;
if (!mode_to_transport(current_mode, &transport) || in_flight.active) {
return false;
}
state = &transport_state[transport];
if (!state->ready) {
return false; return false;
} }
if (report_type == KEYBOARD_REPORT_TYPE_KEYS) { if (report_type == KEYBOARD_REPORT_TYPE_KEYS) {
return usb_state.keys_ready; return state->keys_ready;
} }
return usb_state.consumer_ready; return state->consumer_ready;
} }
static void submit_hid_tx_report_event(enum keyboard_report_type report_type, static void submit_hid_tx_report_event(enum hid_transport transport,
enum keyboard_report_type report_type,
enum keyboard_protocol_mode protocol_mode, enum keyboard_protocol_mode protocol_mode,
const uint8_t *data, size_t size) const uint8_t *data, size_t size)
{ {
struct hid_tx_report_event *event = new_hid_tx_report_event(size); struct hid_tx_report_event *event = new_hid_tx_report_event(size);
event->transport = HID_TRANSPORT_USB; event->transport = transport;
event->report_type = report_type; event->report_type = report_type;
event->protocol_mode = protocol_mode; event->protocol_mode = protocol_mode;
event->sequence = next_sequence++; event->sequence = next_sequence++;
memcpy(event->dyndata.data, data, size); memcpy(event->dyndata.data, data, size);
in_flight.active = true; in_flight.active = true;
in_flight.transport = transport;
in_flight.report_type = report_type; in_flight.report_type = report_type;
in_flight.sequence = event->sequence; in_flight.sequence = event->sequence;
@@ -144,17 +177,26 @@ static void submit_hid_tx_report_event(enum keyboard_report_type report_type,
static void try_send_next(void) static void try_send_next(void)
{ {
struct queued_report queued; struct queued_report queued;
enum hid_transport transport;
struct hid_transport_state_data *state;
if (!running || in_flight.active || (current_mode != MODE_SWITCH_USB) || !usb_state.ready) { if (!running || in_flight.active || !mode_to_transport(current_mode, &transport)) {
return;
}
state = &transport_state[transport];
if (!state->ready) {
return; return;
} }
if (pending_keys.valid && transport_can_send_report(KEYBOARD_REPORT_TYPE_KEYS)) { if (pending_keys.valid && transport_can_send_report(KEYBOARD_REPORT_TYPE_KEYS)) {
if (pending_keys.protocol_mode != usb_state.protocol_mode) { if (pending_keys.protocol_mode != state->protocol_mode) {
LOG_WRN("Drop stale keys report after protocol change"); LOG_WRN("Drop stale keys report after protocol change");
pending_keys.valid = false; pending_keys.valid = false;
} else { } else {
submit_hid_tx_report_event(pending_keys.report_type, submit_hid_tx_report_event(transport,
pending_keys.report_type,
pending_keys.protocol_mode, pending_keys.protocol_mode,
pending_keys.data, pending_keys.data,
pending_keys.size); pending_keys.size);
@@ -166,26 +208,38 @@ static void try_send_next(void)
if ((consumer_fifo_count > 0U) && if ((consumer_fifo_count > 0U) &&
transport_can_send_report(KEYBOARD_REPORT_TYPE_CONSUMER) && transport_can_send_report(KEYBOARD_REPORT_TYPE_CONSUMER) &&
consumer_fifo_pop(&queued)) { consumer_fifo_pop(&queued)) {
submit_hid_tx_report_event(queued.report_type, if (queued.protocol_mode != state->protocol_mode) {
LOG_WRN("Drop stale consumer report after protocol change");
} else {
submit_hid_tx_report_event(transport,
queued.report_type,
queued.protocol_mode, queued.protocol_mode,
queued.data, queued.data,
queued.size); queued.size);
return; return;
} }
}
if (pending_consumer_latest.valid && if (pending_consumer_latest.valid &&
transport_can_send_report(KEYBOARD_REPORT_TYPE_CONSUMER)) { transport_can_send_report(KEYBOARD_REPORT_TYPE_CONSUMER)) {
submit_hid_tx_report_event(pending_consumer_latest.report_type, if (pending_consumer_latest.protocol_mode != state->protocol_mode) {
LOG_WRN("Drop stale latest consumer report after protocol change");
pending_consumer_latest.valid = false;
} else {
submit_hid_tx_report_event(transport,
pending_consumer_latest.report_type,
pending_consumer_latest.protocol_mode, pending_consumer_latest.protocol_mode,
pending_consumer_latest.data, pending_consumer_latest.data,
pending_consumer_latest.size); pending_consumer_latest.size);
pending_consumer_latest.valid = false; pending_consumer_latest.valid = false;
} }
}
} }
static bool handle_keyboard_hid_report_event(const struct keyboard_hid_report_event *event) static bool handle_keyboard_hid_report_event(const struct keyboard_hid_report_event *event)
{ {
if (!running || (event->mode != MODE_SWITCH_USB)) { if (!running ||
((event->mode != MODE_SWITCH_USB) && (event->mode != MODE_SWITCH_BLE))) {
return false; return false;
} }
@@ -215,20 +269,35 @@ static bool handle_keyboard_hid_report_event(const struct keyboard_hid_report_ev
static bool handle_hid_transport_state_event(const struct hid_transport_state_event *event) static bool handle_hid_transport_state_event(const struct hid_transport_state_event *event)
{ {
if (event->transport != HID_TRANSPORT_USB) { enum hid_transport active_transport;
struct hid_transport_state_data *state;
if (event->transport >= HID_TRANSPORT_COUNT) {
return false; return false;
} }
usb_state.ready = event->ready; state = &transport_state[event->transport];
usb_state.keys_ready = event->keys_ready;
usb_state.consumer_ready = event->consumer_ready;
if (usb_state.protocol_mode != event->protocol_mode) { state->ready = event->ready;
usb_state.protocol_mode = event->protocol_mode; state->keys_ready = event->keys_ready;
state->consumer_ready = event->consumer_ready;
if (state->protocol_mode != event->protocol_mode) {
state->protocol_mode = event->protocol_mode;
if (mode_to_transport(current_mode, &active_transport) &&
(active_transport == event->transport)) {
pending_keys.valid = false; pending_keys.valid = false;
pending_consumer_latest.valid = false;
consumer_fifo_head = 0U;
consumer_fifo_tail = 0U;
consumer_fifo_count = 0U;
}
} }
if (!usb_state.ready) { if (!state->ready &&
mode_to_transport(current_mode, &active_transport) &&
(active_transport == event->transport)) {
consumer_fifo_head = 0U; consumer_fifo_head = 0U;
consumer_fifo_tail = 0U; consumer_fifo_tail = 0U;
consumer_fifo_count = 0U; consumer_fifo_count = 0U;
@@ -241,7 +310,7 @@ static bool handle_hid_transport_state_event(const struct hid_transport_state_ev
static bool handle_hid_report_sent_event(const struct hid_report_sent_event *event) static bool handle_hid_report_sent_event(const struct hid_report_sent_event *event)
{ {
if ((event->transport != HID_TRANSPORT_USB) || !in_flight.active) { if (!in_flight.active || (event->transport != in_flight.transport)) {
return false; return false;
} }
@@ -263,9 +332,12 @@ static bool handle_hid_report_sent_event(const struct hid_report_sent_event *eve
static bool handle_mode_switch_event(const struct mode_switch_event *event) static bool handle_mode_switch_event(const struct mode_switch_event *event)
{ {
bool mode_changed = (current_mode != event->mode);
current_mode = event->mode; current_mode = event->mode;
if (current_mode != MODE_SWITCH_USB) { if (mode_changed || ((current_mode != MODE_SWITCH_USB) &&
(current_mode != MODE_SWITCH_BLE))) {
clear_pending_reports(); clear_pending_reports();
} }
@@ -277,10 +349,16 @@ static int module_init(void)
{ {
clear_pending_reports(); clear_pending_reports();
current_mode = MODE_SWITCH_USB; current_mode = MODE_SWITCH_USB;
usb_state.ready = false; transport_state[HID_TRANSPORT_USB].ready = false;
usb_state.keys_ready = false; transport_state[HID_TRANSPORT_USB].keys_ready = false;
usb_state.consumer_ready = false; transport_state[HID_TRANSPORT_USB].consumer_ready = false;
usb_state.protocol_mode = KEYBOARD_PROTOCOL_MODE_REPORT; transport_state[HID_TRANSPORT_USB].protocol_mode =
KEYBOARD_PROTOCOL_MODE_REPORT;
transport_state[HID_TRANSPORT_BLE].ready = false;
transport_state[HID_TRANSPORT_BLE].keys_ready = false;
transport_state[HID_TRANSPORT_BLE].consumer_ready = false;
transport_state[HID_TRANSPORT_BLE].protocol_mode =
KEYBOARD_PROTOCOL_MODE_REPORT;
next_sequence = 1U; next_sequence = 1U;
return 0; return 0;

View File

@@ -85,12 +85,42 @@ static const uint16_t consumer_usage_map[KEYBOARD_CONSUMER_CTRL_COUNT] = {
static struct keyboard_state keyboard_state; static struct keyboard_state keyboard_state;
static struct keyboard_reports_cache reports_cache; static struct keyboard_reports_cache reports_cache;
static enum keyboard_protocol_mode protocol_mode = KEYBOARD_PROTOCOL_MODE_REPORT; static enum keyboard_protocol_mode transport_protocol_modes[HID_TRANSPORT_COUNT] = {
[HID_TRANSPORT_USB] = KEYBOARD_PROTOCOL_MODE_REPORT,
[HID_TRANSPORT_BLE] = KEYBOARD_PROTOCOL_MODE_REPORT,
};
static enum mode_switch_mode current_mode; static enum mode_switch_mode current_mode;
static bool initialized; static bool initialized;
static bool running; static bool running;
static bool mode_valid; static bool mode_valid;
static bool mode_to_transport(enum mode_switch_mode mode, enum hid_transport *transport)
{
switch (mode) {
case MODE_SWITCH_USB:
*transport = HID_TRANSPORT_USB;
return true;
case MODE_SWITCH_BLE:
*transport = HID_TRANSPORT_BLE;
return true;
default:
return false;
}
}
static enum keyboard_protocol_mode active_protocol_mode_get(void)
{
enum hid_transport transport;
if (mode_valid && mode_to_transport(current_mode, &transport)) {
return transport_protocol_modes[transport];
}
return KEYBOARD_PROTOCOL_MODE_REPORT;
}
static const struct keymap_entry *keymap_get(uint16_t key_id) static const struct keymap_entry *keymap_get(uint16_t key_id)
{ {
size_t left = 0; size_t left = 0;
@@ -234,6 +264,7 @@ static void submit_keyboard_report_event(enum keyboard_report_type report_type,
{ {
struct keyboard_hid_report_event *event = struct keyboard_hid_report_event *event =
new_keyboard_hid_report_event(size); new_keyboard_hid_report_event(size);
enum keyboard_protocol_mode protocol_mode = active_protocol_mode_get();
event->mode = current_mode; event->mode = current_mode;
event->report_type = report_type; event->report_type = report_type;
@@ -247,8 +278,10 @@ static void submit_keyboard_report_event(enum keyboard_report_type report_type,
static void submit_consumer_fifo_frame(uint16_t usage_id) static void submit_consumer_fifo_frame(uint16_t usage_id)
{ {
uint8_t report_buf[KEYBOARD_CONSUMER_REPORT_SIZE]; uint8_t report_buf[KEYBOARD_CONSUMER_REPORT_SIZE];
enum keyboard_protocol_mode protocol_mode = active_protocol_mode_get();
if (!running || !mode_valid) { if (!running || !mode_valid ||
(protocol_mode == KEYBOARD_PROTOCOL_MODE_BOOT)) {
return; return;
} }
@@ -264,6 +297,10 @@ static void submit_consumer_pulse_frames(enum keyboard_consumer_control control_
{ {
uint16_t usage_id; uint16_t usage_id;
if (active_protocol_mode_get() == KEYBOARD_PROTOCOL_MODE_BOOT) {
return;
}
if (control_id >= KEYBOARD_CONSUMER_CTRL_COUNT) { if (control_id >= KEYBOARD_CONSUMER_CTRL_COUNT) {
LOG_WRN("Unsupported consumer control id %u", control_id); LOG_WRN("Unsupported consumer control id %u", control_id);
return; return;
@@ -287,6 +324,7 @@ static void emit_keys_report(bool force)
uint8_t report_size; uint8_t report_size;
uint8_t *cache_buf; uint8_t *cache_buf;
bool *cache_valid; bool *cache_valid;
enum keyboard_protocol_mode protocol_mode = active_protocol_mode_get();
if (!mode_valid) { if (!mode_valid) {
return; return;
@@ -320,8 +358,9 @@ static void emit_keys_report(bool force)
static void emit_consumer_report(bool force) static void emit_consumer_report(bool force)
{ {
uint8_t report_buf[KEYBOARD_CONSUMER_REPORT_SIZE]; uint8_t report_buf[KEYBOARD_CONSUMER_REPORT_SIZE];
enum keyboard_protocol_mode protocol_mode = active_protocol_mode_get();
if (!mode_valid) { if (!mode_valid || (protocol_mode == KEYBOARD_PROTOCOL_MODE_BOOT)) {
return; return;
} }
@@ -344,7 +383,10 @@ static void emit_consumer_report(bool force)
static void emit_all_reports(bool force) static void emit_all_reports(bool force)
{ {
emit_keys_report(force); emit_keys_report(force);
if (active_protocol_mode_get() != KEYBOARD_PROTOCOL_MODE_BOOT) {
emit_consumer_report(force); emit_consumer_report(force);
}
} }
static void emit_release_reports(enum mode_switch_mode mode) static void emit_release_reports(enum mode_switch_mode mode)
@@ -352,6 +394,7 @@ static void emit_release_reports(enum mode_switch_mode mode)
struct keyboard_hid_report_event *event; struct keyboard_hid_report_event *event;
uint8_t keys_report[KEYBOARD_NKRO_REPORT_SIZE] = { 0 }; uint8_t keys_report[KEYBOARD_NKRO_REPORT_SIZE] = { 0 };
uint8_t consumer_report[KEYBOARD_CONSUMER_REPORT_SIZE] = { 0 }; uint8_t consumer_report[KEYBOARD_CONSUMER_REPORT_SIZE] = { 0 };
enum keyboard_protocol_mode protocol_mode = active_protocol_mode_get();
size_t keys_report_size = size_t keys_report_size =
(protocol_mode == KEYBOARD_PROTOCOL_MODE_BOOT) ? (protocol_mode == KEYBOARD_PROTOCOL_MODE_BOOT) ?
KEYBOARD_BOOT_REPORT_SIZE : KEYBOARD_NKRO_REPORT_SIZE; KEYBOARD_BOOT_REPORT_SIZE : KEYBOARD_NKRO_REPORT_SIZE;
@@ -363,12 +406,15 @@ static void emit_release_reports(enum mode_switch_mode mode)
memcpy(event->dyndata.data, keys_report, keys_report_size); memcpy(event->dyndata.data, keys_report, keys_report_size);
APP_EVENT_SUBMIT(event); APP_EVENT_SUBMIT(event);
if (protocol_mode != KEYBOARD_PROTOCOL_MODE_BOOT) {
event = new_keyboard_hid_report_event(KEYBOARD_CONSUMER_REPORT_SIZE); event = new_keyboard_hid_report_event(KEYBOARD_CONSUMER_REPORT_SIZE);
event->mode = mode; event->mode = mode;
event->report_type = KEYBOARD_REPORT_TYPE_CONSUMER; event->report_type = KEYBOARD_REPORT_TYPE_CONSUMER;
event->protocol_mode = protocol_mode; event->protocol_mode = protocol_mode;
memcpy(event->dyndata.data, consumer_report, KEYBOARD_CONSUMER_REPORT_SIZE); memcpy(event->dyndata.data, consumer_report,
KEYBOARD_CONSUMER_REPORT_SIZE);
APP_EVENT_SUBMIT(event); APP_EVENT_SUBMIT(event);
}
} }
static int module_init(void) static int module_init(void)
@@ -376,7 +422,10 @@ static int module_init(void)
keyboard_state_clear(); keyboard_state_clear();
reports_cache_invalidate(); reports_cache_invalidate();
mode_valid = false; mode_valid = false;
protocol_mode = KEYBOARD_PROTOCOL_MODE_REPORT; transport_protocol_modes[HID_TRANSPORT_USB] =
KEYBOARD_PROTOCOL_MODE_REPORT;
transport_protocol_modes[HID_TRANSPORT_BLE] =
KEYBOARD_PROTOCOL_MODE_REPORT;
return 0; return 0;
} }
@@ -489,12 +538,24 @@ static bool app_event_handler(const struct app_event_header *aeh)
if (is_set_protocol_event(aeh)) { if (is_set_protocol_event(aeh)) {
const struct set_protocol_event *event = cast_set_protocol_event(aeh); const struct set_protocol_event *event = cast_set_protocol_event(aeh);
enum hid_transport active_transport;
if (protocol_mode != event->protocol_mode) { if (event->transport >= HID_TRANSPORT_COUNT) {
protocol_mode = event->protocol_mode; return false;
}
if (running && mode_valid && (current_mode == MODE_SWITCH_USB)) { if (transport_protocol_modes[event->transport] != event->protocol_mode) {
transport_protocol_modes[event->transport] = event->protocol_mode;
if (running && mode_valid &&
mode_to_transport(current_mode, &active_transport) &&
(active_transport == event->transport)) {
reports_cache_invalidate();
emit_keys_report(true); emit_keys_report(true);
if (event->protocol_mode != KEYBOARD_PROTOCOL_MODE_BOOT) {
emit_consumer_report(true);
}
} }
} }

View File

@@ -28,8 +28,8 @@ LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF);
#define USB_HID_VID 0x1915 #define USB_HID_VID 0x1915
#define USB_HID_PID 0x52F0 #define USB_HID_PID 0x52F0
#define USB_HID_MANUFACTURER "blinky" #define USB_HID_MANUFACTURER "Atguigu"
#define USB_HID_PRODUCT "Mini Keyboard" #define USB_HID_PRODUCT "WH Mini Keyboard"
#define USB_HID_POLLING_US 1000U #define USB_HID_POLLING_US 1000U
#define KBD_LED_REPORT_SIZE 1U #define KBD_LED_REPORT_SIZE 1U
@@ -150,6 +150,7 @@ static void submit_set_protocol_event(enum keyboard_protocol_mode protocol_mode)
{ {
struct set_protocol_event *event = new_set_protocol_event(); struct set_protocol_event *event = new_set_protocol_event();
event->transport = HID_TRANSPORT_USB;
event->protocol_mode = protocol_mode; event->protocol_mode = protocol_mode;
APP_EVENT_SUBMIT(event); APP_EVENT_SUBMIT(event);
} }
@@ -158,6 +159,7 @@ static void submit_hid_led_event(uint8_t led_bm)
{ {
struct hid_led_event *event = new_hid_led_event(); struct hid_led_event *event = new_hid_led_event();
event->transport = HID_TRANSPORT_USB;
event->led_bm = led_bm; event->led_bm = led_bm;
APP_EVENT_SUBMIT(event); APP_EVENT_SUBMIT(event);
} }