1 /*
2 * Copyright (c) 2025, sakumisu
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6 #include <rtthread.h>
7 #include <rtdevice.h>
8
9 #include "usbd_core.h"
10 #include "usbd_cdc_acm.h"
11
12 #define DEV_FORMAT_CDC_ACM "usb-acm%d"
13
14 #ifndef CONFIG_USBDEV_MAX_CDC_ACM_CLASS
15 #define CONFIG_USBDEV_MAX_CDC_ACM_CLASS (4)
16 #endif
17
18 #ifndef CONFIG_USBDEV_SERIAL_RX_BUFSIZE
19 #define CONFIG_USBDEV_SERIAL_RX_BUFSIZE (2048)
20 #endif
21
22 struct usbd_serial {
23 struct rt_device parent;
24 uint8_t busid;
25 uint8_t in_ep;
26 uint8_t out_ep;
27 struct usbd_interface intf_ctrl;
28 struct usbd_interface intf_data;
29 usb_osal_sem_t tx_done;
30 uint8_t minor;
31 char name[32];
32 struct rt_ringbuffer rx_rb;
33 rt_uint8_t rx_rb_buffer[CONFIG_USBDEV_SERIAL_RX_BUFSIZE];
34 };
35
36 static uint32_t g_devinuse = 0;
37
38 static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_usbd_serial_cdc_acm_rx_buf[CONFIG_USBDEV_MAX_CDC_ACM_CLASS][USB_ALIGN_UP(512, CONFIG_USB_ALIGN_SIZE)];
39
40 static struct usbd_serial g_usbd_serial_cdc_acm[CONFIG_USBDEV_MAX_CDC_ACM_CLASS];
41
usbd_serial_alloc(void)42 static struct usbd_serial *usbd_serial_alloc(void)
43 {
44 uint8_t devno;
45 struct usbd_serial *serial;
46
47 for (devno = 0; devno < CONFIG_USBDEV_MAX_CDC_ACM_CLASS; devno++) {
48 if ((g_devinuse & (1U << devno)) == 0) {
49 g_devinuse |= (1U << devno);
50
51 serial = &g_usbd_serial_cdc_acm[devno];
52 memset(serial, 0, sizeof(struct usbd_serial));
53 serial->minor = devno;
54 snprintf(serial->name, CONFIG_USBHOST_DEV_NAMELEN, DEV_FORMAT_CDC_ACM, serial->minor);
55 return serial;
56 }
57 }
58 return NULL;
59 }
60
usbd_serial_free(struct usbd_serial * serial)61 static void usbd_serial_free(struct usbd_serial *serial)
62 {
63 uint8_t devno = serial->minor;
64
65 if (devno < 32) {
66 g_devinuse &= ~(1U << devno);
67 }
68 memset(serial, 0, sizeof(struct usbd_serial));
69 }
70
usbd_serial_open(struct rt_device * dev,rt_uint16_t oflag)71 static rt_err_t usbd_serial_open(struct rt_device *dev, rt_uint16_t oflag)
72 {
73 struct usbd_serial *serial;
74
75 RT_ASSERT(dev != RT_NULL);
76
77 serial = (struct usbd_serial *)dev;
78
79 if (!usb_device_is_configured(serial->busid)) {
80 USB_LOG_ERR("USB device is not configured\n");
81 return -RT_EPERM;
82 }
83
84 usbd_ep_start_read(serial->busid, serial->out_ep,
85 g_usbd_serial_cdc_acm_rx_buf[serial->minor],
86 usbd_get_ep_mps(serial->busid, serial->out_ep));
87 return RT_EOK;
88 }
89
usbd_serial_read(struct rt_device * dev,rt_off_t pos,void * buffer,rt_size_t size)90 static rt_ssize_t usbd_serial_read(struct rt_device *dev,
91 rt_off_t pos,
92 void *buffer,
93 rt_size_t size)
94 {
95 struct usbd_serial *serial;
96
97 RT_ASSERT(dev != RT_NULL);
98
99 serial = (struct usbd_serial *)dev;
100
101 if (!usb_device_is_configured(serial->busid)) {
102 return -RT_EPERM;
103 }
104
105 return rt_ringbuffer_get(&serial->rx_rb, (rt_uint8_t *)buffer, size);
106 }
107
usbd_serial_write(struct rt_device * dev,rt_off_t pos,const void * buffer,rt_size_t size)108 static rt_ssize_t usbd_serial_write(struct rt_device *dev,
109 rt_off_t pos,
110 const void *buffer,
111 rt_size_t size)
112 {
113 struct usbd_serial *serial;
114 int ret = 0;
115 rt_uint8_t *align_buf;
116
117 RT_ASSERT(dev != RT_NULL);
118
119 serial = (struct usbd_serial *)dev;
120
121 if (!usb_device_is_configured(serial->busid)) {
122 return -RT_EPERM;
123 }
124 align_buf = (rt_uint8_t *)buffer;
125
126 #ifdef CONFIG_USB_DCACHE_ENABLE
127 if ((uint32_t)buffer & (CONFIG_USB_ALIGN_SIZE - 1)) {
128 align_buf = rt_malloc_align(size, CONFIG_USB_ALIGN_SIZE);
129 if (!align_buf) {
130 USB_LOG_ERR("serial get align buf failed\n");
131 return 0;
132 }
133
134 usb_memcpy(align_buf, buffer, size);
135 }
136 #endif
137 usb_osal_sem_reset(serial->tx_done);
138 usbd_ep_start_write(serial->busid, serial->in_ep, align_buf, size);
139 ret = usb_osal_sem_take(serial->tx_done, 3000);
140 if (ret < 0) {
141 USB_LOG_ERR("serial write timeout\n");
142 ret = -RT_ETIMEOUT;
143 } else {
144 ret = size;
145 }
146
147 #ifdef CONFIG_USB_DCACHE_ENABLE
148 if ((uint32_t)buffer & (CONFIG_USB_ALIGN_SIZE - 1)) {
149 rt_free_align(align_buf);
150 }
151 #endif
152
153 return ret;
154 }
155
156 #ifdef RT_USING_DEVICE_OPS
157 const static struct rt_device_ops usbd_serial_ops = {
158 NULL,
159 usbd_serial_open,
160 NULL,
161 usbd_serial_read,
162 usbd_serial_write,
163 NULL
164 };
165 #endif
166
usbd_serial_register(struct usbd_serial * serial,void * data)167 rt_err_t usbd_serial_register(struct usbd_serial *serial,
168 void *data)
169 {
170 rt_err_t ret;
171 struct rt_device *device;
172 RT_ASSERT(serial != RT_NULL);
173
174 device = &(serial->parent);
175
176 device->type = RT_Device_Class_Char;
177 device->rx_indicate = RT_NULL;
178 device->tx_complete = RT_NULL;
179
180 #ifdef RT_USING_DEVICE_OPS
181 device->ops = &usbd_serial_ops;
182 #else
183 device->init = NULL;
184 device->open = usbd_serial_open;
185 device->close = NULL;
186 device->read = usbd_serial_read;
187 device->write = usbd_serial_write;
188 device->control = NULL;
189 #endif
190 device->user_data = data;
191
192 /* register a character device */
193 ret = rt_device_register(device, serial->name, RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX | RT_DEVICE_FLAG_REMOVABLE);
194
195 #ifdef RT_USING_POSIX_DEVIO
196 /* set fops */
197 device->fops = NULL;
198 #endif
199 rt_ringbuffer_init(&serial->rx_rb, serial->rx_rb_buffer, sizeof(serial->rx_rb_buffer));
200
201 return ret;
202 }
203
usbd_cdc_acm_bulk_out(uint8_t busid,uint8_t ep,uint32_t nbytes)204 void usbd_cdc_acm_bulk_out(uint8_t busid, uint8_t ep, uint32_t nbytes)
205 {
206 struct usbd_serial *serial;
207
208 for (uint8_t devno = 0; devno < CONFIG_USBDEV_MAX_CDC_ACM_CLASS; devno++) {
209 serial = &g_usbd_serial_cdc_acm[devno];
210 if (serial->out_ep == ep) {
211 rt_ringbuffer_put(&serial->rx_rb, g_usbd_serial_cdc_acm_rx_buf[serial->minor], nbytes);
212 usbd_ep_start_read(serial->busid, serial->out_ep,
213 g_usbd_serial_cdc_acm_rx_buf[serial->minor],
214 usbd_get_ep_mps(serial->busid, serial->out_ep));
215
216 if (serial->parent.rx_indicate) {
217 serial->parent.rx_indicate(&serial->parent, nbytes);
218 }
219 break;
220 }
221 }
222 }
223
usbd_cdc_acm_bulk_in(uint8_t busid,uint8_t ep,uint32_t nbytes)224 void usbd_cdc_acm_bulk_in(uint8_t busid, uint8_t ep, uint32_t nbytes)
225 {
226 struct usbd_serial *serial;
227
228 if ((nbytes % usbd_get_ep_mps(busid, ep)) == 0 && nbytes) {
229 /* send zlp */
230 usbd_ep_start_write(busid, ep, NULL, 0);
231 } else {
232 for (uint8_t devno = 0; devno < CONFIG_USBDEV_MAX_CDC_ACM_CLASS; devno++) {
233 serial = &g_usbd_serial_cdc_acm[devno];
234 if ((serial->in_ep == ep) && serial->tx_done) {
235 usb_osal_sem_give(serial->tx_done);
236 break;
237 }
238 }
239 }
240 }
241
usbd_cdc_acm_serial_init(uint8_t busid,uint8_t in_ep,uint8_t out_ep)242 void usbd_cdc_acm_serial_init(uint8_t busid, uint8_t in_ep, uint8_t out_ep)
243 {
244 struct usbd_serial *serial;
245
246 struct usbd_endpoint cdc_out_ep = {
247 .ep_addr = out_ep,
248 .ep_cb = usbd_cdc_acm_bulk_out
249 };
250
251 struct usbd_endpoint cdc_in_ep = {
252 .ep_addr = in_ep,
253 .ep_cb = usbd_cdc_acm_bulk_in
254 };
255
256 serial = usbd_serial_alloc();
257 if (serial == NULL) {
258 USB_LOG_ERR("No more serial device available\n");
259 return;
260 }
261
262 serial->busid = busid;
263 serial->in_ep = in_ep;
264 serial->out_ep = out_ep;
265 serial->tx_done = usb_osal_sem_create(0);
266
267 usbd_add_interface(busid, usbd_cdc_acm_init_intf(busid, &serial->intf_ctrl));
268 usbd_add_interface(busid, usbd_cdc_acm_init_intf(busid, &serial->intf_data));
269 usbd_add_endpoint(busid, &cdc_out_ep);
270 usbd_add_endpoint(busid, &cdc_in_ep);
271
272 if (usbd_serial_register(serial, NULL) != RT_EOK) {
273 USB_LOG_ERR("Failed to register serial device\n");
274 usbd_serial_free(serial);
275 return;
276 }
277
278 USB_LOG_INFO("USB CDC ACM Serial Device %s initialized\n", serial->name);
279 }
280