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

FL-53: new NFC worker, A/B/F/V poll and display. (#283)

* GUI: view. Flooper-blooper fix compilation error.
* GUI: view and viewdispatcher bones
* GUI: view implementation, view models, view dispatcher
* GUI: view navigation, model refinement. Power: use view, view dispatcher.

* HAL Flash: proper page write. Dolphin: views. Power: views

* Dolphin: transition idle scree to Views
* Dolphin: input events on stats view. Format sources.

* HAL: flash erase. Dolphin: permanent state storage.

* Dolphin: first start welcome. HAL: flash operation status, errata 2.2.9 crutch.

* NFC: rewrite worker
* NFC: add support for B,F,V.
* NFC: replace rfal irq hanlder with realtime thread, more details about cards.

* Bootloader: LSE and RTS shenanigans, LED control, morse code for LSE failure error.

* F4: stop in Error_Handler

* BLE: handle working FUS, but empty radio stack.

* HAL: alive FUS is now sufficient for flash controller access

* Dolphin: update model after state load

* NFC: detect navigation

* RFAL: use osPriorityISR for isr thread

* NFC: emulation

* Bootloader: rollback incorrectly merged rename

* Dolphin: rollback incorrectly merged changes

* RFAL: remove volatile from thread attr

* RFAL: do not call platform ErrorHandler, error codes is enough

* NFC: improved error handling

* Format sources

* NFC: reset detect view model on start

* Format sources

* update codeowners

* NFC: hide last info if no card detected
あく 5 лет назад
Родитель
Сommit
6928122650

+ 1 - 0
.github/CODEOWNERS

@@ -59,6 +59,7 @@ applications/menu/** @skotopes
 # NFC
 
 applications/nfc/** @skotopes
+lib/ST25RFAL002/** @skotopes
 
 # SD Card
 

+ 5 - 5
applications/dolphin/dolphin.c

@@ -92,11 +92,6 @@ Dolphin* dolphin_alloc() {
     view_set_context(dolphin->idle_view_stats, dolphin);
     view_allocate_model(
         dolphin->idle_view_stats, ViewModelTypeLockFree, sizeof(DolphinViewIdleStatsModel));
-    with_view_model(
-        dolphin->idle_view_stats, (DolphinViewIdleStatsModel * model) {
-            model->icounter = dolphin_state_get_icounter(dolphin->state);
-            model->butthurt = dolphin_state_get_butthurt(dolphin->state);
-        });
     view_set_draw_callback(dolphin->idle_view_stats, dolphin_view_idle_stats_draw);
     view_set_input_callback(dolphin->idle_view_stats, dolphin_view_idle_stats_input);
     view_set_previous_callback(dolphin->idle_view_stats, dolphin_view_idle_back);
@@ -137,6 +132,11 @@ void dolphin_task() {
     } else {
         view_dispatcher_switch_to_view(dolphin->idle_view_dispatcher, DolphinViewFirstStart);
     }
+    with_view_model(
+        dolphin->idle_view_stats, (DolphinViewIdleStatsModel * model) {
+            model->icounter = dolphin_state_get_icounter(dolphin->state);
+            model->butthurt = dolphin_state_get_butthurt(dolphin->state);
+        });
 
     if(!furi_create("dolphin", dolphin)) {
         printf("[dolphin_task] cannot create the dolphin record\n");

+ 0 - 57
applications/nfc/dispatcher.c

@@ -1,57 +0,0 @@
-#include "dispatcher.h"
-
-#include <flipper.h>
-#include <flipper_v2.h>
-
-struct Dispatcher {
-    void* message;
-    size_t message_size;
-    osMessageQueueId_t mqueue;
-    osMutexId_t lock_mutex;
-};
-
-Dispatcher* dispatcher_alloc(size_t queue_size, size_t message_size) {
-    Dispatcher* dispatcher = furi_alloc(sizeof(Dispatcher));
-
-    dispatcher->message = furi_alloc(message_size);
-    dispatcher->message_size = message_size;
-
-    dispatcher->mqueue = osMessageQueueNew(queue_size, message_size, NULL);
-    furi_check(dispatcher->mqueue);
-
-    dispatcher->lock_mutex = osMutexNew(NULL);
-    furi_check(dispatcher->lock_mutex);
-    dispatcher_lock(dispatcher);
-
-    return dispatcher;
-}
-
-void dispatcher_free(Dispatcher* dispatcher) {
-    furi_assert(dispatcher);
-    free(dispatcher);
-}
-
-void dispatcher_send(Dispatcher* dispatcher, Message* message) {
-    furi_assert(dispatcher);
-    furi_assert(message);
-    furi_check(osMessageQueuePut(dispatcher->mqueue, message, 0, osWaitForever) == osOK);
-}
-
-// TODO: bad side-effect
-void dispatcher_recieve(Dispatcher* dispatcher, Message* message) {
-    furi_assert(dispatcher);
-    furi_assert(message);
-    dispatcher_unlock(dispatcher);
-    furi_check(osMessageQueueGet(dispatcher->mqueue, message, NULL, osWaitForever) == osOK);
-    dispatcher_lock(dispatcher);
-}
-
-void dispatcher_lock(Dispatcher* dispatcher) {
-    furi_assert(dispatcher);
-    furi_check(osMutexAcquire(dispatcher->lock_mutex, osWaitForever) == osOK);
-}
-
-void dispatcher_unlock(Dispatcher* dispatcher) {
-    furi_assert(dispatcher);
-    furi_check(osMutexRelease(dispatcher->lock_mutex) == osOK);
-}

+ 0 - 28
applications/nfc/dispatcher.h

@@ -1,28 +0,0 @@
-#pragma once
-
-#include <stddef.h>
-#include <stdint.h>
-
-enum MessageTypeBase {
-    MessageTypeExit = 0x00,
-    MessageTypeMemoryLow = 0x01,
-    MessageTypeBatteryLow = 0x02,
-};
-
-typedef struct {
-    enum MessageTypeBase type;
-} Message;
-
-typedef struct Dispatcher Dispatcher;
-
-Dispatcher* dispatcher_alloc(size_t queue_size, size_t message_size);
-
-void dispatcher_free(Dispatcher* dispatcher);
-
-void dispatcher_send(Dispatcher* dispatcher, Message* message);
-
-void dispatcher_recieve(Dispatcher* dispatcher, Message* message);
-
-void dispatcher_lock(Dispatcher* dispatcher);
-
-void dispatcher_unlock(Dispatcher* dispatcher);

+ 92 - 144
applications/nfc/nfc.c

@@ -1,174 +1,109 @@
-#include "nfc.h"
 #include "nfc_i.h"
-#include "nfc_worker.h"
 
-void nfc_draw_callback(Canvas* canvas, void* context) {
-    furi_assert(canvas);
-    furi_assert(context);
-
-    Nfc* nfc = context;
-
-    dispatcher_lock(nfc->dispatcher);
-    canvas_clear(canvas);
-    canvas_set_color(canvas, ColorBlack);
-    canvas_set_font(canvas, FontPrimary);
-
-    if(nfc->screen == 0) {
-        char status[128 / 8];
-        if(nfc->ret == ERR_WRONG_STATE)
-            canvas_draw_str(canvas, 2, 16, "NFC Wrong State");
-        else if(nfc->ret == ERR_PARAM)
-            canvas_draw_str(canvas, 2, 16, "NFC Wrong Param");
-        else if(nfc->ret == ERR_IO)
-            canvas_draw_str(canvas, 2, 16, "NFC IO Error");
-        else if(nfc->ret == ERR_NONE)
-            canvas_draw_str(canvas, 2, 16, "NFC Device Found");
-        else if(nfc->ret == ERR_TIMEOUT)
-            canvas_draw_str(canvas, 2, 16, "NFC Timeout");
-        else
-            canvas_draw_str(canvas, 2, 16, "NFC error");
-
-        canvas_set_font(canvas, FontSecondary);
-        snprintf(status, sizeof(status), "Found: %d", nfc->devCnt);
-        if(nfc->devCnt > 0) {
-            canvas_draw_str(canvas, 2, 32, status);
-            canvas_draw_str(canvas, 2, 42, nfc->current);
-
-            snprintf(
-                status,
-                sizeof(status),
-                "ATQA:%d SAK:%d",
-                nfc->first_atqa.anticollisionInfo,
-                nfc->first_sak.sak);
-            canvas_draw_str(canvas, 2, 52, status);
-        }
-    } else {
-        canvas_draw_str(canvas, 2, 16, "Not implemented");
-    }
-
-    dispatcher_unlock(nfc->dispatcher);
-}
-
-void nfc_input_callback(InputEvent* event, void* context) {
-    furi_assert(event);
+uint32_t nfc_view_exit(void* context) {
     furi_assert(context);
     Nfc* nfc = context;
-
-    if(!event->state || event->input != InputBack) return;
-
-    widget_enabled_set(nfc->widget, false);
+    NfcMessage message;
+    message.type = NfcMessageTypeStop;
+    furi_check(osMessageQueuePut(nfc->message_queue, &message, 0, osWaitForever) == osOK);
+    return VIEW_NONE;
 }
 
-void nfc_test_callback(void* context) {
-    furi_assert(context);
-    Nfc* nfc = context;
-
-    dispatcher_lock(nfc->dispatcher);
-
-    nfc->screen = 0;
-    widget_enabled_set(nfc->widget, true);
-
-    // TODO only for workaround
-    if(nfc->ret != ERR_NONE) {
-        nfc->ret = rfalNfcInitialize();
-        rfalLowPowerModeStart();
-    }
-
-    if(nfc->ret == ERR_NONE && !nfc->worker) {
-        // TODO change to fuirac_start
-        nfc->worker = osThreadNew(nfc_worker_task, nfc, &nfc->worker_attr);
-    }
+Nfc* nfc_alloc() {
+    Nfc* nfc = furi_alloc(sizeof(Nfc));
 
-    dispatcher_unlock(nfc->dispatcher);
-}
+    nfc->message_queue = osMessageQueueNew(8, sizeof(NfcMessage), NULL);
+    nfc->worker = nfc_worker_alloc(nfc->message_queue);
 
-void nfc_field_on_callback(void* context) {
-    furi_assert(context);
-    Nfc* nfc = context;
+    nfc->icon = assets_icons_get(A_NFC_14);
+    nfc->menu_vm = furi_open("menu");
+    furi_check(nfc->menu_vm);
 
-    // TODO only for workaround
-    if(nfc->ret != ERR_NONE) {
-        nfc->ret = rfalNfcInitialize();
-        rfalLowPowerModeStart();
-    }
+    nfc->menu = menu_item_alloc_menu("NFC", nfc->icon);
+    menu_item_subitem_add(
+        nfc->menu, menu_item_alloc_function("Detect", NULL, nfc_menu_detect_callback, nfc));
+    menu_item_subitem_add(
+        nfc->menu, menu_item_alloc_function("Emulate", NULL, nfc_menu_emulate_callback, nfc));
+    menu_item_subitem_add(
+        nfc->menu, menu_item_alloc_function("Field", NULL, nfc_menu_field_callback, nfc));
+
+    nfc->view_detect = view_alloc();
+    view_set_context(nfc->view_detect, nfc);
+    view_set_draw_callback(nfc->view_detect, nfc_view_read_draw);
+    view_set_previous_callback(nfc->view_detect, nfc_view_exit);
+    view_allocate_model(nfc->view_detect, ViewModelTypeLocking, sizeof(NfcViewReadModel));
+    nfc->view_emulate = view_alloc();
+    view_set_context(nfc->view_emulate, nfc);
+    view_set_draw_callback(nfc->view_emulate, nfc_view_emulate_draw);
+    view_set_previous_callback(nfc->view_emulate, nfc_view_exit);
+    nfc->view_field = view_alloc();
+    view_set_context(nfc->view_field, nfc);
+    view_set_draw_callback(nfc->view_field, nfc_view_field_draw);
+    view_set_previous_callback(nfc->view_field, nfc_view_exit);
+    nfc->view_error = view_alloc();
+    view_set_context(nfc->view_error, nfc);
+    view_set_draw_callback(nfc->view_error, nfc_view_error_draw);
+    view_set_previous_callback(nfc->view_error, nfc_view_exit);
+    view_allocate_model(nfc->view_error, ViewModelTypeLockFree, sizeof(NfcViewErrorModel));
+    nfc->view_dispatcher = view_dispatcher_alloc();
+    view_dispatcher_add_view(nfc->view_dispatcher, NfcViewRead, nfc->view_detect);
+    view_dispatcher_add_view(nfc->view_dispatcher, NfcViewEmulate, nfc->view_emulate);
+    view_dispatcher_add_view(nfc->view_dispatcher, NfcViewField, nfc->view_field);
+    view_dispatcher_add_view(nfc->view_dispatcher, NfcViewError, nfc->view_error);
 
-    st25r3916OscOn();
-    st25r3916TxRxOn();
+    return nfc;
 }
 
-void nfc_field_off_callback(void* context) {
+void nfc_menu_detect_callback(void* context) {
     furi_assert(context);
     Nfc* nfc = context;
-
-    // TODO only for workaround
-    if(nfc->ret != ERR_NONE) {
-        nfc->ret = rfalNfcInitialize();
-        rfalLowPowerModeStart();
-    }
-
-    st25r3916TxRxOff();
+    NfcMessage message;
+    message.type = NfcMessageTypeDetect;
+    furi_check(osMessageQueuePut(nfc->message_queue, &message, 0, osWaitForever) == osOK);
 }
 
-void nfc_read_callback(void* context) {
+void nfc_menu_emulate_callback(void* context) {
     furi_assert(context);
     Nfc* nfc = context;
-    nfc->screen = 1;
-    widget_enabled_set(nfc->widget, true);
+    NfcMessage message;
+    message.type = NfcMessageTypeEmulate;
+    furi_check(osMessageQueuePut(nfc->message_queue, &message, 0, osWaitForever) == osOK);
 }
 
-void nfc_write_callback(void* context) {
+void nfc_menu_field_callback(void* context) {
     furi_assert(context);
     Nfc* nfc = context;
-    nfc->screen = 1;
-    widget_enabled_set(nfc->widget, true);
+    NfcMessage message;
+    message.type = NfcMessageTypeField;
+    furi_check(osMessageQueuePut(nfc->message_queue, &message, 0, osWaitForever) == osOK);
 }
 
-void nfc_bridge_callback(void* context) {
+void nfc_menu_field_off_callback(void* context) {
     furi_assert(context);
     Nfc* nfc = context;
-    nfc->screen = 1;
-    widget_enabled_set(nfc->widget, true);
+    NfcMessage message;
+    message.type = NfcMessageTypeField;
+    furi_check(osMessageQueuePut(nfc->message_queue, &message, 0, osWaitForever) == osOK);
 }
 
-Nfc* nfc_alloc() {
-    Nfc* nfc = furi_alloc(sizeof(Nfc));
-
-    nfc->dispatcher = dispatcher_alloc(32, sizeof(NfcMessage));
-
-    nfc->icon = assets_icons_get(A_NFC_14);
-    nfc->widget = widget_alloc();
-    widget_draw_callback_set(nfc->widget, nfc_draw_callback, nfc);
-    widget_input_callback_set(nfc->widget, nfc_input_callback, nfc);
-
-    nfc->menu_vm = furi_open("menu");
-    furi_check(nfc->menu_vm);
-
-    nfc->menu = menu_item_alloc_menu("NFC", nfc->icon);
-    menu_item_subitem_add(
-        nfc->menu, menu_item_alloc_function("Test", NULL, nfc_test_callback, nfc));
-    menu_item_subitem_add(
-        nfc->menu, menu_item_alloc_function("Field On", NULL, nfc_field_on_callback, nfc));
-    menu_item_subitem_add(
-        nfc->menu, menu_item_alloc_function("Field Off", NULL, nfc_field_off_callback, nfc));
-    menu_item_subitem_add(
-        nfc->menu, menu_item_alloc_function("Read", NULL, nfc_read_callback, nfc));
-    menu_item_subitem_add(
-        nfc->menu, menu_item_alloc_function("Write", NULL, nfc_write_callback, nfc));
-    menu_item_subitem_add(
-        nfc->menu, menu_item_alloc_function("Brdige", NULL, nfc_bridge_callback, nfc));
-
-    nfc->worker_attr.name = "nfc_worker";
-    // nfc->worker_attr.attr_bits = osThreadJoinable;
-    nfc->worker_attr.stack_size = 4096;
-    return nfc;
+void nfc_start(Nfc* nfc, NfcView view_id, NfcWorkerState worker_state) {
+    NfcWorkerState state = nfc_worker_get_state(nfc->worker);
+    if(state == NfcWorkerStateBroken) {
+        with_view_model(
+            nfc->view_error,
+            (NfcViewErrorModel * model) { model->error = nfc_worker_get_error(nfc->worker); });
+        view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewError);
+    } else if(state == NfcWorkerStateReady) {
+        view_dispatcher_switch_to_view(nfc->view_dispatcher, view_id);
+        nfc_worker_start(nfc->worker, worker_state);
+    }
 }
 
 void nfc_task(void* p) {
     Nfc* nfc = nfc_alloc();
 
     Gui* gui = furi_open("gui");
-    widget_enabled_set(nfc->widget, false);
-    gui_add_widget(gui, nfc->widget, GuiLayerFullscreen);
+    view_dispatcher_attach_to_gui(nfc->view_dispatcher, gui, ViewDispatcherTypeFullscreen);
 
     with_value_mutex(
         nfc->menu_vm, (Menu * menu) { menu_item_add(menu, nfc->menu); });
@@ -178,17 +113,30 @@ void nfc_task(void* p) {
         furiac_exit(NULL);
     }
 
-    // TODO only for workaround
-    nfc->ret = ERR_WRONG_STATE;
-
     furiac_ready();
 
     NfcMessage message;
     while(1) {
-        dispatcher_recieve(nfc->dispatcher, (Message*)&message);
-
-        if(message.base.type == MessageTypeExit) {
-            break;
+        furi_check(osMessageQueueGet(nfc->message_queue, &message, NULL, osWaitForever) == osOK);
+        if(message.type == NfcMessageTypeDetect) {
+            with_view_model(
+                nfc->view_detect, (NfcViewReadModel * model) { model->found = false; });
+            nfc_start(nfc, NfcViewRead, NfcWorkerStatePoll);
+        } else if(message.type == NfcMessageTypeEmulate) {
+            nfc_start(nfc, NfcViewEmulate, NfcWorkerStateEmulate);
+        } else if(message.type == NfcMessageTypeField) {
+            nfc_start(nfc, NfcViewField, NfcWorkerStateField);
+        } else if(message.type == NfcMessageTypeStop) {
+            nfc_worker_stop(nfc->worker);
+        } else if(message.type == NfcMessageTypeDeviceFound) {
+            with_view_model(
+                nfc->view_detect, (NfcViewReadModel * model) {
+                    model->found = true;
+                    model->device = message.device;
+                });
+        } else if(message.type == NfcMessageTypeDeviceNotFound) {
+            with_view_model(
+                nfc->view_detect, (NfcViewReadModel * model) { model->found = false; });
         }
     }
 }

+ 27 - 32
applications/nfc/nfc_i.h

@@ -1,49 +1,44 @@
 #pragma once
 
-#include <flipper_v2.h>
+#include "nfc.h"
+#include "nfc_types.h"
+#include "nfc_views.h"
+#include "nfc_worker.h"
 
-#include <rfal_analogConfig.h>
-#include <rfal_rf.h>
-#include <rfal_nfc.h>
-#include <rfal_nfca.h>
-#include <st25r3916.h>
-#include <st25r3916_irq.h>
+#include <flipper_v2.h>
 
 #include <gui/gui.h>
-#include <gui/widget.h>
-#include <gui/canvas.h>
+#include <gui/view.h>
+#include <gui/view_dispatcher.h>
 #include <assets_icons.h>
 
 #include <menu/menu.h>
 #include <menu/menu_item.h>
 
-#include "dispatcher.h"
-
-typedef enum {
-    MessageTypeBase,
-} NfcMessageType;
+struct Nfc {
+    osMessageQueueId_t message_queue;
 
-typedef struct {
-    Message base;
-    void* data;
-} NfcMessage;
+    NfcWorker* worker;
 
-struct Nfc {
-    Dispatcher* dispatcher;
-    Icon* icon;
-    Widget* widget;
     ValueMutex* menu_vm;
     MenuItem* menu;
-    rfalNfcDiscoverParam* disParams;
+    Icon* icon;
 
-    osThreadAttr_t worker_attr;
-    osThreadId_t worker;
+    View* view_detect;
+    View* view_emulate;
+    View* view_field;
+    View* view_error;
+    ViewDispatcher* view_dispatcher;
+};
 
-    uint8_t screen;
-    uint8_t ret;
-    uint8_t devCnt;
-    rfalNfcaSensRes first_atqa;
-    rfalNfcaSelRes first_sak;
+Nfc* nfc_alloc();
 
-    char* current;
-};
+void nfc_menu_detect_callback(void* context);
+
+void nfc_menu_emulate_callback(void* context);
+
+void nfc_menu_field_callback(void* context);
+
+void nfc_start(Nfc* nfc, NfcView view_id, NfcWorkerState worker_state);
+
+void nfc_task(void* p);

+ 68 - 0
applications/nfc/nfc_types.h

@@ -0,0 +1,68 @@
+#pragma once
+
+#include <rfal_nfc.h>
+#include <st_errno.h>
+
+static inline const char* nfc_get_nfca_type(rfalNfcaListenDeviceType type) {
+    if(type == RFAL_NFCA_T1T) {
+        return "T1T";
+    } else if(type == RFAL_NFCA_T2T) {
+        return "T2T";
+    } else if(type == RFAL_NFCA_T4T) {
+        return "T4T";
+    } else if(type == RFAL_NFCA_NFCDEP) {
+        return "NFCDEP";
+    } else if(type == RFAL_NFCA_T4T_NFCDEP) {
+        return "T4T_NFCDEP";
+    } else {
+        return "Unknown";
+    }
+}
+
+typedef enum {
+    NfcDeviceTypeNfca,
+    NfcDeviceTypeNfcb,
+    NfcDeviceTypeNfcf,
+    NfcDeviceTypeNfcv,
+    NfcDeviceTypeNfcMifare
+} NfcDeviceType;
+
+typedef struct {
+    NfcDeviceType type;
+    union {
+        rfalNfcaListenDevice nfca;
+        rfalNfcbListenDevice nfcb;
+        rfalNfcfListenDevice nfcf;
+        rfalNfcvListenDevice nfcv;
+    };
+} NfcDevice;
+
+typedef enum {
+    // Init states
+    NfcWorkerStateNone,
+    NfcWorkerStateBroken,
+    NfcWorkerStateReady,
+    // Main worker states
+    NfcWorkerStatePoll,
+    NfcWorkerStateEmulate,
+    NfcWorkerStateField,
+    // Transition
+    NfcWorkerStateStop,
+} NfcWorkerState;
+
+typedef enum {
+    NfcMessageTypeDetect,
+    NfcMessageTypeEmulate,
+    NfcMessageTypeField,
+    NfcMessageTypeStop,
+    // From Worker
+    NfcMessageTypeDeviceFound,
+    NfcMessageTypeDeviceNotFound,
+} NfcMessageType;
+
+typedef struct {
+    NfcMessageType type;
+    union {
+        NfcDevice device;
+    };
+} NfcMessage;

+ 147 - 0
applications/nfc/nfc_views.c

@@ -0,0 +1,147 @@
+#include "nfc_views.h"
+
+void nfc_view_read_draw(Canvas* canvas, void* model) {
+    NfcViewReadModel* m = model;
+    canvas_clear(canvas);
+    canvas_set_font(canvas, FontPrimary);
+    if(m->found) {
+        if(m->device.type == NfcDeviceTypeNfca) {
+            nfc_view_read_nfca_draw(canvas, m);
+        } else if(m->device.type == NfcDeviceTypeNfcb) {
+            nfc_view_read_nfcb_draw(canvas, m);
+        } else if(m->device.type == NfcDeviceTypeNfcv) {
+            nfc_view_read_nfcv_draw(canvas, m);
+        } else if(m->device.type == NfcDeviceTypeNfcf) {
+            nfc_view_read_nfcf_draw(canvas, m);
+        }
+    } else {
+        canvas_draw_str(canvas, 0, 12, "Searching");
+        canvas_set_font(canvas, FontSecondary);
+        canvas_draw_str(canvas, 2, 22, "Place card to the back");
+    }
+}
+
+void nfc_view_read_nfca_draw(Canvas* canvas, NfcViewReadModel* model) {
+    char buffer[32];
+    canvas_draw_str(canvas, 0, 12, "Found NFC-A");
+    canvas_set_font(canvas, FontSecondary);
+    snprintf(buffer, sizeof(buffer), "Type: %s", nfc_get_nfca_type(model->device.nfca.type));
+    canvas_draw_str(canvas, 2, 22, buffer);
+    snprintf(buffer, sizeof(buffer), "UID length: %d", model->device.nfca.nfcId1Len);
+    canvas_draw_str(canvas, 2, 32, buffer);
+
+    canvas_draw_str(canvas, 2, 42, "UID:");
+    for(uint8_t i = 0; i < model->device.nfca.nfcId1Len; i++) {
+        snprintf(buffer + (i * 2), sizeof(buffer) - (i * 2), "%02X", model->device.nfca.nfcId1[i]);
+    }
+    buffer[model->device.nfca.nfcId1Len * 2] = 0;
+    canvas_draw_str(canvas, 18, 42, buffer);
+
+    snprintf(
+        buffer,
+        sizeof(buffer),
+        "SAK: %02X ATQA: %02X/%02X",
+        model->device.nfca.selRes.sak,
+        model->device.nfca.sensRes.anticollisionInfo,
+        model->device.nfca.sensRes.platformInfo);
+    canvas_draw_str(canvas, 2, 52, buffer);
+}
+
+void nfc_view_read_nfcb_draw(Canvas* canvas, NfcViewReadModel* model) {
+    char buffer[32];
+    canvas_draw_str(canvas, 0, 12, "Found NFC-B");
+    canvas_set_font(canvas, FontSecondary);
+
+    snprintf(buffer, sizeof(buffer), "UID length: %d", RFAL_NFCB_NFCID0_LEN);
+    canvas_draw_str(canvas, 2, 32, buffer);
+
+    canvas_draw_str(canvas, 2, 42, "UID:");
+    for(uint8_t i = 0; i < RFAL_NFCB_NFCID0_LEN; i++) {
+        snprintf(
+            buffer + (i * 2),
+            sizeof(buffer) - (i * 2),
+            "%02X",
+            model->device.nfcb.sensbRes.nfcid0[i]);
+    }
+    buffer[RFAL_NFCB_NFCID0_LEN * 2] = 0;
+    canvas_draw_str(canvas, 18, 42, buffer);
+}
+
+void nfc_view_read_nfcf_draw(Canvas* canvas, NfcViewReadModel* model) {
+    char buffer[32];
+    canvas_draw_str(canvas, 0, 12, "Found NFC-F");
+    canvas_set_font(canvas, FontSecondary);
+
+    snprintf(buffer, sizeof(buffer), "UID length: %d", RFAL_NFCF_NFCID2_LEN);
+    canvas_draw_str(canvas, 2, 32, buffer);
+
+    canvas_draw_str(canvas, 2, 42, "UID:");
+    for(uint8_t i = 0; i < RFAL_NFCF_NFCID2_LEN; i++) {
+        snprintf(
+            buffer + (i * 2),
+            sizeof(buffer) - (i * 2),
+            "%02X",
+            model->device.nfcf.sensfRes.NFCID2[i]);
+    }
+    buffer[RFAL_NFCF_NFCID2_LEN * 2] = 0;
+    canvas_draw_str(canvas, 18, 42, buffer);
+}
+
+void nfc_view_read_nfcv_draw(Canvas* canvas, NfcViewReadModel* model) {
+    char buffer[32];
+    canvas_draw_str(canvas, 0, 12, "Found NFC-V");
+    canvas_set_font(canvas, FontSecondary);
+
+    snprintf(buffer, sizeof(buffer), "UID length: %d", RFAL_NFCV_UID_LEN);
+    canvas_draw_str(canvas, 2, 32, buffer);
+
+    canvas_draw_str(canvas, 2, 42, "UID:");
+    for(uint8_t i = 0; i < RFAL_NFCV_UID_LEN; i++) {
+        snprintf(
+            buffer + (i * 2), sizeof(buffer) - (i * 2), "%02X", model->device.nfcv.InvRes.UID[i]);
+    }
+    buffer[RFAL_NFCV_UID_LEN * 2] = 0;
+    canvas_draw_str(canvas, 18, 42, buffer);
+}
+
+void nfc_view_emulate_draw(Canvas* canvas, void* model) {
+    canvas_clear(canvas);
+    canvas_set_font(canvas, FontPrimary);
+    canvas_draw_str(canvas, 0, 12, "Emulating NFC-A");
+    canvas_set_font(canvas, FontSecondary);
+    canvas_draw_str(canvas, 2, 22, "Type: T2T");
+    canvas_draw_str(canvas, 2, 32, "UID length: 7");
+    canvas_draw_str(canvas, 2, 42, "UID: 00010203040506");
+    canvas_draw_str(canvas, 2, 52, "SAK: 00 ATQA: 44/00");
+}
+
+void nfc_view_field_draw(Canvas* canvas, void* model) {
+    canvas_clear(canvas);
+    canvas_set_font(canvas, FontPrimary);
+    canvas_draw_str(canvas, 0, 12, "Field ON");
+    canvas_set_font(canvas, FontSecondary);
+    canvas_draw_str(canvas, 2, 22, "TX/RX is disabled");
+}
+
+void nfc_view_error_draw(Canvas* canvas, void* model) {
+    NfcViewErrorModel* m = model;
+    char buffer[32];
+
+    canvas_clear(canvas);
+    canvas_set_font(canvas, FontPrimary);
+    snprintf(buffer, sizeof(buffer), "Error: %d", m->error);
+    canvas_draw_str(canvas, 0, 12, buffer);
+
+    canvas_set_font(canvas, FontSecondary);
+    if(m->error == ERR_WRONG_STATE) {
+        canvas_draw_str(canvas, 2, 22, "Wrong State");
+    } else if(m->error == ERR_PARAM) {
+        canvas_draw_str(canvas, 2, 22, "Wrong Param");
+    } else if(m->error == ERR_HW_MISMATCH) {
+        canvas_draw_str(canvas, 2, 22, "HW mismatch");
+    } else if(m->error == ERR_IO) {
+        canvas_draw_str(canvas, 2, 22, "IO Error");
+    } else {
+        canvas_draw_str(canvas, 2, 22, "Details in st_errno.h");
+    }
+}

+ 36 - 0
applications/nfc/nfc_views.h

@@ -0,0 +1,36 @@
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <gui/canvas.h>
+#include <flipper_v2.h>
+
+#include "nfc_types.h"
+
+typedef enum {
+    NfcViewRead,
+    NfcViewEmulate,
+    NfcViewField,
+    NfcViewError,
+} NfcView;
+
+typedef struct {
+    bool found;
+    NfcDevice device;
+} NfcViewReadModel;
+
+void nfc_view_read_draw(Canvas* canvas, void* model);
+void nfc_view_read_nfca_draw(Canvas* canvas, NfcViewReadModel* model);
+void nfc_view_read_nfcb_draw(Canvas* canvas, NfcViewReadModel* model);
+void nfc_view_read_nfcf_draw(Canvas* canvas, NfcViewReadModel* model);
+void nfc_view_read_nfcv_draw(Canvas* canvas, NfcViewReadModel* model);
+
+void nfc_view_emulate_draw(Canvas* canvas, void* model);
+
+void nfc_view_field_draw(Canvas* canvas, void* model);
+
+typedef struct {
+    ReturnCode error;
+} NfcViewErrorModel;
+
+void nfc_view_error_draw(Canvas* canvas, void* model);

+ 305 - 93
applications/nfc/nfc_worker.c

@@ -1,108 +1,320 @@
-#include "nfc_worker.h"
-#include "nfc.h"
-#include "nfc_i.h"
+#include "nfc_worker_i.h"
 
-#define EXAMPLE_NFCA_DEVICES 5
+NfcWorker* nfc_worker_alloc(osMessageQueueId_t message_queue) {
+    NfcWorker* nfc_worker = furi_alloc(sizeof(NfcWorker));
+    nfc_worker->message_queue = message_queue;
+    // Worker thread attributes
+    nfc_worker->thread_attr.name = "nfc_worker";
+    nfc_worker->thread_attr.stack_size = 2048;
+    // Initialize rfal
+    rfalAnalogConfigInitialize();
+    nfc_worker->error = rfalNfcInitialize();
+    if(nfc_worker->error == ERR_NONE) {
+        rfalLowPowerModeStart();
+        nfc_worker_change_state(nfc_worker, NfcWorkerStateReady);
+    } else {
+        nfc_worker_change_state(nfc_worker, NfcWorkerStateBroken);
+    }
+
+    return nfc_worker;
+}
+
+NfcWorkerState nfc_worker_get_state(NfcWorker* nfc_worker) {
+    return nfc_worker->state;
+}
 
-// TODO replace with pubsub
-static bool isr_enabled = false;
+ReturnCode nfc_worker_get_error(NfcWorker* nfc_worker) {
+    return nfc_worker->error;
+}
+
+void nfc_worker_free(NfcWorker* nfc_worker) {
+    furi_assert(nfc_worker);
+}
+
+void nfc_worker_start(NfcWorker* nfc_worker, NfcWorkerState state) {
+    furi_assert(nfc_worker);
+    furi_assert(nfc_worker->state == NfcWorkerStateReady);
+    nfc_worker_change_state(nfc_worker, state);
+    nfc_worker->thread = osThreadNew(nfc_worker_task, nfc_worker, &nfc_worker->thread_attr);
+}
 
-void nfc_isr() {
-    if(isr_enabled) {
-        st25r3916Isr();
+void nfc_worker_stop(NfcWorker* nfc_worker) {
+    furi_assert(nfc_worker);
+    if(nfc_worker->state == NfcWorkerStateBroken) {
+        return;
     }
+
+    nfc_worker_change_state(nfc_worker, NfcWorkerStateStop);
+}
+
+void nfc_worker_change_state(NfcWorker* nfc_worker, NfcWorkerState state) {
+    nfc_worker->state = state;
 }
 
 void nfc_worker_task(void* context) {
-    Nfc* nfc = context;
-    ReturnCode err;
-    rfalNfcaSensRes sensRes;
-    rfalNfcaSelRes selRes;
-    rfalNfcaListenDevice nfcaDevList[EXAMPLE_NFCA_DEVICES];
-    uint8_t devCnt;
-    uint8_t devIt;
+    NfcWorker* nfc_worker = context;
 
     rfalLowPowerModeStop();
+    if(nfc_worker->state == NfcWorkerStatePoll) {
+        nfc_worker_poll(nfc_worker);
+    } else if(nfc_worker->state == NfcWorkerStateEmulate) {
+        nfc_worker_emulate(nfc_worker);
+    } else if(nfc_worker->state == NfcWorkerStateField) {
+        nfc_worker_field(nfc_worker);
+    }
+    rfalLowPowerModeStart();
 
-    isr_enabled = true;
+    nfc_worker_change_state(nfc_worker, NfcWorkerStateReady);
 
-    while(widget_is_enabled(nfc->widget)) {
+    osThreadExit();
+}
+
+void nfc_worker_poll(NfcWorker* nfc_worker) {
+    while(nfc_worker->state == NfcWorkerStatePoll) {
+        bool is_found = false;
+        is_found |= nfc_worker_nfca_poll(nfc_worker);
+        is_found |= nfc_worker_nfcb_poll(nfc_worker);
+        is_found |= nfc_worker_nfcf_poll(nfc_worker);
+        is_found |= nfc_worker_nfcv_poll(nfc_worker);
         rfalFieldOff();
-        platformDelay(500);
-        nfc->current = "Not detected";
-        nfc->devCnt = 0;
-
-        rfalNfcaPollerInitialize();
-        rfalFieldOnAndStartGT();
-        nfc->ret = err = rfalNfcaPollerTechnologyDetection(RFAL_COMPLIANCE_MODE_NFC, &sensRes);
-        if(err == ERR_NONE) {
-            err = rfalNfcaPollerFullCollisionResolution(
-                RFAL_COMPLIANCE_MODE_NFC, EXAMPLE_NFCA_DEVICES, nfcaDevList, &devCnt);
-            nfc->devCnt = devCnt;
-            if((err == ERR_NONE) && (devCnt > 0)) {
-                platformLog("NFC-A device(s) found %d\r\n", devCnt);
-                devIt = 0;
-                if(nfcaDevList[devIt].isSleep) {
-                    err = rfalNfcaPollerCheckPresence(
-                        RFAL_14443A_SHORTFRAME_CMD_WUPA, &sensRes); /* Wake up all cards  */
-                    if(err != ERR_NONE) {
-                        continue;
-                    }
-                    err = rfalNfcaPollerSelect(
-                        nfcaDevList[devIt].nfcId1,
-                        nfcaDevList[devIt].nfcId1Len,
-                        &selRes); /* Select specific device  */
-                    if(err != ERR_NONE) {
-                        continue;
-                    }
-                }
-
-                nfc->first_atqa = nfcaDevList[devIt].sensRes;
-                nfc->first_sak = nfcaDevList[devIt].selRes;
-
-                switch(nfcaDevList[devIt].type) {
-                case RFAL_NFCA_T1T:
-                    /* No further activation needed for a T1T (RID already performed)*/
-                    platformLog(
-                        "NFC-A T1T device found \r\n"); /* NFC-A T1T device found, NFCID/UID is contained in: t1tRidRes.uid */
-                    nfc->current = "NFC-A T1T";
-                    /* Following communications shall be performed using:
-                         *   - Non blocking: rfalStartTransceive() + rfalGetTransceiveState()
-                         *   -     Blocking: rfalTransceiveBlockingTx() + rfalTransceiveBlockingRx() or rfalTransceiveBlockingTxRx()    */
-                    break;
-                case RFAL_NFCA_T2T:
-                    /* No specific activation needed for a T2T */
-                    platformLog(
-                        "NFC-A T2T device found \r\n"); /* NFC-A T2T device found, NFCID/UID is contained in: nfcaDev.nfcid */
-                    nfc->current = "NFC-A T2T";
-                    /* Following communications shall be performed using:
-                         *   - Non blocking: rfalStartTransceive() + rfalGetTransceiveState()
-                         *   -     Blocking: rfalTransceiveBlockingTx() + rfalTransceiveBlockingRx() or rfalTransceiveBlockingTxRx()    */
-                    break;
-                case RFAL_NFCA_T4T:
-                    platformLog(
-                        "NFC-A T4T (ISO-DEP) device found \r\n"); /* NFC-A T4T device found, NFCID/UID is contained in: nfcaDev.nfcid */
-                    nfc->current = "NFC-A T4T";
-                    /* Activation should continue using rfalIsoDepPollAHandleActivation(), see exampleRfalPoller.c */
-                    break;
-                case RFAL_NFCA_T4T_NFCDEP: /* Device supports T4T and NFC-DEP */
-                case RFAL_NFCA_NFCDEP: /* Device supports NFC-DEP */
-                    platformLog(
-                        "NFC-A P2P (NFC-DEP) device found \r\n"); /* NFC-A P2P device found, NFCID/UID is contained in: nfcaDev.nfcid */
-                    nfc->current = "NFC-A P2P";
-                    /* Activation should continue using rfalNfcDepInitiatorHandleActivation(), see exampleRfalPoller.c */
-                    break;
-                }
-                rfalNfcaPollerSleep(); /* Put device to sleep / HLTA (useless as the field will be turned off anyhow) */
-            }
+        if(!is_found) {
+            NfcMessage message;
+            message.type = NfcMessageTypeDeviceNotFound;
+            furi_check(
+                osMessageQueuePut(nfc_worker->message_queue, &message, 0, osWaitForever) == osOK);
         }
