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:
@@ -13,10 +13,15 @@ add_subdirectory(drivers)
|
||||
target_sources(app PRIVATE
|
||||
src/main.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/hid_flowctrl_module.c
|
||||
src/keyboard_core_module.c
|
||||
src/usb_hid_module.c
|
||||
src/events/bat_state_event.c
|
||||
src/events/encoder_event.c
|
||||
src/events/hid_led_event.c
|
||||
src/events/hid_report_sent_event.c
|
||||
|
||||
27
inc/events/bat_state_event.h
Normal file
27
inc/events/bat_state_event.h
Normal 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_ */
|
||||
@@ -4,12 +4,15 @@
|
||||
#include <app_event_manager.h>
|
||||
#include <app_event_manager_profiler_tracer.h>
|
||||
|
||||
#include "keyboard_core.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct hid_led_event {
|
||||
struct app_event_header header;
|
||||
enum hid_transport transport;
|
||||
uint8_t led_bm;
|
||||
};
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ extern "C" {
|
||||
|
||||
struct set_protocol_event {
|
||||
struct app_event_header header;
|
||||
enum hid_transport transport;
|
||||
enum keyboard_protocol_mode protocol_mode;
|
||||
};
|
||||
|
||||
|
||||
@@ -30,6 +30,8 @@ enum hid_queue_policy {
|
||||
|
||||
enum hid_transport {
|
||||
HID_TRANSPORT_USB,
|
||||
HID_TRANSPORT_BLE,
|
||||
HID_TRANSPORT_COUNT,
|
||||
};
|
||||
|
||||
enum keyboard_consumer_control {
|
||||
|
||||
15
inc/settings_loader_def.h
Normal file
15
inc/settings_loader_def.h
Normal 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));
|
||||
}
|
||||
63
prj.conf
63
prj.conf
@@ -10,7 +10,13 @@ CONFIG_PINCTRL_DYNAMIC=y
|
||||
CONFIG_REBOOT=y
|
||||
CONFIG_SENSOR=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_ASSERT=y
|
||||
|
||||
@@ -18,7 +24,62 @@ CONFIG_ASSERT=y
|
||||
CONFIG_USB_DEVICE_STACK_NEXT=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
|
||||
CONFIG_CAF_POWER_MANAGER=y
|
||||
CONFIG_CAF_POWER_MANAGER_TIMEOUT=120
|
||||
# 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
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <app_event_manager.h>
|
||||
|
||||
@@ -16,11 +17,15 @@
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <zephyr/pm/device.h>
|
||||
|
||||
#include "bat_state_event.h"
|
||||
|
||||
LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF);
|
||||
|
||||
#define VBATT_NODE DT_PATH(vbatt)
|
||||
#define IP5306_NODE DT_NODELABEL(ip5306)
|
||||
#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),
|
||||
"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 ip5306_dev = DEVICE_DT_GET(IP5306_NODE);
|
||||
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 running;
|
||||
|
||||
@@ -52,6 +63,48 @@ static int measurement_enable(bool enable)
|
||||
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)
|
||||
{
|
||||
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);
|
||||
// LOG_INF("Battery: %d mV, charging=%d, full=%d",
|
||||
// voltage_mv, pmic_status.charging, pmic_status.full);
|
||||
submit_bat_state_event(battery_soc_from_mv(voltage_mv),
|
||||
pmic_status.charging,
|
||||
pmic_status.full);
|
||||
|
||||
reschedule:
|
||||
if (running) {
|
||||
@@ -112,6 +166,7 @@ static int module_init(void)
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return 0;
|
||||
|
||||
115
src/ble_adv_ctrl_module.c
Normal file
115
src/ble_adv_ctrl_module.c
Normal 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
23
src/ble_adv_uuid16.c
Normal 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
121
src/ble_bas_module.c
Normal 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
491
src/ble_hid_module.c
Normal 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);
|
||||
32
src/events/bat_state_event.c
Normal file
32
src/events/bat_state_event.c
Normal 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));
|
||||
@@ -1,10 +1,23 @@
|
||||
#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)
|
||||
{
|
||||
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,
|
||||
@@ -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);
|
||||
|
||||
nrf_profiler_log_encode_uint8(buf, event->transport);
|
||||
nrf_profiler_log_encode_uint8(buf, event->led_bm);
|
||||
}
|
||||
|
||||
APP_EVENT_INFO_DEFINE(hid_led_event,
|
||||
ENCODE(NRF_PROFILER_ARG_U8),
|
||||
ENCODE("led_bm"),
|
||||
ENCODE(NRF_PROFILER_ARG_U8, NRF_PROFILER_ARG_U8),
|
||||
ENCODE("transport", "led_bm"),
|
||||
profile_hid_led_event);
|
||||
|
||||
APP_EVENT_TYPE_DEFINE(hid_led_event,
|
||||
|
||||
@@ -5,6 +5,8 @@ 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 "?";
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ 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 "?";
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@ 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 "?";
|
||||
}
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
#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)
|
||||
{
|
||||
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);
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
nrf_profiler_log_encode_uint8(buf, event->transport);
|
||||
nrf_profiler_log_encode_uint8(buf, event->protocol_mode);
|
||||
}
|
||||
|
||||
APP_EVENT_INFO_DEFINE(set_protocol_event,
|
||||
ENCODE(NRF_PROFILER_ARG_U8),
|
||||
ENCODE("protocol_mode"),
|
||||
ENCODE(NRF_PROFILER_ARG_U8, NRF_PROFILER_ARG_U8),
|
||||
ENCODE("transport", "protocol_mode"),
|
||||
profile_set_protocol_event);
|
||||
|
||||
APP_EVENT_TYPE_DEFINE(set_protocol_event,
|
||||
|
||||
@@ -46,12 +46,18 @@ struct hid_transport_state_data {
|
||||
|
||||
struct in_flight_report {
|
||||
bool active;
|
||||
enum hid_transport transport;
|
||||
enum keyboard_report_type report_type;
|
||||
uint16_t sequence;
|
||||
};
|
||||
|
||||
static struct hid_transport_state_data usb_state = {
|
||||
.protocol_mode = KEYBOARD_PROTOCOL_MODE_REPORT,
|
||||
static struct hid_transport_state_data transport_state[HID_TRANSPORT_COUNT] = {
|
||||
[HID_TRANSPORT_USB] = {
|
||||
.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_consumer_latest;
|
||||
@@ -65,6 +71,22 @@ static uint16_t next_sequence;
|
||||
static bool initialized;
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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,
|
||||
const uint8_t *data, size_t 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->protocol_mode = protocol_mode;
|
||||
event->sequence = next_sequence++;
|
||||
memcpy(event->dyndata.data, data, size);
|
||||
|
||||
in_flight.active = true;
|
||||
in_flight.transport = transport;
|
||||
in_flight.report_type = report_type;
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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");
|
||||
pending_keys.valid = false;
|
||||
} 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.data,
|
||||
pending_keys.size);
|
||||
@@ -166,26 +208,38 @@ static void try_send_next(void)
|
||||
if ((consumer_fifo_count > 0U) &&
|
||||
transport_can_send_report(KEYBOARD_REPORT_TYPE_CONSUMER) &&
|
||||
consumer_fifo_pop(&queued)) {
|
||||
submit_hid_tx_report_event(queued.report_type,
|
||||
queued.protocol_mode,
|
||||
queued.data,
|
||||
queued.size);
|
||||
return;
|
||||
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.data,
|
||||
queued.size);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (pending_consumer_latest.valid &&
|
||||
transport_can_send_report(KEYBOARD_REPORT_TYPE_CONSUMER)) {
|
||||
submit_hid_tx_report_event(pending_consumer_latest.report_type,
|
||||
pending_consumer_latest.protocol_mode,
|
||||
pending_consumer_latest.data,
|
||||
pending_consumer_latest.size);
|
||||
pending_consumer_latest.valid = false;
|
||||
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.data,
|
||||
pending_consumer_latest.size);
|
||||
pending_consumer_latest.valid = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
usb_state.ready = event->ready;
|
||||
usb_state.keys_ready = event->keys_ready;
|
||||
usb_state.consumer_ready = event->consumer_ready;
|
||||
state = &transport_state[event->transport];
|
||||
|
||||
if (usb_state.protocol_mode != event->protocol_mode) {
|
||||
usb_state.protocol_mode = event->protocol_mode;
|
||||
pending_keys.valid = false;
|
||||
state->ready = event->ready;
|
||||
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_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_tail = 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)
|
||||
{
|
||||
if ((event->transport != HID_TRANSPORT_USB) || !in_flight.active) {
|
||||
if (!in_flight.active || (event->transport != in_flight.transport)) {
|
||||
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)
|
||||
{
|
||||
bool mode_changed = (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();
|
||||
}
|
||||
|
||||
@@ -277,10 +349,16 @@ static int module_init(void)
|
||||
{
|
||||
clear_pending_reports();
|
||||
current_mode = MODE_SWITCH_USB;
|
||||
usb_state.ready = false;
|
||||
usb_state.keys_ready = false;
|
||||
usb_state.consumer_ready = false;
|
||||
usb_state.protocol_mode = KEYBOARD_PROTOCOL_MODE_REPORT;
|
||||
transport_state[HID_TRANSPORT_USB].ready = false;
|
||||
transport_state[HID_TRANSPORT_USB].keys_ready = false;
|
||||
transport_state[HID_TRANSPORT_USB].consumer_ready = false;
|
||||
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;
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -85,12 +85,42 @@ static const uint16_t consumer_usage_map[KEYBOARD_CONSUMER_CTRL_COUNT] = {
|
||||
|
||||
static struct keyboard_state keyboard_state;
|
||||
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 bool initialized;
|
||||
static bool running;
|
||||
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)
|
||||
{
|
||||
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 =
|
||||
new_keyboard_hid_report_event(size);
|
||||
enum keyboard_protocol_mode protocol_mode = active_protocol_mode_get();
|
||||
|
||||
event->mode = current_mode;
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -264,6 +297,10 @@ static void submit_consumer_pulse_frames(enum keyboard_consumer_control control_
|
||||
{
|
||||
uint16_t usage_id;
|
||||
|
||||
if (active_protocol_mode_get() == KEYBOARD_PROTOCOL_MODE_BOOT) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (control_id >= KEYBOARD_CONSUMER_CTRL_COUNT) {
|
||||
LOG_WRN("Unsupported consumer control id %u", control_id);
|
||||
return;
|
||||
@@ -287,6 +324,7 @@ static void emit_keys_report(bool force)
|
||||
uint8_t report_size;
|
||||
uint8_t *cache_buf;
|
||||
bool *cache_valid;
|
||||
enum keyboard_protocol_mode protocol_mode = active_protocol_mode_get();
|
||||
|
||||
if (!mode_valid) {
|
||||
return;
|
||||
@@ -320,8 +358,9 @@ static void emit_keys_report(bool force)
|
||||
static void emit_consumer_report(bool force)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -344,7 +383,10 @@ static void emit_consumer_report(bool force)
|
||||
static void emit_all_reports(bool force)
|
||||
{
|
||||
emit_keys_report(force);
|
||||
emit_consumer_report(force);
|
||||
|
||||
if (active_protocol_mode_get() != KEYBOARD_PROTOCOL_MODE_BOOT) {
|
||||
emit_consumer_report(force);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
uint8_t keys_report[KEYBOARD_NKRO_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 =
|
||||
(protocol_mode == KEYBOARD_PROTOCOL_MODE_BOOT) ?
|
||||
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);
|
||||
APP_EVENT_SUBMIT(event);
|
||||
|
||||
event = new_keyboard_hid_report_event(KEYBOARD_CONSUMER_REPORT_SIZE);
|
||||
event->mode = mode;
|
||||
event->report_type = KEYBOARD_REPORT_TYPE_CONSUMER;
|
||||
event->protocol_mode = protocol_mode;
|
||||
memcpy(event->dyndata.data, consumer_report, KEYBOARD_CONSUMER_REPORT_SIZE);
|
||||
APP_EVENT_SUBMIT(event);
|
||||
if (protocol_mode != KEYBOARD_PROTOCOL_MODE_BOOT) {
|
||||
event = new_keyboard_hid_report_event(KEYBOARD_CONSUMER_REPORT_SIZE);
|
||||
event->mode = mode;
|
||||
event->report_type = KEYBOARD_REPORT_TYPE_CONSUMER;
|
||||
event->protocol_mode = protocol_mode;
|
||||
memcpy(event->dyndata.data, consumer_report,
|
||||
KEYBOARD_CONSUMER_REPORT_SIZE);
|
||||
APP_EVENT_SUBMIT(event);
|
||||
}
|
||||
}
|
||||
|
||||
static int module_init(void)
|
||||
@@ -376,7 +422,10 @@ static int module_init(void)
|
||||
keyboard_state_clear();
|
||||
reports_cache_invalidate();
|
||||
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;
|
||||
}
|
||||
@@ -489,12 +538,24 @@ static bool app_event_handler(const struct app_event_header *aeh)
|
||||
|
||||
if (is_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) {
|
||||
protocol_mode = event->protocol_mode;
|
||||
if (event->transport >= HID_TRANSPORT_COUNT) {
|
||||
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);
|
||||
|
||||
if (event->protocol_mode != KEYBOARD_PROTOCOL_MODE_BOOT) {
|
||||
emit_consumer_report(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,8 +28,8 @@ LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF);
|
||||
|
||||
#define USB_HID_VID 0x1915
|
||||
#define USB_HID_PID 0x52F0
|
||||
#define USB_HID_MANUFACTURER "blinky"
|
||||
#define USB_HID_PRODUCT "Mini Keyboard"
|
||||
#define USB_HID_MANUFACTURER "Atguigu"
|
||||
#define USB_HID_PRODUCT "WH Mini Keyboard"
|
||||
#define USB_HID_POLLING_US 1000U
|
||||
|
||||
#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();
|
||||
|
||||
event->transport = HID_TRANSPORT_USB;
|
||||
event->protocol_mode = protocol_mode;
|
||||
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();
|
||||
|
||||
event->transport = HID_TRANSPORT_USB;
|
||||
event->led_bm = led_bm;
|
||||
APP_EVENT_SUBMIT(event);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user