feat: 更新键盘固件的事件系统和模块配置

- 在CMakeLists.txt中添加hid_boot_event.c、keyboard_led_event.c和ble_slot_ctrl_module.c源文件
- 新增Kconfig配置项NEW_KBD_BLE_BOND_ENABLE用于启用应用特定的BLE绑定支持
- 修改prj.conf配置,禁用配对模式下的设备名称广播功能
- 重构电池状态事件结构,将charging和full布尔字段改为flags位域,并提供相应的访问函数
- 添加hid_boot_event事件类型,用于处理HID Boot协议输入报告
- 重命名keyboard_led_state_event为keyboard_led_event并改进LED状态处理逻辑
- 移除hid_protocol_event中的transport字段,简化协议事件处理
- 分离hid_report_event和hid_boot_event,明确区分Report和Boot协议报文处理
- 重构battery_module.c代码结构,改用上下文结构体管理电池模块状态
- 更新ble_battery_module.c使用新的电池状态事件访问接口
This commit is contained in:
2026-03-18 13:41:36 +08:00
parent 2a8b44d058
commit 2a389ef19b
25 changed files with 1237 additions and 484 deletions

View File

@@ -19,14 +19,16 @@ target_sources(app PRIVATE
src/main.c src/main.c
src/events/battery_status_event.c src/events/battery_status_event.c
src/events/config_event.c src/events/config_event.c
src/events/hid_boot_event.c
src/events/hid_protocol_event.c src/events/hid_protocol_event.c
src/events/hid_report_event.c src/events/hid_report_event.c
src/events/keyboard_led_state_event.c src/events/keyboard_led_event.c
src/events/mode_event.c src/events/mode_event.c
src/modules/battery_module.c src/modules/battery_module.c
src/modules/ble_adv_ctrl_module.c src/modules/ble_adv_ctrl_module.c
src/modules/ble_battery_module.c src/modules/ble_battery_module.c
src/modules/ble_bond_module.c src/modules/ble_bond_module.c
src/modules/ble_slot_ctrl_module.c
src/modules/keyboard_module.c src/modules/keyboard_module.c
src/modules/led_state_module.c src/modules/led_state_module.c
src/modules/mode_switch_module.c src/modules/mode_switch_module.c

17
Kconfig Normal file
View File

@@ -0,0 +1,17 @@
menu "new_kbd"
config NEW_KBD_BLE_BOND_ENABLE
bool "Enable app-specific BLE bond support"
default y
depends on BT_BONDABLE
depends on BT_SETTINGS
depends on CAF_SETTINGS_LOADER
depends on CAF_BLE_COMMON_EVENTS
select CAF_BLE_BOND_SUPPORTED
help
Inform CAF modules that the application provides its own BLE bond
implementation and advertising must wait for it to become ready.
endmenu
source "Kconfig.zephyr"

View File

@@ -37,6 +37,7 @@ CONFIG_CAF_SETTINGS_LOADER=y
CONFIG_BT_ADV_PROV_FLAGS=y CONFIG_BT_ADV_PROV_FLAGS=y
CONFIG_BT_ADV_PROV_GAP_APPEARANCE=y CONFIG_BT_ADV_PROV_GAP_APPEARANCE=y
CONFIG_BT_ADV_PROV_DEVICE_NAME=y CONFIG_BT_ADV_PROV_DEVICE_NAME=y
CONFIG_BT_ADV_PROV_DEVICE_NAME_PAIRING_MODE_ONLY=n
CONFIG_BT_ADV_PROV_SWIFT_PAIR=y CONFIG_BT_ADV_PROV_SWIFT_PAIR=y
CONFIG_BT_HIDS=y CONFIG_BT_HIDS=y

View File

@@ -5,9 +5,10 @@ static void log_battery_status_event(const struct app_event_header *aeh)
const struct battery_status_event *event = cast_battery_status_event(aeh); const struct battery_status_event *event = cast_battery_status_event(aeh);
APP_EVENT_MANAGER_LOG(aeh, APP_EVENT_MANAGER_LOG(aeh,
"charging=%u full=%u soc=%u", "flags=0x%02x charging=%u full=%u soc=%u",
event->charging, event->flags,
event->full, BATTERY_STATUS_IS_CHARGING(event->flags),
BATTERY_STATUS_IS_FULL(event->flags),
event->soc); event->soc);
} }
@@ -16,16 +17,14 @@ static void profile_battery_status_event(struct log_event_buf *buf,
{ {
const struct battery_status_event *event = cast_battery_status_event(aeh); const struct battery_status_event *event = cast_battery_status_event(aeh);
nrf_profiler_log_encode_uint8(buf, event->charging ? 1U : 0U); nrf_profiler_log_encode_uint8(buf, event->flags);
nrf_profiler_log_encode_uint8(buf, event->full ? 1U : 0U);
nrf_profiler_log_encode_uint8(buf, event->soc); nrf_profiler_log_encode_uint8(buf, event->soc);
} }
APP_EVENT_INFO_DEFINE(battery_status_event, APP_EVENT_INFO_DEFINE(battery_status_event,
ENCODE(NRF_PROFILER_ARG_U8, ENCODE(NRF_PROFILER_ARG_U8,
NRF_PROFILER_ARG_U8,
NRF_PROFILER_ARG_U8), NRF_PROFILER_ARG_U8),
ENCODE("charging", "full", "soc"), ENCODE("flags", "soc"),
profile_battery_status_event); profile_battery_status_event);
APP_EVENT_TYPE_DEFINE(battery_status_event, APP_EVENT_TYPE_DEFINE(battery_status_event,

View File

@@ -10,11 +10,52 @@
struct battery_status_event struct battery_status_event
{ {
struct app_event_header header; struct app_event_header header;
bool charging; uint8_t flags;
bool full;
uint8_t soc; uint8_t soc;
}; };
#define BATTERY_STATUS_FLAG_CHARGING (1U << 0)
#define BATTERY_STATUS_FLAG_FULL (1U << 1)
#define BATTERY_STATUS_IS_CHARGING(flags) (((flags) & BATTERY_STATUS_FLAG_CHARGING) != 0U)
#define BATTERY_STATUS_IS_FULL(flags) (((flags) & BATTERY_STATUS_FLAG_FULL) != 0U)
APP_EVENT_TYPE_DECLARE(battery_status_event); APP_EVENT_TYPE_DECLARE(battery_status_event);
static inline void battery_status_event_submit(bool charging, bool full, uint8_t soc)
{
struct battery_status_event *event = new_battery_status_event();
event->flags = 0U;
if (charging) {
event->flags |= BATTERY_STATUS_FLAG_CHARGING;
}
if (full) {
event->flags |= BATTERY_STATUS_FLAG_FULL;
}
event->soc = soc;
APP_EVENT_SUBMIT(event);
}
static inline uint8_t battery_status_event_get_flags(const struct battery_status_event *event)
{
return event->flags;
}
static inline bool battery_status_event_is_charging(const struct battery_status_event *event)
{
return BATTERY_STATUS_IS_CHARGING(event->flags);
}
static inline bool battery_status_event_is_full(const struct battery_status_event *event)
{
return BATTERY_STATUS_IS_FULL(event->flags);
}
static inline uint8_t battery_status_event_get_soc(const struct battery_status_event *event)
{
return event->soc;
}
#endif #endif

View File

@@ -0,0 +1,26 @@
#include "hid_boot_event.h"
static void log_hid_boot_event(const struct app_event_header *aeh)
{
const struct hid_boot_event *event = cast_hid_boot_event(aeh);
APP_EVENT_MANAGER_LOG(aeh, "payload_len=%u", event->dyndata.size);
}
static void profile_hid_boot_event(struct log_event_buf *buf,
const struct app_event_header *aeh)
{
const struct hid_boot_event *event = cast_hid_boot_event(aeh);
nrf_profiler_log_encode_uint16(buf, event->dyndata.size);
}
APP_EVENT_INFO_DEFINE(hid_boot_event,
ENCODE(NRF_PROFILER_ARG_U16),
ENCODE("len"),
profile_hid_boot_event);
APP_EVENT_TYPE_DEFINE(hid_boot_event,
log_hid_boot_event,
&hid_boot_event_info,
APP_EVENT_FLAGS_CREATE(APP_EVENT_TYPE_FLAGS_INIT_LOG_ENABLE));

View File

@@ -0,0 +1,45 @@
#ifndef HID_BOOT_EVENT_H__
#define HID_BOOT_EVENT_H__
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <app_event_manager.h>
#include <app_event_manager_profiler_tracer.h>
/*
* HID Boot 输入报告事件:
* - dyndata 仅包含 boot keyboard payload
* - 不包含 report_id消费方固定按 boot keyboard 语义处理。
*/
struct hid_boot_event {
struct app_event_header header;
struct event_dyndata dyndata;
};
APP_EVENT_TYPE_DYNDATA_DECLARE(hid_boot_event);
static inline void hid_boot_event_submit(const uint8_t *data, size_t size)
{
struct hid_boot_event *event = new_hid_boot_event(size);
if ((size > 0U) && (data != NULL)) {
memcpy(event->dyndata.data, data, size);
}
APP_EVENT_SUBMIT(event);
}
static inline const uint8_t *hid_boot_event_get_data(const struct hid_boot_event *event)
{
return event->dyndata.data;
}
static inline size_t hid_boot_event_get_size(const struct hid_boot_event *event)
{
return event->dyndata.size;
}
#endif /* HID_BOOT_EVENT_H__ */

View File

@@ -1,10 +1,5 @@
#include "hid_protocol_event.h" #include "hid_protocol_event.h"
static const char *const hid_transport_name[] = {
[HID_TRANSPORT_BLE] = "BLE",
[HID_TRANSPORT_USB] = "USB",
};
static const char *const hid_protocol_name[] = { static const char *const hid_protocol_name[] = {
[HID_PROTO_BOOT] = "BOOT", [HID_PROTO_BOOT] = "BOOT",
[HID_PROTO_REPORT] = "REPORT", [HID_PROTO_REPORT] = "REPORT",
@@ -14,11 +9,9 @@ static void log_hid_protocol_event(const struct app_event_header *aeh)
{ {
const struct hid_protocol_event *event = cast_hid_protocol_event(aeh); const struct hid_protocol_event *event = cast_hid_protocol_event(aeh);
__ASSERT_NO_MSG(event->transport < ARRAY_SIZE(hid_transport_name));
__ASSERT_NO_MSG(event->protocol < ARRAY_SIZE(hid_protocol_name)); __ASSERT_NO_MSG(event->protocol < ARRAY_SIZE(hid_protocol_name));
APP_EVENT_MANAGER_LOG(aeh, "transport=%s protocol=%s", APP_EVENT_MANAGER_LOG(aeh, "protocol=%s",
hid_transport_name[event->transport],
hid_protocol_name[event->protocol]); hid_protocol_name[event->protocol]);
} }
@@ -27,13 +20,12 @@ static void profile_hid_protocol_event(struct log_event_buf *buf,
{ {
const struct hid_protocol_event *event = cast_hid_protocol_event(aeh); const struct hid_protocol_event *event = cast_hid_protocol_event(aeh);
nrf_profiler_log_encode_uint8(buf, (uint8_t)event->transport);
nrf_profiler_log_encode_uint8(buf, (uint8_t)event->protocol); nrf_profiler_log_encode_uint8(buf, (uint8_t)event->protocol);
} }
APP_EVENT_INFO_DEFINE(hid_protocol_event, APP_EVENT_INFO_DEFINE(hid_protocol_event,
ENCODE(NRF_PROFILER_ARG_U8, NRF_PROFILER_ARG_U8), ENCODE(NRF_PROFILER_ARG_U8),
ENCODE("transport", "protocol"), ENCODE("protocol"),
profile_hid_protocol_event); profile_hid_protocol_event);
APP_EVENT_TYPE_DEFINE(hid_protocol_event, APP_EVENT_TYPE_DEFINE(hid_protocol_event,

View File

@@ -1,30 +1,49 @@
#ifndef HID_PROTOCOL_EVENT_H__ #ifndef HID_PROTOCOL_EVENT_H__
#define HID_PROTOCOL_EVENT_H__ #define HID_PROTOCOL_EVENT_H__
#include <stdbool.h>
#include <app_event_manager.h> #include <app_event_manager.h>
#include <app_event_manager_profiler_tracer.h> #include <app_event_manager_profiler_tracer.h>
enum hid_transport_type {
HID_TRANSPORT_BLE = 0,
HID_TRANSPORT_USB,
};
enum hid_protocol_type { enum hid_protocol_type {
HID_PROTO_BOOT = 0, HID_PROTO_BOOT = 0,
HID_PROTO_REPORT, HID_PROTO_REPORT,
}; };
/* /*
* HID 传输层在收到主机 set_protocol 请求后上报该事件 * HID 传输层在收到主机 set_protocol 请求后上报该事件
* keyboard_module 会依据当前 active 传输的协议格式打包 hid_report_event。
*/ */
struct hid_protocol_event { struct hid_protocol_event {
struct app_event_header header; struct app_event_header header;
enum hid_transport_type transport;
enum hid_protocol_type protocol; enum hid_protocol_type protocol;
}; };
APP_EVENT_TYPE_DECLARE(hid_protocol_event); APP_EVENT_TYPE_DECLARE(hid_protocol_event);
static inline void hid_protocol_event_submit(enum hid_protocol_type protocol)
{
struct hid_protocol_event *event = new_hid_protocol_event();
event->protocol = protocol;
APP_EVENT_SUBMIT(event);
}
static inline enum hid_protocol_type hid_protocol_event_get_protocol(
const struct hid_protocol_event *event)
{
return event->protocol;
}
static inline bool hid_protocol_event_is_boot_protocol(const struct hid_protocol_event *event)
{
return event->protocol == HID_PROTO_BOOT;
}
static inline bool hid_protocol_event_is_report_protocol(const struct hid_protocol_event *event)
{
return event->protocol == HID_PROTO_REPORT;
}
#endif /* HID_PROTOCOL_EVENT_H__ */ #endif /* HID_PROTOCOL_EVENT_H__ */

View File

@@ -1,26 +1,17 @@
#include "hid_report_event.h" #include "hid_report_event.h"
static const char *const hid_protocol_name[] = {
[HID_PROTO_BOOT] = "BOOT",
[HID_PROTO_REPORT] = "REPORT",
};
static void log_hid_report_event(const struct app_event_header *aeh) static void log_hid_report_event(const struct app_event_header *aeh)
{ {
const struct hid_report_event *event = cast_hid_report_event(aeh); const struct hid_report_event *event = cast_hid_report_event(aeh);
uint8_t report_id = 0x00; uint8_t report_id = 0x00;
uint16_t payload_len = event->dyndata.size; uint16_t payload_len = 0U;
__ASSERT_NO_MSG(event->protocol < ARRAY_SIZE(hid_protocol_name)); if (event->dyndata.size >= 1U) {
if (event->protocol == HID_PROTO_REPORT) {
__ASSERT_NO_MSG(event->dyndata.size >= 1U);
report_id = event->dyndata.data[0]; report_id = event->dyndata.data[0];
payload_len = event->dyndata.size - 1U; payload_len = event->dyndata.size - 1U;
} }
APP_EVENT_MANAGER_LOG(aeh, "protocol=%s report_id=0x%02x payload_len=%u", APP_EVENT_MANAGER_LOG(aeh, "report_id=0x%02x payload_len=%u",
hid_protocol_name[event->protocol],
report_id, report_id,
payload_len); payload_len);
} }
@@ -31,20 +22,18 @@ static void profile_hid_report_event(struct log_event_buf *buf,
const struct hid_report_event *event = cast_hid_report_event(aeh); const struct hid_report_event *event = cast_hid_report_event(aeh);
uint8_t report_id = 0x00; uint8_t report_id = 0x00;
if ((event->protocol == HID_PROTO_REPORT) && (event->dyndata.size >= 1U)) { if (event->dyndata.size >= 1U) {
report_id = event->dyndata.data[0]; report_id = event->dyndata.data[0];
} }
nrf_profiler_log_encode_uint8(buf, (uint8_t)event->protocol);
nrf_profiler_log_encode_uint8(buf, report_id); nrf_profiler_log_encode_uint8(buf, report_id);
nrf_profiler_log_encode_uint16(buf, event->dyndata.size); nrf_profiler_log_encode_uint16(buf, event->dyndata.size);
} }
APP_EVENT_INFO_DEFINE(hid_report_event, APP_EVENT_INFO_DEFINE(hid_report_event,
ENCODE(NRF_PROFILER_ARG_U8, ENCODE(NRF_PROFILER_ARG_U8,
NRF_PROFILER_ARG_U8,
NRF_PROFILER_ARG_U16), NRF_PROFILER_ARG_U16),
ENCODE("protocol", "report_id", "len"), ENCODE("report_id", "len"),
profile_hid_report_event); profile_hid_report_event);
APP_EVENT_TYPE_DEFINE(hid_report_event, APP_EVENT_TYPE_DEFINE(hid_report_event,

View File

@@ -1,24 +1,45 @@
#ifndef HID_REPORT_EVENT_H__ #ifndef HID_REPORT_EVENT_H__
#define HID_REPORT_EVENT_H__ #define HID_REPORT_EVENT_H__
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <app_event_manager.h> #include <app_event_manager.h>
#include <app_event_manager_profiler_tracer.h> #include <app_event_manager_profiler_tracer.h>
#include "hid_protocol_event.h"
/* /*
* HID 输入报告统一事件: * HID Report 输入报告事件:
* - protocol 指示当前 dyndata 编码规则 * - dyndata 编码为 [report_id | payload]
* - 当 protocol=HID_PROTO_REPORTdyndata[0]=report_iddyndata[1..]=payload * - 仅承载 Report protocol 报文。
* - 当 protocol=HID_PROTO_BOOTdyndata 仅包含 boot payload不含 report_id
*/ */
struct hid_report_event { struct hid_report_event {
struct app_event_header header; struct app_event_header header;
enum hid_protocol_type protocol;
struct event_dyndata dyndata; struct event_dyndata dyndata;
}; };
APP_EVENT_TYPE_DYNDATA_DECLARE(hid_report_event); APP_EVENT_TYPE_DYNDATA_DECLARE(hid_report_event);
static inline void hid_report_event_submit(const uint8_t *data, size_t size)
{
struct hid_report_event *event = new_hid_report_event(size);
if ((size > 0U) && (data != NULL)) {
memcpy(event->dyndata.data, data, size);
}
APP_EVENT_SUBMIT(event);
}
static inline const uint8_t *hid_report_event_get_data(const struct hid_report_event *event)
{
return event->dyndata.data;
}
static inline size_t hid_report_event_get_size(const struct hid_report_event *event)
{
return event->dyndata.size;
}
#endif /* HID_REPORT_EVENT_H__ */ #endif /* HID_REPORT_EVENT_H__ */

View File

@@ -0,0 +1,28 @@
#include "keyboard_led_event.h"
static void log_keyboard_led_event(const struct app_event_header *aeh)
{
const struct keyboard_led_event *event =
cast_keyboard_led_event(aeh);
APP_EVENT_MANAGER_LOG(aeh, "mask=0x%02x", event->led_mask);
}
static void profile_keyboard_led_event(struct log_event_buf *buf,
const struct app_event_header *aeh)
{
const struct keyboard_led_event *event =
cast_keyboard_led_event(aeh);
nrf_profiler_log_encode_uint8(buf, event->led_mask);
}
APP_EVENT_INFO_DEFINE(keyboard_led_event,
ENCODE(NRF_PROFILER_ARG_U8),
ENCODE("led_mask"),
profile_keyboard_led_event);
APP_EVENT_TYPE_DEFINE(keyboard_led_event,
log_keyboard_led_event,
&keyboard_led_event_info,
APP_EVENT_FLAGS_CREATE(APP_EVENT_TYPE_FLAGS_INIT_LOG_ENABLE));

View File

@@ -0,0 +1,73 @@
#ifndef KEYBOARD_LED_EVENT_H__
#define KEYBOARD_LED_EVENT_H__
#include <stdbool.h>
#include <stdint.h>
#include <app_event_manager.h>
#include <app_event_manager_profiler_tracer.h>
/*
* 键盘 LED 事件:
* - 由 USB/BLE HID 接收主机输出报告后上报;
* - 仅保留主机下发的原始 LED mask消费方按需解码。
*/
struct keyboard_led_event {
struct app_event_header header;
uint8_t led_mask;
};
#define KEYBOARD_LED_MASK_NUM_LOCK (1U << 0)
#define KEYBOARD_LED_MASK_CAPS_LOCK (1U << 1)
#define KEYBOARD_LED_MASK_SCROLL_LOCK (1U << 2)
#define KEYBOARD_LED_MASK_COMPOSE (1U << 3)
#define KEYBOARD_LED_MASK_KANA (1U << 4)
#define KEYBOARD_LED_NUM_LOCK(mask) (((mask) & KEYBOARD_LED_MASK_NUM_LOCK) != 0U)
#define KEYBOARD_LED_CAPS_LOCK(mask) (((mask) & KEYBOARD_LED_MASK_CAPS_LOCK) != 0U)
#define KEYBOARD_LED_SCROLL_LOCK(mask) (((mask) & KEYBOARD_LED_MASK_SCROLL_LOCK) != 0U)
#define KEYBOARD_LED_COMPOSE(mask) (((mask) & KEYBOARD_LED_MASK_COMPOSE) != 0U)
#define KEYBOARD_LED_KANA(mask) (((mask) & KEYBOARD_LED_MASK_KANA) != 0U)
APP_EVENT_TYPE_DECLARE(keyboard_led_event);
static inline void keyboard_led_event_submit(uint8_t led_mask)
{
struct keyboard_led_event *event = new_keyboard_led_event();
event->led_mask = led_mask;
APP_EVENT_SUBMIT(event);
}
static inline uint8_t keyboard_led_event_get_mask(const struct keyboard_led_event *event)
{
return event->led_mask;
}
static inline bool keyboard_led_event_is_num_lock_on(const struct keyboard_led_event *event)
{
return KEYBOARD_LED_NUM_LOCK(event->led_mask);
}
static inline bool keyboard_led_event_is_caps_lock_on(const struct keyboard_led_event *event)
{
return KEYBOARD_LED_CAPS_LOCK(event->led_mask);
}
static inline bool keyboard_led_event_is_scroll_lock_on(const struct keyboard_led_event *event)
{
return KEYBOARD_LED_SCROLL_LOCK(event->led_mask);
}
static inline bool keyboard_led_event_is_compose_on(const struct keyboard_led_event *event)
{
return KEYBOARD_LED_COMPOSE(event->led_mask);
}
static inline bool keyboard_led_event_is_kana_on(const struct keyboard_led_event *event)
{
return KEYBOARD_LED_KANA(event->led_mask);
}
#endif /* KEYBOARD_LED_EVENT_H__ */

View File

@@ -1,31 +0,0 @@
#include "keyboard_led_state_event.h"
static void log_keyboard_led_state_event(const struct app_event_header *aeh)
{
const struct keyboard_led_state_event *event =
cast_keyboard_led_state_event(aeh);
APP_EVENT_MANAGER_LOG(aeh, "mask=0x%02x num_lock=%u",
event->led_mask, event->num_lock);
}
static void profile_keyboard_led_state_event(struct log_event_buf *buf,
const struct app_event_header *aeh)
{
const struct keyboard_led_state_event *event =
cast_keyboard_led_state_event(aeh);
nrf_profiler_log_encode_uint8(buf, event->led_mask);
nrf_profiler_log_encode_uint8(buf, event->num_lock);
}
APP_EVENT_INFO_DEFINE(keyboard_led_state_event,
ENCODE(NRF_PROFILER_ARG_U8,
NRF_PROFILER_ARG_U8),
ENCODE("led_mask", "num_lock"),
profile_keyboard_led_state_event);
APP_EVENT_TYPE_DEFINE(keyboard_led_state_event,
log_keyboard_led_state_event,
&keyboard_led_state_event_info,
APP_EVENT_FLAGS_CREATE(APP_EVENT_TYPE_FLAGS_INIT_LOG_ENABLE));

View File

@@ -1,24 +0,0 @@
#ifndef KEYBOARD_LED_STATE_EVENT_H__
#define KEYBOARD_LED_STATE_EVENT_H__
#include <stdbool.h>
#include <stdint.h>
#include <app_event_manager.h>
#include <app_event_manager_profiler_tracer.h>
/*
* 键盘 LED 状态事件:
* - 由 USB/BLE HID 接收主机输出报告后上报;
* - 当前仅消费 Num Lock 位,保留 led_mask 便于后续扩展 Caps/Scroll。
*/
struct keyboard_led_state_event {
struct app_event_header header;
uint8_t led_mask;
bool num_lock;
};
APP_EVENT_TYPE_DECLARE(keyboard_led_state_event);
#endif /* KEYBOARD_LED_STATE_EVENT_H__ */

View File

@@ -1,6 +1,8 @@
#ifndef MODE_EVENT_H #ifndef MODE_EVENT_H
#define MODE_EVENT_H #define MODE_EVENT_H
#include <stdbool.h>
#include <app_event_manager.h> #include <app_event_manager.h>
#include <app_event_manager_profiler_tracer.h> #include <app_event_manager_profiler_tracer.h>
@@ -20,4 +22,32 @@ struct mode_event
APP_EVENT_TYPE_DECLARE(mode_event); APP_EVENT_TYPE_DECLARE(mode_event);
static inline void mode_event_submit(mode_type_t mode)
{
struct mode_event *event = new_mode_event();
event->mode_type = mode;
APP_EVENT_SUBMIT(event);
}
static inline mode_type_t mode_event_get_mode(const struct mode_event *event)
{
return event->mode_type;
}
static inline bool mode_event_is_usb(const struct mode_event *event)
{
return event->mode_type == MODE_TYPE_USB;
}
static inline bool mode_event_is_ble(const struct mode_event *event)
{
return event->mode_type == MODE_TYPE_BLE;
}
static inline bool mode_event_is_2g4(const struct mode_event *event)
{
return event->mode_type == MODE_TYPE_2G4;
}
#endif #endif

View File

@@ -54,16 +54,28 @@ struct battery_status
uint8_t soc; uint8_t soc;
}; };
static struct k_work_delayable battery_sample_work; struct battery_filter_state
static int16_t battery_adc_sample_buffer; {
static atomic_t active; int32_t window[BATTERY_MV_WINDOW_SIZE];
static struct battery_status last_status; int64_t sum;
static bool has_last_status; size_t count;
static int32_t battery_mv_window[BATTERY_MV_WINDOW_SIZE]; size_t index;
static int64_t battery_mv_sum; };
static size_t battery_mv_count;
static size_t battery_mv_index; struct battery_ctx
static enum power_manager_level pm_restrict_level = POWER_MANAGER_LEVEL_MAX; {
struct k_work_delayable sample_work;
int16_t adc_sample_buffer;
atomic_t active;
struct battery_status last_status;
bool has_last_status;
struct battery_filter_state filter;
enum power_manager_level pm_restrict_level;
};
static struct battery_ctx battery = {
.pm_restrict_level = POWER_MANAGER_LEVEL_MAX,
};
static void battery_module_resume(void); static void battery_module_resume(void);
@@ -92,8 +104,8 @@ static int adc_sample_once_mv(int32_t *mv)
return err; return err;
} }
sequence.buffer = &battery_adc_sample_buffer; sequence.buffer = &battery.adc_sample_buffer;
sequence.buffer_size = sizeof(battery_adc_sample_buffer); sequence.buffer_size = sizeof(battery.adc_sample_buffer);
err = adc_read_dt(&battery_adc, &sequence); err = adc_read_dt(&battery_adc, &sequence);
if (err) if (err)
@@ -102,12 +114,12 @@ static int adc_sample_once_mv(int32_t *mv)
return err; return err;
} }
*mv = battery_adc_sample_buffer; *mv = battery.adc_sample_buffer;
err = adc_raw_to_millivolts_dt(&battery_adc, mv); err = adc_raw_to_millivolts_dt(&battery_adc, mv);
if (err) if (err)
{ {
LOG_WRN("adc_raw_to_millivolts_dt failed (err=%d raw=%d)", LOG_WRN("adc_raw_to_millivolts_dt failed (err=%d raw=%d)",
err, battery_adc_sample_buffer); err, battery.adc_sample_buffer);
} }
return err; return err;
@@ -134,21 +146,21 @@ static int read_battery_mv(int32_t *mv)
* 使用固定窗口平均抑制采样抖动。 * 使用固定窗口平均抑制采样抖动。
* 窗口未填满前按当前样本数求平均,填满后使用环形缓冲滚动更新。 * 窗口未填满前按当前样本数求平均,填满后使用环形缓冲滚动更新。
*/ */
if (battery_mv_count < BATTERY_MV_WINDOW_SIZE) if (battery.filter.count < BATTERY_MV_WINDOW_SIZE)
{ {
battery_mv_window[battery_mv_index] = battery_mv; battery.filter.window[battery.filter.index] = battery_mv;
battery_mv_sum += battery_mv; battery.filter.sum += battery_mv;
battery_mv_count++; battery.filter.count++;
} }
else else
{ {
battery_mv_sum -= battery_mv_window[battery_mv_index]; battery.filter.sum -= battery.filter.window[battery.filter.index];
battery_mv_window[battery_mv_index] = battery_mv; battery.filter.window[battery.filter.index] = battery_mv;
battery_mv_sum += battery_mv; battery.filter.sum += battery_mv;
} }
battery_mv_index = (battery_mv_index + 1U) % BATTERY_MV_WINDOW_SIZE; battery.filter.index = (battery.filter.index + 1U) % BATTERY_MV_WINDOW_SIZE;
*mv = (int32_t)(battery_mv_sum / (int64_t)battery_mv_count); *mv = (int32_t)(battery.filter.sum / (int64_t)battery.filter.count);
return 0; return 0;
} }
@@ -183,13 +195,15 @@ static int read_battery_status(struct battery_status *status)
static void publish_battery_status_event(const struct battery_status *status) static void publish_battery_status_event(const struct battery_status *status)
{ {
struct battery_status_event *event = new_battery_status_event(); battery_status_event_submit(status->charging, status->full, status->soc);
}
event->charging = status->charging; static bool battery_status_changed(const struct battery_status *lhs,
event->full = status->full; const struct battery_status *rhs)
event->soc = status->soc; {
return (lhs->charging != rhs->charging) ||
APP_EVENT_SUBMIT(event); (lhs->full != rhs->full) ||
(lhs->soc != rhs->soc);
} }
/* /*
@@ -202,18 +216,33 @@ static void update_power_restrict_by_charging(bool charging)
enum power_manager_level target = charging ? enum power_manager_level target = charging ?
POWER_MANAGER_LEVEL_ALIVE : POWER_MANAGER_LEVEL_SUSPENDED; POWER_MANAGER_LEVEL_ALIVE : POWER_MANAGER_LEVEL_SUSPENDED;
if (pm_restrict_level == target) if (battery.pm_restrict_level == target)
return; return;
pm_restrict_level = target; battery.pm_restrict_level = target;
power_manager_restrict(MODULE_IDX(MODULE), target); power_manager_restrict(MODULE_IDX(MODULE), target);
} }
static void battery_sampling_set_enabled(bool enable)
{
atomic_set(&battery.active, enable);
(void)gpio_pin_set_dt(&battery_en_gpio, enable ? GPIO_OUTPUT_ACTIVE : GPIO_OUTPUT_INACTIVE);
if (enable)
{
k_work_reschedule(&battery.sample_work, K_NO_WAIT);
}
else
{
(void)k_work_cancel_delayable(&battery.sample_work);
}
}
static void battery_sample_fn(struct k_work *work) static void battery_sample_fn(struct k_work *work)
{ {
ARG_UNUSED(work); ARG_UNUSED(work);
if (!atomic_get(&active)) if (!atomic_get(&battery.active))
{ {
return; return;
} }
@@ -232,18 +261,16 @@ static void battery_sample_fn(struct k_work *work)
* 仅在状态发生变化时上报,避免重复事件淹没总线。 * 仅在状态发生变化时上报,避免重复事件淹没总线。
* 变化条件充电标志、满电标志、SOC 任意一个变化。 * 变化条件充电标志、满电标志、SOC 任意一个变化。
*/ */
if (!has_last_status || if (!battery.has_last_status ||
(sampled.charging != last_status.charging) || battery_status_changed(&sampled, &battery.last_status))
(sampled.full != last_status.full) ||
(sampled.soc != last_status.soc))
{ {
last_status = sampled; battery.last_status = sampled;
has_last_status = true; battery.has_last_status = true;
publish_battery_status_event(&sampled); publish_battery_status_event(&sampled);
} }
out_reschedule: out_reschedule:
k_work_reschedule(&battery_sample_work, K_MSEC(BATTERY_SAMPLE_INTERVAL_MS)); k_work_reschedule(&battery.sample_work, K_MSEC(BATTERY_SAMPLE_INTERVAL_MS));
} }
static int battery_module_init(void) static int battery_module_init(void)
@@ -283,44 +310,38 @@ static int battery_module_init(void)
/* 默认非充电态允许进入 SUSPENDED但禁止进入 OFF。 */ /* 默认非充电态允许进入 SUSPENDED但禁止进入 OFF。 */
update_power_restrict_by_charging(false); update_power_restrict_by_charging(false);
k_work_init_delayable(&battery_sample_work, battery_sample_fn); k_work_init_delayable(&battery.sample_work, battery_sample_fn);
has_last_status = false; battery.has_last_status = false;
battery_mv_sum = 0; battery.filter.sum = 0;
battery_mv_count = 0; battery.filter.count = 0;
battery_mv_index = 0; battery.filter.index = 0;
atomic_set(&active, false); atomic_set(&battery.active, false);
atomic_set(&active, true); battery_sampling_set_enabled(true);
(void)gpio_pin_set_dt(&battery_en_gpio, 1);
k_work_reschedule(&battery_sample_work, K_NO_WAIT);
return 0; return 0;
} }
static void battery_module_suspend(void) static void battery_module_suspend(void)
{ {
if (!atomic_get(&active)) if (!atomic_get(&battery.active))
{ {
/* 已经处于挂起态,避免重复上报 STANDBY 造成 power_down 循环。 */ /* 已经处于挂起态,避免重复上报 STANDBY 造成 power_down 循环。 */
return; return;
} }
atomic_set(&active, false); battery_sampling_set_enabled(false);
(void)k_work_cancel_delayable(&battery_sample_work);
(void)gpio_pin_set_dt(&battery_en_gpio, 0);
module_set_state(MODULE_STATE_STANDBY); module_set_state(MODULE_STATE_STANDBY);
} }
static void battery_module_resume(void) static void battery_module_resume(void)
{ {
if (atomic_get(&active)) if (atomic_get(&battery.active))
{ {
return; return;
} }
atomic_set(&active, true); battery_sampling_set_enabled(true);
(void)gpio_pin_set_dt(&battery_en_gpio, 1);
k_work_reschedule(&battery_sample_work, K_NO_WAIT);
module_set_state(MODULE_STATE_READY); module_set_state(MODULE_STATE_READY);
} }

