Explorar o código

Blocking USB driver API (#2009)

* invalidate memmgt thread dict
* Core: rollback memmgt thread dict invalidation
* Dialogs: move api lock to toolbox
* HAL: blocking usb API
* HAL usb: fix api return data
* HAL usb: api optimization
* api lock: test results
* Fix build errors
* DAP Link: fix imports
* Crash when malloc in ISR
* Fix dap-link copypaste error
* Moar memory management crashes.
* Crash when malloc in IRQ, not ISR
* USB-UART: Blocking VCP mode switch

Co-authored-by: nminaylov <nm29719@gmail.com>
Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
Sergey Gavrilov %!s(int64=3) %!d(string=hai) anos
pai
achega
297f185ef4

+ 1 - 0
applications/main/gpio/gpio_app_i.h

@@ -29,6 +29,7 @@ struct GpioApp {
     GpioTest* gpio_test;
     GpioTest* gpio_test;
     GpioUsbUart* gpio_usb_uart;
     GpioUsbUart* gpio_usb_uart;
     UsbUartBridge* usb_uart_bridge;
     UsbUartBridge* usb_uart_bridge;
+    UsbUartConfig* usb_uart_cfg;
 };
 };
 
 
 typedef enum {
 typedef enum {

+ 1 - 0
applications/main/gpio/gpio_custom_event.h

@@ -9,4 +9,5 @@ typedef enum {
     GpioCustomEventErrorBack,
     GpioCustomEventErrorBack,
 
 
     GpioUsbUartEventConfig,
     GpioUsbUartEventConfig,
+    GpioUsbUartEventConfigSet,
 } GpioCustomEvent;
 } GpioCustomEvent;

+ 44 - 34
applications/main/gpio/scenes/gpio_scene_usb_uart_config.c

@@ -9,8 +9,6 @@ typedef enum {
     UsbUartLineIndexFlow,
     UsbUartLineIndexFlow,
 } LineIndex;
 } LineIndex;
 
 
-static UsbUartConfig* cfg_set;
-
 static const char* vcp_ch[] = {"0 (CLI)", "1"};
 static const char* vcp_ch[] = {"0 (CLI)", "1"};
 static const char* uart_ch[] = {"13,14", "15,16"};
 static const char* uart_ch[] = {"13,14", "15,16"};
 static const char* flow_pins[] = {"None", "2,3", "6,7", "16,15"};
 static const char* flow_pins[] = {"None", "2,3", "6,7", "16,15"};
@@ -28,8 +26,14 @@ static const uint32_t baudrate_list[] = {
 };
 };
 
 
 bool gpio_scene_usb_uart_cfg_on_event(void* context, SceneManagerEvent event) {
 bool gpio_scene_usb_uart_cfg_on_event(void* context, SceneManagerEvent event) {
-    UNUSED(context);
-    UNUSED(event);
+    GpioApp* app = context;
+    furi_assert(app);
+    if(event.type == SceneManagerEventTypeCustom) {
+        if(event.event == GpioUsbUartEventConfigSet) {
+            usb_uart_set_config(app->usb_uart_bridge, app->usb_uart_cfg);
+            return true;
+        }
+    }
     return false;
     return false;
 }
 }
 
 
@@ -38,55 +42,59 @@ void line_ensure_flow_invariant(GpioApp* app) {
     // selected. This function enforces that invariant by resetting flow_pins
     // selected. This function enforces that invariant by resetting flow_pins
     // to None if it is configured to 16,15 when LPUART is selected.
     // to None if it is configured to 16,15 when LPUART is selected.
 
 
-    uint8_t available_flow_pins = cfg_set->uart_ch == FuriHalUartIdLPUART1 ? 3 : 4;
+    uint8_t available_flow_pins = app->usb_uart_cfg->uart_ch == FuriHalUartIdLPUART1 ? 3 : 4;
     VariableItem* item = app->var_item_flow;
     VariableItem* item = app->var_item_flow;
     variable_item_set_values_count(item, available_flow_pins);
     variable_item_set_values_count(item, available_flow_pins);
 
 
-    if(cfg_set->flow_pins >= available_flow_pins) {
-        cfg_set->flow_pins = 0;
-        usb_uart_set_config(app->usb_uart_bridge, cfg_set);
+    if(app->usb_uart_cfg->flow_pins >= available_flow_pins) {
+        app->usb_uart_cfg->flow_pins = 0;
 
 
-        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_set_current_value_index(item, app->usb_uart_cfg->flow_pins);
+        variable_item_set_current_value_text(item, flow_pins[app->usb_uart_cfg->flow_pins]);
     }
     }
 }
 }
 
 
 static void line_vcp_cb(VariableItem* item) {
 static void line_vcp_cb(VariableItem* item) {
     GpioApp* app = variable_item_get_context(item);
     GpioApp* app = variable_item_get_context(item);
+    furi_assert(app);
     uint8_t index = variable_item_get_current_value_index(item);
     uint8_t index = variable_item_get_current_value_index(item);
 
 
     variable_item_set_current_value_text(item, vcp_ch[index]);
     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);
+    app->usb_uart_cfg->vcp_ch = index;
+    view_dispatcher_send_custom_event(app->view_dispatcher, GpioUsbUartEventConfigSet);
 }
 }
 
 
 static void line_port_cb(VariableItem* item) {
 static void line_port_cb(VariableItem* item) {
     GpioApp* app = variable_item_get_context(item);
     GpioApp* app = variable_item_get_context(item);
+    furi_assert(app);
     uint8_t index = variable_item_get_current_value_index(item);
     uint8_t index = variable_item_get_current_value_index(item);
 
 
     variable_item_set_current_value_text(item, uart_ch[index]);
     variable_item_set_current_value_text(item, uart_ch[index]);
 
 
     if(index == 0)
     if(index == 0)
-        cfg_set->uart_ch = FuriHalUartIdUSART1;
+        app->usb_uart_cfg->uart_ch = FuriHalUartIdUSART1;
     else if(index == 1)
     else if(index == 1)
-        cfg_set->uart_ch = FuriHalUartIdLPUART1;
-    usb_uart_set_config(app->usb_uart_bridge, cfg_set);
+        app->usb_uart_cfg->uart_ch = FuriHalUartIdLPUART1;
+
     line_ensure_flow_invariant(app);
     line_ensure_flow_invariant(app);
+    view_dispatcher_send_custom_event(app->view_dispatcher, GpioUsbUartEventConfigSet);
 }
 }
 
 
 static void line_flow_cb(VariableItem* item) {
 static void line_flow_cb(VariableItem* item) {
     GpioApp* app = variable_item_get_context(item);
     GpioApp* app = variable_item_get_context(item);
+    furi_assert(app);
     uint8_t index = variable_item_get_current_value_index(item);
     uint8_t index = variable_item_get_current_value_index(item);
 
 
     variable_item_set_current_value_text(item, flow_pins[index]);
     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);
+    app->usb_uart_cfg->flow_pins = index;
+    view_dispatcher_send_custom_event(app->view_dispatcher, GpioUsbUartEventConfigSet);
 }
 }
 
 
 static void line_baudrate_cb(VariableItem* item) {
 static void line_baudrate_cb(VariableItem* item) {
     GpioApp* app = variable_item_get_context(item);
     GpioApp* app = variable_item_get_context(item);
+    furi_assert(app);
     uint8_t index = variable_item_get_current_value_index(item);
     uint8_t index = variable_item_get_current_value_index(item);
 
 
     char br_text[8];
     char br_text[8];
@@ -94,28 +102,29 @@ static void line_baudrate_cb(VariableItem* item) {
     if(index > 0) {
     if(index > 0) {
         snprintf(br_text, 7, "%lu", baudrate_list[index - 1]);
         snprintf(br_text, 7, "%lu", baudrate_list[index - 1]);
         variable_item_set_current_value_text(item, br_text);
         variable_item_set_current_value_text(item, br_text);
-        cfg_set->baudrate = baudrate_list[index - 1];
+        app->usb_uart_cfg->baudrate = baudrate_list[index - 1];
     } else {
     } else {
         variable_item_set_current_value_text(item, baudrate_mode[index]);
         variable_item_set_current_value_text(item, baudrate_mode[index]);
-        cfg_set->baudrate = 0;
+        app->usb_uart_cfg->baudrate = 0;
     }
     }
-    cfg_set->baudrate_mode = index;
-    usb_uart_set_config(app->usb_uart_bridge, cfg_set);
+    app->usb_uart_cfg->baudrate_mode = index;
+    view_dispatcher_send_custom_event(app->view_dispatcher, GpioUsbUartEventConfigSet);
 }
 }
 
 
 void gpio_scene_usb_uart_cfg_on_enter(void* context) {
 void gpio_scene_usb_uart_cfg_on_enter(void* context) {
     GpioApp* app = context;
     GpioApp* app = context;
+    furi_assert(app);
     VariableItemList* var_item_list = app->var_item_list;
     VariableItemList* var_item_list = app->var_item_list;
 
 
-    cfg_set = malloc(sizeof(UsbUartConfig));
-    usb_uart_get_config(app->usb_uart_bridge, cfg_set);
+    app->usb_uart_cfg = malloc(sizeof(UsbUartConfig));
+    usb_uart_get_config(app->usb_uart_bridge, app->usb_uart_cfg);
 
 
     VariableItem* item;
     VariableItem* item;
     char br_text[8];
     char br_text[8];
 
 
     item = variable_item_list_add(var_item_list, "USB Channel", 2, line_vcp_cb, app);
     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]);
+    variable_item_set_current_value_index(item, app->usb_uart_cfg->vcp_ch);
+    variable_item_set_current_value_text(item, vcp_ch[app->usb_uart_cfg->vcp_ch]);
 
 
     item = variable_item_list_add(
     item = variable_item_list_add(
         var_item_list,
         var_item_list,
@@ -123,22 +132,23 @@ void gpio_scene_usb_uart_cfg_on_enter(void* context) {
         sizeof(baudrate_list) / sizeof(baudrate_list[0]) + 1,
         sizeof(baudrate_list) / sizeof(baudrate_list[0]) + 1,
         line_baudrate_cb,
         line_baudrate_cb,
         app);
         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_index(item, app->usb_uart_cfg->baudrate_mode);
+    if(app->usb_uart_cfg->baudrate_mode > 0) {
+        snprintf(br_text, 7, "%lu", baudrate_list[app->usb_uart_cfg->baudrate_mode - 1]);
         variable_item_set_current_value_text(item, br_text);
         variable_item_set_current_value_text(item, br_text);
     } else {
     } else {
-        variable_item_set_current_value_text(item, baudrate_mode[cfg_set->baudrate_mode]);
+        variable_item_set_current_value_text(
+            item, baudrate_mode[app->usb_uart_cfg->baudrate_mode]);
     }
     }
 
 
     item = variable_item_list_add(var_item_list, "UART Pins", 2, line_port_cb, app);
     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]);
