Files
blinky/src/usb_device_module.c

461 lines
9.7 KiB
C
Raw Normal View History

#include <errno.h>
#include <stdbool.h>
#include <stdint.h>
#include <app_event_manager.h>
#define MODULE usb_device_module
#include <caf/events/module_state_event.h>
#include <caf/events/module_suspend_event.h>
#include <caf/events/power_event.h>
#include <zephyr/drivers/uart.h>
#include <zephyr/logging/log.h>
#include <zephyr/usb/usbd.h>
#include <caf/events/power_manager_event.h>
#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);