#include #include #include #include #define MODULE usb_device_module #include #include #include #include #include #include #include #include "module_lifecycle.h" #include "usb_function_hook.h" #include "usb_control_event.h" #include "usb_state_event.h" LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF); #define USB_DEVICE_VID 0x1915 #define USB_DEVICE_PID 0x52F0 #define USB_DEVICE_MANUFACTURER "Atguigu" #define USB_DEVICE_PRODUCT "WH Mini Keyboard" USBD_DEVICE_DEFINE(blinky_usbd, DEVICE_DT_GET(DT_NODELABEL(usbd)), USB_DEVICE_VID, USB_DEVICE_PID); USBD_DESC_LANG_DEFINE(blinky_lang); USBD_DESC_MANUFACTURER_DEFINE(blinky_mfr, USB_DEVICE_MANUFACTURER); USBD_DESC_PRODUCT_DEFINE(blinky_product, USB_DEVICE_PRODUCT); USBD_DESC_CONFIG_DEFINE(blinky_fs_cfg_desc, "FS Configuration"); USBD_CONFIGURATION_DEFINE(blinky_fs_config, 0, 250, &blinky_fs_cfg_desc); static const char *const class_blocklist[] = { NULL, }; enum usb_stack_state { USB_STACK_UNINITIALIZED = 0, USB_STACK_READY, USB_STACK_ENABLED, }; enum usb_bus_state { USB_BUS_DISCONNECTED = 0, USB_BUS_POWERED, USB_BUS_ACTIVE, USB_BUS_SUSPENDED, }; struct usb_owner_ctx { struct module_lifecycle_ctx lc; enum usb_stack_state stack; enum usb_bus_state bus; }; static int do_init(void); static int do_start(void); static int do_stop(void); /* Project exception: mode policy suspend/resume controls USB availability, * while global power events still map to standby lifecycle transitions. */ static const struct module_lifecycle_cfg lifecycle_cfg = { .mode = ML_MODE_SUSPEND, .stopped_state = MODULE_STATE_STANDBY, }; static const struct module_lifecycle_ops lifecycle_ops = { .do_init = do_init, .do_start = do_start, .do_stop = do_stop, }; static struct usb_owner_ctx usb_ctx = { .lc = { .state = LC_UNINIT, .cfg = &lifecycle_cfg, .ops = &lifecycle_ops, }, .stack = USB_STACK_UNINITIALIZED, .bus = USB_BUS_DISCONNECTED, }; static inline enum usb_state usb_public_state_get(void) { if (!module_lifecycle_is_running(&usb_ctx.lc) || (usb_ctx.stack == USB_STACK_UNINITIALIZED)) { return USB_STATE_DISABLED; } switch (usb_ctx.bus) { case USB_BUS_DISCONNECTED: return USB_STATE_DISCONNECTED; case USB_BUS_POWERED: return USB_STATE_POWERED; case USB_BUS_ACTIVE: return USB_STATE_ACTIVE; case USB_BUS_SUSPENDED: return USB_STATE_SUSPENDED; default: return USB_STATE_DISABLED; } } static inline void usb_bus_set(enum usb_bus_state state) { if (usb_ctx.bus == state) { return; } usb_ctx.bus = state; if (module_lifecycle_is_initialized(&usb_ctx.lc)) { submit_usb_state(usb_public_state_get()); } } static void update_power_manager_restriction(bool vbus_present) { power_manager_restrict(MODULE_IDX(MODULE), vbus_present ? POWER_MANAGER_LEVEL_ALIVE : POWER_MANAGER_LEVEL_SUSPENDED); } static int usb_descriptors_init(void) { int err; err = usbd_add_descriptor(&blinky_usbd, &blinky_lang); if (err) { return err; } err = usbd_add_descriptor(&blinky_usbd, &blinky_mfr); if (err) { return err; } err = usbd_add_descriptor(&blinky_usbd, &blinky_product); if (err) { return err; } err = usbd_add_configuration(&blinky_usbd, USBD_SPEED_FS, &blinky_fs_config); if (err) { return err; } err = usbd_register_all_classes(&blinky_usbd, USBD_SPEED_FS, 1, class_blocklist); if (err) { return err; } usbd_device_set_code_triple(&blinky_usbd, USBD_SPEED_FS, 0, 0, 0); return 0; } static void usbd_msg_cb(struct usbd_context *const usbd_ctx, const struct usbd_msg *const msg) { ARG_UNUSED(usbd_ctx); if (msg->type == USBD_MSG_CDC_ACM_CONTROL_LINE_STATE) { uint32_t dtr = 0U; int err = uart_line_ctrl_get(msg->dev, UART_LINE_CTRL_DTR, &dtr); if (err) { LOG_WRN("Failed to get CDC DTR (%d)", err); } else { submit_usb_control_cdc_line_state_event(msg->dev, dtr != 0U); } return; } if (msg->type == USBD_MSG_CDC_ACM_LINE_CODING) { uint32_t baudrate = 0U; uint8_t data_bits = 0U; uint8_t stop_bits = 0U; uint8_t parity = 0U; uint8_t flow_ctrl = 0U; int err; err = uart_line_ctrl_get(msg->dev, UART_LINE_CTRL_BAUD_RATE, &baudrate); if (err) { LOG_WRN("Failed to get CDC baudrate (%d)", err); } #ifdef CONFIG_UART_USE_RUNTIME_CONFIGURE { struct uart_config cfg; err = uart_config_get(msg->dev, &cfg); if (err) { LOG_WRN("uart_config_get failed (%d)", err); } else { data_bits = (uint8_t)cfg.data_bits; stop_bits = (uint8_t)cfg.stop_bits; parity = (uint8_t)cfg.parity; flow_ctrl = (uint8_t)cfg.flow_ctrl; } } #endif submit_usb_control_cdc_line_coding_event(msg->dev, baudrate, data_bits, stop_bits, parity, flow_ctrl); return; } switch (msg->type) { case USBD_MSG_VBUS_READY: update_power_manager_restriction(true); usb_bus_set(USB_BUS_POWERED); if (module_lifecycle_is_running(&usb_ctx.lc) && (usb_ctx.stack == USB_STACK_READY)) { int err = do_start(); if (err) { LOG_ERR("USB start on VBUS ready failed (%d)", err); } } break; case USBD_MSG_VBUS_REMOVED: if (usb_ctx.stack == USB_STACK_ENABLED) { int err = do_stop(); if (err) { LOG_ERR("USB stop on VBUS removed failed (%d)", err); } } else { update_power_manager_restriction(false); } if (usb_ctx.stack != USB_STACK_UNINITIALIZED) { usb_ctx.stack = USB_STACK_READY; } usb_bus_set(USB_BUS_DISCONNECTED); break; case USBD_MSG_CONFIGURATION: if (msg->status) { usb_bus_set(USB_BUS_ACTIVE); } else if (usb_ctx.stack == USB_STACK_ENABLED) { usb_bus_set(USB_BUS_POWERED); } break; case USBD_MSG_SUSPEND: if (usb_ctx.stack == USB_STACK_ENABLED) { usb_bus_set(USB_BUS_SUSPENDED); } break; case USBD_MSG_RESUME: if (usb_ctx.stack == USB_STACK_ENABLED) { usb_bus_set(USB_BUS_ACTIVE); } break; default: break; } } static int do_init(void) { int err; usb_ctx.stack = USB_STACK_UNINITIALIZED; usb_ctx.bus = USB_BUS_DISCONNECTED; update_power_manager_restriction(false); STRUCT_SECTION_FOREACH(usb_function_hook, hook) { if (hook->pre_stack_init == NULL) { continue; } err = hook->pre_stack_init(); if (err) { LOG_ERR("USB function hook %s failed (%d)", hook->name, err); return err; } } err = usbd_msg_register_cb(&blinky_usbd, usbd_msg_cb); if (err) { LOG_ERR("usbd_msg_register_cb failed (%d)", err); return err; } err = usb_descriptors_init(); if (err) { LOG_ERR("usb descriptor init failed (%d)", err); return err; } err = usbd_init(&blinky_usbd); if (err) { LOG_ERR("usbd_init failed (%d)", err); return err; } usb_ctx.stack = USB_STACK_READY; return 0; } static int do_start(void) { int err; if (usb_ctx.stack == USB_STACK_ENABLED) { return 0; } if (!usbd_can_detect_vbus(&blinky_usbd)) { err = usbd_enable(&blinky_usbd); if (err) { LOG_ERR("usbd_enable failed (%d)", err); return err; } usb_ctx.stack = USB_STACK_ENABLED; usb_ctx.bus = USB_BUS_POWERED; update_power_manager_restriction(true); return 0; } if (usb_ctx.bus == USB_BUS_DISCONNECTED) { return 0; } err = usbd_enable(&blinky_usbd); if (err) { LOG_ERR("usbd_enable failed (%d)", err); return err; } usb_ctx.stack = USB_STACK_ENABLED; update_power_manager_restriction(true); return 0; } static int do_stop(void) { int err; if (usb_ctx.stack == USB_STACK_ENABLED) { err = usbd_disable(&blinky_usbd); if (err) { LOG_ERR("usbd_disable failed (%d)", err); return err; } } usb_ctx.stack = USB_STACK_READY; update_power_manager_restriction(false); return 0; } static bool handle_module_state_event(const struct module_state_event *event) { if (!check_state(event, MODULE_ID(main), MODULE_STATE_READY)) { return false; } if (module_set_lifecycle(&usb_ctx.lc, LC_RUNNING) == 0) { submit_usb_state(usb_public_state_get()); } return false; } static bool handle_module_suspend_req_event( const struct module_suspend_req_event *event) { if ((event->sink_module_id != MODULE_ID(MODULE)) || (usb_ctx.lc.state != LC_RUNNING)) { return false; } if (module_set_lifecycle(&usb_ctx.lc, LC_SUSPENDED) == 0) { submit_usb_state(usb_public_state_get()); } return false; } static bool handle_module_resume_req_event( const struct module_resume_req_event *event) { if ((event->sink_module_id != MODULE_ID(MODULE)) || (usb_ctx.lc.state != LC_SUSPENDED)) { return false; } if (module_set_lifecycle(&usb_ctx.lc, LC_RUNNING) == 0) { submit_usb_state(usb_public_state_get()); } 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_module_suspend_req_event(aeh)) { return handle_module_suspend_req_event( cast_module_suspend_req_event(aeh)); } if (is_module_resume_req_event(aeh)) { return handle_module_resume_req_event( cast_module_resume_req_event(aeh)); } if (is_power_down_event(aeh)) { if (module_lifecycle_is_initialized(&usb_ctx.lc)) { if (module_set_lifecycle(&usb_ctx.lc, LC_STOPPED) == 0) { submit_usb_state(usb_public_state_get()); } } return false; } if (is_wake_up_event(aeh)) { if (module_lifecycle_is_initialized(&usb_ctx.lc)) { if (module_set_lifecycle(&usb_ctx.lc, LC_RUNNING) == 0) { submit_usb_state(usb_public_state_get()); } } return false; } return false; } APP_EVENT_LISTENER(MODULE, app_event_handler); APP_EVENT_SUBSCRIBE(MODULE, module_state_event); APP_EVENT_SUBSCRIBE(MODULE, module_suspend_req_event); APP_EVENT_SUBSCRIBE(MODULE, module_resume_req_event); APP_EVENT_SUBSCRIBE_EARLY(MODULE, power_down_event); APP_EVENT_SUBSCRIBE(MODULE, wake_up_event);