gpio_scene_usb_uart.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  1. #include "../gpio_app_i.h"
  2. #include "furi-hal.h"
  3. #include <stream_buffer.h>
  4. #include <furi-hal-usb-cdc_i.h>
  5. #include "usb_cdc.h"
  6. #define USB_PKT_LEN CDC_DATA_SZ
  7. #define USB_UART_RX_BUF_SIZE (USB_PKT_LEN * 3)
  8. #define USB_UART_TX_BUF_SIZE (USB_PKT_LEN * 3)
  9. typedef enum {
  10. WorkerCmdStop = (1 << 0),
  11. } WorkerCommandFlags;
  12. typedef enum {
  13. UsbUartLineIndexVcp,
  14. UsbUartLineIndexUart,
  15. UsbUartLineIndexBaudrate,
  16. UsbUartLineIndexEnable,
  17. UsbUartLineIndexDisable,
  18. } LineIndex;
  19. typedef enum {
  20. UsbUartPortUSART1 = 0,
  21. UsbUartPortLPUART1 = 1,
  22. } PortIdx;
  23. typedef struct {
  24. uint8_t vcp_ch;
  25. PortIdx uart_ch;
  26. uint32_t baudrate;
  27. } UsbUartConfig;
  28. typedef struct {
  29. UsbUartConfig cfg_cur;
  30. UsbUartConfig cfg_set;
  31. char br_text[8];
  32. bool running;
  33. osThreadId_t parent_thread;
  34. osThreadAttr_t thread_attr;
  35. osThreadId_t thread;
  36. osThreadAttr_t tx_thread_attr;
  37. osThreadId_t tx_thread;
  38. StreamBufferHandle_t rx_stream;
  39. osSemaphoreId_t rx_done_sem;
  40. osSemaphoreId_t usb_sof_sem;
  41. StreamBufferHandle_t tx_stream;
  42. uint8_t rx_buf[USB_PKT_LEN];
  43. uint8_t tx_buf[USB_PKT_LEN];
  44. } UsbUartParams;
  45. static UsbUartParams* usb_uart;
  46. static const char* vcp_ch[] = {"0 (CLI)", "1"};
  47. static const char* uart_ch[] = {"USART1", "LPUART1"};
  48. static const char* baudrate_mode[] = {"Host"};
  49. static const uint32_t baudrate_list[] = {
  50. 2400,
  51. 9600,
  52. 19200,
  53. 38400,
  54. 57600,
  55. 115200,
  56. 230400,
  57. 460800,
  58. 921600,
  59. };
  60. static void vcp_on_cdc_tx_complete();
  61. static void vcp_on_cdc_rx();
  62. static void vcp_state_callback(uint8_t state);
  63. static void vcp_on_cdc_control_line(uint8_t state);
  64. static void vcp_on_line_config(struct usb_cdc_line_coding* config);
  65. static CdcCallbacks cdc_cb = {
  66. vcp_on_cdc_tx_complete,
  67. vcp_on_cdc_rx,
  68. vcp_state_callback,
  69. vcp_on_cdc_control_line,
  70. vcp_on_line_config,
  71. };
  72. /* USB UART worker */
  73. static void usb_uart_tx_thread(void* context);
  74. static void usb_uart_on_irq_cb(UartIrqEvent ev, uint8_t data) {
  75. BaseType_t xHigherPriorityTaskWoken = pdFALSE;
  76. if(ev == UartIrqEventRXNE) {
  77. size_t ret =
  78. xStreamBufferSendFromISR(usb_uart->rx_stream, &data, 1, &xHigherPriorityTaskWoken);
  79. furi_check(ret == 1);
  80. ret = xStreamBufferBytesAvailable(usb_uart->rx_stream);
  81. if(ret > USB_PKT_LEN) osSemaphoreRelease(usb_uart->rx_done_sem);
  82. } else if(ev == UartIrqEventIDLE) {
  83. osSemaphoreRelease(usb_uart->rx_done_sem);
  84. }
  85. portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
  86. }
  87. static void usb_uart_worker(void* context) {
  88. memcpy(&usb_uart->cfg_cur, &usb_uart->cfg_set, sizeof(UsbUartConfig));
  89. usb_uart->rx_stream = xStreamBufferCreate(USB_UART_RX_BUF_SIZE, 1);
  90. usb_uart->rx_done_sem = osSemaphoreNew(1, 1, NULL);
  91. usb_uart->usb_sof_sem = osSemaphoreNew(1, 1, NULL);
  92. usb_uart->tx_stream = xStreamBufferCreate(USB_UART_TX_BUF_SIZE, 1);
  93. usb_uart->tx_thread = NULL;
  94. usb_uart->tx_thread_attr.name = "usb_uart_tx";
  95. usb_uart->tx_thread_attr.stack_size = 512;
  96. UsbMode usb_mode_prev = furi_hal_usb_get_config();
  97. if(usb_uart->cfg_cur.vcp_ch == 0) {
  98. furi_hal_usb_set_config(UsbModeVcpSingle);
  99. furi_hal_vcp_disable();
  100. } else {
  101. furi_hal_usb_set_config(UsbModeVcpDual);
  102. }
  103. if(usb_uart->cfg_cur.uart_ch == UsbUartPortUSART1) {
  104. furi_hal_usart_init();
  105. furi_hal_usart_set_irq_cb(usb_uart_on_irq_cb);
  106. if(usb_uart->cfg_cur.baudrate != 0)
  107. furi_hal_usart_set_br(usb_uart->cfg_cur.baudrate);
  108. else
  109. vcp_on_line_config(furi_hal_cdc_get_port_settings(usb_uart->cfg_cur.vcp_ch));
  110. } else if(usb_uart->cfg_cur.uart_ch == UsbUartPortLPUART1) {
  111. furi_hal_lpuart_init();
  112. furi_hal_lpuart_set_irq_cb(usb_uart_on_irq_cb);
  113. if(usb_uart->cfg_cur.baudrate != 0)
  114. furi_hal_lpuart_set_br(usb_uart->cfg_cur.baudrate);
  115. else
  116. vcp_on_line_config(furi_hal_cdc_get_port_settings(usb_uart->cfg_cur.vcp_ch));
  117. }
  118. furi_hal_cdc_set_callbacks(usb_uart->cfg_cur.vcp_ch, &cdc_cb);
  119. usb_uart->tx_thread = osThreadNew(usb_uart_tx_thread, NULL, &usb_uart->tx_thread_attr);
  120. while(1) {
  121. furi_check(osSemaphoreAcquire(usb_uart->rx_done_sem, osWaitForever) == osOK);
  122. if(osThreadFlagsWait(WorkerCmdStop, osFlagsWaitAny, 0) == WorkerCmdStop) break;
  123. size_t len = 0;
  124. do {
  125. len = xStreamBufferReceive(usb_uart->rx_stream, usb_uart->rx_buf, USB_PKT_LEN, 0);
  126. if(len > 0) {
  127. if(osSemaphoreAcquire(usb_uart->usb_sof_sem, 100) == osOK)
  128. furi_hal_cdc_send(usb_uart->cfg_cur.vcp_ch, usb_uart->rx_buf, len);
  129. else
  130. xStreamBufferReset(usb_uart->rx_stream);
  131. }
  132. } while(len > 0);
  133. }
  134. osThreadTerminate(usb_uart->tx_thread);
  135. if(usb_uart->cfg_cur.uart_ch == UsbUartPortUSART1)
  136. furi_hal_usart_deinit();
  137. else if(usb_uart->cfg_cur.uart_ch == UsbUartPortLPUART1)
  138. furi_hal_lpuart_deinit();
  139. furi_hal_cdc_set_callbacks(usb_uart->cfg_cur.vcp_ch, NULL);
  140. furi_hal_usb_set_config(usb_mode_prev);
  141. if(usb_uart->cfg_cur.vcp_ch == 0) furi_hal_vcp_enable();
  142. vStreamBufferDelete(usb_uart->rx_stream);
  143. osSemaphoreDelete(usb_uart->rx_done_sem);
  144. osSemaphoreDelete(usb_uart->usb_sof_sem);
  145. vStreamBufferDelete(usb_uart->tx_stream);
  146. osThreadFlagsSet(usb_uart->parent_thread, WorkerCmdStop);
  147. osThreadExit();
  148. }
  149. static void usb_uart_tx_thread(void* context) {
  150. uint8_t data = 0;
  151. while(1) {
  152. size_t len = xStreamBufferReceive(usb_uart->tx_stream, &data, 1, osWaitForever);
  153. if(len > 0) {
  154. if(usb_uart->cfg_cur.uart_ch == UsbUartPortUSART1)
  155. furi_hal_usart_tx(&data, len);
  156. else if(usb_uart->cfg_cur.uart_ch == UsbUartPortLPUART1)
  157. furi_hal_lpuart_tx(&data, len);
  158. }
  159. }
  160. osThreadExit();
  161. }
  162. /* VCP callbacks */
  163. static void vcp_on_cdc_tx_complete() {
  164. osSemaphoreRelease(usb_uart->usb_sof_sem);
  165. }
  166. static void vcp_on_cdc_rx() {
  167. BaseType_t xHigherPriorityTaskWoken = pdFALSE;
  168. uint16_t max_len = xStreamBufferSpacesAvailable(usb_uart->tx_stream);
  169. if(max_len > 0) {
  170. if(max_len > USB_PKT_LEN) max_len = USB_PKT_LEN;
  171. int32_t size = furi_hal_cdc_receive(usb_uart->cfg_cur.vcp_ch, usb_uart->tx_buf, max_len);
  172. if(size > 0) {
  173. size_t ret = xStreamBufferSendFromISR(
  174. usb_uart->tx_stream, usb_uart->tx_buf, size, &xHigherPriorityTaskWoken);
  175. furi_check(ret == size);
  176. }
  177. }
  178. portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
  179. }
  180. static void vcp_state_callback(uint8_t state) {
  181. }
  182. static void vcp_on_cdc_control_line(uint8_t state) {
  183. }
  184. static void vcp_on_line_config(struct usb_cdc_line_coding* config) {
  185. if((usb_uart->cfg_cur.baudrate == 0) && (config->dwDTERate != 0)) {
  186. if(usb_uart->cfg_cur.uart_ch == UsbUartPortUSART1)
  187. furi_hal_usart_set_br(config->dwDTERate);
  188. else if(usb_uart->cfg_cur.uart_ch == UsbUartPortLPUART1)
  189. furi_hal_lpuart_set_br(config->dwDTERate);
  190. }
  191. }
  192. /* USB UART app */
  193. static void usb_uart_enable() {
  194. if(usb_uart->running == false) {
  195. usb_uart->thread = NULL;
  196. usb_uart->thread_attr.name = "usb_uart";
  197. usb_uart->thread_attr.stack_size = 1024;
  198. usb_uart->parent_thread = osThreadGetId();
  199. usb_uart->running = true;
  200. usb_uart->thread = osThreadNew(usb_uart_worker, NULL, &usb_uart->thread_attr);
  201. }
  202. }
  203. static void usb_uart_disable() {
  204. if(usb_uart->running == true) {
  205. osThreadFlagsSet(usb_uart->thread, WorkerCmdStop);
  206. osSemaphoreRelease(usb_uart->rx_done_sem);
  207. osThreadFlagsWait(WorkerCmdStop, osFlagsWaitAny, osWaitForever);
  208. usb_uart->running = false;
  209. }
  210. }
  211. bool gpio_scene_usb_uart_on_event(void* context, SceneManagerEvent event) {
  212. //GpioApp* app = context;
  213. bool consumed = false;
  214. if(event.type == SceneManagerEventTypeCustom) {
  215. if(event.event == UsbUartLineIndexEnable) {
  216. usb_uart_enable();
  217. } else if(event.event == UsbUartLineIndexDisable) {
  218. usb_uart_disable();
  219. }
  220. consumed = true;
  221. }
  222. return consumed;
  223. }
  224. /* Scene callbacks */
  225. static void line_vcp_cb(VariableItem* item) {
  226. //GpioApp* app = variable_item_get_context(item);
  227. uint8_t index = variable_item_get_current_value_index(item);
  228. variable_item_set_current_value_text(item, vcp_ch[index]);
  229. usb_uart->cfg_set.vcp_ch = index;
  230. }
  231. static void line_port_cb(VariableItem* item) {
  232. //GpioApp* app = variable_item_get_context(item);
  233. uint8_t index = variable_item_get_current_value_index(item);
  234. variable_item_set_current_value_text(item, uart_ch[index]);
  235. usb_uart->cfg_set.uart_ch = index;
  236. }
  237. static void line_baudrate_cb(VariableItem* item) {
  238. //GpioApp* app = variable_item_get_context(item);
  239. uint8_t index = variable_item_get_current_value_index(item);
  240. if(index > 0) {
  241. snprintf(usb_uart->br_text, 7, "%lu", baudrate_list[index - 1]);
  242. variable_item_set_current_value_text(item, usb_uart->br_text);
  243. usb_uart->cfg_set.baudrate = baudrate_list[index - 1];
  244. } else {
  245. variable_item_set_current_value_text(item, baudrate_mode[index]);
  246. usb_uart->cfg_set.baudrate = 0;
  247. }
  248. }
  249. static void gpio_scene_usb_uart_enter_callback(void* context, uint32_t index) {
  250. furi_assert(context);
  251. GpioApp* app = context;
  252. view_dispatcher_send_custom_event(app->view_dispatcher, index);
  253. }
  254. void gpio_scene_usb_uart_on_enter(void* context) {
  255. GpioApp* app = context;
  256. VariableItemList* var_item_list = app->var_item_list;
  257. usb_uart = furi_alloc(sizeof(UsbUartParams));
  258. VariableItem* item;
  259. variable_item_list_set_enter_callback(var_item_list, gpio_scene_usb_uart_enter_callback, app);
  260. item = variable_item_list_add(var_item_list, "VCP Channel", 2, line_vcp_cb, app);
  261. variable_item_set_current_value_index(item, 0);
  262. variable_item_set_current_value_text(item, vcp_ch[0]);
  263. item = variable_item_list_add(var_item_list, "UART Port", 2, line_port_cb, app);
  264. variable_item_set_current_value_index(item, 0);
  265. variable_item_set_current_value_text(item, uart_ch[0]);
  266. item = variable_item_list_add(
  267. var_item_list,
  268. "Baudrate",
  269. sizeof(baudrate_list) / sizeof(baudrate_list[0]) + 1,
  270. line_baudrate_cb,
  271. app);
  272. variable_item_set_current_value_index(item, 0);
  273. variable_item_set_current_value_text(item, baudrate_mode[0]);
  274. item = variable_item_list_add(var_item_list, "Enable", 0, NULL, NULL);
  275. item = variable_item_list_add(var_item_list, "Disable", 0, NULL, NULL);
  276. view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewUsbUart);
  277. }
  278. void gpio_scene_usb_uart_on_exit(void* context) {
  279. GpioApp* app = context;
  280. usb_uart_disable();
  281. variable_item_list_clean(app->var_item_list);
  282. free(usb_uart);
  283. }