MX 2 лет назад
Родитель
Сommit
e56aa569e3
32 измененных файлов с 1438 добавлено и 1389 удалено
  1. 5 6
      apps_broken_by_last_refactors/seader/ccid.c
  2. 1 1
      apps_broken_by_last_refactors/seader/ccid.h
  3. 162 0
      apps_broken_by_last_refactors/seader/protocol/picopass_poller.c
  4. 59 0
      apps_broken_by_last_refactors/seader/protocol/picopass_poller.h
  5. 107 0
      apps_broken_by_last_refactors/seader/protocol/picopass_poller_i.c
  6. 56 0
      apps_broken_by_last_refactors/seader/protocol/picopass_poller_i.h
  7. 46 0
      apps_broken_by_last_refactors/seader/protocol/picopass_protocol.h
  8. 64 0
      apps_broken_by_last_refactors/seader/protocol/rfal_picopass.h
  9. 9 0
      apps_broken_by_last_refactors/seader/readme.md
  10. 0 88
      apps_broken_by_last_refactors/seader/rfal_picopass.c
  11. 0 32
      apps_broken_by_last_refactors/seader/rfal_picopass.h
  12. 682 0
      apps_broken_by_last_refactors/seader/sam_api.c
  13. 24 0
      apps_broken_by_last_refactors/seader/sam_api.h
  14. 1 2
      apps_broken_by_last_refactors/seader/scenes/seader_scene_card_menu.c
  15. 0 1
      apps_broken_by_last_refactors/seader/scenes/seader_scene_config.h
  16. 15 15
      apps_broken_by_last_refactors/seader/scenes/seader_scene_read_14a.c
  17. 13 15
      apps_broken_by_last_refactors/seader/scenes/seader_scene_read_picopass.c
  18. 6 20
      apps_broken_by_last_refactors/seader/scenes/seader_scene_sam_present.c
  19. 2 2
      apps_broken_by_last_refactors/seader/scenes/seader_scene_save_name.c
  20. 2 3
      apps_broken_by_last_refactors/seader/scenes/seader_scene_start.c
  21. 0 101
      apps_broken_by_last_refactors/seader/scenes/seader_scene_uart.c
  22. 11 9
      apps_broken_by_last_refactors/seader/seader.c
  23. 1 0
      apps_broken_by_last_refactors/seader/seader.h
  24. 1 1
      apps_broken_by_last_refactors/seader/seader_credential.c
  25. 0 5
      apps_broken_by_last_refactors/seader/seader_custom_event.h
  26. 21 4
      apps_broken_by_last_refactors/seader/seader_i.h
  27. 116 894
      apps_broken_by_last_refactors/seader/seader_worker.c
  28. 18 4
      apps_broken_by_last_refactors/seader/seader_worker.h
  29. 12 5
      apps_broken_by_last_refactors/seader/seader_worker_i.h
  30. 4 5
      apps_broken_by_last_refactors/seader/uart.c
  31. 0 152
      apps_broken_by_last_refactors/seader/views/seader_uart_view.c
  32. 0 24
      apps_broken_by_last_refactors/seader/views/seader_uart_view.h

+ 5 - 6
apps_broken_by_last_refactors/seader/ccid.c

@@ -130,7 +130,8 @@ void seader_ccid_XfrBlockToSlot(
     furi_thread_flags_set(furi_thread_get_id(seader_uart->tx_thread), WorkerEvtSamRx);
 }
 
-size_t seader_ccid_process(SeaderWorker* seader_worker, uint8_t* cmd, size_t cmd_len) {
+size_t seader_ccid_process(Seader* seader, uint8_t* cmd, size_t cmd_len) {
+    SeaderWorker* seader_worker = seader->worker;
     SeaderUartBridge* seader_uart = seader_worker->uart;
     CCID_Message message;
     message.consumed = 0;
@@ -165,8 +166,6 @@ size_t seader_ccid_process(SeaderWorker* seader_worker, uint8_t* cmd, size_t cmd
                     retries = 3;
                 }
                 break;
-            default:
-                FURI_LOG_D(TAG, "Unknown slot 0 card event");
             };
 
             switch(cmd[1] & SLOT_1_MASK) {
@@ -191,8 +190,6 @@ size_t seader_ccid_process(SeaderWorker* seader_worker, uint8_t* cmd, size_t cmd
                     retries = 3;
                 }
                 break;
-            default:
-                FURI_LOG_D(TAG, "Unknown slot 1 card event");
             };
 
             return 2;
@@ -234,6 +231,7 @@ size_t seader_ccid_process(SeaderWorker* seader_worker, uint8_t* cmd, size_t cmd
         }
         message.consumed += 2 + 10 + message.dwLength + 1;
 
+        /*
         if(message.dwLength == 0) {
             FURI_LOG_D(
                 TAG,
@@ -252,6 +250,7 @@ size_t seader_ccid_process(SeaderWorker* seader_worker, uint8_t* cmd, size_t cmd
                 message.dwLength,
                 display);
         }
+        */
 
         //0306 81 00000000 0000 0200 01 87
         //0306 81 00000000 0000 0100 01 84
@@ -302,7 +301,7 @@ size_t seader_ccid_process(SeaderWorker* seader_worker, uint8_t* cmd, size_t cmd
         if(message.bMessageType == CCID_MESSAGE_TYPE_RDR_to_PC_DataBlock) {
             if(hasSAM) {
                 if(message.bSlot == sam_slot) {
-                    seader_worker_process_sam_message(seader_worker, &message);
+                    seader_worker_process_sam_message(seader, &message);
                 } else {
                     FURI_LOG_D(TAG, "Discarding message on non-sam slot");
                 }

+ 1 - 1
apps_broken_by_last_refactors/seader/ccid.h

@@ -103,4 +103,4 @@ void seader_ccid_XfrBlockToSlot(
     uint8_t slot,
     uint8_t* data,
     size_t len);
-size_t seader_ccid_process(SeaderWorker* seader_worker, uint8_t* cmd, size_t cmd_len);
+size_t seader_ccid_process(Seader* seader, uint8_t* cmd, size_t cmd_len);

+ 162 - 0
apps_broken_by_last_refactors/seader/protocol/picopass_poller.c

@@ -0,0 +1,162 @@
+#include "picopass_poller_i.h"
+
+#include <furi/furi.h>
+
+#define TAG "Picopass"
+
+typedef NfcCommand (*PicopassPollerStateHandler)(PicopassPoller* instance);
+
+static void picopass_poller_reset(PicopassPoller* instance) {
+    UNUSED(instance);
+}
+
+NfcCommand picopass_poller_request_mode_handler(PicopassPoller* instance) {
+    NfcCommand command = NfcCommandContinue;
+
+    instance->event.type = PicopassPollerEventTypeRequestMode;
+    command = instance->callback(instance->event, instance->context);
+    instance->mode = instance->event_data.req_mode.mode;
+    instance->state = PicopassPollerStateDetect;
+
+    return command;
+}
+
+NfcCommand picopass_poller_detect_handler(PicopassPoller* instance) {
+    NfcCommand command = NfcCommandContinue;
+    PicopassError error = picopass_poller_actall(instance);
+
+    if(error == PicopassErrorNone) {
+        instance->state = PicopassPollerStateSelect;
+        instance->event.type = PicopassPollerEventTypeCardDetected;
+        command = instance->callback(instance->event, instance->context);
+    } else {
+        furi_delay_ms(100);
+    }
+
+    return command;
+}
+
+NfcCommand picopass_poller_select_handler(PicopassPoller* instance) {
+    NfcCommand command = NfcCommandContinue;
+
+    do {
+        PicopassError error = picopass_poller_identify(instance, &instance->col_res_serial_num);
+        if(error != PicopassErrorNone) {
+            instance->state = PicopassPollerStateFail;
+            break;
+        }
+
+        error =
+            picopass_poller_select(instance, &instance->col_res_serial_num, &instance->serial_num);
+        if(error != PicopassErrorNone) {
+            instance->state = PicopassPollerStateFail;
+            break;
+        }
+
+        instance->state = PicopassPollerStateSuccess;
+    } while(false);
+
+    return command;
+}
+
+NfcCommand picopass_poller_success_handler(PicopassPoller* instance) {
+    NfcCommand command = NfcCommandContinue;
+
+    instance->event.type = PicopassPollerEventTypeSuccess;
+    command = instance->callback(instance->event, instance->context);
+    furi_delay_ms(100);
+
+    return command;
+}
+
+NfcCommand picopass_poller_fail_handler(PicopassPoller* instance) {
+    NfcCommand command = NfcCommandReset;
+
+    instance->event.type = PicopassPollerEventTypeFail;
+    command = instance->callback(instance->event, instance->context);
+    picopass_poller_reset(instance);
+    instance->state = PicopassPollerStateDetect;
+
+    return command;
+}
+
+static const PicopassPollerStateHandler picopass_poller_state_handler[PicopassPollerStateNum] = {
+    [PicopassPollerStateRequestMode] = picopass_poller_request_mode_handler,
+    [PicopassPollerStateDetect] = picopass_poller_detect_handler,
+    [PicopassPollerStateSelect] = picopass_poller_select_handler,
+    [PicopassPollerStateSuccess] = picopass_poller_success_handler,
+    [PicopassPollerStateFail] = picopass_poller_fail_handler,
+};
+
+static NfcCommand picopass_poller_callback(NfcEvent event, void* context) {
+    furi_assert(context);
+
+    PicopassPoller* instance = context;
+    NfcCommand command = NfcCommandContinue;
+
+    if(event.type == NfcEventTypePollerReady) {
+        command = picopass_poller_state_handler[instance->state](instance);
+    }
+
+    if(instance->session_state == PicopassPollerSessionStateStopRequest) {
+        command = NfcCommandStop;
+    }
+
+    return command;
+}
+
+void picopass_poller_start(
+    PicopassPoller* instance,
+    PicopassPollerCallback callback,
+    void* context) {
+    furi_assert(instance);
+    furi_assert(instance->session_state == PicopassPollerSessionStateIdle);
+
+    instance->callback = callback;
+    instance->context = context;
+
+    instance->session_state = PicopassPollerSessionStateActive;
+    nfc_start(instance->nfc, picopass_poller_callback, instance);
+}
+
+void picopass_poller_stop(PicopassPoller* instance) {
+    furi_assert(instance);
+
+    instance->session_state = PicopassPollerSessionStateStopRequest;
+    nfc_stop(instance->nfc);
+    instance->session_state = PicopassPollerSessionStateIdle;
+}
+
+PicopassPoller* picopass_poller_alloc(Nfc* nfc) {
+    furi_assert(nfc);
+
+    PicopassPoller* instance = malloc(sizeof(PicopassPoller));
+    instance->nfc = nfc;
+    nfc_config(instance->nfc, NfcModePoller, NfcTechIso15693);
+    nfc_set_guard_time_us(instance->nfc, 10000);
+    nfc_set_fdt_poll_fc(instance->nfc, 5000);
+    nfc_set_fdt_poll_poll_us(instance->nfc, 1000);
+
+    instance->event.data = &instance->event_data;
+
+    instance->tx_buffer = bit_buffer_alloc(PICOPASS_POLLER_BUFFER_SIZE);
+    instance->rx_buffer = bit_buffer_alloc(PICOPASS_POLLER_BUFFER_SIZE);
+    instance->tmp_buffer = bit_buffer_alloc(PICOPASS_POLLER_BUFFER_SIZE);
+
+    return instance;
+}
+
+void picopass_poller_free(PicopassPoller* instance) {
+    furi_assert(instance);
+
+    bit_buffer_free(instance->tx_buffer);
+    bit_buffer_free(instance->rx_buffer);
+    bit_buffer_free(instance->tmp_buffer);
+    free(instance);
+}
+
+uint8_t* picopass_poller_get_csn(PicopassPoller* instance) {
+    furi_assert(instance);
+
+    return instance->serial_num.data;
+}

+ 59 - 0
apps_broken_by_last_refactors/seader/protocol/picopass_poller.h

@@ -0,0 +1,59 @@
+#pragma once
+
+#include <nfc/nfc.h>
+#include "picopass_protocol.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+    PicopassPollerEventTypeRequestMode,
+    PicopassPollerEventTypeCardDetected,
+    PicopassPollerEventTypeCardLost,
+    PicopassPollerEventTypeSuccess,
+    PicopassPollerEventTypeFail,
+} PicopassPollerEventType;
+
+typedef enum {
+    PicopassPollerModeRead,
+} PicopassPollerMode;
+
+typedef struct {
+    PicopassPollerMode mode;
+} PicopassPollerEventDataRequestMode;
+
+typedef union {
+    PicopassPollerEventDataRequestMode req_mode;
+} PicopassPollerEventData;
+
+typedef struct {
+    PicopassPollerEventType type;
+    PicopassPollerEventData* data;
+} PicopassPollerEvent;
+
+typedef NfcCommand (*PicopassPollerCallback)(PicopassPollerEvent event, void* context);
+
+typedef struct PicopassPoller PicopassPoller;
+
+PicopassPoller* picopass_poller_alloc(Nfc* nfc);
+
+void picopass_poller_free(PicopassPoller* instance);
+
+void picopass_poller_start(
+    PicopassPoller* instance,
+    PicopassPollerCallback callback,
+    void* context);
+
+void picopass_poller_stop(PicopassPoller* instance);
+uint8_t* picopass_poller_get_csn(PicopassPoller* instance);
+
+PicopassError picopass_poller_send_frame(
+    PicopassPoller* instance,
+    BitBuffer* tx_buffer,
+    BitBuffer* rx_buffer,
+    uint32_t fwt_fc);
+
+#ifdef __cplusplus
+}
+#endif

+ 107 - 0
apps_broken_by_last_refactors/seader/protocol/picopass_poller_i.c

@@ -0,0 +1,107 @@
+#include "picopass_poller_i.h"
+
+#include <nfc/helpers/iso14443_crc.h>
+
+#define PICOPASS_POLLER_FWT_FC (100000)
+
+#define TAG "Picopass"
+
+static PicopassError picopass_poller_process_error(NfcError error) {
+    PicopassError ret = PicopassErrorNone;
+
+    switch(error) {
+    case NfcErrorNone:
+        ret = PicopassErrorNone;
+        break;
+
+    default:
+        ret = PicopassErrorTimeout;
+        break;
+    }
+
+    return ret;
+}
+
+PicopassError picopass_poller_send_frame(
+    PicopassPoller* instance,
+    BitBuffer* tx_buffer,
+    BitBuffer* rx_buffer,
+    uint32_t fwt_fc) {
+    PicopassError ret = PicopassErrorNone;
+
+    do {
+        NfcError error = nfc_poller_trx(instance->nfc, tx_buffer, rx_buffer, fwt_fc);
+        if(error != NfcErrorNone) {
+            ret = picopass_poller_process_error(error);
+            break;
+        }
+        if(!iso13239_crc_check(Iso13239CrcTypePicopass, rx_buffer)) {
+            ret = PicopassErrorIncorrectCrc;
+            break;
+        }
+        iso13239_crc_trim(instance->rx_buffer);
+    } while(false);
+
+    return ret;
+}
+
+PicopassError picopass_poller_actall(PicopassPoller* instance) {
+    PicopassError ret = PicopassErrorNone;
+
+    bit_buffer_reset(instance->tx_buffer);
+    bit_buffer_append_byte(instance->tx_buffer, RFAL_PICOPASS_CMD_ACTALL);
+
+    NfcError error = nfc_poller_trx(
+        instance->nfc, instance->tx_buffer, instance->rx_buffer, PICOPASS_POLLER_FWT_FC);
+    if(error != NfcErrorIncompleteFrame) {
+        ret = picopass_poller_process_error(error);
+    }
+
+    return ret;
+}
+
+PicopassError picopass_poller_identify(
+    PicopassPoller* instance,
+    PicopassColResSerialNum* col_res_serial_num) {
+    PicopassError ret = PicopassErrorNone;
+
+    do {
+        bit_buffer_reset(instance->tx_buffer);
+        bit_buffer_append_byte(instance->tx_buffer, RFAL_PICOPASS_CMD_READ_OR_IDENTIFY);
+        ret = picopass_poller_send_frame(
+            instance, instance->tx_buffer, instance->rx_buffer, PICOPASS_POLLER_FWT_FC);
+        if(ret != PicopassErrorNone) break;
+        if(bit_buffer_get_size_bytes(instance->rx_buffer) != sizeof(PicopassColResSerialNum)) {
+            ret = PicopassErrorProtocol;
+            break;
+        }
+        bit_buffer_write_bytes(
+            instance->rx_buffer, col_res_serial_num->data, sizeof(PicopassColResSerialNum));
+    } while(false);
+
+    return ret;
+}
+
+PicopassError picopass_poller_select(
+    PicopassPoller* instance,
+    PicopassColResSerialNum* col_res_serial_num,
+    PicopassSerialNum* serial_num) {
+    PicopassError ret = PicopassErrorNone;
+
+    do {
+        bit_buffer_reset(instance->tx_buffer);
+        bit_buffer_append_byte(instance->tx_buffer, RFAL_PICOPASS_CMD_SELECT);
+        bit_buffer_append_bytes(
+            instance->tx_buffer, col_res_serial_num->data, sizeof(PicopassColResSerialNum));
+        ret = picopass_poller_send_frame(
+            instance, instance->tx_buffer, instance->rx_buffer, PICOPASS_POLLER_FWT_FC);
+        if(ret != PicopassErrorNone) break;
+        if(bit_buffer_get_size_bytes(instance->rx_buffer) != sizeof(PicopassSerialNum)) {
+            ret = PicopassErrorProtocol;
+            break;
+        }
+        bit_buffer_write_bytes(instance->rx_buffer, serial_num->data, sizeof(PicopassSerialNum));
+    } while(false);
+
+    return ret;
+}

+ 56 - 0
apps_broken_by_last_refactors/seader/protocol/picopass_poller_i.h

@@ -0,0 +1,56 @@
+#pragma once
+
+#include "rfal_picopass.h"
+#include "picopass_poller.h"
+#include "picopass_protocol.h"
+
+#include <nfc/helpers/iso13239_crc.h>
+
+#define PICOPASS_POLLER_BUFFER_SIZE (255)
+#define PICOPASS_CRC_SIZE (2)
+
+typedef enum {
+    PicopassPollerSessionStateIdle,
+    PicopassPollerSessionStateActive,
+    PicopassPollerSessionStateStopRequest,
+} PicopassPollerSessionState;
+
+typedef enum {
+    PicopassPollerStateRequestMode,
+    PicopassPollerStateDetect,
+    PicopassPollerStateSelect,
+    PicopassPollerStateSuccess,
+    PicopassPollerStateFail,
+
+    PicopassPollerStateNum,
+} PicopassPollerState;
+
+struct PicopassPoller {
+    Nfc* nfc;
+    PicopassPollerSessionState session_state;
+    PicopassPollerState state;
+    PicopassPollerMode mode;
+
+    PicopassColResSerialNum col_res_serial_num;
+    PicopassSerialNum serial_num;
+    PicopassMac mac;
+
+    BitBuffer* tx_buffer;
+    BitBuffer* rx_buffer;
+    BitBuffer* tmp_buffer;
+
+    PicopassPollerEvent event;
+    PicopassPollerEventData event_data;
+    PicopassPollerCallback callback;
+    void* context;
+};
+
+PicopassError picopass_poller_actall(PicopassPoller* instance);
+
+PicopassError
+    picopass_poller_identify(PicopassPoller* instance, PicopassColResSerialNum* col_res_serial_num);
+
+PicopassError picopass_poller_select(
+    PicopassPoller* instance,
+    PicopassColResSerialNum* col_res_serial_num,
+    PicopassSerialNum* serial_num);

+ 46 - 0
apps_broken_by_last_refactors/seader/protocol/picopass_protocol.h

@@ -0,0 +1,46 @@
+#pragma once
+
+#define PICOPASS_BLOCK_LEN 8
+#define PICOPASS_MAX_APP_LIMIT 32
+#define PICOPASS_UID_LEN 8
+#define PICOPASS_READ_CHECK_RESP_LEN 8
+#define PICOPASS_CHECK_RESP_LEN 4
+#define PICOPASS_MAC_LEN 4
+#define PICOPASS_KEY_LEN 8
+
+#define PICOPASS_FDT_LISTEN_FC (1000)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+    PicopassErrorNone,
+    PicopassErrorTimeout,
+    PicopassErrorIncorrectCrc,
+    PicopassErrorProtocol,
+} PicopassError;
+
+typedef struct {
+    uint8_t data[PICOPASS_UID_LEN];
+} PicopassColResSerialNum;
+
+typedef struct {
+    uint8_t data[PICOPASS_UID_LEN];
+} PicopassSerialNum;
+
+typedef struct {
+    uint8_t data[PICOPASS_READ_CHECK_RESP_LEN];
+} PicopassReadCheckResp;
+
+typedef struct {
+    uint8_t data[PICOPASS_CHECK_RESP_LEN];
+} PicopassCheckResp;
+
+typedef struct {
+    uint8_t data[PICOPASS_MAC_LEN];
+} PicopassMac;
+
+#ifdef __cplusplus
+}
+#endif

