#include #include #include #include #define MODULE usb_device_module #include #include #include #include #include "usb_device_module.h" #include "usb_device_state_event.h" #include "usb_function_ready_event.h" #include "usb_prepare_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" #define USB_REQUIRED_FUNCTION_MASK (USB_FUNCTION_HID | USB_FUNCTION_CDC_ACM) 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, }; static bool initialized; static bool running; static bool prepare_broadcasted; static bool usbd_initialized; static bool usb_enabled; static uint8_t ready_function_mask; static enum usb_device_state device_state = USB_DEVICE_STATE_DISCONNECTED; static void submit_usb_device_state_event(enum usb_device_state state) { struct usb_device_state_event *event = new_usb_device_state_event(); event->state = state; APP_EVENT_SUBMIT(event); } static void submit_usb_prepare_event(void) { struct usb_prepare_event *event = new_usb_prepare_event(); APP_EVENT_SUBMIT(event); } static void update_usb_device_state(enum usb_device_state state) { if (device_state == state) { return; } device_state = state; submit_usb_device_state_event(state); } 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); switch (msg->type) { case USBD_MSG_VBUS_READY: if (!usb_enabled) { int err = usbd_enable(&blinky_usbd); if (err) { LOG_ERR("usbd_enable failed (%d)", err); } else { usb_enabled = true; update_usb_device_state(USB_DEVICE_STATE_POWERED); } } break; case USBD_MSG_VBUS_REMOVED: if (usb_enabled) { int err = usbd_disable(&blinky_usbd); if (err) { LOG_ERR("usbd_disable failed (%d)", err); } } usb_enabled = false; update_usb_device_state(USB_DEVICE_STATE_DISCONNECTED); break; case USBD_MSG_CONFIGURATION: if (msg->status) { update_usb_device_state(USB_DEVICE_STATE_ACTIVE); } else if (usb_enabled) { update_usb_device_state(USB_DEVICE_STATE_POWERED); } break; case USBD_MSG_SUSPEND: if (usb_enabled) { update_usb_device_state(USB_DEVICE_STATE_SUSPENDED); } break; case USBD_MSG_RESUME: if (usb_enabled) { update_usb_device_state(USB_DEVICE_STATE_ACTIVE); } break; default: break; } } static int usb_stack_init(void) { int 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; } usbd_initialized = true; if (!usbd_can_detect_vbus(&blinky_usbd)) { err = usbd_enable(&blinky_usbd); if (err) { LOG_ERR("usbd_enable failed (%d)", err); return err; } usb_enabled = true; update_usb_device_state(USB_DEVICE_STATE_POWERED); } return 0; } static int module_init(void) { device_state = USB_DEVICE_STATE_DISCONNECTED; usb_enabled = false; ready_function_mask = 0U; prepare_broadcasted = false; usbd_initialized = false; return 0; } static int module_start(void) { if (running) { return 0; } running = true; if (usbd_initialized || prepare_broadcasted) { return 0; } ready_function_mask = 0U; prepare_broadcasted = true; submit_usb_prepare_event(); return 0; } static void module_pause(void) { if (!running) { return; } running = false; } 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 (!initialized) { int err = module_init(); if (err) { module_set_state(MODULE_STATE_ERROR); return false; } initialized = true; } if (!running) { int err = module_start(); if (err) { module_set_state(MODULE_STATE_ERROR); } else if (usbd_initialized) { module_set_state(MODULE_STATE_READY); } } return false; } static bool handle_usb_function_ready_event(const struct usb_function_ready_event *event) { int err; if (!running || !prepare_broadcasted || usbd_initialized) { return false; } ready_function_mask |= event->function_mask; if ((ready_function_mask & USB_REQUIRED_FUNCTION_MASK) != USB_REQUIRED_FUNCTION_MASK) { return false; } err = usb_stack_init(); if (err) { module_set_state(MODULE_STATE_ERROR); return false; } 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_usb_function_ready_event(aeh)) { return handle_usb_function_ready_event(cast_usb_function_ready_event(aeh)); } if (is_power_down_event(aeh)) { if (initialized) { module_pause(); module_set_state(MODULE_STATE_STANDBY); } return false; } if (is_wake_up_event(aeh)) { if (initialized) { int err = module_start(); if (err) { module_set_state(MODULE_STATE_ERROR); } else if (usbd_initialized) { module_set_state(MODULE_STATE_READY); } } return false; } return false; } APP_EVENT_LISTENER(MODULE, app_event_handler); APP_EVENT_SUBSCRIBE(MODULE, module_state_event); APP_EVENT_SUBSCRIBE(MODULE, usb_function_ready_event); APP_EVENT_SUBSCRIBE_EARLY(MODULE, power_down_event); APP_EVENT_SUBSCRIBE(MODULE, wake_up_event);