-        widget_update(nfc->widget);
+        platformDelay(333);
+    }
+}
+
+bool nfc_worker_nfca_poll(NfcWorker* nfc_worker) {
+    ReturnCode ret;
+    rfalNfcaSensRes sense_res;
+
+    rfalNfcaPollerInitialize();
+    rfalFieldOnAndStartGT();
+    ret = rfalNfcaPollerTechnologyDetection(RFAL_COMPLIANCE_MODE_NFC, &sense_res);
+    if(ret != ERR_NONE) {
+        return false;
     }
 
-    isr_enabled = false;
-    rfalFieldOff();
-    rfalLowPowerModeStart();
-    nfc->ret = ERR_NONE;
-    nfc->worker = NULL;
-    osThreadExit();
-}
+    uint8_t dev_cnt;
+    rfalNfcaListenDevice device;
+    ret = rfalNfcaPollerFullCollisionResolution(RFAL_COMPLIANCE_MODE_NFC, 1, &device, &dev_cnt);
+    if(ret != ERR_NONE) {
+        return false;
+    }
+
+    if(dev_cnt) {
+        rfalNfcaPollerSleep();
+        NfcMessage message;
+        message.type = NfcMessageTypeDeviceFound;
+        message.device.type = NfcDeviceTypeNfca;
+        message.device.nfca = device;
+        furi_check(
+            osMessageQueuePut(nfc_worker->message_queue, &message, 0, osWaitForever) == osOK);
+        return true;
+    }
+    return false;
+}
+
+bool nfc_worker_nfcb_poll(NfcWorker* nfc_worker) {
+    ReturnCode ret;
+
+    rfalNfcbPollerInitialize();
+    rfalFieldOnAndStartGT();
+
+    rfalNfcbSensbRes sensb_res;
+    uint8_t sensb_res_len;
+    ret = rfalNfcbPollerTechnologyDetection(RFAL_COMPLIANCE_MODE_NFC, &sensb_res, &sensb_res_len);
+    if(ret != ERR_NONE) {
+        return false;
+    }
+
+    uint8_t dev_cnt;
+    rfalNfcbListenDevice device;
+    ret = rfalNfcbPollerCollisionResolution(RFAL_COMPLIANCE_MODE_NFC, 1, &device, &dev_cnt);
+    if(ret != ERR_NONE) {
+        return false;
+    }
+
+    if(dev_cnt) {
+        rfalNfcbPollerSleep(device.sensbRes.nfcid0);
+        NfcMessage message;
+        message.type = NfcMessageTypeDeviceFound;
+        message.device.type = NfcDeviceTypeNfcb;
+        message.device.nfcb = device;
+        furi_check(
+            osMessageQueuePut(nfc_worker->message_queue, &message, 0, osWaitForever) == osOK);
+        return true;
+    }
+    return false;
+}
+
+bool nfc_worker_nfcf_poll(NfcWorker* nfc_worker) {
+    ReturnCode ret;
+
+    rfalNfcfPollerInitialize(RFAL_BR_212);
+    rfalFieldOnAndStartGT();
+
+    ret = rfalNfcfPollerCheckPresence();
+    if(ret != ERR_NONE) {
+        return false;
+    }
+
+    uint8_t dev_cnt;
+    rfalNfcfListenDevice device;
+    ret = rfalNfcfPollerCollisionResolution(RFAL_COMPLIANCE_MODE_NFC, 1, &device, &dev_cnt);
+    if(ret != ERR_NONE) {
+        return false;
+    }
+
+    if(dev_cnt) {
+        NfcMessage message;
+        message.type = NfcMessageTypeDeviceFound;
+        message.device.type = NfcDeviceTypeNfcf;
+        message.device.nfcf = device;
+        furi_check(
+            osMessageQueuePut(nfc_worker->message_queue, &message, 0, osWaitForever) == osOK);
+        return true;
+    }
+    return false;
+}
+
+bool nfc_worker_nfcv_poll(NfcWorker* nfc_worker) {
+    ReturnCode ret;
+    rfalNfcvInventoryRes invRes;
+
+    rfalNfcvPollerInitialize();
+    rfalFieldOnAndStartGT();
+
+    ret = rfalNfcvPollerCheckPresence(&invRes);
+    if(ret != ERR_NONE) {
+        return false;
+    }
+
+    uint8_t dev_cnt;
+    rfalNfcvListenDevice device;
+    ret = rfalNfcvPollerCollisionResolution(RFAL_COMPLIANCE_MODE_NFC, 1, &device, &dev_cnt);
+    if(ret != ERR_NONE) {
+        return false;
+    }
+
+    if(dev_cnt) {
+        rfalNfcvPollerSleep(RFAL_NFCV_REQ_FLAG_DEFAULT, device.InvRes.UID);
+        NfcMessage message;
+        message.type = NfcMessageTypeDeviceFound;
+        message.device.type = NfcDeviceTypeNfcv;
+        message.device.nfcv = device;
+        furi_check(
+            osMessageQueuePut(nfc_worker->message_queue, &message, 0, osWaitForever) == osOK);
+        return true;
+    }
+    return false;
+}
+
+void nfc_worker_state_callback(rfalNfcState st) {
+    (void)st;
+}
+
+ReturnCode nfc_worker_trx(
+    uint8_t* txBuf,
+    uint16_t txBufSize,
+    uint8_t** rxData,
+    uint16_t** rcvLen,
+    uint32_t fwt) {
+    ReturnCode err;
+
+    err = rfalNfcDataExchangeStart(txBuf, txBufSize, rxData, rcvLen, fwt);
+    if(err == ERR_NONE) {
+        do {
+            rfalNfcWorker();
+            err = rfalNfcDataExchangeGetStatus();
+        } while(err == ERR_BUSY);
+    }
+    return err;
+}
+
+void nfc_worker_exchange(NfcWorker* nfc_worker, rfalNfcDevice* nfc_device) {
+    ReturnCode err;
+    uint8_t* rxData;
+    uint16_t* rcvLen;
+    uint8_t txBuf[100];
+    uint16_t txLen;
+
+    do {
+        rfalNfcWorker();
+        switch(rfalNfcGetState()) {
+        case RFAL_NFC_STATE_ACTIVATED:
+            err = nfc_worker_trx(NULL, 0, &rxData, &rcvLen, 0);
+            break;
+        case RFAL_NFC_STATE_DATAEXCHANGE:
+        case RFAL_NFC_STATE_DATAEXCHANGE_DONE:
+            // Not supported
+            txBuf[0] = ((char)0x68);
+            txBuf[1] = ((char)0x00);
+            txLen = 2;
+            err = nfc_worker_trx(txBuf, txLen, &rxData, &rcvLen, RFAL_FWT_NONE);
+            break;
+        case RFAL_NFC_STATE_START_DISCOVERY:
+            return;
+        case RFAL_NFC_STATE_LISTEN_SLEEP:
+        default:
+            break;
+        }
+    } while((err == ERR_NONE) || (err == ERR_SLEEP_REQ));
+}
+
+void nfc_worker_emulate(NfcWorker* nfc_worker) {
+    rfalNfcDiscoverParam params;
+    params.compMode = RFAL_COMPLIANCE_MODE_NFC;
+    params.techs2Find = RFAL_NFC_LISTEN_TECH_A;
+    params.totalDuration = 1000U;
+    params.devLimit = 1;
+    params.wakeupEnabled = false;
+    params.wakeupConfigDefault = true;
+    params.nfcfBR = RFAL_BR_212;
+    params.ap2pBR = RFAL_BR_424;
+    params.maxBR = RFAL_BR_KEEP;
+    params.GBLen = RFAL_NFCDEP_GB_MAX_LEN;
+    params.notifyCb = nfc_worker_state_callback;
+
+    params.lmConfigPA.nfcidLen = RFAL_LM_NFCID_LEN_07;
+    params.lmConfigPA.nfcid[0] = 0x00;
+    params.lmConfigPA.nfcid[1] = 0x01;
+    params.lmConfigPA.nfcid[2] = 0x02;
+    params.lmConfigPA.nfcid[3] = 0x03;
+    params.lmConfigPA.nfcid[4] = 0x04;
+    params.lmConfigPA.nfcid[5] = 0x05;
+    params.lmConfigPA.nfcid[6] = 0x06;
+    params.lmConfigPA.SENS_RES[0] = 0x44;
+    params.lmConfigPA.SENS_RES[1] = 0x00;
+    params.lmConfigPA.SEL_RES = 0x00;
+
+    ReturnCode ret;
+    ret = rfalNfcDiscover(&params);
+    if(ret != ERR_NONE) {
+        asm("bkpt 1");
+        return;
+    }
+
+    rfalNfcDevice* nfc_device;
+    while(nfc_worker->state == NfcWorkerStateEmulate) {
+        rfalNfcWorker();
+        if(rfalNfcIsDevActivated(rfalNfcGetState())) {
+            rfalNfcGetActiveDevice(&nfc_device);
+            nfc_worker_exchange(nfc_worker, nfc_device);
+        }
+        osDelay(10);
+    }
+
+    rfalNfcDeactivate(false);
+}
+
+void nfc_worker_field(NfcWorker* nfc_worker) {
+    st25r3916TxRxOn();
+    while(nfc_worker->state == NfcWorkerStateField) {
+        osDelay(50);
+    }
+    st25r3916TxRxOff();
+}

