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

USB-UART: New GUI (#826)

* USB-UART: new gui
* Furi: use furi_console for logging instead of printf.
* CDC: calling open/close callbacks on interface change
* fix vcp_tx block on disconnect
* USB mode set by struct pointer
* FuriHal: proper event sequence on vcp reconnect
* disable debug prints
* HAL: add context to UART IRQ's
* Context usage in UART IRQ and CDC callbacks
* USB-UART: geting rid of baudrate limitations
* FuriHal: remove struct pollutant in usb api.

Co-authored-by: あく <alleteam@gmail.com>
Co-authored-by: DrZlo13 <who.just.the.doctor@gmail.com>
Nikolay Minaylov 4 лет назад
Родитель
Сommit
efded63bcb
37 измененных файлов с 1132 добавлено и 665 удалено
  1. 2 2
      applications/debug_tools/bad_usb.c
  2. 2 2
      applications/debug_tools/usb_mouse.c
  3. 3 3
      applications/debug_tools/usb_test.c
  4. 20 3
      applications/gpio/gpio_app.c
  5. 7 9
      applications/gpio/gpio_app_i.h
  6. 10 0
      applications/gpio/gpio_custom_event.h
  7. 1 0
      applications/gpio/scenes/gpio_scene_config.h
  8. 17 18
      applications/gpio/scenes/gpio_scene_start.c
  9. 47 120
      applications/gpio/scenes/gpio_scene_usb_uart.c
  10. 140 0
      applications/gpio/scenes/gpio_scene_usb_uart_config.c
  11. 217 76
      applications/gpio/usb_uart_bridge.c
  12. 18 2
      applications/gpio/usb_uart_bridge.h
  13. 154 0
      applications/gpio/views/gpio_usb_uart.c
  14. 18 0
      applications/gpio/views/gpio_usb_uart.h
  15. 0 0
      assets/compiled/assets_icons.c
  16. 60 56
      assets/compiled/assets_icons.h
  17. BIN
      assets/icons/GPIO/ArrowDownEmpty_14x15.png
  18. BIN
      assets/icons/GPIO/ArrowDownFilled_14x15.png
  19. BIN
      assets/icons/GPIO/ArrowUpEmpty_14x15.png
  20. BIN
      assets/icons/GPIO/ArrowUpFilled_14x15.png
  21. 22 16
      core/furi/log.c
  22. 2 3
      core/furi/log.h
  23. 39 20
      firmware/targets/f6/furi-hal/furi-hal-usb-cdc.c
  24. 6 6
      firmware/targets/f6/furi-hal/furi-hal-usb-cdc_i.h
  25. 4 3
      firmware/targets/f6/furi-hal/furi-hal-usb-hid.c
  26. 27 38
      firmware/targets/f6/furi-hal/furi-hal-usb.c
  27. 0 15
      firmware/targets/f6/furi-hal/furi-hal-usb_i.h
  28. 108 89
      firmware/targets/f6/furi-hal/furi-hal-vcp.c
  29. 1 1
      firmware/targets/f6/furi-hal/furi-hal.c
  30. 39 20
      firmware/targets/f7/furi-hal/furi-hal-usb-cdc.c
  31. 6 6
      firmware/targets/f7/furi-hal/furi-hal-usb-cdc_i.h
  32. 4 3
      firmware/targets/f7/furi-hal/furi-hal-usb-hid.c
  33. 27 38
      firmware/targets/f7/furi-hal/furi-hal-usb.c
  34. 0 15
      firmware/targets/f7/furi-hal/furi-hal-usb_i.h
  35. 108 89
      firmware/targets/f7/furi-hal/furi-hal-vcp.c
  36. 1 1
      firmware/targets/f7/furi-hal/furi-hal.c
  37. 22 11
      firmware/targets/furi-hal-include/furi-hal-usb.h

+ 2 - 2
applications/debug_tools/bad_usb.c

@@ -285,8 +285,8 @@ int32_t bad_usb_app(void* p) {
     furi_check(app->event_queue);
     ViewPort* view_port = view_port_alloc();
 
-    UsbMode usb_mode_prev = furi_hal_usb_get_config();
-    furi_hal_usb_set_config(UsbModeHid);
+    UsbInterface* usb_mode_prev = furi_hal_usb_get_config();
+    furi_hal_usb_set_config(&usb_hid);
 
     view_port_draw_callback_set(view_port, bad_usb_render_callback, app);
     view_port_input_callback_set(view_port, bad_usb_input_callback, app->event_queue);

+ 2 - 2
applications/debug_tools/usb_mouse.c

@@ -41,8 +41,8 @@ int32_t usb_mouse_app(void* p) {
     furi_check(event_queue);
     ViewPort* view_port = view_port_alloc();
 
-    UsbMode usb_mode_prev = furi_hal_usb_get_config();
-    furi_hal_usb_set_config(UsbModeHid);
+    UsbInterface* usb_mode_prev = furi_hal_usb_get_config();
+    furi_hal_usb_set_config(&usb_hid);
 
     view_port_draw_callback_set(view_port, usb_mouse_render_callback, NULL);
     view_port_input_callback_set(view_port, usb_mouse_input_callback, event_queue);

+ 3 - 3
applications/debug_tools/usb_test.c

@@ -29,11 +29,11 @@ void usb_test_submenu_callback(void* context, uint32_t index) {
     } else if(index == UsbTestSubmenuIndexDisable) {
         furi_hal_usb_disable();
     } else if(index == UsbTestSubmenuIndexVcpSingle) {
-        furi_hal_usb_set_config(UsbModeVcpSingle);
+        furi_hal_usb_set_config(&usb_cdc_single);
     } else if(index == UsbTestSubmenuIndexVcpDual) {
-        furi_hal_usb_set_config(UsbModeVcpDual);
+        furi_hal_usb_set_config(&usb_cdc_dual);
     } else if(index == UsbTestSubmenuIndexHid) {
-        furi_hal_usb_set_config(UsbModeHid);
+        furi_hal_usb_set_config(&usb_hid);
     } else if(index == UsbTestSubmenuIndexHidU2F) {
         //furi_hal_usb_set_config(UsbModeU2F);
     }

+ 20 - 3
applications/gpio/gpio_app.c

@@ -15,11 +15,16 @@ static bool gpio_app_back_event_callback(void* context) {
     return scene_manager_handle_back_event(app->scene_manager);
 }
 
+static void gpio_app_tick_event_callback(void* context) {
+    furi_assert(context);
+    GpioApp* app = context;
+    scene_manager_handle_tick_event(app->scene_manager);
+}
+
 GpioApp* gpio_app_alloc() {
     GpioApp* app = furi_alloc(sizeof(GpioApp));
 
     app->gui = furi_record_open("gui");
-    app->notifications = furi_record_open("notification");
 
     app->view_dispatcher = view_dispatcher_alloc();
     app->scene_manager = scene_manager_alloc(&gpio_scene_handlers, app);
@@ -30,9 +35,13 @@ GpioApp* gpio_app_alloc() {
         app->view_dispatcher, gpio_app_custom_event_callback);
     view_dispatcher_set_navigation_event_callback(
         app->view_dispatcher, gpio_app_back_event_callback);
+    view_dispatcher_set_tick_event_callback(
+        app->view_dispatcher, gpio_app_tick_event_callback, 100);
 
     view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
 
+    app->notifications = furi_record_open("notification");
+
     app->var_item_list = variable_item_list_alloc();
     view_dispatcher_add_view(
         app->view_dispatcher,
@@ -42,8 +51,14 @@ GpioApp* gpio_app_alloc() {
     view_dispatcher_add_view(
         app->view_dispatcher, GpioAppViewGpioTest, gpio_test_get_view(app->gpio_test));
 
+    app->gpio_usb_uart = gpio_usb_uart_alloc();
     view_dispatcher_add_view(
-        app->view_dispatcher, GpioAppViewUsbUart, variable_item_list_get_view(app->var_item_list));
+        app->view_dispatcher, GpioAppViewUsbUart, gpio_usb_uart_get_view(app->gpio_usb_uart));
+
+    view_dispatcher_add_view(
+        app->view_dispatcher,
+        GpioAppViewUsbUartCfg,
+        variable_item_list_get_view(app->var_item_list));
 
     scene_manager_next_scene(app->scene_manager, GpioSceneStart);
 
@@ -55,10 +70,12 @@ void gpio_app_free(GpioApp* app) {
 
     // Views
     view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewVarItemList);
-    variable_item_list_free(app->var_item_list);
     view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewGpioTest);
     view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewUsbUart);
+    view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewUsbUartCfg);
+    variable_item_list_free(app->var_item_list);
     gpio_test_free(app->gpio_test);
+    gpio_usb_uart_free(app->gpio_usb_uart);
 
     // View dispatcher
     view_dispatcher_free(app->view_dispatcher);

+ 7 - 9
applications/gpio/gpio_app_i.h

@@ -3,6 +3,8 @@
 #include "gpio_app.h"
 #include "gpio_item.h"
 #include "scenes/gpio_scene.h"
+#include "gpio_custom_event.h"
+#include "usb_uart_bridge.h"
 
 #include <gui/gui.h>
 #include <gui/view_dispatcher.h>
@@ -11,27 +13,23 @@
 #include <notification/notification-messages.h>
 #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)
+#include "views/gpio_usb_uart.h"
 
 struct GpioApp {
     Gui* gui;
+    NotificationApp* notifications;
     ViewDispatcher* view_dispatcher;
     SceneManager* scene_manager;
-    NotificationApp* notifications;
 
     VariableItemList* var_item_list;
     GpioTest* gpio_test;
+    GpioUsbUart* gpio_usb_uart;
+    UsbUartBridge* usb_uart_bridge;
 };
 
 typedef enum {
     GpioAppViewVarItemList,
     GpioAppViewGpioTest,
     GpioAppViewUsbUart,
+    GpioAppViewUsbUartCfg,
 } GpioAppView;

+ 10 - 0
applications/gpio/gpio_custom_event.h

@@ -0,0 +1,10 @@
+#pragma once
+
+typedef enum {
+    GpioStartEventOtgOff = 0,
+    GpioStartEventOtgOn,
+    GpioStartEventManualConrol,
+    GpioStartEventUsbUart,
+
+    GpioUsbUartEventConfig,
+} GpioCustomEvent;

+ 1 - 0
applications/gpio/scenes/gpio_scene_config.h

@@ -1,3 +1,4 @@
 ADD_SCENE(gpio, start, Start)
 ADD_SCENE(gpio, test, Test)
 ADD_SCENE(gpio, usb_uart, UsbUart)
+ADD_SCENE(gpio, usb_uart_cfg, UsbUartCfg)

+ 17 - 18
applications/gpio/scenes/gpio_scene_start.c

@@ -2,9 +2,9 @@
 #include "furi-hal-power.h"
 
 enum GpioItem {
-    GpioItemOtg,
-    GpioItemTest,
     GpioItemUsbUart,
+    GpioItemTest,
+    GpioItemOtg,
 };
 
 enum GpioOtg {
@@ -22,11 +22,9 @@ static void gpio_scene_start_var_list_enter_callback(void* context, uint32_t ind
     furi_assert(context);
     GpioApp* app = context;
     if(index == GpioItemTest) {
-        view_dispatcher_send_custom_event(
-            app->view_dispatcher, GPIO_SCENE_START_CUSTOM_EVENT_TEST);
+        view_dispatcher_send_custom_event(app->view_dispatcher, GpioStartEventManualConrol);
     } else if(index == GpioItemUsbUart) {
-        view_dispatcher_send_custom_event(
-            app->view_dispatcher, GPIO_SCENE_START_CUSTOM_EVENT_USB_UART);
+        view_dispatcher_send_custom_event(app->view_dispatcher, GpioStartEventUsbUart);
     }
 }
 
@@ -36,11 +34,9 @@ static void gpio_scene_start_var_list_change_callback(VariableItem* item) {
 
     variable_item_set_current_value_text(item, gpio_otg_text[index]);
     if(index == GpioOtgOff) {
-        view_dispatcher_send_custom_event(
-            app->view_dispatcher, GPIO_SCENE_START_CUSTOM_EVENT_OTG_OFF);
+        view_dispatcher_send_custom_event(app->view_dispatcher, GpioStartEventOtgOff);
     } else if(index == GpioOtgOn) {
-        view_dispatcher_send_custom_event(
-            app->view_dispatcher, GPIO_SCENE_START_CUSTOM_EVENT_OTG_ON);
+        view_dispatcher_send_custom_event(app->view_dispatcher, GpioStartEventOtgOn);
     }
 }
 
@@ -51,6 +47,11 @@ void gpio_scene_start_on_enter(void* context) {
     VariableItem* item;
     variable_item_list_set_enter_callback(
         var_item_list, gpio_scene_start_var_list_enter_callback, app);
+
+    variable_item_list_add(var_item_list, "USB-UART bridge", 0, NULL, NULL);
+
+    variable_item_list_add(var_item_list, "GPIO manual control", 0, NULL, NULL);
+
     item = variable_item_list_add(
         var_item_list,
         "5V on GPIO",
@@ -64,8 +65,6 @@ void gpio_scene_start_on_enter(void* context) {
         variable_item_set_current_value_index(item, GpioOtgOff);
         variable_item_set_current_value_text(item, gpio_otg_text[GpioOtgOff]);
     }
-    variable_item_list_add(var_item_list, "GPIO tester", 0, NULL, NULL);
-    variable_item_list_add(var_item_list, "USB-UART bridge", 0, NULL, NULL);
 
     variable_item_list_set_selected_item(
         var_item_list, scene_manager_get_scene_state(app->scene_manager, GpioSceneStart));
@@ -78,15 +77,15 @@ bool gpio_scene_start_on_event(void* context, SceneManagerEvent event) {
     bool consumed = false;
 
     if(event.type == SceneManagerEventTypeCustom) {
-        if(event.event == GPIO_SCENE_START_CUSTOM_EVENT_OTG_ON) {
+        if(event.event == GpioStartEventOtgOn) {
             furi_hal_power_enable_otg();
-        } else if(event.event == GPIO_SCENE_START_CUSTOM_EVENT_OTG_OFF) {
+        } else if(event.event == GpioStartEventOtgOff) {
             furi_hal_power_disable_otg();
-        } else if(event.event == GPIO_SCENE_START_CUSTOM_EVENT_TEST) {
-            scene_manager_set_scene_state(app->scene_manager, GpioSceneStart, 1);
+        } else if(event.event == GpioStartEventManualConrol) {
+            scene_manager_set_scene_state(app->scene_manager, GpioSceneStart, GpioItemTest);
             scene_manager_next_scene(app->scene_manager, GpioSceneTest);
-        } else if(event.event == GPIO_SCENE_START_CUSTOM_EVENT_USB_UART) {
-            scene_manager_set_scene_state(app->scene_manager, GpioSceneStart, 2);
+        } else if(event.event == GpioStartEventUsbUart) {
+            scene_manager_set_scene_state(app->scene_manager, GpioSceneStart, GpioItemUsbUart);
             scene_manager_next_scene(app->scene_manager, GpioSceneUsbUart);
         }
         consumed = true;

+ 47 - 120
applications/gpio/scenes/gpio_scene_usb_uart.c

@@ -1,138 +1,65 @@
-#include "../usb_uart_bridge.h"
 #include "../gpio_app_i.h"
-#include "furi-hal.h"
-
-typedef enum {
-    UsbUartLineIndexVcp,
-    UsbUartLineIndexUart,
-    UsbUartLineIndexBaudrate,
-    UsbUartLineIndexEnable,
-    UsbUartLineIndexDisable,
-} LineIndex;
-
-static UsbUartConfig* cfg_set;
-
-static const char* vcp_ch[] = {"0 (CLI)", "1"};
-static const char* uart_ch[] = {"USART1", "LPUART1"};
-static const char* baudrate_mode[] = {"Host"};
-static const uint32_t baudrate_list[] = {
-    2400,
-    9600,
-    19200,
-    38400,
-    57600,
-    115200,
-    230400,
-    460800,
-    921600,
-};
-
-bool gpio_scene_usb_uart_on_event(void* context, SceneManagerEvent event) {
-    //GpioApp* app = context;
-    bool consumed = false;
-
-    if(event.type == SceneManagerEventTypeCustom) {
-        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;
-    }
-    return consumed;
-}
-
-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]);
-
-    cfg_set->vcp_ch = index;
-}
-
-static void line_port_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, 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);
+#include "../usb_uart_bridge.h"
 
-    char br_text[8];
+typedef struct {
+    UsbUartConfig cfg;
+    UsbUartState state;
+} SceneUsbUartBridge;
 
-    if(index > 0) {
-        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]);
-        cfg_set->baudrate = 0;
-    }
-}
+static SceneUsbUartBridge* scene_usb_uart;
 
-static void gpio_scene_usb_uart_enter_callback(void* context, uint32_t index) {
+void gpio_scene_usb_uart_callback(GpioCustomEvent event, void* context) {
     furi_assert(context);
     GpioApp* app = context;
-    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);
+    view_dispatcher_send_custom_event(app->view_dispatcher, event);
 }
 
 void gpio_scene_usb_uart_on_enter(void* context) {
     GpioApp* app = context;
-    VariableItemList* var_item_list = app->var_item_list;
-
-    cfg_set = furi_alloc(sizeof(UsbUartConfig));
-
-    VariableItem* item;
-
-    variable_item_list_set_enter_callback(var_item_list, gpio_scene_usb_uart_enter_callback, app);
-
-    item = variable_item_list_add(var_item_list, "VCP Channel", 2, line_vcp_cb, app);
-    variable_item_set_current_value_index(item, 0);
-    variable_item_set_current_value_text(item, vcp_ch[0]);
-
-    item = variable_item_list_add(var_item_list, "UART Port", 2, line_port_cb, app);
-    variable_item_set_current_value_index(item, 0);
-    variable_item_set_current_value_text(item, uart_ch[0]);
-
-    item = variable_item_list_add(
-        var_item_list,
-        "Baudrate",
-        sizeof(baudrate_list) / sizeof(baudrate_list[0]) + 1,
-        line_baudrate_cb,
-        app);
-    variable_item_set_current_value_index(item, 0);
-    variable_item_set_current_value_text(item, baudrate_mode[0]);
-
-    item = variable_item_list_add(var_item_list, "Enable", 0, NULL, NULL);
-    item = variable_item_list_add(var_item_list, "Disable", 0, NULL, NULL);
+    uint32_t prev_state = scene_manager_get_scene_state(app->scene_manager, GpioAppViewUsbUart);
+    if(prev_state == 0) {
+        scene_usb_uart = furi_alloc(sizeof(SceneUsbUartBridge));
+        scene_usb_uart->cfg.vcp_ch = 0; // TODO: settings load
+        scene_usb_uart->cfg.uart_ch = 0;
+        scene_usb_uart->cfg.flow_pins = 0;
+        scene_usb_uart->cfg.baudrate_mode = 0;
+        scene_usb_uart->cfg.baudrate = 0;
+        app->usb_uart_bridge = usb_uart_enable(&scene_usb_uart->cfg);
+    }
 
-    variable_item_list_set_selected_item(
-        var_item_list, scene_manager_get_scene_state(app->scene_manager, GpioSceneUsbUart));
+    usb_uart_get_config(app->usb_uart_bridge, &scene_usb_uart->cfg);
+    usb_uart_get_state(app->usb_uart_bridge, &scene_usb_uart->state);
 
+    gpio_usb_uart_set_callback(app->gpio_usb_uart, gpio_scene_usb_uart_callback, app);
+    scene_manager_set_scene_state(app->scene_manager, GpioAppViewUsbUart, 0);
     view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewUsbUart);
 }
 
