1+ /*
2+ * Copyright (c) 2025, sakumisu
3+ *
4+ * SPDX-License-Identifier: Apache-2.0
5+ */
6+ #include <nuttx/kmalloc.h>
7+ #include <nuttx/mutex.h>
8+ #include <nuttx/semaphore.h>
9+
10+ #include "usbd_core.h"
11+ #include "usbd_cdc_acm.h"
12+
13+ #include <nuttx/mm/circbuf.h>
14+
15+ #ifndef CONFIG_USBDEV_CDCACM_RXBUFSIZE
16+ #define CONFIG_USBDEV_CDCACM_RXBUFSIZE 512
17+ #endif
18+
19+ #ifndef CONFIG_USBDEV_CDCACM_TXBUFSIZE
20+ #define CONFIG_USBDEV_CDCACM_TXBUFSIZE 512
21+ #endif
22+
23+ struct usbdev_serial_s {
24+ char name [16 ];
25+ struct circbuf_s circ ;
26+ uint8_t inep ;
27+ uint8_t outep ;
28+ sem_t txdone_sem ;
29+ sem_t rxdone_sem ;
30+ struct usbd_interface ctrl_intf ;
31+ struct usbd_interface data_intf ;
32+ __attribute__((aligned (32 ))) uint8_t cache_rxbuffer [CONFIG_USBDEV_CDCACM_RXBUFSIZE ];
33+ __attribute__((aligned (32 ))) uint8_t cache_txbuffer [CONFIG_USBDEV_CDCACM_TXBUFSIZE ];
34+ };
35+
36+ struct usbdev_serial_s * g_usb_cdcacm_serial [8 ];
37+
38+ static int nuttx_errorcode (int error )
39+ {
40+ int err = 0 ;
41+
42+ switch (error ) {
43+ case - USB_ERR_NOMEM :
44+ err = - EIO ;
45+ break ;
46+ case - USB_ERR_INVAL :
47+ err = - EINVAL ;
48+ break ;
49+ case - USB_ERR_NODEV :
50+ err = - ENODEV ;
51+ break ;
52+ case - USB_ERR_NOTCONN :
53+ err = - ENOTCONN ;
54+ break ;
55+ case - USB_ERR_NOTSUPP :
56+ err = - EIO ;
57+ break ;
58+ case - USB_ERR_BUSY :
59+ err = - EBUSY ;
60+ break ;
61+ case - USB_ERR_RANGE :
62+ err = - ERANGE ;
63+ break ;
64+ case - USB_ERR_STALL :
65+ err = - EPERM ;
66+ break ;
67+ case - USB_ERR_NAK :
68+ err = - EAGAIN ;
69+ break ;
70+ case - USB_ERR_DT :
71+ err = - EIO ;
72+ break ;
73+ case - USB_ERR_IO :
74+ err = - EIO ;
75+ break ;
76+ case - USB_ERR_SHUTDOWN :
77+ err = - ESHUTDOWN ;
78+ break ;
79+ case - USB_ERR_TIMEOUT :
80+ err = - ETIMEDOUT ;
81+ break ;
82+
83+ default :
84+ break ;
85+ }
86+ return err ;
87+ }
88+
89+ void usbd_cdc_acm_bulk_out1 (uint8_t busid , uint8_t ep , uint32_t nbytes )
90+ {
91+ nxsem_post (& g_usb_cdcacm_serial [0 ]-> rxdone_sem );
92+ }
93+
94+ void usbd_cdc_acm_bulk_in1 (uint8_t busid , uint8_t ep , uint32_t nbytes )
95+ {
96+ if ((nbytes % usbd_get_ep_mps (busid , ep )) == 0 && nbytes ) {
97+ /* send zlp */
98+ usbd_ep_start_write (busid , ep , NULL , 0 );
99+ } else {
100+ nxsem_post (& g_usb_cdcacm_serial [0 ]-> txdone_sem );
101+ }
102+ }
103+
104+ void usbd_cdc_acm_bulk_out2 (uint8_t busid , uint8_t ep , uint32_t nbytes )
105+ {
106+ nxsem_post (& g_usb_cdcacm_serial [1 ]-> rxdone_sem );
107+ }
108+
109+ void usbd_cdc_acm_bulk_in2 (uint8_t busid , uint8_t ep , uint32_t nbytes )
110+ {
111+ if ((nbytes % usbd_get_ep_mps (busid , ep )) == 0 && nbytes ) {
112+ /* send zlp */
113+ usbd_ep_start_write (busid , ep , NULL , 0 );
114+ } else {
115+ nxsem_post (& g_usb_cdcacm_serial [1 ]-> txdone_sem );
116+ }
117+ }
118+
119+ void usbd_cdc_acm_bulk_out3 (uint8_t busid , uint8_t ep , uint32_t nbytes )
120+ {
121+ nxsem_post (& g_usb_cdcacm_serial [2 ]-> rxdone_sem );
122+ }
123+
124+ void usbd_cdc_acm_bulk_in3 (uint8_t busid , uint8_t ep , uint32_t nbytes )
125+ {
126+ if ((nbytes % usbd_get_ep_mps (busid , ep )) == 0 && nbytes ) {
127+ /* send zlp */
128+ usbd_ep_start_write (busid , ep , NULL , 0 );
129+ } else {
130+ nxsem_post (& g_usb_cdcacm_serial [2 ]-> txdone_sem );
131+ }
132+ }
133+
134+ /* Character driver methods */
135+
136+ static int usbdev_open (FAR struct file * filep );
137+ static int usbdev_close (FAR struct file * filep );
138+ static ssize_t usbdev_read (FAR struct file * filep , FAR char * buffer ,
139+ size_t buflen );
140+ static ssize_t usbdev_write (FAR struct file * filep ,
141+ FAR const char * buffer , size_t buflen );
142+
143+ /****************************************************************************
144+ * Private Data
145+ ****************************************************************************/
146+
147+ static const struct file_operations g_usbdevops = {
148+ usbdev_open , /* open */
149+ usbdev_close , /* close */
150+ usbdev_read , /* read */
151+ usbdev_write , /* write */
152+ NULL , /* seek */
153+ NULL , /* ioctl */
154+ NULL , /* mmap */
155+ NULL , /* truncate */
156+ NULL /* poll */
157+ };
158+
159+ static int usbdev_open (FAR struct file * filep )
160+ {
161+ FAR struct inode * inode = filep -> f_inode ;
162+
163+ DEBUGASSERT (inode -> i_private );
164+
165+ if (usb_device_is_configured (0 )) {
166+ return OK ;
167+ } else {
168+ return - ENODEV ;
169+ }
170+ }
171+
172+ static int usbdev_close (FAR struct file * filep )
173+ {
174+ FAR struct inode * inode = filep -> f_inode ;
175+
176+ DEBUGASSERT (inode -> i_private );
177+
178+ if (!usb_device_is_configured (0 )) {
179+ return - ENODEV ;
180+ }
181+
182+ return 0 ;
183+ }
184+
185+ static ssize_t usbdev_read (FAR struct file * filep , FAR char * buffer ,
186+ size_t buflen )
187+ {
188+ FAR struct inode * inode = filep -> f_inode ;
189+ struct usbdev_serial_s * serial ;
190+ __attribute__((aligned (32 ))) uint8_t cache_tempbuffer [512 ];
191+ int ret ;
192+
193+ DEBUGASSERT (inode -> i_private );
194+ serial = (struct usbdev_serial_s * )inode -> i_private ;
195+
196+ if (!usb_device_is_configured (0 )) {
197+ return - ENODEV ;
198+ }
199+
200+ while (circbuf_used (& serial -> circ ) < buflen ) {
201+ nxsem_trywait (& serial -> rxdone_sem );
202+ usbd_ep_start_read (0 , serial -> outep , cache_tempbuffer , usbd_get_ep_mps (0 , serial -> outep ));
203+ ret = nxsem_wait (& serial -> rxdone_sem );
204+ if (ret < 0 ) {
205+ return nuttx_errorcode (ret );
206+ }
207+ #if defined(CONFIG_ARCH_DCACHE ) && !defined(CONFIG_USB_DCACHE_ENABLE )
208+ up_invalidate_dcache ((uintptr_t )cache_tempbuffer , (uintptr_t )(cache_tempbuffer + USB_ALIGN_UP (ret , 64 )));
209+ #endif
210+ circbuf_overwrite (& serial -> circ , cache_tempbuffer , ret );
211+ }
212+ circbuf_read (& serial -> circ , buffer , buflen );
213+ return buflen ;
214+ }
215+
216+ static ssize_t usbdev_write (FAR struct file * filep , FAR const char * buffer ,
217+ size_t buflen )
218+ {
219+ FAR struct inode * inode = filep -> f_inode ;
220+ struct usbdev_serial_s * serial ;
221+ int ret ;
222+
223+ DEBUGASSERT (inode -> i_private );
224+ serial = (struct usbdev_serial_s * )inode -> i_private ;
225+
226+ if (!usb_device_is_configured (0 )) {
227+ return - ENODEV ;
228+ }
229+
230+ #ifdef CONFIG_ARCH_DCACHE
231+ uint32_t write_len = 0 ;
232+
233+ while (write_len < buflen ) {
234+ uint32_t len = buflen - write_len ;
235+ if (len > CONFIG_USBDEV_CDCACM_TXBUFSIZE ) {
236+ len = CONFIG_USBDEV_CDCACM_TXBUFSIZE ;
237+ }
238+ memcpy (serial -> cache_txbuffer , buffer + write_len , len );
239+ #ifndef CONFIG_USB_DCACHE_ENABLE
240+ up_clean_dcache ((uintptr_t )serial -> cache_txbuffer , (uintptr_t )(serial -> cache_txbuffer + USB_ALIGN_UP (len , 64 )));
241+ #endif
242+ nxsem_trywait (& serial -> txdone_sem );
243+ usbd_ep_start_write (0 , serial -> inep , serial -> cache_txbuffer , usbd_get_ep_mps (0 , serial -> inep ));
244+ ret = nxsem_wait (& serial -> txdone_sem );
245+ if (ret < 0 ) {
246+ return nuttx_errorcode (ret );
247+ } else {
248+ write_len += len ;
249+ }
250+ }
251+ return buflen ;
252+ #else
253+ nxsem_trywait (& serial -> txdone_sem );
254+ usbd_ep_start_write (0 , serial -> inep , buffer , buflen );
255+ ret = nxsem_wait (& serial -> txdone_sem );
256+ if (ret < 0 ) {
257+ return nuttx_errorcode (ret );
258+ } else {
259+ return buflen ;
260+ }
261+ #endif
262+ }
263+
264+ static struct usbd_endpoint cdc_out_ep [8 ] = {
265+ { .ep_addr = 0 ,
266+ .ep_cb = usbd_cdc_acm_bulk_out1 },
267+ { .ep_addr = 0 ,
268+ .ep_cb = usbd_cdc_acm_bulk_out2 },
269+ { .ep_addr = 0 ,
270+ .ep_cb = usbd_cdc_acm_bulk_out3 }
271+ };
272+
273+ static struct usbd_endpoint cdc_in_ep [8 ] = {
274+ { .ep_addr = 0 ,
275+ .ep_cb = usbd_cdc_acm_bulk_in1 },
276+ { .ep_addr = 0 ,
277+ .ep_cb = usbd_cdc_acm_bulk_in2 },
278+ { .ep_addr = 0 ,
279+ .ep_cb = usbd_cdc_acm_bulk_in3 }
280+ };
281+
282+ static void cdcacm_notify_handler1 (uint8_t busid , uint8_t event , void * arg )
283+ {
284+ switch (event ) {
285+ case USBD_EVENT_RESET :
286+ break ;
287+
288+ case USBD_EVENT_CONFIGURED :
289+ nxsem_post (& g_usb_cdcacm_serial [0 ]-> txdone_sem );
290+ nxsem_post (& g_usb_cdcacm_serial [0 ]-> rxdone_sem );
291+ break ;
292+ default :
293+ break ;
294+ }
295+ }
296+
297+ static void cdcacm_notify_handler2 (uint8_t busid , uint8_t event , void * arg )
298+ {
299+ switch (event ) {
300+ case USBD_EVENT_RESET :
301+
302+ break ;
303+
304+ case USBD_EVENT_CONFIGURED :
305+ nxsem_post (& g_usb_cdcacm_serial [1 ]-> txdone_sem );
306+ nxsem_post (& g_usb_cdcacm_serial [1 ]-> rxdone_sem );
307+ break ;
308+ default :
309+ break ;
310+ }
311+ }
312+
313+ static void cdcacm_notify_handler3 (uint8_t busid , uint8_t event , void * arg )
314+ {
315+ switch (event ) {
316+ case USBD_EVENT_RESET :
317+
318+ break ;
319+
320+ case USBD_EVENT_CONFIGURED :
321+ nxsem_post (& g_usb_cdcacm_serial [2 ]-> txdone_sem );
322+ nxsem_post (& g_usb_cdcacm_serial [2 ]-> rxdone_sem );
323+ break ;
324+ default :
325+ break ;
326+ }
327+ }
328+
329+ usbd_notify_handler cdcacm_notify_handler [8 ] = {
330+ cdcacm_notify_handler1 ,
331+ cdcacm_notify_handler2 ,
332+ cdcacm_notify_handler3
333+ };
334+
335+ void usbd_cdcacm_init (uint8_t busid , uint8_t id , const char * name , uint8_t inep , uint8_t outep )
336+ {
337+ g_usb_cdcacm_serial [id ] = kmm_malloc (sizeof (struct usbdev_serial_s ));
338+ DEBUGASSERT (g_usb_cdcacm_serial [id ]);
339+
340+ memset (g_usb_cdcacm_serial [id ], 0 , sizeof (struct usbdev_serial_s ));
341+ memcpy (g_usb_cdcacm_serial [id ]-> name , name , strlen (name ));
342+
343+ circbuf_init (& g_usb_cdcacm_serial [id ]-> circ , g_usb_cdcacm_serial [id ]-> cache_rxbuffer , CONFIG_USBDEV_CDCACM_RXBUFSIZE );
344+
345+ nxsem_init (& g_usb_cdcacm_serial [id ]-> rxdone_sem , 0 , 0 );
346+ nxsem_init (& g_usb_cdcacm_serial [id ]-> txdone_sem , 0 , 0 );
347+
348+ usbd_add_interface (busid , usbd_cdc_acm_init_intf (busid , & g_usb_cdcacm_serial [id ]-> ctrl_intf ));
349+ usbd_add_interface (busid , usbd_cdc_acm_init_intf (busid , & g_usb_cdcacm_serial [id ]-> data_intf ));
350+ g_usb_cdcacm_serial [id ]-> ctrl_intf .notify_handler = cdcacm_notify_handler [id ];
351+
352+ cdc_out_ep [id ].ep_addr = outep ;
353+ cdc_in_ep [id ].ep_addr = inep ;
354+ usbd_add_endpoint (busid , & cdc_out_ep [id ]);
355+ usbd_add_endpoint (busid , & cdc_in_ep [id ]);
356+
357+ register_driver (name , & g_usbdevops , 0666 , g_usb_cdcacm_serial [id ]);
358+ }
359+
360+ void usbd_cdcacm_deinit (uint8_t busid , uint8_t id )
361+ {
362+ unregister_driver (g_usb_cdcacm_serial [id ]-> name );
363+
364+ kmm_free (g_usb_cdcacm_serial [id ]);
365+ }
0 commit comments