diff --git a/CMakeLists.txt b/CMakeLists.txt index d84b4f8..b559b5c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,9 +16,11 @@ target_sources(app PRIVATE src/events/battery_status_event.c src/events/config_event.c src/events/mode_event.c + src/events/usb_hid_event.c src/modules/battery_module.c src/modules/ble_bond_module.c src/modules/button_map_module.c src/modules/mode_switch_module.c + src/modules/usb_hid_module.c src/modules/hids_module.c ) diff --git a/app.overlay b/app.overlay index f57dfdb..a552a65 100644 --- a/app.overlay +++ b/app.overlay @@ -5,6 +5,36 @@ vbat-en-gpios = <&gpio0 9 GPIO_ACTIVE_HIGH>; io-channels = <&adc 5>, <&adc 7>; }; + + hid_dev_0: hid_dev_0 { + compatible = "zephyr,hid-device"; + label = "HID_BOOT"; + protocol-code = "keyboard"; + in-report-size = <8>; + in-polling-period-us = <1000>; + out-report-size = <8>; + out-polling-period-us = <1000>; + }; + + hid_dev_1: hid_dev_1 { + compatible = "zephyr,hid-device"; + label = "HID_NKRO"; + protocol-code = "none"; + in-report-size = <31>; + in-polling-period-us = <1000>; + out-report-size = <8>; + out-polling-period-us = <1000>; + }; + + raw_hid: hid_dev_2 { + compatible = "zephyr,hid-device"; + label = "HID_RAW"; + protocol-code = "none"; + in-report-size = <64>; + in-polling-period-us = <1000>; + out-report-size = <64>; + out-polling-period-us = <1000>; + }; }; &gpio0 { @@ -29,11 +59,15 @@ }; &i2c1 { - status = "okay"; + status = "okay"; - ip5305: pmic@75 { - status = "okay"; - /* 试验项:调整 IP5305 KEY 保活周期,观察 I2C 失败窗口是否随周期移动。 */ - keepalive-interval-ms = <10000>; - }; + ip5305: pmic@75 { + status = "okay"; + /* 试验项:调整 IP5305 KEY 保活周期,观察 I2C 失败窗口是否随周期移动。 */ + keepalive-interval-ms = <10000>; + }; +}; + +&usbd { + status = "okay"; }; diff --git a/prj.conf b/prj.conf index 99787d9..ba8e33f 100644 --- a/prj.conf +++ b/prj.conf @@ -2,6 +2,10 @@ CONFIG_CAF=y CONFIG_HEAP_MEM_POOL_SIZE=2048 CONFIG_LOG=y CONFIG_BOOTLOADER_MCUBOOT=y +CONFIG_ASSERT=y +CONFIG_ASSERT_VERBOSE=y +CONFIG_RESET_ON_FATAL_ERROR=n +CONFIG_FAULT_DUMP=2 CONFIG_BT=y CONFIG_BT_PERIPHERAL=y @@ -36,6 +40,13 @@ CONFIG_BT_HIDS_INPUT_REP_MAX=2 CONFIG_BT_HIDS_OUTPUT_REP_MAX=1 CONFIG_BT_HIDS_FEATURE_REP_MAX=0 +CONFIG_USB_DEVICE_STACK_NEXT=y +CONFIG_USBD_HID_SUPPORT=y +CONFIG_UDC_BUF_POOL_SIZE=8192 +CONFIG_UDC_BUF_COUNT=32 +CONFIG_USBD_MAX_UDC_MSG=20 +CONFIG_USBD_MSG_SLAB_COUNT=16 + CONFIG_LED=y CONFIG_LED_GPIO=y CONFIG_CAF_LEDS=y diff --git a/src/events/usb_hid_event.c b/src/events/usb_hid_event.c new file mode 100644 index 0000000..db9330b --- /dev/null +++ b/src/events/usb_hid_event.c @@ -0,0 +1,58 @@ +#include "usb_hid_event.h" + +static const char *const usb_hid_evt_type_name[] = { + [USB_HID_EVT_STATE_REPORT] = "STATE_REPORT", +}; + +static const char *const usb_hid_usbd_state_name[] = { + [USB_HID_USBD_DISCONNECTED] = "DISCONNECTED", + [USB_HID_USBD_CONNECTED] = "CONNECTED", + [USB_HID_USBD_SUSPENDED] = "SUSPENDED", +}; + +static const char *const usb_hid_stack_state_name[] = { + [USB_HID_STACK_OFF] = "OFF", + [USB_HID_STACK_READY] = "READY", + [USB_HID_STACK_ACTIVE] = "ACTIVE", + [USB_HID_STACK_SUSPENDED] = "SUSPENDED", + [USB_HID_STACK_ERROR] = "ERROR", +}; + +static void log_usb_hid_event(const struct app_event_header *aeh) +{ + const struct usb_hid_event *event = cast_usb_hid_event(aeh); + + __ASSERT_NO_MSG(event->evt_type < ARRAY_SIZE(usb_hid_evt_type_name)); + __ASSERT_NO_MSG(event->usbd_state < ARRAY_SIZE(usb_hid_usbd_state_name)); + __ASSERT_NO_MSG(event->hid_state < ARRAY_SIZE(usb_hid_stack_state_name)); + + APP_EVENT_MANAGER_LOG(aeh, "type=%s en=%u usbd=%s hid=%s", + usb_hid_evt_type_name[event->evt_type], + event->enable, + usb_hid_usbd_state_name[event->usbd_state], + usb_hid_stack_state_name[event->hid_state]); +} + +static void profile_usb_hid_event(struct log_event_buf *buf, + const struct app_event_header *aeh) +{ + const struct usb_hid_event *event = cast_usb_hid_event(aeh); + + nrf_profiler_log_encode_uint8(buf, (uint8_t)event->evt_type); + nrf_profiler_log_encode_uint8(buf, (uint8_t)event->enable); + nrf_profiler_log_encode_uint8(buf, (uint8_t)event->usbd_state); + nrf_profiler_log_encode_uint8(buf, (uint8_t)event->hid_state); +} + +APP_EVENT_INFO_DEFINE(usb_hid_event, + ENCODE(NRF_PROFILER_ARG_U8, + NRF_PROFILER_ARG_U8, + NRF_PROFILER_ARG_U8, + NRF_PROFILER_ARG_U8), + ENCODE("evt_type", "enable", "usbd", "hid"), + profile_usb_hid_event); + +APP_EVENT_TYPE_DEFINE(usb_hid_event, + log_usb_hid_event, + &usb_hid_event_info, + APP_EVENT_FLAGS_CREATE(APP_EVENT_TYPE_FLAGS_INIT_LOG_ENABLE)); diff --git a/src/events/usb_hid_event.h b/src/events/usb_hid_event.h new file mode 100644 index 0000000..d0293ef --- /dev/null +++ b/src/events/usb_hid_event.h @@ -0,0 +1,41 @@ +#ifndef USB_HID_EVENT_H__ +#define USB_HID_EVENT_H__ + +#include +#include + +#include +#include + +enum usb_hid_event_type { + USB_HID_EVT_STATE_REPORT = 0, +}; + +/* USB 连接层状态(偏“链路可用性”) */ +enum usb_hid_usbd_state { + USB_HID_USBD_DISCONNECTED = 0, + USB_HID_USBD_CONNECTED, + USB_HID_USBD_SUSPENDED, +}; + +/* HID 协议栈状态(偏“服务是否运行”) */ +enum usb_hid_stack_state { + USB_HID_STACK_OFF = 0, + USB_HID_STACK_READY, + USB_HID_STACK_ACTIVE, + USB_HID_STACK_SUSPENDED, + USB_HID_STACK_ERROR, +}; + +struct usb_hid_event { + struct app_event_header header; + + enum usb_hid_event_type evt_type; + bool enable; + enum usb_hid_usbd_state usbd_state; + enum usb_hid_stack_state hid_state; +}; + +APP_EVENT_TYPE_DECLARE(usb_hid_event); + +#endif /* USB_HID_EVENT_H__ */ diff --git a/src/modules/battery_module.c b/src/modules/battery_module.c index be287e4..f78fbb7 100644 --- a/src/modules/battery_module.c +++ b/src/modules/battery_module.c @@ -282,6 +282,12 @@ static int battery_module_init(void) static void battery_module_suspend(void) { + if (!atomic_get(&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); diff --git a/src/modules/ble_bond_module.c b/src/modules/ble_bond_module.c index 0741575..55d64a7 100644 --- a/src/modules/ble_bond_module.c +++ b/src/modules/ble_bond_module.c @@ -1,5 +1,6 @@ #include #include +#include #include @@ -46,6 +47,20 @@ static bool bt_stack_id_lut_valid; static uint8_t cur_ble_peer_id; static bool cur_peer_id_valid; +static const char *state_name(enum state s) +{ + switch (s) { + case STATE_DISABLED: + return "DISABLED"; + case STATE_IDLE: + return "IDLE"; + case STATE_STANDBY: + return "STANDBY"; + default: + return "UNKNOWN"; + } +} + static uint8_t get_bt_stack_peer_id(uint8_t app_id) { __ASSERT_NO_MSG(app_id < APP_PEER_COUNT); @@ -87,16 +102,22 @@ static void init_bt_stack_id_lut(void) static bool storage_data_is_valid(void) { if (!cur_peer_id_valid || !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); return false; } if (cur_ble_peer_id >= APP_PEER_COUNT) { + LOG_WRN("Stored peer id out of range: peer_id=%u max=%u", + cur_ble_peer_id, APP_PEER_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)) { + LOG_WRN("Stored LUT invalid at idx=%u value=%u", + (uint32_t)i, bt_stack_id_lut[i]); return false; } } @@ -111,20 +132,30 @@ static int settings_set(const char *key, size_t len_rd, if (!strcmp(key, PEER_ID_KEY)) { if (len_rd != sizeof(cur_ble_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; 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) { + 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)) { + 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; 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) { + LOG_WRN("Settings '%s' read failed: rc=%d", BT_LUT_KEY, (int)rc); + } } return 0; @@ -132,21 +163,24 @@ static int settings_set(const char *key, size_t len_rd, SETTINGS_STATIC_HANDLER_DEFINE(ble_bond, MODULE_NAME, NULL, settings_set, NULL, NULL); -static void load_identities(void) +static int load_identities(void) { bt_addr_le_t addrs[CONFIG_BT_ID_MAX]; size_t count = ARRAY_SIZE(addrs); bt_id_get(addrs, &count); + LOG_INF("Identity count before ensure: %u / %u", (uint32_t)count, CONFIG_BT_ID_MAX); for (; count < CONFIG_BT_ID_MAX; count++) { int err = bt_id_create(NULL, NULL); if (err < 0) { LOG_ERR("Cannot create identity (err:%d)", err); - module_set_state(MODULE_STATE_ERROR); - return; + return err; } + LOG_INF("Created identity idx=%u", (uint32_t)count); } + + return 0; } static int erase_peer(uint8_t app_id) @@ -258,21 +292,33 @@ static bool handle_config_event(const struct config_event *event) static int init_after_settings_loaded(void) { - load_identities(); - if (state == STATE_DISABLED) { - return -EFAULT; + int err = load_identities(); + if (err) { + LOG_ERR("Identity initialization failed: %d", err); + return err; } if (!storage_data_is_valid()) { + LOG_WRN("Stored BLE bond data invalid, reinitializing defaults"); cur_ble_peer_id = 0; init_bt_stack_id_lut(); - if (store_peer_id(cur_ble_peer_id) || store_bt_stack_id_lut()) { + err = store_peer_id(cur_ble_peer_id); + if (err) { + LOG_ERR("Failed to store peer_id=%u (err:%d)", cur_ble_peer_id, err); + return -EIO; + } + + err = store_bt_stack_id_lut(); + if (err) { + LOG_ERR("Failed to store bt_stack_id_lut (err:%d)", err); return -EIO; } } state = 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); module_set_state(MODULE_STATE_READY); @@ -286,7 +332,11 @@ static bool app_event_handler(const struct app_event_header *aeh) if (check_state(event, MODULE_ID(settings_loader), MODULE_STATE_READY) && (state == STATE_DISABLED)) { - if (init_after_settings_loaded()) { + 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)); module_set_state(MODULE_STATE_ERROR); } } diff --git a/src/modules/mode_switch_module.c b/src/modules/mode_switch_module.c index 182b910..de7110c 100644 --- a/src/modules/mode_switch_module.c +++ b/src/modules/mode_switch_module.c @@ -167,6 +167,9 @@ static void mode_sample_fn(struct k_work *work) static void mode_switch_suspend(void) { + if (!atomic_get(&active)) + return; + atomic_set(&active, false); (void)k_work_cancel_delayable(&mode_sample_work); module_set_state(MODULE_STATE_STANDBY); diff --git a/src/modules/usb_hid_module.c b/src/modules/usb_hid_module.c new file mode 100644 index 0000000..d89d969 --- /dev/null +++ b/src/modules/usb_hid_module.c @@ -0,0 +1,546 @@ +#include +#include + +#include +#include +#include +#include + +#include +#include + +#define MODULE usb_hid +#include + +#include "hid_report_descriptor.h" +#include "mode_event.h" +#include "usb_hid_event.h" + +#include +LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF); + +#define APP_USB_VID 0x1209 +#define APP_USB_PID 0x0001 + +/* + * 模块目标: + * 1) 模块内聚控制 USB HID 栈生命周期(初始化/启用/禁用),不依赖外部 usb_hid_event 控制。 + * 2) 对外统一上报 usb_hid_event,供 LED/上层状态机消费。 + * 3) 仅响应 mode_event(USB/BLE/2.4G)和 power_event(休眠/唤醒)。 + * + * 约束: + * - 启动时只做 USB 设备初始化,不自动 enable USB 栈; + * - 只有当 mode 切到 USB 且系统非休眠时才 enable; + * - BLE 逻辑保持不变,不在本模块中触碰。 + */ +struct usb_hid_ctx { + const struct device *boot_dev; + const struct device *nkro_dev; + const struct device *raw_dev; + + bool stack_initialized; + bool stack_enabled; + bool stack_error; + + bool usb_mode_selected; + bool pm_suspended; + + bool boot_iface_ready; + bool nkro_iface_ready; + bool raw_iface_ready; + + enum usb_hid_usbd_state usbd_state; + enum usb_hid_stack_state hid_state; +}; + +static struct usb_hid_ctx g_usb_hid = { + .usbd_state = USB_HID_USBD_DISCONNECTED, + .hid_state = USB_HID_STACK_OFF, +}; + +USBD_DEVICE_DEFINE(new_kbd_usbd, + DEVICE_DT_GET(DT_NODELABEL(usbd)), + APP_USB_VID, APP_USB_PID); +USBD_DESC_LANG_DEFINE(new_kbd_lang); +USBD_DESC_MANUFACTURER_DEFINE(new_kbd_mfr, "new_kbd"); +USBD_DESC_PRODUCT_DEFINE(new_kbd_product, "new_kbd composite HID"); +USBD_DESC_CONFIG_DEFINE(new_kbd_fs_cfg_desc, "FS Configuration"); +USBD_CONFIGURATION_DEFINE(new_kbd_fs_config, 0, 100, &new_kbd_fs_cfg_desc); + +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(); + +static void publish_usb_hid_state(void) +{ + struct usb_hid_event *event = new_usb_hid_event(); + + event->evt_type = USB_HID_EVT_STATE_REPORT; + event->enable = g_usb_hid.stack_enabled; + event->usbd_state = g_usb_hid.usbd_state; + event->hid_state = g_usb_hid.hid_state; + APP_EVENT_SUBMIT(event); +} + +static void recompute_hid_state(void) +{ + enum usb_hid_stack_state new_hid_state; + + if (g_usb_hid.stack_error) { + new_hid_state = USB_HID_STACK_ERROR; + } else if (g_usb_hid.pm_suspended && g_usb_hid.stack_enabled) { + new_hid_state = USB_HID_STACK_SUSPENDED; + } else if (!g_usb_hid.stack_initialized) { + new_hid_state = USB_HID_STACK_OFF; + } else if (g_usb_hid.stack_enabled && + (g_usb_hid.boot_iface_ready || + g_usb_hid.nkro_iface_ready || + g_usb_hid.raw_iface_ready)) { + new_hid_state = USB_HID_STACK_ACTIVE; + } else { + /* + * 栈已初始化但未进入 ACTIVE(例如刚 enable 还未配置、或 mode 切走后 disable)。 + * 用 READY 表示“协议栈可用但当前未承载有效 HID 会话”。 + */ + new_hid_state = USB_HID_STACK_READY; + } + + if (g_usb_hid.hid_state != new_hid_state) { + g_usb_hid.hid_state = new_hid_state; + publish_usb_hid_state(); + } +} + +static void set_usbd_state(enum usb_hid_usbd_state state) +{ + if (g_usb_hid.usbd_state != state) { + g_usb_hid.usbd_state = state; + publish_usb_hid_state(); + } +} + +static int hid_stub_get_report(const struct device *dev, + uint8_t type, uint8_t id, + uint16_t len, uint8_t *buf) +{ + ARG_UNUSED(dev); + ARG_UNUSED(type); + ARG_UNUSED(id); + ARG_UNUSED(len); + ARG_UNUSED(buf); + return -ENOTSUP; +} + +static int hid_stub_set_report(const struct device *dev, + uint8_t type, uint8_t id, + uint16_t len, const uint8_t *buf) +{ + ARG_UNUSED(dev); + ARG_UNUSED(type); + ARG_UNUSED(id); + ARG_UNUSED(len); + ARG_UNUSED(buf); + return 0; +} + +static void hid_stub_set_idle(const struct device *dev, uint8_t id, uint32_t duration) +{ + ARG_UNUSED(dev); + ARG_UNUSED(id); + ARG_UNUSED(duration); +} + +static uint32_t hid_stub_get_idle(const struct device *dev, uint8_t id) +{ + ARG_UNUSED(dev); + ARG_UNUSED(id); + return 0; +} + +static void hid_stub_set_protocol(const struct device *dev, uint8_t proto) +{ + ARG_UNUSED(dev); + ARG_UNUSED(proto); +} + +static void hid_stub_input_done(const struct device *dev, const uint8_t *report) +{ + ARG_UNUSED(dev); + ARG_UNUSED(report); +} + +static void hid_stub_output_report(const struct device *dev, uint16_t len, const uint8_t *buf) +{ + ARG_UNUSED(dev); + ARG_UNUSED(len); + ARG_UNUSED(buf); +} + +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; + } else if (dev == g_usb_hid.nkro_dev) { + g_usb_hid.nkro_iface_ready = ready; + } else if (dev == g_usb_hid.raw_dev) { + g_usb_hid.raw_iface_ready = ready; + } + + if (ready) { + set_usbd_state(USB_HID_USBD_CONNECTED); + } + + recompute_hid_state(); +} + +static const struct hid_device_ops boot_hid_ops = { + .iface_ready = hid_iface_ready_cb, + .get_report = hid_stub_get_report, + .set_report = hid_stub_set_report, + .set_idle = hid_stub_set_idle, + .get_idle = hid_stub_get_idle, + .set_protocol = hid_stub_set_protocol, + .input_report_done = hid_stub_input_done, + .output_report = hid_stub_output_report, +}; + +static const struct hid_device_ops report_hid_ops = { + .iface_ready = hid_iface_ready_cb, + .get_report = hid_stub_get_report, + .set_report = hid_stub_set_report, + .set_idle = hid_stub_set_idle, + .get_idle = hid_stub_get_idle, + .input_report_done = hid_stub_input_done, + .output_report = hid_stub_output_report, +}; + +static const struct hid_device_ops raw_hid_ops = { + .iface_ready = hid_iface_ready_cb, + .get_report = hid_stub_get_report, + .set_report = hid_stub_set_report, + .set_idle = hid_stub_set_idle, + .get_idle = hid_stub_get_idle, + .input_report_done = hid_stub_input_done, + .output_report = hid_stub_output_report, +}; + +static void usbd_msg_cb(struct usbd_context *const usbd_ctx, + const struct usbd_msg *const msg) +{ + switch (msg->type) { + case USBD_MSG_VBUS_READY: + set_usbd_state(USB_HID_USBD_CONNECTED); + /* + * 只有在 USB 模式下才允许拉起 USB 栈。 + * 这样即使插着线,只要用户切到 BLE/2.4G,也不会强制进入 USB HID。 + */ + if (usbd_can_detect_vbus(usbd_ctx) && g_usb_hid.stack_enabled) { + (void)usbd_enable(usbd_ctx); + } + break; + + case USBD_MSG_VBUS_REMOVED: + set_usbd_state(USB_HID_USBD_DISCONNECTED); + break; + + case USBD_MSG_SUSPEND: + set_usbd_state(USB_HID_USBD_SUSPENDED); + break; + + case USBD_MSG_RESUME: + set_usbd_state(USB_HID_USBD_CONNECTED); + break; + + case USBD_MSG_CONFIGURATION: + set_usbd_state(USB_HID_USBD_CONNECTED); + break; + + 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; + recompute_hid_state(); + break; + + default: + break; + } +} + +static bool usb_hid_devices_ready(void) +{ + 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)) { + LOG_ERR("HID nkro device is not ready"); + return false; + } + + if (!device_is_ready(g_usb_hid.raw_dev)) { + LOG_ERR("HID raw device is not ready"); + return false; + } + + if (!device_is_ready(DEVICE_DT_GET(DT_NODELABEL(usbd)))) { + LOG_ERR("USBD device is not ready"); + return false; + } + + return true; +} + +static int usb_hid_register_hid_devices(void) +{ + int err = hid_device_register(g_usb_hid.boot_dev, + boot_report_desc, sizeof(boot_report_desc), + &boot_hid_ops); + if (err) { + LOG_ERR("hid_device_register(boot) failed: %d", err); + return err; + } + + err = hid_device_register(g_usb_hid.nkro_dev, + nkro_report_desc, sizeof(nkro_report_desc), + &report_hid_ops); + if (err) { + LOG_ERR("hid_device_register(nkro) failed: %d", err); + return err; + } + + err = hid_device_register(g_usb_hid.raw_dev, + raw_report_desc, sizeof(raw_report_desc), + &raw_hid_ops); + if (err) { + LOG_ERR("hid_device_register(raw) failed: %d", err); + return err; + } + + return 0; +} + +static int usb_hid_configure_usbd(void) +{ + int err = usbd_add_descriptor(&new_kbd_usbd, &new_kbd_lang); + if (err) { + LOG_ERR("usbd_add_descriptor(lang) failed: %d", err); + return err; + } + + err = usbd_add_descriptor(&new_kbd_usbd, &new_kbd_mfr); + if (err) { + LOG_ERR("usbd_add_descriptor(mfr) failed: %d", err); + return err; + } + + err = usbd_add_descriptor(&new_kbd_usbd, &new_kbd_product); + if (err) { + LOG_ERR("usbd_add_descriptor(product) failed: %d", err); + return err; + } + + err = usbd_add_configuration(&new_kbd_usbd, USBD_SPEED_FS, &new_kbd_fs_config); + if (err) { + LOG_ERR("usbd_add_configuration failed: %d", err); + return err; + } + + err = usbd_register_all_classes(&new_kbd_usbd, USBD_SPEED_FS, 1, NULL); + if (err) { + LOG_ERR("usbd_register_all_classes failed: %d", err); + return err; + } + + return 0; +} + +static int usb_hid_init_usbd_stack(void) +{ + int err; + + usbd_device_set_code_triple(&new_kbd_usbd, USBD_SPEED_FS, 0, 0, 0); + + err = usbd_msg_register_cb(&new_kbd_usbd, usbd_msg_cb); + if (err) { + LOG_ERR("usbd_msg_register_cb failed: %d", err); + return err; + } + + err = usbd_init(&new_kbd_usbd); + if (err && (err != -EALREADY)) { + LOG_ERR("usbd_init failed: %d", err); + return err; + } + + return 0; +} + +static int usb_hid_stack_init(void) +{ + if (g_usb_hid.stack_initialized) { + 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)); + + if (!usb_hid_devices_ready()) { + return -ENODEV; + } + + int err = usb_hid_register_hid_devices(); + if (err) { + return err; + } + + err = usb_hid_configure_usbd(); + if (err) { + return err; + } + + err = usb_hid_init_usbd_stack(); + if (err) { + return err; + } + + g_usb_hid.stack_initialized = true; + recompute_hid_state(); + return 0; +} + +static int usb_hid_set_enabled(bool enable) +{ + int err; + + if (!g_usb_hid.stack_initialized) { + err = usb_hid_stack_init(); + if (err) { + return err; + } + } + + if (g_usb_hid.stack_enabled == enable) { + return 0; + } + + g_usb_hid.stack_enabled = enable; + + 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; + set_usbd_state(USB_HID_USBD_DISCONNECTED); + } + + if (err && (err != -EALREADY)) { + LOG_ERR("usbd_%s failed: %d", enable ? "enable" : "disable", err); + g_usb_hid.stack_error = true; + recompute_hid_state(); + return err; + } + + recompute_hid_state(); + publish_usb_hid_state(); + return 0; +} + +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; + int err = usb_hid_set_enabled(should_enable); + + if (err) { + LOG_ERR("usb_hid_set_enabled(%d) failed: %d", should_enable, err); + } +} + +static bool handle_module_state_event(const struct module_state_event *event) +{ + if (!check_state(event, MODULE_ID(main), MODULE_STATE_READY)) { + return false; + } + + int err = usb_hid_stack_init(); + if (err) { + LOG_ERR("USB HID stack init failed: %d", err); + g_usb_hid.stack_error = true; + module_set_state(MODULE_STATE_ERROR); + recompute_hid_state(); + return false; + } + + module_set_state(MODULE_STATE_READY); + publish_usb_hid_state(); + return false; +} + +static bool handle_mode_event(const struct mode_event *event) +{ + g_usb_hid.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) { + /* 避免重复上报 STANDBY 导致 power_manager 在 SUSPENDING 期间反复迭代。 */ + return false; + } + + g_usb_hid.pm_suspended = true; + refresh_usb_state_by_policy(); + module_set_state(MODULE_STATE_STANDBY); + return false; +} + +static bool handle_wake_up_event(void) +{ + if (!g_usb_hid.pm_suspended) { + return false; + } + + g_usb_hid.pm_suspended = false; + refresh_usb_state_by_policy(); + module_set_state(MODULE_STATE_READY); + return false; +} + +static bool app_event_handler(const struct app_event_header *aeh) +{ + if (is_module_state_event(aeh)) { + return handle_module_state_event(cast_module_state_event(aeh)); + } + + if (is_mode_event(aeh)) { + return handle_mode_event(cast_mode_event(aeh)); + } + + if (is_power_down_event(aeh)) { + return handle_power_down_event(); + } + + if (is_wake_up_event(aeh)) { + return handle_wake_up_event(); + } + + __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(MODULE, wake_up_event);