View File

@@ -45,7 +45,7 @@ BT_GATT_SERVICE_DEFINE(ble_battery_svc,
static bool handle_battery_status_event(const struct battery_status_event *event) static bool handle_battery_status_event(const struct battery_status_event *event)
{ {
battery_level = event->soc; battery_level = battery_status_event_get_soc(event);
if (!notify_enabled) { if (!notify_enabled) {
return false; return false;

View File

@@ -1,4 +1,5 @@
#include <zephyr/bluetooth/bluetooth.h> #include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/settings/settings.h> #include <zephyr/settings/settings.h>
#include <errno.h> #include <errno.h>
@@ -28,33 +29,45 @@ enum ble_bond_cfg_opt {
#define PEER_ID_KEY "peer_id" #define PEER_ID_KEY "peer_id"
#define BT_LUT_KEY "bt_lut" #define BT_LUT_KEY "bt_lut"
enum state { enum ble_bond_state {
STATE_DISABLED, BLE_BOND_STATE_DISABLED,
STATE_IDLE, BLE_BOND_STATE_IDLE,
STATE_STANDBY, BLE_BOND_STATE_STANDBY,
}; };
BUILD_ASSERT(CONFIG_BT_ID_MAX >= 2, "Need at least one resettable identity"); BUILD_ASSERT(CONFIG_BT_ID_MAX >= 2, "Need at least one resettable identity");
#define APP_PEER_COUNT (CONFIG_BT_ID_MAX - 1) #define APP_PEER_COUNT (CONFIG_BT_ID_MAX - 1)
#define BLE_BOND_SLOT_COUNT 3
static enum state state = STATE_DISABLED; BUILD_ASSERT(BLE_BOND_SLOT_COUNT <= APP_PEER_COUNT,
"BLE slot count exceeds available Bluetooth identities");
/* app peer id -> bt stack id mapping (default identity 0 is not used). */ struct ble_bond_storage {
static uint8_t bt_stack_id_lut[APP_PEER_COUNT]; uint8_t bt_stack_id_lut[APP_PEER_COUNT];
static bool bt_stack_id_lut_valid; bool bt_stack_id_lut_valid;
uint8_t cur_peer_id;
bool cur_peer_id_valid;
};
static uint8_t cur_ble_peer_id; struct ble_bond_ctx {
static bool cur_peer_id_valid; enum ble_bond_state state;
struct ble_bond_storage storage;
bool auto_switch_in_progress;
};
static const char *state_name(enum state s) static struct ble_bond_ctx bond = {
.state = BLE_BOND_STATE_DISABLED,
};
static const char *state_name(enum ble_bond_state s)
{ {
switch (s) { switch (s) {
case STATE_DISABLED: case BLE_BOND_STATE_DISABLED:
return "DISABLED"; return "DISABLED";
case STATE_IDLE: case BLE_BOND_STATE_IDLE:
return "IDLE"; return "IDLE";
case STATE_STANDBY: case BLE_BOND_STATE_STANDBY:
return "STANDBY"; return "STANDBY";
default: default:
return "UNKNOWN"; return "UNKNOWN";
@@ -63,8 +76,8 @@ static const char *state_name(enum state s)
static uint8_t get_bt_stack_peer_id(uint8_t app_id) static uint8_t get_bt_stack_peer_id(uint8_t app_id)
{ {
__ASSERT_NO_MSG(app_id < APP_PEER_COUNT); __ASSERT_NO_MSG(app_id < BLE_BOND_SLOT_COUNT);
return bt_stack_id_lut[app_id]; return bond.storage.bt_stack_id_lut[app_id];
} }
static int store_peer_id(uint8_t peer_id) static int store_peer_id(uint8_t peer_id)
@@ -78,7 +91,9 @@ static int store_bt_stack_id_lut(void)
{ {
char key[] = MODULE_NAME "/" BT_LUT_KEY; char key[] = MODULE_NAME "/" BT_LUT_KEY;
return settings_save_one(key, bt_stack_id_lut, sizeof(bt_stack_id_lut)); return settings_save_one(key,
bond.storage.bt_stack_id_lut,
sizeof(bond.storage.bt_stack_id_lut));
} }
static void submit_peer_op_event(enum peer_operation op, uint8_t app_id) static void submit_peer_op_event(enum peer_operation op, uint8_t app_id)
@@ -93,31 +108,31 @@ static void submit_peer_op_event(enum peer_operation op, uint8_t app_id)
static void init_bt_stack_id_lut(void) static void init_bt_stack_id_lut(void)
{ {
for (size_t i = 0; i < ARRAY_SIZE(bt_stack_id_lut); i++) { for (size_t i = 0; i < ARRAY_SIZE(bond.storage.bt_stack_id_lut); i++) {
/* Keep id 0 (BT_ID_DEFAULT) untouched for safe reset/unpair flow. */ /* Keep id 0 (BT_ID_DEFAULT) untouched for safe reset/unpair flow. */
bt_stack_id_lut[i] = i + 1; bond.storage.bt_stack_id_lut[i] = i + 1;
} }
} }
static bool storage_data_is_valid(void) static bool storage_data_is_valid(void)
{ {
if (!cur_peer_id_valid || !bt_stack_id_lut_valid) { if (!bond.storage.cur_peer_id_valid || !bond.storage.bt_stack_id_lut_valid) {
LOG_WRN("Stored data invalid: peer_valid=%d lut_valid=%d", LOG_WRN("Stored data invalid: peer_valid=%d lut_valid=%d",
cur_peer_id_valid, bt_stack_id_lut_valid); bond.storage.cur_peer_id_valid, bond.storage.bt_stack_id_lut_valid);
return false; return false;
} }
if (cur_ble_peer_id >= APP_PEER_COUNT) { if (bond.storage.cur_peer_id >= BLE_BOND_SLOT_COUNT) {
LOG_WRN("Stored peer id out of range: peer_id=%u max=%u", LOG_WRN("Stored peer id out of range: peer_id=%u max=%u",
cur_ble_peer_id, APP_PEER_COUNT - 1); bond.storage.cur_peer_id, BLE_BOND_SLOT_COUNT - 1);
return false; return false;
} }
for (size_t i = 0; i < ARRAY_SIZE(bt_stack_id_lut); i++) { for (size_t i = 0; i < ARRAY_SIZE(bond.storage.bt_stack_id_lut); i++) {
if ((bt_stack_id_lut[i] == BT_ID_DEFAULT) || if ((bond.storage.bt_stack_id_lut[i] == BT_ID_DEFAULT) ||
(bt_stack_id_lut[i] >= CONFIG_BT_ID_MAX)) { (bond.storage.bt_stack_id_lut[i] >= CONFIG_BT_ID_MAX)) {
LOG_WRN("Stored LUT invalid at idx=%u value=%u", LOG_WRN("Stored LUT invalid at idx=%u value=%u",
(uint32_t)i, bt_stack_id_lut[i]); (uint32_t)i, bond.storage.bt_stack_id_lut[i]);
return false; return false;
} }
} }
@@ -131,29 +146,31 @@ static int settings_set(const char *key, size_t len_rd,
ssize_t rc; ssize_t rc;
if (!strcmp(key, PEER_ID_KEY)) { if (!strcmp(key, PEER_ID_KEY)) {
if (len_rd != sizeof(cur_ble_peer_id)) { if (len_rd != sizeof(bond.storage.cur_peer_id)) {
LOG_WRN("Settings '%s' size mismatch: got=%u expect=%u", LOG_WRN("Settings '%s' size mismatch: got=%u expect=%u",
PEER_ID_KEY, (uint32_t)len_rd, sizeof(cur_ble_peer_id)); PEER_ID_KEY, (uint32_t)len_rd, sizeof(bond.storage.cur_peer_id));
cur_peer_id_valid = false; bond.storage.cur_peer_id_valid = false;
return 0; return 0;
} }
rc = read_cb(cb_arg, &cur_ble_peer_id, sizeof(cur_ble_peer_id)); rc = read_cb(cb_arg, &bond.storage.cur_peer_id, sizeof(bond.storage.cur_peer_id));
cur_peer_id_valid = (rc == sizeof(cur_ble_peer_id)); bond.storage.cur_peer_id_valid = (rc == sizeof(bond.storage.cur_peer_id));
if (!cur_peer_id_valid) { if (!bond.storage.cur_peer_id_valid) {
LOG_WRN("Settings '%s' read failed: rc=%d", PEER_ID_KEY, (int)rc); LOG_WRN("Settings '%s' read failed: rc=%d", PEER_ID_KEY, (int)rc);
} }
} else if (!strcmp(key, BT_LUT_KEY)) { } else if (!strcmp(key, BT_LUT_KEY)) {
if (len_rd != sizeof(bt_stack_id_lut)) { if (len_rd != sizeof(bond.storage.bt_stack_id_lut)) {
LOG_WRN("Settings '%s' size mismatch: got=%u expect=%u", LOG_WRN("Settings '%s' size mismatch: got=%u expect=%u",
BT_LUT_KEY, (uint32_t)len_rd, sizeof(bt_stack_id_lut)); BT_LUT_KEY, (uint32_t)len_rd, sizeof(bond.storage.bt_stack_id_lut));
bt_stack_id_lut_valid = false; bond.storage.bt_stack_id_lut_valid = false;
return 0; return 0;
} }
rc = read_cb(cb_arg, bt_stack_id_lut, sizeof(bt_stack_id_lut)); rc = read_cb(cb_arg,
bt_stack_id_lut_valid = (rc == sizeof(bt_stack_id_lut)); bond.storage.bt_stack_id_lut,
if (!bt_stack_id_lut_valid) { sizeof(bond.storage.bt_stack_id_lut));
bond.storage.bt_stack_id_lut_valid = (rc == sizeof(bond.storage.bt_stack_id_lut));
if (!bond.storage.bt_stack_id_lut_valid) {
LOG_WRN("Settings '%s' read failed: rc=%d", BT_LUT_KEY, (int)rc); LOG_WRN("Settings '%s' read failed: rc=%d", BT_LUT_KEY, (int)rc);
} }
} }
@@ -183,6 +200,164 @@ static int load_identities(void)
return 0; return 0;
} }
static void disconnect_le_conn_cb(struct bt_conn *conn, void *user_data)
{
(void)user_data;
int err = bt_conn_disconnect(conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN);
if (!err) {
LOG_INF("Disconnect LE peer for slot switch");
} else if (err == -ENOTCONN) {
LOG_INF("LE peer already disconnected during slot switch");
} else {
LOG_WRN("Failed to disconnect LE peer for slot switch err=%d", err);
}
}
struct peer_bond_lookup {
const bt_addr_le_t *peer_addr;
bool found;
};
static void peer_bond_lookup_cb(const struct bt_bond_info *info, void *user_data)
{
struct peer_bond_lookup *lookup = user_data;
if (!bt_addr_le_cmp(&info->addr, lookup->peer_addr)) {
lookup->found = true;
}
}
static bool slot_has_peer_bond(uint8_t app_id, const bt_addr_le_t *peer_addr)
{
struct peer_bond_lookup lookup = {
.peer_addr = peer_addr,
};
bt_foreach_bond(get_bt_stack_peer_id(app_id), peer_bond_lookup_cb, &lookup);
return lookup.found;
}
static bool find_peer_owner_slot(const bt_addr_le_t *peer_addr, uint8_t *owner_slot)
{
for (uint8_t slot = 0; slot < BLE_BOND_SLOT_COUNT; slot++) {
if (slot_has_peer_bond(slot, peer_addr)) {
*owner_slot = slot;
return true;
}
}
return false;
}
static int select_peer(uint8_t peer_id)
{
if (peer_id >= BLE_BOND_SLOT_COUNT) {
return -EINVAL;
}
uint8_t previous_peer_id = bond.storage.cur_peer_id;
if (bond.storage.cur_peer_id_valid && (previous_peer_id == peer_id)) {
LOG_INF("Peer slot already selected: slot=%u stack_id=%u",
peer_id, get_bt_stack_peer_id(peer_id));
return 0;
}
bond.storage.cur_peer_id = peer_id;
bond.storage.cur_peer_id_valid = true;
int err = store_peer_id(bond.storage.cur_peer_id);
if (err) {
LOG_ERR("Failed to store peer_id=%u (err:%d)", bond.storage.cur_peer_id, err);
return err;
}
submit_peer_op_event(PEER_OPERATION_SELECTED, bond.storage.cur_peer_id);
bt_conn_foreach(BT_CONN_TYPE_LE, disconnect_le_conn_cb, NULL);
return 0;
}
static bool handle_ble_peer_event(const struct ble_peer_event *event)
{
if (event->state == PEER_STATE_CONNECTED) {
const bt_addr_le_t *peer_addr = bt_conn_get_dst(event->id);
uint8_t owner_slot;
if (!peer_addr) {
return false;
}
if (find_peer_owner_slot(peer_addr, &owner_slot) &&
(owner_slot != bond.storage.cur_peer_id)) {
char addr_str[BT_ADDR_LE_STR_LEN];
int err;
bt_addr_le_to_str(peer_addr, addr_str, sizeof(addr_str));
LOG_INF("Peer %s belongs to slot=%u, auto-switch from slot=%u",
addr_str, owner_slot, bond.storage.cur_peer_id);
bond.auto_switch_in_progress = true;
err = select_peer(owner_slot);
if (err) {
bond.auto_switch_in_progress = false;
LOG_ERR("Auto-switch to slot=%u failed err=%d",
owner_slot, err);
module_set_state(MODULE_STATE_ERROR);
}
}
return false;
}
if (event->state == PEER_STATE_DISCONNECTED) {
if (bond.auto_switch_in_progress) {
bond.auto_switch_in_progress = false;
LOG_INF("Auto-switch disconnect complete, waiting for reconnect on slot=%u",
bond.storage.cur_peer_id);
}
return false;
}
if (event->state != PEER_STATE_SECURED) {
return false;
}
struct bt_conn_info info;
int err = bt_conn_get_info(event->id, &info);
if (err) {
LOG_ERR("Cannot get conn info for secured peer err=%d", err);
module_set_state(MODULE_STATE_ERROR);
return false;
}
uint8_t expected_stack_id = get_bt_stack_peer_id(bond.storage.cur_peer_id);
if (info.id == expected_stack_id) {
LOG_INF("Secured peer matches selected slot=%u stack_id=%u",
bond.storage.cur_peer_id,
expected_stack_id);
return false;
}
LOG_INF("Disconnect peer on old id=%u expected=%u selected_slot=%u",
info.id,
expected_stack_id,
bond.storage.cur_peer_id);
err = bt_conn_disconnect(event->id, BT_HCI_ERR_REMOTE_USER_TERM_CONN);
if (err && (err != -ENOTCONN)) {
LOG_ERR("Cannot disconnect peer on old id err=%d", err);
module_set_state(MODULE_STATE_ERROR);
}
return false;
}
static int erase_peer(uint8_t app_id) static int erase_peer(uint8_t app_id)
{ {
uint8_t stack_id = get_bt_stack_peer_id(app_id); uint8_t stack_id = get_bt_stack_peer_id(app_id);
@@ -209,7 +384,7 @@ static int erase_peer(uint8_t app_id)
static int erase_all_peers(void) static int erase_all_peers(void)
{ {
for (uint8_t i = 0; i < APP_PEER_COUNT; i++) { for (uint8_t i = 0; i < BLE_BOND_SLOT_COUNT; i++) {
int err = erase_peer(i); int err = erase_peer(i);
if (err) { if (err) {
return err; return err;
@@ -244,18 +419,14 @@ static bool handle_config_event(const struct config_event *event)
case BLE_BOND_CFG_PEER_SELECT: case BLE_BOND_CFG_PEER_SELECT:
if (event->dyndata.size >= 1) { if (event->dyndata.size >= 1) {
uint8_t peer_id = event->dyndata.data[0]; uint8_t peer_id = event->dyndata.data[0];
if (peer_id < APP_PEER_COUNT) { if (!select_peer(peer_id)) {
cur_ble_peer_id = peer_id;
if (!store_peer_id(cur_ble_peer_id)) {
submit_peer_op_event(PEER_OPERATION_SELECTED, cur_ble_peer_id);
rsp->status = CONFIG_STATUS_SUCCESS; rsp->status = CONFIG_STATUS_SUCCESS;
} }
} }
}
break; break;
case BLE_BOND_CFG_PEER_ERASE: case BLE_BOND_CFG_PEER_ERASE:
if (!erase_peer(cur_ble_peer_id)) { if (!erase_peer(bond.storage.cur_peer_id)) {
rsp->status = CONFIG_STATUS_SUCCESS; rsp->status = CONFIG_STATUS_SUCCESS;
} }
break; break;
@@ -280,7 +451,7 @@ static bool handle_config_event(const struct config_event *event)
rsp_data->event_id = event->event_id; rsp_data->event_id = event->event_id;
rsp_data->recipient = event->recipient; rsp_data->recipient = event->recipient;
rsp_data->status = CONFIG_STATUS_SUCCESS; rsp_data->status = CONFIG_STATUS_SUCCESS;
rsp_data->dyndata.data[0] = cur_ble_peer_id; rsp_data->dyndata.data[0] = bond.storage.cur_peer_id;
APP_EVENT_SUBMIT(rsp_data); APP_EVENT_SUBMIT(rsp_data);
return true; return true;
} }
@@ -300,12 +471,15 @@ static int init_after_settings_loaded(void)
if (!storage_data_is_valid()) { if (!storage_data_is_valid()) {
LOG_WRN("Stored BLE bond data invalid, reinitializing defaults"); LOG_WRN("Stored BLE bond data invalid, reinitializing defaults");
cur_ble_peer_id = 0; bond.storage.cur_peer_id = 0;
bond.storage.cur_peer_id_valid = true;
init_bt_stack_id_lut(); init_bt_stack_id_lut();
bond.storage.bt_stack_id_lut_valid = true;
err = store_peer_id(cur_ble_peer_id); err = store_peer_id(bond.storage.cur_peer_id);
if (err) { if (err) {
LOG_ERR("Failed to store peer_id=%u (err:%d)", cur_ble_peer_id, err); LOG_ERR("Failed to store peer_id=%u (err:%d)",
bond.storage.cur_peer_id, err);
return -EIO; return -EIO;
} }
@@ -316,10 +490,12 @@ static int init_after_settings_loaded(void)
} }
} }
state = STATE_IDLE; bond.state = BLE_BOND_STATE_IDLE;
LOG_INF("ble_bond init done: state=%s peer_id=%u stack_id=%u", LOG_INF("ble_bond init done: state=%s peer_id=%u stack_id=%u",
state_name(state), cur_ble_peer_id, get_bt_stack_peer_id(cur_ble_peer_id)); state_name(bond.state),
submit_peer_op_event(PEER_OPERATION_SELECTED, cur_ble_peer_id); bond.storage.cur_peer_id,
get_bt_stack_peer_id(bond.storage.cur_peer_id));
submit_peer_op_event(PEER_OPERATION_SELECTED, bond.storage.cur_peer_id);
module_set_state(MODULE_STATE_READY); module_set_state(MODULE_STATE_READY);
return 0; return 0;
@@ -331,12 +507,12 @@ static bool app_event_handler(const struct app_event_header *aeh)
const struct module_state_event *event = cast_module_state_event(aeh); const struct module_state_event *event = cast_module_state_event(aeh);
if (check_state(event, MODULE_ID(settings_loader), MODULE_STATE_READY) && if (check_state(event, MODULE_ID(settings_loader), MODULE_STATE_READY) &&
(state == STATE_DISABLED)) { (bond.state == BLE_BOND_STATE_DISABLED)) {
LOG_INF("settings_loader ready, starting ble_bond init"); LOG_INF("settings_loader ready, starting ble_bond init");
int err = init_after_settings_loaded(); int err = init_after_settings_loaded();
if (err) { if (err) {
LOG_ERR("ble_bond init failed (err:%d), state=%s", LOG_ERR("ble_bond init failed (err:%d), state=%s",
err, state_name(state)); err, state_name(bond.state));
module_set_state(MODULE_STATE_ERROR); module_set_state(MODULE_STATE_ERROR);
} }
} }
@@ -345,22 +521,26 @@ static bool app_event_handler(const struct app_event_header *aeh)
} }
if (is_power_down_event(aeh)) { if (is_power_down_event(aeh)) {
if (state == STATE_IDLE) { if (bond.state == BLE_BOND_STATE_IDLE) {
state = STATE_STANDBY; bond.state = BLE_BOND_STATE_STANDBY;
module_set_state(MODULE_STATE_OFF); module_set_state(MODULE_STATE_OFF);
} }
return false; return false;
} }
if (is_wake_up_event(aeh)) { if (is_wake_up_event(aeh)) {
if (state == STATE_STANDBY) { if (bond.state == BLE_BOND_STATE_STANDBY) {
state = STATE_IDLE; bond.state = BLE_BOND_STATE_IDLE;
module_set_state(MODULE_STATE_READY); module_set_state(MODULE_STATE_READY);
submit_peer_op_event(PEER_OPERATION_SELECTED, cur_ble_peer_id); submit_peer_op_event(PEER_OPERATION_SELECTED, bond.storage.cur_peer_id);
} }
return false; return false;
} }
if (is_ble_peer_event(aeh)) {
return handle_ble_peer_event(cast_ble_peer_event(aeh));
}
if (is_config_event(aeh)) { if (is_config_event(aeh)) {
return handle_config_event(cast_config_event(aeh)); return handle_config_event(cast_config_event(aeh));
} }
@@ -373,4 +553,5 @@ APP_EVENT_LISTENER(MODULE, app_event_handler);
APP_EVENT_SUBSCRIBE(MODULE, module_state_event); APP_EVENT_SUBSCRIBE(MODULE, module_state_event);
APP_EVENT_SUBSCRIBE(MODULE, power_down_event); APP_EVENT_SUBSCRIBE(MODULE, power_down_event);
APP_EVENT_SUBSCRIBE(MODULE, wake_up_event); APP_EVENT_SUBSCRIBE(MODULE, wake_up_event);
APP_EVENT_SUBSCRIBE(MODULE, ble_peer_event);
APP_EVENT_SUBSCRIBE_EARLY(MODULE, config_event); APP_EVENT_SUBSCRIBE_EARLY(MODULE, config_event);

View File

@@ -8,9 +8,10 @@
#include <caf/events/ble_common_event.h> #include <caf/events/ble_common_event.h>
#include "hid_protocol_event.h" #include "hid_protocol_event.h"
#include "hid_boot_event.h"
#include "hid_report_event.h" #include "hid_report_event.h"
#include "hid_report_descriptor.h" #include "hid_report_descriptor.h"
#include "keyboard_led_state_event.h" #include "keyboard_led_event.h"
#include "mode_event.h" #include "mode_event.h"
#include <zephyr/logging/log.h> #include <zephyr/logging/log.h>
@@ -25,43 +26,66 @@ LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF);
BT_HIDS_DEF(hids_obj, INPUT_REPORT_COUNT, OUTPUT_REPORT_COUNT, 0); BT_HIDS_DEF(hids_obj, INPUT_REPORT_COUNT, OUTPUT_REPORT_COUNT, 0);
static struct bt_conn *active_conn; struct ble_hid_link {
static enum bt_hids_pm current_pm = BT_HIDS_PM_REPORT; struct bt_conn *conn;
static bool ble_mode_selected; enum bt_hids_pm protocol_mode;
static bool num_lock_known; };
static bool num_lock_on;
static enum hid_protocol_type pm_to_protocol(enum bt_hids_pm pm) struct ble_hid_policy {
bool ble_mode_selected;
};
struct ble_hid_led_state {
bool valid;
uint8_t led_mask;
};
struct ble_hid_ctx {
struct ble_hid_link link;
struct ble_hid_policy policy;
struct ble_hid_led_state led;
};
static struct ble_hid_ctx ble_hid = {
.link.protocol_mode = BT_HIDS_PM_REPORT,
};
static bool ble_hid_is_connected(void)
{ {
return (pm == BT_HIDS_PM_BOOT) ? HID_PROTO_BOOT : HID_PROTO_REPORT; return ble_hid.link.conn != NULL;
}
static bool ble_hid_is_active(void)
{
return ble_hid.policy.ble_mode_selected && ble_hid_is_connected();
}
static bool ble_hid_is_boot_mode(void)
{
return ble_hid.link.protocol_mode == BT_HIDS_PM_BOOT;
}
static bool ble_hid_is_report_mode(void)
{
return ble_hid.link.protocol_mode == BT_HIDS_PM_REPORT;
} }
static void publish_hid_protocol_event(enum hid_protocol_type protocol) static void publish_hid_protocol_event(enum hid_protocol_type protocol)
{ {
struct hid_protocol_event *event = new_hid_protocol_event(); hid_protocol_event_submit(protocol);
event->transport = HID_TRANSPORT_BLE;
event->protocol = protocol;
APP_EVENT_SUBMIT(event);
} }
/* BLE 输出报告的 bit0 对应 Num Lock。仅在状态变化时上报,避免重复通知。 */ /* 主机 LED 输出报告变化时上报,避免重复事件淹没总线。 */
static void publish_num_lock_state_from_led_mask(uint8_t led_mask) static void publish_num_lock_state_from_led_mask(uint8_t led_mask)
{ {
bool new_num_lock = (led_mask & BIT(0)) != 0U; if (ble_hid.led.valid && (ble_hid.led.led_mask == led_mask)) {
if (num_lock_known && (num_lock_on == new_num_lock)) {
return; return;
} }
num_lock_known = true; ble_hid.led.valid = true;
num_lock_on = new_num_lock; ble_hid.led.led_mask = led_mask;
struct keyboard_led_state_event *event = new_keyboard_led_state_event(); keyboard_led_event_submit(led_mask);
event->led_mask = led_mask;
event->num_lock = new_num_lock;
APP_EVENT_SUBMIT(event);
} }
static void pm_evt_handler(enum bt_hids_pm_evt evt, struct bt_conn *conn) static void pm_evt_handler(enum bt_hids_pm_evt evt, struct bt_conn *conn)
@@ -70,16 +94,16 @@ static void pm_evt_handler(enum bt_hids_pm_evt evt, struct bt_conn *conn)
switch (evt) { switch (evt) {
case BT_HIDS_PM_EVT_BOOT_MODE_ENTERED: case BT_HIDS_PM_EVT_BOOT_MODE_ENTERED:
current_pm = BT_HIDS_PM_BOOT; ble_hid.link.protocol_mode = BT_HIDS_PM_BOOT;
LOG_INF("HIDS protocol: boot"); LOG_INF("HIDS protocol: boot");
if (active_conn) { if (ble_hid_is_connected()) {
publish_hid_protocol_event(HID_PROTO_BOOT); publish_hid_protocol_event(HID_PROTO_BOOT);
} }
break; break;
case BT_HIDS_PM_EVT_REPORT_MODE_ENTERED: case BT_HIDS_PM_EVT_REPORT_MODE_ENTERED:
current_pm = BT_HIDS_PM_REPORT; ble_hid.link.protocol_mode = BT_HIDS_PM_REPORT;
LOG_INF("HIDS protocol: report"); LOG_INF("HIDS protocol: report");
if (active_conn) { if (ble_hid_is_connected()) {
publish_hid_protocol_event(HID_PROTO_REPORT); publish_hid_protocol_event(HID_PROTO_REPORT);
} }
break; break;
@@ -166,21 +190,19 @@ static void handle_ble_peer_event(const struct ble_peer_event *event)
{ {
switch (event->state) { switch (event->state) {
case PEER_STATE_CONNECTED: case PEER_STATE_CONNECTED:
__ASSERT_NO_MSG(active_conn == NULL); __ASSERT_NO_MSG(ble_hid.link.conn == NULL);
active_conn = event->id; ble_hid.link.conn = event->id;
if (bt_hids_connected(&hids_obj, active_conn)) { if (bt_hids_connected(&hids_obj, ble_hid.link.conn)) {
LOG_WRN("bt_hids_connected failed"); LOG_WRN("bt_hids_connected failed");
} }
/* 连接建立后按当前协议主动同步一次,避免 keyboard_module 等待下一次 set_protocol。 */
publish_hid_protocol_event(pm_to_protocol(current_pm));
break; break;
case PEER_STATE_DISCONNECTED: case PEER_STATE_DISCONNECTED:
if (active_conn == event->id) { if (ble_hid.link.conn == event->id) {
if (bt_hids_disconnected(&hids_obj, active_conn)) { if (bt_hids_disconnected(&hids_obj, ble_hid.link.conn)) {
LOG_WRN("bt_hids_disconnected failed"); LOG_WRN("bt_hids_disconnected failed");
} }
active_conn = NULL; ble_hid.link.conn = NULL;
} }
break; break;
@@ -189,54 +211,58 @@ static void handle_ble_peer_event(const struct ble_peer_event *event)
} }
} }
static bool handle_hid_report_event(const struct hid_report_event *event) static bool handle_hid_boot_event(const struct hid_boot_event *event)
{ {
if (!ble_mode_selected || !active_conn) { if (!ble_hid_is_active()) {
return false; return false;
} }
uint8_t report_id; if (!ble_hid_is_boot_mode()) {
const uint8_t *payload;
size_t payload_len;
/*
* keyboard_module 已经按照 protocol 打包好了完整 payload。
* BLE 模块这里只做协议一致性检查与发送。
*/
if ((current_pm == BT_HIDS_PM_BOOT) && (event->protocol != HID_PROTO_BOOT)) {
return false;
}
if ((current_pm == BT_HIDS_PM_REPORT) && (event->protocol != HID_PROTO_REPORT)) {
return false; return false;
} }
if (event->protocol == HID_PROTO_BOOT) { const uint8_t *payload = hid_boot_event_get_data(event);
report_id = REPORT_ID_KEYBOARD; size_t payload_len = hid_boot_event_get_size(event);
payload = event->dyndata.data;
payload_len = event->dyndata.size;
} else {
if (event->dyndata.size < 1U) {
return false;
}
report_id = event->dyndata.data[0];
payload = &event->dyndata.data[1];
payload_len = event->dyndata.size - 1U;
}
int err = 0;
if (event->protocol == HID_PROTO_BOOT) {
if (report_id != REPORT_ID_KEYBOARD) {
return false;
}
if (payload_len != BOOT_KEYBOARD_REPORT_LEN) { if (payload_len != BOOT_KEYBOARD_REPORT_LEN) {
LOG_WRN("Invalid boot keyboard payload len=%u", payload_len); LOG_WRN("Invalid boot keyboard payload len=%u", payload_len);
return false; return false;
} }
err = bt_hids_boot_kb_inp_rep_send(&hids_obj, active_conn, int err = bt_hids_boot_kb_inp_rep_send(&hids_obj, ble_hid.link.conn,
payload, payload_len, NULL); payload, payload_len, NULL);
} else {
if (err) {
LOG_WRN("BLE HID boot send failed err=%d", err);
}
return false;
}
static bool handle_hid_report_event(const struct hid_report_event *event)
{
if (!ble_hid_is_active()) {
return false;
}
if (!ble_hid_is_report_mode()) {
return false;
}
const uint8_t *data = hid_report_event_get_data(event);
size_t data_len = hid_report_event_get_size(event);
uint8_t report_id;
const uint8_t *payload;
size_t payload_len;
if (data_len < 1U) {
return false;
}
report_id = data[0];
payload = &data[1];
payload_len = data_len - 1U;
uint8_t rep_index; uint8_t rep_index;
if (report_id == REPORT_ID_KEYBOARD) { if (report_id == REPORT_ID_KEYBOARD) {
@@ -252,9 +278,8 @@ static bool handle_hid_report_event(const struct hid_report_event *event)
return false; return false;
} }
err = bt_hids_inp_rep_send(&hids_obj, active_conn, rep_index, int err = bt_hids_inp_rep_send(&hids_obj, ble_hid.link.conn, rep_index,
payload, (uint8_t)payload_len, NULL); payload, (uint8_t)payload_len, NULL);
}
if (err) { if (err) {
LOG_WRN("BLE HID send failed report=0x%02x err=%d", report_id, err); LOG_WRN("BLE HID send failed report=0x%02x err=%d", report_id, err);
@@ -292,7 +317,7 @@ static bool app_event_handler(const struct app_event_header *aeh)
if (is_mode_event(aeh)) { if (is_mode_event(aeh)) {
const struct mode_event *event = cast_mode_event(aeh); const struct mode_event *event = cast_mode_event(aeh);
ble_mode_selected = (event->mode_type == MODE_TYPE_BLE); ble_hid.policy.ble_mode_selected = (event->mode_type == MODE_TYPE_BLE);
return false; return false;
} }
@@ -300,6 +325,10 @@ static bool app_event_handler(const struct app_event_header *aeh)
return handle_hid_report_event(cast_hid_report_event(aeh)); return handle_hid_report_event(cast_hid_report_event(aeh));
} }
if (is_hid_boot_event(aeh)) {
return handle_hid_boot_event(cast_hid_boot_event(aeh));
}
__ASSERT_NO_MSG(false); __ASSERT_NO_MSG(false);
return false; return false;
} }
@@ -308,4 +337,5 @@ APP_EVENT_LISTENER(MODULE, app_event_handler);
APP_EVENT_SUBSCRIBE_EARLY(MODULE, module_state_event); APP_EVENT_SUBSCRIBE_EARLY(MODULE, module_state_event);
APP_EVENT_SUBSCRIBE_EARLY(MODULE, ble_peer_event); APP_EVENT_SUBSCRIBE_EARLY(MODULE, ble_peer_event);
APP_EVENT_SUBSCRIBE(MODULE, mode_event); APP_EVENT_SUBSCRIBE(MODULE, mode_event);
APP_EVENT_SUBSCRIBE(MODULE, hid_boot_event);
APP_EVENT_SUBSCRIBE(MODULE, hid_report_event); APP_EVENT_SUBSCRIBE(MODULE, hid_report_event);

View File

@@ -0,0 +1,281 @@
#include <string.h>
#include <zephyr/kernel.h>
#include <app_event_manager.h>
#define MODULE ble_slot_ctrl
#include <caf/events/button_event.h>
#include <caf/events/module_state_event.h>
#include <caf/events/power_event.h>
#include <caf/key_id.h>
#include "config_event.h"
#include "mode_event.h"
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF);
#define BLE_BOND_CFG_MODULE_ID 0x01
#define BLE_BOND_CFG_PEER_SELECT 0
#define BLE_BOND_CFG_PEER_ERASE 1
#define BLE_SLOT_CTRL_MUTE_HOLD_MS 3000
#define BLE_SLOT_CTRL_SLOT_KEY_NONE UINT16_MAX
#define BLE_SLOT_CTRL_KEY_MUTE KEY_ID(3, 0)
#define BLE_SLOT_CTRL_SLOT_COUNT 3
struct ble_slot_ctrl_ctx {
bool ble_mode_selected;
bool mute_pressed;
bool mute_hold_fired;
bool slot_combo_used;
bool initialized;
bool passthrough_mute_press;
bool passthrough_mute_release;
uint16_t slot_key_id;
struct k_work_delayable mute_hold_work;
};
static struct ble_slot_ctrl_ctx slot_ctrl = {
.slot_key_id = BLE_SLOT_CTRL_SLOT_KEY_NONE,
};
static uint8_t ble_slot_ctrl_make_event_id(uint8_t opt_id)
{
return (BLE_BOND_CFG_MODULE_ID << MOD_FIELD_POS) | (uint8_t)(opt_id + 1U);
}
static void ble_slot_ctrl_submit_config_request(uint8_t opt_id,
const uint8_t *payload,
size_t payload_len)
{
struct config_event *event = new_config_event(payload_len);
event->transport_id = 0U;
event->is_request = true;
event->event_id = ble_slot_ctrl_make_event_id(opt_id);
event->recipient = CFG_CHAN_RECIPIENT_LOCAL;
event->status = CONFIG_STATUS_SET;
if ((payload_len > 0U) && (payload != NULL)) {
memcpy(event->dyndata.data, payload, payload_len);
}
LOG_INF("Submit cfg request opt=%u len=%u", opt_id, payload_len);
APP_EVENT_SUBMIT(event);
}
static void ble_slot_ctrl_request_select(uint8_t slot_id)
{
ble_slot_ctrl_submit_config_request(BLE_BOND_CFG_PEER_SELECT, &slot_id, sizeof(slot_id));
}
static void ble_slot_ctrl_request_erase_current(void)
{
ble_slot_ctrl_submit_config_request(BLE_BOND_CFG_PEER_ERASE, NULL, 0U);
}
static void ble_slot_ctrl_reset(void)
{
LOG_DBG("Reset slot ctrl state mute=%d hold=%d combo=%d key=0x%04x",
slot_ctrl.mute_pressed,
slot_ctrl.mute_hold_fired,
slot_ctrl.slot_combo_used,
slot_ctrl.slot_key_id);
slot_ctrl.mute_pressed = false;
slot_ctrl.mute_hold_fired = false;
slot_ctrl.slot_combo_used = false;
slot_ctrl.slot_key_id = BLE_SLOT_CTRL_SLOT_KEY_NONE;
if (slot_ctrl.initialized) {
(void)k_work_cancel_delayable(&slot_ctrl.mute_hold_work);
}
}
static void ble_slot_ctrl_submit_button(uint16_t key_id, bool pressed)
{
struct button_event *event = new_button_event();
event->key_id = key_id;
event->pressed = pressed;
APP_EVENT_SUBMIT(event);
}
static void ble_slot_ctrl_forward_mute_tap(void)
{
LOG_INF("Forward mute tap to HID path");
slot_ctrl.passthrough_mute_press = true;
slot_ctrl.passthrough_mute_release = true;
ble_slot_ctrl_submit_button(BLE_SLOT_CTRL_KEY_MUTE, true);
ble_slot_ctrl_submit_button(BLE_SLOT_CTRL_KEY_MUTE, false);
}
static bool ble_slot_ctrl_try_get_slot_id(uint16_t key_id, uint8_t *slot_id)
{
switch (key_id) {
case KEY_ID(0, 4):
*slot_id = 0U;
return true;
case KEY_ID(1, 4):
*slot_id = 1U;
return true;
case KEY_ID(2, 4):
*slot_id = 2U;
return true;
default:
return false;
}
}
static bool ble_slot_ctrl_active(void)
{
return slot_ctrl.ble_mode_selected;
}
static void ble_slot_ctrl_mute_hold_work_fn(struct k_work *work)
{
ARG_UNUSED(work);
if (!ble_slot_ctrl_active() ||
!slot_ctrl.mute_pressed ||
slot_ctrl.slot_combo_used) {
LOG_DBG("Ignore mute hold active=%d mute=%d combo=%d",
ble_slot_ctrl_active(),
slot_ctrl.mute_pressed,
slot_ctrl.slot_combo_used);
return;
}
ble_slot_ctrl_request_erase_current();
slot_ctrl.mute_hold_fired = true;
LOG_INF("Requested erase current BLE slot by mute hold");
}
static bool handle_button_event(const struct button_event *event)
{
if (!ble_slot_ctrl_active()) {
return false;
}
if (event->key_id == BLE_SLOT_CTRL_KEY_MUTE) {
if (event->pressed && slot_ctrl.passthrough_mute_press) {
LOG_DBG("Pass through synthetic mute press");
slot_ctrl.passthrough_mute_press = false;
return false;
}
if (!event->pressed && slot_ctrl.passthrough_mute_release) {
LOG_DBG("Pass through synthetic mute release");
slot_ctrl.passthrough_mute_release = false;
return false;
}
if (event->pressed) {
if (slot_ctrl.mute_pressed) {
LOG_DBG("Drop repeated mute press");
return true;
}
LOG_INF("Mute pressed: start hold timer %u ms", BLE_SLOT_CTRL_MUTE_HOLD_MS);
slot_ctrl.mute_pressed = true;
slot_ctrl.mute_hold_fired = false;
slot_ctrl.slot_combo_used = false;
slot_ctrl.slot_key_id = BLE_SLOT_CTRL_SLOT_KEY_NONE;
k_work_reschedule(&slot_ctrl.mute_hold_work,
K_MSEC(BLE_SLOT_CTRL_MUTE_HOLD_MS));
return true;
}
if (!slot_ctrl.mute_pressed) {
return false;
}
LOG_INF("Mute released hold=%d combo=%d",
slot_ctrl.mute_hold_fired,
slot_ctrl.slot_combo_used);
(void)k_work_cancel_delayable(&slot_ctrl.mute_hold_work);
if (!slot_ctrl.mute_hold_fired && !slot_ctrl.slot_combo_used) {
ble_slot_ctrl_forward_mute_tap();
}
ble_slot_ctrl_reset();
return true;
}
if (!slot_ctrl.mute_pressed) {
return false;
}
if (!event->pressed && (slot_ctrl.slot_key_id == event->key_id)) {
slot_ctrl.slot_key_id = BLE_SLOT_CTRL_SLOT_KEY_NONE;
return true;
}
if (!event->pressed) {
return false;
}
uint8_t slot_id;
if (!ble_slot_ctrl_try_get_slot_id(event->key_id, &slot_id)) {
LOG_DBG("Mute combo ignore key_id=0x%04x", event->key_id);
return false;
}
(void)k_work_cancel_delayable(&slot_ctrl.mute_hold_work);
slot_ctrl.slot_combo_used = true;
slot_ctrl.slot_key_id = event->key_id;
ble_slot_ctrl_request_select(slot_id);
LOG_INF("Requested BLE slot=%u from key_id=0x%04x", slot_id, event->key_id);
return true;
}
static bool app_event_handler(const struct app_event_header *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)) {
if (!slot_ctrl.initialized) {
k_work_init_delayable(&slot_ctrl.mute_hold_work,
ble_slot_ctrl_mute_hold_work_fn);
slot_ctrl.initialized = true;
}
module_set_state(MODULE_STATE_READY);
}
return false;
}
if (is_mode_event(aeh)) {
const struct mode_event *event = cast_mode_event(aeh);
slot_ctrl.ble_mode_selected = mode_event_is_ble(event);
LOG_INF("BLE slot ctrl mode selected=%d", slot_ctrl.ble_mode_selected);
if (!slot_ctrl.ble_mode_selected) {
ble_slot_ctrl_reset();
}
return false;
}
if (is_power_down_event(aeh)) {
ble_slot_ctrl_reset();
return false;
}
if (is_button_event(aeh)) {
return handle_button_event(cast_button_event(aeh));
}
__ASSERT_NO_MSG(false);
return false;
}
APP_EVENT_LISTENER(MODULE, app_event_handler);
APP_EVENT_SUBSCRIBE(MODULE, module_state_event);
APP_EVENT_SUBSCRIBE(MODULE, mode_event);
APP_EVENT_SUBSCRIBE_EARLY(MODULE, power_down_event);
APP_EVENT_SUBSCRIBE_EARLY(MODULE, button_event);

View File

@@ -10,9 +10,9 @@
#include <caf/key_id.h> #include <caf/key_id.h>
#include "hid_report_descriptor.h" #include "hid_report_descriptor.h"
#include "hid_boot_event.h"
#include "hid_protocol_event.h" #include "hid_protocol_event.h"
#include "hid_report_event.h" #include "hid_report_event.h"
#include "mode_event.h"
#include <zephyr/sys/util.h> #include <zephyr/sys/util.h>
#include <zephyr/logging/log.h> #include <zephyr/logging/log.h>
@@ -103,23 +103,19 @@ static const struct hid_keymap *hid_keymap_get_local(uint16_t key_id)
struct keyboard_state { struct keyboard_state {
uint8_t modifier_bm; uint8_t modifier_bm;
uint8_t usage_bm[KEYBOARD_BITMAP_SIZE]; uint8_t usage_bm[KEYBOARD_BITMAP_SIZE];
enum hid_protocol_type ble_protocol; enum hid_protocol_type current_protocol;
enum hid_protocol_type usb_protocol;
mode_type_t current_mode;
uint16_t consumer_usage; uint16_t consumer_usage;
}; };
static struct keyboard_state ks = { static struct keyboard_state ks = {
.ble_protocol = HID_PROTO_REPORT, .current_protocol = HID_PROTO_REPORT,
.usb_protocol = HID_PROTO_REPORT,
.current_mode = MODE_TYPE_COUNT,
.consumer_usage = 0, .consumer_usage = 0,
}; };
/* 依据当前 mode 选择生效的 HID 协议来源BLE 或 USB。 */ /* 当前 HID 报告编码仅跟最近一次 set_protocol 结果相关。 */
static enum hid_protocol_type active_protocol_get(void) static enum hid_protocol_type active_protocol_get(void)
{ {
return (ks.current_mode == MODE_TYPE_USB) ? ks.usb_protocol : ks.ble_protocol; return ks.current_protocol;
} }
/* 查询某 usage 位在当前键盘位图里是否处于按下状态。 */ /* 查询某 usage 位在当前键盘位图里是否处于按下状态。 */
@@ -182,18 +178,17 @@ static void submit_hid_report(enum hid_protocol_type protocol,
const uint8_t *payload, const uint8_t *payload,
size_t payload_len) size_t payload_len)
{ {
size_t report_len = (protocol == HID_PROTO_REPORT) ? (payload_len + 1U) : payload_len; uint8_t report_buf[KEYBOARD_REPORT_PAYLOAD + 1U];
struct hid_report_event *event = new_hid_report_event(report_len);
event->protocol = protocol;
if (protocol == HID_PROTO_REPORT) { if (protocol == HID_PROTO_REPORT) {
event->dyndata.data[0] = report_id; size_t report_len = payload_len + 1U;
memcpy(&event->dyndata.data[1], payload, payload_len);
} else {
memcpy(event->dyndata.data, payload, payload_len);
}
APP_EVENT_SUBMIT(event); report_buf[0] = report_id;
memcpy(&report_buf[1], payload, payload_len);
hid_report_event_submit(report_buf, report_len);
} else {
hid_boot_event_submit(payload, payload_len);
}
} }
/* /*
@@ -309,24 +304,10 @@ static bool handle_button_event(const struct button_event *event)
return false; return false;
} }
/* 同步 BLE/USB 传输层上报的当前协议。 */ /* 记录最近一次 set_protocol 结果。 */
static bool handle_hid_protocol_event(const struct hid_protocol_event *event) static bool handle_hid_protocol_event(const struct hid_protocol_event *event)
{ {
if (event->transport == HID_TRANSPORT_BLE) { ks.current_protocol = hid_protocol_event_get_protocol(event);
ks.ble_protocol = event->protocol;
} else if (event->transport == HID_TRANSPORT_USB) {
ks.usb_protocol = event->protocol;
} else {
__ASSERT_NO_MSG(false);
}
return false;
}
/* 更新当前激活模式,决定协议来源取 BLE 还是 USB。 */
static bool handle_mode_event(const struct mode_event *event)
{
ks.current_mode = event->mode_type;
return false; return false;
} }
@@ -341,10 +322,6 @@ static bool app_event_handler(const struct app_event_header *aeh)
return handle_hid_protocol_event(cast_hid_protocol_event(aeh)); return handle_hid_protocol_event(cast_hid_protocol_event(aeh));
} }
if (is_mode_event(aeh)) {
return handle_mode_event(cast_mode_event(aeh));
}
if (is_module_state_event(aeh)) { if (is_module_state_event(aeh)) {
const struct module_state_event *event = cast_module_state_event(aeh); const struct module_state_event *event = cast_module_state_event(aeh);
@@ -364,5 +341,4 @@ static bool app_event_handler(const struct app_event_header *aeh)
APP_EVENT_LISTENER(MODULE, app_event_handler); APP_EVENT_LISTENER(MODULE, app_event_handler);
APP_EVENT_SUBSCRIBE(MODULE, button_event); APP_EVENT_SUBSCRIBE(MODULE, button_event);
APP_EVENT_SUBSCRIBE(MODULE, hid_protocol_event); APP_EVENT_SUBSCRIBE(MODULE, hid_protocol_event);
APP_EVENT_SUBSCRIBE(MODULE, mode_event);
APP_EVENT_SUBSCRIBE(MODULE, module_state_event); APP_EVENT_SUBSCRIBE(MODULE, module_state_event);

View File

@@ -7,7 +7,7 @@
#include <caf/events/ble_common_event.h> #include <caf/events/ble_common_event.h>
#include <caf/events/led_event.h> #include <caf/events/led_event.h>
#include "keyboard_led_state_event.h" #include "keyboard_led_event.h"
#include "led_state.h" #include "led_state.h"
#include "mode_event.h" #include "mode_event.h"
#include "led_state_def.h" #include "led_state_def.h"
@@ -129,9 +129,9 @@ static bool handle_ble_peer_operation_event(const struct ble_peer_operation_even
return false; return false;
} }
static bool handle_keyboard_led_state_event(const struct keyboard_led_state_event *event) static bool handle_keyboard_led_event(const struct keyboard_led_event *event)
{ {
num_lock_on = event->num_lock; num_lock_on = keyboard_led_event_is_num_lock_on(event);
submit_num_lock_led(); submit_num_lock_led();
return false; return false;
} }
@@ -153,8 +153,8 @@ static bool app_event_handler(const struct app_event_header *aeh)
if (is_mode_event(aeh)) if (is_mode_event(aeh))
return handle_mode_event(cast_mode_event(aeh)); return handle_mode_event(cast_mode_event(aeh));
if (is_keyboard_led_state_event(aeh)) if (is_keyboard_led_event(aeh))
return handle_keyboard_led_state_event(cast_keyboard_led_state_event(aeh)); return handle_keyboard_led_event(cast_keyboard_led_event(aeh));
if (IS_ENABLED(CONFIG_CAF_BLE_COMMON_EVENTS) && is_ble_peer_event(aeh)) if (IS_ENABLED(CONFIG_CAF_BLE_COMMON_EVENTS) && is_ble_peer_event(aeh))
return handle_ble_peer_event(cast_ble_peer_event(aeh)); return handle_ble_peer_event(cast_ble_peer_event(aeh));
@@ -172,7 +172,7 @@ static bool app_event_handler(const struct app_event_header *aeh)
APP_EVENT_LISTENER(MODULE, app_event_handler); APP_EVENT_LISTENER(MODULE, app_event_handler);
APP_EVENT_SUBSCRIBE(MODULE, module_state_event); APP_EVENT_SUBSCRIBE(MODULE, module_state_event);
APP_EVENT_SUBSCRIBE(MODULE, mode_event); APP_EVENT_SUBSCRIBE(MODULE, mode_event);
APP_EVENT_SUBSCRIBE(MODULE, keyboard_led_state_event); APP_EVENT_SUBSCRIBE(MODULE, keyboard_led_event);
#ifdef CONFIG_CAF_BLE_COMMON_EVENTS #ifdef CONFIG_CAF_BLE_COMMON_EVENTS
APP_EVENT_SUBSCRIBE(MODULE, ble_peer_event); APP_EVENT_SUBSCRIBE(MODULE, ble_peer_event);
APP_EVENT_SUBSCRIBE(MODULE, ble_peer_search_event); APP_EVENT_SUBSCRIBE(MODULE, ble_peer_search_event);

View File

@@ -115,10 +115,7 @@ static void publish_mode_event(mode_type_t mode)
return; return;
current_mode = mode; current_mode = mode;
struct mode_event *event = new_mode_event(); mode_event_submit(mode);
event->mode_type = mode;
APP_EVENT_SUBMIT(event);
/* /*
* 模式切换是明确的人机交互动作。这里同步上报 keep_alive_event * 模式切换是明确的人机交互动作。这里同步上报 keep_alive_event
* 让 power manager 重置休眠倒计时,避免用户刚切换模式就进入省电流程。 * 让 power manager 重置休眠倒计时,避免用户刚切换模式就进入省电流程。

View File

@@ -13,9 +13,10 @@
#include <caf/events/module_state_event.h> #include <caf/events/module_state_event.h>
#include "hid_report_descriptor.h" #include "hid_report_descriptor.h"
#include "hid_boot_event.h"
#include "hid_protocol_event.h" #include "hid_protocol_event.h"
#include "hid_report_event.h" #include "hid_report_event.h"
#include "keyboard_led_state_event.h" #include "keyboard_led_event.h"
#include "mode_event.h" #include "mode_event.h"
#include <zephyr/logging/log.h> #include <zephyr/logging/log.h>
@@ -35,24 +36,30 @@ LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF);
* - 只有当 mode 切到 USB 且系统非休眠时才 enable * - 只有当 mode 切到 USB 且系统非休眠时才 enable
* - BLE 逻辑保持不变,不在本模块中触碰。 * - BLE 逻辑保持不变,不在本模块中触碰。
*/ */
struct usb_hid_ctx { struct usb_hid_iface {
const struct device *boot_dev; const struct device *dev;
const struct device *nkro_dev; bool iface_ready;
const struct device *raw_dev; bool in_flight;
};
bool stack_initialized; enum usb_hid_stack_state {
bool stack_enabled; USB_HID_STACK_STATE_OFF,
bool stack_error; USB_HID_STACK_STATE_READY,
USB_HID_STACK_STATE_ACTIVE,
USB_HID_STACK_STATE_ERROR,
};
struct usb_hid_policy {
bool usb_mode_selected; bool usb_mode_selected;
bool pm_suspended; bool pm_suspended;
};
bool boot_iface_ready; struct usb_hid_ctx {
bool nkro_iface_ready; struct usb_hid_iface boot;
bool raw_iface_ready; struct usb_hid_iface nkro;
bool boot_in_flight; struct usb_hid_iface raw;
bool nkro_in_flight; enum usb_hid_stack_state stack_state;
struct usb_hid_policy policy;
enum hid_protocol_type current_protocol; enum hid_protocol_type current_protocol;
}; };
@@ -73,22 +80,54 @@ static const uint8_t boot_report_desc[] = HID_KEYBOARD_REPORT_DESC();
static const uint8_t nkro_report_desc[] = HID_DESC_KEYBOARD_NKRO_CONSUMER(); static const uint8_t nkro_report_desc[] = HID_DESC_KEYBOARD_NKRO_CONSUMER();
static const uint8_t raw_report_desc[] = HID_DESC_RAW_64(); static const uint8_t raw_report_desc[] = HID_DESC_RAW_64();
/* 统一入口仅处理单字节 LED 报告并发布事件。 */ static bool usb_hid_stack_is_active(void)
static void process_usb_led_input_report(uint8_t led_report)
{ {
struct keyboard_led_state_event *event = new_keyboard_led_state_event(); return g_usb_hid.stack_state == USB_HID_STACK_STATE_ACTIVE;
}
event->led_mask = led_report; static bool usb_hid_stack_is_error(void)
event->num_lock = (led_report & BIT(0)) != 0U; {
APP_EVENT_SUBMIT(event); return g_usb_hid.stack_state == USB_HID_STACK_STATE_ERROR;
}
static bool usb_hid_should_be_active(void)
{
return g_usb_hid.policy.usb_mode_selected && !g_usb_hid.policy.pm_suspended;
}
static struct usb_hid_iface *usb_hid_iface_from_dev(const struct device *dev)
{
if (dev == g_usb_hid.boot.dev) {
return &g_usb_hid.boot;
}
if (dev == g_usb_hid.nkro.dev) {
return &g_usb_hid.nkro;
}
if (dev == g_usb_hid.raw.dev) {
return &g_usb_hid.raw;
}
return NULL;
}
static void usb_hid_clear_runtime_iface_state(void)
{
g_usb_hid.boot.iface_ready = false;
g_usb_hid.nkro.iface_ready = false;
g_usb_hid.raw.iface_ready = false;
g_usb_hid.boot.in_flight = false;
g_usb_hid.nkro.in_flight = false;
g_usb_hid.raw.in_flight = false;
} }
static bool should_handle_led_input_from_dev(const struct device *dev) static bool should_handle_led_input_from_dev(const struct device *dev)
{ {
if (g_usb_hid.current_protocol == HID_PROTO_BOOT) if (g_usb_hid.current_protocol == HID_PROTO_BOOT)
return (dev == g_usb_hid.boot_dev); return (dev == g_usb_hid.boot.dev);
return (dev == g_usb_hid.nkro_dev); return (dev == g_usb_hid.nkro.dev);
} }
static bool try_extract_led_mask(const struct device *dev, static bool try_extract_led_mask(const struct device *dev,
@@ -99,12 +138,12 @@ static bool try_extract_led_mask(const struct device *dev,
if ((buf == NULL) || (len == 0U)) if ((buf == NULL) || (len == 0U))
return false; return false;
if (dev == g_usb_hid.boot_dev) { if (dev == g_usb_hid.boot.dev) {
*led_mask = buf[0]; *led_mask = buf[0];
return true; return true;
} }
if (dev != g_usb_hid.nkro_dev) if (dev != g_usb_hid.nkro.dev)
return false; return false;
if (len >= 2U) { if (len >= 2U) {
@@ -149,7 +188,7 @@ static int hid_stub_set_report(const struct device *dev,
} }
LOG_INF("hid_stub_set_report led_mask=0x%02x", led_mask); LOG_INF("hid_stub_set_report led_mask=0x%02x", led_mask);
process_usb_led_input_report(led_mask); keyboard_led_event_submit(led_mask);
return 0; return 0;
} }
@@ -185,12 +224,8 @@ static void hid_stub_set_protocol(const struct device *dev, uint8_t proto)
* 按需求USB HID 在连接后收到 set_protocol 时上报 hid_protocol_event。 * 按需求USB HID 在连接后收到 set_protocol 时上报 hid_protocol_event。
* 这里额外检查接口 ready避免在未枚举完成阶段上报无意义协议切换。 * 这里额外检查接口 ready避免在未枚举完成阶段上报无意义协议切换。
*/ */
if (g_usb_hid.boot_iface_ready || g_usb_hid.nkro_iface_ready) { if (g_usb_hid.boot.iface_ready || g_usb_hid.nkro.iface_ready) {
struct hid_protocol_event *event = new_hid_protocol_event(); hid_protocol_event_submit(new_protocol);
event->transport = HID_TRANSPORT_USB;
event->protocol = new_protocol;
APP_EVENT_SUBMIT(event);
} }
} }
@@ -203,13 +238,10 @@ static void hid_stub_input_done(const struct device *dev, const uint8_t *report)
* - 仅在这里清除“在途发送”标志,确保“上一包未完成则丢弃新包”的策略可闭环; * - 仅在这里清除“在途发送”标志,确保“上一包未完成则丢弃新包”的策略可闭环;
* - 若收到未知 dev 的回调,仅记录告警,避免静默状态错乱。 * - 若收到未知 dev 的回调,仅记录告警,避免静默状态错乱。
*/ */
if (dev == g_usb_hid.boot_dev) { struct usb_hid_iface *iface = usb_hid_iface_from_dev(dev);
g_usb_hid.boot_in_flight = false;
return;
}
if (dev == g_usb_hid.nkro_dev) { if (iface) {
g_usb_hid.nkro_in_flight = false; iface->in_flight = false;
return; return;
} }
@@ -229,34 +261,21 @@ static void hid_stub_output_report(const struct device *dev, uint16_t len, const
} }
LOG_INF("hid_stub_output_report led_mask=0x%02x", led_mask); LOG_INF("hid_stub_output_report led_mask=0x%02x", led_mask);
process_usb_led_input_report(led_mask); keyboard_led_event_submit(led_mask);
} }
static void hid_iface_ready_cb(const struct device *dev, bool ready) static void hid_iface_ready_cb(const struct device *dev, bool ready)
{ {
if (dev == g_usb_hid.boot_dev) { struct usb_hid_iface *iface = usb_hid_iface_from_dev(dev);
g_usb_hid.boot_iface_ready = ready;
if (!iface) {
return;
}
iface->iface_ready = ready;
if (!ready) { if (!ready) {
g_usb_hid.boot_in_flight = false; iface->in_flight = false;
} }
} else if (dev == g_usb_hid.nkro_dev) {
g_usb_hid.nkro_iface_ready = ready;
if (!ready) {
g_usb_hid.nkro_in_flight = false;
}
} else if (dev == g_usb_hid.raw_dev) {
g_usb_hid.raw_iface_ready = ready;
}
if (ready) {
/* 连接可用后同步一次当前协议,让 keyboard_module 与传输侧编码一致。 */
struct hid_protocol_event *event = new_hid_protocol_event();
event->transport = HID_TRANSPORT_USB;
event->protocol = g_usb_hid.current_protocol;
APP_EVENT_SUBMIT(event);
}
} }
static const struct hid_device_ops boot_hid_ops = { static const struct hid_device_ops boot_hid_ops = {
@@ -296,7 +315,7 @@ static void usbd_msg_cb(struct usbd_context *const usbd_ctx,
{ {
switch (msg->type) { switch (msg->type) {
case USBD_MSG_VBUS_READY: case USBD_MSG_VBUS_READY:
if (g_usb_hid.pm_suspended) { if (g_usb_hid.policy.pm_suspended) {
LOG_INF("VBUS ready: submit wake_up_event"); LOG_INF("VBUS ready: submit wake_up_event");
APP_EVENT_SUBMIT(new_wake_up_event()); APP_EVENT_SUBMIT(new_wake_up_event());
} }
@@ -305,7 +324,7 @@ static void usbd_msg_cb(struct usbd_context *const usbd_ctx,
* 只有在 USB 模式下才允许拉起 USB 栈。 * 只有在 USB 模式下才允许拉起 USB 栈。
* 这样即使插着线,只要用户切到 BLE/2.4G,也不会强制进入 USB HID。 * 这样即使插着线,只要用户切到 BLE/2.4G,也不会强制进入 USB HID。
*/ */
if (usbd_can_detect_vbus(usbd_ctx) && g_usb_hid.stack_enabled) { if (usbd_can_detect_vbus(usbd_ctx) && usb_hid_stack_is_active()) {
(void)usbd_enable(usbd_ctx); (void)usbd_enable(usbd_ctx);
} }
break; break;
@@ -321,7 +340,7 @@ static void usbd_msg_cb(struct usbd_context *const usbd_ctx,
case USBD_MSG_UDC_ERROR: case USBD_MSG_UDC_ERROR:
case USBD_MSG_STACK_ERROR: case USBD_MSG_STACK_ERROR:
LOG_ERR("USBD stack error message: %d", msg->type); LOG_ERR("USBD stack error message: %d", msg->type);
g_usb_hid.stack_error = true; g_usb_hid.stack_state = USB_HID_STACK_STATE_ERROR;
break; break;
default: default:
@@ -331,17 +350,17 @@ static void usbd_msg_cb(struct usbd_context *const usbd_ctx,
static bool usb_hid_devices_ready(void) static bool usb_hid_devices_ready(void)
{ {
if (!device_is_ready(g_usb_hid.boot_dev)) { if (!device_is_ready(g_usb_hid.boot.dev)) {
LOG_ERR("HID boot device is not ready"); LOG_ERR("HID boot device is not ready");
return false; return false;
} }
if (!device_is_ready(g_usb_hid.nkro_dev)) { if (!device_is_ready(g_usb_hid.nkro.dev)) {
LOG_ERR("HID nkro device is not ready"); LOG_ERR("HID nkro device is not ready");
return false; return false;
} }
if (!device_is_ready(g_usb_hid.raw_dev)) { if (!device_is_ready(g_usb_hid.raw.dev)) {
LOG_ERR("HID raw device is not ready"); LOG_ERR("HID raw device is not ready");
return false; return false;
} }
@@ -356,7 +375,7 @@ static bool usb_hid_devices_ready(void)
static int usb_hid_register_hid_devices(void) static int usb_hid_register_hid_devices(void)
{ {
int err = hid_device_register(g_usb_hid.boot_dev, int err = hid_device_register(g_usb_hid.boot.dev,
boot_report_desc, sizeof(boot_report_desc), boot_report_desc, sizeof(boot_report_desc),
&boot_hid_ops); &boot_hid_ops);
if (err) { if (err) {
@@ -364,7 +383,7 @@ static int usb_hid_register_hid_devices(void)
return err; return err;
} }
err = hid_device_register(g_usb_hid.nkro_dev, err = hid_device_register(g_usb_hid.nkro.dev,
nkro_report_desc, sizeof(nkro_report_desc), nkro_report_desc, sizeof(nkro_report_desc),
&report_hid_ops); &report_hid_ops);
if (err) { if (err) {
@@ -372,7 +391,7 @@ static int usb_hid_register_hid_devices(void)
return err; return err;
} }
err = hid_device_register(g_usb_hid.raw_dev, err = hid_device_register(g_usb_hid.raw.dev,
raw_report_desc, sizeof(raw_report_desc), raw_report_desc, sizeof(raw_report_desc),
&raw_hid_ops); &raw_hid_ops);
if (err) { if (err) {
@@ -441,13 +460,13 @@ static int usb_hid_init_usbd_stack(void)
static int usb_hid_stack_init(void) static int usb_hid_stack_init(void)
{ {
if (g_usb_hid.stack_initialized) { if (g_usb_hid.stack_state != USB_HID_STACK_STATE_OFF) {
return 0; return 0;
} }
g_usb_hid.boot_dev = DEVICE_DT_GET(DT_NODELABEL(hid_dev_0)); g_usb_hid.boot.dev = DEVICE_DT_GET(DT_NODELABEL(hid_dev_0));
g_usb_hid.nkro_dev = DEVICE_DT_GET(DT_NODELABEL(hid_dev_1)); g_usb_hid.nkro.dev = DEVICE_DT_GET(DT_NODELABEL(hid_dev_1));
g_usb_hid.raw_dev = DEVICE_DT_GET(DT_NODELABEL(raw_hid)); g_usb_hid.raw.dev = DEVICE_DT_GET(DT_NODELABEL(raw_hid));
if (!usb_hid_devices_ready()) { if (!usb_hid_devices_ready()) {
return -ENODEV; return -ENODEV;
@@ -468,7 +487,7 @@ static int usb_hid_stack_init(void)
return err; return err;
} }
g_usb_hid.stack_initialized = true; g_usb_hid.stack_state = USB_HID_STACK_STATE_READY;
return 0; return 0;
} }
@@ -476,36 +495,39 @@ static int usb_hid_set_enabled(bool enable)
{ {
int err; int err;
if (!g_usb_hid.stack_initialized) { if (usb_hid_stack_is_error()) {
return -EIO;
}
if (g_usb_hid.stack_state == USB_HID_STACK_STATE_OFF) {
err = usb_hid_stack_init(); err = usb_hid_stack_init();
if (err) { if (err) {
return err; return err;
} }
} }
if (g_usb_hid.stack_enabled == enable) { if (enable && usb_hid_stack_is_active()) {
return 0; return 0;
} }
g_usb_hid.stack_enabled = enable; if (!enable && (g_usb_hid.stack_state == USB_HID_STACK_STATE_READY)) {
return 0;
}
if (enable) { if (enable) {
err = usbd_enable(&new_kbd_usbd); err = usbd_enable(&new_kbd_usbd);
} else { } else {
err = usbd_disable(&new_kbd_usbd); err = usbd_disable(&new_kbd_usbd);
g_usb_hid.boot_iface_ready = false; usb_hid_clear_runtime_iface_state();
g_usb_hid.nkro_iface_ready = false;
g_usb_hid.raw_iface_ready = false;
g_usb_hid.boot_in_flight = false;
g_usb_hid.nkro_in_flight = false;
} }
if (err && (err != -EALREADY)) { if (err && (err != -EALREADY)) {
LOG_ERR("usbd_%s failed: %d", enable ? "enable" : "disable", err); LOG_ERR("usbd_%s failed: %d", enable ? "enable" : "disable", err);
g_usb_hid.stack_error = true; g_usb_hid.stack_state = USB_HID_STACK_STATE_ERROR;
return err; return err;
} }
g_usb_hid.stack_state = enable ? USB_HID_STACK_STATE_ACTIVE : USB_HID_STACK_STATE_READY;
return 0; return 0;
} }
@@ -516,7 +538,7 @@ static void refresh_usb_state_by_policy(void)
* - USB 模式 + 非休眠:启用 USB HID。 * - USB 模式 + 非休眠:启用 USB HID。
* - 其他情况:关闭 USB HID不销毁初始化结果后续可快速恢复 * - 其他情况:关闭 USB HID不销毁初始化结果后续可快速恢复
*/ */
bool should_enable = g_usb_hid.usb_mode_selected && !g_usb_hid.pm_suspended; bool should_enable = usb_hid_should_be_active();
int err = usb_hid_set_enabled(should_enable); int err = usb_hid_set_enabled(should_enable);
if (err) { if (err) {
@@ -533,7 +555,7 @@ static bool handle_module_state_event(const struct module_state_event *event)
int err = usb_hid_stack_init(); int err = usb_hid_stack_init();
if (err) { if (err) {
LOG_ERR("USB HID stack init failed: %d", err); LOG_ERR("USB HID stack init failed: %d", err);
g_usb_hid.stack_error = true; g_usb_hid.stack_state = USB_HID_STACK_STATE_ERROR;
module_set_state(MODULE_STATE_ERROR); module_set_state(MODULE_STATE_ERROR);
return false; return false;
} }
@@ -544,19 +566,19 @@ static bool handle_module_state_event(const struct module_state_event *event)
static bool handle_mode_event(const struct mode_event *event) static bool handle_mode_event(const struct mode_event *event)
{ {
g_usb_hid.usb_mode_selected = (event->mode_type == MODE_TYPE_USB); g_usb_hid.policy.usb_mode_selected = (event->mode_type == MODE_TYPE_USB);
refresh_usb_state_by_policy(); refresh_usb_state_by_policy();
return false; return false;
} }
static bool handle_power_down_event(void) static bool handle_power_down_event(void)
{ {
if (g_usb_hid.pm_suspended) { if (g_usb_hid.policy.pm_suspended) {
/* 避免重复上报 STANDBY 导致 power_manager 在 SUSPENDING 期间反复迭代。 */ /* 避免重复上报 STANDBY 导致 power_manager 在 SUSPENDING 期间反复迭代。 */
return false; return false;
} }
g_usb_hid.pm_suspended = true; g_usb_hid.policy.pm_suspended = true;
refresh_usb_state_by_policy(); refresh_usb_state_by_policy();
module_set_state(MODULE_STATE_STANDBY); module_set_state(MODULE_STATE_STANDBY);
return false; return false;
@@ -564,16 +586,49 @@ static bool handle_power_down_event(void)
static bool handle_wake_up_event(void) static bool handle_wake_up_event(void)
{ {
if (!g_usb_hid.pm_suspended) { if (!g_usb_hid.policy.pm_suspended) {
return false; return false;
} }
g_usb_hid.pm_suspended = false; g_usb_hid.policy.pm_suspended = false;
refresh_usb_state_by_policy(); refresh_usb_state_by_policy();
module_set_state(MODULE_STATE_READY); module_set_state(MODULE_STATE_READY);
return false; return false;
} }
static bool handle_hid_boot_event(const struct hid_boot_event *event)
{
if (!g_usb_hid.policy.usb_mode_selected || !usb_hid_stack_is_active()) {
return false;
}
if (g_usb_hid.current_protocol != HID_PROTO_BOOT) {
return false;
}
const uint8_t *payload = hid_boot_event_get_data(event);
size_t payload_len = hid_boot_event_get_size(event);
if (!g_usb_hid.boot.iface_ready || !g_usb_hid.boot.dev) {
return false;
}
if (g_usb_hid.boot.in_flight) {
LOG_WRN("Drop boot report: previous report not sent");
return false;
}
int err = hid_device_submit_report(g_usb_hid.boot.dev,
payload_len,
payload);
if (err) {
LOG_WRN("USB boot report send failed err=%d", err);
} else {
g_usb_hid.boot.in_flight = true;
}
return false;
}
static bool handle_hid_report_event(const struct hid_report_event *event) static bool handle_hid_report_event(const struct hid_report_event *event)
{ {
/* /*
@@ -581,63 +636,42 @@ static bool handle_hid_report_event(const struct hid_report_event *event)
* - 当前 mode 为 USB * - 当前 mode 为 USB
* - USB HID 栈已启用且对应接口 ready。 * - USB HID 栈已启用且对应接口 ready。
*/ */
if (!g_usb_hid.usb_mode_selected || !g_usb_hid.stack_enabled) { if (!g_usb_hid.policy.usb_mode_selected || !usb_hid_stack_is_active()) {
return false; return false;
} }
if (g_usb_hid.current_protocol != HID_PROTO_REPORT) {
return false;
}
const uint8_t *data = hid_report_event_get_data(event);
size_t data_len = hid_report_event_get_size(event);
uint8_t report_id; uint8_t report_id;
if (event->protocol != g_usb_hid.current_protocol) { if (data_len < 1U) {
return false; return false;
} }
if (event->protocol == HID_PROTO_BOOT) { report_id = data[0];
const uint8_t *payload = event->dyndata.data;
size_t payload_len = event->dyndata.size;
if (!g_usb_hid.boot_iface_ready || !g_usb_hid.boot_dev) { if (!g_usb_hid.nkro.iface_ready || !g_usb_hid.nkro.dev) {
return false;
}
if (g_usb_hid.boot_in_flight) {
LOG_WRN("Drop boot report: previous report not sent");
return false;
}
int err = hid_device_submit_report(g_usb_hid.boot_dev,
payload_len,
payload);
if (err) {
LOG_WRN("USB boot report send failed err=%d", err);
} else {
g_usb_hid.boot_in_flight = true;
}
return false;
}
if (event->dyndata.size < 1U) {
return false;
}
report_id = event->dyndata.data[0];
if (!g_usb_hid.nkro_iface_ready || !g_usb_hid.nkro_dev) {
return false; return false;
} }
if ((report_id != REPORT_ID_KEYBOARD) && (report_id != REPORT_ID_CONSUMER)) { if ((report_id != REPORT_ID_KEYBOARD) && (report_id != REPORT_ID_CONSUMER)) {
return false; return false;
} }
if (g_usb_hid.nkro_in_flight) { if (g_usb_hid.nkro.in_flight) {
LOG_WRN("Drop report id=0x%02x: previous report not sent", report_id); LOG_WRN("Drop report id=0x%02x: previous report not sent", report_id);
return false; return false;
} }
/* Report 协议下 dyndata 是 [report_id|payload],可直接透传。 */ /* Report 协议下 dyndata 是 [report_id|payload],可直接透传。 */
int err = hid_device_submit_report(g_usb_hid.nkro_dev, event->dyndata.size, event->dyndata.data); int err = hid_device_submit_report(g_usb_hid.nkro.dev, data_len, data);
if (err) { if (err) {
LOG_WRN("USB report send failed id=0x%02x err=%d", report_id, err); LOG_WRN("USB report send failed id=0x%02x err=%d", report_id, err);
} else { } else {
g_usb_hid.nkro_in_flight = true; g_usb_hid.nkro.in_flight = true;
} }
return false; return false;
@@ -665,6 +699,10 @@ static bool app_event_handler(const struct app_event_header *aeh)
return handle_hid_report_event(cast_hid_report_event(aeh)); return handle_hid_report_event(cast_hid_report_event(aeh));
} }
if (is_hid_boot_event(aeh)) {
return handle_hid_boot_event(cast_hid_boot_event(aeh));
}
__ASSERT_NO_MSG(false); __ASSERT_NO_MSG(false);
return false; return false;
} }
@@ -674,4 +712,5 @@ APP_EVENT_SUBSCRIBE(MODULE, module_state_event);
APP_EVENT_SUBSCRIBE(MODULE, mode_event); APP_EVENT_SUBSCRIBE(MODULE, mode_event);
APP_EVENT_SUBSCRIBE_EARLY(MODULE, power_down_event); APP_EVENT_SUBSCRIBE_EARLY(MODULE, power_down_event);
APP_EVENT_SUBSCRIBE(MODULE, wake_up_event); APP_EVENT_SUBSCRIBE(MODULE, wake_up_event);
APP_EVENT_SUBSCRIBE_EARLY(MODULE, hid_boot_event);
APP_EVENT_SUBSCRIBE_EARLY(MODULE, hid_report_event); APP_EVENT_SUBSCRIBE_EARLY(MODULE, hid_report_event);