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