furi-hal-vcp.c 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. #include <furi-hal-usb-cdc_i.h>
  2. #include <furi.h>
  3. #include <stream_buffer.h>
  4. #define USB_CDC_PKT_LEN CDC_DATA_SZ
  5. #define VCP_IF_NUM 0
  6. typedef enum {
  7. VcpConnect,
  8. VcpDisconnect,
  9. } VcpEvent;
  10. typedef struct {
  11. volatile bool connected;
  12. uint8_t rx_buf[USB_CDC_PKT_LEN];
  13. uint8_t rx_buf_start;
  14. uint8_t rx_buf_len;
  15. osMessageQueueId_t event_queue;
  16. osMutexId_t usb_mutex;
  17. osSemaphoreId_t tx_sem;
  18. osSemaphoreId_t rx_sem;
  19. } FuriHalVcp;
  20. static void vcp_on_cdc_tx_complete();
  21. static void vcp_on_cdc_rx();
  22. static void vcp_state_callback(uint8_t state);
  23. static void vcp_on_cdc_control_line(uint8_t state);
  24. static CdcCallbacks cdc_cb = {
  25. vcp_on_cdc_tx_complete,
  26. vcp_on_cdc_rx,
  27. vcp_state_callback,
  28. vcp_on_cdc_control_line,
  29. NULL,
  30. };
  31. static FuriHalVcp* vcp = NULL;
  32. static const uint8_t ascii_soh = 0x01;
  33. static const uint8_t ascii_eot = 0x04;
  34. void furi_hal_vcp_init() {
  35. vcp = furi_alloc(sizeof(FuriHalVcp));
  36. vcp->connected = false;
  37. vcp->usb_mutex = osMutexNew(NULL);
  38. vcp->tx_sem = osSemaphoreNew(1, 1, NULL);
  39. vcp->rx_sem = osSemaphoreNew(1, 0, NULL);
  40. vcp->event_queue = osMessageQueueNew(8, sizeof(VcpEvent), NULL);
  41. furi_hal_cdc_set_callbacks(VCP_IF_NUM, &cdc_cb);
  42. FURI_LOG_I("FuriHalVcp", "Init OK");
  43. }
  44. void furi_hal_vcp_enable() {
  45. furi_hal_cdc_set_callbacks(VCP_IF_NUM, &cdc_cb);
  46. VcpEvent evt = VcpConnect;
  47. osMessageQueuePut(vcp->event_queue, &evt, 0, 0);
  48. vcp->connected = true;
  49. osSemaphoreRelease(vcp->tx_sem);
  50. osSemaphoreRelease(vcp->rx_sem);
  51. }
  52. void furi_hal_vcp_disable() {
  53. furi_hal_cdc_set_callbacks(VCP_IF_NUM, NULL);
  54. VcpEvent evt = VcpDisconnect;
  55. osMessageQueuePut(vcp->event_queue, &evt, 0, 0);
  56. vcp->connected = false;
  57. osSemaphoreRelease(vcp->tx_sem);
  58. osSemaphoreRelease(vcp->rx_sem);
  59. }
  60. size_t furi_hal_vcp_rx_with_timeout(uint8_t* buffer, size_t size, uint32_t timeout) {
  61. furi_assert(vcp);
  62. furi_assert(buffer);
  63. size_t rx_cnt = 0;
  64. VcpEvent evt = VcpDisconnect;
  65. if (vcp->rx_buf_len > 0) {
  66. size_t len = (vcp->rx_buf_len > size) ? (size) : (vcp->rx_buf_len);
  67. memcpy(&buffer[rx_cnt], &vcp->rx_buf[vcp->rx_buf_start], len);
  68. vcp->rx_buf_len -= len;
  69. vcp->rx_buf_start += len;
  70. rx_cnt += len;
  71. }
  72. while (rx_cnt < size) {
  73. if (osMessageQueueGet(vcp->event_queue, &evt, NULL, 0) == osOK) {
  74. if (evt == VcpConnect)
  75. buffer[rx_cnt] = ascii_soh;
  76. else {
  77. buffer[rx_cnt] = ascii_eot;
  78. vcp->rx_buf_len = 0;
  79. }
  80. rx_cnt++;
  81. return rx_cnt;
  82. }
  83. if (osSemaphoreAcquire(vcp->rx_sem, timeout) == osErrorTimeout)
  84. return rx_cnt;
  85. furi_check(osMutexAcquire(vcp->usb_mutex, osWaitForever) == osOK);
  86. size_t len = furi_hal_cdc_receive(VCP_IF_NUM, vcp->rx_buf, USB_CDC_PKT_LEN);
  87. furi_check(osMutexRelease(vcp->usb_mutex) == osOK);
  88. vcp->rx_buf_len = len;
  89. vcp->rx_buf_start = 0;
  90. if (vcp->rx_buf_len > (size - rx_cnt)) {
  91. len = size - rx_cnt;
  92. memcpy(&buffer[rx_cnt], vcp->rx_buf, len);
  93. vcp->rx_buf_len -= len;
  94. vcp->rx_buf_start += len;
  95. } else {
  96. memcpy(&buffer[rx_cnt], vcp->rx_buf, vcp->rx_buf_len);
  97. vcp->rx_buf_len = 0;
  98. }
  99. rx_cnt += len;
  100. }
  101. return rx_cnt;
  102. }
  103. size_t furi_hal_vcp_rx(uint8_t* buffer, size_t size) {
  104. furi_assert(vcp);
  105. return furi_hal_vcp_rx_with_timeout(buffer, size, portMAX_DELAY);
  106. }
  107. void furi_hal_vcp_tx(const uint8_t* buffer, size_t size) {
  108. furi_assert(vcp);
  109. while (size > 0 && vcp->connected) {
  110. furi_check(osSemaphoreAcquire(vcp->tx_sem, osWaitForever) == osOK);
  111. if (!vcp->connected)
  112. break;
  113. size_t batch_size = size;
  114. if (batch_size > USB_CDC_PKT_LEN) {
  115. batch_size = USB_CDC_PKT_LEN;
  116. }
  117. furi_check(osMutexAcquire(vcp->usb_mutex, osWaitForever) == osOK);
  118. furi_hal_cdc_send(VCP_IF_NUM, (uint8_t*)buffer, batch_size);
  119. furi_check(osMutexRelease(vcp->usb_mutex) == osOK);
  120. size -= batch_size;
  121. buffer += batch_size;
  122. }
  123. }
  124. static void vcp_state_callback(uint8_t state) {
  125. if (state == 1) {
  126. osSemaphoreRelease(vcp->rx_sem);
  127. //osSemaphoreRelease(vcp->tx_sem);
  128. }
  129. else if (vcp->connected) {
  130. vcp->connected = false;
  131. osSemaphoreRelease(vcp->rx_sem);
  132. VcpEvent evt = VcpDisconnect;
  133. osMessageQueuePut(vcp->event_queue, &evt, 0, 0);
  134. //osSemaphoreRelease(vcp->tx_sem);
  135. }
  136. }
  137. static void vcp_on_cdc_control_line(uint8_t state) {
  138. // bit 0: DTR state, bit 1: RTS state
  139. bool dtr = state & 0b1;
  140. if (dtr) {
  141. if (!vcp->connected) {
  142. vcp->connected = true;
  143. VcpEvent evt = VcpConnect;
  144. osMessageQueuePut(vcp->event_queue, &evt, 0, 0);
  145. }
  146. } else {
  147. if (vcp->connected) {
  148. VcpEvent evt = VcpDisconnect;
  149. osMessageQueuePut(vcp->event_queue, &evt, 0, 0);
  150. vcp->connected = false;
  151. }
  152. }
  153. osSemaphoreRelease(vcp->tx_sem);
  154. osSemaphoreRelease(vcp->rx_sem);
  155. }
  156. static void vcp_on_cdc_rx() {
  157. if (vcp->connected == false)
  158. return;
  159. osSemaphoreRelease(vcp->rx_sem);
  160. }
  161. static void vcp_on_cdc_tx_complete() {
  162. osSemaphoreRelease(vcp->tx_sem);
  163. }
  164. bool furi_hal_vcp_is_connected(void) {
  165. return vcp->connected;
  166. }