diff --git a/applications/bad_usb/bad_usb_app.c b/applications/bad_usb/bad_usb_app.c index 8f806f9f..eb647e00 100644 --- a/applications/bad_usb/bad_usb_app.c +++ b/applications/bad_usb/bad_usb_app.c @@ -79,12 +79,18 @@ BadUsbApp* bad_usb_app_alloc(char* arg) { view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); - if(*app->file_name != '\0') { - scene_manager_next_scene(app->scene_manager, BadUsbSceneWork); - } else if(bad_usb_check_assets()) { - scene_manager_next_scene(app->scene_manager, BadUsbSceneFileSelect); - } else { + if(furi_hal_usb_is_locked()) { + app->error = BadUsbAppErrorCloseRpc; scene_manager_next_scene(app->scene_manager, BadUsbSceneError); + } else { + if(*app->file_name != '\0') { + scene_manager_next_scene(app->scene_manager, BadUsbSceneWork); + } else if(bad_usb_check_assets()) { + scene_manager_next_scene(app->scene_manager, BadUsbSceneFileSelect); + } else { + app->error = BadUsbAppErrorNoFiles; + scene_manager_next_scene(app->scene_manager, BadUsbSceneError); + } } return app; diff --git a/applications/bad_usb/bad_usb_app_i.h b/applications/bad_usb/bad_usb_app_i.h index 5772725d..67f5816b 100644 --- a/applications/bad_usb/bad_usb_app_i.h +++ b/applications/bad_usb/bad_usb_app_i.h @@ -18,6 +18,11 @@ #define BAD_USB_APP_EXTENSION ".txt" #define BAD_USB_FILE_NAME_LEN 40 +typedef enum { + BadUsbAppErrorNoFiles, + BadUsbAppErrorCloseRpc, +} BadUsbAppError; + struct BadUsbApp { Gui* gui; ViewDispatcher* view_dispatcher; @@ -26,6 +31,7 @@ struct BadUsbApp { DialogsApp* dialogs; Widget* widget; + BadUsbAppError error; char file_name[BAD_USB_FILE_NAME_LEN + 1]; BadUsb* bad_usb_view; BadUsbScript* bad_usb_script; diff --git a/applications/bad_usb/scenes/bad_usb_scene_error.c b/applications/bad_usb/scenes/bad_usb_scene_error.c index 52650022..c8e1c361 100644 --- a/applications/bad_usb/scenes/bad_usb_scene_error.c +++ b/applications/bad_usb/scenes/bad_usb_scene_error.c @@ -1,7 +1,7 @@ #include "../bad_usb_app_i.h" typedef enum { - SubghzCustomEventErrorBack, + BadUsbCustomEventErrorBack, } BadUsbCustomEvent; static void @@ -10,23 +10,33 @@ static void BadUsbApp* app = context; if((result == GuiButtonTypeLeft) && (type == InputTypeShort)) { - view_dispatcher_send_custom_event(app->view_dispatcher, SubghzCustomEventErrorBack); + view_dispatcher_send_custom_event(app->view_dispatcher, BadUsbCustomEventErrorBack); } } void bad_usb_scene_error_on_enter(void* context) { BadUsbApp* app = context; - widget_add_icon_element(app->widget, 0, 0, &I_SDQuestion_35x43); - - widget_add_string_multiline_element( - app->widget, - 81, - 4, - AlignCenter, - AlignTop, - FontSecondary, - "No SD card or\napp data found.\nThis app will not\nwork without\nrequired files."); + if(app->error == BadUsbAppErrorNoFiles) { + widget_add_icon_element(app->widget, 0, 0, &I_SDQuestion_35x43); + widget_add_string_multiline_element( + app->widget, + 81, + 4, + AlignCenter, + AlignTop, + FontSecondary, + "No SD card or\napp data found.\nThis app will not\nwork without\nrequired files."); + } else if(app->error == BadUsbAppErrorCloseRpc) { + widget_add_string_multiline_element( + app->widget, + 63, + 10, + AlignCenter, + AlignTop, + FontSecondary, + "Disconnect from\ncompanion app\nto use this function"); + } widget_add_button_element( app->widget, GuiButtonTypeLeft, "Back", bad_usb_scene_error_event_callback, app); @@ -39,7 +49,7 @@ bool bad_usb_scene_error_on_event(void* context, SceneManagerEvent event) { bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubghzCustomEventErrorBack) { + if(event.event == BadUsbCustomEventErrorBack) { view_dispatcher_stop(app->view_dispatcher); consumed = true; } diff --git a/applications/debug_tools/usb_mouse.c b/applications/debug_tools/usb_mouse.c index 93550bec..1468f6c6 100644 --- a/applications/debug_tools/usb_mouse.c +++ b/applications/debug_tools/usb_mouse.c @@ -42,7 +42,8 @@ int32_t usb_mouse_app(void* p) { ViewPort* view_port = view_port_alloc(); FuriHalUsbInterface* usb_mode_prev = furi_hal_usb_get_config(); - furi_hal_usb_set_config(&usb_hid, NULL); + furi_hal_usb_unlock(); + furi_check(furi_hal_usb_set_config(&usb_hid, NULL) == true); 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); diff --git a/applications/gpio/gpio_app.c b/applications/gpio/gpio_app.c index 489c9854..7dfce836 100644 --- a/applications/gpio/gpio_app.c +++ b/applications/gpio/gpio_app.c @@ -51,6 +51,10 @@ GpioApp* gpio_app_alloc() { view_dispatcher_add_view( app->view_dispatcher, GpioAppViewGpioTest, gpio_test_get_view(app->gpio_test)); + app->widget = widget_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, GpioAppViewUsbUartCloseRpc, widget_get_view(app->widget)); + app->gpio_usb_uart = gpio_usb_uart_alloc(); view_dispatcher_add_view( app->view_dispatcher, GpioAppViewUsbUart, gpio_usb_uart_get_view(app->gpio_usb_uart)); @@ -73,7 +77,9 @@ void gpio_app_free(GpioApp* app) { view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewGpioTest); view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewUsbUart); view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewUsbUartCfg); + view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewUsbUartCloseRpc); variable_item_list_free(app->var_item_list); + widget_free(app->widget); gpio_test_free(app->gpio_test); gpio_usb_uart_free(app->gpio_usb_uart); diff --git a/applications/gpio/gpio_app_i.h b/applications/gpio/gpio_app_i.h index b288794d..fa91e8f7 100644 --- a/applications/gpio/gpio_app_i.h +++ b/applications/gpio/gpio_app_i.h @@ -12,6 +12,7 @@ #include #include #include +#include #include "views/gpio_test.h" #include "views/gpio_usb_uart.h" @@ -20,6 +21,7 @@ struct GpioApp { NotificationApp* notifications; ViewDispatcher* view_dispatcher; SceneManager* scene_manager; + Widget* widget; VariableItemList* var_item_list; GpioTest* gpio_test; @@ -32,4 +34,5 @@ typedef enum { GpioAppViewGpioTest, GpioAppViewUsbUart, GpioAppViewUsbUartCfg, + GpioAppViewUsbUartCloseRpc, } GpioAppView; diff --git a/applications/gpio/gpio_custom_event.h b/applications/gpio/gpio_custom_event.h index 1682dc0f..cae36035 100644 --- a/applications/gpio/gpio_custom_event.h +++ b/applications/gpio/gpio_custom_event.h @@ -6,5 +6,7 @@ typedef enum { GpioStartEventManualConrol, GpioStartEventUsbUart, + GpioCustomEventErrorBack, + GpioUsbUartEventConfig, } GpioCustomEvent; diff --git a/applications/gpio/scenes/gpio_scene_config.h b/applications/gpio/scenes/gpio_scene_config.h index 925f4c5c..3406e42d 100644 --- a/applications/gpio/scenes/gpio_scene_config.h +++ b/applications/gpio/scenes/gpio_scene_config.h @@ -2,3 +2,4 @@ ADD_SCENE(gpio, start, Start) ADD_SCENE(gpio, test, Test) ADD_SCENE(gpio, usb_uart, UsbUart) ADD_SCENE(gpio, usb_uart_cfg, UsbUartCfg) +ADD_SCENE(gpio, usb_uart_close_rpc, UsbUartCloseRpc) diff --git a/applications/gpio/scenes/gpio_scene_start.c b/applications/gpio/scenes/gpio_scene_start.c index d0adb35e..39aabbd8 100644 --- a/applications/gpio/scenes/gpio_scene_start.c +++ b/applications/gpio/scenes/gpio_scene_start.c @@ -1,5 +1,6 @@ #include "../gpio_app_i.h" #include "furi_hal_power.h" +#include "furi_hal_usb.h" enum GpioItem { GpioItemUsbUart, @@ -86,7 +87,11 @@ bool gpio_scene_start_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(app->scene_manager, GpioSceneTest); } else if(event.event == GpioStartEventUsbUart) { scene_manager_set_scene_state(app->scene_manager, GpioSceneStart, GpioItemUsbUart); - scene_manager_next_scene(app->scene_manager, GpioSceneUsbUart); + if(!furi_hal_usb_is_locked()) { + scene_manager_next_scene(app->scene_manager, GpioSceneUsbUart); + } else { + scene_manager_next_scene(app->scene_manager, GpioSceneUsbUartCloseRpc); + } } consumed = true; } diff --git a/applications/gpio/scenes/gpio_scene_usb_uart.c b/applications/gpio/scenes/gpio_scene_usb_uart.c index 8bf900cd..f8f473ee 100644 --- a/applications/gpio/scenes/gpio_scene_usb_uart.c +++ b/applications/gpio/scenes/gpio_scene_usb_uart.c @@ -31,7 +31,7 @@ void gpio_scene_usb_uart_on_enter(void* context) { 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); + scene_manager_set_scene_state(app->scene_manager, GpioSceneUsbUart, 0); view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewUsbUart); notification_message(app->notifications, &sequence_display_lock); } @@ -39,8 +39,8 @@ void gpio_scene_usb_uart_on_enter(void* context) { 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); + scene_manager_set_scene_state(app->scene_manager, GpioSceneUsbUart, 1); + scene_manager_next_scene(app->scene_manager, GpioSceneUsbUartCfg); return true; } else if(event.type == SceneManagerEventTypeTick) { uint32_t tx_cnt_last = scene_usb_uart->state.tx_cnt; @@ -58,7 +58,7 @@ bool gpio_scene_usb_uart_on_event(void* context, SceneManagerEvent event) { void gpio_scene_usb_uart_on_exit(void* context) { GpioApp* app = context; - uint32_t prev_state = scene_manager_get_scene_state(app->scene_manager, GpioAppViewUsbUart); + uint32_t prev_state = scene_manager_get_scene_state(app->scene_manager, GpioSceneUsbUart); if(prev_state == 0) { usb_uart_disable(app->usb_uart_bridge); free(scene_usb_uart); diff --git a/applications/gpio/scenes/gpio_scene_usb_uart_close_rpc.c b/applications/gpio/scenes/gpio_scene_usb_uart_close_rpc.c new file mode 100644 index 00000000..e09f2fd3 --- /dev/null +++ b/applications/gpio/scenes/gpio_scene_usb_uart_close_rpc.c @@ -0,0 +1,53 @@ +#include "../gpio_app_i.h" +#include "../gpio_custom_event.h" + +static void gpio_scene_usb_uart_close_rpc_event_callback( + GuiButtonType result, + InputType type, + void* context) { + furi_assert(context); + GpioApp* app = context; + + if((result == GuiButtonTypeLeft) && (type == InputTypeShort)) { + view_dispatcher_send_custom_event(app->view_dispatcher, GpioCustomEventErrorBack); + } +} + +void gpio_scene_usb_uart_close_rpc_on_enter(void* context) { + GpioApp* app = context; + + widget_add_string_multiline_element( + app->widget, + 63, + 10, + AlignCenter, + AlignTop, + FontSecondary, + "Disconnect from\ncompanion app\nto use this function"); + + widget_add_button_element( + app->widget, GuiButtonTypeLeft, "Back", gpio_scene_usb_uart_close_rpc_event_callback, app); + + view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewUsbUartCloseRpc); +} + +bool gpio_scene_usb_uart_close_rpc_on_event(void* context, SceneManagerEvent event) { + GpioApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GpioCustomEventErrorBack) { + if(!scene_manager_previous_scene(app->scene_manager)) { + scene_manager_stop(app->scene_manager); + view_dispatcher_stop(app->view_dispatcher); + } + consumed = true; + } + } + return consumed; +} + +void gpio_scene_usb_uart_close_rpc_on_exit(void* context) { + GpioApp* app = context; + widget_reset(app->widget); +} diff --git a/applications/gpio/usb_uart_bridge.c b/applications/gpio/usb_uart_bridge.c index 2832e7f8..51c2bc32 100644 --- a/applications/gpio/usb_uart_bridge.c +++ b/applications/gpio/usb_uart_bridge.c @@ -83,11 +83,12 @@ static void usb_uart_on_irq_cb(UartIrqEvent ev, uint8_t data, void* context) { } static void usb_uart_vcp_init(UsbUartBridge* usb_uart, uint8_t vcp_ch) { + furi_hal_usb_unlock(); if(vcp_ch == 0) { - furi_hal_usb_set_config(&usb_cdc_single, NULL); + furi_check(furi_hal_usb_set_config(&usb_cdc_single, NULL) == true); furi_hal_vcp_disable(); } else { - furi_hal_usb_set_config(&usb_cdc_dual, NULL); + furi_check(furi_hal_usb_set_config(&usb_cdc_dual, NULL) == true); } furi_hal_cdc_set_callbacks(vcp_ch, (CdcCallbacks*)&cdc_cb, usb_uart); } @@ -247,6 +248,7 @@ static int32_t usb_uart_worker(void* context) { 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_unlock(); furi_hal_usb_set_config(usb_mode_prev, NULL); if(usb_uart->cfg.flow_pins != 0) { hal_gpio_init_simple(flow_pins[usb_uart->cfg.flow_pins - 1][0], GpioModeAnalog); diff --git a/applications/gpio/usb_uart_bridge.h b/applications/gpio/usb_uart_bridge.h index e35ec2da..b456c3cc 100644 --- a/applications/gpio/usb_uart_bridge.h +++ b/applications/gpio/usb_uart_bridge.h @@ -1,6 +1,7 @@ #pragma once #include +#include typedef struct UsbUartBridge UsbUartBridge; diff --git a/applications/rpc/rpc_cli.c b/applications/rpc/rpc_cli.c index 36ae9ef3..db184529 100644 --- a/applications/rpc/rpc_cli.c +++ b/applications/rpc/rpc_cli.c @@ -2,10 +2,12 @@ #include #include #include +#include typedef struct { Cli* cli; bool session_close_request; + SemaphoreHandle_t terminate_semaphore; } CliRpc; #define CLI_READ_BUFFER_SIZE 64 @@ -26,19 +28,30 @@ static void rpc_session_close_callback(void* context) { cli_rpc->session_close_request = true; } +static void rpc_session_terminated_callback(void* context) { + furi_check(context); + CliRpc* cli_rpc = context; + + xSemaphoreGive(cli_rpc->terminate_semaphore); +} + void rpc_cli_command_start_session(Cli* cli, string_t args, void* context) { Rpc* rpc = context; + furi_hal_usb_lock(); RpcSession* rpc_session = rpc_session_open(rpc); if(rpc_session == NULL) { printf("Session start error\r\n"); + furi_hal_usb_unlock(); return; } CliRpc cli_rpc = {.cli = cli, .session_close_request = false}; + cli_rpc.terminate_semaphore = xSemaphoreCreateBinary(); rpc_session_set_context(rpc_session, &cli_rpc); rpc_session_set_send_bytes_callback(rpc_session, rpc_send_bytes_callback); rpc_session_set_close_callback(rpc_session, rpc_session_close_callback); + rpc_session_set_terminated_callback(rpc_session, rpc_session_terminated_callback); uint8_t* buffer = malloc(CLI_READ_BUFFER_SIZE); size_t size_received = 0; @@ -57,5 +70,11 @@ void rpc_cli_command_start_session(Cli* cli, string_t args, void* context) { } rpc_session_close(rpc_session); + + furi_check(xSemaphoreTake(cli_rpc.terminate_semaphore, portMAX_DELAY)); + + vSemaphoreDelete(cli_rpc.terminate_semaphore); + free(buffer); + furi_hal_usb_unlock(); } diff --git a/applications/u2f/scenes/u2f_scene_error.c b/applications/u2f/scenes/u2f_scene_error.c index 76406fbf..d4018367 100644 --- a/applications/u2f/scenes/u2f_scene_error.c +++ b/applications/u2f/scenes/u2f_scene_error.c @@ -12,16 +12,26 @@ static void u2f_scene_error_event_callback(GuiButtonType result, InputType type, void u2f_scene_error_on_enter(void* context) { U2fApp* app = context; - widget_add_icon_element(app->widget, 0, 0, &I_SDQuestion_35x43); - - widget_add_string_multiline_element( - app->widget, - 81, - 4, - AlignCenter, - AlignTop, - FontSecondary, - "No SD card or\napp data found.\nThis app will not\nwork without\nrequired files."); + if(app->error == U2fAppErrorNoFiles) { + widget_add_icon_element(app->widget, 0, 0, &I_SDQuestion_35x43); + widget_add_string_multiline_element( + app->widget, + 81, + 4, + AlignCenter, + AlignTop, + FontSecondary, + "No SD card or\napp data found.\nThis app will not\nwork without\nrequired files."); + } else if(app->error == U2fAppErrorCloseRpc) { + widget_add_string_multiline_element( + app->widget, + 63, + 10, + AlignCenter, + AlignTop, + FontSecondary, + "Disconnect from\ncompanion app\nto use this function"); + } widget_add_button_element( app->widget, GuiButtonTypeLeft, "Back", u2f_scene_error_event_callback, app); diff --git a/applications/u2f/u2f_app.c b/applications/u2f/u2f_app.c index 88d86855..8a673dbc 100644 --- a/applications/u2f/u2f_app.c +++ b/applications/u2f/u2f_app.c @@ -48,10 +48,16 @@ U2fApp* u2f_app_alloc() { view_dispatcher_add_view( app->view_dispatcher, U2fAppViewMain, u2f_view_get_view(app->u2f_view)); - if(u2f_data_check(true)) { - scene_manager_next_scene(app->scene_manager, U2fSceneMain); - } else { + if(furi_hal_usb_is_locked()) { + app->error = U2fAppErrorCloseRpc; scene_manager_next_scene(app->scene_manager, U2fSceneError); + } else { + if(u2f_data_check(true)) { + scene_manager_next_scene(app->scene_manager, U2fSceneMain); + } else { + app->error = U2fAppErrorNoFiles; + scene_manager_next_scene(app->scene_manager, U2fSceneError); + } } return app; diff --git a/applications/u2f/u2f_app_i.h b/applications/u2f/u2f_app_i.h index 41048b31..fa9f6f09 100644 --- a/applications/u2f/u2f_app_i.h +++ b/applications/u2f/u2f_app_i.h @@ -15,6 +15,11 @@ #include "u2f_hid.h" #include "u2f.h" +typedef enum { + U2fAppErrorNoFiles, + U2fAppErrorCloseRpc, +} U2fAppError; + typedef enum { U2fCustomEventNone, @@ -52,4 +57,5 @@ struct U2fApp { U2fData* u2f_instance; GpioCustomEvent event_cur; bool u2f_ready; + U2fAppError error; }; diff --git a/applications/u2f/u2f_hid.c b/applications/u2f/u2f_hid.c index e48f8b1c..a2b291d6 100644 --- a/applications/u2f/u2f_hid.c +++ b/applications/u2f/u2f_hid.c @@ -191,7 +191,7 @@ static int32_t u2f_hid_worker(void* context) { FURI_LOG_D(WORKER_TAG, "Init"); FuriHalUsbInterface* usb_mode_prev = furi_hal_usb_get_config(); - furi_hal_usb_set_config(&usb_hid_u2f, NULL); + furi_check(furi_hal_usb_set_config(&usb_hid_u2f, NULL) == true); u2f_hid->lock_timer = osTimerNew(u2f_hid_lock_timeout_callback, osTimerOnce, u2f_hid, NULL); diff --git a/firmware/targets/f7/furi_hal/furi_hal_usb.c b/firmware/targets/f7/furi_hal/furi_hal_usb.c index ac87dfe2..db769e2a 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_usb.c +++ b/firmware/targets/f7/furi_hal/furi_hal_usb.c @@ -16,6 +16,7 @@ typedef struct { osTimerId_t tmr; bool enabled; bool connected; + bool mode_lock; FuriHalUsbInterface* if_cur; FuriHalUsbInterface* if_next; void* if_ctx; @@ -89,27 +90,47 @@ void furi_hal_usb_init(void) { FURI_LOG_I(TAG, "Init OK"); } -void furi_hal_usb_set_config(FuriHalUsbInterface* new_if, void* ctx) { +bool furi_hal_usb_set_config(FuriHalUsbInterface* new_if, void* ctx) { + if(usb.mode_lock) { + return false; + } + 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; + return true; } furi_assert(usb.thread); osThreadFlagsSet(furi_thread_get_thread_id(usb.thread), EventModeChange); + return true; } FuriHalUsbInterface* furi_hal_usb_get_config() { return usb.if_cur; } +void furi_hal_usb_lock() { + FURI_LOG_I(TAG, "Mode lock"); + usb.mode_lock = true; +} + +void furi_hal_usb_unlock() { + FURI_LOG_I(TAG, "Mode unlock"); + usb.mode_lock = false; +} + +bool furi_hal_usb_is_locked() { + return usb.mode_lock; +} + void furi_hal_usb_disable() { furi_assert(usb.thread); osThreadFlagsSet(furi_thread_get_thread_id(usb.thread), EventDisable); } void furi_hal_usb_enable() { + furi_assert(usb.thread); osThreadFlagsSet(furi_thread_get_thread_id(usb.thread), EventEnable); } diff --git a/firmware/targets/furi_hal_include/furi_hal_usb.h b/firmware/targets/furi_hal_include/furi_hal_usb.h index f1a05107..33115c11 100644 --- a/firmware/targets/furi_hal_include/furi_hal_usb.h +++ b/firmware/targets/furi_hal_include/furi_hal_usb.h @@ -42,8 +42,9 @@ void furi_hal_usb_init(); * * @param mode new USB device mode * @param ctx context passed to device mode init function + * @return true - mode switch started, false - mode switch is locked */ -void furi_hal_usb_set_config(FuriHalUsbInterface* new_if, void* ctx); +bool furi_hal_usb_set_config(FuriHalUsbInterface* new_if, void* ctx); /** Get USB device configuration * @@ -51,6 +52,20 @@ void furi_hal_usb_set_config(FuriHalUsbInterface* new_if, void* ctx); */ FuriHalUsbInterface* furi_hal_usb_get_config(); +/** Lock USB device mode switch + */ +void furi_hal_usb_lock(); + +/** Unlock USB device mode switch + */ +void furi_hal_usb_unlock(); + +/** Check if USB device mode switch locked + * + * @return lock state + */ +bool furi_hal_usb_is_locked(); + /** Disable USB device */ void furi_hal_usb_disable();