Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/ClusterM/flipperzero-firmware.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikolay Minaylov <nm29719@gmail.com>2021-10-26 21:41:56 +0300
committerGitHub <noreply@github.com>2021-10-26 21:41:56 +0300
commit732b9546fc29d0eee07d2230e52d035b32b89c81 (patch)
tree1d4bb55c0a670e351ed097a9db92684dbc40d600 /applications/gpio
parentfae8d8f23ceb971cb08119d0dc8de48a06091a13 (diff)
[FL-1984] USB-UART improvements and fixes (#785)
* [FL-1984] USB-UART fixes * FuriHal: fix SOF wait on CDC0 * FuriHal: fixed stuck in UART IRQ with ORE event Co-authored-by: あく <alleteam@gmail.com>
Diffstat (limited to 'applications/gpio')
-rw-r--r--applications/gpio/gpio_app_i.h8
-rw-r--r--applications/gpio/scenes/gpio_scene_start.c5
-rw-r--r--applications/gpio/scenes/gpio_scene_usb_uart.c271
-rw-r--r--applications/gpio/usb_uart_bridge.c246
-rw-r--r--applications/gpio/usb_uart_bridge.h13
5 files changed, 292 insertions, 251 deletions
diff --git a/applications/gpio/gpio_app_i.h b/applications/gpio/gpio_app_i.h
index bfeab404..226fa06e 100644
--- a/applications/gpio/gpio_app_i.h
+++ b/applications/gpio/gpio_app_i.h
@@ -12,6 +12,14 @@
#include <gui/modules/variable-item-list.h>
#include "views/gpio_test.h"
+#define GPIO_SCENE_START_CUSTOM_EVENT_OTG_OFF (0UL)
+#define GPIO_SCENE_START_CUSTOM_EVENT_OTG_ON (1UL)
+#define GPIO_SCENE_START_CUSTOM_EVENT_TEST (2UL)
+#define GPIO_SCENE_START_CUSTOM_EVENT_USB_UART (3UL)
+
+#define GPIO_SCENE_USB_UART_CUSTOM_EVENT_ENABLE (4UL)
+#define GPIO_SCENE_USB_UART_CUSTOM_EVENT_DISABLE (5UL)
+
struct GpioApp {
Gui* gui;
ViewDispatcher* view_dispatcher;
diff --git a/applications/gpio/scenes/gpio_scene_start.c b/applications/gpio/scenes/gpio_scene_start.c
index b3ed40fd..b7d94fe7 100644
--- a/applications/gpio/scenes/gpio_scene_start.c
+++ b/applications/gpio/scenes/gpio_scene_start.c
@@ -1,11 +1,6 @@
#include "../gpio_app_i.h"
#include "furi-hal-power.h"
-#define GPIO_SCENE_START_CUSTOM_EVENT_OTG_OFF (0UL)
-#define GPIO_SCENE_START_CUSTOM_EVENT_OTG_ON (1UL)
-#define GPIO_SCENE_START_CUSTOM_EVENT_TEST (2UL)
-#define GPIO_SCENE_START_CUSTOM_EVENT_USB_UART (3UL)
-
enum GpioItem {
GpioItemOtg,
GpioItemTest,
diff --git a/applications/gpio/scenes/gpio_scene_usb_uart.c b/applications/gpio/scenes/gpio_scene_usb_uart.c
index 05de8e7b..c8ec0141 100644
--- a/applications/gpio/scenes/gpio_scene_usb_uart.c
+++ b/applications/gpio/scenes/gpio_scene_usb_uart.c
@@ -1,17 +1,6 @@
+#include "../usb_uart_bridge.h"
#include "../gpio_app_i.h"
#include "furi-hal.h"
-#include <stream_buffer.h>
-#include <furi-hal-usb-cdc_i.h>
-#include "usb_cdc.h"
-
-#define USB_PKT_LEN CDC_DATA_SZ
-#define USB_UART_RX_BUF_SIZE (USB_PKT_LEN * 3)
-#define USB_UART_TX_BUF_SIZE (USB_PKT_LEN * 3)
-
-typedef enum {
- WorkerCmdStop = (1 << 0),
-
-} WorkerCommandFlags;
typedef enum {
UsbUartLineIndexVcp,
@@ -21,42 +10,7 @@ typedef enum {
UsbUartLineIndexDisable,
} LineIndex;
-typedef enum {
- UsbUartPortUSART1 = 0,
- UsbUartPortLPUART1 = 1,
-} PortIdx;
-
-typedef struct {
- uint8_t vcp_ch;
- PortIdx uart_ch;
- uint32_t baudrate;
-} UsbUartConfig;
-
-typedef struct {
- UsbUartConfig cfg_cur;
- UsbUartConfig cfg_set;
- char br_text[8];
-
- bool running;
- osThreadId_t parent_thread;
-
- osThreadAttr_t thread_attr;
- osThreadId_t thread;
-
- osThreadAttr_t tx_thread_attr;
- osThreadId_t tx_thread;
-
- StreamBufferHandle_t rx_stream;
- osSemaphoreId_t rx_done_sem;
- osSemaphoreId_t usb_sof_sem;
-
- StreamBufferHandle_t tx_stream;
-
- uint8_t rx_buf[USB_PKT_LEN];
- uint8_t tx_buf[USB_PKT_LEN];
-} UsbUartParams;
-
-static UsbUartParams* usb_uart;
+static UsbUartConfig* cfg_set;
static const char* vcp_ch[] = {"0 (CLI)", "1"};
static const char* uart_ch[] = {"USART1", "LPUART1"};
@@ -73,197 +27,14 @@ static const uint32_t baudrate_list[] = {
921600,
};
-static void vcp_on_cdc_tx_complete();
-static void vcp_on_cdc_rx();
-static void vcp_state_callback(uint8_t state);
-static void vcp_on_cdc_control_line(uint8_t state);
-static void vcp_on_line_config(struct usb_cdc_line_coding* config);
-
-static CdcCallbacks cdc_cb = {
- vcp_on_cdc_tx_complete,
- vcp_on_cdc_rx,
- vcp_state_callback,
- vcp_on_cdc_control_line,
- vcp_on_line_config,
-};
-
-/* USB UART worker */
-
-static void usb_uart_tx_thread(void* context);
-
-static void usb_uart_on_irq_cb(UartIrqEvent ev, uint8_t data) {
- BaseType_t xHigherPriorityTaskWoken = pdFALSE;
-
- if(ev == UartIrqEventRXNE) {
- size_t ret =
- xStreamBufferSendFromISR(usb_uart->rx_stream, &data, 1, &xHigherPriorityTaskWoken);
- furi_check(ret == 1);
- ret = xStreamBufferBytesAvailable(usb_uart->rx_stream);
- if(ret > USB_PKT_LEN) osSemaphoreRelease(usb_uart->rx_done_sem);
- } else if(ev == UartIrqEventIDLE) {
- osSemaphoreRelease(usb_uart->rx_done_sem);
- }
-
- portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
-}
-
-static void usb_uart_worker(void* context) {
- memcpy(&usb_uart->cfg_cur, &usb_uart->cfg_set, sizeof(UsbUartConfig));
-
- usb_uart->rx_stream = xStreamBufferCreate(USB_UART_RX_BUF_SIZE, 1);
- usb_uart->rx_done_sem = osSemaphoreNew(1, 1, NULL);
- usb_uart->usb_sof_sem = osSemaphoreNew(1, 1, NULL);
-
- usb_uart->tx_stream = xStreamBufferCreate(USB_UART_TX_BUF_SIZE, 1);
-
- usb_uart->tx_thread = NULL;
- usb_uart->tx_thread_attr.name = "usb_uart_tx";
- usb_uart->tx_thread_attr.stack_size = 512;
-
- UsbMode usb_mode_prev = furi_hal_usb_get_config();
- if(usb_uart->cfg_cur.vcp_ch == 0) {
- furi_hal_usb_set_config(UsbModeVcpSingle);
- furi_hal_vcp_disable();
- } else {
- furi_hal_usb_set_config(UsbModeVcpDual);
- }
-
- if(usb_uart->cfg_cur.uart_ch == UsbUartPortUSART1) {
- furi_hal_usart_init();
- furi_hal_usart_set_irq_cb(usb_uart_on_irq_cb);
- if(usb_uart->cfg_cur.baudrate != 0)
- furi_hal_usart_set_br(usb_uart->cfg_cur.baudrate);
- else
- vcp_on_line_config(furi_hal_cdc_get_port_settings(usb_uart->cfg_cur.vcp_ch));
- } else if(usb_uart->cfg_cur.uart_ch == UsbUartPortLPUART1) {
- furi_hal_lpuart_init();
- furi_hal_lpuart_set_irq_cb(usb_uart_on_irq_cb);
- if(usb_uart->cfg_cur.baudrate != 0)
- furi_hal_lpuart_set_br(usb_uart->cfg_cur.baudrate);
- else
- vcp_on_line_config(furi_hal_cdc_get_port_settings(usb_uart->cfg_cur.vcp_ch));
- }
-
- furi_hal_cdc_set_callbacks(usb_uart->cfg_cur.vcp_ch, &cdc_cb);
- usb_uart->tx_thread = osThreadNew(usb_uart_tx_thread, NULL, &usb_uart->tx_thread_attr);
-
- while(1) {
- furi_check(osSemaphoreAcquire(usb_uart->rx_done_sem, osWaitForever) == osOK);
- if(osThreadFlagsWait(WorkerCmdStop, osFlagsWaitAny, 0) == WorkerCmdStop) break;
- size_t len = 0;
- do {
- len = xStreamBufferReceive(usb_uart->rx_stream, usb_uart->rx_buf, USB_PKT_LEN, 0);
- if(len > 0) {
- if(osSemaphoreAcquire(usb_uart->usb_sof_sem, 100) == osOK)
- furi_hal_cdc_send(usb_uart->cfg_cur.vcp_ch, usb_uart->rx_buf, len);
- else
- xStreamBufferReset(usb_uart->rx_stream);
- }
- } while(len > 0);
- }
-
- osThreadTerminate(usb_uart->tx_thread);
-
- if(usb_uart->cfg_cur.uart_ch == UsbUartPortUSART1)
- furi_hal_usart_deinit();
- else if(usb_uart->cfg_cur.uart_ch == UsbUartPortLPUART1)
- furi_hal_lpuart_deinit();
-
- furi_hal_cdc_set_callbacks(usb_uart->cfg_cur.vcp_ch, NULL);
- furi_hal_usb_set_config(usb_mode_prev);
- if(usb_uart->cfg_cur.vcp_ch == 0) furi_hal_vcp_enable();
-
- vStreamBufferDelete(usb_uart->rx_stream);
- osSemaphoreDelete(usb_uart->rx_done_sem);
- osSemaphoreDelete(usb_uart->usb_sof_sem);
-
- vStreamBufferDelete(usb_uart->tx_stream);
- osThreadFlagsSet(usb_uart->parent_thread, WorkerCmdStop);
- osThreadExit();
-}
-
-static void usb_uart_tx_thread(void* context) {
- uint8_t data = 0;
- while(1) {
- size_t len = xStreamBufferReceive(usb_uart->tx_stream, &data, 1, osWaitForever);
- if(len > 0) {
- if(usb_uart->cfg_cur.uart_ch == UsbUartPortUSART1)
- furi_hal_usart_tx(&data, len);
- else if(usb_uart->cfg_cur.uart_ch == UsbUartPortLPUART1)
- furi_hal_lpuart_tx(&data, len);
- }
- }
- osThreadExit();
-}
-
-/* VCP callbacks */
-
-static void vcp_on_cdc_tx_complete() {
- osSemaphoreRelease(usb_uart->usb_sof_sem);
-}
-
-static void vcp_on_cdc_rx() {
- BaseType_t xHigherPriorityTaskWoken = pdFALSE;
-
- uint16_t max_len = xStreamBufferSpacesAvailable(usb_uart->tx_stream);
- if(max_len > 0) {
- if(max_len > USB_PKT_LEN) max_len = USB_PKT_LEN;
- int32_t size = furi_hal_cdc_receive(usb_uart->cfg_cur.vcp_ch, usb_uart->tx_buf, max_len);
-
- if(size > 0) {
- size_t ret = xStreamBufferSendFromISR(
- usb_uart->tx_stream, usb_uart->tx_buf, size, &xHigherPriorityTaskWoken);
- furi_check(ret == size);
- }
- }
- portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
-}
-
-static void vcp_state_callback(uint8_t state) {
-}
-
-static void vcp_on_cdc_control_line(uint8_t state) {
-}
-
-static void vcp_on_line_config(struct usb_cdc_line_coding* config) {
- if((usb_uart->cfg_cur.baudrate == 0) && (config->dwDTERate != 0)) {
- if(usb_uart->cfg_cur.uart_ch == UsbUartPortUSART1)
- furi_hal_usart_set_br(config->dwDTERate);
- else if(usb_uart->cfg_cur.uart_ch == UsbUartPortLPUART1)
- furi_hal_lpuart_set_br(config->dwDTERate);
- }
-}
-
-/* USB UART app */
-
-static void usb_uart_enable() {
- if(usb_uart->running == false) {
- usb_uart->thread = NULL;
- usb_uart->thread_attr.name = "usb_uart";
- usb_uart->thread_attr.stack_size = 1024;
- usb_uart->parent_thread = osThreadGetId();
- usb_uart->running = true;
- usb_uart->thread = osThreadNew(usb_uart_worker, NULL, &usb_uart->thread_attr);
- }
-}
-
-static void usb_uart_disable() {
- if(usb_uart->running == true) {
- osThreadFlagsSet(usb_uart->thread, WorkerCmdStop);
- osSemaphoreRelease(usb_uart->rx_done_sem);
- osThreadFlagsWait(WorkerCmdStop, osFlagsWaitAny, osWaitForever);
- usb_uart->running = false;
- }
-}
-
bool gpio_scene_usb_uart_on_event(void* context, SceneManagerEvent event) {
//GpioApp* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
- if(event.event == UsbUartLineIndexEnable) {
- usb_uart_enable();
- } else if(event.event == UsbUartLineIndexDisable) {
+ if(event.event == GPIO_SCENE_USB_UART_CUSTOM_EVENT_ENABLE) {
+ usb_uart_enable(cfg_set);
+ } else if(event.event == GPIO_SCENE_USB_UART_CUSTOM_EVENT_DISABLE) {
usb_uart_disable();
}
consumed = true;
@@ -271,15 +42,13 @@ bool gpio_scene_usb_uart_on_event(void* context, SceneManagerEvent event) {
return consumed;
}
-/* Scene callbacks */
-
static void line_vcp_cb(VariableItem* item) {
//GpioApp* app = variable_item_get_context(item);
uint8_t index = variable_item_get_current_value_index(item);
variable_item_set_current_value_text(item, vcp_ch[index]);
- usb_uart->cfg_set.vcp_ch = index;
+ cfg_set->vcp_ch = index;
}
static void line_port_cb(VariableItem* item) {
@@ -288,34 +57,44 @@ static void line_port_cb(VariableItem* item) {
variable_item_set_current_value_text(item, uart_ch[index]);
- usb_uart->cfg_set.uart_ch = index;
+ if(index == 0)
+ cfg_set->uart_ch = FuriHalUartIdUSART1;
+ else if(index == 1)
+ cfg_set->uart_ch = FuriHalUartIdLPUART1;
}
static void line_baudrate_cb(VariableItem* item) {
//GpioApp* app = variable_item_get_context(item);
uint8_t index = variable_item_get_current_value_index(item);
+ char br_text[8];
+
if(index > 0) {
- snprintf(usb_uart->br_text, 7, "%lu", baudrate_list[index - 1]);
- variable_item_set_current_value_text(item, usb_uart->br_text);
- usb_uart->cfg_set.baudrate = baudrate_list[index - 1];
+ snprintf(br_text, 7, "%lu", baudrate_list[index - 1]);
+ variable_item_set_current_value_text(item, br_text);
+ cfg_set->baudrate = baudrate_list[index - 1];
} else {
variable_item_set_current_value_text(item, baudrate_mode[index]);
- usb_uart->cfg_set.baudrate = 0;
+ cfg_set->baudrate = 0;
}
}
static void gpio_scene_usb_uart_enter_callback(void* context, uint32_t index) {
furi_assert(context);
GpioApp* app = context;
- view_dispatcher_send_custom_event(app->view_dispatcher, index);
+ if(index == UsbUartLineIndexEnable)
+ view_dispatcher_send_custom_event(
+ app->view_dispatcher, GPIO_SCENE_USB_UART_CUSTOM_EVENT_ENABLE);
+ else if(index == UsbUartLineIndexDisable)
+ view_dispatcher_send_custom_event(
+ app->view_dispatcher, GPIO_SCENE_USB_UART_CUSTOM_EVENT_DISABLE);
}
void gpio_scene_usb_uart_on_enter(void* context) {
GpioApp* app = context;
VariableItemList* var_item_list = app->var_item_list;
- usb_uart = furi_alloc(sizeof(UsbUartParams));
+ cfg_set = furi_alloc(sizeof(UsbUartConfig));
VariableItem* item;
@@ -348,5 +127,5 @@ void gpio_scene_usb_uart_on_exit(void* context) {
GpioApp* app = context;
usb_uart_disable();
variable_item_list_clean(app->var_item_list);
- free(usb_uart);
-} \ No newline at end of file
+ free(cfg_set);
+}
diff --git a/applications/gpio/usb_uart_bridge.c b/applications/gpio/usb_uart_bridge.c
new file mode 100644
index 00000000..b50cc158
--- /dev/null
+++ b/applications/gpio/usb_uart_bridge.c
@@ -0,0 +1,246 @@
+#include "usb_uart_bridge.h"
+#include "furi-hal.h"
+#include <stream_buffer.h>
+#include <furi-hal-usb-cdc_i.h>
+#include "usb_cdc.h"
+
+#define USB_PKT_LEN CDC_DATA_SZ
+#define USB_UART_RX_BUF_SIZE (USB_PKT_LEN * 3)
+#define USB_UART_TX_BUF_SIZE (USB_PKT_LEN * 3)
+
+typedef enum {
+ WorkerEvtStop = (1 << 0),
+ WorkerEvtRxReady = (1 << 1),
+
+ WorkerEvtTxStop = (1 << 2),
+ WorkerEvtTxReady = (1 << 3),
+
+ WorkerEvtSof = (1 << 4),
+
+} WorkerEvtFlags;
+
+#define WORKER_ALL_RX_EVENTS (WorkerEvtStop | WorkerEvtRxReady)
+#define WORKER_ALL_TX_EVENTS (WorkerEvtTxStop | WorkerEvtTxReady)
+
+typedef struct {
+ UsbUartConfig cfg;
+
+ FuriThread* thread;
+ FuriThread* tx_thread;
+
+ osEventFlagsId_t events;
+
+ StreamBufferHandle_t rx_stream;
+ StreamBufferHandle_t tx_stream;
+
+ uint8_t rx_buf[USB_PKT_LEN];
+ uint8_t tx_buf[USB_PKT_LEN];
+
+ bool buf_full;
+} UsbUartParams;
+
+static UsbUartParams* usb_uart;
+static bool running = false;
+
+static void vcp_on_cdc_tx_complete();
+static void vcp_on_cdc_rx();
+static void vcp_state_callback(uint8_t state);
+static void vcp_on_cdc_control_line(uint8_t state);
+static void vcp_on_line_config(struct usb_cdc_line_coding* config);
+
+static CdcCallbacks cdc_cb = {
+ vcp_on_cdc_tx_complete,
+ vcp_on_cdc_rx,
+ vcp_state_callback,
+ vcp_on_cdc_control_line,
+ vcp_on_line_config,
+};
+
+/* USB UART worker */
+
+static int32_t usb_uart_tx_thread(void* context);
+
+static void usb_uart_on_irq_cb(UartIrqEvent ev, uint8_t data) {
+ BaseType_t xHigherPriorityTaskWoken = pdFALSE;
+
+ if(ev == UartIrqEventRXNE) {
+ xStreamBufferSendFromISR(usb_uart->rx_stream, &data, 1, &xHigherPriorityTaskWoken);
+
+ size_t ret = xStreamBufferBytesAvailable(usb_uart->rx_stream);
+ if(ret > USB_PKT_LEN) osEventFlagsSet(usb_uart->events, WorkerEvtRxReady);
+ } else if(ev == UartIrqEventIDLE) {
+ osEventFlagsSet(usb_uart->events, WorkerEvtRxReady);
+ }
+
+ portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
+}
+
+static int32_t usb_uart_worker(void* context) {
+ memcpy(&usb_uart->cfg, context, sizeof(UsbUartConfig));
+
+ usb_uart->rx_stream = xStreamBufferCreate(USB_UART_RX_BUF_SIZE, 1);
+ usb_uart->tx_stream = xStreamBufferCreate(USB_UART_TX_BUF_SIZE, 1);
+
+ usb_uart->tx_thread = furi_thread_alloc();
+ furi_thread_set_name(usb_uart->tx_thread, "usb_uart_tx");
+ furi_thread_set_stack_size(usb_uart->tx_thread, 512);
+ furi_thread_set_context(usb_uart->tx_thread, NULL);
+ furi_thread_set_callback(usb_uart->tx_thread, usb_uart_tx_thread);
+
+ UsbMode usb_mode_prev = furi_hal_usb_get_config();
+ if(usb_uart->cfg.vcp_ch == 0) {
+ furi_hal_usb_set_config(UsbModeVcpSingle);
+ furi_hal_vcp_disable();
+ osEventFlagsSet(usb_uart->events, WorkerEvtSof);
+ } else {
+ furi_hal_usb_set_config(UsbModeVcpDual);
+ }
+
+ if(usb_uart->cfg.uart_ch == FuriHalUartIdUSART1) {
+ furi_hal_console_disable();
+ } else if(usb_uart->cfg.uart_ch == FuriHalUartIdLPUART1) {
+ furi_hal_uart_init(usb_uart->cfg.uart_ch, 115200);
+ furi_hal_uart_set_irq_cb(usb_uart->cfg.uart_ch, usb_uart_on_irq_cb);
+ }
+
+ furi_hal_uart_set_irq_cb(usb_uart->cfg.uart_ch, usb_uart_on_irq_cb);
+ if(usb_uart->cfg.baudrate != 0)
+ furi_hal_uart_set_br(usb_uart->cfg.uart_ch, usb_uart->cfg.baudrate);
+ else
+ vcp_on_line_config(furi_hal_cdc_get_port_settings(usb_uart->cfg.vcp_ch));
+
+ furi_hal_cdc_set_callbacks(usb_uart->cfg.vcp_ch, &cdc_cb);
+
+ furi_thread_start(usb_uart->tx_thread);
+
+ while(1) {
+ uint32_t events = osEventFlagsWait(
+ usb_uart->events, WORKER_ALL_RX_EVENTS, osFlagsWaitAny, osWaitForever);
+ furi_check((events & osFlagsError) == 0);
+ if(events & WorkerEvtStop) break;
+ if(events & WorkerEvtRxReady) {
+ size_t len = 0;
+ do {
+ len = xStreamBufferReceive(usb_uart->rx_stream, usb_uart->rx_buf, USB_PKT_LEN, 0);
+ if(len > 0) {
+ if((osEventFlagsWait(usb_uart->events, WorkerEvtSof, osFlagsWaitAny, 100) &
+ osFlagsError) == 0)
+ furi_hal_cdc_send(usb_uart->cfg.vcp_ch, usb_uart->rx_buf, len);
+ else
+ xStreamBufferReset(usb_uart->rx_stream);
+ }
+ } while(len > 0);
+ }
+ }
+
+ osEventFlagsSet(usb_uart->events, WorkerEvtTxStop);
+ furi_thread_join(usb_uart->tx_thread);
+ furi_thread_free(usb_uart->tx_thread);
+
+ if(usb_uart->cfg.uart_ch == FuriHalUartIdUSART1)
+ furi_hal_console_enable();
+ else if(usb_uart->cfg.uart_ch == FuriHalUartIdLPUART1)
+ furi_hal_uart_deinit(usb_uart->cfg.uart_ch);
+
+ furi_hal_cdc_set_callbacks(usb_uart->cfg.vcp_ch, NULL);
+ furi_hal_usb_set_config(usb_mode_prev);
+ if(usb_uart->cfg.vcp_ch == 0) furi_hal_vcp_enable();
+
+ vStreamBufferDelete(usb_uart->rx_stream);
+ vStreamBufferDelete(usb_uart->tx_stream);
+
+ return 0;
+}
+
+static int32_t usb_uart_tx_thread(void* context) {
+ uint8_t data[USB_PKT_LEN];
+ while(1) {
+ uint32_t events = osEventFlagsWait(
+ usb_uart->events, WORKER_ALL_TX_EVENTS, osFlagsWaitAny, osWaitForever);
+ furi_check((events & osFlagsError) == 0);
+ if(events & WorkerEvtTxStop) break;
+ if(events & WorkerEvtTxReady) {
+ size_t len = 0;
+ do {
+ len = xStreamBufferReceive(usb_uart->tx_stream, &data, 1, 0);
+ if(len > 0) {
+ furi_hal_uart_tx(usb_uart->cfg.uart_ch, data, len);
+ }
+ if((usb_uart->buf_full == true) &&
+ (xStreamBufferBytesAvailable(usb_uart->tx_stream) == 0)) {
+ // Stream buffer was overflown, but now is free. Reading USB buffer to resume USB transfers
+ usb_uart->buf_full = false;
+ int32_t size = furi_hal_cdc_receive(usb_uart->cfg.vcp_ch, data, USB_PKT_LEN);
+ if(size > 0) {
+ furi_hal_uart_tx(usb_uart->cfg.uart_ch, data, size);
+ }
+ }
+ } while(len > 0);
+ }
+ }
+ return 0;
+}
+
+/* VCP callbacks */
+
+static void vcp_on_cdc_tx_complete() {
+ osEventFlagsSet(usb_uart->events, WorkerEvtSof);
+}
+
+static void vcp_on_cdc_rx() {
+ BaseType_t xHigherPriorityTaskWoken = pdFALSE;
+
+ uint16_t max_len = xStreamBufferSpacesAvailable(usb_uart->tx_stream);
+ if(max_len >= USB_PKT_LEN) {
+ //if(max_len > USB_PKT_LEN) max_len = USB_PKT_LEN;
+ int32_t size = furi_hal_cdc_receive(usb_uart->cfg.vcp_ch, usb_uart->tx_buf, USB_PKT_LEN);
+ if(size > 0) {
+ size_t ret = xStreamBufferSendFromISR(
+ usb_uart->tx_stream, usb_uart->tx_buf, size, &xHigherPriorityTaskWoken);
+ furi_check(ret == size);
+ }
+ } else {
+ usb_uart->buf_full = true;
+ }
+ osEventFlagsSet(usb_uart->events, WorkerEvtTxReady);
+ portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
+}
+
+static void vcp_state_callback(uint8_t state) {
+}
+
+static void vcp_on_cdc_control_line(uint8_t state) {
+}
+
+static void vcp_on_line_config(struct usb_cdc_line_coding* config) {
+ if((usb_uart->cfg.baudrate == 0) && (config->dwDTERate != 0))
+ furi_hal_uart_set_br(usb_uart->cfg.uart_ch, config->dwDTERate);
+}
+
+void usb_uart_enable(UsbUartConfig* cfg) {
+ if(running == false) {
+ running = true;
+ usb_uart = furi_alloc(sizeof(UsbUartParams));
+
+ usb_uart->thread = furi_thread_alloc();
+ furi_thread_set_name(usb_uart->thread, "usb_uart");
+ furi_thread_set_stack_size(usb_uart->thread, 1024);
+ furi_thread_set_context(usb_uart->thread, cfg);
+ furi_thread_set_callback(usb_uart->thread, usb_uart_worker);
+
+ usb_uart->events = osEventFlagsNew(NULL);
+
+ furi_thread_start(usb_uart->thread);
+ }
+}
+
+void usb_uart_disable() {
+ if(running == true) {
+ osEventFlagsSet(usb_uart->events, WorkerEvtStop);
+ furi_thread_join(usb_uart->thread);
+ furi_thread_free(usb_uart->thread);
+ osEventFlagsDelete(usb_uart->events);
+ free(usb_uart);
+ running = false;
+ }
+}
diff --git a/applications/gpio/usb_uart_bridge.h b/applications/gpio/usb_uart_bridge.h
new file mode 100644
index 00000000..2fe6d1d8
--- /dev/null
+++ b/applications/gpio/usb_uart_bridge.h
@@ -0,0 +1,13 @@
+#pragma once
+
+#include <stdint.h>
+
+typedef struct {
+ uint8_t vcp_ch;
+ uint8_t uart_ch;
+ uint32_t baudrate;
+} UsbUartConfig;
+
+void usb_uart_enable(UsbUartConfig* cfg);
+
+void usb_uart_disable();