#include "usb_cdc_char_device.h" #include #include #include #include #include #include "usbd_core.h" #include "usbd_cdc_acm.h" #if !defined(RT_USING_DEVICE_OPS) #error "RT_USING_DEVICE_OPS must be enabled" #endif #if !defined(CONFIG_USBDEV_ADVANCE_DESC) #error "Please enable CONFIG_USBDEV_ADVANCE_DESC" #endif /* -------------------- FS fixed config -------------------- */ enum { CDC_IN_EP = 0x81, CDC_OUT_EP = 0x02, CDC_INT_EP = 0x83, CDC_VID = 0xFFFF, CDC_PID = 0xFFFF, CDC_MAX_POWER_MA = 100, CDC_MAX_MPS = 64, /* FS */ CDC_RX_DMA_SIZE = 2048, /* OUT DMA buffer */ CDC_TX_DMA_SIZE = 2048, /* IN DMA buffer */ CDC_RX_RING_SIZE = 8192, /* software RX ringbuffer */ CDC_CONFIG_SIZE = (9 + CDC_ACM_DESCRIPTOR_LEN), /* 保险丝:防止上层误传超大 size(比如把负数当无符号) */ CDC_WRITE_SIZE_GUARD = 64 * 1024, /* 64KB */ }; /* -------------------- DMA buffers in nocache RAM -------------------- */ USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX static uint8_t g_rx_dma[CDC_RX_DMA_SIZE]; USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX static uint8_t g_tx_dma[CDC_TX_DMA_SIZE]; /* -------------------- descriptors (advance-desc callbacks) -------------------- */ static const uint8_t g_device_desc[] = { USB_DEVICE_DESCRIPTOR_INIT(USB_2_0, 0xEF, 0x02, 0x01, CDC_VID, CDC_PID, 0x0100, 0x01) }; static const uint8_t g_config_desc[] = { USB_CONFIG_DESCRIPTOR_INIT(CDC_CONFIG_SIZE, 0x02, 0x01, USB_CONFIG_BUS_POWERED, CDC_MAX_POWER_MA), CDC_ACM_DESCRIPTOR_INIT(0x00, CDC_INT_EP, CDC_OUT_EP, CDC_IN_EP, CDC_MAX_MPS, 0x02) }; /* qualifier:FS 下不是必须,但留着不影响 */ static const uint8_t g_device_qual_desc[] = { 0x0a, USB_DESCRIPTOR_TYPE_DEVICE_QUALIFIER, 0x00, 0x02, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, }; static const char g_langid_bytes[] = { 0x09, 0x04 }; static const char *g_string_desc[] = { g_langid_bytes, "CherryUSB", "RTT CDC ACM FS", "000000000001", }; static const uint8_t *device_descriptor_cb(uint8_t speed) { (void)speed; return g_device_desc; } static const uint8_t *config_descriptor_cb(uint8_t speed) { (void)speed; return g_config_desc; } static const uint8_t *device_qual_descriptor_cb(uint8_t speed) { (void)speed; return g_device_qual_desc; } static const char *string_descriptor_cb(uint8_t speed, uint8_t index) { (void)speed; if (index > 3) return RT_NULL; return g_string_desc[index]; } static const struct usb_descriptor g_cdc_desc = { .device_descriptor_callback = device_descriptor_cb, .config_descriptor_callback = config_descriptor_cb, .device_quality_descriptor_callback = device_qual_descriptor_cb, .string_descriptor_callback = string_descriptor_cb }; /* -------------------- driver object -------------------- */ struct cdc_char_dev { struct rt_device dev; /* must be first */ uint8_t busid; uintptr_t reg_base; volatile rt_uint8_t dtr_enable; volatile rt_uint8_t connected; volatile rt_uint8_t configured; struct rt_ringbuffer *rx_rb; rt_sem_t tx_sem; rt_mutex_t tx_lock; uint32_t tx_timeout_ms; uint8_t require_dtr_tx; struct usbd_interface intf0; struct usbd_interface intf1; struct usbd_endpoint out_ep; struct usbd_endpoint in_ep; }; static struct cdc_char_dev g_cdc; /* -------------------- forward declarations -------------------- */ static void usbd_event_handler(uint8_t busid, uint8_t event); static void cdc_bulk_out(uint8_t busid, uint8_t ep, uint32_t nbytes); static void cdc_bulk_in(uint8_t busid, uint8_t ep, uint32_t nbytes); /* -------------------- RT device ops -------------------- */ static rt_err_t cdc_init(rt_device_t dev) { (void)dev; return RT_EOK; } static rt_err_t cdc_open(rt_device_t dev, rt_uint16_t oflag) { (void)dev; (void)oflag; return RT_EOK; } static rt_err_t cdc_close(rt_device_t dev) { (void)dev; return RT_EOK; } static rt_ssize_t cdc_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size) { (void)pos; if (!dev || !buffer) return -RT_EINVAL; if (size == 0) return 0; struct cdc_char_dev *c = (struct cdc_char_dev *)dev; /* 未枚举配置完成,不允许读(避免上层误用) */ if (!c->configured) return -RT_EIO; if (!c->rx_rb) return -RT_ERROR; rt_base_t level = rt_hw_interrupt_disable(); rt_size_t got = rt_ringbuffer_get(c->rx_rb, (rt_uint8_t *)buffer, size); rt_hw_interrupt_enable(level); return (rt_ssize_t)got; /* ringbuffer 空则返回 0 */ } static rt_ssize_t cdc_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size) { (void)pos; if (!dev || !buffer) return -RT_EINVAL; if (size == 0) return 0; /* 保险丝:防止上层把负数当 size 传下来 */ if (size > CDC_WRITE_SIZE_GUARD) return -RT_EINVAL; struct cdc_char_dev *c = (struct cdc_char_dev *)dev; /* 未配置/断开,不允许写 */ if (!c->configured) return -RT_EIO; if (c->require_dtr_tx && !c->dtr_enable) return -RT_EIO; if (!c->tx_sem || !c->tx_lock) return -RT_ERROR; rt_mutex_take(c->tx_lock, RT_WAITING_FOREVER); rt_size_t sent_total = 0; const rt_uint8_t *p = (const rt_uint8_t *)buffer; while (sent_total < size) { /* 如果中途断开/重置,立刻失败 */ if (!c->configured) { rt_mutex_release(c->tx_lock); return (sent_total > 0) ? (rt_ssize_t)sent_total : -RT_EIO; } rt_size_t chunk = size - sent_total; if (chunk > CDC_TX_DMA_SIZE) chunk = CDC_TX_DMA_SIZE; memcpy(g_tx_dma, p + sent_total, chunk); /* 清掉旧 token */ while (rt_sem_trytake(c->tx_sem) == RT_EOK) {} usbd_ep_start_write(c->busid, CDC_IN_EP, g_tx_dma, (uint32_t)chunk); rt_tick_t to = rt_tick_from_millisecond(c->tx_timeout_ms); if (rt_sem_take(c->tx_sem, to) != RT_EOK) { rt_mutex_release(c->tx_lock); return (sent_total > 0) ? (rt_ssize_t)sent_total : -RT_ETIMEOUT; } /* 被唤醒后再次确认仍处于 configured(避免断开时“假完成”) */ if (!c->configured) { rt_mutex_release(c->tx_lock); return (sent_total > 0) ? (rt_ssize_t)sent_total : -RT_EIO; } sent_total += chunk; } rt_mutex_release(c->tx_lock); return (rt_ssize_t)sent_total; } static rt_err_t cdc_control(rt_device_t dev, int cmd, void *args) { if (!dev) return -RT_EINVAL; struct cdc_char_dev *c = (struct cdc_char_dev *)dev; switch (cmd) { case CDC_CTRL_SET_TX_TIMEOUT_MS: if (args) c->tx_timeout_ms = *(uint32_t *)args; return RT_EOK; case CDC_CTRL_GET_TX_TIMEOUT_MS: if (args) *(uint32_t *)args = c->tx_timeout_ms; return RT_EOK; case CDC_CTRL_GET_DTR: if (args) *(uint8_t *)args = c->dtr_enable; return RT_EOK; case CDC_CTRL_SET_REQUIRE_DTR_TX: if (args) c->require_dtr_tx = (*(uint8_t *)args) ? 1 : 0; return RT_EOK; case CDC_CTRL_GET_CONFIGURED: /* ✅ 新增:查询是否已枚举配置 */ if (args) *(uint8_t *)args = c->configured; return RT_EOK; default: return -RT_ENOSYS; } } /* ops table */ static const struct rt_device_ops cdc_ops = { .init = cdc_init, .open = cdc_open, .close = cdc_close, .read = cdc_read, .write = cdc_write, .control = cdc_control, }; /* -------------------- CherryUSB callbacks -------------------- */ static void usbd_event_handler(uint8_t busid, uint8_t event) { (void)busid; switch (event) { case USBD_EVENT_RESET: g_cdc.configured = 0; g_cdc.connected = 0; /* 可选:唤醒等待线程,让它能尽快感知状态变化 */ if (g_cdc.dev.rx_indicate) g_cdc.dev.rx_indicate((rt_device_t)&g_cdc.dev, 0); break; case USBD_EVENT_CONNECTED: g_cdc.connected = 1; break; case USBD_EVENT_DISCONNECTED: g_cdc.configured = 0; g_cdc.connected = 0; /* 可选:唤醒等待线程 */ if (g_cdc.dev.rx_indicate) g_cdc.dev.rx_indicate((rt_device_t)&g_cdc.dev, 0); break; case USBD_EVENT_CONFIGURED: g_cdc.connected = 1; g_cdc.configured = 1; /* 重新启动 OUT 读(防止 reset 后没读) */ usbd_ep_start_read(g_cdc.busid, CDC_OUT_EP, g_rx_dma, CDC_RX_DMA_SIZE); /* 可选:唤醒等待线程,让“先启动线程再插线”也能快速进入工作状态 */ if (g_cdc.dev.rx_indicate) g_cdc.dev.rx_indicate((rt_device_t)&g_cdc.dev, 0); break; default: break; } } static void cdc_bulk_out(uint8_t busid, uint8_t ep, uint32_t nbytes) { (void)busid; (void)ep; /* 未配置时忽略数据,并继续挂下一次读 */ if (!g_cdc.configured) { usbd_ep_start_read(g_cdc.busid, CDC_OUT_EP, g_rx_dma, CDC_RX_DMA_SIZE); return; } if (nbytes > 0 && nbytes <= CDC_RX_DMA_SIZE && g_cdc.rx_rb) { (void)rt_ringbuffer_put(g_cdc.rx_rb, g_rx_dma, (rt_size_t)nbytes); if (g_cdc.dev.rx_indicate) g_cdc.dev.rx_indicate((rt_device_t)&g_cdc.dev, (rt_size_t)nbytes); } usbd_ep_start_read(g_cdc.busid, CDC_OUT_EP, g_rx_dma, CDC_RX_DMA_SIZE); } static void cdc_bulk_in(uint8_t busid, uint8_t ep, uint32_t nbytes) { (void)busid; /* ZLP:长度是 MPS 整倍数时补 0 包 */ if ((nbytes % usbd_get_ep_mps(g_cdc.busid, ep)) == 0 && nbytes) { usbd_ep_start_write(g_cdc.busid, CDC_IN_EP, RT_NULL, 0); return; } if (g_cdc.tx_sem) rt_sem_release(g_cdc.tx_sem); if (g_cdc.dev.tx_complete) g_cdc.dev.tx_complete((rt_device_t)&g_cdc.dev, RT_NULL); } /* DTR hook */ void usbd_cdc_acm_set_dtr(uint8_t busid, uint8_t intf, bool dtr) { (void)busid; (void)intf; g_cdc.dtr_enable = dtr ? 1 : 0; } /* endpoints */ static struct usbd_endpoint g_out_ep = { .ep_addr = CDC_OUT_EP, .ep_cb = cdc_bulk_out }; static struct usbd_endpoint g_in_ep = { .ep_addr = CDC_IN_EP, .ep_cb = cdc_bulk_in }; /* -------------------- public init -------------------- */ int rt_hw_usbd_cdc_char_init(const char *dev_name, uint8_t busid, uintptr_t reg_base) { if (!dev_name) return -RT_EINVAL; memset(&g_cdc, 0, sizeof(g_cdc)); g_cdc.busid = busid; g_cdc.reg_base = reg_base; g_cdc.tx_timeout_ms = 2000; g_cdc.require_dtr_tx = 0; g_cdc.connected = 0; g_cdc.configured = 0; g_cdc.rx_rb = rt_ringbuffer_create(CDC_RX_RING_SIZE); if (!g_cdc.rx_rb) return -RT_ENOMEM; g_cdc.tx_sem = rt_sem_create("cdc_tx", 0, RT_IPC_FLAG_FIFO); if (!g_cdc.tx_sem) return -RT_ENOMEM; g_cdc.tx_lock = rt_mutex_create("cdc_txL", RT_IPC_FLAG_FIFO); if (!g_cdc.tx_lock) return -RT_ENOMEM; /* register char device */ g_cdc.dev.type = RT_Device_Class_Char; g_cdc.dev.rx_indicate = RT_NULL; g_cdc.dev.tx_complete = RT_NULL; g_cdc.dev.ops = &cdc_ops; g_cdc.dev.user_data = RT_NULL; rt_err_t r = rt_device_register((rt_device_t)&g_cdc.dev, dev_name, RT_DEVICE_FLAG_RDWR); if (r != RT_EOK) return r; /* init CherryUSB CDC */ usbd_desc_register(g_cdc.busid, &g_cdc_desc); usbd_add_interface(g_cdc.busid, usbd_cdc_acm_init_intf(g_cdc.busid, &g_cdc.intf0)); usbd_add_interface(g_cdc.busid, usbd_cdc_acm_init_intf(g_cdc.busid, &g_cdc.intf1)); g_cdc.out_ep = g_out_ep; g_cdc.in_ep = g_in_ep; usbd_add_endpoint(g_cdc.busid, &g_cdc.out_ep); usbd_add_endpoint(g_cdc.busid, &g_cdc.in_ep); usbd_initialize(g_cdc.busid, g_cdc.reg_base, usbd_event_handler); return 0; }