usbd_cdcacm.c 9.1 KB

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