+bool gpio_scene_usb_uart_on_event(void* context, SceneManagerEvent event) {
+    GpioApp* app = context;
+    if(event.type == SceneManagerEventTypeCustom) {
+        scene_manager_set_scene_state(app->scene_manager, GpioAppViewUsbUart, 1);
+        scene_manager_next_scene(app->scene_manager, GpioAppViewUsbUartCfg);
+        return true;
+    } else if(event.type == SceneManagerEventTypeTick) {
+        uint32_t tx_cnt_last = scene_usb_uart->state.tx_cnt;
+        uint32_t rx_cnt_last = scene_usb_uart->state.rx_cnt;
+        usb_uart_get_state(app->usb_uart_bridge, &scene_usb_uart->state);
+        gpio_usb_uart_update_state(
+            app->gpio_usb_uart, &scene_usb_uart->cfg, &scene_usb_uart->state);
+        if(tx_cnt_last != scene_usb_uart->state.tx_cnt)
+            notification_message(app->notifications, &sequence_blink_blue_10);
+        if(rx_cnt_last != scene_usb_uart->state.rx_cnt)
+            notification_message(app->notifications, &sequence_blink_green_10);
+    }
+    return false;
+}
+
 void gpio_scene_usb_uart_on_exit(void* context) {
     GpioApp* app = context;
-    usb_uart_disable();
-    scene_manager_set_scene_state(
-        app->scene_manager,
-        GpioSceneUsbUart,
-        variable_item_list_get_selected_item_index(app->var_item_list));
-    variable_item_list_clean(app->var_item_list);
-    free(cfg_set);
+    uint32_t prev_state = scene_manager_get_scene_state(app->scene_manager, GpioAppViewUsbUart);
+    if(prev_state == 0) {
+        usb_uart_disable(app->usb_uart_bridge);
+        free(scene_usb_uart);
+    }
 }

+ 140 - 0
applications/gpio/scenes/gpio_scene_usb_uart_config.c

@@ -0,0 +1,140 @@
+#include "../usb_uart_bridge.h"
+#include "../gpio_app_i.h"
+#include "furi-hal.h"
+
+typedef enum {
+    UsbUartLineIndexVcp,
+    UsbUartLineIndexBaudrate,
+    UsbUartLineIndexUart,
+    UsbUartLineIndexFlow,
+} LineIndex;
+
+static UsbUartConfig* cfg_set;
+
+static const char* vcp_ch[] = {"0 (CLI)", "1"};
+static const char* uart_ch[] = {"13,14", "15,16"};
+static const char* flow_pins[] = {"None", "2,3", "6,7"};
+static const char* baudrate_mode[] = {"Host"};
+static const uint32_t baudrate_list[] = {
+    2400,
+    9600,
+    19200,
+    38400,
+    57600,
+    115200,
+    230400,
+    460800,
+    921600,
+};
+
+bool gpio_scene_usb_uart_cfg_on_event(void* context, SceneManagerEvent event) {
+    //GpioApp* app = context;
+    bool consumed = false;
+
+    if(event.type == SceneManagerEventTypeCustom) {
+    }
+    return consumed;
+}
+
+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]);
+
+    cfg_set->vcp_ch = index;
+    usb_uart_set_config(app->usb_uart_bridge, cfg_set);
+}
+
+static void line_port_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, uart_ch[index]);
+
+    if(index == 0)
+        cfg_set->uart_ch = FuriHalUartIdUSART1;
+    else if(index == 1)
+        cfg_set->uart_ch = FuriHalUartIdLPUART1;
+    usb_uart_set_config(app->usb_uart_bridge, cfg_set);
+}
+
+static void line_flow_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, flow_pins[index]);
+
+    cfg_set->flow_pins = index;
+    usb_uart_set_config(app->usb_uart_bridge, cfg_set);
+}
+
+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(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]);
+        cfg_set->baudrate = 0;
+    }
+    cfg_set->baudrate_mode = index;
+    usb_uart_set_config(app->usb_uart_bridge, cfg_set);
+}
+
+void gpio_scene_usb_uart_cfg_on_enter(void* context) {
+    GpioApp* app = context;
+    VariableItemList* var_item_list = app->var_item_list;
+
+    cfg_set = furi_alloc(sizeof(UsbUartConfig));
+    usb_uart_get_config(app->usb_uart_bridge, cfg_set);
+
+    VariableItem* item;
+    char br_text[8];
+
+    item = variable_item_list_add(var_item_list, "USB Channel", 2, line_vcp_cb, app);
+    variable_item_set_current_value_index(item, cfg_set->vcp_ch);
+    variable_item_set_current_value_text(item, vcp_ch[cfg_set->vcp_ch]);
+
+    item = variable_item_list_add(
+        var_item_list,
+        "Baudrate",
+        sizeof(baudrate_list) / sizeof(baudrate_list[0]) + 1,
+        line_baudrate_cb,
+        app);
+    variable_item_set_current_value_index(item, cfg_set->baudrate_mode);
+    if(cfg_set->baudrate_mode > 0) {
+        snprintf(br_text, 7, "%lu", baudrate_list[cfg_set->baudrate_mode - 1]);
+        variable_item_set_current_value_text(item, br_text);
+    } else {
+        variable_item_set_current_value_text(item, baudrate_mode[cfg_set->baudrate_mode]);
+    }
+
+    item = variable_item_list_add(var_item_list, "UART Pins", 2, line_port_cb, app);
+    variable_item_set_current_value_index(item, cfg_set->uart_ch);
+    variable_item_set_current_value_text(item, uart_ch[cfg_set->uart_ch]);
+
+    item = variable_item_list_add(var_item_list, "RTS/DTR Pins", 3, line_flow_cb, app);
+    variable_item_set_current_value_index(item, cfg_set->flow_pins);
+    variable_item_set_current_value_text(item, flow_pins[cfg_set->flow_pins]);
+
+    variable_item_list_set_selected_item(
+        var_item_list, scene_manager_get_scene_state(app->scene_manager, GpioAppViewUsbUartCfg));
+
+    view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewUsbUartCfg);
+}
+
+void gpio_scene_usb_uart_cfg_on_exit(void* context) {
+    GpioApp* app = context;
+    scene_manager_set_scene_state(
+        app->scene_manager,
+        GpioAppViewUsbUartCfg,
+        variable_item_list_get_selected_item_index(app->var_item_list));
+    variable_item_list_clean(app->var_item_list);
+    free(cfg_set);
+}

+ 217 - 76
applications/gpio/usb_uart_bridge.c

@@ -7,6 +7,14 @@
 #define USB_CDC_PKT_LEN CDC_DATA_SZ
 #define USB_UART_RX_BUF_SIZE (USB_CDC_PKT_LEN * 5)
 
