* Added basic template of Felica listener * Raw nfc felica listener functions * Added functions to setup chip for felica listener * Cleanup function templates from unnecessary parts * Removed todo comment * Updated api versions * Adjusted chip config for felica * Set proper chip passive target mode for felica * Added felica function to unit tests * Update furi_hal_nfc_felica.c * Removed duplication Co-authored-by: gornekich <n.gorbadey@gmail.com>
		
			
				
	
	
		
			474 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			474 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
#ifdef FW_CFG_unit_tests
 | 
						|
 | 
						|
#include <lib/nfc/nfc.h>
 | 
						|
#include <lib/nfc/helpers/iso14443_crc.h>
 | 
						|
#include <lib/nfc/protocols/iso14443_3a/iso14443_3a.h>
 | 
						|
 | 
						|
#include <furi/furi.h>
 | 
						|
 | 
						|
#define NFC_MAX_BUFFER_SIZE (256)
 | 
						|
 | 
						|
typedef enum {
 | 
						|
    NfcTransportLogLevelWarning,
 | 
						|
    NfcTransportLogLevelInfo,
 | 
						|
} NfcTransportLogLevel;
 | 
						|
 | 
						|
FuriMessageQueue* poller_queue = NULL;
 | 
						|
FuriMessageQueue* listener_queue = NULL;
 | 
						|
 | 
						|
typedef enum {
 | 
						|
    NfcMessageTypeTx,
 | 
						|
    NfcMessageTypeTimeout,
 | 
						|
    NfcMessageTypeAbort,
 | 
						|
} NfcMessageType;
 | 
						|
 | 
						|
typedef struct {
 | 
						|
    uint16_t data_bits;
 | 
						|
    uint8_t data[NFC_MAX_BUFFER_SIZE];
 | 
						|
} NfcMessageData;
 | 
						|
 | 
						|
typedef struct {
 | 
						|
    NfcMessageType type;
 | 
						|
    NfcMessageData data;
 | 
						|
} NfcMessage;
 | 
						|
 | 
						|
typedef enum {
 | 
						|
    NfcStateIdle,
 | 
						|
    NfcStateReady,
 | 
						|
    NfcStateReset,
 | 
						|
} NfcState;
 | 
						|
 | 
						|
typedef enum {
 | 
						|
    Iso14443_3aColResStatusIdle,
 | 
						|
    Iso14443_3aColResStatusInProgress,
 | 
						|
    Iso14443_3aColResStatusDone,
 | 
						|
} Iso14443_3aColResStatus;
 | 
						|
 | 
						|
typedef struct {
 | 
						|
    Iso14443_3aSensResp sens_resp;
 | 
						|
    Iso14443_3aSddResp sdd_resp[2];
 | 
						|
    Iso14443_3aSelResp sel_resp[2];
 | 
						|
} Iso14443_3aColResData;
 | 
						|
 | 
						|
struct Nfc {
 | 
						|
    NfcState state;
 | 
						|
 | 
						|
    Iso14443_3aColResStatus col_res_status;
 | 
						|
    Iso14443_3aColResData col_res_data;
 | 
						|
 | 
						|
    NfcEventCallback callback;
 | 
						|
    void* context;
 | 
						|
 | 
						|
    NfcMode mode;
 | 
						|
 | 
						|
    FuriThread* worker_thread;
 | 
						|
};
 | 
						|
 | 
						|
static void nfc_test_print(
 | 
						|
    NfcTransportLogLevel log_level,
 | 
						|
    const char* message,
 | 
						|
    uint8_t* buffer,
 | 
						|
    uint16_t bits) {
 | 
						|
    FuriString* str = furi_string_alloc();
 | 
						|
    size_t bytes = (bits + 7) / 8;
 | 
						|
 | 
						|
    for(size_t i = 0; i < bytes; i++) {
 | 
						|
        furi_string_cat_printf(str, " %02X", buffer[i]);
 | 
						|
    }
 | 
						|
    if(log_level == NfcTransportLogLevelWarning) {
 | 
						|
        FURI_LOG_W(message, "%s", furi_string_get_cstr(str));
 | 
						|
    } else {
 | 
						|
        FURI_LOG_I(message, "%s", furi_string_get_cstr(str));
 | 
						|
    }
 | 
						|
 | 
						|
    furi_string_free(str);
 | 
						|
}
 | 
						|
 | 
						|