+ 13 - 1
applications/nfc/nfc_worker.h

@@ -1,3 +1,15 @@
 #pragma once
 
-void nfc_worker_task(void* context);
+typedef struct NfcWorker NfcWorker;
+
+NfcWorker* nfc_worker_alloc(osMessageQueueId_t message_queue);
+
+NfcWorkerState nfc_worker_get_state(NfcWorker* nfc_worker);
+
+ReturnCode nfc_worker_get_error(NfcWorker* nfc_worker);
+
+void nfc_worker_free(NfcWorker* nfc_worker);
+
+void nfc_worker_start(NfcWorker* nfc_worker, NfcWorkerState state);
+
+void nfc_worker_stop(NfcWorker* nfc_worker);

+ 41 - 0
applications/nfc/nfc_worker_i.h

@@ -0,0 +1,41 @@
+#pragma once
+
+#include "nfc_types.h"
+#include "nfc_worker.h"
+
+#include <flipper_v2.h>
+#include <cmsis_os2.h>
+#include <stdbool.h>
+
+#include <rfal_analogConfig.h>
+#include <rfal_rf.h>
+#include <rfal_nfc.h>
+#include <rfal_nfca.h>
+#include <rfal_nfcb.h>
+#include <rfal_nfcf.h>
+#include <rfal_nfcv.h>
+#include <st25r3916.h>
+#include <st25r3916_irq.h>
+
+struct NfcWorker {
+    osMessageQueueId_t message_queue;
+    osThreadAttr_t thread_attr;
+    osThreadId_t thread;
+
+    NfcWorkerState state;
+    ReturnCode error;
+};
+
+void nfc_worker_change_state(NfcWorker* nfc_worker, NfcWorkerState state);
+
+void nfc_worker_task(void* context);
+
+void nfc_worker_poll(NfcWorker* nfc_worker);
+bool nfc_worker_nfca_poll(NfcWorker* nfc_worker);
+bool nfc_worker_nfcb_poll(NfcWorker* nfc_worker);
+bool nfc_worker_nfcf_poll(NfcWorker* nfc_worker);
+bool nfc_worker_nfcv_poll(NfcWorker* nfc_worker);
+
+void nfc_worker_emulate(NfcWorker* nfc_worker);
+
+void nfc_worker_field(NfcWorker* nfc_worker);

