From 33fb416cfa1789091e153fa469ad3e94d79ca9e9 Mon Sep 17 00:00:00 2001 From: skiinder Date: Sat, 11 Apr 2026 17:15:11 +0800 Subject: [PATCH] =?UTF-8?q?feat(usb):=20=E6=B7=BB=E5=8A=A0USB=20CDC?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=E6=A8=A1=E5=9D=97=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在CMakeLists.txt中添加usb_cdc_module、usb_cdc_test_module和 usb_device_module源文件 - 添加usb_cdc_rx_event、usb_cdc_tx_event、usb_device_state_event、 usb_function_ready_event和usb_prepare_event事件定义 - 实现USB CDC串口通信功能,包括接收和发送数据处理 - 添加USB设备状态管理,支持连接、断开、激活等状态变化 - 配置设备树中的USB端点数量以支持CDC ACM功能 - 创建USB设备模块用于管理USB堆栈初始化和状态监控 - 添加USB功能就绪事件以协调不同USB功能的准备状态 --- CMakeLists.txt | 8 + .../atguigu/mini_keyboard/mini_keyboard.dts | 11 +- inc/events/usb_cdc_rx_event.h | 22 + inc/events/usb_cdc_tx_event.h | 22 + inc/events/usb_device_state_event.h | 24 + inc/events/usb_function_ready_event.h | 24 + inc/events/usb_prepare_event.h | 21 + inc/usb_device_module.h | 26 + src/events/usb_cdc_rx_event.c | 73 +++ src/events/usb_cdc_tx_event.c | 62 +++ src/events/usb_device_state_event.c | 46 ++ src/events/usb_function_ready_event.c | 42 ++ src/events/usb_prepare_event.c | 24 + src/usb_cdc_module.c | 464 ++++++++++++++++++ src/usb_cdc_test_module.c | 164 +++++++ src/usb_device_module.c | 337 +++++++++++++ src/usb_hid_module.c | 230 ++++----- 17 files changed, 1455 insertions(+), 145 deletions(-) create mode 100644 inc/events/usb_cdc_rx_event.h create mode 100644 inc/events/usb_cdc_tx_event.h create mode 100644 inc/events/usb_device_state_event.h create mode 100644 inc/events/usb_function_ready_event.h create mode 100644 inc/events/usb_prepare_event.h create mode 100644 inc/usb_device_module.h create mode 100644 src/events/usb_cdc_rx_event.c create mode 100644 src/events/usb_cdc_tx_event.c create mode 100644 src/events/usb_device_state_event.c create mode 100644 src/events/usb_function_ready_event.c create mode 100644 src/events/usb_prepare_event.c create mode 100644 src/usb_cdc_module.c create mode 100644 src/usb_cdc_test_module.c create mode 100644 src/usb_device_module.c diff --git a/CMakeLists.txt b/CMakeLists.txt index dc277c2..73112b0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,6 +22,9 @@ target_sources(app PRIVATE src/hid_flowctrl_module.c src/keyboard_core_module.c src/ui/ui_main.c + src/usb_cdc_module.c + src/usb_cdc_test_module.c + src/usb_device_module.c src/usb_hid_module.c src/events/bat_state_event.c src/events/encoder_event.c @@ -33,4 +36,9 @@ target_sources(app PRIVATE src/events/keyboard_hid_report_event.c src/events/mode_switch_event.c src/events/set_protocol_event.c + src/events/usb_cdc_rx_event.c + src/events/usb_cdc_tx_event.c + src/events/usb_device_state_event.c + src/events/usb_function_ready_event.c + src/events/usb_prepare_event.c ) diff --git a/boards/atguigu/mini_keyboard/mini_keyboard.dts b/boards/atguigu/mini_keyboard/mini_keyboard.dts index 3b6542d..71151da 100644 --- a/boards/atguigu/mini_keyboard/mini_keyboard.dts +++ b/boards/atguigu/mini_keyboard/mini_keyboard.dts @@ -225,9 +225,14 @@ &usbd { status = "okay"; - num-bidir-endpoints = <0>; - num-in-endpoints = <2>; - num-out-endpoints = <1>; + num-bidir-endpoints = <1>; + num-in-endpoints = <7>; + num-out-endpoints = <7>; num-isoin-endpoints = <0>; num-isoout-endpoints = <0>; + + cdc_acm_uart0: cdc_acm_uart0 { + compatible = "zephyr,cdc-acm-uart"; + label = "CDC_ACM_0"; + }; }; diff --git a/inc/events/usb_cdc_rx_event.h b/inc/events/usb_cdc_rx_event.h new file mode 100644 index 0000000..66683c9 --- /dev/null +++ b/inc/events/usb_cdc_rx_event.h @@ -0,0 +1,22 @@ +#ifndef BLINKY_USB_CDC_RX_EVENT_H_ +#define BLINKY_USB_CDC_RX_EVENT_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct usb_cdc_rx_event { + struct app_event_header header; + struct event_dyndata dyndata; +}; + +APP_EVENT_TYPE_DYNDATA_DECLARE(usb_cdc_rx_event); + +#ifdef __cplusplus +} +#endif + +#endif /* BLINKY_USB_CDC_RX_EVENT_H_ */ diff --git a/inc/events/usb_cdc_tx_event.h b/inc/events/usb_cdc_tx_event.h new file mode 100644 index 0000000..1214b31 --- /dev/null +++ b/inc/events/usb_cdc_tx_event.h @@ -0,0 +1,22 @@ +#ifndef BLINKY_USB_CDC_TX_EVENT_H_ +#define BLINKY_USB_CDC_TX_EVENT_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct usb_cdc_tx_event { + struct app_event_header header; + struct event_dyndata dyndata; +}; + +APP_EVENT_TYPE_DYNDATA_DECLARE(usb_cdc_tx_event); + +#ifdef __cplusplus +} +#endif + +#endif /* BLINKY_USB_CDC_TX_EVENT_H_ */ diff --git a/inc/events/usb_device_state_event.h b/inc/events/usb_device_state_event.h new file mode 100644 index 0000000..1bc7afa --- /dev/null +++ b/inc/events/usb_device_state_event.h @@ -0,0 +1,24 @@ +#ifndef BLINKY_USB_DEVICE_STATE_EVENT_H_ +#define BLINKY_USB_DEVICE_STATE_EVENT_H_ + +#include +#include + +#include "usb_device_module.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct usb_device_state_event { + struct app_event_header header; + enum usb_device_state state; +}; + +APP_EVENT_TYPE_DECLARE(usb_device_state_event); + +#ifdef __cplusplus +} +#endif + +#endif /* BLINKY_USB_DEVICE_STATE_EVENT_H_ */ diff --git a/inc/events/usb_function_ready_event.h b/inc/events/usb_function_ready_event.h new file mode 100644 index 0000000..93e82d1 --- /dev/null +++ b/inc/events/usb_function_ready_event.h @@ -0,0 +1,24 @@ +#ifndef BLINKY_USB_FUNCTION_READY_EVENT_H_ +#define BLINKY_USB_FUNCTION_READY_EVENT_H_ + +#include +#include + +#include "usb_device_module.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct usb_function_ready_event { + struct app_event_header header; + uint8_t function_mask; +}; + +APP_EVENT_TYPE_DECLARE(usb_function_ready_event); + +#ifdef __cplusplus +} +#endif + +#endif /* BLINKY_USB_FUNCTION_READY_EVENT_H_ */ diff --git a/inc/events/usb_prepare_event.h b/inc/events/usb_prepare_event.h new file mode 100644 index 0000000..12710a1 --- /dev/null +++ b/inc/events/usb_prepare_event.h @@ -0,0 +1,21 @@ +#ifndef BLINKY_USB_PREPARE_EVENT_H_ +#define BLINKY_USB_PREPARE_EVENT_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct usb_prepare_event { + struct app_event_header header; +}; + +APP_EVENT_TYPE_DECLARE(usb_prepare_event); + +#ifdef __cplusplus +} +#endif + +#endif /* BLINKY_USB_PREPARE_EVENT_H_ */ diff --git a/inc/usb_device_module.h b/inc/usb_device_module.h new file mode 100644 index 0000000..549e803 --- /dev/null +++ b/inc/usb_device_module.h @@ -0,0 +1,26 @@ +#ifndef BLINKY_USB_DEVICE_MODULE_H_ +#define BLINKY_USB_DEVICE_MODULE_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +enum usb_function { + USB_FUNCTION_HID = 0x01, + USB_FUNCTION_CDC_ACM = 0x02, +}; + +enum usb_device_state { + USB_DEVICE_STATE_DISCONNECTED, + USB_DEVICE_STATE_POWERED, + USB_DEVICE_STATE_ACTIVE, + USB_DEVICE_STATE_SUSPENDED, +}; + +#ifdef __cplusplus +} +#endif + +#endif /* BLINKY_USB_DEVICE_MODULE_H_ */ diff --git a/src/events/usb_cdc_rx_event.c b/src/events/usb_cdc_rx_event.c new file mode 100644 index 0000000..a941afe --- /dev/null +++ b/src/events/usb_cdc_rx_event.c @@ -0,0 +1,73 @@ +#include +#include + +#include "usb_cdc_rx_event.h" + +#define USB_CDC_RX_EVENT_LOG_BUF_LEN 384 + +static void log_usb_cdc_rx_event(const struct app_event_header *aeh) +{ + const struct usb_cdc_rx_event *event = cast_usb_cdc_rx_event(aeh); + char log_buf[USB_CDC_RX_EVENT_LOG_BUF_LEN]; + int pos; + + pos = snprintf(log_buf, sizeof(log_buf), "len:%zu ascii:\"", + event->dyndata.size); + if ((pos < 0) || (pos >= sizeof(log_buf))) { + APP_EVENT_MANAGER_LOG(aeh, "log message preparation failure"); + return; + } + + for (size_t i = 0; i < event->dyndata.size; i++) { + int tmp = snprintf(&log_buf[pos], sizeof(log_buf) - pos, "%c", + isprint(event->dyndata.data[i]) ? + event->dyndata.data[i] : '.'); + + if ((tmp < 0) || ((pos + tmp) >= sizeof(log_buf))) { + APP_EVENT_MANAGER_LOG(aeh, "len:%zu ascii:\"...\"", + event->dyndata.size); + return; + } + + pos += tmp; + } + + pos += snprintf(&log_buf[pos], sizeof(log_buf) - pos, "\" hex:"); + if ((pos < 0) || (pos >= sizeof(log_buf))) { + APP_EVENT_MANAGER_LOG(aeh, "len:%zu ascii:\"...\"", + event->dyndata.size); + return; + } + + for (size_t i = 0; i < event->dyndata.size; i++) { + int tmp = snprintf(&log_buf[pos], sizeof(log_buf) - pos, " %02x", + event->dyndata.data[i]); + + if ((tmp < 0) || ((pos + tmp) >= sizeof(log_buf))) { + break; + } + + pos += tmp; + } + + APP_EVENT_MANAGER_LOG(aeh, "%s", log_buf); +} + +static void profile_usb_cdc_rx_event(struct log_event_buf *buf, + const struct app_event_header *aeh) +{ + const struct usb_cdc_rx_event *event = cast_usb_cdc_rx_event(aeh); + + nrf_profiler_log_encode_uint8(buf, (uint8_t)event->dyndata.size); +} + +APP_EVENT_INFO_DEFINE(usb_cdc_rx_event, + ENCODE(NRF_PROFILER_ARG_U8), + ENCODE("len"), + profile_usb_cdc_rx_event); + +APP_EVENT_TYPE_DEFINE(usb_cdc_rx_event, + log_usb_cdc_rx_event, + &usb_cdc_rx_event_info, + APP_EVENT_FLAGS_CREATE( + APP_EVENT_TYPE_FLAGS_INIT_LOG_ENABLE)); diff --git a/src/events/usb_cdc_tx_event.c b/src/events/usb_cdc_tx_event.c new file mode 100644 index 0000000..8524105 --- /dev/null +++ b/src/events/usb_cdc_tx_event.c @@ -0,0 +1,62 @@ +#include +#include + +#include "usb_cdc_tx_event.h" + +#define USB_CDC_TX_EVENT_LOG_BUF_LEN 256 + +static void log_usb_cdc_tx_event(const struct app_event_header *aeh) +{ + const struct usb_cdc_tx_event *event = cast_usb_cdc_tx_event(aeh); + char log_buf[USB_CDC_TX_EVENT_LOG_BUF_LEN]; + int pos; + + pos = snprintf(log_buf, sizeof(log_buf), "len:%zu ascii:\"", + event->dyndata.size); + if ((pos < 0) || (pos >= sizeof(log_buf))) { + APP_EVENT_MANAGER_LOG(aeh, "log message preparation failure"); + return; + } + + for (size_t i = 0; i < event->dyndata.size; i++) { + int tmp = snprintf(&log_buf[pos], sizeof(log_buf) - pos, "%c", + isprint(event->dyndata.data[i]) ? + event->dyndata.data[i] : '.'); + + if ((tmp < 0) || ((pos + tmp) >= sizeof(log_buf))) { + APP_EVENT_MANAGER_LOG(aeh, "len:%zu ascii:\"...\"", + event->dyndata.size); + return; + } + + pos += tmp; + } + + pos += snprintf(&log_buf[pos], sizeof(log_buf) - pos, "\""); + if ((pos < 0) || (pos >= sizeof(log_buf))) { + APP_EVENT_MANAGER_LOG(aeh, "len:%zu ascii:\"...\"", + event->dyndata.size); + return; + } + + APP_EVENT_MANAGER_LOG(aeh, "%s", log_buf); +} + +static void profile_usb_cdc_tx_event(struct log_event_buf *buf, + const struct app_event_header *aeh) +{ + const struct usb_cdc_tx_event *event = cast_usb_cdc_tx_event(aeh); + + nrf_profiler_log_encode_uint8(buf, (uint8_t)event->dyndata.size); +} + +APP_EVENT_INFO_DEFINE(usb_cdc_tx_event, + ENCODE(NRF_PROFILER_ARG_U8), + ENCODE("len"), + profile_usb_cdc_tx_event); + +APP_EVENT_TYPE_DEFINE(usb_cdc_tx_event, + log_usb_cdc_tx_event, + &usb_cdc_tx_event_info, + APP_EVENT_FLAGS_CREATE( + APP_EVENT_TYPE_FLAGS_INIT_LOG_ENABLE)); diff --git a/src/events/usb_device_state_event.c b/src/events/usb_device_state_event.c new file mode 100644 index 0000000..e726a63 --- /dev/null +++ b/src/events/usb_device_state_event.c @@ -0,0 +1,46 @@ +#include "usb_device_state_event.h" + +static const char *usb_device_state_name(enum usb_device_state state) +{ + switch (state) { + case USB_DEVICE_STATE_DISCONNECTED: + return "disconnected"; + case USB_DEVICE_STATE_POWERED: + return "powered"; + case USB_DEVICE_STATE_ACTIVE: + return "active"; + case USB_DEVICE_STATE_SUSPENDED: + return "suspended"; + default: + return "?"; + } +} + +static void log_usb_device_state_event(const struct app_event_header *aeh) +{ + const struct usb_device_state_event *event = + cast_usb_device_state_event(aeh); + + APP_EVENT_MANAGER_LOG(aeh, "state:%s", + usb_device_state_name(event->state)); +} + +static void profile_usb_device_state_event(struct log_event_buf *buf, + const struct app_event_header *aeh) +{ + const struct usb_device_state_event *event = + cast_usb_device_state_event(aeh); + + nrf_profiler_log_encode_uint8(buf, event->state); +} + +APP_EVENT_INFO_DEFINE(usb_device_state_event, + ENCODE(NRF_PROFILER_ARG_U8), + ENCODE("state"), + profile_usb_device_state_event); + +APP_EVENT_TYPE_DEFINE(usb_device_state_event, + log_usb_device_state_event, + &usb_device_state_event_info, + APP_EVENT_FLAGS_CREATE( + APP_EVENT_TYPE_FLAGS_INIT_LOG_ENABLE)); diff --git a/src/events/usb_function_ready_event.c b/src/events/usb_function_ready_event.c new file mode 100644 index 0000000..8d74646 --- /dev/null +++ b/src/events/usb_function_ready_event.c @@ -0,0 +1,42 @@ +#include "usb_function_ready_event.h" + +static const char *usb_function_name(uint8_t function_mask) +{ + switch (function_mask) { + case USB_FUNCTION_HID: + return "hid"; + case USB_FUNCTION_CDC_ACM: + return "cdc_acm"; + default: + return "?"; + } +} + +static void log_usb_function_ready_event(const struct app_event_header *aeh) +{ + const struct usb_function_ready_event *event = + cast_usb_function_ready_event(aeh); + + APP_EVENT_MANAGER_LOG(aeh, "function:%s", + usb_function_name(event->function_mask)); +} + +static void profile_usb_function_ready_event(struct log_event_buf *buf, + const struct app_event_header *aeh) +{ + const struct usb_function_ready_event *event = + cast_usb_function_ready_event(aeh); + + nrf_profiler_log_encode_uint8(buf, event->function_mask); +} + +APP_EVENT_INFO_DEFINE(usb_function_ready_event, + ENCODE(NRF_PROFILER_ARG_U8), + ENCODE("function_mask"), + profile_usb_function_ready_event); + +APP_EVENT_TYPE_DEFINE(usb_function_ready_event, + log_usb_function_ready_event, + &usb_function_ready_event_info, + APP_EVENT_FLAGS_CREATE( + APP_EVENT_TYPE_FLAGS_INIT_LOG_ENABLE)); diff --git a/src/events/usb_prepare_event.c b/src/events/usb_prepare_event.c new file mode 100644 index 0000000..3bbfa44 --- /dev/null +++ b/src/events/usb_prepare_event.c @@ -0,0 +1,24 @@ +#include "usb_prepare_event.h" + +static void log_usb_prepare_event(const struct app_event_header *aeh) +{ + APP_EVENT_MANAGER_LOG(aeh, "prepare"); +} + +static void profile_usb_prepare_event(struct log_event_buf *buf, + const struct app_event_header *aeh) +{ + ARG_UNUSED(buf); + ARG_UNUSED(aeh); +} + +APP_EVENT_INFO_DEFINE(usb_prepare_event, + ENCODE(), + ENCODE(), + profile_usb_prepare_event); + +APP_EVENT_TYPE_DEFINE(usb_prepare_event, + log_usb_prepare_event, + &usb_prepare_event_info, + APP_EVENT_FLAGS_CREATE( + APP_EVENT_TYPE_FLAGS_INIT_LOG_ENABLE)); diff --git a/src/usb_cdc_module.c b/src/usb_cdc_module.c new file mode 100644 index 0000000..0bbc2e8 --- /dev/null +++ b/src/usb_cdc_module.c @@ -0,0 +1,464 @@ +#include +#include +#include +#include + +#include + +#define MODULE usb_cdc_module +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "usb_cdc_rx_event.h" +#include "usb_cdc_tx_event.h" +#include "usb_function_ready_event.h" +#include "usb_prepare_event.h" +#include "usb_device_module.h" +#include "usb_device_state_event.h" + +LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF); + +#define USB_CDC_RX_RING_BUF_SIZE 256 +#define USB_CDC_TX_RING_BUF_SIZE 256 +#define USB_CDC_RX_CHUNK_SIZE 32 +#define USB_CDC_MAX_LINE_SIZE 96 +#define USB_CDC_CONTROL_POLL_INTERVAL K_MSEC(100) +#define USB_CDC_EXPECTED_BAUDRATE 115200U + +static const struct device *const cdc_dev = DEVICE_DT_GET_ONE(zephyr_cdc_acm_uart); + +static uint8_t rx_ring_buffer[USB_CDC_RX_RING_BUF_SIZE]; +static uint8_t tx_ring_buffer[USB_CDC_TX_RING_BUF_SIZE]; +static struct ring_buf rx_ringbuf; +static struct ring_buf tx_ringbuf; +static struct k_work rx_work; +static struct k_work_delayable control_work; +static bool initialized; +static bool running; +static bool usb_active; +static bool usb_function_prepared; +static bool dtr_ready; +static bool rx_enabled; +static bool rx_line_overflow; +static uint8_t rx_line_buf[USB_CDC_MAX_LINE_SIZE]; +static size_t rx_line_len; + +static void submit_usb_cdc_rx_event(const uint8_t *data, size_t len) +{ + struct usb_cdc_rx_event *event = new_usb_cdc_rx_event(len); + + memcpy(event->dyndata.data, data, len); + APP_EVENT_SUBMIT(event); +} + +static void submit_usb_function_ready_event(void) +{ + struct usb_function_ready_event *event = new_usb_function_ready_event(); + + event->function_mask = USB_FUNCTION_CDC_ACM; + APP_EVENT_SUBMIT(event); +} + +static void reset_ring_buffers(void) +{ + unsigned int key = irq_lock(); + + ring_buf_init(&rx_ringbuf, sizeof(rx_ring_buffer), rx_ring_buffer); + ring_buf_init(&tx_ringbuf, sizeof(tx_ring_buffer), tx_ring_buffer); + rx_line_len = 0U; + rx_line_overflow = false; + + irq_unlock(key); +} + +static void disable_uart_io(void) +{ + uart_irq_rx_disable(cdc_dev); + uart_irq_tx_disable(cdc_dev); + rx_enabled = false; + dtr_ready = false; + reset_ring_buffers(); +} + +static void kick_tx(void) +{ + if (!running || !usb_active || !dtr_ready) { + return; + } + + uart_irq_tx_enable(cdc_dev); +} + +static void validate_line_coding(void) +{ + uint32_t baudrate = 0U; + int err; + + err = uart_line_ctrl_get(cdc_dev, UART_LINE_CTRL_BAUD_RATE, &baudrate); + if (err) { + LOG_WRN("Failed to get CDC baudrate (%d)", err); + } else { + LOG_INF("CDC baudrate %u", baudrate); + if (baudrate != USB_CDC_EXPECTED_BAUDRATE) { + LOG_WRN("Expected CDC baudrate %u, got %u", + USB_CDC_EXPECTED_BAUDRATE, baudrate); + } + } + +#ifdef CONFIG_UART_USE_RUNTIME_CONFIGURE + { + struct uart_config cfg; + + err = uart_config_get(cdc_dev, &cfg); + if (err) { + LOG_WRN("uart_config_get failed (%d)", err); + } else { + LOG_INF("CDC line coding data:%u stop:%u parity:%u flow:%u", + cfg.data_bits, cfg.stop_bits, cfg.parity, + cfg.flow_ctrl); + if ((cfg.data_bits != UART_CFG_DATA_BITS_8) || + (cfg.stop_bits != UART_CFG_STOP_BITS_1) || + (cfg.parity != UART_CFG_PARITY_NONE) || + (cfg.flow_ctrl != UART_CFG_FLOW_CTRL_NONE)) { + LOG_WRN("Expected CDC line coding 115200 8N1 no flow control"); + } + } + } +#endif +} + +static void process_rx_byte(uint8_t byte) +{ + if ((byte == '\r') || (byte == '\n')) { + if (rx_line_overflow) { + LOG_WRN("Drop oversized CDC line"); + rx_line_overflow = false; + rx_line_len = 0U; + return; + } + + if (rx_line_len > 0U) { + submit_usb_cdc_rx_event(rx_line_buf, rx_line_len); + rx_line_len = 0U; + } + + return; + } + + if (rx_line_overflow) { + return; + } + + if (rx_line_len >= sizeof(rx_line_buf)) { + rx_line_overflow = true; + return; + } + + rx_line_buf[rx_line_len++] = byte; +} + +static void rx_work_handler(struct k_work *work) +{ + uint8_t buffer[USB_CDC_RX_CHUNK_SIZE]; + + ARG_UNUSED(work); + + while (true) { + uint32_t len; + unsigned int key = irq_lock(); + + len = ring_buf_get(&rx_ringbuf, buffer, sizeof(buffer)); + irq_unlock(key); + + if (len == 0U) { + return; + } + + for (uint32_t i = 0; i < len; i++) { + process_rx_byte(buffer[i]); + } + } +} + +static void control_work_handler(struct k_work *work) +{ + uint32_t dtr = 0U; + int err; + + ARG_UNUSED(work); + + if (!running || !usb_active) { + return; + } + + err = uart_line_ctrl_get(cdc_dev, UART_LINE_CTRL_DTR, &dtr); + if (err) { + LOG_WRN("Failed to get CDC DTR (%d)", err); + goto reschedule; + } + + if (dtr && !dtr_ready) { + dtr_ready = true; + LOG_INF("CDC DTR set"); + validate_line_coding(); + + err = uart_line_ctrl_set(cdc_dev, UART_LINE_CTRL_DCD, 1); + if (err) { + LOG_WRN("Failed to set DCD (%d)", err); + } + + err = uart_line_ctrl_set(cdc_dev, UART_LINE_CTRL_DSR, 1); + if (err) { + LOG_WRN("Failed to set DSR (%d)", err); + } + + if (!rx_enabled) { + uart_irq_rx_enable(cdc_dev); + rx_enabled = true; + } + + kick_tx(); + } else if (!dtr && dtr_ready) { + LOG_INF("CDC DTR cleared"); + disable_uart_io(); + } + +reschedule: + k_work_reschedule(&control_work, USB_CDC_CONTROL_POLL_INTERVAL); +} + +static void cdc_interrupt_handler(const struct device *dev, void *user_data) +{ + ARG_UNUSED(user_data); + + while (uart_irq_update(dev) && uart_irq_is_pending(dev)) { + if (uart_irq_rx_ready(dev)) { + uint8_t buffer[USB_CDC_RX_CHUNK_SIZE]; + int recv_len = uart_fifo_read(dev, buffer, sizeof(buffer)); + + if (recv_len < 0) { + LOG_ERR("Failed to read CDC RX FIFO"); + continue; + } + + if (recv_len > 0) { + uint32_t written; + unsigned int key = irq_lock(); + + written = ring_buf_put(&rx_ringbuf, buffer, + (uint32_t)recv_len); + irq_unlock(key); + + if (written < (uint32_t)recv_len) { + LOG_WRN("Drop %d CDC RX bytes", recv_len - (int)written); + } + + k_work_submit(&rx_work); + } + } + + if (uart_irq_tx_ready(dev)) { + uint8_t buffer[USB_CDC_RX_CHUNK_SIZE]; + uint32_t len; + int sent_len; + unsigned int key = irq_lock(); + + len = ring_buf_get(&tx_ringbuf, buffer, sizeof(buffer)); + irq_unlock(key); + + if (len == 0U) { + uart_irq_tx_disable(dev); + continue; + } + + sent_len = uart_fifo_fill(dev, buffer, len); + if (sent_len < 0) { + LOG_ERR("Failed to write CDC TX FIFO"); + uart_irq_tx_disable(dev); + } else if ((uint32_t)sent_len < len) { + LOG_WRN("Drop %u CDC TX bytes", + (unsigned int)(len - (uint32_t)sent_len)); + } + } + } +} + +static int module_init(void) +{ + if (!device_is_ready(cdc_dev)) { + LOG_ERR("CDC ACM device not ready"); + return -ENODEV; + } + + reset_ring_buffers(); + k_work_init(&rx_work, rx_work_handler); + k_work_init_delayable(&control_work, control_work_handler); + uart_irq_callback_set(cdc_dev, cdc_interrupt_handler); + usb_function_prepared = false; + return 0; +} + +static int module_start(void) +{ + if (running) { + return 0; + } + + running = true; + if (usb_active) { + k_work_reschedule(&control_work, K_NO_WAIT); + } + + return 0; +} + +static void module_pause(void) +{ + if (!running) { + return; + } + + k_work_cancel_delayable(&control_work); + disable_uart_io(); + running = false; +} + +static bool handle_usb_prepare_event(const struct usb_prepare_event *event) +{ + ARG_UNUSED(event); + + if (!running || usb_function_prepared) { + return false; + } + + usb_function_prepared = true; + submit_usb_function_ready_event(); + return false; +} + +static bool handle_usb_device_state_event(const struct usb_device_state_event *event) +{ + bool new_usb_active = (event->state == USB_DEVICE_STATE_ACTIVE); + + if (new_usb_active == usb_active) { + return false; + } + + usb_active = new_usb_active; + + if (!usb_active) { + k_work_cancel_delayable(&control_work); + disable_uart_io(); + } else if (running) { + k_work_reschedule(&control_work, K_NO_WAIT); + } + + return false; +} + +static bool handle_usb_cdc_tx_event(const struct usb_cdc_tx_event *event) +{ + uint32_t written; + unsigned int key; + + if (!running || !usb_active || !dtr_ready) { + return false; + } + + key = irq_lock(); + written = ring_buf_put(&tx_ringbuf, event->dyndata.data, + (uint32_t)event->dyndata.size); + irq_unlock(key); + + if (written < event->dyndata.size) { + LOG_WRN("Drop %zu CDC TX bytes", event->dyndata.size - written); + } + + if (written > 0U) { + kick_tx(); + } + + return false; +} + +static bool app_event_handler(const struct app_event_header *aeh) +{ + if (is_usb_device_state_event(aeh)) { + return handle_usb_device_state_event(cast_usb_device_state_event(aeh)); + } + + if (is_usb_prepare_event(aeh)) { + return handle_usb_prepare_event(cast_usb_prepare_event(aeh)); + } + + if (is_usb_cdc_tx_event(aeh)) { + return handle_usb_cdc_tx_event(cast_usb_cdc_tx_event(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)) { + int err; + + if (!initialized) { + err = module_init(); + if (err) { + module_set_state(MODULE_STATE_ERROR); + return false; + } + + initialized = true; + } + + err = module_start(); + if (err) { + module_set_state(MODULE_STATE_ERROR); + } else { + module_set_state(MODULE_STATE_READY); + } + } + + return false; + } + + 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 { + 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_prepare_event); +APP_EVENT_SUBSCRIBE(MODULE, usb_device_state_event); +APP_EVENT_SUBSCRIBE(MODULE, usb_cdc_tx_event); +APP_EVENT_SUBSCRIBE_EARLY(MODULE, power_down_event); +APP_EVENT_SUBSCRIBE(MODULE, wake_up_event); diff --git a/src/usb_cdc_test_module.c b/src/usb_cdc_test_module.c new file mode 100644 index 0000000..99b666a --- /dev/null +++ b/src/usb_cdc_test_module.c @@ -0,0 +1,164 @@ +#include +#include + +#include + +#define MODULE usb_cdc_test_module +#include +#include + +#include +#include + +#include "usb_cdc_tx_event.h" +#include "usb_device_state_event.h" + +LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF); + +#define USB_CDC_TEST_PERIOD K_SECONDS(1) + +static const uint8_t hello_message[] = "hello\r\n"; + +static struct k_work_delayable hello_work; +static bool initialized; +static bool running; +static bool usb_active; + +static void submit_hello_message(void) +{ + struct usb_cdc_tx_event *event = + new_usb_cdc_tx_event(sizeof(hello_message) - 1U); + + memcpy(event->dyndata.data, hello_message, sizeof(hello_message) - 1U); + APP_EVENT_SUBMIT(event); +} + +static void hello_work_handler(struct k_work *work) +{ + ARG_UNUSED(work); + + if (!running || !usb_active) { + return; + } + + submit_hello_message(); + k_work_reschedule(&hello_work, USB_CDC_TEST_PERIOD); +} + +static int module_init(void) +{ + k_work_init_delayable(&hello_work, hello_work_handler); + return 0; +} + +static int module_start(void) +{ + if (running) { + return 0; + } + + running = true; + if (usb_active) { + k_work_reschedule(&hello_work, USB_CDC_TEST_PERIOD); + } + + return 0; +} + +static void module_pause(void) +{ + if (!running) { + return; + } + + k_work_cancel_delayable(&hello_work); + running = false; +} + +static bool handle_usb_device_state_event(const struct usb_device_state_event *event) +{ + bool new_usb_active = (event->state == USB_DEVICE_STATE_ACTIVE); + + if (new_usb_active == usb_active) { + return false; + } + + usb_active = new_usb_active; + + if (!running) { + return false; + } + + if (usb_active) { + k_work_reschedule(&hello_work, USB_CDC_TEST_PERIOD); + } else { + k_work_cancel_delayable(&hello_work); + } + + return false; +} + +static bool app_event_handler(const struct app_event_header *aeh) +{ + if (is_usb_device_state_event(aeh)) { + return handle_usb_device_state_event(cast_usb_device_state_event(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)) { + int err; + + if (!initialized) { + err = module_init(); + if (err) { + module_set_state(MODULE_STATE_ERROR); + return false; + } + + initialized = true; + } + + err = module_start(); + if (err) { + module_set_state(MODULE_STATE_ERROR); + } else { + module_set_state(MODULE_STATE_READY); + } + } + + return false; + } + + 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 { + 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_device_state_event); +APP_EVENT_SUBSCRIBE_EARLY(MODULE, power_down_event); +APP_EVENT_SUBSCRIBE(MODULE, wake_up_event); diff --git a/src/usb_device_module.c b/src/usb_device_module.c new file mode 100644 index 0000000..2605e32 --- /dev/null +++ b/src/usb_device_module.c @@ -0,0 +1,337 @@ +#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); diff --git a/src/usb_hid_module.c b/src/usb_hid_module.c index 40efd57..cf7f99c 100644 --- a/src/usb_hid_module.c +++ b/src/usb_hid_module.c @@ -12,10 +12,8 @@ #include #include #include -#include #include #include -#include #include "hid_led_event.h" #include "hid_report_sent_event.h" @@ -23,15 +21,13 @@ #include "hid_tx_report_event.h" #include "keyboard_core.h" #include "set_protocol_event.h" +#include "usb_function_ready_event.h" +#include "usb_prepare_event.h" +#include "usb_device_module.h" +#include "usb_device_state_event.h" LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF); -#define USB_HID_VID 0x1915 -#define USB_HID_PID 0x52F0 -#define USB_HID_MANUFACTURER "Atguigu" -#define USB_HID_PRODUCT "WH Mini Keyboard" -#define USB_HID_POLLING_US 1000U - #define KBD_LED_REPORT_SIZE 1U enum usb_hid_interface { @@ -106,7 +102,8 @@ static enum keyboard_protocol_mode keyboard_protocol_mode = KEYBOARD_PROTOCOL_MODE_REPORT; static bool initialized; static bool running; -static bool usb_enabled; +static bool usb_active; +static bool usb_function_prepared; static bool keyboard_report_in_flight; static bool consumer_report_in_flight; static uint16_t keyboard_in_flight_sequence; @@ -115,20 +112,6 @@ static uint16_t consumer_in_flight_sequence; UDC_STATIC_BUF_DEFINE(keyboard_tx_buf, KEYBOARD_NKRO_REPORT_SIZE); UDC_STATIC_BUF_DEFINE(consumer_tx_buf, KEYBOARD_CONSUMER_REPORT_SIZE); -USBD_DEVICE_DEFINE(blinky_usbd, DEVICE_DT_GET(DT_NODELABEL(usbd)), - USB_HID_VID, USB_HID_PID); - -USBD_DESC_LANG_DEFINE(blinky_lang); -USBD_DESC_MANUFACTURER_DEFINE(blinky_mfr, USB_HID_MANUFACTURER); -USBD_DESC_PRODUCT_DEFINE(blinky_product, USB_HID_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 struct usb_hid_interface_state *iface_from_dev(const struct device *dev) { for (size_t i = 0; i < ARRAY_SIZE(hid_ifaces); i++) { @@ -146,6 +129,17 @@ static enum keyboard_protocol_mode usb_proto_to_keyboard_proto(uint8_t proto) : KEYBOARD_PROTOCOL_MODE_REPORT; } +static void reset_usb_runtime_state(void) +{ + for (size_t i = 0; i < ARRAY_SIZE(hid_ifaces); i++) { + hid_ifaces[i].ready = false; + } + + usb_active = false; + keyboard_report_in_flight = false; + consumer_report_in_flight = false; +} + static void submit_set_protocol_event(enum keyboard_protocol_mode protocol_mode) { struct set_protocol_event *event = new_set_protocol_event(); @@ -167,12 +161,13 @@ static void submit_hid_led_event(uint8_t led_bm) static void submit_transport_state_event(void) { struct hid_transport_state_event *event = new_hid_transport_state_event(); + bool ready = running && usb_active; event->transport = HID_TRANSPORT_USB; - event->ready = usb_enabled; - event->keys_ready = usb_enabled && + event->ready = ready; + event->keys_ready = ready && hid_ifaces[USB_HID_INTERFACE_KEYBOARD].ready; - event->consumer_ready = usb_enabled && + event->consumer_ready = ready && hid_ifaces[USB_HID_INTERFACE_CONSUMER].ready; event->protocol_mode = keyboard_protocol_mode; @@ -192,6 +187,14 @@ static void submit_hid_report_sent_event(enum keyboard_report_type report_type, APP_EVENT_SUBMIT(event); } +static void submit_usb_function_ready_event(void) +{ + struct usb_function_ready_event *event = new_usb_function_ready_event(); + + event->function_mask = USB_FUNCTION_HID; + APP_EVENT_SUBMIT(event); +} + static void keyboard_iface_ready(const struct device *dev, const bool ready) { struct usb_hid_interface_state *iface = iface_from_dev(dev); @@ -204,8 +207,8 @@ static void keyboard_iface_ready(const struct device *dev, const bool ready) if (!ready) { keyboard_report_in_flight = false; } - LOG_INF("%s interface %s", - dev->name, ready ? "ready" : "not ready"); + + LOG_INF("%s interface %s", dev->name, ready ? "ready" : "not ready"); submit_transport_state_event(); } @@ -316,8 +319,8 @@ static void consumer_iface_ready(const struct device *dev, const bool ready) if (!ready) { consumer_report_in_flight = false; } - LOG_INF("%s interface %s", - dev->name, ready ? "ready" : "not ready"); + + LOG_INF("%s interface %s", dev->name, ready ? "ready" : "not ready"); submit_transport_state_event(); } @@ -400,81 +403,6 @@ static const struct hid_device_ops consumer_ops = { .output_report = consumer_output_report, }; -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_VBUS_READY) { - if (!usb_enabled) { - int err = usbd_enable(&blinky_usbd); - - if (err) { - LOG_ERR("usbd_enable failed (%d)", err); - } else { - usb_enabled = true; - submit_transport_state_event(); - } - } - - return; - } - - if (msg->type == USBD_MSG_VBUS_REMOVED) { - if (usb_enabled) { - int err = usbd_disable(&blinky_usbd); - - if (err) { - LOG_ERR("usbd_disable failed (%d)", err); - } else { - usb_enabled = false; - for (size_t i = 0; i < ARRAY_SIZE(hid_ifaces); i++) { - hid_ifaces[i].ready = false; - } - keyboard_report_in_flight = false; - consumer_report_in_flight = false; - submit_transport_state_event(); - } - } - - return; - } -} - -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 int usb_hid_register_devices(void) { int err; @@ -507,41 +435,9 @@ static int usb_hid_register_devices(void) static int module_init(void) { - int err; - - err = usb_hid_register_devices(); - if (err) { - LOG_ERR("hid_device_register failed (%d)", 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; - } - - 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; - } + reset_usb_runtime_state(); + keyboard_protocol_mode = KEYBOARD_PROTOCOL_MODE_REPORT; + usb_function_prepared = false; return 0; } @@ -560,13 +456,54 @@ static int module_start(void) static void module_pause(void) { running = false; + submit_transport_state_event(); +} + +static bool handle_usb_prepare_event(const struct usb_prepare_event *event) +{ + int err; + + ARG_UNUSED(event); + + if (!running || usb_function_prepared) { + return false; + } + + err = usb_hid_register_devices(); + if (err) { + LOG_ERR("hid_device_register failed (%d)", err); + module_set_state(MODULE_STATE_ERROR); + return false; + } + + usb_function_prepared = true; + submit_usb_function_ready_event(); + return false; +} + +static bool handle_usb_device_state_event(const struct usb_device_state_event *event) +{ + bool new_usb_active = (event->state == USB_DEVICE_STATE_ACTIVE); + + if (new_usb_active == usb_active) { + return false; + } + + if (!new_usb_active) { + reset_usb_runtime_state(); + } else { + usb_active = true; + } + + submit_transport_state_event(); + return false; } static bool handle_hid_tx_report_event(const struct hid_tx_report_event *event) { int err; - if (!running || (event->transport != HID_TRANSPORT_USB)) { + if (!running || !usb_active || (event->transport != HID_TRANSPORT_USB)) { return false; } @@ -640,6 +577,14 @@ static bool app_event_handler(const struct app_event_header *aeh) return handle_hid_tx_report_event(cast_hid_tx_report_event(aeh)); } + if (is_usb_device_state_event(aeh)) { + return handle_usb_device_state_event(cast_usb_device_state_event(aeh)); + } + + if (is_usb_prepare_event(aeh)) { + return handle_usb_prepare_event(cast_usb_prepare_event(aeh)); + } + if (is_module_state_event(aeh)) { const struct module_state_event *event = cast_module_state_event(aeh); @@ -690,12 +635,13 @@ static bool app_event_handler(const struct app_event_header *aeh) return false; } - __ASSERT_NO_MSG(false); return false; } APP_EVENT_LISTENER(MODULE, app_event_handler); APP_EVENT_SUBSCRIBE(MODULE, module_state_event); APP_EVENT_SUBSCRIBE(MODULE, hid_tx_report_event); +APP_EVENT_SUBSCRIBE(MODULE, usb_prepare_event); +APP_EVENT_SUBSCRIBE(MODULE, usb_device_state_event); APP_EVENT_SUBSCRIBE_EARLY(MODULE, power_down_event); APP_EVENT_SUBSCRIBE(MODULE, wake_up_event);