Просмотр исходного кода

[FL-2198], [FL-2161] NFC emulation refactoring (#968)

* rfal: add state changed callback
* furi_hal_nfc: add NFC-A emulation API
* nfc: add emulation logger, refactor scenes
* elements: fix text_box element
* gui: fix text box module
* nfc: remove unnecessary buffers
* nfc: introduce emulation callback concept
* nfc: format sources
* bt settings: fix incorrect scene switch
* bt settings: format sources
* Debug: fix x2d import for python 3
* Gui: rename method name widget_clear to widget_reset
* nfc: add nfca emulation handler
* nfc: add global custom events enum
* nfc: UID emulation Data -> Log
* furi_hal_nfc: fix incorrect timings
* u2f, badusb: widget_clear() -> widget_reset()

Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
gornekich 4 лет назад
Родитель
Сommit
8cfd0eab9e
46 измененных файлов с 715 добавлено и 247 удалено
  1. 1 1
      applications/bad_usb/scenes/bad_usb_scene_error.c
  2. 1 2
      applications/gui/elements.c
  3. 23 23
      applications/gui/modules/text_box.c
  4. 9 11
      applications/gui/modules/text_box.h
  5. 2 2
      applications/gui/modules/widget.c
  6. 2 2
      applications/gui/modules/widget.h
  7. 2 2
      applications/ibutton/scene/ibutton_scene_delete_confirm.cpp
  8. 1 1
      applications/ibutton/scene/ibutton_scene_info.cpp
  9. 11 0
      applications/nfc/helpers/nfc_custom_event.h
  10. 7 0
      applications/nfc/nfc_device.h
  11. 1 0
      applications/nfc/nfc_i.h
  12. 40 55
      applications/nfc/nfc_worker.c
  13. 1 1
      applications/nfc/scenes/nfc_scene_delete.c
  14. 2 4
      applications/nfc/scenes/nfc_scene_delete_success.c
  15. 13 13
      applications/nfc/scenes/nfc_scene_device_info.c
  16. 112 34
      applications/nfc/scenes/nfc_scene_emulate_uid.c
  17. 2 4
      applications/nfc/scenes/nfc_scene_read_card.c
  18. 1 1
      applications/nfc/scenes/nfc_scene_read_card_success.c
  19. 2 4
      applications/nfc/scenes/nfc_scene_read_emv_app.c
  20. 2 4
      applications/nfc/scenes/nfc_scene_read_emv_data.c
  21. 1 1
      applications/nfc/scenes/nfc_scene_read_emv_data_success.c
  22. 2 4
      applications/nfc/scenes/nfc_scene_read_mifare_ul.c
  23. 16 30
      applications/nfc/scenes/nfc_scene_read_mifare_ul_success.c
  24. 2 4
      applications/nfc/scenes/nfc_scene_restore_original.c
  25. 2 4
      applications/nfc/scenes/nfc_scene_save_name.c
  26. 2 4
      applications/nfc/scenes/nfc_scene_save_success.c
  27. 2 4
      applications/nfc/scenes/nfc_scene_set_atqa.c
  28. 2 4
      applications/nfc/scenes/nfc_scene_set_sak.c
  29. 2 4
      applications/nfc/scenes/nfc_scene_set_uid.c
  30. 1 1
      applications/nfc/views/bank_card.c
  31. 1 1
      applications/subghz/scenes/subghz_scene_delete.c
  32. 1 1
      applications/subghz/scenes/subghz_scene_delete_raw.c
  33. 1 1
      applications/subghz/scenes/subghz_scene_need_saving.c
  34. 1 1
      applications/subghz/scenes/subghz_scene_receiver_info.c
  35. 1 1
      applications/subghz/scenes/subghz_scene_show_error.c
  36. 1 1
      applications/u2f/scenes/u2f_scene_error.c
  37. 6 5
      debug/PyCortexMDebug/cmdebug/svd.py
  38. 131 0
      firmware/targets/f6/furi_hal/furi_hal_nfc.c
  39. 131 0
      firmware/targets/f7/furi_hal/furi_hal_nfc.c
  40. 31 0
      firmware/targets/furi_hal_include/furi_hal_nfc.h
  41. 27 2
      lib/ST25RFAL002/include/rfal_rf.h
  42. 43 2
      lib/ST25RFAL002/source/st25r3916/rfal_rfst25r3916.c
  43. 26 5
      lib/nfc_protocols/mifare_ultralight.c
  44. 5 3
      lib/nfc_protocols/mifare_ultralight.h
  45. 32 0
      lib/nfc_protocols/nfca.c
  46. 10 0
      lib/nfc_protocols/nfca.h

+ 1 - 1
applications/bad_usb/scenes/bad_usb_scene_error.c

@@ -49,5 +49,5 @@ bool bad_usb_scene_error_on_event(void* context, SceneManagerEvent event) {
 
 void bad_usb_scene_error_on_exit(void* context) {
     BadUsbApp* app = context;
-    widget_clear(app->widget);
+    widget_reset(app->widget);
 }

+ 1 - 2
applications/gui/elements.c

@@ -594,7 +594,6 @@ void elements_text_box(
             line[line_num].height = line_height;
             line[line_num].descender = line_descender;
             if(total_height_min + line_leading_min > height) {
-                line_num--;
                 break;
             }
             total_height_min += line_leading_min;
@@ -640,7 +639,7 @@ void elements_text_box(
             uint8_t free_pixel_num = height - total_height_min;
             uint8_t fill_pixel = 0;
             uint8_t j = 1;
-            line[0].y = line[0].height;
+            line[0].y = y + line[0].height;
             while(fill_pixel < free_pixel_num) {
                 line[j].y = line[j - 1].y + line[j - 1].leading_min + 1;
                 fill_pixel++;

+ 23 - 23
applications/gui/modules/text_box.c

@@ -7,17 +7,16 @@
 
 struct TextBox {
     View* view;
-    void* context;
-    TextBoxExitCallback callback;
 };
 
 typedef struct {
     const char* text;
     char* text_pos;
     string_t text_formatted;
-    size_t scroll_pos;
-    size_t scroll_num;
+    int32_t scroll_pos;
+    int32_t scroll_num;
     TextBoxFont font;
+    TextBoxFocus focus;
     bool formatted;
 } TextBoxModel;
 
@@ -52,12 +51,6 @@ static void text_box_process_up(TextBox* text_box) {
         });
 }
 
-static void text_box_process_back(TextBox* text_box) {
-    if(text_box->callback) {
-        text_box->callback(text_box->context);
-    }
-}
-
 static void text_box_insert_endline(Canvas* canvas, TextBoxModel* model) {
     size_t i = 0;
     size_t line_width = 0;
@@ -84,8 +77,18 @@ static void text_box_insert_endline(Canvas* canvas, TextBoxModel* model) {
     line_num++;
     model->text = string_get_cstr(model->text_formatted);
     model->text_pos = (char*)model->text;
-    model->scroll_num = MAX(line_num - 4, 0);
-    model->scroll_pos = 0;
+    if(model->focus == TextBoxFocusEnd && line_num > 5) {
+        // Set text position to 5th line from the end
+        for(uint8_t i = 0; i < line_num - 5; i++) {
+            while(*model->text_pos++ != '\n') {
+            };
+        }
+        model->scroll_num = line_num - 4;
+        model->scroll_pos = line_num - 5;
+    } else {
+        model->scroll_num = MAX(line_num - 4, 0);
+        model->scroll_pos = 0;
+    }
 }
 
 static void text_box_view_draw_callback(Canvas* canvas, void* _model) {
@@ -119,9 +122,6 @@ static bool text_box_view_input_callback(InputEvent* event, void* context) {
         } else if(event->key == InputKeyUp) {
             text_box_process_up(text_box);
             consumed = true;
-        } else if(event->key == InputKeyBack) {
-            text_box_process_back(text_box);
-            consumed = true;
         }
     }
     return consumed;
@@ -172,10 +172,9 @@ void text_box_reset(TextBox* text_box) {
             model->text = NULL;
             string_set_str(model->text_formatted, "");
             model->font = TextBoxFontText;
+            model->focus = TextBoxFocusStart;
             return true;
         });
-    text_box->context = NULL;
-    text_box->callback = NULL;
 }
 
 void text_box_set_text(TextBox* text_box, const char* text) {
@@ -185,6 +184,7 @@ void text_box_set_text(TextBox* text_box, const char* text) {
     with_view_model(
         text_box->view, (TextBoxModel * model) {
             model->text = text;
+            string_reset(model->text_formatted);
             string_reserve(model->text_formatted, strlen(text));
             model->formatted = false;
             return true;
@@ -201,12 +201,12 @@ void text_box_set_font(TextBox* text_box, TextBoxFont font) {
         });
 }
 
-void text_box_set_context(TextBox* text_box, void* context) {
+void text_box_set_focus(TextBox* text_box, TextBoxFocus focus) {
     furi_assert(text_box);
-    text_box->context = context;
-}
 
-void text_box_set_exit_callback(TextBox* text_box, TextBoxExitCallback callback) {
-    furi_assert(text_box);
-    text_box->callback = callback;
+    with_view_model(
+        text_box->view, (TextBoxModel * model) {
+            model->focus = focus;
+            return true;
+        });
 }

+ 9 - 11
applications/gui/modules/text_box.h

@@ -13,13 +13,17 @@ extern "C" {
 
 /** TextBox anonymous structure */
 typedef struct TextBox TextBox;
-typedef void (*TextBoxExitCallback)(void* context);
 
 typedef enum {
     TextBoxFontText,
     TextBoxFontHex,
 } TextBoxFont;
 
+typedef enum {
+    TextBoxFocusStart,
+    TextBoxFocusEnd,
+} TextBoxFocus;
+
 /** Allocate and initialize text_box
  *
  * @return     TextBox instance
@@ -60,19 +64,13 @@ void text_box_set_text(TextBox* text_box, const char* text);
  */
 void text_box_set_font(TextBox* text_box, TextBoxFont font);
 
-/** Set text_box context
- *
- * @param      text_box  TextBox instance
- * @param      context   context pointer
- */
-void text_box_set_context(TextBox* text_box, void* context);
-
-/** Set exit callback
+/** Set TextBox focus
+ * @note Use to display from start or from end
  *
  * @param      text_box  TextBox instance
- * @param      callback  TextBoxExitCallback callback pointer
+ * @param      focus     TextBoxFocus instance
  */
-void text_box_set_exit_callback(TextBox* text_box, TextBoxExitCallback callback);
+void text_box_set_focus(TextBox* text_box, TextBoxFocus focus);
 
 #ifdef __cplusplus
 }

+ 2 - 2
applications/gui/modules/widget.c

@@ -68,7 +68,7 @@ Widget* widget_alloc() {
     return widget;
 }
 
-void widget_clear(Widget* widget) {
+void widget_reset(Widget* widget) {
     furi_assert(widget);
 
     with_view_model(
@@ -89,7 +89,7 @@ void widget_clear(Widget* widget) {
 void widget_free(Widget* widget) {
     furi_assert(widget);
     // Free all elements
-    widget_clear(widget);
+    widget_reset(widget);
     // Free elements container
     with_view_model(
         widget->view, (GuiWidgetModel * model) {

+ 2 - 2
applications/gui/modules/widget.h

@@ -27,11 +27,11 @@ Widget* widget_alloc();
  */
 void widget_free(Widget* widget);
 
-/** Clear Widget
+/** Reset Widget
  *
  * @param      widget  Widget instance
  */
-void widget_clear(Widget* widget);
+void widget_reset(Widget* widget);
 
 /** Get Widget view
  *

+ 2 - 2
applications/ibutton/scene/ibutton_scene_delete_confirm.cpp

@@ -75,7 +75,7 @@ void iButtonSceneDeleteConfirm::on_exit(iButtonApp* app) {
 
     app->set_text_store("");
 
-    widget_clear(widget);
+    widget_reset(widget);
 }
 
 void iButtonSceneDeleteConfirm::widget_callback(
@@ -91,4 +91,4 @@ void iButtonSceneDeleteConfirm::widget_callback(
     }
 
     app->get_view_manager()->send_event(&event);
-}
+}

+ 1 - 1
applications/ibutton/scene/ibutton_scene_info.cpp

@@ -69,7 +69,7 @@ void iButtonSceneInfo::on_exit(iButtonApp* app) {
 
     app->set_text_store("");
 
-    widget_clear(widget);
+    widget_reset(widget);
 }
 
 void iButtonSceneInfo::widget_callback(GuiButtonType result, InputType type, void* context) {

+ 11 - 0
applications/nfc/helpers/nfc_custom_event.h

@@ -0,0 +1,11 @@
+#pragma once
+
+enum NfcCustomEvent {
+    // Reserve first 100 events for button types and indexes, starting from 0
+    NfcCustomEventReserved = 100,
+
+    NfcCustomEventViewExit,
+    NfcCustomEventWorkerExit,
+    NfcCustomEventByteInputDone,
+    NfcCustomEventTextInputDone,
+};

+ 7 - 0
applications/nfc/nfc_device.h

@@ -9,6 +9,7 @@
 
 #define NFC_DEV_NAME_MAX_LEN 22
 #define NFC_FILE_NAME_MAX_LEN 120
+#define NFC_READER_DATA_MAX_SIZE 64
 
 #define NFC_APP_FOLDER "/any/nfc"
 #define NFC_APP_EXTENSION ".nfc"
@@ -54,11 +55,17 @@ typedef struct {
     uint16_t currency_code;
 } NfcEmvData;
 
+typedef struct {
+    uint8_t data[NFC_READER_DATA_MAX_SIZE];
+    uint16_t size;
+} NfcReaderRequestData;
+
 typedef struct {
     NfcDeviceCommonData nfc_data;
     union {
         NfcEmvData emv_data;
         MifareUlData mf_ul_data;
+        NfcReaderRequestData reader_data;
     };
 } NfcDeviceData;
 

+ 1 - 0
applications/nfc/nfc_i.h

@@ -26,6 +26,7 @@
 #include "views/bank_card.h"
 
 #include <nfc/scenes/nfc_scene.h>
+#include <nfc/helpers/nfc_custom_event.h>
 
 #define NFC_SEND_NOTIFICATION_FALSE (0UL)
 #define NFC_SEND_NOTIFICATION_TRUE (1UL)

+ 40 - 55
applications/nfc/nfc_worker.c

@@ -57,7 +57,7 @@ void nfc_worker_stop(NfcWorker* nfc_worker) {
     if(nfc_worker->state == NfcWorkerStateBroken || nfc_worker->state == NfcWorkerStateReady) {
         return;
     }
-
+    furi_hal_nfc_stop();
     nfc_worker_change_state(nfc_worker, NfcWorkerStateStop);
 }
 
@@ -140,13 +140,37 @@ void nfc_worker_detect(NfcWorker* nfc_worker) {
     }
 }
 
+bool nfc_worker_emulate_uid_callback(
+    uint8_t* buff_rx,
+    uint16_t buff_rx_len,
+    uint8_t* buff_tx,
+    uint16_t* buff_tx_len,
+    uint32_t* data_type,
+    void* context) {
+    furi_assert(context);
+    NfcWorker* nfc_worker = context;
+    NfcReaderRequestData* reader_data = &nfc_worker->dev_data->reader_data;
+    reader_data->size = buff_rx_len / 8;
+    if(reader_data->size > 0) {
+        memcpy(reader_data->data, buff_rx, reader_data->size);
+        if(nfc_worker->callback) {
+            nfc_worker->callback(nfc_worker->context);
+        }
+    }
+    return true;
+}
+
 void nfc_worker_emulate(NfcWorker* nfc_worker) {
     NfcDeviceCommonData* data = &nfc_worker->dev_data->nfc_data;
     while(nfc_worker->state == NfcWorkerStateEmulate) {
-        if(furi_hal_nfc_listen(data->uid, data->uid_len, data->atqa, data->sak, false, 100)) {
-            FURI_LOG_D(TAG, "Reader detected");
-        }
-        osDelay(10);
+        furi_hal_nfc_emulate_nfca(
+            data->uid,
+            data->uid_len,
+            data->atqa,
+            data->sak,
+            nfc_worker_emulate_uid_callback,
+            nfc_worker,
+            1000);
     }
 }
 
@@ -603,65 +627,26 @@ void nfc_worker_read_mifare_ul(NfcWorker* nfc_worker) {
 }
 
 void nfc_worker_emulate_mifare_ul(NfcWorker* nfc_worker) {
-    ReturnCode err;
-    uint8_t tx_buff[255] = {};
-    uint16_t tx_len = 0;
-    uint8_t* rx_buff;
-    uint16_t* rx_len;
-    NfcDeviceData* data = nfc_worker->dev_data;
+    NfcDeviceCommonData* nfc_common = &nfc_worker->dev_data->nfc_data;
     MifareUlDevice mf_ul_emulate;
-    // Setup emulation parameters from mifare ultralight data structure
-    mf_ul_prepare_emulation(&mf_ul_emulate, &data->mf_ul_data);
+    mf_ul_prepare_emulation(&mf_ul_emulate, &nfc_worker->dev_data->mf_ul_data);
     while(nfc_worker->state == NfcWorkerStateEmulateMifareUl) {
-        // WARNING
-        // DO NOT call any blocking functions (e.g. FURI_LOG_*) in this loop,
-        // as any delay will negatively affect the stability of the emulation.
-        if(furi_hal_nfc_listen(
-               data->nfc_data.uid,
-               data->nfc_data.uid_len,
-               data->nfc_data.atqa,
-               data->nfc_data.sak,
-               true,
-               200)) {
-            if(furi_hal_nfc_get_first_frame(&rx_buff, &rx_len)) {
-                // Data exchange loop
-                while(nfc_worker->state == NfcWorkerStateEmulateMifareUl) {
-                    tx_len = mf_ul_prepare_emulation_response(
-                        rx_buff, *rx_len, tx_buff, &mf_ul_emulate);
-                    if(tx_len > 0) {
-                        if(tx_len < 8) {
-                            err = furi_hal_nfc_raw_bitstream_exchange(
-                                tx_buff, tx_len, &rx_buff, &rx_len, false);
-                            *rx_len /= 8;
-                        } else {
-                            err = furi_hal_nfc_data_exchange(
-                                tx_buff, tx_len / 8, &rx_buff, &rx_len, false);
-                        }
-                        if(err == ERR_NONE) {
-                            continue;
-                        } else {
-                            FURI_LOG_D(TAG, "Communication error: %d", err);
-                            break;
-                        }
-                    } else {
-                        furi_hal_nfc_deactivate();
-                        break;
-                    }
-                }
-            } else {
-                FURI_LOG_D(TAG, "Error in 1st data exchange");
-                furi_hal_nfc_deactivate();
-            }
-        }
+        furi_hal_nfc_emulate_nfca(
+            nfc_common->uid,
+            nfc_common->uid_len,
+            nfc_common->atqa,
+            nfc_common->sak,
+            mf_ul_prepare_emulation_response,
+            &mf_ul_emulate,
+            5000);
         // Check if data was modified
         if(mf_ul_emulate.data_changed) {
             nfc_worker->dev_data->mf_ul_data = mf_ul_emulate.data;
             if(nfc_worker->callback) {
                 nfc_worker->callback(nfc_worker->context);
             }
+            mf_ul_emulate.data_changed = false;
         }
-        FURI_LOG_D(TAG, "Can't find reader");
-        osThreadYield();
     }
 }
 

+ 1 - 1
applications/nfc/scenes/nfc_scene_delete.c

@@ -88,5 +88,5 @@ bool nfc_scene_delete_on_event(void* context, SceneManagerEvent event) {
 void nfc_scene_delete_on_exit(void* context) {
     Nfc* nfc = (Nfc*)context;
 
-    widget_clear(nfc->widget);
+    widget_reset(nfc->widget);
 }

+ 2 - 4
applications/nfc/scenes/nfc_scene_delete_success.c

@@ -1,10 +1,8 @@
 #include "../nfc_i.h"
 
-#define SCENE_SAVE_SUCCESS_CUSTOM_EVENT (0UL)
-
 void nfc_scene_delete_success_popup_callback(void* context) {
     Nfc* nfc = (Nfc*)context;
-    view_dispatcher_send_custom_event(nfc->view_dispatcher, SCENE_SAVE_SUCCESS_CUSTOM_EVENT);
+    view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit);
 }
 
 void nfc_scene_delete_success_on_enter(void* context) {
@@ -25,7 +23,7 @@ bool nfc_scene_delete_success_on_event(void* context, SceneManagerEvent event) {
     Nfc* nfc = (Nfc*)context;
 
     if(event.type == SceneManagerEventTypeCustom) {
-        if(event.event == SCENE_SAVE_SUCCESS_CUSTOM_EVENT) {
+        if(event.event == NfcCustomEventViewExit) {
             return scene_manager_search_and_switch_to_previous_scene(
                 nfc->scene_manager, NfcSceneStart);
         }

+ 13 - 13
applications/nfc/scenes/nfc_scene_device_info.c

@@ -1,8 +1,6 @@
 #include "../nfc_i.h"
 #include "../helpers/nfc_emv_parser.h"
 
-#define NFC_SCENE_DEVICE_INFO_BACK_EVENT (0UL)
-
 enum {
     NfcSceneDeviceInfoUid,
     NfcSceneDeviceInfoData,
@@ -17,18 +15,15 @@ void nfc_scene_device_info_widget_callback(GuiButtonType result, InputType type,
 
 void nfc_scene_device_info_dialog_callback(DialogExResult result, void* context) {
     Nfc* nfc = context;
-    view_dispatcher_send_custom_event(nfc->view_dispatcher, result);
-}
-
-void nfc_scene_device_info_text_box_callback(void* context) {
-    Nfc* nfc = context;
-    view_dispatcher_send_custom_event(nfc->view_dispatcher, NFC_SCENE_DEVICE_INFO_BACK_EVENT);
+    if(result == DialogExResultLeft) {
+        view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit);
+    }
 }
 
 void nfc_scene_device_info_bank_card_callback(GuiButtonType result, InputType type, void* context) {
     Nfc* nfc = context;
     if(type == InputTypeShort) {
-        view_dispatcher_send_custom_event(nfc->view_dispatcher, NFC_SCENE_DEVICE_INFO_BACK_EVENT);
+        view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit);
     }
 }
 
@@ -97,8 +92,6 @@ void nfc_scene_device_info_on_enter(void* context) {
     } else if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) {
         MifareUlData* mf_ul_data = &nfc->dev->dev_data.mf_ul_data;
         TextBox* text_box = nfc->text_box;
-        text_box_set_context(text_box, nfc);
-        text_box_set_exit_callback(text_box, nfc_scene_device_info_text_box_callback);
         text_box_set_font(text_box, TextBoxFontHex);
         for(uint16_t i = 0; i < mf_ul_data->data_size; i += 2) {
             if(!(i % 8) && i) {
@@ -170,7 +163,14 @@ bool nfc_scene_device_info_on_event(void* context, SceneManagerEvent event) {
                 view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewBankCard);
                 consumed = true;
             }
-        } else if(state == NfcSceneDeviceInfoData) {
+        } else if(state == NfcSceneDeviceInfoData && event.event == NfcCustomEventViewExit) {
+            scene_manager_set_scene_state(
+                nfc->scene_manager, NfcSceneDeviceInfo, NfcSceneDeviceInfoUid);
+            view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
+            consumed = true;
+        }
+    } else if(event.type == SceneManagerEventTypeBack) {
+        if(state == NfcSceneDeviceInfoData) {
             scene_manager_set_scene_state(
                 nfc->scene_manager, NfcSceneDeviceInfo, NfcSceneDeviceInfoUid);
             view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
@@ -184,7 +184,7 @@ void nfc_scene_device_info_on_exit(void* context) {
     Nfc* nfc = (Nfc*)context;
 
     // Clear Custom Widget
-    widget_clear(nfc->widget);
+    widget_reset(nfc->widget);
 
     if(nfc->dev->format == NfcDeviceSaveFormatUid) {
         // Clear Dialog

+ 112 - 34
applications/nfc/scenes/nfc_scene_emulate_uid.c

@@ -1,61 +1,139 @@
 #include "../nfc_i.h"
 #include <dolphin/dolphin.h>
 
-void nfc_scene_emulate_uid_on_enter(void* context) {
-    Nfc* nfc = (Nfc*)context;
-    DOLPHIN_DEED(DolphinDeedNfcEmulate);
+enum {
+    NfcSceneEmulateUidStateWidget,
+    NfcSceneEmulateUidStateTextBox,
+};
+
+void nfc_emulate_uid_worker_callback(void* context) {
+    furi_assert(context);
+    Nfc* nfc = context;
+    view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventWorkerExit);
+}
+
+void nfc_scene_emulate_uid_widget_callback(GuiButtonType result, InputType type, void* context) {
+    furi_assert(context);
+    Nfc* nfc = context;
+    if(type == InputTypeShort) {
+        view_dispatcher_send_custom_event(nfc->view_dispatcher, result);
+    }
+}
 
-    // Setup view
-    Popup* popup = nfc->popup;
+void nfc_emulate_uid_textbox_callback(void* context) {
+    furi_assert(context);
+    Nfc* nfc = context;
+    view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit);
+}
+
+// Add widget with device name or inform that data received
+static void nfc_scene_emulate_uid_widget_config(Nfc* nfc, bool data_received) {
     NfcDeviceCommonData* data = &nfc->dev->dev_data.nfc_data;
+    Widget* widget = nfc->widget;
+    widget_reset(widget);
+    string_t info_str;
+    string_init(info_str);
 
+    widget_add_icon_element(widget, 0, 3, &I_RFIDDolphinSend_97x61);
+    widget_add_string_element(widget, 56, 32, AlignLeft, AlignTop, FontPrimary, "Emulating UID");
     if(strcmp(nfc->dev->dev_name, "")) {
-        nfc_text_store_set(nfc, "%s", nfc->dev->dev_name);
-    } else if(data->uid_len == 4) {
-        nfc_text_store_set(
-            nfc, "%02X %02X %02X %02X", data->uid[0], data->uid[1], data->uid[2], data->uid[3]);
-    } else if(data->uid_len == 7) {
-        nfc_text_store_set(
-            nfc,
-            "%02X %02X %02X %02X\n%02X %02X %02X",
-            data->uid[0],
-            data->uid[1],
-            data->uid[2],
-            data->uid[3],
-            data->uid[4],
-            data->uid[5],
-            data->uid[6]);
+        string_printf(info_str, "%s", nfc->dev->dev_name);
+    } else {
+        for(uint8_t i = 0; i < data->uid_len; i++) {
+            string_cat_printf(info_str, "%02X ", data->uid[i]);
+        }
     }
+    string_strim(info_str);
+    widget_add_text_box_element(
+        widget, 56, 43, 70, 21, AlignLeft, AlignTop, string_get_cstr(info_str));
+    string_clear(info_str);
+    if(data_received) {
+        widget_add_button_element(
+            widget, GuiButtonTypeCenter, "Log", nfc_scene_emulate_uid_widget_callback, nfc);
+    }
+}
 
-    popup_set_icon(popup, 0, 3, &I_RFIDDolphinSend_97x61);
-    popup_set_header(popup, "Emulating UID", 56, 31, AlignLeft, AlignTop);
-    popup_set_text(popup, nfc->text_store, 56, 43, AlignLeft, AlignTop);
+void nfc_scene_emulate_uid_on_enter(void* context) {
+    Nfc* nfc = context;
+    DOLPHIN_DEED(DolphinDeedNfcEmulate);
 
-    // Setup and start worker
+    // Setup Widget
+    nfc_scene_emulate_uid_widget_config(nfc, false);
+    // Setup TextBox
+    TextBox* text_box = nfc->text_box;
+    text_box_set_font(text_box, TextBoxFontHex);
+    text_box_set_focus(text_box, TextBoxFocusEnd);
+    string_reset(nfc->text_box_store);
 
-    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup);
-    nfc_worker_start(nfc->worker, NfcWorkerStateEmulate, &nfc->dev->dev_data, NULL, nfc);
+    // Set Widget state and view
+    scene_manager_set_scene_state(
+        nfc->scene_manager, NfcSceneEmulateUid, NfcSceneEmulateUidStateWidget);
+    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
+    // Start worker
+    memset(&nfc->dev->dev_data.reader_data, 0, sizeof(NfcReaderRequestData));
+    nfc_worker_start(
+        nfc->worker,
+        NfcWorkerStateEmulate,
+        &nfc->dev->dev_data,
+        nfc_emulate_uid_worker_callback,
+        nfc);
 }
 
 bool nfc_scene_emulate_uid_on_event(void* context, SceneManagerEvent event) {
-    Nfc* nfc = (Nfc*)context;
+    Nfc* nfc = context;
+    NfcReaderRequestData* reader_data = &nfc->dev->dev_data.reader_data;
+    uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneEmulateUid);
+    bool consumed = false;
 
     if(event.type == SceneManagerEventTypeTick) {
         notification_message(nfc->notifications, &sequence_blink_blue_10);
-        return true;
+        consumed = true;
+    } else if(event.type == SceneManagerEventTypeCustom) {
+        if(event.event == NfcCustomEventWorkerExit) {
+            // Add data button to widget if data is received for the first time
+            if(!string_size(nfc->text_box_store)) {
+                nfc_scene_emulate_uid_widget_config(nfc, true);
+            }
+            // Update TextBox data
+            string_cat_printf(nfc->text_box_store, "R:");
+            for(uint16_t i = 0; i < reader_data->size; i++) {
+                string_cat_printf(nfc->text_box_store, " %02X", reader_data->data[i]);
+            }
+            string_push_back(nfc->text_box_store, '\n');
+            memset(reader_data, 0, sizeof(NfcReaderRequestData));
+            text_box_set_text(nfc->text_box, string_get_cstr(nfc->text_box_store));
+            consumed = true;
+        } else if(event.event == GuiButtonTypeCenter && state == NfcSceneEmulateUidStateWidget) {
+            view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox);
+            scene_manager_set_scene_state(
+                nfc->scene_manager, NfcSceneEmulateUid, NfcSceneEmulateUidStateTextBox);
+            consumed = true;
+        } else if(event.event == NfcCustomEventViewExit && state == NfcSceneEmulateUidStateTextBox) {
+            view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
+            scene_manager_set_scene_state(
+                nfc->scene_manager, NfcSceneEmulateUid, NfcSceneEmulateUidStateWidget);
+            consumed = true;
+        }
+    } else if(event.type == SceneManagerEventTypeBack) {
+        if(state == NfcSceneEmulateUidStateTextBox) {
+            view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
+            scene_manager_set_scene_state(
+                nfc->scene_manager, NfcSceneEmulateUid, NfcSceneEmulateUidStateWidget);
+            consumed = true;
+        }
     }
-    return false;
+
+    return consumed;
 }
 
 void nfc_scene_emulate_uid_on_exit(void* context) {
-    Nfc* nfc = (Nfc*)context;
+    Nfc* nfc = context;
 
     // Stop worker
     nfc_worker_stop(nfc->worker);
 
     // Clear view
-    Popup* popup = nfc->popup;
-    popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom);
-    popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop);
-    popup_set_icon(popup, 0, 0, NULL);
+    widget_reset(nfc->widget);
+    text_box_reset(nfc->text_box);
+    string_reset(nfc->text_box_store);
 }

+ 2 - 4
applications/nfc/scenes/nfc_scene_read_card.c

@@ -1,11 +1,9 @@
 #include "../nfc_i.h"
 #include <dolphin/dolphin.h>
 
-#define NFC_READ_CARD_CUSTOM_EVENT (10UL)
-
 void nfc_read_card_worker_callback(void* context) {
     Nfc* nfc = (Nfc*)context;
-    view_dispatcher_send_custom_event(nfc->view_dispatcher, NFC_READ_CARD_CUSTOM_EVENT);
+    view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventWorkerExit);
 }
 
 void nfc_scene_read_card_on_enter(void* context) {
@@ -27,7 +25,7 @@ bool nfc_scene_read_card_on_event(void* context, SceneManagerEvent event) {
     Nfc* nfc = (Nfc*)context;
 
     if(event.type == SceneManagerEventTypeCustom) {
-        if(event.event == NFC_READ_CARD_CUSTOM_EVENT) {
+        if(event.event == NfcCustomEventWorkerExit) {
             scene_manager_next_scene(nfc->scene_manager, NfcSceneReadCardSuccess);
             return true;
         }

+ 1 - 1
applications/nfc/scenes/nfc_scene_read_card_success.c

@@ -85,5 +85,5 @@ bool nfc_scene_read_card_success_on_event(void* context, SceneManagerEvent event
 
 void nfc_scene_read_card_success_on_exit(void* context) {
     Nfc* nfc = (Nfc*)context;
-    widget_clear(nfc->widget);
+    widget_reset(nfc->widget);
 }

+ 2 - 4
applications/nfc/scenes/nfc_scene_read_emv_app.c

@@ -1,11 +1,9 @@
 #include "../nfc_i.h"
 #include <dolphin/dolphin.h>
 
-#define NFC_READ_EMV_APP_CUSTOM_EVENT (10UL)
-
 void nfc_read_emv_app_worker_callback(void* context) {
     Nfc* nfc = (Nfc*)context;
-    view_dispatcher_send_custom_event(nfc->view_dispatcher, NFC_READ_EMV_APP_CUSTOM_EVENT);
+    view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventWorkerExit);
 }
 
 void nfc_scene_read_emv_app_on_enter(void* context) {
@@ -31,7 +29,7 @@ bool nfc_scene_read_emv_app_on_event(void* context, SceneManagerEvent event) {
     Nfc* nfc = (Nfc*)context;
 
     if(event.type == SceneManagerEventTypeCustom) {
-        if(event.event == NFC_READ_EMV_APP_CUSTOM_EVENT) {
+        if(event.event == NfcCustomEventWorkerExit) {
             scene_manager_set_scene_state(
                 nfc->scene_manager, NfcSceneReadEmvAppSuccess, NFC_SEND_NOTIFICATION_TRUE);
             scene_manager_next_scene(nfc->scene_manager, NfcSceneReadEmvAppSuccess);

+ 2 - 4
applications/nfc/scenes/nfc_scene_read_emv_data.c

@@ -1,11 +1,9 @@
 #include "../nfc_i.h"
 #include <dolphin/dolphin.h>
 
-#define NFC_READ_EMV_DATA_CUSTOM_EVENT (10UL)
-
 void nfc_read_emv_data_worker_callback(void* context) {
     Nfc* nfc = (Nfc*)context;
-    view_dispatcher_send_custom_event(nfc->view_dispatcher, NFC_READ_EMV_DATA_CUSTOM_EVENT);
+    view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventWorkerExit);
 }
 
 void nfc_scene_read_emv_data_on_enter(void* context) {
@@ -33,7 +31,7 @@ bool nfc_scene_read_emv_data_on_event(void* context, SceneManagerEvent event) {
     Nfc* nfc = (Nfc*)context;
 
     if(event.type == SceneManagerEventTypeCustom) {
-        if(event.event == NFC_READ_EMV_DATA_CUSTOM_EVENT) {
+        if(event.event == NfcCustomEventWorkerExit) {
             scene_manager_set_scene_state(
                 nfc->scene_manager, NfcSceneReadEmvDataSuccess, NFC_SEND_NOTIFICATION_TRUE);
             scene_manager_next_scene(nfc->scene_manager, NfcSceneReadEmvDataSuccess);

+ 1 - 1
applications/nfc/scenes/nfc_scene_read_emv_data_success.c

@@ -140,5 +140,5 @@ bool nfc_scene_read_emv_data_success_on_event(void* context, SceneManagerEvent e
 void nfc_scene_read_emv_data_success_on_exit(void* context) {
     Nfc* nfc = (Nfc*)context;
 
-    widget_clear(nfc->widget);
+    widget_reset(nfc->widget);
 }

+ 2 - 4
applications/nfc/scenes/nfc_scene_read_mifare_ul.c

@@ -1,11 +1,9 @@
 #include "../nfc_i.h"
 #include <dolphin/dolphin.h>
 
-#define NFC_READ_MIFARE_UL_CUSTOM_EVENT (10UL)
-
 void nfc_read_mifare_ul_worker_callback(void* context) {
     Nfc* nfc = (Nfc*)context;
-    view_dispatcher_send_custom_event(nfc->view_dispatcher, NFC_READ_MIFARE_UL_CUSTOM_EVENT);
+    view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventWorkerExit);
 }
 
 void nfc_scene_read_mifare_ul_on_enter(void* context) {
@@ -31,7 +29,7 @@ bool nfc_scene_read_mifare_ul_on_event(void* context, SceneManagerEvent event) {
     Nfc* nfc = (Nfc*)context;
 
     if(event.type == SceneManagerEventTypeCustom) {
-        if(event.event == NFC_READ_MIFARE_UL_CUSTOM_EVENT) {
+        if(event.event == NfcCustomEventWorkerExit) {
             scene_manager_next_scene(nfc->scene_manager, NfcSceneReadMifareUlSuccess);
             return true;
         }

+ 16 - 30
applications/nfc/scenes/nfc_scene_read_mifare_ul_success.c

@@ -2,7 +2,6 @@
 #include <dolphin/dolphin.h>
 
 #define NFC_SCENE_READ_SUCCESS_SHIFT "              "
-#define NFC_SCENE_READ_MF_UL_CUSTOM_EVENT (0UL)
 
 enum {
     ReadMifareUlStateShowUID,
@@ -15,12 +14,6 @@ void nfc_scene_read_mifare_ul_success_dialog_callback(DialogExResult result, voi
     view_dispatcher_send_custom_event(nfc->view_dispatcher, result);
 }
 
-void nfc_scene_read_mifare_ul_success_text_box_callback(void* context) {
-    Nfc* nfc = (Nfc*)context;
-
-    view_dispatcher_send_custom_event(nfc->view_dispatcher, NFC_SCENE_READ_MF_UL_CUSTOM_EVENT);
-}
-
 void nfc_scene_read_mifare_ul_success_on_enter(void* context) {
     Nfc* nfc = (Nfc*)context;
     DOLPHIN_DEED(DolphinDeedNfcReadSuccess);
@@ -59,8 +52,6 @@ void nfc_scene_read_mifare_ul_success_on_enter(void* context) {
 
     // Setup TextBox view
     TextBox* text_box = nfc->text_box;
-    text_box_set_context(text_box, nfc);
-    text_box_set_exit_callback(text_box, nfc_scene_read_mifare_ul_success_text_box_callback);
     text_box_set_font(text_box, TextBoxFontHex);
     for(uint16_t i = 0; i < mf_ul_data->data_size; i += 2) {
         if(!(i % 8) && i) {
@@ -77,39 +68,34 @@ void nfc_scene_read_mifare_ul_success_on_enter(void* context) {
 }
 
 bool nfc_scene_read_mifare_ul_success_on_event(void* context, SceneManagerEvent event) {
-    Nfc* nfc = (Nfc*)context;
+    Nfc* nfc = context;
+    uint32_t state =
+        scene_manager_get_scene_state(nfc->scene_manager, NfcSceneReadMifareUlSuccess);
+    bool consumed = false;
 
     if(event.type == SceneManagerEventTypeCustom) {
-        if((scene_manager_get_scene_state(nfc->scene_manager, NfcSceneReadMifareUlSuccess) ==
-            ReadMifareUlStateShowUID) &&
-           (event.event == DialogExResultLeft)) {
+        if(state == ReadMifareUlStateShowUID && event.event == DialogExResultLeft) {
             scene_manager_previous_scene(nfc->scene_manager);
-            return true;
-        } else if(
-            (scene_manager_get_scene_state(nfc->scene_manager, NfcSceneReadMifareUlSuccess) ==
-             ReadMifareUlStateShowUID) &&
-            (event.event == DialogExResultRight)) {
+            consumed = true;
+        } else if(state == ReadMifareUlStateShowUID && event.event == DialogExResultRight) {
             scene_manager_next_scene(nfc->scene_manager, NfcSceneMifareUlMenu);
-            return true;
-        } else if(
-            (scene_manager_get_scene_state(nfc->scene_manager, NfcSceneReadMifareUlSuccess) ==
-             ReadMifareUlStateShowUID) &&
-            (event.event == DialogExResultCenter)) {
+            consumed = true;
+        } else if(state == ReadMifareUlStateShowUID && event.event == DialogExResultCenter) {
             view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox);
             scene_manager_set_scene_state(
                 nfc->scene_manager, NfcSceneReadMifareUlSuccess, ReadMifareUlStateShowData);
-            return true;
-        } else if(
-            (scene_manager_get_scene_state(nfc->scene_manager, NfcSceneReadMifareUlSuccess) ==
-             ReadMifareUlStateShowData) &&
-            (event.event == NFC_SCENE_READ_MF_UL_CUSTOM_EVENT)) {
+            consumed = true;
+        }
+    } else if(event.type == SceneManagerEventTypeBack) {
+        if(state == ReadMifareUlStateShowData) {
             view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx);
             scene_manager_set_scene_state(
                 nfc->scene_manager, NfcSceneReadMifareUlSuccess, ReadMifareUlStateShowUID);
-            return true;
+            consumed = true;
         }
     }
-    return false;
+
+    return consumed;
 }
 
 void nfc_scene_read_mifare_ul_success_on_exit(void* context) {

+ 2 - 4
applications/nfc/scenes/nfc_scene_restore_original.c

@@ -1,10 +1,8 @@
 #include "../nfc_i.h"
 
-#define SCENE_RESTORE_ORIGINAL_CUSTOM_EVENT (0UL)
-
 void nfc_scene_restore_original_popup_callback(void* context) {
     Nfc* nfc = (Nfc*)context;
-    view_dispatcher_send_custom_event(nfc->view_dispatcher, SCENE_RESTORE_ORIGINAL_CUSTOM_EVENT);
+    view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit);
 }
 
 void nfc_scene_restore_original_on_enter(void* context) {
@@ -26,7 +24,7 @@ bool nfc_scene_restore_original_on_event(void* context, SceneManagerEvent event)
     bool consumed = false;
 
     if(event.type == SceneManagerEventTypeCustom) {
-        if(event.event == SCENE_RESTORE_ORIGINAL_CUSTOM_EVENT) {
+        if(event.event == NfcCustomEventViewExit) {
             consumed = scene_manager_previous_scene(nfc->scene_manager);
         }
     }

+ 2 - 4
applications/nfc/scenes/nfc_scene_save_name.c

@@ -2,12 +2,10 @@
 #include <lib/toolbox/random_name.h>
 #include <gui/modules/validators.h>
 
-#define SCENE_SAVE_NAME_CUSTOM_EVENT (0UL)
-
 void nfc_scene_save_name_text_input_callback(void* context) {
     Nfc* nfc = (Nfc*)context;
 
-    view_dispatcher_send_custom_event(nfc->view_dispatcher, SCENE_SAVE_NAME_CUSTOM_EVENT);
+    view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventTextInputDone);
 }
 
 void nfc_scene_save_name_on_enter(void* context) {
@@ -42,7 +40,7 @@ bool nfc_scene_save_name_on_event(void* context, SceneManagerEvent event) {
     Nfc* nfc = (Nfc*)context;
 
     if(event.type == SceneManagerEventTypeCustom) {
-        if(event.event == SCENE_SAVE_NAME_CUSTOM_EVENT) {
+        if(event.event == NfcCustomEventTextInputDone) {
             if(strcmp(nfc->dev->dev_name, "")) {
                 nfc_device_delete(nfc->dev);
             }

+ 2 - 4
applications/nfc/scenes/nfc_scene_save_success.c

@@ -1,11 +1,9 @@
 #include "../nfc_i.h"
 #include <dolphin/dolphin.h>
 
-#define SCENE_SAVE_SUCCESS_CUSTOM_EVENT (0UL)
-
 void nfc_scene_save_success_popup_callback(void* context) {
     Nfc* nfc = (Nfc*)context;
-    view_dispatcher_send_custom_event(nfc->view_dispatcher, SCENE_SAVE_SUCCESS_CUSTOM_EVENT);
+    view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit);
 }
 
 void nfc_scene_save_success_on_enter(void* context) {
@@ -28,7 +26,7 @@ bool nfc_scene_save_success_on_event(void* context, SceneManagerEvent event) {
     bool consumed = false;
 
     if(event.type == SceneManagerEventTypeCustom) {
-        if(event.event == SCENE_SAVE_SUCCESS_CUSTOM_EVENT) {
+        if(event.event == NfcCustomEventViewExit) {
             if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneCardMenu)) {
                 consumed = scene_manager_search_and_switch_to_previous_scene(
                     nfc->scene_manager, NfcSceneCardMenu);

+ 2 - 4
applications/nfc/scenes/nfc_scene_set_atqa.c

@@ -1,11 +1,9 @@
 #include "../nfc_i.h"
 
-#define SCENE_SET_ATQA_CUSTOM_EVENT (0UL)
-
 void nfc_scene_set_atqa_byte_input_callback(void* context) {
     Nfc* nfc = (Nfc*)context;
 
-    view_dispatcher_send_custom_event(nfc->view_dispatcher, SCENE_SET_ATQA_CUSTOM_EVENT);
+    view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventByteInputDone);
 }
 
 void nfc_scene_set_atqa_on_enter(void* context) {
@@ -28,7 +26,7 @@ bool nfc_scene_set_atqa_on_event(void* context, SceneManagerEvent event) {
     Nfc* nfc = (Nfc*)context;
 
     if(event.type == SceneManagerEventTypeCustom) {
-        if(event.event == SCENE_SET_ATQA_CUSTOM_EVENT) {
+        if(event.event == NfcCustomEventByteInputDone) {
             scene_manager_next_scene(nfc->scene_manager, NfcSceneSetUid);
             return true;
         }

+ 2 - 4
applications/nfc/scenes/nfc_scene_set_sak.c

@@ -1,11 +1,9 @@
 #include "../nfc_i.h"
 
-#define SCENE_SET_SAK_CUSTOM_EVENT (0UL)
-
 void nfc_scene_set_sak_byte_input_callback(void* context) {
     Nfc* nfc = (Nfc*)context;
 
-    view_dispatcher_send_custom_event(nfc->view_dispatcher, SCENE_SET_SAK_CUSTOM_EVENT);
+    view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventByteInputDone);
 }
 
 void nfc_scene_set_sak_on_enter(void* context) {
@@ -28,7 +26,7 @@ bool nfc_scene_set_sak_on_event(void* context, SceneManagerEvent event) {
     Nfc* nfc = (Nfc*)context;
 
     if(event.type == SceneManagerEventTypeCustom) {
-        if(event.event == SCENE_SET_SAK_CUSTOM_EVENT) {
+        if(event.event == NfcCustomEventByteInputDone) {
             scene_manager_next_scene(nfc->scene_manager, NfcSceneSetAtqua);
             return true;
         }

+ 2 - 4
applications/nfc/scenes/nfc_scene_set_uid.c

@@ -1,12 +1,10 @@
 #include "../nfc_i.h"
 #include <dolphin/dolphin.h>
 
-#define SCENE_SET_UID_CUSTOM_EVENT (0UL)
-
 void nfc_scene_set_uid_byte_input_callback(void* context) {
     Nfc* nfc = (Nfc*)context;
 
-    view_dispatcher_send_custom_event(nfc->view_dispatcher, SCENE_SET_UID_CUSTOM_EVENT);
+    view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventByteInputDone);
 }
 
 void nfc_scene_set_uid_on_enter(void* context) {
@@ -30,7 +28,7 @@ bool nfc_scene_set_uid_on_event(void* context, SceneManagerEvent event) {
     Nfc* nfc = (Nfc*)context;
 
     if(event.type == SceneManagerEventTypeCustom) {
-        if(event.event == SCENE_SET_UID_CUSTOM_EVENT) {
+        if(event.event == NfcCustomEventByteInputDone) {
             DOLPHIN_DEED(DolphinDeedNfcAdd);
             scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName);
             return true;

+ 1 - 1
applications/nfc/views/bank_card.c

@@ -25,7 +25,7 @@ View* bank_card_get_view(BankCard* bank_card) {
 
 void bank_card_clear(BankCard* bank_card) {
     furi_assert(bank_card);
-    widget_clear(bank_card->widget);
+    widget_reset(bank_card->widget);
 }
 
 void bank_card_set_name(BankCard* bank_card, char* name) {

+ 1 - 1
applications/subghz/scenes/subghz_scene_delete.c

@@ -65,5 +65,5 @@ bool subghz_scene_delete_on_event(void* context, SceneManagerEvent event) {
 
 void subghz_scene_delete_on_exit(void* context) {
     SubGhz* subghz = context;
-    widget_clear(subghz->widget);
+    widget_reset(subghz->widget);
 }

+ 1 - 1
applications/subghz/scenes/subghz_scene_delete_raw.c

@@ -73,5 +73,5 @@ bool subghz_scene_delete_raw_on_event(void* context, SceneManagerEvent event) {
 
 void subghz_scene_delete_raw_on_exit(void* context) {
     SubGhz* subghz = context;
-    widget_clear(subghz->widget);
+    widget_reset(subghz->widget);
 }

+ 1 - 1
applications/subghz/scenes/subghz_scene_need_saving.c

@@ -63,5 +63,5 @@ bool subghz_scene_need_saving_on_event(void* context, SceneManagerEvent event) {
 
 void subghz_scene_need_saving_on_exit(void* context) {
     SubGhz* subghz = context;
-    widget_clear(subghz->widget);
+    widget_reset(subghz->widget);
 }

+ 1 - 1
applications/subghz/scenes/subghz_scene_receiver_info.c

@@ -180,5 +180,5 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event)
 
 void subghz_scene_receiver_info_on_exit(void* context) {
     SubGhz* subghz = context;
-    widget_clear(subghz->widget);
+    widget_reset(subghz->widget);
 }

+ 1 - 1
applications/subghz/scenes/subghz_scene_show_error.c

@@ -79,6 +79,6 @@ void subghz_scene_show_error_on_exit(void* context) {
     SubGhz* subghz = context;
     scene_manager_set_scene_state(
         subghz->scene_manager, SubGhzSceneShowError, SubghzCustomEventManagerNoSet);
-    widget_clear(subghz->widget);
+    widget_reset(subghz->widget);
     string_reset(subghz->error_str);
 }

+ 1 - 1
applications/u2f/scenes/u2f_scene_error.c

@@ -45,5 +45,5 @@ bool u2f_scene_error_on_event(void* context, SceneManagerEvent event) {
 
 void u2f_scene_error_on_exit(void* context) {
     U2fApp* app = context;
-    widget_clear(app->widget);
+    widget_reset(app->widget);
 }

+ 6 - 5
debug/PyCortexMDebug/cmdebug/svd.py

@@ -16,14 +16,15 @@ You should have received a copy of the GNU General Public License
 along with PyCortexMDebug.  If not, see <http://www.gnu.org/licenses/>.
 """
 
-import sys
 from collections import OrderedDict
-import os
-import pickle
+from . import x2d
+
 import traceback
-import re
 import warnings
-import x2d
+import pickle
+import sys
+import os
+import re
 
 
 class SmartDict:

+ 131 - 0
firmware/targets/f6/furi_hal/furi_hal_nfc.c

@@ -1,14 +1,25 @@
 #include "furi_hal_nfc.h"
 #include <st25r3916.h>
+#include <rfal_rf.h>
+#include <furi.h>
+#include <m-string.h>
+#include <lib/nfc_protocols/nfca.h>
 
 #define TAG "FuriHalNfc"
 
 static const uint32_t clocks_in_ms = 64 * 1000;
 
+osEventFlagsId_t event = NULL;
+#define EVENT_FLAG_INTERRUPT (1UL << 0)
+#define EVENT_FLAG_STATE_CHANGED (1UL << 1)
+#define EVENT_FLAG_STOP (1UL << 2)
+#define EVENT_FLAG_ALL (EVENT_FLAG_INTERRUPT | EVENT_FLAG_STATE_CHANGED | EVENT_FLAG_STOP)
+
 void furi_hal_nfc_init() {
     ReturnCode ret = rfalNfcInitialize();
     if(ret == ERR_NONE) {
         furi_hal_nfc_start_sleep();
+        event = osEventFlagsNew(NULL);
         FURI_LOG_I(TAG, "Init OK");
     } else {
         FURI_LOG_W(TAG, "Initialization failed, RFAL returned: %d", ret);
@@ -140,6 +151,126 @@ bool furi_hal_nfc_listen(
     return true;
 }
 
+void rfal_interrupt_callback_handler() {
+    osEventFlagsSet(event, EVENT_FLAG_INTERRUPT);
+}
+
+void rfal_state_changed_callback(void* context) {
+    osEventFlagsSet(event, EVENT_FLAG_STATE_CHANGED);
+}
+
+void furi_hal_nfc_stop() {
+    if(event) {
+        osEventFlagsSet(event, EVENT_FLAG_STOP);
+    }
+}
+
+bool furi_hal_nfc_emulate_nfca(
+    uint8_t* uid,
+    uint8_t uid_len,
+    uint8_t* atqa,
+    uint8_t sak,
+    FuriHalNfcEmulateCallback callback,
+    void* context,
+    uint32_t timeout) {
+    rfalSetUpperLayerCallback(rfal_interrupt_callback_handler);
+    rfal_set_state_changed_callback(rfal_state_changed_callback);
+
+    rfalLmConfPA config;
+    config.nfcidLen = uid_len;
+    memcpy(config.nfcid, uid, uid_len);
+    memcpy(config.SENS_RES, atqa, RFAL_LM_SENS_RES_LEN);
+    config.SEL_RES = sak;
+    uint8_t buff_rx[256];
+    uint16_t buff_rx_size = 256;
+    uint16_t buff_rx_len = 0;
+    uint8_t buff_tx[256];
+    uint16_t buff_tx_len = 0;
+    uint32_t data_type = FURI_HAL_NFC_TXRX_DEFAULT;
+
+    rfalLowPowerModeStop();
+    if(rfalListenStart(
+           RFAL_LM_MASK_NFCA,
+           &config,
+           NULL,
+           NULL,
+           buff_rx,
+           rfalConvBytesToBits(buff_rx_size),
+           &buff_rx_len)) {
+        rfalListenStop();
+        FURI_LOG_E(TAG, "Failed to start listen mode");
+        return false;
+    }
+    while(true) {
+        buff_rx_len = 0;
+        buff_tx_len = 0;
+        uint32_t flag = osEventFlagsWait(event, EVENT_FLAG_ALL, osFlagsWaitAny, timeout);
+        if(flag == osErrorTimeout || flag == EVENT_FLAG_STOP) {
+            break;
+        }
+        bool data_received = false;
+        buff_rx_len = 0;
+        rfalWorker();
+        rfalLmState state = rfalListenGetState(&data_received, NULL);
+        if(data_received) {
+            rfalTransceiveBlockingRx();
+            if(nfca_emulation_handler(buff_rx, buff_rx_len, buff_tx, &buff_tx_len)) {
+                if(rfalListenSleepStart(
+                       RFAL_LM_STATE_SLEEP_A,
+                       buff_rx,
+                       rfalConvBytesToBits(buff_rx_size),
+                       &buff_rx_len)) {
+                    FURI_LOG_E(TAG, "Failed to enter sleep mode");
+                    break;
+                } else {
+                    continue;
+                }
+            }
+            if(buff_tx_len) {
+                ReturnCode ret = rfalTransceiveBitsBlockingTx(
+                    buff_tx,
+                    buff_tx_len,
+                    buff_rx,
+                    sizeof(buff_rx),
+                    &buff_rx_len,
+                    data_type,
+                    RFAL_FWT_NONE);
+                if(ret) {
+                    FURI_LOG_E(TAG, "Tranceive failed with status %d", ret);
+                    break;
+                }
+                continue;
+            }
+            if((state == RFAL_LM_STATE_ACTIVE_A || state == RFAL_LM_STATE_ACTIVE_Ax)) {
+                if(callback) {
+                    callback(buff_rx, buff_rx_len, buff_tx, &buff_tx_len, &data_type, context);
+                }
+                if(!rfalIsExtFieldOn()) {
+                    break;
+                }
+                if(buff_tx_len) {
+                    ReturnCode ret = rfalTransceiveBitsBlockingTx(
+                        buff_tx,
+                        buff_tx_len,
+                        buff_rx,
+                        sizeof(buff_rx),
+                        &buff_rx_len,
+                        data_type,
+                        RFAL_FWT_NONE);
+                    if(ret) {
+                        FURI_LOG_E(TAG, "Tranceive failed with status %d", ret);
+                        continue;
+                    }
+                } else {
+                    break;
+                }
+            }
+        }
+    }
+    rfalListenStop();
+    return true;
+}
+
 bool furi_hal_nfc_get_first_frame(uint8_t** rx_buff, uint16_t** rx_len) {
     ReturnCode ret =
         rfalNfcDataExchangeStart(NULL, 0, rx_buff, rx_len, 0, RFAL_TXRX_FLAGS_DEFAULT);

+ 131 - 0
firmware/targets/f7/furi_hal/furi_hal_nfc.c

@@ -1,14 +1,25 @@
 #include "furi_hal_nfc.h"
 #include <st25r3916.h>
+#include <rfal_rf.h>
+#include <furi.h>
+#include <m-string.h>
+#include <lib/nfc_protocols/nfca.h>
 
 #define TAG "FuriHalNfc"
 
 static const uint32_t clocks_in_ms = 64 * 1000;
 
+osEventFlagsId_t event = NULL;
+#define EVENT_FLAG_INTERRUPT (1UL << 0)
+#define EVENT_FLAG_STATE_CHANGED (1UL << 1)
+#define EVENT_FLAG_STOP (1UL << 2)
+#define EVENT_FLAG_ALL (EVENT_FLAG_INTERRUPT | EVENT_FLAG_STATE_CHANGED | EVENT_FLAG_STOP)
+
 void furi_hal_nfc_init() {
     ReturnCode ret = rfalNfcInitialize();
     if(ret == ERR_NONE) {
         furi_hal_nfc_start_sleep();
+        event = osEventFlagsNew(NULL);
         FURI_LOG_I(TAG, "Init OK");
     } else {
         FURI_LOG_W(TAG, "Initialization failed, RFAL returned: %d", ret);
@@ -140,6 +151,126 @@ bool furi_hal_nfc_listen(
     return true;
 }
 
+void rfal_interrupt_callback_handler() {
+    osEventFlagsSet(event, EVENT_FLAG_INTERRUPT);
+}
+
+void rfal_state_changed_callback(void* context) {
+    osEventFlagsSet(event, EVENT_FLAG_STATE_CHANGED);
+}
+
+void furi_hal_nfc_stop() {
+    if(event) {
+        osEventFlagsSet(event, EVENT_FLAG_STOP);
+    }
+}
+
+bool furi_hal_nfc_emulate_nfca(
+    uint8_t* uid,
+    uint8_t uid_len,
+    uint8_t* atqa,
+    uint8_t sak,
+    FuriHalNfcEmulateCallback callback,
+    void* context,
+    uint32_t timeout) {
+    rfalSetUpperLayerCallback(rfal_interrupt_callback_handler);
+    rfal_set_state_changed_callback(rfal_state_changed_callback);
+
+    rfalLmConfPA config;
+    config.nfcidLen = uid_len;
+    memcpy(config.nfcid, uid, uid_len);
+    memcpy(config.SENS_RES, atqa, RFAL_LM_SENS_RES_LEN);
+    config.SEL_RES = sak;
+    uint8_t buff_rx[256];
+    uint16_t buff_rx_size = 256;
+    uint16_t buff_rx_len = 0;
+    uint8_t buff_tx[256];
+    uint16_t buff_tx_len = 0;
+    uint32_t data_type = FURI_HAL_NFC_TXRX_DEFAULT;
+
+    rfalLowPowerModeStop();
+    if(rfalListenStart(
+           RFAL_LM_MASK_NFCA,
+           &config,
+           NULL,
+           NULL,
+           buff_rx,
+           rfalConvBytesToBits(buff_rx_size),
+           &buff_rx_len)) {
+        rfalListenStop();
+        FURI_LOG_E(TAG, "Failed to start listen mode");
+        return false;
+    }
+    while(true) {
+        buff_rx_len = 0;
+        buff_tx_len = 0;
+        uint32_t flag = osEventFlagsWait(event, EVENT_FLAG_ALL, osFlagsWaitAny, timeout);
+        if(flag == osErrorTimeout || flag == EVENT_FLAG_STOP) {
+            break;
+        }
+        bool data_received = false;
+        buff_rx_len = 0;
+        rfalWorker();
+        rfalLmState state = rfalListenGetState(&data_received, NULL);
+        if(data_received) {
+            rfalTransceiveBlockingRx();
+            if(nfca_emulation_handler(buff_rx, buff_rx_len, buff_tx, &buff_tx_len)) {
+                if(rfalListenSleepStart(
+                       RFAL_LM_STATE_SLEEP_A,
+                       buff_rx,
+                       rfalConvBytesToBits(buff_rx_size),
+                       &buff_rx_len)) {
+                    FURI_LOG_E(TAG, "Failed to enter sleep mode");
+                    break;
+                } else {
+                    continue;
+                }
+            }
+            if(buff_tx_len) {
+                ReturnCode ret = rfalTransceiveBitsBlockingTx(
+                    buff_tx,
+                    buff_tx_len,
+                    buff_rx,
+                    sizeof(buff_rx),
+                    &buff_rx_len,
+                    data_type,
+                    RFAL_FWT_NONE);
+                if(ret) {
+                    FURI_LOG_E(TAG, "Tranceive failed with status %d", ret);
+                    break;
+                }
+                continue;
+            }
+            if((state == RFAL_LM_STATE_ACTIVE_A || state == RFAL_LM_STATE_ACTIVE_Ax)) {
+                if(callback) {
+                    callback(buff_rx, buff_rx_len, buff_tx, &buff_tx_len, &data_type, context);
+                }
+                if(!rfalIsExtFieldOn()) {
+                    break;
+                }
+                if(buff_tx_len) {
+                    ReturnCode ret = rfalTransceiveBitsBlockingTx(
+                        buff_tx,
+                        buff_tx_len,
+                        buff_rx,
+                        sizeof(buff_rx),
+                        &buff_rx_len,
+                        data_type,
+                        RFAL_FWT_NONE);
+                    if(ret) {
+                        FURI_LOG_E(TAG, "Tranceive failed with status %d", ret);
+                        continue;
+                    }
+                } else {
+                    break;
+                }
+            }
+        }
+    }
+    rfalListenStop();
+    return true;
+}
+
 bool furi_hal_nfc_get_first_frame(uint8_t** rx_buff, uint16_t** rx_len) {
     ReturnCode ret =
         rfalNfcDataExchangeStart(NULL, 0, rx_buff, rx_len, 0, RFAL_TXRX_FLAGS_DEFAULT);

+ 31 - 0
firmware/targets/furi_hal_include/furi_hal_nfc.h

@@ -16,6 +16,26 @@ extern "C" {
 
 #define FURI_HAL_NFC_UID_MAX_LEN 10
 
+#define FURI_HAL_NFC_TXRX_DEFAULT                                                    \
+    ((uint32_t)RFAL_TXRX_FLAGS_CRC_TX_AUTO | (uint32_t)RFAL_TXRX_FLAGS_CRC_RX_REMV | \
+     (uint32_t)RFAL_TXRX_FLAGS_NFCIP1_OFF | (uint32_t)RFAL_TXRX_FLAGS_AGC_ON |       \
+     (uint32_t)RFAL_TXRX_FLAGS_PAR_RX_REMV | (uint32_t)RFAL_TXRX_FLAGS_PAR_TX_AUTO | \
+     (uint32_t)RFAL_TXRX_FLAGS_NFCV_FLAG_AUTO)
+
+#define FURI_HAL_NFC_TXRX_RAW                                                          \
+    ((uint32_t)RFAL_TXRX_FLAGS_CRC_TX_MANUAL | (uint32_t)RFAL_TXRX_FLAGS_CRC_RX_REMV | \
+     (uint32_t)RFAL_TXRX_FLAGS_NFCIP1_OFF | (uint32_t)RFAL_TXRX_FLAGS_AGC_ON |         \
+     (uint32_t)RFAL_TXRX_FLAGS_PAR_RX_REMV | (uint32_t)RFAL_TXRX_FLAGS_PAR_TX_NONE |   \
+     (uint32_t)RFAL_TXRX_FLAGS_NFCV_FLAG_AUTO)
+
+typedef bool (*FuriHalNfcEmulateCallback)(
+    uint8_t* buff_rx,
+    uint16_t buff_rx_len,
+    uint8_t* buff_tx,
+    uint16_t* buff_tx_len,
+    uint32_t* flags,
+    void* context);
+
 /** Init nfc
  */
 void furi_hal_nfc_init();
@@ -76,6 +96,15 @@ bool furi_hal_nfc_listen(
     bool activate_after_sak,
     uint32_t timeout);
 
+bool furi_hal_nfc_emulate_nfca(
+    uint8_t* uid,
+    uint8_t uid_len,
+    uint8_t* atqa,
+    uint8_t sak,
+    FuriHalNfcEmulateCallback callback,
+    void* context,
+    uint32_t timeout);
+
 /** Get first command from reader after activation in emulation mode
  *
  * @param      rx_buff  pointer to receive buffer
@@ -113,6 +142,8 @@ ReturnCode furi_hal_nfc_raw_bitstream_exchange(
  */
 void furi_hal_nfc_deactivate();
 
+void furi_hal_nfc_stop();
+
 #ifdef __cplusplus
 }
 #endif

+ 27 - 2
lib/ST25RFAL002/include/rfal_rf.h

@@ -545,10 +545,13 @@ typedef struct {
 typedef void (*rfalUpperLayerCallback)(void);
 
 /*! Callback to be executed before a Transceive                              */
-typedef void (*rfalPreTxRxCallback)(void);
+typedef void (*rfalPreTxRxCallback)(void* context);
 
 /*! Callback to be executed after a Transceive                               */
-typedef void (*rfalPostTxRxCallback)(void);
+typedef void (*rfalPostTxRxCallback)(void* context);
+
+/** Callback to be executed on each RFAL state change */
+typedef void (*RfalStateChangedCallback)(void* context);
 
 /*******************************************************************************/
 /*  ISO14443A                                                                  */
@@ -819,6 +822,19 @@ void rfalSetPreTxRxCallback(rfalPreTxRxCallback pFunc);
  */
 void rfalSetPostTxRxCallback(rfalPostTxRxCallback pFunc);
 
+/** Set RFAL state changed callback
+ *
+ * @param cb    RfalStateChangedCallback instance
+ * @param ctx   pointer to context
+ */
+void rfal_set_state_changed_callback(RfalStateChangedCallback callback);
+
+/** Set callback context
+ *
+ * @param ctx pointer to context
+ */
+void rfal_set_callback_context(void* context);
+
 /*! 
  *****************************************************************************
  * \brief  RFAL Deinitialize
@@ -1480,6 +1496,15 @@ ReturnCode rfalTransceiveBlockingTxRx(
     uint32_t flags,
     uint32_t fwt);
 
+ReturnCode rfalTransceiveBitsBlockingTx(
+    uint8_t* txBuf,
+    uint16_t txBufLen,
+    uint8_t* rxBuf,
+    uint16_t rxBufLen,
+    uint16_t* actLen,
+    uint32_t flags,
+    uint32_t fwt);
+
 /*****************************************************************************
  *  Listen Mode                                                              *  
  *****************************************************************************/

+ 43 - 2
lib/ST25RFAL002/source/st25r3916/rfal_rfst25r3916.c

@@ -130,6 +130,8 @@ typedef struct {
 typedef struct {
     rfalPreTxRxCallback preTxRx; /*!< RFAL's Pre TxRx callback  */
     rfalPostTxRxCallback postTxRx; /*!< RFAL's Post TxRx callback */
+    RfalStateChangedCallback state_changed_cb;
+    void* ctx;
 } rfalCallbacks;
 
 /*! Struct that holds counters to control the FIFO on Tx and Rx                                                                          */
@@ -595,6 +597,8 @@ ReturnCode rfalInitialize(void) {
 
     gRFAL.callbacks.preTxRx = NULL;
     gRFAL.callbacks.postTxRx = NULL;
+    gRFAL.callbacks.state_changed_cb = NULL;
+    gRFAL.callbacks.ctx = NULL;
 
 #if RFAL_FEATURE_NFCV
     /* Initialize NFC-V Data */
@@ -669,6 +673,14 @@ void rfalSetPostTxRxCallback(rfalPostTxRxCallback pFunc) {
     gRFAL.callbacks.postTxRx = pFunc;
 }
 
+void rfal_set_state_changed_callback(RfalStateChangedCallback callback) {
+    gRFAL.callbacks.state_changed_cb = callback;
+}
+
+void rfal_set_callback_context(void* context) {
+    gRFAL.callbacks.ctx = context;
+}
+
 /*******************************************************************************/
 ReturnCode rfalDeinitialize(void) {
     /* Deinitialize chip */
@@ -1520,6 +1532,30 @@ ReturnCode rfalTransceiveBlockingTx(
     return rfalTransceiveRunBlockingTx();
 }
 
+ReturnCode rfalTransceiveBitsBlockingTx(
+    uint8_t* txBuf,
+    uint16_t txBufLen,
+    uint8_t* rxBuf,
+    uint16_t rxBufLen,
+    uint16_t* actLen,
+    uint32_t flags,
+    uint32_t fwt) {
+    ReturnCode ret;
+    rfalTransceiveContext ctx = {
+        .rxBuf = rxBuf,
+        .rxBufLen = rxBufLen,
+        .rxRcvdLen = actLen,
+        .txBuf = txBuf,
+        .txBufLen = txBufLen,
+        .flags = flags,
+        .fwt = fwt,
+    };
+
+    EXIT_ON_ERR(ret, rfalStartTransceive(&ctx));
+
+    return rfalTransceiveRunBlockingTx();
+}
+
 /*******************************************************************************/
 static ReturnCode rfalTransceiveRunBlockingTx(void) {
     ReturnCode ret;
@@ -1797,7 +1833,7 @@ static void rfalCleanupTransceive(void) {
     /* Execute Post Transceive Callback                                            */
     /*******************************************************************************/
     if(gRFAL.callbacks.postTxRx != NULL) {
-        gRFAL.callbacks.postTxRx();
+        gRFAL.callbacks.postTxRx(gRFAL.callbacks.ctx);
     }
     /*******************************************************************************/
 }
@@ -1838,7 +1874,7 @@ static void rfalPrepareTransceive(void) {
     /* Execute Pre Transceive Callback                                             */
     /*******************************************************************************/
     if(gRFAL.callbacks.preTxRx != NULL) {
-        gRFAL.callbacks.preTxRx();
+        gRFAL.callbacks.preTxRx(gRFAL.callbacks.ctx);
     }
     /*******************************************************************************/
 
@@ -4164,6 +4200,11 @@ ReturnCode rfalListenSetState(rfalLmState newSt) {
 
     gRFAL.Lm.state = newState;
 
+    // Call callback on state change
+    if(gRFAL.callbacks.state_changed_cb) {
+        gRFAL.callbacks.state_changed_cb(gRFAL.callbacks.ctx);
+    }
+
     return ret;
 }
 

+ 26 - 5
lib/nfc_protocols/mifare_ultralight.c

@@ -1,4 +1,6 @@
 #include "mifare_ultralight.h"
+#include <furi.h>
+#include <furi_hal_nfc.h>
 
 bool mf_ul_check_card_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK) {
     if((ATQA0 == 0x44) && (ATQA1 == 0x00) && (SAK == 0x00)) {
@@ -154,6 +156,7 @@ void mf_ul_prepare_emulation(MifareUlDevice* mf_ul_emulate, MifareUlData* data)
     mf_ul_emulate->data = *data;
     mf_ul_emulate->auth_data = NULL;
     mf_ul_emulate->data_changed = false;
+    mf_ul_emulate->comp_write_cmd_started = false;
     if(data->version.storage_size == 0) {
         mf_ul_emulate->data.type = MfUltralightTypeUnknown;
         mf_ul_emulate->support_fast_read = false;
@@ -197,11 +200,15 @@ void mf_ul_protect_auth_data_on_read_command(
     }
 }
 
-uint16_t mf_ul_prepare_emulation_response(
+bool mf_ul_prepare_emulation_response(
     uint8_t* buff_rx,
-    uint16_t len_rx,
+    uint16_t buff_rx_len,
     uint8_t* buff_tx,
-    MifareUlDevice* mf_ul_emulate) {
+    uint16_t* buff_tx_len,
+    uint32_t* data_type,
+    void* context) {
+    furi_assert(context);
+    MifareUlDevice* mf_ul_emulate = context;
     uint8_t cmd = buff_rx[0];
     uint16_t page_num = mf_ul_emulate->data.data_size / 4;
     uint16_t tx_bytes = 0;
@@ -211,12 +218,13 @@ uint16_t mf_ul_prepare_emulation_response(
     // Check composite commands
     if(mf_ul_emulate->comp_write_cmd_started) {
         // Compatibility write is the only one composit command
-        if(len_rx == 16) {
+        if(buff_rx_len == 16) {
             memcpy(&mf_ul_emulate->data.data[mf_ul_emulate->comp_write_page_addr * 4], buff_rx, 4);
             mf_ul_emulate->data_changed = true;
             // Send ACK message
             buff_tx[0] = 0x0A;
             tx_bits = 4;
+            *data_type = FURI_HAL_NFC_TXRX_RAW;
             command_parsed = true;
         }
         mf_ul_emulate->comp_write_cmd_started = false;
@@ -224,6 +232,7 @@ uint16_t mf_ul_prepare_emulation_response(
         if(mf_ul_emulate->data.type != MfUltralightTypeUnknown) {
             tx_bytes = sizeof(mf_ul_emulate->data.version);
             memcpy(buff_tx, &mf_ul_emulate->data.version, tx_bytes);
+            *data_type = FURI_HAL_NFC_TXRX_DEFAULT;
             command_parsed = true;
         }
     } else if(cmd == MF_UL_READ_CMD) {
@@ -243,6 +252,7 @@ uint16_t mf_ul_prepare_emulation_response(
             }
             mf_ul_protect_auth_data_on_read_command(
                 buff_tx, start_page, (start_page + 4), mf_ul_emulate);
+            *data_type = FURI_HAL_NFC_TXRX_DEFAULT;
             command_parsed = true;
         }
     } else if(cmd == MF_UL_FAST_READ_CMD) {
@@ -254,6 +264,7 @@ uint16_t mf_ul_prepare_emulation_response(
                 memcpy(buff_tx, &mf_ul_emulate->data.data[start_page * 4], tx_bytes);
                 mf_ul_protect_auth_data_on_read_command(
                     buff_tx, start_page, end_page, mf_ul_emulate);
+                *data_type = FURI_HAL_NFC_TXRX_DEFAULT;
                 command_parsed = true;
             }
         }
@@ -265,6 +276,7 @@ uint16_t mf_ul_prepare_emulation_response(
             // ACK
             buff_tx[0] = 0x0A;
             tx_bits = 4;
+            *data_type = FURI_HAL_NFC_TXRX_RAW;
             command_parsed = true;
         }
     } else if(cmd == MF_UL_COMP_WRITE) {
@@ -275,6 +287,7 @@ uint16_t mf_ul_prepare_emulation_response(
             // ACK
             buff_tx[0] = 0x0A;
             tx_bits = 4;
+            *data_type = FURI_HAL_NFC_TXRX_RAW;
             command_parsed = true;
         }
     } else if(cmd == MF_UL_READ_CNT) {
@@ -284,6 +297,7 @@ uint16_t mf_ul_prepare_emulation_response(
             buff_tx[1] = mf_ul_emulate->data.counter[cnt_num] >> 8;
             buff_tx[2] = mf_ul_emulate->data.counter[cnt_num];
             tx_bytes = 3;
+            *data_type = FURI_HAL_NFC_TXRX_DEFAULT;
             command_parsed = true;
         }
     } else if(cmd == MF_UL_INC_CNT) {
@@ -295,6 +309,7 @@ uint16_t mf_ul_prepare_emulation_response(
             // ACK
             buff_tx[0] = 0x0A;
             tx_bits = 4;
+            *data_type = FURI_HAL_NFC_TXRX_RAW;
             command_parsed = true;
         }
     } else if(cmd == MF_UL_AUTH) {
@@ -303,11 +318,13 @@ uint16_t mf_ul_prepare_emulation_response(
                 buff_tx[0] = mf_ul_emulate->auth_data->pack.raw[0];
                 buff_tx[1] = mf_ul_emulate->auth_data->pack.raw[1];
                 tx_bytes = 2;
+                *data_type = FURI_HAL_NFC_TXRX_DEFAULT;
                 command_parsed = true;
             } else if(!mf_ul_emulate->auth_data->pack.value) {
                 buff_tx[0] = 0x80;
                 buff_tx[1] = 0x80;
                 tx_bytes = 2;
+                *data_type = FURI_HAL_NFC_TXRX_DEFAULT;
                 command_parsed = true;
             }
         }
@@ -316,6 +333,7 @@ uint16_t mf_ul_prepare_emulation_response(
         if(buff_rx[1] == 0x00) {
             tx_bytes = sizeof(mf_ul_emulate->data.signature);
             memcpy(buff_tx, mf_ul_emulate->data.signature, tx_bytes);
+            *data_type = FURI_HAL_NFC_TXRX_DEFAULT;
             command_parsed = true;
         }
     } else if(cmd == MF_UL_CHECK_TEARING) {
@@ -323,6 +341,7 @@ uint16_t mf_ul_prepare_emulation_response(
         if(cnt_num < 3) {
             buff_tx[0] = mf_ul_emulate->data.tearing[cnt_num];
             tx_bytes = 1;
+            *data_type = FURI_HAL_NFC_TXRX_DEFAULT;
             command_parsed = true;
         }
     } else if(cmd == MF_UL_HALT_START) {
@@ -334,10 +353,12 @@ uint16_t mf_ul_prepare_emulation_response(
         // Send NACK
         buff_tx[0] = 0x00;
         tx_bits = 4;
+        *data_type = FURI_HAL_NFC_TXRX_RAW;
     }
     // Return tx buffer size in bits
     if(tx_bytes) {
         tx_bits = tx_bytes * 8;
     }
-    return tx_bits;
+    *buff_tx_len = tx_bits;
+    return tx_bits > 0;
 }

+ 5 - 3
lib/nfc_protocols/mifare_ultralight.h

@@ -116,8 +116,10 @@ void mf_ul_parse_fast_read_response(
 uint16_t mf_ul_prepare_write(uint8_t* dest, uint16_t page_addr, uint32_t data);
 
 void mf_ul_prepare_emulation(MifareUlDevice* mf_ul_emulate, MifareUlData* data);
-uint16_t mf_ul_prepare_emulation_response(
+bool mf_ul_prepare_emulation_response(
     uint8_t* buff_rx,
-    uint16_t len_rx,
+    uint16_t buff_rx_len,
     uint8_t* buff_tx,
-    MifareUlDevice* mf_ul_emulate);
+    uint16_t* buff_tx_len,
+    uint32_t* data_type,
+    void* context);

+ 32 - 0
lib/nfc_protocols/nfca.c

@@ -0,0 +1,32 @@
+#include "nfca.h"
+#include <string.h>
+#include <stdio.h>
+
+#define NFCA_CMD_RATS (0xE0U)
+
+typedef struct {
+    uint8_t cmd;
+    uint8_t param;
+} nfca_cmd_rats;
+
+static uint8_t nfca_default_ats[] = {0x05, 0x78, 0x80, 0x80, 0x00};
+
+static uint8_t nfca_sleep_req[] = {0x50, 0x00};
+
+bool nfca_emulation_handler(
+    uint8_t* buff_rx,
+    uint16_t buff_rx_len,
+    uint8_t* buff_tx,
+    uint16_t* buff_tx_len) {
+    bool sleep = false;
+    uint8_t rx_bytes = buff_rx_len / 8;
+
+    if(rx_bytes == sizeof(nfca_sleep_req) && !memcmp(buff_rx, nfca_sleep_req, rx_bytes)) {
+        sleep = true;
+    } else if(rx_bytes == sizeof(nfca_cmd_rats) && buff_rx[0] == NFCA_CMD_RATS) {
+        memcpy(buff_tx, nfca_default_ats, sizeof(nfca_default_ats));
+        *buff_tx_len = sizeof(nfca_default_ats) * 8;
+    }
+
+    return sleep;
+}

+ 10 - 0
lib/nfc_protocols/nfca.h

@@ -0,0 +1,10 @@
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+
+bool nfca_emulation_handler(
+    uint8_t* buff_rx,
+    uint16_t buff_rx_len,
+    uint8_t* buff_tx,
+    uint16_t* buff_tx_len);