Просмотр исходного кода

[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>
Nikolay Minaylov 4 лет назад
Родитель
Сommit
732b9546fc

+ 8 - 0
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;

+ 0 - 5
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,

+ 25 - 246
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);
-}
+    free(cfg_set);
+}

+ 246 - 0
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;
+    }
+}

+ 13 - 0
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();

+ 2 - 0
firmware/targets/f6/furi-hal/furi-hal-clock.c

@@ -84,6 +84,7 @@ void furi_hal_clock_init() {
     LL_RCC_EnableRTC();
 
     LL_RCC_SetUSARTClockSource(LL_RCC_USART1_CLKSOURCE_PCLK2);
+    LL_RCC_SetLPUARTClockSource(LL_RCC_LPUART1_CLKSOURCE_PCLK1);
     LL_RCC_SetADCClockSource(LL_RCC_ADC_CLKSOURCE_PLLSAI1);
     LL_RCC_SetI2CClockSource(LL_RCC_I2C1_CLKSOURCE_PCLK1);
     LL_RCC_SetRNGClockSource(LL_RCC_RNG_CLKSOURCE_CLK48);
@@ -117,6 +118,7 @@ void furi_hal_clock_init() {
     // APB1
     LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_RTCAPB);
     LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM2);
+    LL_APB1_GRP2_EnableClock(LL_APB1_GRP2_PERIPH_LPUART1);
 
     // APB2
     LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_USART1);

+ 12 - 87
firmware/targets/f6/furi-hal/furi-hal-console.c

@@ -1,5 +1,5 @@
 #include <furi-hal-console.h>
-#include <furi-hal-lpuart.h>
+#include <furi-hal-uart.h>
 
 #include <stdbool.h>
 #include <stm32wbxx_ll_gpio.h>
@@ -12,98 +12,23 @@
 
 volatile bool furi_hal_console_alive = false;
 
-static void (*irq_cb)(uint8_t ev, uint8_t data);
-
 void furi_hal_console_init() {
-    LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
-    GPIO_InitStruct.Pin = LL_GPIO_PIN_6|LL_GPIO_PIN_7;
-    GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
-    GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;
-    GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
-    GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
-    GPIO_InitStruct.Alternate = LL_GPIO_AF_7;
-    LL_GPIO_Init(GPIOB, &GPIO_InitStruct);
-
-    LL_USART_InitTypeDef USART_InitStruct = {0};
-    USART_InitStruct.PrescalerValue = LL_USART_PRESCALER_DIV1;
-    USART_InitStruct.BaudRate = CONSOLE_BAUDRATE;
-    USART_InitStruct.DataWidth = LL_USART_DATAWIDTH_8B;
-    USART_InitStruct.StopBits = LL_USART_STOPBITS_1;
-    USART_InitStruct.Parity = LL_USART_PARITY_NONE;
-    USART_InitStruct.TransferDirection = LL_USART_DIRECTION_TX_RX;
-    USART_InitStruct.HardwareFlowControl = LL_USART_HWCONTROL_NONE;
-    USART_InitStruct.OverSampling = LL_USART_OVERSAMPLING_16;
-    LL_USART_Init(USART1, &USART_InitStruct);
-    LL_USART_SetTXFIFOThreshold(USART1, LL_USART_FIFOTHRESHOLD_1_2);
-    LL_USART_EnableFIFO(USART1);
-    LL_USART_ConfigAsyncMode(USART1);
-
-    LL_USART_Enable(USART1);
-
-    while(!LL_USART_IsActiveFlag_TEACK(USART1)) ;
-
-    LL_USART_EnableIT_RXNE_RXFNE(USART1);
-    LL_USART_EnableIT_IDLE(USART1);
-    HAL_NVIC_SetPriority(USART1_IRQn, 5, 0);
-
+    furi_hal_uart_init(FuriHalUartIdUSART1, CONSOLE_BAUDRATE);
     furi_hal_console_alive = true;
 
     FURI_LOG_I("FuriHalConsole", "Init OK");
 }
 
-void furi_hal_usart_init() {
-    furi_hal_console_alive = false;
-}
-
-void furi_hal_usart_set_br(uint32_t baud) {
-    if (LL_USART_IsEnabled(USART1)) {
-        // Wait for transfer complete flag
-        while (!LL_USART_IsActiveFlag_TC(USART1));
-        LL_USART_Disable(USART1);
-        uint32_t uartclk = LL_RCC_GetUSARTClockFreq(LL_RCC_USART1_CLKSOURCE);
-        LL_USART_SetBaudRate(USART1, uartclk, LL_USART_PRESCALER_DIV1, LL_USART_OVERSAMPLING_16, baud);
-        LL_USART_Enable(USART1);
-    }
-}
-
-void furi_hal_usart_deinit() {
+void furi_hal_console_enable() {
+    furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, NULL);
     while (!LL_USART_IsActiveFlag_TC(USART1));
-    furi_hal_usart_set_br(CONSOLE_BAUDRATE);
+    furi_hal_uart_set_br(FuriHalUartIdUSART1, CONSOLE_BAUDRATE);
     furi_hal_console_alive = true;
 }
 