+    variable_item_set_current_value_index(item, app->usb_uart_cfg->uart_ch);
+    variable_item_set_current_value_text(item, uart_ch[app->usb_uart_cfg->uart_ch]);
 
 
     item = variable_item_list_add(
     item = variable_item_list_add(
         var_item_list, "RTS/DTR Pins", COUNT_OF(flow_pins), line_flow_cb, app);
         var_item_list, "RTS/DTR Pins", COUNT_OF(flow_pins), 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_set_current_value_index(item, app->usb_uart_cfg->flow_pins);
+    variable_item_set_current_value_text(item, flow_pins[app->usb_uart_cfg->flow_pins]);
     app->var_item_flow = item;
     app->var_item_flow = item;
     line_ensure_flow_invariant(app);
     line_ensure_flow_invariant(app);
 
 
@@ -155,5 +165,5 @@ void gpio_scene_usb_uart_cfg_on_exit(void* context) {
         GpioAppViewUsbUartCfg,
         GpioAppViewUsbUartCfg,
         variable_item_list_get_selected_item_index(app->var_item_list));
         variable_item_list_get_selected_item_index(app->var_item_list));
     variable_item_list_reset(app->var_item_list);
     variable_item_list_reset(app->var_item_list);
-    free(cfg_set);
+    free(app->usb_uart_cfg);
 }
 }

+ 6 - 0
applications/main/gpio/usb_uart_bridge.c

@@ -3,6 +3,7 @@
 #include <furi_hal_usb_cdc.h>
 #include <furi_hal_usb_cdc.h>
 #include "usb_cdc.h"
 #include "usb_cdc.h"
 #include "cli/cli_vcp.h"
 #include "cli/cli_vcp.h"
+#include <toolbox/api_lock.h>
 #include "cli/cli.h"
 #include "cli/cli.h"
 
 
 #define USB_CDC_PKT_LEN CDC_DATA_SZ
 #define USB_CDC_PKT_LEN CDC_DATA_SZ
@@ -51,6 +52,8 @@ struct UsbUartBridge {
 
 
     UsbUartState st;
     UsbUartState st;
 
 
+    FuriApiLock cfg_lock;
+
     uint8_t rx_buf[USB_CDC_PKT_LEN];
     uint8_t rx_buf[USB_CDC_PKT_LEN];
 };
 };
 
 
@@ -244,6 +247,7 @@ static int32_t usb_uart_worker(void* context) {
                 usb_uart->cfg.flow_pins = usb_uart->cfg_new.flow_pins;
                 usb_uart->cfg.flow_pins = usb_uart->cfg_new.flow_pins;
                 events |= WorkerEvtCtrlLineSet;
                 events |= WorkerEvtCtrlLineSet;
             }
             }
