diff --git a/CMakeLists.txt b/CMakeLists.txt index 93e1dc8..9abceb5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,14 +19,16 @@ target_sources(app PRIVATE src/main.c src/events/battery_status_event.c src/events/config_event.c + src/events/hid_boot_event.c src/events/hid_protocol_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/modules/battery_module.c src/modules/ble_adv_ctrl_module.c src/modules/ble_battery_module.c src/modules/ble_bond_module.c + src/modules/ble_slot_ctrl_module.c src/modules/keyboard_module.c src/modules/led_state_module.c src/modules/mode_switch_module.c diff --git a/Kconfig b/Kconfig new file mode 100644 index 0000000..f67b023 --- /dev/null +++ b/Kconfig @@ -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" diff --git a/prj.conf b/prj.conf index fb8396b..1bac018 100644 --- a/prj.conf +++ b/prj.conf @@ -37,6 +37,7 @@ CONFIG_CAF_SETTINGS_LOADER=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_PAIRING_MODE_ONLY=n CONFIG_BT_ADV_PROV_SWIFT_PAIR=y CONFIG_BT_HIDS=y diff --git a/src/events/battery_status_event.c b/src/events/battery_status_event.c index 984a169..278b1e3 100644 --- a/src/events/battery_status_event.c +++ b/src/events/battery_status_event.c @@ -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); APP_EVENT_MANAGER_LOG(aeh, - "charging=%u full=%u soc=%u", - event->charging, - event->full, + "flags=0x%02x charging=%u full=%u soc=%u", + event->flags, + BATTERY_STATUS_IS_CHARGING(event->flags), + BATTERY_STATUS_IS_FULL(event->flags), 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); - nrf_profiler_log_encode_uint8(buf, event->charging ? 1U : 0U); - nrf_profiler_log_encode_uint8(buf, event->full ? 1U : 0U); + nrf_profiler_log_encode_uint8(buf, event->flags); nrf_profiler_log_encode_uint8(buf, event->soc); } APP_EVENT_INFO_DEFINE(battery_status_event, ENCODE(NRF_PROFILER_ARG_U8, - NRF_PROFILER_ARG_U8, NRF_PROFILER_ARG_U8), - ENCODE("charging", "full", "soc"), + ENCODE("flags", "soc"), profile_battery_status_event); APP_EVENT_TYPE_DEFINE(battery_status_event, diff --git a/src/events/battery_status_event.h b/src/events/battery_status_event.h index b41174e..35c9b47 100644 --- a/src/events/battery_status_event.h +++ b/src/events/battery_status_event.h @@ -10,11 +10,52 @@ struct battery_status_event { struct app_event_header header; - bool charging; - bool full; + uint8_t flags; 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); +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 diff --git a/src/events/hid_boot_event.c b/src/events/hid_boot_event.c new file mode 100644 index 0000000..2320d64 --- /dev/null +++ b/src/events/hid_boot_event.c @@ -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)); diff --git a/src/events/hid_boot_event.h b/src/events/hid_boot_event.h new file mode 100644 index 0000000..25f480c --- /dev/null +++ b/src/events/hid_boot_event.h @@ -0,0 +1,45 @@ +#ifndef HID_BOOT_EVENT_H__ +#define HID_BOOT_EVENT_H__ + +#include +#include +#include + +#include +#include + +/* + * 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__ */ diff --git a/src/events/hid_protocol_event.c b/src/events/hid_protocol_event.c index 767a8b0..6e973a0 100644 --- a/src/events/hid_protocol_event.c +++ b/src/events/hid_protocol_event.c @@ -1,10 +1,5 @@ #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[] = { [HID_PROTO_BOOT] = "BOOT", [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); - __ASSERT_NO_MSG(event->transport < ARRAY_SIZE(hid_transport_name)); __ASSERT_NO_MSG(event->protocol < ARRAY_SIZE(hid_protocol_name)); - APP_EVENT_MANAGER_LOG(aeh, "transport=%s protocol=%s", - hid_transport_name[event->transport], + APP_EVENT_MANAGER_LOG(aeh, "protocol=%s", 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); - nrf_profiler_log_encode_uint8(buf, (uint8_t)event->transport); nrf_profiler_log_encode_uint8(buf, (uint8_t)event->protocol); } APP_EVENT_INFO_DEFINE(hid_protocol_event, - ENCODE(NRF_PROFILER_ARG_U8, NRF_PROFILER_ARG_U8), - ENCODE("transport", "protocol"), + ENCODE(NRF_PROFILER_ARG_U8), + ENCODE("protocol"), profile_hid_protocol_event); APP_EVENT_TYPE_DEFINE(hid_protocol_event, diff --git a/src/events/hid_protocol_event.h b/src/events/hid_protocol_event.h index c190a2a..386d59d 100644 --- a/src/events/hid_protocol_event.h +++ b/src/events/hid_protocol_event.h @@ -1,30 +1,49 @@ #ifndef HID_PROTOCOL_EVENT_H__ #define HID_PROTOCOL_EVENT_H__ +#include + #include #include -enum hid_transport_type { - HID_TRANSPORT_BLE = 0, - HID_TRANSPORT_USB, -}; - enum hid_protocol_type { HID_PROTO_BOOT = 0, HID_PROTO_REPORT, }; /* - * HID 传输层在收到主机 set_protocol 请求后上报该事件, - * keyboard_module 会依据当前 active 传输的协议格式打包 hid_report_event。 + * HID 传输层在收到主机 set_protocol 请求后上报该事件。 */ struct hid_protocol_event { struct app_event_header header; - enum hid_transport_type transport; enum hid_protocol_type protocol; }; 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__ */ diff --git a/src/events/hid_report_event.c b/src/events/hid_report_event.c index 33b9204..0459194 100644 --- a/src/events/hid_report_event.c +++ b/src/events/hid_report_event.c @@ -1,26 +1,17 @@ #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) { const struct hid_report_event *event = cast_hid_report_event(aeh); 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->protocol == HID_PROTO_REPORT) { - __ASSERT_NO_MSG(event->dyndata.size >= 1U); + if (event->dyndata.size >= 1U) { report_id = event->dyndata.data[0]; payload_len = event->dyndata.size - 1U; } - APP_EVENT_MANAGER_LOG(aeh, "protocol=%s report_id=0x%02x payload_len=%u", - hid_protocol_name[event->protocol], + APP_EVENT_MANAGER_LOG(aeh, "report_id=0x%02x payload_len=%u", report_id, 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); 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]; } - nrf_profiler_log_encode_uint8(buf, (uint8_t)event->protocol); nrf_profiler_log_encode_uint8(buf, report_id); nrf_profiler_log_encode_uint16(buf, event->dyndata.size); } APP_EVENT_INFO_DEFINE(hid_report_event, ENCODE(NRF_PROFILER_ARG_U8, - NRF_PROFILER_ARG_U8, NRF_PROFILER_ARG_U16), - ENCODE("protocol", "report_id", "len"), + ENCODE("report_id", "len"), profile_hid_report_event); APP_EVENT_TYPE_DEFINE(hid_report_event, diff --git a/src/events/hid_report_event.h b/src/events/hid_report_event.h index 3e5582d..3c7930c 100644 --- a/src/events/hid_report_event.h +++ b/src/events/hid_report_event.h @@ -1,24 +1,45 @@ #ifndef HID_REPORT_EVENT_H__ #define HID_REPORT_EVENT_H__ +#include +#include +#include + #include #include -#include "hid_protocol_event.h" - /* - * HID 输入报告统一事件: - * - protocol 指示当前 dyndata 编码规则; - * - 当 protocol=HID_PROTO_REPORT:dyndata[0]=report_id,dyndata[1..]=payload; - * - 当 protocol=HID_PROTO_BOOT:dyndata 仅包含 boot payload(不含 report_id)。 + * HID Report 输入报告事件: + * - dyndata 编码为 [report_id | payload]; + * - 仅承载 Report protocol 报文。 */ struct hid_report_event { struct app_event_header header; - enum hid_protocol_type protocol; struct event_dyndata dyndata; }; 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__ */ diff --git a/src/events/keyboard_led_event.c b/src/events/keyboard_led_event.c new file mode 100644 index 0000000..f4797fb --- /dev/null +++ b/src/events/keyboard_led_event.c @@ -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)); diff --git a/src/events/keyboard_led_event.h b/src/events/keyboard_led_event.h new file mode 100644 index 0000000..ff77591 --- /dev/null +++ b/src/events/keyboard_led_event.h @@ -0,0 +1,73 @@ +#ifndef KEYBOARD_LED_EVENT_H__ +#define KEYBOARD_LED_EVENT_H__ + +#include +#include + +#include +#include + +/* + * 键盘 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__ */ diff --git a/src/events/keyboard_led_state_event.c b/src/events/keyboard_led_state_event.c deleted file mode 100644 index fcfac10..0000000 --- a/src/events/keyboard_led_state_event.c +++ /dev/null @@ -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)); diff --git a/src/events/keyboard_led_state_event.h b/src/events/keyboard_led_state_event.h deleted file mode 100644 index 5fc5fa2..0000000 --- a/src/events/keyboard_led_state_event.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef KEYBOARD_LED_STATE_EVENT_H__ -#define KEYBOARD_LED_STATE_EVENT_H__ - -#include -#include - -#include -#include - -/* - * 键盘 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__ */ diff --git a/src/events/mode_event.h b/src/events/mode_event.h index 6fa80de..ac10df7 100644 --- a/src/events/mode_event.h +++ b/src/events/mode_event.h @@ -1,6 +1,8 @@ #ifndef MODE_EVENT_H #define MODE_EVENT_H +#include + #include #include @@ -20,4 +22,32 @@ struct 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 diff --git a/src/modules/battery_module.c b/src/modules/battery_module.c index 42a44e6..a689e7f 100644 --- a/src/modules/battery_module.c +++ b/src/modules/battery_module.c @@ -54,16 +54,28 @@ struct battery_status uint8_t soc; }; -static struct k_work_delayable battery_sample_work; -static int16_t battery_adc_sample_buffer; -static atomic_t active; -static struct battery_status last_status; -static bool has_last_status; -static int32_t battery_mv_window[BATTERY_MV_WINDOW_SIZE]; -static int64_t battery_mv_sum; -static size_t battery_mv_count; -static size_t battery_mv_index; -static enum power_manager_level pm_restrict_level = POWER_MANAGER_LEVEL_MAX; +struct battery_filter_state +{ + int32_t window[BATTERY_MV_WINDOW_SIZE]; + int64_t sum; + size_t count; + size_t index; +}; + +struct battery_ctx +{ + 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); @@ -92,8 +104,8 @@ static int adc_sample_once_mv(int32_t *mv) return err; } - sequence.buffer = &battery_adc_sample_buffer; - sequence.buffer_size = sizeof(battery_adc_sample_buffer); + sequence.buffer = &battery.adc_sample_buffer; + sequence.buffer_size = sizeof(battery.adc_sample_buffer); err = adc_read_dt(&battery_adc, &sequence); if (err) @@ -102,12 +114,12 @@ static int adc_sample_once_mv(int32_t *mv) return err; } - *mv = battery_adc_sample_buffer; + *mv = battery.adc_sample_buffer; err = adc_raw_to_millivolts_dt(&battery_adc, mv); if (err) { LOG_WRN("adc_raw_to_millivolts_dt failed (err=%d raw=%d)", - err, battery_adc_sample_buffer); + err, battery.adc_sample_buffer); } 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_mv_sum += battery_mv; - battery_mv_count++; + battery.filter.window[battery.filter.index] = battery_mv; + battery.filter.sum += battery_mv; + battery.filter.count++; } else { - battery_mv_sum -= battery_mv_window[battery_mv_index]; - battery_mv_window[battery_mv_index] = battery_mv; - battery_mv_sum += battery_mv; + battery.filter.sum -= battery.filter.window[battery.filter.index]; + battery.filter.window[battery.filter.index] = battery_mv; + battery.filter.sum += battery_mv; } - battery_mv_index = (battery_mv_index + 1U) % BATTERY_MV_WINDOW_SIZE; - *mv = (int32_t)(battery_mv_sum / (int64_t)battery_mv_count); + battery.filter.index = (battery.filter.index + 1U) % BATTERY_MV_WINDOW_SIZE; + *mv = (int32_t)(battery.filter.sum / (int64_t)battery.filter.count); 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) { - struct battery_status_event *event = new_battery_status_event(); + battery_status_event_submit(status->charging, status->full, status->soc); +} - event->charging = status->charging; - event->full = status->full; - event->soc = status->soc; - - APP_EVENT_SUBMIT(event); +static bool battery_status_changed(const struct battery_status *lhs, + const struct battery_status *rhs) +{ + return (lhs->charging != rhs->charging) || + (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 ? POWER_MANAGER_LEVEL_ALIVE : POWER_MANAGER_LEVEL_SUSPENDED; - if (pm_restrict_level == target) + if (battery.pm_restrict_level == target) return; - pm_restrict_level = target; + battery.pm_restrict_level = 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) { ARG_UNUSED(work); - if (!atomic_get(&active)) + if (!atomic_get(&battery.active)) { return; } @@ -232,18 +261,16 @@ static void battery_sample_fn(struct k_work *work) * 仅在状态发生变化时上报,避免重复事件淹没总线。 * 变化条件:充电标志、满电标志、SOC 任意一个变化。 */ - if (!has_last_status || - (sampled.charging != last_status.charging) || - (sampled.full != last_status.full) || - (sampled.soc != last_status.soc)) + if (!battery.has_last_status || + battery_status_changed(&sampled, &battery.last_status)) { - last_status = sampled; - has_last_status = true; + battery.last_status = sampled; + battery.has_last_status = true; publish_battery_status_event(&sampled); } 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) @@ -283,44 +310,38 @@ static int battery_module_init(void) /* 默认非充电态允许进入 SUSPENDED,但禁止进入 OFF。 */ update_power_restrict_by_charging(false); - k_work_init_delayable(&battery_sample_work, battery_sample_fn); - has_last_status = false; - battery_mv_sum = 0; - battery_mv_count = 0; - battery_mv_index = 0; - atomic_set(&active, false); + k_work_init_delayable(&battery.sample_work, battery_sample_fn); + battery.has_last_status = false; + battery.filter.sum = 0; + battery.filter.count = 0; + battery.filter.index = 0; + atomic_set(&battery.active, false); - atomic_set(&active, true); - (void)gpio_pin_set_dt(&battery_en_gpio, 1); - k_work_reschedule(&battery_sample_work, K_NO_WAIT); + battery_sampling_set_enabled(true); return 0; } static void battery_module_suspend(void) { - if (!atomic_get(&active)) + if (!atomic_get(&battery.active)) { /* 已经处于挂起态,避免重复上报 STANDBY 造成 power_down 循环。 */ return; } - atomic_set(&active, false); - (void)k_work_cancel_delayable(&battery_sample_work); - (void)gpio_pin_set_dt(&battery_en_gpio, 0); + battery_sampling_set_enabled(false); module_set_state(MODULE_STATE_STANDBY); } static void battery_module_resume(void) { - if (atomic_get(&active)) + if (atomic_get(&battery.active)) { return; } - atomic_set(&active, true); - (void)gpio_pin_set_dt(&battery_en_gpio, 1); - k_work_reschedule(&battery_sample_work, K_NO_WAIT); + battery_sampling_set_enabled(true); module_set_state(MODULE_STATE_READY); } diff --git a/src/modules/ble_battery_module.c b/src/modules/ble_battery_module.c index 521e6ff..cc364f3 100644 --- a/src/modules/ble_battery_module.c +++ b/src/modules/ble_battery_module.c @@ -45,7 +45,7 @@ BT_GATT_SERVICE_DEFINE(ble_battery_svc, 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) { return false; diff --git a/src/modules/ble_bond_module.c b/src/modules/ble_bond_module.c index 55d64a7..9ccf683 100644 --- a/src/modules/ble_bond_module.c +++ b/src/modules/ble_bond_module.c @@ -1,4 +1,5 @@ #include +#include #include #include @@ -28,33 +29,45 @@ enum ble_bond_cfg_opt { #define PEER_ID_KEY "peer_id" #define BT_LUT_KEY "bt_lut" -enum state { - STATE_DISABLED, - STATE_IDLE, - STATE_STANDBY, +enum ble_bond_state { + BLE_BOND_STATE_DISABLED, + BLE_BOND_STATE_IDLE, + BLE_BOND_STATE_STANDBY, }; BUILD_ASSERT(CONFIG_BT_ID_MAX >= 2, "Need at least one resettable identity"); #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). */ -static uint8_t bt_stack_id_lut[APP_PEER_COUNT]; -static bool bt_stack_id_lut_valid; +struct ble_bond_storage { + uint8_t bt_stack_id_lut[APP_PEER_COUNT]; + bool bt_stack_id_lut_valid; + uint8_t cur_peer_id; + bool cur_peer_id_valid; +}; -static uint8_t cur_ble_peer_id; -static bool cur_peer_id_valid; +struct ble_bond_ctx { + 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) { - case STATE_DISABLED: + case BLE_BOND_STATE_DISABLED: return "DISABLED"; - case STATE_IDLE: + case BLE_BOND_STATE_IDLE: return "IDLE"; - case STATE_STANDBY: + case BLE_BOND_STATE_STANDBY: return "STANDBY"; default: 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) { - __ASSERT_NO_MSG(app_id < APP_PEER_COUNT); - return bt_stack_id_lut[app_id]; + __ASSERT_NO_MSG(app_id < BLE_BOND_SLOT_COUNT); + return bond.storage.bt_stack_id_lut[app_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; - 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) @@ -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) { - 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. */ - bt_stack_id_lut[i] = i + 1; + bond.storage.bt_stack_id_lut[i] = i + 1; } } 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", - cur_peer_id_valid, bt_stack_id_lut_valid); + bond.storage.cur_peer_id_valid, bond.storage.bt_stack_id_lut_valid); 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", - cur_ble_peer_id, APP_PEER_COUNT - 1); + bond.storage.cur_peer_id, BLE_BOND_SLOT_COUNT - 1); return false; } - for (size_t i = 0; i < ARRAY_SIZE(bt_stack_id_lut); i++) { - if ((bt_stack_id_lut[i] == BT_ID_DEFAULT) || - (bt_stack_id_lut[i] >= CONFIG_BT_ID_MAX)) { + for (size_t i = 0; i < ARRAY_SIZE(bond.storage.bt_stack_id_lut); i++) { + if ((bond.storage.bt_stack_id_lut[i] == BT_ID_DEFAULT) || + (bond.storage.bt_stack_id_lut[i] >= CONFIG_BT_ID_MAX)) { 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; } } @@ -131,29 +146,31 @@ static int settings_set(const char *key, size_t len_rd, ssize_t rc; 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", - PEER_ID_KEY, (uint32_t)len_rd, sizeof(cur_ble_peer_id)); - cur_peer_id_valid = false; + PEER_ID_KEY, (uint32_t)len_rd, sizeof(bond.storage.cur_peer_id)); + bond.storage.cur_peer_id_valid = false; return 0; } - rc = read_cb(cb_arg, &cur_ble_peer_id, sizeof(cur_ble_peer_id)); - cur_peer_id_valid = (rc == sizeof(cur_ble_peer_id)); - if (!cur_peer_id_valid) { + rc = read_cb(cb_arg, &bond.storage.cur_peer_id, sizeof(bond.storage.cur_peer_id)); + bond.storage.cur_peer_id_valid = (rc == sizeof(bond.storage.cur_peer_id)); + if (!bond.storage.cur_peer_id_valid) { LOG_WRN("Settings '%s' read failed: rc=%d", PEER_ID_KEY, (int)rc); } } 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", - BT_LUT_KEY, (uint32_t)len_rd, sizeof(bt_stack_id_lut)); - bt_stack_id_lut_valid = false; + BT_LUT_KEY, (uint32_t)len_rd, sizeof(bond.storage.bt_stack_id_lut)); + bond.storage.bt_stack_id_lut_valid = false; return 0; } - rc = read_cb(cb_arg, bt_stack_id_lut, sizeof(bt_stack_id_lut)); - bt_stack_id_lut_valid = (rc == sizeof(bt_stack_id_lut)); - if (!bt_stack_id_lut_valid) { + rc = read_cb(cb_arg, + bond.storage.bt_stack_id_lut, + 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); } } @@ -183,6 +200,164 @@ static int load_identities(void) 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) { 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) { - 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); if (err) { return err; @@ -244,18 +419,14 @@ static bool handle_config_event(const struct config_event *event) case BLE_BOND_CFG_PEER_SELECT: if (event->dyndata.size >= 1) { uint8_t peer_id = event->dyndata.data[0]; - if (peer_id < APP_PEER_COUNT) { - 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; - } + if (!select_peer(peer_id)) { + rsp->status = CONFIG_STATUS_SUCCESS; } } break; 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; } break; @@ -280,7 +451,7 @@ static bool handle_config_event(const struct config_event *event) rsp_data->event_id = event->event_id; rsp_data->recipient = event->recipient; 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); return true; } @@ -300,12 +471,15 @@ static int init_after_settings_loaded(void) if (!storage_data_is_valid()) { 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(); + 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) { - 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; } @@ -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", - state_name(state), cur_ble_peer_id, get_bt_stack_peer_id(cur_ble_peer_id)); - submit_peer_op_event(PEER_OPERATION_SELECTED, cur_ble_peer_id); + state_name(bond.state), + 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); 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); 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"); int err = init_after_settings_loaded(); if (err) { 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); } } @@ -345,22 +521,26 @@ static bool app_event_handler(const struct app_event_header *aeh) } if (is_power_down_event(aeh)) { - if (state == STATE_IDLE) { - state = STATE_STANDBY; + if (bond.state == BLE_BOND_STATE_IDLE) { + bond.state = BLE_BOND_STATE_STANDBY; module_set_state(MODULE_STATE_OFF); } return false; } if (is_wake_up_event(aeh)) { - if (state == STATE_STANDBY) { - state = STATE_IDLE; + if (bond.state == BLE_BOND_STATE_STANDBY) { + bond.state = BLE_BOND_STATE_IDLE; 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; } + if (is_ble_peer_event(aeh)) { + return handle_ble_peer_event(cast_ble_peer_event(aeh)); + } + if (is_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, power_down_event); APP_EVENT_SUBSCRIBE(MODULE, wake_up_event); +APP_EVENT_SUBSCRIBE(MODULE, ble_peer_event); APP_EVENT_SUBSCRIBE_EARLY(MODULE, config_event); diff --git a/src/modules/ble_hid_module.c b/src/modules/ble_hid_module.c index db248b3..5a45ccd 100644 --- a/src/modules/ble_hid_module.c +++ b/src/modules/ble_hid_module.c @@ -8,9 +8,10 @@ #include #include "hid_protocol_event.h" +#include "hid_boot_event.h" #include "hid_report_event.h" #include "hid_report_descriptor.h" -#include "keyboard_led_state_event.h" +#include "keyboard_led_event.h" #include "mode_event.h" #include @@ -25,43 +26,66 @@ LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF); BT_HIDS_DEF(hids_obj, INPUT_REPORT_COUNT, OUTPUT_REPORT_COUNT, 0); -static struct bt_conn *active_conn; -static enum bt_hids_pm current_pm = BT_HIDS_PM_REPORT; -static bool ble_mode_selected; -static bool num_lock_known; -static bool num_lock_on; +struct ble_hid_link { + struct bt_conn *conn; + enum bt_hids_pm protocol_mode; +}; -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) { - struct hid_protocol_event *event = new_hid_protocol_event(); - - event->transport = HID_TRANSPORT_BLE; - event->protocol = protocol; - APP_EVENT_SUBMIT(event); + hid_protocol_event_submit(protocol); } -/* BLE 输出报告的 bit0 对应 Num Lock。仅在状态变化时上报,避免重复通知。 */ +/* 主机 LED 输出报告变化时才上报,避免重复事件淹没总线。 */ static void publish_num_lock_state_from_led_mask(uint8_t led_mask) { - bool new_num_lock = (led_mask & BIT(0)) != 0U; - - if (num_lock_known && (num_lock_on == new_num_lock)) { + if (ble_hid.led.valid && (ble_hid.led.led_mask == led_mask)) { return; } - num_lock_known = true; - num_lock_on = new_num_lock; + ble_hid.led.valid = true; + ble_hid.led.led_mask = led_mask; - struct keyboard_led_state_event *event = new_keyboard_led_state_event(); - - event->led_mask = led_mask; - event->num_lock = new_num_lock; - APP_EVENT_SUBMIT(event); + keyboard_led_event_submit(led_mask); } 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) { 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"); - if (active_conn) { + if (ble_hid_is_connected()) { publish_hid_protocol_event(HID_PROTO_BOOT); } break; 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"); - if (active_conn) { + if (ble_hid_is_connected()) { publish_hid_protocol_event(HID_PROTO_REPORT); } break; @@ -166,21 +190,19 @@ static void handle_ble_peer_event(const struct ble_peer_event *event) { switch (event->state) { case PEER_STATE_CONNECTED: - __ASSERT_NO_MSG(active_conn == NULL); - active_conn = event->id; - if (bt_hids_connected(&hids_obj, active_conn)) { + __ASSERT_NO_MSG(ble_hid.link.conn == NULL); + ble_hid.link.conn = event->id; + if (bt_hids_connected(&hids_obj, ble_hid.link.conn)) { LOG_WRN("bt_hids_connected failed"); } - /* 连接建立后按当前协议主动同步一次,避免 keyboard_module 等待下一次 set_protocol。 */ - publish_hid_protocol_event(pm_to_protocol(current_pm)); break; case PEER_STATE_DISCONNECTED: - if (active_conn == event->id) { - if (bt_hids_disconnected(&hids_obj, active_conn)) { + if (ble_hid.link.conn == event->id) { + if (bt_hids_disconnected(&hids_obj, ble_hid.link.conn)) { LOG_WRN("bt_hids_disconnected failed"); } - active_conn = NULL; + ble_hid.link.conn = NULL; } break; @@ -189,73 +211,76 @@ 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; } + if (!ble_hid_is_boot_mode()) { + return false; + } + + const uint8_t *payload = hid_boot_event_get_data(event); + size_t payload_len = hid_boot_event_get_size(event); + + if (payload_len != BOOT_KEYBOARD_REPORT_LEN) { + LOG_WRN("Invalid boot keyboard payload len=%u", payload_len); + return false; + } + + int err = bt_hids_boot_kb_inp_rep_send(&hids_obj, ble_hid.link.conn, + payload, payload_len, NULL); + + 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; - /* - * 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)) { + if (data_len < 1U) { return false; } - if (event->protocol == HID_PROTO_BOOT) { - report_id = REPORT_ID_KEYBOARD; - payload = event->dyndata.data; - payload_len = event->dyndata.size; + report_id = data[0]; + payload = &data[1]; + payload_len = data_len - 1U; + + uint8_t rep_index; + + if (report_id == REPORT_ID_KEYBOARD) { + rep_index = 0U; + } else if (report_id == REPORT_ID_CONSUMER) { + rep_index = 1U; } 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; + return false; } - int err = 0; - - if (event->protocol == HID_PROTO_BOOT) { - if (report_id != REPORT_ID_KEYBOARD) { - return false; - } - if (payload_len != BOOT_KEYBOARD_REPORT_LEN) { - LOG_WRN("Invalid boot keyboard payload len=%u", payload_len); - return false; - } - - err = bt_hids_boot_kb_inp_rep_send(&hids_obj, active_conn, - payload, payload_len, NULL); - } else { - uint8_t rep_index; - - if (report_id == REPORT_ID_KEYBOARD) { - rep_index = 0U; - } else if (report_id == REPORT_ID_CONSUMER) { - rep_index = 1U; - } else { - return false; - } - - if (payload_len > UINT8_MAX) { - LOG_WRN("Payload too large=%u", payload_len); - return false; - } - - err = bt_hids_inp_rep_send(&hids_obj, active_conn, rep_index, - payload, (uint8_t)payload_len, NULL); + if (payload_len > UINT8_MAX) { + LOG_WRN("Payload too large=%u", payload_len); + return false; } + int err = bt_hids_inp_rep_send(&hids_obj, ble_hid.link.conn, rep_index, + payload, (uint8_t)payload_len, NULL); + if (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)) { 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; } @@ -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)); } + if (is_hid_boot_event(aeh)) { + return handle_hid_boot_event(cast_hid_boot_event(aeh)); + } + __ASSERT_NO_MSG(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, ble_peer_event); APP_EVENT_SUBSCRIBE(MODULE, mode_event); +APP_EVENT_SUBSCRIBE(MODULE, hid_boot_event); APP_EVENT_SUBSCRIBE(MODULE, hid_report_event); diff --git a/src/modules/ble_slot_ctrl_module.c b/src/modules/ble_slot_ctrl_module.c new file mode 100644 index 0000000..502ec33 --- /dev/null +++ b/src/modules/ble_slot_ctrl_module.c @@ -0,0 +1,281 @@ +#include +#include + +#include + +#define MODULE ble_slot_ctrl +#include +#include +#include +#include + +#include "config_event.h" +#include "mode_event.h" + +#include +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); diff --git a/src/modules/keyboard_module.c b/src/modules/keyboard_module.c index b6c8709..e2dd419 100644 --- a/src/modules/keyboard_module.c +++ b/src/modules/keyboard_module.c @@ -10,9 +10,9 @@ #include #include "hid_report_descriptor.h" +#include "hid_boot_event.h" #include "hid_protocol_event.h" #include "hid_report_event.h" -#include "mode_event.h" #include #include @@ -103,23 +103,19 @@ static const struct hid_keymap *hid_keymap_get_local(uint16_t key_id) struct keyboard_state { uint8_t modifier_bm; uint8_t usage_bm[KEYBOARD_BITMAP_SIZE]; - enum hid_protocol_type ble_protocol; - enum hid_protocol_type usb_protocol; - mode_type_t current_mode; + enum hid_protocol_type current_protocol; uint16_t consumer_usage; }; static struct keyboard_state ks = { - .ble_protocol = HID_PROTO_REPORT, - .usb_protocol = HID_PROTO_REPORT, - .current_mode = MODE_TYPE_COUNT, + .current_protocol = HID_PROTO_REPORT, .consumer_usage = 0, }; -/* 依据当前 mode 选择生效的 HID 协议来源(BLE 或 USB)。 */ +/* 当前 HID 报告编码仅跟最近一次 set_protocol 结果相关。 */ 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 位在当前键盘位图里是否处于按下状态。 */ @@ -182,18 +178,17 @@ static void submit_hid_report(enum hid_protocol_type protocol, const uint8_t *payload, size_t payload_len) { - size_t report_len = (protocol == HID_PROTO_REPORT) ? (payload_len + 1U) : payload_len; - struct hid_report_event *event = new_hid_report_event(report_len); + uint8_t report_buf[KEYBOARD_REPORT_PAYLOAD + 1U]; - event->protocol = protocol; if (protocol == HID_PROTO_REPORT) { - event->dyndata.data[0] = report_id; - memcpy(&event->dyndata.data[1], payload, payload_len); - } else { - memcpy(event->dyndata.data, payload, payload_len); - } + size_t report_len = payload_len + 1U; - 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; } -/* 同步 BLE/USB 传输层上报的当前协议。 */ +/* 记录最近一次 set_protocol 结果。 */ static bool handle_hid_protocol_event(const struct hid_protocol_event *event) { - if (event->transport == HID_TRANSPORT_BLE) { - 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; + ks.current_protocol = hid_protocol_event_get_protocol(event); 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)); } - if (is_mode_event(aeh)) { - return handle_mode_event(cast_mode_event(aeh)); - } - if (is_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_SUBSCRIBE(MODULE, button_event); APP_EVENT_SUBSCRIBE(MODULE, hid_protocol_event); -APP_EVENT_SUBSCRIBE(MODULE, mode_event); APP_EVENT_SUBSCRIBE(MODULE, module_state_event); diff --git a/src/modules/led_state_module.c b/src/modules/led_state_module.c index 58bf563..6399b40 100644 --- a/src/modules/led_state_module.c +++ b/src/modules/led_state_module.c @@ -7,7 +7,7 @@ #include #include -#include "keyboard_led_state_event.h" +#include "keyboard_led_event.h" #include "led_state.h" #include "mode_event.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; } -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(); return false; } @@ -153,8 +153,8 @@ static bool app_event_handler(const struct app_event_header *aeh) if (is_mode_event(aeh)) return handle_mode_event(cast_mode_event(aeh)); - if (is_keyboard_led_state_event(aeh)) - return handle_keyboard_led_state_event(cast_keyboard_led_state_event(aeh)); + if (is_keyboard_led_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)) 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_SUBSCRIBE(MODULE, module_state_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 APP_EVENT_SUBSCRIBE(MODULE, ble_peer_event); APP_EVENT_SUBSCRIBE(MODULE, ble_peer_search_event); diff --git a/src/modules/mode_switch_module.c b/src/modules/mode_switch_module.c index de7110c..0680330 100644 --- a/src/modules/mode_switch_module.c +++ b/src/modules/mode_switch_module.c @@ -115,10 +115,7 @@ static void publish_mode_event(mode_type_t mode) return; current_mode = mode; - struct mode_event *event = new_mode_event(); - - event->mode_type = mode; - APP_EVENT_SUBMIT(event); + mode_event_submit(mode); /* * 模式切换是明确的人机交互动作。这里同步上报 keep_alive_event, * 让 power manager 重置休眠倒计时,避免用户刚切换模式就进入省电流程。 diff --git a/src/modules/usb_hid_module.c b/src/modules/usb_hid_module.c index 663cc62..37e1c6c 100644 --- a/src/modules/usb_hid_module.c +++ b/src/modules/usb_hid_module.c @@ -13,9 +13,10 @@ #include #include "hid_report_descriptor.h" +#include "hid_boot_event.h" #include "hid_protocol_event.h" #include "hid_report_event.h" -#include "keyboard_led_state_event.h" +#include "keyboard_led_event.h" #include "mode_event.h" #include @@ -35,24 +36,30 @@ LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF); * - 只有当 mode 切到 USB 且系统非休眠时才 enable; * - BLE 逻辑保持不变,不在本模块中触碰。 */ -struct usb_hid_ctx { - const struct device *boot_dev; - const struct device *nkro_dev; - const struct device *raw_dev; +struct usb_hid_iface { + const struct device *dev; + bool iface_ready; + bool in_flight; +}; - bool stack_initialized; - bool stack_enabled; - bool stack_error; +enum usb_hid_stack_state { + USB_HID_STACK_STATE_OFF, + USB_HID_STACK_STATE_READY, + USB_HID_STACK_STATE_ACTIVE, + USB_HID_STACK_STATE_ERROR, +}; +struct usb_hid_policy { bool usb_mode_selected; bool pm_suspended; +}; - bool boot_iface_ready; - bool nkro_iface_ready; - bool raw_iface_ready; - bool boot_in_flight; - bool nkro_in_flight; - +struct usb_hid_ctx { + struct usb_hid_iface boot; + struct usb_hid_iface nkro; + struct usb_hid_iface raw; + enum usb_hid_stack_state stack_state; + struct usb_hid_policy policy; 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 raw_report_desc[] = HID_DESC_RAW_64(); -/* 统一入口仅处理单字节 LED 报告并发布事件。 */ -static void process_usb_led_input_report(uint8_t led_report) +static bool usb_hid_stack_is_active(void) { - 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; - event->num_lock = (led_report & BIT(0)) != 0U; - APP_EVENT_SUBMIT(event); +static bool usb_hid_stack_is_error(void) +{ + 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) { 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, @@ -99,12 +138,12 @@ static bool try_extract_led_mask(const struct device *dev, if ((buf == NULL) || (len == 0U)) return false; - if (dev == g_usb_hid.boot_dev) { + if (dev == g_usb_hid.boot.dev) { *led_mask = buf[0]; return true; } - if (dev != g_usb_hid.nkro_dev) + if (dev != g_usb_hid.nkro.dev) return false; 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); - process_usb_led_input_report(led_mask); + keyboard_led_event_submit(led_mask); 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。 * 这里额外检查接口 ready,避免在未枚举完成阶段上报无意义协议切换。 */ - if (g_usb_hid.boot_iface_ready || g_usb_hid.nkro_iface_ready) { - struct hid_protocol_event *event = new_hid_protocol_event(); - - event->transport = HID_TRANSPORT_USB; - event->protocol = new_protocol; - APP_EVENT_SUBMIT(event); + if (g_usb_hid.boot.iface_ready || g_usb_hid.nkro.iface_ready) { + hid_protocol_event_submit(new_protocol); } } @@ -203,13 +238,10 @@ static void hid_stub_input_done(const struct device *dev, const uint8_t *report) * - 仅在这里清除“在途发送”标志,确保“上一包未完成则丢弃新包”的策略可闭环; * - 若收到未知 dev 的回调,仅记录告警,避免静默状态错乱。 */ - if (dev == g_usb_hid.boot_dev) { - g_usb_hid.boot_in_flight = false; - return; - } + struct usb_hid_iface *iface = usb_hid_iface_from_dev(dev); - if (dev == g_usb_hid.nkro_dev) { - g_usb_hid.nkro_in_flight = false; + if (iface) { + iface->in_flight = false; 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); - 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) { - if (dev == g_usb_hid.boot_dev) { - g_usb_hid.boot_iface_ready = ready; - if (!ready) { - g_usb_hid.boot_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; + struct usb_hid_iface *iface = usb_hid_iface_from_dev(dev); + + if (!iface) { + return; } - 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); + iface->iface_ready = ready; + if (!ready) { + iface->in_flight = false; } - } 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) { 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"); APP_EVENT_SUBMIT(new_wake_up_event()); } @@ -305,7 +324,7 @@ static void usbd_msg_cb(struct usbd_context *const usbd_ctx, * 只有在 USB 模式下才允许拉起 USB 栈。 * 这样即使插着线,只要用户切到 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); } break; @@ -321,7 +340,7 @@ static void usbd_msg_cb(struct usbd_context *const usbd_ctx, case USBD_MSG_UDC_ERROR: case USBD_MSG_STACK_ERROR: 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; default: @@ -331,17 +350,17 @@ static void usbd_msg_cb(struct usbd_context *const usbd_ctx, 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"); 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"); 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"); return false; } @@ -356,7 +375,7 @@ static bool usb_hid_devices_ready(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_hid_ops); if (err) { @@ -364,7 +383,7 @@ static int usb_hid_register_hid_devices(void) 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), &report_hid_ops); if (err) { @@ -372,7 +391,7 @@ static int usb_hid_register_hid_devices(void) 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_hid_ops); if (err) { @@ -441,13 +460,13 @@ static int usb_hid_init_usbd_stack(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; } - 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.raw_dev = DEVICE_DT_GET(DT_NODELABEL(raw_hid)); + 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.raw.dev = DEVICE_DT_GET(DT_NODELABEL(raw_hid)); if (!usb_hid_devices_ready()) { return -ENODEV; @@ -468,7 +487,7 @@ static int usb_hid_stack_init(void) return err; } - g_usb_hid.stack_initialized = true; + g_usb_hid.stack_state = USB_HID_STACK_STATE_READY; return 0; } @@ -476,36 +495,39 @@ static int usb_hid_set_enabled(bool enable) { 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(); if (err) { return err; } } - if (g_usb_hid.stack_enabled == enable) { + if (enable && usb_hid_stack_is_active()) { return 0; } - g_usb_hid.stack_enabled = enable; + if (!enable && (g_usb_hid.stack_state == USB_HID_STACK_STATE_READY)) { + return 0; + } if (enable) { err = usbd_enable(&new_kbd_usbd); } else { err = usbd_disable(&new_kbd_usbd); - 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; + usb_hid_clear_runtime_iface_state(); } if (err && (err != -EALREADY)) { 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; } + g_usb_hid.stack_state = enable ? USB_HID_STACK_STATE_ACTIVE : USB_HID_STACK_STATE_READY; return 0; } @@ -516,7 +538,7 @@ static void refresh_usb_state_by_policy(void) * - USB 模式 + 非休眠:启用 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); if (err) { @@ -533,7 +555,7 @@ static bool handle_module_state_event(const struct module_state_event *event) int err = usb_hid_stack_init(); if (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); 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) { - 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(); return false; } static bool handle_power_down_event(void) { - if (g_usb_hid.pm_suspended) { + if (g_usb_hid.policy.pm_suspended) { /* 避免重复上报 STANDBY 导致 power_manager 在 SUSPENDING 期间反复迭代。 */ return false; } - g_usb_hid.pm_suspended = true; + g_usb_hid.policy.pm_suspended = true; refresh_usb_state_by_policy(); module_set_state(MODULE_STATE_STANDBY); return false; @@ -564,16 +586,49 @@ static bool handle_power_down_event(void) static bool handle_wake_up_event(void) { - if (!g_usb_hid.pm_suspended) { + if (!g_usb_hid.policy.pm_suspended) { return false; } - g_usb_hid.pm_suspended = false; + g_usb_hid.policy.pm_suspended = false; refresh_usb_state_by_policy(); module_set_state(MODULE_STATE_READY); 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) { /* @@ -581,63 +636,42 @@ static bool handle_hid_report_event(const struct hid_report_event *event) * - 当前 mode 为 USB; * - 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; } + 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; - if (event->protocol != g_usb_hid.current_protocol) { + if (data_len < 1U) { return false; } - if (event->protocol == HID_PROTO_BOOT) { - const uint8_t *payload = event->dyndata.data; - size_t payload_len = event->dyndata.size; + report_id = data[0]; - 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; - } - - 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) { + if (!g_usb_hid.nkro.iface_ready || !g_usb_hid.nkro.dev) { return false; } if ((report_id != REPORT_ID_KEYBOARD) && (report_id != REPORT_ID_CONSUMER)) { 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); return false; } /* 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) { LOG_WRN("USB report send failed id=0x%02x err=%d", report_id, err); } else { - g_usb_hid.nkro_in_flight = true; + g_usb_hid.nkro.in_flight = true; } 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)); } + if (is_hid_boot_event(aeh)) { + return handle_hid_boot_event(cast_hid_boot_event(aeh)); + } + __ASSERT_NO_MSG(false); return false; } @@ -674,4 +712,5 @@ APP_EVENT_SUBSCRIBE(MODULE, module_state_event); APP_EVENT_SUBSCRIBE(MODULE, mode_event); APP_EVENT_SUBSCRIBE_EARLY(MODULE, power_down_event); APP_EVENT_SUBSCRIBE(MODULE, wake_up_event); +APP_EVENT_SUBSCRIBE_EARLY(MODULE, hid_boot_event); APP_EVENT_SUBSCRIBE_EARLY(MODULE, hid_report_event);