esp8266_deauth.c 19 KB

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