mirror of
https://github.com/DarkFlippers/unleashed-firmware
synced 2024-12-30 06:33:07 +00:00
28eb4d1060
Not full refactoring, only small issues is fixed and moved all plugins to furi mutex instead of valuemutex Many small issues was found and fixed due mutex upgrade OFW removed 60 lines of code and it was painful
540 lines
18 KiB
C
540 lines
18 KiB
C
#include <furi.h>
|
|
#include <furi_hal_console.h>
|
|
#include <furi_hal_gpio.h>
|
|
#include <furi_hal_power.h>
|
|
#include <furi_hal_uart.h>
|
|
#include <gui/canvas_i.h>
|
|
#include <gui/gui.h>
|
|
#include <input/input.h>
|
|
//#include <math.h>
|
|
//#include <notification/notification.h>
|
|
//#include <notification/notification_messages.h>
|
|
//#include <stdlib.h>
|
|
|
|
#include <u8g2.h>
|
|
|
|
#include "FlipperZeroWiFiDeauthModuleDefines.h"
|
|
|
|
#define DEAUTH_APP_DEBUG 0
|
|
|
|
#if DEAUTH_APP_DEBUG
|
|
#define APP_NAME_TAG "WiFi_Deauther"
|
|
#define DEAUTH_APP_LOG_I(format, ...) FURI_LOG_I(APP_NAME_TAG, format, ##__VA_ARGS__)
|
|
#define DEAUTH_APP_LOG_D(format, ...) FURI_LOG_D(APP_NAME_TAG, format, ##__VA_ARGS__)
|
|
#define DEAUTH_APP_LOG_E(format, ...) FURI_LOG_E(APP_NAME_TAG, format, ##__VA_ARGS__)
|
|
#else
|
|
#define DEAUTH_APP_LOG_I(format, ...)
|
|
#define DEAUTH_APP_LOG_D(format, ...)
|
|
#define DEAUTH_APP_LOG_E(format, ...)
|
|
#endif // WIFI_APP_DEBUG
|
|
|
|
#define DISABLE_CONSOLE !DEAUTH_APP_DEBUG
|
|
#define ENABLE_MODULE_POWER 1
|
|
#define ENABLE_MODULE_DETECTION 1
|
|
|
|
typedef enum EEventType // app internally defined event types
|
|
{ EventTypeKey // flipper input.h type
|
|
} EEventType;
|
|
|
|
typedef struct SPluginEvent {
|
|
EEventType m_type;
|
|
InputEvent m_input;
|
|
} SPluginEvent;
|
|
|
|
typedef enum EAppContext {
|
|
Undefined,
|
|
WaitingForModule,
|
|
Initializing,
|
|
ModuleActive,
|
|
} EAppContext;
|
|
|
|
typedef enum EWorkerEventFlags {
|
|
WorkerEventReserved = (1 << 0), // Reserved for StreamBuffer internal event
|
|
WorkerEventStop = (1 << 1),
|
|
WorkerEventRx = (1 << 2),
|
|
} EWorkerEventFlags;
|
|
|
|
typedef struct SGpioButtons {
|
|
GpioPin const* pinButtonUp;
|
|
GpioPin const* pinButtonDown;
|
|
GpioPin const* pinButtonOK;
|
|
GpioPin const* pinButtonBack;
|
|
} SGpioButtons;
|
|
|
|
typedef struct SWiFiDeauthApp {
|
|
FuriMutex* mutex;
|
|
Gui* m_gui;
|
|
FuriThread* m_worker_thread;
|
|
//NotificationApp* m_notification;
|
|
FuriStreamBuffer* m_rx_stream;
|
|
SGpioButtons m_GpioButtons;
|
|
|
|
bool m_wifiDeauthModuleInitialized;
|
|
bool m_wifiDeauthModuleAttached;
|
|
|
|
EAppContext m_context;
|
|
|
|
uint8_t m_backBuffer[128 * 8 * 8];
|
|
//uint8_t m_renderBuffer[128 * 8 * 8];
|
|
|
|
uint8_t* m_backBufferPtr;
|
|
//uint8_t* m_m_renderBufferPtr;
|
|
|
|
//uint8_t* m_originalBuffer;
|
|
//uint8_t** m_originalBufferLocation;
|
|
size_t m_canvasSize;
|
|
|
|
bool m_needUpdateGUI;
|
|
} SWiFiDeauthApp;
|
|
|
|
/////// INIT STATE ///////
|
|
static void esp8266_deauth_app_init(SWiFiDeauthApp* const app) {
|
|
app->m_context = Undefined;
|
|
|
|
app->m_canvasSize = 128 * 8 * 8;
|
|
memset(app->m_backBuffer, DEAUTH_APP_DEBUG ? 0xFF : 0x00, app->m_canvasSize);
|
|
//memset(app->m_renderBuffer, DEAUTH_APP_DEBUG ? 0xFF : 0x00, app->m_canvasSize);
|
|
|
|
//app->m_originalBuffer = NULL;
|
|
//app->m_originalBufferLocation = NULL;
|
|
|
|
//app->m_m_renderBufferPtr = app->m_renderBuffer;
|
|
app->m_backBufferPtr = app->m_backBuffer;
|
|
|
|
app->m_GpioButtons.pinButtonUp = &gpio_ext_pc3;
|
|
app->m_GpioButtons.pinButtonDown = &gpio_ext_pb2;
|
|
app->m_GpioButtons.pinButtonOK = &gpio_ext_pb3;
|
|
app->m_GpioButtons.pinButtonBack = &gpio_ext_pa4;
|
|
|
|
app->m_needUpdateGUI = false;
|
|
|
|
#if ENABLE_MODULE_POWER
|
|
app->m_wifiDeauthModuleInitialized = false;
|
|
#else
|
|
app->m_wifiDeauthModuleInitialized = true;
|
|
#endif // ENABLE_MODULE_POWER
|
|
|
|
#if ENABLE_MODULE_DETECTION
|
|
app->m_wifiDeauthModuleAttached = false;
|
|
#else
|
|
app->m_wifiDeauthModuleAttached = true;
|
|
#endif
|
|
}
|
|
|
|
static void esp8266_deauth_module_render_callback(Canvas* const canvas, void* ctx) {
|
|
furi_assert(ctx);
|
|
SWiFiDeauthApp* app = ctx;
|
|
furi_mutex_acquire(app->mutex, FuriWaitForever);
|
|
|
|
//if(app->m_needUpdateGUI)
|
|
//{
|
|
// app->m_needUpdateGUI = false;
|
|
|
|
// //app->m_canvasSize = canvas_get_buffer_size(canvas);
|
|
// //app->m_originalBuffer = canvas_get_buffer(canvas);
|
|
// //app->m_originalBufferLocation = &u8g2_GetBufferPtr(&canvas->fb);
|
|
// //u8g2_GetBufferPtr(&canvas->fb) = app->m_m_renderBufferPtr;
|
|
//}
|
|
|
|
//uint8_t* exchangeBuffers = app->m_m_renderBufferPtr;
|
|
//app->m_m_renderBufferPtr = app->m_backBufferPtr;
|
|
//app->m_backBufferPtr = exchangeBuffers;
|
|
|
|
//if(app->m_needUpdateGUI)
|
|
//{
|
|
// //memcpy(app->m_renderBuffer, app->m_backBuffer, app->m_canvasSize);
|
|
// app->m_needUpdateGUI = false;
|
|
//}
|
|
|
|
switch(app->m_context) {
|
|
case Undefined: {
|
|
canvas_clear(canvas);
|
|
canvas_set_font(canvas, FontPrimary);
|
|
|
|
const char* strInitializing = "Something wrong";
|
|
canvas_draw_str(
|
|
canvas,
|
|
(u8g2_GetDisplayWidth(&canvas->fb) / 2) -
|
|
(canvas_string_width(canvas, strInitializing) / 2),
|
|
(u8g2_GetDisplayHeight(&canvas->fb) /
|
|
2) /* - (u8g2_GetMaxCharHeight(&canvas->fb) / 2)*/,
|
|
strInitializing);
|
|
} break;
|
|
case WaitingForModule:
|
|
#if ENABLE_MODULE_DETECTION
|
|
furi_assert(!app->m_wifiDeauthModuleAttached);
|
|
if(!app->m_wifiDeauthModuleAttached) {
|
|
canvas_clear(canvas);
|
|
canvas_set_font(canvas, FontSecondary);
|
|
|
|
const char* strInitializing = "Attach WiFi Deauther module";
|
|
canvas_draw_str(
|
|
canvas,
|
|
(u8g2_GetDisplayWidth(&canvas->fb) / 2) -
|
|
(canvas_string_width(canvas, strInitializing) / 2),
|
|
(u8g2_GetDisplayHeight(&canvas->fb) /
|
|
2) /* - (u8g2_GetMaxCharHeight(&canvas->fb) / 2)*/,
|
|
strInitializing);
|
|
}
|
|
#endif
|
|
break;
|
|
case Initializing:
|
|
#if ENABLE_MODULE_POWER
|
|
{
|
|
furi_assert(!app->m_wifiDeauthModuleInitialized);
|
|
if(!app->m_wifiDeauthModuleInitialized) {
|
|
canvas_set_font(canvas, FontPrimary);
|
|
|
|
const char* strInitializing = "Initializing...";
|
|
canvas_draw_str(
|
|
canvas,
|
|
(u8g2_GetDisplayWidth(&canvas->fb) / 2) -
|
|
(canvas_string_width(canvas, strInitializing) / 2),
|
|
(u8g2_GetDisplayHeight(&canvas->fb) / 2) -
|
|
(u8g2_GetMaxCharHeight(&canvas->fb) / 2),
|
|
strInitializing);
|
|
}
|
|
}
|
|
#endif // ENABLE_MODULE_POWER
|
|
break;
|
|
case ModuleActive: {
|
|
uint8_t* buffer = canvas_get_buffer(canvas);
|
|
app->m_canvasSize = canvas_get_buffer_size(canvas);
|
|
memcpy(buffer, app->m_backBuffer, app->m_canvasSize);
|
|
} break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
furi_mutex_release(app->mutex);
|
|
}
|
|
|
|
static void
|
|
esp8266_deauth_module_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) {
|
|
furi_assert(event_queue);
|
|
|
|
SPluginEvent event = {.m_type = EventTypeKey, .m_input = *input_event};
|
|
furi_message_queue_put(event_queue, &event, FuriWaitForever);
|
|
}
|
|
|
|
static void uart_on_irq_cb(UartIrqEvent ev, uint8_t data, void* context) {
|
|
furi_assert(context);
|
|
|
|
SWiFiDeauthApp* app = context;
|
|
|
|
DEAUTH_APP_LOG_I("uart_echo_on_irq_cb");
|
|
|
|
if(ev == UartIrqEventRXNE) {
|
|
DEAUTH_APP_LOG_I("ev == UartIrqEventRXNE");
|
|
furi_stream_buffer_send(app->m_rx_stream, &data, 1, 0);
|
|
furi_thread_flags_set(furi_thread_get_id(app->m_worker_thread), WorkerEventRx);
|
|
}
|
|
}
|
|
|
|
static int32_t uart_worker(void* context) {
|
|
furi_assert(context);
|
|
DEAUTH_APP_LOG_I("[UART] Worker thread init");
|
|
|
|
SWiFiDeauthApp* app = context;
|
|
furi_mutex_acquire(app->mutex, FuriWaitForever);
|
|
if(app == NULL) {
|
|
return 1;
|
|
}
|
|
|
|
FuriStreamBuffer* rx_stream = app->m_rx_stream;
|
|
|
|
furi_mutex_release(app->mutex);
|
|
|
|
#if ENABLE_MODULE_POWER
|
|
bool initialized = false;
|
|
|
|
FuriString* receivedString;
|
|
receivedString = furi_string_alloc();
|
|
#endif // ENABLE_MODULE_POWER
|
|
|
|
while(true) {
|
|
uint32_t events = furi_thread_flags_wait(
|
|
WorkerEventStop | WorkerEventRx, FuriFlagWaitAny, FuriWaitForever);
|
|
furi_check((events & FuriFlagError) == 0);
|
|
|
|
if(events & WorkerEventStop) break;
|
|
if(events & WorkerEventRx) {
|
|
DEAUTH_APP_LOG_I("[UART] Received data");
|
|
SWiFiDeauthApp* app = context;
|
|
furi_mutex_acquire(app->mutex, FuriWaitForever);
|
|
if(app == NULL) {
|
|
return 1;
|
|
}
|
|
|
|
size_t dataReceivedLength = 0;
|
|
int index = 0;
|
|
do {
|
|
const uint8_t dataBufferSize = 64;
|
|
uint8_t dataBuffer[dataBufferSize];
|
|
dataReceivedLength =
|
|
furi_stream_buffer_receive(rx_stream, dataBuffer, dataBufferSize, 25);
|
|
if(dataReceivedLength > 0) {
|
|
#if ENABLE_MODULE_POWER
|
|
if(!initialized) {
|
|
if(!(dataReceivedLength > strlen(MODULE_CONTEXT_INITIALIZATION))) {
|
|
DEAUTH_APP_LOG_I("[UART] Found possible init candidate");
|
|
for(uint16_t i = 0; i < dataReceivedLength; i++) {
|
|
furi_string_push_back(receivedString, dataBuffer[i]);
|
|
}
|
|
}
|
|
} else
|
|
#endif // ENABLE_MODULE_POWER
|
|
{
|
|
DEAUTH_APP_LOG_I("[UART] Data copied to backbuffer");
|
|
memcpy(app->m_backBuffer + index, dataBuffer, dataReceivedLength);
|
|
index += dataReceivedLength;
|
|
app->m_needUpdateGUI = true;
|
|
}
|
|
}
|
|
|
|
} while(dataReceivedLength > 0);
|
|
|
|
#if ENABLE_MODULE_POWER
|
|
if(!app->m_wifiDeauthModuleInitialized) {
|
|
if(furi_string_cmp_str(receivedString, MODULE_CONTEXT_INITIALIZATION) == 0) {
|
|
DEAUTH_APP_LOG_I("[UART] Initialized");
|
|
initialized = true;
|
|
app->m_wifiDeauthModuleInitialized = true;
|
|
app->m_context = ModuleActive;
|
|
furi_string_free(receivedString);
|
|
} else {
|
|
DEAUTH_APP_LOG_I("[UART] Not an initialization command");
|
|
furi_string_reset(receivedString);
|
|
}
|
|
}
|
|
#endif // ENABLE_MODULE_POWER
|
|
|
|
furi_mutex_release(app->mutex);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int32_t esp8266_deauth_app(void* p) {
|
|
UNUSED(p);
|
|
|
|
DEAUTH_APP_LOG_I("Init");
|
|
|
|
// FuriTimer* timer = furi_timer_alloc(blink_test_update, FuriTimerTypePeriodic, event_queue);
|
|
// furi_timer_start(timer, furi_kernel_get_tick_frequency());
|
|
|
|
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(SPluginEvent));
|
|
|
|
SWiFiDeauthApp* app = malloc(sizeof(SWiFiDeauthApp));
|
|
|
|
esp8266_deauth_app_init(app);
|
|
|
|
furi_hal_gpio_init_simple(app->m_GpioButtons.pinButtonUp, GpioModeOutputPushPull);
|
|
furi_hal_gpio_init_simple(app->m_GpioButtons.pinButtonDown, GpioModeOutputPushPull);
|
|
furi_hal_gpio_init_simple(app->m_GpioButtons.pinButtonOK, GpioModeOutputPushPull);
|
|
furi_hal_gpio_init_simple(app->m_GpioButtons.pinButtonBack, GpioModeOutputPushPull);
|
|
|
|
furi_hal_gpio_write(app->m_GpioButtons.pinButtonUp, true);
|
|
furi_hal_gpio_write(app->m_GpioButtons.pinButtonDown, true);
|
|
furi_hal_gpio_write(app->m_GpioButtons.pinButtonOK, true);
|
|
furi_hal_gpio_write(
|
|
app->m_GpioButtons.pinButtonBack, false); // GPIO15 - Boot fails if pulled HIGH
|
|
|
|
#if ENABLE_MODULE_DETECTION
|
|
furi_hal_gpio_init(
|
|
&gpio_ext_pc0,
|
|
GpioModeInput,
|
|
GpioPullUp,
|
|
GpioSpeedLow); // Connect to the Flipper's ground just to be sure
|
|
//furi_hal_gpio_add_int_callback(pinD0, input_isr_d0, this);
|
|
app->m_context = WaitingForModule;
|
|
#else
|
|
#if ENABLE_MODULE_POWER
|
|
app->m_context = Initializing;
|
|
furi_hal_power_enable_otg();
|
|
#else
|
|
app->m_context = ModuleActive;
|
|
#endif
|
|
#endif // ENABLE_MODULE_DETECTION
|
|
|
|
app->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
|
|
if(!app->mutex) {
|
|
DEAUTH_APP_LOG_E("cannot create mutex\r\n");
|
|
free(app);
|
|
return 255;
|
|
}
|
|
|
|
DEAUTH_APP_LOG_I("Mutex created");
|
|
|
|
//app->m_notification = furi_record_open(RECORD_NOTIFICATION);
|
|
|
|
ViewPort* view_port = view_port_alloc();
|
|
view_port_draw_callback_set(view_port, esp8266_deauth_module_render_callback, app);
|
|
view_port_input_callback_set(view_port, esp8266_deauth_module_input_callback, event_queue);
|
|
|
|
// Open GUI and register view_port
|
|
Gui* gui = furi_record_open(RECORD_GUI);
|
|
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
|
|
|
|
//notification_message(app->notification, &sequence_set_only_blue_255);
|
|
|
|
app->m_rx_stream = furi_stream_buffer_alloc(1 * 1024, 1);
|
|
|
|
app->m_worker_thread = furi_thread_alloc();
|
|
furi_thread_set_name(app->m_worker_thread, "WiFiDeauthModuleUARTWorker");
|
|
furi_thread_set_stack_size(app->m_worker_thread, 1 * 1024);
|
|
furi_thread_set_context(app->m_worker_thread, app);
|
|
furi_thread_set_callback(app->m_worker_thread, uart_worker);
|
|
furi_thread_start(app->m_worker_thread);
|
|
DEAUTH_APP_LOG_I("UART thread allocated");
|
|
|
|
// Enable uart listener
|
|
#if DISABLE_CONSOLE
|
|
furi_hal_console_disable();
|
|
#endif
|
|
furi_hal_uart_set_br(FuriHalUartIdUSART1, FLIPPERZERO_SERIAL_BAUD);
|
|
furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, uart_on_irq_cb, app);
|
|
DEAUTH_APP_LOG_I("UART Listener created");
|
|
|
|
SPluginEvent event;
|
|
for(bool processing = true; processing;) {
|
|
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
|
|
furi_mutex_acquire(app->mutex, FuriWaitForever);
|
|
|
|
#if ENABLE_MODULE_DETECTION
|
|
if(!app->m_wifiDeauthModuleAttached) {
|
|
if(furi_hal_gpio_read(&gpio_ext_pc0) == false) {
|
|
DEAUTH_APP_LOG_I("Module Attached");
|
|
app->m_wifiDeauthModuleAttached = true;
|
|
#if ENABLE_MODULE_POWER
|
|
app->m_context = Initializing;
|
|
furi_hal_power_enable_otg();
|
|
#else
|
|
app->m_context = ModuleActive;
|
|
#endif
|
|
}
|
|
}
|
|
#endif // ENABLE_MODULE_DETECTION
|
|
|
|
if(event_status == FuriStatusOk) {
|
|
if(event.m_type == EventTypeKey) {
|
|
if(app->m_wifiDeauthModuleInitialized) {
|
|
if(app->m_context == ModuleActive) {
|
|
switch(event.m_input.key) {
|
|
case InputKeyUp:
|
|
if(event.m_input.type == InputTypePress) {
|
|
DEAUTH_APP_LOG_I("Up Press");
|
|
furi_hal_gpio_write(app->m_GpioButtons.pinButtonUp, false);
|
|
} else if(event.m_input.type == InputTypeRelease) {
|
|
DEAUTH_APP_LOG_I("Up Release");
|
|
furi_hal_gpio_write(app->m_GpioButtons.pinButtonUp, true);
|
|
}
|
|
break;
|
|
case InputKeyDown:
|
|
if(event.m_input.type == InputTypePress) {
|
|
DEAUTH_APP_LOG_I("Down Press");
|
|
furi_hal_gpio_write(app->m_GpioButtons.pinButtonDown, false);
|
|
} else if(event.m_input.type == InputTypeRelease) {
|
|
DEAUTH_APP_LOG_I("Down Release");
|
|
furi_hal_gpio_write(app->m_GpioButtons.pinButtonDown, true);
|
|
}
|
|
break;
|
|
case InputKeyOk:
|
|
if(event.m_input.type == InputTypePress) {
|
|
DEAUTH_APP_LOG_I("OK Press");
|
|
furi_hal_gpio_write(app->m_GpioButtons.pinButtonOK, false);
|
|
} else if(event.m_input.type == InputTypeRelease) {
|
|
DEAUTH_APP_LOG_I("OK Release");
|
|
furi_hal_gpio_write(app->m_GpioButtons.pinButtonOK, true);
|
|
}
|
|
break;
|
|
case InputKeyBack:
|
|
if(event.m_input.type == InputTypePress) {
|
|
DEAUTH_APP_LOG_I("Back Press");
|
|
furi_hal_gpio_write(app->m_GpioButtons.pinButtonBack, false);
|
|
} else if(event.m_input.type == InputTypeRelease) {
|
|
DEAUTH_APP_LOG_I("Back Release");
|
|
furi_hal_gpio_write(app->m_GpioButtons.pinButtonBack, true);
|
|
} else if(event.m_input.type == InputTypeLong) {
|
|
DEAUTH_APP_LOG_I("Back Long");
|
|
processing = false;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
if(event.m_input.key == InputKeyBack) {
|
|
if(event.m_input.type == InputTypeShort ||
|
|
event.m_input.type == InputTypeLong) {
|
|
processing = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#if ENABLE_MODULE_DETECTION
|
|
if(app->m_wifiDeauthModuleAttached && furi_hal_gpio_read(&gpio_ext_pc0) == true) {
|
|
DEAUTH_APP_LOG_D("Module Disconnected - Exit");
|
|
processing = false;
|
|
app->m_wifiDeauthModuleAttached = false;
|
|
app->m_wifiDeauthModuleInitialized = false;
|
|
}
|
|
#endif
|
|
|
|
view_port_update(view_port);
|
|
furi_mutex_release(app->mutex);
|
|
}
|
|
|
|
DEAUTH_APP_LOG_I("Start exit app");
|
|
|
|
furi_thread_flags_set(furi_thread_get_id(app->m_worker_thread), WorkerEventStop);
|
|
furi_thread_join(app->m_worker_thread);
|
|
furi_thread_free(app->m_worker_thread);
|
|
|
|
DEAUTH_APP_LOG_I("Thread Deleted");
|
|
|
|
// Reset GPIO pins to default state
|
|
furi_hal_gpio_init(&gpio_ext_pc0, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
|
|
furi_hal_gpio_init(&gpio_ext_pc3, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
|
|
furi_hal_gpio_init(&gpio_ext_pb2, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
|
|
furi_hal_gpio_init(&gpio_ext_pb3, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
|
|
furi_hal_gpio_init(&gpio_ext_pa4, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
|
|
|
|
#if DISABLE_CONSOLE
|
|
furi_hal_console_enable();
|
|
#endif
|
|
|
|
//*app->m_originalBufferLocation = app->m_originalBuffer;
|
|
|
|
view_port_enabled_set(view_port, false);
|
|
|
|
gui_remove_view_port(gui, view_port);
|
|
|
|
// Close gui record
|
|
furi_record_close(RECORD_GUI);
|
|
//furi_record_close(RECORD_NOTIFICATION);
|
|
app->m_gui = NULL;
|
|
|
|
view_port_free(view_port);
|
|
|
|
furi_message_queue_free(event_queue);
|
|
|
|
furi_stream_buffer_free(app->m_rx_stream);
|
|
|
|
furi_mutex_free(app->mutex);
|
|
|
|
// Free rest
|
|
free(app);
|
|
|
|
DEAUTH_APP_LOG_I("App freed");
|
|
|
|
#if ENABLE_MODULE_POWER
|
|
furi_hal_power_disable_otg();
|
|
#endif
|
|
|
|
return 0;
|
|
}
|