mirror of
https://github.com/DarkFlippers/unleashed-firmware
synced 2024-11-23 04:53:08 +00:00
467e973da2
* Moved some structs and defs from poller to generic felica * Buffer size increased for transferring more data * Felica HAL Tx function implemented * Some structs and fields for listener * Raw listener implementation * Added new event for felica activation * Proper config fot listener added * Moved some structs from poller in order to use them in listener too * New function for calculating MAC * Listener data structures and function definitions * Private listener functions implementation added * Raw felica listener logic implementation added * Fix total sector count both for poller and listener * Defined type for write handlers * New logic for write operations added * Removed old commented code * Splitted read logic into several separate functions * New type added and some fields to instance * New logic of read command implemented * Defines added for response codes * Functions moved to private namespace * Function visibility changed and some cleanups * Update felica_listener.c, felica_listener_i.c, and felica_listener_i.h * Some type adjustments * Moved frame_exchange function to private namespace * Error handling added * Function to get data_ptr for write request added * Missing declaration added * Add processing of nfc errors * write_with_mac is a local variable now * Adjustments to MAC calculation logic * Values replaced with defines * Update nfc_transport.c with felica logic * Sync felica poller added for unit tests * Felica unit_tests and data dump added * Fixed proper reading of MAC_A block when it is 1st * Macro definitions for MC added * Function simplified * More defines * CRC check for incomming packets added * Readonly logic adjusted * Block write validation adjusted * New logic for ID block writing * Some cleanups * New logic of moving across the block list with different element length * Some cleanups * Adjusted requires_mac logic to cover all blocks needed * Cleanups and renaming * New block list validation logic * Block list logic iteration simplified * Some asserts and checks added * Replaced MC[2] checks with macros * Marked def values as unsigned * Removed old code * Removed commented function declarations * Changed protected block in felica test card dump and adjusted tests * Fixes after merge * Moved defines to header * Now we allocate memory for max possible response pack in any case * Some renaming and documentation * Bump api symbols * Set feature to emulate full for felica * Removed 'More' button and added MoreInfo feature which adds this button back * Types renamed * Removed unnecessary code * Reformat comments * Fixing missing signatures * Replaced crash with error log and return value * Format doxygen comments Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
520 lines
15 KiB
C
520 lines
15 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 <lib/nfc/protocols/felica/felica.h>
|
|
#include <lib/nfc/helpers/felica_crc.h>
|
|
#include <lib/nfc/protocols/felica/felica_poller_sync.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;
|
|
|
|
typedef struct {
|
|
uint8_t length;
|
|
uint8_t polling_cmd;
|
|
uint16_t system_code;
|
|
uint8_t request_code;
|
|
uint8_t time_slot;
|
|
} FelicaPollingRequest;
|
|
|
|
typedef struct {
|
|
uint8_t code;
|
|
FelicaIDm idm;
|
|
FelicaPMm pmm;
|
|
} FelicaSensfResData;
|
|
|
|
typedef struct {
|
|
uint16_t system_code;
|
|
FelicaSensfResData sens_res;
|
|
} FelicaPTMemory;
|
|
|
|
struct Nfc {
|
|
NfcState state;
|
|
|
|
Iso14443_3aColResStatus col_res_status;
|
|
Iso14443_3aColResData col_res_data;
|
|
FelicaPTMemory pt_memory;
|
|
bool software_col_res_required;
|
|
|
|
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(void) {
|
|
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);
|
|
instance->software_col_res_required = true;
|
|
|
|
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;
|
|
}
|
|
} else if(rx_bits == 8 * 8) {
|
|
FelicaPollingRequest* request = (FelicaPollingRequest*)rx_data;
|
|
if(request->system_code == instance->pt_memory.system_code) {
|
|
uint8_t response_size = sizeof(FelicaSensfResData) + 1;
|
|
bit_buffer_reset(tx_buffer);
|
|
bit_buffer_append_byte(tx_buffer, response_size);
|
|
bit_buffer_append_bytes(
|
|
tx_buffer, (uint8_t*)&instance->pt_memory.sens_res, sizeof(FelicaSensfResData));
|
|
felica_crc_append(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->software_col_res_required &&
|
|
(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);
|
|
|
|
instance->pt_memory.system_code = 0xFFFF;
|
|
instance->pt_memory.sens_res.code = 0x01;
|
|
instance->software_col_res_required = true;
|
|
memcpy(instance->pt_memory.sens_res.idm.data, idm, idm_len);
|
|
memcpy(instance->pt_memory.sens_res.pmm.data, pmm, pmm_len);
|
|
|
|
return NfcErrorNone;
|
|
}
|
|
|
|
#endif
|