static void nfc_prepare_col_res_data(
 | 
						|
    Nfc* instance,
 | 
						|
    uint8_t* uid,
 | 
						|
    uint8_t uid_len,
 | 
						|
    uint8_t* atqa,
 | 
						|
    uint8_t sak) {
 | 
						|
    memcpy(instance->col_res_data.sens_resp.sens_resp, atqa, 2);
 | 
						|
 | 
						|
    if(uid_len == 7) {
 | 
						|
        instance->col_res_data.sdd_resp[0].nfcid[0] = 0x88;
 | 
						|
        memcpy(&instance->col_res_data.sdd_resp[0].nfcid[1], uid, 3);
 | 
						|
        uint8_t bss = 0;
 | 
						|
        for(size_t i = 0; i < 4; i++) {
 | 
						|
            bss ^= instance->col_res_data.sdd_resp[0].nfcid[i];
 | 
						|
        }
 | 
						|
        instance->col_res_data.sdd_resp[0].bss = bss;
 | 
						|
        instance->col_res_data.sel_resp[0].sak = 0x04;
 | 
						|
 | 
						|
        memcpy(instance->col_res_data.sdd_resp[1].nfcid, &uid[3], 4);
 | 
						|
        bss = 0;
 | 
						|
        for(size_t i = 0; i < 4; i++) {
 | 
						|
            bss ^= instance->col_res_data.sdd_resp[1].nfcid[i];
 | 
						|
        }
 | 
						|
        instance->col_res_data.sdd_resp[1].bss = bss;
 | 
						|
        instance->col_res_data.sel_resp[1].sak = sak;
 | 
						|
 | 
						|
    } else {
 | 
						|
        furi_crash("Not supporting not 7 bytes");
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
Nfc* nfc_alloc() {
 | 
						|
    Nfc* instance = malloc(sizeof(Nfc));
 | 
						|
 | 
						|
    return instance;
 | 
						|
}
 | 
						|
 | 
						|
void nfc_free(Nfc* instance) {
 | 
						|
    furi_check(instance);
 | 
						|
 | 
						|
    free(instance);
 | 
						|
}
 | 
						|
 | 
						|
void nfc_config(Nfc* instance, NfcMode mode, NfcTech tech) {
 | 
						|
    UNUSED(instance);
 | 
						|
    UNUSED(tech);
 | 
						|
 | 
						|
    instance->mode = mode;
 | 
						|
}
 | 
						|
 | 
						|
void nfc_set_fdt_poll_fc(Nfc* instance, uint32_t fdt_poll_fc) {
 | 
						|
    UNUSED(instance);
 | 
						|
    UNUSED(fdt_poll_fc);
 | 
						|
}
 | 
						|
 | 
						|
void nfc_set_fdt_listen_fc(Nfc* instance, uint32_t fdt_listen_fc) {
 | 
						|
    UNUSED(instance);
 | 
						|
    UNUSED(fdt_listen_fc);
 | 
						|
}
 | 
						|
 | 
						|
void nfc_set_mask_receive_time_fc(Nfc* instance, uint32_t mask_rx_time_fc) {
 | 
						|
    UNUSED(instance);
 | 
						|
    UNUSED(mask_rx_time_fc);
 | 
						|
}
 | 
						|
 | 
						|
void nfc_set_fdt_poll_poll_us(Nfc* instance, uint32_t fdt_poll_poll_us) {
 | 
						|
    UNUSED(instance);
 | 
						|
    UNUSED(fdt_poll_poll_us);
 | 
						|
}
 | 
						|
 | 
						|
void nfc_set_guard_time_us(Nfc* instance, uint32_t guard_time_us) {
 | 
						|
    UNUSED(instance);
 | 
						|
    UNUSED(guard_time_us);
 | 
						|
}
 | 
						|
 | 
						|
NfcError nfc_iso14443a_listener_set_col_res_data(
 | 
						|
    Nfc* instance,
 | 
						|
    uint8_t* uid,
 | 
						|
    uint8_t uid_len,
 | 
						|
    uint8_t* atqa,
 | 
						|
    uint8_t sak) {
 | 
						|
    furi_check(instance);
 | 
						|
    furi_check(uid);
 | 
						|
    furi_check(atqa);
 | 
						|
 | 
						|
    nfc_prepare_col_res_data(instance, uid, uid_len, atqa, sak);
 | 
						|
 | 
						|
    return NfcErrorNone;
 | 
						|
}
 | 
						|
 | 
						|
static int32_t nfc_worker_poller(void* context) {
 | 
						|
    Nfc* instance = context;
 | 
						|
    furi_check(instance->callback);
 | 
						|
 | 
						|
    instance->state = NfcStateReady;
 | 
						|
    NfcCommand command = NfcCommandContinue;
 | 
						|
    NfcEvent event = {};
 | 
						|
 | 
						|
    while(true) {
 | 
						|
        event.type = NfcEventTypePollerReady;
 | 
						|
        command = instance->callback(event, instance->context);
 | 
						|
        if(command == NfcCommandStop) {
 | 
						|
            break;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    instance->state = NfcStateIdle;
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
static void nfc_worker_listener_pass_col_res(Nfc* instance, uint8_t* rx_data, uint16_t rx_bits) {
 | 
						|
    furi_check(instance->col_res_status != Iso14443_3aColResStatusDone);
 | 
						|
    BitBuffer* tx_buffer = bit_buffer_alloc(NFC_MAX_BUFFER_SIZE);
 | 
						|
 | 
						|
    bool processed = false;
 | 
						|
 | 
						|
    if((rx_bits == 7) && (rx_data[0] == 0x52)) {
 | 
						|
        instance->col_res_status = Iso14443_3aColResStatusInProgress;
 | 
						|
        bit_buffer_copy_bytes(
 | 
						|
            tx_buffer,
 | 
						|
            instance->col_res_data.sens_resp.sens_resp,
 | 
						|
            sizeof(instance->col_res_data.sens_resp.sens_resp));
 | 
						|
        nfc_listener_tx(instance, tx_buffer);
 | 
						|
        processed = true;
 | 
						|
    } else if(rx_bits == 2 * 8) {
 | 
						|
        if((rx_data[0] == 0x93) && (rx_data[1] == 0x20)) {
 | 
						|
            bit_buffer_copy_bytes(
 | 
						|
                tx_buffer,
 | 
						|
                (const uint8_t*)&instance->col_res_data.sdd_resp[0],
 | 
						|
                sizeof(Iso14443_3aSddResp));
 | 
						|
            nfc_listener_tx(instance, tx_buffer);
 | 
						|
            processed = true;
 | 
						|
        } else if((rx_data[0] == 0x95) && (rx_data[1] == 0x20)) {
 | 
						|
            bit_buffer_copy_bytes(
 | 
						|
                tx_buffer,
 | 
						|
                (const uint8_t*)&instance->col_res_data.sdd_resp[1],
 | 
						|
                sizeof(Iso14443_3aSddResp));
 | 
						|
            nfc_listener_tx(instance, tx_buffer);
 | 
						|
            processed = true;
 | 
						|
        }
 | 
						|
    } else if(rx_bits == 9 * 8) {
 | 
						|
        if((rx_data[0] == 0x93) && (rx_data[1] == 0x70)) {
 | 
						|
            bit_buffer_set_size_bytes(tx_buffer, 1);
 | 
						|
            bit_buffer_set_byte(tx_buffer, 0, instance->col_res_data.sel_resp[0].sak);
 | 
						|
            iso14443_crc_append(Iso14443CrcTypeA, tx_buffer);
 | 
						|
            nfc_listener_tx(instance, tx_buffer);
 | 
						|
            processed = true;
 | 
						|
        } else if((rx_data[0] == 0x95) && (rx_data[1] == 0x70)) {
 | 
						|
            bit_buffer_set_size_bytes(tx_buffer, 1);
 | 
						|
            bit_buffer_set_byte(tx_buffer, 0, instance->col_res_data.sel_resp[1].sak);
 | 
						|
            iso14443_crc_append(Iso14443CrcTypeA, tx_buffer);
 | 
						|
            nfc_listener_tx(instance, tx_buffer);
 | 
						|
            instance->col_res_status = Iso14443_3aColResStatusDone;
 | 
						|
            NfcEvent event = {.type = NfcEventTypeListenerActivated};
 | 
						|
            instance->callback(event, instance->context);
 | 
						|
 | 
						|
            processed = true;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    if(!processed) {
 | 
						|
        NfcMessage message = {.type = NfcMessageTypeTimeout};
 | 
						|
        furi_message_queue_put(poller_queue, &message, FuriWaitForever);
 | 
						|
    }
 | 
						|
 | 
						|
    bit_buffer_free(tx_buffer);
 | 
						|
}
 | 
						|
 | 
						|
static int32_t nfc_worker_listener(void* context) {
 | 
						|
    Nfc* instance = context;
 | 
						|
    furi_check(instance->callback);
 | 
						|
 | 
						|
    NfcMessage message = {};
 | 
						|
 | 
						|
    NfcEventData event_data = {};
 | 
						|
    event_data.buffer = bit_buffer_alloc(NFC_MAX_BUFFER_SIZE);
 | 
						|
    NfcEvent nfc_event = {.data = event_data};
 | 
						|
 | 
						|
    while(true) {
 | 
						|
        furi_message_queue_get(listener_queue, &message, FuriWaitForever);
 | 
						|
        bit_buffer_copy_bits(event_data.buffer, message.data.data, message.data.data_bits);
 | 
						|
        if((message.data.data[0] == 0x52) && (message.data.data_bits == 7)) {
 | 
						|
            instance->col_res_status = Iso14443_3aColResStatusIdle;
 | 
						|
        }
 | 
						|
 | 
						|
        if(message.type == NfcMessageTypeAbort) {
 | 
						|
            break;
 | 
						|
        } else if(message.type == NfcMessageTypeTx) {
 | 
						|
            nfc_test_print(
 | 
						|
                NfcTransportLogLevelInfo, "RDR", message.data.data, message.data.data_bits);
 | 
						|
            if(instance->col_res_status != Iso14443_3aColResStatusDone) {
 | 
						|
                nfc_worker_listener_pass_col_res(
 | 
						|
                    instance, message.data.data, message.data.data_bits);
 | 
						|
            } else {
 | 
						|
                instance->state = NfcStateReady;
 | 
						|
                nfc_event.type = NfcEventTypeRxEnd;
 | 
						|
                instance->callback(nfc_event, instance->context);
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    instance->state = NfcStateIdle;
 | 
						|
    instance->col_res_status = Iso14443_3aColResStatusIdle;
 | 
						|
    memset(&instance->col_res_data, 0, sizeof(instance->col_res_data));
 | 
						|
    bit_buffer_free(nfc_event.data.buffer);
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
void nfc_start(Nfc* instance, NfcEventCallback callback, void* context) {
 | 
						|
    furi_check(instance);
 | 
						|
    furi_check(instance->worker_thread == NULL);
 | 
						|
 | 
						|
    if(instance->mode == NfcModeListener) {
 | 
						|
        furi_check(listener_queue == NULL);
 | 
						|
        // Check that poller didn't start
 | 
						|
        furi_check(poller_queue == NULL);
 | 
						|
    } else {
 | 
						|
        furi_check(poller_queue == NULL);
 | 
						|
        // Check that poller is started after listener
 | 
						|
        furi_check(listener_queue);
 | 
						|
    }
 | 
						|
 | 
						|
    instance->callback = callback;
 | 
						|
    instance->context = context;
 | 
						|
 | 
						|
    if(instance->mode == NfcModeListener) {
 | 
						|
        listener_queue = furi_message_queue_alloc(4, sizeof(NfcMessage));
 | 
						|
    } else {
 | 
						|
        poller_queue = furi_message_queue_alloc(4, sizeof(NfcMessage));
 | 
						|
    }
 | 
						|
 | 
						|
    instance->worker_thread = furi_thread_alloc();
 | 
						|
    furi_thread_set_context(instance->worker_thread, instance);
 | 
						|
    furi_thread_set_priority(instance->worker_thread, FuriThreadPriorityHigh);
 | 
						|
    furi_thread_set_stack_size(instance->worker_thread, 8 * 1024);
 | 
						|
 | 
						|
    if(instance->mode == NfcModeListener) {
 | 
						|
        furi_thread_set_name(instance->worker_thread, "NfcWorkerListener");
 | 
						|
        furi_thread_set_callback(instance->worker_thread, nfc_worker_listener);
 | 
						|
    } else {
 | 
						|
        furi_thread_set_name(instance->worker_thread, "NfcWorkerPoller");
 | 
						|
        furi_thread_set_callback(instance->worker_thread, nfc_worker_poller);
 | 
						|
    }
 | 
						|
 | 
						|
    furi_thread_start(instance->worker_thread);
 | 
						|
}
 | 
						|
 | 
						|
void nfc_stop(Nfc* instance) {
 | 
						|
    furi_check(instance);
 | 
						|
    furi_check(instance->worker_thread);
 | 
						|
 | 
						|
    if(instance->mode == NfcModeListener) {
 | 
						|
        NfcMessage message = {.type = NfcMessageTypeAbort};
 | 
						|
        furi_message_queue_put(listener_queue, &message, FuriWaitForever);
 | 
						|
        furi_thread_join(instance->worker_thread);
 | 
						|
 | 
						|
        furi_message_queue_free(listener_queue);
 | 
						|
        listener_queue = NULL;
 | 
						|
 | 
						|
        furi_thread_free(instance->worker_thread);
 | 
						|
        instance->worker_thread = NULL;
 | 
						|
    } else {
 | 
						|
        furi_thread_join(instance->worker_thread);
 | 
						|
 | 
						|
        furi_message_queue_free(poller_queue);
 | 
						|
        poller_queue = NULL;
 | 
						|
 | 
						|
        furi_thread_free(instance->worker_thread);
 | 
						|
        instance->worker_thread = NULL;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
// Called from worker thread
 | 
						|
 | 
						|
NfcError nfc_listener_tx(Nfc* instance, const BitBuffer* tx_buffer) {
 | 
						|
    furi_check(instance);
 | 
						|
    furi_check(poller_queue);
 | 
						|
    furi_check(listener_queue);
 | 
						|
    furi_check(tx_buffer);
 | 
						|
 | 
						|
    NfcMessage message = {};
 | 
						|
    message.type = NfcMessageTypeTx;
 | 
						|
    message.data.data_bits = bit_buffer_get_size(tx_buffer);
 | 
						|
    bit_buffer_write_bytes(tx_buffer, message.data.data, bit_buffer_get_size_bytes(tx_buffer));
 | 
						|
 | 
						|
    furi_message_queue_put(poller_queue, &message, FuriWaitForever);
 | 
						|
 | 
						|
    return NfcErrorNone;
 | 
						|
}
 | 
						|
 | 
						|
NfcError nfc_iso14443a_listener_tx_custom_parity(Nfc* instance, const BitBuffer* tx_buffer) {
 | 
						|
    return nfc_listener_tx(instance, tx_buffer);
 | 
						|
}
 | 
						|
 | 
						|
NfcError
 | 
						|
    nfc_poller_trx(Nfc* instance, const BitBuffer* tx_buffer, BitBuffer* rx_buffer, uint32_t fwt) {
 | 
						|
    furi_check(instance);
 | 
						|
    furi_check(tx_buffer);
 | 
						|
    furi_check(rx_buffer);
 | 
						|
    furi_check(poller_queue);
 | 
						|
    furi_check(listener_queue);
 | 
						|
    UNUSED(fwt);
 | 
						|
 | 
						|
    NfcError error = NfcErrorNone;
 | 
						|
 | 
						|
    NfcMessage message = {};
 | 
						|
    message.type = NfcMessageTypeTx;
 | 
						|
    message.data.data_bits = bit_buffer_get_size(tx_buffer);
 | 
						|
    bit_buffer_write_bytes(tx_buffer, message.data.data, bit_buffer_get_size_bytes(tx_buffer));
 | 
						|
    // Tx
 | 
						|
    furi_check(furi_message_queue_put(listener_queue, &message, FuriWaitForever) == FuriStatusOk);
 | 
						|
    // Rx
 | 
						|
    FuriStatus status = furi_message_queue_get(poller_queue, &message, 50);
 | 
						|
 | 
						|
    if(status == FuriStatusErrorTimeout) {
 | 
						|
        error = NfcErrorTimeout;
 | 
						|
    } else if(message.type == NfcMessageTypeTx) {
 | 
						|
        bit_buffer_copy_bits(rx_buffer, message.data.data, message.data.data_bits);
 | 
						|
        nfc_test_print(
 | 
						|
            NfcTransportLogLevelWarning, "TAG", message.data.data, message.data.data_bits);
 | 
						|
    } else if(message.type == NfcMessageTypeTimeout) {
 | 
						|
        error = NfcErrorTimeout;
 | 
						|
    }
 | 
						|
 | 
						|
    return error;
 | 
						|
}
 | 
						|
 | 
						|
NfcError nfc_iso14443a_poller_trx_custom_parity(
 | 
						|
    Nfc* instance,
 | 
						|
    const BitBuffer* tx_buffer,
 | 
						|
    BitBuffer* rx_buffer,
 | 
						|
    uint32_t fwt) {
 | 
						|
    return nfc_poller_trx(instance, tx_buffer, rx_buffer, fwt);
 | 
						|
}
 | 
						|
 | 
						|
// Technology specific API
 | 
						|
 | 
						|
NfcError nfc_iso14443a_poller_trx_short_frame(
 | 
						|
    Nfc* instance,
 | 
						|
    NfcIso14443aShortFrame frame,
 | 
						|
    BitBuffer* rx_buffer,
 | 
						|
    uint32_t fwt) {
 | 
						|
    UNUSED(frame);
 | 
						|
 | 
						|
    BitBuffer* tx_buffer = bit_buffer_alloc(32);
 | 
						|
    bit_buffer_set_size(tx_buffer, 7);
 | 
						|
    bit_buffer_set_byte(tx_buffer, 0, 0x52);
 | 
						|
 | 
						|
    NfcError error = nfc_poller_trx(instance, tx_buffer, rx_buffer, fwt);
 | 
						|
 | 
						|
    bit_buffer_free(tx_buffer);
 | 
						|
 | 
						|
    return error;
 | 
						|
}
 | 
						|
 | 
						|
NfcError nfc_iso14443a_poller_trx_sdd_frame(
 | 
						|
    Nfc* instance,
 | 
						|
    const BitBuffer* tx_buffer,
 | 
						|
    BitBuffer* rx_buffer,
 | 
						|
    uint32_t fwt) {
 | 
						|
    return nfc_poller_trx(instance, tx_buffer, rx_buffer, fwt);
 | 
						|
}
 | 
						|
 | 
						|
NfcError nfc_iso15693_listener_tx_sof(Nfc* instance) {
 | 
						|
    UNUSED(instance);
 | 
						|
 | 
						|
    return NfcErrorNone;
 | 
						|
}
 | 
						|
 | 
						|
NfcError nfc_felica_listener_set_sensf_res_data(
 | 
						|
    Nfc* instance,
 | 
						|
    const uint8_t* idm,
 | 
						|
    const uint8_t idm_len,
 | 
						|
    const uint8_t* pmm,
 | 
						|
    const uint8_t pmm_len) {
 | 
						|
    furi_assert(instance);
 | 
						|
    furi_assert(idm);
 | 
						|
    furi_assert(pmm);
 | 
						|
    furi_assert(idm_len == 8);
 | 
						|
    furi_assert(pmm_len == 8);
 | 
						|
 | 
						|
    return NfcErrorNone;
 | 
						|
}
 | 
						|
 | 
						|
#endif
 |