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+ struct usbd_interface ctrl_intf ;
30+ struct usbd_interface data_intf ;
31+ __attribute__((aligned (32 ))) uint8_t cache_tempbuffer [512 ];
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_ep_s {
37+ uint32_t rxlen ;
38+ int error ;
39+ sem_t txdone_sem ;
40+ sem_t rxdone_sem ;
41+ bool used ;
42+ };
43+
44+ struct usbdev_serial_s * g_usb_cdcacm_serial [8 ] = { 0 };
45+ struct usbdev_serial_ep_s g_usb_cdcacm_serial_ep [2 ][8 ] = { 0 };
46+
47+ void usbd_cdc_acm_bulk_out (uint8_t busid , uint8_t ep , uint32_t nbytes )
48+ {
49+ g_usb_cdcacm_serial_ep [0 ][ep & 0x0f ].error = 0 ;
50+ g_usb_cdcacm_serial_ep [0 ][ep & 0x0f ].rxlen = nbytes ;
51+ nxsem_post (& g_usb_cdcacm_serial_ep [0 ][ep & 0x0f ].rxdone_sem );
52+ }
53+
54+ void usbd_cdc_acm_bulk_in (uint8_t busid , uint8_t ep , uint32_t nbytes )
55+ {
56+ if ((nbytes % usbd_get_ep_mps (busid , ep )) == 0 && nbytes ) {
57+ /* send zlp */
58+ usbd_ep_start_write (busid , ep , NULL , 0 );
59+ } else {
60+ nxsem_post (& g_usb_cdcacm_serial_ep [1 ][ep & 0x0f ].txdone_sem );
61+ }
62+ }
63+
64+ /* Character driver methods */
65+
66+ static int usbdev_open (FAR struct file * filep );
67+ static int usbdev_close (FAR struct file * filep );
68+ static ssize_t usbdev_read (FAR struct file * filep , FAR char * buffer ,
69+ size_t buflen );
70+ static ssize_t usbdev_write (FAR struct file * filep ,
71+ FAR const char * buffer , size_t buflen );
72+
73+ /****************************************************************************
74+ * Private Data
75+ ****************************************************************************/
76+
77+ static const struct file_operations g_usbdevops = {
78+ usbdev_open , /* open */
79+ usbdev_close , /* close */
80+ usbdev_read , /* read */
81+ usbdev_write , /* write */
82+ NULL , /* seek */
83+ NULL , /* ioctl */
84+ NULL , /* mmap */
85+ NULL , /* truncate */
86+ NULL /* poll */
87+ };
88+
89+ static int usbdev_open (FAR struct file * filep )
90+ {
91+ FAR struct inode * inode = filep -> f_inode ;
92+
93+ DEBUGASSERT (inode -> i_private );
94+
95+ if (usb_device_is_configured (0 )) {
96+ return OK ;
97+ } else {
98+ return - ENODEV ;
99+ }
100+ }
101+
102+ static int usbdev_close (FAR struct file * filep )
103+ {
104+ FAR struct inode * inode = filep -> f_inode ;
105+
106+ DEBUGASSERT (inode -> i_private );
107+
108+ if (!usb_device_is_configured (0 )) {
109+ return - ENODEV ;
110+ }
111+
112+ return 0 ;
113+ }
114+
115+ static ssize_t usbdev_read (FAR struct file * filep , FAR char * buffer ,
116+ size_t buflen )
117+ {
118+ FAR struct inode * inode = filep -> f_inode ;
119+ struct usbdev_serial_s * serial ;
120+ int ret ;
121+
122+ DEBUGASSERT (inode -> i_private );
123+ serial = (struct usbdev_serial_s * )inode -> i_private ;
124+
125+ if (!usb_device_is_configured (0 )) {
126+ return - ENODEV ;
127+ }
128+
129+ while (circbuf_used (& serial -> circ ) == 0 ) {
130+ nxsem_reset (& g_usb_cdcacm_serial_ep [0 ][serial -> outep & 0x0f ].rxdone_sem , 0 );
131+ usbd_ep_start_read (0 , serial -> outep , serial -> cache_tempbuffer , usbd_get_ep_mps (0 , serial -> outep ));
132+ ret = nxsem_wait (& g_usb_cdcacm_serial_ep [0 ][serial -> outep & 0x0f ].rxdone_sem );
133+ if (ret < 0 ) {
134+ return ret ;
135+ }
136+ if (g_usb_cdcacm_serial_ep [0 ][serial -> outep & 0x0f ].error < 0 ) {
137+ return g_usb_cdcacm_serial_ep [0 ][serial -> outep & 0x0f ].error ;
138+ }
139+ #if defined(CONFIG_ARCH_DCACHE ) && !defined(CONFIG_USB_DCACHE_ENABLE )
140+ up_invalidate_dcache ((uintptr_t )serial -> cache_tempbuffer , (uintptr_t )(serial -> cache_tempbuffer + USB_ALIGN_UP (g_usb_cdcacm_serial_ep [0 ][serial -> outep & 0x0f ].rxlen , 64 )));
141+ #endif
142+ circbuf_overwrite (& serial -> circ , serial -> cache_tempbuffer , g_usb_cdcacm_serial_ep [0 ][serial -> outep & 0x0f ].rxlen );
143+ }
144+ return circbuf_read (& serial -> circ , buffer , buflen );
145+ }
146+
147+ static ssize_t usbdev_write (FAR struct file * filep , FAR const char * buffer ,
148+ size_t buflen )
149+ {
150+ FAR struct inode * inode = filep -> f_inode ;
151+ struct usbdev_serial_s * serial ;
152+ int ret ;
153+
154+ DEBUGASSERT (inode -> i_private );
155+ serial = (struct usbdev_serial_s * )inode -> i_private ;
156+
157+ if (!usb_device_is_configured (0 )) {
158+ return - ENODEV ;
159+ }
160+
161+ #ifdef CONFIG_ARCH_DCACHE
162+ uint32_t write_len = 0 ;
163+
164+ while (write_len < buflen ) {
165+ uint32_t len = buflen - write_len ;
166+ if (len > CONFIG_USBDEV_CDCACM_TXBUFSIZE ) {
167+ len = CONFIG_USBDEV_CDCACM_TXBUFSIZE ;
168+ }
169+ memcpy (serial -> cache_txbuffer , buffer + write_len , len );
170+ #ifndef CONFIG_USB_DCACHE_ENABLE
171+ up_clean_dcache ((uintptr_t )serial -> cache_txbuffer , (uintptr_t )(serial -> cache_txbuffer + USB_ALIGN_UP (len , 64 )));
172+ #endif
173+ nxsem_reset (& g_usb_cdcacm_serial_ep [0 ][serial -> inep & 0x0f ].txdone_sem , 0 );
174+ usbd_ep_start_write (0 , serial -> inep , serial -> cache_txbuffer , len );
175+ ret = nxsem_wait (& g_usb_cdcacm_serial_ep [0 ][serial -> inep & 0x0f ].txdone_sem );
176+ if (ret < 0 ) {
177+ return ret ;
178+ } else {
179+ if (g_usb_cdcacm_serial_ep [1 ][serial -> inep & 0x0f ].error < 0 ) {
180+ return g_usb_cdcacm_serial_ep [1 ][serial -> inep & 0x0f ].error ;
181+ }
182+ write_len += len ;
183+ }
184+ }
185+ return buflen ;
186+ #else
187+ nxsem_reset (& g_usb_cdcacm_serial_ep [0 ][outep & 0x0f ].txdone_sem , 0 );
188+ usbd_ep_start_write (0 , serial -> inep , buffer , buflen );
189+ ret = nxsem_wait (& g_usb_cdcacm_serial_ep [0 ][outep & 0x0f ].txdone_sem );
190+ if (ret < 0 ) {
191+ return ret ;
192+ } else {
193+ if (g_usb_cdcacm_serial_ep [1 ][serial -> inep & 0x0f ].error < 0 ) {
194+ return g_usb_cdcacm_serial_ep [1 ][serial -> inep & 0x0f ].error ;
195+ }
196+ return buflen ;
197+ }
198+ #endif
199+ }
200+
201+ static struct usbd_endpoint cdc_out_ep [8 ] = { 0 };
202+ static struct usbd_endpoint cdc_in_ep [8 ] = { 0 };
203+
204+ static void cdcacm_notify_handler (uint8_t busid , uint8_t event , void * arg )
205+ {
206+ switch (event ) {
207+ case USBD_EVENT_RESET :
208+ break ;
209+ case USBD_EVENT_DISCONNECTED :
210+ for (size_t i = 0 ; i < 8 ; i ++ ) {
211+ if (g_usb_cdcacm_serial_ep [0 ][i & 0x0f ].used ) {
212+ g_usb_cdcacm_serial_ep [0 ][i & 0x0f ].error = - ESHUTDOWN ;
213+ nxsem_post (& g_usb_cdcacm_serial_ep [0 ][i & 0x0f ].rxdone_sem );
214+ }
215+ if (g_usb_cdcacm_serial_ep [1 ][i & 0x0f ].used ) {
216+ g_usb_cdcacm_serial_ep [1 ][i & 0x0f ].error = - ESHUTDOWN ;
217+ nxsem_post (& g_usb_cdcacm_serial_ep [1 ][i & 0x0f ].txdone_sem );
218+ }
219+ }
220+ break ;
221+ case USBD_EVENT_CONFIGURED :
222+ for (size_t i = 0 ; i < 8 ; i ++ ) {
223+ if (g_usb_cdcacm_serial_ep [0 ][i & 0x0f ].used ) {
224+ g_usb_cdcacm_serial_ep [0 ][i & 0x0f ].error = 0 ;
225+ nxsem_post (& g_usb_cdcacm_serial_ep [0 ][i & 0x0f ].rxdone_sem );
226+ }
227+ if (g_usb_cdcacm_serial_ep [1 ][i & 0x0f ].used ) {
228+ g_usb_cdcacm_serial_ep [1 ][i & 0x0f ].error = 0 ;
229+ nxsem_post (& g_usb_cdcacm_serial_ep [1 ][i & 0x0f ].txdone_sem );
230+ }
231+ }
232+ break ;
233+ default :
234+ break ;
235+ }
236+ }
237+
238+ void usbd_cdcacm_init (uint8_t busid , uint8_t id , const char * path , uint8_t outep , uint8_t inep )
239+ {
240+ g_usb_cdcacm_serial [id ] = kmm_malloc (sizeof (struct usbdev_serial_s ));
241+ DEBUGASSERT (g_usb_cdcacm_serial [id ]);
242+
243+ memset (g_usb_cdcacm_serial [id ], 0 , sizeof (struct usbdev_serial_s ));
244+ strncpy (g_usb_cdcacm_serial [id ]-> name , path , sizeof (g_usb_cdcacm_serial [id ]-> name ) - 1 );
245+
246+ circbuf_init (& g_usb_cdcacm_serial [id ]-> circ , g_usb_cdcacm_serial [id ]-> cache_rxbuffer , CONFIG_USBDEV_CDCACM_RXBUFSIZE );
247+
248+ nxsem_init (& g_usb_cdcacm_serial_ep [0 ][outep & 0x0f ].rxdone_sem , 0 , 0 );
249+ nxsem_init (& g_usb_cdcacm_serial_ep [1 ][inep & 0x0f ].txdone_sem , 0 , 0 );
250+ g_usb_cdcacm_serial_ep [0 ][outep & 0x0f ].used = true;
251+ g_usb_cdcacm_serial_ep [1 ][inep & 0x0f ].used = true;
252+
253+ usbd_add_interface (busid , usbd_cdc_acm_init_intf (busid , & g_usb_cdcacm_serial [id ]-> ctrl_intf ));
254+ usbd_add_interface (busid , usbd_cdc_acm_init_intf (busid , & g_usb_cdcacm_serial [id ]-> data_intf ));
255+ g_usb_cdcacm_serial [id ]-> ctrl_intf .notify_handler = cdcacm_notify_handler ;
256+ g_usb_cdcacm_serial [id ]-> outep = outep ;
257+ g_usb_cdcacm_serial [id ]-> inep = inep ;
258+
259+ cdc_out_ep [id ].ep_addr = outep ;
260+ cdc_out_ep [id ].ep_cb = usbd_cdc_acm_bulk_out ;
261+ cdc_in_ep [id ].ep_addr = inep ;
262+ cdc_in_ep [id ].ep_cb = usbd_cdc_acm_bulk_in ;
263+
264+ usbd_add_endpoint (busid , & cdc_out_ep [id ]);
265+ usbd_add_endpoint (busid , & cdc_in_ep [id ]);
266+
267+ register_driver (path , & g_usbdevops , 0666 , g_usb_cdcacm_serial [id ]);
268+ }
269+
270+ void usbd_cdcacm_deinit (uint8_t busid , uint8_t id )
271+ {
272+ unregister_driver (g_usb_cdcacm_serial [id ]-> name );
273+
274+ kmm_free (g_usb_cdcacm_serial [id ]);
275+ }
0 commit comments