+#define USB_CDC_BIT_DTR (1 << 0)
+#define USB_CDC_BIT_RTS (1 << 1)
+
+static const GpioPin* flow_pins[][2] = {
+    {&gpio_ext_pa7, &gpio_ext_pa6}, // 2, 3
+    {&gpio_ext_pb2, &gpio_ext_pc3}, // 6, 7
+};
+
 typedef enum {
     WorkerEvtReserved = (1 << 0), // Reserved for StreamBuffer internal event
     WorkerEvtStop = (1 << 1),
@@ -14,13 +22,22 @@ typedef enum {
 
     WorkerEvtTxStop = (1 << 3),
     WorkerEvtCdcRx = (1 << 4),
+
+    WorkerEvtCfgChange = (1 << 5),
+
+    WorkerEvtLineCfgSet = (1 << 6),
+    WorkerEvtCtrlLineSet = (1 << 7),
+
 } WorkerEvtFlags;
 
-#define WORKER_ALL_RX_EVENTS (WorkerEvtStop | WorkerEvtRxDone)
+#define WORKER_ALL_RX_EVENTS                                                      \
+    (WorkerEvtStop | WorkerEvtRxDone | WorkerEvtCfgChange | WorkerEvtLineCfgSet | \
+     WorkerEvtCtrlLineSet)
 #define WORKER_ALL_TX_EVENTS (WorkerEvtTxStop | WorkerEvtCdcRx)
 
-typedef struct {
+struct UsbUartBridge {
     UsbUartConfig cfg;
+    UsbUartConfig cfg_new;
 
     FuriThread* thread;
     FuriThread* tx_thread;
@@ -31,21 +48,18 @@ typedef struct {
 
     osSemaphoreId_t tx_sem;
 
-    uint8_t rx_buf[USB_CDC_PKT_LEN];
-
-    bool buf_full;
-} UsbUartParams;
+    UsbUartState st;
 
-static UsbUartParams* usb_uart;
-static bool running = false;
+    uint8_t rx_buf[USB_CDC_PKT_LEN];
+};
 
-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 void vcp_on_cdc_tx_complete(void* context);
+static void vcp_on_cdc_rx(void* context);
+static void vcp_state_callback(void* context, uint8_t state);
+static void vcp_on_cdc_control_line(void* context, uint8_t state);
+static void vcp_on_line_config(void* context, struct usb_cdc_line_coding* config);
 
-static CdcCallbacks cdc_cb = {
+static const CdcCallbacks cdc_cb = {
     vcp_on_cdc_tx_complete,
     vcp_on_cdc_rx,
     vcp_state_callback,
@@ -58,17 +72,76 @@ static CdcCallbacks cdc_cb = {
 static int32_t usb_uart_tx_thread(void* context);
 
 static void usb_uart_on_irq_cb(UartIrqEvent ev, uint8_t data, void* context) {
+    UsbUartBridge* usb_uart = (UsbUartBridge*)context;
     BaseType_t xHigherPriorityTaskWoken = pdFALSE;
 
     if(ev == UartIrqEventRXNE) {
         xStreamBufferSendFromISR(usb_uart->rx_stream, &data, 1, &xHigherPriorityTaskWoken);
-        portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
         osThreadFlagsSet(furi_thread_get_thread_id(usb_uart->thread), WorkerEvtRxDone);
+        portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
+    }
+}
+
+static void usb_uart_vcp_init(UsbUartBridge* usb_uart, uint8_t vcp_ch) {
+    if(vcp_ch == 0) {
+        furi_hal_usb_set_config(&usb_cdc_single);
+        furi_hal_vcp_disable();
+    } else {
+        furi_hal_usb_set_config(&usb_cdc_dual);
+    }
+    furi_hal_cdc_set_callbacks(vcp_ch, (CdcCallbacks*)&cdc_cb, usb_uart);
+}
+
+static void usb_uart_vcp_deinit(UsbUartBridge* usb_uart, uint8_t vcp_ch) {
+    furi_hal_cdc_set_callbacks(vcp_ch, NULL, NULL);
+    if(vcp_ch == 0) furi_hal_vcp_enable();
+}
+
+static void usb_uart_serial_init(UsbUartBridge* usb_uart, uint8_t uart_ch) {
+    if(uart_ch == FuriHalUartIdUSART1) {
+        furi_hal_console_disable();
+    } else if(uart_ch == FuriHalUartIdLPUART1) {
+        furi_hal_uart_init(uart_ch, 115200);
+    }
+    furi_hal_uart_set_irq_cb(uart_ch, usb_uart_on_irq_cb, usb_uart);
+}
+
+static void usb_uart_serial_deinit(UsbUartBridge* usb_uart, uint8_t uart_ch) {
+    furi_hal_uart_set_irq_cb(uart_ch, NULL, NULL);
+    if(uart_ch == FuriHalUartIdUSART1)
+        furi_hal_console_enable();
+    else if(uart_ch == FuriHalUartIdLPUART1)
+        furi_hal_uart_deinit(uart_ch);
+}
+
+static void usb_uart_set_baudrate(UsbUartBridge* usb_uart, uint32_t baudrate) {
+    if(baudrate != 0) {
+        furi_hal_uart_set_br(usb_uart->cfg.uart_ch, baudrate);
+        usb_uart->st.baudrate_cur = baudrate;
+    } else {
+        struct usb_cdc_line_coding* line_cfg =
+            furi_hal_cdc_get_port_settings(usb_uart->cfg.vcp_ch);
+        if(line_cfg->dwDTERate > 0) {
+            furi_hal_uart_set_br(usb_uart->cfg.uart_ch, line_cfg->dwDTERate);
+            usb_uart->st.baudrate_cur = line_cfg->dwDTERate;
+        }
+    }
+}
+
+static void usb_uart_update_ctrl_lines(UsbUartBridge* usb_uart) {
+    if(usb_uart->cfg.flow_pins != 0) {
+        furi_assert((usb_uart->cfg.flow_pins - 1) < (sizeof(flow_pins) / sizeof(flow_pins[0])));
+        uint8_t state = furi_hal_cdc_get_ctrl_line_state(usb_uart->cfg.vcp_ch);
+
+        hal_gpio_write(flow_pins[usb_uart->cfg.flow_pins - 1][0], !(state & USB_CDC_BIT_RTS));
+        hal_gpio_write(flow_pins[usb_uart->cfg.flow_pins - 1][1], !(state & USB_CDC_BIT_DTR));
     }
 }
 
 static int32_t usb_uart_worker(void* context) {
-    memcpy(&usb_uart->cfg, context, sizeof(UsbUartConfig));
+    UsbUartBridge* usb_uart = (UsbUartBridge*)context;
+
+    memcpy(&usb_uart->cfg, &usb_uart->cfg_new, sizeof(UsbUartConfig));
 
     usb_uart->rx_stream = xStreamBufferCreate(USB_UART_RX_BUF_SIZE, 1);
 
@@ -78,32 +151,21 @@ static int32_t usb_uart_worker(void* context) {
     usb_uart->tx_thread = furi_thread_alloc();
     furi_thread_set_name(usb_uart->tx_thread, "UsbUartTxWorker");
     furi_thread_set_stack_size(usb_uart->tx_thread, 512);
-    furi_thread_set_context(usb_uart->tx_thread, NULL);
+    furi_thread_set_context(usb_uart->tx_thread, usb_uart);
     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();
-    } else {
-        furi_hal_usb_set_config(UsbModeVcpDual);
-    }
-    osThreadFlagsSet(furi_thread_get_thread_id(usb_uart->tx_thread), WorkerEvtCdcRx);
-
-    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, NULL);
+    UsbInterface* usb_mode_prev = furi_hal_usb_get_config();
+    usb_uart_vcp_init(usb_uart, usb_uart->cfg.vcp_ch);
+    usb_uart_serial_init(usb_uart, usb_uart->cfg.uart_ch);
+    usb_uart_set_baudrate(usb_uart, usb_uart->cfg.baudrate);
+    if(usb_uart->cfg.flow_pins != 0) {
+        furi_assert((usb_uart->cfg.flow_pins - 1) < (sizeof(flow_pins) / sizeof(flow_pins[0])));
+        hal_gpio_init_simple(flow_pins[usb_uart->cfg.flow_pins - 1][0], GpioModeOutputPushPull);
+        hal_gpio_init_simple(flow_pins[usb_uart->cfg.flow_pins - 1][1], GpioModeOutputPushPull);
+        usb_uart_update_ctrl_lines(usb_uart);
     }
 
-    furi_hal_uart_set_irq_cb(usb_uart->cfg.uart_ch, usb_uart_on_irq_cb, NULL);
-    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);
+    osThreadFlagsSet(furi_thread_get_thread_id(usb_uart->tx_thread), WorkerEvtCdcRx);
 
     furi_thread_start(usb_uart->tx_thread);
 
@@ -116,6 +178,7 @@ static int32_t usb_uart_worker(void* context) {
                 xStreamBufferReceive(usb_uart->rx_stream, usb_uart->rx_buf, USB_CDC_PKT_LEN, 0);
             if(len > 0) {
                 if(osSemaphoreAcquire(usb_uart->tx_sem, 100) == osOK) {
+                    usb_uart->st.rx_cnt += len;
                     furi_check(osMutexAcquire(usb_uart->usb_mutex, osWaitForever) == osOK);
                     furi_hal_cdc_send(usb_uart->cfg.vcp_ch, usb_uart->rx_buf, len);
                     furi_check(osMutexRelease(usb_uart->usb_mutex) == osOK);
@@ -124,21 +187,76 @@ static int32_t usb_uart_worker(void* context) {
                 }
             }
         }
+        if(events & WorkerEvtCfgChange) {
+            if(usb_uart->cfg.vcp_ch != usb_uart->cfg_new.vcp_ch) {
+                osThreadFlagsSet(furi_thread_get_thread_id(usb_uart->tx_thread), WorkerEvtTxStop);
+                furi_thread_join(usb_uart->tx_thread);
+
+                usb_uart_vcp_deinit(usb_uart, usb_uart->cfg.vcp_ch);
+                usb_uart_vcp_init(usb_uart, usb_uart->cfg_new.vcp_ch);
+
+                usb_uart->cfg.vcp_ch = usb_uart->cfg_new.vcp_ch;
+                furi_thread_start(usb_uart->tx_thread);
+                events |= WorkerEvtCtrlLineSet;
+                events |= WorkerEvtLineCfgSet;
+            }
+            if(usb_uart->cfg.uart_ch != usb_uart->cfg_new.uart_ch) {
+                osThreadFlagsSet(furi_thread_get_thread_id(usb_uart->tx_thread), WorkerEvtTxStop);
+                furi_thread_join(usb_uart->tx_thread);
+
+                usb_uart_serial_deinit(usb_uart, usb_uart->cfg.uart_ch);
+                usb_uart_serial_init(usb_uart, usb_uart->cfg_new.uart_ch);
+
+                usb_uart->cfg.uart_ch = usb_uart->cfg_new.uart_ch;
+                usb_uart_set_baudrate(usb_uart, usb_uart->cfg.baudrate);
+
+                furi_thread_start(usb_uart->tx_thread);
+            }
+            if(usb_uart->cfg.baudrate != usb_uart->cfg_new.baudrate) {
+                usb_uart_set_baudrate(usb_uart, usb_uart->cfg_new.baudrate);
+                usb_uart->cfg.baudrate = usb_uart->cfg_new.baudrate;
+            }
+            if(usb_uart->cfg.flow_pins != usb_uart->cfg_new.flow_pins) {
+                if(usb_uart->cfg.flow_pins != 0) {
+                    hal_gpio_init_simple(
+                        flow_pins[usb_uart->cfg.flow_pins - 1][0], GpioModeAnalog);
+                    hal_gpio_init_simple(
+                        flow_pins[usb_uart->cfg.flow_pins - 1][1], GpioModeAnalog);
+                }
+                if(usb_uart->cfg_new.flow_pins != 0) {
+                    furi_assert(
+                        (usb_uart->cfg_new.flow_pins - 1) <
+                        (sizeof(flow_pins) / sizeof(flow_pins[0])));
+                    hal_gpio_init_simple(
+                        flow_pins[usb_uart->cfg_new.flow_pins - 1][0], GpioModeOutputPushPull);
+                    hal_gpio_init_simple(
+                        flow_pins[usb_uart->cfg_new.flow_pins - 1][1], GpioModeOutputPushPull);
+                }
+                usb_uart->cfg.flow_pins = usb_uart->cfg_new.flow_pins;
+                events |= WorkerEvtCtrlLineSet;
+            }
+        }
+        if(events & WorkerEvtLineCfgSet) {
+            if(usb_uart->cfg.baudrate == 0)
+                usb_uart_set_baudrate(usb_uart, usb_uart->cfg.baudrate);
+        }
+        if(events & WorkerEvtCtrlLineSet) {
+            usb_uart_update_ctrl_lines(usb_uart);
+        }
+    }
+
+    usb_uart_vcp_deinit(usb_uart, usb_uart->cfg.vcp_ch);
+    usb_uart_serial_deinit(usb_uart, usb_uart->cfg.uart_ch);
+    furi_hal_usb_set_config(usb_mode_prev);
+    if(usb_uart->cfg.flow_pins != 0) {
+        hal_gpio_init_simple(flow_pins[usb_uart->cfg.flow_pins - 1][0], GpioModeAnalog);
+        hal_gpio_init_simple(flow_pins[usb_uart->cfg.flow_pins - 1][1], GpioModeAnalog);
     }
 
     osThreadFlagsSet(furi_thread_get_thread_id(usb_uart->tx_thread), 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);
     osMutexDelete(usb_uart->usb_mutex);
     osSemaphoreDelete(usb_uart->tx_sem);
@@ -147,6 +265,8 @@ static int32_t usb_uart_worker(void* context) {
 }
 
 static int32_t usb_uart_tx_thread(void* context) {
+    UsbUartBridge* usb_uart = (UsbUartBridge*)context;
+
     uint8_t data[USB_CDC_PKT_LEN];
     while(1) {
         uint32_t events = osThreadFlagsWait(WORKER_ALL_TX_EVENTS, osFlagsWaitAny, osWaitForever);
@@ -154,11 +274,12 @@ static int32_t usb_uart_tx_thread(void* context) {
         if(events & WorkerEvtTxStop) break;
         if(events & WorkerEvtCdcRx) {
             furi_check(osMutexAcquire(usb_uart->usb_mutex, osWaitForever) == osOK);
-            int32_t size = furi_hal_cdc_receive(usb_uart->cfg.vcp_ch, data, USB_CDC_PKT_LEN);
+            size_t len = furi_hal_cdc_receive(usb_uart->cfg.vcp_ch, data, USB_CDC_PKT_LEN);
             furi_check(osMutexRelease(usb_uart->usb_mutex) == osOK);
 
-            if(size > 0) {
-                furi_hal_uart_tx(usb_uart->cfg.uart_ch, data, size);
+            if(len > 0) {
+                usb_uart->st.tx_cnt += len;
+                furi_hal_uart_tx(usb_uart->cfg.uart_ch, data, len);
             }
         }
     }
@@ -167,46 +288,66 @@ static int32_t usb_uart_tx_thread(void* context) {
 
 /* VCP callbacks */
 
-static void vcp_on_cdc_tx_complete() {
+static void vcp_on_cdc_tx_complete(void* context) {
+    UsbUartBridge* usb_uart = (UsbUartBridge*)context;
     osSemaphoreRelease(usb_uart->tx_sem);
 }
 
-static void vcp_on_cdc_rx() {
+static void vcp_on_cdc_rx(void* context) {
+    UsbUartBridge* usb_uart = (UsbUartBridge*)context;
     osThreadFlagsSet(furi_thread_get_thread_id(usb_uart->tx_thread), WorkerEvtCdcRx);
 }
 
-static void vcp_state_callback(uint8_t state) {
+static void vcp_state_callback(void* context, uint8_t state) {
 }
 
-static void vcp_on_cdc_control_line(uint8_t state) {
+static void vcp_on_cdc_control_line(void* context, uint8_t state) {
+    UsbUartBridge* usb_uart = (UsbUartBridge*)context;
+    osThreadFlagsSet(furi_thread_get_thread_id(usb_uart->thread), WorkerEvtCtrlLineSet);
 }
 
-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);
+static void vcp_on_line_config(void* context, struct usb_cdc_line_coding* config) {
+    UsbUartBridge* usb_uart = (UsbUartBridge*)context;
+    osThreadFlagsSet(furi_thread_get_thread_id(usb_uart->thread), WorkerEvtLineCfgSet);
 }
 
-void usb_uart_enable(UsbUartConfig* cfg) {
-    if(running == false) {
-        running = true;
-        usb_uart = furi_alloc(sizeof(UsbUartParams));
+UsbUartBridge* usb_uart_enable(UsbUartConfig* cfg) {
+    UsbUartBridge* usb_uart = furi_alloc(sizeof(UsbUartBridge));
 
-        usb_uart->thread = furi_thread_alloc();
-        furi_thread_set_name(usb_uart->thread, "UsbUartWorker");
-        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);
+    memcpy(&(usb_uart->cfg_new), cfg, sizeof(UsbUartConfig));
 
-        furi_thread_start(usb_uart->thread);
-    }
+    usb_uart->thread = furi_thread_alloc();
+    furi_thread_set_name(usb_uart->thread, "UsbUartWorker");
+    furi_thread_set_stack_size(usb_uart->thread, 1024);
+    furi_thread_set_context(usb_uart->thread, usb_uart);
+    furi_thread_set_callback(usb_uart->thread, usb_uart_worker);
+
+    furi_thread_start(usb_uart->thread);
+    return usb_uart;
 }
 
-void usb_uart_disable() {
-    if(running == true) {
-        osThreadFlagsSet(furi_thread_get_thread_id(usb_uart->thread), WorkerEvtStop);
-        furi_thread_join(usb_uart->thread);
-        furi_thread_free(usb_uart->thread);
-        free(usb_uart);
-        running = false;
-    }
+void usb_uart_disable(UsbUartBridge* usb_uart) {
+    osThreadFlagsSet(furi_thread_get_thread_id(usb_uart->thread), WorkerEvtStop);
+    furi_thread_join(usb_uart->thread);
+    furi_thread_free(usb_uart->thread);
+    free(usb_uart);
+}
+
+void usb_uart_set_config(UsbUartBridge* usb_uart, UsbUartConfig* cfg) {
+    furi_assert(usb_uart);
+    furi_assert(cfg);
+    memcpy(&(usb_uart->cfg_new), cfg, sizeof(UsbUartConfig));
+    osThreadFlagsSet(furi_thread_get_thread_id(usb_uart->thread), WorkerEvtCfgChange);
+}
+
+void usb_uart_get_config(UsbUartBridge* usb_uart, UsbUartConfig* cfg) {
+    furi_assert(usb_uart);
+    furi_assert(cfg);
+    memcpy(cfg, &(usb_uart->cfg_new), sizeof(UsbUartConfig));
+}
+
+void usb_uart_get_state(UsbUartBridge* usb_uart, UsbUartState* st) {
+    furi_assert(usb_uart);
+    furi_assert(st);
+    memcpy(st, &(usb_uart->st), sizeof(UsbUartState));
 }

+ 18 - 2
applications/gpio/usb_uart_bridge.h

@@ -2,12 +2,28 @@
 
 #include <stdint.h>
 
+typedef struct UsbUartBridge UsbUartBridge;
+
 typedef struct {
     uint8_t vcp_ch;
     uint8_t uart_ch;
+    uint8_t flow_pins;
+    uint8_t baudrate_mode;
     uint32_t baudrate;
 } UsbUartConfig;
 
-void usb_uart_enable(UsbUartConfig* cfg);
+typedef struct {
+    uint32_t rx_cnt;
+    uint32_t tx_cnt;
+    uint32_t baudrate_cur;
+} UsbUartState;
+
+UsbUartBridge* usb_uart_enable(UsbUartConfig* cfg);
+
+void usb_uart_disable(UsbUartBridge* usb_uart);
+
+void usb_uart_set_config(UsbUartBridge* usb_uart, UsbUartConfig* cfg);
+
+void usb_uart_get_config(UsbUartBridge* usb_uart, UsbUartConfig* cfg);
 
-void usb_uart_disable();
+void usb_uart_get_state(UsbUartBridge* usb_uart, UsbUartState* st);

+ 154 - 0
applications/gpio/views/gpio_usb_uart.c

@@ -0,0 +1,154 @@
+#include "../usb_uart_bridge.h"
+#include "../gpio_app_i.h"
+#include "furi-hal.h"
+#include <gui/elements.h>
+
+struct GpioUsbUart {
+    View* view;
+    GpioUsbUartCallback callback;
+    void* context;
+};
+
+typedef struct {
+    uint32_t baudrate;
+    uint32_t tx_cnt;
+    uint32_t rx_cnt;
+    uint8_t vcp_port;
+    uint8_t tx_pin;
+    uint8_t rx_pin;
+    bool tx_active;
+    bool rx_active;
+} GpioUsbUartModel;
+
+static void gpio_usb_uart_draw_callback(Canvas* canvas, void* _model) {
+    GpioUsbUartModel* model = _model;
+    char temp_str[18];
+    elements_button_left(canvas, "Config");
+    canvas_draw_line(canvas, 2, 10, 125, 10);
+    canvas_draw_line(canvas, 44, 52, 123, 52);
+
+    canvas_set_font(canvas, FontPrimary);
+    canvas_draw_str(canvas, 2, 9, "USB Serial");
+    canvas_draw_str(canvas, 3, 25, "TX:");
+    canvas_draw_str(canvas, 3, 42, "RX:");
+
+    canvas_set_font(canvas, FontSecondary);
+    snprintf(temp_str, 18, "COM PORT:%u", model->vcp_port);
+    canvas_draw_str_aligned(canvas, 126, 8, AlignRight, AlignBottom, temp_str);
+    snprintf(temp_str, 18, "Pin %u", model->tx_pin);
+    canvas_draw_str(canvas, 22, 25, temp_str);
+    snprintf(temp_str, 18, "Pin %u", model->rx_pin);
+    canvas_draw_str(canvas, 22, 42, temp_str);
+
+    canvas_draw_str_aligned(canvas, 127, 24, AlignRight, AlignBottom, "B.");
+    canvas_draw_str_aligned(canvas, 127, 41, AlignRight, AlignBottom, "B.");
+
+    if(model->baudrate == 0)
+        snprintf(temp_str, 18, "Baud: ????");
+    else
+        snprintf(temp_str, 18, "Baud: %lu", model->baudrate);
+    canvas_draw_str(canvas, 45, 62, temp_str);
+
+    if(model->tx_cnt < 100000000) {
+        canvas_set_font(canvas, FontSecondary);
+        canvas_draw_str_aligned(canvas, 127, 24, AlignRight, AlignBottom, "B.");
+        canvas_set_font(canvas, FontKeyboard);
+        snprintf(temp_str, 18, "%lu", model->tx_cnt);
+        canvas_draw_str_aligned(canvas, 116, 24, AlignRight, AlignBottom, temp_str);
+    } else {
+        canvas_set_font(canvas, FontSecondary);
+        canvas_draw_str_aligned(canvas, 127, 24, AlignRight, AlignBottom, "KB.");
+        canvas_set_font(canvas, FontKeyboard);
+        snprintf(temp_str, 18, "%lu", model->tx_cnt);
+        canvas_draw_str_aligned(canvas, 110, 24, AlignRight, AlignBottom, temp_str);
+    }
+
+    if(model->rx_cnt < 100000000) {
+        canvas_set_font(canvas, FontSecondary);
+        canvas_draw_str_aligned(canvas, 127, 41, AlignRight, AlignBottom, "B.");
+        canvas_set_font(canvas, FontKeyboard);
+        snprintf(temp_str, 18, "%lu", model->rx_cnt);
+        canvas_draw_str_aligned(canvas, 116, 41, AlignRight, AlignBottom, temp_str);
+    } else {
+        canvas_set_font(canvas, FontSecondary);
+        canvas_draw_str_aligned(canvas, 127, 41, AlignRight, AlignBottom, "KB.");
+        canvas_set_font(canvas, FontKeyboard);
+        snprintf(temp_str, 18, "%lu", model->rx_cnt);
+        canvas_draw_str_aligned(canvas, 110, 41, AlignRight, AlignBottom, temp_str);
+    }
+
+    if(model->tx_active)
+        canvas_draw_icon(canvas, 48, 14, &I_ArrowUpFilled_14x15);
+    else
+        canvas_draw_icon(canvas, 48, 14, &I_ArrowUpEmpty_14x15);
+
+    if(model->rx_active)
+        canvas_draw_icon(canvas, 48, 34, &I_ArrowDownFilled_14x15);
+    else
+        canvas_draw_icon(canvas, 48, 34, &I_ArrowDownEmpty_14x15);
+}
+
+static bool gpio_usb_uart_input_callback(InputEvent* event, void* context) {
+    furi_assert(context);
+    GpioUsbUart* usb_uart = context;
+    bool consumed = false;
+
+    if(event->type == InputTypeShort) {
+        if(event->key == InputKeyLeft) {
+            consumed = true;
+            furi_assert(usb_uart->callback);
+            usb_uart->callback(GpioUsbUartEventConfig, usb_uart->context);
+        }
+    }
+
+    return consumed;
+}
+
+GpioUsbUart* gpio_usb_uart_alloc() {
+    GpioUsbUart* usb_uart = furi_alloc(sizeof(GpioUsbUart));
+
+    usb_uart->view = view_alloc();
+    view_allocate_model(usb_uart->view, ViewModelTypeLocking, sizeof(GpioUsbUartModel));
+    view_set_context(usb_uart->view, usb_uart);
+    view_set_draw_callback(usb_uart->view, gpio_usb_uart_draw_callback);
+    view_set_input_callback(usb_uart->view, gpio_usb_uart_input_callback);
+
+    return usb_uart;
+}
+
+void gpio_usb_uart_free(GpioUsbUart* usb_uart) {
+    furi_assert(usb_uart);
+    view_free(usb_uart->view);
+    free(usb_uart);
+}
+
+View* gpio_usb_uart_get_view(GpioUsbUart* usb_uart) {
+    furi_assert(usb_uart);
+    return usb_uart->view;
+}
+
+void gpio_usb_uart_set_callback(GpioUsbUart* usb_uart, GpioUsbUartCallback callback, void* context) {
+    furi_assert(usb_uart);
+    furi_assert(callback);
+    usb_uart->callback = callback;
+    usb_uart->context = context;
+}
+
+void gpio_usb_uart_update_state(GpioUsbUart* instance, UsbUartConfig* cfg, UsbUartState* st) {
+    furi_assert(instance);
+    furi_assert(cfg);
+    furi_assert(st);
+
+    with_view_model(
+        instance->view, (GpioUsbUartModel * model) {
+            model->baudrate = st->baudrate_cur;
+            model->vcp_port = cfg->vcp_ch;
+            model->tx_pin = (cfg->uart_ch == 0) ? (13) : (15);
+            model->rx_pin = (cfg->uart_ch == 0) ? (14) : (16);
+            model->tx_active = (model->tx_cnt != st->tx_cnt);
+            model->rx_active = (model->rx_cnt != st->rx_cnt);
+            model->tx_cnt = st->tx_cnt;
+            model->rx_cnt = st->rx_cnt;
+            return false;
+        });
+}

+ 18 - 0
applications/gpio/views/gpio_usb_uart.h

@@ -0,0 +1,18 @@
+#pragma once
+
+#include <gui/view.h>
+#include "../gpio_custom_event.h"
+#include "../usb_uart_bridge.h"
+
+typedef struct GpioUsbUart GpioUsbUart;
+typedef void (*GpioUsbUartCallback)(GpioCustomEvent event, void* context);
+
+GpioUsbUart* gpio_usb_uart_alloc();
+
+void gpio_usb_uart_free(GpioUsbUart* usb_uart);
+
+View* gpio_usb_uart_get_view(GpioUsbUart* usb_uart);
+
+void gpio_usb_uart_set_callback(GpioUsbUart* usb_uart, GpioUsbUartCallback callback, void* context);
+
+void gpio_usb_uart_update_state(GpioUsbUart* instance, UsbUartConfig* cfg, UsbUartState* st);

Разница между файлами не показана из-за своего большого размера
+ 0 - 0
assets/compiled/assets_icons.c


+ 60 - 56
assets/compiled/assets_icons.h

@@ -5,66 +5,70 @@ extern const Icon I_Certification2_119x30;
 extern const Icon I_Certification1_103x23;
 extern const Icon A_WatchingTV_128x64;
 extern const Icon A_Wink_128x64;
-extern const Icon I_sub1_10px;
-extern const Icon I_ir_10px;
-extern const Icon I_unknown_10px;
-extern const Icon I_ibutt_10px;
-extern const Icon I_Nfc_10px;
 extern const Icon I_ble_10px;
+extern const Icon I_ibutt_10px;
 extern const Icon I_125_10px;
+extern const Icon I_sub1_10px;
 extern const Icon I_dir_10px;
+extern const Icon I_ir_10px;
+extern const Icon I_Nfc_10px;
+extern const Icon I_unknown_10px;
 extern const Icon I_BLE_Pairing_128x64;
-extern const Icon I_ButtonDown_7x4;
-extern const Icon I_ButtonCenter_7x7;
-extern const Icon I_ButtonLeft_4x7;
-extern const Icon I_ButtonUp_7x4;
-extern const Icon I_DFU_128x50;
-extern const Icon I_ButtonLeftSmall_3x5;
 extern const Icon I_ButtonRightSmall_3x5;
+extern const Icon I_ButtonLeftSmall_3x5;
+extern const Icon I_ButtonCenter_7x7;
+extern const Icon I_ButtonDown_7x4;
 extern const Icon I_ButtonRight_4x7;
+extern const Icon I_DFU_128x50;
+extern const Icon I_ButtonUp_7x4;
 extern const Icon I_Warning_30x23;
-extern const Icon I_DolphinFirstStart2_59x51;
+extern const Icon I_ButtonLeft_4x7;
+extern const Icon I_DolphinFirstStart7_61x51;
+extern const Icon I_DolphinOkay_41x43;
 extern const Icon I_DolphinFirstStart5_54x49;
-extern const Icon I_DolphinFirstStart6_58x54;
 extern const Icon I_Flipper_young_80x60;
+extern const Icon I_DolphinFirstStart2_59x51;
 extern const Icon I_DolphinFirstStart8_56x51;
-extern const Icon I_DolphinFirstStart1_59x53;
-extern const Icon I_DolphinOkay_41x43;
 extern const Icon I_DolphinFirstStart3_57x48;
-extern const Icon I_DolphinFirstStart7_61x51;
 extern const Icon I_DolphinFirstStart0_70x53;
 extern const Icon I_DolphinFirstStart4_67x53;
-extern const Icon I_PassportBottom_128x17;
-extern const Icon I_DoorLeft_70x55;
-extern const Icon I_DoorRight_70x55;
+extern const Icon I_DolphinFirstStart6_58x54;
+extern const Icon I_DolphinFirstStart1_59x53;
+extern const Icon I_ArrowDownFilled_14x15;
+extern const Icon I_ArrowUpEmpty_14x15;
+extern const Icon I_ArrowUpFilled_14x15;
+extern const Icon I_ArrowDownEmpty_14x15;
 extern const Icon I_DoorLocked_10x56;
+extern const Icon I_PassportBottom_128x17;
 extern const Icon I_PassportLeft_6x47;
+extern const Icon I_DoorLeft_70x55;
 extern const Icon I_LockPopup_100x49;
+extern const Icon I_DoorRight_70x55;
+extern const Icon I_IrdaArrowDown_4x8;
+extern const Icon I_Power_25x27;
+extern const Icon I_Mute_25x27;
 extern const Icon I_Down_hvr_25x27;
-extern const Icon I_Vol_down_hvr_25x27;
-extern const Icon I_Down_25x27;
-extern const Icon I_Fill_marker_7x7;
-extern const Icon I_Vol_down_25x27;
 extern const Icon I_Vol_up_25x27;
-extern const Icon I_Up_hvr_25x27;
-extern const Icon I_Vol_up_hvr_25x27;
 extern const Icon I_IrdaLearnShort_128x31;
-extern const Icon I_IrdaSend_128x64;
-extern const Icon I_DolphinReadingSuccess_59x63;
-extern const Icon I_Mute_hvr_25x27;
-extern const Icon I_Back_15x10;
 extern const Icon I_Up_25x27;
+extern const Icon I_Vol_down_hvr_25x27;
+extern const Icon I_Vol_down_25x27;
+extern const Icon I_Vol_up_hvr_25x27;
+extern const Icon I_Fill_marker_7x7;
+extern const Icon I_Up_hvr_25x27;
 extern const Icon I_IrdaArrowUp_4x8;
-extern const Icon I_Mute_25x27;
-extern const Icon I_Power_25x27;
+extern const Icon I_Down_25x27;
+extern const Icon I_DolphinReadingSuccess_59x63;
 extern const Icon I_IrdaSendShort_128x34;
-extern const Icon I_IrdaArrowDown_4x8;
 extern const Icon I_IrdaLearn_128x64;
+extern const Icon I_Mute_hvr_25x27;
+extern const Icon I_IrdaSend_128x64;
 extern const Icon I_Power_hvr_25x27;
+extern const Icon I_Back_15x10;
 extern const Icon I_KeySaveSelected_24x11;
-extern const Icon I_KeyBackspace_16x9;
-extern const Icon I_KeyBackspaceSelected_16x9;
 extern const Icon I_KeySave_24x11;
+extern const Icon I_KeyBackspaceSelected_16x9;
+extern const Icon I_KeyBackspace_16x9;
 extern const Icon A_125khz_14;
 extern const Icon A_Bluetooth_14;
 extern const Icon A_Debug_14;
@@ -81,46 +85,46 @@ extern const Icon A_Sub1ghz_14;
 extern const Icon A_Tamagotchi_14;
 extern const Icon A_U2F_14;
 extern const Icon A_iButton_14;
-extern const Icon I_Detailed_chip_17x13;
 extern const Icon I_Medium_chip_22x21;
+extern const Icon I_Detailed_chip_17x13;
+extern const Icon I_Health_16x16;
+extern const Icon I_Voltage_16x16;
 extern const Icon I_BatteryBody_52x28;
+extern const Icon I_FaceNormal_29x14;
 extern const Icon I_FaceCharging_29x14;
-extern const Icon I_Health_16x16;
-extern const Icon I_Temperature_16x16;
 extern const Icon I_Battery_16x16;
 extern const Icon I_FaceConfused_29x14;
-extern const Icon I_FaceNormal_29x14;
-extern const Icon I_Voltage_16x16;
+extern const Icon I_Temperature_16x16;
 extern const Icon I_FaceNopower_29x14;
-extern const Icon I_RFIDDolphinSend_97x61;
 extern const Icon I_RFIDDolphinSuccess_108x57;
-extern const Icon I_RFIDDolphinReceive_97x61;
 extern const Icon I_RFIDBigChip_37x36;
-extern const Icon I_SDQuestion_35x43;
+extern const Icon I_RFIDDolphinReceive_97x61;
+extern const Icon I_RFIDDolphinSend_97x61;
 extern const Icon I_SDError_43x35;
+extern const Icon I_SDQuestion_35x43;
 extern const Icon I_Cry_dolph_55x52;
-extern const Icon I_Background_128x11;
-extern const Icon I_Lock_8x8;
-extern const Icon I_Battery_26x8;
 extern const Icon I_Battery_19x8;
-extern const Icon I_USBConnected_15x8;
-extern const Icon I_Background_128x8;
-extern const Icon I_BadUsb_9x8;
-extern const Icon I_BT_Pair_9x8;
-extern const Icon I_PlaceholderL_11x13;
 extern const Icon I_SDcardFail_11x8;
 extern const Icon I_Bluetooth_5x8;
 extern const Icon I_PlaceholderR_30x13;
+extern const Icon I_Battery_26x8;
+extern const Icon I_Lock_8x8;
 extern const Icon I_SDcardMounted_11x8;
+extern const Icon I_BadUsb_9x8;
+extern const Icon I_BT_Pair_9x8;
+extern const Icon I_PlaceholderL_11x13;
+extern const Icon I_Background_128x11;
+extern const Icon I_Background_128x8;
+extern const Icon I_USBConnected_15x8;
 extern const Icon I_Quest_7x8;
-extern const Icon I_Lock_7x8;
-extern const Icon I_Scanning_123x52;
 extern const Icon I_MHz_25x11;
+extern const Icon I_Scanning_123x52;
 extern const Icon I_Unlock_7x8;
-extern const Icon I_iButtonKey_49x44;
+extern const Icon I_Lock_7x8;
+extern const Icon I_DolphinNice_96x59;
+extern const Icon I_iButtonDolphinSuccess_109x60;
 extern const Icon I_DolphinExcited_64x63;
-extern const Icon I_DolphinWait_61x59;
+extern const Icon I_iButtonKey_49x44;
 extern const Icon I_iButtonDolphinVerySuccess_108x52;
+extern const Icon I_DolphinWait_61x59;
 extern const Icon I_DolphinMafia_115x62;
-extern const Icon I_DolphinNice_96x59;
-extern const Icon I_iButtonDolphinSuccess_109x60;

BIN
assets/icons/GPIO/ArrowDownEmpty_14x15.png


BIN
assets/icons/GPIO/ArrowDownFilled_14x15.png


BIN
assets/icons/GPIO/ArrowUpEmpty_14x15.png


BIN
assets/icons/GPIO/ArrowUpFilled_14x15.png


+ 22 - 16
core/furi/log.c

@@ -1,12 +1,11 @@
 #include "log.h"
-#include <stm32wbxx_hal.h>
 #include "check.h"
 #include <cmsis_os2.h>
+#include <furi-hal.h>
 
 typedef struct {
     FuriLogLevel log_level;
-    FuriLogPrint print;
-    FuriLogVPrint vprint;
+    FuriLogPuts puts;
     FuriLogTimestamp timetamp;
     osMutexId_t mutex;
 } FuriLogParams;
@@ -16,8 +15,7 @@ static FuriLogParams furi_log;
 void furi_log_init() {
     // Set default logging parameters
     furi_log.log_level = FURI_LOG_LEVEL;
-    furi_log.print = printf;
-    furi_log.vprint = vprintf;
+    furi_log.puts = furi_hal_console_puts;
     furi_log.timetamp = HAL_GetTick;
     furi_log.mutex = osMutexNew(NULL);
 }
@@ -25,10 +23,22 @@ void furi_log_init() {
 void furi_log_print(FuriLogLevel level, const char* format, ...) {
     va_list args;
     va_start(args, format);
-    if(level <= furi_log.log_level) {
-        osMutexAcquire(furi_log.mutex, osWaitForever);
-        furi_log.print("%lu ", furi_log.timetamp());
-        furi_log.vprint(format, args);
+    if(level <= furi_log.log_level && osMutexAcquire(furi_log.mutex, osWaitForever) == osOK) {
+        string_t string;
+
+        // Timestamp
+        string_init_printf(string, "%lu ", furi_log.timetamp());
+        furi_log.puts(string_get_cstr(string));
+        string_clear(string);
+
+        va_list args;
+        va_start(args, format);
+        string_init_vprintf(string, format, args);
+        va_end(args);
+
+        furi_log.puts(string_get_cstr(string));
+        string_clear(string);
+
         osMutexRelease(furi_log.mutex);
     }
     va_end(args);
@@ -42,16 +52,12 @@ FuriLogLevel furi_log_get_level(void) {
     return furi_log.log_level;
 }
 
-void furi_log_set_print(FuriLogPrint print, FuriLogVPrint vprint) {
-    furi_assert(print);
-    furi_assert(vprint);
-
-    furi_log.print = print;
-    furi_log.vprint = vprint;
+void furi_log_set_puts(FuriLogPuts puts) {
+    furi_assert(puts);
+    furi_log.puts = puts;
 }
 
 void furi_log_set_timestamp(FuriLogTimestamp timestamp) {
     furi_assert(timestamp);
-
     furi_log.timetamp = timestamp;
 }

+ 2 - 3
core/furi/log.h

@@ -30,8 +30,7 @@ extern "C" {
 #define FURI_LOG_CLR_D FURI_LOG_CLR(FURI_LOG_CLR_BLUE)
 #define FURI_LOG_CLR_V
 
-typedef int (*FuriLogPrint)(const char*, ...);
-typedef int (*FuriLogVPrint)(const char*, va_list);
+typedef void (*FuriLogPuts)(const char* data);
 typedef uint32_t (*FuriLogTimestamp)(void);
 
 typedef enum {
@@ -47,7 +46,7 @@ void furi_log_init();
 void furi_log_print(FuriLogLevel level, const char* format, ...);
 void furi_log_set_level(FuriLogLevel level);
 FuriLogLevel furi_log_get_level();
-void furi_log_set_print(FuriLogPrint print, FuriLogVPrint vprint);
+void furi_log_set_puts(FuriLogPuts puts);
 void furi_log_set_timestamp(FuriLogTimestamp timestamp);
 
 #define FURI_LOG_FORMAT(log_letter, tag, format) \

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

@@ -1,5 +1,6 @@
 #include "furi-hal-version.h"
 #include "furi-hal-usb_i.h"
+#include "furi-hal-usb.h"
 #include "furi-hal-usb-cdc_i.h"
 #include <furi.h>
 
@@ -347,7 +348,7 @@ static const struct CdcConfigDescriptorDual cdc_cfg_desc_dual = {
 static struct usb_cdc_line_coding cdc_config[IF_NUM_MAX] = {};
 static uint8_t cdc_ctrl_line_state[IF_NUM_MAX];
 
-static void cdc_init(usbd_device* dev, struct UsbInterface* intf);
+static void cdc_init(usbd_device* dev, UsbInterface* intf);
 static void cdc_deinit(usbd_device *dev);
 static void cdc_on_wakeup(usbd_device *dev);
 static void cdc_on_suspend(usbd_device *dev);
@@ -355,10 +356,12 @@ static void cdc_on_suspend(usbd_device *dev);
 static usbd_respond cdc_ep_config (usbd_device *dev, uint8_t cfg);
 static usbd_respond cdc_control (usbd_device *dev, usbd_ctlreq *req, usbd_rqc_callback *callback);
 static usbd_device* usb_dev;
-static struct UsbInterface* cdc_if_cur = NULL;
+static UsbInterface* cdc_if_cur = NULL;
+static bool connected = false;
 static CdcCallbacks* callbacks[IF_NUM_MAX] = {NULL};
+static void* cb_ctx[IF_NUM_MAX];
 
-struct UsbInterface usb_cdc_single = {
+UsbInterface usb_cdc_single = {
     .init = cdc_init,
     .deinit = cdc_deinit,
     .wakeup = cdc_on_wakeup,
@@ -373,7 +376,7 @@ struct UsbInterface usb_cdc_single = {
     .cfg_descr = (void*)&cdc_cfg_desc_single,
 };
 
-struct UsbInterface usb_cdc_dual = {
+UsbInterface usb_cdc_dual = {
     .init = cdc_init,
     .deinit = cdc_deinit,
     .wakeup = cdc_on_wakeup,
@@ -388,7 +391,7 @@ struct UsbInterface usb_cdc_dual = {
     .cfg_descr = (void*)&cdc_cfg_desc_dual,
 };
 
-static void cdc_init(usbd_device* dev, struct UsbInterface* intf) {
+static void cdc_init(usbd_device* dev, UsbInterface* intf) {
     usb_dev = dev;
     cdc_if_cur = intf;
     
@@ -428,21 +431,35 @@ static void cdc_deinit(usbd_device *dev) {
     cdc_if_cur = NULL;
 }
 
-void furi_hal_cdc_set_callbacks(uint8_t if_num, CdcCallbacks* cb) {
-    if (if_num < 2)
-        callbacks[if_num] = cb;
+void furi_hal_cdc_set_callbacks(uint8_t if_num, CdcCallbacks* cb, void* context) {
+    furi_assert(if_num < IF_NUM_MAX);
+
+    if (callbacks[if_num] != NULL) {
+        if (callbacks[if_num]->state_callback != NULL) {
+            if (connected == true)
+                callbacks[if_num]->state_callback(cb_ctx[if_num], 0);
+        }
+    }
+
+    callbacks[if_num] = cb;
+    cb_ctx[if_num] = context;
+
+    if (callbacks[if_num] != NULL) {
+        if (callbacks[if_num]->state_callback != NULL) {
+            if (connected == true)
+                callbacks[if_num]->state_callback(cb_ctx[if_num], 1);
+        }
+    }
 }
 
 struct usb_cdc_line_coding* furi_hal_cdc_get_port_settings(uint8_t if_num) {
-    if (if_num < 2)
-        return &cdc_config[if_num];
-    return NULL;
+    furi_assert(if_num < IF_NUM_MAX);
+    return &cdc_config[if_num];
 }
 
 uint8_t furi_hal_cdc_get_ctrl_line_state(uint8_t if_num) {
-    if (if_num < 2)
-        return cdc_ctrl_line_state[if_num];
-    return 0;
+    furi_assert(if_num < IF_NUM_MAX);
+    return cdc_ctrl_line_state[if_num];
 }
 
 void furi_hal_cdc_send(uint8_t if_num, uint8_t* buf, uint16_t len) {
@@ -462,20 +479,22 @@ int32_t furi_hal_cdc_receive(uint8_t if_num, uint8_t* buf, uint16_t max_len) {
 }
 
 static void cdc_on_wakeup(usbd_device *dev) {
+    connected = true;
     for (uint8_t i = 0; i < IF_NUM_MAX; i++) {
         if (callbacks[i] != NULL) {
             if (callbacks[i]->state_callback != NULL)
-                callbacks[i]->state_callback(1);
+                callbacks[i]->state_callback(cb_ctx[i], 1);
         }
     }
 }
 
 static void cdc_on_suspend(usbd_device *dev) {
+    connected = false;
     for (uint8_t i = 0; i < IF_NUM_MAX; i++) {
         cdc_ctrl_line_state[i] = 0;
         if (callbacks[i] != NULL) {
             if (callbacks[i]->state_callback != NULL)
-                callbacks[i]->state_callback(0);
+                callbacks[i]->state_callback(cb_ctx[i], 0);
         }
     }
 }
@@ -489,7 +508,7 @@ static void cdc_rx_ep_callback (usbd_device *dev, uint8_t event, uint8_t ep) {
     
     if (callbacks[if_num] != NULL) {
         if (callbacks[if_num]->rx_ep_callback != NULL)
-            callbacks[if_num]->rx_ep_callback();
+            callbacks[if_num]->rx_ep_callback(cb_ctx[if_num]);
     }
 }
 
@@ -502,7 +521,7 @@ static void cdc_tx_ep_callback (usbd_device *dev, uint8_t event, uint8_t ep) {
     
     if (callbacks[if_num] != NULL) {
         if (callbacks[if_num]->tx_ep_callback != NULL)
-            callbacks[if_num]->tx_ep_callback();
+            callbacks[if_num]->tx_ep_callback(cb_ctx[if_num]);
     }
 }
 
@@ -590,14 +609,14 @@ static usbd_respond cdc_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_cal
             if (callbacks[if_num] != NULL) {
                 cdc_ctrl_line_state[if_num] = req->wValue;
                 if (callbacks[if_num]->ctrl_line_callback != NULL)
-                    callbacks[if_num]->ctrl_line_callback(cdc_ctrl_line_state[if_num]);
+                    callbacks[if_num]->ctrl_line_callback(cb_ctx[if_num], cdc_ctrl_line_state[if_num]);
             }
             return usbd_ack;
         case USB_CDC_SET_LINE_CODING:
             memcpy(&cdc_config[if_num], req->data, sizeof(cdc_config[0]));
             if (callbacks[if_num] != NULL) {
                 if (callbacks[if_num]->config_callback != NULL)
-                    callbacks[if_num]->config_callback(&cdc_config[if_num]);
+                    callbacks[if_num]->config_callback(cb_ctx[if_num], &cdc_config[if_num]);
             }
             return usbd_ack;
         case USB_CDC_GET_LINE_CODING:

+ 6 - 6
firmware/targets/f6/furi-hal/furi-hal-usb-cdc_i.h

@@ -6,14 +6,14 @@
 #define CDC_DATA_SZ 64
 
 typedef struct {
-    void (*tx_ep_callback)(void);
-    void (*rx_ep_callback)(void);
-    void (*state_callback)(uint8_t state);
-    void (*ctrl_line_callback)(uint8_t state);
-    void (*config_callback)(struct usb_cdc_line_coding* config);
+    void (*tx_ep_callback)(void* context);
+    void (*rx_ep_callback)(void* context);
+    void (*state_callback)(void* context, uint8_t state);
+    void (*ctrl_line_callback)(void* context, uint8_t state);
+    void (*config_callback)(void* context, struct usb_cdc_line_coding* config);
 } CdcCallbacks;
 
-void furi_hal_cdc_set_callbacks(uint8_t if_num, CdcCallbacks* cb);
+void furi_hal_cdc_set_callbacks(uint8_t if_num, CdcCallbacks* cb, void* context);
 
 struct usb_cdc_line_coding* furi_hal_cdc_get_port_settings(uint8_t if_num);
 

+ 4 - 3
firmware/targets/f6/furi-hal/furi-hal-usb-hid.c

@@ -1,5 +1,6 @@
 #include "furi-hal-version.h"
 #include "furi-hal-usb_i.h"
+#include "furi-hal-usb.h"
 #include <furi.h>
 
 #include "usb.h"
@@ -182,7 +183,7 @@ static struct HidReport {
     struct HidReportMouse mouse;
 } __attribute__((packed)) hid_report;
 
-static void hid_init(usbd_device* dev, struct UsbInterface* intf);
+static void hid_init(usbd_device* dev, UsbInterface* intf);
 static void hid_deinit(usbd_device *dev);
 static void hid_on_wakeup(usbd_device *dev);
 static void hid_on_suspend(usbd_device *dev);
@@ -254,7 +255,7 @@ bool furi_hal_hid_mouse_scroll(int8_t delta) {
     return state;
 }
 
-struct UsbInterface usb_hid = {
+UsbInterface usb_hid = {
     .init = hid_init,
     .deinit = hid_deinit,
     .wakeup = hid_on_wakeup,
@@ -269,7 +270,7 @@ struct UsbInterface usb_hid = {
     .cfg_descr = (void*)&hid_cfg_desc,
 };
 
-static void hid_init(usbd_device* dev, struct UsbInterface* intf) {
+static void hid_init(usbd_device* dev, UsbInterface* intf) {
     if (hid_semaphore == NULL)
         hid_semaphore = osSemaphoreNew(1, 1, NULL);
     usb_dev = dev;

+ 27 - 38
firmware/targets/f6/furi-hal/furi-hal-usb.c

@@ -9,17 +9,8 @@
 
 #define USB_RECONNECT_DELAY 500
 
-extern struct UsbInterface usb_cdc_single;
-extern struct UsbInterface usb_cdc_dual;
-extern struct UsbInterface usb_hid;
-
-static struct UsbInterface* const usb_if_modes[UsbModesNum] = {
-    NULL,
-    &usb_cdc_single,
-    &usb_cdc_dual,
-    &usb_hid,
-    NULL,//&usb_hid_u2f,
-};
+static UsbInterface* usb_if_cur;
+static UsbInterface* usb_if_next;
 
 static const struct usb_string_descriptor dev_lang_desc = USB_ARRAY_DESC(USB_LANGID_ENG_US);
 
@@ -32,8 +23,6 @@ static void wkup_evt(usbd_device *dev, uint8_t event, uint8_t ep);
 
 struct UsbCfg{
     osTimerId_t reconnect_tmr;
-    UsbMode mode_cur;
-    UsbMode mode_next;
     bool enabled;
     bool connected;
 } usb_config;
@@ -69,30 +58,30 @@ void furi_hal_usb_init(void) {
     FURI_LOG_I(TAG, "Init OK");
 }
 
-void furi_hal_usb_set_config(UsbMode new_mode) {
-    if (new_mode != usb_config.mode_cur) {
+void furi_hal_usb_set_config(UsbInterface* new_if) {
+    if (new_if != usb_if_cur) {
         if (usb_config.enabled) {
-            usb_config.mode_next = new_mode;
+            usb_if_next = new_if;
             if (usb_config.reconnect_tmr == NULL)
                 usb_config.reconnect_tmr = osTimerNew(furi_hal_usb_tmr_cb, osTimerOnce, NULL, NULL);
             furi_hal_usb_disable();
             osTimerStart(usb_config.reconnect_tmr, USB_RECONNECT_DELAY);
         }
         else {
-            if (usb_if_modes[usb_config.mode_cur] != NULL)
-                usb_if_modes[usb_config.mode_cur]->deinit(&udev);
-            if (usb_if_modes[new_mode] != NULL) {
-                usb_if_modes[new_mode]->init(&udev, usb_if_modes[new_mode]);
-                FURI_LOG_I(TAG, "USB mode change %u -> %u", usb_config.mode_cur, new_mode);
+            if (usb_if_cur != NULL)
+                usb_if_cur->deinit(&udev);
+            if (new_if != NULL) {
+                new_if->init(&udev, new_if);
+                FURI_LOG_I(TAG, "USB mode change");
                 usb_config.enabled = true;
-                usb_config.mode_cur = new_mode;
+                usb_if_cur = new_if;
             }
         }
     }
 }
 
-UsbMode furi_hal_usb_get_config() {
-    return usb_config.mode_cur;
+UsbInterface* furi_hal_usb_get_config() {
+    return usb_if_cur;
 }
 
 void furi_hal_usb_disable() {
@@ -105,7 +94,7 @@ void furi_hal_usb_disable() {
 }
 
 void furi_hal_usb_enable() {
-    if ((!usb_config.enabled) && (usb_if_modes[usb_config.mode_cur] != NULL)) {
+    if ((!usb_config.enabled) && (usb_if_cur != NULL)) {
         usbd_connect(&udev, true);
         usb_config.enabled = true;
         FURI_LOG_I(TAG, "USB Enable");
@@ -113,35 +102,35 @@ void furi_hal_usb_enable() {
 }
 
 static void furi_hal_usb_tmr_cb(void* context) {
-    furi_hal_usb_set_config(usb_config.mode_next);
+    furi_hal_usb_set_config(usb_if_next);
 }
 
 /* Get device / configuration descriptors */
-static usbd_respond usb_descriptor_get (usbd_ctlreq *req, void **address, uint16_t *length) {
+static usbd_respond usb_descriptor_get(usbd_ctlreq *req, void **address, uint16_t *length) {
     const uint8_t dtype = req->wValue >> 8;
     const uint8_t dnumber = req->wValue & 0xFF;
     const void* desc;
     uint16_t len = 0;
-    if (usb_if_modes[usb_config.mode_cur] == NULL)
+    if (usb_if_cur == NULL)
         return usbd_fail;
 
     switch (dtype) {
     case USB_DTYPE_DEVICE:
-        desc = usb_if_modes[usb_config.mode_cur]->dev_descr;
+        desc = usb_if_cur->dev_descr;
         break;
     case USB_DTYPE_CONFIGURATION:
-        desc = usb_if_modes[usb_config.mode_cur]->cfg_descr;
-        len = ((struct usb_string_descriptor*)(usb_if_modes[usb_config.mode_cur]->cfg_descr))->wString[0];
+        desc = usb_if_cur->cfg_descr;
+        len = ((struct usb_string_descriptor*)(usb_if_cur->cfg_descr))->wString[0];
         break;
     case USB_DTYPE_STRING:
         if (dnumber == UsbDevLang) {
             desc = &dev_lang_desc;
         } else if (dnumber == UsbDevManuf) {
-            desc = usb_if_modes[usb_config.mode_cur]->str_manuf_descr;
+            desc = usb_if_cur->str_manuf_descr;
         } else if (dnumber == UsbDevProduct) {
-            desc = usb_if_modes[usb_config.mode_cur]->str_prod_descr;
+            desc = usb_if_cur->str_prod_descr;
         } else if (dnumber == UsbDevSerial) {
-            desc = usb_if_modes[usb_config.mode_cur]->str_serial_descr;
+            desc = usb_if_cur->str_serial_descr;
         } else 
             return usbd_fail;
         break;
@@ -160,15 +149,15 @@ static usbd_respond usb_descriptor_get (usbd_ctlreq *req, void **address, uint16
 }
 
 static void susp_evt(usbd_device *dev, uint8_t event, uint8_t ep) {
-    if ((usb_if_modes[usb_config.mode_cur] != NULL) && (usb_config.connected == true)) {
+    if ((usb_if_cur != NULL) && (usb_config.connected == true)) {
         usb_config.connected = false;
-        usb_if_modes[usb_config.mode_cur]->suspend(&udev);
+        usb_if_cur->suspend(&udev);
     }
 }
 
 static void wkup_evt(usbd_device *dev, uint8_t event, uint8_t ep) {
-    if ((usb_if_modes[usb_config.mode_cur] != NULL) && (usb_config.connected == false)) {
+    if ((usb_if_cur != NULL) && (usb_config.connected == false)) {
         usb_config.connected = true;
-        usb_if_modes[usb_config.mode_cur]->wakeup(&udev);
+        usb_if_cur->wakeup(&udev);
     } 
 }

+ 0 - 15
firmware/targets/f6/furi-hal/furi-hal-usb_i.h

@@ -11,18 +11,3 @@ enum UsbDevDescStr{
     UsbDevProduct   = 2,
     UsbDevSerial    = 3,
 };
-
-struct UsbInterface {
-    void (*init)(usbd_device *dev, struct UsbInterface* intf);
-    void (*deinit)(usbd_device *dev);
-    void (*wakeup)(usbd_device *dev);
-    void (*suspend)(usbd_device *dev);    
-
-    struct usb_device_descriptor* dev_descr;
-
-    void* str_manuf_descr;
-    void* str_prod_descr;
-    void* str_serial_descr;
-
-    void* cfg_descr;
-};

+ 108 - 89
firmware/targets/f6/furi-hal/furi-hal-vcp.c

@@ -13,17 +13,17 @@
 
 typedef enum {
     VcpEvtReserved      = (1 << 0), // Reserved for StreamBuffer internal event
-    VcpEvtConnect       = (1 << 1),
-    VcpEvtDisconnect    = (1 << 2),
-    VcpEvtEnable        = (1 << 3),
-    VcpEvtDisable       = (1 << 4),
-    VcpEvtRx            = (1 << 5),
-    VcpEvtTx            = (1 << 6),
-    VcpEvtStreamRx      = (1 << 7),
-    VcpEvtStreamTx      = (1 << 8),
+    VcpEvtEnable        = (1 << 1),
+    VcpEvtDisable       = (1 << 2),
+    VcpEvtConnect       = (1 << 3),
+    VcpEvtDisconnect    = (1 << 4),
+    VcpEvtStreamRx      = (1 << 5),
+    VcpEvtRx            = (1 << 6),
+    VcpEvtStreamTx      = (1 << 7),
+    VcpEvtTx            = (1 << 8),
 } WorkerEvtFlags;
 
-#define VCP_THREAD_FLAG_ALL (VcpEvtConnect | VcpEvtDisconnect | VcpEvtEnable | VcpEvtDisable | VcpEvtRx | VcpEvtTx | VcpEvtStreamRx | VcpEvtStreamTx)
+#define VCP_THREAD_FLAG_ALL (VcpEvtEnable | VcpEvtDisable | VcpEvtConnect | VcpEvtDisconnect | VcpEvtRx | VcpEvtTx | VcpEvtStreamRx | VcpEvtStreamTx)
 
 typedef struct {
     FuriThread* thread;
@@ -37,10 +37,10 @@ typedef struct {
 } FuriHalVcp;
 
 static int32_t vcp_worker(void* context);
-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_cdc_tx_complete(void* context);
+static void vcp_on_cdc_rx(void* context);
+static void vcp_state_callback(void* context, uint8_t state);
+static void vcp_on_cdc_control_line(void* context, uint8_t state);
 
 static CdcCallbacks cdc_cb = {
     vcp_on_cdc_tx_complete,
@@ -76,16 +76,65 @@ static int32_t vcp_worker(void* context) {
     bool tx_idle = false;
     size_t missed_rx = 0;
 
-    furi_hal_cdc_set_callbacks(VCP_IF_NUM, &cdc_cb);
+    furi_hal_cdc_set_callbacks(VCP_IF_NUM, &cdc_cb, NULL);
 
     while (1) {
         uint32_t flags = osThreadFlagsWait(VCP_THREAD_FLAG_ALL, osFlagsWaitAny, osWaitForever);
         furi_assert((flags & osFlagsError) == 0);
 
-        // New data received
+        // VCP enabled
+        if((flags & VcpEvtEnable) && !enabled){
+#ifdef FURI_HAL_USB_VCP_DEBUG
+            FURI_LOG_D(TAG, "Enable");
+#endif            
+            flags |= VcpEvtTx;
+            furi_hal_cdc_set_callbacks(VCP_IF_NUM, &cdc_cb, NULL);
+            enabled = true;
+            furi_hal_cdc_receive(VCP_IF_NUM, vcp->data_buffer, USB_CDC_PKT_LEN); // flush Rx buffer
+            if (furi_hal_cdc_get_ctrl_line_state(VCP_IF_NUM) & (1 << 0)) {
+                vcp->connected = true;
+                xStreamBufferSend(vcp->rx_stream, &ascii_soh, 1, osWaitForever);
+            }
+        }
+
+        // VCP disabled
+        if((flags & VcpEvtDisable) && enabled) {
+#ifdef FURI_HAL_USB_VCP_DEBUG            
+            FURI_LOG_D(TAG, "Disable");
+#endif            
+            enabled = false;
+            vcp->connected = false;
+            xStreamBufferReceive(vcp->tx_stream, vcp->data_buffer, USB_CDC_PKT_LEN, 0);
+            xStreamBufferSend(vcp->rx_stream, &ascii_eot, 1, osWaitForever);
+        }
+
+        // VCP session opened
+        if((flags & VcpEvtConnect) && enabled) {
+#ifdef FURI_HAL_USB_VCP_DEBUG            
+            FURI_LOG_D(TAG, "Connect");
+#endif            
+            if (vcp->connected == false) {
+                vcp->connected = true;
+                xStreamBufferSend(vcp->rx_stream, &ascii_soh, 1, osWaitForever);
+            }
+        }
+
+        // VCP session closed
+        if((flags & VcpEvtDisconnect) && enabled) {
+#ifdef FURI_HAL_USB_VCP_DEBUG            
+            FURI_LOG_D(TAG, "Disconnect");
+#endif            
+            if (vcp->connected == true) {
+                vcp->connected = false;
+                xStreamBufferReceive(vcp->tx_stream, vcp->data_buffer, USB_CDC_PKT_LEN, 0);
+                xStreamBufferSend(vcp->rx_stream, &ascii_eot, 1, osWaitForever);
+            }
+        }
+
+        // Rx buffer was read, maybe there is enough space for new data?
         if((flags & VcpEvtStreamRx) && enabled && missed_rx > 0) {
 #ifdef FURI_HAL_USB_VCP_DEBUG
-            furi_hal_console_puts("VCP StreamRx\r\n");
+            FURI_LOG_D(TAG, "StreamRx");
 #endif
             if (xStreamBufferSpacesAvailable(vcp->rx_stream) >= USB_CDC_PKT_LEN) {
                 flags |= VcpEvtRx;
@@ -93,29 +142,29 @@ static int32_t vcp_worker(void* context) {
             }
         }
 
-        // Rx buffer was read, maybe there is enough space for new data?
+        // New data received
         if((flags & VcpEvtRx)) {
             if (xStreamBufferSpacesAvailable(vcp->rx_stream) >= USB_CDC_PKT_LEN) {
                 int32_t len = furi_hal_cdc_receive(VCP_IF_NUM, vcp->data_buffer, USB_CDC_PKT_LEN);
-#ifdef FURI_HAL_USB_VCP_DEBUG
-                furi_hal_console_printf("VCP Rx %d\r\n", len);
-#endif
+#ifdef FURI_HAL_USB_VCP_DEBUG                
+                FURI_LOG_D(TAG, "Rx %d", len);
+#endif                
                 if (len > 0) {
                     furi_check(xStreamBufferSend(vcp->rx_stream, vcp->data_buffer, len, osWaitForever) == len);
                 }
             } else {
-#ifdef FURI_HAL_USB_VCP_DEBUG
-                furi_hal_console_puts("VCP Rx missed\r\n");
-#endif
+#ifdef FURI_HAL_USB_VCP_DEBUG                
+                FURI_LOG_D(TAG, "Rx missed");
+#endif                
                 missed_rx++;
             }
         }
 
         // New data in Tx buffer
         if((flags & VcpEvtStreamTx) && enabled) {
-#ifdef FURI_HAL_USB_VCP_DEBUG
-            furi_hal_console_puts("VCP StreamTx\r\n");
-#endif
+#ifdef FURI_HAL_USB_VCP_DEBUG            
+            FURI_LOG_D(TAG, "StreamTx");
+#endif            
             if (tx_idle) {
                 flags |= VcpEvtTx;
             }
@@ -124,9 +173,9 @@ static int32_t vcp_worker(void* context) {
         // CDC write transfer done
         if((flags & VcpEvtTx) && enabled) {
             size_t len = xStreamBufferReceive(vcp->tx_stream, vcp->data_buffer, USB_CDC_PKT_LEN, 0);
-#ifdef FURI_HAL_USB_VCP_DEBUG
-            furi_hal_console_printf("VCP Tx %d\r\n", len);
-#endif
+#ifdef FURI_HAL_USB_VCP_DEBUG            
+            FURI_LOG_D(TAG, "Tx %d", len);
+#endif            
             if (len > 0) { // Some data left in Tx buffer. Sending it now
                 tx_idle = false;
                 furi_hal_cdc_send(VCP_IF_NUM, vcp->data_buffer, len);
@@ -134,52 +183,6 @@ static int32_t vcp_worker(void* context) {
                 tx_idle = true;
             }
         }
-
-        // VCP session opened
-        if((flags & VcpEvtConnect) && enabled) {
-#ifdef FURI_HAL_USB_VCP_DEBUG
-            furi_hal_console_puts("VCP Connect\r\n");
-#endif
-            if (vcp->connected == false) {
-                vcp->connected = true;
-                xStreamBufferSend(vcp->rx_stream, &ascii_soh, 1, osWaitForever);
-            }
-        }
-
-        // VCP session closed
-        if((flags & VcpEvtDisconnect) && enabled) {
-#ifdef FURI_HAL_USB_VCP_DEBUG
-            furi_hal_console_puts("VCP Disconnect\r\n");
-#endif
-            if (vcp->connected == true) {
-                vcp->connected = false;
-                xStreamBufferSend(vcp->rx_stream, &ascii_eot, 1, osWaitForever);
-            }
-        }
-
-        // VCP enabled
-        if((flags & VcpEvtEnable) && !enabled){
-#ifdef FURI_HAL_USB_VCP_DEBUG
-            furi_hal_console_puts("VCP Enable\r\n");
-#endif
-            furi_hal_cdc_set_callbacks(VCP_IF_NUM, &cdc_cb);
-            enabled = true;
-            furi_hal_cdc_receive(VCP_IF_NUM, vcp->data_buffer, USB_CDC_PKT_LEN); // flush Rx buffer
-            if (furi_hal_cdc_get_ctrl_line_state(VCP_IF_NUM) & (1 << 0)) {
-                vcp->connected = true;
-                xStreamBufferSend(vcp->rx_stream, &ascii_soh, 1, osWaitForever);
-            }
-        }
-
-        // VCP disabled
-        if((flags & VcpEvtDisable) && enabled) {
-#ifdef FURI_HAL_USB_VCP_DEBUG
-            furi_hal_console_puts("VCP Disable\r\n");
-#endif
-            enabled = false;
-            vcp->connected = false;
-            xStreamBufferSend(vcp->rx_stream, &ascii_eot, 1, osWaitForever);
-        }
     }
     return 0;
 }
@@ -196,6 +199,10 @@ size_t furi_hal_vcp_rx_with_timeout(uint8_t* buffer, size_t size, uint32_t timeo
     furi_assert(vcp);
     furi_assert(buffer);
 
+#ifdef FURI_HAL_USB_VCP_DEBUG
+    FURI_LOG_D(TAG, "rx %u start", size);
+#endif    
+
     size_t rx_cnt = 0;
 
     while (size > 0) {
@@ -204,6 +211,9 @@ size_t furi_hal_vcp_rx_with_timeout(uint8_t* buffer, size_t size, uint32_t timeo
             batch_size = VCP_RX_BUF_SIZE;
 
         size_t len = xStreamBufferReceive(vcp->rx_stream, buffer, batch_size, timeout);
+#ifdef FURI_HAL_USB_VCP_DEBUG        
+        FURI_LOG_D(TAG, "%u ", batch_size);
+#endif        
         if (len == 0)
             break;
         osThreadFlagsSet(furi_thread_get_thread_id(vcp->thread), VcpEvtStreamRx);
@@ -211,6 +221,10 @@ size_t furi_hal_vcp_rx_with_timeout(uint8_t* buffer, size_t size, uint32_t timeo
         buffer += len;
         rx_cnt += len;
     }
+
+#ifdef FURI_HAL_USB_VCP_DEBUG
+    FURI_LOG_D(TAG, "rx %u end", size);
+#endif    
     return rx_cnt;
 }
 
@@ -223,34 +237,40 @@ void furi_hal_vcp_tx(const uint8_t* buffer, size_t size) {
     furi_assert(vcp);
     furi_assert(buffer);
 
-    while (size > 0) {
+#ifdef FURI_HAL_USB_VCP_DEBUG
+    FURI_LOG_D(TAG, "tx %u start", size);
+#endif    
+
+    while (size > 0 && vcp->connected) {
         size_t batch_size = size;
-        if (batch_size > VCP_TX_BUF_SIZE)
-            batch_size = VCP_TX_BUF_SIZE;
+        if (batch_size > USB_CDC_PKT_LEN)
+            batch_size = USB_CDC_PKT_LEN;
 
         xStreamBufferSend(vcp->tx_stream, buffer, batch_size, osWaitForever);
         osThreadFlagsSet(furi_thread_get_thread_id(vcp->thread), VcpEvtStreamTx);
+#ifdef FURI_HAL_USB_VCP_DEBUG
+        FURI_LOG_D(TAG, "%u ", batch_size);
+#endif        
 
         size -= batch_size;
         buffer += batch_size;
     }
-}
 
-static void vcp_state_callback(uint8_t state) {
 #ifdef FURI_HAL_USB_VCP_DEBUG
-    furi_hal_console_puts("VCP State\r\n");
-#endif
+    FURI_LOG_D(TAG, "tx %u end", size);
+#endif    
+}
+
+static void vcp_state_callback(void* context, uint8_t state) {
     if (state == 0) {
         osThreadFlagsSet(furi_thread_get_thread_id(vcp->thread), VcpEvtDisconnect);
     }
 }
 
-static void vcp_on_cdc_control_line(uint8_t state) {
+static void vcp_on_cdc_control_line(void* context, uint8_t state) {
     // bit 0: DTR state, bit 1: RTS state
     bool dtr = state & (1 << 0);
-#ifdef FURI_HAL_USB_VCP_DEBUG
-    furi_hal_console_puts("VCP CtrlLine\r\n");
-#endif
+
     if (dtr == true) {
         osThreadFlagsSet(furi_thread_get_thread_id(vcp->thread), VcpEvtConnect);
     } else {
@@ -258,12 +278,12 @@ static void vcp_on_cdc_control_line(uint8_t state) {
     }
 }
 
-static void vcp_on_cdc_rx() {
+static void vcp_on_cdc_rx(void* context) {
     uint32_t ret = osThreadFlagsSet(furi_thread_get_thread_id(vcp->thread), VcpEvtRx);
     furi_assert((ret & osFlagsError) == 0);
 }
 
-static void vcp_on_cdc_tx_complete() {
+static void vcp_on_cdc_tx_complete(void* context) {
     osThreadFlagsSet(furi_thread_get_thread_id(vcp->thread), VcpEvtTx);
 }
 
@@ -271,4 +291,3 @@ bool furi_hal_vcp_is_connected(void) {
     furi_assert(vcp);
     return vcp->connected;
 }
-

+ 1 - 1
firmware/targets/f6/furi-hal/furi-hal.c

@@ -40,7 +40,7 @@ void furi_hal_init() {
 
     // VCP + USB
     furi_hal_usb_init();
-    furi_hal_usb_set_config(UsbModeVcpSingle);
+    furi_hal_usb_set_config(&usb_cdc_single);
     furi_hal_vcp_init();
     FURI_LOG_I(TAG, "USB OK");
 

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

@@ -1,5 +1,6 @@
 #include "furi-hal-version.h"
 #include "furi-hal-usb_i.h"
+#include "furi-hal-usb.h"
 #include "furi-hal-usb-cdc_i.h"
 #include <furi.h>
 
@@ -347,7 +348,7 @@ static const struct CdcConfigDescriptorDual cdc_cfg_desc_dual = {
 static struct usb_cdc_line_coding cdc_config[IF_NUM_MAX] = {};
 static uint8_t cdc_ctrl_line_state[IF_NUM_MAX];
 
-static void cdc_init(usbd_device* dev, struct UsbInterface* intf);
+static void cdc_init(usbd_device* dev, UsbInterface* intf);
 static void cdc_deinit(usbd_device *dev);
 static void cdc_on_wakeup(usbd_device *dev);
 static void cdc_on_suspend(usbd_device *dev);
@@ -355,10 +356,12 @@ static void cdc_on_suspend(usbd_device *dev);
 static usbd_respond cdc_ep_config (usbd_device *dev, uint8_t cfg);
 static usbd_respond cdc_control (usbd_device *dev, usbd_ctlreq *req, usbd_rqc_callback *callback);
 static usbd_device* usb_dev;
-static struct UsbInterface* cdc_if_cur = NULL;
+static UsbInterface* cdc_if_cur = NULL;
+static bool connected = false;
 static CdcCallbacks* callbacks[IF_NUM_MAX] = {NULL};
+static void* cb_ctx[IF_NUM_MAX];
 
-struct UsbInterface usb_cdc_single = {
+UsbInterface usb_cdc_single = {
     .init = cdc_init,
     .deinit = cdc_deinit,
     .wakeup = cdc_on_wakeup,
@@ -373,7 +376,7 @@ struct UsbInterface usb_cdc_single = {
     .cfg_descr = (void*)&cdc_cfg_desc_single,
 };
 
-struct UsbInterface usb_cdc_dual = {
+UsbInterface usb_cdc_dual = {
     .init = cdc_init,
     .deinit = cdc_deinit,
     .wakeup = cdc_on_wakeup,
@@ -388,7 +391,7 @@ struct UsbInterface usb_cdc_dual = {
     .cfg_descr = (void*)&cdc_cfg_desc_dual,
 };
 
-static void cdc_init(usbd_device* dev, struct UsbInterface* intf) {
+static void cdc_init(usbd_device* dev, UsbInterface* intf) {
     usb_dev = dev;
     cdc_if_cur = intf;
     
@@ -428,21 +431,35 @@ static void cdc_deinit(usbd_device *dev) {
     cdc_if_cur = NULL;
 }
 
-void furi_hal_cdc_set_callbacks(uint8_t if_num, CdcCallbacks* cb) {
-    if (if_num < 2)
-        callbacks[if_num] = cb;
+void furi_hal_cdc_set_callbacks(uint8_t if_num, CdcCallbacks* cb, void* context) {
+    furi_assert(if_num < IF_NUM_MAX);
+
+    if (callbacks[if_num] != NULL) {
+        if (callbacks[if_num]->state_callback != NULL) {
+            if (connected == true)
+                callbacks[if_num]->state_callback(cb_ctx[if_num], 0);
+        }
+    }
+
+    callbacks[if_num] = cb;
+    cb_ctx[if_num] = context;
+
+    if (callbacks[if_num] != NULL) {
+        if (callbacks[if_num]->state_callback != NULL) {
+            if (connected == true)
+                callbacks[if_num]->state_callback(cb_ctx[if_num], 1);
+        }
+    }
 }
 
 struct usb_cdc_line_coding* furi_hal_cdc_get_port_settings(uint8_t if_num) {
-    if (if_num < 2)
-        return &cdc_config[if_num];
-    return NULL;
+    furi_assert(if_num < IF_NUM_MAX);
+    return &cdc_config[if_num];
 }
 
 uint8_t furi_hal_cdc_get_ctrl_line_state(uint8_t if_num) {
-    if (if_num < 2)
-        return cdc_ctrl_line_state[if_num];
-    return 0;
+    furi_assert(if_num < IF_NUM_MAX);
+    return cdc_ctrl_line_state[if_num];
 }
 
 void furi_hal_cdc_send(uint8_t if_num, uint8_t* buf, uint16_t len) {
@@ -462,20 +479,22 @@ int32_t furi_hal_cdc_receive(uint8_t if_num, uint8_t* buf, uint16_t max_len) {
 }
 
 static void cdc_on_wakeup(usbd_device *dev) {
+    connected = true;
     for (uint8_t i = 0; i < IF_NUM_MAX; i++) {
         if (callbacks[i] != NULL) {
             if (callbacks[i]->state_callback != NULL)
-                callbacks[i]->state_callback(1);
+                callbacks[i]->state_callback(cb_ctx[i], 1);
         }
     }
 }
 
 static void cdc_on_suspend(usbd_device *dev) {
+    connected = false;
     for (uint8_t i = 0; i < IF_NUM_MAX; i++) {
         cdc_ctrl_line_state[i] = 0;
         if (callbacks[i] != NULL) {
             if (callbacks[i]->state_callback != NULL)
-                callbacks[i]->state_callback(0);
+                callbacks[i]->state_callback(cb_ctx[i], 0);
         }
     }
 }
@@ -489,7 +508,7 @@ static void cdc_rx_ep_callback (usbd_device *dev, uint8_t event, uint8_t ep) {
     
     if (callbacks[if_num] != NULL) {
         if (callbacks[if_num]->rx_ep_callback != NULL)
-            callbacks[if_num]->rx_ep_callback();
+            callbacks[if_num]->rx_ep_callback(cb_ctx[if_num]);
     }
 }
 
@@ -502,7 +521,7 @@ static void cdc_tx_ep_callback (usbd_device *dev, uint8_t event, uint8_t ep) {
     
     if (callbacks[if_num] != NULL) {
         if (callbacks[if_num]->tx_ep_callback != NULL)
-            callbacks[if_num]->tx_ep_callback();
+            callbacks[if_num]->tx_ep_callback(cb_ctx[if_num]);
     }
 }
 
@@ -590,14 +609,14 @@ static usbd_respond cdc_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_cal
             if (callbacks[if_num] != NULL) {
                 cdc_ctrl_line_state[if_num] = req->wValue;
                 if (callbacks[if_num]->ctrl_line_callback != NULL)
-                    callbacks[if_num]->ctrl_line_callback(cdc_ctrl_line_state[if_num]);
+                    callbacks[if_num]->ctrl_line_callback(cb_ctx[if_num], cdc_ctrl_line_state[if_num]);
             }
             return usbd_ack;
         case USB_CDC_SET_LINE_CODING:
             memcpy(&cdc_config[if_num], req->data, sizeof(cdc_config[0]));
             if (callbacks[if_num] != NULL) {
                 if (callbacks[if_num]->config_callback != NULL)
-                    callbacks[if_num]->config_callback(&cdc_config[if_num]);
+                    callbacks[if_num]->config_callback(cb_ctx[if_num], &cdc_config[if_num]);
             }
             return usbd_ack;
         case USB_CDC_GET_LINE_CODING:

+ 6 - 6
firmware/targets/f7/furi-hal/furi-hal-usb-cdc_i.h

@@ -6,14 +6,14 @@
 #define CDC_DATA_SZ 64
 
 typedef struct {
-    void (*tx_ep_callback)(void);
-    void (*rx_ep_callback)(void);
-    void (*state_callback)(uint8_t state);
-    void (*ctrl_line_callback)(uint8_t state);
-    void (*config_callback)(struct usb_cdc_line_coding* config);
+    void (*tx_ep_callback)(void* context);
+    void (*rx_ep_callback)(void* context);
+    void (*state_callback)(void* context, uint8_t state);
+    void (*ctrl_line_callback)(void* context, uint8_t state);
+    void (*config_callback)(void* context, struct usb_cdc_line_coding* config);
 } CdcCallbacks;
 
-void furi_hal_cdc_set_callbacks(uint8_t if_num, CdcCallbacks* cb);
+void furi_hal_cdc_set_callbacks(uint8_t if_num, CdcCallbacks* cb, void* context);
 
 struct usb_cdc_line_coding* furi_hal_cdc_get_port_settings(uint8_t if_num);
 

+ 4 - 3
firmware/targets/f7/furi-hal/furi-hal-usb-hid.c

@@ -1,5 +1,6 @@
 #include "furi-hal-version.h"
 #include "furi-hal-usb_i.h"
+#include "furi-hal-usb.h"
 #include <furi.h>
 
 #include "usb.h"
@@ -182,7 +183,7 @@ static struct HidReport {
     struct HidReportMouse mouse;
 } __attribute__((packed)) hid_report;
 
-static void hid_init(usbd_device* dev, struct UsbInterface* intf);
+static void hid_init(usbd_device* dev, UsbInterface* intf);
 static void hid_deinit(usbd_device *dev);
 static void hid_on_wakeup(usbd_device *dev);
 static void hid_on_suspend(usbd_device *dev);
@@ -254,7 +255,7 @@ bool furi_hal_hid_mouse_scroll(int8_t delta) {
     return state;
 }
 
-struct UsbInterface usb_hid = {
+UsbInterface usb_hid = {
     .init = hid_init,
     .deinit = hid_deinit,
     .wakeup = hid_on_wakeup,
@@ -269,7 +270,7 @@ struct UsbInterface usb_hid = {
     .cfg_descr = (void*)&hid_cfg_desc,
 };
 
-static void hid_init(usbd_device* dev, struct UsbInterface* intf) {
+static void hid_init(usbd_device* dev, UsbInterface* intf) {
     if (hid_semaphore == NULL)
         hid_semaphore = osSemaphoreNew(1, 1, NULL);
     usb_dev = dev;

+ 27 - 38
firmware/targets/f7/furi-hal/furi-hal-usb.c

@@ -9,17 +9,8 @@
 
 #define USB_RECONNECT_DELAY 500
 
-extern struct UsbInterface usb_cdc_single;
-extern struct UsbInterface usb_cdc_dual;
-extern struct UsbInterface usb_hid;
-
-static struct UsbInterface* const usb_if_modes[UsbModesNum] = {
-    NULL,
-    &usb_cdc_single,
-    &usb_cdc_dual,
-    &usb_hid,
-    NULL,//&usb_hid_u2f,
-};
+static UsbInterface* usb_if_cur;
+static UsbInterface* usb_if_next;
 
 static const struct usb_string_descriptor dev_lang_desc = USB_ARRAY_DESC(USB_LANGID_ENG_US);
 
@@ -32,8 +23,6 @@ static void wkup_evt(usbd_device *dev, uint8_t event, uint8_t ep);
 
 struct UsbCfg{
     osTimerId_t reconnect_tmr;
-    UsbMode mode_cur;
-    UsbMode mode_next;
     bool enabled;
     bool connected;
 } usb_config;
@@ -69,30 +58,30 @@ void furi_hal_usb_init(void) {
     FURI_LOG_I(TAG, "Init OK");
 }
 
-void furi_hal_usb_set_config(UsbMode new_mode) {
-    if (new_mode != usb_config.mode_cur) {
+void furi_hal_usb_set_config(UsbInterface* new_if) {
+    if (new_if != usb_if_cur) {
         if (usb_config.enabled) {
-            usb_config.mode_next = new_mode;
+            usb_if_next = new_if;
             if (usb_config.reconnect_tmr == NULL)
                 usb_config.reconnect_tmr = osTimerNew(furi_hal_usb_tmr_cb, osTimerOnce, NULL, NULL);
             furi_hal_usb_disable();
             osTimerStart(usb_config.reconnect_tmr, USB_RECONNECT_DELAY);
         }
         else {
-            if (usb_if_modes[usb_config.mode_cur] != NULL)
-                usb_if_modes[usb_config.mode_cur]->deinit(&udev);
-            if (usb_if_modes[new_mode] != NULL) {
-                usb_if_modes[new_mode]->init(&udev, usb_if_modes[new_mode]);
-                FURI_LOG_I(TAG, "USB mode change %u -> %u", usb_config.mode_cur, new_mode);
+            if (usb_if_cur != NULL)
+                usb_if_cur->deinit(&udev);
+            if (new_if != NULL) {
+                new_if->init(&udev, new_if);
+                FURI_LOG_I(TAG, "USB mode change");
                 usb_config.enabled = true;
-                usb_config.mode_cur = new_mode;
+                usb_if_cur = new_if;
             }
         }
     }
 }
 
-UsbMode furi_hal_usb_get_config() {
-    return usb_config.mode_cur;
+UsbInterface* furi_hal_usb_get_config() {
+    return usb_if_cur;
 }
 
 void furi_hal_usb_disable() {
@@ -105,7 +94,7 @@ void furi_hal_usb_disable() {
 }
 
 void furi_hal_usb_enable() {
-    if ((!usb_config.enabled) && (usb_if_modes[usb_config.mode_cur] != NULL)) {
+    if ((!usb_config.enabled) && (usb_if_cur != NULL)) {
         usbd_connect(&udev, true);
         usb_config.enabled = true;
         FURI_LOG_I(TAG, "USB Enable");
@@ -113,35 +102,35 @@ void furi_hal_usb_enable() {
 }
 
 static void furi_hal_usb_tmr_cb(void* context) {
-    furi_hal_usb_set_config(usb_config.mode_next);
+    furi_hal_usb_set_config(usb_if_next);
 }
 
 /* Get device / configuration descriptors */
-static usbd_respond usb_descriptor_get (usbd_ctlreq *req, void **address, uint16_t *length) {
+static usbd_respond usb_descriptor_get(usbd_ctlreq *req, void **address, uint16_t *length) {
     const uint8_t dtype = req->wValue >> 8;
     const uint8_t dnumber = req->wValue & 0xFF;
     const void* desc;
     uint16_t len = 0;
-    if (usb_if_modes[usb_config.mode_cur] == NULL)
+    if (usb_if_cur == NULL)
         return usbd_fail;
 
     switch (dtype) {
     case USB_DTYPE_DEVICE:
-        desc = usb_if_modes[usb_config.mode_cur]->dev_descr;
+        desc = usb_if_cur->dev_descr;
         break;
     case USB_DTYPE_CONFIGURATION:
-        desc = usb_if_modes[usb_config.mode_cur]->cfg_descr;
-        len = ((struct usb_string_descriptor*)(usb_if_modes[usb_config.mode_cur]->cfg_descr))->wString[0];
+        desc = usb_if_cur->cfg_descr;
+        len = ((struct usb_string_descriptor*)(usb_if_cur->cfg_descr))->wString[0];
         break;
     case USB_DTYPE_STRING:
         if (dnumber == UsbDevLang) {
             desc = &dev_lang_desc;
         } else if (dnumber == UsbDevManuf) {
-            desc = usb_if_modes[usb_config.mode_cur]->str_manuf_descr;
+            desc = usb_if_cur->str_manuf_descr;
         } else if (dnumber == UsbDevProduct) {
-            desc = usb_if_modes[usb_config.mode_cur]->str_prod_descr;
+            desc = usb_if_cur->str_prod_descr;
         } else if (dnumber == UsbDevSerial) {
-            desc = usb_if_modes[usb_config.mode_cur]->str_serial_descr;
+            desc = usb_if_cur->str_serial_descr;
         } else 
             return usbd_fail;
         break;
@@ -160,15 +149,15 @@ static usbd_respond usb_descriptor_get (usbd_ctlreq *req, void **address, uint16
 }
 
 static void susp_evt(usbd_device *dev, uint8_t event, uint8_t ep) {
-    if ((usb_if_modes[usb_config.mode_cur] != NULL) && (usb_config.connected == true)) {
+    if ((usb_if_cur != NULL) && (usb_config.connected == true)) {
         usb_config.connected = false;
-        usb_if_modes[usb_config.mode_cur]->suspend(&udev);
+        usb_if_cur->suspend(&udev);
     }
 }
 
 static void wkup_evt(usbd_device *dev, uint8_t event, uint8_t ep) {
-    if ((usb_if_modes[usb_config.mode_cur] != NULL) && (usb_config.connected == false)) {
+    if ((usb_if_cur != NULL) && (usb_config.connected == false)) {
         usb_config.connected = true;
-        usb_if_modes[usb_config.mode_cur]->wakeup(&udev);
+        usb_if_cur->wakeup(&udev);
     } 
 }

+ 0 - 15
firmware/targets/f7/furi-hal/furi-hal-usb_i.h

@@ -11,18 +11,3 @@ enum UsbDevDescStr{
     UsbDevProduct   = 2,
     UsbDevSerial    = 3,
 };
-
-struct UsbInterface {
-    void (*init)(usbd_device *dev, struct UsbInterface* intf);
-    void (*deinit)(usbd_device *dev);
-    void (*wakeup)(usbd_device *dev);
-    void (*suspend)(usbd_device *dev);    
-
-    struct usb_device_descriptor* dev_descr;
-
-    void* str_manuf_descr;
-    void* str_prod_descr;
-    void* str_serial_descr;
-
-    void* cfg_descr;
-};

+ 108 - 89
firmware/targets/f7/furi-hal/furi-hal-vcp.c

@@ -13,17 +13,17 @@
 
 typedef enum {
     VcpEvtReserved      = (1 << 0), // Reserved for StreamBuffer internal event
-    VcpEvtConnect       = (1 << 1),
-    VcpEvtDisconnect    = (1 << 2),
-    VcpEvtEnable        = (1 << 3),
-    VcpEvtDisable       = (1 << 4),
-    VcpEvtRx            = (1 << 5),
-    VcpEvtTx            = (1 << 6),
-    VcpEvtStreamRx      = (1 << 7),
-    VcpEvtStreamTx      = (1 << 8),
+    VcpEvtEnable        = (1 << 1),
+    VcpEvtDisable       = (1 << 2),
+    VcpEvtConnect       = (1 << 3),
+    VcpEvtDisconnect    = (1 << 4),
+    VcpEvtStreamRx      = (1 << 5),
+    VcpEvtRx            = (1 << 6),
+    VcpEvtStreamTx      = (1 << 7),
+    VcpEvtTx            = (1 << 8),
 } WorkerEvtFlags;
 
-#define VCP_THREAD_FLAG_ALL (VcpEvtConnect | VcpEvtDisconnect | VcpEvtEnable | VcpEvtDisable | VcpEvtRx | VcpEvtTx | VcpEvtStreamRx | VcpEvtStreamTx)
+#define VCP_THREAD_FLAG_ALL (VcpEvtEnable | VcpEvtDisable | VcpEvtConnect | VcpEvtDisconnect | VcpEvtRx | VcpEvtTx | VcpEvtStreamRx | VcpEvtStreamTx)
 
 typedef struct {
     FuriThread* thread;
@@ -37,10 +37,10 @@ typedef struct {
 } FuriHalVcp;
 
 static int32_t vcp_worker(void* context);
-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_cdc_tx_complete(void* context);
+static void vcp_on_cdc_rx(void* context);
+static void vcp_state_callback(void* context, uint8_t state);
+static void vcp_on_cdc_control_line(void* context, uint8_t state);
 
 static CdcCallbacks cdc_cb = {
     vcp_on_cdc_tx_complete,
@@ -76,16 +76,65 @@ static int32_t vcp_worker(void* context) {
     bool tx_idle = false;
     size_t missed_rx = 0;
 
-    furi_hal_cdc_set_callbacks(VCP_IF_NUM, &cdc_cb);
+    furi_hal_cdc_set_callbacks(VCP_IF_NUM, &cdc_cb, NULL);
 
     while (1) {
         uint32_t flags = osThreadFlagsWait(VCP_THREAD_FLAG_ALL, osFlagsWaitAny, osWaitForever);
         furi_assert((flags & osFlagsError) == 0);
 
-        // New data received
+        // VCP enabled
+        if((flags & VcpEvtEnable) && !enabled){
+#ifdef FURI_HAL_USB_VCP_DEBUG
+            FURI_LOG_D(TAG, "Enable");
+#endif            
+            flags |= VcpEvtTx;
+            furi_hal_cdc_set_callbacks(VCP_IF_NUM, &cdc_cb, NULL);
+            enabled = true;
+            furi_hal_cdc_receive(VCP_IF_NUM, vcp->data_buffer, USB_CDC_PKT_LEN); // flush Rx buffer
+            if (furi_hal_cdc_get_ctrl_line_state(VCP_IF_NUM) & (1 << 0)) {
+                vcp->connected = true;
+                xStreamBufferSend(vcp->rx_stream, &ascii_soh, 1, osWaitForever);
+            }
+        }
+
+        // VCP disabled
+        if((flags & VcpEvtDisable) && enabled) {
+#ifdef FURI_HAL_USB_VCP_DEBUG            
+            FURI_LOG_D(TAG, "Disable");
+#endif            
+            enabled = false;
+            vcp->connected = false;
+            xStreamBufferReceive(vcp->tx_stream, vcp->data_buffer, USB_CDC_PKT_LEN, 0);
+            xStreamBufferSend(vcp->rx_stream, &ascii_eot, 1, osWaitForever);
+        }
+
+        // VCP session opened
+        if((flags & VcpEvtConnect) && enabled) {
+#ifdef FURI_HAL_USB_VCP_DEBUG            
+            FURI_LOG_D(TAG, "Connect");
+#endif            
+            if (vcp->connected == false) {
+                vcp->connected = true;
+                xStreamBufferSend(vcp->rx_stream, &ascii_soh, 1, osWaitForever);
+            }
+        }
+
+        // VCP session closed
+        if((flags & VcpEvtDisconnect) && enabled) {
+#ifdef FURI_HAL_USB_VCP_DEBUG            
+            FURI_LOG_D(TAG, "Disconnect");
+#endif            
+            if (vcp->connected == true) {
+                vcp->connected = false;
+                xStreamBufferReceive(vcp->tx_stream, vcp->data_buffer, USB_CDC_PKT_LEN, 0);
+                xStreamBufferSend(vcp->rx_stream, &ascii_eot, 1, osWaitForever);
+            }
+        }
+
+        // Rx buffer was read, maybe there is enough space for new data?
         if((flags & VcpEvtStreamRx) && enabled && missed_rx > 0) {
 #ifdef FURI_HAL_USB_VCP_DEBUG
-            furi_hal_console_puts("VCP StreamRx\r\n");
+            FURI_LOG_D(TAG, "StreamRx");
 #endif
             if (xStreamBufferSpacesAvailable(vcp->rx_stream) >= USB_CDC_PKT_LEN) {
                 flags |= VcpEvtRx;
@@ -93,29 +142,29 @@ static int32_t vcp_worker(void* context) {
             }
         }
 
-        // Rx buffer was read, maybe there is enough space for new data?
+        // New data received
         if((flags & VcpEvtRx)) {
             if (xStreamBufferSpacesAvailable(vcp->rx_stream) >= USB_CDC_PKT_LEN) {
                 int32_t len = furi_hal_cdc_receive(VCP_IF_NUM, vcp->data_buffer, USB_CDC_PKT_LEN);
-#ifdef FURI_HAL_USB_VCP_DEBUG
-                furi_hal_console_printf("VCP Rx %d\r\n", len);
-#endif
+#ifdef FURI_HAL_USB_VCP_DEBUG                
+                FURI_LOG_D(TAG, "Rx %d", len);
+#endif                
                 if (len > 0) {
                     furi_check(xStreamBufferSend(vcp->rx_stream, vcp->data_buffer, len, osWaitForever) == len);
                 }
             } else {
-#ifdef FURI_HAL_USB_VCP_DEBUG
-                furi_hal_console_puts("VCP Rx missed\r\n");
-#endif
+#ifdef FURI_HAL_USB_VCP_DEBUG                
+                FURI_LOG_D(TAG, "Rx missed");
+#endif                
                 missed_rx++;
             }
         }
 
         // New data in Tx buffer
         if((flags & VcpEvtStreamTx) && enabled) {
-#ifdef FURI_HAL_USB_VCP_DEBUG
-            furi_hal_console_puts("VCP StreamTx\r\n");
-#endif
+#ifdef FURI_HAL_USB_VCP_DEBUG            
+            FURI_LOG_D(TAG, "StreamTx");
+#endif            
             if (tx_idle) {
                 flags |= VcpEvtTx;
             }
@@ -124,9 +173,9 @@ static int32_t vcp_worker(void* context) {
         // CDC write transfer done
         if((flags & VcpEvtTx) && enabled) {
             size_t len = xStreamBufferReceive(vcp->tx_stream, vcp->data_buffer, USB_CDC_PKT_LEN, 0);
-#ifdef FURI_HAL_USB_VCP_DEBUG
-            furi_hal_console_printf("VCP Tx %d\r\n", len);
-#endif
+#ifdef FURI_HAL_USB_VCP_DEBUG            
+            FURI_LOG_D(TAG, "Tx %d", len);
+#endif            
             if (len > 0) { // Some data left in Tx buffer. Sending it now
                 tx_idle = false;
                 furi_hal_cdc_send(VCP_IF_NUM, vcp->data_buffer, len);
@@ -134,52 +183,6 @@ static int32_t vcp_worker(void* context) {
                 tx_idle = true;
             }
         }
-
-        // VCP session opened
-        if((flags & VcpEvtConnect) && enabled) {
-#ifdef FURI_HAL_USB_VCP_DEBUG
-            furi_hal_console_puts("VCP Connect\r\n");
-#endif
-            if (vcp->connected == false) {
-                vcp->connected = true;
-                xStreamBufferSend(vcp->rx_stream, &ascii_soh, 1, osWaitForever);
-            }
-        }
-
-        // VCP session closed
-        if((flags & VcpEvtDisconnect) && enabled) {
-#ifdef FURI_HAL_USB_VCP_DEBUG
-            furi_hal_console_puts("VCP Disconnect\r\n");
-#endif
-            if (vcp->connected == true) {
-                vcp->connected = false;
-                xStreamBufferSend(vcp->rx_stream, &ascii_eot, 1, osWaitForever);
-            }
-        }
-
-        // VCP enabled
-        if((flags & VcpEvtEnable) && !enabled){
-#ifdef FURI_HAL_USB_VCP_DEBUG
-            furi_hal_console_puts("VCP Enable\r\n");
-#endif
-            furi_hal_cdc_set_callbacks(VCP_IF_NUM, &cdc_cb);
-            enabled = true;
-            furi_hal_cdc_receive(VCP_IF_NUM, vcp->data_buffer, USB_CDC_PKT_LEN); // flush Rx buffer
-            if (furi_hal_cdc_get_ctrl_line_state(VCP_IF_NUM) & (1 << 0)) {
-                vcp->connected = true;
-                xStreamBufferSend(vcp->rx_stream, &ascii_soh, 1, osWaitForever);
-            }
-        }
-
-        // VCP disabled
-        if((flags & VcpEvtDisable) && enabled) {
-#ifdef FURI_HAL_USB_VCP_DEBUG
-            furi_hal_console_puts("VCP Disable\r\n");
-#endif
-            enabled = false;
-            vcp->connected = false;
-            xStreamBufferSend(vcp->rx_stream, &ascii_eot, 1, osWaitForever);
-        }
     }
     return 0;
 }
@@ -196,6 +199,10 @@ size_t furi_hal_vcp_rx_with_timeout(uint8_t* buffer, size_t size, uint32_t timeo
     furi_assert(vcp);
     furi_assert(buffer);
 
+#ifdef FURI_HAL_USB_VCP_DEBUG
+    FURI_LOG_D(TAG, "rx %u start", size);
+#endif    
+
     size_t rx_cnt = 0;
 
     while (size > 0) {
@@ -204,6 +211,9 @@ size_t furi_hal_vcp_rx_with_timeout(uint8_t* buffer, size_t size, uint32_t timeo
             batch_size = VCP_RX_BUF_SIZE;
 
         size_t len = xStreamBufferReceive(vcp->rx_stream, buffer, batch_size, timeout);
+#ifdef FURI_HAL_USB_VCP_DEBUG        
+        FURI_LOG_D(TAG, "%u ", batch_size);
+#endif        
         if (len == 0)
             break;
         osThreadFlagsSet(furi_thread_get_thread_id(vcp->thread), VcpEvtStreamRx);
@@ -211,6 +221,10 @@ size_t furi_hal_vcp_rx_with_timeout(uint8_t* buffer, size_t size, uint32_t timeo
         buffer += len;
         rx_cnt += len;
     }
+
+#ifdef FURI_HAL_USB_VCP_DEBUG
+    FURI_LOG_D(TAG, "rx %u end", size);
+#endif    
     return rx_cnt;
 }
 
@@ -223,34 +237,40 @@ void furi_hal_vcp_tx(const uint8_t* buffer, size_t size) {
     furi_assert(vcp);
     furi_assert(buffer);
 
-    while (size > 0) {
+#ifdef FURI_HAL_USB_VCP_DEBUG
+    FURI_LOG_D(TAG, "tx %u start", size);
+#endif    
+
+    while (size > 0 && vcp->connected) {
         size_t batch_size = size;
-        if (batch_size > VCP_TX_BUF_SIZE)
-            batch_size = VCP_TX_BUF_SIZE;
+        if (batch_size > USB_CDC_PKT_LEN)
+            batch_size = USB_CDC_PKT_LEN;
 
         xStreamBufferSend(vcp->tx_stream, buffer, batch_size, osWaitForever);
         osThreadFlagsSet(furi_thread_get_thread_id(vcp->thread), VcpEvtStreamTx);
+#ifdef FURI_HAL_USB_VCP_DEBUG
+        FURI_LOG_D(TAG, "%u ", batch_size);
+#endif        
 
         size -= batch_size;
         buffer += batch_size;
     }
-}
 
-static void vcp_state_callback(uint8_t state) {
 #ifdef FURI_HAL_USB_VCP_DEBUG
-    furi_hal_console_puts("VCP State\r\n");
-#endif
+    FURI_LOG_D(TAG, "tx %u end", size);
+#endif    
+}
+
+static void vcp_state_callback(void* context, uint8_t state) {
     if (state == 0) {
         osThreadFlagsSet(furi_thread_get_thread_id(vcp->thread), VcpEvtDisconnect);
     }
 }
 
-static void vcp_on_cdc_control_line(uint8_t state) {
+static void vcp_on_cdc_control_line(void* context, uint8_t state) {
     // bit 0: DTR state, bit 1: RTS state
     bool dtr = state & (1 << 0);
-#ifdef FURI_HAL_USB_VCP_DEBUG
-    furi_hal_console_puts("VCP CtrlLine\r\n");
-#endif
+
     if (dtr == true) {
         osThreadFlagsSet(furi_thread_get_thread_id(vcp->thread), VcpEvtConnect);
     } else {
@@ -258,12 +278,12 @@ static void vcp_on_cdc_control_line(uint8_t state) {
     }
 }
 
-static void vcp_on_cdc_rx() {
+static void vcp_on_cdc_rx(void* context) {
     uint32_t ret = osThreadFlagsSet(furi_thread_get_thread_id(vcp->thread), VcpEvtRx);
     furi_assert((ret & osFlagsError) == 0);
 }
 
-static void vcp_on_cdc_tx_complete() {
+static void vcp_on_cdc_tx_complete(void* context) {
     osThreadFlagsSet(furi_thread_get_thread_id(vcp->thread), VcpEvtTx);
 }
 
@@ -271,4 +291,3 @@ bool furi_hal_vcp_is_connected(void) {
     furi_assert(vcp);
     return vcp->connected;
 }
-

+ 1 - 1
firmware/targets/f7/furi-hal/furi-hal.c

@@ -40,7 +40,7 @@ void furi_hal_init() {
 
     // VCP + USB
     furi_hal_usb_init();
-    furi_hal_usb_set_config(UsbModeVcpSingle);
+    furi_hal_usb_set_config(&usb_cdc_single);
     furi_hal_vcp_init();
     FURI_LOG_I(TAG, "USB OK");
 

+ 22 - 11
firmware/targets/furi-hal-include/furi-hal-usb.h

@@ -2,16 +2,27 @@
 
 #include "usb.h"
 
-/** USB device modes */
-typedef enum {
-    UsbModeNone,
-    UsbModeVcpSingle,
-    UsbModeVcpDual,
-    UsbModeHid,
-    UsbModeU2F,
+typedef struct UsbInterface UsbInterface;
 
-    UsbModesNum,
-} UsbMode;
+struct UsbInterface {
+    void (*init)(usbd_device *dev, UsbInterface* intf);
+    void (*deinit)(usbd_device *dev);
+    void (*wakeup)(usbd_device *dev);
+    void (*suspend)(usbd_device *dev);
+
+    struct usb_device_descriptor* dev_descr;
+
+    void* str_manuf_descr;
+    void* str_prod_descr;
+    void* str_serial_descr;
+
+    void* cfg_descr;
+};
+
+/** USB device interface modes */
+extern UsbInterface usb_cdc_single;
+extern UsbInterface usb_cdc_dual;
+extern UsbInterface usb_hid;
 
 /** USB device low-level initialization
  */
@@ -21,13 +32,13 @@ void furi_hal_usb_init();
  *
  * @param      mode new USB device mode
  */
-void furi_hal_usb_set_config(UsbMode mode);
+void furi_hal_usb_set_config(UsbInterface* new_if);
 
 /** Get USB device configuration
  *
  * @return    current USB device mode
  */
-UsbMode furi_hal_usb_get_config();
+UsbInterface* furi_hal_usb_get_config();
 
 /** Disable USB device
  */

Некоторые файлы не были показаны из-за большого количества измененных файлов