+ 4 - 2
firmware/targets/api-hal-include/api-hal-bt.h

@@ -16,8 +16,10 @@ void api_hal_bt_dump_state(string_t buffer);
 /* Get BT/BLE system component state */
 bool api_hal_bt_is_alive();
 
-/* Lock shared access to flash controller */
-void api_hal_bt_lock_flash();
+/* Lock shared access to flash controller
+ * @return true if lock was successful, false if not
+ */
+bool api_hal_bt_lock_flash();
 
 /* Unlock shared access to flash controller */
 void api_hal_bt_unlock_flash();

+ 1 - 8
firmware/targets/f4/Src/main.c

@@ -223,13 +223,6 @@ void SystemClock_Config(void)
     Error_Handler();
   }
   /* USER CODE BEGIN Smps */
-
-  if (!LL_RCC_LSE_IsReady()) {
-    LL_RCC_ForceBackupDomainReset();
-    LL_RCC_ReleaseBackupDomainReset();
-    NVIC_SystemReset();
-  }
-
   /* USER CODE END Smps */
   /** Enables the Clock Security System
   */
@@ -272,7 +265,7 @@ void Error_Handler(void)
 {
   /* USER CODE BEGIN Error_Handler_Debug */
   /* User can add his own implementation to report the HAL error return state */
-
+  asm("bkpt 1");
   /* USER CODE END Error_Handler_Debug */
 }
 