+ 64 - 0
apps_broken_by_last_refactors/seader/protocol/rfal_picopass.h

@@ -0,0 +1,64 @@
+#pragma once
+
+#include <furi_hal_nfc.h>
+
+#define RFAL_PICOPASS_UID_LEN 8
+#define PICOPASS_BLOCK_LEN 8
+
+enum {
+    // PicoPass command bytes:
+    // Low nibble used for command
+    // High nibble used for options and checksum (MSB)
+    // The only option we care about in 15693 mode is the key
+    // which is only used by READCHECK, so for simplicity we
+    // don't bother breaking down the command and flags into parts
+
+    // READ: ADDRESS(1) CRC16(2) -> DATA(8) CRC16(2)
+    // IDENTIFY: No args -> ASNB(8) CRC16(2)
+    RFAL_PICOPASS_CMD_READ_OR_IDENTIFY = 0x0C,
+    // ADDRESS(1) CRC16(2) -> DATA(32) CRC16(2)
+    RFAL_PICOPASS_CMD_READ4 = 0x06,
+    // ADDRESS(1) DATA(8) SIGN(4)|CRC16(2) -> DATA(8) CRC16(2)
+    RFAL_PICOPASS_CMD_UPDATE = 0x87,
+    // ADDRESS(1) -> DATA(8)
+    RFAL_PICOPASS_CMD_READCHECK_KD = 0x88,
+    // ADDRESS(1) -> DATA(8)
+    RFAL_PICOPASS_CMD_READCHECK_KC = 0x18,
+    // CHALLENGE(4) READERSIGNATURE(4) -> CHIPRESPONSE(4)
+    RFAL_PICOPASS_CMD_CHECK = 0x05,
+    // No args -> SOF
+    RFAL_PICOPASS_CMD_ACTALL = 0x0A,
+    // No args -> SOF
+    RFAL_PICOPASS_CMD_ACT = 0x8E,
+    // ASNB(8)|SERIALNB(8) -> SERIALNB(8) CRC16(2)
+    RFAL_PICOPASS_CMD_SELECT = 0x81,
+    // No args -> SERIALNB(8) CRC16(2)
+    RFAL_PICOPASS_CMD_DETECT = 0x0F,
+    // No args -> SOF
+    RFAL_PICOPASS_CMD_HALT = 0x00,
+    // PAGE(1) CRC16(2) -> BLOCK1(8) CRC16(2)
+    RFAL_PICOPASS_CMD_PAGESEL = 0x84,
+};
+
+typedef struct {
+    uint8_t CSN[RFAL_PICOPASS_UID_LEN]; // Anti-collision CSN
+    uint8_t crc[2];
+} rfalPicoPassIdentifyRes;
+
+typedef struct {
+    uint8_t CSN[RFAL_PICOPASS_UID_LEN]; // Real CSN
+    uint8_t crc[2];
+} rfalPicoPassSelectRes;
+
+typedef struct {
+    uint8_t CCNR[8];
+} rfalPicoPassReadCheckRes;
+
+typedef struct {
+    uint8_t mac[4];
+} rfalPicoPassCheckRes;
+
+typedef struct {
+    uint8_t data[PICOPASS_BLOCK_LEN];
+    uint8_t crc[2];
+} rfalPicoPassReadBlockRes;

+ 9 - 0
apps_broken_by_last_refactors/seader/readme.md

