NFC refactoring (#3050)

"A long time ago in a galaxy far, far away...." we started NFC subsystem refactoring.

Starring:

- @gornekich - NFC refactoring project lead, architect, senior developer
- @gsurkov - architect, senior developer
- @RebornedBrain - senior developer

Supporting roles:

- @skotopes, @DrZlo13, @hedger - general architecture advisors, code review
- @Astrrra, @doomwastaken, @Hellitron, @ImagineVagon333 - quality assurance

Special thanks:

@bettse, @pcunning, @nxv, @noproto, @AloneLiberty and everyone else who has been helping us all this time and contributing valuable knowledges, ideas and source code.
This commit is contained in:
gornekich
2023-10-24 12:08:09 +09:00
committed by GitHub
parent 35c903494c
commit d92b0a82cc
514 changed files with 41487 additions and 68124 deletions
+147
View File
@@ -0,0 +1,147 @@
#include "felica.h"
#include <furi.h>
#include <nfc/nfc_common.h>
#define FELICA_PROTOCOL_NAME "FeliCa"
#define FELICA_DEVICE_NAME "FeliCa"
#define FELICA_DATA_FORMAT_VERSION "Data format version"
#define FELICA_MANUFACTURE_ID "Manufacture id"
#define FELICA_MANUFACTURE_PARAMETER "Manufacture parameter"
static const uint32_t felica_data_format_version = 1;
const NfcDeviceBase nfc_device_felica = {
.protocol_name = FELICA_PROTOCOL_NAME,
.alloc = (NfcDeviceAlloc)felica_alloc,
.free = (NfcDeviceFree)felica_free,
.reset = (NfcDeviceReset)felica_reset,
.copy = (NfcDeviceCopy)felica_copy,
.verify = (NfcDeviceVerify)felica_verify,
.load = (NfcDeviceLoad)felica_load,
.save = (NfcDeviceSave)felica_save,
.is_equal = (NfcDeviceEqual)felica_is_equal,
.get_name = (NfcDeviceGetName)felica_get_device_name,
.get_uid = (NfcDeviceGetUid)felica_get_uid,
.set_uid = (NfcDeviceSetUid)felica_set_uid,
.get_base_data = (NfcDeviceGetBaseData)felica_get_base_data,
};
FelicaData* felica_alloc() {
FelicaData* data = malloc(sizeof(FelicaData));
return data;
}
void felica_free(FelicaData* data) {
furi_assert(data);
free(data);
}
void felica_reset(FelicaData* data) {
memset(data, 0, sizeof(FelicaData));
}
void felica_copy(FelicaData* data, const FelicaData* other) {
furi_assert(data);
furi_assert(other);
*data = *other;
}
bool felica_verify(FelicaData* data, const FuriString* device_type) {
UNUSED(data);
UNUSED(device_type);
return false;
}
bool felica_load(FelicaData* data, FlipperFormat* ff, uint32_t version) {
furi_assert(data);
bool parsed = false;
do {
if(version < NFC_UNIFIED_FORMAT_VERSION) break;
uint32_t data_format_version = 0;
if(!flipper_format_read_uint32(ff, FELICA_DATA_FORMAT_VERSION, &data_format_version, 1))
break;
if(data_format_version != felica_data_format_version) break;
if(!flipper_format_read_hex(ff, FELICA_MANUFACTURE_ID, data->idm.data, FELICA_IDM_SIZE))
break;
if(!flipper_format_read_hex(
ff, FELICA_MANUFACTURE_PARAMETER, data->pmm.data, FELICA_PMM_SIZE))
break;
parsed = true;
} while(false);
return parsed;
}
bool felica_save(const FelicaData* data, FlipperFormat* ff) {
furi_assert(data);
bool saved = false;
do {
if(!flipper_format_write_comment_cstr(ff, FELICA_PROTOCOL_NAME " specific data")) break;
if(!flipper_format_write_uint32(
ff, FELICA_DATA_FORMAT_VERSION, &felica_data_format_version, 1))
break;
if(!flipper_format_write_hex(ff, FELICA_MANUFACTURE_ID, data->idm.data, FELICA_IDM_SIZE))
break;
if(!flipper_format_write_hex(
ff, FELICA_MANUFACTURE_PARAMETER, data->pmm.data, FELICA_PMM_SIZE))
break;
saved = true;
} while(false);
return saved;
}
bool felica_is_equal(const FelicaData* data, const FelicaData* other) {
furi_assert(data);
furi_assert(other);
return memcmp(data, other, sizeof(FelicaData)) == 0;
}
const char* felica_get_device_name(const FelicaData* data, NfcDeviceNameType name_type) {
UNUSED(data);
UNUSED(name_type);
return FELICA_DEVICE_NAME;
}
const uint8_t* felica_get_uid(const FelicaData* data, size_t* uid_len) {
furi_assert(data);
// Consider Manufacturer ID as UID
if(uid_len) {
*uid_len = FELICA_IDM_SIZE;
}
return data->idm.data;
}
bool felica_set_uid(FelicaData* data, const uint8_t* uid, size_t uid_len) {
furi_assert(data);
// Consider Manufacturer ID as UID
const bool uid_valid = uid_len == FELICA_IDM_SIZE;
if(uid_valid) {
memcpy(data->idm.data, uid, uid_len);
}
return uid_valid;
}
FelicaData* felica_get_base_data(const FelicaData* data) {
UNUSED(data);
furi_crash("No base data");
}
+77
View File
@@ -0,0 +1,77 @@
#pragma once
#include <toolbox/bit_buffer.h>
#include <nfc/protocols/nfc_device_base_i.h>
#ifdef __cplusplus
extern "C" {
#endif
#define FELICA_IDM_SIZE (8U)
#define FELICA_PMM_SIZE (8U)
#define FELICA_GUARD_TIME_US (20000U)
#define FELICA_FDT_POLL_FC (10000U)
#define FELICA_POLL_POLL_MIN_US (1280U)
#define FELICA_SYSTEM_CODE_CODE (0xFFFFU)
#define FELICA_TIME_SLOT_1 (0x00U)
#define FELICA_TIME_SLOT_2 (0x01U)
#define FELICA_TIME_SLOT_4 (0x03U)
#define FELICA_TIME_SLOT_8 (0x07U)
#define FELICA_TIME_SLOT_16 (0x0FU)
typedef enum {
FelicaErrorNone,
FelicaErrorNotPresent,
FelicaErrorColResFailed,
FelicaErrorBufferOverflow,
FelicaErrorCommunication,
FelicaErrorFieldOff,
FelicaErrorWrongCrc,
FelicaErrorProtocol,
FelicaErrorTimeout,
} FelicaError;
typedef struct {
uint8_t data[FELICA_IDM_SIZE];
} FelicaIDm;
typedef struct {
uint8_t data[FELICA_PMM_SIZE];
} FelicaPMm;
typedef struct {
FelicaIDm idm;
FelicaPMm pmm;
} FelicaData;
extern const NfcDeviceBase nfc_device_felica;
FelicaData* felica_alloc();
void felica_free(FelicaData* data);
void felica_reset(FelicaData* data);
void felica_copy(FelicaData* data, const FelicaData* other);
bool felica_verify(FelicaData* data, const FuriString* device_type);
bool felica_load(FelicaData* data, FlipperFormat* ff, uint32_t version);
bool felica_save(const FelicaData* data, FlipperFormat* ff);
bool felica_is_equal(const FelicaData* data, const FelicaData* other);
const char* felica_get_device_name(const FelicaData* data, NfcDeviceNameType name_type);
const uint8_t* felica_get_uid(const FelicaData* data, size_t* uid_len);
bool felica_set_uid(FelicaData* data, const uint8_t* uid, size_t uid_len);
FelicaData* felica_get_base_data(const FelicaData* data);
#ifdef __cplusplus
}
#endif
+117
View File
@@ -0,0 +1,117 @@
#include "felica_poller_i.h"
#include <nfc/protocols/nfc_poller_base.h>
#include <furi.h>
const FelicaData* felica_poller_get_data(FelicaPoller* instance) {
furi_assert(instance);
furi_assert(instance->data);
return instance->data;
}
static FelicaPoller* felica_poller_alloc(Nfc* nfc) {
furi_assert(nfc);
FelicaPoller* instance = malloc(sizeof(FelicaPoller));
instance->nfc = nfc;
instance->tx_buffer = bit_buffer_alloc(FELICA_POLLER_MAX_BUFFER_SIZE);
instance->rx_buffer = bit_buffer_alloc(FELICA_POLLER_MAX_BUFFER_SIZE);
nfc_config(instance->nfc, NfcModePoller, NfcTechFelica);
nfc_set_guard_time_us(instance->nfc, FELICA_GUARD_TIME_US);
nfc_set_fdt_poll_fc(instance->nfc, FELICA_FDT_POLL_FC);
nfc_set_fdt_poll_poll_us(instance->nfc, FELICA_POLL_POLL_MIN_US);
instance->data = felica_alloc();
instance->felica_event.data = &instance->felica_event_data;
instance->general_event.protocol = NfcProtocolFelica;
instance->general_event.event_data = &instance->felica_event;
instance->general_event.instance = instance;
return instance;
}
static void felica_poller_free(FelicaPoller* instance) {
furi_assert(instance);
furi_assert(instance->tx_buffer);
furi_assert(instance->rx_buffer);
furi_assert(instance->data);
bit_buffer_free(instance->tx_buffer);
bit_buffer_free(instance->rx_buffer);
felica_free(instance->data);
free(instance);
}
static void
felica_poller_set_callback(FelicaPoller* instance, NfcGenericCallback callback, void* context) {
furi_assert(instance);
furi_assert(callback);
instance->callback = callback;
instance->context = context;
}
static NfcCommand felica_poller_run(NfcGenericEvent event, void* context) {
furi_assert(context);
furi_assert(event.protocol == NfcProtocolInvalid);
furi_assert(event.event_data);
FelicaPoller* instance = context;
NfcEvent* nfc_event = event.event_data;
NfcCommand command = NfcCommandContinue;
if(nfc_event->type == NfcEventTypePollerReady) {
if(instance->state != FelicaPollerStateActivated) {
FelicaError error = felica_poller_async_activate(instance, instance->data);
if(error == FelicaErrorNone) {
instance->felica_event.type = FelicaPollerEventTypeReady;
instance->felica_event_data.error = error;
command = instance->callback(instance->general_event, instance->context);
} else {
instance->felica_event.type = FelicaPollerEventTypeError;
instance->felica_event_data.error = error;
command = instance->callback(instance->general_event, instance->context);
// Add delay to switch context
furi_delay_ms(100);
}
} else {
instance->felica_event.type = FelicaPollerEventTypeReady;
instance->felica_event_data.error = FelicaErrorNone;
command = instance->callback(instance->general_event, instance->context);
}
}
return command;
}
static bool felica_poller_detect(NfcGenericEvent event, void* context) {
furi_assert(context);
furi_assert(event.event_data);
furi_assert(event.instance);
furi_assert(event.protocol == NfcProtocolInvalid);
bool protocol_detected = false;
FelicaPoller* instance = context;
NfcEvent* nfc_event = event.event_data;
furi_assert(instance->state == FelicaPollerStateIdle);
if(nfc_event->type == NfcEventTypePollerReady) {
FelicaError error = felica_poller_async_activate(instance, instance->data);
protocol_detected = (error == FelicaErrorNone);
}
return protocol_detected;
}
const NfcPollerBase nfc_poller_felica = {
.alloc = (NfcPollerAlloc)felica_poller_alloc,
.free = (NfcPollerFree)felica_poller_free,
.set_callback = (NfcPollerSetCallback)felica_poller_set_callback,
.run = (NfcPollerRun)felica_poller_run,
.detect = (NfcPollerDetect)felica_poller_detect,
.get_data = (NfcPollerGetData)felica_poller_get_data,
};
+30
View File
@@ -0,0 +1,30 @@
#pragma once
#include "felica.h"
#include <lib/nfc/nfc.h>
#include <nfc/nfc_poller.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct FelicaPoller FelicaPoller;
typedef enum {
FelicaPollerEventTypeError,
FelicaPollerEventTypeReady,
} FelicaPollerEventType;
typedef struct {
FelicaError error;
} FelicaPollerEventData;
typedef struct {
FelicaPollerEventType type;
FelicaPollerEventData* data;
} FelicaPollerEvent;
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,13 @@
#pragma once
#include <nfc/protocols/nfc_poller_base.h>
#ifdef __cplusplus
extern "C" {
#endif
extern const NfcPollerBase nfc_poller_felica;
#ifdef __cplusplus
}
#endif
+128
View File
@@ -0,0 +1,128 @@
#include "felica_poller_i.h"
#include <nfc/helpers/felica_crc.h>
#define TAG "FelicaPoller"
static FelicaError felica_poller_process_error(NfcError error) {
switch(error) {
case NfcErrorNone:
return FelicaErrorNone;
case NfcErrorTimeout:
return FelicaErrorTimeout;
default:
return FelicaErrorNotPresent;
}
}
static FelicaError felica_poller_frame_exchange(
FelicaPoller* instance,
const BitBuffer* tx_buffer,
BitBuffer* rx_buffer,
uint32_t fwt) {
furi_assert(instance);
const size_t tx_bytes = bit_buffer_get_size_bytes(tx_buffer);
furi_assert(tx_bytes <= bit_buffer_get_capacity_bytes(instance->tx_buffer) - FELICA_CRC_SIZE);
felica_crc_append(instance->tx_buffer);
FelicaError ret = FelicaErrorNone;
do {
NfcError error =
nfc_poller_trx(instance->nfc, instance->tx_buffer, instance->rx_buffer, fwt);
if(error != NfcErrorNone) {
ret = felica_poller_process_error(error);
break;
}
bit_buffer_copy(rx_buffer, instance->rx_buffer);
if(!felica_crc_check(instance->rx_buffer)) {
ret = FelicaErrorWrongCrc;
break;
}
felica_crc_trim(rx_buffer);
} while(false);
return ret;
}
FelicaError felica_poller_async_polling(
FelicaPoller* instance,
const FelicaPollerPollingCommand* cmd,
FelicaPollerPollingResponse* resp) {
furi_assert(instance);
furi_assert(cmd);
furi_assert(resp);
FelicaError error = FelicaErrorNone;
do {
bit_buffer_set_size_bytes(instance->tx_buffer, 2);
// Set frame len
bit_buffer_set_byte(
instance->tx_buffer, 0, sizeof(FelicaPollerPollingCommand) + FELICA_CRC_SIZE);
// Set command code
bit_buffer_set_byte(instance->tx_buffer, 1, FELICA_POLLER_CMD_POLLING_REQ_CODE);
// Set other data
bit_buffer_append_bytes(
instance->tx_buffer, (uint8_t*)cmd, sizeof(FelicaPollerPollingCommand));
error = felica_poller_frame_exchange(
instance, instance->tx_buffer, instance->rx_buffer, FELICA_POLLER_POLLING_FWT);
if(error != FelicaErrorNone) break;
if(bit_buffer_get_byte(instance->rx_buffer, 1) != FELICA_POLLER_CMD_POLLING_RESP_CODE) {
error = FelicaErrorProtocol;
break;
}
if(bit_buffer_get_size_bytes(instance->rx_buffer) <
sizeof(FelicaIDm) + sizeof(FelicaPMm) + 1) {
error = FelicaErrorProtocol;
break;
}
bit_buffer_write_bytes_mid(instance->rx_buffer, resp->idm.data, 2, sizeof(FelicaIDm));
bit_buffer_write_bytes_mid(
instance->rx_buffer, resp->pmm.data, sizeof(FelicaIDm) + 2, sizeof(FelicaPMm));
} while(false);
return error;
}
FelicaError felica_poller_async_activate(FelicaPoller* instance, FelicaData* data) {
furi_assert(instance);
felica_reset(data);
FelicaError ret;
do {
bit_buffer_reset(instance->tx_buffer);
bit_buffer_reset(instance->rx_buffer);
// Send Polling command
const FelicaPollerPollingCommand polling_cmd = {
.system_code = FELICA_SYSTEM_CODE_CODE,
.request_code = 0,
.time_slot = FELICA_TIME_SLOT_1,
};
FelicaPollerPollingResponse polling_resp = {};
ret = felica_poller_async_polling(instance, &polling_cmd, &polling_resp);
if(ret != FelicaErrorNone) {
FURI_LOG_T(TAG, "Activation failed error: %d", ret);
break;
}
data->idm = polling_resp.idm;
data->pmm = polling_resp.pmm;
instance->state = FelicaPollerStateActivated;
} while(false);
return ret;
}
@@ -0,0 +1,60 @@
#pragma once
#include "felica_poller.h"
#include <toolbox/bit_buffer.h>
#ifdef __cplusplus
extern "C" {
#endif
#define FELICA_POLLER_MAX_BUFFER_SIZE (256U)
#define FELICA_POLLER_POLLING_FWT (200000U)
#define FELICA_POLLER_CMD_POLLING_REQ_CODE (0x00U)
#define FELICA_POLLER_CMD_POLLING_RESP_CODE (0x01U)
typedef enum {
FelicaPollerStateIdle,
FelicaPollerStateActivated,
} FelicaPollerState;
struct FelicaPoller {
Nfc* nfc;
FelicaPollerState state;
FelicaData* data;
BitBuffer* tx_buffer;
BitBuffer* rx_buffer;
NfcGenericEvent general_event;
FelicaPollerEvent felica_event;
FelicaPollerEventData felica_event_data;
NfcGenericCallback callback;
void* context;
};
typedef struct {
uint16_t system_code;
uint8_t request_code;
uint8_t time_slot;
} FelicaPollerPollingCommand;
typedef struct {
FelicaIDm idm;
FelicaPMm pmm;
uint8_t request_data[2];
} FelicaPollerPollingResponse;
const FelicaData* felica_poller_get_data(FelicaPoller* instance);
FelicaError felica_poller_async_polling(
FelicaPoller* instance,
const FelicaPollerPollingCommand* cmd,
FelicaPollerPollingResponse* resp);
FelicaError felica_poller_async_activate(FelicaPoller* instance, FelicaData* data);
#ifdef __cplusplus
}
#endif