+ 24 - 18
firmware/targets/f4/api-hal/api-hal-bt.c

@@ -39,34 +39,40 @@ bool api_hal_bt_is_alive() {
 }
 
 bool api_hal_bt_wait_transition() {
-    if (APPE_Status() == BleGlueStatusUninitialized) {
-        return false;
-    }
-    while (APPE_Status() != BleGlueStatusStarted) {
-        osDelay(1);
+    uint8_t counter = 0;
+    while (APPE_Status() == BleGlueStatusStartup) {
+        osDelay(10);
+        counter++;
+        if (counter > 1000) {
+            return false;
+        }
     }
     return true;
 }
 
-void api_hal_bt_lock_flash() {
+bool api_hal_bt_lock_flash() {
     if (!api_hal_bt_wait_transition()) {
-        HAL_FLASH_Unlock();
-        return;
+        return false;
     }
-    while (HAL_HSEM_FastTake(CFG_HW_FLASH_SEMID) != HAL_OK) {
-        osDelay(1);
+    if (APPE_Status() == BleGlueStatusUninitialized) {
+        HAL_FLASH_Unlock();
+    } else {
+        while (HAL_HSEM_FastTake(CFG_HW_FLASH_SEMID) != HAL_OK) {
+            osDelay(1);
+        }
+        SHCI_C2_FLASH_EraseActivity(ERASE_ACTIVITY_ON);
+        HAL_FLASH_Unlock();
+        while(LL_FLASH_IsOperationSuspended()) {};
     }
-    SHCI_C2_FLASH_EraseActivity(ERASE_ACTIVITY_ON);
-    HAL_FLASH_Unlock();
-    while(LL_FLASH_IsOperationSuspended()) {};
+    return true;
 }
 
 void api_hal_bt_unlock_flash() {
-    if (!api_hal_bt_wait_transition()) {
+    if (APPE_Status() == BleGlueStatusUninitialized) {
+        HAL_FLASH_Lock();
+    } else {
+        SHCI_C2_FLASH_EraseActivity(ERASE_ACTIVITY_OFF);
         HAL_FLASH_Lock();
-        return;
+        HAL_HSEM_Release(CFG_HW_FLASH_SEMID, HSEM_CPU1_COREID);
     }
-    SHCI_C2_FLASH_EraseActivity(ERASE_ACTIVITY_OFF);
-    HAL_FLASH_Lock();
-    HAL_HSEM_Release(CFG_HW_FLASH_SEMID, HSEM_CPU1_COREID);
 }

+ 9 - 3
firmware/targets/f4/api-hal/api-hal-flash.c

@@ -3,7 +3,9 @@
 #include <stm32wbxx.h>
 
 bool api_hal_flash_erase(uint8_t page, uint8_t count) {
-    api_hal_bt_lock_flash();
+    if (!api_hal_bt_lock_flash()) {
+        return false;
+    }
     FLASH_EraseInitTypeDef erase;
     erase.TypeErase = FLASH_TYPEERASE_PAGES;
     erase.Page = page;
@@ -15,14 +17,18 @@ bool api_hal_flash_erase(uint8_t page, uint8_t count) {
 }
 
 bool api_hal_flash_write_dword(size_t address, uint64_t data) {
-    api_hal_bt_lock_flash();
+    if (!api_hal_bt_lock_flash()) {
+        return false;
+    }
     HAL_StatusTypeDef status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, address, data);
     api_hal_bt_unlock_flash();
     return status == HAL_OK;
 }
 
 bool api_hal_flash_write_row(size_t address, size_t source_address) {
-    api_hal_bt_lock_flash();
+    if (!api_hal_bt_lock_flash()) {
+        return false;
+    }
     HAL_StatusTypeDef status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_FAST, address, source_address);
     api_hal_bt_unlock_flash();
     return status == HAL_OK;

+ 3 - 2
firmware/targets/f4/ble-glue/app_ble.c

@@ -129,7 +129,7 @@ static void AdvUpdateProcess(void *argument);
 static void Adv_Update( void );
 
 
-void APP_BLE_Init() {
+bool APP_BLE_Init() {
   SHCI_C2_Ble_Init_Cmd_Packet_t ble_init_cmd_packet = {
     {{0,0,0}},                  /**< Header unused */
     {0,                         /** pBleBufferAddress not used */
@@ -158,7 +158,7 @@ void APP_BLE_Init() {
   HciUserEvtProcessId = osThreadNew(HciUserEvtProcess, NULL, &HciUserEvtProcess_attr);
   // Starts the BLE Stack on CPU2
   if (SHCI_C2_BLE_Init( &ble_init_cmd_packet ) != SHCI_Success) {
-    Error_Handler();
+    return false;
   }
   // Initialization of HCI & GATT & GAP layer
   Ble_Hci_Gap_Gatt_Init();
@@ -191,6 +191,7 @@ void APP_BLE_Init() {
   AdvIntervalMax = CFG_FAST_CONN_ADV_INTERVAL_MAX;
 
   Adv_Request(APP_BLE_FAST_ADV);
+  return true;
 }
 
 SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification( void *pckt )

+ 2 - 1
firmware/targets/f4/ble-glue/app_ble.h

@@ -4,6 +4,7 @@
 extern "C" {
 #endif
 
+#include <stdbool.h>
 #include "hci_tl.h"
 
 typedef enum {
@@ -16,7 +17,7 @@ typedef enum {
     APP_BLE_CONNECTED_CLIENT
 } APP_BLE_ConnStatus_t;
 
-void APP_BLE_Init();
+bool APP_BLE_Init();
 
 APP_BLE_ConnStatus_t APP_BLE_Get_Server_Connection_Status();
 

+ 6 - 2
firmware/targets/f4/ble-glue/app_entry.c

@@ -136,8 +136,12 @@ static void APPE_SysUserEvtRx( void * pPayload ) {
   UNUSED(pPayload);
   /* Traces channel initialization */
   // APPD_EnableCPU2( );
-  ble_glue_status = BleGlueStatusStarted;
-  APP_BLE_Init( );
+  
+  if (APP_BLE_Init()) {
+    ble_glue_status = BleGlueStatusStarted;
+  } else {
+    ble_glue_status = BleGlueStatusBroken;
+  }
 }
 
 /*************************************************************

+ 1 - 0
firmware/targets/f4/ble-glue/app_entry.h

@@ -7,6 +7,7 @@ extern "C" {
 typedef enum {
     BleGlueStatusUninitialized,
     BleGlueStatusStartup,
+    BleGlueStatusBroken,
     BleGlueStatusStarted
 } BleGlueStatus;
 

+ 34 - 8
lib/ST25RFAL002/platform.c

@@ -1,7 +1,34 @@
 #include "platform.h"
 #include <assert.h>
 #include <main.h>
-#include <spi.h>
+#include <api-hal-spi.h>
+
+static osThreadAttr_t platform_irq_thread_attr;
+static volatile osThreadId_t platform_irq_thread_id = NULL;
+static volatile PlatformIrqCallback platform_irq_callback = NULL;
+
+void nfc_isr() {
+    if(platform_irq_callback && platformGpioIsHigh( ST25R_INT_PORT, ST25R_INT_PIN )) {
+        osThreadFlagsSet(platform_irq_thread_id, 0x1);
+    }
+}
+
+void platformIrqWorker() {
+    while(1) {
+        uint32_t flags = osThreadFlagsWait(0x1, osFlagsWaitAny, osWaitForever);
+        if (flags & 0x1) {
+            platform_irq_callback();
+        }
+    }
+}
+
+void platformSetIrqCallback(PlatformIrqCallback callback) {
+    platform_irq_callback = callback;
+    platform_irq_thread_attr.name = "rfal_irq_worker";
+    platform_irq_thread_attr.stack_size = 512;
+    platform_irq_thread_attr.priority = osPriorityISR;
+    platform_irq_thread_id = osThreadNew(platformIrqWorker, NULL, &platform_irq_thread_attr);
+}
 
 HAL_StatusTypeDef platformSpiTxRx(const uint8_t *txBuf, uint8_t *rxBuf, uint16_t len) {
     HAL_StatusTypeDef ret;
@@ -14,18 +41,17 @@ HAL_StatusTypeDef platformSpiTxRx(const uint8_t *txBuf, uint8_t *rxBuf, uint16_t
     }
     
     if(ret != HAL_OK) {
-        exit(250);
+        asm("bkpt 1");
+        exit(255);
     }
     return ret;
 }
 
-
-void platformProtectST25RComm()
-{
+void platformProtectST25RComm() {
+    api_hal_spi_lock(&SPI_R);
     NFC_SPI_Reconfigure();
 }
 
-void platformUnprotectST25RComm()
-{
-    
+void platformUnprotectST25RComm() {
+    api_hal_spi_unlock(&SPI_R);
 }

+ 7 - 3
lib/ST25RFAL002/platform.h

@@ -11,6 +11,9 @@
 #include "spi.h"
 #include "main.h"
 
+typedef void (*PlatformIrqCallback)();
+void platformSetIrqCallback(PlatformIrqCallback cb);
+
 HAL_StatusTypeDef platformSpiTxRx(const uint8_t *txBuf, uint8_t *rxBuf, uint16_t len);
 void platformProtectST25RComm();
 void platformUnprotectST25RComm();
@@ -21,8 +24,8 @@ void platformUnprotectST25RComm();
 #define ST25R_INT_PIN NFC_IRQ_Pin
 #define ST25R_INT_PORT NFC_IRQ_GPIO_Port
 
-#define PLATFORM_LED_RX_PIN LED_GREEN_Pin
-#define PLATFORM_LED_RX_PORT LED_GREEN_GPIO_Port
+#define PLATFORM_LED_RX_PIN LED_RED_Pin
+#define PLATFORM_LED_RX_PORT LED_RED_GPIO_Port
 #define PLATFORM_LED_FIELD_PIN LED_BLUE_Pin
 #define PLATFORM_LED_FIELD_PORT LED_BLUE_GPIO_Port
 
@@ -52,6 +55,7 @@ void platformUnprotectST25RComm();
 #define RFAL_FEATURE_ISO_DEP_APDU_MAX_LEN      512U       /*!< ISO-DEP APDU max length.                                                  */
 #define RFAL_FEATURE_NFC_DEP_PDU_MAX_LEN       512U       /*!< NFC-DEP PDU max length.                                                   */
 
+#define platformIrqST25RSetCallback( cb )           platformSetIrqCallback(cb)
 
 #define platformProtectST25RIrqStatus()               platformProtectST25RComm()                               /*!< Protect unique access to IRQ status var - IRQ disable on single thread environment (MCU) ; Mutex lock on a multi thread environment */
 #define platformUnprotectST25RIrqStatus()             platformUnprotectST25RComm()                             /*!< Unprotect the IRQ status var - IRQ enable on a single thread environment (MCU) ; Mutex unlock on a multi thread environment         */
@@ -72,7 +76,7 @@ void platformUnprotectST25RComm();
 #define platformGetSysTick()                          osKernelGetTickCount()                                    /*!< Get System Tick (1 tick = 1 ms)             */
 
 #define platformAssert( exp )                         assert_param( exp )                                      /*!< Asserts whether the given expression is true*/
-#define platformErrorHandle()                         Error_Handler()                                           /*!< Global error handle\trap                    */
+// #define platformErrorHandle()                         Error_Handler()                                           /*!< Global error handle\trap                    */
 
 #define platformSpiSelect()                           platformGpioClear( ST25R_SS_PORT, ST25R_SS_PIN )         /*!< SPI SS\CS: Chip|Slave Select                */
 #define platformSpiDeselect()                         platformGpioSet( ST25R_SS_PORT, ST25R_SS_PIN )           /*!< SPI SS\CS: Chip|Slave Deselect              */