@@ -22,6 +22,15 @@ Put SAM ([USA](https://www.cdw.com/product/hp-sim-for-hid-iclass-for-hip2-reader
 
 Optionally 3d print a [case designed by sean](https://www.printables.com/model/543149-case-for-flipper-zero-devboard-smart2click-samsim)
 
+#### Connections
+
+| Smart Card 2 Click | Flipper     |
+| ------------------ | ----------- |
+| 5v                 | 1           |
+| GND                | 8 / 11 / 18 |
+| TX                 | 16          |
+| RX                 | 15          |
+
 ## Development
 
 ### To Build App

+ 0 - 88
apps_broken_by_last_refactors/seader/rfal_picopass.c

@@ -1,88 +0,0 @@
-#include "seader_i.h"
-
-#define RFAL_PICOPASS_TXRX_FLAGS                                                    \
-    (FURI_HAL_NFC_LL_TXRX_FLAGS_CRC_TX_MANUAL | FURI_HAL_NFC_LL_TXRX_FLAGS_AGC_ON | \
-     FURI_HAL_NFC_LL_TXRX_FLAGS_PAR_RX_REMV | FURI_HAL_NFC_LL_TXRX_FLAGS_CRC_RX_KEEP)
-
-#define TAG "RFAL_PICOPASS"
-
-typedef struct {
-    uint8_t CMD;
-    uint8_t CSN[RFAL_PICOPASS_UID_LEN];
-} rfalPicoPassSelectReq;
-
-FuriHalNfcReturn rfalPicoPassPollerInitialize(void) {
-    FuriHalNfcReturn ret;
-
-    ret = furi_hal_nfc_ll_set_mode(
-        FuriHalNfcModePollPicopass, FuriHalNfcBitrate26p48, FuriHalNfcBitrate26p48);
-    if(ret != FuriHalNfcReturnOk) {
-        return ret;
-    };
-
-    furi_hal_nfc_ll_set_error_handling(FuriHalNfcErrorHandlingNfc);
-    furi_hal_nfc_ll_set_guard_time(FURI_HAL_NFC_LL_GT_PICOPASS);
-    furi_hal_nfc_ll_set_fdt_listen(FURI_HAL_NFC_LL_FDT_LISTEN_PICOPASS_POLLER);
-    furi_hal_nfc_ll_set_fdt_poll(FURI_HAL_NFC_LL_FDT_POLL_PICOPASS_POLLER);
-
-    return FuriHalNfcReturnOk;
-}
-
-FuriHalNfcReturn rfalPicoPassPollerCheckPresence(void) {
-    FuriHalNfcReturn ret;
-    uint8_t txBuf[1] = {RFAL_PICOPASS_CMD_ACTALL};
-    uint8_t rxBuf[32] = {0};
-    uint16_t recvLen = 0;
-    uint32_t flags = RFAL_PICOPASS_TXRX_FLAGS;
-    uint32_t fwt = furi_hal_nfc_ll_ms2fc(20);
-
-    ret = furi_hal_nfc_ll_txrx(txBuf, 1, rxBuf, 32, &recvLen, flags, fwt);
-    return ret;
-}
-
-FuriHalNfcReturn rfalPicoPassPollerIdentify(rfalPicoPassIdentifyRes* idRes) {
-    FuriHalNfcReturn ret;
-
-    uint8_t txBuf[1] = {RFAL_PICOPASS_CMD_IDENTIFY};
-    uint16_t recvLen = 0;
-    uint32_t flags = RFAL_PICOPASS_TXRX_FLAGS;
-    uint32_t fwt = furi_hal_nfc_ll_ms2fc(20);
-
-    ret = furi_hal_nfc_ll_txrx(
-        txBuf,
-        sizeof(txBuf),
-        (uint8_t*)idRes,
-        sizeof(rfalPicoPassIdentifyRes),
-        &recvLen,
-        flags,
-        fwt);
-    // printf("identify rx: %d %s\n", recvLen, hex2Str(idRes->CSN, RFAL_PICOPASS_UID_LEN));
-
-    return ret;
-}
-
-FuriHalNfcReturn rfalPicoPassPollerSelect(uint8_t* csn, rfalPicoPassSelectRes* selRes) {
-    FuriHalNfcReturn ret;
-
-    rfalPicoPassSelectReq selReq;
-    selReq.CMD = RFAL_PICOPASS_CMD_SELECT;
-    memcpy(selReq.CSN, csn, RFAL_PICOPASS_UID_LEN);
-    uint16_t recvLen = 0;
-    uint32_t flags = RFAL_PICOPASS_TXRX_FLAGS;
-    uint32_t fwt = furi_hal_nfc_ll_ms2fc(20);
-
-    ret = furi_hal_nfc_ll_txrx(
-        (uint8_t*)&selReq,
-        sizeof(rfalPicoPassSelectReq),
-        (uint8_t*)selRes,
-        sizeof(rfalPicoPassSelectRes),
-        &recvLen,
-        flags,
-        fwt);
-    // printf("select rx: %d %s\n", recvLen, hex2Str(selRes->CSN, RFAL_PICOPASS_UID_LEN));
-    if(ret == FuriHalNfcReturnTimeout) {
-        return FuriHalNfcReturnOk;
-    }
-
-    return ret;
-}

+ 0 - 32
apps_broken_by_last_refactors/seader/rfal_picopass.h

@@ -1,32 +0,0 @@
-#pragma once
-
-#include <furi_hal_nfc.h>
-
-#define RFAL_PICOPASS_UID_LEN 8
-#define RFAL_PICOPASS_MAX_BLOCK_LEN 8
-
-enum {
-    RFAL_PICOPASS_CMD_ACTALL = 0x0A,
-    RFAL_PICOPASS_CMD_IDENTIFY = 0x0C,
-    RFAL_PICOPASS_CMD_SELECT = 0x81,
-    RFAL_PICOPASS_CMD_READCHECK = 0x88,
-    RFAL_PICOPASS_CMD_CHECK = 0x05,
-    RFAL_PICOPASS_CMD_READ4 = 0x06,
-    RFAL_PICOPASS_CMD_READ = 0x0C,
-    RFAL_PICOPASS_CMD_WRITE = 0x87,
-};
-
-typedef struct {
-    uint8_t CSN[RFAL_PICOPASS_UID_LEN]; // Anti-collision CSN
-    uint8_t crc[2];
-} rfalPicoPassIdentifyRes;
-
-typedef struct {
-    uint8_t CSN[RFAL_PICOPASS_UID_LEN]; // Real CSN
-    uint8_t crc[2];
-} rfalPicoPassSelectRes;
-
-FuriHalNfcReturn rfalPicoPassPollerInitialize(void);
-FuriHalNfcReturn rfalPicoPassPollerCheckPresence(void);
-FuriHalNfcReturn rfalPicoPassPollerIdentify(rfalPicoPassIdentifyRes* idRes);
-FuriHalNfcReturn rfalPicoPassPollerSelect(uint8_t* csn, rfalPicoPassSelectRes* selRes);

+ 682 - 0
apps_broken_by_last_refactors/seader/sam_api.c

@@ -0,0 +1,682 @@
+
+#include "sam_api.h"
+
+#define TAG "SAMAPI"
+
+#define APDU_HEADER_LEN 5
+#define ASN1_PREFIX 6
+#define ASN1_DEBUG true
+
+#ifdef ASN1_DEBUG
+char payloadDebug[384] = {0};
+#endif
+
+static char display[SEADER_UART_RX_BUF_SIZE * 2 + 1] = {0};
+char asn1_log[SEADER_UART_RX_BUF_SIZE] = {0};
+bool requestPacs = true;
+
+void* calloc(size_t count, size_t size) {
+    return malloc(count * size);
+}
+
+bool seader_send_apdu(
+    SeaderUartBridge* seader_uart,
+    uint8_t CLA,
+    uint8_t INS,
+    uint8_t P1,
+    uint8_t P2,
+    uint8_t* payload,
+    uint8_t length) {
+    if(APDU_HEADER_LEN + length > SEADER_UART_RX_BUF_SIZE) {
+        FURI_LOG_E(TAG, "Cannot send message, too long: %d", APDU_HEADER_LEN + length);
+        return false;
+    }
+
+    uint8_t* apdu = malloc(APDU_HEADER_LEN + length);
+    apdu[0] = CLA;
+    apdu[1] = INS;
+    apdu[2] = P1;
+    apdu[3] = P2;
+    apdu[4] = length;
+    memcpy(apdu + APDU_HEADER_LEN, payload, length);
+
+    seader_ccid_XfrBlock(seader_uart, apdu, APDU_HEADER_LEN + length);
+    free(apdu);
+    return true;
+}
+
+static int seader_asn_to_string(const void* buffer, size_t size, void* app_key) {
+    if(app_key) {
+        char* str = (char*)app_key;
+        size_t next = strlen(str);
+        strncpy(str + next, buffer, size);
+    } else {
+        uint8_t next = strlen(asn1_log);
+        strncpy(asn1_log + next, buffer, size);
+    }
+    return 0;
+}
+
+void seader_send_payload(
+    SeaderUartBridge* seader_uart,
+    Payload_t* payload,
+    uint8_t to,
+    uint8_t from,
+    uint8_t replyTo) {
+    uint8_t rBuffer[SEADER_UART_RX_BUF_SIZE] = {0};
+
+    asn_enc_rval_t er = der_encode_to_buffer(
+        &asn_DEF_Payload, payload, rBuffer + ASN1_PREFIX, sizeof(rBuffer) - ASN1_PREFIX);
+
+#ifdef ASN1_DEBUG
+    if(er.encoded > -1) {
+        memset(payloadDebug, 0, sizeof(payloadDebug));
+        (&asn_DEF_Payload)
+            ->op->print_struct(&asn_DEF_Payload, payload, 1, seader_asn_to_string, payloadDebug);
+        if(strlen(payloadDebug) > 0) {
+            FURI_LOG_D(TAG, "Sending payload[%d %d %d]: %s", to, from, replyTo, payloadDebug);
+        }
+    }
+#endif
+    //0xa0, 0xda, 0x02, 0x63, 0x00, 0x00, 0x0a,
+    //0x44, 0x0a, 0x44, 0x00, 0x00, 0x00, 0xa0, 0x02, 0x96, 0x00
+    rBuffer[0] = to;
+    rBuffer[1] = from;
+    rBuffer[2] = replyTo;
+
+    seader_send_apdu(seader_uart, 0xA0, 0xDA, 0x02, 0x63, rBuffer, 6 + er.encoded);
+}
+
+void seader_send_response(
+    SeaderUartBridge* seader_uart,
+    Response_t* response,
+    uint8_t to,
+    uint8_t from,
+    uint8_t replyTo) {
+    Payload_t* payload = 0;
+    payload = calloc(1, sizeof *payload);
+    assert(payload);
+
+    payload->present = Payload_PR_response;
+    payload->choice.response = *response;
+
+    seader_send_payload(seader_uart, payload, to, from, replyTo);
+
+    ASN_STRUCT_FREE(asn_DEF_Payload, payload);
+}
+
+void sendRequestPacs(SeaderUartBridge* seader_uart) {
+    RequestPacs_t* requestPacs = 0;
+    requestPacs = calloc(1, sizeof *requestPacs);
+    assert(requestPacs);
+
+    requestPacs->contentElementTag = ContentElementTag_implicitFormatPhysicalAccessBits;
+
+    SamCommand_t* samCommand = 0;
+    samCommand = calloc(1, sizeof *samCommand);
+    assert(samCommand);
+
+    samCommand->present = SamCommand_PR_requestPacs;
+    samCommand->choice.requestPacs = *requestPacs;
+
+    Payload_t* payload = 0;
+    payload = calloc(1, sizeof *payload);
+    assert(payload);
+
+    payload->present = Payload_PR_samCommand;
+    payload->choice.samCommand = *samCommand;
+
+    seader_send_payload(seader_uart, payload, 0x44, 0x0a, 0x44);
+
+    ASN_STRUCT_FREE(asn_DEF_RequestPacs, requestPacs);
+    ASN_STRUCT_FREE(asn_DEF_SamCommand, samCommand);
+    ASN_STRUCT_FREE(asn_DEF_Payload, payload);
+}
+
+void seader_worker_send_version(SeaderWorker* seader_worker) {
+    SeaderUartBridge* seader_uart = seader_worker->uart;
+    SamCommand_t* samCommand = 0;
+    samCommand = calloc(1, sizeof *samCommand);
+    assert(samCommand);
+
+    samCommand->present = SamCommand_PR_version;
+
+    Payload_t* payload = 0;
+    payload = calloc(1, sizeof *payload);
+    assert(payload);
+
+    payload->present = Payload_PR_samCommand;
+    payload->choice.samCommand = *samCommand;
+
+    seader_send_payload(seader_uart, payload, 0x44, 0x0a, 0x44);
+
+    ASN_STRUCT_FREE(asn_DEF_SamCommand, samCommand);
+    ASN_STRUCT_FREE(asn_DEF_Payload, payload);
+}
+
+void seader_send_card_detected(SeaderUartBridge* seader_uart, CardDetails_t* cardDetails) {
+    CardDetected_t* cardDetected = 0;
+    cardDetected = calloc(1, sizeof *cardDetected);
+    assert(cardDetected);
+
+    cardDetected->detectedCardDetails = *cardDetails;
+
+    SamCommand_t* samCommand = 0;
+    samCommand = calloc(1, sizeof *samCommand);
+    assert(samCommand);
+
+    samCommand->present = SamCommand_PR_cardDetected;
+    samCommand->choice.cardDetected = *cardDetected;
+
+    Payload_t* payload = 0;
+    payload = calloc(1, sizeof *payload);
+    assert(payload);
+
+    payload->present = Payload_PR_samCommand;
+    payload->choice.samCommand = *samCommand;
+
+    seader_send_payload(seader_uart, payload, 0x44, 0x0a, 0x44);
+
+    ASN_STRUCT_FREE(asn_DEF_CardDetected, cardDetected);
+    ASN_STRUCT_FREE(asn_DEF_SamCommand, samCommand);
+    ASN_STRUCT_FREE(asn_DEF_Payload, payload);
+}
+
+bool seader_unpack_pacs(SeaderCredential* seader_credential, uint8_t* buf, size_t size) {
+    PAC_t* pac = 0;
+    pac = calloc(1, sizeof *pac);
+    assert(pac);
+    bool rtn = false;
+
+    asn_dec_rval_t rval = asn_decode(0, ATS_DER, &asn_DEF_PAC, (void**)&pac, buf, size);
+
+    if(rval.code == RC_OK) {
+        char pacDebug[384] = {0};
+        (&asn_DEF_PAC)->op->print_struct(&asn_DEF_PAC, pac, 1, seader_asn_to_string, pacDebug);
+        if(strlen(pacDebug) > 0) {
+            FURI_LOG_D(TAG, "Received pac: %s", pacDebug);
+
+            memset(display, 0, sizeof(display));
+            if(seader_credential->sio[0] == 0x30) {
+                for(uint8_t i = 0; i < sizeof(seader_credential->sio); i++) {
+                    snprintf(
+                        display + (i * 2), sizeof(display), "%02x", seader_credential->sio[i]);
+                }
+                FURI_LOG_D(TAG, "SIO %s", display);
+            }
+        }
+
+        if(pac->size <= sizeof(seader_credential->credential)) {
+            // TODO: make credential into a 12 byte array
+            seader_credential->bit_length = pac->size * 8 - pac->bits_unused;
+            memcpy(&seader_credential->credential, pac->buf, pac->size);
+            seader_credential->credential = __builtin_bswap64(seader_credential->credential);
+            seader_credential->credential = seader_credential->credential >>
+                                            (64 - seader_credential->bit_length);
+            rtn = true;
+        } else {
+            // PACS too big (probably bad data)
+        }
+    }
+
+    ASN_STRUCT_FREE(asn_DEF_PAC, pac);
+    return rtn;
+}
+
+//    800201298106683d052026b6820101
+//300F800201298106683D052026B6820101
+bool seader_parse_version(SeaderWorker* seader_worker, uint8_t* buf, size_t size) {
+    SamVersion_t* version = 0;
+    version = calloc(1, sizeof *version);
+    assert(version);
+
+    bool rtn = false;
+    if(size > 30) {
+        // Too large to handle now
+        FURI_LOG_W(TAG, "Version of %d is to long to parse", size);
+        return false;
+    }
+    // Add sequence prefix
+    uint8_t seq[32] = {0x30};
+    seq[1] = (uint8_t)size;
+    memcpy(seq + 2, buf, size);
+
+    asn_dec_rval_t rval =
+        asn_decode(0, ATS_DER, &asn_DEF_SamVersion, (void**)&version, seq, size + 2);
+
+    if(rval.code == RC_OK) {
+        char versionDebug[128] = {0};
+        (&asn_DEF_SamVersion)
+            ->op->print_struct(
+                &asn_DEF_SamVersion, version, 1, seader_asn_to_string, versionDebug);
+        if(strlen(versionDebug) > 0) {
+            // FURI_LOG_D(TAG, "Received version: %s", versionDebug);
+        }
+        if(version->version.size == 2) {
+            memcpy(seader_worker->sam_version, version->version.buf, version->version.size);
+        }
+
+        rtn = true;
+    }
+
+    ASN_STRUCT_FREE(asn_DEF_SamVersion, version);
+    return rtn;
+}
+
+bool seader_parse_sam_response(Seader* seader, SamResponse_t* samResponse) {
+    SeaderWorker* seader_worker = seader->worker;
+    SeaderUartBridge* seader_uart = seader_worker->uart;
+    SeaderCredential* credential = seader->credential;
+
+    if(samResponse->size == 0) {
+        if(requestPacs) {
+            FURI_LOG_D(TAG, "samResponse %d => requesting PACS", samResponse->size);
+            sendRequestPacs(seader_uart);
+            requestPacs = false;
+        } else {
+            FURI_LOG_D(TAG, "samResponse %d, no action", samResponse->size);
+        }
+    } else if(seader_parse_version(seader_worker, samResponse->buf, samResponse->size)) {
+        // no-op
+    } else if(seader_unpack_pacs(credential, samResponse->buf, samResponse->size)) {
+        view_dispatcher_send_custom_event(seader->view_dispatcher, SeaderCustomEventWorkerExit);
+    } else {
+        memset(display, 0, sizeof(display));
+        for(uint8_t i = 0; i < samResponse->size; i++) {
+            snprintf(display + (i * 2), sizeof(display), "%02x", samResponse->buf[i]);
+        }
+        FURI_LOG_D(TAG, "Unknown samResponse %d: %s", samResponse->size, display);
+    }
+
+    return false;
+}
+
+bool seader_parse_response(Seader* seader, Response_t* response) {
+    switch(response->present) {
+    case Response_PR_samResponse:
+        seader_parse_sam_response(seader, &response->choice.samResponse);
+        break;
+    default:
+        break;
+    };
+    return false;
+}
+
+void seader_send_nfc_rx(SeaderUartBridge* seader_uart, uint8_t* buffer, size_t len) {
+    OCTET_STRING_t rxData = {.buf = buffer, .size = len};
+    uint8_t status[] = {0x00, 0x00};
+    RfStatus_t rfStatus = {.buf = status, .size = 2};
+
+    NFCRx_t* nfcRx = 0;
+    nfcRx = calloc(1, sizeof *nfcRx);
+    assert(nfcRx);
+
+    nfcRx->rfStatus = rfStatus;
+    nfcRx->data = &rxData;
+
+    NFCResponse_t* nfcResponse = 0;
+    nfcResponse = calloc(1, sizeof *nfcResponse);
+    assert(nfcResponse);
+
+    nfcResponse->present = NFCResponse_PR_nfcRx;
+    nfcResponse->choice.nfcRx = *nfcRx;
+
+    Response_t* response = 0;
+    response = calloc(1, sizeof *response);
+    assert(response);
+
+    response->present = Response_PR_nfcResponse;
+    response->choice.nfcResponse = *nfcResponse;
+
+    seader_send_response(seader_uart, response, 0x14, 0x0a, 0x0);
+
+    ASN_STRUCT_FREE(asn_DEF_NFCRx, nfcRx);
+    ASN_STRUCT_FREE(asn_DEF_NFCResponse, nfcResponse);
+    ASN_STRUCT_FREE(asn_DEF_Response, response);
+}
+
+static uint16_t seader_worker_picopass_update_ccitt(uint16_t crcSeed, uint8_t dataByte) {
+    uint16_t crc = crcSeed;
+    uint8_t dat = dataByte;
+
+    dat ^= (uint8_t)(crc & 0xFFU);
+    dat ^= (dat << 4);
+
+    crc = (crc >> 8) ^ (((uint16_t)dat) << 8) ^ (((uint16_t)dat) << 3) ^ (((uint16_t)dat) >> 4);
+
+    return crc;
+}
+
+static uint16_t seader_worker_picopass_calculate_ccitt(
+    uint16_t preloadValue,
+    const uint8_t* buf,
+    uint16_t length) {
+    uint16_t crc = preloadValue;
+    uint16_t index;
+
+    for(index = 0; index < length; index++) {
+        crc = seader_worker_picopass_update_ccitt(crc, buf[index]);
+    }
+
+    return crc;
+}
+
+uint8_t read4Block6[] = {0x06, 0x06, 0x45, 0x56};
+uint8_t read4Block9[] = {0x06, 0x09, 0xB2, 0xAE};
+uint8_t read4Block10[] = {0x06, 0x0A, 0x29, 0x9C};
+uint8_t read4Block13[] = {0x06, 0x0D, 0x96, 0xE8};
+uint8_t updateBlock2[] = {0x87, 0x02}; // TODO
+
+void seader_capture_sio(BitBuffer* tx_buffer, BitBuffer* rx_buffer, SeaderCredential* credential) {
+    const uint8_t* buffer = bit_buffer_get_data(tx_buffer);
+    size_t len = bit_buffer_get_size_bytes(tx_buffer);
+    const uint8_t* rxBuffer = bit_buffer_get_data(rx_buffer);
+
+    if(memcmp(buffer, read4Block6, len) == 0 && rxBuffer[0] == 0x30) {
+        memcpy(credential->sio, rxBuffer, 32);
+    } else if(memcmp(buffer, read4Block10, len) == 0 && rxBuffer[0] == 0x30) {
+        memcpy(credential->sio, rxBuffer, 32);
+    } else if(memcmp(buffer, read4Block9, len) == 0) {
+        memcpy(credential->sio + 32, rxBuffer + 8, 24);
+    } else if(memcmp(buffer, read4Block13, len) == 0) {
+        memcpy(credential->sio + 32, rxBuffer + 8, 24);
+    }
+}
+
+PicopassError seader_worker_fake_epurse_update(BitBuffer* tx_buffer, BitBuffer* rx_buffer) {
+    const uint8_t* buffer = bit_buffer_get_data(tx_buffer);
+    uint8_t fake_response[10];
+    memset(fake_response, 0, sizeof(fake_response));
+    memcpy(fake_response + 0, buffer + 6, 4);
+    memcpy(fake_response + 4, buffer + 2, 4);
+
+    uint16_t crc = seader_worker_picopass_calculate_ccitt(0xE012, fake_response, 8);
+    memcpy(fake_response + 8, &crc, sizeof(uint16_t));
+
+    bit_buffer_append_bytes(rx_buffer, fake_response, sizeof(fake_response));
+
+    memset(display, 0, sizeof(display));
+    for(uint8_t i = 0; i < sizeof(fake_response); i++) {
+        snprintf(display + (i * 2), sizeof(display), "%02x", fake_response[i]);
+    }
+    FURI_LOG_I(TAG, "Fake update E-Purse response: %s", display);
+
+    return PicopassErrorNone;
+}
+
+void seader_iso15693_transmit(
+    Seader* seader,
+    PicopassPoller* picopass_poller,
+    uint8_t* buffer,
+    size_t len) {
+    UNUSED(seader);
+    UNUSED(buffer);
+    UNUSED(len);
+
+    SeaderWorker* seader_worker = seader->worker;
+    SeaderUartBridge* seader_uart = seader_worker->uart;
+
+    BitBuffer* tx_buffer = bit_buffer_alloc(len);
+    BitBuffer* rx_buffer = bit_buffer_alloc(SEADER_POLLER_MAX_BUFFER_SIZE);
+
+    PicopassError error = PicopassErrorNone;
+
+    do {
+        bit_buffer_append_bytes(tx_buffer, buffer, len);
+
+        if(memcmp(buffer, updateBlock2, sizeof(updateBlock2)) == 0) {
+            error = seader_worker_fake_epurse_update(tx_buffer, rx_buffer);
+        } else {
+            error = picopass_poller_send_frame(
+                picopass_poller, tx_buffer, rx_buffer, SEADER_POLLER_MAX_FWT);
+        }
+        if(error == PicopassErrorIncorrectCrc) {
+            error = PicopassErrorNone;
+        }
+
+        if(error != PicopassErrorNone) {
+            seader_worker->stage = SeaderPollerEventTypeFail;
+            break;
+        }
+
+        seader_capture_sio(tx_buffer, rx_buffer, seader->credential);
+        seader_send_nfc_rx(
+            seader_uart,
+            (uint8_t*)bit_buffer_get_data(rx_buffer),
+            bit_buffer_get_size_bytes(rx_buffer));
+
+    } while(false);
+
+    bit_buffer_free(tx_buffer);
+    bit_buffer_free(rx_buffer);
+}
+
+/* Assumes this is called in the context of the NFC API callback */
+void seader_iso14443a_transmit(
+    Seader* seader,
+    Iso14443_4aPoller* iso14443_4a_poller,
+    uint8_t* buffer,
+    size_t len,
+    uint16_t timeout,
+    uint8_t format[3]) {
+    UNUSED(timeout);
+    UNUSED(format);
+
+    furi_assert(seader);
+    furi_assert(buffer);
+    furi_assert(iso14443_4a_poller);
+    SeaderWorker* seader_worker = seader->worker;
+    SeaderUartBridge* seader_uart = seader_worker->uart;
+
+    BitBuffer* tx_buffer = bit_buffer_alloc(len);
+    BitBuffer* rx_buffer = bit_buffer_alloc(SEADER_POLLER_MAX_BUFFER_SIZE);
+
+    do {
+        bit_buffer_append_bytes(tx_buffer, buffer, len);
+
+        Iso14443_4aError error =
+            iso14443_4a_poller_send_block(iso14443_4a_poller, tx_buffer, rx_buffer);
+        if(error != Iso14443_4aErrorNone) {
+            FURI_LOG_W(TAG, "iso14443_4a_poller_send_block error %d", error);
+            seader_worker->stage = SeaderPollerEventTypeFail;
+            break;
+        }
+
+        seader_send_nfc_rx(
+            seader_uart,
+            (uint8_t*)bit_buffer_get_data(rx_buffer),
+            bit_buffer_get_size_bytes(rx_buffer));
+
+    } while(false);
+    bit_buffer_free(tx_buffer);
+    bit_buffer_free(rx_buffer);
+}
+
+void seader_parse_nfc_command_transmit(
+    Seader* seader,
+    NFCSend_t* nfcSend,
+    SeaderPollerContainer* spc) {
+    long timeOut = nfcSend->timeOut;
+    Protocol_t protocol = nfcSend->protocol;
+    FrameProtocol_t frameProtocol = protocol.buf[1];
+
+#ifdef ASN1_DEBUG
+    memset(display, 0, sizeof(display));
+    for(uint8_t i = 0; i < nfcSend->data.size; i++) {
+        snprintf(display + (i * 2), sizeof(display), "%02x", nfcSend->data.buf[i]);
+    }
+
+    FURI_LOG_D(
+        TAG,
+        "Transmit (%ld timeout) %d bytes [%s] via %lx",
+        timeOut,
+        nfcSend->data.size,
+        display,
+        frameProtocol);
+#endif
+
+    if(frameProtocol == FrameProtocol_iclass) {
+        seader_iso15693_transmit(
+            seader, spc->picopass_poller, nfcSend->data.buf, nfcSend->data.size);
+    } else if(frameProtocol == FrameProtocol_nfc) {
+        seader_iso14443a_transmit(
+            seader,
+            spc->iso14443_4a_poller,
+            nfcSend->data.buf,
+            nfcSend->data.size,
+            (uint16_t)timeOut,
+            nfcSend->format->buf);
+    } else {
+        FURI_LOG_W(TAG, "unknown frame protocol %lx", frameProtocol);
+    }
+}
+
+void seader_parse_nfc_off(SeaderUartBridge* seader_uart) {
+    FURI_LOG_D(TAG, "Set Field Off");
+
+    NFCResponse_t* nfcResponse = 0;
+    nfcResponse = calloc(1, sizeof *nfcResponse);
+    assert(nfcResponse);
+
+    nfcResponse->present = NFCResponse_PR_nfcAck;
+
+    Response_t* response = 0;
+    response = calloc(1, sizeof *response);
+    assert(response);
+
+    response->present = Response_PR_nfcResponse;
+    response->choice.nfcResponse = *nfcResponse;
+
+    seader_send_response(seader_uart, response, 0x44, 0x0a, 0);
+
+    ASN_STRUCT_FREE(asn_DEF_Response, response);
+    ASN_STRUCT_FREE(asn_DEF_NFCResponse, nfcResponse);
+}
+
+void seader_parse_nfc_command(Seader* seader, NFCCommand_t* nfcCommand, SeaderPollerContainer* spc) {
+    SeaderWorker* seader_worker = seader->worker;
+    SeaderUartBridge* seader_uart = seader_worker->uart;
+    switch(nfcCommand->present) {
+    case NFCCommand_PR_nfcSend:
+        seader_parse_nfc_command_transmit(seader, &nfcCommand->choice.nfcSend, spc);
+        break;
+    case NFCCommand_PR_nfcOff:
+        seader_parse_nfc_off(seader_uart);
+        seader->worker->stage = SeaderPollerEventTypeComplete;
+        break;
+    default:
+        FURI_LOG_W(TAG, "unparsed NFCCommand");
+        break;
+    };
+}
+
+bool seader_worker_state_machine(
+    Seader* seader,
+    Payload_t* payload,
+    bool online,
+    SeaderPollerContainer* spc) {
+    bool processed = false;
+
+    switch(payload->present) {
+    case Payload_PR_response:
+        seader_parse_response(seader, &payload->choice.response);
+        processed = true;
+        break;
+    case Payload_PR_nfcCommand:
+        if(online) {
+            seader_parse_nfc_command(seader, &payload->choice.nfcCommand, spc);
+            processed = true;
+        }
+        break;
+    case Payload_PR_errorResponse:
+        FURI_LOG_W(TAG, "Error Response");
+        processed = true;
+        break;
+    default:
+        FURI_LOG_W(TAG, "unhandled payload");
+        break;
+    };
+
+    return processed;
+}
+
+bool seader_process_success_response_i(
+    Seader* seader,
+    uint8_t* apdu,
+    size_t len,
+    bool online,
+    SeaderPollerContainer* spc) {
+    Payload_t* payload = 0;
+    payload = calloc(1, sizeof *payload);
+    assert(payload);
+    bool processed = false;
+
+    asn_dec_rval_t rval =
+        asn_decode(0, ATS_DER, &asn_DEF_Payload, (void**)&payload, apdu + 6, len - 6);
+    if(rval.code == RC_OK) {
+        processed = seader_worker_state_machine(seader, payload, online, spc);
+
+#ifdef ASN1_DEBUG
+        if(processed) {
+            memset(payloadDebug, 0, sizeof(payloadDebug));
+            (&asn_DEF_Payload)
+                ->op->print_struct(
+                    &asn_DEF_Payload, payload, 1, seader_asn_to_string, payloadDebug);
+            if(strlen(payloadDebug) > 0) {
+                FURI_LOG_D(TAG, "Received payload: %s", payloadDebug);
+            }
+        }
+#endif
+    } else {
+        FURI_LOG_D(TAG, "Failed to decode APDU payload");
+    }
+
+    ASN_STRUCT_FREE(asn_DEF_Payload, payload);
+    return processed;
+}
+
+NfcCommand seader_worker_card_detect(
+    Seader* seader,
+    uint8_t sak,
+    uint8_t* atqa,
+    const uint8_t* uid,
+    uint8_t uid_len,
+    uint8_t* ats,
+    uint8_t ats_len) {
+    UNUSED(ats);
+    UNUSED(ats_len);
+
+    // We're telling the SAM we've seen a new card, so reset out requestPacs check
+    requestPacs = true;
+
+    SeaderWorker* seader_worker = seader->worker;
+    SeaderUartBridge* seader_uart = seader_worker->uart;
+    CardDetails_t* cardDetails = 0;
+    cardDetails = calloc(1, sizeof *cardDetails);
+    assert(cardDetails);
+
+    OCTET_STRING_fromBuf(&cardDetails->csn, (const char*)uid, uid_len);
+
+    if(sak != 0 && atqa != NULL) {
+        uint8_t protocol_bytes[] = {0x00, FrameProtocol_nfc};
+        OCTET_STRING_fromBuf(
+            &cardDetails->protocol, (const char*)protocol_bytes, sizeof(protocol_bytes));
+
+        OCTET_STRING_t sak_string = {.buf = &sak, .size = 1};
+        cardDetails->sak = &sak_string;
+
+        OCTET_STRING_t atqa_string = {.buf = atqa, .size = 2};
+        cardDetails->atqa = &atqa_string;
+
+    } else {
+        uint8_t protocol_bytes[] = {0x00, FrameProtocol_iclass};
+        OCTET_STRING_fromBuf(
+            &cardDetails->protocol, (const char*)protocol_bytes, sizeof(protocol_bytes));
+    }
+
+    seader_send_card_detected(seader_uart, cardDetails);
+
+    ASN_STRUCT_FREE(asn_DEF_CardDetails, cardDetails);
+
+    return NfcCommandContinue;
+}

+ 24 - 0
apps_broken_by_last_refactors/seader/sam_api.h

@@ -0,0 +1,24 @@
+#pragma once
+
+#include "seader_i.h"
+#include "seader_credential.h"
+#include "seader_bridge.h"
+#include "seader_worker.h"
+
+#include <Payload.h>
+
+NfcCommand seader_worker_card_detect(
+    Seader* seader,
+    uint8_t sak,
+    uint8_t* atqa,
+    const uint8_t* uid,
+    uint8_t uid_len,
+    uint8_t* ats,
+    uint8_t ats_len);
+
+bool seader_process_success_response_i(
+    Seader* seader,
+    uint8_t* apdu,
+    size_t len,
+    bool online,
+    SeaderPollerContainer* spc);

+ 1 - 2
apps_broken_by_last_refactors/seader/scenes/seader_scene_card_menu.c

@@ -16,8 +16,7 @@ void seader_scene_card_menu_submenu_callback(void* context, uint32_t index) {
 
 void seader_scene_card_menu_on_enter(void* context) {
     Seader* seader = context;
-    SeaderWorker* seader_worker = seader->worker;
-    SeaderCredential* credential = seader_worker->credential;
+    SeaderCredential* credential = seader->credential;
     Submenu* submenu = seader->submenu;
 
     submenu_add_item(

+ 0 - 1
apps_broken_by_last_refactors/seader/scenes/seader_scene_config.h

@@ -14,4 +14,3 @@ ADD_SCENE(seader, delete, Delete)
 ADD_SCENE(seader, delete_success, DeleteSuccess)
 ADD_SCENE(seader, credential_info, CredentialInfo)
 ADD_SCENE(seader, sam_info, SamInfo)
-ADD_SCENE(seader, uart, Uart)

+ 15 - 15
apps_broken_by_last_refactors/seader/scenes/seader_scene_read_14a.c

@@ -1,12 +1,6 @@
 #include "../seader_i.h"
 #include <dolphin/dolphin.h>
 
-void seader_read_14a_worker_callback(SeaderWorkerEvent event, void* context) {
-    UNUSED(event);
-    Seader* seader = context;
-    view_dispatcher_send_custom_event(seader->view_dispatcher, SeaderCustomEventWorkerExit);
-}
-
 void seader_scene_read_14a_on_enter(void* context) {
     Seader* seader = context;
     dolphin_deed(DolphinDeedNfcRead);
@@ -18,13 +12,12 @@ void seader_scene_read_14a_on_enter(void* context) {
 
     // Start worker
     view_dispatcher_switch_to_view(seader->view_dispatcher, SeaderViewPopup);
-    seader_worker_start(
-        seader->worker,
-        SeaderWorkerStateRead14a,
-        seader->uart,
-        seader->credential,
-        seader_read_14a_worker_callback,
-        seader);
+
+    seader->poller = nfc_poller_alloc(seader->nfc, NfcProtocolIso14443_4a);
+
+    seader->worker->stage = SeaderPollerEventTypeCardDetect;
+    seader_credential_clear(seader->credential);
+    nfc_poller_start(seader->poller, seader_worker_poller_callback_iso14443_4a, seader);
 
     seader_blink_start(seader);
 }
@@ -39,15 +32,22 @@ bool seader_scene_read_14a_on_event(void* context, SceneManagerEvent event) {
             scene_manager_next_scene(seader->scene_manager, SeaderSceneReadCardSuccess);
             consumed = true;
         }
+    } else if(event.type == SceneManagerEventTypeBack) {
+        scene_manager_search_and_switch_to_previous_scene(seader->scene_manager, SeaderSceneStart);
+        consumed = true;
     }
+
     return consumed;
 }
 
 void seader_scene_read_14a_on_exit(void* context) {
     Seader* seader = context;
 
-    // Stop worker
-    seader_worker_stop(seader->worker);
+    if(seader->poller) {
+        nfc_poller_stop(seader->poller);
+        nfc_poller_free(seader->poller);
+    }
+
     // Clear view
     popup_reset(seader->popup);
 

+ 13 - 15
apps_broken_by_last_refactors/seader/scenes/seader_scene_read_picopass.c

@@ -1,12 +1,6 @@
 #include "../seader_i.h"
 #include <dolphin/dolphin.h>
 
-void seader_read_picopass_worker_callback(SeaderWorkerEvent event, void* context) {
-    UNUSED(event);
-    Seader* seader = context;
-    view_dispatcher_send_custom_event(seader->view_dispatcher, SeaderCustomEventWorkerExit);
-}
-
 void seader_scene_read_picopass_on_enter(void* context) {
     Seader* seader = context;
     dolphin_deed(DolphinDeedNfcRead);
@@ -18,13 +12,11 @@ void seader_scene_read_picopass_on_enter(void* context) {
 
     // Start worker
     view_dispatcher_switch_to_view(seader->view_dispatcher, SeaderViewPopup);
-    seader_worker_start(
-        seader->worker,
-        SeaderWorkerStateReadPicopass,
-        seader->uart,
-        seader->credential,
-        seader_read_picopass_worker_callback,
-        seader);
+
+    seader->worker->stage = SeaderPollerEventTypeCardDetect;
+    seader_credential_clear(seader->credential);
+    seader->picopass_poller = picopass_poller_alloc(seader->nfc);
+    picopass_poller_start(seader->picopass_poller, seader_worker_poller_callback_picopass, seader);
 
     seader_blink_start(seader);
 }
@@ -39,6 +31,9 @@ bool seader_scene_read_picopass_on_event(void* context, SceneManagerEvent event)
             scene_manager_next_scene(seader->scene_manager, SeaderSceneReadCardSuccess);
             consumed = true;
         }
+    } else if(event.type == SceneManagerEventTypeBack) {
+        scene_manager_search_and_switch_to_previous_scene(seader->scene_manager, SeaderSceneStart);
+        consumed = true;
     }
     return consumed;
 }
@@ -46,8 +41,11 @@ bool seader_scene_read_picopass_on_event(void* context, SceneManagerEvent event)
 void seader_scene_read_picopass_on_exit(void* context) {
     Seader* seader = context;
 
-    // Stop worker
-    seader_worker_stop(seader->worker);
+    if(seader->picopass_poller) {
+        picopass_poller_stop(seader->picopass_poller);
+        picopass_poller_free(seader->picopass_poller);
+    }
+
     // Clear view
     popup_reset(seader->popup);
 

+ 6 - 20
apps_broken_by_last_refactors/seader/scenes/seader_scene_sam_present.c

@@ -66,28 +66,14 @@ bool seader_scene_sam_present_on_event(void* context, SceneManagerEvent event) {
 
     if(event.type == SceneManagerEventTypeCustom) {
         if(event.event == SubmenuIndexReadPicopass) {
-            if(seader->is_debug_enabled) {
-                seader->credential->type = SeaderCredentialTypePicopass;
-                scene_manager_set_scene_state(
-                    seader->scene_manager, SeaderSceneSamPresent, SubmenuIndexReadPicopass);
-                scene_manager_next_scene(seader->scene_manager, SeaderSceneUart);
-            } else {
-                scene_manager_set_scene_state(
-                    seader->scene_manager, SeaderSceneSamPresent, SubmenuIndexReadPicopass);
-                scene_manager_next_scene(seader->scene_manager, SeaderSceneReadPicopass);
-            }
+            scene_manager_set_scene_state(
+                seader->scene_manager, SeaderSceneSamPresent, SubmenuIndexReadPicopass);
+            scene_manager_next_scene(seader->scene_manager, SeaderSceneReadPicopass);
             consumed = true;
         } else if(event.event == SubmenuIndexRead14a) {
-            if(seader->is_debug_enabled) {
-                seader->credential->type = SeaderCredentialType14A;
-                scene_manager_set_scene_state(
-                    seader->scene_manager, SeaderSceneSamPresent, SubmenuIndexRead14a);
-                scene_manager_next_scene(seader->scene_manager, SeaderSceneUart);
-            } else {
-                scene_manager_set_scene_state(
-                    seader->scene_manager, SeaderSceneSamPresent, SubmenuIndexRead14a);
-                scene_manager_next_scene(seader->scene_manager, SeaderSceneRead14a);
-            }
+            scene_manager_set_scene_state(
+                seader->scene_manager, SeaderSceneSamPresent, SubmenuIndexRead14a);
+            scene_manager_next_scene(seader->scene_manager, SeaderSceneRead14a);
             consumed = true;
         } else if(event.event == SubmenuIndexSamInfo) {
             scene_manager_set_scene_state(

+ 2 - 2
apps_broken_by_last_refactors/seader/scenes/seader_scene_save_name.c

@@ -1,5 +1,5 @@
 #include "../seader_i.h"
-#include <toolbox/name_generator.h>
+#include <lib/toolbox/name_generator.h>
 #include <gui/modules/validators.h>
 #include <toolbox/path.h>
 
@@ -16,7 +16,7 @@ void seader_scene_save_name_on_enter(void* context) {
     TextInput* text_input = seader->text_input;
     bool cred_name_empty = false;
     if(!strcmp(seader->credential->name, "")) {
-        name_generator_make_auto(seader->text_store, sizeof(seader->text_store), "Seader");
+        name_generator_make_random(seader->text_store, sizeof(seader->text_store));
         cred_name_empty = true;
     } else {
         seader_text_store_set(seader, seader->credential->name);

+ 2 - 3
apps_broken_by_last_refactors/seader/scenes/seader_scene_start.c

@@ -4,7 +4,7 @@ enum SubmenuIndex {
     SubmenuIndexSamMissing,
 };
 
-static void detect_callback(void* context) {
+static void seader_scene_start_detect_callback(void* context) {
     Seader* seader = context;
     view_dispatcher_send_custom_event(seader->view_dispatcher, SeaderWorkerEventSamMissing);
 }
@@ -25,7 +25,7 @@ void seader_scene_start_on_enter(void* context) {
     Popup* popup = seader->popup;
 
     popup_set_context(seader->popup, seader);
-    popup_set_callback(seader->popup, detect_callback);
+    popup_set_callback(seader->popup, seader_scene_start_detect_callback);
     popup_set_header(popup, "Detecting SAM", 58, 48, AlignCenter, AlignCenter);
     popup_set_timeout(seader->popup, 2500);
     popup_enable_timeout(seader->popup);
@@ -35,7 +35,6 @@ void seader_scene_start_on_enter(void* context) {
         seader->worker,
         SeaderWorkerStateCheckSam,
         seader->uart,
-        seader->credential,
         seader_sam_check_worker_callback,
         seader);
     view_dispatcher_switch_to_view(seader->view_dispatcher, SeaderViewPopup);

+ 0 - 101
apps_broken_by_last_refactors/seader/scenes/seader_scene_uart.c

@@ -1,101 +0,0 @@
-#include "../seader_i.h"
-#include "../seader_bridge.h"
-#define TAG "SeaderSceneUart"
-
-typedef struct {
-    SeaderUartConfig cfg;
-    SeaderUartState state;
-} SceneUartBridge;
-
-static SceneUartBridge* scene_uart;
-
-void seader_uart_worker_callback(SeaderWorkerEvent event, void* context) {
-    UNUSED(event);
-    Seader* seader = context;
-    view_dispatcher_send_custom_event(seader->view_dispatcher, SeaderCustomEventWorkerExit);
-}
-
-void seader_scene_uart_callback(SeaderCustomEvent event, void* context) {
-    furi_assert(context);
-    Seader* app = context;
-    view_dispatcher_send_custom_event(app->view_dispatcher, event);
-}
-
-void seader_scene_uart_on_enter(void* context) {
-    Seader* app = context;
-    uint32_t prev_state = scene_manager_get_scene_state(app->scene_manager, SeaderViewUart);
-    if(prev_state == 0) {
-        scene_uart = malloc(sizeof(SceneUartBridge));
-        scene_uart->cfg.uart_ch = 0;
-        scene_uart->cfg.flow_pins = 0;
-        scene_uart->cfg.baudrate_mode = 0;
-        scene_uart->cfg.baudrate = 0;
-    }
-
-    seader_uart_get_config(app->uart, &scene_uart->cfg);
-    seader_uart_get_state(app->uart, &scene_uart->state);
-
-    seader_uart_view_set_callback(app->seader_uart_view, seader_scene_uart_callback, app);
-    scene_manager_set_scene_state(app->scene_manager, SeaderSceneUart, 0);
-    view_dispatcher_switch_to_view(app->view_dispatcher, SeaderViewUart);
-    notification_message(app->notifications, &sequence_display_backlight_enforce_on);
-
-    Seader* seader = app;
-
-    if(seader->credential->type == SeaderCredentialTypePicopass) {
-        seader_worker_start(
-            seader->worker,
-            SeaderWorkerStateReadPicopass,
-            seader->uart,
-            seader->credential,
-            seader_uart_worker_callback,
-            seader);
-    } else if(seader->credential->type == SeaderCredentialType14A) {
-        seader_worker_start(
-            seader->worker,
-            SeaderWorkerStateRead14a,
-            seader->uart,
-            seader->credential,
-            seader_uart_worker_callback,
-            seader);
-    }
-}
-
-bool seader_scene_uart_on_event(void* context, SceneManagerEvent event) {
-    Seader* app = context;
-    Seader* seader = context;
-    bool consumed = false;
-
-    if(event.type == SceneManagerEventTypeCustom) {
-        if(event.event == SeaderCustomEventWorkerExit) {
-            scene_manager_next_scene(seader->scene_manager, SeaderSceneReadCardSuccess);
-            consumed = true;
-        } else if(event.type == SceneManagerEventTypeTick) {
-            scene_manager_set_scene_state(app->scene_manager, SeaderSceneUart, 1);
-            consumed = true;
-        }
-    } else if(event.type == SceneManagerEventTypeTick) {
-        uint32_t tx_cnt_last = scene_uart->state.tx_cnt;
-        uint32_t rx_cnt_last = scene_uart->state.rx_cnt;
-        seader_uart_get_state(app->uart, &scene_uart->state);
-        if(seader->credential->type == SeaderCredentialTypePicopass) {
-            scene_uart->state.protocol = FrameProtocol_iclass;
-        } else if(seader->credential->type == SeaderCredentialType14A) {
-            scene_uart->state.protocol = FrameProtocol_nfc;
-        }
-        seader_uart_view_update_state(app->seader_uart_view, &scene_uart->cfg, &scene_uart->state);
-        if(tx_cnt_last != scene_uart->state.tx_cnt) {
-            notification_message(app->notifications, &sequence_blink_blue_10);
-        }
-        if(rx_cnt_last != scene_uart->state.rx_cnt) {
-            notification_message(app->notifications, &sequence_blink_green_10);
-        }
-    }
-    return consumed;
-}
-
-void seader_scene_uart_on_exit(void* context) {
-    Seader* app = context;
-    seader_worker_stop(app->worker);
-    notification_message(app->notifications, &sequence_display_backlight_enforce_auto);
-}

+ 11 - 9
apps_broken_by_last_refactors/seader/seader.c

@@ -45,6 +45,12 @@ Seader* seader_alloc() {
 
     seader->credential = seader_credential_alloc();
 
+    seader->nfc = nfc_alloc();
+
+    // Nfc device
+    seader->nfc_device = nfc_device_alloc();
+    nfc_device_set_loading_callback(seader->nfc_device, seader_show_loading_popup, seader);
+
     // Open GUI record
     seader->gui = furi_record_open(RECORD_GUI);
     view_dispatcher_attach_to_gui(
@@ -78,12 +84,6 @@ Seader* seader_alloc() {
     view_dispatcher_add_view(
         seader->view_dispatcher, SeaderViewWidget, widget_get_view(seader->widget));
 
-    seader->seader_uart_view = seader_uart_view_alloc();
-    view_dispatcher_add_view(
-        seader->view_dispatcher,
-        SeaderViewUart,
-        seader_uart_view_get_view(seader->seader_uart_view));
-
     return seader;
 }
 
@@ -100,6 +100,11 @@ void seader_free(Seader* seader) {
     seader_credential_free(seader->credential);
     seader->credential = NULL;
 
+    nfc_free(seader->nfc);
+
+    // Nfc device
+    nfc_device_free(seader->nfc_device);
+
     // Submenu
     view_dispatcher_remove_view(seader->view_dispatcher, SeaderViewMenu);
     submenu_free(seader->submenu);
@@ -120,9 +125,6 @@ void seader_free(Seader* seader) {
     view_dispatcher_remove_view(seader->view_dispatcher, SeaderViewWidget);
     widget_free(seader->widget);
 
-    view_dispatcher_remove_view(seader->view_dispatcher, SeaderViewUart);
-    seader_uart_view_free(seader->seader_uart_view);
-
     // Worker
     seader_worker_stop(seader->worker);
     seader_worker_free(seader->worker);

+ 1 - 0
apps_broken_by_last_refactors/seader/seader.h

@@ -1,3 +1,4 @@
 #pragma once
 
 typedef struct Seader Seader;
+typedef struct SeaderPollerContainer SeaderPollerContainer;

+ 1 - 1
apps_broken_by_last_refactors/seader/seader_credential.c

@@ -564,7 +564,7 @@ bool seader_credential_save(SeaderCredential* cred, const char* name) {
                 target = __builtin_bswap64((cred->credential | sentinel | header) << 4) >>
                          (64 - 48);
             } else {
-                target = __builtin_bswap64((cred->credential | sentinel) << 4) >> (64 - 48);
+                target = __builtin_bswap64(cred->credential << 4) >> (64 - 48);
             }
         } else {
             //8 bytes

+ 0 - 5
apps_broken_by_last_refactors/seader/seader_custom_event.h

@@ -1,5 +0,0 @@
-#pragma once
-
-typedef enum {
-    SeaderStartEventNone = 0,
-} SeaderCustomEvent;

+ 21 - 4
apps_broken_by_last_refactors/seader/seader_i.h

@@ -22,6 +22,14 @@
 
 #include <input/input.h>
 
+#include <lib/nfc/nfc.h>
+#include <lib/nfc/protocols/iso14443_3a/iso14443_3a.h>
+
+#include <nfc/nfc_poller.h>
+
+#include <nfc/nfc_device.h>
+#include <nfc/helpers/nfc_data_generator.h>
+
 // ASN1
 #include <asn_system.h>
 #include <asn_internal.h>
@@ -32,14 +40,13 @@
 #include <Payload.h>
 #include <FrameProtocol.h>
 
+#include "protocol/picopass_poller.h"
 #include "scenes/seader_scene.h"
-#include "views/seader_uart_view.h"
 
 #include "seader_bridge.h"
 #include "seader.h"
 #include "ccid.h"
 #include "uart.h"
-#include "rfal_picopass.h"
 #include "seader_worker.h"
 #include "seader_credential.h"
 
@@ -58,6 +65,8 @@ enum SeaderCustomEvent {
     SeaderCustomEventWorkerExit,
     SeaderCustomEventByteInputDone,
     SeaderCustomEventTextInputDone,
+
+    SeaderCustomEventPollerSuccess,
 };
 
 typedef enum {
@@ -95,8 +104,16 @@ struct Seader {
     TextInput* text_input;
     Widget* widget;
 
-    //Custom views
-    SeaderUartView* seader_uart_view;
+    Nfc* nfc;
+    NfcPoller* poller;
+    PicopassPoller* picopass_poller;
+
+    NfcDevice* nfc_device;
+};
+
+struct SeaderPollerContainer {
+    Iso14443_4aPoller* iso14443_4a_poller;
+    PicopassPoller* picopass_poller;
 };
 
 typedef enum {

+ 116 - 894
apps_broken_by_last_refactors/seader/seader_worker.c

@@ -1,7 +1,6 @@
 #include "seader_worker_i.h"
 
 #include <flipper_format/flipper_format.h>
-#include <lib/nfc/protocols/nfc_util.h>
 #include <lib/lfrfid/tools/bit_lib.h>
 
 #define TAG "SeaderWorker"
@@ -17,52 +16,11 @@
 // TODO: const
 uint8_t GET_RESPONSE[] = {0x00, 0xc0, 0x00, 0x00, 0xff};
 
-char payloadDebug[384] = {0};
 char display[SEADER_UART_RX_BUF_SIZE * 2 + 1] = {0};
-char asn1_log[SEADER_UART_RX_BUF_SIZE] = {0};
-bool requestPacs = true;
 
 // Forward declaration
 void seader_send_card_detected(SeaderUartBridge* seader_uart, CardDetails_t* cardDetails);
 
-static void seader_worker_enable_field() {
-    furi_hal_nfc_ll_txrx_on();
-    furi_hal_nfc_exit_sleep();
-    furi_hal_nfc_ll_poll();
-}
-
-static ReturnCode seader_worker_disable_field(ReturnCode rc) {
-    furi_hal_nfc_ll_txrx_off();
-    furi_hal_nfc_start_sleep();
-    return rc;
-}
-
-static uint16_t seader_worker_picopass_update_ccitt(uint16_t crcSeed, uint8_t dataByte) {
-    uint16_t crc = crcSeed;
-    uint8_t dat = dataByte;
-
-    dat ^= (uint8_t)(crc & 0xFFU);
-    dat ^= (dat << 4);
-
-    crc = (crc >> 8) ^ (((uint16_t)dat) << 8) ^ (((uint16_t)dat) << 3) ^ (((uint16_t)dat) >> 4);
-
-    return crc;
-}
-
-static uint16_t seader_worker_picopass_calculate_ccitt(
-    uint16_t preloadValue,
-    const uint8_t* buf,
-    uint16_t length) {
-    uint16_t crc = preloadValue;
-    uint16_t index;
-
-    for(index = 0; index < length; index++) {
-        crc = seader_worker_picopass_update_ccitt(crc, buf[index]);
-    }
-
-    return crc;
-}
-
 /***************************** Seader Worker API *******************************/
 
 SeaderWorker* seader_worker_alloc() {
@@ -71,6 +29,8 @@ SeaderWorker* seader_worker_alloc() {
     // Worker thread attributes
     seader_worker->thread =
         furi_thread_alloc_ex("SeaderWorker", 8192, seader_worker_task, seader_worker);
+    seader_worker->messages = furi_message_queue_alloc(3, sizeof(SeaderAPDU));
+    seader_worker->mq_mutex = furi_mutex_alloc(FuriMutexTypeNormal);
 
     seader_worker->callback = NULL;
     seader_worker->context = NULL;
@@ -86,6 +46,8 @@ void seader_worker_free(SeaderWorker* seader_worker) {
     furi_assert(seader_worker);
 
     furi_thread_free(seader_worker->thread);
+    furi_message_queue_free(seader_worker->messages);
+    furi_mutex_free(seader_worker->mq_mutex);
 
     furi_record_close(RECORD_STORAGE);
 
@@ -100,17 +62,15 @@ void seader_worker_start(
     SeaderWorker* seader_worker,
     SeaderWorkerState state,
     SeaderUartBridge* uart,
-    SeaderCredential* credential,
     SeaderWorkerCallback callback,
     void* context) {
     furi_assert(seader_worker);
     furi_assert(uart);
-    furi_assert(credential);
 
+    seader_worker->stage = SeaderPollerEventTypeCardDetect;
     seader_worker->callback = callback;
     seader_worker->context = context;
     seader_worker->uart = uart;
-    seader_worker->credential = credential;
     seader_worker_change_state(seader_worker, state);
     furi_thread_start(seader_worker->thread);
 }
@@ -121,7 +81,6 @@ void seader_worker_stop(SeaderWorker* seader_worker) {
        seader_worker->state == SeaderWorkerStateReady) {
         return;
     }
-    seader_worker_disable_field(ERR_NONE);
 
     seader_worker_change_state(seader_worker, SeaderWorkerStateStop);
     furi_thread_join(seader_worker->thread);
@@ -133,758 +92,39 @@ void seader_worker_change_state(SeaderWorker* seader_worker, SeaderWorkerState s
 
 /***************************** Seader Worker Thread *******************************/
 
-void* calloc(size_t count, size_t size) {
-    return malloc(count * size);
-}
-
-void seader_nfc_scene_field_on_enter() {
-    furi_hal_nfc_field_on();
-    furi_hal_nfc_exit_sleep();
-}
-
-void seader_nfc_scene_field_on_exit() {
-    furi_hal_nfc_sleep();
-    furi_hal_nfc_field_off();
-}
+bool seader_process_success_response(Seader* seader, uint8_t* apdu, size_t len) {
+    SeaderWorker* seader_worker = seader->worker;
 
-bool seader_send_apdu(
-    SeaderUartBridge* seader_uart,
-    uint8_t CLA,
-    uint8_t INS,
-    uint8_t P1,
-    uint8_t P2,
-    uint8_t* payload,
-    uint8_t length) {
-    if(APDU_HEADER_LEN + length > SEADER_UART_RX_BUF_SIZE) {
-        FURI_LOG_E(TAG, "Cannot send message, too long: %d", APDU_HEADER_LEN + length);
-        return false;
-    }
-
-    uint8_t* apdu = malloc(APDU_HEADER_LEN + length);
-    apdu[0] = CLA;
-    apdu[1] = INS;
-    apdu[2] = P1;
-    apdu[3] = P2;
-    apdu[4] = length;
-    memcpy(apdu + APDU_HEADER_LEN, payload, length);
-
-    seader_ccid_XfrBlock(seader_uart, apdu, APDU_HEADER_LEN + length);
-    free(apdu);
-    return true;
-}
-
-static int seader_asn_to_string(const void* buffer, size_t size, void* app_key) {
-    if(app_key) {
-        char* str = (char*)app_key;
-        size_t next = strlen(str);
-        strncpy(str + next, buffer, size);
-    } else {
-        uint8_t next = strlen(asn1_log);
-        strncpy(asn1_log + next, buffer, size);
-    }
-    return 0;
-}
-
-bool seader_mf_df_check_card_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK) {
-    return ATQA0 == 0x44 && ATQA1 == 0x03 && SAK == 0x20;
-}
-
-bool seader_mf_classic_check_card_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK) {
-    if((ATQA0 == 0x44 || ATQA0 == 0x04) && (SAK == 0x08 || SAK == 0x88 || SAK == 0x09)) {
-        return true;
-    } else if((ATQA0 == 0x01) && (ATQA1 == 0x0F) && (SAK == 0x01)) {
-        //skylanders support
-        return true;
-    } else if((ATQA0 == 0x42 || ATQA0 == 0x02) && (SAK == 0x18)) {
-        return true;
+    if(seader_process_success_response_i(seader, apdu, len, false, NULL)) {
+        // no-op, message was processed
     } else {
-        return false;
-    }
-}
-
-bool seader_read_nfc(SeaderUartBridge* seader_uart) {
-    FuriHalNfcDevData nfc_data = {};
-    bool rtn = false;
-    if(furi_hal_nfc_detect(&nfc_data, 300)) {
-        // Process first found device
-        if(nfc_data.type == FuriHalNfcTypeA) {
-            FURI_LOG_D(TAG, "NFC-A detected");
-            CardDetails_t* cardDetails = 0;
-            cardDetails = calloc(1, sizeof *cardDetails);
-            assert(cardDetails);
-
-            OCTET_STRING_fromBuf(&cardDetails->csn, (const char*)nfc_data.uid, nfc_data.uid_len);
-            uint8_t protocolBytes[] = {0x00, FrameProtocol_nfc};
-            OCTET_STRING_fromBuf(
-                &cardDetails->protocol, (const char*)protocolBytes, sizeof(protocolBytes));
-
-            OCTET_STRING_t sak = {.buf = &(nfc_data.sak), .size = 1};
-            cardDetails->sak = &sak;
-
-            uint8_t fake_seos_ats[] = {0x78, 0x77, 0x80, 0x02};
-            uint8_t fake_desfire_ats[] = {0x75, 0x77, 0x81, 0x02, 0x80};
-            if(seader_mf_df_check_card_type(nfc_data.atqa[0], nfc_data.atqa[1], nfc_data.sak)) {
-                FURI_LOG_D(TAG, "Desfire");
-                OCTET_STRING_t atqa = {.buf = fake_desfire_ats, .size = sizeof(fake_desfire_ats)};
-                cardDetails->atqa = &atqa;
-                seader_send_card_detected(seader_uart, cardDetails);
-                rtn = true;
-            } else if(seader_mf_classic_check_card_type(
-                          nfc_data.atqa[0], nfc_data.atqa[1], nfc_data.sak)) {
-                FURI_LOG_D(TAG, "MFC");
-                seader_send_card_detected(seader_uart, cardDetails);
-                rtn = true;
-            } else if(nfc_data.interface == FuriHalNfcInterfaceIsoDep) {
-                FURI_LOG_D(TAG, "ISO-DEP");
-                OCTET_STRING_t atqa = {.buf = fake_seos_ats, .size = sizeof(fake_seos_ats)};
-                cardDetails->atqa = &atqa;
-                seader_send_card_detected(seader_uart, cardDetails);
-                rtn = true;
-            }
-            ASN_STRUCT_FREE(asn_DEF_CardDetails, cardDetails);
-        }
-    }
-    return rtn;
-}
-
-bool seader_detect_nfc(SeaderWorker* seader_worker) {
-    SeaderUartBridge* seader_uart = seader_worker->uart;
-
-    while(seader_worker->state == SeaderWorkerStateRead14a) {
-        // Card found
-        if(seader_read_nfc(seader_uart)) {
-            return true;
-        }
-
-        furi_delay_ms(100);
-    }
-    return false;
-}
-
-void seader_send_payload(
-    SeaderUartBridge* seader_uart,
-    Payload_t* payload,
-    uint8_t to,
-    uint8_t from,
-    uint8_t replyTo) {
-    uint8_t rBuffer[SEADER_UART_RX_BUF_SIZE] = {0};
-
-    asn_enc_rval_t er = der_encode_to_buffer(
-        &asn_DEF_Payload, payload, rBuffer + ASN1_PREFIX, sizeof(rBuffer) - ASN1_PREFIX);
-
-#ifdef ASN1_DEBUG
-    if(er.encoded > -1) {
-        memset(payloadDebug, 0, sizeof(payloadDebug));
-        (&asn_DEF_Payload)
-            ->op->print_struct(&asn_DEF_Payload, payload, 1, seader_asn_to_string, payloadDebug);
-        if(strlen(payloadDebug) > 0) {
-            FURI_LOG_D(TAG, "Sending payload[%d %d %d]: %s", to, from, replyTo, payloadDebug);
-        }
-    }
-#endif
-    //0xa0, 0xda, 0x02, 0x63, 0x00, 0x00, 0x0a,
-    //0x44, 0x0a, 0x44, 0x00, 0x00, 0x00, 0xa0, 0x02, 0x96, 0x00
-    rBuffer[0] = to;
-    rBuffer[1] = from;
-    rBuffer[2] = replyTo;
-
-    seader_send_apdu(seader_uart, 0xA0, 0xDA, 0x02, 0x63, rBuffer, 6 + er.encoded);
-}
-
-void seader_send_response(
-    SeaderUartBridge* seader_uart,
-    Response_t* response,
-    uint8_t to,
-    uint8_t from,
-    uint8_t replyTo) {
-    Payload_t* payload = 0;
-    payload = calloc(1, sizeof *payload);
-    assert(payload);
-
-    payload->present = Payload_PR_response;
-    payload->choice.response = *response;
-
-    seader_send_payload(seader_uart, payload, to, from, replyTo);
-
-    ASN_STRUCT_FREE(asn_DEF_Payload, payload);
-}
-
-void sendRequestPacs(SeaderUartBridge* seader_uart) {
-    RequestPacs_t* requestPacs = 0;
-    requestPacs = calloc(1, sizeof *requestPacs);
-    assert(requestPacs);
-
-    requestPacs->contentElementTag = ContentElementTag_implicitFormatPhysicalAccessBits;
-
-    SamCommand_t* samCommand = 0;
-    samCommand = calloc(1, sizeof *samCommand);
-    assert(samCommand);
-
-    samCommand->present = SamCommand_PR_requestPacs;
-    samCommand->choice.requestPacs = *requestPacs;
-
-    Payload_t* payload = 0;
-    payload = calloc(1, sizeof *payload);
-    assert(payload);
-
-    payload->present = Payload_PR_samCommand;
-    payload->choice.samCommand = *samCommand;
-
-    seader_send_payload(seader_uart, payload, 0x44, 0x0a, 0x44);
-
-    ASN_STRUCT_FREE(asn_DEF_RequestPacs, requestPacs);
-    ASN_STRUCT_FREE(asn_DEF_SamCommand, samCommand);
-    ASN_STRUCT_FREE(asn_DEF_Payload, payload);
-}
-
-void seader_worker_send_version(SeaderWorker* seader_worker) {
-    SeaderUartBridge* seader_uart = seader_worker->uart;
-    SamCommand_t* samCommand = 0;
-    samCommand = calloc(1, sizeof *samCommand);
-    assert(samCommand);
-
-    samCommand->present = SamCommand_PR_version;
-
-    Payload_t* payload = 0;
-    payload = calloc(1, sizeof *payload);
-    assert(payload);
-
-    payload->present = Payload_PR_samCommand;
-    payload->choice.samCommand = *samCommand;
-
-    seader_send_payload(seader_uart, payload, 0x44, 0x0a, 0x44);
-
-    ASN_STRUCT_FREE(asn_DEF_SamCommand, samCommand);
-    ASN_STRUCT_FREE(asn_DEF_Payload, payload);
-}
-
-void seader_send_card_detected(SeaderUartBridge* seader_uart, CardDetails_t* cardDetails) {
-    CardDetected_t* cardDetected = 0;
-    cardDetected = calloc(1, sizeof *cardDetected);
-    assert(cardDetected);
-
-    cardDetected->detectedCardDetails = *cardDetails;
-
-    SamCommand_t* samCommand = 0;
-    samCommand = calloc(1, sizeof *samCommand);
-    assert(samCommand);
-
-    samCommand->present = SamCommand_PR_cardDetected;
-    samCommand->choice.cardDetected = *cardDetected;
-
-    Payload_t* payload = 0;
-    payload = calloc(1, sizeof *payload);
-    assert(payload);
-
-    payload->present = Payload_PR_samCommand;
-    payload->choice.samCommand = *samCommand;
-
-    seader_send_payload(seader_uart, payload, 0x44, 0x0a, 0x44);
-
-    ASN_STRUCT_FREE(asn_DEF_CardDetected, cardDetected);
-    ASN_STRUCT_FREE(asn_DEF_SamCommand, samCommand);
-    ASN_STRUCT_FREE(asn_DEF_Payload, payload);
-}
-
-bool seader_unpack_pacs(
-    SeaderWorker* seader_worker,
-    SeaderCredential* seader_credential,
-    uint8_t* buf,
-    size_t size) {
-    PAC_t* pac = 0;
-    pac = calloc(1, sizeof *pac);
-    assert(pac);
-    bool rtn = false;
-
-    asn_dec_rval_t rval = asn_decode(0, ATS_DER, &asn_DEF_PAC, (void**)&pac, buf, size);
-
-    if(rval.code == RC_OK) {
-        char pacDebug[384] = {0};
-        (&asn_DEF_PAC)->op->print_struct(&asn_DEF_PAC, pac, 1, seader_asn_to_string, pacDebug);
-        if(strlen(pacDebug) > 0) {
-            FURI_LOG_D(TAG, "Received pac: %s", pacDebug);
-
-            memset(display, 0, sizeof(display));
-            if(seader_credential->sio[0] == 0x30) {
-                for(uint8_t i = 0; i < sizeof(seader_credential->sio); i++) {
-                    snprintf(
-                        display + (i * 2), sizeof(display), "%02x", seader_credential->sio[i]);
-                }
-                FURI_LOG_D(TAG, "SIO %s", display);
-            }
-        }
-
-        if(pac->size <= sizeof(seader_credential->credential)) {
-            // TODO: make credential into a 12 byte array
-            seader_credential->bit_length = pac->size * 8 - pac->bits_unused;
-            memcpy(&seader_credential->credential, pac->buf, pac->size);
-            seader_credential->credential = __builtin_bswap64(seader_credential->credential);
-            seader_credential->credential = seader_credential->credential >>
-                                            (64 - seader_credential->bit_length);
-            rtn = true;
-        } else {
-            // PACS too big (probably bad data)
-            if(seader_worker->callback) {
-                seader_worker->callback(SeaderWorkerEventFail, seader_worker->context);
-            }
-        }
-    }
-
-    ASN_STRUCT_FREE(asn_DEF_PAC, pac);
-    return rtn;
-}
-
-//    800201298106683d052026b6820101
-//300F800201298106683D052026B6820101
-bool seader_parse_version(SeaderWorker* seader_worker, uint8_t* buf, size_t size) {
-    SamVersion_t* version = 0;
-    version = calloc(1, sizeof *version);
-    assert(version);
-
-    bool rtn = false;
-    if(size > 30) {
-        // Too large to handle now
-        FURI_LOG_W(TAG, "Version of %d is to long to parse", size);
-        return false;
-    }
-    // Add sequence prefix
-    uint8_t seq[32] = {0x30};
-    seq[1] = (uint8_t)size;
-    memcpy(seq + 2, buf, size);
-
-    asn_dec_rval_t rval =
-        asn_decode(0, ATS_DER, &asn_DEF_SamVersion, (void**)&version, seq, size + 2);
-
-    if(rval.code == RC_OK) {
-        char versionDebug[128] = {0};
-        (&asn_DEF_SamVersion)
-            ->op->print_struct(
-                &asn_DEF_SamVersion, version, 1, seader_asn_to_string, versionDebug);
-        if(strlen(versionDebug) > 0) {
-            FURI_LOG_D(TAG, "Received version: %s", versionDebug);
-        }
-        if(version->version.size == 2) {
-            memcpy(seader_worker->sam_version, version->version.buf, version->version.size);
-        }
-
-        rtn = true;
-    }
-
-    ASN_STRUCT_FREE(asn_DEF_SamVersion, version);
-    return rtn;
-}
-
-bool seader_parse_sam_response(SeaderWorker* seader_worker, SamResponse_t* samResponse) {
-    SeaderUartBridge* seader_uart = seader_worker->uart;
-    SeaderCredential* credential = seader_worker->credential;
-
-    if(samResponse->size == 0) {
-        if(requestPacs) {
-            // FURI_LOG_D(TAG, "samResponse %d => requesting PACS", samResponse->size);
-            sendRequestPacs(seader_uart);
-            requestPacs = false;
-        } else {
-            // FURI_LOG_D(TAG, "samResponse %d, no action", samResponse->size);
-            if(seader_worker->callback) {
-                seader_worker->callback(SeaderWorkerEventFail, seader_worker->context);
+        FURI_LOG_I(TAG, "Queue New SAM Message, %d bytes", len);
+        uint32_t space = furi_message_queue_get_space(seader_worker->messages);
+        if(space > 0) {
+            SeaderAPDU seaderApdu = {};
+            seaderApdu.len = len;
+            memcpy(seaderApdu.buf, apdu, len);
+
+            if(furi_mutex_acquire(seader_worker->mq_mutex, FuriWaitForever) == FuriStatusOk) {
+                furi_message_queue_put(seader_worker->messages, &seaderApdu, FuriWaitForever);
+                furi_mutex_release(seader_worker->mq_mutex);
             }
         }
-    } else if(seader_parse_version(seader_worker, samResponse->buf, samResponse->size)) {
-        // no-op
-    } else if(seader_unpack_pacs(seader_worker, credential, samResponse->buf, samResponse->size)) {
-        if(seader_worker->callback) {
-            seader_worker->callback(SeaderWorkerEventSuccess, seader_worker->context);
-        }
-    } else {
-        memset(display, 0, sizeof(display));
-        for(uint8_t i = 0; i < samResponse->size; i++) {
-            snprintf(display + (i * 2), sizeof(display), "%02x", samResponse->buf[i]);
-        }
-        FURI_LOG_D(TAG, "unknown samResponse %d: %s", samResponse->size, display);
     }
-
-    return false;
-}
-
-bool seader_parse_response(SeaderWorker* seader_worker, Response_t* response) {
-    switch(response->present) {
-    case Response_PR_samResponse:
-        seader_parse_sam_response(seader_worker, &response->choice.samResponse);
-        break;
-    default:
-        break;
-    };
-    return false;
-}
-
-void seader_send_nfc_rx(SeaderUartBridge* seader_uart, uint8_t* buffer, size_t len) {
-    OCTET_STRING_t rxData = {.buf = buffer, .size = len};
-    uint8_t status[] = {0x00, 0x00};
-    RfStatus_t rfStatus = {.buf = status, .size = 2};
-
-    NFCRx_t* nfcRx = 0;
-    nfcRx = calloc(1, sizeof *nfcRx);
-    assert(nfcRx);
-
-    nfcRx->rfStatus = rfStatus;
-    nfcRx->data = &rxData;
-
-    NFCResponse_t* nfcResponse = 0;
-    nfcResponse = calloc(1, sizeof *nfcResponse);
-    assert(nfcResponse);
-
-    nfcResponse->present = NFCResponse_PR_nfcRx;
-    nfcResponse->choice.nfcRx = *nfcRx;
-
-    Response_t* response = 0;
-    response = calloc(1, sizeof *response);
-    assert(response);
-
-    response->present = Response_PR_nfcResponse;
-    response->choice.nfcResponse = *nfcResponse;
-
-    seader_send_response(seader_uart, response, 0x14, 0x0a, 0x0);
-
-    ASN_STRUCT_FREE(asn_DEF_NFCRx, nfcRx);
-    ASN_STRUCT_FREE(asn_DEF_NFCResponse, nfcResponse);
-    ASN_STRUCT_FREE(asn_DEF_Response, response);
-}
-
-bool seader_iso14443a_transmit(
-    SeaderWorker* seader_worker,
-    uint8_t* buffer,
-    size_t len,
-    uint16_t timeout,
-    uint8_t format[3]) {
-    SeaderUartBridge* seader_uart = seader_worker->uart;
-    FuriHalNfcTxRxContext tx_rx = {.tx_rx_type = FuriHalNfcTxRxTypeDefault};
-
-    memcpy(&tx_rx.tx_data, buffer, len);
-    tx_rx.tx_bits = len * 8;
-
-    if(format[0] == 0x00 && format[1] == 0xC0 && format[2] == 0x00) {
-        tx_rx.tx_rx_type = FuriHalNfcTxRxTypeRxNoCrc;
-        tx_rx.tx_bits -= 16;
-    } else if(
-        (format[0] == 0x00 && format[1] == 0x00 && format[2] == 0x40) ||
-        (format[0] == 0x00 && format[1] == 0x00 && format[2] == 0x24) ||
-        (format[0] == 0x00 && format[1] == 0x00 && format[2] == 0x44)) {
-        tx_rx.tx_rx_type = FuriHalNfcTxRxTypeRaw;
-        tx_rx.tx_bits -= 8;
-        tx_rx.tx_parity[0] = 0;
-
-        // Don't forget to swap the bits of buffer[8]
-        for(size_t i = 0; i < 8 + 1; i++) {
-            bit_lib_reverse_bits(buffer + i, 0, 8);
-        }
-
-        // Pull out parity bits
-        for(size_t i = 0; i < 8; i++) {
-            bool val = bit_lib_get_bit(buffer + i + 1, i);
-            bit_lib_set_bit(tx_rx.tx_parity, i, val);
-        }
-
-        for(size_t i = 0; i < 8; i++) {
-            buffer[i] = (buffer[i] << i) | (buffer[i + 1] >> (8 - i));
-        }
-
-        for(size_t i = 0; i < 8; i++) {
-            bit_lib_reverse_bits(buffer + i, 0, 8);
-            tx_rx.tx_data[i] = buffer[i];
-        }
-    }
-
-    if(furi_hal_nfc_tx_rx(&tx_rx, timeout)) {
-        furi_delay_ms(1);
-        size_t length = tx_rx.rx_bits / 8;
-        memset(display, 0, sizeof(display));
-        for(uint8_t i = 0; i < length; i++) {
-            snprintf(display + (i * 2), sizeof(display), "%02x", tx_rx.rx_data[i]);
-        }
-        FURI_LOG_D(TAG, "NFC Response %d: %s [%02x]", length, display, tx_rx.rx_parity[0]);
-
-        if(tx_rx.tx_rx_type == FuriHalNfcTxRxTypeRaw) {
-            for(size_t i = 0; i < length; i++) {
-                bit_lib_reverse_bits(tx_rx.rx_data + i, 0, 8);
-            }
-
-            uint8_t with_parity[FURI_HAL_NFC_DATA_BUFF_SIZE];
-            memset(with_parity, 0, sizeof(with_parity));
-            length = length + (length / 8) + 1;
-
-            uint8_t parts = 1 + length / 9;
-            for(size_t p = 0; p < parts; p++) {
-                uint8_t doffset = p * 9;
-                uint8_t soffset = p * 8;
-
-                for(size_t i = 0; i < 9; i++) {
-                    with_parity[i + doffset] = tx_rx.rx_data[i + soffset] >> i;
-                    if(i > 0) {
-                        with_parity[i + doffset] |= tx_rx.rx_data[i + soffset - 1] << (9 - i);
-                    }
-
-                    if(i > 0) {
-                        bool val = bit_lib_get_bit(tx_rx.rx_parity, i - 1);
-                        bit_lib_set_bit(with_parity + i, i - 1, val);
-                    }
-                }
-            }
-
-            memcpy(tx_rx.rx_data, with_parity, length);
-
-            for(size_t i = 0; i < length; i++) {
-                bit_lib_reverse_bits(tx_rx.rx_data + i, 0, 8);
-            }
-
-            memset(display, 0, sizeof(display));
-
-            for(uint8_t i = 0; i < length; i++) {
-                snprintf(display + (i * 2), sizeof(display), "%02x", tx_rx.rx_data[i]);
-            }
-            FURI_LOG_D(
-                TAG, "Mutated NFC Response %d: %s [%02x]", length, display, tx_rx.rx_parity[0]);
-        }
-
-        seader_send_nfc_rx(seader_uart, tx_rx.rx_data, length);
-    } else {
-        FURI_LOG_W(TAG, "Bad exchange");
-        if(seader_worker->callback) {
-            seader_worker->callback(SeaderWorkerEventFail, seader_worker->context);
-        }
-    }
-
-    return false;
-}
-
-uint8_t read4Block6[] = {0x06, 0x06, 0x45, 0x56};
-uint8_t read4Block9[] = {0x06, 0x09, 0xB2, 0xAE};
-uint8_t read4Block10[] = {0x06, 0x0A, 0x29, 0x9C};
-uint8_t read4Block13[] = {0x06, 0x0D, 0x96, 0xE8};
-uint8_t updateBlock2[] = {0x87, 0x02};
-
-void seader_capture_sio(
-    uint8_t* buffer,
-    size_t len,
-    uint8_t* rxBuffer,
-    SeaderCredential* credential) {
-    if(memcmp(buffer, read4Block6, len) == 0 && rxBuffer[0] == 0x30) {
-        memcpy(credential->sio, rxBuffer, 32);
-    } else if(memcmp(buffer, read4Block10, len) == 0 && rxBuffer[0] == 0x30) {
-        memcpy(credential->sio, rxBuffer, 32);
-    } else if(memcmp(buffer, read4Block9, len) == 0) {
-        memcpy(credential->sio + 32, rxBuffer + 8, 24);
-    } else if(memcmp(buffer, read4Block13, len) == 0) {
-        memcpy(credential->sio + 32, rxBuffer + 8, 24);
-    }
-}
-
-FuriHalNfcReturn
-    seader_worker_fake_epurse_update(uint8_t* buffer, uint8_t* rxBuffer, uint16_t* recvLen) {
-    uint8_t fake_response[10];
-    memset(fake_response, 0, sizeof(fake_response));
-    memcpy(fake_response + 0, buffer + 6, 4);
-    memcpy(fake_response + 4, buffer + 2, 4);
-
-    uint16_t crc = seader_worker_picopass_calculate_ccitt(0xE012, fake_response, 8);
-    memcpy(fake_response + 8, &crc, sizeof(uint16_t));
-
-    memcpy(rxBuffer, fake_response, sizeof(fake_response));
-    *recvLen = sizeof(fake_response);
-
-    memset(display, 0, sizeof(display));
-    for(uint8_t i = 0; i < sizeof(fake_response); i++) {
-        snprintf(display + (i * 2), sizeof(display), "%02x", fake_response[i]);
-    }
-    FURI_LOG_I(TAG, "Fake update E-Purse response: %s", display);
-    return FuriHalNfcReturnOk;
-}
-
-bool seader_iso15693_transmit(SeaderWorker* seader_worker, uint8_t* buffer, size_t len) {
-    SeaderUartBridge* seader_uart = seader_worker->uart;
-    SeaderCredential* credential = seader_worker->credential;
-    char display[SEADER_UART_RX_BUF_SIZE * 2 + 1] = {0};
-    FuriHalNfcReturn ret;
-    uint16_t recvLen = 0;
-    uint32_t flags = RFAL_PICOPASS_TXRX_FLAGS;
-    uint32_t fwt = furi_hal_nfc_ll_ms2fc(20);
-
-    uint8_t rxBuffer[64] = {0};
-
-    if(memcmp(buffer, updateBlock2, sizeof(updateBlock2)) == 0) {
-        ret = seader_worker_fake_epurse_update(buffer, rxBuffer, &recvLen);
-    } else {
-        ret = furi_hal_nfc_ll_txrx(buffer, len, rxBuffer, sizeof(rxBuffer), &recvLen, flags, fwt);
-    }
-
-    if(ret == FuriHalNfcReturnOk) {
-        memset(display, 0, sizeof(display));
-        for(uint8_t i = 0; i < recvLen; i++) {
-            snprintf(display + (i * 2), sizeof(display), "%02x", rxBuffer[i]);
-        }
-        // FURI_LOG_D(TAG, "Result %d %s", recvLen, display);
-        seader_capture_sio(buffer, len, rxBuffer, credential);
-        seader_send_nfc_rx(seader_uart, rxBuffer, recvLen);
-    } else if(ret == FuriHalNfcReturnCrc) {
-        memset(display, 0, sizeof(display));
-        for(uint8_t i = 0; i < recvLen; i++) {
-            snprintf(display + (i * 2), sizeof(display), "%02x", rxBuffer[i]);
-        }
-        // FURI_LOG_D(TAG, "[CRC error] Result %d %s", recvLen, display);
-        seader_capture_sio(buffer, len, rxBuffer, credential);
-        seader_send_nfc_rx(seader_uart, rxBuffer, recvLen);
-
-        // Act as if it was OK
-        return true;
-    } else {
-        FURI_LOG_E(TAG, "furi_hal_nfc_ll_txrx Error %d", ret);
-
-        if(seader_worker->callback) {
-            seader_worker->callback(SeaderWorkerEventFail, seader_worker->context);
-        }
-    }
-
-    return ret == FuriHalNfcReturnOk;
-}
-
-bool seader_parse_nfc_command_transmit(SeaderWorker* seader_worker, NFCSend_t* nfcSend) {
-    long timeOut = nfcSend->timeOut;
-    Protocol_t protocol = nfcSend->protocol;
-    FrameProtocol_t frameProtocol = protocol.buf[1];
-
-#ifdef ASN1_DEBUG
-    memset(display, 0, sizeof(display));
-    for(uint8_t i = 0; i < nfcSend->data.size; i++) {
-        snprintf(display + (i * 2), sizeof(display), "%02x", nfcSend->data.buf[i]);
-    }
-
-    char protocolName[8] = {0};
-    memset(protocolName, 0, sizeof(protocolName));
-    (&asn_DEF_FrameProtocol)
-        ->op->print_struct(
-            &asn_DEF_FrameProtocol, &frameProtocol, 1, seader_asn_to_string, protocolName);
-
-    FURI_LOG_D(
-        TAG,
-        "Transmit (%ld timeout) %d bytes [%s] via %s",
-        timeOut,
-        nfcSend->data.size,
-        display,
-        protocolName);
-#endif
-
-    if(frameProtocol == FrameProtocol_iclass) {
-        return seader_iso15693_transmit(seader_worker, nfcSend->data.buf, nfcSend->data.size);
-    } else if(frameProtocol == FrameProtocol_nfc) {
-        return seader_iso14443a_transmit(
-            seader_worker,
-            nfcSend->data.buf,
-            nfcSend->data.size,
-            (uint16_t)timeOut,
-            nfcSend->format->buf);
-    }
-    return false;
-}
-
-bool seader_parse_nfc_off(SeaderUartBridge* seader_uart) {
-    FURI_LOG_D(TAG, "Set Field Off");
-    seader_worker_disable_field(ERR_NONE);
-    seader_nfc_scene_field_on_exit();
-
-    NFCResponse_t* nfcResponse = 0;
-    nfcResponse = calloc(1, sizeof *nfcResponse);
-    assert(nfcResponse);
-
-    nfcResponse->present = NFCResponse_PR_nfcAck;
-
-    Response_t* response = 0;
-    response = calloc(1, sizeof *response);
-    assert(response);
-
-    response->present = Response_PR_nfcResponse;
-    response->choice.nfcResponse = *nfcResponse;
-
-    seader_send_response(seader_uart, response, 0x44, 0x0a, 0);
-
-    ASN_STRUCT_FREE(asn_DEF_Response, response);
-    ASN_STRUCT_FREE(asn_DEF_NFCResponse, nfcResponse);
-
-    return false;
-}
-
-bool seader_parse_nfc_command(SeaderWorker* seader_worker, NFCCommand_t* nfcCommand) {
-    SeaderUartBridge* seader_uart = seader_worker->uart;
-    switch(nfcCommand->present) {
-    case NFCCommand_PR_nfcSend:
-        seader_parse_nfc_command_transmit(seader_worker, &nfcCommand->choice.nfcSend);
-        break;
-    case NFCCommand_PR_nfcOff:
-        seader_parse_nfc_off(seader_uart);
-        break;
-    default:
-        FURI_LOG_W(TAG, "unparsed NFCCommand");
-        break;
-    };
-
-    return false;
-}
-
-bool seader_worker_state_machine(SeaderWorker* seader_worker, Payload_t* payload) {
-    switch(payload->present) {
-    case Payload_PR_response:
-        seader_parse_response(seader_worker, &payload->choice.response);
-        break;
-    case Payload_PR_nfcCommand:
-        seader_parse_nfc_command(seader_worker, &payload->choice.nfcCommand);
-        break;
-    case Payload_PR_errorResponse:
-        // TODO: screen saying this was a failure, or maybe start over?
-        if(seader_worker->callback) {
-            seader_worker->callback(SeaderWorkerEventFail, seader_worker->context);
-        }
-        break;
-    default:
-        FURI_LOG_W(TAG, "unhandled payload");
-        break;
-    };
-
-    return false;
-}
-
-bool seader_process_success_response(SeaderWorker* seader_worker, uint8_t* apdu, size_t len) {
-    Payload_t* payload = 0;
-    payload = calloc(1, sizeof *payload);
-    assert(payload);
-
-    asn_dec_rval_t rval =
-        asn_decode(0, ATS_DER, &asn_DEF_Payload, (void**)&payload, apdu + 6, len - 6);
-    if(rval.code == RC_OK) {
-#ifdef ASN1_DEBUG
-        memset(payloadDebug, 0, sizeof(payloadDebug));
-        (&asn_DEF_Payload)
-            ->op->print_struct(&asn_DEF_Payload, payload, 1, seader_asn_to_string, payloadDebug);
-        if(strlen(payloadDebug) > 0) {
-            FURI_LOG_D(TAG, "Received payload: %s", payloadDebug);
-        }
-#endif
-        seader_worker_state_machine(seader_worker, payload);
-    }
-
-    ASN_STRUCT_FREE(asn_DEF_Payload, payload);
-    return (rval.code == RC_OK);
+    return true;
 }
 
-bool seader_process_apdu(SeaderWorker* seader_worker, uint8_t* apdu, size_t len) {
+bool seader_process_apdu(Seader* seader, uint8_t* apdu, size_t len) {
+    SeaderWorker* seader_worker = seader->worker;
     SeaderUartBridge* seader_uart = seader_worker->uart;
     if(len < 2) {
         return false;
     }
-    /*
     memset(display, 0, sizeof(display));
     for(uint8_t i = 0; i < len; i++) {
         snprintf(display + (i * 2), sizeof(display), "%02x", apdu[i]);
     }
-    FURI_LOG_I(TAG, "APDU: %s", display);
-    */
+    // FURI_LOG_I(TAG, "APDU: %s", display);
 
     uint8_t SW1 = apdu[len - 2];
     uint8_t SW2 = apdu[len - 1];
@@ -900,7 +140,7 @@ bool seader_process_apdu(SeaderWorker* seader_worker, uint8_t* apdu, size_t len)
     case 0x90:
         if(SW2 == 0x00) {
             if(len > 2) {
-                return seader_process_success_response(seader_worker, apdu, len - 2);
+                return seader_process_success_response(seader, apdu, len - 2);
             }
         }
         break;
@@ -909,140 +149,122 @@ bool seader_process_apdu(SeaderWorker* seader_worker, uint8_t* apdu, size_t len)
     return false;
 }
 
-ReturnCode seader_picopass_card_init(SeaderWorker* seader_worker) {
-    SeaderUartBridge* seader_uart = seader_worker->uart;
-    SeaderCredential* credential = seader_worker->credential;
-    rfalPicoPassIdentifyRes idRes;
-    rfalPicoPassSelectRes selRes;
-
-    ReturnCode err;
-
-    err = rfalPicoPassPollerIdentify(&idRes);
-    if(err != ERR_NONE) {
-        FURI_LOG_E(TAG, "rfalPicoPassPollerIdentify error %d", err);
-        return err;
-    }
+void seader_worker_process_sam_message(Seader* seader, CCID_Message* message) {
+    // TODO: inline seader_process_apdu
+    seader_process_apdu(seader, message->payload, message->dwLength);
+}
 
-    err = rfalPicoPassPollerSelect(idRes.CSN, &selRes);
-    if(err != ERR_NONE) {
-        FURI_LOG_E(TAG, "rfalPicoPassPollerSelect error %d", err);
-        return err;
-    }
+int32_t seader_worker_task(void* context) {
+    SeaderWorker* seader_worker = context;
+    SeaderUartBridge* seader_uart = seader_worker->uart;
 
-    memset(display, 0, sizeof(display));
-    for(uint8_t i = 0; i < RFAL_PICOPASS_MAX_BLOCK_LEN; i++) {
-        snprintf(display + (i * 2), sizeof(display), "%02x", selRes.CSN[i]);
+    if(seader_worker->state == SeaderWorkerStateCheckSam) {
+        FURI_LOG_D(TAG, "Check for SAM");
+        seader_ccid_check_for_sam(seader_uart);
     }
+    seader_worker_change_state(seader_worker, SeaderWorkerStateReady);
 
-    FURI_LOG_D(TAG, "Sending card detected info: %s", display);
-
-    CardDetails_t* cardDetails = 0;
-    cardDetails = calloc(1, sizeof *cardDetails);
-    assert(cardDetails);
-
-    uint8_t protocolBytes[] = {0x00, FrameProtocol_iclass};
-    OCTET_STRING_fromBuf(
-        &cardDetails->protocol, (const char*)protocolBytes, sizeof(protocolBytes));
-    OCTET_STRING_fromBuf(&cardDetails->csn, (const char*)selRes.CSN, RFAL_PICOPASS_MAX_BLOCK_LEN);
-
-    memcpy(credential->diversifier, selRes.CSN, RFAL_PICOPASS_MAX_BLOCK_LEN);
-
-    seader_send_card_detected(seader_uart, cardDetails);
-
-    ASN_STRUCT_FREE(asn_DEF_CardDetails, cardDetails);
-    return ERR_NONE;
+    return 0;
 }
 
-ReturnCode seader_picopass_card_detect() {
-    ReturnCode err;
+void seader_worker_poller_conversation(Seader* seader, SeaderPollerContainer* spc) {
+    SeaderWorker* seader_worker = seader->worker;
 
-    err = rfalPicoPassPollerInitialize();
-    if(err != ERR_NONE) {
-        FURI_LOG_E(TAG, "rfalPicoPassPollerInitialize error %d", err);
-        return err;
-    }
+    if(furi_mutex_acquire(seader_worker->mq_mutex, 0) == FuriStatusOk) {
+        furi_thread_set_current_priority(FuriThreadPriorityHighest);
+        uint32_t count = furi_message_queue_get_count(seader_worker->messages);
+        if(count > 0) {
+            FURI_LOG_D(TAG, "MessageQueue: %ld messages", count);
 
-    err = rfalFieldOnAndStartGT();
-    if(err != ERR_NONE) {
-        FURI_LOG_E(TAG, "rfalFieldOnAndStartGT error %d", err);
-        return err;
-    }
+            SeaderAPDU seaderApdu = {};
+            FuriStatus status =
+                furi_message_queue_get(seader_worker->messages, &seaderApdu, FuriWaitForever);
+            if(status != FuriStatusOk) {
+                FURI_LOG_W(TAG, "furi_message_queue_get fail %d", status);
+                seader_worker->stage = SeaderPollerEventTypeComplete;
+            }
 
-    err = rfalPicoPassPollerCheckPresence();
-    if(err != ERR_RF_COLLISION) {
-        if(err != ERR_TIMEOUT) {
-            FURI_LOG_E(TAG, "rfalPicoPassPollerCheckPresence error %d", err);
+            if(seader_process_success_response_i(
+                   seader, seaderApdu.buf, seaderApdu.len, true, spc)) {
+                // no-op
+            } else {
+                FURI_LOG_I(TAG, "Response false");
+                seader_worker->stage = SeaderPollerEventTypeComplete;
+            }
         }
-        return err;
+        furi_mutex_release(seader_worker->mq_mutex);
+    } else {
+        furi_thread_set_current_priority(FuriThreadPriorityLowest);
     }
-
-    return ERR_NONE;
 }
 
-ReturnCode seader_picopass_card_read(SeaderWorker* seader_worker) {
-    ReturnCode err = ERR_TIMEOUT;
+NfcCommand seader_worker_poller_callback_iso14443_4a(NfcGenericEvent event, void* context) {
+    furi_assert(event.protocol == NfcProtocolIso14443_4a);
+    NfcCommand ret = NfcCommandContinue;
 
-    while(seader_worker->state == SeaderWorkerStateReadPicopass) {
-        // Card found
-        if(seader_picopass_card_detect() == ERR_NONE) {
-            err = seader_picopass_card_init(seader_worker);
-            if(err != ERR_NONE) {
-                FURI_LOG_E(TAG, "picopass_card_init error %d", err);
-            }
-            break;
-        }
+    Seader* seader = context;
+    SeaderWorker* seader_worker = seader->worker;
 
-        furi_delay_ms(100);
-    }
+    const Iso14443_4aPollerEvent* iso14443_4a_event = event.event_data;
+    SeaderPollerContainer spc = {.iso14443_4a_poller = event.instance};
 
-    return err;
-}
+    if(iso14443_4a_event->type == Iso14443_4aPollerEventTypeReady) {
+        if(seader_worker->stage == SeaderPollerEventTypeCardDetect) {
+            nfc_device_set_data(
+                seader->nfc_device, NfcProtocolIso14443_4a, nfc_poller_get_data(seader->poller));
 
-void seader_worker_process_sam_message(SeaderWorker* seader_worker, CCID_Message* message) {
-    if(seader_process_apdu(seader_worker, message->payload, message->dwLength)) {
-        // no-op
-    } else {
-        memset(display, 0, sizeof(display));
-        for(uint8_t i = 0; i < message->dwLength; i++) {
-            snprintf(display + (i * 2), sizeof(display), "%02x", message->payload[i]);
-        }
+            size_t uid_len;
+            const uint8_t* uid = nfc_device_get_uid(seader->nfc_device, &uid_len);
+
+            const Iso14443_3aData* iso14443_3a_data =
+                nfc_device_get_data(seader->nfc_device, NfcProtocolIso14443_3a);
+            uint8_t sak = iso14443_3a_get_sak(iso14443_3a_data);
 
-        FURI_LOG_W(TAG, "Unknown block: [%ld] %s", message->dwLength, display);
-        if(seader_worker->callback) {
-            seader_worker->callback(SeaderWorkerEventFail, seader_worker->context);
+            seader_worker_card_detect(
+                seader, sak, (uint8_t*)iso14443_3a_data->atqa, uid, uid_len, NULL, 0);
+
+            // nfc_set_fdt_poll_fc(event.instance, SEADER_POLLER_MAX_FWT);
+            furi_thread_set_current_priority(FuriThreadPriorityLowest);
+            seader_worker->stage = SeaderPollerEventTypeConversation;
+        } else if(seader_worker->stage == SeaderPollerEventTypeConversation) {
+            seader_worker_poller_conversation(seader, &spc);
+        } else if(seader_worker->stage == SeaderPollerEventTypeComplete) {
+            ret = NfcCommandStop;
         }
+    } else {
+        // add failure callback if failure type
+        FURI_LOG_D(TAG, "14a event type %x", iso14443_4a_event->type);
     }
+
+    return ret;
 }
 
-int32_t seader_worker_task(void* context) {
-    SeaderWorker* seader_worker = context;
-    SeaderUartBridge* seader_uart = seader_worker->uart;
+NfcCommand seader_worker_poller_callback_picopass(PicopassPollerEvent event, void* context) {
+    furi_assert(context);
+    NfcCommand ret = NfcCommandContinue;
 
-    if(seader_worker->state == SeaderWorkerStateCheckSam) {
-        seader_ccid_check_for_sam(seader_uart);
-    } else if(seader_worker->state == SeaderWorkerStateReadPicopass) {
-        FURI_LOG_D(TAG, "Read Picopass");
-        requestPacs = true;
-        seader_credential_clear(seader_worker->credential);
-        seader_worker->credential->type = SeaderCredentialTypePicopass;
-        seader_worker_enable_field();
-        if(seader_picopass_card_read(seader_worker) != ERR_NONE) {
-            // Turn off if cancelled / no card found
-            seader_worker_disable_field(ERR_NONE);
-        }
-    } else if(seader_worker->state == SeaderWorkerStateRead14a) {
-        FURI_LOG_D(TAG, "Read 14a");
-        requestPacs = true;
-        seader_credential_clear(seader_worker->credential);
-        seader_worker->credential->type = SeaderCredentialType14A;
-        seader_nfc_scene_field_on_enter();
-        if(!seader_detect_nfc(seader_worker)) {
-            // Turn off if cancelled / no card found
-            seader_nfc_scene_field_on_exit();
+    Seader* seader = context;
+    SeaderWorker* seader_worker = seader->worker;
+    // I know this is is passing the same thing that is on seader all the way down, but I prefer the symmetry between the 15a and iso15 stuff
+    PicopassPoller* instance = seader->picopass_poller;
+    SeaderPollerContainer spc = {.picopass_poller = instance};
+
+    if(event.type == PicopassPollerEventTypeSuccess) {
+        if(seader_worker->stage == SeaderPollerEventTypeCardDetect) {
+            uint8_t* csn = picopass_poller_get_csn(instance);
+            seader_worker_card_detect(seader, 0, NULL, csn, sizeof(PicopassSerialNum), NULL, 0);
+            furi_thread_set_current_priority(FuriThreadPriorityLowest);
+            seader_worker->stage = SeaderPollerEventTypeConversation;
+        } else if(seader_worker->stage == SeaderPollerEventTypeConversation) {
+            seader_worker_poller_conversation(seader, &spc);
+        } else if(seader_worker->stage == SeaderPollerEventTypeComplete) {
+            ret = NfcCommandStop;
         }
+    } else if(event.type == PicopassPollerEventTypeFail) {
+        ret = NfcCommandStop;
+    } else {
+        FURI_LOG_D(TAG, "picopass event type %x", event.type);
     }
-    FURI_LOG_D(TAG, "Worker Task Complete");
-    seader_worker_change_state(seader_worker, SeaderWorkerStateReady);
 
-    return 0;
+    return ret;
 }

+ 18 - 4
apps_broken_by_last_refactors/seader/seader_worker.h

@@ -1,10 +1,14 @@
 #pragma once
 
+#include <lib/nfc/protocols/iso14443_4a/iso14443_4a_poller.h>
+
+#include "sam_api.h"
 #include "seader_credential.h"
 #include "seader_bridge.h"
 
 typedef struct SeaderWorker SeaderWorker;
 typedef struct CCID_Message CCID_Message;
+typedef struct SeaderAPDU SeaderAPDU;
 
 typedef enum {
     // Init states
@@ -13,8 +17,6 @@ typedef enum {
     SeaderWorkerStateReady,
     // Main worker states
     SeaderWorkerStateCheckSam,
-    SeaderWorkerStateReadPicopass,
-    SeaderWorkerStateRead14a,
     // Transition
     SeaderWorkerStateStop,
 } SeaderWorkerState;
@@ -33,6 +35,15 @@ typedef enum {
     SeaderWorkerEventStartReading,
 } SeaderWorkerEvent;
 
+typedef enum {
+    SeaderPollerEventTypeCardDetect,
+    SeaderPollerEventTypeConversation,
+    SeaderPollerEventTypeComplete,
+
+    SeaderPollerEventTypeSuccess,
+    SeaderPollerEventTypeFail,
+} SeaderPollerEventType;
+
 typedef void (*SeaderWorkerCallback)(SeaderWorkerEvent event, void* context);
 
 SeaderWorker* seader_worker_alloc();
@@ -45,10 +56,13 @@ void seader_worker_start(
     SeaderWorker* seader_worker,
     SeaderWorkerState state,
     SeaderUartBridge* uart,
-    SeaderCredential* credential,
     SeaderWorkerCallback callback,
     void* context);
 
 void seader_worker_stop(SeaderWorker* seader_worker);
-void seader_worker_process_sam_message(SeaderWorker* seader_worker, CCID_Message* message);
+void seader_worker_process_sam_message(Seader* seader, CCID_Message* message);
 void seader_worker_send_version(SeaderWorker* seader_worker);
+
+NfcCommand seader_worker_poller_callback_iso14443_4a(NfcGenericEvent event, void* context);
+
+NfcCommand seader_worker_poller_callback_picopass(PicopassPollerEvent event, void* context);

+ 12 - 5
apps_broken_by_last_refactors/seader/seader_worker_i.h

@@ -1,7 +1,7 @@
 #pragma once
 
-#include "seader_worker.h"
 #include "seader_i.h"
+#include "seader_worker.h"
 
 #include <furi.h>
 #include <lib/toolbox/stream/file_stream.h>
@@ -9,26 +9,33 @@
 #include <furi_hal.h>
 
 #include <stdlib.h>
-#include <rfal_rf.h>
 
-#include <platform.h>
 #include <PAC.h>
 #include <SamVersion.h>
 
+#define SEADER_POLLER_MAX_FWT (200000U)
+#define SEADER_POLLER_MAX_BUFFER_SIZE (255U)
+
 struct SeaderWorker {
     FuriThread* thread;
     Storage* storage;
-    Stream* dict_stream;
     uint8_t sam_version[2];
+    FuriMessageQueue* messages;
+    FuriMutex* mq_mutex;
 
     SeaderUartBridge* uart;
-    SeaderCredential* credential;
     SeaderWorkerCallback callback;
     void* context;
 
+    SeaderPollerEventType stage;
     SeaderWorkerState state;
 };
 
+struct SeaderAPDU {
+    size_t len;
+    uint8_t buf[SEADER_POLLER_MAX_BUFFER_SIZE];
+};
+
 void seader_worker_change_state(SeaderWorker* seader_worker, SeaderWorkerState state);
 
 int32_t seader_worker_task(void* context);

+ 4 - 5
apps_broken_by_last_refactors/seader/uart.c

@@ -39,7 +39,6 @@ void seader_uart_set_baudrate(SeaderUartBridge* seader_uart, uint32_t baudrate)
 }
 
 size_t seader_uart_process_buffer(Seader* seader, uint8_t* cmd, size_t cmd_len) {
-    SeaderWorker* seader_worker = seader->worker;
     SeaderUartBridge* seader_uart = seader->uart;
     if(cmd_len < 2) {
         return cmd_len;
@@ -47,7 +46,7 @@ size_t seader_uart_process_buffer(Seader* seader, uint8_t* cmd, size_t cmd_len)
 
     size_t consumed = 0;
     do {
-        consumed = seader_ccid_process(seader_worker, cmd, cmd_len);
+        consumed = seader_ccid_process(seader, cmd, cmd_len);
 
         if(consumed > 0) {
             memset(cmd, 0, consumed);
@@ -72,6 +71,7 @@ size_t seader_uart_process_buffer(Seader* seader, uint8_t* cmd, size_t cmd_len)
 int32_t seader_uart_worker(void* context) {
     Seader* seader = (Seader*)context;
     SeaderUartBridge* seader_uart = seader->uart;
+    furi_thread_set_current_priority(FuriThreadPriorityHighest);
 
     memcpy(&seader_uart->cfg, &seader_uart->cfg_new, sizeof(SeaderUartConfig));
 
@@ -154,6 +154,7 @@ int32_t seader_uart_tx_thread(void* context) {
     Seader* seader = (Seader*)context;
     SeaderUartBridge* seader_uart = seader->uart;
 
+    furi_thread_set_current_priority(FuriThreadPriorityHighest);
     while(1) {
         uint32_t events =
             furi_thread_flags_wait(WORKER_ALL_TX_EVENTS, FuriFlagWaitAny, FuriWaitForever);
@@ -161,13 +162,11 @@ int32_t seader_uart_tx_thread(void* context) {
         if(events & WorkerEvtTxStop) break;
         if(events & WorkerEvtSamRx) {
             if(seader_uart->tx_len > 0) {
-                /*
                 char display[SEADER_UART_RX_BUF_SIZE * 2 + 1] = {0};
                 for(uint8_t i = 0; i < seader_uart->tx_len; i++) {
                     snprintf(display + (i * 2), sizeof(display), "%02x", seader_uart->tx_buf[i]);
                 }
-                FURI_LOG_I(TAG, "SEND %d bytes: %s", seader_uart->tx_len, display);
-                */
+                // FURI_LOG_I(TAG, "SEND %d bytes: %s", seader_uart->tx_len, display);
                 seader_uart->st.tx_cnt += seader_uart->tx_len;
                 furi_hal_uart_tx(
                     seader_uart->cfg.uart_ch, seader_uart->tx_buf, seader_uart->tx_len);

+ 0 - 152
apps_broken_by_last_refactors/seader/views/seader_uart_view.c

@@ -1,152 +0,0 @@
-#include "../seader_bridge.h"
-#include "../seader_i.h"
-#include <furi_hal.h>
-#include <gui/elements.h>
-
-struct SeaderUartView {
-    View* view;
-    SeaderUartViewCallback callback;
-    void* context;
-};
-
-typedef struct {
-    uint32_t tx_cnt;
-    uint32_t rx_cnt;
-    bool tx_active;
-    bool rx_active;
-    uint8_t protocol;
-} SeaderUartViewModel;
-
-static void seader_uart_view_draw_callback(Canvas* canvas, void* _model) {
-    SeaderUartViewModel* model = _model;
-    char temp_str[18];
-
-    canvas_set_font(canvas, FontPrimary);
-    canvas_draw_str_aligned(canvas, 64, 0, AlignCenter, AlignTop, "PICC <=> SAM");
-    canvas_draw_str(canvas, 3, 25, "TX:");
-    canvas_draw_str(canvas, 3, 42, "RX:");
-
-    if(model->protocol == FrameProtocol_iclass) {
-        canvas_draw_str_aligned(canvas, 64, 62, AlignCenter, AlignBottom, "Detecting picopass");
-    } else if(model->protocol == FrameProtocol_nfc) {
-        canvas_draw_str_aligned(canvas, 64, 62, AlignCenter, AlignBottom, "Detecting 14a");
-    }
-
-    if(model->tx_cnt < 100000000) {
-        canvas_set_font(canvas, FontSecondary);
-        canvas_draw_str_aligned(canvas, 127, 24, AlignRight, AlignBottom, "B.");
-        canvas_set_font(canvas, FontKeyboard);
-        snprintf(temp_str, 18, "%lu", model->tx_cnt);
-        canvas_draw_str_aligned(canvas, 116, 24, AlignRight, AlignBottom, temp_str);
-    } else {
-        canvas_set_font(canvas, FontSecondary);
-        canvas_draw_str_aligned(canvas, 127, 24, AlignRight, AlignBottom, "KiB.");
-        canvas_set_font(canvas, FontKeyboard);
-        snprintf(temp_str, 18, "%lu", model->tx_cnt / 1024);
-        canvas_draw_str_aligned(canvas, 111, 24, AlignRight, AlignBottom, temp_str);
-    }
-
-    if(model->rx_cnt < 100000000) {
-        canvas_set_font(canvas, FontSecondary);
-        canvas_draw_str_aligned(canvas, 127, 41, AlignRight, AlignBottom, "B.");
-        canvas_set_font(canvas, FontKeyboard);
-        snprintf(temp_str, 18, "%lu", model->rx_cnt);
-        canvas_draw_str_aligned(canvas, 116, 41, AlignRight, AlignBottom, temp_str);
-    } else {
-        canvas_set_font(canvas, FontSecondary);
-        canvas_draw_str_aligned(canvas, 127, 41, AlignRight, AlignBottom, "KiB.");
-        canvas_set_font(canvas, FontKeyboard);
-        snprintf(temp_str, 18, "%lu", model->rx_cnt / 1024);
-        canvas_draw_str_aligned(canvas, 111, 41, AlignRight, AlignBottom, temp_str);
-    }
-
-    if(model->tx_active) {
-        canvas_draw_icon(canvas, 48, 14, &I_ArrowUpFilled_14x15);
-    } else {
-        canvas_draw_icon(canvas, 48, 14, &I_ArrowUpEmpty_14x15);
-    }
-
-    if(model->rx_active) {
-        canvas_draw_icon_ex(canvas, 48, 34, &I_ArrowUpFilled_14x15, IconRotation180);
-    } else {
-        canvas_draw_icon_ex(canvas, 48, 34, &I_ArrowUpEmpty_14x15, IconRotation180);
-    }
-}
-
-static bool seader_uart_view_input_callback(InputEvent* event, void* context) {
-    furi_assert(context);
-    SeaderUartView* seader_uart_view = context;
-    bool consumed = false;
-
-    if(event->type == InputTypeShort) {
-        if(event->key == InputKeyLeft) {
-            consumed = true;
-            furi_assert(seader_uart_view->callback);
-            // seader_uart_view->callback(SeaderUartViewEventConfig, seader_uart_view->context);
-        }
-    }
-
-    return consumed;
-}
-
-SeaderUartView* seader_uart_view_alloc() {
-    SeaderUartView* seader_uart_view = malloc(sizeof(SeaderUartView));
-
-    seader_uart_view->view = view_alloc();
-    view_allocate_model(seader_uart_view->view, ViewModelTypeLocking, sizeof(SeaderUartViewModel));
-    view_set_context(seader_uart_view->view, seader_uart_view);
-    view_set_draw_callback(seader_uart_view->view, seader_uart_view_draw_callback);
-    view_set_input_callback(seader_uart_view->view, seader_uart_view_input_callback);
-
-    return seader_uart_view;
-}
-
-void seader_uart_view_free(SeaderUartView* seader_uart_view) {
-    furi_assert(seader_uart_view);
-    view_free(seader_uart_view->view);
-    free(seader_uart_view);
-}
-
-View* seader_uart_view_get_view(SeaderUartView* seader_uart_view) {
-    furi_assert(seader_uart_view);
-    return seader_uart_view->view;
-}
-
-void seader_uart_view_set_callback(
-    SeaderUartView* seader_uart_view,
-    SeaderUartViewCallback callback,
-    void* context) {
-    furi_assert(seader_uart_view);
-    furi_assert(callback);
-
-    with_view_model(
-        seader_uart_view->view,
-        SeaderUartViewModel * model,
-        {
-            UNUSED(model);
-            seader_uart_view->callback = callback;
-            seader_uart_view->context = context;
-        },
-        false);
-}
-
-void seader_uart_view_update_state(
-    SeaderUartView* instance,
-    SeaderUartConfig* cfg,
-    SeaderUartState* st) {
-    furi_assert(instance);
-    furi_assert(cfg);
-    furi_assert(st);
-
-    with_view_model(
-        instance->view,
-        SeaderUartViewModel * model,
-        {
-            model->tx_active = (model->tx_cnt != st->tx_cnt);
-            model->rx_active = (model->rx_cnt != st->rx_cnt);
-            model->tx_cnt = st->tx_cnt;
-            model->rx_cnt = st->rx_cnt;
-            model->protocol = st->protocol;
-        },
-        true);
-}

+ 0 - 24
apps_broken_by_last_refactors/seader/views/seader_uart_view.h

@@ -1,24 +0,0 @@
-#pragma once
-
-#include <gui/view.h>
-#include "../seader_custom_event.h"
-#include "../seader_bridge.h"
-
-typedef struct SeaderUartView SeaderUartView;
-typedef void (*SeaderUartViewCallback)(SeaderCustomEvent event, void* context);
-
-SeaderUartView* seader_uart_view_alloc();
-
-void seader_uart_view_free(SeaderUartView* seader_uart_view);
-
-View* seader_uart_view_get_view(SeaderUartView* seader_uart_view);
-
-void seader_uart_view_set_callback(
-    SeaderUartView* seader_uart_view,
-    SeaderUartViewCallback callback,
-    void* context);
-
-void seader_uart_view_update_state(
-    SeaderUartView* instance,
-    SeaderUartConfig* cfg,
-    SeaderUartState* st);