-void furi_hal_usart_tx(const uint8_t* buffer, size_t buffer_size) {
-    if (LL_USART_IsEnabled(USART1) == 0)
-        return;
-
-    while(buffer_size > 0) {
-        while (!LL_USART_IsActiveFlag_TXE(USART1));
-
-        LL_USART_TransmitData8(USART1, *buffer);
-
-        buffer++;
-        buffer_size--;
-    }
-}
-
-void furi_hal_usart_set_irq_cb(void (*cb)(UartIrqEvent ev, uint8_t data)) {
-    irq_cb = cb;
-    if (irq_cb == NULL)
-        NVIC_DisableIRQ(USART1_IRQn);
-    else
-        NVIC_EnableIRQ(USART1_IRQn);
-}
-
-void USART1_IRQHandler(void) {
-    if (LL_USART_IsActiveFlag_RXNE_RXFNE(USART1)) {
-        uint8_t data = LL_USART_ReceiveData8(USART1);
-        irq_cb(UartIrqEventRXNE, data);
-    } else if (LL_USART_IsActiveFlag_IDLE(USART1)) {
-        irq_cb(UartIrqEventIDLE, 0);
-        LL_USART_ClearFlag_IDLE(USART1);
-    }
-
-    //TODO: more events
+void furi_hal_console_disable() {
+    while (!LL_USART_IsActiveFlag_TC(USART1));
+    furi_hal_console_alive = false;
 }
 
 void furi_hal_console_tx(const uint8_t* buffer, size_t buffer_size) {
@@ -111,7 +36,7 @@ void furi_hal_console_tx(const uint8_t* buffer, size_t buffer_size) {
         return;
 
     // Transmit data
-    furi_hal_usart_tx(buffer, buffer_size);
+    furi_hal_uart_tx(FuriHalUartIdUSART1, (uint8_t*)buffer, buffer_size);
     // Wait for TC flag to be raised for last char
     while (!LL_USART_IsActiveFlag_TC(USART1));
 }
@@ -121,9 +46,9 @@ void furi_hal_console_tx_with_new_line(const uint8_t* buffer, size_t buffer_size
         return;
 
     // Transmit data
-    furi_hal_usart_tx(buffer, buffer_size);
+    furi_hal_uart_tx(FuriHalUartIdUSART1, (uint8_t*)buffer, buffer_size);
     // Transmit new line symbols
-    furi_hal_usart_tx((const uint8_t*)"\r\n", 2);
+    furi_hal_uart_tx(FuriHalUartIdUSART1, (uint8_t*)"\r\n", 2);
     // Wait for TC flag to be raised for last char
     while (!LL_USART_IsActiveFlag_TC(USART1));
 }
@@ -140,4 +65,4 @@ void furi_hal_console_printf(const char format[], ...) {
 
 void furi_hal_console_puts(const char *data) {
     furi_hal_console_tx((const uint8_t*)data, strlen(data));
-}
+}

+ 4 - 12
firmware/targets/f6/furi-hal/furi-hal-console.h

@@ -15,6 +15,10 @@ typedef enum {
 
 void furi_hal_console_init();
 
+void furi_hal_console_enable();
+
+void furi_hal_console_disable();
+
 void furi_hal_console_tx(const uint8_t* buffer, size_t buffer_size);
 
 void furi_hal_console_tx_with_new_line(const uint8_t* buffer, size_t buffer_size);
@@ -29,18 +33,6 @@ void furi_hal_console_printf(const char format[], ...);
 
 void furi_hal_console_puts(const char* data);
 
-
-void furi_hal_usart_init();
-
-void furi_hal_usart_deinit();
-
-void furi_hal_usart_set_br(uint32_t baud);
-
-void furi_hal_usart_tx(const uint8_t* buffer, size_t buffer_size);
-
-void furi_hal_usart_set_irq_cb(void (*cb)(UartIrqEvent ev, uint8_t data));
-
-
 #ifdef __cplusplus
 }
 #endif

+ 0 - 105
firmware/targets/f6/furi-hal/furi-hal-lpuart.c

@@ -1,105 +0,0 @@
-#include <furi-hal-lpuart.h>
-#include <stdbool.h>
-#include <stm32wbxx_ll_gpio.h>
-#include <stm32wbxx_ll_lpuart.h>
-
-#include <furi.h>
-
-static void (*irq_cb)(uint8_t ev, uint8_t data);
-
-void furi_hal_lpuart_init() {
-    LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
-    GPIO_InitStruct.Pin = PC0_Pin|PC1_Pin;
-    GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
-    GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;
-    GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
-    GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
-    GPIO_InitStruct.Alternate = LL_GPIO_AF_8;
-    LL_GPIO_Init(GPIOC, &GPIO_InitStruct);
-
-    LL_RCC_SetLPUARTClockSource(LL_RCC_LPUART1_CLKSOURCE_PCLK1);
-    LL_APB1_GRP2_EnableClock(LL_APB1_GRP2_PERIPH_LPUART1);
-
-    LL_LPUART_InitTypeDef LPUART_InitStruct = {0};
-    LPUART_InitStruct.PrescalerValue = LL_LPUART_PRESCALER_DIV1;
-    LPUART_InitStruct.BaudRate = 115200;
-    LPUART_InitStruct.DataWidth = LL_LPUART_DATAWIDTH_8B;
-    LPUART_InitStruct.StopBits = LL_LPUART_STOPBITS_1;
-    LPUART_InitStruct.Parity = LL_LPUART_PARITY_NONE;
-    LPUART_InitStruct.TransferDirection = LL_LPUART_DIRECTION_TX_RX;
-    LPUART_InitStruct.HardwareFlowControl = LL_LPUART_HWCONTROL_NONE;
-    LL_LPUART_Init(LPUART1, &LPUART_InitStruct);
-    LL_LPUART_SetTXFIFOThreshold(LPUART1, LL_LPUART_FIFOTHRESHOLD_1_8);
-    LL_LPUART_SetRXFIFOThreshold(LPUART1, LL_LPUART_FIFOTHRESHOLD_1_8);
-    LL_LPUART_EnableFIFO(LPUART1);
-
-    LL_LPUART_Enable(LPUART1);
-
-    while((!(LL_LPUART_IsActiveFlag_TEACK(LPUART1))) || (!(LL_LPUART_IsActiveFlag_REACK(LPUART1))));
-
-    LL_LPUART_EnableIT_RXNE_RXFNE(LPUART1);
-    LL_LPUART_EnableIT_IDLE(LPUART1);
-    HAL_NVIC_SetPriority(LPUART1_IRQn, 5, 0);
-
-    FURI_LOG_I("FuriHalLpUart", "Init OK");
-}
-
-void furi_hal_lpuart_set_br(uint32_t baud) {
-    if (LL_LPUART_IsEnabled(LPUART1)) {
-        // Wait for transfer complete flag
-        while (!LL_LPUART_IsActiveFlag_TC(LPUART1));
-        LL_LPUART_Disable(LPUART1);
-        uint32_t uartclk = LL_RCC_GetLPUARTClockFreq(LL_RCC_GetLPUARTClockSource(LL_RCC_LPUART1_CLKSOURCE_PCLK1));
-        if (uartclk/baud > 4095) {
-            LL_LPUART_SetPrescaler(LPUART1, LL_LPUART_PRESCALER_DIV32);
-            LL_LPUART_SetBaudRate(LPUART1, uartclk, LL_LPUART_PRESCALER_DIV32, baud);
-        } else {
-            LL_LPUART_SetPrescaler(LPUART1, LL_LPUART_PRESCALER_DIV1);
-            LL_LPUART_SetBaudRate(LPUART1, uartclk, LL_LPUART_PRESCALER_DIV1, baud);
-        }
-        
-        LL_LPUART_Enable(LPUART1);
-    }
-}
-
-void furi_hal_lpuart_deinit() {
-    furi_hal_lpuart_set_irq_cb(NULL);
-    LL_GPIO_SetPinMode(GPIOC, PC0_Pin, LL_GPIO_MODE_ANALOG);
-    LL_GPIO_SetPinMode(GPIOC, PC1_Pin, LL_GPIO_MODE_ANALOG);
-    LL_LPUART_Disable(LPUART1);
-    LL_APB1_GRP2_DisableClock(LL_APB1_GRP2_PERIPH_LPUART1);
-}
-
-void furi_hal_lpuart_tx(const uint8_t* buffer, size_t buffer_size) {
-    if (LL_LPUART_IsEnabled(LPUART1) == 0)
-        return;
-
-    while(buffer_size > 0) {
-        while (!LL_LPUART_IsActiveFlag_TXE(LPUART1));
-
-        LL_LPUART_TransmitData8(LPUART1, *buffer);
-  
-        buffer++;
-        buffer_size--;
-    }
-}
-
-void furi_hal_lpuart_set_irq_cb(void (*cb)(UartIrqEvent ev, uint8_t data)) {
-    irq_cb = cb;
-    if (irq_cb == NULL)
-        NVIC_DisableIRQ(LPUART1_IRQn);
-    else
-        NVIC_EnableIRQ(LPUART1_IRQn);
-}
-
-void LPUART1_IRQHandler(void) {
-    if (LL_LPUART_IsActiveFlag_RXNE_RXFNE(LPUART1)) {
-        uint8_t data = LL_LPUART_ReceiveData8(LPUART1);
-        irq_cb(UartIrqEventRXNE, data);
-    } else if (LL_LPUART_IsActiveFlag_IDLE(LPUART1)) {
-        irq_cb(UartIrqEventIDLE, 0);
-        LL_LPUART_ClearFlag_IDLE(LPUART1);
-    }
-
-    //TODO: more events
-}

+ 0 - 24
firmware/targets/f6/furi-hal/furi-hal-lpuart.h

@@ -1,24 +0,0 @@
-#pragma once
-
-#include <stddef.h>
-#include <stdint.h>
-#include "furi-hal-console.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-
-void furi_hal_lpuart_init();
-
-void furi_hal_lpuart_deinit();
-
-void furi_hal_lpuart_set_br(uint32_t baud);
-
-void furi_hal_lpuart_tx(const uint8_t* buffer, size_t buffer_size);
-
-void furi_hal_lpuart_set_irq_cb(void (*cb)(UartIrqEvent ev, uint8_t data));
-
-#ifdef __cplusplus
-}
-#endif

+ 201 - 0
firmware/targets/f6/furi-hal/furi-hal-uart.c

@@ -0,0 +1,201 @@
+#include <furi-hal-uart.h>
+#include <stdbool.h>
+#include <stm32wbxx_ll_lpuart.h>
+#include <stm32wbxx_ll_usart.h>
+#include <furi-hal-resources.h>
+
+#include <furi.h>
+
+static void (*irq_cb[2])(uint8_t ev, uint8_t data);
+
+static void furi_hal_usart_init(uint32_t baud) {
+    hal_gpio_init_ex(
+        &gpio_usart_tx,
+        GpioModeAltFunctionPushPull,
+        GpioPullUp,
+        GpioSpeedVeryHigh,
+        GpioAltFn7USART1);
+    hal_gpio_init_ex(
+        &gpio_usart_rx,
+        GpioModeAltFunctionPushPull,
+        GpioPullUp,
+        GpioSpeedVeryHigh,
+        GpioAltFn7USART1);
+
+    LL_USART_InitTypeDef USART_InitStruct = {0};
+    USART_InitStruct.PrescalerValue = LL_USART_PRESCALER_DIV1;
+    USART_InitStruct.BaudRate = baud;
+    USART_InitStruct.DataWidth = LL_USART_DATAWIDTH_8B;
+    USART_InitStruct.StopBits = LL_USART_STOPBITS_1;
+    USART_InitStruct.Parity = LL_USART_PARITY_NONE;
+    USART_InitStruct.TransferDirection = LL_USART_DIRECTION_TX_RX;
+    USART_InitStruct.HardwareFlowControl = LL_USART_HWCONTROL_NONE;
+    USART_InitStruct.OverSampling = LL_USART_OVERSAMPLING_16;
+    LL_USART_Init(USART1, &USART_InitStruct);
+    LL_USART_SetTXFIFOThreshold(USART1, LL_USART_FIFOTHRESHOLD_1_2);
+    LL_USART_EnableFIFO(USART1);
+    LL_USART_ConfigAsyncMode(USART1);
+
+    LL_USART_Enable(USART1);
+
+    while(!LL_USART_IsActiveFlag_TEACK(USART1));
+
+    LL_USART_EnableIT_RXNE_RXFNE(USART1);
+    LL_USART_EnableIT_IDLE(USART1);
+    HAL_NVIC_SetPriority(USART1_IRQn, 5, 0);
+}
+
+static void furi_hal_lpuart_init(uint32_t baud) {
+    hal_gpio_init_ex(
+        &gpio_ext_pc0,
+        GpioModeAltFunctionPushPull,
+        GpioPullUp,
+        GpioSpeedVeryHigh,
+        GpioAltFn8LPUART1);
+    hal_gpio_init_ex(
+        &gpio_ext_pc1,
+        GpioModeAltFunctionPushPull,
+        GpioPullUp,
+        GpioSpeedVeryHigh,
+        GpioAltFn8LPUART1);
+
+    LL_LPUART_InitTypeDef LPUART_InitStruct = {0};
+    LPUART_InitStruct.PrescalerValue = LL_LPUART_PRESCALER_DIV1;
+    LPUART_InitStruct.BaudRate = 115200;
+    LPUART_InitStruct.DataWidth = LL_LPUART_DATAWIDTH_8B;
+    LPUART_InitStruct.StopBits = LL_LPUART_STOPBITS_1;
+    LPUART_InitStruct.Parity = LL_LPUART_PARITY_NONE;
+    LPUART_InitStruct.TransferDirection = LL_LPUART_DIRECTION_TX_RX;
+    LPUART_InitStruct.HardwareFlowControl = LL_LPUART_HWCONTROL_NONE;
+    LL_LPUART_Init(LPUART1, &LPUART_InitStruct);
+    LL_LPUART_SetTXFIFOThreshold(LPUART1, LL_LPUART_FIFOTHRESHOLD_1_8);
+    LL_LPUART_SetRXFIFOThreshold(LPUART1, LL_LPUART_FIFOTHRESHOLD_1_8);
+    LL_LPUART_EnableFIFO(LPUART1);
+
+    LL_LPUART_Enable(LPUART1);
+
+    while((!(LL_LPUART_IsActiveFlag_TEACK(LPUART1))) || (!(LL_LPUART_IsActiveFlag_REACK(LPUART1))));
+
+    furi_hal_uart_set_br(FuriHalUartIdLPUART1, baud);
+
+    LL_LPUART_EnableIT_RXNE_RXFNE(LPUART1);
+    LL_LPUART_EnableIT_IDLE(LPUART1);
+    HAL_NVIC_SetPriority(LPUART1_IRQn, 5, 0);
+}
+
+void furi_hal_uart_init(FuriHalUartId ch, uint32_t baud) {
+    if (ch == FuriHalUartIdLPUART1)
+        furi_hal_lpuart_init(baud);
+    else if (ch == FuriHalUartIdUSART1)
+        furi_hal_usart_init(baud);
+}
+
+void furi_hal_uart_set_br(FuriHalUartId ch, uint32_t baud) {
+    if (ch == FuriHalUartIdUSART1) {
+        if (LL_USART_IsEnabled(USART1)) {
+            // Wait for transfer complete flag
+            while (!LL_USART_IsActiveFlag_TC(USART1));
+            LL_USART_Disable(USART1);
+            uint32_t uartclk = LL_RCC_GetUSARTClockFreq(LL_RCC_USART1_CLKSOURCE);
+            LL_USART_SetBaudRate(USART1, uartclk, LL_USART_PRESCALER_DIV1, LL_USART_OVERSAMPLING_16, baud);
+            LL_USART_Enable(USART1);
+        }
+    } else if (ch == FuriHalUartIdLPUART1) {
+        if (LL_LPUART_IsEnabled(LPUART1)) {
+            // Wait for transfer complete flag
+            while (!LL_LPUART_IsActiveFlag_TC(LPUART1));
+            LL_LPUART_Disable(LPUART1);
+            uint32_t uartclk = LL_RCC_GetLPUARTClockFreq(LL_RCC_GetLPUARTClockSource(LL_RCC_LPUART1_CLKSOURCE_PCLK1));
+            if (uartclk/baud > 4095) {
+                LL_LPUART_SetPrescaler(LPUART1, LL_LPUART_PRESCALER_DIV32);
+                LL_LPUART_SetBaudRate(LPUART1, uartclk, LL_LPUART_PRESCALER_DIV32, baud);
+            } else {
+                LL_LPUART_SetPrescaler(LPUART1, LL_LPUART_PRESCALER_DIV1);
+                LL_LPUART_SetBaudRate(LPUART1, uartclk, LL_LPUART_PRESCALER_DIV1, baud);
+            }
+            LL_LPUART_Enable(LPUART1);
+        }
+    }
+}
+
+void furi_hal_uart_deinit(FuriHalUartId ch) {
+    furi_hal_uart_set_irq_cb(ch, NULL);
+    if (ch == FuriHalUartIdUSART1) {
+        LL_USART_Disable(USART1);
+        hal_gpio_init(&gpio_usart_tx, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
+        hal_gpio_init(&gpio_usart_rx, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
+    } else if (ch == FuriHalUartIdLPUART1) {
+        LL_LPUART_Disable(LPUART1);
+        hal_gpio_init(&gpio_ext_pc0, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
+        hal_gpio_init(&gpio_ext_pc1, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
+    }
+}
+
+void furi_hal_uart_tx(FuriHalUartId ch, uint8_t* buffer, size_t buffer_size) {
+    if (ch == FuriHalUartIdUSART1) {
+        if (LL_USART_IsEnabled(USART1) == 0)
+            return;
+
+        while(buffer_size > 0) {
+            while (!LL_USART_IsActiveFlag_TXE(USART1));
+
+            LL_USART_TransmitData8(USART1, *buffer);
+            buffer++;
+            buffer_size--;
+        }
+
+    } else if (ch == FuriHalUartIdLPUART1) {
+        if (LL_LPUART_IsEnabled(LPUART1) == 0)
+            return;
+
+        while(buffer_size > 0) {
+            while (!LL_LPUART_IsActiveFlag_TXE(LPUART1));
+
+            LL_LPUART_TransmitData8(LPUART1, *buffer);
+    
+            buffer++;
+            buffer_size--;
+        }
+    }
+}
+
+void furi_hal_uart_set_irq_cb(FuriHalUartId ch, void (*cb)(UartIrqEvent ev, uint8_t data)) {
+    if (cb == NULL) {
+        if (ch == FuriHalUartIdUSART1)
+            NVIC_DisableIRQ(USART1_IRQn);
+        else if (ch == FuriHalUartIdLPUART1)
+            NVIC_DisableIRQ(LPUART1_IRQn);
+        irq_cb[ch] = cb;
+    } else {
+        irq_cb[ch] = cb;
+        if (ch == FuriHalUartIdUSART1)
+            NVIC_EnableIRQ(USART1_IRQn);
+        else if (ch == FuriHalUartIdLPUART1)
+            NVIC_EnableIRQ(LPUART1_IRQn);
+    }
+}
+
+void LPUART1_IRQHandler(void) {
+    if (LL_LPUART_IsActiveFlag_RXNE_RXFNE(LPUART1)) {
+        uint8_t data = LL_LPUART_ReceiveData8(LPUART1);
+        irq_cb[FuriHalUartIdLPUART1](UartIrqEventRXNE, data);
+    } else if (LL_LPUART_IsActiveFlag_IDLE(LPUART1)) {
+        irq_cb[FuriHalUartIdLPUART1](UartIrqEventIDLE, 0);
+        LL_LPUART_ClearFlag_IDLE(LPUART1);
+    } else if (LL_LPUART_IsActiveFlag_ORE(LPUART1)) {
+        LL_LPUART_ClearFlag_ORE(LPUART1);
+    }
+    //TODO: more events
+}
+
+void USART1_IRQHandler(void) {
+    if (LL_USART_IsActiveFlag_RXNE_RXFNE(USART1)) {
+        uint8_t data = LL_USART_ReceiveData8(USART1);
+        irq_cb[FuriHalUartIdUSART1](UartIrqEventRXNE, data);
+    } else if (LL_USART_IsActiveFlag_IDLE(USART1)) {
+        irq_cb[FuriHalUartIdUSART1](UartIrqEventIDLE, 0);
+        LL_USART_ClearFlag_IDLE(USART1);
+    } else if (LL_USART_IsActiveFlag_ORE(USART1)) {
+        LL_USART_ClearFlag_ORE(USART1);
+    }
+}

+ 29 - 0
firmware/targets/f6/furi-hal/furi-hal-uart.h

@@ -0,0 +1,29 @@
+#pragma once
+
+#include <stddef.h>
+#include <stdint.h>
+#include "furi-hal-console.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+    FuriHalUartIdUSART1,
+    FuriHalUartIdLPUART1,
+} FuriHalUartId;
+
+
+void furi_hal_uart_init(FuriHalUartId ch, uint32_t baud);
+
+void furi_hal_uart_deinit(FuriHalUartId ch);
+
+void furi_hal_uart_set_br(FuriHalUartId ch, uint32_t baud);
+
+void furi_hal_uart_tx(FuriHalUartId ch, uint8_t* buffer, size_t buffer_size);
+
+void furi_hal_uart_set_irq_cb(FuriHalUartId ch, void (*cb)(UartIrqEvent ev, uint8_t data));
+
+#ifdef __cplusplus
+}
+#endif

+ 5 - 5
firmware/targets/f6/furi-hal/furi-hal-usb-cdc.c

@@ -7,12 +7,12 @@
 #include "usb_cdc.h"
 
 #define CDC0_RXD_EP      0x01
-#define CDC0_TXD_EP      0x82
-#define CDC0_NTF_EP      0x83
+#define CDC0_TXD_EP      0x81
+#define CDC0_NTF_EP      0x82
 
-#define CDC1_RXD_EP      0x04
-#define CDC1_TXD_EP      0x85
-#define CDC1_NTF_EP      0x86
+#define CDC1_RXD_EP      0x03
+#define CDC1_TXD_EP      0x83
+#define CDC1_NTF_EP      0x84
 
 #define CDC_NTF_SZ      0x08
 

+ 2 - 0
firmware/targets/f7/furi-hal/furi-hal-clock.c

@@ -84,6 +84,7 @@ void furi_hal_clock_init() {
     LL_RCC_EnableRTC();
 
     LL_RCC_SetUSARTClockSource(LL_RCC_USART1_CLKSOURCE_PCLK2);
+    LL_RCC_SetLPUARTClockSource(LL_RCC_LPUART1_CLKSOURCE_PCLK1);
     LL_RCC_SetADCClockSource(LL_RCC_ADC_CLKSOURCE_PLLSAI1);
     LL_RCC_SetI2CClockSource(LL_RCC_I2C1_CLKSOURCE_PCLK1);
     LL_RCC_SetRNGClockSource(LL_RCC_RNG_CLKSOURCE_CLK48);
@@ -117,6 +118,7 @@ void furi_hal_clock_init() {
     // APB1
     LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_RTCAPB);
     LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM2);
+    LL_APB1_GRP2_EnableClock(LL_APB1_GRP2_PERIPH_LPUART1);
 
     // APB2
     LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_USART1);

+ 12 - 87
firmware/targets/f7/furi-hal/furi-hal-console.c

@@ -1,5 +1,5 @@
 #include <furi-hal-console.h>
-#include <furi-hal-lpuart.h>
+#include <furi-hal-uart.h>
 
 #include <stdbool.h>
 #include <stm32wbxx_ll_gpio.h>
@@ -12,98 +12,23 @@
 
 volatile bool furi_hal_console_alive = false;
 
-static void (*irq_cb)(uint8_t ev, uint8_t data);
-
 void furi_hal_console_init() {
-    LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
-    GPIO_InitStruct.Pin = LL_GPIO_PIN_6|LL_GPIO_PIN_7;
-    GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
-    GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;
-    GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
-    GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
-    GPIO_InitStruct.Alternate = LL_GPIO_AF_7;
-    LL_GPIO_Init(GPIOB, &GPIO_InitStruct);
-
-    LL_USART_InitTypeDef USART_InitStruct = {0};
-    USART_InitStruct.PrescalerValue = LL_USART_PRESCALER_DIV1;
-    USART_InitStruct.BaudRate = CONSOLE_BAUDRATE;
-    USART_InitStruct.DataWidth = LL_USART_DATAWIDTH_8B;
-    USART_InitStruct.StopBits = LL_USART_STOPBITS_1;
-    USART_InitStruct.Parity = LL_USART_PARITY_NONE;
-    USART_InitStruct.TransferDirection = LL_USART_DIRECTION_TX_RX;
-    USART_InitStruct.HardwareFlowControl = LL_USART_HWCONTROL_NONE;
-    USART_InitStruct.OverSampling = LL_USART_OVERSAMPLING_16;
-    LL_USART_Init(USART1, &USART_InitStruct);
-    LL_USART_SetTXFIFOThreshold(USART1, LL_USART_FIFOTHRESHOLD_1_2);
-    LL_USART_EnableFIFO(USART1);
-    LL_USART_ConfigAsyncMode(USART1);
-
-    LL_USART_Enable(USART1);
-
-    while(!LL_USART_IsActiveFlag_TEACK(USART1)) ;
-
-    LL_USART_EnableIT_RXNE_RXFNE(USART1);
-    LL_USART_EnableIT_IDLE(USART1);
-    HAL_NVIC_SetPriority(USART1_IRQn, 5, 0);
-
+    furi_hal_uart_init(FuriHalUartIdUSART1, CONSOLE_BAUDRATE);
     furi_hal_console_alive = true;
 
     FURI_LOG_I("FuriHalConsole", "Init OK");
 }
 
-void furi_hal_usart_init() {
-    furi_hal_console_alive = false;
-}
-
-void furi_hal_usart_set_br(uint32_t baud) {
-    if (LL_USART_IsEnabled(USART1)) {
-        // Wait for transfer complete flag
-        while (!LL_USART_IsActiveFlag_TC(USART1));
-        LL_USART_Disable(USART1);
-        uint32_t uartclk = LL_RCC_GetUSARTClockFreq(LL_RCC_USART1_CLKSOURCE);
-        LL_USART_SetBaudRate(USART1, uartclk, LL_USART_PRESCALER_DIV1, LL_USART_OVERSAMPLING_16, baud);
-        LL_USART_Enable(USART1);
-    }
-}
-
-void furi_hal_usart_deinit() {
+void furi_hal_console_enable() {
+    furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, NULL);
     while (!LL_USART_IsActiveFlag_TC(USART1));
-    furi_hal_usart_set_br(CONSOLE_BAUDRATE);
+    furi_hal_uart_set_br(FuriHalUartIdUSART1, CONSOLE_BAUDRATE);
     furi_hal_console_alive = true;
 }
 
-void furi_hal_usart_tx(const uint8_t* buffer, size_t buffer_size) {
-    if (LL_USART_IsEnabled(USART1) == 0)
-        return;
-
-    while(buffer_size > 0) {
-        while (!LL_USART_IsActiveFlag_TXE(USART1));
-
-        LL_USART_TransmitData8(USART1, *buffer);
-
-        buffer++;
-        buffer_size--;
-    }
-}
-
-void furi_hal_usart_set_irq_cb(void (*cb)(UartIrqEvent ev, uint8_t data)) {
-    irq_cb = cb;
-    if (irq_cb == NULL)
-        NVIC_DisableIRQ(USART1_IRQn);
-    else
-        NVIC_EnableIRQ(USART1_IRQn);
-}
-
-void USART1_IRQHandler(void) {
-    if (LL_USART_IsActiveFlag_RXNE_RXFNE(USART1)) {
-        uint8_t data = LL_USART_ReceiveData8(USART1);
-        irq_cb(UartIrqEventRXNE, data);
-    } else if (LL_USART_IsActiveFlag_IDLE(USART1)) {
-        irq_cb(UartIrqEventIDLE, 0);
-        LL_USART_ClearFlag_IDLE(USART1);
-    }
-
-    //TODO: more events
+void furi_hal_console_disable() {
+    while (!LL_USART_IsActiveFlag_TC(USART1));
+    furi_hal_console_alive = false;
 }
 
 void furi_hal_console_tx(const uint8_t* buffer, size_t buffer_size) {
@@ -111,7 +36,7 @@ void furi_hal_console_tx(const uint8_t* buffer, size_t buffer_size) {
         return;
 
     // Transmit data
-    furi_hal_usart_tx(buffer, buffer_size);
+    furi_hal_uart_tx(FuriHalUartIdUSART1, (uint8_t*)buffer, buffer_size);
     // Wait for TC flag to be raised for last char
     while (!LL_USART_IsActiveFlag_TC(USART1));
 }
@@ -121,9 +46,9 @@ void furi_hal_console_tx_with_new_line(const uint8_t* buffer, size_t buffer_size
         return;
 
     // Transmit data
-    furi_hal_usart_tx(buffer, buffer_size);
+    furi_hal_uart_tx(FuriHalUartIdUSART1, (uint8_t*)buffer, buffer_size);
     // Transmit new line symbols
-    furi_hal_usart_tx((const uint8_t*)"\r\n", 2);
+    furi_hal_uart_tx(FuriHalUartIdUSART1, (uint8_t*)"\r\n", 2);
     // Wait for TC flag to be raised for last char
     while (!LL_USART_IsActiveFlag_TC(USART1));
 }
@@ -140,4 +65,4 @@ void furi_hal_console_printf(const char format[], ...) {
 
 void furi_hal_console_puts(const char *data) {
     furi_hal_console_tx((const uint8_t*)data, strlen(data));
-}
+}

+ 4 - 12
firmware/targets/f7/furi-hal/furi-hal-console.h

@@ -15,6 +15,10 @@ typedef enum {
 
 void furi_hal_console_init();
 
+void furi_hal_console_enable();
+
+void furi_hal_console_disable();
+
 void furi_hal_console_tx(const uint8_t* buffer, size_t buffer_size);
 
 void furi_hal_console_tx_with_new_line(const uint8_t* buffer, size_t buffer_size);
@@ -29,18 +33,6 @@ void furi_hal_console_printf(const char format[], ...);
 
 void furi_hal_console_puts(const char* data);
 
-
-void furi_hal_usart_init();
-
-void furi_hal_usart_deinit();
-
-void furi_hal_usart_set_br(uint32_t baud);
-
-void furi_hal_usart_tx(const uint8_t* buffer, size_t buffer_size);
-
-void furi_hal_usart_set_irq_cb(void (*cb)(UartIrqEvent ev, uint8_t data));
-
-
 #ifdef __cplusplus
 }
 #endif

+ 0 - 105
firmware/targets/f7/furi-hal/furi-hal-lpuart.c

@@ -1,105 +0,0 @@
-#include <furi-hal-lpuart.h>
-#include <stdbool.h>
-#include <stm32wbxx_ll_gpio.h>
-#include <stm32wbxx_ll_lpuart.h>
-
-#include <furi.h>
-
-static void (*irq_cb)(uint8_t ev, uint8_t data);
-
-void furi_hal_lpuart_init() {
-    LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
-    GPIO_InitStruct.Pin = PC0_Pin|PC1_Pin;
-    GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
-    GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;
-    GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
-    GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
-    GPIO_InitStruct.Alternate = LL_GPIO_AF_8;
-    LL_GPIO_Init(GPIOC, &GPIO_InitStruct);
-
-    LL_RCC_SetLPUARTClockSource(LL_RCC_LPUART1_CLKSOURCE_PCLK1);
-    LL_APB1_GRP2_EnableClock(LL_APB1_GRP2_PERIPH_LPUART1);
-
-    LL_LPUART_InitTypeDef LPUART_InitStruct = {0};
-    LPUART_InitStruct.PrescalerValue = LL_LPUART_PRESCALER_DIV1;
-    LPUART_InitStruct.BaudRate = 115200;
-    LPUART_InitStruct.DataWidth = LL_LPUART_DATAWIDTH_8B;
-    LPUART_InitStruct.StopBits = LL_LPUART_STOPBITS_1;
-    LPUART_InitStruct.Parity = LL_LPUART_PARITY_NONE;
-    LPUART_InitStruct.TransferDirection = LL_LPUART_DIRECTION_TX_RX;
-    LPUART_InitStruct.HardwareFlowControl = LL_LPUART_HWCONTROL_NONE;
-    LL_LPUART_Init(LPUART1, &LPUART_InitStruct);
-    LL_LPUART_SetTXFIFOThreshold(LPUART1, LL_LPUART_FIFOTHRESHOLD_1_8);
-    LL_LPUART_SetRXFIFOThreshold(LPUART1, LL_LPUART_FIFOTHRESHOLD_1_8);
-    LL_LPUART_EnableFIFO(LPUART1);
-
-    LL_LPUART_Enable(LPUART1);
-
-    while((!(LL_LPUART_IsActiveFlag_TEACK(LPUART1))) || (!(LL_LPUART_IsActiveFlag_REACK(LPUART1))));
-
-    LL_LPUART_EnableIT_RXNE_RXFNE(LPUART1);
-    LL_LPUART_EnableIT_IDLE(LPUART1);
-    HAL_NVIC_SetPriority(LPUART1_IRQn, 5, 0);
-
-    FURI_LOG_I("FuriHalLpUart", "Init OK");
-}
-
-void furi_hal_lpuart_set_br(uint32_t baud) {
-    if (LL_LPUART_IsEnabled(LPUART1)) {
-        // Wait for transfer complete flag
-        while (!LL_LPUART_IsActiveFlag_TC(LPUART1));
-        LL_LPUART_Disable(LPUART1);
-        uint32_t uartclk = LL_RCC_GetLPUARTClockFreq(LL_RCC_GetLPUARTClockSource(LL_RCC_LPUART1_CLKSOURCE_PCLK1));
-        if (uartclk/baud > 4095) {
-            LL_LPUART_SetPrescaler(LPUART1, LL_LPUART_PRESCALER_DIV32);
-            LL_LPUART_SetBaudRate(LPUART1, uartclk, LL_LPUART_PRESCALER_DIV32, baud);
-        } else {
-            LL_LPUART_SetPrescaler(LPUART1, LL_LPUART_PRESCALER_DIV1);
-            LL_LPUART_SetBaudRate(LPUART1, uartclk, LL_LPUART_PRESCALER_DIV1, baud);
-        }
-        
-        LL_LPUART_Enable(LPUART1);
-    }
-}
-
-void furi_hal_lpuart_deinit() {
-    furi_hal_lpuart_set_irq_cb(NULL);
-    LL_GPIO_SetPinMode(GPIOC, PC0_Pin, LL_GPIO_MODE_ANALOG);
-    LL_GPIO_SetPinMode(GPIOC, PC1_Pin, LL_GPIO_MODE_ANALOG);
-    LL_LPUART_Disable(LPUART1);
-    LL_APB1_GRP2_DisableClock(LL_APB1_GRP2_PERIPH_LPUART1);
-}
-
-void furi_hal_lpuart_tx(const uint8_t* buffer, size_t buffer_size) {
-    if (LL_LPUART_IsEnabled(LPUART1) == 0)
-        return;
-
-    while(buffer_size > 0) {
-        while (!LL_LPUART_IsActiveFlag_TXE(LPUART1));
-
-        LL_LPUART_TransmitData8(LPUART1, *buffer);
-  
-        buffer++;
-        buffer_size--;
-    }
-}
-
-void furi_hal_lpuart_set_irq_cb(void (*cb)(UartIrqEvent ev, uint8_t data)) {
-    irq_cb = cb;
-    if (irq_cb == NULL)
-        NVIC_DisableIRQ(LPUART1_IRQn);
-    else
-        NVIC_EnableIRQ(LPUART1_IRQn);
-}
-
-void LPUART1_IRQHandler(void) {
-    if (LL_LPUART_IsActiveFlag_RXNE_RXFNE(LPUART1)) {
-        uint8_t data = LL_LPUART_ReceiveData8(LPUART1);
-        irq_cb(UartIrqEventRXNE, data);
-    } else if (LL_LPUART_IsActiveFlag_IDLE(LPUART1)) {
-        irq_cb(UartIrqEventIDLE, 0);
-        LL_LPUART_ClearFlag_IDLE(LPUART1);
-    }
-
-    //TODO: more events
-}

+ 0 - 24
firmware/targets/f7/furi-hal/furi-hal-lpuart.h

@@ -1,24 +0,0 @@
-#pragma once
-
-#include <stddef.h>
-#include <stdint.h>
-#include "furi-hal-console.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-
-void furi_hal_lpuart_init();
-
-void furi_hal_lpuart_deinit();
-
-void furi_hal_lpuart_set_br(uint32_t baud);
-
-void furi_hal_lpuart_tx(const uint8_t* buffer, size_t buffer_size);
-
-void furi_hal_lpuart_set_irq_cb(void (*cb)(UartIrqEvent ev, uint8_t data));
-
-#ifdef __cplusplus
-}
-#endif

+ 197 - 0
firmware/targets/f7/furi-hal/furi-hal-uart.c

@@ -0,0 +1,197 @@
+#include <furi-hal-uart.h>
+#include <stdbool.h>
+#include <stm32wbxx_ll_lpuart.h>
+#include <stm32wbxx_ll_usart.h>
+#include <furi-hal-resources.h>
+
+#include <furi.h>
+
+static void (*irq_cb[2])(uint8_t ev, uint8_t data);
+
+static void furi_hal_usart_init(uint32_t baud) {
+    hal_gpio_init_ex(
+        &gpio_usart_tx,
+        GpioModeAltFunctionPushPull,
+        GpioPullUp,
+        GpioSpeedVeryHigh,
+        GpioAltFn7USART1);
+    hal_gpio_init_ex(
+        &gpio_usart_rx,
+        GpioModeAltFunctionPushPull,
+        GpioPullUp,
+        GpioSpeedVeryHigh,
+        GpioAltFn7USART1);
+
+    LL_USART_InitTypeDef USART_InitStruct = {0};
+    USART_InitStruct.PrescalerValue = LL_USART_PRESCALER_DIV1;
+    USART_InitStruct.BaudRate = baud;
+    USART_InitStruct.DataWidth = LL_USART_DATAWIDTH_8B;
+    USART_InitStruct.StopBits = LL_USART_STOPBITS_1;
+    USART_InitStruct.Parity = LL_USART_PARITY_NONE;
+    USART_InitStruct.TransferDirection = LL_USART_DIRECTION_TX_RX;
+    USART_InitStruct.HardwareFlowControl = LL_USART_HWCONTROL_NONE;
+    USART_InitStruct.OverSampling = LL_USART_OVERSAMPLING_16;
+    LL_USART_Init(USART1, &USART_InitStruct);
+    LL_USART_SetTXFIFOThreshold(USART1, LL_USART_FIFOTHRESHOLD_1_2);
+    LL_USART_EnableFIFO(USART1);
+    LL_USART_ConfigAsyncMode(USART1);
+
+    LL_USART_Enable(USART1);
+
+    while(!LL_USART_IsActiveFlag_TEACK(USART1));
+
+    LL_USART_EnableIT_RXNE_RXFNE(USART1);
+    LL_USART_EnableIT_IDLE(USART1);
+    HAL_NVIC_SetPriority(USART1_IRQn, 5, 0);
+}
+
+static void furi_hal_lpuart_init(uint32_t baud) {
+    hal_gpio_init_ex(
+        &gpio_ext_pc0,
+        GpioModeAltFunctionPushPull,
+        GpioPullUp,
+        GpioSpeedVeryHigh,
+        GpioAltFn8LPUART1);
+    hal_gpio_init_ex(
+        &gpio_ext_pc1,
+        GpioModeAltFunctionPushPull,
+        GpioPullUp,
+        GpioSpeedVeryHigh,
+        GpioAltFn8LPUART1);
+
+    LL_LPUART_InitTypeDef LPUART_InitStruct = {0};
+    LPUART_InitStruct.PrescalerValue = LL_LPUART_PRESCALER_DIV1;
+    LPUART_InitStruct.BaudRate = 115200;
+    LPUART_InitStruct.DataWidth = LL_LPUART_DATAWIDTH_8B;
+    LPUART_InitStruct.StopBits = LL_LPUART_STOPBITS_1;
+    LPUART_InitStruct.Parity = LL_LPUART_PARITY_NONE;
+    LPUART_InitStruct.TransferDirection = LL_LPUART_DIRECTION_TX_RX;
+    LPUART_InitStruct.HardwareFlowControl = LL_LPUART_HWCONTROL_NONE;
+    LL_LPUART_Init(LPUART1, &LPUART_InitStruct);
+    LL_LPUART_SetTXFIFOThreshold(LPUART1, LL_LPUART_FIFOTHRESHOLD_1_8);
+    LL_LPUART_SetRXFIFOThreshold(LPUART1, LL_LPUART_FIFOTHRESHOLD_1_8);
+    LL_LPUART_EnableFIFO(LPUART1);
+
+    LL_LPUART_Enable(LPUART1);
+
+    while((!(LL_LPUART_IsActiveFlag_TEACK(LPUART1))) || (!(LL_LPUART_IsActiveFlag_REACK(LPUART1))));
+
+    furi_hal_uart_set_br(FuriHalUartIdLPUART1, baud);
+
+    LL_LPUART_EnableIT_RXNE_RXFNE(LPUART1);
+    LL_LPUART_EnableIT_IDLE(LPUART1);
+    HAL_NVIC_SetPriority(LPUART1_IRQn, 5, 0);
+}
+
+void furi_hal_uart_init(FuriHalUartId ch, uint32_t baud) {
+    if (ch == FuriHalUartIdLPUART1)
+        furi_hal_lpuart_init(baud);
+    else if (ch == FuriHalUartIdUSART1)
+        furi_hal_usart_init(baud);
+}
+
+void furi_hal_uart_set_br(FuriHalUartId ch, uint32_t baud) {
+    if (ch == FuriHalUartIdUSART1) {
+        if (LL_USART_IsEnabled(USART1)) {
+            // Wait for transfer complete flag
+            while (!LL_USART_IsActiveFlag_TC(USART1));
+            LL_USART_Disable(USART1);
+            uint32_t uartclk = LL_RCC_GetUSARTClockFreq(LL_RCC_USART1_CLKSOURCE);
+            LL_USART_SetBaudRate(USART1, uartclk, LL_USART_PRESCALER_DIV1, LL_USART_OVERSAMPLING_16, baud);
+            LL_USART_Enable(USART1);
+        }
+    } else if (ch == FuriHalUartIdLPUART1) {
+        if (LL_LPUART_IsEnabled(LPUART1)) {
+            // Wait for transfer complete flag
+            while (!LL_LPUART_IsActiveFlag_TC(LPUART1));
+            LL_LPUART_Disable(LPUART1);
+            uint32_t uartclk = LL_RCC_GetLPUARTClockFreq(LL_RCC_GetLPUARTClockSource(LL_RCC_LPUART1_CLKSOURCE_PCLK1));
+            if (uartclk/baud > 4095) {
+                LL_LPUART_SetPrescaler(LPUART1, LL_LPUART_PRESCALER_DIV32);
+                LL_LPUART_SetBaudRate(LPUART1, uartclk, LL_LPUART_PRESCALER_DIV32, baud);
+            } else {
+                LL_LPUART_SetPrescaler(LPUART1, LL_LPUART_PRESCALER_DIV1);
+                LL_LPUART_SetBaudRate(LPUART1, uartclk, LL_LPUART_PRESCALER_DIV1, baud);
+            }
+            LL_LPUART_Enable(LPUART1);
+        }
+    }
+}
+
+void furi_hal_uart_deinit(FuriHalUartId ch) {
+    furi_hal_uart_set_irq_cb(ch, NULL);
+    if (ch == FuriHalUartIdUSART1) {
+        LL_USART_Disable(USART1);
+        hal_gpio_init(&gpio_usart_tx, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
+        hal_gpio_init(&gpio_usart_rx, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
+    } else if (ch == FuriHalUartIdLPUART1) {
+        LL_LPUART_Disable(LPUART1);
+        hal_gpio_init(&gpio_ext_pc0, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
+        hal_gpio_init(&gpio_ext_pc1, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
+    }
+}
+
+void furi_hal_uart_tx(FuriHalUartId ch, uint8_t* buffer, size_t buffer_size) {
+    if (ch == FuriHalUartIdUSART1) {
+        if (LL_USART_IsEnabled(USART1) == 0)
+            return;
+
+        while(buffer_size > 0) {
+            while (!LL_USART_IsActiveFlag_TXE(USART1));
+
+            LL_USART_TransmitData8(USART1, *buffer);
+            buffer++;
+            buffer_size--;
+        }
+
+    } else if (ch == FuriHalUartIdLPUART1) {
+        if (LL_LPUART_IsEnabled(LPUART1) == 0)
+            return;
+
+        while(buffer_size > 0) {
+            while (!LL_LPUART_IsActiveFlag_TXE(LPUART1));
+
+            LL_LPUART_TransmitData8(LPUART1, *buffer);
+    
+            buffer++;
+            buffer_size--;
+        }
+    }
+}
+
+void furi_hal_uart_set_irq_cb(FuriHalUartId ch, void (*cb)(UartIrqEvent ev, uint8_t data)) {
+    if (cb == NULL) {
+        if (ch == FuriHalUartIdUSART1)
+            NVIC_DisableIRQ(USART1_IRQn);
+        else if (ch == FuriHalUartIdLPUART1)
+            NVIC_DisableIRQ(LPUART1_IRQn);
+        irq_cb[ch] = cb;
+    } else {
+        irq_cb[ch] = cb;
+        if (ch == FuriHalUartIdUSART1)
+            NVIC_EnableIRQ(USART1_IRQn);
+        else if (ch == FuriHalUartIdLPUART1)
+            NVIC_EnableIRQ(LPUART1_IRQn);
+    }
+}
+
+void LPUART1_IRQHandler(void) {
+    if (LL_LPUART_IsActiveFlag_RXNE_RXFNE(LPUART1)) {
+        uint8_t data = LL_LPUART_ReceiveData8(LPUART1);
+        irq_cb[FuriHalUartIdLPUART1](UartIrqEventRXNE, data);
+    } else if (LL_LPUART_IsActiveFlag_IDLE(LPUART1)) {
+        irq_cb[FuriHalUartIdLPUART1](UartIrqEventIDLE, 0);
+        LL_LPUART_ClearFlag_IDLE(LPUART1);
+    }
+    //TODO: more events
+}
+
+void USART1_IRQHandler(void) {
+    if (LL_USART_IsActiveFlag_RXNE_RXFNE(USART1)) {
+        uint8_t data = LL_USART_ReceiveData8(USART1);
+        irq_cb[FuriHalUartIdUSART1](UartIrqEventRXNE, data);
+    } else if (LL_USART_IsActiveFlag_IDLE(USART1)) {
+        irq_cb[FuriHalUartIdUSART1](UartIrqEventIDLE, 0);
+        LL_USART_ClearFlag_IDLE(USART1);
+    }
+}

+ 29 - 0
firmware/targets/f7/furi-hal/furi-hal-uart.h

@@ -0,0 +1,29 @@
+#pragma once
+
+#include <stddef.h>
+#include <stdint.h>
+#include "furi-hal-console.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+    FuriHalUartIdUSART1,
+    FuriHalUartIdLPUART1,
+} FuriHalUartId;
+
+
+void furi_hal_uart_init(FuriHalUartId ch, uint32_t baud);
+
+void furi_hal_uart_deinit(FuriHalUartId ch);
+
+void furi_hal_uart_set_br(FuriHalUartId ch, uint32_t baud);
+
+void furi_hal_uart_tx(FuriHalUartId ch, uint8_t* buffer, size_t buffer_size);
+
+void furi_hal_uart_set_irq_cb(FuriHalUartId ch, void (*cb)(UartIrqEvent ev, uint8_t data));
+
+#ifdef __cplusplus
+}
+#endif

+ 5 - 5
firmware/targets/f7/furi-hal/furi-hal-usb-cdc.c

@@ -7,12 +7,12 @@
 #include "usb_cdc.h"
 
 #define CDC0_RXD_EP      0x01
-#define CDC0_TXD_EP      0x82
-#define CDC0_NTF_EP      0x83
+#define CDC0_TXD_EP      0x81
+#define CDC0_NTF_EP      0x82
 
-#define CDC1_RXD_EP      0x04
-#define CDC1_TXD_EP      0x85
-#define CDC1_NTF_EP      0x86
+#define CDC1_RXD_EP      0x03
+#define CDC1_TXD_EP      0x83
+#define CDC1_NTF_EP      0x84
 
 #define CDC_NTF_SZ      0x08
 

+ 1 - 1
firmware/targets/furi-hal-include/furi-hal.h

@@ -36,7 +36,7 @@ template <unsigned int N> struct STOP_EXTERNING_ME {};
 #include "furi-hal-usb.h"
 #include "furi-hal-usb-hid.h"
 #include "furi-hal-compress.h"
-#include "furi-hal-lpuart.h"
+#include "furi-hal-uart.h"
 
 /** Init furi-hal */
 void furi_hal_init();