From 39d2962258b27a69908d3d6b3a21bc41377b01c0 Mon Sep 17 00:00:00 2001 From: skiinder Date: Fri, 10 Apr 2026 19:28:20 +0800 Subject: [PATCH] =?UTF-8?q?feat(keyboard):=20=E6=B7=BB=E5=8A=A0=E8=93=9D?= =?UTF-8?q?=E7=89=99HID=E6=94=AF=E6=8C=81=E5=92=8C=E7=94=B5=E6=B1=A0?= =?UTF-8?q?=E7=8A=B6=E6=80=81=E4=BA=8B=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加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设置加载器以管理蓝牙配对信息 --- CMakeLists.txt | 5 + inc/events/bat_state_event.h | 27 ++ inc/events/hid_led_event.h | 3 + inc/events/set_protocol_event.h | 1 + inc/keyboard_core.h | 2 + inc/settings_loader_def.h | 15 + prj.conf | 63 +++- src/battery_module.c | 59 ++- src/ble_adv_ctrl_module.c | 115 ++++++ src/ble_adv_uuid16.c | 23 ++ src/ble_bas_module.c | 121 ++++++ src/ble_hid_module.c | 491 +++++++++++++++++++++++++ src/events/bat_state_event.c | 32 ++ src/events/hid_led_event.c | 20 +- src/events/hid_report_sent_event.c | 2 + src/events/hid_transport_state_event.c | 2 + src/events/hid_tx_report_event.c | 2 + src/events/set_protocol_event.c | 20 +- src/hid_flowctrl_module.c | 148 ++++++-- src/keyboard_core_module.c | 89 ++++- src/usb_hid_module.c | 6 +- 21 files changed, 1186 insertions(+), 60 deletions(-) create mode 100644 inc/events/bat_state_event.h create mode 100644 inc/settings_loader_def.h create mode 100644 src/ble_adv_ctrl_module.c create mode 100644 src/ble_adv_uuid16.c create mode 100644 src/ble_bas_module.c create mode 100644 src/ble_hid_module.c create mode 100644 src/events/bat_state_event.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 99ee416..69b8a66 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/inc/events/bat_state_event.h b/inc/events/bat_state_event.h new file mode 100644 index 0000000..ff2433b --- /dev/null +++ b/inc/events/bat_state_event.h @@ -0,0 +1,27 @@ +#ifndef BLINKY_BAT_STATE_EVENT_H_ +#define BLINKY_BAT_STATE_EVENT_H_ + +#include +#include + +#include +#include + +#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_ */ diff --git a/inc/events/hid_led_event.h b/inc/events/hid_led_event.h index bc30781..847a504 100644 --- a/inc/events/hid_led_event.h +++ b/inc/events/hid_led_event.h @@ -4,12 +4,15 @@ #include #include +#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; }; diff --git a/inc/events/set_protocol_event.h b/inc/events/set_protocol_event.h index 60064af..7f372c4 100644 --- a/inc/events/set_protocol_event.h +++ b/inc/events/set_protocol_event.h @@ -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; }; diff --git a/inc/keyboard_core.h b/inc/keyboard_core.h index 206154a..8dfda05 100644 --- a/inc/keyboard_core.h +++ b/inc/keyboard_core.h @@ -30,6 +30,8 @@ enum hid_queue_policy { enum hid_transport { HID_TRANSPORT_USB, + HID_TRANSPORT_BLE, + HID_TRANSPORT_COUNT, }; enum keyboard_consumer_control { diff --git a/inc/settings_loader_def.h b/inc/settings_loader_def.h new file mode 100644 index 0000000..37c63ed --- /dev/null +++ b/inc/settings_loader_def.h @@ -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 + +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)); +} diff --git a/prj.conf b/prj.conf index 80d1d51..286418a 100644 --- a/prj.conf +++ b/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 diff --git a/src/battery_module.c b/src/battery_module.c index ad9570f..f9e57e6 100644 --- a/src/battery_module.c +++ b/src/battery_module.c @@ -1,5 +1,6 @@ #include #include +#include #include @@ -16,11 +17,15 @@ #include #include +#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; diff --git a/src/ble_adv_ctrl_module.c b/src/ble_adv_ctrl_module.c new file mode 100644 index 0000000..c7c7367 --- /dev/null +++ b/src/ble_adv_ctrl_module.c @@ -0,0 +1,115 @@ +#include + +#include + +#define MODULE ble_adv_ctrl_module +#include +#include + +#include + +#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); diff --git a/src/ble_adv_uuid16.c b/src/ble_adv_uuid16.c new file mode 100644 index 0000000..c181f0c --- /dev/null +++ b/src/ble_adv_uuid16.c @@ -0,0 +1,23 @@ +#include + +#include + +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); diff --git a/src/ble_bas_module.c b/src/ble_bas_module.c new file mode 100644 index 0000000..36ba566 --- /dev/null +++ b/src/ble_bas_module.c @@ -0,0 +1,121 @@ +#include +#include + +#include + +#define MODULE ble_bas_module +#include + +#include +#include +#include + +#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); diff --git a/src/ble_hid_module.c b/src/ble_hid_module.c new file mode 100644 index 0000000..1a86ede --- /dev/null +++ b/src/ble_hid_module.c @@ -0,0 +1,491 @@ +#include +#include +#include +#include + +#include + +#define MODULE ble_hid_module +#include +#include + +#include +#include +#include + +#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); diff --git a/src/events/bat_state_event.c b/src/events/bat_state_event.c new file mode 100644 index 0000000..232d94f --- /dev/null +++ b/src/events/bat_state_event.c @@ -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)); diff --git a/src/events/hid_led_event.c b/src/events/hid_led_event.c index c30596a..73dc489 100644 --- a/src/events/hid_led_event.c +++ b/src/events/hid_led_event.c @@ -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, diff --git a/src/events/hid_report_sent_event.c b/src/events/hid_report_sent_event.c index bd5538a..c908f5a 100644 --- a/src/events/hid_report_sent_event.c +++ b/src/events/hid_report_sent_event.c @@ -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 "?"; } diff --git a/src/events/hid_transport_state_event.c b/src/events/hid_transport_state_event.c index 1546271..ce794eb 100644 --- a/src/events/hid_transport_state_event.c +++ b/src/events/hid_transport_state_event.c @@ -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 "?"; } diff --git a/src/events/hid_tx_report_event.c b/src/events/hid_tx_report_event.c index 0207e6c..06f8551 100644 --- a/src/events/hid_tx_report_event.c +++ b/src/events/hid_tx_report_event.c @@ -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 "?"; } diff --git a/src/events/set_protocol_event.c b/src/events/set_protocol_event.c index fc1bfc5..b4b8d4f 100644 --- a/src/events/set_protocol_event.c +++ b/src/events/set_protocol_event.c @@ -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, diff --git a/src/hid_flowctrl_module.c b/src/hid_flowctrl_module.c index 32f295b..79adfae 100644 --- a/src/hid_flowctrl_module.c +++ b/src/hid_flowctrl_module.c @@ -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; diff --git a/src/keyboard_core_module.c b/src/keyboard_core_module.c index 3987198..1c82589 100644 --- a/src/keyboard_core_module.c +++ b/src/keyboard_core_module.c @@ -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); + } } } diff --git a/src/usb_hid_module.c b/src/usb_hid_module.c index ab06bd5..40efd57 100644 --- a/src/usb_hid_module.c +++ b/src/usb_hid_module.c @@ -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); }