+            api_lock_unlock(usb_uart->cfg_lock);
         }
         }
         if(events & WorkerEvtLineCfgSet) {
         if(events & WorkerEvtLineCfgSet) {
             if(usb_uart->cfg.baudrate == 0)
             if(usb_uart->cfg.baudrate == 0)
@@ -352,8 +356,10 @@ void usb_uart_disable(UsbUartBridge* usb_uart) {
 void usb_uart_set_config(UsbUartBridge* usb_uart, UsbUartConfig* cfg) {
 void usb_uart_set_config(UsbUartBridge* usb_uart, UsbUartConfig* cfg) {
     furi_assert(usb_uart);
     furi_assert(usb_uart);
     furi_assert(cfg);
     furi_assert(cfg);
+    usb_uart->cfg_lock = api_lock_alloc_locked();
     memcpy(&(usb_uart->cfg_new), cfg, sizeof(UsbUartConfig));
     memcpy(&(usb_uart->cfg_new), cfg, sizeof(UsbUartConfig));
     furi_thread_flags_set(furi_thread_get_id(usb_uart->thread), WorkerEvtCfgChange);
     furi_thread_flags_set(furi_thread_get_id(usb_uart->thread), WorkerEvtCfgChange);
+    api_lock_wait_unlock_and_free(usb_uart->cfg_lock);
 }
 }
 
 
 void usb_uart_get_config(UsbUartBridge* usb_uart, UsbUartConfig* cfg) {
 void usb_uart_get_config(UsbUartBridge* usb_uart, UsbUartConfig* cfg) {

+ 0 - 1
applications/plugins/dap_link/dap_link.c

@@ -247,7 +247,6 @@ static int32_t dap_process(void* p) {
 
 
     // deinit usb
     // deinit usb
     furi_hal_usb_set_config(usb_config_prev, NULL);
     furi_hal_usb_set_config(usb_config_prev, NULL);
-    dap_common_wait_for_deinit();
     dap_common_usb_free_name();
     dap_common_usb_free_name();
     dap_deinit_gpio(swd_pins_prev);
     dap_deinit_gpio(swd_pins_prev);
     return 0;
     return 0;

+ 0 - 17
applications/plugins/dap_link/usb/dap_v2_usb.c

@@ -618,23 +618,12 @@ static void hid_init(usbd_device* dev, FuriHalUsbInterface* intf, void* ctx) {
     if(dap_state.semaphore_v2 == NULL) dap_state.semaphore_v2 = furi_semaphore_alloc(1, 1);
     if(dap_state.semaphore_v2 == NULL) dap_state.semaphore_v2 = furi_semaphore_alloc(1, 1);
     if(dap_state.semaphore_cdc == NULL) dap_state.semaphore_cdc = furi_semaphore_alloc(1, 1);
     if(dap_state.semaphore_cdc == NULL) dap_state.semaphore_cdc = furi_semaphore_alloc(1, 1);
 
 
-    usb_hid.dev_descr->idVendor = DAP_HID_VID;
-    usb_hid.dev_descr->idProduct = DAP_HID_PID;
-
     usbd_reg_config(dev, hid_ep_config);
     usbd_reg_config(dev, hid_ep_config);
     usbd_reg_control(dev, hid_control);
     usbd_reg_control(dev, hid_control);
 
 
     usbd_connect(dev, true);
     usbd_connect(dev, true);
 }
 }
 
 
-static bool deinit_flag = false;
-
-void dap_common_wait_for_deinit() {
-    while(!deinit_flag) {
-        furi_delay_ms(50);
-    }
-}
-
 static void hid_deinit(usbd_device* dev) {
 static void hid_deinit(usbd_device* dev) {
     dap_state.usb_dev = NULL;
     dap_state.usb_dev = NULL;
 
 
@@ -647,12 +636,6 @@ static void hid_deinit(usbd_device* dev) {
 
 
     usbd_reg_config(dev, NULL);
     usbd_reg_config(dev, NULL);
     usbd_reg_control(dev, NULL);
     usbd_reg_control(dev, NULL);
-
-    free(usb_hid.str_manuf_descr);
-    free(usb_hid.str_prod_descr);
-
-    FURI_SW_MEMBARRIER();
-    deinit_flag = true;
 }
 }
 
 
 static void hid_on_wakeup(usbd_device* dev) {
 static void hid_on_wakeup(usbd_device* dev) {

+ 0 - 2
applications/plugins/dap_link/usb/dap_v2_usb.h

@@ -51,5 +51,3 @@ void dap_common_usb_set_state_callback(DapStateCallback callback);
 void dap_common_usb_alloc_name(const char* name);
 void dap_common_usb_alloc_name(const char* name);
 
 
 void dap_common_usb_free_name();
 void dap_common_usb_free_name();
-
-void dap_common_wait_for_deinit();

+ 2 - 2
applications/services/dialogs/dialogs.c

@@ -1,6 +1,6 @@
 #include "dialogs/dialogs_message.h"
 #include "dialogs/dialogs_message.h"
 #include "dialogs_i.h"
 #include "dialogs_i.h"
-#include "dialogs_api_lock.h"
+#include <toolbox/api_lock.h>
 #include "dialogs_module_file_browser.h"
 #include "dialogs_module_file_browser.h"
 #include "dialogs_module_message.h"
 #include "dialogs_module_message.h"
 
 
@@ -35,7 +35,7 @@ static void dialogs_app_process_message(DialogsApp* app, DialogsAppMessage* mess
             dialogs_app_process_module_message(&message->data->dialog);
             dialogs_app_process_module_message(&message->data->dialog);
         break;
         break;
     }
     }
-    API_LOCK_UNLOCK(message->lock);
+    api_lock_unlock(message->lock);
 }
 }
 
 
 int32_t dialogs_srv(void* p) {
 int32_t dialogs_srv(void* p) {

+ 5 - 5
applications/services/dialogs/dialogs_api.c

@@ -1,6 +1,6 @@
 #include "dialogs/dialogs_message.h"
 #include "dialogs/dialogs_message.h"
 #include "dialogs_i.h"
 #include "dialogs_i.h"
-#include "dialogs_api_lock.h"
+#include <toolbox/api_lock.h>
 #include <assets_icons.h>
 #include <assets_icons.h>
 
 
 /****************** File browser ******************/
 /****************** File browser ******************/
@@ -10,7 +10,7 @@ bool dialog_file_browser_show(
     FuriString* result_path,
     FuriString* result_path,
     FuriString* path,
     FuriString* path,
     const DialogsFileBrowserOptions* options) {
     const DialogsFileBrowserOptions* options) {
-    FuriApiLock lock = API_LOCK_INIT_LOCKED();
+    FuriApiLock lock = api_lock_alloc_locked();
     furi_check(lock != NULL);
     furi_check(lock != NULL);
 
 
     DialogsAppData data = {
     DialogsAppData data = {
@@ -35,7 +35,7 @@ bool dialog_file_browser_show(
 
 
     furi_check(
     furi_check(
         furi_message_queue_put(context->message_queue, &message, FuriWaitForever) == FuriStatusOk);
         furi_message_queue_put(context->message_queue, &message, FuriWaitForever) == FuriStatusOk);
-    API_LOCK_WAIT_UNTIL_UNLOCK_AND_FREE(lock);
+    api_lock_wait_unlock_and_free(lock);
 
 
     return return_data.bool_value;
     return return_data.bool_value;
 }
 }
@@ -43,7 +43,7 @@ bool dialog_file_browser_show(
 /****************** Message ******************/
 /****************** Message ******************/
 
 
 DialogMessageButton dialog_message_show(DialogsApp* context, const DialogMessage* dialog_message) {
 DialogMessageButton dialog_message_show(DialogsApp* context, const DialogMessage* dialog_message) {
-    FuriApiLock lock = API_LOCK_INIT_LOCKED();
+    FuriApiLock lock = api_lock_alloc_locked();
     furi_check(lock != NULL);
     furi_check(lock != NULL);
 
 
     DialogsAppData data = {
     DialogsAppData data = {
@@ -61,7 +61,7 @@ DialogMessageButton dialog_message_show(DialogsApp* context, const DialogMessage
 
 
     furi_check(
     furi_check(
         furi_message_queue_put(context->message_queue, &message, FuriWaitForever) == FuriStatusOk);
         furi_message_queue_put(context->message_queue, &message, FuriWaitForever) == FuriStatusOk);
-    API_LOCK_WAIT_UNTIL_UNLOCK_AND_FREE(lock);
+    api_lock_wait_unlock_and_free(lock);
 
 
     return return_data.dialog_value;
     return return_data.dialog_value;
 }
 }

+ 0 - 18
applications/services/dialogs/dialogs_api_lock.h

@@ -1,18 +0,0 @@
-#pragma once
-
-typedef FuriEventFlag* FuriApiLock;
-
-#define API_LOCK_EVENT (1U << 0)
-
-#define API_LOCK_INIT_LOCKED() furi_event_flag_alloc();
-
-#define API_LOCK_WAIT_UNTIL_UNLOCK(_lock) \
-    furi_event_flag_wait(_lock, API_LOCK_EVENT, FuriFlagWaitAny, FuriWaitForever);
-
-#define API_LOCK_FREE(_lock) furi_event_flag_free(_lock);
-
-#define API_LOCK_UNLOCK(_lock) furi_event_flag_set(_lock, API_LOCK_EVENT);
-
-#define API_LOCK_WAIT_UNTIL_UNLOCK_AND_FREE(_lock) \
-    API_LOCK_WAIT_UNTIL_UNLOCK(_lock);             \
-    API_LOCK_FREE(_lock);

+ 1 - 1
applications/services/dialogs/dialogs_message.h

@@ -1,7 +1,7 @@
 #pragma once
 #pragma once
 #include <furi.h>
 #include <furi.h>
 #include "dialogs_i.h"
 #include "dialogs_i.h"
-#include "dialogs_api_lock.h"
+#include <toolbox/api_lock.h>
 
 
 #ifdef __cplusplus
 #ifdef __cplusplus
 extern "C" {
 extern "C" {

+ 6 - 6
applications/services/dialogs/dialogs_module_file_browser.c

@@ -1,5 +1,5 @@
 #include "dialogs_i.h"
 #include "dialogs_i.h"
-#include "dialogs_api_lock.h"
+#include <toolbox/api_lock.h>
 #include "gui/modules/file_browser.h"
 #include "gui/modules/file_browser.h"
 
 
 typedef struct {
 typedef struct {
@@ -11,14 +11,14 @@ static void dialogs_app_file_browser_back_callback(void* context) {
     furi_assert(context);
     furi_assert(context);
     DialogsAppFileBrowserContext* file_browser_context = context;
     DialogsAppFileBrowserContext* file_browser_context = context;
     file_browser_context->result = false;
     file_browser_context->result = false;
-    API_LOCK_UNLOCK(file_browser_context->lock);
+    api_lock_unlock(file_browser_context->lock);
 }
 }
 
 
 static void dialogs_app_file_browser_callback(void* context) {
 static void dialogs_app_file_browser_callback(void* context) {
     furi_assert(context);
     furi_assert(context);
     DialogsAppFileBrowserContext* file_browser_context = context;
     DialogsAppFileBrowserContext* file_browser_context = context;
     file_browser_context->result = true;
     file_browser_context->result = true;
-    API_LOCK_UNLOCK(file_browser_context->lock);
+    api_lock_unlock(file_browser_context->lock);
 }
 }
 
 
 bool dialogs_app_process_module_file_browser(const DialogsAppMessageDataFileBrowser* data) {
 bool dialogs_app_process_module_file_browser(const DialogsAppMessageDataFileBrowser* data) {
@@ -27,7 +27,7 @@ bool dialogs_app_process_module_file_browser(const DialogsAppMessageDataFileBrow
 
 
     DialogsAppFileBrowserContext* file_browser_context =
     DialogsAppFileBrowserContext* file_browser_context =
         malloc(sizeof(DialogsAppFileBrowserContext));
         malloc(sizeof(DialogsAppFileBrowserContext));
-    file_browser_context->lock = API_LOCK_INIT_LOCKED();
+    file_browser_context->lock = api_lock_alloc_locked();
 
 
     ViewHolder* view_holder = view_holder_alloc();
     ViewHolder* view_holder = view_holder_alloc();
     view_holder_attach_to_gui(view_holder, gui);
     view_holder_attach_to_gui(view_holder, gui);
@@ -44,7 +44,7 @@ bool dialogs_app_process_module_file_browser(const DialogsAppMessageDataFileBrow
 
 
     view_holder_set_view(view_holder, file_browser_get_view(file_browser));
     view_holder_set_view(view_holder, file_browser_get_view(file_browser));
     view_holder_start(view_holder);
     view_holder_start(view_holder);
-    API_LOCK_WAIT_UNTIL_UNLOCK(file_browser_context->lock);
+    api_lock_wait_unlock(file_browser_context->lock);
 
 
     ret = file_browser_context->result;
     ret = file_browser_context->result;
 
 
@@ -52,7 +52,7 @@ bool dialogs_app_process_module_file_browser(const DialogsAppMessageDataFileBrow
     view_holder_free(view_holder);
     view_holder_free(view_holder);
     file_browser_stop(file_browser);
     file_browser_stop(file_browser);
     file_browser_free(file_browser);
     file_browser_free(file_browser);
-    API_LOCK_FREE(file_browser_context->lock);
+    api_lock_free(file_browser_context->lock);
     free(file_browser_context);
     free(file_browser_context);
     furi_record_close(RECORD_GUI);
     furi_record_close(RECORD_GUI);
 
 

+ 6 - 6
applications/services/dialogs/dialogs_module_message.c

@@ -1,5 +1,5 @@
 #include "dialogs_i.h"
 #include "dialogs_i.h"
-#include "dialogs_api_lock.h"
+#include <toolbox/api_lock.h>
 #include <gui/modules/dialog_ex.h>
 #include <gui/modules/dialog_ex.h>
 
 
 typedef struct {
 typedef struct {
@@ -30,7 +30,7 @@ static void dialogs_app_message_back_callback(void* context) {
     furi_assert(context);
     furi_assert(context);
     DialogsAppMessageContext* message_context = context;
     DialogsAppMessageContext* message_context = context;
     message_context->result = DialogMessageButtonBack;
     message_context->result = DialogMessageButtonBack;
-    API_LOCK_UNLOCK(message_context->lock);
+    api_lock_unlock(message_context->lock);
 }
 }
 
 
 static void dialogs_app_message_callback(DialogExResult result, void* context) {
 static void dialogs_app_message_callback(DialogExResult result, void* context) {
@@ -49,7 +49,7 @@ static void dialogs_app_message_callback(DialogExResult result, void* context) {
     default:
     default:
         break;
         break;
     }
     }
-    API_LOCK_UNLOCK(message_context->lock);
+    api_lock_unlock(message_context->lock);
 }
 }
 
 
 DialogMessageButton dialogs_app_process_module_message(const DialogsAppMessageDataDialog* data) {
 DialogMessageButton dialogs_app_process_module_message(const DialogsAppMessageDataDialog* data) {
@@ -57,7 +57,7 @@ DialogMessageButton dialogs_app_process_module_message(const DialogsAppMessageDa
     Gui* gui = furi_record_open(RECORD_GUI);
     Gui* gui = furi_record_open(RECORD_GUI);
     const DialogMessage* message = data->message;
     const DialogMessage* message = data->message;
     DialogsAppMessageContext* message_context = malloc(sizeof(DialogsAppMessageContext));
     DialogsAppMessageContext* message_context = malloc(sizeof(DialogsAppMessageContext));
-    message_context->lock = API_LOCK_INIT_LOCKED();
+    message_context->lock = api_lock_alloc_locked();
 
 
     ViewHolder* view_holder = view_holder_alloc();
     ViewHolder* view_holder = view_holder_alloc();
     view_holder_attach_to_gui(view_holder, gui);
     view_holder_attach_to_gui(view_holder, gui);
@@ -87,14 +87,14 @@ DialogMessageButton dialogs_app_process_module_message(const DialogsAppMessageDa
 
 
     view_holder_set_view(view_holder, dialog_ex_get_view(dialog_ex));
     view_holder_set_view(view_holder, dialog_ex_get_view(dialog_ex));
     view_holder_start(view_holder);
     view_holder_start(view_holder);
-    API_LOCK_WAIT_UNTIL_UNLOCK(message_context->lock);
+    api_lock_wait_unlock(message_context->lock);
 
 
     ret = message_context->result;
     ret = message_context->result;
 
 
     view_holder_stop(view_holder);
     view_holder_stop(view_holder);
     view_holder_free(view_holder);
     view_holder_free(view_holder);
     dialog_ex_free(dialog_ex);
     dialog_ex_free(dialog_ex);
-    API_LOCK_FREE(message_context->lock);
+    api_lock_free(message_context->lock);
     free(message_context);
     free(message_context);
     furi_record_close(RECORD_GUI);
     furi_record_close(RECORD_GUI);
 
 

+ 4 - 0
firmware/targets/f7/furi_hal/furi_hal_memory.c

@@ -81,6 +81,10 @@ void furi_hal_memory_init() {
 }
 }
 
 
 void* furi_hal_memory_alloc(size_t size) {
 void* furi_hal_memory_alloc(size_t size) {
+    if(FURI_IS_IRQ_MODE()) {
+        furi_crash("memmgt in ISR");
+    }
+
     if(furi_hal_memory == NULL) {
     if(furi_hal_memory == NULL) {
         return NULL;
         return NULL;
     }
     }

+ 291 - 136
firmware/targets/f7/furi_hal/furi_hal_usb.c

@@ -4,6 +4,7 @@
 #include <furi_hal_power.h>
 #include <furi_hal_power.h>
 #include <stm32wbxx_ll_pwr.h>
 #include <stm32wbxx_ll_pwr.h>
 #include <furi.h>
 #include <furi.h>
+#include <toolbox/api_lock.h>
 
 
 #include "usb.h"
 #include "usb.h"
 
 
@@ -11,35 +12,67 @@
 
 
 #define USB_RECONNECT_DELAY 500
 #define USB_RECONNECT_DELAY 500
 
 
+typedef enum {
+    UsbApiEventTypeSetConfig,
+    UsbApiEventTypeGetConfig,
+    UsbApiEventTypeLock,
+    UsbApiEventTypeUnlock,
+    UsbApiEventTypeIsLocked,
+    UsbApiEventTypeEnable,
+    UsbApiEventTypeDisable,
+    UsbApiEventTypeReinit,
+    UsbApiEventTypeSetStateCallback,
+} UsbApiEventType;
+
+typedef struct {
+    FuriHalUsbStateCallback callback;
+    void* context;
+} UsbApiEventDataStateCallback;
+
+typedef struct {
+    FuriHalUsbInterface* interface;
+    void* context;
+} UsbApiEventDataInterface;
+
+typedef union {
+    UsbApiEventDataStateCallback state_callback;
+    UsbApiEventDataInterface interface;
+} UsbApiEventData;
+
+typedef union {
+    bool bool_value;
+    void* void_value;
+} UsbApiEventReturnData;
+
+typedef struct {
+    FuriApiLock lock;
+    UsbApiEventType type;
+    UsbApiEventData data;
+    UsbApiEventReturnData* return_data;
+} UsbApiEventMessage;
+
 typedef struct {
 typedef struct {
     FuriThread* thread;
     FuriThread* thread;
+    FuriMessageQueue* queue;
     bool enabled;
     bool enabled;
     bool connected;
     bool connected;
     bool mode_lock;
     bool mode_lock;
-    FuriHalUsbInterface* if_cur;
-    FuriHalUsbInterface* if_next;
-    void* if_ctx;
+    bool request_pending;
+    FuriHalUsbInterface* interface;
+    void* interface_context;
     FuriHalUsbStateCallback callback;
     FuriHalUsbStateCallback callback;
-    void* cb_ctx;
+    void* callback_context;
 } UsbSrv;
 } UsbSrv;
 
 
 typedef enum {
 typedef enum {
-    EventModeChange = (1 << 0),
-    EventEnable = (1 << 1),
-    EventDisable = (1 << 2),
-    EventReinit = (1 << 3),
-
-    EventReset = (1 << 4),
-    EventRequest = (1 << 5),
-
-    EventModeChangeStart = (1 << 6),
+    UsbEventReset = (1 << 0),
+    UsbEventRequest = (1 << 1),
+    UsbEventMessage = (1 << 2),
 } UsbEvent;
 } UsbEvent;
 
 
-#define USB_SRV_ALL_EVENTS                                                                    \
-    (EventModeChange | EventEnable | EventDisable | EventReinit | EventReset | EventRequest | \
-     EventModeChangeStart)
+#define USB_SRV_ALL_EVENTS (UsbEventReset | UsbEventRequest | UsbEventMessage)
 
 
-static UsbSrv usb;
+PLACE_IN_SECTION("MB_MEM2") static UsbSrv usb = {0};
 
 
 static const struct usb_string_descriptor dev_lang_desc = USB_ARRAY_DESC(USB_LANGID_ENG_US);
 static const struct usb_string_descriptor dev_lang_desc = USB_ARRAY_DESC(USB_LANGID_ENG_US);
 
 
@@ -74,12 +107,13 @@ void furi_hal_usb_init(void) {
     // Reset callback will be enabled after first mode change to avoid getting false reset events
     // Reset callback will be enabled after first mode change to avoid getting false reset events
 
 
     usb.enabled = false;
     usb.enabled = false;
-    usb.if_cur = NULL;
+    usb.interface = NULL;
     NVIC_SetPriority(USB_LP_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0));
     NVIC_SetPriority(USB_LP_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0));
     NVIC_SetPriority(USB_HP_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 15, 0));
     NVIC_SetPriority(USB_HP_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 15, 0));
     NVIC_EnableIRQ(USB_LP_IRQn);
     NVIC_EnableIRQ(USB_LP_IRQn);
     NVIC_EnableIRQ(USB_HP_IRQn);
     NVIC_EnableIRQ(USB_HP_IRQn);
 
 
+    usb.queue = furi_message_queue_alloc(1, sizeof(UsbApiEventMessage));
     usb.thread = furi_thread_alloc_ex("UsbDriver", 1024, furi_hal_usb_thread, NULL);
     usb.thread = furi_thread_alloc_ex("UsbDriver", 1024, furi_hal_usb_thread, NULL);
     furi_thread_mark_as_service(usb.thread);
     furi_thread_mark_as_service(usb.thread);
     furi_thread_start(usb.thread);
     furi_thread_start(usb.thread);
@@ -87,53 +121,119 @@ void furi_hal_usb_init(void) {
     FURI_LOG_I(TAG, "Init OK");
     FURI_LOG_I(TAG, "Init OK");
 }
 }
 
 
-bool furi_hal_usb_set_config(FuriHalUsbInterface* new_if, void* ctx) {
-    if(usb.mode_lock) {
-        return false;
-    }
+static void furi_hal_usb_send_message(UsbApiEventMessage* message) {
+    furi_message_queue_put(usb.queue, message, FuriWaitForever);
+    furi_thread_flags_set(furi_thread_get_id(usb.thread), UsbEventMessage);
+    api_lock_wait_unlock_and_free(message->lock);
+}
 
 
-    usb.if_next = new_if;
-    usb.if_ctx = ctx;
-    if(usb.thread == NULL) {
-        // Service thread hasn't started yet, so just save interface mode
-        return true;
-    }
-    furi_assert(usb.thread);
-    furi_thread_flags_set(furi_thread_get_id(usb.thread), EventModeChange);
-    return true;
+bool furi_hal_usb_set_config(FuriHalUsbInterface* new_if, void* ctx) {
+    UsbApiEventReturnData return_data = {
+        .bool_value = false,
+    };
+
+    UsbApiEventMessage msg = {
+        .lock = api_lock_alloc_locked(),
+        .type = UsbApiEventTypeSetConfig,
+        .data.interface =
+            {
+                .interface = new_if,
+                .context = ctx,
+            },
+        .return_data = &return_data,
+    };
+
+    furi_hal_usb_send_message(&msg);
+    return return_data.bool_value;
 }
 }
 
 
 FuriHalUsbInterface* furi_hal_usb_get_config() {
 FuriHalUsbInterface* furi_hal_usb_get_config() {
-    return usb.if_cur;
+    UsbApiEventReturnData return_data = {
+        .void_value = NULL,
+    };
+
+    UsbApiEventMessage msg = {
+        .lock = api_lock_alloc_locked(),
+        .type = UsbApiEventTypeGetConfig,
+        .return_data = &return_data,
+    };
+
+    furi_hal_usb_send_message(&msg);
+    return return_data.void_value;
 }
 }
 
 
 void furi_hal_usb_lock() {
 void furi_hal_usb_lock() {
-    FURI_LOG_I(TAG, "Mode lock");
-    usb.mode_lock = true;
+    UsbApiEventMessage msg = {
+        .lock = api_lock_alloc_locked(),
+        .type = UsbApiEventTypeLock,
+    };
+
+    furi_hal_usb_send_message(&msg);
 }
 }
 
 
 void furi_hal_usb_unlock() {
 void furi_hal_usb_unlock() {
-    FURI_LOG_I(TAG, "Mode unlock");
-    usb.mode_lock = false;
+    UsbApiEventMessage msg = {
+        .lock = api_lock_alloc_locked(),
+        .type = UsbApiEventTypeUnlock,
+    };
+
+    furi_hal_usb_send_message(&msg);
 }
 }
 
 
 bool furi_hal_usb_is_locked() {
 bool furi_hal_usb_is_locked() {
-    return usb.mode_lock;
+    UsbApiEventReturnData return_data = {
+        .bool_value = false,
+    };
+
+    UsbApiEventMessage msg = {
+        .lock = api_lock_alloc_locked(),
+        .type = UsbApiEventTypeIsLocked,
+        .return_data = &return_data,
+    };
+
+    furi_hal_usb_send_message(&msg);
+    return return_data.bool_value;
 }
 }
 
 
 void furi_hal_usb_disable() {
 void furi_hal_usb_disable() {
-    furi_assert(usb.thread);
-    furi_thread_flags_set(furi_thread_get_id(usb.thread), EventDisable);
+    UsbApiEventMessage msg = {
+        .lock = api_lock_alloc_locked(),
+        .type = UsbApiEventTypeDisable,
+    };
+
+    furi_hal_usb_send_message(&msg);
 }
 }
 
 
 void furi_hal_usb_enable() {
 void furi_hal_usb_enable() {
-    furi_assert(usb.thread);
-    furi_thread_flags_set(furi_thread_get_id(usb.thread), EventEnable);
+    UsbApiEventMessage msg = {
+        .lock = api_lock_alloc_locked(),
+        .type = UsbApiEventTypeEnable,
+    };
+
+    furi_hal_usb_send_message(&msg);
 }
 }
 
 
 void furi_hal_usb_reinit() {
 void furi_hal_usb_reinit() {
-    furi_assert(usb.thread);
-    furi_thread_flags_set(furi_thread_get_id(usb.thread), EventReinit);
+    UsbApiEventMessage msg = {
+        .lock = api_lock_alloc_locked(),
+        .type = UsbApiEventTypeReinit,
+    };
+
+    furi_hal_usb_send_message(&msg);
+}
+
+void furi_hal_usb_set_state_callback(FuriHalUsbStateCallback cb, void* ctx) {
+    UsbApiEventMessage msg = {
+        .lock = api_lock_alloc_locked(),
+        .type = UsbApiEventTypeSetStateCallback,
+        .data.state_callback =
+            {
+                .callback = cb,
+                .context = ctx,
+            },
+    };
+
+    furi_hal_usb_send_message(&msg);
 }
 }
 
 
 /* Get device / configuration descriptors */
 /* Get device / configuration descriptors */
@@ -142,29 +242,29 @@ static usbd_respond usb_descriptor_get(usbd_ctlreq* req, void** address, uint16_
     const uint8_t dnumber = req->wValue & 0xFF;
     const uint8_t dnumber = req->wValue & 0xFF;
     const void* desc;
     const void* desc;
     uint16_t len = 0;
     uint16_t len = 0;
-    if(usb.if_cur == NULL) return usbd_fail;
+    if(usb.interface == NULL) return usbd_fail;
 
 
     switch(dtype) {
     switch(dtype) {
     case USB_DTYPE_DEVICE:
     case USB_DTYPE_DEVICE:
-        furi_thread_flags_set(furi_thread_get_id(usb.thread), EventRequest);
+        furi_thread_flags_set(furi_thread_get_id(usb.thread), UsbEventRequest);
         if(usb.callback != NULL) {
         if(usb.callback != NULL) {
-            usb.callback(FuriHalUsbStateEventDescriptorRequest, usb.cb_ctx);
+            usb.callback(FuriHalUsbStateEventDescriptorRequest, usb.callback_context);
         }
         }
-        desc = usb.if_cur->dev_descr;
+        desc = usb.interface->dev_descr;
         break;
         break;
     case USB_DTYPE_CONFIGURATION:
     case USB_DTYPE_CONFIGURATION:
-        desc = usb.if_cur->cfg_descr;
-        len = ((struct usb_string_descriptor*)(usb.if_cur->cfg_descr))->wString[0];
+        desc = usb.interface->cfg_descr;
+        len = ((struct usb_string_descriptor*)(usb.interface->cfg_descr))->wString[0];
         break;
         break;
     case USB_DTYPE_STRING:
     case USB_DTYPE_STRING:
         if(dnumber == UsbDevLang) {
         if(dnumber == UsbDevLang) {
             desc = &dev_lang_desc;
             desc = &dev_lang_desc;
-        } else if((dnumber == UsbDevManuf) && (usb.if_cur->str_manuf_descr != NULL)) {
-            desc = usb.if_cur->str_manuf_descr;
-        } else if((dnumber == UsbDevProduct) && (usb.if_cur->str_prod_descr != NULL)) {
-            desc = usb.if_cur->str_prod_descr;
-        } else if((dnumber == UsbDevSerial) && (usb.if_cur->str_serial_descr != NULL)) {
-            desc = usb.if_cur->str_serial_descr;
+        } else if((dnumber == UsbDevManuf) && (usb.interface->str_manuf_descr != NULL)) {
+            desc = usb.interface->str_manuf_descr;
+        } else if((dnumber == UsbDevProduct) && (usb.interface->str_prod_descr != NULL)) {
+            desc = usb.interface->str_prod_descr;
+        } else if((dnumber == UsbDevSerial) && (usb.interface->str_serial_descr != NULL)) {
+            desc = usb.interface->str_serial_descr;
         } else
         } else
             return usbd_fail;
             return usbd_fail;
         break;
         break;
@@ -181,18 +281,13 @@ static usbd_respond usb_descriptor_get(usbd_ctlreq* req, void** address, uint16_
     return usbd_ack;
     return usbd_ack;
 }
 }
 
 
-void furi_hal_usb_set_state_callback(FuriHalUsbStateCallback cb, void* ctx) {
-    usb.callback = cb;
-    usb.cb_ctx = ctx;
-}
-
 static void reset_evt(usbd_device* dev, uint8_t event, uint8_t ep) {
 static void reset_evt(usbd_device* dev, uint8_t event, uint8_t ep) {
     UNUSED(dev);
     UNUSED(dev);
     UNUSED(event);
     UNUSED(event);
     UNUSED(ep);
     UNUSED(ep);
-    furi_thread_flags_set(furi_thread_get_id(usb.thread), EventReset);
+    furi_thread_flags_set(furi_thread_get_id(usb.thread), UsbEventReset);
     if(usb.callback != NULL) {
     if(usb.callback != NULL) {
-        usb.callback(FuriHalUsbStateEventReset, usb.cb_ctx);
+        usb.callback(FuriHalUsbStateEventReset, usb.callback_context);
     }
     }
 }
 }
 
 
@@ -200,14 +295,14 @@ static void susp_evt(usbd_device* dev, uint8_t event, uint8_t ep) {
     UNUSED(dev);
     UNUSED(dev);
     UNUSED(event);
     UNUSED(event);
     UNUSED(ep);
     UNUSED(ep);
-    if((usb.if_cur != NULL) && (usb.connected == true)) {
+    if((usb.interface != NULL) && (usb.connected == true)) {
         usb.connected = false;
         usb.connected = false;
-        usb.if_cur->suspend(&udev);
+        usb.interface->suspend(&udev);
 
 
         furi_hal_power_insomnia_exit();
         furi_hal_power_insomnia_exit();
     }
     }
     if(usb.callback != NULL) {
     if(usb.callback != NULL) {
-        usb.callback(FuriHalUsbStateEventSuspend, usb.cb_ctx);
+        usb.callback(FuriHalUsbStateEventSuspend, usb.callback_context);
     }
     }
 }
 }
 
 
@@ -215,101 +310,161 @@ static void wkup_evt(usbd_device* dev, uint8_t event, uint8_t ep) {
     UNUSED(dev);
     UNUSED(dev);
     UNUSED(event);
     UNUSED(event);
     UNUSED(ep);
     UNUSED(ep);
-    if((usb.if_cur != NULL) && (usb.connected == false)) {
+    if((usb.interface != NULL) && (usb.connected == false)) {
         usb.connected = true;
         usb.connected = true;
-        usb.if_cur->wakeup(&udev);
+        usb.interface->wakeup(&udev);
 
 
         furi_hal_power_insomnia_enter();
         furi_hal_power_insomnia_enter();
     }
     }
     if(usb.callback != NULL) {
     if(usb.callback != NULL) {
-        usb.callback(FuriHalUsbStateEventWakeup, usb.cb_ctx);
+        usb.callback(FuriHalUsbStateEventWakeup, usb.callback_context);
+    }
+}
+
+static void usb_process_mode_start(FuriHalUsbInterface* interface, void* context) {
+    if(usb.interface != NULL) {
+        usb.interface->deinit(&udev);
+    }
+
+    __disable_irq();
+    usb.interface = interface;
+    usb.interface_context = context;
+    __enable_irq();
+
+    if(interface != NULL) {
+        interface->init(&udev, interface, context);
+        usbd_reg_event(&udev, usbd_evt_reset, reset_evt);
+        FURI_LOG_I(TAG, "USB Mode change done");
+        usb.enabled = true;
     }
     }
 }
 }
 
 
+static void usb_process_mode_change(FuriHalUsbInterface* interface, void* context) {
+    if(interface != usb.interface) {
+        if(usb.enabled) {
+            // Disable current interface
+            susp_evt(&udev, 0, 0);
+            usbd_connect(&udev, false);
+            usb.enabled = false;
+            furi_delay_ms(USB_RECONNECT_DELAY);
+        }
+        usb_process_mode_start(interface, context);
+    }
+}
+
+static void usb_process_mode_reinit() {
+    // Temporary disable callback to avoid getting false reset events
+    usbd_reg_event(&udev, usbd_evt_reset, NULL);
+    FURI_LOG_I(TAG, "USB Reinit");
+    susp_evt(&udev, 0, 0);
+    usbd_connect(&udev, false);
+    usb.enabled = false;
+
+    usbd_enable(&udev, false);
+    usbd_enable(&udev, true);
+
+    furi_delay_ms(USB_RECONNECT_DELAY);
+    usb_process_mode_start(usb.interface, usb.interface_context);
+}
+
+static bool usb_process_set_config(FuriHalUsbInterface* interface, void* context) {
+    if(usb.mode_lock) {
+        return false;
+    } else {
+        usb_process_mode_change(interface, context);
+        return true;
+    }
+}
+
+static void usb_process_enable(bool enable) {
+    if(enable) {
+        if((!usb.enabled) && (usb.interface != NULL)) {
+            usbd_connect(&udev, true);
+            usb.enabled = true;
+            FURI_LOG_I(TAG, "USB Enable");
+        }
+    } else {
+        if(usb.enabled) {
+            susp_evt(&udev, 0, 0);
+            usbd_connect(&udev, false);
+            usb.enabled = false;
+            usb.request_pending = false;
+            FURI_LOG_I(TAG, "USB Disable");
+        }
+    }
+}
+
+static void usb_process_message(UsbApiEventMessage* message) {
+    switch(message->type) {
+    case UsbApiEventTypeSetConfig:
+        message->return_data->bool_value = usb_process_set_config(
+            message->data.interface.interface, message->data.interface.context);
+        break;
+    case UsbApiEventTypeGetConfig:
+        message->return_data->void_value = usb.interface;
+        break;
+    case UsbApiEventTypeLock:
+        FURI_LOG_I(TAG, "Mode lock");
+        usb.mode_lock = true;
+        break;
+    case UsbApiEventTypeUnlock:
+        FURI_LOG_I(TAG, "Mode unlock");
+        usb.mode_lock = false;
+        break;
+    case UsbApiEventTypeIsLocked:
+        message->return_data->bool_value = usb.mode_lock;
+        break;
+    case UsbApiEventTypeDisable:
+        usb_process_enable(false);
+        break;
+    case UsbApiEventTypeEnable:
+        usb_process_enable(true);
+        break;
+    case UsbApiEventTypeReinit:
+        usb_process_mode_reinit();
+        break;
+    case UsbApiEventTypeSetStateCallback:
+        usb.callback = message->data.state_callback.callback;
+        usb.callback_context = message->data.state_callback.context;
+        break;
+    }
+
+    api_lock_unlock(message->lock);
+}
+
 static int32_t furi_hal_usb_thread(void* context) {
 static int32_t furi_hal_usb_thread(void* context) {
     UNUSED(context);
     UNUSED(context);
-    bool usb_request_pending = false;
     uint8_t usb_wait_time = 0;
     uint8_t usb_wait_time = 0;
-    FuriHalUsbInterface* if_new = NULL;
-    FuriHalUsbInterface* if_ctx_new = NULL;
 
 
-    if(usb.if_next != NULL) {
-        furi_thread_flags_set(furi_thread_get_id(usb.thread), EventModeChange);
+    if(furi_message_queue_get_count(usb.queue) > 0) {
+        furi_thread_flags_set(furi_thread_get_id(usb.thread), UsbEventMessage);
     }
     }
 
 
     while(true) {
     while(true) {
         uint32_t flags = furi_thread_flags_wait(USB_SRV_ALL_EVENTS, FuriFlagWaitAny, 500);
         uint32_t flags = furi_thread_flags_wait(USB_SRV_ALL_EVENTS, FuriFlagWaitAny, 500);
-        if((flags & FuriFlagError) == 0) {
-            if(flags & EventModeChange) {
-                if(usb.if_next != usb.if_cur) {
-                    if_new = usb.if_next;
-                    if_ctx_new = usb.if_ctx;
-                    if(usb.enabled) { // Disable current interface
-                        susp_evt(&udev, 0, 0);
-                        usbd_connect(&udev, false);
-                        usb.enabled = false;
-                        furi_delay_ms(USB_RECONNECT_DELAY);
-                    }
-                    flags |= EventModeChangeStart;
-                }
-            }
-            if(flags & EventReinit) {
-                // Temporary disable callback to avoid getting false reset events
-                usbd_reg_event(&udev, usbd_evt_reset, NULL);
-                FURI_LOG_I(TAG, "USB Reinit");
-                susp_evt(&udev, 0, 0);
-                usbd_connect(&udev, false);
-                usb.enabled = false;
-
-                usbd_enable(&udev, false);
-                usbd_enable(&udev, true);
-
-                if_new = usb.if_cur;
-                furi_delay_ms(USB_RECONNECT_DELAY);
-                flags |= EventModeChangeStart;
-            }
-            if(flags & EventModeChangeStart) { // Second stage of mode change process
-                if(usb.if_cur != NULL) {
-                    usb.if_cur->deinit(&udev);
-                }
-                if(if_new != NULL) {
-                    if_new->init(&udev, if_new, if_ctx_new);
-                    usbd_reg_event(&udev, usbd_evt_reset, reset_evt);
-                    FURI_LOG_I(TAG, "USB Mode change done");
-                    usb.enabled = true;
-                }
-                usb.if_cur = if_new;
-            }
-            if(flags & EventEnable) {
-                if((!usb.enabled) && (usb.if_cur != NULL)) {
-                    usbd_connect(&udev, true);
-                    usb.enabled = true;
-                    FURI_LOG_I(TAG, "USB Enable");
-                }
-            }
-            if(flags & EventDisable) {
-                if(usb.enabled) {
-                    susp_evt(&udev, 0, 0);
-                    usbd_connect(&udev, false);
-                    usb.enabled = false;
-                    usb_request_pending = false;
-                    FURI_LOG_I(TAG, "USB Disable");
-                }
+
+        {
+            UsbApiEventMessage message;
+            if(furi_message_queue_get(usb.queue, &message, 0) == FuriStatusOk) {
+                usb_process_message(&message);
             }
             }
-            if(flags & EventReset) {
+        }
+
+        if((flags & FuriFlagError) == 0) {
+            if(flags & UsbEventReset) {
                 if(usb.enabled) {
                 if(usb.enabled) {
-                    usb_request_pending = true;
+                    usb.request_pending = true;
                     usb_wait_time = 0;
                     usb_wait_time = 0;
                 }
                 }
             }
             }
-            if(flags & EventRequest) {
-                usb_request_pending = false;
+            if(flags & UsbEventRequest) {
+                usb.request_pending = false;
             }
             }
-        } else if(usb_request_pending) {
+        } else if(usb.request_pending) {
             usb_wait_time++;
             usb_wait_time++;
             if(usb_wait_time > 4) {
             if(usb_wait_time > 4) {
-                furi_hal_usb_reinit();
-                usb_request_pending = false;
+                usb_process_mode_reinit();
+                usb.request_pending = false;
             }
             }
         }
         }
     }
     }

+ 8 - 0
furi/core/memmgr_heap.c

@@ -340,6 +340,10 @@ void* pvPortMalloc(size_t xWantedSize) {
     void* pvReturn = NULL;
     void* pvReturn = NULL;
     size_t to_wipe = xWantedSize;
     size_t to_wipe = xWantedSize;
 
 
+    if(FURI_IS_IRQ_MODE()) {
+        furi_crash("memmgt in ISR");
+    }
+
 #ifdef HEAP_PRINT_DEBUG
 #ifdef HEAP_PRINT_DEBUG
     BlockLink_t* print_heap_block = NULL;
     BlockLink_t* print_heap_block = NULL;
 #endif
 #endif
@@ -486,6 +490,10 @@ void vPortFree(void* pv) {
     uint8_t* puc = (uint8_t*)pv;
     uint8_t* puc = (uint8_t*)pv;
     BlockLink_t* pxLink;
     BlockLink_t* pxLink;
 
 
+    if(FURI_IS_IRQ_MODE()) {
+        furi_crash("memmgt in ISR");
+    }
+
     if(pv != NULL) {
     if(pv != NULL) {
         /* The memory being freed will have an BlockLink_t structure immediately
         /* The memory being freed will have an BlockLink_t structure immediately
         before it. */
         before it. */

+ 43 - 0
lib/toolbox/api_lock.h

@@ -0,0 +1,43 @@
+#pragma once
+#include <furi/furi.h>
+
+/*
+Testing 10000 api calls
+
+No lock
+    Time diff: 445269.218750 us
+    Time per call: 44.526920 us
+
+furi_thread_flags
+    Time diff: 430279.875000 us // lol wtf? smaller than no lock?
+    Time per call: 43.027988 us // I tested it many times, it's always smaller
+
+FuriEventFlag
+    Time diff: 831523.625000 us
+    Time per call: 83.152359 us
+
+FuriSemaphore
+    Time diff: 999807.125000 us
+    Time per call: 99.980713 us
+
+FuriMutex
+    Time diff: 1071417.500000 us
+    Time per call: 107.141747 us
+*/
+
+typedef FuriEventFlag* FuriApiLock;
+
+#define API_LOCK_EVENT (1U << 0)
+
+#define api_lock_alloc_locked() furi_event_flag_alloc()
+
+#define api_lock_wait_unlock(_lock) \
+    furi_event_flag_wait(_lock, API_LOCK_EVENT, FuriFlagWaitAny, FuriWaitForever)
+
+#define api_lock_free(_lock) furi_event_flag_free(_lock)
+
+#define api_lock_unlock(_lock) furi_event_flag_set(_lock, API_LOCK_EVENT)
+
+#define api_lock_wait_unlock_and_free(_lock) \
+    api_lock_wait_unlock(_lock);             \
+    api_lock_free(_lock);