esubghz_chat.c 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829
  1. #include <furi_hal.h>
  2. #include <gui/elements.h>
  3. #include <gui/gui.h>
  4. #include "helpers/radio_device_loader.h"
  5. #include "esubghz_chat_i.h"
  6. #include "bgloader_api.h"
  7. #define CHAT_LEAVE_DELAY 10
  8. #define TICK_INTERVAL 50
  9. #define MESSAGE_COMPLETION_TIMEOUT 500
  10. #define KBD_UNLOCK_CNT 3
  11. #define KBD_UNLOCK_TIMEOUT 1000
  12. /* Callback for RX events from the Sub-GHz worker. Records the current ticks as
  13. * the time of the last reception. */
  14. static void have_read_cb(void* context) {
  15. furi_assert(context);
  16. ESubGhzChatState* state = context;
  17. state->last_time_rx_data = furi_get_tick();
  18. }
  19. /* Sets the header for the chat input field depending on whether or not a
  20. * message preview exists. */
  21. void set_chat_input_header(ESubGhzChatState* state) {
  22. if(strlen(state->msg_preview) == 0) {
  23. text_input_set_header_text(state->text_input, "Message");
  24. } else {
  25. text_input_set_header_text(state->text_input, state->msg_preview);
  26. }
  27. }
  28. /* Appends the latest message to the chat box and prepares the message preview.
  29. */
  30. void append_msg(ESubGhzChatState* state, const char* msg) {
  31. /* append message to text box */
  32. furi_string_cat_printf(state->chat_box_store, "\n%s", msg);
  33. /* prepare message preview */
  34. strncpy(state->msg_preview, msg, MSG_PREVIEW_SIZE);
  35. state->msg_preview[MSG_PREVIEW_SIZE] = 0;
  36. set_chat_input_header(state);
  37. /* reset text box contents and focus */
  38. text_box_set_text(state->chat_box, furi_string_get_cstr(state->chat_box_store));
  39. text_box_set_focus(state->chat_box, TextBoxFocusEnd);
  40. }
  41. /* Decrypts a message for post_rx(). */
  42. static bool post_rx_decrypt(ESubGhzChatState* state, size_t rx_size) {
  43. bool ret = crypto_ctx_decrypt(
  44. state->crypto_ctx, state->rx_buffer, rx_size, (uint8_t*)state->rx_str_buffer);
  45. if(ret) {
  46. state->rx_str_buffer[rx_size - (MSG_OVERHEAD)] = 0;
  47. } else {
  48. state->rx_str_buffer[0] = 0;
  49. }
  50. return ret;
  51. }
  52. /* Post RX handler, decrypts received messages and calls append_msg(). */
  53. static void post_rx(ESubGhzChatState* state, size_t rx_size) {
  54. furi_assert(state);
  55. if(rx_size == 0) {
  56. return;
  57. }
  58. furi_check(rx_size <= RX_TX_BUFFER_SIZE);
  59. /* decrypt if necessary */
  60. if(!state->encrypted) {
  61. memcpy(state->rx_str_buffer, state->rx_buffer, rx_size);
  62. state->rx_str_buffer[rx_size] = 0;
  63. /* remove trailing newline if it is there, for compat with CLI
  64. * Sub-GHz chat */
  65. if(state->rx_str_buffer[rx_size - 1] == '\n') {
  66. state->rx_str_buffer[rx_size - 1] = 0;
  67. }
  68. } else {
  69. /* if decryption fails output an error message */
  70. if(!post_rx_decrypt(state, rx_size)) {
  71. strcpy(state->rx_str_buffer, "ERR: Decryption failed!");
  72. }
  73. }
  74. /* append message to text box and prepare message preview */
  75. append_msg(state, state->rx_str_buffer);
  76. /* send notification (make the flipper vibrate) */
  77. notification_message(state->notification, &sequence_single_vibro);
  78. }
  79. /* Reads the message from msg_input, encrypts it if necessary and then
  80. * transmits it. */
  81. void tx_msg_input(ESubGhzChatState* state) {
  82. /* encrypt message if necessary */
  83. size_t msg_len = strlen(furi_string_get_cstr(state->msg_input));
  84. size_t tx_size = msg_len;
  85. if(state->encrypted) {
  86. tx_size += MSG_OVERHEAD;
  87. furi_check(tx_size <= sizeof(state->tx_buffer));
  88. crypto_ctx_encrypt(
  89. state->crypto_ctx,
  90. (uint8_t*)furi_string_get_cstr(state->msg_input),
  91. msg_len,
  92. state->tx_buffer);
  93. } else {
  94. tx_size += 2;
  95. furi_check(tx_size <= sizeof(state->tx_buffer));
  96. memcpy(state->tx_buffer, furi_string_get_cstr(state->msg_input), msg_len);
  97. /* append \r\n for compat with Sub-GHz CLI chat */
  98. state->tx_buffer[msg_len] = '\r';
  99. state->tx_buffer[msg_len + 1] = '\n';
  100. }
  101. /* transmit */
  102. subghz_tx_rx_worker_write(state->subghz_worker, state->tx_buffer, tx_size);
  103. }
  104. /* Displays information on frequency, encryption and radio type in the text
  105. * box. Also clears the text input buffer to remove the password and starts the
  106. * Sub-GHz worker. After starting the worker a join message is transmitted. */
  107. void enter_chat(ESubGhzChatState* state) {
  108. furi_string_cat_printf(state->chat_box_store, "Frequency: %lu", state->frequency);
  109. furi_string_cat_printf(
  110. state->chat_box_store, "\nEncrypted: %s", (state->encrypted ? "yes" : "no"));
  111. subghz_tx_rx_worker_start(state->subghz_worker, state->subghz_device, state->frequency);
  112. if(strcmp(state->subghz_device->name, "cc1101_ext") == 0) {
  113. furi_string_cat_printf(state->chat_box_store, "\nRadio: External");
  114. } else {
  115. furi_string_cat_printf(state->chat_box_store, "\nRadio: Internal");
  116. }
  117. /* concatenate the name prefix and join message */
  118. furi_string_set(state->msg_input, state->name_prefix);
  119. furi_string_cat_str(state->msg_input, " joined chat.");
  120. /* encrypt and transmit message */
  121. tx_msg_input(state);
  122. /* clear message input buffer */
  123. furi_string_set_char(state->msg_input, 0, 0);
  124. }
  125. /* Sends a leave message */
  126. void exit_chat(ESubGhzChatState* state) {
  127. /* concatenate the name prefix and leave message */
  128. furi_string_set(state->msg_input, state->name_prefix);
  129. furi_string_cat_str(state->msg_input, " left chat.");
  130. /* encrypt and transmit message */
  131. tx_msg_input(state);
  132. /* clear message input buffer */
  133. furi_string_set_char(state->msg_input, 0, 0);
  134. /* wait for leave message to be delivered */
  135. furi_delay_ms(CHAT_LEAVE_DELAY);
  136. }
  137. /* Whether or not to display the locked message. */
  138. static bool kbd_lock_msg_display(ESubGhzChatState* state) {
  139. return (state->kbd_lock_msg_ticks != 0);
  140. }
  141. /* Whether or not to hide the locked message again. */
  142. static bool kbd_lock_msg_reset_timeout(ESubGhzChatState* state) {
  143. if(state->kbd_lock_msg_ticks == 0) {
  144. return false;
  145. }
  146. if(furi_get_tick() - state->kbd_lock_msg_ticks > KBD_UNLOCK_TIMEOUT) {
  147. return true;
  148. }
  149. return false;
  150. }
  151. /* Resets the timeout for the locked message and turns off the backlight if
  152. * specified. */
  153. static void kbd_lock_msg_reset(ESubGhzChatState* state, bool backlight_off) {
  154. state->kbd_lock_msg_ticks = 0;
  155. state->kbd_lock_count = 0;
  156. if(backlight_off) {
  157. notification_message(state->notification, &sequence_display_backlight_off);
  158. }
  159. }
  160. /* Locks the keyboard. */
  161. static void kbd_lock(ESubGhzChatState* state) {
  162. state->kbd_locked = true;
  163. kbd_lock_msg_reset(state, true);
  164. }
  165. /* Unlocks the keyboard. */
  166. static void kbd_unlock(ESubGhzChatState* state) {
  167. state->kbd_locked = false;
  168. kbd_lock_msg_reset(state, false);
  169. }
  170. /* Custom event callback for view dispatcher. Just calls scene manager. */
  171. static bool esubghz_chat_custom_event_callback(void* context, uint32_t event) {
  172. FURI_LOG_T(APPLICATION_NAME, "esubghz_chat_custom_event_callback");
  173. furi_assert(context);
  174. ESubGhzChatState* state = context;
  175. return scene_manager_handle_custom_event(state->scene_manager, event);
  176. }
  177. /* Navigation event callback for view dispatcher. Just calls scene manager. */
  178. static bool esubghz_chat_navigation_event_callback(void* context) {
  179. FURI_LOG_T(APPLICATION_NAME, "esubghz_chat_navigation_event_callback");
  180. furi_assert(context);
  181. ESubGhzChatState* state = context;
  182. return scene_manager_handle_back_event(state->scene_manager);
  183. }
  184. static void esubghz_chat_check_messages(ESubGhzChatState* state) {
  185. /* if the maximum message size was reached or the
  186. * MESSAGE_COMPLETION_TIMEOUT has expired, retrieve a message and call
  187. * post_rx() */
  188. size_t avail = 0;
  189. while((avail = subghz_tx_rx_worker_available(state->subghz_worker)) > 0) {
  190. volatile uint32_t since_last_rx = furi_get_tick() - state->last_time_rx_data;
  191. if(avail < RX_TX_BUFFER_SIZE && since_last_rx < MESSAGE_COMPLETION_TIMEOUT) {
  192. break;
  193. }
  194. size_t rx_size =
  195. subghz_tx_rx_worker_read(state->subghz_worker, state->rx_buffer, RX_TX_BUFFER_SIZE);
  196. post_rx(state, rx_size);
  197. }
  198. }
  199. /* Tick event callback for view dispatcher. Called every TICK_INTERVAL. Resets
  200. * the locked message if necessary. Retrieves a received message from the
  201. * Sub-GHz worker and calls post_rx(). Then calls the scene manager. */
  202. static void esubghz_chat_tick_event_callback(void* context) {
  203. FURI_LOG_T(APPLICATION_NAME, "esubghz_chat_tick_event_callback");
  204. furi_assert(context);
  205. ESubGhzChatState* state = context;
  206. /* reset locked message if necessary */
  207. if(kbd_lock_msg_reset_timeout(state)) {
  208. kbd_lock_msg_reset(state, true);
  209. }
  210. esubghz_chat_check_messages(state);
  211. /* call scene manager */
  212. scene_manager_handle_tick_event(state->scene_manager);
  213. }
  214. /* Hooks into the view port's draw callback to overlay the keyboard locked
  215. * message. */
  216. static void esubghz_hooked_draw_callback(Canvas* canvas, void* context) {
  217. FURI_LOG_T(APPLICATION_NAME, "esubghz_hooked_draw_callback");
  218. furi_assert(canvas);
  219. furi_assert(context);
  220. ESubGhzChatState* state = context;
  221. /* call original callback */
  222. state->orig_draw_cb(canvas, state->view_dispatcher);
  223. /* display if the keyboard is locked */
  224. if(state->kbd_locked) {
  225. canvas_set_font(canvas, FontPrimary);
  226. elements_multiline_text_framed(canvas, 42, 30, "Locked");
  227. }
  228. /* display the unlock message if necessary */
  229. if(kbd_lock_msg_display(state)) {
  230. canvas_set_font(canvas, FontSecondary);
  231. elements_bold_rounded_frame(canvas, 14, 8, 99, 48);
  232. elements_multiline_text(canvas, 65, 26, "To unlock\npress:");
  233. canvas_draw_icon(canvas, 65, 42, &I_Pin_back_arrow_10x8);
  234. canvas_draw_icon(canvas, 80, 42, &I_Pin_back_arrow_10x8);
  235. canvas_draw_icon(canvas, 95, 42, &I_Pin_back_arrow_10x8);
  236. canvas_draw_icon(canvas, 16, 13, &I_WarningDolphin_45x42);
  237. }
  238. }
  239. /* Hooks into the view port's input callback to handle the user locking the
  240. * keyboard. */
  241. static void esubghz_hooked_input_callback(InputEvent* event, void* context) {
  242. FURI_LOG_T(APPLICATION_NAME, "esubghz_hooked_input_callback");
  243. furi_assert(event);
  244. furi_assert(context);
  245. ESubGhzChatState* state = context;
  246. /* if the keyboard is locked no key presses are forwarded */
  247. if(state->kbd_locked) {
  248. /* key has been pressed, display the unlock message and
  249. * initiate the timer */
  250. if(state->kbd_lock_count == 0) {
  251. state->kbd_lock_msg_ticks = furi_get_tick();
  252. }
  253. /* back button has been pressed, increase the lock counter */
  254. if(event->key == InputKeyBack && event->type == InputTypeShort) {
  255. state->kbd_lock_count++;
  256. }
  257. /* unlock the keyboard */
  258. if(state->kbd_lock_count >= KBD_UNLOCK_CNT) {
  259. kbd_unlock(state);
  260. }
  261. /* do not handle the event */
  262. return;
  263. }
  264. /* handle long press of back key to exit for real */
  265. if(event->key == InputKeyBack) {
  266. if(state->view_dispatcher->current_view == text_input_get_view(state->text_input)) {
  267. if(event->type == InputTypeLong) {
  268. state->exit_for_real = true;
  269. view_dispatcher_stop(state->view_dispatcher);
  270. return;
  271. }
  272. }
  273. }
  274. if(event->key == InputKeyOk) {
  275. /* if we are in the chat view and no input is ongoing, allow
  276. * locking */
  277. if(state->view_dispatcher->current_view == text_box_get_view(state->chat_box) &&
  278. !(state->kbd_ok_input_ongoing)) {
  279. /* lock keyboard upon long press of Ok button */
  280. if(event->type == InputTypeLong) {
  281. kbd_lock(state);
  282. }
  283. /* do not handle any Ok key events to prevent blocking
  284. * of other keys */
  285. return;
  286. }
  287. /* handle ongoing inputs when changing to chat view */
  288. if(event->type == InputTypePress) {
  289. state->kbd_ok_input_ongoing = true;
  290. } else if(event->type == InputTypeRelease) {
  291. state->kbd_ok_input_ongoing = false;
  292. }
  293. }
  294. if(event->key == InputKeyLeft) {
  295. /* if we are in the chat view and no input is ongoing, allow
  296. * switching to msg input */
  297. if(state->view_dispatcher->current_view == text_box_get_view(state->chat_box) &&
  298. !(state->kbd_left_input_ongoing)) {
  299. /* go to msg input upon short press of Left button */
  300. if(event->type == InputTypeShort) {
  301. view_dispatcher_send_custom_event(
  302. state->view_dispatcher, ESubGhzChatEvent_GotoMsgInput);
  303. }
  304. /* do not handle any Left key events to prevent
  305. * blocking of other keys */
  306. return;
  307. }
  308. /* handle ongoing inputs when changing to chat view */
  309. if(event->type == InputTypePress) {
  310. state->kbd_left_input_ongoing = true;
  311. } else if(event->type == InputTypeRelease) {
  312. state->kbd_left_input_ongoing = false;
  313. }
  314. }
  315. if(event->key == InputKeyRight) {
  316. /* if we are in the chat view and no input is ongoing, allow
  317. * switching to key display */
  318. if(state->view_dispatcher->current_view == text_box_get_view(state->chat_box) &&
  319. !(state->kbd_right_input_ongoing)) {
  320. /* go to key display upon short press of Right button
  321. */
  322. if(event->type == InputTypeShort) {
  323. view_dispatcher_send_custom_event(
  324. state->view_dispatcher, ESubGhzChatEvent_GotoKeyDisplay);
  325. }
  326. /* do not handle any Right key events to prevent
  327. * blocking of other keys */
  328. return;
  329. }
  330. /* handle ongoing inputs when changing to chat view */
  331. if(event->type == InputTypePress) {
  332. state->kbd_right_input_ongoing = true;
  333. } else if(event->type == InputTypeRelease) {
  334. state->kbd_right_input_ongoing = false;
  335. }
  336. }
  337. /* call original callback */
  338. state->orig_input_cb(event, state->view_dispatcher);
  339. }
  340. static const char* esubghz_get_bgloader_app_path(const char* args) {
  341. size_t base_args_len = strlen(APP_BASE_ARGS);
  342. return (args + base_args_len + 1);
  343. }
  344. static bool esubghz_run_with_bgloader(const char* args) {
  345. size_t base_args_len = strlen(APP_BASE_ARGS);
  346. if(args == NULL) {
  347. return false;
  348. }
  349. if(strncmp(args, APP_BASE_ARGS, base_args_len) != 0) {
  350. return false;
  351. }
  352. if(strlen(args) < base_args_len + 2) {
  353. return false;
  354. }
  355. if(args[base_args_len] != ':') {
  356. return false;
  357. }
  358. const char* app_path = esubghz_get_bgloader_app_path(args);
  359. return furi_record_exists(app_path);
  360. }
  361. static void esubghz_attach_to_gui(ESubGhzChatState* state) {
  362. Gui* gui = furi_record_open(RECORD_GUI);
  363. view_dispatcher_attach_to_gui(state->view_dispatcher, gui, ViewDispatcherTypeFullscreen);
  364. }
  365. static void esubghz_detach_from_gui(ESubGhzChatState* state) {
  366. gui_remove_view_port(state->view_dispatcher->gui, state->view_dispatcher->view_port);
  367. state->view_dispatcher->gui = NULL;
  368. furi_record_close(RECORD_GUI);
  369. }
  370. static void esubghz_bgloader_loop(ESubGhzChatState* state, const char* bg_app_path) {
  371. while(true) {
  372. view_dispatcher_run(state->view_dispatcher);
  373. if(state->exit_for_real) {
  374. /* exit for real */
  375. break;
  376. }
  377. BGLoaderApp* bg_app = furi_record_open(bg_app_path);
  378. /* signal loader that we're ready to go to background */
  379. BGLoaderMessage msg;
  380. msg.type = BGLoaderMessageType_LoaderBackground;
  381. furi_check(
  382. furi_message_queue_put(bg_app->to_loader, &msg, FuriWaitForever) == FuriStatusOk);
  383. esubghz_detach_from_gui(state);
  384. while(true) {
  385. /* wait for loader to wake us up again */
  386. if(furi_message_queue_get(bg_app->to_app, &msg, TICK_INTERVAL) != FuriStatusOk) {
  387. /* check for messages on timeout */
  388. esubghz_chat_check_messages(state);
  389. continue;
  390. }
  391. if(msg.type == BGLoaderMessageType_AppReattached) {
  392. break;
  393. } else {
  394. furi_check(0);
  395. }
  396. }
  397. furi_record_close(bg_app_path);
  398. esubghz_attach_to_gui(state);
  399. }
  400. }
  401. static bool helper_strings_alloc(ESubGhzChatState* state) {
  402. furi_assert(state);
  403. state->name_prefix = furi_string_alloc();
  404. if(state->name_prefix == NULL) {
  405. return false;
  406. }
  407. state->msg_input = furi_string_alloc();
  408. if(state->msg_input == NULL) {
  409. furi_string_free(state->name_prefix);
  410. return false;
  411. }
  412. return true;
  413. }
  414. static void helper_strings_free(ESubGhzChatState* state) {
  415. furi_assert(state);
  416. furi_string_free(state->name_prefix);
  417. furi_string_free(state->msg_input);
  418. }
  419. static bool chat_box_alloc(ESubGhzChatState* state) {
  420. furi_assert(state);
  421. state->chat_box = text_box_alloc();
  422. if(state->chat_box == NULL) {
  423. return false;
  424. }
  425. state->chat_box_store = furi_string_alloc();
  426. if(state->chat_box_store == NULL) {
  427. text_box_free(state->chat_box);
  428. return false;
  429. }
  430. furi_string_reserve(state->chat_box_store, CHAT_BOX_STORE_SIZE);
  431. furi_string_set_char(state->chat_box_store, 0, 0);
  432. text_box_set_text(state->chat_box, furi_string_get_cstr(state->chat_box_store));
  433. text_box_set_focus(state->chat_box, TextBoxFocusEnd);
  434. return true;
  435. }
  436. static void chat_box_free(ESubGhzChatState* state) {
  437. furi_assert(state);
  438. text_box_free(state->chat_box);
  439. furi_string_free(state->chat_box_store);
  440. }
  441. int32_t esubghz_chat(const char* args) {
  442. if(furi_hal_nfc_is_hal_ready() != FuriHalNfcErrorNone) {
  443. printf("NFC chip failed to start\r\n");
  444. return -1;
  445. }
  446. furi_hal_nfc_acquire();
  447. furi_hal_nfc_low_power_mode_start();
  448. furi_hal_nfc_release();
  449. furry_hal_nfc_init();
  450. /* init the crypto system */
  451. crypto_init();
  452. int32_t err = -1;
  453. FURI_LOG_I(APPLICATION_NAME, "Starting...");
  454. /* allocate necessary structs and buffers */
  455. ESubGhzChatState* state = malloc(sizeof(ESubGhzChatState));
  456. if(state == NULL) {
  457. goto err_alloc;
  458. }
  459. memset(state, 0, sizeof(*state));
  460. state->scene_manager = scene_manager_alloc(&esubghz_chat_scene_event_handlers, state);
  461. if(state->scene_manager == NULL) {
  462. goto err_alloc_sm;
  463. }
  464. state->view_dispatcher = view_dispatcher_alloc();
  465. if(state->view_dispatcher == NULL) {
  466. goto err_alloc_vd;
  467. }
  468. if(!helper_strings_alloc(state)) {
  469. goto err_alloc_hs;
  470. }
  471. state->menu = menu_alloc();
  472. if(state->menu == NULL) {
  473. goto err_alloc_menu;
  474. }
  475. state->text_input = text_input_alloc();
  476. if(state->text_input == NULL) {
  477. goto err_alloc_ti;
  478. }
  479. state->hex_key_input = byte_input_alloc();
  480. if(state->hex_key_input == NULL) {
  481. goto err_alloc_hki;
  482. }
  483. if(!chat_box_alloc(state)) {
  484. goto err_alloc_cb;
  485. }
  486. state->key_display = dialog_ex_alloc();
  487. if(state->key_display == NULL) {
  488. goto err_alloc_kd;
  489. }
  490. state->nfc_popup = popup_alloc();
  491. if(state->nfc_popup == NULL) {
  492. goto err_alloc_np;
  493. }
  494. state->subghz_worker = subghz_tx_rx_worker_alloc();
  495. if(state->subghz_worker == NULL) {
  496. goto err_alloc_worker;
  497. }
  498. NfcDevice* nfcdevic = nfc_device_alloc();
  499. state->nfc_dev_data = &nfcdevic->dev_data;
  500. state->nfc_worker = nfc_worker_alloc();
  501. if(state->nfc_worker == NULL) {
  502. goto err_alloc_nworker;
  503. }
  504. /*state->nfc_dev_data = malloc(sizeof(NfcDeviceData));
  505. if(state->nfc_dev_data == NULL) {
  506. goto err_alloc_ndevdata;
  507. }*/
  508. //memset(state->nfc_dev_data, 0, sizeof(NfcDeviceData));
  509. state->crypto_ctx = crypto_ctx_alloc();
  510. if(state->crypto_ctx == NULL) {
  511. goto err_alloc_crypto;
  512. }
  513. /* set the default frequency */
  514. state->frequency = DEFAULT_FREQ;
  515. /* in the first few views there is no background support */
  516. state->exit_for_real = true;
  517. /* set the have_read callback of the Sub-GHz worker */
  518. subghz_tx_rx_worker_set_callback_have_read(state->subghz_worker, have_read_cb, state);
  519. /* enter suppress charge mode */
  520. furi_hal_power_suppress_charge_enter();
  521. /* init internal device */
  522. subghz_devices_init();
  523. state->subghz_device =
  524. radio_device_loader_set(state->subghz_device, SubGhzRadioDeviceTypeExternalCC1101);
  525. subghz_devices_reset(state->subghz_device);
  526. subghz_devices_idle(state->subghz_device);
  527. /* set chat name prefix */
  528. furi_string_printf(state->name_prefix, "%s", furi_hal_version_get_name_ptr());
  529. /* get notification record, we use this to make the flipper vibrate */
  530. /* no error handling here, don't know how */
  531. state->notification = furi_record_open(RECORD_NOTIFICATION);
  532. /* hook into the view port's draw and input callbacks */
  533. state->orig_draw_cb = state->view_dispatcher->view_port->draw_callback;
  534. state->orig_input_cb = state->view_dispatcher->view_port->input_callback;
  535. view_port_draw_callback_set(
  536. state->view_dispatcher->view_port, esubghz_hooked_draw_callback, state);
  537. view_port_input_callback_set(
  538. state->view_dispatcher->view_port, esubghz_hooked_input_callback, state);
  539. /* set callbacks for view dispatcher */
  540. view_dispatcher_set_event_callback_context(state->view_dispatcher, state);
  541. view_dispatcher_set_custom_event_callback(
  542. state->view_dispatcher, esubghz_chat_custom_event_callback);
  543. view_dispatcher_set_navigation_event_callback(
  544. state->view_dispatcher, esubghz_chat_navigation_event_callback);
  545. view_dispatcher_set_tick_event_callback(
  546. state->view_dispatcher, esubghz_chat_tick_event_callback, TICK_INTERVAL);
  547. /* add our two views to the view dispatcher */
  548. view_dispatcher_add_view(
  549. state->view_dispatcher, ESubGhzChatView_Menu, menu_get_view(state->menu));
  550. view_dispatcher_add_view(
  551. state->view_dispatcher, ESubGhzChatView_Input, text_input_get_view(state->text_input));
  552. view_dispatcher_add_view(
  553. state->view_dispatcher,
  554. ESubGhzChatView_HexKeyInput,
  555. byte_input_get_view(state->hex_key_input));
  556. view_dispatcher_add_view(
  557. state->view_dispatcher, ESubGhzChatView_ChatBox, text_box_get_view(state->chat_box));
  558. view_dispatcher_add_view(
  559. state->view_dispatcher,
  560. ESubGhzChatView_KeyDisplay,
  561. dialog_ex_get_view(state->key_display));
  562. view_dispatcher_add_view(
  563. state->view_dispatcher, ESubGhzChatView_NfcPopup, popup_get_view(state->nfc_popup));
  564. /* get the GUI record and attach the view dispatcher to the GUI */
  565. /* no error handling here, don't know how */
  566. Gui* gui = furi_record_open(RECORD_GUI);
  567. view_dispatcher_attach_to_gui(state->view_dispatcher, gui, ViewDispatcherTypeFullscreen);
  568. /* switch to the key menu scene */
  569. scene_manager_next_scene(state->scene_manager, ESubGhzChatScene_KeyMenu);
  570. /* run the view dispatcher, this call only returns when we close the
  571. * application */
  572. if(!esubghz_run_with_bgloader(args)) {
  573. view_dispatcher_run(state->view_dispatcher);
  574. } else {
  575. const char* bg_app_path = esubghz_get_bgloader_app_path(args);
  576. esubghz_bgloader_loop(state, bg_app_path);
  577. }
  578. /* if it is running, stop the Sub-GHz worker */
  579. if(subghz_tx_rx_worker_is_running(state->subghz_worker)) {
  580. exit_chat(state);
  581. subghz_tx_rx_worker_stop(state->subghz_worker);
  582. }
  583. /* if it is running, stop the NFC worker */
  584. nfc_worker_stop(state->nfc_worker);
  585. err = 0;
  586. /* close GUI record */
  587. furi_record_close(RECORD_GUI);
  588. /* remove our two views from the view dispatcher */
  589. view_dispatcher_remove_view(state->view_dispatcher, ESubGhzChatView_Menu);
  590. view_dispatcher_remove_view(state->view_dispatcher, ESubGhzChatView_Input);
  591. view_dispatcher_remove_view(state->view_dispatcher, ESubGhzChatView_HexKeyInput);
  592. view_dispatcher_remove_view(state->view_dispatcher, ESubGhzChatView_ChatBox);
  593. view_dispatcher_remove_view(state->view_dispatcher, ESubGhzChatView_KeyDisplay);
  594. view_dispatcher_remove_view(state->view_dispatcher, ESubGhzChatView_NfcPopup);
  595. /* close notification record */
  596. furi_record_close(RECORD_NOTIFICATION);
  597. /* clear the key and potential password */
  598. crypto_explicit_bzero(state->text_input_store, sizeof(state->text_input_store));
  599. crypto_explicit_bzero(state->hex_key_input_store, sizeof(state->hex_key_input_store));
  600. crypto_explicit_bzero(state->key_hex_str, sizeof(state->key_hex_str));
  601. crypto_ctx_clear(state->crypto_ctx);
  602. /* clear nfc data */
  603. if(state->nfc_dev_data->parsed_data != NULL) {
  604. furi_string_free(state->nfc_dev_data->parsed_data);
  605. }
  606. //nfc_device_data_clear(state->nfc_dev_data);
  607. crypto_explicit_bzero(state->nfc_dev_data, sizeof(NfcDeviceData));
  608. /* deinit devices */
  609. radio_device_loader_end(state->subghz_device);
  610. subghz_devices_deinit();
  611. /* exit suppress charge mode */
  612. furi_hal_power_suppress_charge_exit();
  613. /* free everything we allocated */
  614. crypto_ctx_free(state->crypto_ctx);
  615. err_alloc_crypto:
  616. //free(state->nfc_dev_data);
  617. //err_alloc_ndevdata:
  618. nfc_worker_free(state->nfc_worker);
  619. nfc_device_free(nfcdevic);
  620. err_alloc_nworker:
  621. subghz_tx_rx_worker_free(state->subghz_worker);
  622. err_alloc_worker:
  623. popup_free(state->nfc_popup);
  624. err_alloc_np:
  625. dialog_ex_free(state->key_display);
  626. err_alloc_kd:
  627. chat_box_free(state);
  628. err_alloc_cb:
  629. byte_input_free(state->hex_key_input);
  630. err_alloc_hki:
  631. text_input_free(state->text_input);
  632. err_alloc_ti:
  633. menu_free(state->menu);
  634. err_alloc_menu:
  635. helper_strings_free(state);
  636. err_alloc_hs:
  637. view_dispatcher_free(state->view_dispatcher);
  638. err_alloc_vd:
  639. scene_manager_free(state->scene_manager);
  640. err_alloc_sm:
  641. free(state);
  642. err_alloc:
  643. if(err != 0) {
  644. FURI_LOG_E(APPLICATION_NAME, "Failed to launch (alloc error)!");
  645. } else {
  646. FURI_LOG_I(APPLICATION_NAME, "Clean exit.");
  647. }
  648. furry_hal_nfc_deinit();
  649. //
  650. furi_hal_nfc_acquire();
  651. furi_hal_nfc_low_power_mode_start();
  652. furi_hal_nfc_release();
  653. return err;
  654. }