feat(usb): 添加USB CDC功能模块支持

- 在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功能的准备状态
This commit is contained in:
2026-04-11 17:15:11 +08:00
parent c40fc709d5
commit 33fb416cfa
17 changed files with 1455 additions and 145 deletions

464
src/usb_cdc_module.c Normal file
View File

@@ -0,0 +1,464 @@
#include <errno.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include <app_event_manager.h>
#define MODULE usb_cdc_module
#include <caf/events/module_state_event.h>
#include <caf/events/power_event.h>
#include <zephyr/device.h>
#include <zephyr/drivers/uart.h>
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#include <zephyr/sys/ring_buffer.h>
#include <zephyr/sys/util.h>
#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);