esp8266_deauth.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560
  1. #include <furi.h>
  2. #include <furi_hal_gpio.h>
  3. #include <furi_hal_power.h>
  4. #include <furi_hal_serial_control.h>
  5. #include <furi_hal_serial.h>
  6. #include <gui/canvas_i.h>
  7. #include <gui/gui.h>
  8. #include <input/input.h>
  9. #include <expansion/expansion.h>
  10. //#include <math.h>
  11. //#include <notification/notification.h>
  12. //#include <notification/notification_messages.h>
  13. //#include <stdlib.h>
  14. #include <momentum/momentum.h>
  15. #include "FlipperZeroWiFiDeauthModuleDefines.h"
  16. #define UART_CH (momentum_settings.uart_esp_channel)
  17. #define DEAUTH_APP_DEBUG 0
  18. #if DEAUTH_APP_DEBUG
  19. #define APP_NAME_TAG "WiFi_Deauther"
  20. #define DEAUTH_APP_LOG_I(format, ...) FURI_LOG_I(APP_NAME_TAG, format, ##__VA_ARGS__)
  21. #define DEAUTH_APP_LOG_D(format, ...) FURI_LOG_D(APP_NAME_TAG, format, ##__VA_ARGS__)
  22. #define DEAUTH_APP_LOG_E(format, ...) FURI_LOG_E(APP_NAME_TAG, format, ##__VA_ARGS__)
  23. #else
  24. #define DEAUTH_APP_LOG_I(format, ...)
  25. #define DEAUTH_APP_LOG_D(format, ...)
  26. #define DEAUTH_APP_LOG_E(format, ...)
  27. #endif // WIFI_APP_DEBUG
  28. #define ENABLE_MODULE_POWER 1
  29. #define ENABLE_MODULE_DETECTION 1
  30. typedef enum EEventType // app internally defined event types
  31. {
  32. EventTypeKey // flipper input.h type
  33. } EEventType;
  34. typedef struct SPluginEvent {
  35. EEventType m_type;
  36. InputEvent m_input;
  37. } SPluginEvent;
  38. typedef enum EAppContext {
  39. Undefined,
  40. WaitingForModule,
  41. Initializing,
  42. ModuleActive,
  43. } EAppContext;
  44. typedef enum EWorkerEventFlags {
  45. WorkerEventReserved = (1 << 0), // Reserved for StreamBuffer internal event
  46. WorkerEventStop = (1 << 1),
  47. WorkerEventRx = (1 << 2),
  48. } EWorkerEventFlags;
  49. typedef struct SGpioButtons {
  50. GpioPin const* pinButtonUp;
  51. GpioPin const* pinButtonDown;
  52. GpioPin const* pinButtonOK;
  53. GpioPin const* pinButtonBack;
  54. } SGpioButtons;
  55. typedef struct SWiFiDeauthApp {
  56. FuriMutex* mutex;
  57. Gui* m_gui;
  58. FuriThread* m_worker_thread;
  59. //NotificationApp* m_notification;
  60. FuriStreamBuffer* m_rx_stream;
  61. FuriHalSerialHandle* serial_handle;
  62. SGpioButtons m_GpioButtons;
  63. bool m_wifiDeauthModuleInitialized;
  64. bool m_wifiDeauthModuleAttached;
  65. EAppContext m_context;
  66. uint8_t m_backBuffer[128 * 8 * 8];
  67. //uint8_t m_renderBuffer[128 * 8 * 8];
  68. uint8_t* m_backBufferPtr;
  69. //uint8_t* m_m_renderBufferPtr;
  70. //uint8_t* m_originalBuffer;
  71. //uint8_t** m_originalBufferLocation;
  72. size_t m_canvasSize;
  73. bool m_needUpdateGUI;
  74. } SWiFiDeauthApp;
  75. /////// INIT STATE ///////
  76. static void esp8266_deauth_app_init(SWiFiDeauthApp* const app) {
  77. app->m_context = Undefined;
  78. app->m_canvasSize = 128 * 8 * 8;
  79. memset(app->m_backBuffer, DEAUTH_APP_DEBUG ? 0xFF : 0x00, app->m_canvasSize);
  80. //memset(app->m_renderBuffer, DEAUTH_APP_DEBUG ? 0xFF : 0x00, app->m_canvasSize);
  81. //app->m_originalBuffer = NULL;
  82. //app->m_originalBufferLocation = NULL;
  83. //app->m_m_renderBufferPtr = app->m_renderBuffer;
  84. app->m_backBufferPtr = app->m_backBuffer;
  85. app->m_GpioButtons.pinButtonUp = &gpio_ext_pc3;
  86. app->m_GpioButtons.pinButtonDown = &gpio_ext_pb2;
  87. app->m_GpioButtons.pinButtonOK = &gpio_ext_pb3;
  88. app->m_GpioButtons.pinButtonBack = &gpio_ext_pa4;
  89. app->m_needUpdateGUI = false;
  90. #if ENABLE_MODULE_POWER
  91. app->m_wifiDeauthModuleInitialized = false;
  92. #else
  93. app->m_wifiDeauthModuleInitialized = true;
  94. #endif // ENABLE_MODULE_POWER
  95. #if ENABLE_MODULE_DETECTION
  96. app->m_wifiDeauthModuleAttached = false;
  97. #else
  98. app->m_wifiDeauthModuleAttached = true;
  99. #endif
  100. }
  101. static void esp8266_deauth_module_render_callback(Canvas* const canvas, void* ctx) {
  102. furi_assert(ctx);
  103. SWiFiDeauthApp* app = ctx;
  104. furi_mutex_acquire(app->mutex, FuriWaitForever);
  105. //if(app->m_needUpdateGUI)
  106. //{
  107. // app->m_needUpdateGUI = false;
  108. // //app->m_canvasSize = canvas_get_buffer_size(canvas);
  109. // //app->m_originalBuffer = canvas_get_buffer(canvas);
  110. // //app->m_originalBufferLocation = &u8g2_GetBufferPtr(&canvas->fb);
  111. // //u8g2_GetBufferPtr(&canvas->fb) = app->m_m_renderBufferPtr;
  112. //}
  113. //uint8_t* exchangeBuffers = app->m_m_renderBufferPtr;
  114. //app->m_m_renderBufferPtr = app->m_backBufferPtr;
  115. //app->m_backBufferPtr = exchangeBuffers;
  116. //if(app->m_needUpdateGUI)
  117. //{
  118. // //memcpy(app->m_renderBuffer, app->m_backBuffer, app->m_canvasSize);
  119. // app->m_needUpdateGUI = false;
  120. //}
  121. switch(app->m_context) {
  122. case Undefined: {
  123. canvas_clear(canvas);
  124. canvas_set_font(canvas, FontPrimary);
  125. const char* strInitializing = "Something wrong";
  126. canvas_draw_str(
  127. canvas,
  128. (128 / 2) - (canvas_string_width(canvas, strInitializing) / 2),
  129. (64 / 2) /* - (canvas_current_font_height(canvas) / 2)*/,
  130. strInitializing);
  131. } break;
  132. case WaitingForModule:
  133. #if ENABLE_MODULE_DETECTION
  134. furi_assert(!app->m_wifiDeauthModuleAttached);
  135. if(!app->m_wifiDeauthModuleAttached) {
  136. canvas_clear(canvas);
  137. canvas_set_font(canvas, FontSecondary);
  138. const char* strInitializing = "Attach WiFi Deauther module";
  139. canvas_draw_str(
  140. canvas,
  141. (128 / 2) - (canvas_string_width(canvas, strInitializing) / 2),
  142. (64 / 2) /* - (canvas_current_font_height(canvas) / 2)*/,
  143. strInitializing);
  144. }
  145. #endif
  146. break;
  147. case Initializing:
  148. #if ENABLE_MODULE_POWER
  149. {
  150. furi_assert(!app->m_wifiDeauthModuleInitialized);
  151. if(!app->m_wifiDeauthModuleInitialized) {
  152. canvas_set_font(canvas, FontPrimary);
  153. const char* strInitializing = "Initializing...";
  154. canvas_draw_str(
  155. canvas,
  156. (128 / 2) - (canvas_string_width(canvas, strInitializing) / 2),
  157. (64 / 2) - (canvas_current_font_height(canvas) / 2),
  158. strInitializing);
  159. }
  160. }
  161. #endif // ENABLE_MODULE_POWER
  162. break;
  163. case ModuleActive: {
  164. uint8_t* buffer = canvas->fb.tile_buf_ptr;
  165. app->m_canvasSize = gui_get_framebuffer_size(app->m_gui);
  166. memcpy(buffer, app->m_backBuffer, app->m_canvasSize);
  167. } break;
  168. default:
  169. break;
  170. }
  171. furi_mutex_release(app->mutex);
  172. }
  173. static void esp8266_deauth_module_input_callback(InputEvent* input_event, void* ctx) {
  174. furi_assert(ctx);
  175. FuriMessageQueue* event_queue = ctx;
  176. SPluginEvent event = {.m_type = EventTypeKey, .m_input = *input_event};
  177. furi_message_queue_put(event_queue, &event, FuriWaitForever);
  178. }
  179. static void
  180. uart_on_irq_cb(FuriHalSerialHandle* handle, FuriHalSerialRxEvent event, void* context) {
  181. furi_assert(context);
  182. SWiFiDeauthApp* app = context;
  183. DEAUTH_APP_LOG_I("uart_echo_on_irq_cb");
  184. if(event == FuriHalSerialRxEventData) {
  185. uint8_t data = furi_hal_serial_async_rx(handle);
  186. DEAUTH_APP_LOG_I("ev == UartIrqEventRXNE");
  187. furi_stream_buffer_send(app->m_rx_stream, &data, 1, 0);
  188. furi_thread_flags_set(furi_thread_get_id(app->m_worker_thread), WorkerEventRx);
  189. }
  190. }
  191. static int32_t uart_worker(void* context) {
  192. furi_assert(context);
  193. DEAUTH_APP_LOG_I("[UART] Worker thread init");
  194. SWiFiDeauthApp* app = context;
  195. furi_mutex_acquire(app->mutex, FuriWaitForever);
  196. if(app == NULL) {
  197. return 1;
  198. }
  199. FuriStreamBuffer* rx_stream = app->m_rx_stream;
  200. furi_mutex_release(app->mutex);
  201. #if ENABLE_MODULE_POWER
  202. bool initialized = false;
  203. FuriString* receivedString;
  204. receivedString = furi_string_alloc();
  205. #endif // ENABLE_MODULE_POWER
  206. while(true) {
  207. uint32_t events = furi_thread_flags_wait(
  208. WorkerEventStop | WorkerEventRx, FuriFlagWaitAny, FuriWaitForever);
  209. furi_check((events & FuriFlagError) == 0);
  210. if(events & WorkerEventStop) break;
  211. if(events & WorkerEventRx) {
  212. DEAUTH_APP_LOG_I("[UART] Received data");
  213. SWiFiDeauthApp* app = context;
  214. furi_mutex_acquire(app->mutex, FuriWaitForever);
  215. if(app == NULL) {
  216. return 1;
  217. }
  218. size_t dataReceivedLength = 0;
  219. int index = 0;
  220. do {
  221. const uint8_t dataBufferSize = 64;
  222. uint8_t dataBuffer[dataBufferSize];
  223. dataReceivedLength =
  224. furi_stream_buffer_receive(rx_stream, dataBuffer, dataBufferSize, 25);
  225. if(dataReceivedLength > 0) {
  226. #if ENABLE_MODULE_POWER
  227. if(!initialized) {
  228. if(!(dataReceivedLength > strlen(MODULE_CONTEXT_INITIALIZATION))) {
  229. DEAUTH_APP_LOG_I("[UART] Found possible init candidate");
  230. for(uint16_t i = 0; i < dataReceivedLength; i++) {
  231. furi_string_push_back(receivedString, dataBuffer[i]);
  232. }
  233. }
  234. } else
  235. #endif // ENABLE_MODULE_POWER
  236. {
  237. DEAUTH_APP_LOG_I("[UART] Data copied to backbuffer");
  238. memcpy(app->m_backBuffer + index, dataBuffer, dataReceivedLength);
  239. index += dataReceivedLength;
  240. app->m_needUpdateGUI = true;
  241. }
  242. }
  243. } while(dataReceivedLength > 0);
  244. #if ENABLE_MODULE_POWER
  245. if(!app->m_wifiDeauthModuleInitialized) {
  246. if(furi_string_cmp_str(receivedString, MODULE_CONTEXT_INITIALIZATION) == 0) {
  247. DEAUTH_APP_LOG_I("[UART] Initialized");
  248. initialized = true;
  249. app->m_wifiDeauthModuleInitialized = true;
  250. app->m_context = ModuleActive;
  251. furi_string_free(receivedString);
  252. } else {
  253. DEAUTH_APP_LOG_I("[UART] Not an initialization command");
  254. furi_string_reset(receivedString);
  255. }
  256. }
  257. #endif // ENABLE_MODULE_POWER
  258. furi_mutex_release(app->mutex);
  259. }
  260. }
  261. return 0;
  262. }
  263. int32_t esp8266_deauth_app(void* p) {
  264. UNUSED(p);
  265. // Disable expansion protocol to avoid interference with UART Handle
  266. Expansion* expansion = furi_record_open(RECORD_EXPANSION);
  267. expansion_disable(expansion);
  268. DEAUTH_APP_LOG_I("Init");
  269. // FuriTimer* timer = furi_timer_alloc(blink_test_update, FuriTimerTypePeriodic, event_queue);
  270. // furi_timer_start(timer, furi_kernel_get_tick_frequency());
  271. FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(SPluginEvent));
  272. SWiFiDeauthApp* app = malloc(sizeof(SWiFiDeauthApp));
  273. esp8266_deauth_app_init(app);
  274. furi_hal_gpio_init_simple(app->m_GpioButtons.pinButtonUp, GpioModeOutputPushPull);
  275. furi_hal_gpio_init_simple(app->m_GpioButtons.pinButtonDown, GpioModeOutputPushPull);
  276. furi_hal_gpio_init_simple(app->m_GpioButtons.pinButtonOK, GpioModeOutputPushPull);
  277. furi_hal_gpio_init_simple(app->m_GpioButtons.pinButtonBack, GpioModeOutputPushPull);
  278. furi_hal_gpio_write(app->m_GpioButtons.pinButtonUp, true);
  279. furi_hal_gpio_write(app->m_GpioButtons.pinButtonDown, true);
  280. furi_hal_gpio_write(app->m_GpioButtons.pinButtonOK, true);
  281. furi_hal_gpio_write(
  282. app->m_GpioButtons.pinButtonBack, false); // GPIO15 - Boot fails if pulled HIGH
  283. #if ENABLE_MODULE_DETECTION
  284. furi_hal_gpio_init(
  285. &gpio_ext_pc0,
  286. GpioModeInput,
  287. GpioPullUp,
  288. GpioSpeedLow); // Connect to the Flipper's ground just to be sure
  289. //furi_hal_gpio_add_int_callback(pinD0, input_isr_d0, this);
  290. app->m_context = WaitingForModule;
  291. #else
  292. #if ENABLE_MODULE_POWER
  293. app->m_context = Initializing;
  294. uint8_t attempts = 0;
  295. while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) {
  296. furi_hal_power_enable_otg();
  297. furi_delay_ms(10);
  298. }
  299. furi_delay_ms(200);
  300. #else
  301. app->m_context = ModuleActive;
  302. #endif
  303. #endif // ENABLE_MODULE_DETECTION
  304. app->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
  305. if(!app->mutex) {
  306. DEAUTH_APP_LOG_E("cannot create mutex\r\n");
  307. free(app);
  308. // Return previous state of expansion
  309. expansion_enable(expansion);
  310. furi_record_close(RECORD_EXPANSION);
  311. return 255;
  312. }
  313. DEAUTH_APP_LOG_I("Mutex created");
  314. //app->m_notification = furi_record_open(RECORD_NOTIFICATION);
  315. ViewPort* view_port = view_port_alloc();
  316. view_port_draw_callback_set(view_port, esp8266_deauth_module_render_callback, app);
  317. view_port_input_callback_set(view_port, esp8266_deauth_module_input_callback, event_queue);
  318. // Open GUI and register view_port
  319. app->m_gui = furi_record_open(RECORD_GUI);
  320. gui_add_view_port(app->m_gui, view_port, GuiLayerFullscreen);
  321. //notification_message(app->notification, &sequence_set_only_blue_255);
  322. app->m_rx_stream = furi_stream_buffer_alloc(1 * 1024, 1);
  323. app->m_worker_thread = furi_thread_alloc();
  324. furi_thread_set_name(app->m_worker_thread, "WiFiDeauthModuleUARTWorker");
  325. furi_thread_set_stack_size(app->m_worker_thread, 1 * 1024);
  326. furi_thread_set_context(app->m_worker_thread, app);
  327. furi_thread_set_callback(app->m_worker_thread, uart_worker);
  328. furi_thread_start(app->m_worker_thread);
  329. DEAUTH_APP_LOG_I("UART thread allocated");
  330. // Enable uart listener
  331. app->serial_handle = furi_hal_serial_control_acquire(FuriHalSerialIdUsart);
  332. furi_check(app->serial_handle);
  333. furi_hal_serial_init(app->serial_handle, FLIPPERZERO_SERIAL_BAUD);
  334. furi_hal_serial_async_rx_start(app->serial_handle, uart_on_irq_cb, app, false);
  335. DEAUTH_APP_LOG_I("UART Listener created");
  336. SPluginEvent event;
  337. for(bool processing = true; processing;) {
  338. FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
  339. furi_mutex_acquire(app->mutex, FuriWaitForever);
  340. #if ENABLE_MODULE_DETECTION
  341. if(!app->m_wifiDeauthModuleAttached) {
  342. if(furi_hal_gpio_read(&gpio_ext_pc0) == false) {
  343. DEAUTH_APP_LOG_I("Module Attached");
  344. app->m_wifiDeauthModuleAttached = true;
  345. #if ENABLE_MODULE_POWER
  346. app->m_context = Initializing;
  347. uint8_t attempts2 = 0;
  348. while(!furi_hal_power_is_otg_enabled() && attempts2++ < 3) {
  349. furi_hal_power_enable_otg();
  350. furi_delay_ms(10);
  351. }
  352. #else
  353. app->m_context = ModuleActive;
  354. #endif
  355. }
  356. }
  357. #endif // ENABLE_MODULE_DETECTION
  358. if(event_status == FuriStatusOk) {
  359. if(event.m_type == EventTypeKey) {
  360. if(app->m_wifiDeauthModuleInitialized) {
  361. if(app->m_context == ModuleActive) {
  362. switch(event.m_input.key) {
  363. case InputKeyUp:
  364. if(event.m_input.type == InputTypePress) {
  365. DEAUTH_APP_LOG_I("Up Press");
  366. furi_hal_gpio_write(app->m_GpioButtons.pinButtonUp, false);
  367. } else if(event.m_input.type == InputTypeRelease) {
  368. DEAUTH_APP_LOG_I("Up Release");
  369. furi_hal_gpio_write(app->m_GpioButtons.pinButtonUp, true);
  370. }
  371. break;
  372. case InputKeyDown:
  373. if(event.m_input.type == InputTypePress) {
  374. DEAUTH_APP_LOG_I("Down Press");
  375. furi_hal_gpio_write(app->m_GpioButtons.pinButtonDown, false);
  376. } else if(event.m_input.type == InputTypeRelease) {
  377. DEAUTH_APP_LOG_I("Down Release");
  378. furi_hal_gpio_write(app->m_GpioButtons.pinButtonDown, true);
  379. }
  380. break;
  381. case InputKeyOk:
  382. if(event.m_input.type == InputTypePress) {
  383. DEAUTH_APP_LOG_I("OK Press");
  384. furi_hal_gpio_write(app->m_GpioButtons.pinButtonOK, false);
  385. } else if(event.m_input.type == InputTypeRelease) {
  386. DEAUTH_APP_LOG_I("OK Release");
  387. furi_hal_gpio_write(app->m_GpioButtons.pinButtonOK, true);
  388. }
  389. break;
  390. case InputKeyBack:
  391. if(event.m_input.type == InputTypePress) {
  392. DEAUTH_APP_LOG_I("Back Press");
  393. furi_hal_gpio_write(app->m_GpioButtons.pinButtonBack, false);
  394. } else if(event.m_input.type == InputTypeRelease) {
  395. DEAUTH_APP_LOG_I("Back Release");
  396. furi_hal_gpio_write(app->m_GpioButtons.pinButtonBack, true);
  397. } else if(event.m_input.type == InputTypeLong) {
  398. DEAUTH_APP_LOG_I("Back Long");
  399. processing = false;
  400. }
  401. break;
  402. default:
  403. break;
  404. }
  405. }
  406. } else {
  407. if(event.m_input.key == InputKeyBack) {
  408. if(event.m_input.type == InputTypeShort ||
  409. event.m_input.type == InputTypeLong) {
  410. processing = false;
  411. }
  412. }
  413. }
  414. }
  415. }
  416. #if ENABLE_MODULE_DETECTION
  417. if(app->m_wifiDeauthModuleAttached && furi_hal_gpio_read(&gpio_ext_pc0) == true) {
  418. DEAUTH_APP_LOG_D("Module Disconnected - Exit");
  419. processing = false;
  420. app->m_wifiDeauthModuleAttached = false;
  421. app->m_wifiDeauthModuleInitialized = false;
  422. }
  423. #endif
  424. furi_mutex_release(app->mutex);
  425. view_port_update(view_port);
  426. }
  427. DEAUTH_APP_LOG_I("Start exit app");
  428. furi_hal_serial_async_rx_stop(app->serial_handle);
  429. furi_hal_serial_deinit(app->serial_handle);
  430. furi_hal_serial_control_release(app->serial_handle);
  431. furi_thread_flags_set(furi_thread_get_id(app->m_worker_thread), WorkerEventStop);
  432. furi_thread_join(app->m_worker_thread);
  433. furi_thread_free(app->m_worker_thread);
  434. DEAUTH_APP_LOG_I("Thread Deleted");
  435. // Reset GPIO pins to default state
  436. furi_hal_gpio_init(&gpio_ext_pc0, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
  437. furi_hal_gpio_init(&gpio_ext_pc3, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
  438. furi_hal_gpio_init(&gpio_ext_pb2, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
  439. furi_hal_gpio_init(&gpio_ext_pb3, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
  440. furi_hal_gpio_init(&gpio_ext_pa4, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
  441. //*app->m_originalBufferLocation = app->m_originalBuffer;
  442. view_port_enabled_set(view_port, false);
  443. gui_remove_view_port(app->m_gui, view_port);
  444. // Close gui record
  445. furi_record_close(RECORD_GUI);
  446. //furi_record_close(RECORD_NOTIFICATION);
  447. app->m_gui = NULL;
  448. view_port_free(view_port);
  449. furi_message_queue_free(event_queue);
  450. furi_stream_buffer_free(app->m_rx_stream);
  451. furi_mutex_free(app->mutex);
  452. // Free rest
  453. free(app);
  454. DEAUTH_APP_LOG_I("App freed");
  455. #if ENABLE_MODULE_POWER
  456. if(furi_hal_power_is_otg_enabled()) {
  457. furi_hal_power_disable_otg();
  458. }
  459. #endif
  460. // Return previous state of expansion
  461. expansion_enable(expansion);
  462. furi_record_close(RECORD_EXPANSION);
  463. return 0;
  464. }