cdc_device.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394
  1. /*
  2. * The MIT License (MIT)
  3. *
  4. * Copyright (c) 2019 Ha Thach (tinyusb.org)
  5. *
  6. * Permission is hereby granted, free of charge, to any person obtaining a copy
  7. * of this software and associated documentation files (the "Software"), to deal
  8. * in the Software without restriction, including without limitation the rights
  9. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10. * copies of the Software, and to permit persons to whom the Software is
  11. * furnished to do so, subject to the following conditions:
  12. *
  13. * The above copyright notice and this permission notice shall be included in
  14. * all copies or substantial portions of the Software.
  15. *
  16. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22. * THE SOFTWARE.
  23. *
  24. * This file is part of the TinyUSB stack.
  25. */
  26. #include "tusb_option.h"
  27. #if (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_CDC)
  28. #include "cdc_device.h"
  29. #include "device/usbd_pvt.h"
  30. //--------------------------------------------------------------------+
  31. // MACRO CONSTANT TYPEDEF
  32. //--------------------------------------------------------------------+
  33. typedef struct
  34. {
  35. uint8_t itf_num;
  36. uint8_t ep_notif;
  37. uint8_t ep_in;
  38. uint8_t ep_out;
  39. // Bit 0: DTR (Data Terminal Ready), Bit 1: RTS (Request to Send)
  40. uint8_t line_state;
  41. /*------------- From this point, data is not cleared by bus reset -------------*/
  42. char wanted_char;
  43. cdc_line_coding_t line_coding;
  44. // FIFO
  45. tu_fifo_t rx_ff;
  46. tu_fifo_t tx_ff;
  47. uint8_t rx_ff_buf[CFG_TUD_CDC_RX_BUFSIZE];
  48. uint8_t tx_ff_buf[CFG_TUD_CDC_TX_BUFSIZE];
  49. #if CFG_FIFO_MUTEX
  50. osal_mutex_def_t rx_ff_mutex;
  51. osal_mutex_def_t tx_ff_mutex;
  52. #endif
  53. // Endpoint Transfer buffer
  54. CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_CDC_EPSIZE];
  55. CFG_TUSB_MEM_ALIGN uint8_t epin_buf[CFG_TUD_CDC_EPSIZE];
  56. }cdcd_interface_t;
  57. #define ITF_MEM_RESET_SIZE offsetof(cdcd_interface_t, wanted_char)
  58. //--------------------------------------------------------------------+
  59. // INTERNAL OBJECT & FUNCTION DECLARATION
  60. //--------------------------------------------------------------------+
  61. CFG_TUSB_MEM_SECTION static cdcd_interface_t _cdcd_itf[CFG_TUD_CDC];
  62. static void _prep_out_transaction (uint8_t itf)
  63. {
  64. cdcd_interface_t* p_cdc = &_cdcd_itf[itf];
  65. // skip if previous transfer not complete
  66. if ( usbd_edpt_busy(TUD_OPT_RHPORT, p_cdc->ep_out) ) return;
  67. // Prepare for incoming data but only allow what we can store in the ring buffer.
  68. uint16_t max_read = tu_fifo_remaining(&p_cdc->rx_ff);
  69. if ( max_read >= CFG_TUD_CDC_EPSIZE )
  70. {
  71. usbd_edpt_xfer(TUD_OPT_RHPORT, p_cdc->ep_out, p_cdc->epout_buf, CFG_TUD_CDC_EPSIZE);
  72. }
  73. }
  74. //--------------------------------------------------------------------+
  75. // APPLICATION API
  76. //--------------------------------------------------------------------+
  77. bool tud_cdc_n_connected(uint8_t itf)
  78. {
  79. // DTR (bit 0) active is considered as connected
  80. return tud_ready() && tu_bit_test(_cdcd_itf[itf].line_state, 0);
  81. }
  82. uint8_t tud_cdc_n_get_line_state (uint8_t itf)
  83. {
  84. return _cdcd_itf[itf].line_state;
  85. }
  86. void tud_cdc_n_get_line_coding (uint8_t itf, cdc_line_coding_t* coding)
  87. {
  88. (*coding) = _cdcd_itf[itf].line_coding;
  89. }
  90. void tud_cdc_n_set_wanted_char (uint8_t itf, char wanted)
  91. {
  92. _cdcd_itf[itf].wanted_char = wanted;
  93. }
  94. //--------------------------------------------------------------------+
  95. // READ API
  96. //--------------------------------------------------------------------+
  97. uint32_t tud_cdc_n_available(uint8_t itf)
  98. {
  99. return tu_fifo_count(&_cdcd_itf[itf].rx_ff);
  100. }
  101. uint32_t tud_cdc_n_read(uint8_t itf, void* buffer, uint32_t bufsize)
  102. {
  103. uint32_t num_read = tu_fifo_read_n(&_cdcd_itf[itf].rx_ff, buffer, bufsize);
  104. _prep_out_transaction(itf);
  105. return num_read;
  106. }
  107. bool tud_cdc_n_peek(uint8_t itf, int pos, uint8_t* chr)
  108. {
  109. return tu_fifo_peek_at(&_cdcd_itf[itf].rx_ff, pos, chr);
  110. }
  111. void tud_cdc_n_read_flush (uint8_t itf)
  112. {
  113. tu_fifo_clear(&_cdcd_itf[itf].rx_ff);
  114. _prep_out_transaction(itf);
  115. }
  116. //--------------------------------------------------------------------+
  117. // WRITE API
  118. //--------------------------------------------------------------------+
  119. uint32_t tud_cdc_n_write(uint8_t itf, void const* buffer, uint32_t bufsize)
  120. {
  121. uint16_t ret = tu_fifo_write_n(&_cdcd_itf[itf].tx_ff, buffer, bufsize);
  122. #if 0 // TODO issue with circuitpython's REPL
  123. // flush if queue more than endpoint size
  124. if ( tu_fifo_count(&_cdcd_itf[itf].tx_ff) >= CFG_TUD_CDC_EPSIZE )
  125. {
  126. tud_cdc_n_write_flush(itf);
  127. }
  128. #endif
  129. return ret;
  130. }
  131. bool tud_cdc_n_write_flush (uint8_t itf)
  132. {
  133. cdcd_interface_t* p_cdc = &_cdcd_itf[itf];
  134. TU_VERIFY( !usbd_edpt_busy(TUD_OPT_RHPORT, p_cdc->ep_in) ); // skip if previous transfer not complete
  135. uint16_t count = tu_fifo_read_n(&_cdcd_itf[itf].tx_ff, p_cdc->epin_buf, CFG_TUD_CDC_EPSIZE);
  136. if ( count )
  137. {
  138. TU_VERIFY( tud_cdc_n_connected(itf) ); // fifo is empty if not connected
  139. TU_ASSERT( usbd_edpt_xfer(TUD_OPT_RHPORT, p_cdc->ep_in, p_cdc->epin_buf, count) );
  140. }
  141. return true;
  142. }
  143. uint32_t tud_cdc_n_write_available (uint8_t itf)
  144. {
  145. return tu_fifo_remaining(&_cdcd_itf[itf].tx_ff);
  146. }
  147. //--------------------------------------------------------------------+
  148. // USBD Driver API
  149. //--------------------------------------------------------------------+
  150. void cdcd_init(void)
  151. {
  152. tu_memclr(_cdcd_itf, sizeof(_cdcd_itf));
  153. for(uint8_t i=0; i<CFG_TUD_CDC; i++)
  154. {
  155. cdcd_interface_t* p_cdc = &_cdcd_itf[i];
  156. p_cdc->wanted_char = -1;
  157. // default line coding is : stop bit = 1, parity = none, data bits = 8
  158. p_cdc->line_coding.bit_rate = 115200;
  159. p_cdc->line_coding.stop_bits = 0;
  160. p_cdc->line_coding.parity = 0;
  161. p_cdc->line_coding.data_bits = 8;
  162. // config fifo
  163. tu_fifo_config(&p_cdc->rx_ff, p_cdc->rx_ff_buf, CFG_TUD_CDC_RX_BUFSIZE, 1, false);
  164. tu_fifo_config(&p_cdc->tx_ff, p_cdc->tx_ff_buf, CFG_TUD_CDC_TX_BUFSIZE, 1, false);
  165. #if CFG_FIFO_MUTEX
  166. tu_fifo_config_mutex(&p_cdc->rx_ff, osal_mutex_create(&p_cdc->rx_ff_mutex));
  167. tu_fifo_config_mutex(&p_cdc->tx_ff, osal_mutex_create(&p_cdc->tx_ff_mutex));
  168. #endif
  169. }
  170. }
  171. void cdcd_reset(uint8_t rhport)
  172. {
  173. (void) rhport;
  174. for(uint8_t i=0; i<CFG_TUD_CDC; i++)
  175. {
  176. tu_memclr(&_cdcd_itf[i], ITF_MEM_RESET_SIZE);
  177. tu_fifo_clear(&_cdcd_itf[i].rx_ff);
  178. tu_fifo_clear(&_cdcd_itf[i].tx_ff);
  179. }
  180. }
  181. bool cdcd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t *p_length)
  182. {
  183. // Only support ACM subclass
  184. TU_ASSERT ( CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL == itf_desc->bInterfaceSubClass);
  185. // Only support AT commands, no protocol and vendor specific commands.
  186. TU_ASSERT(tu_within(CDC_COMM_PROTOCOL_NONE, itf_desc->bInterfaceProtocol, CDC_COMM_PROTOCOL_ATCOMMAND_CDMA) ||
  187. itf_desc->bInterfaceProtocol == 0xff);
  188. // Find available interface
  189. cdcd_interface_t * p_cdc = NULL;
  190. uint8_t cdc_id;
  191. for(cdc_id=0; cdc_id<CFG_TUD_CDC; cdc_id++)
  192. {
  193. if ( _cdcd_itf[cdc_id].ep_in == 0 )
  194. {
  195. p_cdc = &_cdcd_itf[cdc_id];
  196. break;
  197. }
  198. }
  199. TU_ASSERT(p_cdc);
  200. //------------- Control Interface -------------//
  201. p_cdc->itf_num = itf_desc->bInterfaceNumber;
  202. uint8_t const * p_desc = tu_desc_next( itf_desc );
  203. (*p_length) = sizeof(tusb_desc_interface_t);
  204. // Communication Functional Descriptors
  205. while ( TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc) )
  206. {
  207. (*p_length) += tu_desc_len(p_desc);
  208. p_desc = tu_desc_next(p_desc);
  209. }
  210. if ( TUSB_DESC_ENDPOINT == tu_desc_type(p_desc) )
  211. {
  212. // notification endpoint if any
  213. TU_ASSERT( dcd_edpt_open(rhport, (tusb_desc_endpoint_t const *) p_desc) );
  214. p_cdc->ep_notif = ((tusb_desc_endpoint_t const *) p_desc)->bEndpointAddress;
  215. (*p_length) += p_desc[DESC_OFFSET_LEN];
  216. p_desc = tu_desc_next(p_desc);
  217. }
  218. //------------- Data Interface (if any) -------------//
  219. if ( (TUSB_DESC_INTERFACE == p_desc[DESC_OFFSET_TYPE]) &&
  220. (TUSB_CLASS_CDC_DATA == ((tusb_desc_interface_t const *) p_desc)->bInterfaceClass) )
  221. {
  222. // next to endpoint descriptor
  223. p_desc = tu_desc_next(p_desc);
  224. // Open endpoint pair
  225. TU_ASSERT( usbd_open_edpt_pair(rhport, p_desc, 2, TUSB_XFER_BULK, &p_cdc->ep_out, &p_cdc->ep_in) );
  226. (*p_length) += sizeof(tusb_desc_interface_t) + 2*sizeof(tusb_desc_endpoint_t);
  227. }
  228. // Prepare for incoming data
  229. _prep_out_transaction(cdc_id);
  230. return true;
  231. }
  232. // Invoked when class request DATA stage is finished.
  233. // return false to stall control endpoint (e.g Host send non-sense DATA)
  234. bool cdcd_control_complete(uint8_t rhport, tusb_control_request_t const * request)
  235. {
  236. (void) rhport;
  237. //------------- Class Specific Request -------------//
  238. TU_VERIFY (request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS);
  239. // TODO Support multiple interfaces
  240. uint8_t const itf = 0;
  241. cdcd_interface_t* p_cdc = &_cdcd_itf[itf];
  242. // Invoke callback
  243. if ( CDC_REQUEST_SET_LINE_CODING == request->bRequest )
  244. {
  245. if ( tud_cdc_line_coding_cb ) tud_cdc_line_coding_cb(itf, &p_cdc->line_coding);
  246. }
  247. return true;
  248. }
  249. // Handle class control request
  250. // return false to stall control endpoint (e.g unsupported request)
  251. bool cdcd_control_request(uint8_t rhport, tusb_control_request_t const * request)
  252. {
  253. //------------- Class Specific Request -------------//
  254. TU_ASSERT(request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS);
  255. // TODO Support multiple interfaces
  256. uint8_t const itf = 0;
  257. cdcd_interface_t* p_cdc = &_cdcd_itf[itf];
  258. switch ( request->bRequest )
  259. {
  260. case CDC_REQUEST_SET_LINE_CODING:
  261. tud_control_xfer(rhport, request, &p_cdc->line_coding, sizeof(cdc_line_coding_t));
  262. break;
  263. case CDC_REQUEST_GET_LINE_CODING:
  264. tud_control_xfer(rhport, request, &p_cdc->line_coding, sizeof(cdc_line_coding_t));
  265. break;
  266. case CDC_REQUEST_SET_CONTROL_LINE_STATE:
  267. // CDC PSTN v1.2 section 6.3.12
  268. // Bit 0: Indicates if DTE is present or not.
  269. // This signal corresponds to V.24 signal 108/2 and RS-232 signal DTR (Data Terminal Ready)
  270. // Bit 1: Carrier control for half-duplex modems.
  271. // This signal corresponds to V.24 signal 105 and RS-232 signal RTS (Request to Send)
  272. p_cdc->line_state = (uint8_t) request->wValue;
  273. tud_control_status(rhport, request);
  274. // Invoke callback
  275. if ( tud_cdc_line_state_cb) tud_cdc_line_state_cb(itf, tu_bit_test(request->wValue, 0), tu_bit_test(request->wValue, 1));
  276. break;
  277. default: return false; // stall unsupported request
  278. }
  279. return true;
  280. }
  281. bool cdcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
  282. {
  283. (void) rhport;
  284. (void) result;
  285. // TODO Support multiple interfaces
  286. uint8_t const itf = 0;
  287. cdcd_interface_t* p_cdc = &_cdcd_itf[itf];
  288. // Received new data
  289. if ( ep_addr == p_cdc->ep_out )
  290. {
  291. for(uint32_t i=0; i<xferred_bytes; i++)
  292. {
  293. tu_fifo_write(&p_cdc->rx_ff, &p_cdc->epout_buf[i]);
  294. // Check for wanted char and invoke callback if needed
  295. if ( tud_cdc_rx_wanted_cb && ( ((signed char) p_cdc->wanted_char) != -1 ) && ( p_cdc->wanted_char == p_cdc->epout_buf[i] ) )
  296. {
  297. tud_cdc_rx_wanted_cb(itf, p_cdc->wanted_char);
  298. }
  299. }
  300. // invoke receive callback (if there is still data)
  301. if (tud_cdc_rx_cb && tu_fifo_count(&p_cdc->rx_ff) ) tud_cdc_rx_cb(itf);
  302. // prepare for OUT transaction
  303. _prep_out_transaction(itf);
  304. }
  305. // Data sent to host, we could continue to fetch data tx fifo to send.
  306. // But it will cause incorrect baudrate set in line coding.
  307. // Though maybe the baudrate is not really important !!!
  308. // if ( ep_addr == p_cdc->ep_in )
  309. // {
  310. //
  311. // }
  312. // nothing to do with notif endpoint for now
  313. return true;
  314. }
  315. #endif