mirror of
https://github.com/DarkFlippers/unleashed-firmware
synced 2024-11-23 21:13:16 +00:00
d92b0a82cc
"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.
267 lines
8.5 KiB
C
267 lines
8.5 KiB
C
#include "nfc_scanner.h"
|
|
#include "nfc_poller.h"
|
|
|
|
#include <nfc/protocols/nfc_poller_defs.h>
|
|
|
|
#include <furi/furi.h>
|
|
|
|
#define TAG "NfcScanner"
|
|
|
|
typedef enum {
|
|
NfcScannerStateIdle,
|
|
NfcScannerStateTryBasePollers,
|
|
NfcScannerStateFindChildrenProtocols,
|
|
NfcScannerStateDetectChildrenProtocols,
|
|
NfcScannerStateComplete,
|
|
|
|
NfcScannerStateNum,
|
|
} NfcScannerState;
|
|
|
|
typedef enum {
|
|
NfcScannerSessionStateIdle,
|
|
NfcScannerSessionStateActive,
|
|
NfcScannerSessionStateStopRequest,
|
|
} NfcScannerSessionState;
|
|
|
|
struct NfcScanner {
|
|
Nfc* nfc;
|
|
NfcScannerState state;
|
|
NfcScannerSessionState session_state;
|
|
|
|
NfcScannerCallback callback;
|
|
void* context;
|
|
|
|
NfcEvent nfc_event;
|
|
|
|
NfcProtocol first_detected_protocol;
|
|
|
|
size_t base_protocols_num;
|
|
size_t base_protocols_idx;
|
|
NfcProtocol base_protocols[NfcProtocolNum];
|
|
|
|
size_t detected_base_protocols_num;
|
|
NfcProtocol detected_base_protocols[NfcProtocolNum];
|
|
|
|
size_t children_protocols_num;
|
|
size_t children_protocols_idx;
|
|
NfcProtocol children_protocols[NfcProtocolNum];
|
|
|
|
size_t detected_protocols_num;
|
|
NfcProtocol detected_protocols[NfcProtocolNum];
|
|
|
|
NfcProtocol current_protocol;
|
|
|
|
FuriThread* scan_worker;
|
|
};
|
|
|
|
static void nfc_scanner_reset(NfcScanner* instance) {
|
|
instance->base_protocols_idx = 0;
|
|
instance->base_protocols_num = 0;
|
|
|
|
instance->children_protocols_idx = 0;
|
|
instance->children_protocols_num = 0;
|
|
|
|
instance->detected_protocols_num = 0;
|
|
instance->detected_base_protocols_num = 0;
|
|
|
|
instance->current_protocol = 0;
|
|
}
|
|
|
|
typedef void (*NfcScannerStateHandler)(NfcScanner* instance);
|
|
|
|
void nfc_scanner_state_handler_idle(NfcScanner* instance) {
|
|
for(size_t i = 0; i < NfcProtocolNum; i++) {
|
|
NfcProtocol parent_protocol = nfc_protocol_get_parent(i);
|
|
if(parent_protocol == NfcProtocolInvalid) {
|
|
instance->base_protocols[instance->base_protocols_num] = i;
|
|
instance->base_protocols_num++;
|
|
}
|
|
}
|
|
FURI_LOG_D(TAG, "Found %zu base protocols", instance->base_protocols_num);
|
|
|
|
instance->first_detected_protocol = NfcProtocolInvalid;
|
|
instance->state = NfcScannerStateTryBasePollers;
|
|
}
|
|
|
|
void nfc_scanner_state_handler_try_base_pollers(NfcScanner* instance) {
|
|
do {
|
|
instance->current_protocol = instance->base_protocols[instance->base_protocols_idx];
|
|
|
|
if(instance->first_detected_protocol == instance->current_protocol) {
|
|
instance->state = NfcScannerStateFindChildrenProtocols;
|
|
break;
|
|
}
|
|
|
|
NfcPoller* poller = nfc_poller_alloc(instance->nfc, instance->current_protocol);
|
|
bool protocol_detected = nfc_poller_detect(poller);
|
|
nfc_poller_free(poller);
|
|
|
|
if(protocol_detected) {
|
|
instance->detected_protocols[instance->detected_protocols_num] =
|
|
instance->current_protocol;
|
|
instance->detected_protocols_num++;
|
|
|
|
instance->detected_base_protocols[instance->detected_base_protocols_num] =
|
|
instance->current_protocol;
|
|
instance->detected_base_protocols_num++;
|
|
|
|
if(instance->first_detected_protocol == NfcProtocolInvalid) {
|
|
instance->first_detected_protocol = instance->current_protocol;
|
|
instance->current_protocol = NfcProtocolInvalid;
|
|
}
|
|
}
|
|
|
|
instance->base_protocols_idx =
|
|
(instance->base_protocols_idx + 1) % instance->base_protocols_num;
|
|
} while(false);
|
|
}
|
|
|
|
void nfc_scanner_state_handler_find_children_protocols(NfcScanner* instance) {
|
|
for(size_t i = 0; i < NfcProtocolNum; i++) {
|
|
for(size_t j = 0; j < instance->detected_base_protocols_num; j++) {
|
|
if(nfc_protocol_has_parent(i, instance->detected_base_protocols[j])) {
|
|
instance->children_protocols[instance->children_protocols_num] = i;
|
|
instance->children_protocols_num++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(instance->children_protocols_num > 0) {
|
|
instance->state = NfcScannerStateDetectChildrenProtocols;
|
|
} else {
|
|
instance->state = NfcScannerStateComplete;
|
|
}
|
|
FURI_LOG_D(TAG, "Found %zu children", instance->children_protocols_num);
|
|
}
|
|
|
|
void nfc_scanner_state_handler_detect_children_protocols(NfcScanner* instance) {
|
|
furi_assert(instance->children_protocols_num);
|
|
|
|
instance->current_protocol = instance->children_protocols[instance->children_protocols_idx];
|
|
|
|
NfcPoller* poller = nfc_poller_alloc(instance->nfc, instance->current_protocol);
|
|
bool protocol_detected = nfc_poller_detect(poller);
|
|
nfc_poller_free(poller);
|
|
|
|
if(protocol_detected) {
|
|
instance->detected_protocols[instance->detected_protocols_num] =
|
|
instance->current_protocol;
|
|
instance->detected_protocols_num++;
|
|
}
|
|
|
|
instance->children_protocols_idx++;
|
|
if(instance->children_protocols_idx == instance->children_protocols_num) {
|
|
instance->state = NfcScannerStateComplete;
|
|
}
|
|
}
|
|
|
|
static void nfc_scanner_filter_detected_protocols(NfcScanner* instance) {
|
|
size_t filtered_protocols_num = 0;
|
|
NfcProtocol filtered_protocols[NfcProtocolNum] = {};
|
|
|
|
for(size_t i = 0; i < instance->detected_protocols_num; i++) {
|
|
bool is_parent = false;
|
|
for(size_t j = i; j < instance->detected_protocols_num; j++) {
|
|
is_parent = nfc_protocol_has_parent(
|
|
instance->detected_protocols[j], instance->detected_protocols[i]);
|
|
if(is_parent) break;
|
|
}
|
|
if(!is_parent) {
|
|
filtered_protocols[filtered_protocols_num] = instance->detected_protocols[i];
|
|
filtered_protocols_num++;
|
|
}
|
|
}
|
|
|
|
instance->detected_protocols_num = filtered_protocols_num;
|
|
memcpy(instance->detected_protocols, filtered_protocols, filtered_protocols_num);
|
|
}
|
|
|
|
void nfc_scanner_state_handler_complete(NfcScanner* instance) {
|
|
if(instance->detected_protocols_num > 1) {
|
|
nfc_scanner_filter_detected_protocols(instance);
|
|
}
|
|
FURI_LOG_I(TAG, "Detected %zu protocols", instance->detected_protocols_num);
|
|
|
|
NfcScannerEvent event = {
|
|
.type = NfcScannerEventTypeDetected,
|
|
.data =
|
|
{
|
|
.protocol_num = instance->detected_protocols_num,
|
|
.protocols = instance->detected_protocols,
|
|
},
|
|
};
|
|
|
|
instance->callback(event, instance->context);
|
|
furi_delay_ms(100);
|
|
}
|
|
|
|
static NfcScannerStateHandler nfc_scanner_state_handlers[NfcScannerStateNum] = {
|
|
[NfcScannerStateIdle] = nfc_scanner_state_handler_idle,
|
|
[NfcScannerStateTryBasePollers] = nfc_scanner_state_handler_try_base_pollers,
|
|
[NfcScannerStateFindChildrenProtocols] = nfc_scanner_state_handler_find_children_protocols,
|
|
[NfcScannerStateDetectChildrenProtocols] = nfc_scanner_state_handler_detect_children_protocols,
|
|
[NfcScannerStateComplete] = nfc_scanner_state_handler_complete,
|
|
};
|
|
|
|
static int32_t nfc_scanner_worker(void* context) {
|
|
furi_assert(context);
|
|
|
|
NfcScanner* instance = context;
|
|
|
|
while(instance->session_state == NfcScannerSessionStateActive) {
|
|
nfc_scanner_state_handlers[instance->state](instance);
|
|
}
|
|
|
|
nfc_scanner_reset(instance);
|
|
|
|
return 0;
|
|
}
|
|
|
|
NfcScanner* nfc_scanner_alloc(Nfc* nfc) {
|
|
furi_assert(nfc);
|
|
|
|
NfcScanner* instance = malloc(sizeof(NfcScanner));
|
|
instance->nfc = nfc;
|
|
|
|
return instance;
|
|
}
|
|
|
|
void nfc_scanner_free(NfcScanner* instance) {
|
|
furi_assert(instance);
|
|
|
|
free(instance);
|
|
}
|
|
|
|
void nfc_scanner_start(NfcScanner* instance, NfcScannerCallback callback, void* context) {
|
|
furi_assert(instance);
|
|
furi_assert(callback);
|
|
furi_assert(instance->session_state == NfcScannerSessionStateIdle);
|
|
furi_assert(instance->scan_worker == NULL);
|
|
|
|
instance->callback = callback;
|
|
instance->context = context;
|
|
instance->session_state = NfcScannerSessionStateActive;
|
|
|
|
instance->scan_worker = furi_thread_alloc();
|
|
furi_thread_set_name(instance->scan_worker, "NfcScanWorker");
|
|
furi_thread_set_context(instance->scan_worker, instance);
|
|
furi_thread_set_stack_size(instance->scan_worker, 4 * 1024);
|
|
furi_thread_set_callback(instance->scan_worker, nfc_scanner_worker);
|
|
|
|
furi_thread_start(instance->scan_worker);
|
|
}
|
|
|
|
void nfc_scanner_stop(NfcScanner* instance) {
|
|
furi_assert(instance);
|
|
furi_assert(instance->scan_worker);
|
|
|
|
instance->session_state = NfcScannerSessionStateStopRequest;
|
|
furi_thread_join(instance->scan_worker);
|
|
instance->session_state = NfcScannerSessionStateIdle;
|
|
|
|
furi_thread_free(instance->scan_worker);
|
|
instance->scan_worker = NULL;
|
|
instance->callback = NULL;
|
|
instance->context = NULL;
|
|
instance->state = NfcScannerStateIdle;
|
|
}
|