From 30e251a934b501262a7dd5edc1705406cbbb1ad7 Mon Sep 17 00:00:00 2001 From: Daan De Witte Date: Wed, 10 May 2023 12:54:54 +0200 Subject: [PATCH 01/14] Included full (read & emulate)Reader Talks First mode in LFRFID app, supporting HITAG1 protocol --- applications/main/lfrfid/lfrfid_i.h | 2 + .../scenes/lfrfid_scene_extra_actions.c | 12 + .../main/lfrfid/scenes/lfrfid_scene_read.c | 24 +- .../lfrfid/scenes/lfrfid_scene_saved_info.c | 14 +- .../main/lfrfid/views/lfrfid_view_read.c | 86 +- .../main/lfrfid/views/lfrfid_view_read.h | 11 +- lib/lfrfid/lfrfid_dict_file.c | 83 +- lib/lfrfid/lfrfid_hitag_worker.c | 2114 +++++++++++++++++ lib/lfrfid/lfrfid_hitag_worker.h | 92 + lib/lfrfid/lfrfid_worker.c | 4 +- lib/lfrfid/lfrfid_worker.h | 6 +- lib/lfrfid/lfrfid_worker_i.h | 1 + lib/lfrfid/lfrfid_worker_modes.c | 127 +- lib/lfrfid/protocols/lfrfid_protocols.c | 2 + lib/lfrfid/protocols/lfrfid_protocols.h | 2 + lib/lfrfid/protocols/protocol_hitag1.c | 104 + lib/lfrfid/protocols/protocol_hitag1.h | 4 + 17 files changed, 2636 insertions(+), 52 deletions(-) create mode 100644 lib/lfrfid/lfrfid_hitag_worker.c create mode 100644 lib/lfrfid/lfrfid_hitag_worker.h create mode 100644 lib/lfrfid/protocols/protocol_hitag1.c create mode 100644 lib/lfrfid/protocols/protocol_hitag1.h diff --git a/applications/main/lfrfid/lfrfid_i.h b/applications/main/lfrfid/lfrfid_i.h index 72b0619304f..81051e785f9 100644 --- a/applications/main/lfrfid/lfrfid_i.h +++ b/applications/main/lfrfid/lfrfid_i.h @@ -56,6 +56,8 @@ enum LfRfidCustomEvent { LfRfidEventReadSenseCardEnd, LfRfidEventReadStartASK, LfRfidEventReadStartPSK, + LfRfidEventReadStartRTF, + LfRfidEventReadSenseHitag, //TODO combine with sensecardstart? LfRfidEventReadDone, LfRfidEventReadOverrun, LfRfidEventReadError, diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c b/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c index fac2ebcec54..7e551d3310c 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c @@ -4,6 +4,7 @@ typedef enum { SubmenuIndexASK, SubmenuIndexPSK, + SubmenuIndexHitag, SubmenuIndexRAW, } SubmenuIndex; @@ -29,6 +30,12 @@ void lfrfid_scene_extra_actions_on_enter(void* context) { SubmenuIndexPSK, lfrfid_scene_extra_actions_submenu_callback, app); + submenu_add_item( + submenu, + "Read RTF (Reader Talks First)", + SubmenuIndexHitag, + lfrfid_scene_extra_actions_submenu_callback, + app); if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { submenu_add_item( @@ -65,6 +72,11 @@ bool lfrfid_scene_extra_actions_on_event(void* context, SceneManagerEvent event) scene_manager_next_scene(app->scene_manager, LfRfidSceneRead); DOLPHIN_DEED(DolphinDeedRfidRead); consumed = true; + } else if(event.event == SubmenuIndexHitag) { + app->read_type = LFRFIDWorkerReadTypeRTFOnly; + scene_manager_next_scene(app->scene_manager, LfRfidSceneRead); + DOLPHIN_DEED(DolphinDeedRfidRead); + consumed = true; } else if(event.event == SubmenuIndexRAW) { scene_manager_next_scene(app->scene_manager, LfRfidSceneRawName); consumed = true; diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_read.c b/applications/main/lfrfid/scenes/lfrfid_scene_read.c index 5f19597282a..8a1480d9285 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_read.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_read.c @@ -16,8 +16,7 @@ static const NotificationSequence sequence_blink_set_cyan = { NULL, }; -static void - lfrfid_read_callback(LFRFIDWorkerReadResult result, ProtocolId protocol, void* context) { +static void lfrfid_read_callback(LFRFIDWorkerReadResult result, ProtocolId protocol, void* context) { LfRfid* app = context; uint32_t event = 0; @@ -36,6 +35,10 @@ static void event = LfRfidEventReadStartASK; } else if(result == LFRFIDWorkerReadStartPSK) { event = LfRfidEventReadStartPSK; + } else if(result == LFRFIDWorkerReadStartRTF) { + event = LfRfidEventReadStartRTF; + } else if(result == LFRFIDWorkerReadSenseHitag) { //TODO combine with sensecardstart? + event = LfRfidEventReadSenseHitag; } else { return; } @@ -50,6 +53,9 @@ void lfrfid_scene_read_on_enter(void* context) { lfrfid_view_read_set_read_mode(app->read_view, LfRfidReadPskOnly); } else if(app->read_type == LFRFIDWorkerReadTypeASKOnly) { lfrfid_view_read_set_read_mode(app->read_view, LfRfidReadAskOnly); + } else if(app->read_type == LFRFIDWorkerReadTypeRTFOnly) { + lfrfid_view_read_set_read_state(app->read_view, LfRfidReadScanning); + lfrfid_view_read_set_read_mode(app->read_view, LfRfidReadRtfOnly); } lfrfid_worker_start_thread(app->lfworker); @@ -93,7 +99,19 @@ bool lfrfid_scene_read_on_event(void* context, SceneManagerEvent event) { lfrfid_view_read_set_read_mode(app->read_view, LfRfidReadAsk); } consumed = true; - } + } else if(event.event == LfRfidEventReadStartRTF) { + if(app->read_type == LFRFIDWorkerReadTypeAuto) { + lfrfid_view_read_set_read_state(app->read_view, LfRfidReadScanning); + lfrfid_view_read_set_read_mode(app->read_view, LfRfidReadHitag); + } + consumed = true; + } else if(event.event == LfRfidEventReadSenseHitag) { //TODO combine with sensecardstart? + if( + app->read_type == LFRFIDWorkerReadTypeAuto || + app->read_type == LFRFIDWorkerReadTypeRTFOnly) { + lfrfid_view_read_set_read_state(app->read_view, LfRfidReadTagDetected); + } + } } return consumed; diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_saved_info.c b/applications/main/lfrfid/scenes/lfrfid_scene_saved_info.c index 3f1c2d400e4..83fb0a2bd2a 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_saved_info.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_saved_info.c @@ -17,12 +17,16 @@ void lfrfid_scene_saved_info_on_enter(void* context) { uint8_t* data = (uint8_t*)malloc(size); protocol_dict_get_data(app->dict, app->protocol_id, data, size); for(uint8_t i = 0; i < size; i++) { - if(i != 0) { - furi_string_cat_printf(tmp_string, " "); + if(i >= 18) { + furi_string_cat_printf(tmp_string, ".."); + break; + } else { + if(i != 0) { + furi_string_cat_printf(tmp_string, " "); + } + furi_string_cat_printf(tmp_string, "%02X", data[i]); } - - furi_string_cat_printf(tmp_string, "%02X", data[i]); - } + } free(data); FuriString* render_data; diff --git a/applications/main/lfrfid/views/lfrfid_view_read.c b/applications/main/lfrfid/views/lfrfid_view_read.c index 094afb61758..b5d89bc99ba 100644 --- a/applications/main/lfrfid/views/lfrfid_view_read.c +++ b/applications/main/lfrfid/views/lfrfid_view_read.c @@ -11,6 +11,7 @@ struct LfRfidReadView { typedef struct { IconAnimation* icon; LfRfidReadViewMode read_mode; + LfRfidReadViewState read_state; } LfRfidReadViewModel; static void lfrfid_view_read_draw_callback(Canvas* canvas, void* _model) { @@ -22,36 +23,69 @@ static void lfrfid_view_read_draw_callback(Canvas* canvas, void* _model) { canvas_set_font(canvas, FontPrimary); if(model->read_mode == LfRfidReadAsk) { - canvas_draw_str(canvas, 70, 16, "Reading 1/2"); + canvas_draw_str(canvas, 70, 8, "Reading 1/3"); - canvas_draw_str(canvas, 77, 29, "ASK"); - canvas_draw_icon(canvas, 70, 22, &I_ButtonRight_4x7); - canvas_draw_icon_animation(canvas, 102, 21, model->icon); + canvas_draw_str(canvas, 77, 20, "ASK"); + canvas_draw_icon(canvas, 70, 13, &I_ButtonRight_4x7); + canvas_draw_icon_animation(canvas, 112, 12, model->icon); canvas_set_font(canvas, FontSecondary); - canvas_draw_str(canvas, 77, 43, "PSK"); + canvas_draw_str(canvas, 77, 33, "PSK"); + canvas_draw_str(canvas, 77, 46, "RTF"); } else if(model->read_mode == LfRfidReadPsk) { - canvas_draw_str(canvas, 70, 16, "Reading 2/2"); + canvas_draw_str(canvas, 70, 8, "Reading 2/3"); - canvas_draw_str(canvas, 77, 43, "PSK"); - canvas_draw_icon(canvas, 70, 36, &I_ButtonRight_4x7); - canvas_draw_icon_animation(canvas, 102, 35, model->icon); + canvas_draw_str(canvas, 77, 33, "PSK"); + canvas_draw_icon(canvas, 70, 26, &I_ButtonRight_4x7); + canvas_draw_icon_animation(canvas, 112, 25, model->icon); canvas_set_font(canvas, FontSecondary); - canvas_draw_str(canvas, 77, 29, "ASK"); - } else { - canvas_draw_str(canvas, 72, 16, "Reading"); - - if(model->read_mode == LfRfidReadAskOnly) { - canvas_draw_str(canvas, 77, 35, "ASK"); - } else { - canvas_draw_str(canvas, 77, 35, "PSK"); - } - canvas_draw_icon_animation(canvas, 102, 27, model->icon); - } + canvas_draw_str(canvas, 77, 20, "ASK"); + canvas_draw_str(canvas, 77, 46, "RTF"); + } else if(model->read_mode == LfRfidReadHitag) { + if (model->read_state == LfRfidReadScanning){ + canvas_draw_str(canvas, 70, 8, "Reading 3/3"); + + canvas_draw_str(canvas, 77, 46, "RTF"); + canvas_draw_icon(canvas, 70, 39, &I_ButtonRight_4x7); + canvas_draw_icon_animation(canvas, 112, 38, model->icon); + + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 77, 20, "ASK"); + canvas_draw_str(canvas, 77, 33, "PSK"); + } else if (model->read_state == LfRfidReadTagDetected){//TODO switch to other scene? + canvas_draw_str(canvas, 65, 8, "Hitag1 found"); + + canvas_set_font(canvas, FontSecondary); + //canvas_draw_str(canvas, 70, 20, "## ## ## ##"); //TODO get tag SN from hitag worker + canvas_draw_str(canvas, 70, 33, "Reading data"); + //canvas_draw_str(canvas, 70, 46, "Page: X/64"); //TODO get current page index from hitag worker + } + } else if(model->read_mode == LfRfidReadAskOnly) { + canvas_draw_str(canvas, 72, 16, "Reading"); + canvas_draw_str(canvas, 77, 35, "ASK"); + canvas_draw_icon_animation(canvas, 112, 27, model->icon); + } else if(model->read_mode == LfRfidReadPskOnly) { + canvas_draw_str(canvas, 72, 16, "Reading"); + canvas_draw_str(canvas, 77, 35, "PSK"); + canvas_draw_icon_animation(canvas, 112, 27, model->icon); + } else if(model->read_mode == LfRfidReadRtfOnly) { + if (model->read_state == LfRfidReadScanning){ + canvas_draw_str(canvas, 72, 16, "Reading"); + canvas_draw_str(canvas, 77, 35, "RTF"); + canvas_draw_icon_animation(canvas, 112, 27, model->icon); + } else if (model->read_state == LfRfidReadTagDetected){ //TODO switch to other scene? + canvas_draw_str(canvas, 65, 8, "Hitag1 found"); + + canvas_set_font(canvas, FontSecondary); + //canvas_draw_str(canvas, 70, 20, "## ## ## ##"); //TODO get tag SN from hitag worker + canvas_draw_str(canvas, 70, 33, "Reading data"); + //canvas_draw_str(canvas, 70, 46, "Page: X/64"); //TODO get current page index from hitag worker + } + } canvas_set_font(canvas, FontSecondary); - canvas_draw_str(canvas, 61, 56, "Don't move card"); + canvas_draw_str(canvas, 61, 60, "Don't move card"); } void lfrfid_view_read_enter(void* context) { @@ -111,3 +145,13 @@ void lfrfid_view_read_set_read_mode(LfRfidReadView* read_view, LfRfidReadViewMod }, true); } + +void lfrfid_view_read_set_read_state(LfRfidReadView* read_view, LfRfidReadViewState state) { + with_view_model( + read_view->view, + LfRfidReadViewModel * model, + { + model->read_state = state; + }, + true); +} \ No newline at end of file diff --git a/applications/main/lfrfid/views/lfrfid_view_read.h b/applications/main/lfrfid/views/lfrfid_view_read.h index 55bb1f230fa..9b2d749a80c 100644 --- a/applications/main/lfrfid/views/lfrfid_view_read.h +++ b/applications/main/lfrfid/views/lfrfid_view_read.h @@ -4,10 +4,17 @@ typedef enum { LfRfidReadAsk, LfRfidReadPsk, + LfRfidReadHitag, LfRfidReadAskOnly, - LfRfidReadPskOnly + LfRfidReadPskOnly, + LfRfidReadRtfOnly, } LfRfidReadViewMode; +typedef enum { + LfRfidReadScanning, + LfRfidReadTagDetected, +} LfRfidReadViewState; + typedef struct LfRfidReadView LfRfidReadView; LfRfidReadView* lfrfid_view_read_alloc(); @@ -17,3 +24,5 @@ void lfrfid_view_read_free(LfRfidReadView* read_view); View* lfrfid_view_read_get_view(LfRfidReadView* read_view); void lfrfid_view_read_set_read_mode(LfRfidReadView* read_view, LfRfidReadViewMode mode); + +void lfrfid_view_read_set_read_state(LfRfidReadView* read_view, LfRfidReadViewState state); \ No newline at end of file diff --git a/lib/lfrfid/lfrfid_dict_file.c b/lib/lfrfid/lfrfid_dict_file.c index 7ae84f8b687..f053f463b53 100644 --- a/lib/lfrfid/lfrfid_dict_file.c +++ b/lib/lfrfid/lfrfid_dict_file.c @@ -5,6 +5,39 @@ #define LFRFID_DICT_FILETYPE "Flipper RFID key" +bool lfrfid_dict_file_save_hitag1_data(FlipperFormat* file, uint8_t* data){ + FuriString* string = furi_string_alloc(); + bool result = false; + uint8_t pageSize = 4; + + do { + //write shortened data (tag ID) + if(!flipper_format_write_hex(file, "Data", data, pageSize)) break; + + if(!flipper_format_write_comment_cstr(file, "Hitag1 specific data")) break; + + //write pages + for (uint8_t p=0;p<64;p++){ + furi_string_printf(string, "Page %2u", p); + if (data[64*pageSize+p]){ + //write page data + if(!flipper_format_write_hex(file, furi_string_get_cstr(string), data+p*pageSize, pageSize)) break; + } else { + //write ?? ?? ?? ?? + if(!flipper_format_write_string_cstr(file, furi_string_get_cstr(string), "?? ?? ?? ??")) break; + } + if (p==64-1){ + result = true; + } + } + } + while (false); + + furi_string_free(string); + + return result; +} + bool lfrfid_dict_file_save(ProtocolDict* dict, ProtocolId protocol, const char* filename) { furi_check(protocol != PROTOCOL_NO); Storage* storage = furi_record_open(RECORD_STORAGE); @@ -25,9 +58,13 @@ bool lfrfid_dict_file_save(ProtocolDict* dict, ProtocolId protocol, const char* // TODO: write comment about protocol sizes into file - protocol_dict_get_data(dict, protocol, data, data_size); + protocol_dict_get_data(dict, protocol, data, data_size); + if (protocol == LFRFIDProtocolHitag1){ + if(!lfrfid_dict_file_save_hitag1_data(file, data)) break; + } else { + if(!flipper_format_write_hex(file, "Data", data, data_size)) break; + } - if(!flipper_format_write_hex(file, "Data", data, data_size)) break; result = true; } while(false); @@ -138,6 +175,41 @@ static ProtocolId lfrfid_dict_protocol_fallback( return result; } +bool lfrfid_dict_file_load_hitag1_data(FlipperFormat* file, uint8_t* data){ + FuriString* string = furi_string_alloc(); + bool result = false; + uint8_t tagID[4]; + uint8_t pageSize = 4; + + do { + //read shortened data (tag ID) + if(!flipper_format_read_hex(file, "Data", tagID, 4)) break; + + //read pages + for (uint8_t p=0;p<64;p++){ + furi_string_printf(string, "Page %2u", p); + if (flipper_format_read_hex(file, furi_string_get_cstr(string), data+p*pageSize, pageSize)){ + data[64*pageSize+p]=1; + } else { + data[64*pageSize+p]=0; + } + } + + //check data consistency + if (memcmp(tagID, data, pageSize)!=0) break; + + //check if tag ID & config page are succesfully read + if (data[64*pageSize+0] && data[64*pageSize+1]){ + result = true; + } + } + while (false); + + furi_string_free(string); + + return result; +} + ProtocolId lfrfid_dict_file_load(ProtocolDict* dict, const char* filename) { Storage* storage = furi_record_open(RECORD_STORAGE); FlipperFormat* file = flipper_format_file_alloc(storage); @@ -163,7 +235,12 @@ ProtocolId lfrfid_dict_file_load(ProtocolDict* dict, const char* filename) { if(protocol == PROTOCOL_NO) { protocol = lfrfid_dict_protocol_fallback(dict, furi_string_get_cstr(str_result), file); if(protocol == PROTOCOL_NO) break; - } else { + } else if(protocol == LFRFIDProtocolHitag1){ + // Hitag1 data + size_t data_size = protocol_dict_get_data_size(dict, protocol); + if(!lfrfid_dict_file_load_hitag1_data(file, data)) break; + protocol_dict_set_data(dict, protocol, data, data_size); + } else { // data size_t data_size = protocol_dict_get_data_size(dict, protocol); if(!flipper_format_read_hex(file, "Data", data, data_size)) break; diff --git a/lib/lfrfid/lfrfid_hitag_worker.c b/lib/lfrfid/lfrfid_hitag_worker.c new file mode 100644 index 00000000000..6c6422856d1 --- /dev/null +++ b/lib/lfrfid/lfrfid_hitag_worker.c @@ -0,0 +1,2114 @@ +#include "lfrfid_hitag_worker.h" + +#define DMA_BUFFER_SIZE 4000 //I need to keep this big enough? to have enough time inbetween HT & TC events perhaps? +#define HITAG_CMD_BUFFER_SIZE 100 //this was originally 1000, but can be lowered + +#define READ_BUFFER_SIZE 4000 +#define READ_BUFFER_COUNT 4 + +#define EMULATE_BUFFER_SIZE 50 +#define EMULATE_BUFFER_COUNT 50 + +//bitlength/ID for read cmd structure +#define HITAG_STOP 102 +#define HITAG_ON 101 +#define HITAG_OFF 100 +#define HITAG_SET 5 +#define HITAG_SELECT 5 +#define HITAG_CMD 4 +#define HITAG_BYTE 8 +#define HITAG_CRC 8 + +//carrier periods for read cmd structure +#define HITAG_LOW 7 +#define HITAG_STOP_HIGH 40 +#define HITAG_1_HIGH 21 +#define HITAG_0_HIGH 15 + +//bitlength for emulate reply cmd structure +#define HITAG_PAGE 32 +#define HITAG_STARTBIT 1 +#define HITAG_STARTBIT3 3 +#define HITAG_STARTBIT6 6 +#define HITAG_ACK 3 +#define HITAG_ACK_ADV 8 + +#define HITAG_BASEPERIOD 8 + +//carrier periods for read reply decoding +#define HITAG_DURATION_S 256 +#define HITAG_DURATION_M 384 +#define HITAG_DURATION_L 512 +#define HITAG_DURATION_ERROR_MARGIN 0.15 + +#define HITAG_BITPERIODS_AC 64 +#define HITAG_BITPERIODS_MC 32 + +//MISC +#define READ 1 +#define WRITE 2 + +#define CRC_PRESET 0xFF +#define CRC_POLYNOM 0x1D + +#define TAG "LFRFIDHitagWorker" + +// TIMER definitions +#define CARRIER_OUT_TIMER TIM1 +//#define CARRIER_OUT_TIMER_IRQ FuriHalInterruptIdTIM1 //TODO am i using this? +#define CARRIER_OUT_TIMER_CHANNEL LL_TIM_CHANNEL_CH1 // or LL_TIM_CHANNEL_CH1N + +#define CARRIER_IN_TIMER TIM2 +#define CARRIER_IN_TIMER_IND_CH LL_TIM_CHANNEL_CH2 //no longer used via ETR setup +#define CARRIER_IN_TIMER_DIR_CH LL_TIM_CHANNEL_CH1 //no longer used via ETR setup + +#define CARRIER_IN_REFERENCE_TIMER TIM1 + +#define PULL_OUT_TIMER TIM2 +#define PULL_OUT_TIMER_CHANNEL LL_TIM_CHANNEL_CH3 + +// DMA Channels definition +#define PULL_OUT_DMA DMA2 +#define PULL_OUT_DMA_CH1 LL_DMA_CHANNEL_1 +#define PULL_OUT_DMA_CH2 LL_DMA_CHANNEL_2 +#define PULL_OUT_DMA_CH1_IRQ FuriHalInterruptIdDma2Ch1 +#define PULL_OUT_DMA_CH1_DEF PULL_OUT_DMA, PULL_OUT_DMA_CH1 +#define PULL_OUT_DMA_CH2_DEF PULL_OUT_DMA, PULL_OUT_DMA_CH2 + +typedef enum { + HitagProtocolAntiCollision, + HitagProtocolManchesterCoding, +} LfRfidHitagProtocol; + +typedef enum { + LFRFIDHitagWorkerSignalHalfTransfer, + LFRFIDHitagWorkerSignalTransferComplete, + LFRFIDHitagWorkerSignalStop, +} LFRFIDHitagWorkerSignal; + +typedef enum { + HitagStateIdle, + HitagStateSelected, + HitagStateQuiet, +} LfRfidHitagState; + +typedef enum { + HitagModeBasic, + HitagModeAdvanced, +} LfRfidHitagMode; + + +typedef struct { + uint32_t timer_buffer_arr[DMA_BUFFER_SIZE]; + uint32_t timer_buffer_ccr[DMA_BUFFER_SIZE]; +} TimerDMAData; + +typedef struct { + uint32_t CMDvalue[HITAG_CMD_BUFFER_SIZE]; //this can likely be a uint16_t instead of uint32_t + uint8_t CMDtype[HITAG_CMD_BUFFER_SIZE]; + uint16_t CMDlength; + uint16_t CMDposition; + uint16_t CMDsubposition; + bool CMDloop; + bool CMDactive; + uint32_t CMDcount; +} LfRfidHitagCMDData; + +typedef struct { + uint32_t replyvalue[HITAG_CMD_BUFFER_SIZE]; //this can likely be a uint8_t instead of uint32_t + uint8_t replytype[HITAG_CMD_BUFFER_SIZE]; + uint16_t replylength; +} LfRfidHitagReplyData; + +typedef struct { + BufferStream* stream; + VarintPair* pair; + uint32_t capCounter; + uint32_t prevTIMval; +} LfRfidHitagCaptureData; + + +typedef struct { + uint8_t pageData[HITAG_PAGES*HITAG_PAGEBYTES]; + uint8_t pageKnown[HITAG_PAGES]; + uint8_t tagData[HITAG_PAGES*HITAG_PAGEBYTES+HITAG_PAGES]; + uint8_t pageRW[HITAG_PAGES]; + uint8_t pagePublic[HITAG_PAGES]; +} LFRFIDHitag; + +// main worker +struct LFRFIDHitagWorker { + FuriThread* thread; + FuriEventFlag* events; + uint8_t DMAeventCount; + + uint8_t carrierPrescaler; + + LFRFIDHitag* tag; + + LFRFIDHitagStatus workerstatus; + + ProtocolDict* dict; +}; + + +//------------------------------------------------------------------ forward function definitions ------------------------------------------------------------------ + +static int32_t lfrfid_hitag_worker_emulate_thread(void* thread_context); +static int32_t lfrfid_hitag_worker_read_thread(void* thread_context); + +static void lfrfid_hitag_worker_carrier_in_IC_mode(void* capture_context); + +//------------------------------------------------------------------ shared functions ------------------------------------------------------------------ + +LFRFIDHitagWorker* lfrfid_hitag_worker_alloc(ProtocolDict* dict) { + furi_assert(dict); + + LFRFIDHitagWorker* worker = malloc(sizeof(LFRFIDHitagWorker)); + worker->dict = dict; + + worker->thread = furi_thread_alloc_ex("LFRFIDHitagWorker", 2048, NULL, worker); + + worker->events = furi_event_flag_alloc(NULL); + worker->DMAeventCount = 0; + + worker->carrierPrescaler = 2; + + worker->workerstatus = LFRFIDHitagStatusScanning; + + + return worker; +} + +void lfrfid_hitag_worker_free(LFRFIDHitagWorker* worker) { + furi_thread_free(worker->thread); + furi_event_flag_free(worker->events); + free(worker); +} + +LFRFIDHitagStatus lfrfid_hitag_worker_get_status(LFRFIDHitagWorker* worker){ + return worker->workerstatus; +} + +void lfrfid_hitag_worker_start(LFRFIDHitagWorker* worker, LFRFIDHitagWorkerSetting setting) { + if(furi_thread_get_state(worker->thread) == FuriThreadStateStopped){ + switch(setting){ + case (LFRFIDHitagWorkerSettingRead): + DOLPHIN_DEED(DolphinDeedRfidRead); + furi_thread_set_callback(worker->thread, lfrfid_hitag_worker_read_thread); + break; + case (LFRFIDHitagWorkerSettingEmulate): + DOLPHIN_DEED(DolphinDeedRfidEmulate); + furi_thread_set_callback(worker->thread, lfrfid_hitag_worker_emulate_thread); + break; + default : //safety meassure, not to start a thread without a callback function + furi_thread_set_callback(worker->thread, lfrfid_hitag_worker_read_thread); + break; + } + furi_thread_start(worker->thread); + } +} + +void lfrfid_hitag_worker_stop(LFRFIDHitagWorker* worker) { + furi_event_flag_set(worker->events, 1 << LFRFIDHitagWorkerSignalStop); + furi_thread_join(worker->thread); +} + +//------------------------------------------------------------------ READ TAG: capture input interrupt functions ------------------------------------------------------------------ + +static void lfrfid_hitag_worker_capture_in_cc_isr(bool level, uint32_t duration, void* context) { + LfRfidHitagCaptureData* capData = context; + + //check if there is a new pair available: pulse + period + bool need_to_send = varint_pair_pack(capData->pair, level, duration); + + //if so put it on buffer stream so it can be processed outside this interrupt routine + if(need_to_send) { + buffer_stream_send_from_isr( + capData->stream, varint_pair_get_data(capData->pair), varint_pair_get_size(capData->pair)); + varint_pair_reset(capData->pair); + } + +} + +//------------------------------------------------------------------ READ TAG: carrier out TIMER, DMA & interrupts functions ------------------------------------------------------------------ + +static void lfrfid_hitag_worker_carrier_out_dma_isr(void* dma_context) { + LFRFIDHitagWorker* worker = (LFRFIDHitagWorker*)dma_context; + worker->DMAeventCount++; + + if(LL_DMA_IsActiveFlag_HT1(DMA1)) { + LL_DMA_ClearFlag_HT1(DMA1); + //halfway through DMA buffer --> first half can be repopulated + //just signal that it can be repopulated, not execute the repopulating itself, since this is an interrupt, holding up all other stuff + furi_event_flag_set(worker->events, 1 << LFRFIDHitagWorkerSignalHalfTransfer); + } + + if(LL_DMA_IsActiveFlag_TC1(DMA1)) { + LL_DMA_ClearFlag_TC1(DMA1); + //fully through DMA buffer --> second half can be repopulated + //just signal that it can be repopulated, not execute the repopulating itself, since this is an interrupt, holding up all other stuff + furi_event_flag_set(worker->events, 1 << LFRFIDHitagWorkerSignalTransferComplete); + } +} + +void lfrfid_hitag_worker_carrier_out_start( + uint32_t* duration, + uint32_t* pulse, + size_t length, + void* context) { + + // configure timer + FURI_CRITICAL_ENTER(); + LL_TIM_DeInit(CARRIER_OUT_TIMER); + FURI_CRITICAL_EXIT(); + + LL_TIM_InitTypeDef TIM_InitStruct = {0}; + TIM_InitStruct.Prescaler = SystemCoreClock / (125000 * HITAG_BASEPERIOD) - 1; // sets the basis TIMER frequency to 8*freq (1MHz) for the timer + TIM_InitStruct.Autoreload = HITAG_BASEPERIOD - 1; //initial PWM period =125kHz (ARR will be handled via DMA further on) + LL_TIM_Init(CARRIER_OUT_TIMER, &TIM_InitStruct); + LL_TIM_DisableARRPreload(CARRIER_OUT_TIMER); + + LL_TIM_OC_InitTypeDef TIM_OC_InitStruct = {0}; + TIM_OC_InitStruct.OCMode = LL_TIM_OCMODE_PWM1; + TIM_OC_InitStruct.OCNState = LL_TIM_OCSTATE_ENABLE; + TIM_OC_InitStruct.CompareValue = HITAG_BASEPERIOD/2; //initial pulse duration of half period ((CCR will be handled via DMA further on) + LL_TIM_OC_Init( + CARRIER_OUT_TIMER, CARRIER_OUT_TIMER_CHANNEL, &TIM_OC_InitStruct); + + LL_TIM_OC_SetPolarity( + CARRIER_OUT_TIMER, CARRIER_OUT_TIMER_CHANNEL, LL_TIM_OCPOLARITY_HIGH); + LL_TIM_EnableDMAReq_UPDATE(CARRIER_OUT_TIMER); + + // configure DMA "mem -> ARR" channel + LL_DMA_InitTypeDef dma_config = {0}; + dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (CARRIER_OUT_TIMER->ARR); + dma_config.MemoryOrM2MDstAddress = (uint32_t)duration; + dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; + dma_config.Mode = LL_DMA_MODE_CIRCULAR; + dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; + dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; + dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; + dma_config.NbData = length; + dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM1_UP; + dma_config.Priority = LL_DMA_MODE_NORMAL; + LL_DMA_Init(DMA1, LL_DMA_CHANNEL_1, &dma_config); + LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1); + + // configure DMA "mem -> CCR1" channel + dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (CARRIER_OUT_TIMER->CCR1); + dma_config.MemoryOrM2MDstAddress = (uint32_t)pulse; + dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; + dma_config.Mode = LL_DMA_MODE_CIRCULAR; + dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; + dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; + dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; + dma_config.NbData = length; + dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM1_UP; + dma_config.Priority = LL_DMA_MODE_NORMAL; + LL_DMA_Init(DMA1, LL_DMA_CHANNEL_2, &dma_config); + LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2); + + // attach interrupt to one of DMA channels + furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch1, lfrfid_hitag_worker_carrier_out_dma_isr, context); + LL_DMA_EnableIT_TC(DMA1, LL_DMA_CHANNEL_1); + LL_DMA_EnableIT_HT(DMA1, LL_DMA_CHANNEL_1); + + // start + LL_TIM_EnableAllOutputs(CARRIER_OUT_TIMER); + + LL_TIM_SetCounter(CARRIER_OUT_TIMER, 0); + LL_TIM_EnableCounter(CARRIER_OUT_TIMER); +} + +void lfrfid_hitag_worker_carrier_out_stop() { + LL_TIM_DisableCounter(CARRIER_OUT_TIMER); + LL_TIM_DisableAllOutputs(CARRIER_OUT_TIMER); + + furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch1, NULL, NULL); + LL_DMA_DisableIT_TC(DMA1, LL_DMA_CHANNEL_1); + LL_DMA_DisableIT_HT(DMA1, LL_DMA_CHANNEL_1); + + FURI_CRITICAL_ENTER(); + + LL_DMA_DeInit(DMA1, LL_DMA_CHANNEL_1); + LL_DMA_DeInit(DMA1, LL_DMA_CHANNEL_2); + LL_TIM_DeInit(CARRIER_OUT_TIMER); + + FURI_CRITICAL_EXIT(); +} + +//------------------------------------------------------------------ EMULATE TAG: pull out TIMER, DMA & interrupts functions ------------------------------------------------------------------ + +static void lfrfid_hitag_worker_pull_out_dma_stop(void* capture_context){ + //reconfigure pull_out pin to fixed low state + //via forced OC INACTIVE MODE + LL_TIM_OC_SetMode(PULL_OUT_TIMER, PULL_OUT_TIMER_CHANNEL, LL_TIM_OCMODE_FORCED_INACTIVE); + + //disable DMA interrupts + //furi_hal_interrupt_set_isr(PULL_OUT_DMA_CH1_IRQ, NULL, NULL); + //LL_DMA_DisableIT_HT(PULL_OUT_DMA_CH1_DEF); + //LL_DMA_DisableIT_TC(PULL_OUT_DMA_CH1_DEF); + + //disable DMA channels & requests + LL_TIM_DisableDMAReq_UPDATE(PULL_OUT_TIMER); + LL_DMA_DisableChannel(PULL_OUT_DMA_CH1_DEF); //need to disable when using normal mode? + LL_DMA_DisableChannel(PULL_OUT_DMA_CH2_DEF); //need to disable when using normal mode? + //LL_TIM_DisableAllOutputs(PULL_OUT_TIMER); //no need to disable when reconfiguring pin? + + //for logging both ccr & arr times during emulate (iso only arr) + LL_TIM_DisableIT_CC3(PULL_OUT_TIMER); + + //switch carrier detection back to input capture for stable readings (required for command detection) + lfrfid_hitag_worker_carrier_in_IC_mode(capture_context); +} + +static void lfrfid_hitag_worker_pull_out_dma_isr(void* capture_context) { + + /* currently no HT interrupt enabled, only TC + if(LL_DMA_IsActiveFlag_HT1(PULL_OUT_DMA)) { + LL_DMA_ClearFlag_HT1(PULL_OUT_DMA); + //lfrfid_hitag_worker_pull_out_dma_stop(); + } + */ + + if(LL_DMA_IsActiveFlag_TC1(PULL_OUT_DMA)) { + LL_DMA_ClearFlag_TC1(PULL_OUT_DMA); + lfrfid_hitag_worker_pull_out_dma_stop(capture_context); + } +} + +static void lfrfid_hitag_worker_pull_out_dma_start(size_t length){ + + //array pointers remain the same, but length changes so + //reset DMA length (only possible when DMA channel is disabled + //this also resets the DMA counter (the DMA length is the 'remaining counter' + LL_DMA_SetDataLength(PULL_OUT_DMA_CH1_DEF, length); + LL_DMA_SetDataLength(PULL_OUT_DMA_CH2_DEF, length); + + //enable DMA + LL_DMA_EnableChannel(PULL_OUT_DMA_CH1_DEF); + LL_DMA_EnableChannel(PULL_OUT_DMA_CH2_DEF); + + //set OC to PWM1 mode + LL_TIM_OC_SetMode(PULL_OUT_TIMER, PULL_OUT_TIMER_CHANNEL, LL_TIM_OCMODE_PWM1); //during emulate (dma controlled) mode + + //only enable DMA requests on timer update after enabling both channels (otherwise you risk that first DMA request only triggers one of the channels) + LL_TIM_EnableDMAReq_UPDATE(PULL_OUT_TIMER); + + //for logging both ccr & arr times during emulate (iso only arr) + LL_TIM_EnableIT_CC3(PULL_OUT_TIMER); +} + +static void lfrfid_hitag_worker_pull_out_dma_setup(uint32_t* duration, uint32_t* pulse, size_t length, void* capture_context){ + + //DMA setup + LL_TIM_DisableDMAReq_UPDATE(PULL_OUT_TIMER);//start with DMA requests disabled + + // configure DMA "mem -> ARR" channel + LL_DMA_InitTypeDef dma_config = {0}; + dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (PULL_OUT_TIMER->ARR); + dma_config.MemoryOrM2MDstAddress = (uint32_t)duration; + dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; + //dma_config.Mode = LL_DMA_MODE_CIRCULAR; //keep cycling through memory array + dma_config.Mode = LL_DMA_MODE_NORMAL; //cycle only once through memory array + dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; + dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; + dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; + dma_config.NbData = length; + dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; + dma_config.Priority = LL_DMA_MODE_NORMAL; + LL_DMA_Init(PULL_OUT_DMA_CH1_DEF, &dma_config); + + // configure DMA "mem -> CCR3" channel + dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (PULL_OUT_TIMER->CCR3); + dma_config.MemoryOrM2MDstAddress = (uint32_t)pulse; + dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; + //dma_config.Mode = LL_DMA_MODE_CIRCULAR; //keep cycling through memory array + dma_config.Mode = LL_DMA_MODE_NORMAL; //cycle only once through memory array + dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; + dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; + dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; + dma_config.NbData = length; + dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; + dma_config.Priority = LL_DMA_MODE_NORMAL; + LL_DMA_Init(PULL_OUT_DMA_CH2_DEF, &dma_config); + + // enable DMA interrupts + furi_hal_interrupt_set_isr(PULL_OUT_DMA_CH1_IRQ, lfrfid_hitag_worker_pull_out_dma_isr, capture_context); + //LL_DMA_EnableIT_HT(PULL_OUT_DMA_CH1_DEF); //let's try normal DMA mode and only transfer complete interrupt + LL_DMA_EnableIT_TC(PULL_OUT_DMA_CH1_DEF); + +} + + +//------------------------------------------------------------------ EMULATE TAG: carrier input TIMER & interrupt functions (switching input capture & ETR)------------------------------------------------------------------ + +static void lfrfid_hitag_worker_carrier_in_isr(void* capture_context) { + if(LL_TIM_IsActiveFlag_CC1(CARRIER_IN_TIMER)) { + uint32_t newTIMval = LL_TIM_IC_GetCaptureCH1(CARRIER_IN_TIMER); + LL_TIM_ClearFlag_CC1(CARRIER_IN_TIMER); + + //ARR for TIM2 in input capture mode is UINT32_MAX so at freq of 1MHz this is 70+ minutes, instead of resetting counter, just store prev value and take difference of both + //this will increase measurement accuracy, since both values are then purely hardware controlled, no dependency of interrupt timing to (re)set any timer values + //LL_TIM_SetCounter(CARRIER_IN_TIMER, 0); + + LfRfidHitagCaptureData* capData = capture_context; + capData->capCounter++; + //pack varint (as part of a pair) + //cleaner would be to pack as a varint iso varint_pair, though i'm not sure if I can get the data & size of a packed varint + varint_pair_pack(capData->pair, true, newTIMval - capData->prevTIMval); + //but send anyhow, doesn't matter that it's not a pair + buffer_stream_send_from_isr(capData->stream, varint_pair_get_data(capData->pair), varint_pair_get_size(capData->pair)); + varint_pair_reset(capData->pair); + + capData->prevTIMval = newTIMval; + } + else if(LL_TIM_IsActiveFlag_UPDATE(CARRIER_IN_TIMER)) { + uint32_t newTIMval = LL_TIM_GetCounter(CARRIER_IN_REFERENCE_TIMER); + LL_TIM_ClearFlag_UPDATE(CARRIER_IN_TIMER); + + //ARR for ref timer in ETR mode is UINT32_MAX so at freq of 1MHz this is 70+ minutes, instead of resetting counter, just store prev value and take difference of both + //this will increase measurement accuracy, since both values are then purely hardware controlled, no dependency of interrupt timing to (re)set any timer values + LL_TIM_SetCounter(CARRIER_IN_REFERENCE_TIMER, 0); + + LfRfidHitagCaptureData* capData = capture_context; + capData->capCounter++; + //pack varint (as part of a pair) + //cleaner would be to pack as a varint iso varint_pair, though i'm not sure if I can get the data & size of a packed varint + varint_pair_pack(capData->pair, true, newTIMval);// - capData->prevTIMval); + //but send anyhow, doesn't matter that it's not a pair + buffer_stream_send_from_isr(capData->stream, varint_pair_get_data(capData->pair), varint_pair_get_size(capData->pair)); + varint_pair_reset(capData->pair); + + //capData->prevTIMval = newTIMval; + } else if(LL_TIM_IsActiveFlag_CC3(CARRIER_IN_TIMER)) { + uint32_t newTIMval = LL_TIM_GetCounter(CARRIER_IN_REFERENCE_TIMER); + LL_TIM_ClearFlag_CC3(CARRIER_IN_TIMER); + + //don't reset timer for the ccr (HIGH) timing, this way the arr (duration) timing is still full duraton timing (iso only LOW timing) + //LL_TIM_SetCounter(CARRIER_IN_REFERENCE_TIMER, 0); + + LfRfidHitagCaptureData* capData = capture_context; + capData->capCounter++; + //pack varint (as part of a pair) + //cleaner would be to pack as a varint iso varint_pair, though i'm not sure if I can get the data & size of a packed varint + varint_pair_pack(capData->pair, true, newTIMval);// - capData->prevTIMval); + //but send anyhow, doesn't matter that it's not a pair + buffer_stream_send_from_isr(capData->stream, varint_pair_get_data(capData->pair), varint_pair_get_size(capData->pair)); + varint_pair_reset(capData->pair); + + //capData->prevTIMval = newTIMval; + } +} + +static void lfrfid_hitag_worker_carrier_in_ETR_mode(void* capture_context, uint8_t ext_prescaler){ + //disable counters temporarily + LL_TIM_DisableCounter(CARRIER_IN_TIMER); + LL_TIM_DisableCounter(CARRIER_IN_REFERENCE_TIMER); + + //disable Input capture & related interrupt + LL_TIM_DisableIT_CC1(CARRIER_IN_TIMER); + LL_TIM_CC_DisableChannel(CARRIER_IN_TIMER, CARRIER_IN_TIMER_DIR_CH); + + //reset capdata prev value + LfRfidHitagCaptureData* capData = capture_context; + capData->prevTIMval = 0; + + //switch clocksource to ETR with external prescaling via ARR + LL_TIM_SetPrescaler(CARRIER_IN_TIMER, 1-1);//prescaler is only applied at next update event + LL_TIM_GenerateEvent_UPDATE(CARRIER_IN_TIMER); //prescaler is only applied at next update event //TODO: how to prevent this from creating a capdata entry + LL_TIM_SetAutoReload(CARRIER_IN_TIMER, ext_prescaler-1); + LL_TIM_SetClockSource(CARRIER_IN_TIMER, LL_TIM_CLOCKSOURCE_EXT_MODE2); + + //reconfigure carrier_in pin to TIM2 ETR + furi_hal_gpio_init_ex(&gpio_rfid_carrier, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedLow, GpioAltFn2TIM2); + + //reset timer counter & capture context for period calculation + LL_TIM_SetCounter(CARRIER_IN_TIMER, 0); + LL_TIM_SetCounter(CARRIER_IN_REFERENCE_TIMER, 0); + + //enable interrupt via update + LL_TIM_EnableIT_UPDATE(CARRIER_IN_TIMER); + + //re-enable counters + LL_TIM_EnableCounter(CARRIER_IN_TIMER); + LL_TIM_EnableCounter(CARRIER_IN_REFERENCE_TIMER); +} + +static void lfrfid_hitag_worker_carrier_in_IC_mode(void* capture_context){ + //disable counters temporarily + LL_TIM_DisableCounter(CARRIER_IN_TIMER); + LL_TIM_DisableCounter(CARRIER_IN_REFERENCE_TIMER); + + //disable interrupt via update + LL_TIM_DisableIT_UPDATE(CARRIER_IN_TIMER); + + //reset capdata prev value + LfRfidHitagCaptureData* capData = capture_context; + capData->prevTIMval = 0; + + //switch clocksource to system/64, external prescaling is handled in IC prescaler + LL_TIM_SetClockSource(CARRIER_IN_TIMER, LL_TIM_CLOCKSOURCE_INTERNAL); + LL_TIM_SetPrescaler(CARRIER_IN_TIMER, 64-1); //prescaler is only applied at next update event + LL_TIM_GenerateEvent_UPDATE(CARRIER_IN_TIMER); //prescaler is only applied at next update event //TODO: how to prevent this from creating a capdata entry + LL_TIM_SetAutoReload(CARRIER_IN_TIMER, UINT32_MAX); + + //reconfigure carrier_in pin to TIM2 CH1 for input capture + furi_hal_gpio_init_ex(&gpio_rfid_carrier, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedLow, GpioAltFn1TIM2); + + //reset timer counter & capture context for period calculation + LL_TIM_SetCounter(CARRIER_IN_TIMER, 0); + LL_TIM_SetCounter(CARRIER_IN_REFERENCE_TIMER, 0); + + //enable Input capture & related interrupt + LL_TIM_EnableIT_CC1(CARRIER_IN_TIMER); + LL_TIM_CC_EnableChannel(CARRIER_IN_TIMER, CARRIER_IN_TIMER_DIR_CH); + + //re-enable counters + LL_TIM_EnableCounter(CARRIER_IN_TIMER); + LL_TIM_EnableCounter(CARRIER_IN_REFERENCE_TIMER); +} + +static void lfrfid_hitag_worker_carrier_in_start(void* capture_context, uint8_t ext_prescaler, uint32_t* duration, uint32_t* pulse, size_t length) { + FURI_CRITICAL_ENTER(); + LL_DMA_DeInit(PULL_OUT_DMA_CH1_DEF); //required? + LL_DMA_DeInit(PULL_OUT_DMA_CH2_DEF); //required? + LL_TIM_DeInit(CARRIER_IN_TIMER); + LL_TIM_DeInit(CARRIER_IN_REFERENCE_TIMER); + FURI_CRITICAL_EXIT(); + + //setup reference timer: simple setup with base freq of 1MHz and max autoreload + LL_TIM_InitTypeDef TIM_InitStruct_Ref = {0}; + TIM_InitStruct_Ref.Prescaler = 64 - 1; //system base freq is 64MHz, so this sets base freq for TIM to 1MHz (aka 1us period) + TIM_InitStruct_Ref.CounterMode = LL_TIM_COUNTERMODE_UP; + TIM_InitStruct_Ref.Autoreload = UINT32_MAX; + TIM_InitStruct_Ref.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1; + LL_TIM_Init(CARRIER_IN_REFERENCE_TIMER, &TIM_InitStruct_Ref); + + //setup carrier in timer for input capture + LL_TIM_InitTypeDef TIM_InitStruct = {0}; + TIM_InitStruct.Prescaler = 64 - 1; //system base freq is 64MHz, so this sets base freq for TIM to 1MHz (aka 1us period) + TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP; + TIM_InitStruct.Autoreload = UINT32_MAX; + TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1; + LL_TIM_Init(CARRIER_IN_TIMER, &TIM_InitStruct); + + LL_TIM_DisableARRPreload(CARRIER_IN_TIMER); + LL_TIM_SetClockSource(CARRIER_IN_TIMER, LL_TIM_CLOCKSOURCE_INTERNAL); //default is internal, so likely not required + LL_TIM_DisableDMAReq_TRIG(CARRIER_IN_TIMER); + LL_TIM_DisableIT_TRIG(CARRIER_IN_TIMER); + + //meanwhile already prepre the ETR + LL_TIM_ConfigETR( + CARRIER_IN_TIMER, + LL_TIM_ETR_POLARITY_INVERTED, + LL_TIM_ETR_PRESCALER_DIV1, + LL_TIM_ETR_FILTER_FDIV1); + + //INPUT CAPTURE SETUP + // Timer: channel 1 direct (from GPIO) + LL_TIM_IC_SetActiveInput(CARRIER_IN_TIMER, CARRIER_IN_TIMER_DIR_CH, LL_TIM_ACTIVEINPUT_DIRECTTI); + //prescaling direct channel seems to be working fine (and is necessary since otherwise sd write cannot keep up) + if (ext_prescaler==4){ + LL_TIM_IC_SetPrescaler(CARRIER_IN_TIMER, CARRIER_IN_TIMER_DIR_CH, LL_TIM_ICPSC_DIV4); + } else if (ext_prescaler==2){ + LL_TIM_IC_SetPrescaler(CARRIER_IN_TIMER, CARRIER_IN_TIMER_DIR_CH, LL_TIM_ICPSC_DIV2); + } else { + LL_TIM_IC_SetPrescaler(CARRIER_IN_TIMER, CARRIER_IN_TIMER_DIR_CH, LL_TIM_ICPSC_DIV1); + } + LL_TIM_IC_SetPolarity(CARRIER_IN_TIMER, CARRIER_IN_TIMER_DIR_CH, LL_TIM_IC_POLARITY_RISING); + LL_TIM_IC_SetFilter(CARRIER_IN_TIMER, CARRIER_IN_TIMER_DIR_CH, LL_TIM_IC_FILTER_FDIV1); + /* Timer: channel 2 indirect (from channel 1) + // only measure & write to file the period (not the on & off cycle), this reduced CPU load on SD write to (hopefully) keep up with period measurements without prescaler + LL_TIM_IC_SetActiveInput(CARRIER_IN_TIMER, CARRIER_IN_TIMER_IND_CH, LL_TIM_ACTIVEINPUT_INDIRECTTI); + //presaling indirect channel doesn't really work well yet :/ + LL_TIM_IC_SetPrescaler(CARRIER_IN_TIMER, CARRIER_IN_TIMER_IND_CH, LL_TIM_ICPSC_DIV2); + LL_TIM_IC_SetPolarity(CARRIER_IN_TIMER, CARRIER_IN_TIMER_IND_CH, LL_TIM_IC_POLARITY_FALLING); + LL_TIM_IC_SetFilter(CARRIER_IN_TIMER, CARRIER_IN_TIMER_IND_CH, LL_TIM_IC_FILTER_FDIV1); + */ + + //set interrupt callback for capturing period + LL_TIM_EnableIT_CC1(CARRIER_IN_TIMER); + //LL_TIM_EnableIT_CC2(CARRIER_IN_TIMER); + furi_hal_interrupt_set_isr(FuriHalInterruptIdTIM2, lfrfid_hitag_worker_carrier_in_isr, capture_context); + + //OUTPUT COMPARE SETUP + LL_TIM_OC_InitTypeDef TIM_OC_InitStruct = {0}; + //TIM_OC_InitStruct.OCMode = LL_TIM_OCMODE_PWM1; //during emulate (dma controlled) mode + TIM_OC_InitStruct.OCMode = LL_TIM_OCMODE_FORCED_INACTIVE; //during carrier in put output to forced low state + TIM_OC_InitStruct.OCState = LL_TIM_OCSTATE_ENABLE; + TIM_OC_InitStruct.CompareValue = 0; //0% this should have almost similar effect as keeping output forced inactive (there's still some micropulse emited, going high at ARR and immediately down again at CCR value) + LL_TIM_OC_Init(PULL_OUT_TIMER, PULL_OUT_TIMER_CHANNEL, &TIM_OC_InitStruct); + LL_TIM_OC_SetPolarity( + PULL_OUT_TIMER, PULL_OUT_TIMER_CHANNEL, LL_TIM_OCPOLARITY_HIGH); //active high (gpio goes high when pulse is high) + + //INIT DMA (do not start it yet) + lfrfid_hitag_worker_pull_out_dma_setup(duration, pulse, length, capture_context); + + LL_TIM_CC_EnableChannel(CARRIER_IN_TIMER, CARRIER_IN_TIMER_DIR_CH); + //LL_TIM_CC_EnableChannel(CARRIER_IN_TIMER, CARRIER_IN_TIMER_IND_CH); + LL_TIM_CC_EnableChannel(PULL_OUT_TIMER, PULL_OUT_TIMER_CHANNEL); + LL_TIM_SetCounter(CARRIER_IN_TIMER, 0); + LL_TIM_SetCounter(CARRIER_IN_REFERENCE_TIMER, 0); + LL_TIM_EnableCounter(CARRIER_IN_TIMER); + LL_TIM_EnableCounter(CARRIER_IN_REFERENCE_TIMER); +} + +void lfrfid_hitag_worker_carrier_in_stop() { + furi_hal_interrupt_set_isr(FuriHalInterruptIdTIM2, NULL, NULL); + + LL_TIM_DisableCounter(CARRIER_IN_TIMER); + LL_TIM_DisableCounter(CARRIER_IN_REFERENCE_TIMER); + LL_TIM_DisableAllOutputs(CARRIER_IN_TIMER); //used for pull pin OC + + FURI_CRITICAL_ENTER(); + LL_DMA_DeInit(PULL_OUT_DMA_CH1_DEF); + LL_DMA_DeInit(PULL_OUT_DMA_CH2_DEF); + LL_TIM_DeInit(CARRIER_IN_TIMER); + LL_TIM_DeInit(CARRIER_IN_REFERENCE_TIMER); + FURI_CRITICAL_EXIT(); +} + +//------------------------------------------------------------------ hitag data parsing functions ------------------------------------------------------------------ + +static void lfrfid_hitag_worker_yield_dma_buffer_BPLM(LfRfidHitagCMDData* dataCMD, LfRfidHitagCMDData* backupCMD, TimerDMAData* dataDMA, size_t start, size_t len){ + //based on interal timer with freq of 1MHz (period of 1us) + //use active command + LfRfidHitagCMDData* data = dataCMD; + if (!data->CMDactive){ + data = backupCMD; + } + uint16_t lastarrposition = 0; + size_t toskip = 0; + bool itemProcessed = true; + size_t s=start; + //for ( ; data->CMDloop || data->CMDposition < data->CMDlength ; data->CMDposition++){ + while(1){ + //at the end of current command (real or backup), check if there's unprocessed CMD available, if not switch to backup + if (data->CMDposition == data->CMDlength){ + data->CMDcount++; + if (dataCMD->CMDposition < dataCMD->CMDlength){ //currently backup & new unprocessed command available --> switch over to real CMD + data->CMDactive = false; + data = dataCMD; + data->CMDactive = true; + data->CMDcount = 0; + } else if (dataCMD->CMDloop){ //if looping (real or backup) --> reset real (current) CMD to startposition + data->CMDposition = 0; + data->CMDsubposition = 0; + } else if (dataCMD->CMDactive){ //real command and not looping --> switch to backup CMD and reset backup CMD to startposition + data->CMDactive = false; + data = backupCMD; + data->CMDactive = true; + data->CMDposition = 0; + data->CMDsubposition = 0; + data->CMDcount = 0; + } else { //backupCMD but set to not loop --> loop anyway: reset to startposition + data->CMDposition = 0; + data->CMDsubposition = 0; + } + } + //start processing the next CMD item + lastarrposition = s; + if (s==start){ + toskip = data->CMDsubposition; + } else { + toskip = 0; + data->CMDsubposition=0; + } + switch (data->CMDtype[data->CMDposition]) + { + case HITAG_STOP: //just 1 STOP cycle (HITAG_LOW lows + HITAG_STOP_HIGH highs) + if(s>start){ + dataDMA->timer_buffer_arr[s-1]+=HITAG_LOW*HITAG_BASEPERIOD; + } + for (size_t t=0;ttimer_buffer_ccr[s]=HITAG_BASEPERIOD/2; + dataDMA->timer_buffer_arr[s]=HITAG_BASEPERIOD-1; + s++; + } + break; + case HITAG_ON: //x ON cycles --> update next X arr/ccr values + for (size_t x=0;xCMDvalue[data->CMDposition]-toskip;x++){ + if (s==len+start){ + //arrays are filled, do not continue out + itemProcessed = false; + break; + } + dataDMA->timer_buffer_ccr[s]=HITAG_BASEPERIOD/2; + dataDMA->timer_buffer_arr[s]=HITAG_BASEPERIOD-1; + s++; + } + break; + case HITAG_OFF: //x OFF cycles (update previous arr value) + if(s>start){ + dataDMA->timer_buffer_arr[s-1]+=data->CMDvalue[data->CMDposition]*HITAG_BASEPERIOD; + } + break; + default: // <=HITAG_BYTE: //bits to be sent + //get each bit & update arr/ccr accordingly (7 lows + HITAG_0_HIGH/HITAG_1_HIGH highs) + for (size_t b=8-data->CMDtype[data->CMDposition];b<8;b++){ + if(s>start){ + dataDMA->timer_buffer_arr[s-1]+=HITAG_LOW*HITAG_BASEPERIOD; + } + if (0x80 & (data->CMDvalue[data->CMDposition]<timer_buffer_ccr[s]=HITAG_BASEPERIOD/2; + dataDMA->timer_buffer_arr[s]=HITAG_BASEPERIOD-1; + s++; + if(toskip>0){ + toskip--; + s--; + } + } + } else { + for (size_t t=0;ttimer_buffer_ccr[s]=HITAG_BASEPERIOD/2; + dataDMA->timer_buffer_arr[s]=HITAG_BASEPERIOD-1; + s++; + if(toskip>0){ + toskip--; + s--; + } + } + } + if (!itemProcessed){ + break; + } + } + break; + } + if(!itemProcessed){ + break; + } + data->CMDposition++; + } + + /*if buffer is not full, but command is fully processed: fill remainder of buffer with normal on cycles + if (stimer_buffer_ccr[s]=HITAG_BASEPERIOD/4; //25%duty cycle + dataDMA->timer_buffer_arr[s]=1*HITAG_BASEPERIOD-1; + } else { + dataDMA->timer_buffer_ccr[s]=HITAG_BASEPERIOD/2; + dataDMA->timer_buffer_arr[s]=HITAG_BASEPERIOD-1; + } + } + //other stuff to do? + } + */ + + //if buffer was full, but command not yet fully processed: set starting position for next time: + if(!itemProcessed){ + data->CMDsubposition += (len+start)-lastarrposition; + } else { + data->CMDsubposition = 0; + } + +} + +static uint16_t lfrfid_hitag_worker_yield_dma_buffer_MC(LfRfidHitagReplyData* dataReply, TimerDMAData* dataDMA, uint8_t carrierPrescaler){ + //based on external carrier with freq of 125kHz (period of 8us) + + uint16_t dma_len = 0; + + bool bit=false; + bool prevBit = true; //must be true to initialze MC yield for first bit + + //process reply bits and add HIGH-LOW for 1 or LOW-HIGH for 0 + for (uint8_t i = 0;ireplylength;i++){ + for (int8_t b=dataReply->replytype[i]-1;b>=0;b--){ + bit = (dataReply->replyvalue[i]>>b & 0b00000001); + if (bit){ //HIGH-LOW + if (prevBit){ //if first bit (initial value of prevBit=true) or if prev bit was 1: start of new default cycle + dataDMA->timer_buffer_arr[dma_len]=32-1; + dataDMA->timer_buffer_ccr[dma_len]=16; + dma_len++; + } else { //previous bit was 0, so extend the ccr and arr with 16 + dataDMA->timer_buffer_arr[dma_len-1]+=16; + dataDMA->timer_buffer_ccr[dma_len-1]+=16; + } + } else { //LOW-HIGH + if (prevBit){ //previous bit was 1 so extend the LOW (arr only) of prev cycle with 16 and add new default cycle + dataDMA->timer_buffer_arr[dma_len-1]+=16; + + dataDMA->timer_buffer_arr[dma_len]=32-1; + dataDMA->timer_buffer_ccr[dma_len]=16; + dma_len++; + } else { //previous bit was 0 so just add new default cycle + dataDMA->timer_buffer_arr[dma_len]=32-1; + dataDMA->timer_buffer_ccr[dma_len]=16; + dma_len++; + } + } + prevBit = bit; + } + } + + //add extra period, to reset timer back to normal carrier_in detection after reply has finished + //also required to make sure the last real dma values are fully executed before dma signals transfer complete, which resets worker into command detection + dataDMA->timer_buffer_arr[dma_len]=carrierPrescaler-1; + dataDMA->timer_buffer_ccr[dma_len]=0; + dma_len++; + + return dma_len; +} + +static uint16_t lfrfid_hitag_worker_yield_dma_buffer_AC(LfRfidHitagReplyData* dataReply, TimerDMAData* dataDMA, uint8_t carrierPrescaler){ + //based on external carrier with freq of 125kHz (period of 8us) + + uint16_t dma_len = 0; + + //process reply bits and add 2 short pulses for 1 or 1 long pulse for 0 + for (uint8_t i = 0;ireplylength;i++){ + for (int8_t b=dataReply->replytype[i]-1;b>=0;b--){ + if (dataReply->replyvalue[i]>>b & 0b00000001){ + dataDMA->timer_buffer_arr[dma_len]=32-1; + dataDMA->timer_buffer_ccr[dma_len]=16;//16 + dma_len++; + dataDMA->timer_buffer_arr[dma_len]=32-1; + dataDMA->timer_buffer_ccr[dma_len]=16;//16 + dma_len++; + } else { + dataDMA->timer_buffer_arr[dma_len]=64-1; + dataDMA->timer_buffer_ccr[dma_len]=32;//32 + dma_len++; + } + } + } + + //add extra period, to reset timer back to normal carrier_in detection after reply has finished + //also required to make sure the last real dma values are fully executed before dma signals transfer complete, which resets worker into command detection + dataDMA->timer_buffer_arr[dma_len]=carrierPrescaler-1; + dataDMA->timer_buffer_ccr[dma_len]=0; + dma_len++; + + return dma_len; +} + +static void lfrfid_hitag_worker_decoder_feed(uint32_t pulse, uint32_t duration, uint32_t* memBlock, uint8_t* bits, uint8_t* Scount, bool* finished, LfRfidHitagProtocol protocol){ + UNUSED(pulse); + uint32_t bit = 1<<(32-1); + if (protocol == HitagProtocolManchesterCoding){ + if (HITAG_DURATION_S*(1-HITAG_DURATION_ERROR_MARGIN)<=duration && duration <=HITAG_DURATION_S*(1+HITAG_DURATION_ERROR_MARGIN) ){ + if (*bits == 0){ + //first bit is always a 1, this is startbit, no need to store it in memBlock + (*bits)++; + } else if (*bits == 1){ + //add 1 to the memBlock + memBlock[(*bits-1)/32] |= (bit >> (*bits-1)%32); //add 1 + (*bits)++; + } else { + //add same as previous bit + if (memBlock[(*bits-2)/32] & (bit >> (*bits-2)%32)){ + memBlock[(*bits-1)/32] |= (bit >> (*bits-1)%32); //add 1 + (*bits)++; + } else { + (*bits)++; + } + } + } else if (HITAG_DURATION_M*(1-HITAG_DURATION_ERROR_MARGIN)<=duration && duration <=HITAG_DURATION_M*(1+HITAG_DURATION_ERROR_MARGIN) ){ + if (*bits == 0){ + //10 detected, but startbit is not to be added to memBlock, and 0 is already in bitarry: + (*bits)++; + (*bits)++; + } else if (*bits == 1){ + //add 10 to the memBlock + memBlock[(*bits-1)/32] |= (bit >> (*bits-1)%32); //add 1 + (*bits)++; + (*bits)++;//add 0 is not necessary, since memBlock is initialized with all 0s, just increase counter + } else { + //if prev bit is 1 then add 10, else add 1 + if (memBlock[(*bits-2)/32] & (bit >> (*bits-2)%32)){ + memBlock[(*bits-1)/32] |= (bit >> (*bits-1)%32); //add 1 + (*bits)++; + (*bits)++;//add 0 is not necessary, since memBlock is initialized with all 0s, just increase counter + } else { + memBlock[(*bits-1)/32] |= (bit >> (*bits-1)%32); //add 1 + (*bits)++; + } + } + } else if (HITAG_DURATION_L*(1-HITAG_DURATION_ERROR_MARGIN)<=duration && duration <=HITAG_DURATION_L*(1+HITAG_DURATION_ERROR_MARGIN) ){ + if (*bits==0){ + //error, first pulse-duration of a bit sequence can't be a large one + //no need to do anything if bits is 0 then memBlock is also 00000... + } else { + //add 10 to the memBlock + memBlock[(*bits-1)/32] |= (bit >> (*bits-1)%32); //add 1 + (*bits)++; + (*bits)++;//add 0 is not necessary, since memBlock is initialized with all 0s, just increase counter + } + } else if (1000<=duration){ + if (*bits>0){ + *finished = true; + } + } else { + //invalid pulse: reset tempData if necessary + if (*bits>0){ + memset(memBlock, 0, HITAG_BLOCKPAGES*sizeof(memBlock[0])); + *bits = 0; + *finished = false; + } + } + } else if (protocol == HitagProtocolAntiCollision){ + if (HITAG_DURATION_S*(1-HITAG_DURATION_ERROR_MARGIN)<=duration && duration <=HITAG_DURATION_S*(1+HITAG_DURATION_ERROR_MARGIN) ){ + //if first small: add 1 to memBlock and increase small counter + if (*Scount==0){ + memBlock[(*bits-1)/32] |= (bit >> (*bits-1)%32); + (*bits)++; + (*Scount)++; + } else { + //if second small: reset small counter + *Scount=0; + } + } else if (HITAG_DURATION_L*(1-HITAG_DURATION_ERROR_MARGIN)<=duration && duration <=HITAG_DURATION_L*(1+HITAG_DURATION_ERROR_MARGIN) ){ + //if small counter == 1: error + if (*bits==0 || *Scount ==1){ + //error, first pulse-duration of a bit sequence can't be a large one or coming after only 1 small pulse-duration + memset(memBlock, 0, HITAG_BLOCKPAGES*sizeof(memBlock[0])); + *bits = 0; + *finished = false; + } else { + //add 0 to memBlock + (*bits)++; + } + } else if (1000<=duration){ + if (*bits>0){ + *finished = true; + } + } else { + //invalid pulse: reset tempData if necessary + if (*bits>0){ + memset(memBlock, 0, HITAG_BLOCKPAGES*sizeof(memBlock[0])); + *bits = 0; + *finished = false; + } + } + } +} + +static void lfrfid_hitag_worker_calc_crc(uint8_t* crc, uint8_t data, uint8_t bitcount){ + *crc ^= data; // crc = crc (exor) data + do { + if( *crc & 0x80 ) // if (MSB-CRC == 1) + { + *crc<<=1; // CRC = CRC bit-shift left + *crc ^= CRC_POLYNOM; // CRC = CRC (exor) CRC_POLYNOM + } + else + { + *crc<<=1; // CRC = CRC bit-shift left + } + } while(--bitcount); +} + +static bool lfrfid_hitag_worker_validate_command(uint32_t* bits, uint8_t len){ + if (len == 5 && (bits[0]>>(32-5)) == 0b11001){ + //SET_CCNEW + return true; + } + else if (len == 5 && (bits[0]>>(32-5)) == 0b00110){ + //SET_CC + return true; + } + else if (len == 45 && (bits[0]>>(32-5)) == 0b00000){ + //SELECT + + //calculate CRC for first 37 bits (4bytes + 5 bits) + uint8_t byte1 = bits[0]>>24 & 0xff; + uint8_t byte2 = bits[0]>>16 & 0xff; + uint8_t byte3 = bits[0]>>8 & 0xff; + uint8_t byte4 = bits[0] & 0xff; + uint8_t byte5 = bits[1]>>24 & 0b11111000; + uint8_t crcRead = bits[1]>>19 & 0xff; + + uint8_t crc = CRC_PRESET; + lfrfid_hitag_worker_calc_crc(&crc, byte1, 8); + lfrfid_hitag_worker_calc_crc(&crc, byte2, 8); + lfrfid_hitag_worker_calc_crc(&crc, byte3, 8); + lfrfid_hitag_worker_calc_crc(&crc, byte4, 8); + lfrfid_hitag_worker_calc_crc(&crc, byte5, 5); + + //and compare result with received crc + return (crcRead == crc); + } + //else if (len == 20 && (bits[0]>>31 || (bits[0]>>(32-4)) == 0b0111)){ + else if (len == 20 && bits[0] > 0x70000000){ + //R/W page/block (cmd starting with 1) or HALT (0111) --> so bit0 should be >= 0b01110000 00000000 00000000 00000000 (0x70000000) + //calculate CRC for first 12 bits (1byte + 4 bits) + uint8_t byte1 = bits[0]>>24 & 0xff; + uint8_t byte2 = bits[0]>>16 & 0b11110000; + uint8_t crcRead = bits[0]>>12 & 0xff; + + uint8_t crc = CRC_PRESET; + lfrfid_hitag_worker_calc_crc(&crc, byte1, 8); + lfrfid_hitag_worker_calc_crc(&crc, byte2, 4); + + //and compare result with received crc + return (crcRead == crc); + } + return false; +} + +static bool lfrfid_hitag_worker_validate_bits(uint32_t* bits, uint8_t bitcounter){ + if (47 >= bitcounter && bitcounter >= 45){ + for (uint8_t i=0;i0){ + bits[0] = bits[0]<<1 | bits[1]>>(32-1); + bits[1] = bits[1]<<1; + } + if (lfrfid_hitag_worker_validate_command(bits, 45)) + return true; + } + } + else if (22 >= bitcounter && bitcounter >= 20){ + for (uint8_t i=0;i0){ + bits[0] = bits[0]<<1;// | bits[1]>>(32-1); + //bits[1] = bits[1]<<1; + } + if (lfrfid_hitag_worker_validate_command(bits, 20)) + return true; + } + } + else if (7 >= bitcounter && bitcounter >= 5){ + for (uint8_t i=0;i0){ + bits[0] = bits[0]<<1;// | bits[1]>>(32-1); + //bits[1] = bits[1]<<1; + } + if (lfrfid_hitag_worker_validate_command(bits, 5)) + return true; + } + } + return false; +} + +static uint8_t lfrfid_hitag_worker_validate_ripple_delta(uint32_t delta){ + if (18 <= delta && delta <= 22) + return 1; + if (26 <= delta && delta <= 32) + return 1; + return 0; +} + +void lfrfid_hitag_worker_initialize_tag_data(LFRFIDHitag* tag){ + //initialize a tag datastructure + //set all pages to 0x00000000 + memset(tag->pageData, 0, sizeof(tag->pageData)); + + //set all pages to unknown + for (uint8_t i=0;ipageKnown[i]=0; + } + + //default RW settings: + memset(tag->pageRW, READ, HITAG_PAGES/2*sizeof(tag->pageRW[0])); + memset(tag->pageRW+HITAG_PAGES/2, READ|WRITE, HITAG_PAGES/2*sizeof(tag->pageRW[0])); + tag->pageRW[2]=0; + tag->pageRW[3]=0; + + //default public/secret settings: + for (uint8_t i=0;ipagePublic[i]=1; + } + for (uint8_t i=2;i<=31;i++){ + tag->pagePublic[i]=0; + } +} + +static void lfrfid_hitag_worker_combine_tag_data(LFRFIDHitag* tag){ + memcpy(tag->tagData, tag->pageData, HITAG_PAGES*HITAG_PAGEBYTES); + memcpy(tag->tagData+HITAG_PAGES*HITAG_PAGEBYTES, tag->pageKnown, HITAG_PAGES); +} + +static void lfrfid_hitag_worker_split_tag_data(LFRFIDHitag* tag){ + memcpy(tag->pageData, tag->tagData, HITAG_PAGES*HITAG_PAGEBYTES); + memcpy(tag->pageKnown, tag->tagData+HITAG_PAGES*HITAG_PAGEBYTES, HITAG_PAGES); +} + +void lfrfid_hitag_worker_process_config_page(LFRFIDHitag* tag){ + //RW settings via byte 0 + if (tag->pageData[4+0] & 0b10000000){ //block 1 (logData) RW or 0 + memset(tag->pageRW+1*4, READ|WRITE, 4*sizeof(tag->pageRW[0])); + } else { + memset(tag->pageRW+1*4, 0, 4*sizeof(tag->pageRW[0])); + } + if (tag->pageData[4+0] & 0b01000000){ //key A&B (page 2&3) W or 0 + memset(tag->pageRW+2, WRITE, 2*sizeof(tag->pageRW[0])); + } else { + memset(tag->pageRW+2, 0, 2*sizeof(tag->pageRW[0])); + } + if (tag->pageData[4+0] & 0b00100000){ //block 2 RW or RO + memset(tag->pageRW+2*4, READ|WRITE, 4*sizeof(tag->pageRW[0])); + } else { + memset(tag->pageRW+2*4, READ, 4*sizeof(tag->pageRW[0])); + } + if (tag->pageData[4+0] & 0b00010000){ //block 3 RW or RO + memset(tag->pageRW+3*4, READ|WRITE, 4*sizeof(tag->pageRW[0])); + } else { + memset(tag->pageRW+3*4, READ, 4*sizeof(tag->pageRW[0])); + } + if (tag->pageData[4+0] & 0b00001000){ //block 4 RW or RO + memset(tag->pageRW+4*4, READ|WRITE, 4*sizeof(tag->pageRW[0])); + } else { + memset(tag->pageRW+4*4, READ, 4*sizeof(tag->pageRW[0])); + } + if (tag->pageData[4+0] & 0b00000100){ //block 5 RW or RO + memset(tag->pageRW+5*4, READ|WRITE, 4*sizeof(tag->pageRW[0])); + } else { + memset(tag->pageRW+5*4, READ, 4*sizeof(tag->pageRW[0])); + } + if (tag->pageData[4+0] & 0b00000010){ //block 6 RW or RO + memset(tag->pageRW+6*4, READ|WRITE, 4*sizeof(tag->pageRW[0])); + } else { + memset(tag->pageRW+6*4, READ, 4*sizeof(tag->pageRW[0])); + } + if (tag->pageData[4+0] & 0b00000001){ //block 7 RW or RO + memset(tag->pageRW+7*4, READ|WRITE, 4*sizeof(tag->pageRW[0])); + } else { + memset(tag->pageRW+7*4, READ, 4*sizeof(tag->pageRW[0])); + } + //RW settings via byte 1 + if (tag->pageData[4+1] & 0b00010000){ //config page (1) RW or RO + tag->pageRW[1]=READ/WRITE; + } else { + tag->pageRW[1]=READ; + } + + //public/secret settings via byte 1 + if (tag->pageData[4+1] & 0b00000001){ //block 4-7 public or secret + for (uint8_t i=4*4;i<8*4;i++){ + tag->pagePublic[i]=1; + } + } else { + for (uint8_t i=4*4;i<8*4;i++){ + tag->pagePublic[i]=0; + } + } + +} + +//------------------------------------------------------------------ worker threads ------------------------------------------------------------------ + +static int32_t lfrfid_hitag_worker_emulate_thread(void* thread_context) { + LFRFIDHitagWorker* worker = (LFRFIDHitagWorker*)thread_context; + + //TODO: check if a tag has been properly loaded with known serial nr & config page + worker->tag = malloc(sizeof(LFRFIDHitag)); + lfrfid_hitag_worker_initialize_tag_data(worker->tag); + + size_t data_size = protocol_dict_get_data_size(worker->dict, LFRFIDProtocolHitag1); + protocol_dict_get_data(worker->dict, LFRFIDProtocolHitag1, worker->tag->tagData, data_size); + + lfrfid_hitag_worker_split_tag_data(worker->tag); + lfrfid_hitag_worker_process_config_page(worker->tag); + + //init capData for carrier_in + LfRfidHitagCaptureData* capData = malloc(sizeof(LfRfidHitagCaptureData)); + capData->stream = buffer_stream_alloc(EMULATE_BUFFER_SIZE, EMULATE_BUFFER_COUNT); + capData->pair = varint_pair_alloc(); + capData->capCounter = 0; + capData->prevTIMval = 0; + + //init dmaData for emulate mode: + uint16_t dmaLen = 0; + TimerDMAData* dataDMA = malloc(sizeof(TimerDMAData)); + + LfRfidHitagReplyData* dataReply = malloc(sizeof(LfRfidHitagReplyData)); + + //set pins to read setup + furi_hal_rfid_pins_read(); + // reconfigure carrier_out to fixed low state instead + furi_hal_gpio_init(&gpio_rfid_carrier_out, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_write(&gpio_rfid_carrier_out, false); + //reconfigure PA15 carrier_in to alt ftn 1 (TIM2 CH1) to use is for input capture + furi_hal_gpio_init_ex(&gpio_rfid_carrier, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedLow, GpioAltFn1TIM2); + //reconfigure pull to alternate function 1 (TIM2 CH3) to drive low (for read carrier) or pull antenna via DMA (for emulation) + furi_hal_gpio_init_ex(&gpio_nfc_irq_rfid_pull, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedLow, GpioAltFn1TIM2); + + //start capture via carrier in instead of rfid in + lfrfid_hitag_worker_carrier_in_start(capData, worker->carrierPrescaler, dataDMA->timer_buffer_arr, dataDMA->timer_buffer_ccr, dmaLen); + + uint8_t prescaler = worker->carrierPrescaler; + uint8_t period = 8*prescaler-1; + + bool carrier_on = false; + int8_t carrier_count = 0; + uint8_t carrier_threshold_on = 5; //better to use the #define on top for this constant or make this a constant + int8_t carrier_threshold_off = -5; //better to use the #define on top for this constant or make this a constant + + uint32_t periodcounter = 0; + + uint8_t localMax = 0; + uint8_t localMin = 255; + uint32_t localMaxIndex = 0; + uint32_t localMinIndex = 0; + uint32_t lastMaxIndex = 0; + uint32_t lastMinIndex = 0; + uint8_t maxBitCounter = 0; + uint8_t minBitCounter = 0; + uint32_t maxBits[5]; //room for 5x32 bits + uint32_t minBits[5]; //room for 5x32 bits + int8_t cmd = 0; + uint32_t one = 1<<(32-1); + uint32_t cmdIndex = 0; + + uint32_t miniMaxDelta = 0; + uint32_t miniMaxIndex = 0; + uint32_t prevMaxDelta = 0; + uint32_t maxDelta = 0; + + uint32_t miniMinDelta = 0; + uint32_t miniMinIndex = 0; + uint32_t prevMinDelta = 0; + uint32_t minDelta = 0; + + uint32_t prevduration = 0; + uint32_t duration = 0; + uint8_t durationcounter = 0; + uint8_t durationthreshold = 5; //better to use the #define on top for this constant or make this a constant + + uint8_t tagState = HitagStateIdle; + uint8_t tagMode = HitagModeBasic; + + while(1) { + Buffer* buffer = buffer_stream_receive(capData->stream, 1); + //and if so write fo file + if(buffer != NULL) { + size_t buffsize = buffer_get_size(buffer); + uint8_t* buffdata = buffer_get_data(buffer); + + //parse carrier_in duration & scan for cmds + size_t index = 0; + while(index < buffsize) { + prevduration = duration; + size_t tmp_size=varint_uint32_unpack(&duration, &buffdata[index], buffsize - index); + + if(tmp_size==0) { + FURI_LOG_E(TAG, "can't unpack varint pair"); + break; + } else { + index += tmp_size; + + periodcounter++; + + if (!carrier_on){ //detect if carrier is available + if (0.75*period <= duration && duration <= 1.25*period){ + carrier_count++; + if (carrier_count>=carrier_threshold_on){ + carrier_on = true; + localMax = 0; + localMin = 255; + minBitCounter = 0; + maxBitCounter = 0; + minBits[0] = 0; + minBits[1] = 0; + maxBits[0] = 0; + maxBits[1] = 0; + //maxBits = "" + + carrier_count = 0; + + //TODO: signal view that field is on (including the freq?) + } + } else { + carrier_count = 0; + } + } + else { + //detect if carrier is still available + if (duration <= 0.75*period || 1.25*period <= duration){ + carrier_count--; + if (carrier_count<=carrier_threshold_off){ + carrier_on = false; + carrier_count = 0; + + //TODO: signal view that field is off? eg colored LED sequence? + } + } else{ + carrier_count = 0; + } + + if (carrier_on){//if carrier is still available + //finetune the average period for reference + if (prevduration == duration){ + durationcounter++; + if (durationcounter == durationthreshold && period != duration){ + period = duration; + } + } else { + durationcounter = 0; + } + + //detect local MAX, and update maxCmd detection + if (duration > prevduration && duration > period){ + //rising edge above the normal periodduration: update localMax + localMax = duration; + localMaxIndex = periodcounter; + } else if (duration < prevduration && localMax > period && periodcounter <= localMaxIndex+2) { + //we passed a local maximum, derive length between current and prev max index + if (miniMaxDelta == 0){ + prevMaxDelta = maxDelta; + } + maxDelta = (localMaxIndex-lastMaxIndex)*prescaler; + + //check if there's a miniDelta and if it should be added to previous or next delta + if (miniMaxDelta > 0){ + //by default minidelta is added to next + //in case doing this does not result in 2 consequtive valid deltas, while adding it to previous does result in 2 consequtive valid deltas, then assign it to previous delta + if ((lfrfid_hitag_worker_validate_ripple_delta(prevMaxDelta+miniMaxDelta) + lfrfid_hitag_worker_validate_ripple_delta(maxDelta-miniMaxDelta)) > (lfrfid_hitag_worker_validate_ripple_delta(prevMaxDelta) + lfrfid_hitag_worker_validate_ripple_delta(maxDelta))){ + //update the previous (last) bit from 0 to 1 if a command was ongoing + if (maxBitCounter > 0){ + maxBits[(maxBitCounter-1)/32] |= (one >> (maxBitCounter-1)%32); + } + maxDelta -= miniMaxDelta; + lastMaxIndex = miniMaxIndex; + } + miniMaxDelta = 0; + } + + //now process the (possibly updated) delta + if (maxDelta >= 18){ + if (18 <= maxDelta && maxDelta <= 22){ + maxBitCounter++; + } else if (26 <= maxDelta && maxDelta <= 32){ + maxBits[(maxBitCounter)/32] |= (one >> (maxBitCounter)%32); + maxBitCounter++; + } else if (maxBitCounter > 0){ + //end of bitstring caused by invalid delta + + if (lfrfid_hitag_worker_validate_bits(maxBits, maxBitCounter)){ + cmd = 1; + cmdIndex = localMaxIndex; + + //CMD found, stop searching for minCMD: reset the minCmd detection + minBits[0] = 0; + minBits[1] = 0; + minBitCounter = 0; + } else { + //reset the maxCmd detection + maxBits[0] = 0; + maxBits[1] = 0; + maxBitCounter = 0; + } + } + lastMaxIndex = localMaxIndex; + } else { + miniMaxDelta = maxDelta; + miniMaxIndex = localMaxIndex; + } + localMax = 0; + } + + //terminate bitstring ico absence of next delta (a true end) + if (periodcounter == lastMaxIndex + 40/prescaler && maxBitCounter >0){ + + if (lfrfid_hitag_worker_validate_bits(maxBits, maxBitCounter)){ + cmd = 1; + cmdIndex = localMaxIndex; + + //CMD found, stop searching for minCMD: reset the minCmd detection + minBits[0] = 0; + minBits[1] = 0; + minBitCounter=0; + } else { + //reset the maxCmd detection + maxBits[0] = 0; + maxBits[1] = 0; + maxBitCounter=0; + } + } + + //detect local MIN, and update minCmd detection + if (duration < prevduration && duration < period){ + //rising edge above the normal periodduration: update localMax + localMin = duration; + localMinIndex = periodcounter; + } else if (duration > prevduration && localMin < period && periodcounter <= localMinIndex+2) { + //we passed a local minimum, derive length between current and prev max index + if (miniMinDelta == 0){ + prevMinDelta = minDelta; + } + minDelta = (localMinIndex-lastMinIndex)*prescaler; + + //check if there's a miniDelta and if it should be added to previous or next delta + if (miniMinDelta > 0){ + //by default minidelta is added to next + //in case doing this does not result in 2 consequtive valid deltas, while adding it to previous does result in 2 consequtive valid deltas, then assign it to previous delta + if ((lfrfid_hitag_worker_validate_ripple_delta(prevMinDelta+miniMinDelta) + lfrfid_hitag_worker_validate_ripple_delta(minDelta-miniMinDelta)) > (lfrfid_hitag_worker_validate_ripple_delta(prevMinDelta) + lfrfid_hitag_worker_validate_ripple_delta(minDelta))){ + //update the previous (last) bit from 0 to 1 if a command was ongoing + if (minBitCounter > 0){ + minBits[(minBitCounter-1)/32] |= (one >> (minBitCounter-1)%32); + } + minDelta -= miniMinDelta; + lastMinIndex = miniMinIndex; + } + miniMinDelta = 0; + } + + //now process the (possibly updated) delta + if (minDelta >= 18){ + if (18 <= minDelta && minDelta <= 22){ + minBitCounter ++; + } else if (26 <= minDelta && minDelta <= 32){ + minBits[(minBitCounter)/32] |= (one >> (minBitCounter)%32); + minBitCounter ++; + } else if (minBitCounter > 0){ + //end of bitstring caused by invalid delta + if (lfrfid_hitag_worker_validate_bits(minBits, minBitCounter)){ + cmd = -1; + cmdIndex = localMinIndex; + + //CMD found, stop searching for maxCMD: reset the maxCmd detection + maxBits[0] = 0; + maxBits[1] = 0; + maxBitCounter=0; + } else { + //reset the minCmd detection + minBits[0] = 0; + minBits[1] = 0; + minBitCounter = 0; + } + } + lastMinIndex = localMinIndex; + } else { + miniMinDelta = minDelta; + miniMinIndex = localMinIndex; + } + localMin = 255; + } + + //terminate of bitstring caused by absence of next delta (a true end) + if (periodcounter == lastMinIndex + 40/prescaler && minBitCounter >0){ + if (lfrfid_hitag_worker_validate_bits(minBits, minBitCounter)){ + cmd = -1; + cmdIndex = localMinIndex; + + //CMD found, stop searching for maxCMD: reset the maxCmd detection + maxBits[0] = 0; + maxBits[1] = 0; + maxBitCounter=0; + } else { + //reset the minCmd detection + minBits[0] = 0; + minBits[1] = 0; + minBitCounter = 0; + } + } + + //if a valid cmd has been detected: process it & reply + if (cmd != 0 ){//|| periodcounter%62500 == 0){ + uint32_t bits[5]; + uint8_t size = 0; + //select the cmd bit source (max or min detection) + if (cmd == 1){ + bits[0] = maxBits[0]; + bits[1] = maxBits[1]; + size = maxBitCounter; + + maxBits[0] = 0; + maxBits[1] = 0; + maxBitCounter = 0; + } else {//if (cmd ==-1){ + bits[0] = minBits[0]; + bits[1] = minBits[1]; + size = minBitCounter; + + minBits[0] = 0; + minBits[1] = 0; + minBitCounter = 0; + } + cmd = 0; + + //TODO: signal view that cmd has been found? (eg start colored LED sequence + + dmaLen = 0; + if (size == 5 && (bits[0]>>(32-5)) == 0b11001){ //SET_CCNEW + //reset selected state + tagState = HitagStateIdle; + + //set tag in advanced mode + tagMode = HitagModeAdvanced; + + //prepare emulate DMA buffer with advanced SN reply (page 0) and switch Timer from carrier_in capture to carrier_pull driver + dataReply->replyvalue[0] = 0b00000111; + dataReply->replytype[0] = HITAG_STARTBIT3; + dataReply->replyvalue[1] = worker->tag->pageData[0]; + dataReply->replytype[1] = HITAG_BYTE; + dataReply->replyvalue[2] = worker->tag->pageData[1]; + dataReply->replytype[2] = HITAG_BYTE; + dataReply->replyvalue[3] = worker->tag->pageData[2]; + dataReply->replytype[3] = HITAG_BYTE; + dataReply->replyvalue[4] = worker->tag->pageData[3]; + dataReply->replytype[4] = HITAG_BYTE; + + dataReply->replylength=5; + + dmaLen = lfrfid_hitag_worker_yield_dma_buffer_AC(dataReply, dataDMA, worker->carrierPrescaler); + + } + else if (size == 5 && (bits[0]>>(32-5)) == 0b00110){ //SET_CC + //reset selected state + tagState = HitagStateIdle; + + //do not (re)set tag in basic mode, this only happens during power on reset + + //prepare emulate DMA buffer with basic SN reply (page 0) and switch Timer from carrier_in capture to carrier_pull driver + dataReply->replyvalue[0] = 0b00000001; + dataReply->replytype[0] = HITAG_STARTBIT; + dataReply->replyvalue[1] = worker->tag->pageData[0]; + dataReply->replytype[1] = HITAG_BYTE; + dataReply->replyvalue[2] = worker->tag->pageData[1]; + dataReply->replytype[2] = HITAG_BYTE; + dataReply->replyvalue[3] = worker->tag->pageData[2]; + dataReply->replytype[3] = HITAG_BYTE; + dataReply->replyvalue[4] = worker->tag->pageData[3]; + dataReply->replytype[4] = HITAG_BYTE; + + dataReply->replylength=5; + + dmaLen = lfrfid_hitag_worker_yield_dma_buffer_AC(dataReply, dataDMA, worker->carrierPrescaler); + } + else if (size == 45 && (bits[0]>>(32-5)) == 0b00000){ //SELECT + //check if SN is matching + uint32_t SN_cmd = bits[0]<<5 | bits[1]>>(32-5); + uint32_t SN_tag = worker->tag->pageData[0] << 24 | + worker->tag->pageData[1] << 16 | + worker->tag->pageData[2] << 8 | + worker->tag->pageData[3]; + if (SN_cmd == SN_tag){ + //reply with config (page 1 from tag memory) + tagState = HitagStateSelected; + + //prepare emulate DMA buffer with basic/advanced config page reply (page 1) and switch Timer from carrier_in capture to carrier_pull driver + if (tagMode == HitagModeBasic){ + dataReply->replyvalue[0] = 0b00000001; + dataReply->replytype[0] = HITAG_STARTBIT; + + dataReply->replyvalue[1] = worker->tag->pageData[4+0]; + dataReply->replytype[1] = HITAG_BYTE; + dataReply->replyvalue[2] = worker->tag->pageData[4+1]; + dataReply->replytype[2] = HITAG_BYTE; + dataReply->replyvalue[3] = worker->tag->pageData[4+2]; + dataReply->replytype[3] = HITAG_BYTE; + dataReply->replyvalue[4] = worker->tag->pageData[4+3]; + dataReply->replytype[4] = HITAG_BYTE; + + dataReply->replylength=5; + } else if (tagMode == HitagModeAdvanced){ + dataReply->replyvalue[0] = 0b00111111; + dataReply->replytype[0] = HITAG_STARTBIT6; + + dataReply->replyvalue[1] = worker->tag->pageData[4+0]; + dataReply->replytype[1] = HITAG_BYTE; + dataReply->replyvalue[2] = worker->tag->pageData[4+1]; + dataReply->replytype[2] = HITAG_BYTE; + dataReply->replyvalue[3] = worker->tag->pageData[4+2]; + dataReply->replytype[3] = HITAG_BYTE; + dataReply->replyvalue[4] = worker->tag->pageData[4+3]; + dataReply->replytype[4] = HITAG_BYTE; + + uint8_t crc = CRC_PRESET; + lfrfid_hitag_worker_calc_crc(&crc, dataReply->replyvalue[1], 8); + lfrfid_hitag_worker_calc_crc(&crc, dataReply->replyvalue[2], 8); + lfrfid_hitag_worker_calc_crc(&crc, dataReply->replyvalue[3], 8); + lfrfid_hitag_worker_calc_crc(&crc, dataReply->replyvalue[4], 8); + + dataReply->replyvalue[5] = crc; + dataReply->replytype[5] = HITAG_CRC; + + dataReply->replylength=6; + } + + dmaLen = lfrfid_hitag_worker_yield_dma_buffer_MC(dataReply, dataDMA, worker->carrierPrescaler); + } + } + else if (size == 20 && (bits[0]>>(31))){ //R/W + if (tagState == HitagStateSelected){ + //reply with requested pages in case of read public (ignore secret block/pages & ignore write cmd) + uint8_t rwCmd = bits[0]>>(32-4); + uint8_t addr = bits[0]>>20 & 0xff; + uint8_t pages = 0; + switch (rwCmd){ + case (0b1100): + //read public page + if (worker->tag->pageKnown[addr] && worker->tag->pagePublic[addr]){ + pages = 1; + } + break; + case (0b1101): + //read public block + pages = (64-addr)%4; + if (pages==0){ + pages=4; + } + for (uint8_t p=addr;ptag->pageKnown[p] || !worker->tag->pagePublic[p]){ + pages=0; + break; + } + } + break; + } + if (pages>0){ + //pages + for (uint8_t i=0;ireplyvalue[1+i*4+0] = worker->tag->pageData[(addr+i)*4+0]; + dataReply->replytype[1+i*4+0] = HITAG_BYTE; + dataReply->replyvalue[1+i*4+1] = worker->tag->pageData[(addr+i)*4+1]; + dataReply->replytype[1+i*4+1] = HITAG_BYTE; + dataReply->replyvalue[1+i*4+2] = worker->tag->pageData[(addr+i)*4+2]; + dataReply->replytype[1+i*4+2] = HITAG_BYTE; + dataReply->replyvalue[1+i*4+3] = worker->tag->pageData[(addr+i)*4+3]; + dataReply->replytype[1+i*4+3] = HITAG_BYTE; + + } + //startsequence & CRC + if (tagMode == HitagModeBasic){ + dataReply->replyvalue[0] = 0b00000001; + dataReply->replytype[0] = HITAG_STARTBIT; + + dataReply->replylength=1+pages*4; + } else if (tagMode == HitagModeAdvanced){ + dataReply->replyvalue[0] = 0b00111111; + dataReply->replytype[0] = HITAG_STARTBIT6; + + uint8_t crc = CRC_PRESET; + + for (uint8_t i=0;ireplyvalue[1+i*4+0], 8); + lfrfid_hitag_worker_calc_crc(&crc, dataReply->replyvalue[1+i*4+1], 8); + lfrfid_hitag_worker_calc_crc(&crc, dataReply->replyvalue[1+i*4+2], 8); + lfrfid_hitag_worker_calc_crc(&crc, dataReply->replyvalue[1+i*4+3], 8); + } + + dataReply->replyvalue[1+pages*4] = crc; + dataReply->replytype[1+pages*4] = HITAG_CRC; + + dataReply->replylength=2+pages*4; + } + + dmaLen = lfrfid_hitag_worker_yield_dma_buffer_MC(dataReply, dataDMA, worker->carrierPrescaler); + } + } + } + else if (size == 20 && (bits[0]>>(32-4)) == 0b0111){ //HALT + //TODO validate if dummy address is a valid one (one of the default public pages (32-63)) + + //acknowledge & go silent untill next reset + if (tagState == HitagStateSelected){ + tagState = HitagStateQuiet; + + if (tagMode == HitagModeBasic){ + dataReply->replyvalue[0] = 0b00000101; + dataReply->replytype[0] = HITAG_ACK; + } else if (tagMode == HitagModeAdvanced){ + dataReply->replyvalue[0] = 0b11111101; + dataReply->replytype[0] = HITAG_ACK_ADV; + } + + dataReply->replylength=1; + + dmaLen = lfrfid_hitag_worker_yield_dma_buffer_MC(dataReply, dataDMA, worker->carrierPrescaler); + } + } + + if (dmaLen > 0){ //switch to reply mode + //switch carrier mode to ETR required for emulation + lfrfid_hitag_worker_carrier_in_ETR_mode(capData, prescaler); + + //respect hitag WAIT1 time (& make sure to have sufficient room in the capdata buffer to store all capdata while waiting for this + uint32_t wait1_periods = 180/prescaler; //originally 204 + while (capData->capCounter-cmdIndex < wait1_periods){ + furi_delay_us(40); //wait 5 more periods (datasheet says 204-213 periods, so waiting for 5 periods should land somewhere in this range) + } + + //just start the DMA, stopping is handled in DMA transfer complete interrupt + lfrfid_hitag_worker_pull_out_dma_start((size_t) dmaLen); + } + } + } + } + } + } + + //reset buffer + buffer_reset(buffer); + } + + if(buffer_stream_get_overrun_count(capData->stream) > 0) { + FURI_LOG_E(TAG, "Read overrun, recovering"); + buffer_stream_reset(capData->stream); + } + + uint32_t flags = furi_event_flag_get(worker->events); + if(FURI_BIT(flags, LFRFIDHitagWorkerSignalStop)) { + break; + } + } + + //stop carrier capture in + lfrfid_hitag_worker_carrier_in_stop(); + + free(worker->tag); + free(dataDMA); + free(dataReply); + varint_pair_free(capData->pair); + buffer_stream_free(capData->stream); + free(capData); + + //clear the stop flag on workder so that i can use the worker to launch a new read routine without having to restart the app + furi_event_flag_clear(worker->events, 1 << LFRFIDHitagWorkerSignalStop); + + return 0; +} + +static int32_t lfrfid_hitag_worker_read_thread(void* thread_context) { + LFRFIDHitagWorker* worker = (LFRFIDHitagWorker*)thread_context; + + LfRfidHitagProtocol currentProtocol = HitagProtocolAntiCollision; + LfRfidHitagCaptureData* capData = malloc(sizeof(LfRfidHitagCaptureData)); + capData->stream = buffer_stream_alloc(READ_BUFFER_SIZE, READ_BUFFER_COUNT); + capData->pair = varint_pair_alloc(); + + LfRfidHitagCMDData* dataCMD = malloc(sizeof(LfRfidHitagCMDData)); + dataCMD->CMDlength = 0; + dataCMD->CMDposition = 0; + dataCMD->CMDsubposition = 0; + dataCMD->CMDactive = false; + LfRfidHitagCMDData* backupCMD = malloc(sizeof(LfRfidHitagCMDData)); + TimerDMAData* dataDMA = malloc(sizeof(TimerDMAData)); + + //initialize a tag datastructure + worker->tag = malloc(sizeof(LFRFIDHitag)); + lfrfid_hitag_worker_initialize_tag_data(worker->tag); + uint32_t memBlock[HITAG_BLOCKPAGES] = {0}; + uint8_t bits = 0; + uint8_t expectedBits = 33; + uint8_t Scount = 0; + bool finished = false; + uint8_t readings = 0; + uint8_t currPage = 0; + bool readSucces = false; + + // setup carrier timer to generate ASK field + furi_hal_rfid_pins_read(); //--> ibutton low, pull low, carrier_out to TIM1 CH1N; carrier_pull low; data_in analog + + //and setup TIM1 CH1N with DMA (similar as is done with TIM2 in emulate mode + //fill the DMA arr & ccr buffers + + /*start with looping SETCC command*/ + uint32_t value[] = {128, 0b00110, 1, 213, (uint32_t)(1.05*33*HITAG_BITPERIODS_AC)}; //always take 5% reading time buffer + uint8_t type[] = {HITAG_ON, HITAG_SET, HITAG_STOP, HITAG_ON, HITAG_ON}; + for (size_t i = 0;i<5;i++){ + backupCMD->CMDvalue[i]=value[i]; + backupCMD->CMDtype[i]=type[i]; + } + backupCMD->CMDlength=5; + backupCMD->CMDposition=0; + backupCMD->CMDsubposition=0; + backupCMD->CMDloop = true; //not really required since by definition the backup cmd is looping already in yield code + backupCMD->CMDactive = true; + lfrfid_hitag_worker_yield_dma_buffer_BPLM(dataCMD, backupCMD, dataDMA, 0, DMA_BUFFER_SIZE/2); + lfrfid_hitag_worker_yield_dma_buffer_BPLM(dataCMD, backupCMD, dataDMA, DMA_BUFFER_SIZE/2, DMA_BUFFER_SIZE/2); + + //my own version of the TIMER with DMA function, since this was hardcoded using TIM2 CH3 and I need TIM1 CH1N + lfrfid_hitag_worker_carrier_out_start( + dataDMA->timer_buffer_arr, + dataDMA->timer_buffer_ccr, + DMA_BUFFER_SIZE, + worker); + + // start capture + furi_hal_rfid_tim_read_capture_start(lfrfid_hitag_worker_capture_in_cc_isr, capData); + + while(1) { + //check if there's data in raw capture buffer (this also serves a delay in case there's nothing in the buffer, not to overload mcu) + //I need to shorten the delay time, since I'm also using this routine to process DMA half transfer & transfer complete signals, which occurs sooner than 100ms? + //minimal time interfal for HT & TC is DMA_BUFFER_SIZE/2*8us (normal 125kHz operation): 4000/2*8=16 milliseconds, if I do timeout with 8 ms, then i have minimum 8ms remaining to process the DMA HT & TC signals + Buffer* buffer = buffer_stream_receive(capData->stream, 1); + + //process date if available + if(buffer != NULL) { + size_t buffsize = buffer_get_size(buffer); + uint8_t* buffdata = buffer_get_data(buffer); + + //if scanning for tagdata, then also parse buffer data + size_t index = 0; + while(expectedBits > 0 && index < buffsize) { + uint32_t duration; + uint32_t pulse; + size_t tmp_size; + + if(!varint_pair_unpack(&buffdata[index], buffsize - index, &pulse, &duration, &tmp_size)) { + FURI_LOG_E(TAG, "can't unpack varint pair"); + break; + } else { + index += tmp_size; + + //feed pulse duration to decoder + lfrfid_hitag_worker_decoder_feed(pulse, duration, memBlock, &bits, &Scount, &finished, currentProtocol); + + //see if i have a valid bit sequence + if (finished ){ + //bitsequence ready for falidation + if (bits == expectedBits){//eligable bitsequence detected, accept it only after 3 consequtive readings + //reset the backupCMD count to ensure some pause between bitsequence detection & the CMD injection + //rest is only required when scanning for the SN actually, since otherwise backup count is reset anyhow after switching from real to backup command + backupCMD->CMDcount = 0; + + if (readings == 0){ + for (uint8_t p=0;p<(expectedBits-1)/32;p++){ + worker->tag->pageData[(currPage+p)*4+0] = memBlock[p]>>24 & 0xff; + worker->tag->pageData[(currPage+p)*4+1] = memBlock[p]>>16 & 0xff; + worker->tag->pageData[(currPage+p)*4+2] = memBlock[p]>>8 & 0xff; + worker->tag->pageData[(currPage+p)*4+3] = memBlock[p] & 0xff; + worker->tag->pageKnown[currPage+p] = 0; + } + readings=1; + } else if (readings < 3){ + bool match = true; + for (uint8_t p=0;p<(expectedBits-1)/32;p++){ + if (worker->tag->pageData[(currPage+p)*4+0] != (memBlock[p]>>24 & 0xff) || + worker->tag->pageData[(currPage+p)*4+1] != (memBlock[p]>>16 & 0xff) || + worker->tag->pageData[(currPage+p)*4+2] != (memBlock[p]>>8 & 0xff) || + worker->tag->pageData[(currPage+p)*4+3] != (memBlock[p] & 0xff)){ + match = false; + break; + } + } + if(match){ + readings++; + if (readings==3){ + //succesful page(s) reading + readSucces = true; + + for (uint8_t p=0;p<(expectedBits-1)/32;p++){ + worker->tag->pageKnown[currPage+p] = 1; + } + + readings = 0; + expectedBits = 0; //to prevent further updates to tag data + } + } else { + for (uint8_t p=0;p<(expectedBits-1)/32;p++){ + worker->tag->pageData[(currPage+p)*4+0] = memBlock[p]>>24 & 0xff; + worker->tag->pageData[(currPage+p)*4+1] = memBlock[p]>>16 & 0xff; + worker->tag->pageData[(currPage+p)*4+2] = memBlock[p]>>8 & 0xff; + worker->tag->pageData[(currPage+p)*4+3] = memBlock[p] & 0xff; + worker->tag->pageKnown[currPage+p] = 0; + } + readings=1; + } + } + } + //reset tempdata for next bitsequence readout + memset(memBlock, 0, HITAG_BLOCKPAGES*sizeof(memBlock[0])); + bits = 0; + finished = false; + } + } + } + + //reset buffer + buffer_reset(buffer); + } + + + //if bitsequence found & after pause, prep & launch the command for reading out next bit of data + if (readSucces && backupCMD->CMDcount>=7){ + readSucces=false; + backupCMD->CMDcount=0;//required since otherwise the 'relaunch' of cmd is triggered straightaway + + //update protocolData + size_t data_size = protocol_dict_get_data_size(worker->dict, LFRFIDProtocolHitag1); + lfrfid_hitag_worker_combine_tag_data(worker->tag); + protocol_dict_set_data(worker->dict, LFRFIDProtocolHitag1, worker->tag->tagData, data_size); + + if (currPage==0){ //read config page next via SELECT cmd + FURI_LOG_D(TAG, "Hitag1, [%02X %02X %02X %02X] found", worker->tag->pageData[0], worker->tag->pageData[1], worker->tag->pageData[2], worker->tag->pageData[3]); + DOLPHIN_DEED(DolphinDeedRfidReadSuccess); + worker->workerstatus = LFRFIDHitagStatusDetected; + + currentProtocol = HitagProtocolManchesterCoding; + expectedBits=33; + currPage=1; + } + else if (currPage == 1){ //process config page and start reading public blocks + lfrfid_hitag_worker_process_config_page(worker->tag); + FURI_LOG_D(TAG, "Hitag1, [%02X %02X %02X %02X] config page read", worker->tag->pageData[0], worker->tag->pageData[1], worker->tag->pageData[2], worker->tag->pageData[3]); + //find next public readible block + for (currPage=4;currPage<=HITAG_PAGES-4;currPage+=4){ + if (worker->tag->pagePublic[currPage] && (worker->tag->pageRW[currPage] & READ) ){ //public readible page/block found + expectedBits=129; + break; + } else { + FURI_LOG_D(TAG, "Hitag1, [%02X %02X %02X %02X] block %u not public/readible", worker->tag->pageData[0], worker->tag->pageData[1], worker->tag->pageData[2], worker->tag->pageData[3], currPage/4); + } + } + } + else if (currPagetag->pageData[0], worker->tag->pageData[1], worker->tag->pageData[2], worker->tag->pageData[3], currPage/4); + for (currPage+=4;currPage<=HITAG_PAGES-4;currPage+=4){ + if (worker->tag->pagePublic[currPage] && (worker->tag->pageRW[currPage] & READ) ){ //public readible page/block found + expectedBits=129; + break; + } else { + FURI_LOG_D(TAG, "Hitag1, [%02X %02X %02X %02X] block %u not public/readible", worker->tag->pageData[0], worker->tag->pageData[1], worker->tag->pageData[2], worker->tag->pageData[3], currPage/4); + } + } + } + else if (currPage==HITAG_PAGES-4){ //all public readible pages read: signal view & break out of while loop (stops the worker) + FURI_LOG_D(TAG, "Hitag1, [%02X %02X %02X %02X] block %u read", worker->tag->pageData[0], worker->tag->pageData[1], worker->tag->pageData[2], worker->tag->pageData[3], currPage/4); + + worker->workerstatus = LFRFIDHitagStatusRead; + break; + } + + if (expectedBits>0){ //inject CMD + //prep SELECT cmd bytes + uint8_t SN0 = worker->tag->pageData[0]; + uint8_t SN1 = worker->tag->pageData[1]; + uint8_t SN2 = worker->tag->pageData[2]; + uint8_t SN3 = worker->tag->pageData[3]; + uint8_t crc = CRC_PRESET; + lfrfid_hitag_worker_calc_crc(&crc, (0b00000<<(8-5)), 5); + lfrfid_hitag_worker_calc_crc(&crc, SN0, 8); + lfrfid_hitag_worker_calc_crc(&crc, SN1, 8); + lfrfid_hitag_worker_calc_crc(&crc, SN2, 8); + lfrfid_hitag_worker_calc_crc(&crc, SN3, 8); + + //prep SELECT cmd array + uint32_t value[] = {128, 0b00000, SN0, SN1, SN2, SN3, crc, 1, 213, (uint32_t)(1.05*33*HITAG_BITPERIODS_MC)}; //always take 5% reading time buffer + uint8_t type[] = {HITAG_ON, HITAG_SELECT, HITAG_BYTE, HITAG_BYTE, HITAG_BYTE, HITAG_BYTE, HITAG_CRC, HITAG_STOP, HITAG_ON, HITAG_ON}; + uint16_t len = 10; + uint16_t cnt = 1; + for (size_t i = 0;iCMDvalue[i]=value[i%len]; + dataCMD->CMDtype[i]=type[i%len]; + } + dataCMD->CMDlength=cnt*len; + dataCMD->CMDposition=0; + dataCMD->CMDsubposition=0; + dataCMD->CMDloop = false; + + if (currPage < 4*4){ //add READPAGE cmd to array + crc = CRC_PRESET; + lfrfid_hitag_worker_calc_crc(&crc, (0b1100<<(8-4)), 4); + lfrfid_hitag_worker_calc_crc(&crc, currPage, 8); + uint32_t value2[] = {128, 0b1100, currPage, crc, 1, 213, (uint32_t)(1.05*33*HITAG_BITPERIODS_MC)}; //always take 5% reading time buffer + uint8_t type2[] = {HITAG_ON, HITAG_CMD, HITAG_BYTE, HITAG_CRC, HITAG_STOP, HITAG_ON, HITAG_ON}; + uint16_t len2 = 7; + uint16_t cnt2 = 5; + for (size_t i = 0;iCMDvalue[cnt*len+i]=value2[i%len2]; + dataCMD->CMDtype[cnt*len+i]=type2[i%len2]; + } + dataCMD->CMDlength+=cnt2*len2; + } + else { //add READBLOCK cmd to array + crc = CRC_PRESET; + lfrfid_hitag_worker_calc_crc(&crc, (0b1101<<(8-4)), 4); + lfrfid_hitag_worker_calc_crc(&crc, currPage, 8); + uint32_t value2[] = {128, 0b1101, currPage, crc, 1, 213, (uint32_t)(1.05*129*HITAG_BITPERIODS_MC)}; //always take 5% reading time buffer + uint8_t type2[] = {HITAG_ON, HITAG_CMD, HITAG_BYTE, HITAG_CRC, HITAG_STOP, HITAG_ON, HITAG_ON}; + uint16_t len2 = 7; + uint16_t cnt2 = 5; + for (size_t i = 0;iCMDvalue[cnt*len+i]=value2[i%len2]; + dataCMD->CMDtype[cnt*len+i]=type2[i%len2]; + } + dataCMD->CMDlength+=cnt2*len2; + } + } + } + + //while not having 3 consequtive reads, relaunch the (already prepared) command + if (!readSucces && backupCMD->CMDcount>=7 && currPage > 0 && expectedBits > 0){ + backupCMD->CMDcount=0;//required since in next loop there might not be a HT/TC trigger to trigger the yield ftion where cmd count is updated/reset + dataCMD->CMDposition=0; + dataCMD->CMDsubposition=0; + } + + if(buffer_stream_get_overrun_count(capData->stream) > 0 ) { + FURI_LOG_E(TAG, "Read overrun, recovering"); + buffer_stream_reset(capData->stream); + } + + //check for stop events + uint32_t flags = furi_event_flag_get(worker->events); + if(FURI_BIT(flags, LFRFIDHitagWorkerSignalStop)) { + break; + } + + //check for DMA HT/TC signals to repopulate the DMA buffer + if (worker->DMAeventCount>1){ //more than 1 unprocessed DMA event (aka DMA buffer is not timely refilled) + FURI_LOG_E(TAG, "DMA refilling can't keep up %u",worker->DMAeventCount); + } + if(FURI_BIT(flags, LFRFIDHitagWorkerSignalHalfTransfer)) { + //refill first half of dma buffer + lfrfid_hitag_worker_yield_dma_buffer_BPLM(dataCMD, backupCMD, dataDMA, 0, DMA_BUFFER_SIZE/2); + } + if(FURI_BIT(flags, LFRFIDHitagWorkerSignalTransferComplete)) { + //refill second half + lfrfid_hitag_worker_yield_dma_buffer_BPLM(dataCMD, backupCMD, dataDMA, DMA_BUFFER_SIZE/2, DMA_BUFFER_SIZE/2); + } + worker->DMAeventCount=0; + //clear all flags (i have processed all known/expected events) + furi_event_flag_clear(worker->events, flags); + + } + + //stop timers + furi_hal_rfid_tim_read_capture_stop(); + lfrfid_hitag_worker_carrier_out_stop(); + + + free(dataCMD); + free(backupCMD); + free(dataDMA); + varint_pair_free(capData->pair); + buffer_stream_free(capData->stream); + free(capData); + + //is this a routine to allow the view that started the thread to signal the stop, which will properly merge the threads & de-init the worker? + if(currPage==HITAG_PAGES-4) { + const uint32_t available_flags = (1 << LFRFIDHitagWorkerSignalStop); + while(true) { + uint32_t flags = furi_event_flag_wait( + worker->events, available_flags, FuriFlagWaitAny, FuriWaitForever); + + if(FURI_BIT(flags, LFRFIDHitagWorkerSignalStop)) { + break; + } + } + } + + free(worker->tag); + + //clear the stop flag on workder so that i can use the worker to launch a new read routine without having to restart the app + furi_event_flag_clear(worker->events, 1 << LFRFIDHitagWorkerSignalStop); + + return 0; +} diff --git a/lib/lfrfid/lfrfid_hitag_worker.h b/lib/lfrfid/lfrfid_hitag_worker.h new file mode 100644 index 00000000000..a9da388358b --- /dev/null +++ b/lib/lfrfid/lfrfid_hitag_worker.h @@ -0,0 +1,92 @@ +#pragma once +#include +#include +#include +#include +#include +#include +//#include +#include "protocols/lfrfid_protocols.h" + +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include + +#include + +#define HITAG_BLOCKS 16 +#define HITAG_BLOCKPAGES 4 +#define HITAG_PAGES 64 +#define HITAG_PAGEBYTES 4 + +#ifdef __cplusplus +extern "C" { +#endif + + +typedef enum { + LFRFIDHitagWorkerSettingRead, + LFRFIDHitagWorkerSettingEmulate, +} LFRFIDHitagWorkerSetting; + +typedef enum { + LFRFIDHitagStatusScanning, + LFRFIDHitagStatusDetected, + LFRFIDHitagStatusRead, +} LFRFIDHitagStatus; + +typedef struct LFRFIDHitagWorker LFRFIDHitagWorker; + +/** + * @brief Get the tag read status + * + * @return tag read status + */ +LFRFIDHitagStatus lfrfid_hitag_worker_get_status(LFRFIDHitagWorker* worker); + + +/** + * @brief Allocate a new LFRFIDHitagWorker instance + * + * @return LFRFIDHitagWorker* + */ +LFRFIDHitagWorker* lfrfid_hitag_worker_alloc(ProtocolDict* dict); + + +/** + * @brief Free a LFRFIDHitagWorker instance + * + * @param worker LFRFIDHitagWorker instance + */ +void lfrfid_hitag_worker_free(LFRFIDHitagWorker* worker); + +/** + * @brief Start hitag worker from own generated field + * + * @param worker LFRFIDHitagWorker instance + * @param setting read/emulate + */ +void lfrfid_hitag_worker_start( + LFRFIDHitagWorker* worker, + LFRFIDHitagWorkerSetting setting); + +/** + * @brief Stop worker + * + * @param worker + */ +void lfrfid_hitag_worker_stop(LFRFIDHitagWorker* worker); + + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/lib/lfrfid/lfrfid_worker.c b/lib/lfrfid/lfrfid_worker.c index 1e491c6b787..669fe4fbfad 100644 --- a/lib/lfrfid/lfrfid_worker.c +++ b/lib/lfrfid/lfrfid_worker.c @@ -11,7 +11,7 @@ typedef enum { LFRFIDEventEmulate = (1 << 4), LFRFIDEventReadRaw = (1 << 5), LFRFIDEventEmulateRaw = (1 << 6), - LFRFIDEventAll = + LFRFIDEventAll = (LFRFIDEventStopThread | LFRFIDEventStopMode | LFRFIDEventRead | LFRFIDEventWrite | LFRFIDEventEmulate | LFRFIDEventReadRaw | LFRFIDEventEmulateRaw), } LFRFIDEventType; @@ -149,7 +149,7 @@ static int32_t lfrfid_worker_thread(void* thread_context) { if(flags & LFRFIDEventEmulate) worker->mode_index = LFRFIDWorkerEmulate; if(flags & LFRFIDEventReadRaw) worker->mode_index = LFRFIDWorkerReadRaw; if(flags & LFRFIDEventEmulateRaw) worker->mode_index = LFRFIDWorkerEmulateRaw; - + // do mode, if it exists if(lfrfid_worker_modes[worker->mode_index].process) { lfrfid_worker_modes[worker->mode_index].process(worker); diff --git a/lib/lfrfid/lfrfid_worker.h b/lib/lfrfid/lfrfid_worker.h index def9f89a474..f292dc28e38 100644 --- a/lib/lfrfid/lfrfid_worker.h +++ b/lib/lfrfid/lfrfid_worker.h @@ -23,6 +23,7 @@ typedef enum { LFRFIDWorkerReadTypeAuto, LFRFIDWorkerReadTypeASKOnly, LFRFIDWorkerReadTypePSKOnly, + LFRFIDWorkerReadTypeRTFOnly, } LFRFIDWorkerReadType; typedef enum { @@ -32,6 +33,8 @@ typedef enum { LFRFIDWorkerReadSenseCardEnd, LFRFIDWorkerReadStartASK, LFRFIDWorkerReadStartPSK, + LFRFIDWorkerReadStartRTF, + LFRFIDWorkerReadSenseHitag, //TODO combine with sense carstart? LFRFIDWorkerReadDone, } LFRFIDWorkerReadResult; @@ -45,8 +48,7 @@ typedef enum { LFRFIDWorkerEmulateRawOverrun, } LFRFIDWorkerEmulateRawResult; -typedef void ( - *LFRFIDWorkerReadCallback)(LFRFIDWorkerReadResult result, ProtocolId protocol, void* context); +typedef void (*LFRFIDWorkerReadCallback)(LFRFIDWorkerReadResult result, ProtocolId protocol, void* context); typedef void (*LFRFIDWorkerWriteCallback)(LFRFIDWorkerWriteResult result, void* context); typedef void (*LFRFIDWorkerReadRawCallback)(LFRFIDWorkerReadRawResult result, void* context); diff --git a/lib/lfrfid/lfrfid_worker_i.h b/lib/lfrfid/lfrfid_worker_i.h index 33c0bff0851..d46ef2bbae1 100644 --- a/lib/lfrfid/lfrfid_worker_i.h +++ b/lib/lfrfid/lfrfid_worker_i.h @@ -8,6 +8,7 @@ #include #include "lfrfid_worker.h" #include "lfrfid_raw_worker.h" +#include "lfrfid_hitag_worker.h" #include "protocols/lfrfid_protocols.h" #ifdef __cplusplus diff --git a/lib/lfrfid/lfrfid_worker_modes.c b/lib/lfrfid/lfrfid_worker_modes.c index 9b6f16eb14c..66aec3052f8 100644 --- a/lib/lfrfid/lfrfid_worker_modes.c +++ b/lib/lfrfid/lfrfid_worker_modes.c @@ -94,7 +94,7 @@ typedef enum { LFRFIDWorkerReadTimeout, } LFRFIDWorkerReadState; -static LFRFIDWorkerReadState lfrfid_worker_read_internal( +static LFRFIDWorkerReadState lfrfid_worker_read_ttf( //tag talks first LFRFIDWorker* worker, LFRFIDFeature feature, uint32_t timeout, @@ -334,6 +334,61 @@ static LFRFIDWorkerReadState lfrfid_worker_read_internal( return state; } +static LFRFIDWorkerReadState lfrfid_worker_read_rtf( //reader talks first + LFRFIDWorker* worker, + LFRFIDFeature feature, + uint32_t timeout, + ProtocolId* result_protocol) { + UNUSED(feature); + LFRFIDWorkerReadState state = LFRFIDWorkerReadTimeout; + + FURI_LOG_D(TAG, "Start RTF"); + if(worker->read_cb) { + worker->read_cb(LFRFIDWorkerReadStartRTF, PROTOCOL_NO, worker->cb_ctx); + } + LFRFIDHitagWorker* hitag_worker = lfrfid_hitag_worker_alloc(worker->protocols); + + lfrfid_hitag_worker_start(hitag_worker, LFRFIDHitagWorkerSettingRead); + + FURI_LOG_D(TAG, "Read started"); + + //scan for hitag for a while and stay in hitag mode if card was detected + uint8_t delays = 0; + uint8_t delay_ms = 100; + bool notified = false; + while (1){ + furi_delay_ms(delay_ms); + + if (lfrfid_worker_check_for_stop(worker)){ + state = LFRFIDWorkerReadExit; + *result_protocol = PROTOCOL_NO; + break; + } + + if (lfrfid_hitag_worker_get_status(hitag_worker) == LFRFIDHitagStatusDetected){ + *result_protocol = LFRFIDProtocolHitag1; //TODO get protocol ID from hitag_worker when expanding the worker to include other hitag protocols + if (!notified && worker->read_cb){ + worker->read_cb(LFRFIDWorkerReadSenseHitag, *result_protocol, worker->cb_ctx); + notified = true; + } + } else if (lfrfid_hitag_worker_get_status(hitag_worker) == LFRFIDHitagStatusRead){ + state = LFRFIDWorkerReadOK; + *result_protocol = LFRFIDProtocolHitag1; //TODO get protocol ID from hitag_worker when expanding the worker to include other hitag protocols + break; + } else if (++delays >= timeout/delay_ms){ + state = LFRFIDWorkerReadTimeout; + break; + } + } + + lfrfid_hitag_worker_stop(hitag_worker); + lfrfid_hitag_worker_free(hitag_worker); + + FURI_LOG_D(TAG, "Read stopped"); + + return state; +} + static void lfrfid_worker_mode_read_process(LFRFIDWorker* worker) { ProtocolId read_result = PROTOCOL_NO; LFRFIDWorkerReadState state; @@ -341,16 +396,24 @@ static void lfrfid_worker_mode_read_process(LFRFIDWorker* worker) { if(worker->read_type == LFRFIDWorkerReadTypePSKOnly) { feature = LFRFIDFeaturePSK; - } else { + } else if (worker->read_type == LFRFIDWorkerReadTypeRTFOnly){ + feature = LFRFIDFeatureRTF; + } else { feature = LFRFIDFeatureASK; - } + } if(worker->read_type == LFRFIDWorkerReadTypeAuto) { while(1) { // read for a while - state = lfrfid_worker_read_internal( - worker, feature, LFRFID_WORKER_READ_SWITCH_TIME_MS, &read_result); - + + if (feature == LFRFIDFeatureASK || feature == LFRFIDFeaturePSK){ + state = lfrfid_worker_read_ttf( + worker, feature, LFRFID_WORKER_READ_SWITCH_TIME_MS, &read_result); + } else if (feature == LFRFIDFeatureRTF){ + state = lfrfid_worker_read_rtf( + worker, feature, LFRFID_WORKER_READ_SWITCH_TIME_MS, &read_result); + } + if(state == LFRFIDWorkerReadOK || state == LFRFIDWorkerReadExit) { break; } @@ -358,8 +421,10 @@ static void lfrfid_worker_mode_read_process(LFRFIDWorker* worker) { // switch to next feature if(feature == LFRFIDFeatureASK) { feature = LFRFIDFeaturePSK; - } else { - feature = LFRFIDFeatureASK; + } else if (feature == LFRFIDFeaturePSK){ + feature = LFRFIDFeatureRTF; + } else if (feature == LFRFIDFeatureRTF){ + feature = LFRFIDFeatureASK; } lfrfid_worker_delay(worker, LFRFID_WORKER_READ_DROP_TIME_MS); @@ -367,11 +432,15 @@ static void lfrfid_worker_mode_read_process(LFRFIDWorker* worker) { } else { while(1) { if(worker->read_type == LFRFIDWorkerReadTypeASKOnly) { - state = lfrfid_worker_read_internal(worker, feature, UINT32_MAX, &read_result); - } else { - state = lfrfid_worker_read_internal( + state = lfrfid_worker_read_ttf( + worker, feature, UINT32_MAX, &read_result); + } else if (worker->read_type == LFRFIDWorkerReadTypePSKOnly) { + state = lfrfid_worker_read_ttf( worker, feature, LFRFID_WORKER_READ_SWITCH_TIME_MS, &read_result); - } + } else { + state = lfrfid_worker_read_rtf( + worker, feature, UINT32_MAX, &read_result); + } if(state == LFRFIDWorkerReadOK || state == LFRFIDWorkerReadExit) { break; @@ -406,8 +475,8 @@ static void lfrfid_worker_emulate_dma_isr(bool half, void* context) { furi_stream_buffer_send(stream, &flag, sizeof(uint32_t), 0); } -static void lfrfid_worker_mode_emulate_process(LFRFIDWorker* worker) { - LFRFIDWorkerEmulateBuffer* buffer = malloc(sizeof(LFRFIDWorkerEmulateBuffer)); +static void lfrfid_worker_emulate_ttf(LFRFIDWorker* worker){ + LFRFIDWorkerEmulateBuffer* buffer = malloc(sizeof(LFRFIDWorkerEmulateBuffer)); FuriStreamBuffer* stream = furi_stream_buffer_alloc(sizeof(uint32_t), sizeof(uint32_t)); LFRFIDProtocol protocol = worker->protocol; PulseGlue* pulse_glue = pulse_glue_alloc(); @@ -495,6 +564,33 @@ static void lfrfid_worker_mode_emulate_process(LFRFIDWorker* worker) { pulse_glue_free(pulse_glue); } +static void lfrfid_worker_emulate_rtf(LFRFIDWorker* worker){ + LFRFIDHitagWorker* hitag_worker = lfrfid_hitag_worker_alloc(worker->protocols); //todo, pass protocols & protocol id when expanding the worker to include other hitag protocols + + lfrfid_hitag_worker_start(hitag_worker, LFRFIDHitagWorkerSettingEmulate); + uint8_t delay_ms = 100; + while (1){ + furi_delay_ms(delay_ms); + + if (lfrfid_worker_check_for_stop(worker)){ + break; + } + } + + lfrfid_hitag_worker_stop(hitag_worker); + lfrfid_hitag_worker_free(hitag_worker); +} + +static void lfrfid_worker_mode_emulate_process(LFRFIDWorker* worker) { + LFRFIDFeature feature = protocol_dict_get_features(worker->protocols, worker->protocol); + + if (feature == LFRFIDFeatureRTF){ + lfrfid_worker_emulate_rtf(worker); + } else { + lfrfid_worker_emulate_ttf(worker); + } +} + /**************************************************************************************************/ /********************************************* WRITE **********************************************/ /**************************************************************************************************/ @@ -521,7 +617,7 @@ static void lfrfid_worker_mode_write_process(LFRFIDWorker* worker) { t5577_write(&request->t5577); ProtocolId read_result = PROTOCOL_NO; - LFRFIDWorkerReadState state = lfrfid_worker_read_internal( + LFRFIDWorkerReadState state = lfrfid_worker_read_ttf( worker, protocol_dict_get_features(worker->protocols, protocol), LFRFID_WORKER_WRITE_VERIFY_TIME_MS, @@ -624,6 +720,7 @@ static void lfrfid_worker_mode_emulate_raw_process(LFRFIDWorker* worker) { lfrfid_raw_worker_free(raw_worker); } + /**************************************************************************************************/ /******************************************** MODES ***********************************************/ /**************************************************************************************************/ diff --git a/lib/lfrfid/protocols/lfrfid_protocols.c b/lib/lfrfid/protocols/lfrfid_protocols.c index 2c1f0ad97cd..488b0b08842 100644 --- a/lib/lfrfid/protocols/lfrfid_protocols.c +++ b/lib/lfrfid/protocols/lfrfid_protocols.c @@ -16,6 +16,7 @@ #include "protocol_pac_stanley.h" #include "protocol_keri.h" #include "protocol_gallagher.h" +#include "protocol_hitag1.h" const ProtocolBase* lfrfid_protocols[] = { [LFRFIDProtocolEM4100] = &protocol_em4100, @@ -35,4 +36,5 @@ const ProtocolBase* lfrfid_protocols[] = { [LFRFIDProtocolPACStanley] = &protocol_pac_stanley, [LFRFIDProtocolKeri] = &protocol_keri, [LFRFIDProtocolGallagher] = &protocol_gallagher, + [LFRFIDProtocolHitag1] = &protocol_hitag1, }; \ No newline at end of file diff --git a/lib/lfrfid/protocols/lfrfid_protocols.h b/lib/lfrfid/protocols/lfrfid_protocols.h index 848f003a31e..6cba763a8da 100644 --- a/lib/lfrfid/protocols/lfrfid_protocols.h +++ b/lib/lfrfid/protocols/lfrfid_protocols.h @@ -5,6 +5,7 @@ typedef enum { LFRFIDFeatureASK = 1 << 0, /** ASK Demodulation */ LFRFIDFeaturePSK = 1 << 1, /** PSK Demodulation */ + LFRFIDFeatureRTF = 1 << 2, /** Reader Talks First: ASK Demodulation with 2 way communication */ } LFRFIDFeature; typedef enum { @@ -25,6 +26,7 @@ typedef enum { LFRFIDProtocolPACStanley, LFRFIDProtocolKeri, LFRFIDProtocolGallagher, + LFRFIDProtocolHitag1, LFRFIDProtocolMax, } LFRFIDProtocol; diff --git a/lib/lfrfid/protocols/protocol_hitag1.c b/lib/lfrfid/protocols/protocol_hitag1.c new file mode 100644 index 00000000000..de9782150b8 --- /dev/null +++ b/lib/lfrfid/protocols/protocol_hitag1.c @@ -0,0 +1,104 @@ +#include +#include +#include "lfrfid_protocols.h" + +#define HITAG1_PAGES 64 +#define HITAG1_DATA_SIZE HITAG1_PAGES*4 + HITAG1_PAGES + +typedef struct { + uint8_t tagData[HITAG1_DATA_SIZE]; +} ProtocolHitag1; + +ProtocolHitag1* protocol_hitag1_alloc(void) { + ProtocolHitag1* protocol = malloc(sizeof(ProtocolHitag1)); + + return protocol; +}; + +void protocol_hitag1_free(ProtocolHitag1* protocol) { + free(protocol); +}; + +uint8_t* protocol_hitag1_get_data(ProtocolHitag1* protocol) { + return protocol->tagData; +}; + +void protocol_hitag1_decoder_start(ProtocolHitag1* protocol) { + UNUSED(protocol); + // Not applicalble, encoding & decoding is handled in lfrfid_hitag_worker... +}; + +bool protocol_hitag1_decoder_feed(ProtocolHitag1* protocol, bool level, uint32_t duration) { + UNUSED(protocol); + UNUSED(level); + UNUSED(duration); + // Not applicalble, encoding & decoding is handled in lfrfid_hitag_worker... + + bool result = false; + return result; +}; + +bool protocol_hitag1_encoder_start(ProtocolHitag1* protocol) { + UNUSED(protocol); + // Not applicalble, encoding & decoding is handled in lfrfid_hitag_worker... + + return false; +}; + +LevelDuration protocol_hitag1_encoder_yield(ProtocolHitag1* protocol) { + UNUSED(protocol); + // Not applicalble, encoding & decoding is handled in lfrfid_hitag_worker... + + bool level = 0; + uint32_t duration = 0; + return level_duration_make(level, duration); +}; + +bool protocol_hitag1_write_data(ProtocolHitag1* protocol, void* data) { + UNUSED(protocol); + UNUSED(data); + + //this protocol cannot be simply written to card --> don't do anything, just return false + + return false; +}; + +void protocol_hitag1_render_data(ProtocolHitag1* protocol, FuriString* result) { + uint8_t pages = 0; + for (uint8_t p=0;ptagData[HITAG1_PAGES*4+p]; + } + furi_string_printf( + result, + "SN: %02X %02X %02X %02X\r\n" + "Pages read: %u / 64", + protocol->tagData[0], + protocol->tagData[1], + protocol->tagData[2], + protocol->tagData[3], + pages); +}; + +const ProtocolBase protocol_hitag1 = { + .name = "Hitag1", + .manufacturer = "Philips", + .data_size = HITAG1_DATA_SIZE, + .features = LFRFIDFeatureRTF, + .validate_count = 1, + .alloc = (ProtocolAlloc)protocol_hitag1_alloc, + .free = (ProtocolFree)protocol_hitag1_free, + .get_data = (ProtocolGetData)protocol_hitag1_get_data, + .decoder = + { + .start = (ProtocolDecoderStart)protocol_hitag1_decoder_start, + .feed = (ProtocolDecoderFeed)protocol_hitag1_decoder_feed, + }, + .encoder = + { + .start = (ProtocolEncoderStart)protocol_hitag1_encoder_start, + .yield = (ProtocolEncoderYield)protocol_hitag1_encoder_yield, + }, + .render_data = (ProtocolRenderData)protocol_hitag1_render_data, + .render_brief_data = (ProtocolRenderData)protocol_hitag1_render_data, + .write_data = (ProtocolWriteData)protocol_hitag1_write_data, +}; diff --git a/lib/lfrfid/protocols/protocol_hitag1.h b/lib/lfrfid/protocols/protocol_hitag1.h new file mode 100644 index 00000000000..517eeb5efe1 --- /dev/null +++ b/lib/lfrfid/protocols/protocol_hitag1.h @@ -0,0 +1,4 @@ +#pragma once +#include + +extern const ProtocolBase protocol_hitag1; \ No newline at end of file From 203f4ad533327f7b4e1c4abc8a5ef9cbb2e471b7 Mon Sep 17 00:00:00 2001 From: Daan De Witte Date: Wed, 10 May 2023 12:56:52 +0200 Subject: [PATCH 02/14] updated byte_input gui module to handle +256 databytes --- applications/services/gui/modules/byte_input.c | 14 +++++++------- applications/services/gui/modules/byte_input.h | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/applications/services/gui/modules/byte_input.c b/applications/services/gui/modules/byte_input.c index b2d21f7ae39..f1edf9de837 100644 --- a/applications/services/gui/modules/byte_input.c +++ b/applications/services/gui/modules/byte_input.c @@ -17,17 +17,17 @@ typedef struct { typedef struct { const char* header; uint8_t* bytes; - uint8_t bytes_count; + uint16_t bytes_count; ByteInputCallback input_callback; ByteChangedCallback changed_callback; void* callback_context; bool selected_high_nibble; - uint8_t selected_byte; + uint16_t selected_byte; int8_t selected_row; // row -1 - input, row 0 & 1 - keyboard uint8_t selected_column; - uint8_t first_visible_byte; + uint16_t first_visible_byte; } ByteInputModel; static const uint8_t keyboard_origin_x = 7; @@ -164,7 +164,7 @@ static void byte_input_draw_input(Canvas* canvas, ByteInputModel* model) { canvas_draw_icon(canvas, 2, 19, &I_ButtonLeftSmall_3x5); canvas_draw_icon(canvas, 123, 19, &I_ButtonRightSmall_3x5); - for(uint8_t i = model->first_visible_byte; + for(uint16_t i = model->first_visible_byte; i < model->first_visible_byte + MIN(model->bytes_count, max_drawable_bytes); i++) { uint8_t byte_position = i - model->first_visible_byte; @@ -253,7 +253,7 @@ static void byte_input_draw_input_selected(Canvas* canvas, ByteInputModel* model canvas_draw_icon(canvas, 2, 19, &I_ButtonLeftSmall_3x5); canvas_draw_icon(canvas, 122, 19, &I_ButtonRightSmall_3x5); - for(uint8_t i = model->first_visible_byte; + for(uint16_t i = model->first_visible_byte; i < model->first_visible_byte + MIN(model->bytes_count, max_drawable_bytes); i++) { uint8_t byte_position = i - model->first_visible_byte; @@ -305,7 +305,7 @@ static void byte_input_draw_input_selected(Canvas* canvas, ByteInputModel* model * @param value char value * @param high_nibble set high nibble */ -static void byte_input_set_nibble(uint8_t* data, uint8_t position, char value, bool high_nibble) { +static void byte_input_set_nibble(uint8_t* data, uint16_t position, char value, bool high_nibble) { switch(value) { case '0': case '1': @@ -750,7 +750,7 @@ void byte_input_set_result_callback( ByteChangedCallback changed_callback, void* callback_context, uint8_t* bytes, - uint8_t bytes_count) { + uint16_t bytes_count) { with_view_model( byte_input->view, ByteInputModel * model, diff --git a/applications/services/gui/modules/byte_input.h b/applications/services/gui/modules/byte_input.h index 42c4b5d65f9..b8a4d4455f1 100644 --- a/applications/services/gui/modules/byte_input.h +++ b/applications/services/gui/modules/byte_input.h @@ -55,7 +55,7 @@ void byte_input_set_result_callback( ByteChangedCallback changed_callback, void* callback_context, uint8_t* bytes, - uint8_t bytes_count); + uint16_t bytes_count); /** Set byte input header text * From a4f2ae9062fa195cd49e3ef8cd0381b86fb7de86 Mon Sep 17 00:00:00 2001 From: Daan De Witte Date: Wed, 10 May 2023 12:58:17 +0200 Subject: [PATCH 03/14] updated api symbols cf the gui module byte_input updates --- firmware/targets/f7/api_symbols.csv | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 1c8424fe8c9..2a0bf6a859a 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,24.0,, +Version,+,25.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -609,7 +609,7 @@ Function,+,byte_input_alloc,ByteInput*, Function,+,byte_input_free,void,ByteInput* Function,+,byte_input_get_view,View*,ByteInput* Function,+,byte_input_set_header_text,void,"ByteInput*, const char*" -Function,+,byte_input_set_result_callback,void,"ByteInput*, ByteInputCallback, ByteChangedCallback, void*, uint8_t*, uint8_t" +Function,+,byte_input_set_result_callback,void,"ByteInput*, ByteInputCallback, ByteChangedCallback, void*, uint8_t*, uint16_t" Function,-,bzero,void,"void*, size_t" Function,-,calloc,void*,"size_t, size_t" Function,+,canvas_clear,void,Canvas* From c9fe4a48660396e1180bf1ff1aac029e8d8231d8 Mon Sep 17 00:00:00 2001 From: hedger Date: Thu, 11 May 2023 05:03:48 +0400 Subject: [PATCH 04/14] hal: f18: Updated api_symbols.csv --- firmware/targets/f18/api_symbols.csv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware/targets/f18/api_symbols.csv b/firmware/targets/f18/api_symbols.csv index 828c771662c..ee1ae115423 100644 --- a/firmware/targets/f18/api_symbols.csv +++ b/firmware/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,26.2,, +Version,+,26.3,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, From d8704f69673dbfd40b3478e019b5f15c14a8efe6 Mon Sep 17 00:00:00 2001 From: Daan De Witte Date: Mon, 15 May 2023 22:11:10 +0200 Subject: [PATCH 05/14] minor memory correction in decoder_feed --- lib/lfrfid/lfrfid_hitag_worker.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/lfrfid/lfrfid_hitag_worker.c b/lib/lfrfid/lfrfid_hitag_worker.c index 6c6422856d1..702abfe3f41 100644 --- a/lib/lfrfid/lfrfid_hitag_worker.c +++ b/lib/lfrfid/lfrfid_hitag_worker.c @@ -974,7 +974,9 @@ static void lfrfid_hitag_worker_decoder_feed(uint32_t pulse, uint32_t duration, if (HITAG_DURATION_S*(1-HITAG_DURATION_ERROR_MARGIN)<=duration && duration <=HITAG_DURATION_S*(1+HITAG_DURATION_ERROR_MARGIN) ){ //if first small: add 1 to memBlock and increase small counter if (*Scount==0){ - memBlock[(*bits-1)/32] |= (bit >> (*bits-1)%32); + if (*bits>0){ + memBlock[(*bits-1)/32] |= (bit >> (*bits-1)%32); + } (*bits)++; (*Scount)++; } else { From 8baacc88c24a841f12f359a8acac2adc02d132e1 Mon Sep 17 00:00:00 2001 From: Daan De Witte Date: Wed, 17 May 2023 12:49:08 +0200 Subject: [PATCH 06/14] improved decoder feed with flexible startbits and bit sequence termination --- lib/lfrfid/lfrfid_hitag_worker.c | 346 +++++++++++++++++-------------- 1 file changed, 192 insertions(+), 154 deletions(-) diff --git a/lib/lfrfid/lfrfid_hitag_worker.c b/lib/lfrfid/lfrfid_hitag_worker.c index 702abfe3f41..2692cd14690 100644 --- a/lib/lfrfid/lfrfid_hitag_worker.c +++ b/lib/lfrfid/lfrfid_hitag_worker.c @@ -906,107 +906,146 @@ static uint16_t lfrfid_hitag_worker_yield_dma_buffer_AC(LfRfidHitagReplyData* da return dma_len; } -static void lfrfid_hitag_worker_decoder_feed(uint32_t pulse, uint32_t duration, uint32_t* memBlock, uint8_t* bits, uint8_t* Scount, bool* finished, LfRfidHitagProtocol protocol){ - UNUSED(pulse); +static void lfrfid_hitag_worker_decoder_feed(uint32_t pulse, uint32_t duration, uint32_t* memBlock, uint8_t* bits, bool* half, bool* finished, LfRfidHitagProtocol protocol, uint8_t startBits){ uint32_t bit = 1<<(32-1); + if (*bits==0){ + *half = false; + } if (protocol == HitagProtocolManchesterCoding){ - if (HITAG_DURATION_S*(1-HITAG_DURATION_ERROR_MARGIN)<=duration && duration <=HITAG_DURATION_S*(1+HITAG_DURATION_ERROR_MARGIN) ){ - if (*bits == 0){ - //first bit is always a 1, this is startbit, no need to store it in memBlock + if (HITAG_DURATION_S*(1-HITAG_DURATION_ERROR_MARGIN) <= duration && duration <= HITAG_DURATION_S*(1+HITAG_DURATION_ERROR_MARGIN) ){ + //short period (HL) detected: same bit as previous + if (*bits < startBits){ + //don't store startbits in memory, just increase bitcounter (*bits)++; - } else if (*bits == 1){ - //add 1 to the memBlock - memBlock[(*bits-1)/32] |= (bit >> (*bits-1)%32); //add 1 + } else if (*bits == startBits){ + //same as previous bit (last startbit it 1, so add 1 to the memBlock) + memBlock[(*bits-startBits)/32] |= (bit >> (*bits-startBits)%32); //add 1 (*bits)++; } else { //add same as previous bit - if (memBlock[(*bits-2)/32] & (bit >> (*bits-2)%32)){ - memBlock[(*bits-1)/32] |= (bit >> (*bits-1)%32); //add 1 + if (memBlock[(*bits-startBits-1)/32] & (bit >> (*bits-startBits-1)%32)){ + memBlock[(*bits-startBits)/32] |= (bit >> (*bits-startBits)%32); //add 1 (*bits)++; } else { - (*bits)++; + (*bits)++; //no mem action for 0, just increase bitcounter } } - } else if (HITAG_DURATION_M*(1-HITAG_DURATION_ERROR_MARGIN)<=duration && duration <=HITAG_DURATION_M*(1+HITAG_DURATION_ERROR_MARGIN) ){ - if (*bits == 0){ - //10 detected, but startbit is not to be added to memBlock, and 0 is already in bitarry: + } + else if (HITAG_DURATION_M*(1-HITAG_DURATION_ERROR_MARGIN) <= duration && duration <= HITAG_DURATION_M*(1+HITAG_DURATION_ERROR_MARGIN) ){ + //medium period (H|HL or HL|L) detected: half 0 + full 1 or full 1 + half 0 detected + if (*bits < startBits){ + //assuming all startbits are 1's, this can only happen on last startbit followed by 0 + //don't store startbits, no mem action for 0, just increase bitcounter twice (*bits)++; (*bits)++; - } else if (*bits == 1){ - //add 10 to the memBlock - memBlock[(*bits-1)/32] |= (bit >> (*bits-1)%32); //add 1 + *half = true; + } else if (*bits == startBits){ + //assuming all startbits were 1's, this is a data 10 detection + memBlock[(*bits-startBits)/32] |= (bit >> (*bits-startBits)%32); //add 1 (*bits)++; - (*bits)++;//add 0 is not necessary, since memBlock is initialized with all 0s, just increase counter + (*bits)++; //no mem action for 0, just increase bitcounter + *half = true; } else { - //if prev bit is 1 then add 10, else add 1 - if (memBlock[(*bits-2)/32] & (bit >> (*bits-2)%32)){ - memBlock[(*bits-1)/32] |= (bit >> (*bits-1)%32); //add 1 + //if we're in half of a bit, add 1, else add 10 and set half flag + if (*half){ + memBlock[(*bits-startBits)/32] |= (bit >> (*bits-startBits)%32); //add 1 (*bits)++; - (*bits)++;//add 0 is not necessary, since memBlock is initialized with all 0s, just increase counter + *half = false; } else { - memBlock[(*bits-1)/32] |= (bit >> (*bits-1)%32); //add 1 + memBlock[(*bits-startBits)/32] |= (bit >> (*bits-startBits)%32); //add 1 (*bits)++; + (*bits)++; //no mem action for 0, just increase bitcounter + *half = true; } } - } else if (HITAG_DURATION_L*(1-HITAG_DURATION_ERROR_MARGIN)<=duration && duration <=HITAG_DURATION_L*(1+HITAG_DURATION_ERROR_MARGIN) ){ - if (*bits==0){ - //error, first pulse-duration of a bit sequence can't be a large one - //no need to do anything if bits is 0 then memBlock is also 00000... + } + else if (HITAG_DURATION_L*(1-HITAG_DURATION_ERROR_MARGIN) <= duration && duration <= HITAG_DURATION_L*(1+HITAG_DURATION_ERROR_MARGIN) ){ + //long period (H|HL|L) detected: half 0 + full 1 + half 0 detected + if (*bits <= startBits){ + //TODO: throw error since this can't happen till after first real bit, since all startbits should be 1's } else { + //TODO check that prev bit is indeed 0 //add 10 to the memBlock - memBlock[(*bits-1)/32] |= (bit >> (*bits-1)%32); //add 1 + memBlock[(*bits-startBits)/32] |= (bit >> (*bits-startBits)%32); //add 1 (*bits)++; - (*bits)++;//add 0 is not necessary, since memBlock is initialized with all 0s, just increase counter + (*bits)++;//no mem action for 0, just increase bitcounter } - } else if (1000<=duration){ + } + else if (HITAG_DURATION_L*(1+HITAG_DURATION_ERROR_MARGIN) < duration){ + //end of modulation by tag, check if this pulse duration includes a bit if (*bits>0){ + if (*half && pulse < HITAG_DURATION_S){ + //second half of 0 bit, no action + } else if (*half && pulse < HITAG_DURATION_M){ + //second half or 0 bit + another 1 bit + memBlock[(*bits-startBits)/32] |= (bit >> (*bits-startBits)%32); //add 1 + (*bits)++; + } else if (!(*half) && pulse < HITAG_DURATION_S){ + //another 1 bit + memBlock[(*bits-startBits)/32] |= (bit >> (*bits-startBits)%32); //add 1 + (*bits)++; + } else { + //invalid pulse duration --> reset? + } *finished = true; } - } else { - //invalid pulse: reset tempData if necessary + } + else { + //invalid pulse duration: reset tempData if necessary if (*bits>0){ memset(memBlock, 0, HITAG_BLOCKPAGES*sizeof(memBlock[0])); *bits = 0; *finished = false; } } - } else if (protocol == HitagProtocolAntiCollision){ - if (HITAG_DURATION_S*(1-HITAG_DURATION_ERROR_MARGIN)<=duration && duration <=HITAG_DURATION_S*(1+HITAG_DURATION_ERROR_MARGIN) ){ - //if first small: add 1 to memBlock and increase small counter - if (*Scount==0){ - if (*bits>0){ - memBlock[(*bits-1)/32] |= (bit >> (*bits-1)%32); - } + } + else if (protocol == HitagProtocolAntiCollision){ + if (HITAG_DURATION_S*(1-HITAG_DURATION_ERROR_MARGIN) <= duration && duration <= HITAG_DURATION_S*(1+HITAG_DURATION_ERROR_MARGIN) ){ + //short period (HL): first or second half of a 1 detected + //if first short: add 1 to memBlock and increase half counter + if (*half==false){ + if (*bits >= startBits){ + memBlock[(*bits-startBits)/32] |= (bit >> (*bits-startBits)%32); + } (*bits)++; - (*Scount)++; + *half = true; } else { - //if second small: reset small counter - *Scount=0; + //if second small: reset half counter + *half = false; } - } else if (HITAG_DURATION_L*(1-HITAG_DURATION_ERROR_MARGIN)<=duration && duration <=HITAG_DURATION_L*(1+HITAG_DURATION_ERROR_MARGIN) ){ - //if small counter == 1: error - if (*bits==0 || *Scount ==1){ - //error, first pulse-duration of a bit sequence can't be a large one or coming after only 1 small pulse-duration + } + else if (HITAG_DURATION_L*(1-HITAG_DURATION_ERROR_MARGIN) <= duration && duration <= HITAG_DURATION_L*(1+HITAG_DURATION_ERROR_MARGIN) ){ + //long period (HHLL): 0 detected + //should only happen after full detection of 1 or after startBits + if (*bits < startBits || *half){ + //reset memset(memBlock, 0, HITAG_BLOCKPAGES*sizeof(memBlock[0])); *bits = 0; *finished = false; } else { - //add 0 to memBlock - (*bits)++; + (*bits)++; //add 0 to memBlock } - } else if (1000<=duration){ + } + else if (HITAG_DURATION_L*(1+HITAG_DURATION_ERROR_MARGIN) < duration){ + //end of modulation by tag, check if this pulse duration includes a bit if (*bits>0){ + if (!(*half) && pulse > HITAG_DURATION_S){ + (*bits)++; //add 0 to memBlock + } else { + //invalid pulse duration: reset? + } *finished = true; } - } else { - //invalid pulse: reset tempData if necessary + } + else { + //invalid pulse duration: reset tempData if necessary if (*bits>0){ memset(memBlock, 0, HITAG_BLOCKPAGES*sizeof(memBlock[0])); *bits = 0; *finished = false; } } - } + } } static void lfrfid_hitag_worker_calc_crc(uint8_t* crc, uint8_t data, uint8_t bitcount){ @@ -1216,7 +1255,7 @@ void lfrfid_hitag_worker_process_config_page(LFRFIDHitag* tag){ static int32_t lfrfid_hitag_worker_emulate_thread(void* thread_context) { LFRFIDHitagWorker* worker = (LFRFIDHitagWorker*)thread_context; - //TODO: check if a tag has been properly loaded with known serial nr & config page + //load tag worker->tag = malloc(sizeof(LFRFIDHitag)); lfrfid_hitag_worker_initialize_tag_data(worker->tag); @@ -1224,6 +1263,7 @@ static int32_t lfrfid_hitag_worker_emulate_thread(void* thread_context) { protocol_dict_get_data(worker->dict, LFRFIDProtocolHitag1, worker->tag->tagData, data_size); lfrfid_hitag_worker_split_tag_data(worker->tag); + //TODO: check if a tag has been properly loaded with known serial nr & config page lfrfid_hitag_worker_process_config_page(worker->tag); //init capData for carrier_in @@ -1241,10 +1281,10 @@ static int32_t lfrfid_hitag_worker_emulate_thread(void* thread_context) { //set pins to read setup furi_hal_rfid_pins_read(); - // reconfigure carrier_out to fixed low state instead + //reconfigure carrier_out to fixed low state instead furi_hal_gpio_init(&gpio_rfid_carrier_out, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); furi_hal_gpio_write(&gpio_rfid_carrier_out, false); - //reconfigure PA15 carrier_in to alt ftn 1 (TIM2 CH1) to use is for input capture + //reconfigure PA15 carrier_in to alt ftn 1 (TIM2 CH1) to use it for input capture furi_hal_gpio_init_ex(&gpio_rfid_carrier, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedLow, GpioAltFn1TIM2); //reconfigure pull to alternate function 1 (TIM2 CH3) to drive low (for read carrier) or pull antenna via DMA (for emulation) furi_hal_gpio_init_ex(&gpio_nfc_irq_rfid_pull, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedLow, GpioAltFn1TIM2); @@ -1257,8 +1297,8 @@ static int32_t lfrfid_hitag_worker_emulate_thread(void* thread_context) { bool carrier_on = false; int8_t carrier_count = 0; - uint8_t carrier_threshold_on = 5; //better to use the #define on top for this constant or make this a constant - int8_t carrier_threshold_off = -5; //better to use the #define on top for this constant or make this a constant + uint8_t carrier_threshold_on = 5; + int8_t carrier_threshold_off = -5; uint32_t periodcounter = 0; @@ -1270,8 +1310,8 @@ static int32_t lfrfid_hitag_worker_emulate_thread(void* thread_context) { uint32_t lastMinIndex = 0; uint8_t maxBitCounter = 0; uint8_t minBitCounter = 0; - uint32_t maxBits[5]; //room for 5x32 bits - uint32_t minBits[5]; //room for 5x32 bits + uint32_t maxBits[5]; + uint32_t minBits[5]; int8_t cmd = 0; uint32_t one = 1<<(32-1); uint32_t cmdIndex = 0; @@ -1289,14 +1329,14 @@ static int32_t lfrfid_hitag_worker_emulate_thread(void* thread_context) { uint32_t prevduration = 0; uint32_t duration = 0; uint8_t durationcounter = 0; - uint8_t durationthreshold = 5; //better to use the #define on top for this constant or make this a constant + uint8_t durationthreshold = 5; uint8_t tagState = HitagStateIdle; uint8_t tagMode = HitagModeBasic; while(1) { Buffer* buffer = buffer_stream_receive(capData->stream, 1); - //and if so write fo file + if(buffer != NULL) { size_t buffsize = buffer_get_size(buffer); uint8_t* buffdata = buffer_get_data(buffer); @@ -1328,11 +1368,9 @@ static int32_t lfrfid_hitag_worker_emulate_thread(void* thread_context) { minBits[1] = 0; maxBits[0] = 0; maxBits[1] = 0; - //maxBits = "" carrier_count = 0; - - //TODO: signal view that field is on (including the freq?) + //TODO reset tagstate to Idle & tagMode to basic after power_on_reset (no carrier for 10ms) } } else { carrier_count = 0; @@ -1345,8 +1383,6 @@ static int32_t lfrfid_hitag_worker_emulate_thread(void* thread_context) { if (carrier_count<=carrier_threshold_off){ carrier_on = false; carrier_count = 0; - - //TODO: signal view that field is off? eg colored LED sequence? } } else{ carrier_count = 0; @@ -1378,7 +1414,7 @@ static int32_t lfrfid_hitag_worker_emulate_thread(void* thread_context) { //check if there's a miniDelta and if it should be added to previous or next delta if (miniMaxDelta > 0){ //by default minidelta is added to next - //in case doing this does not result in 2 consequtive valid deltas, while adding it to previous does result in 2 consequtive valid deltas, then assign it to previous delta + //in case doing this does not result in 2 consequtive valid deltas, but adding it to previous does result in 2 consequtive valid deltas, then assign it to previous delta if ((lfrfid_hitag_worker_validate_ripple_delta(prevMaxDelta+miniMaxDelta) + lfrfid_hitag_worker_validate_ripple_delta(maxDelta-miniMaxDelta)) > (lfrfid_hitag_worker_validate_ripple_delta(prevMaxDelta) + lfrfid_hitag_worker_validate_ripple_delta(maxDelta))){ //update the previous (last) bit from 0 to 1 if a command was ongoing if (maxBitCounter > 0){ @@ -1457,7 +1493,7 @@ static int32_t lfrfid_hitag_worker_emulate_thread(void* thread_context) { //check if there's a miniDelta and if it should be added to previous or next delta if (miniMinDelta > 0){ //by default minidelta is added to next - //in case doing this does not result in 2 consequtive valid deltas, while adding it to previous does result in 2 consequtive valid deltas, then assign it to previous delta + //in case doing this does not result in 2 consequtive valid deltas, but adding it to previous does result in 2 consequtive valid deltas, then assign it to previous delta if ((lfrfid_hitag_worker_validate_ripple_delta(prevMinDelta+miniMinDelta) + lfrfid_hitag_worker_validate_ripple_delta(minDelta-miniMinDelta)) > (lfrfid_hitag_worker_validate_ripple_delta(prevMinDelta) + lfrfid_hitag_worker_validate_ripple_delta(minDelta))){ //update the previous (last) bit from 0 to 1 if a command was ongoing if (minBitCounter > 0){ @@ -1520,7 +1556,7 @@ static int32_t lfrfid_hitag_worker_emulate_thread(void* thread_context) { } //if a valid cmd has been detected: process it & reply - if (cmd != 0 ){//|| periodcounter%62500 == 0){ + if (cmd != 0 ){ uint32_t bits[5]; uint8_t size = 0; //select the cmd bit source (max or min detection) @@ -1532,7 +1568,7 @@ static int32_t lfrfid_hitag_worker_emulate_thread(void* thread_context) { maxBits[0] = 0; maxBits[1] = 0; maxBitCounter = 0; - } else {//if (cmd ==-1){ + } else { bits[0] = minBits[0]; bits[1] = minBits[1]; size = minBitCounter; @@ -1543,54 +1579,55 @@ static int32_t lfrfid_hitag_worker_emulate_thread(void* thread_context) { } cmd = 0; - //TODO: signal view that cmd has been found? (eg start colored LED sequence - dmaLen = 0; if (size == 5 && (bits[0]>>(32-5)) == 0b11001){ //SET_CCNEW - //reset selected state - tagState = HitagStateIdle; - - //set tag in advanced mode - tagMode = HitagModeAdvanced; - - //prepare emulate DMA buffer with advanced SN reply (page 0) and switch Timer from carrier_in capture to carrier_pull driver - dataReply->replyvalue[0] = 0b00000111; - dataReply->replytype[0] = HITAG_STARTBIT3; - dataReply->replyvalue[1] = worker->tag->pageData[0]; - dataReply->replytype[1] = HITAG_BYTE; - dataReply->replyvalue[2] = worker->tag->pageData[1]; - dataReply->replytype[2] = HITAG_BYTE; - dataReply->replyvalue[3] = worker->tag->pageData[2]; - dataReply->replytype[3] = HITAG_BYTE; - dataReply->replyvalue[4] = worker->tag->pageData[3]; - dataReply->replytype[4] = HITAG_BYTE; - - dataReply->replylength=5; + if (tagState != HitagStateQuiet){ + //reset selected state + tagState = HitagStateIdle; + + //set tag in advanced mode + tagMode = HitagModeAdvanced; + + //prepare emulate DMA buffer with advanced SN reply (page 0) + dataReply->replyvalue[0] = 0b00000111; + dataReply->replytype[0] = HITAG_STARTBIT3; + dataReply->replyvalue[1] = worker->tag->pageData[0]; + dataReply->replytype[1] = HITAG_BYTE; + dataReply->replyvalue[2] = worker->tag->pageData[1]; + dataReply->replytype[2] = HITAG_BYTE; + dataReply->replyvalue[3] = worker->tag->pageData[2]; + dataReply->replytype[3] = HITAG_BYTE; + dataReply->replyvalue[4] = worker->tag->pageData[3]; + dataReply->replytype[4] = HITAG_BYTE; + + dataReply->replylength=5; - dmaLen = lfrfid_hitag_worker_yield_dma_buffer_AC(dataReply, dataDMA, worker->carrierPrescaler); - + dmaLen = lfrfid_hitag_worker_yield_dma_buffer_AC(dataReply, dataDMA, worker->carrierPrescaler); + } } else if (size == 5 && (bits[0]>>(32-5)) == 0b00110){ //SET_CC - //reset selected state - tagState = HitagStateIdle; - - //do not (re)set tag in basic mode, this only happens during power on reset - - //prepare emulate DMA buffer with basic SN reply (page 0) and switch Timer from carrier_in capture to carrier_pull driver - dataReply->replyvalue[0] = 0b00000001; - dataReply->replytype[0] = HITAG_STARTBIT; - dataReply->replyvalue[1] = worker->tag->pageData[0]; - dataReply->replytype[1] = HITAG_BYTE; - dataReply->replyvalue[2] = worker->tag->pageData[1]; - dataReply->replytype[2] = HITAG_BYTE; - dataReply->replyvalue[3] = worker->tag->pageData[2]; - dataReply->replytype[3] = HITAG_BYTE; - dataReply->replyvalue[4] = worker->tag->pageData[3]; - dataReply->replytype[4] = HITAG_BYTE; - - dataReply->replylength=5; - - dmaLen = lfrfid_hitag_worker_yield_dma_buffer_AC(dataReply, dataDMA, worker->carrierPrescaler); + if (tagState != HitagStateQuiet){ + //reset selected state + tagState = HitagStateIdle; + + //do not (re)set tag in basic mode, this only happens during power on reset + + //prepare emulate DMA buffer with basic SN reply (page 0) + dataReply->replyvalue[0] = 0b00000001; + dataReply->replytype[0] = HITAG_STARTBIT; + dataReply->replyvalue[1] = worker->tag->pageData[0]; + dataReply->replytype[1] = HITAG_BYTE; + dataReply->replyvalue[2] = worker->tag->pageData[1]; + dataReply->replytype[2] = HITAG_BYTE; + dataReply->replyvalue[3] = worker->tag->pageData[2]; + dataReply->replytype[3] = HITAG_BYTE; + dataReply->replyvalue[4] = worker->tag->pageData[3]; + dataReply->replytype[4] = HITAG_BYTE; + + dataReply->replylength=5; + + dmaLen = lfrfid_hitag_worker_yield_dma_buffer_AC(dataReply, dataDMA, worker->carrierPrescaler); + } } else if (size == 45 && (bits[0]>>(32-5)) == 0b00000){ //SELECT //check if SN is matching @@ -1603,7 +1640,7 @@ static int32_t lfrfid_hitag_worker_emulate_thread(void* thread_context) { //reply with config (page 1 from tag memory) tagState = HitagStateSelected; - //prepare emulate DMA buffer with basic/advanced config page reply (page 1) and switch Timer from carrier_in capture to carrier_pull driver + //prepare emulate DMA buffer with basic/advanced config page reply (page 1) if (tagMode == HitagModeBasic){ dataReply->replyvalue[0] = 0b00000001; dataReply->replytype[0] = HITAG_STARTBIT; @@ -1740,10 +1777,10 @@ static int32_t lfrfid_hitag_worker_emulate_thread(void* thread_context) { //switch carrier mode to ETR required for emulation lfrfid_hitag_worker_carrier_in_ETR_mode(capData, prescaler); - //respect hitag WAIT1 time (& make sure to have sufficient room in the capdata buffer to store all capdata while waiting for this - uint32_t wait1_periods = 180/prescaler; //originally 204 + //respect hitag WAIT1 time + uint32_t wait1_periods = 180/prescaler; while (capData->capCounter-cmdIndex < wait1_periods){ - furi_delay_us(40); //wait 5 more periods (datasheet says 204-213 periods, so waiting for 5 periods should land somewhere in this range) + furi_delay_us(40); } //just start the DMA, stopping is handled in DMA transfer complete interrupt @@ -1789,38 +1826,38 @@ static int32_t lfrfid_hitag_worker_emulate_thread(void* thread_context) { static int32_t lfrfid_hitag_worker_read_thread(void* thread_context) { LFRFIDHitagWorker* worker = (LFRFIDHitagWorker*)thread_context; - LfRfidHitagProtocol currentProtocol = HitagProtocolAntiCollision; - LfRfidHitagCaptureData* capData = malloc(sizeof(LfRfidHitagCaptureData)); - capData->stream = buffer_stream_alloc(READ_BUFFER_SIZE, READ_BUFFER_COUNT); - capData->pair = varint_pair_alloc(); - - LfRfidHitagCMDData* dataCMD = malloc(sizeof(LfRfidHitagCMDData)); - dataCMD->CMDlength = 0; - dataCMD->CMDposition = 0; - dataCMD->CMDsubposition = 0; - dataCMD->CMDactive = false; - LfRfidHitagCMDData* backupCMD = malloc(sizeof(LfRfidHitagCMDData)); - TimerDMAData* dataDMA = malloc(sizeof(TimerDMAData)); - //initialize a tag datastructure worker->tag = malloc(sizeof(LFRFIDHitag)); lfrfid_hitag_worker_initialize_tag_data(worker->tag); + + //prep variables for decoder + LfRfidHitagProtocol currentProtocol = HitagProtocolAntiCollision; + uint8_t startBits = 1; uint32_t memBlock[HITAG_BLOCKPAGES] = {0}; uint8_t bits = 0; - uint8_t expectedBits = 33; - uint8_t Scount = 0; + bool halfBit = false; bool finished = false; + + //prep variables for worker + uint8_t expectedBits = 33; uint8_t readings = 0; uint8_t currPage = 0; bool readSucces = false; + uint8_t readThreshold = 3; - // setup carrier timer to generate ASK field - furi_hal_rfid_pins_read(); //--> ibutton low, pull low, carrier_out to TIM1 CH1N; carrier_pull low; data_in analog + // setup pins for reading + furi_hal_rfid_pins_read(); - //and setup TIM1 CH1N with DMA (similar as is done with TIM2 in emulate mode + //and setup TIM1 CH1N with DMA //fill the DMA arr & ccr buffers - /*start with looping SETCC command*/ + LfRfidHitagCMDData* dataCMD = malloc(sizeof(LfRfidHitagCMDData)); + dataCMD->CMDlength = 0; + dataCMD->CMDposition = 0; + dataCMD->CMDsubposition = 0; + dataCMD->CMDactive = false; + + LfRfidHitagCMDData* backupCMD = malloc(sizeof(LfRfidHitagCMDData)); uint32_t value[] = {128, 0b00110, 1, 213, (uint32_t)(1.05*33*HITAG_BITPERIODS_AC)}; //always take 5% reading time buffer uint8_t type[] = {HITAG_ON, HITAG_SET, HITAG_STOP, HITAG_ON, HITAG_ON}; for (size_t i = 0;i<5;i++){ @@ -1832,10 +1869,12 @@ static int32_t lfrfid_hitag_worker_read_thread(void* thread_context) { backupCMD->CMDsubposition=0; backupCMD->CMDloop = true; //not really required since by definition the backup cmd is looping already in yield code backupCMD->CMDactive = true; + + TimerDMAData* dataDMA = malloc(sizeof(TimerDMAData)); lfrfid_hitag_worker_yield_dma_buffer_BPLM(dataCMD, backupCMD, dataDMA, 0, DMA_BUFFER_SIZE/2); lfrfid_hitag_worker_yield_dma_buffer_BPLM(dataCMD, backupCMD, dataDMA, DMA_BUFFER_SIZE/2, DMA_BUFFER_SIZE/2); - //my own version of the TIMER with DMA function, since this was hardcoded using TIM2 CH3 and I need TIM1 CH1N + //start TIM1 with DMA function lfrfid_hitag_worker_carrier_out_start( dataDMA->timer_buffer_arr, dataDMA->timer_buffer_ccr, @@ -1843,15 +1882,15 @@ static int32_t lfrfid_hitag_worker_read_thread(void* thread_context) { worker); // start capture + LfRfidHitagCaptureData* capData = malloc(sizeof(LfRfidHitagCaptureData)); + capData->stream = buffer_stream_alloc(READ_BUFFER_SIZE, READ_BUFFER_COUNT); + capData->pair = varint_pair_alloc(); furi_hal_rfid_tim_read_capture_start(lfrfid_hitag_worker_capture_in_cc_isr, capData); while(1) { - //check if there's data in raw capture buffer (this also serves a delay in case there's nothing in the buffer, not to overload mcu) - //I need to shorten the delay time, since I'm also using this routine to process DMA half transfer & transfer complete signals, which occurs sooner than 100ms? - //minimal time interfal for HT & TC is DMA_BUFFER_SIZE/2*8us (normal 125kHz operation): 4000/2*8=16 milliseconds, if I do timeout with 8 ms, then i have minimum 8ms remaining to process the DMA HT & TC signals Buffer* buffer = buffer_stream_receive(capData->stream, 1); - //process date if available + //process data if available if(buffer != NULL) { size_t buffsize = buffer_get_size(buffer); uint8_t* buffdata = buffer_get_data(buffer); @@ -1870,14 +1909,13 @@ static int32_t lfrfid_hitag_worker_read_thread(void* thread_context) { index += tmp_size; //feed pulse duration to decoder - lfrfid_hitag_worker_decoder_feed(pulse, duration, memBlock, &bits, &Scount, &finished, currentProtocol); + lfrfid_hitag_worker_decoder_feed(pulse, duration, memBlock, &bits, &halfBit, &finished, currentProtocol, startBits); //see if i have a valid bit sequence if (finished ){ - //bitsequence ready for falidation - if (bits == expectedBits){//eligable bitsequence detected, accept it only after 3 consequtive readings + //bitsequence ready for validation + if (bits == expectedBits){ //eligable bitsequence detected, accept it only after x consequtive readings //reset the backupCMD count to ensure some pause between bitsequence detection & the CMD injection - //rest is only required when scanning for the SN actually, since otherwise backup count is reset anyhow after switching from real to backup command backupCMD->CMDcount = 0; if (readings == 0){ @@ -1889,7 +1927,7 @@ static int32_t lfrfid_hitag_worker_read_thread(void* thread_context) { worker->tag->pageKnown[currPage+p] = 0; } readings=1; - } else if (readings < 3){ + } else if (readings < readThreshold){ bool match = true; for (uint8_t p=0;p<(expectedBits-1)/32;p++){ if (worker->tag->pageData[(currPage+p)*4+0] != (memBlock[p]>>24 & 0xff) || @@ -1902,7 +1940,7 @@ static int32_t lfrfid_hitag_worker_read_thread(void* thread_context) { } if(match){ readings++; - if (readings==3){ + if (readings==readThreshold){ //succesful page(s) reading readSucces = true; @@ -2046,9 +2084,9 @@ static int32_t lfrfid_hitag_worker_read_thread(void* thread_context) { } } - //while not having 3 consequtive reads, relaunch the (already prepared) command + //while not having x consequtive reads, relaunch the (already prepared) command if (!readSucces && backupCMD->CMDcount>=7 && currPage > 0 && expectedBits > 0){ - backupCMD->CMDcount=0;//required since in next loop there might not be a HT/TC trigger to trigger the yield ftion where cmd count is updated/reset + backupCMD->CMDcount=0; dataCMD->CMDposition=0; dataCMD->CMDsubposition=0; } @@ -2065,7 +2103,7 @@ static int32_t lfrfid_hitag_worker_read_thread(void* thread_context) { } //check for DMA HT/TC signals to repopulate the DMA buffer - if (worker->DMAeventCount>1){ //more than 1 unprocessed DMA event (aka DMA buffer is not timely refilled) + if (worker->DMAeventCount>1){ FURI_LOG_E(TAG, "DMA refilling can't keep up %u",worker->DMAeventCount); } if(FURI_BIT(flags, LFRFIDHitagWorkerSignalHalfTransfer)) { @@ -2073,7 +2111,7 @@ static int32_t lfrfid_hitag_worker_read_thread(void* thread_context) { lfrfid_hitag_worker_yield_dma_buffer_BPLM(dataCMD, backupCMD, dataDMA, 0, DMA_BUFFER_SIZE/2); } if(FURI_BIT(flags, LFRFIDHitagWorkerSignalTransferComplete)) { - //refill second half + //refill second half of dma buffer lfrfid_hitag_worker_yield_dma_buffer_BPLM(dataCMD, backupCMD, dataDMA, DMA_BUFFER_SIZE/2, DMA_BUFFER_SIZE/2); } worker->DMAeventCount=0; @@ -2086,7 +2124,7 @@ static int32_t lfrfid_hitag_worker_read_thread(void* thread_context) { furi_hal_rfid_tim_read_capture_stop(); lfrfid_hitag_worker_carrier_out_stop(); - + //free memory free(dataCMD); free(backupCMD); free(dataDMA); @@ -2094,7 +2132,7 @@ static int32_t lfrfid_hitag_worker_read_thread(void* thread_context) { buffer_stream_free(capData->stream); free(capData); - //is this a routine to allow the view that started the thread to signal the stop, which will properly merge the threads & de-init the worker? + //allow the view that started the thread to signal the stop, which will properly merge the threads & de-init the worker if(currPage==HITAG_PAGES-4) { const uint32_t available_flags = (1 << LFRFIDHitagWorkerSignalStop); while(true) { @@ -2109,7 +2147,7 @@ static int32_t lfrfid_hitag_worker_read_thread(void* thread_context) { free(worker->tag); - //clear the stop flag on workder so that i can use the worker to launch a new read routine without having to restart the app + //clear the stop flag on workder so that i can use the same worker to launch a new routine furi_event_flag_clear(worker->events, 1 << LFRFIDHitagWorkerSignalStop); return 0; From 3c9574f21c16867b9a53748c119ed832c34cd3dc Mon Sep 17 00:00:00 2001 From: Daan De Witte Date: Mon, 17 Jul 2023 21:12:44 +0200 Subject: [PATCH 07/14] code formatted and reworked in line with bus abstraction mods --- applications/main/lfrfid/lfrfid_i.h | 4 +- .../scenes/lfrfid_scene_extra_actions.c | 6 +- .../main/lfrfid/scenes/lfrfid_scene_read.c | 22 +- .../lfrfid/scenes/lfrfid_scene_saved_info.c | 2 +- .../main/lfrfid/views/lfrfid_view_read.c | 87 +- .../main/lfrfid/views/lfrfid_view_read.h | 6 +- lib/lfrfid/lfrfid_dict_file.c | 151 +- lib/lfrfid/lfrfid_hitag_worker.c | 3693 +++++++++-------- lib/lfrfid/lfrfid_hitag_worker.h | 20 +- lib/lfrfid/lfrfid_worker.c | 4 +- lib/lfrfid/lfrfid_worker.h | 9 +- lib/lfrfid/lfrfid_worker_modes.c | 194 +- lib/lfrfid/protocols/lfrfid_protocols.h | 2 +- lib/lfrfid/protocols/protocol_hitag1.c | 62 +- 14 files changed, 2247 insertions(+), 2015 deletions(-) diff --git a/applications/main/lfrfid/lfrfid_i.h b/applications/main/lfrfid/lfrfid_i.h index 81051e785f9..b3dbe6a612e 100644 --- a/applications/main/lfrfid/lfrfid_i.h +++ b/applications/main/lfrfid/lfrfid_i.h @@ -56,8 +56,8 @@ enum LfRfidCustomEvent { LfRfidEventReadSenseCardEnd, LfRfidEventReadStartASK, LfRfidEventReadStartPSK, - LfRfidEventReadStartRTF, - LfRfidEventReadSenseHitag, //TODO combine with sensecardstart? + LfRfidEventReadStartRTF, + LfRfidEventReadSenseHitag, //TODO combine with sensecardstart? LfRfidEventReadDone, LfRfidEventReadOverrun, LfRfidEventReadError, diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c b/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c index 3c7ce188ec5..2bd60e35809 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c @@ -4,7 +4,7 @@ typedef enum { SubmenuIndexASK, SubmenuIndexPSK, - SubmenuIndexHitag, + SubmenuIndexHitag, SubmenuIndexRAW, } SubmenuIndex; @@ -30,7 +30,7 @@ void lfrfid_scene_extra_actions_on_enter(void* context) { SubmenuIndexPSK, lfrfid_scene_extra_actions_submenu_callback, app); - submenu_add_item( + submenu_add_item( submenu, "Read RTF (Reader Talks First)", SubmenuIndexHitag, @@ -75,7 +75,7 @@ bool lfrfid_scene_extra_actions_on_event(void* context, SceneManagerEvent event) } else if(event.event == SubmenuIndexHitag) { app->read_type = LFRFIDWorkerReadTypeRTFOnly; scene_manager_next_scene(app->scene_manager, LfRfidSceneRead); - DOLPHIN_DEED(DolphinDeedRfidRead); + dolphin_deed(DolphinDeedRfidRead); consumed = true; } else if(event.event == SubmenuIndexRAW) { scene_manager_next_scene(app->scene_manager, LfRfidSceneRawName); diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_read.c b/applications/main/lfrfid/scenes/lfrfid_scene_read.c index c67d398af64..fa90e492abf 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_read.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_read.c @@ -16,7 +16,8 @@ static const NotificationSequence sequence_blink_set_cyan = { NULL, }; -static void lfrfid_read_callback(LFRFIDWorkerReadResult result, ProtocolId protocol, void* context) { +static void + lfrfid_read_callback(LFRFIDWorkerReadResult result, ProtocolId protocol, void* context) { LfRfid* app = context; uint32_t event = 0; @@ -37,7 +38,7 @@ static void lfrfid_read_callback(LFRFIDWorkerReadResult result, ProtocolId proto event = LfRfidEventReadStartPSK; } else if(result == LFRFIDWorkerReadStartRTF) { event = LfRfidEventReadStartRTF; - } else if(result == LFRFIDWorkerReadSenseHitag) { //TODO combine with sensecardstart? + } else if(result == LFRFIDWorkerReadSenseHitag) { //TODO combine with sensecardstart? event = LfRfidEventReadSenseHitag; } else { return; @@ -54,7 +55,7 @@ void lfrfid_scene_read_on_enter(void* context) { } else if(app->read_type == LFRFIDWorkerReadTypeASKOnly) { lfrfid_view_read_set_read_mode(app->read_view, LfRfidReadAskOnly); } else if(app->read_type == LFRFIDWorkerReadTypeRTFOnly) { - lfrfid_view_read_set_read_state(app->read_view, LfRfidReadScanning); + lfrfid_view_read_set_read_state(app->read_view, LfRfidReadScanning); lfrfid_view_read_set_read_mode(app->read_view, LfRfidReadRtfOnly); } @@ -101,17 +102,16 @@ bool lfrfid_scene_read_on_event(void* context, SceneManagerEvent event) { consumed = true; } else if(event.event == LfRfidEventReadStartRTF) { if(app->read_type == LFRFIDWorkerReadTypeAuto) { - lfrfid_view_read_set_read_state(app->read_view, LfRfidReadScanning); + lfrfid_view_read_set_read_state(app->read_view, LfRfidReadScanning); lfrfid_view_read_set_read_mode(app->read_view, LfRfidReadHitag); } consumed = true; - } else if(event.event == LfRfidEventReadSenseHitag) { //TODO combine with sensecardstart? - if( - app->read_type == LFRFIDWorkerReadTypeAuto || - app->read_type == LFRFIDWorkerReadTypeRTFOnly) { - lfrfid_view_read_set_read_state(app->read_view, LfRfidReadTagDetected); - } - } + } else if(event.event == LfRfidEventReadSenseHitag) { //TODO combine with sensecardstart? + if(app->read_type == LFRFIDWorkerReadTypeAuto || + app->read_type == LFRFIDWorkerReadTypeRTFOnly) { + lfrfid_view_read_set_read_state(app->read_view, LfRfidReadTagDetected); + } + } } return consumed; diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_saved_info.c b/applications/main/lfrfid/scenes/lfrfid_scene_saved_info.c index 83fb0a2bd2a..7a0b572ccf9 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_saved_info.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_saved_info.c @@ -26,7 +26,7 @@ void lfrfid_scene_saved_info_on_enter(void* context) { } furi_string_cat_printf(tmp_string, "%02X", data[i]); } - } + } free(data); FuriString* render_data; diff --git a/applications/main/lfrfid/views/lfrfid_view_read.c b/applications/main/lfrfid/views/lfrfid_view_read.c index b5d89bc99ba..c0dda4178e6 100644 --- a/applications/main/lfrfid/views/lfrfid_view_read.c +++ b/applications/main/lfrfid/views/lfrfid_view_read.c @@ -11,7 +11,7 @@ struct LfRfidReadView { typedef struct { IconAnimation* icon; LfRfidReadViewMode read_mode; - LfRfidReadViewState read_state; + LfRfidReadViewState read_state; } LfRfidReadViewModel; static void lfrfid_view_read_draw_callback(Canvas* canvas, void* _model) { @@ -31,7 +31,7 @@ static void lfrfid_view_read_draw_callback(Canvas* canvas, void* _model) { canvas_set_font(canvas, FontSecondary); canvas_draw_str(canvas, 77, 33, "PSK"); - canvas_draw_str(canvas, 77, 46, "RTF"); + canvas_draw_str(canvas, 77, 46, "RTF"); } else if(model->read_mode == LfRfidReadPsk) { canvas_draw_str(canvas, 70, 8, "Reading 2/3"); @@ -41,48 +41,48 @@ static void lfrfid_view_read_draw_callback(Canvas* canvas, void* _model) { canvas_set_font(canvas, FontSecondary); canvas_draw_str(canvas, 77, 20, "ASK"); - canvas_draw_str(canvas, 77, 46, "RTF"); + canvas_draw_str(canvas, 77, 46, "RTF"); } else if(model->read_mode == LfRfidReadHitag) { - if (model->read_state == LfRfidReadScanning){ - canvas_draw_str(canvas, 70, 8, "Reading 3/3"); - - canvas_draw_str(canvas, 77, 46, "RTF"); - canvas_draw_icon(canvas, 70, 39, &I_ButtonRight_4x7); - canvas_draw_icon_animation(canvas, 112, 38, model->icon); - - canvas_set_font(canvas, FontSecondary); - canvas_draw_str(canvas, 77, 20, "ASK"); - canvas_draw_str(canvas, 77, 33, "PSK"); - } else if (model->read_state == LfRfidReadTagDetected){//TODO switch to other scene? - canvas_draw_str(canvas, 65, 8, "Hitag1 found"); - - canvas_set_font(canvas, FontSecondary); - //canvas_draw_str(canvas, 70, 20, "## ## ## ##"); //TODO get tag SN from hitag worker - canvas_draw_str(canvas, 70, 33, "Reading data"); - //canvas_draw_str(canvas, 70, 46, "Page: X/64"); //TODO get current page index from hitag worker - } + if(model->read_state == LfRfidReadScanning) { + canvas_draw_str(canvas, 70, 8, "Reading 3/3"); + + canvas_draw_str(canvas, 77, 46, "RTF"); + canvas_draw_icon(canvas, 70, 39, &I_ButtonRight_4x7); + canvas_draw_icon_animation(canvas, 112, 38, model->icon); + + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 77, 20, "ASK"); + canvas_draw_str(canvas, 77, 33, "PSK"); + } else if(model->read_state == LfRfidReadTagDetected) { //TODO switch to other scene? + canvas_draw_str(canvas, 65, 8, "Hitag1 found"); + + canvas_set_font(canvas, FontSecondary); + //canvas_draw_str(canvas, 70, 20, "## ## ## ##"); //TODO get tag SN from hitag worker + canvas_draw_str(canvas, 70, 33, "Reading data"); + //canvas_draw_str(canvas, 70, 46, "Page: X/64"); //TODO get current page index from hitag worker + } } else if(model->read_mode == LfRfidReadAskOnly) { - canvas_draw_str(canvas, 72, 16, "Reading"); - canvas_draw_str(canvas, 77, 35, "ASK"); + canvas_draw_str(canvas, 72, 16, "Reading"); + canvas_draw_str(canvas, 77, 35, "ASK"); canvas_draw_icon_animation(canvas, 112, 27, model->icon); - } else if(model->read_mode == LfRfidReadPskOnly) { - canvas_draw_str(canvas, 72, 16, "Reading"); - canvas_draw_str(canvas, 77, 35, "PSK"); + } else if(model->read_mode == LfRfidReadPskOnly) { + canvas_draw_str(canvas, 72, 16, "Reading"); + canvas_draw_str(canvas, 77, 35, "PSK"); canvas_draw_icon_animation(canvas, 112, 27, model->icon); - } else if(model->read_mode == LfRfidReadRtfOnly) { - if (model->read_state == LfRfidReadScanning){ - canvas_draw_str(canvas, 72, 16, "Reading"); - canvas_draw_str(canvas, 77, 35, "RTF"); - canvas_draw_icon_animation(canvas, 112, 27, model->icon); - } else if (model->read_state == LfRfidReadTagDetected){ //TODO switch to other scene? - canvas_draw_str(canvas, 65, 8, "Hitag1 found"); - - canvas_set_font(canvas, FontSecondary); - //canvas_draw_str(canvas, 70, 20, "## ## ## ##"); //TODO get tag SN from hitag worker - canvas_draw_str(canvas, 70, 33, "Reading data"); - //canvas_draw_str(canvas, 70, 46, "Page: X/64"); //TODO get current page index from hitag worker - } - } + } else if(model->read_mode == LfRfidReadRtfOnly) { + if(model->read_state == LfRfidReadScanning) { + canvas_draw_str(canvas, 72, 16, "Reading"); + canvas_draw_str(canvas, 77, 35, "RTF"); + canvas_draw_icon_animation(canvas, 112, 27, model->icon); + } else if(model->read_state == LfRfidReadTagDetected) { //TODO switch to other scene? + canvas_draw_str(canvas, 65, 8, "Hitag1 found"); + + canvas_set_font(canvas, FontSecondary); + //canvas_draw_str(canvas, 70, 20, "## ## ## ##"); //TODO get tag SN from hitag worker + canvas_draw_str(canvas, 70, 33, "Reading data"); + //canvas_draw_str(canvas, 70, 46, "Page: X/64"); //TODO get current page index from hitag worker + } + } canvas_set_font(canvas, FontSecondary); canvas_draw_str(canvas, 61, 60, "Don't move card"); @@ -148,10 +148,5 @@ void lfrfid_view_read_set_read_mode(LfRfidReadView* read_view, LfRfidReadViewMod void lfrfid_view_read_set_read_state(LfRfidReadView* read_view, LfRfidReadViewState state) { with_view_model( - read_view->view, - LfRfidReadViewModel * model, - { - model->read_state = state; - }, - true); + read_view->view, LfRfidReadViewModel * model, { model->read_state = state; }, true); } \ No newline at end of file diff --git a/applications/main/lfrfid/views/lfrfid_view_read.h b/applications/main/lfrfid/views/lfrfid_view_read.h index 9b2d749a80c..7d6d8ead496 100644 --- a/applications/main/lfrfid/views/lfrfid_view_read.h +++ b/applications/main/lfrfid/views/lfrfid_view_read.h @@ -4,15 +4,15 @@ typedef enum { LfRfidReadAsk, LfRfidReadPsk, - LfRfidReadHitag, + LfRfidReadHitag, LfRfidReadAskOnly, LfRfidReadPskOnly, - LfRfidReadRtfOnly, + LfRfidReadRtfOnly, } LfRfidReadViewMode; typedef enum { LfRfidReadScanning, - LfRfidReadTagDetected, + LfRfidReadTagDetected, } LfRfidReadViewState; typedef struct LfRfidReadView LfRfidReadView; diff --git a/lib/lfrfid/lfrfid_dict_file.c b/lib/lfrfid/lfrfid_dict_file.c index f053f463b53..f8c3d5ef71b 100644 --- a/lib/lfrfid/lfrfid_dict_file.c +++ b/lib/lfrfid/lfrfid_dict_file.c @@ -5,37 +5,40 @@ #define LFRFID_DICT_FILETYPE "Flipper RFID key" -bool lfrfid_dict_file_save_hitag1_data(FlipperFormat* file, uint8_t* data){ - FuriString* string = furi_string_alloc(); - bool result = false; - uint8_t pageSize = 4; - - do { - //write shortened data (tag ID) - if(!flipper_format_write_hex(file, "Data", data, pageSize)) break; - - if(!flipper_format_write_comment_cstr(file, "Hitag1 specific data")) break; - - //write pages - for (uint8_t p=0;p<64;p++){ - furi_string_printf(string, "Page %2u", p); - if (data[64*pageSize+p]){ - //write page data - if(!flipper_format_write_hex(file, furi_string_get_cstr(string), data+p*pageSize, pageSize)) break; - } else { - //write ?? ?? ?? ?? - if(!flipper_format_write_string_cstr(file, furi_string_get_cstr(string), "?? ?? ?? ??")) break; - } - if (p==64-1){ - result = true; - } - } - } - while (false); - - furi_string_free(string); - - return result; +bool lfrfid_dict_file_save_hitag1_data(FlipperFormat* file, uint8_t* data) { + FuriString* string = furi_string_alloc(); + bool result = false; + uint8_t pageSize = 4; + + do { + //write shortened data (tag ID) + if(!flipper_format_write_hex(file, "Data", data, pageSize)) break; + + if(!flipper_format_write_comment_cstr(file, "Hitag1 specific data")) break; + + //write pages + for(uint8_t p = 0; p < 64; p++) { + furi_string_printf(string, "Page %2u", p); + if(data[64 * pageSize + p]) { + //write page data + if(!flipper_format_write_hex( + file, furi_string_get_cstr(string), data + p * pageSize, pageSize)) + break; + } else { + //write ?? ?? ?? ?? + if(!flipper_format_write_string_cstr( + file, furi_string_get_cstr(string), "?? ?? ?? ??")) + break; + } + if(p == 64 - 1) { + result = true; + } + } + } while(false); + + furi_string_free(string); + + return result; } bool lfrfid_dict_file_save(ProtocolDict* dict, ProtocolId protocol, const char* filename) { @@ -58,12 +61,12 @@ bool lfrfid_dict_file_save(ProtocolDict* dict, ProtocolId protocol, const char* // TODO: write comment about protocol sizes into file - protocol_dict_get_data(dict, protocol, data, data_size); - if (protocol == LFRFIDProtocolHitag1){ - if(!lfrfid_dict_file_save_hitag1_data(file, data)) break; - } else { - if(!flipper_format_write_hex(file, "Data", data, data_size)) break; - } + protocol_dict_get_data(dict, protocol, data, data_size); + if(protocol == LFRFIDProtocolHitag1) { + if(!lfrfid_dict_file_save_hitag1_data(file, data)) break; + } else { + if(!flipper_format_write_hex(file, "Data", data, data_size)) break; + } result = true; } while(false); @@ -175,39 +178,39 @@ static ProtocolId lfrfid_dict_protocol_fallback( return result; } -bool lfrfid_dict_file_load_hitag1_data(FlipperFormat* file, uint8_t* data){ - FuriString* string = furi_string_alloc(); - bool result = false; - uint8_t tagID[4]; - uint8_t pageSize = 4; - - do { - //read shortened data (tag ID) - if(!flipper_format_read_hex(file, "Data", tagID, 4)) break; - - //read pages - for (uint8_t p=0;p<64;p++){ - furi_string_printf(string, "Page %2u", p); - if (flipper_format_read_hex(file, furi_string_get_cstr(string), data+p*pageSize, pageSize)){ - data[64*pageSize+p]=1; - } else { - data[64*pageSize+p]=0; - } - } - - //check data consistency - if (memcmp(tagID, data, pageSize)!=0) break; - - //check if tag ID & config page are succesfully read - if (data[64*pageSize+0] && data[64*pageSize+1]){ - result = true; - } - } - while (false); - - furi_string_free(string); - - return result; +bool lfrfid_dict_file_load_hitag1_data(FlipperFormat* file, uint8_t* data) { + FuriString* string = furi_string_alloc(); + bool result = false; + uint8_t tagID[4]; + uint8_t pageSize = 4; + + do { + //read shortened data (tag ID) + if(!flipper_format_read_hex(file, "Data", tagID, 4)) break; + + //read pages + for(uint8_t p = 0; p < 64; p++) { + furi_string_printf(string, "Page %2u", p); + if(flipper_format_read_hex( + file, furi_string_get_cstr(string), data + p * pageSize, pageSize)) { + data[64 * pageSize + p] = 1; + } else { + data[64 * pageSize + p] = 0; + } + } + + //check data consistency + if(memcmp(tagID, data, pageSize) != 0) break; + + //check if tag ID & config page are succesfully read + if(data[64 * pageSize + 0] && data[64 * pageSize + 1]) { + result = true; + } + } while(false); + + furi_string_free(string); + + return result; } ProtocolId lfrfid_dict_file_load(ProtocolDict* dict, const char* filename) { @@ -235,12 +238,12 @@ ProtocolId lfrfid_dict_file_load(ProtocolDict* dict, const char* filename) { if(protocol == PROTOCOL_NO) { protocol = lfrfid_dict_protocol_fallback(dict, furi_string_get_cstr(str_result), file); if(protocol == PROTOCOL_NO) break; - } else if(protocol == LFRFIDProtocolHitag1){ - // Hitag1 data - size_t data_size = protocol_dict_get_data_size(dict, protocol); + } else if(protocol == LFRFIDProtocolHitag1) { + // Hitag1 data + size_t data_size = protocol_dict_get_data_size(dict, protocol); if(!lfrfid_dict_file_load_hitag1_data(file, data)) break; protocol_dict_set_data(dict, protocol, data, data_size); - } else { + } else { // data size_t data_size = protocol_dict_get_data_size(dict, protocol); if(!flipper_format_read_hex(file, "Data", data, data_size)) break; diff --git a/lib/lfrfid/lfrfid_hitag_worker.c b/lib/lfrfid/lfrfid_hitag_worker.c index 2692cd14690..4f315ef6f1f 100644 --- a/lib/lfrfid/lfrfid_hitag_worker.c +++ b/lib/lfrfid/lfrfid_hitag_worker.c @@ -1,7 +1,8 @@ #include "lfrfid_hitag_worker.h" -#define DMA_BUFFER_SIZE 4000 //I need to keep this big enough? to have enough time inbetween HT & TC events perhaps? -#define HITAG_CMD_BUFFER_SIZE 100 //this was originally 1000, but can be lowered +#define DMA_BUFFER_SIZE \ + 4000 //I need to keep this big enough? to have enough time inbetween HT & TC events perhaps? +#define HITAG_CMD_BUFFER_SIZE 100 //this was originally 1000, but can be lowered #define READ_BUFFER_SIZE 4000 #define READ_BUFFER_COUNT 4 @@ -55,16 +56,20 @@ // TIMER definitions #define CARRIER_OUT_TIMER TIM1 +#define CARRIER_OUT_TIMER_BUS FuriHalBusTIM1 //#define CARRIER_OUT_TIMER_IRQ FuriHalInterruptIdTIM1 //TODO am i using this? #define CARRIER_OUT_TIMER_CHANNEL LL_TIM_CHANNEL_CH1 // or LL_TIM_CHANNEL_CH1N #define CARRIER_IN_TIMER TIM2 -#define CARRIER_IN_TIMER_IND_CH LL_TIM_CHANNEL_CH2 //no longer used via ETR setup -#define CARRIER_IN_TIMER_DIR_CH LL_TIM_CHANNEL_CH1 //no longer used via ETR setup +#define CARRIER_IN_TIMER_BUS FuriHalBusTIM2 +#define CARRIER_IN_TIMER_IND_CH LL_TIM_CHANNEL_CH2 //no longer used via ETR setup +#define CARRIER_IN_TIMER_DIR_CH LL_TIM_CHANNEL_CH1 //no longer used via ETR setup #define CARRIER_IN_REFERENCE_TIMER TIM1 +#define CARRIER_IN_REFERENCE_TIMER_BUS FuriHalBusTIM1 #define PULL_OUT_TIMER TIM2 +#define PULL_OUT_TIMER_BUS FuriHalBusTIM2 #define PULL_OUT_TIMER_CHANNEL LL_TIM_CHANNEL_CH3 // DMA Channels definition @@ -76,81 +81,78 @@ #define PULL_OUT_DMA_CH2_DEF PULL_OUT_DMA, PULL_OUT_DMA_CH2 typedef enum { - HitagProtocolAntiCollision, - HitagProtocolManchesterCoding, + HitagProtocolAntiCollision, + HitagProtocolManchesterCoding, } LfRfidHitagProtocol; typedef enum { LFRFIDHitagWorkerSignalHalfTransfer, - LFRFIDHitagWorkerSignalTransferComplete, - LFRFIDHitagWorkerSignalStop, + LFRFIDHitagWorkerSignalTransferComplete, + LFRFIDHitagWorkerSignalStop, } LFRFIDHitagWorkerSignal; typedef enum { - HitagStateIdle, - HitagStateSelected, - HitagStateQuiet, + HitagStateIdle, + HitagStateSelected, + HitagStateQuiet, } LfRfidHitagState; typedef enum { - HitagModeBasic, - HitagModeAdvanced, + HitagModeBasic, + HitagModeAdvanced, } LfRfidHitagMode; - typedef struct { uint32_t timer_buffer_arr[DMA_BUFFER_SIZE]; uint32_t timer_buffer_ccr[DMA_BUFFER_SIZE]; } TimerDMAData; typedef struct { - uint32_t CMDvalue[HITAG_CMD_BUFFER_SIZE]; //this can likely be a uint16_t instead of uint32_t - uint8_t CMDtype[HITAG_CMD_BUFFER_SIZE]; - uint16_t CMDlength; - uint16_t CMDposition; - uint16_t CMDsubposition; - bool CMDloop; - bool CMDactive; - uint32_t CMDcount; + uint32_t CMDvalue[HITAG_CMD_BUFFER_SIZE]; //this can likely be a uint16_t instead of uint32_t + uint8_t CMDtype[HITAG_CMD_BUFFER_SIZE]; + uint16_t CMDlength; + uint16_t CMDposition; + uint16_t CMDsubposition; + bool CMDloop; + bool CMDactive; + uint32_t CMDcount; } LfRfidHitagCMDData; typedef struct { - uint32_t replyvalue[HITAG_CMD_BUFFER_SIZE]; //this can likely be a uint8_t instead of uint32_t - uint8_t replytype[HITAG_CMD_BUFFER_SIZE]; - uint16_t replylength; + uint32_t replyvalue[HITAG_CMD_BUFFER_SIZE]; //this can likely be a uint8_t instead of uint32_t + uint8_t replytype[HITAG_CMD_BUFFER_SIZE]; + uint16_t replylength; } LfRfidHitagReplyData; typedef struct { BufferStream* stream; VarintPair* pair; - uint32_t capCounter; - uint32_t prevTIMval; + uint32_t capCounter; + uint32_t prevTIMval; } LfRfidHitagCaptureData; - -typedef struct { - uint8_t pageData[HITAG_PAGES*HITAG_PAGEBYTES]; - uint8_t pageKnown[HITAG_PAGES]; - uint8_t tagData[HITAG_PAGES*HITAG_PAGEBYTES+HITAG_PAGES]; - uint8_t pageRW[HITAG_PAGES]; - uint8_t pagePublic[HITAG_PAGES]; +typedef struct { + uint8_t pageData[HITAG_PAGES * HITAG_PAGEBYTES]; + uint8_t pageKnown[HITAG_PAGES]; + uint8_t tagData[HITAG_PAGES * HITAG_PAGEBYTES + HITAG_PAGES]; + uint8_t pageRW[HITAG_PAGES]; + uint8_t pagePublic[HITAG_PAGES]; } LFRFIDHitag; // main worker struct LFRFIDHitagWorker { FuriThread* thread; FuriEventFlag* events; - uint8_t DMAeventCount; + uint8_t DMAeventCount; uint8_t carrierPrescaler; - - LFRFIDHitag* tag; - - LFRFIDHitagStatus workerstatus; - - ProtocolDict* dict; -}; + LFRFIDHitag* tag; + + LFRFIDHitagStatus workerstatus; + + ProtocolDict* dict; +}; //------------------------------------------------------------------ forward function definitions ------------------------------------------------------------------ @@ -163,50 +165,49 @@ static void lfrfid_hitag_worker_carrier_in_IC_mode(void* capture_context); LFRFIDHitagWorker* lfrfid_hitag_worker_alloc(ProtocolDict* dict) { furi_assert(dict); - - LFRFIDHitagWorker* worker = malloc(sizeof(LFRFIDHitagWorker)); - worker->dict = dict; - + + LFRFIDHitagWorker* worker = malloc(sizeof(LFRFIDHitagWorker)); + worker->dict = dict; + worker->thread = furi_thread_alloc_ex("LFRFIDHitagWorker", 2048, NULL, worker); - worker->events = furi_event_flag_alloc(NULL); - worker->DMAeventCount = 0; - + worker->events = furi_event_flag_alloc(NULL); + worker->DMAeventCount = 0; + worker->carrierPrescaler = 2; - - worker->workerstatus = LFRFIDHitagStatusScanning; - - - return worker; + + worker->workerstatus = LFRFIDHitagStatusScanning; + + return worker; } void lfrfid_hitag_worker_free(LFRFIDHitagWorker* worker) { - furi_thread_free(worker->thread); - furi_event_flag_free(worker->events); - free(worker); + furi_thread_free(worker->thread); + furi_event_flag_free(worker->events); + free(worker); } -LFRFIDHitagStatus lfrfid_hitag_worker_get_status(LFRFIDHitagWorker* worker){ - return worker->workerstatus; +LFRFIDHitagStatus lfrfid_hitag_worker_get_status(LFRFIDHitagWorker* worker) { + return worker->workerstatus; } - -void lfrfid_hitag_worker_start(LFRFIDHitagWorker* worker, LFRFIDHitagWorkerSetting setting) { - if(furi_thread_get_state(worker->thread) == FuriThreadStateStopped){ - switch(setting){ - case (LFRFIDHitagWorkerSettingRead): - DOLPHIN_DEED(DolphinDeedRfidRead); - furi_thread_set_callback(worker->thread, lfrfid_hitag_worker_read_thread); - break; - case (LFRFIDHitagWorkerSettingEmulate): - DOLPHIN_DEED(DolphinDeedRfidEmulate); - furi_thread_set_callback(worker->thread, lfrfid_hitag_worker_emulate_thread); - break; - default : //safety meassure, not to start a thread without a callback function - furi_thread_set_callback(worker->thread, lfrfid_hitag_worker_read_thread); - break; - } - furi_thread_start(worker->thread); - } + +void lfrfid_hitag_worker_start(LFRFIDHitagWorker* worker, LFRFIDHitagWorkerSetting setting) { + if(furi_thread_get_state(worker->thread) == FuriThreadStateStopped) { + switch(setting) { + case(LFRFIDHitagWorkerSettingRead): + dolphin_deed(DolphinDeedRfidRead); + furi_thread_set_callback(worker->thread, lfrfid_hitag_worker_read_thread); + break; + case(LFRFIDHitagWorkerSettingEmulate): + dolphin_deed(DolphinDeedRfidEmulate); + furi_thread_set_callback(worker->thread, lfrfid_hitag_worker_emulate_thread); + break; + default: //safety meassure, not to start a thread without a callback function + furi_thread_set_callback(worker->thread, lfrfid_hitag_worker_read_thread); + break; + } + furi_thread_start(worker->thread); + } } void lfrfid_hitag_worker_stop(LFRFIDHitagWorker* worker) { @@ -218,66 +219,67 @@ void lfrfid_hitag_worker_stop(LFRFIDHitagWorker* worker) { static void lfrfid_hitag_worker_capture_in_cc_isr(bool level, uint32_t duration, void* context) { LfRfidHitagCaptureData* capData = context; - - //check if there is a new pair available: pulse + period + + //check if there is a new pair available: pulse + period bool need_to_send = varint_pair_pack(capData->pair, level, duration); - - //if so put it on buffer stream so it can be processed outside this interrupt routine + + //if so put it on buffer stream so it can be processed outside this interrupt routine if(need_to_send) { buffer_stream_send_from_isr( - capData->stream, varint_pair_get_data(capData->pair), varint_pair_get_size(capData->pair)); + capData->stream, + varint_pair_get_data(capData->pair), + varint_pair_get_size(capData->pair)); varint_pair_reset(capData->pair); } - } //------------------------------------------------------------------ READ TAG: carrier out TIMER, DMA & interrupts functions ------------------------------------------------------------------ static void lfrfid_hitag_worker_carrier_out_dma_isr(void* dma_context) { - LFRFIDHitagWorker* worker = (LFRFIDHitagWorker*)dma_context; - worker->DMAeventCount++; - + LFRFIDHitagWorker* worker = (LFRFIDHitagWorker*)dma_context; + worker->DMAeventCount++; + if(LL_DMA_IsActiveFlag_HT1(DMA1)) { LL_DMA_ClearFlag_HT1(DMA1); //halfway through DMA buffer --> first half can be repopulated - //just signal that it can be repopulated, not execute the repopulating itself, since this is an interrupt, holding up all other stuff - furi_event_flag_set(worker->events, 1 << LFRFIDHitagWorkerSignalHalfTransfer); + //just signal that it can be repopulated, not execute the repopulating itself, since this is an interrupt, holding up all other stuff + furi_event_flag_set(worker->events, 1 << LFRFIDHitagWorkerSignalHalfTransfer); } if(LL_DMA_IsActiveFlag_TC1(DMA1)) { LL_DMA_ClearFlag_TC1(DMA1); //fully through DMA buffer --> second half can be repopulated - //just signal that it can be repopulated, not execute the repopulating itself, since this is an interrupt, holding up all other stuff - furi_event_flag_set(worker->events, 1 << LFRFIDHitagWorkerSignalTransferComplete); + //just signal that it can be repopulated, not execute the repopulating itself, since this is an interrupt, holding up all other stuff + furi_event_flag_set(worker->events, 1 << LFRFIDHitagWorkerSignalTransferComplete); } } void lfrfid_hitag_worker_carrier_out_start( - uint32_t* duration, + uint32_t* duration, uint32_t* pulse, size_t length, - void* context) { - + void* context) { // configure timer - FURI_CRITICAL_ENTER(); - LL_TIM_DeInit(CARRIER_OUT_TIMER); - FURI_CRITICAL_EXIT(); + furi_hal_bus_enable(CARRIER_OUT_TIMER_BUS); LL_TIM_InitTypeDef TIM_InitStruct = {0}; - TIM_InitStruct.Prescaler = SystemCoreClock / (125000 * HITAG_BASEPERIOD) - 1; // sets the basis TIMER frequency to 8*freq (1MHz) for the timer - TIM_InitStruct.Autoreload = HITAG_BASEPERIOD - 1; //initial PWM period =125kHz (ARR will be handled via DMA further on) + TIM_InitStruct.Prescaler = SystemCoreClock / (125000 * HITAG_BASEPERIOD) - + 1; // sets the basis TIMER frequency to 8*freq (1MHz) for the timer + TIM_InitStruct.Autoreload = + HITAG_BASEPERIOD - + 1; //initial PWM period =125kHz (ARR will be handled via DMA further on) LL_TIM_Init(CARRIER_OUT_TIMER, &TIM_InitStruct); LL_TIM_DisableARRPreload(CARRIER_OUT_TIMER); LL_TIM_OC_InitTypeDef TIM_OC_InitStruct = {0}; TIM_OC_InitStruct.OCMode = LL_TIM_OCMODE_PWM1; TIM_OC_InitStruct.OCNState = LL_TIM_OCSTATE_ENABLE; - TIM_OC_InitStruct.CompareValue = HITAG_BASEPERIOD/2; //initial pulse duration of half period ((CCR will be handled via DMA further on) - LL_TIM_OC_Init( - CARRIER_OUT_TIMER, CARRIER_OUT_TIMER_CHANNEL, &TIM_OC_InitStruct); + TIM_OC_InitStruct.CompareValue = + HITAG_BASEPERIOD / + 2; //initial pulse duration of half period ((CCR will be handled via DMA further on) + LL_TIM_OC_Init(CARRIER_OUT_TIMER, CARRIER_OUT_TIMER_CHANNEL, &TIM_OC_InitStruct); - LL_TIM_OC_SetPolarity( - CARRIER_OUT_TIMER, CARRIER_OUT_TIMER_CHANNEL, LL_TIM_OCPOLARITY_HIGH); + LL_TIM_OC_SetPolarity(CARRIER_OUT_TIMER, CARRIER_OUT_TIMER_CHANNEL, LL_TIM_OCPOLARITY_HIGH); LL_TIM_EnableDMAReq_UPDATE(CARRIER_OUT_TIMER); // configure DMA "mem -> ARR" channel @@ -311,8 +313,9 @@ void lfrfid_hitag_worker_carrier_out_start( LL_DMA_Init(DMA1, LL_DMA_CHANNEL_2, &dma_config); LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2); - // attach interrupt to one of DMA channels - furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch1, lfrfid_hitag_worker_carrier_out_dma_isr, context); + // attach interrupt to one of DMA channels + furi_hal_interrupt_set_isr( + FuriHalInterruptIdDma1Ch1, lfrfid_hitag_worker_carrier_out_dma_isr, context); LL_DMA_EnableIT_TC(DMA1, LL_DMA_CHANNEL_1); LL_DMA_EnableIT_HT(DMA1, LL_DMA_CHANNEL_1); @@ -324,7 +327,7 @@ void lfrfid_hitag_worker_carrier_out_start( } void lfrfid_hitag_worker_carrier_out_stop() { - LL_TIM_DisableCounter(CARRIER_OUT_TIMER); + LL_TIM_DisableCounter(CARRIER_OUT_TIMER); LL_TIM_DisableAllOutputs(CARRIER_OUT_TIMER); furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch1, NULL, NULL); @@ -335,77 +338,77 @@ void lfrfid_hitag_worker_carrier_out_stop() { LL_DMA_DeInit(DMA1, LL_DMA_CHANNEL_1); LL_DMA_DeInit(DMA1, LL_DMA_CHANNEL_2); - LL_TIM_DeInit(CARRIER_OUT_TIMER); + + furi_hal_bus_disable(CARRIER_OUT_TIMER_BUS); FURI_CRITICAL_EXIT(); } //------------------------------------------------------------------ EMULATE TAG: pull out TIMER, DMA & interrupts functions ------------------------------------------------------------------ -static void lfrfid_hitag_worker_pull_out_dma_stop(void* capture_context){ - //reconfigure pull_out pin to fixed low state - //via forced OC INACTIVE MODE - LL_TIM_OC_SetMode(PULL_OUT_TIMER, PULL_OUT_TIMER_CHANNEL, LL_TIM_OCMODE_FORCED_INACTIVE); - - //disable DMA interrupts - //furi_hal_interrupt_set_isr(PULL_OUT_DMA_CH1_IRQ, NULL, NULL); - //LL_DMA_DisableIT_HT(PULL_OUT_DMA_CH1_DEF); - //LL_DMA_DisableIT_TC(PULL_OUT_DMA_CH1_DEF); - - //disable DMA channels & requests - LL_TIM_DisableDMAReq_UPDATE(PULL_OUT_TIMER); - LL_DMA_DisableChannel(PULL_OUT_DMA_CH1_DEF); //need to disable when using normal mode? - LL_DMA_DisableChannel(PULL_OUT_DMA_CH2_DEF); //need to disable when using normal mode? +static void lfrfid_hitag_worker_pull_out_dma_stop(void* capture_context) { + //reconfigure pull_out pin to fixed low state + //via forced OC INACTIVE MODE + LL_TIM_OC_SetMode(PULL_OUT_TIMER, PULL_OUT_TIMER_CHANNEL, LL_TIM_OCMODE_FORCED_INACTIVE); + + //disable DMA channels & requests + LL_TIM_DisableDMAReq_UPDATE(PULL_OUT_TIMER); + LL_DMA_DisableChannel(PULL_OUT_DMA_CH1_DEF); //need to disable when using normal mode? + LL_DMA_DisableChannel(PULL_OUT_DMA_CH2_DEF); //need to disable when using normal mode? //LL_TIM_DisableAllOutputs(PULL_OUT_TIMER); //no need to disable when reconfiguring pin? - - //for logging both ccr & arr times during emulate (iso only arr) - LL_TIM_DisableIT_CC3(PULL_OUT_TIMER); - - //switch carrier detection back to input capture for stable readings (required for command detection) - lfrfid_hitag_worker_carrier_in_IC_mode(capture_context); -} + + //for logging both ccr & arr times during emulate (iso only arr) + LL_TIM_DisableIT_CC3(PULL_OUT_TIMER); + + //switch carrier detection back to input capture for stable readings (required for command detection) + lfrfid_hitag_worker_carrier_in_IC_mode(capture_context); +} static void lfrfid_hitag_worker_pull_out_dma_isr(void* capture_context) { - - /* currently no HT interrupt enabled, only TC + /* currently no HT interrupt enabled, only TC if(LL_DMA_IsActiveFlag_HT1(PULL_OUT_DMA)) { LL_DMA_ClearFlag_HT1(PULL_OUT_DMA); //lfrfid_hitag_worker_pull_out_dma_stop(); } */ - + if(LL_DMA_IsActiveFlag_TC1(PULL_OUT_DMA)) { LL_DMA_ClearFlag_TC1(PULL_OUT_DMA); lfrfid_hitag_worker_pull_out_dma_stop(capture_context); } } -static void lfrfid_hitag_worker_pull_out_dma_start(size_t length){ - - //array pointers remain the same, but length changes so - //reset DMA length (only possible when DMA channel is disabled - //this also resets the DMA counter (the DMA length is the 'remaining counter' - LL_DMA_SetDataLength(PULL_OUT_DMA_CH1_DEF, length); +static void lfrfid_hitag_worker_pull_out_dma_start(size_t length) { + //array pointers remain the same, but length changes so + //reset DMA length (only possible when DMA channel is disabled + //this also resets the DMA counter (the DMA length is the 'remaining counter' + LL_DMA_SetDataLength(PULL_OUT_DMA_CH1_DEF, length); LL_DMA_SetDataLength(PULL_OUT_DMA_CH2_DEF, length); - - //enable DMA - LL_DMA_EnableChannel(PULL_OUT_DMA_CH1_DEF); - LL_DMA_EnableChannel(PULL_OUT_DMA_CH2_DEF); - - //set OC to PWM1 mode - LL_TIM_OC_SetMode(PULL_OUT_TIMER, PULL_OUT_TIMER_CHANNEL, LL_TIM_OCMODE_PWM1); //during emulate (dma controlled) mode - - //only enable DMA requests on timer update after enabling both channels (otherwise you risk that first DMA request only triggers one of the channels) - LL_TIM_EnableDMAReq_UPDATE(PULL_OUT_TIMER); - - //for logging both ccr & arr times during emulate (iso only arr) - LL_TIM_EnableIT_CC3(PULL_OUT_TIMER); + + //enable DMA + LL_DMA_EnableChannel(PULL_OUT_DMA_CH1_DEF); + LL_DMA_EnableChannel(PULL_OUT_DMA_CH2_DEF); + + //set OC to PWM1 mode + LL_TIM_OC_SetMode( + PULL_OUT_TIMER, + PULL_OUT_TIMER_CHANNEL, + LL_TIM_OCMODE_PWM1); //during emulate (dma controlled) mode + + //only enable DMA requests on timer update after enabling both channels (otherwise you risk that first DMA request only triggers one of the channels) + LL_TIM_EnableDMAReq_UPDATE(PULL_OUT_TIMER); + + //for logging both ccr & arr times during emulate (iso only arr) + LL_TIM_EnableIT_CC3(PULL_OUT_TIMER); } -static void lfrfid_hitag_worker_pull_out_dma_setup(uint32_t* duration, uint32_t* pulse, size_t length, void* capture_context){ - - //DMA setup - LL_TIM_DisableDMAReq_UPDATE(PULL_OUT_TIMER);//start with DMA requests disabled +static void lfrfid_hitag_worker_pull_out_dma_setup( + uint32_t* duration, + uint32_t* pulse, + size_t length, + void* capture_context) { + //DMA setup + LL_TIM_DisableDMAReq_UPDATE(PULL_OUT_TIMER); //start with DMA requests disabled // configure DMA "mem -> ARR" channel LL_DMA_InitTypeDef dma_config = {0}; @@ -413,7 +416,7 @@ static void lfrfid_hitag_worker_pull_out_dma_setup(uint32_t* duration, uint32_t* dma_config.MemoryOrM2MDstAddress = (uint32_t)duration; dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; //dma_config.Mode = LL_DMA_MODE_CIRCULAR; //keep cycling through memory array - dma_config.Mode = LL_DMA_MODE_NORMAL; //cycle only once through memory array + dma_config.Mode = LL_DMA_MODE_NORMAL; //cycle only once through memory array dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; @@ -422,13 +425,13 @@ static void lfrfid_hitag_worker_pull_out_dma_setup(uint32_t* duration, uint32_t* dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; dma_config.Priority = LL_DMA_MODE_NORMAL; LL_DMA_Init(PULL_OUT_DMA_CH1_DEF, &dma_config); - + // configure DMA "mem -> CCR3" channel dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (PULL_OUT_TIMER->CCR3); dma_config.MemoryOrM2MDstAddress = (uint32_t)pulse; dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; //dma_config.Mode = LL_DMA_MODE_CIRCULAR; //keep cycling through memory array - dma_config.Mode = LL_DMA_MODE_NORMAL; //cycle only once through memory array + dma_config.Mode = LL_DMA_MODE_NORMAL; //cycle only once through memory array dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; @@ -437,193 +440,218 @@ static void lfrfid_hitag_worker_pull_out_dma_setup(uint32_t* duration, uint32_t* dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; dma_config.Priority = LL_DMA_MODE_NORMAL; LL_DMA_Init(PULL_OUT_DMA_CH2_DEF, &dma_config); - - // enable DMA interrupts - furi_hal_interrupt_set_isr(PULL_OUT_DMA_CH1_IRQ, lfrfid_hitag_worker_pull_out_dma_isr, capture_context); + + // enable DMA interrupts + furi_hal_interrupt_set_isr( + PULL_OUT_DMA_CH1_IRQ, lfrfid_hitag_worker_pull_out_dma_isr, capture_context); + //LL_DMA_EnableIT_HT(PULL_OUT_DMA_CH1_DEF); //let's try normal DMA mode and only transfer complete interrupt - LL_DMA_EnableIT_TC(PULL_OUT_DMA_CH1_DEF); - + LL_DMA_EnableIT_TC(PULL_OUT_DMA_CH1_DEF); } - //------------------------------------------------------------------ EMULATE TAG: carrier input TIMER & interrupt functions (switching input capture & ETR)------------------------------------------------------------------ static void lfrfid_hitag_worker_carrier_in_isr(void* capture_context) { if(LL_TIM_IsActiveFlag_CC1(CARRIER_IN_TIMER)) { uint32_t newTIMval = LL_TIM_IC_GetCaptureCH1(CARRIER_IN_TIMER); - LL_TIM_ClearFlag_CC1(CARRIER_IN_TIMER); - - //ARR for TIM2 in input capture mode is UINT32_MAX so at freq of 1MHz this is 70+ minutes, instead of resetting counter, just store prev value and take difference of both - //this will increase measurement accuracy, since both values are then purely hardware controlled, no dependency of interrupt timing to (re)set any timer values - //LL_TIM_SetCounter(CARRIER_IN_TIMER, 0); - - LfRfidHitagCaptureData* capData = capture_context; - capData->capCounter++; - //pack varint (as part of a pair) - //cleaner would be to pack as a varint iso varint_pair, though i'm not sure if I can get the data & size of a packed varint - varint_pair_pack(capData->pair, true, newTIMval - capData->prevTIMval); - //but send anyhow, doesn't matter that it's not a pair - buffer_stream_send_from_isr(capData->stream, varint_pair_get_data(capData->pair), varint_pair_get_size(capData->pair)); - varint_pair_reset(capData->pair); - - capData->prevTIMval = newTIMval; - } - else if(LL_TIM_IsActiveFlag_UPDATE(CARRIER_IN_TIMER)) { + LL_TIM_ClearFlag_CC1(CARRIER_IN_TIMER); + + //ARR for TIM2 in input capture mode is UINT32_MAX so at freq of 1MHz this is 70+ minutes, instead of resetting counter, just store prev value and take difference of both + //this will increase measurement accuracy, since both values are then purely hardware controlled, no dependency of interrupt timing to (re)set any timer values + //LL_TIM_SetCounter(CARRIER_IN_TIMER, 0); + + LfRfidHitagCaptureData* capData = capture_context; + capData->capCounter++; + //pack varint (as part of a pair) + //cleaner would be to pack as a varint iso varint_pair, though i'm not sure if I can get the data & size of a packed varint + varint_pair_pack(capData->pair, true, newTIMval - capData->prevTIMval); + //but send anyhow, doesn't matter that it's not a pair + buffer_stream_send_from_isr( + capData->stream, + varint_pair_get_data(capData->pair), + varint_pair_get_size(capData->pair)); + varint_pair_reset(capData->pair); + + capData->prevTIMval = newTIMval; + } else if(LL_TIM_IsActiveFlag_UPDATE(CARRIER_IN_TIMER)) { uint32_t newTIMval = LL_TIM_GetCounter(CARRIER_IN_REFERENCE_TIMER); - LL_TIM_ClearFlag_UPDATE(CARRIER_IN_TIMER); - - //ARR for ref timer in ETR mode is UINT32_MAX so at freq of 1MHz this is 70+ minutes, instead of resetting counter, just store prev value and take difference of both - //this will increase measurement accuracy, since both values are then purely hardware controlled, no dependency of interrupt timing to (re)set any timer values + LL_TIM_ClearFlag_UPDATE(CARRIER_IN_TIMER); + + //ARR for ref timer in ETR mode is UINT32_MAX so at freq of 1MHz this is 70+ minutes, instead of resetting counter, just store prev value and take difference of both + //this will increase measurement accuracy, since both values are then purely hardware controlled, no dependency of interrupt timing to (re)set any timer values LL_TIM_SetCounter(CARRIER_IN_REFERENCE_TIMER, 0); - - LfRfidHitagCaptureData* capData = capture_context; - capData->capCounter++; - //pack varint (as part of a pair) - //cleaner would be to pack as a varint iso varint_pair, though i'm not sure if I can get the data & size of a packed varint - varint_pair_pack(capData->pair, true, newTIMval);// - capData->prevTIMval); - //but send anyhow, doesn't matter that it's not a pair - buffer_stream_send_from_isr(capData->stream, varint_pair_get_data(capData->pair), varint_pair_get_size(capData->pair)); - varint_pair_reset(capData->pair); - - //capData->prevTIMval = newTIMval; + + LfRfidHitagCaptureData* capData = capture_context; + capData->capCounter++; + //pack varint (as part of a pair) + //cleaner would be to pack as a varint iso varint_pair, though i'm not sure if I can get the data & size of a packed varint + varint_pair_pack(capData->pair, true, newTIMval); // - capData->prevTIMval); + //but send anyhow, doesn't matter that it's not a pair + buffer_stream_send_from_isr( + capData->stream, + varint_pair_get_data(capData->pair), + varint_pair_get_size(capData->pair)); + varint_pair_reset(capData->pair); + + //capData->prevTIMval = newTIMval; } else if(LL_TIM_IsActiveFlag_CC3(CARRIER_IN_TIMER)) { uint32_t newTIMval = LL_TIM_GetCounter(CARRIER_IN_REFERENCE_TIMER); - LL_TIM_ClearFlag_CC3(CARRIER_IN_TIMER); - - //don't reset timer for the ccr (HIGH) timing, this way the arr (duration) timing is still full duraton timing (iso only LOW timing) - //LL_TIM_SetCounter(CARRIER_IN_REFERENCE_TIMER, 0); - - LfRfidHitagCaptureData* capData = capture_context; - capData->capCounter++; - //pack varint (as part of a pair) - //cleaner would be to pack as a varint iso varint_pair, though i'm not sure if I can get the data & size of a packed varint - varint_pair_pack(capData->pair, true, newTIMval);// - capData->prevTIMval); - //but send anyhow, doesn't matter that it's not a pair - buffer_stream_send_from_isr(capData->stream, varint_pair_get_data(capData->pair), varint_pair_get_size(capData->pair)); - varint_pair_reset(capData->pair); - - //capData->prevTIMval = newTIMval; + LL_TIM_ClearFlag_CC3(CARRIER_IN_TIMER); + + //don't reset timer for the ccr (HIGH) timing, this way the arr (duration) timing is still full duraton timing (iso only LOW timing) + //LL_TIM_SetCounter(CARRIER_IN_REFERENCE_TIMER, 0); + + LfRfidHitagCaptureData* capData = capture_context; + capData->capCounter++; + //pack varint (as part of a pair) + //cleaner would be to pack as a varint iso varint_pair, though i'm not sure if I can get the data & size of a packed varint + varint_pair_pack(capData->pair, true, newTIMval); // - capData->prevTIMval); + //but send anyhow, doesn't matter that it's not a pair + buffer_stream_send_from_isr( + capData->stream, + varint_pair_get_data(capData->pair), + varint_pair_get_size(capData->pair)); + varint_pair_reset(capData->pair); + + //capData->prevTIMval = newTIMval; } } -static void lfrfid_hitag_worker_carrier_in_ETR_mode(void* capture_context, uint8_t ext_prescaler){ - //disable counters temporarily - LL_TIM_DisableCounter(CARRIER_IN_TIMER); - LL_TIM_DisableCounter(CARRIER_IN_REFERENCE_TIMER); - - //disable Input capture & related interrupt - LL_TIM_DisableIT_CC1(CARRIER_IN_TIMER); - LL_TIM_CC_DisableChannel(CARRIER_IN_TIMER, CARRIER_IN_TIMER_DIR_CH); - - //reset capdata prev value - LfRfidHitagCaptureData* capData = capture_context; - capData->prevTIMval = 0; - - //switch clocksource to ETR with external prescaling via ARR - LL_TIM_SetPrescaler(CARRIER_IN_TIMER, 1-1);//prescaler is only applied at next update event - LL_TIM_GenerateEvent_UPDATE(CARRIER_IN_TIMER); //prescaler is only applied at next update event //TODO: how to prevent this from creating a capdata entry - LL_TIM_SetAutoReload(CARRIER_IN_TIMER, ext_prescaler-1); - LL_TIM_SetClockSource(CARRIER_IN_TIMER, LL_TIM_CLOCKSOURCE_EXT_MODE2); - - //reconfigure carrier_in pin to TIM2 ETR - furi_hal_gpio_init_ex(&gpio_rfid_carrier, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedLow, GpioAltFn2TIM2); - - //reset timer counter & capture context for period calculation - LL_TIM_SetCounter(CARRIER_IN_TIMER, 0); - LL_TIM_SetCounter(CARRIER_IN_REFERENCE_TIMER, 0); - - //enable interrupt via update - LL_TIM_EnableIT_UPDATE(CARRIER_IN_TIMER); - - //re-enable counters +static void lfrfid_hitag_worker_carrier_in_ETR_mode(void* capture_context, uint8_t ext_prescaler) { + //disable counters temporarily + LL_TIM_DisableCounter(CARRIER_IN_TIMER); + LL_TIM_DisableCounter(CARRIER_IN_REFERENCE_TIMER); + + //disable Input capture & related interrupt + LL_TIM_DisableIT_CC1(CARRIER_IN_TIMER); + LL_TIM_CC_DisableChannel(CARRIER_IN_TIMER, CARRIER_IN_TIMER_DIR_CH); + + //reset capdata prev value + LfRfidHitagCaptureData* capData = capture_context; + capData->prevTIMval = 0; + + //switch clocksource to ETR with external prescaling via ARR + LL_TIM_SetPrescaler(CARRIER_IN_TIMER, 1 - 1); //prescaler is only applied at next update event + LL_TIM_GenerateEvent_UPDATE( + CARRIER_IN_TIMER); //prescaler is only applied at next update event //TODO: how to prevent this from creating a capdata entry + LL_TIM_SetAutoReload(CARRIER_IN_TIMER, ext_prescaler - 1); + LL_TIM_SetClockSource(CARRIER_IN_TIMER, LL_TIM_CLOCKSOURCE_EXT_MODE2); + + //reconfigure carrier_in pin to TIM2 ETR + furi_hal_gpio_init_ex( + &gpio_rfid_carrier, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedLow, GpioAltFn2TIM2); + + //reset timer counter & capture context for period calculation + LL_TIM_SetCounter(CARRIER_IN_TIMER, 0); + LL_TIM_SetCounter(CARRIER_IN_REFERENCE_TIMER, 0); + + //enable interrupt via update + LL_TIM_EnableIT_UPDATE(CARRIER_IN_TIMER); + + //re-enable counters LL_TIM_EnableCounter(CARRIER_IN_TIMER); - LL_TIM_EnableCounter(CARRIER_IN_REFERENCE_TIMER); + LL_TIM_EnableCounter(CARRIER_IN_REFERENCE_TIMER); } -static void lfrfid_hitag_worker_carrier_in_IC_mode(void* capture_context){ - //disable counters temporarily - LL_TIM_DisableCounter(CARRIER_IN_TIMER); - LL_TIM_DisableCounter(CARRIER_IN_REFERENCE_TIMER); - - //disable interrupt via update - LL_TIM_DisableIT_UPDATE(CARRIER_IN_TIMER); - - //reset capdata prev value - LfRfidHitagCaptureData* capData = capture_context; - capData->prevTIMval = 0; - - //switch clocksource to system/64, external prescaling is handled in IC prescaler - LL_TIM_SetClockSource(CARRIER_IN_TIMER, LL_TIM_CLOCKSOURCE_INTERNAL); - LL_TIM_SetPrescaler(CARRIER_IN_TIMER, 64-1); //prescaler is only applied at next update event - LL_TIM_GenerateEvent_UPDATE(CARRIER_IN_TIMER); //prescaler is only applied at next update event //TODO: how to prevent this from creating a capdata entry - LL_TIM_SetAutoReload(CARRIER_IN_TIMER, UINT32_MAX); - - //reconfigure carrier_in pin to TIM2 CH1 for input capture - furi_hal_gpio_init_ex(&gpio_rfid_carrier, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedLow, GpioAltFn1TIM2); - - //reset timer counter & capture context for period calculation - LL_TIM_SetCounter(CARRIER_IN_TIMER, 0); - LL_TIM_SetCounter(CARRIER_IN_REFERENCE_TIMER, 0); - - //enable Input capture & related interrupt - LL_TIM_EnableIT_CC1(CARRIER_IN_TIMER); - LL_TIM_CC_EnableChannel(CARRIER_IN_TIMER, CARRIER_IN_TIMER_DIR_CH); - - //re-enable counters +static void lfrfid_hitag_worker_carrier_in_IC_mode(void* capture_context) { + //disable counters temporarily + LL_TIM_DisableCounter(CARRIER_IN_TIMER); + LL_TIM_DisableCounter(CARRIER_IN_REFERENCE_TIMER); + + //disable interrupt via update + LL_TIM_DisableIT_UPDATE(CARRIER_IN_TIMER); + + //reset capdata prev value + LfRfidHitagCaptureData* capData = capture_context; + capData->prevTIMval = 0; + + //switch clocksource to system/64, external prescaling is handled in IC prescaler + LL_TIM_SetClockSource(CARRIER_IN_TIMER, LL_TIM_CLOCKSOURCE_INTERNAL); + LL_TIM_SetPrescaler(CARRIER_IN_TIMER, 64 - 1); //prescaler is only applied at next update event + LL_TIM_GenerateEvent_UPDATE( + CARRIER_IN_TIMER); //prescaler is only applied at next update event //TODO: how to prevent this from creating a capdata entry + LL_TIM_SetAutoReload(CARRIER_IN_TIMER, UINT32_MAX); + + //reconfigure carrier_in pin to TIM2 CH1 for input capture + furi_hal_gpio_init_ex( + &gpio_rfid_carrier, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedLow, GpioAltFn1TIM2); + + //reset timer counter & capture context for period calculation + LL_TIM_SetCounter(CARRIER_IN_TIMER, 0); + LL_TIM_SetCounter(CARRIER_IN_REFERENCE_TIMER, 0); + + //enable Input capture & related interrupt + LL_TIM_EnableIT_CC1(CARRIER_IN_TIMER); + LL_TIM_CC_EnableChannel(CARRIER_IN_TIMER, CARRIER_IN_TIMER_DIR_CH); + + //re-enable counters LL_TIM_EnableCounter(CARRIER_IN_TIMER); - LL_TIM_EnableCounter(CARRIER_IN_REFERENCE_TIMER); + LL_TIM_EnableCounter(CARRIER_IN_REFERENCE_TIMER); } -static void lfrfid_hitag_worker_carrier_in_start(void* capture_context, uint8_t ext_prescaler, uint32_t* duration, uint32_t* pulse, size_t length) { - FURI_CRITICAL_ENTER(); - LL_DMA_DeInit(PULL_OUT_DMA_CH1_DEF); //required? - LL_DMA_DeInit(PULL_OUT_DMA_CH2_DEF); //required? - LL_TIM_DeInit(CARRIER_IN_TIMER); - LL_TIM_DeInit(CARRIER_IN_REFERENCE_TIMER); +static void lfrfid_hitag_worker_carrier_in_start( + void* capture_context, + uint8_t ext_prescaler, + uint32_t* duration, + uint32_t* pulse, + size_t length) { + FURI_CRITICAL_ENTER(); + LL_DMA_DeInit(PULL_OUT_DMA_CH1_DEF); //required? + LL_DMA_DeInit(PULL_OUT_DMA_CH2_DEF); //required? FURI_CRITICAL_EXIT(); - - //setup reference timer: simple setup with base freq of 1MHz and max autoreload - LL_TIM_InitTypeDef TIM_InitStruct_Ref = {0}; - TIM_InitStruct_Ref.Prescaler = 64 - 1; //system base freq is 64MHz, so this sets base freq for TIM to 1MHz (aka 1us period) - TIM_InitStruct_Ref.CounterMode = LL_TIM_COUNTERMODE_UP; + + furi_hal_bus_enable(CARRIER_IN_TIMER_BUS); + furi_hal_bus_enable(CARRIER_IN_REFERENCE_TIMER_BUS); + + //setup reference timer: simple setup with base freq of 1MHz and max autoreload + LL_TIM_InitTypeDef TIM_InitStruct_Ref = {0}; + TIM_InitStruct_Ref.Prescaler = + 64 - + 1; //system base freq is 64MHz, so this sets base freq for TIM to 1MHz (aka 1us period) + TIM_InitStruct_Ref.CounterMode = LL_TIM_COUNTERMODE_UP; TIM_InitStruct_Ref.Autoreload = UINT32_MAX; TIM_InitStruct_Ref.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1; LL_TIM_Init(CARRIER_IN_REFERENCE_TIMER, &TIM_InitStruct_Ref); - - //setup carrier in timer for input capture + + //setup carrier in timer for input capture LL_TIM_InitTypeDef TIM_InitStruct = {0}; - TIM_InitStruct.Prescaler = 64 - 1; //system base freq is 64MHz, so this sets base freq for TIM to 1MHz (aka 1us period) + TIM_InitStruct.Prescaler = + 64 - + 1; //system base freq is 64MHz, so this sets base freq for TIM to 1MHz (aka 1us period) TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP; TIM_InitStruct.Autoreload = UINT32_MAX; TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1; LL_TIM_Init(CARRIER_IN_TIMER, &TIM_InitStruct); - LL_TIM_DisableARRPreload(CARRIER_IN_TIMER); - LL_TIM_SetClockSource(CARRIER_IN_TIMER, LL_TIM_CLOCKSOURCE_INTERNAL); //default is internal, so likely not required + LL_TIM_DisableARRPreload(CARRIER_IN_TIMER); + LL_TIM_SetClockSource( + CARRIER_IN_TIMER, + LL_TIM_CLOCKSOURCE_INTERNAL); //default is internal, so likely not required LL_TIM_DisableDMAReq_TRIG(CARRIER_IN_TIMER); LL_TIM_DisableIT_TRIG(CARRIER_IN_TIMER); - - //meanwhile already prepre the ETR - LL_TIM_ConfigETR( + + //meanwhile already prepre the ETR + LL_TIM_ConfigETR( CARRIER_IN_TIMER, LL_TIM_ETR_POLARITY_INVERTED, LL_TIM_ETR_PRESCALER_DIV1, LL_TIM_ETR_FILTER_FDIV1); - - //INPUT CAPTURE SETUP - // Timer: channel 1 direct (from GPIO) - LL_TIM_IC_SetActiveInput(CARRIER_IN_TIMER, CARRIER_IN_TIMER_DIR_CH, LL_TIM_ACTIVEINPUT_DIRECTTI); - //prescaling direct channel seems to be working fine (and is necessary since otherwise sd write cannot keep up) - if (ext_prescaler==4){ - LL_TIM_IC_SetPrescaler(CARRIER_IN_TIMER, CARRIER_IN_TIMER_DIR_CH, LL_TIM_ICPSC_DIV4); - } else if (ext_prescaler==2){ - LL_TIM_IC_SetPrescaler(CARRIER_IN_TIMER, CARRIER_IN_TIMER_DIR_CH, LL_TIM_ICPSC_DIV2); - } else { - LL_TIM_IC_SetPrescaler(CARRIER_IN_TIMER, CARRIER_IN_TIMER_DIR_CH, LL_TIM_ICPSC_DIV1); + + //INPUT CAPTURE SETUP + // Timer: channel 1 direct (from GPIO) + LL_TIM_IC_SetActiveInput( + CARRIER_IN_TIMER, CARRIER_IN_TIMER_DIR_CH, LL_TIM_ACTIVEINPUT_DIRECTTI); + //prescaling direct channel seems to be working fine (and is necessary since otherwise sd write cannot keep up) + if(ext_prescaler == 4) { + LL_TIM_IC_SetPrescaler(CARRIER_IN_TIMER, CARRIER_IN_TIMER_DIR_CH, LL_TIM_ICPSC_DIV4); + } else if(ext_prescaler == 2) { + LL_TIM_IC_SetPrescaler(CARRIER_IN_TIMER, CARRIER_IN_TIMER_DIR_CH, LL_TIM_ICPSC_DIV2); + } else { + LL_TIM_IC_SetPrescaler(CARRIER_IN_TIMER, CARRIER_IN_TIMER_DIR_CH, LL_TIM_ICPSC_DIV1); } - LL_TIM_IC_SetPolarity(CARRIER_IN_TIMER, CARRIER_IN_TIMER_DIR_CH, LL_TIM_IC_POLARITY_RISING); + LL_TIM_IC_SetPolarity(CARRIER_IN_TIMER, CARRIER_IN_TIMER_DIR_CH, LL_TIM_IC_POLARITY_RISING); LL_TIM_IC_SetFilter(CARRIER_IN_TIMER, CARRIER_IN_TIMER_DIR_CH, LL_TIM_IC_FILTER_FDIV1); - /* Timer: channel 2 indirect (from channel 1) + /* Timer: channel 2 indirect (from channel 1) // only measure & write to file the period (not the on & off cycle), this reduced CPU load on SD write to (hopefully) keep up with period measurements without prescaler LL_TIM_IC_SetActiveInput(CARRIER_IN_TIMER, CARRIER_IN_TIMER_IND_CH, LL_TIM_ACTIVEINPUT_INDIRECTTI); //presaling indirect channel doesn't really work well yet :/ @@ -631,179 +659,196 @@ static void lfrfid_hitag_worker_carrier_in_start(void* capture_context, uint8_t LL_TIM_IC_SetPolarity(CARRIER_IN_TIMER, CARRIER_IN_TIMER_IND_CH, LL_TIM_IC_POLARITY_FALLING); LL_TIM_IC_SetFilter(CARRIER_IN_TIMER, CARRIER_IN_TIMER_IND_CH, LL_TIM_IC_FILTER_FDIV1); */ - - //set interrupt callback for capturing period - LL_TIM_EnableIT_CC1(CARRIER_IN_TIMER); + + //set interrupt callback for capturing period + LL_TIM_EnableIT_CC1(CARRIER_IN_TIMER); //LL_TIM_EnableIT_CC2(CARRIER_IN_TIMER); - furi_hal_interrupt_set_isr(FuriHalInterruptIdTIM2, lfrfid_hitag_worker_carrier_in_isr, capture_context); - - //OUTPUT COMPARE SETUP - LL_TIM_OC_InitTypeDef TIM_OC_InitStruct = {0}; + furi_hal_interrupt_set_isr( + FuriHalInterruptIdTIM2, lfrfid_hitag_worker_carrier_in_isr, capture_context); + + //OUTPUT COMPARE SETUP + LL_TIM_OC_InitTypeDef TIM_OC_InitStruct = {0}; //TIM_OC_InitStruct.OCMode = LL_TIM_OCMODE_PWM1; //during emulate (dma controlled) mode - TIM_OC_InitStruct.OCMode = LL_TIM_OCMODE_FORCED_INACTIVE; //during carrier in put output to forced low state + TIM_OC_InitStruct.OCMode = + LL_TIM_OCMODE_FORCED_INACTIVE; //during carrier in put output to forced low state TIM_OC_InitStruct.OCState = LL_TIM_OCSTATE_ENABLE; - TIM_OC_InitStruct.CompareValue = 0; //0% this should have almost similar effect as keeping output forced inactive (there's still some micropulse emited, going high at ARR and immediately down again at CCR value) + TIM_OC_InitStruct.CompareValue = + 0; //0% this should have almost similar effect as keeping output forced inactive (there's still some micropulse emited, going high at ARR and immediately down again at CCR value) LL_TIM_OC_Init(PULL_OUT_TIMER, PULL_OUT_TIMER_CHANNEL, &TIM_OC_InitStruct); - LL_TIM_OC_SetPolarity( - PULL_OUT_TIMER, PULL_OUT_TIMER_CHANNEL, LL_TIM_OCPOLARITY_HIGH); //active high (gpio goes high when pulse is high) - - //INIT DMA (do not start it yet) - lfrfid_hitag_worker_pull_out_dma_setup(duration, pulse, length, capture_context); - - LL_TIM_CC_EnableChannel(CARRIER_IN_TIMER, CARRIER_IN_TIMER_DIR_CH); + LL_TIM_OC_SetPolarity( + PULL_OUT_TIMER, + PULL_OUT_TIMER_CHANNEL, + LL_TIM_OCPOLARITY_HIGH); //active high (gpio goes high when pulse is high) + + //INIT DMA (do not start it yet) + lfrfid_hitag_worker_pull_out_dma_setup(duration, pulse, length, capture_context); + + LL_TIM_CC_EnableChannel(CARRIER_IN_TIMER, CARRIER_IN_TIMER_DIR_CH); //LL_TIM_CC_EnableChannel(CARRIER_IN_TIMER, CARRIER_IN_TIMER_IND_CH); - LL_TIM_CC_EnableChannel(PULL_OUT_TIMER, PULL_OUT_TIMER_CHANNEL); + LL_TIM_CC_EnableChannel(PULL_OUT_TIMER, PULL_OUT_TIMER_CHANNEL); LL_TIM_SetCounter(CARRIER_IN_TIMER, 0); - LL_TIM_SetCounter(CARRIER_IN_REFERENCE_TIMER, 0); + LL_TIM_SetCounter(CARRIER_IN_REFERENCE_TIMER, 0); LL_TIM_EnableCounter(CARRIER_IN_TIMER); - LL_TIM_EnableCounter(CARRIER_IN_REFERENCE_TIMER); + LL_TIM_EnableCounter(CARRIER_IN_REFERENCE_TIMER); } void lfrfid_hitag_worker_carrier_in_stop() { furi_hal_interrupt_set_isr(FuriHalInterruptIdTIM2, NULL, NULL); - - LL_TIM_DisableCounter(CARRIER_IN_TIMER); - LL_TIM_DisableCounter(CARRIER_IN_REFERENCE_TIMER); - LL_TIM_DisableAllOutputs(CARRIER_IN_TIMER); //used for pull pin OC - - FURI_CRITICAL_ENTER(); - LL_DMA_DeInit(PULL_OUT_DMA_CH1_DEF); + furi_hal_interrupt_set_isr(PULL_OUT_DMA_CH1_IRQ, NULL, NULL); + + LL_TIM_DisableCounter(CARRIER_IN_TIMER); + LL_TIM_DisableCounter(CARRIER_IN_REFERENCE_TIMER); + LL_TIM_DisableAllOutputs(CARRIER_IN_TIMER); //used for pull pin OC + + FURI_CRITICAL_ENTER(); + LL_DMA_DeInit(PULL_OUT_DMA_CH1_DEF); LL_DMA_DeInit(PULL_OUT_DMA_CH2_DEF); - LL_TIM_DeInit(CARRIER_IN_TIMER); - LL_TIM_DeInit(CARRIER_IN_REFERENCE_TIMER); + + furi_hal_bus_disable(CARRIER_IN_TIMER_BUS); + furi_hal_bus_disable(CARRIER_IN_REFERENCE_TIMER_BUS); + FURI_CRITICAL_EXIT(); } //------------------------------------------------------------------ hitag data parsing functions ------------------------------------------------------------------ -static void lfrfid_hitag_worker_yield_dma_buffer_BPLM(LfRfidHitagCMDData* dataCMD, LfRfidHitagCMDData* backupCMD, TimerDMAData* dataDMA, size_t start, size_t len){ - //based on interal timer with freq of 1MHz (period of 1us) - //use active command - LfRfidHitagCMDData* data = dataCMD; - if (!data->CMDactive){ - data = backupCMD; - } - uint16_t lastarrposition = 0; - size_t toskip = 0; - bool itemProcessed = true; - size_t s=start; - //for ( ; data->CMDloop || data->CMDposition < data->CMDlength ; data->CMDposition++){ - while(1){ - //at the end of current command (real or backup), check if there's unprocessed CMD available, if not switch to backup - if (data->CMDposition == data->CMDlength){ - data->CMDcount++; - if (dataCMD->CMDposition < dataCMD->CMDlength){ //currently backup & new unprocessed command available --> switch over to real CMD - data->CMDactive = false; - data = dataCMD; - data->CMDactive = true; - data->CMDcount = 0; - } else if (dataCMD->CMDloop){ //if looping (real or backup) --> reset real (current) CMD to startposition - data->CMDposition = 0; - data->CMDsubposition = 0; - } else if (dataCMD->CMDactive){ //real command and not looping --> switch to backup CMD and reset backup CMD to startposition - data->CMDactive = false; - data = backupCMD; - data->CMDactive = true; - data->CMDposition = 0; - data->CMDsubposition = 0; - data->CMDcount = 0; - } else { //backupCMD but set to not loop --> loop anyway: reset to startposition - data->CMDposition = 0; - data->CMDsubposition = 0; - } - } - //start processing the next CMD item - lastarrposition = s; - if (s==start){ - toskip = data->CMDsubposition; - } else { - toskip = 0; - data->CMDsubposition=0; - } - switch (data->CMDtype[data->CMDposition]) - { - case HITAG_STOP: //just 1 STOP cycle (HITAG_LOW lows + HITAG_STOP_HIGH highs) - if(s>start){ - dataDMA->timer_buffer_arr[s-1]+=HITAG_LOW*HITAG_BASEPERIOD; - } - for (size_t t=0;ttimer_buffer_ccr[s]=HITAG_BASEPERIOD/2; - dataDMA->timer_buffer_arr[s]=HITAG_BASEPERIOD-1; - s++; - } - break; - case HITAG_ON: //x ON cycles --> update next X arr/ccr values - for (size_t x=0;xCMDvalue[data->CMDposition]-toskip;x++){ - if (s==len+start){ - //arrays are filled, do not continue out - itemProcessed = false; - break; - } - dataDMA->timer_buffer_ccr[s]=HITAG_BASEPERIOD/2; - dataDMA->timer_buffer_arr[s]=HITAG_BASEPERIOD-1; - s++; - } - break; - case HITAG_OFF: //x OFF cycles (update previous arr value) - if(s>start){ - dataDMA->timer_buffer_arr[s-1]+=data->CMDvalue[data->CMDposition]*HITAG_BASEPERIOD; - } - break; - default: // <=HITAG_BYTE: //bits to be sent - //get each bit & update arr/ccr accordingly (7 lows + HITAG_0_HIGH/HITAG_1_HIGH highs) - for (size_t b=8-data->CMDtype[data->CMDposition];b<8;b++){ - if(s>start){ - dataDMA->timer_buffer_arr[s-1]+=HITAG_LOW*HITAG_BASEPERIOD; - } - if (0x80 & (data->CMDvalue[data->CMDposition]<timer_buffer_ccr[s]=HITAG_BASEPERIOD/2; - dataDMA->timer_buffer_arr[s]=HITAG_BASEPERIOD-1; - s++; - if(toskip>0){ - toskip--; - s--; - } - } - } else { - for (size_t t=0;ttimer_buffer_ccr[s]=HITAG_BASEPERIOD/2; - dataDMA->timer_buffer_arr[s]=HITAG_BASEPERIOD-1; - s++; - if(toskip>0){ - toskip--; - s--; - } - } - } - if (!itemProcessed){ - break; - } - } - break; - } - if(!itemProcessed){ - break; - } - data->CMDposition++; - } - - /*if buffer is not full, but command is fully processed: fill remainder of buffer with normal on cycles +static void lfrfid_hitag_worker_yield_dma_buffer_BPLM( + LfRfidHitagCMDData* dataCMD, + LfRfidHitagCMDData* backupCMD, + TimerDMAData* dataDMA, + size_t start, + size_t len) { + //based on interal timer with freq of 1MHz (period of 1us) + //use active command + LfRfidHitagCMDData* data = dataCMD; + if(!data->CMDactive) { + data = backupCMD; + } + uint16_t lastarrposition = 0; + size_t toskip = 0; + bool itemProcessed = true; + size_t s = start; + //for ( ; data->CMDloop || data->CMDposition < data->CMDlength ; data->CMDposition++){ + while(1) { + //at the end of current command (real or backup), check if there's unprocessed CMD available, if not switch to backup + if(data->CMDposition == data->CMDlength) { + data->CMDcount++; + if(dataCMD->CMDposition < + dataCMD + ->CMDlength) { //currently backup & new unprocessed command available --> switch over to real CMD + data->CMDactive = false; + data = dataCMD; + data->CMDactive = true; + data->CMDcount = 0; + } else if(dataCMD + ->CMDloop) { //if looping (real or backup) --> reset real (current) CMD to startposition + data->CMDposition = 0; + data->CMDsubposition = 0; + } else if(dataCMD + ->CMDactive) { //real command and not looping --> switch to backup CMD and reset backup CMD to startposition + data->CMDactive = false; + data = backupCMD; + data->CMDactive = true; + data->CMDposition = 0; + data->CMDsubposition = 0; + data->CMDcount = 0; + } else { //backupCMD but set to not loop --> loop anyway: reset to startposition + data->CMDposition = 0; + data->CMDsubposition = 0; + } + } + //start processing the next CMD item + lastarrposition = s; + if(s == start) { + toskip = data->CMDsubposition; + } else { + toskip = 0; + data->CMDsubposition = 0; + } + switch(data->CMDtype[data->CMDposition]) { + case HITAG_STOP: //just 1 STOP cycle (HITAG_LOW lows + HITAG_STOP_HIGH highs) + if(s > start) { + dataDMA->timer_buffer_arr[s - 1] += HITAG_LOW * HITAG_BASEPERIOD; + } + for(size_t t = 0; t < HITAG_STOP_HIGH - toskip; t++) { + if(s == len + start) { + //arrays are filled, do not continue out + itemProcessed = false; + break; + } + dataDMA->timer_buffer_ccr[s] = HITAG_BASEPERIOD / 2; + dataDMA->timer_buffer_arr[s] = HITAG_BASEPERIOD - 1; + s++; + } + break; + case HITAG_ON: //x ON cycles --> update next X arr/ccr values + for(size_t x = 0; x < data->CMDvalue[data->CMDposition] - toskip; x++) { + if(s == len + start) { + //arrays are filled, do not continue out + itemProcessed = false; + break; + } + dataDMA->timer_buffer_ccr[s] = HITAG_BASEPERIOD / 2; + dataDMA->timer_buffer_arr[s] = HITAG_BASEPERIOD - 1; + s++; + } + break; + case HITAG_OFF: //x OFF cycles (update previous arr value) + if(s > start) { + dataDMA->timer_buffer_arr[s - 1] += + data->CMDvalue[data->CMDposition] * HITAG_BASEPERIOD; + } + break; + default: // <=HITAG_BYTE: //bits to be sent + //get each bit & update arr/ccr accordingly (7 lows + HITAG_0_HIGH/HITAG_1_HIGH highs) + for(size_t b = 8 - data->CMDtype[data->CMDposition]; b < 8; b++) { + if(s > start) { + dataDMA->timer_buffer_arr[s - 1] += HITAG_LOW * HITAG_BASEPERIOD; + } + if(0x80 & (data->CMDvalue[data->CMDposition] << b)) { + for(size_t t = 0; t < HITAG_1_HIGH; t++) { + if(s == len + start) { + //arrays are filled, do not continue out + itemProcessed = false; + break; + } + dataDMA->timer_buffer_ccr[s] = HITAG_BASEPERIOD / 2; + dataDMA->timer_buffer_arr[s] = HITAG_BASEPERIOD - 1; + s++; + if(toskip > 0) { + toskip--; + s--; + } + } + } else { + for(size_t t = 0; t < HITAG_0_HIGH; t++) { + if(s == len + start) { + //arrays are filled, do not continue out + itemProcessed = false; + break; + } + dataDMA->timer_buffer_ccr[s] = HITAG_BASEPERIOD / 2; + dataDMA->timer_buffer_arr[s] = HITAG_BASEPERIOD - 1; + s++; + if(toskip > 0) { + toskip--; + s--; + } + } + } + if(!itemProcessed) { + break; + } + } + break; + } + if(!itemProcessed) { + break; + } + data->CMDposition++; + } + + /*if buffer is not full, but command is fully processed: fill remainder of buffer with normal on cycles if (sCMDsubposition += (len+start)-lastarrposition; - } else { - data->CMDsubposition = 0; - } - + + //if buffer was full, but command not yet fully processed: set starting position for next time: + if(!itemProcessed) { + data->CMDsubposition += (len + start) - lastarrposition; + } else { + data->CMDsubposition = 0; + } } -static uint16_t lfrfid_hitag_worker_yield_dma_buffer_MC(LfRfidHitagReplyData* dataReply, TimerDMAData* dataDMA, uint8_t carrierPrescaler){ - //based on external carrier with freq of 125kHz (period of 8us) - - uint16_t dma_len = 0; - - bool bit=false; - bool prevBit = true; //must be true to initialze MC yield for first bit - - //process reply bits and add HIGH-LOW for 1 or LOW-HIGH for 0 - for (uint8_t i = 0;ireplylength;i++){ - for (int8_t b=dataReply->replytype[i]-1;b>=0;b--){ - bit = (dataReply->replyvalue[i]>>b & 0b00000001); - if (bit){ //HIGH-LOW - if (prevBit){ //if first bit (initial value of prevBit=true) or if prev bit was 1: start of new default cycle - dataDMA->timer_buffer_arr[dma_len]=32-1; - dataDMA->timer_buffer_ccr[dma_len]=16; - dma_len++; - } else { //previous bit was 0, so extend the ccr and arr with 16 - dataDMA->timer_buffer_arr[dma_len-1]+=16; - dataDMA->timer_buffer_ccr[dma_len-1]+=16; - } - } else { //LOW-HIGH - if (prevBit){ //previous bit was 1 so extend the LOW (arr only) of prev cycle with 16 and add new default cycle - dataDMA->timer_buffer_arr[dma_len-1]+=16; - - dataDMA->timer_buffer_arr[dma_len]=32-1; - dataDMA->timer_buffer_ccr[dma_len]=16; - dma_len++; - } else { //previous bit was 0 so just add new default cycle - dataDMA->timer_buffer_arr[dma_len]=32-1; - dataDMA->timer_buffer_ccr[dma_len]=16; - dma_len++; - } - } - prevBit = bit; - } - } - - //add extra period, to reset timer back to normal carrier_in detection after reply has finished - //also required to make sure the last real dma values are fully executed before dma signals transfer complete, which resets worker into command detection - dataDMA->timer_buffer_arr[dma_len]=carrierPrescaler-1; - dataDMA->timer_buffer_ccr[dma_len]=0; - dma_len++; - - return dma_len; +static uint16_t lfrfid_hitag_worker_yield_dma_buffer_MC( + LfRfidHitagReplyData* dataReply, + TimerDMAData* dataDMA, + uint8_t carrierPrescaler) { + //based on external carrier with freq of 125kHz (period of 8us) + + uint16_t dma_len = 0; + + bool bit = false; + bool prevBit = true; //must be true to initialze MC yield for first bit + + //process reply bits and add HIGH-LOW for 1 or LOW-HIGH for 0 + for(uint8_t i = 0; i < dataReply->replylength; i++) { + for(int8_t b = dataReply->replytype[i] - 1; b >= 0; b--) { + bit = (dataReply->replyvalue[i] >> b & 0b00000001); + if(bit) { //HIGH-LOW + if(prevBit) { //if first bit (initial value of prevBit=true) or if prev bit was 1: start of new default cycle + dataDMA->timer_buffer_arr[dma_len] = 32 - 1; + dataDMA->timer_buffer_ccr[dma_len] = 16; + dma_len++; + } else { //previous bit was 0, so extend the ccr and arr with 16 + dataDMA->timer_buffer_arr[dma_len - 1] += 16; + dataDMA->timer_buffer_ccr[dma_len - 1] += 16; + } + } else { //LOW-HIGH + if(prevBit) { //previous bit was 1 so extend the LOW (arr only) of prev cycle with 16 and add new default cycle + dataDMA->timer_buffer_arr[dma_len - 1] += 16; + + dataDMA->timer_buffer_arr[dma_len] = 32 - 1; + dataDMA->timer_buffer_ccr[dma_len] = 16; + dma_len++; + } else { //previous bit was 0 so just add new default cycle + dataDMA->timer_buffer_arr[dma_len] = 32 - 1; + dataDMA->timer_buffer_ccr[dma_len] = 16; + dma_len++; + } + } + prevBit = bit; + } + } + + //add extra period, to reset timer back to normal carrier_in detection after reply has finished + //also required to make sure the last real dma values are fully executed before dma signals transfer complete, which resets worker into command detection + dataDMA->timer_buffer_arr[dma_len] = carrierPrescaler - 1; + dataDMA->timer_buffer_ccr[dma_len] = 0; + dma_len++; + + return dma_len; } -static uint16_t lfrfid_hitag_worker_yield_dma_buffer_AC(LfRfidHitagReplyData* dataReply, TimerDMAData* dataDMA, uint8_t carrierPrescaler){ - //based on external carrier with freq of 125kHz (period of 8us) - - uint16_t dma_len = 0; - - //process reply bits and add 2 short pulses for 1 or 1 long pulse for 0 - for (uint8_t i = 0;ireplylength;i++){ - for (int8_t b=dataReply->replytype[i]-1;b>=0;b--){ - if (dataReply->replyvalue[i]>>b & 0b00000001){ - dataDMA->timer_buffer_arr[dma_len]=32-1; - dataDMA->timer_buffer_ccr[dma_len]=16;//16 - dma_len++; - dataDMA->timer_buffer_arr[dma_len]=32-1; - dataDMA->timer_buffer_ccr[dma_len]=16;//16 - dma_len++; - } else { - dataDMA->timer_buffer_arr[dma_len]=64-1; - dataDMA->timer_buffer_ccr[dma_len]=32;//32 - dma_len++; - } - } - } - - //add extra period, to reset timer back to normal carrier_in detection after reply has finished - //also required to make sure the last real dma values are fully executed before dma signals transfer complete, which resets worker into command detection - dataDMA->timer_buffer_arr[dma_len]=carrierPrescaler-1; - dataDMA->timer_buffer_ccr[dma_len]=0; - dma_len++; - - return dma_len; +static uint16_t lfrfid_hitag_worker_yield_dma_buffer_AC( + LfRfidHitagReplyData* dataReply, + TimerDMAData* dataDMA, + uint8_t carrierPrescaler) { + //based on external carrier with freq of 125kHz (period of 8us) + + uint16_t dma_len = 0; + + //process reply bits and add 2 short pulses for 1 or 1 long pulse for 0 + for(uint8_t i = 0; i < dataReply->replylength; i++) { + for(int8_t b = dataReply->replytype[i] - 1; b >= 0; b--) { + if(dataReply->replyvalue[i] >> b & 0b00000001) { + dataDMA->timer_buffer_arr[dma_len] = 32 - 1; + dataDMA->timer_buffer_ccr[dma_len] = 16; //16 + dma_len++; + dataDMA->timer_buffer_arr[dma_len] = 32 - 1; + dataDMA->timer_buffer_ccr[dma_len] = 16; //16 + dma_len++; + } else { + dataDMA->timer_buffer_arr[dma_len] = 64 - 1; + dataDMA->timer_buffer_ccr[dma_len] = 32; //32 + dma_len++; + } + } + } + + //add extra period, to reset timer back to normal carrier_in detection after reply has finished + //also required to make sure the last real dma values are fully executed before dma signals transfer complete, which resets worker into command detection + dataDMA->timer_buffer_arr[dma_len] = carrierPrescaler - 1; + dataDMA->timer_buffer_ccr[dma_len] = 0; + dma_len++; + + return dma_len; } -static void lfrfid_hitag_worker_decoder_feed(uint32_t pulse, uint32_t duration, uint32_t* memBlock, uint8_t* bits, bool* half, bool* finished, LfRfidHitagProtocol protocol, uint8_t startBits){ - uint32_t bit = 1<<(32-1); - if (*bits==0){ - *half = false; - } - if (protocol == HitagProtocolManchesterCoding){ - if (HITAG_DURATION_S*(1-HITAG_DURATION_ERROR_MARGIN) <= duration && duration <= HITAG_DURATION_S*(1+HITAG_DURATION_ERROR_MARGIN) ){ - //short period (HL) detected: same bit as previous - if (*bits < startBits){ - //don't store startbits in memory, just increase bitcounter - (*bits)++; - } else if (*bits == startBits){ - //same as previous bit (last startbit it 1, so add 1 to the memBlock) - memBlock[(*bits-startBits)/32] |= (bit >> (*bits-startBits)%32); //add 1 - (*bits)++; - } else { - //add same as previous bit - if (memBlock[(*bits-startBits-1)/32] & (bit >> (*bits-startBits-1)%32)){ - memBlock[(*bits-startBits)/32] |= (bit >> (*bits-startBits)%32); //add 1 - (*bits)++; - } else { - (*bits)++; //no mem action for 0, just increase bitcounter - } - } - } - else if (HITAG_DURATION_M*(1-HITAG_DURATION_ERROR_MARGIN) <= duration && duration <= HITAG_DURATION_M*(1+HITAG_DURATION_ERROR_MARGIN) ){ - //medium period (H|HL or HL|L) detected: half 0 + full 1 or full 1 + half 0 detected - if (*bits < startBits){ - //assuming all startbits are 1's, this can only happen on last startbit followed by 0 - //don't store startbits, no mem action for 0, just increase bitcounter twice - (*bits)++; - (*bits)++; - *half = true; - } else if (*bits == startBits){ - //assuming all startbits were 1's, this is a data 10 detection - memBlock[(*bits-startBits)/32] |= (bit >> (*bits-startBits)%32); //add 1 - (*bits)++; - (*bits)++; //no mem action for 0, just increase bitcounter - *half = true; - } else { - //if we're in half of a bit, add 1, else add 10 and set half flag - if (*half){ - memBlock[(*bits-startBits)/32] |= (bit >> (*bits-startBits)%32); //add 1 - (*bits)++; - *half = false; - } else { - memBlock[(*bits-startBits)/32] |= (bit >> (*bits-startBits)%32); //add 1 - (*bits)++; - (*bits)++; //no mem action for 0, just increase bitcounter - *half = true; - } - } - } - else if (HITAG_DURATION_L*(1-HITAG_DURATION_ERROR_MARGIN) <= duration && duration <= HITAG_DURATION_L*(1+HITAG_DURATION_ERROR_MARGIN) ){ - //long period (H|HL|L) detected: half 0 + full 1 + half 0 detected - if (*bits <= startBits){ - //TODO: throw error since this can't happen till after first real bit, since all startbits should be 1's - } else { - //TODO check that prev bit is indeed 0 - //add 10 to the memBlock - memBlock[(*bits-startBits)/32] |= (bit >> (*bits-startBits)%32); //add 1 - (*bits)++; - (*bits)++;//no mem action for 0, just increase bitcounter - } - } - else if (HITAG_DURATION_L*(1+HITAG_DURATION_ERROR_MARGIN) < duration){ - //end of modulation by tag, check if this pulse duration includes a bit - if (*bits>0){ - if (*half && pulse < HITAG_DURATION_S){ - //second half of 0 bit, no action - } else if (*half && pulse < HITAG_DURATION_M){ - //second half or 0 bit + another 1 bit - memBlock[(*bits-startBits)/32] |= (bit >> (*bits-startBits)%32); //add 1 - (*bits)++; - } else if (!(*half) && pulse < HITAG_DURATION_S){ - //another 1 bit - memBlock[(*bits-startBits)/32] |= (bit >> (*bits-startBits)%32); //add 1 - (*bits)++; - } else { - //invalid pulse duration --> reset? - } - *finished = true; - } - } - else { - //invalid pulse duration: reset tempData if necessary - if (*bits>0){ - memset(memBlock, 0, HITAG_BLOCKPAGES*sizeof(memBlock[0])); - *bits = 0; - *finished = false; - } - } - } - else if (protocol == HitagProtocolAntiCollision){ - if (HITAG_DURATION_S*(1-HITAG_DURATION_ERROR_MARGIN) <= duration && duration <= HITAG_DURATION_S*(1+HITAG_DURATION_ERROR_MARGIN) ){ - //short period (HL): first or second half of a 1 detected - //if first short: add 1 to memBlock and increase half counter - if (*half==false){ - if (*bits >= startBits){ - memBlock[(*bits-startBits)/32] |= (bit >> (*bits-startBits)%32); - } - (*bits)++; - *half = true; - } else { - //if second small: reset half counter - *half = false; - } - } - else if (HITAG_DURATION_L*(1-HITAG_DURATION_ERROR_MARGIN) <= duration && duration <= HITAG_DURATION_L*(1+HITAG_DURATION_ERROR_MARGIN) ){ - //long period (HHLL): 0 detected - //should only happen after full detection of 1 or after startBits - if (*bits < startBits || *half){ - //reset - memset(memBlock, 0, HITAG_BLOCKPAGES*sizeof(memBlock[0])); - *bits = 0; - *finished = false; - } else { - (*bits)++; //add 0 to memBlock - } - } - else if (HITAG_DURATION_L*(1+HITAG_DURATION_ERROR_MARGIN) < duration){ - //end of modulation by tag, check if this pulse duration includes a bit - if (*bits>0){ - if (!(*half) && pulse > HITAG_DURATION_S){ - (*bits)++; //add 0 to memBlock - } else { - //invalid pulse duration: reset? - } - *finished = true; - } - } - else { - //invalid pulse duration: reset tempData if necessary - if (*bits>0){ - memset(memBlock, 0, HITAG_BLOCKPAGES*sizeof(memBlock[0])); - *bits = 0; - *finished = false; - } - } - } +static void lfrfid_hitag_worker_decoder_feed( + uint32_t pulse, + uint32_t duration, + uint32_t* memBlock, + uint8_t* bits, + bool* half, + bool* finished, + LfRfidHitagProtocol protocol, + uint8_t startBits) { + uint32_t bit = 1 << (32 - 1); + if(*bits == 0) { + *half = false; + } + if(protocol == HitagProtocolManchesterCoding) { + if(HITAG_DURATION_S * (1 - HITAG_DURATION_ERROR_MARGIN) <= duration && + duration <= HITAG_DURATION_S * (1 + HITAG_DURATION_ERROR_MARGIN)) { + //short period (HL) detected: same bit as previous + if(*bits < startBits) { + //don't store startbits in memory, just increase bitcounter + (*bits)++; + } else if(*bits == startBits) { + //same as previous bit (last startbit it 1, so add 1 to the memBlock) + memBlock[(*bits - startBits) / 32] |= (bit >> (*bits - startBits) % 32); //add 1 + (*bits)++; + } else { + //add same as previous bit + if(memBlock[(*bits - startBits - 1) / 32] & + (bit >> (*bits - startBits - 1) % 32)) { + memBlock[(*bits - startBits) / 32] |= + (bit >> (*bits - startBits) % 32); //add 1 + (*bits)++; + } else { + (*bits)++; //no mem action for 0, just increase bitcounter + } + } + } else if( + HITAG_DURATION_M * (1 - HITAG_DURATION_ERROR_MARGIN) <= duration && + duration <= HITAG_DURATION_M * (1 + HITAG_DURATION_ERROR_MARGIN)) { + //medium period (H|HL or HL|L) detected: half 0 + full 1 or full 1 + half 0 detected + if(*bits < startBits) { + //assuming all startbits are 1's, this can only happen on last startbit followed by 0 + //don't store startbits, no mem action for 0, just increase bitcounter twice + (*bits)++; + (*bits)++; + *half = true; + } else if(*bits == startBits) { + //assuming all startbits were 1's, this is a data 10 detection + memBlock[(*bits - startBits) / 32] |= (bit >> (*bits - startBits) % 32); //add 1 + (*bits)++; + (*bits)++; //no mem action for 0, just increase bitcounter + *half = true; + } else { + //if we're in half of a bit, add 1, else add 10 and set half flag + if(*half) { + memBlock[(*bits - startBits) / 32] |= + (bit >> (*bits - startBits) % 32); //add 1 + (*bits)++; + *half = false; + } else { + memBlock[(*bits - startBits) / 32] |= + (bit >> (*bits - startBits) % 32); //add 1 + (*bits)++; + (*bits)++; //no mem action for 0, just increase bitcounter + *half = true; + } + } + } else if( + HITAG_DURATION_L * (1 - HITAG_DURATION_ERROR_MARGIN) <= duration && + duration <= HITAG_DURATION_L * (1 + HITAG_DURATION_ERROR_MARGIN)) { + //long period (H|HL|L) detected: half 0 + full 1 + half 0 detected + if(*bits <= startBits) { + //TODO: throw error since this can't happen till after first real bit, since all startbits should be 1's + } else { + //TODO check that prev bit is indeed 0 + //add 10 to the memBlock + memBlock[(*bits - startBits) / 32] |= (bit >> (*bits - startBits) % 32); //add 1 + (*bits)++; + (*bits)++; //no mem action for 0, just increase bitcounter + } + } else if(HITAG_DURATION_L * (1 + HITAG_DURATION_ERROR_MARGIN) < duration) { + //end of modulation by tag, check if this pulse duration includes a bit + if(*bits > 0) { + if(*half && pulse < HITAG_DURATION_S) { + //second half of 0 bit, no action + } else if(*half && pulse < HITAG_DURATION_M) { + //second half or 0 bit + another 1 bit + memBlock[(*bits - startBits) / 32] |= + (bit >> (*bits - startBits) % 32); //add 1 + (*bits)++; + } else if(!(*half) && pulse < HITAG_DURATION_S) { + //another 1 bit + memBlock[(*bits - startBits) / 32] |= + (bit >> (*bits - startBits) % 32); //add 1 + (*bits)++; + } else { + //invalid pulse duration --> reset? + } + *finished = true; + } + } else { + //invalid pulse duration: reset tempData if necessary + if(*bits > 0) { + memset(memBlock, 0, HITAG_BLOCKPAGES * sizeof(memBlock[0])); + *bits = 0; + *finished = false; + } + } + } else if(protocol == HitagProtocolAntiCollision) { + if(HITAG_DURATION_S * (1 - HITAG_DURATION_ERROR_MARGIN) <= duration && + duration <= HITAG_DURATION_S * (1 + HITAG_DURATION_ERROR_MARGIN)) { + //short period (HL): first or second half of a 1 detected + //if first short: add 1 to memBlock and increase half counter + if(*half == false) { + if(*bits >= startBits) { + memBlock[(*bits - startBits) / 32] |= (bit >> (*bits - startBits) % 32); + } + (*bits)++; + *half = true; + } else { + //if second small: reset half counter + *half = false; + } + } else if( + HITAG_DURATION_L * (1 - HITAG_DURATION_ERROR_MARGIN) <= duration && + duration <= HITAG_DURATION_L * (1 + HITAG_DURATION_ERROR_MARGIN)) { + //long period (HHLL): 0 detected + //should only happen after full detection of 1 or after startBits + if(*bits < startBits || *half) { + //reset + memset(memBlock, 0, HITAG_BLOCKPAGES * sizeof(memBlock[0])); + *bits = 0; + *finished = false; + } else { + (*bits)++; //add 0 to memBlock + } + } else if(HITAG_DURATION_L * (1 + HITAG_DURATION_ERROR_MARGIN) < duration) { + //end of modulation by tag, check if this pulse duration includes a bit + if(*bits > 0) { + if(!(*half) && pulse > HITAG_DURATION_S) { + (*bits)++; //add 0 to memBlock + } else { + //invalid pulse duration: reset? + } + *finished = true; + } + } else { + //invalid pulse duration: reset tempData if necessary + if(*bits > 0) { + memset(memBlock, 0, HITAG_BLOCKPAGES * sizeof(memBlock[0])); + *bits = 0; + *finished = false; + } + } + } } -static void lfrfid_hitag_worker_calc_crc(uint8_t* crc, uint8_t data, uint8_t bitcount){ - *crc ^= data; // crc = crc (exor) data - do { - if( *crc & 0x80 ) // if (MSB-CRC == 1) - { - *crc<<=1; // CRC = CRC bit-shift left - *crc ^= CRC_POLYNOM; // CRC = CRC (exor) CRC_POLYNOM - } - else - { - *crc<<=1; // CRC = CRC bit-shift left - } - } while(--bitcount); +static void lfrfid_hitag_worker_calc_crc(uint8_t* crc, uint8_t data, uint8_t bitcount) { + *crc ^= data; // crc = crc (exor) data + do { + if(*crc & 0x80) // if (MSB-CRC == 1) + { + *crc <<= 1; // CRC = CRC bit-shift left + *crc ^= CRC_POLYNOM; // CRC = CRC (exor) CRC_POLYNOM + } else { + *crc <<= 1; // CRC = CRC bit-shift left + } + } while(--bitcount); } -static bool lfrfid_hitag_worker_validate_command(uint32_t* bits, uint8_t len){ - if (len == 5 && (bits[0]>>(32-5)) == 0b11001){ +static bool lfrfid_hitag_worker_validate_command(uint32_t* bits, uint8_t len) { + if(len == 5 && (bits[0] >> (32 - 5)) == 0b11001) { //SET_CCNEW return true; - } - else if (len == 5 && (bits[0]>>(32-5)) == 0b00110){ + } else if(len == 5 && (bits[0] >> (32 - 5)) == 0b00110) { //SET_CC return true; - } - else if (len == 45 && (bits[0]>>(32-5)) == 0b00000){ + } else if(len == 45 && (bits[0] >> (32 - 5)) == 0b00000) { //SELECT - - //calculate CRC for first 37 bits (4bytes + 5 bits) - uint8_t byte1 = bits[0]>>24 & 0xff; - uint8_t byte2 = bits[0]>>16 & 0xff; - uint8_t byte3 = bits[0]>>8 & 0xff; - uint8_t byte4 = bits[0] & 0xff; - uint8_t byte5 = bits[1]>>24 & 0b11111000; - uint8_t crcRead = bits[1]>>19 & 0xff; - - uint8_t crc = CRC_PRESET; - lfrfid_hitag_worker_calc_crc(&crc, byte1, 8); - lfrfid_hitag_worker_calc_crc(&crc, byte2, 8); - lfrfid_hitag_worker_calc_crc(&crc, byte3, 8); - lfrfid_hitag_worker_calc_crc(&crc, byte4, 8); - lfrfid_hitag_worker_calc_crc(&crc, byte5, 5); - - //and compare result with received crc - return (crcRead == crc); - } + + //calculate CRC for first 37 bits (4bytes + 5 bits) + uint8_t byte1 = bits[0] >> 24 & 0xff; + uint8_t byte2 = bits[0] >> 16 & 0xff; + uint8_t byte3 = bits[0] >> 8 & 0xff; + uint8_t byte4 = bits[0] & 0xff; + uint8_t byte5 = bits[1] >> 24 & 0b11111000; + uint8_t crcRead = bits[1] >> 19 & 0xff; + + uint8_t crc = CRC_PRESET; + lfrfid_hitag_worker_calc_crc(&crc, byte1, 8); + lfrfid_hitag_worker_calc_crc(&crc, byte2, 8); + lfrfid_hitag_worker_calc_crc(&crc, byte3, 8); + lfrfid_hitag_worker_calc_crc(&crc, byte4, 8); + lfrfid_hitag_worker_calc_crc(&crc, byte5, 5); + + //and compare result with received crc + return (crcRead == crc); + } //else if (len == 20 && (bits[0]>>31 || (bits[0]>>(32-4)) == 0b0111)){ - else if (len == 20 && bits[0] > 0x70000000){ + else if(len == 20 && bits[0] > 0x70000000) { //R/W page/block (cmd starting with 1) or HALT (0111) --> so bit0 should be >= 0b01110000 00000000 00000000 00000000 (0x70000000) //calculate CRC for first 12 bits (1byte + 4 bits) - uint8_t byte1 = bits[0]>>24 & 0xff; - uint8_t byte2 = bits[0]>>16 & 0b11110000; - uint8_t crcRead = bits[0]>>12 & 0xff; - - uint8_t crc = CRC_PRESET; - lfrfid_hitag_worker_calc_crc(&crc, byte1, 8); - lfrfid_hitag_worker_calc_crc(&crc, byte2, 4); - - //and compare result with received crc - return (crcRead == crc); - } - return false; + uint8_t byte1 = bits[0] >> 24 & 0xff; + uint8_t byte2 = bits[0] >> 16 & 0b11110000; + uint8_t crcRead = bits[0] >> 12 & 0xff; + + uint8_t crc = CRC_PRESET; + lfrfid_hitag_worker_calc_crc(&crc, byte1, 8); + lfrfid_hitag_worker_calc_crc(&crc, byte2, 4); + + //and compare result with received crc + return (crcRead == crc); + } + return false; } -static bool lfrfid_hitag_worker_validate_bits(uint32_t* bits, uint8_t bitcounter){ - if (47 >= bitcounter && bitcounter >= 45){ - for (uint8_t i=0;i0){ - bits[0] = bits[0]<<1 | bits[1]>>(32-1); - bits[1] = bits[1]<<1; - } - if (lfrfid_hitag_worker_validate_command(bits, 45)) - return true; - } - } - else if (22 >= bitcounter && bitcounter >= 20){ - for (uint8_t i=0;i0){ - bits[0] = bits[0]<<1;// | bits[1]>>(32-1); - //bits[1] = bits[1]<<1; - } - if (lfrfid_hitag_worker_validate_command(bits, 20)) - return true; - } - } - else if (7 >= bitcounter && bitcounter >= 5){ - for (uint8_t i=0;i0){ - bits[0] = bits[0]<<1;// | bits[1]>>(32-1); - //bits[1] = bits[1]<<1; - } - if (lfrfid_hitag_worker_validate_command(bits, 5)) - return true; - } - } +static bool lfrfid_hitag_worker_validate_bits(uint32_t* bits, uint8_t bitcounter) { + if(47 >= bitcounter && bitcounter >= 45) { + for(uint8_t i = 0; i < bitcounter - 44; i++) { + if(i > 0) { + bits[0] = bits[0] << 1 | bits[1] >> (32 - 1); + bits[1] = bits[1] << 1; + } + if(lfrfid_hitag_worker_validate_command(bits, 45)) return true; + } + } else if(22 >= bitcounter && bitcounter >= 20) { + for(uint8_t i = 0; i < bitcounter - 19; i++) { + if(i > 0) { + bits[0] = bits[0] << 1; // | bits[1]>>(32-1); + //bits[1] = bits[1]<<1; + } + if(lfrfid_hitag_worker_validate_command(bits, 20)) return true; + } + } else if(7 >= bitcounter && bitcounter >= 5) { + for(uint8_t i = 0; i < bitcounter - 4; i++) { + if(i > 0) { + bits[0] = bits[0] << 1; // | bits[1]>>(32-1); + //bits[1] = bits[1]<<1; + } + if(lfrfid_hitag_worker_validate_command(bits, 5)) return true; + } + } return false; } -static uint8_t lfrfid_hitag_worker_validate_ripple_delta(uint32_t delta){ - if (18 <= delta && delta <= 22) - return 1; - if (26 <= delta && delta <= 32) - return 1; +static uint8_t lfrfid_hitag_worker_validate_ripple_delta(uint32_t delta) { + if(18 <= delta && delta <= 22) return 1; + if(26 <= delta && delta <= 32) return 1; return 0; } -void lfrfid_hitag_worker_initialize_tag_data(LFRFIDHitag* tag){ - //initialize a tag datastructure - //set all pages to 0x00000000 - memset(tag->pageData, 0, sizeof(tag->pageData)); - - //set all pages to unknown - for (uint8_t i=0;ipageKnown[i]=0; - } - - //default RW settings: - memset(tag->pageRW, READ, HITAG_PAGES/2*sizeof(tag->pageRW[0])); - memset(tag->pageRW+HITAG_PAGES/2, READ|WRITE, HITAG_PAGES/2*sizeof(tag->pageRW[0])); - tag->pageRW[2]=0; - tag->pageRW[3]=0; - - //default public/secret settings: - for (uint8_t i=0;ipagePublic[i]=1; - } - for (uint8_t i=2;i<=31;i++){ - tag->pagePublic[i]=0; - } +void lfrfid_hitag_worker_initialize_tag_data(LFRFIDHitag* tag) { + //initialize a tag datastructure + //set all pages to 0x00000000 + memset(tag->pageData, 0, sizeof(tag->pageData)); + + //set all pages to unknown + for(uint8_t i = 0; i < HITAG_PAGES; i++) { + tag->pageKnown[i] = 0; + } + + //default RW settings: + memset(tag->pageRW, READ, HITAG_PAGES / 2 * sizeof(tag->pageRW[0])); + memset(tag->pageRW + HITAG_PAGES / 2, READ | WRITE, HITAG_PAGES / 2 * sizeof(tag->pageRW[0])); + tag->pageRW[2] = 0; + tag->pageRW[3] = 0; + + //default public/secret settings: + for(uint8_t i = 0; i < HITAG_PAGES; i++) { + tag->pagePublic[i] = 1; + } + for(uint8_t i = 2; i <= 31; i++) { + tag->pagePublic[i] = 0; + } } -static void lfrfid_hitag_worker_combine_tag_data(LFRFIDHitag* tag){ - memcpy(tag->tagData, tag->pageData, HITAG_PAGES*HITAG_PAGEBYTES); - memcpy(tag->tagData+HITAG_PAGES*HITAG_PAGEBYTES, tag->pageKnown, HITAG_PAGES); +static void lfrfid_hitag_worker_combine_tag_data(LFRFIDHitag* tag) { + memcpy(tag->tagData, tag->pageData, HITAG_PAGES * HITAG_PAGEBYTES); + memcpy(tag->tagData + HITAG_PAGES * HITAG_PAGEBYTES, tag->pageKnown, HITAG_PAGES); } -static void lfrfid_hitag_worker_split_tag_data(LFRFIDHitag* tag){ - memcpy(tag->pageData, tag->tagData, HITAG_PAGES*HITAG_PAGEBYTES); - memcpy(tag->pageKnown, tag->tagData+HITAG_PAGES*HITAG_PAGEBYTES, HITAG_PAGES); +static void lfrfid_hitag_worker_split_tag_data(LFRFIDHitag* tag) { + memcpy(tag->pageData, tag->tagData, HITAG_PAGES * HITAG_PAGEBYTES); + memcpy(tag->pageKnown, tag->tagData + HITAG_PAGES * HITAG_PAGEBYTES, HITAG_PAGES); } -void lfrfid_hitag_worker_process_config_page(LFRFIDHitag* tag){ - //RW settings via byte 0 - if (tag->pageData[4+0] & 0b10000000){ //block 1 (logData) RW or 0 - memset(tag->pageRW+1*4, READ|WRITE, 4*sizeof(tag->pageRW[0])); - } else { - memset(tag->pageRW+1*4, 0, 4*sizeof(tag->pageRW[0])); - } - if (tag->pageData[4+0] & 0b01000000){ //key A&B (page 2&3) W or 0 - memset(tag->pageRW+2, WRITE, 2*sizeof(tag->pageRW[0])); - } else { - memset(tag->pageRW+2, 0, 2*sizeof(tag->pageRW[0])); - } - if (tag->pageData[4+0] & 0b00100000){ //block 2 RW or RO - memset(tag->pageRW+2*4, READ|WRITE, 4*sizeof(tag->pageRW[0])); - } else { - memset(tag->pageRW+2*4, READ, 4*sizeof(tag->pageRW[0])); - } - if (tag->pageData[4+0] & 0b00010000){ //block 3 RW or RO - memset(tag->pageRW+3*4, READ|WRITE, 4*sizeof(tag->pageRW[0])); - } else { - memset(tag->pageRW+3*4, READ, 4*sizeof(tag->pageRW[0])); - } - if (tag->pageData[4+0] & 0b00001000){ //block 4 RW or RO - memset(tag->pageRW+4*4, READ|WRITE, 4*sizeof(tag->pageRW[0])); - } else { - memset(tag->pageRW+4*4, READ, 4*sizeof(tag->pageRW[0])); - } - if (tag->pageData[4+0] & 0b00000100){ //block 5 RW or RO - memset(tag->pageRW+5*4, READ|WRITE, 4*sizeof(tag->pageRW[0])); - } else { - memset(tag->pageRW+5*4, READ, 4*sizeof(tag->pageRW[0])); - } - if (tag->pageData[4+0] & 0b00000010){ //block 6 RW or RO - memset(tag->pageRW+6*4, READ|WRITE, 4*sizeof(tag->pageRW[0])); - } else { - memset(tag->pageRW+6*4, READ, 4*sizeof(tag->pageRW[0])); - } - if (tag->pageData[4+0] & 0b00000001){ //block 7 RW or RO - memset(tag->pageRW+7*4, READ|WRITE, 4*sizeof(tag->pageRW[0])); - } else { - memset(tag->pageRW+7*4, READ, 4*sizeof(tag->pageRW[0])); - } - //RW settings via byte 1 - if (tag->pageData[4+1] & 0b00010000){ //config page (1) RW or RO - tag->pageRW[1]=READ/WRITE; - } else { - tag->pageRW[1]=READ; - } - - //public/secret settings via byte 1 - if (tag->pageData[4+1] & 0b00000001){ //block 4-7 public or secret - for (uint8_t i=4*4;i<8*4;i++){ - tag->pagePublic[i]=1; - } - } else { - for (uint8_t i=4*4;i<8*4;i++){ - tag->pagePublic[i]=0; - } - } - +void lfrfid_hitag_worker_process_config_page(LFRFIDHitag* tag) { + //RW settings via byte 0 + if(tag->pageData[4 + 0] & 0b10000000) { //block 1 (logData) RW or 0 + memset(tag->pageRW + 1 * 4, READ | WRITE, 4 * sizeof(tag->pageRW[0])); + } else { + memset(tag->pageRW + 1 * 4, 0, 4 * sizeof(tag->pageRW[0])); + } + if(tag->pageData[4 + 0] & 0b01000000) { //key A&B (page 2&3) W or 0 + memset(tag->pageRW + 2, WRITE, 2 * sizeof(tag->pageRW[0])); + } else { + memset(tag->pageRW + 2, 0, 2 * sizeof(tag->pageRW[0])); + } + if(tag->pageData[4 + 0] & 0b00100000) { //block 2 RW or RO + memset(tag->pageRW + 2 * 4, READ | WRITE, 4 * sizeof(tag->pageRW[0])); + } else { + memset(tag->pageRW + 2 * 4, READ, 4 * sizeof(tag->pageRW[0])); + } + if(tag->pageData[4 + 0] & 0b00010000) { //block 3 RW or RO + memset(tag->pageRW + 3 * 4, READ | WRITE, 4 * sizeof(tag->pageRW[0])); + } else { + memset(tag->pageRW + 3 * 4, READ, 4 * sizeof(tag->pageRW[0])); + } + if(tag->pageData[4 + 0] & 0b00001000) { //block 4 RW or RO + memset(tag->pageRW + 4 * 4, READ | WRITE, 4 * sizeof(tag->pageRW[0])); + } else { + memset(tag->pageRW + 4 * 4, READ, 4 * sizeof(tag->pageRW[0])); + } + if(tag->pageData[4 + 0] & 0b00000100) { //block 5 RW or RO + memset(tag->pageRW + 5 * 4, READ | WRITE, 4 * sizeof(tag->pageRW[0])); + } else { + memset(tag->pageRW + 5 * 4, READ, 4 * sizeof(tag->pageRW[0])); + } + if(tag->pageData[4 + 0] & 0b00000010) { //block 6 RW or RO + memset(tag->pageRW + 6 * 4, READ | WRITE, 4 * sizeof(tag->pageRW[0])); + } else { + memset(tag->pageRW + 6 * 4, READ, 4 * sizeof(tag->pageRW[0])); + } + if(tag->pageData[4 + 0] & 0b00000001) { //block 7 RW or RO + memset(tag->pageRW + 7 * 4, READ | WRITE, 4 * sizeof(tag->pageRW[0])); + } else { + memset(tag->pageRW + 7 * 4, READ, 4 * sizeof(tag->pageRW[0])); + } + //RW settings via byte 1 + if(tag->pageData[4 + 1] & 0b00010000) { //config page (1) RW or RO + tag->pageRW[1] = READ / WRITE; + } else { + tag->pageRW[1] = READ; + } + + //public/secret settings via byte 1 + if(tag->pageData[4 + 1] & 0b00000001) { //block 4-7 public or secret + for(uint8_t i = 4 * 4; i < 8 * 4; i++) { + tag->pagePublic[i] = 1; + } + } else { + for(uint8_t i = 4 * 4; i < 8 * 4; i++) { + tag->pagePublic[i] = 0; + } + } } //------------------------------------------------------------------ worker threads ------------------------------------------------------------------ @@ -1255,885 +1307,1070 @@ void lfrfid_hitag_worker_process_config_page(LFRFIDHitag* tag){ static int32_t lfrfid_hitag_worker_emulate_thread(void* thread_context) { LFRFIDHitagWorker* worker = (LFRFIDHitagWorker*)thread_context; - //load tag - worker->tag = malloc(sizeof(LFRFIDHitag)); - lfrfid_hitag_worker_initialize_tag_data(worker->tag); - - size_t data_size = protocol_dict_get_data_size(worker->dict, LFRFIDProtocolHitag1); - protocol_dict_get_data(worker->dict, LFRFIDProtocolHitag1, worker->tag->tagData, data_size); - - lfrfid_hitag_worker_split_tag_data(worker->tag); - //TODO: check if a tag has been properly loaded with known serial nr & config page - lfrfid_hitag_worker_process_config_page(worker->tag); - - //init capData for carrier_in + //load tag + worker->tag = malloc(sizeof(LFRFIDHitag)); + lfrfid_hitag_worker_initialize_tag_data(worker->tag); + + size_t data_size = protocol_dict_get_data_size(worker->dict, LFRFIDProtocolHitag1); + protocol_dict_get_data(worker->dict, LFRFIDProtocolHitag1, worker->tag->tagData, data_size); + + lfrfid_hitag_worker_split_tag_data(worker->tag); + //TODO: check if a tag has been properly loaded with known serial nr & config page + lfrfid_hitag_worker_process_config_page(worker->tag); + + //init capData for carrier_in LfRfidHitagCaptureData* capData = malloc(sizeof(LfRfidHitagCaptureData)); capData->stream = buffer_stream_alloc(EMULATE_BUFFER_SIZE, EMULATE_BUFFER_COUNT); capData->pair = varint_pair_alloc(); - capData->capCounter = 0; - capData->prevTIMval = 0; - - //init dmaData for emulate mode: - uint16_t dmaLen = 0; - TimerDMAData* dataDMA = malloc(sizeof(TimerDMAData)); - - LfRfidHitagReplyData* dataReply = malloc(sizeof(LfRfidHitagReplyData)); - - //set pins to read setup - furi_hal_rfid_pins_read(); - //reconfigure carrier_out to fixed low state instead - furi_hal_gpio_init(&gpio_rfid_carrier_out, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); - furi_hal_gpio_write(&gpio_rfid_carrier_out, false); - //reconfigure PA15 carrier_in to alt ftn 1 (TIM2 CH1) to use it for input capture - furi_hal_gpio_init_ex(&gpio_rfid_carrier, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedLow, GpioAltFn1TIM2); - //reconfigure pull to alternate function 1 (TIM2 CH3) to drive low (for read carrier) or pull antenna via DMA (for emulation) - furi_hal_gpio_init_ex(&gpio_nfc_irq_rfid_pull, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedLow, GpioAltFn1TIM2); - - //start capture via carrier in instead of rfid in - lfrfid_hitag_worker_carrier_in_start(capData, worker->carrierPrescaler, dataDMA->timer_buffer_arr, dataDMA->timer_buffer_ccr, dmaLen); - - uint8_t prescaler = worker->carrierPrescaler; - uint8_t period = 8*prescaler-1; - - bool carrier_on = false; - int8_t carrier_count = 0; - uint8_t carrier_threshold_on = 5; - int8_t carrier_threshold_off = -5; - - uint32_t periodcounter = 0; - - uint8_t localMax = 0; - uint8_t localMin = 255; - uint32_t localMaxIndex = 0; - uint32_t localMinIndex = 0; - uint32_t lastMaxIndex = 0; - uint32_t lastMinIndex = 0; - uint8_t maxBitCounter = 0; - uint8_t minBitCounter = 0; - uint32_t maxBits[5]; - uint32_t minBits[5]; - int8_t cmd = 0; - uint32_t one = 1<<(32-1); - uint32_t cmdIndex = 0; - - uint32_t miniMaxDelta = 0; - uint32_t miniMaxIndex = 0; - uint32_t prevMaxDelta = 0; - uint32_t maxDelta = 0; - - uint32_t miniMinDelta = 0; - uint32_t miniMinIndex = 0; - uint32_t prevMinDelta = 0; - uint32_t minDelta = 0; - - uint32_t prevduration = 0; - uint32_t duration = 0; - uint8_t durationcounter = 0; - uint8_t durationthreshold = 5; - - uint8_t tagState = HitagStateIdle; - uint8_t tagMode = HitagModeBasic; - - while(1) { - Buffer* buffer = buffer_stream_receive(capData->stream, 1); - - if(buffer != NULL) { - size_t buffsize = buffer_get_size(buffer); - uint8_t* buffdata = buffer_get_data(buffer); - - //parse carrier_in duration & scan for cmds - size_t index = 0; - while(index < buffsize) { - prevduration = duration; - size_t tmp_size=varint_uint32_unpack(&duration, &buffdata[index], buffsize - index); - - if(tmp_size==0) { - FURI_LOG_E(TAG, "can't unpack varint pair"); - break; - } else { - index += tmp_size; - - periodcounter++; - - if (!carrier_on){ //detect if carrier is available - if (0.75*period <= duration && duration <= 1.25*period){ - carrier_count++; - if (carrier_count>=carrier_threshold_on){ - carrier_on = true; - localMax = 0; - localMin = 255; - minBitCounter = 0; - maxBitCounter = 0; - minBits[0] = 0; - minBits[1] = 0; - maxBits[0] = 0; - maxBits[1] = 0; - - carrier_count = 0; - //TODO reset tagstate to Idle & tagMode to basic after power_on_reset (no carrier for 10ms) - } - } else { - carrier_count = 0; - } - } - else { - //detect if carrier is still available - if (duration <= 0.75*period || 1.25*period <= duration){ - carrier_count--; - if (carrier_count<=carrier_threshold_off){ - carrier_on = false; - carrier_count = 0; - } - } else{ - carrier_count = 0; - } - - if (carrier_on){//if carrier is still available - //finetune the average period for reference - if (prevduration == duration){ - durationcounter++; - if (durationcounter == durationthreshold && period != duration){ - period = duration; - } - } else { - durationcounter = 0; - } - - //detect local MAX, and update maxCmd detection - if (duration > prevduration && duration > period){ - //rising edge above the normal periodduration: update localMax - localMax = duration; - localMaxIndex = periodcounter; - } else if (duration < prevduration && localMax > period && periodcounter <= localMaxIndex+2) { - //we passed a local maximum, derive length between current and prev max index - if (miniMaxDelta == 0){ - prevMaxDelta = maxDelta; - } - maxDelta = (localMaxIndex-lastMaxIndex)*prescaler; - - //check if there's a miniDelta and if it should be added to previous or next delta - if (miniMaxDelta > 0){ - //by default minidelta is added to next - //in case doing this does not result in 2 consequtive valid deltas, but adding it to previous does result in 2 consequtive valid deltas, then assign it to previous delta - if ((lfrfid_hitag_worker_validate_ripple_delta(prevMaxDelta+miniMaxDelta) + lfrfid_hitag_worker_validate_ripple_delta(maxDelta-miniMaxDelta)) > (lfrfid_hitag_worker_validate_ripple_delta(prevMaxDelta) + lfrfid_hitag_worker_validate_ripple_delta(maxDelta))){ - //update the previous (last) bit from 0 to 1 if a command was ongoing - if (maxBitCounter > 0){ - maxBits[(maxBitCounter-1)/32] |= (one >> (maxBitCounter-1)%32); - } - maxDelta -= miniMaxDelta; - lastMaxIndex = miniMaxIndex; - } - miniMaxDelta = 0; - } - - //now process the (possibly updated) delta - if (maxDelta >= 18){ - if (18 <= maxDelta && maxDelta <= 22){ - maxBitCounter++; - } else if (26 <= maxDelta && maxDelta <= 32){ - maxBits[(maxBitCounter)/32] |= (one >> (maxBitCounter)%32); - maxBitCounter++; - } else if (maxBitCounter > 0){ - //end of bitstring caused by invalid delta - - if (lfrfid_hitag_worker_validate_bits(maxBits, maxBitCounter)){ - cmd = 1; - cmdIndex = localMaxIndex; - - //CMD found, stop searching for minCMD: reset the minCmd detection - minBits[0] = 0; - minBits[1] = 0; - minBitCounter = 0; - } else { - //reset the maxCmd detection - maxBits[0] = 0; - maxBits[1] = 0; - maxBitCounter = 0; - } - } - lastMaxIndex = localMaxIndex; - } else { - miniMaxDelta = maxDelta; - miniMaxIndex = localMaxIndex; - } - localMax = 0; - } - - //terminate bitstring ico absence of next delta (a true end) - if (periodcounter == lastMaxIndex + 40/prescaler && maxBitCounter >0){ - - if (lfrfid_hitag_worker_validate_bits(maxBits, maxBitCounter)){ - cmd = 1; - cmdIndex = localMaxIndex; - - //CMD found, stop searching for minCMD: reset the minCmd detection - minBits[0] = 0; - minBits[1] = 0; - minBitCounter=0; - } else { - //reset the maxCmd detection - maxBits[0] = 0; - maxBits[1] = 0; - maxBitCounter=0; - } - } - - //detect local MIN, and update minCmd detection - if (duration < prevduration && duration < period){ - //rising edge above the normal periodduration: update localMax - localMin = duration; - localMinIndex = periodcounter; - } else if (duration > prevduration && localMin < period && periodcounter <= localMinIndex+2) { - //we passed a local minimum, derive length between current and prev max index - if (miniMinDelta == 0){ - prevMinDelta = minDelta; - } - minDelta = (localMinIndex-lastMinIndex)*prescaler; - - //check if there's a miniDelta and if it should be added to previous or next delta - if (miniMinDelta > 0){ - //by default minidelta is added to next - //in case doing this does not result in 2 consequtive valid deltas, but adding it to previous does result in 2 consequtive valid deltas, then assign it to previous delta - if ((lfrfid_hitag_worker_validate_ripple_delta(prevMinDelta+miniMinDelta) + lfrfid_hitag_worker_validate_ripple_delta(minDelta-miniMinDelta)) > (lfrfid_hitag_worker_validate_ripple_delta(prevMinDelta) + lfrfid_hitag_worker_validate_ripple_delta(minDelta))){ - //update the previous (last) bit from 0 to 1 if a command was ongoing - if (minBitCounter > 0){ - minBits[(minBitCounter-1)/32] |= (one >> (minBitCounter-1)%32); - } - minDelta -= miniMinDelta; - lastMinIndex = miniMinIndex; - } - miniMinDelta = 0; - } - - //now process the (possibly updated) delta - if (minDelta >= 18){ - if (18 <= minDelta && minDelta <= 22){ - minBitCounter ++; - } else if (26 <= minDelta && minDelta <= 32){ - minBits[(minBitCounter)/32] |= (one >> (minBitCounter)%32); - minBitCounter ++; - } else if (minBitCounter > 0){ - //end of bitstring caused by invalid delta - if (lfrfid_hitag_worker_validate_bits(minBits, minBitCounter)){ - cmd = -1; - cmdIndex = localMinIndex; - - //CMD found, stop searching for maxCMD: reset the maxCmd detection - maxBits[0] = 0; - maxBits[1] = 0; - maxBitCounter=0; - } else { - //reset the minCmd detection - minBits[0] = 0; - minBits[1] = 0; - minBitCounter = 0; - } - } - lastMinIndex = localMinIndex; - } else { - miniMinDelta = minDelta; - miniMinIndex = localMinIndex; - } - localMin = 255; - } - - //terminate of bitstring caused by absence of next delta (a true end) - if (periodcounter == lastMinIndex + 40/prescaler && minBitCounter >0){ - if (lfrfid_hitag_worker_validate_bits(minBits, minBitCounter)){ - cmd = -1; - cmdIndex = localMinIndex; - - //CMD found, stop searching for maxCMD: reset the maxCmd detection - maxBits[0] = 0; - maxBits[1] = 0; - maxBitCounter=0; - } else { - //reset the minCmd detection - minBits[0] = 0; - minBits[1] = 0; - minBitCounter = 0; - } - } - - //if a valid cmd has been detected: process it & reply - if (cmd != 0 ){ - uint32_t bits[5]; - uint8_t size = 0; - //select the cmd bit source (max or min detection) - if (cmd == 1){ - bits[0] = maxBits[0]; - bits[1] = maxBits[1]; - size = maxBitCounter; - - maxBits[0] = 0; - maxBits[1] = 0; - maxBitCounter = 0; - } else { - bits[0] = minBits[0]; - bits[1] = minBits[1]; - size = minBitCounter; - - minBits[0] = 0; - minBits[1] = 0; - minBitCounter = 0; - } - cmd = 0; - - dmaLen = 0; - if (size == 5 && (bits[0]>>(32-5)) == 0b11001){ //SET_CCNEW - if (tagState != HitagStateQuiet){ - //reset selected state - tagState = HitagStateIdle; - - //set tag in advanced mode - tagMode = HitagModeAdvanced; - - //prepare emulate DMA buffer with advanced SN reply (page 0) - dataReply->replyvalue[0] = 0b00000111; - dataReply->replytype[0] = HITAG_STARTBIT3; - dataReply->replyvalue[1] = worker->tag->pageData[0]; - dataReply->replytype[1] = HITAG_BYTE; - dataReply->replyvalue[2] = worker->tag->pageData[1]; - dataReply->replytype[2] = HITAG_BYTE; - dataReply->replyvalue[3] = worker->tag->pageData[2]; - dataReply->replytype[3] = HITAG_BYTE; - dataReply->replyvalue[4] = worker->tag->pageData[3]; - dataReply->replytype[4] = HITAG_BYTE; - - dataReply->replylength=5; - - dmaLen = lfrfid_hitag_worker_yield_dma_buffer_AC(dataReply, dataDMA, worker->carrierPrescaler); - } - } - else if (size == 5 && (bits[0]>>(32-5)) == 0b00110){ //SET_CC - if (tagState != HitagStateQuiet){ - //reset selected state - tagState = HitagStateIdle; - - //do not (re)set tag in basic mode, this only happens during power on reset - - //prepare emulate DMA buffer with basic SN reply (page 0) - dataReply->replyvalue[0] = 0b00000001; - dataReply->replytype[0] = HITAG_STARTBIT; - dataReply->replyvalue[1] = worker->tag->pageData[0]; - dataReply->replytype[1] = HITAG_BYTE; - dataReply->replyvalue[2] = worker->tag->pageData[1]; - dataReply->replytype[2] = HITAG_BYTE; - dataReply->replyvalue[3] = worker->tag->pageData[2]; - dataReply->replytype[3] = HITAG_BYTE; - dataReply->replyvalue[4] = worker->tag->pageData[3]; - dataReply->replytype[4] = HITAG_BYTE; - - dataReply->replylength=5; - - dmaLen = lfrfid_hitag_worker_yield_dma_buffer_AC(dataReply, dataDMA, worker->carrierPrescaler); - } - } - else if (size == 45 && (bits[0]>>(32-5)) == 0b00000){ //SELECT - //check if SN is matching - uint32_t SN_cmd = bits[0]<<5 | bits[1]>>(32-5); - uint32_t SN_tag = worker->tag->pageData[0] << 24 | - worker->tag->pageData[1] << 16 | - worker->tag->pageData[2] << 8 | - worker->tag->pageData[3]; - if (SN_cmd == SN_tag){ - //reply with config (page 1 from tag memory) - tagState = HitagStateSelected; - - //prepare emulate DMA buffer with basic/advanced config page reply (page 1) - if (tagMode == HitagModeBasic){ - dataReply->replyvalue[0] = 0b00000001; - dataReply->replytype[0] = HITAG_STARTBIT; - - dataReply->replyvalue[1] = worker->tag->pageData[4+0]; - dataReply->replytype[1] = HITAG_BYTE; - dataReply->replyvalue[2] = worker->tag->pageData[4+1]; - dataReply->replytype[2] = HITAG_BYTE; - dataReply->replyvalue[3] = worker->tag->pageData[4+2]; - dataReply->replytype[3] = HITAG_BYTE; - dataReply->replyvalue[4] = worker->tag->pageData[4+3]; - dataReply->replytype[4] = HITAG_BYTE; - - dataReply->replylength=5; - } else if (tagMode == HitagModeAdvanced){ - dataReply->replyvalue[0] = 0b00111111; - dataReply->replytype[0] = HITAG_STARTBIT6; - - dataReply->replyvalue[1] = worker->tag->pageData[4+0]; - dataReply->replytype[1] = HITAG_BYTE; - dataReply->replyvalue[2] = worker->tag->pageData[4+1]; - dataReply->replytype[2] = HITAG_BYTE; - dataReply->replyvalue[3] = worker->tag->pageData[4+2]; - dataReply->replytype[3] = HITAG_BYTE; - dataReply->replyvalue[4] = worker->tag->pageData[4+3]; - dataReply->replytype[4] = HITAG_BYTE; - - uint8_t crc = CRC_PRESET; - lfrfid_hitag_worker_calc_crc(&crc, dataReply->replyvalue[1], 8); - lfrfid_hitag_worker_calc_crc(&crc, dataReply->replyvalue[2], 8); - lfrfid_hitag_worker_calc_crc(&crc, dataReply->replyvalue[3], 8); - lfrfid_hitag_worker_calc_crc(&crc, dataReply->replyvalue[4], 8); - - dataReply->replyvalue[5] = crc; - dataReply->replytype[5] = HITAG_CRC; - - dataReply->replylength=6; - } - - dmaLen = lfrfid_hitag_worker_yield_dma_buffer_MC(dataReply, dataDMA, worker->carrierPrescaler); - } - } - else if (size == 20 && (bits[0]>>(31))){ //R/W - if (tagState == HitagStateSelected){ - //reply with requested pages in case of read public (ignore secret block/pages & ignore write cmd) - uint8_t rwCmd = bits[0]>>(32-4); - uint8_t addr = bits[0]>>20 & 0xff; - uint8_t pages = 0; - switch (rwCmd){ - case (0b1100): - //read public page - if (worker->tag->pageKnown[addr] && worker->tag->pagePublic[addr]){ - pages = 1; - } - break; - case (0b1101): - //read public block - pages = (64-addr)%4; - if (pages==0){ - pages=4; - } - for (uint8_t p=addr;ptag->pageKnown[p] || !worker->tag->pagePublic[p]){ - pages=0; - break; - } - } - break; - } - if (pages>0){ - //pages - for (uint8_t i=0;ireplyvalue[1+i*4+0] = worker->tag->pageData[(addr+i)*4+0]; - dataReply->replytype[1+i*4+0] = HITAG_BYTE; - dataReply->replyvalue[1+i*4+1] = worker->tag->pageData[(addr+i)*4+1]; - dataReply->replytype[1+i*4+1] = HITAG_BYTE; - dataReply->replyvalue[1+i*4+2] = worker->tag->pageData[(addr+i)*4+2]; - dataReply->replytype[1+i*4+2] = HITAG_BYTE; - dataReply->replyvalue[1+i*4+3] = worker->tag->pageData[(addr+i)*4+3]; - dataReply->replytype[1+i*4+3] = HITAG_BYTE; - - } - //startsequence & CRC - if (tagMode == HitagModeBasic){ - dataReply->replyvalue[0] = 0b00000001; - dataReply->replytype[0] = HITAG_STARTBIT; - - dataReply->replylength=1+pages*4; - } else if (tagMode == HitagModeAdvanced){ - dataReply->replyvalue[0] = 0b00111111; - dataReply->replytype[0] = HITAG_STARTBIT6; - - uint8_t crc = CRC_PRESET; - - for (uint8_t i=0;ireplyvalue[1+i*4+0], 8); - lfrfid_hitag_worker_calc_crc(&crc, dataReply->replyvalue[1+i*4+1], 8); - lfrfid_hitag_worker_calc_crc(&crc, dataReply->replyvalue[1+i*4+2], 8); - lfrfid_hitag_worker_calc_crc(&crc, dataReply->replyvalue[1+i*4+3], 8); - } - - dataReply->replyvalue[1+pages*4] = crc; - dataReply->replytype[1+pages*4] = HITAG_CRC; - - dataReply->replylength=2+pages*4; - } - - dmaLen = lfrfid_hitag_worker_yield_dma_buffer_MC(dataReply, dataDMA, worker->carrierPrescaler); - } - } - } - else if (size == 20 && (bits[0]>>(32-4)) == 0b0111){ //HALT - //TODO validate if dummy address is a valid one (one of the default public pages (32-63)) - - //acknowledge & go silent untill next reset - if (tagState == HitagStateSelected){ - tagState = HitagStateQuiet; - - if (tagMode == HitagModeBasic){ - dataReply->replyvalue[0] = 0b00000101; - dataReply->replytype[0] = HITAG_ACK; - } else if (tagMode == HitagModeAdvanced){ - dataReply->replyvalue[0] = 0b11111101; - dataReply->replytype[0] = HITAG_ACK_ADV; - } - - dataReply->replylength=1; - - dmaLen = lfrfid_hitag_worker_yield_dma_buffer_MC(dataReply, dataDMA, worker->carrierPrescaler); - } - } - - if (dmaLen > 0){ //switch to reply mode - //switch carrier mode to ETR required for emulation - lfrfid_hitag_worker_carrier_in_ETR_mode(capData, prescaler); - - //respect hitag WAIT1 time - uint32_t wait1_periods = 180/prescaler; - while (capData->capCounter-cmdIndex < wait1_periods){ - furi_delay_us(40); - } - - //just start the DMA, stopping is handled in DMA transfer complete interrupt - lfrfid_hitag_worker_pull_out_dma_start((size_t) dmaLen); - } - } - } - } - } - } - - //reset buffer - buffer_reset(buffer); - } + capData->capCounter = 0; + capData->prevTIMval = 0; + + //init dmaData for emulate mode: + uint16_t dmaLen = 0; + TimerDMAData* dataDMA = malloc(sizeof(TimerDMAData)); + + LfRfidHitagReplyData* dataReply = malloc(sizeof(LfRfidHitagReplyData)); + + // setup pins for emulating + // ibutton low + furi_hal_ibutton_pin_configure(); + furi_hal_ibutton_pin_write(false); + // comparator in + furi_hal_gpio_init(&gpio_rfid_data_in, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + // carrier_out to fixed low state + furi_hal_gpio_init(&gpio_rfid_carrier_out, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_write(&gpio_rfid_carrier_out, false); + // PA15 carrier_in to alt ftn 1 (TIM2 CH1) to use it for input capture + furi_hal_gpio_init_ex( + &gpio_rfid_carrier, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedLow, GpioAltFn1TIM2); + // pull to alternate function 1 (TIM2 CH3) to drive low (for read carrier) or pull antenna via DMA (for emulation) + furi_hal_gpio_init_ex( + &gpio_nfc_irq_rfid_pull, + GpioModeAltFunctionPushPull, + GpioPullNo, + GpioSpeedLow, + GpioAltFn1TIM2); + + //start capture via carrier in instead of rfid in + lfrfid_hitag_worker_carrier_in_start( + capData, + worker->carrierPrescaler, + dataDMA->timer_buffer_arr, + dataDMA->timer_buffer_ccr, + dmaLen); + + uint8_t prescaler = worker->carrierPrescaler; + uint8_t period = 8 * prescaler - 1; + + bool carrier_on = false; + int8_t carrier_count = 0; + uint8_t carrier_threshold_on = 5; + int8_t carrier_threshold_off = -5; + + uint32_t periodcounter = 0; + + uint8_t localMax = 0; + uint8_t localMin = 255; + uint32_t localMaxIndex = 0; + uint32_t localMinIndex = 0; + uint32_t lastMaxIndex = 0; + uint32_t lastMinIndex = 0; + uint8_t maxBitCounter = 0; + uint8_t minBitCounter = 0; + uint32_t maxBits[5]; + uint32_t minBits[5]; + int8_t cmd = 0; + uint32_t one = 1 << (32 - 1); + uint32_t cmdIndex = 0; + + uint32_t miniMaxDelta = 0; + uint32_t miniMaxIndex = 0; + uint32_t prevMaxDelta = 0; + uint32_t maxDelta = 0; + + uint32_t miniMinDelta = 0; + uint32_t miniMinIndex = 0; + uint32_t prevMinDelta = 0; + uint32_t minDelta = 0; + + uint32_t prevduration = 0; + uint32_t duration = 0; + uint8_t durationcounter = 0; + uint8_t durationthreshold = 5; + + uint8_t tagState = HitagStateIdle; + uint8_t tagMode = HitagModeBasic; + + while(1) { + Buffer* buffer = buffer_stream_receive(capData->stream, 1); + + if(buffer != NULL) { + size_t buffsize = buffer_get_size(buffer); + uint8_t* buffdata = buffer_get_data(buffer); + + //parse carrier_in duration & scan for cmds + size_t index = 0; + while(index < buffsize) { + prevduration = duration; + size_t tmp_size = + varint_uint32_unpack(&duration, &buffdata[index], buffsize - index); + + if(tmp_size == 0) { + FURI_LOG_E(TAG, "can't unpack varint pair"); + break; + } else { + index += tmp_size; + + periodcounter++; + + if(!carrier_on) { //detect if carrier is available + if(0.75 * period <= duration && duration <= 1.25 * period) { + carrier_count++; + if(carrier_count >= carrier_threshold_on) { + carrier_on = true; + localMax = 0; + localMin = 255; + minBitCounter = 0; + maxBitCounter = 0; + minBits[0] = 0; + minBits[1] = 0; + maxBits[0] = 0; + maxBits[1] = 0; + + carrier_count = 0; + //TODO reset tagstate to Idle & tagMode to basic after power_on_reset (no carrier for 10ms) + } + } else { + carrier_count = 0; + } + } else { + //detect if carrier is still available + if(duration <= 0.75 * period || 1.25 * period <= duration) { + carrier_count--; + if(carrier_count <= carrier_threshold_off) { + carrier_on = false; + carrier_count = 0; + } + } else { + carrier_count = 0; + } + + if(carrier_on) { //if carrier is still available + //finetune the average period for reference + if(prevduration == duration) { + durationcounter++; + if(durationcounter == durationthreshold && period != duration) { + period = duration; + } + } else { + durationcounter = 0; + } + + //detect local MAX, and update maxCmd detection + if(duration > prevduration && duration > period) { + //rising edge above the normal periodduration: update localMax + localMax = duration; + localMaxIndex = periodcounter; + } else if( + duration < prevduration && localMax > period && + periodcounter <= localMaxIndex + 2) { + //we passed a local maximum, derive length between current and prev max index + if(miniMaxDelta == 0) { + prevMaxDelta = maxDelta; + } + maxDelta = (localMaxIndex - lastMaxIndex) * prescaler; + + //check if there's a miniDelta and if it should be added to previous or next delta + if(miniMaxDelta > 0) { + //by default minidelta is added to next + //in case doing this does not result in 2 consequtive valid deltas, but adding it to previous does result in 2 consequtive valid deltas, then assign it to previous delta + if((lfrfid_hitag_worker_validate_ripple_delta( + prevMaxDelta + miniMaxDelta) + + lfrfid_hitag_worker_validate_ripple_delta( + maxDelta - miniMaxDelta)) > + (lfrfid_hitag_worker_validate_ripple_delta(prevMaxDelta) + + lfrfid_hitag_worker_validate_ripple_delta(maxDelta))) { + //update the previous (last) bit from 0 to 1 if a command was ongoing + if(maxBitCounter > 0) { + maxBits[(maxBitCounter - 1) / 32] |= + (one >> (maxBitCounter - 1) % 32); + } + maxDelta -= miniMaxDelta; + lastMaxIndex = miniMaxIndex; + } + miniMaxDelta = 0; + } + + //now process the (possibly updated) delta + if(maxDelta >= 18) { + if(18 <= maxDelta && maxDelta <= 22) { + maxBitCounter++; + } else if(26 <= maxDelta && maxDelta <= 32) { + maxBits[(maxBitCounter) / 32] |= + (one >> (maxBitCounter) % 32); + maxBitCounter++; + } else if(maxBitCounter > 0) { + //end of bitstring caused by invalid delta + + if(lfrfid_hitag_worker_validate_bits( + maxBits, maxBitCounter)) { + cmd = 1; + cmdIndex = localMaxIndex; + + //CMD found, stop searching for minCMD: reset the minCmd detection + minBits[0] = 0; + minBits[1] = 0; + minBitCounter = 0; + } else { + //reset the maxCmd detection + maxBits[0] = 0; + maxBits[1] = 0; + maxBitCounter = 0; + } + } + lastMaxIndex = localMaxIndex; + } else { + miniMaxDelta = maxDelta; + miniMaxIndex = localMaxIndex; + } + localMax = 0; + } + + //terminate bitstring ico absence of next delta (a true end) + if(periodcounter == lastMaxIndex + 40 / prescaler && + maxBitCounter > 0) { + if(lfrfid_hitag_worker_validate_bits(maxBits, maxBitCounter)) { + cmd = 1; + cmdIndex = localMaxIndex; + + //CMD found, stop searching for minCMD: reset the minCmd detection + minBits[0] = 0; + minBits[1] = 0; + minBitCounter = 0; + } else { + //reset the maxCmd detection + maxBits[0] = 0; + maxBits[1] = 0; + maxBitCounter = 0; + } + } + + //detect local MIN, and update minCmd detection + if(duration < prevduration && duration < period) { + //rising edge above the normal periodduration: update localMax + localMin = duration; + localMinIndex = periodcounter; + } else if( + duration > prevduration && localMin < period && + periodcounter <= localMinIndex + 2) { + //we passed a local minimum, derive length between current and prev max index + if(miniMinDelta == 0) { + prevMinDelta = minDelta; + } + minDelta = (localMinIndex - lastMinIndex) * prescaler; + + //check if there's a miniDelta and if it should be added to previous or next delta + if(miniMinDelta > 0) { + //by default minidelta is added to next + //in case doing this does not result in 2 consequtive valid deltas, but adding it to previous does result in 2 consequtive valid deltas, then assign it to previous delta + if((lfrfid_hitag_worker_validate_ripple_delta( + prevMinDelta + miniMinDelta) + + lfrfid_hitag_worker_validate_ripple_delta( + minDelta - miniMinDelta)) > + (lfrfid_hitag_worker_validate_ripple_delta(prevMinDelta) + + lfrfid_hitag_worker_validate_ripple_delta(minDelta))) { + //update the previous (last) bit from 0 to 1 if a command was ongoing + if(minBitCounter > 0) { + minBits[(minBitCounter - 1) / 32] |= + (one >> (minBitCounter - 1) % 32); + } + minDelta -= miniMinDelta; + lastMinIndex = miniMinIndex; + } + miniMinDelta = 0; + } + + //now process the (possibly updated) delta + if(minDelta >= 18) { + if(18 <= minDelta && minDelta <= 22) { + minBitCounter++; + } else if(26 <= minDelta && minDelta <= 32) { + minBits[(minBitCounter) / 32] |= + (one >> (minBitCounter) % 32); + minBitCounter++; + } else if(minBitCounter > 0) { + //end of bitstring caused by invalid delta + if(lfrfid_hitag_worker_validate_bits( + minBits, minBitCounter)) { + cmd = -1; + cmdIndex = localMinIndex; + + //CMD found, stop searching for maxCMD: reset the maxCmd detection + maxBits[0] = 0; + maxBits[1] = 0; + maxBitCounter = 0; + } else { + //reset the minCmd detection + minBits[0] = 0; + minBits[1] = 0; + minBitCounter = 0; + } + } + lastMinIndex = localMinIndex; + } else { + miniMinDelta = minDelta; + miniMinIndex = localMinIndex; + } + localMin = 255; + } + + //terminate of bitstring caused by absence of next delta (a true end) + if(periodcounter == lastMinIndex + 40 / prescaler && + minBitCounter > 0) { + if(lfrfid_hitag_worker_validate_bits(minBits, minBitCounter)) { + cmd = -1; + cmdIndex = localMinIndex; + + //CMD found, stop searching for maxCMD: reset the maxCmd detection + maxBits[0] = 0; + maxBits[1] = 0; + maxBitCounter = 0; + } else { + //reset the minCmd detection + minBits[0] = 0; + minBits[1] = 0; + minBitCounter = 0; + } + } + + //if a valid cmd has been detected: process it & reply + if(cmd != 0) { + uint32_t bits[5]; + uint8_t size = 0; + //select the cmd bit source (max or min detection) + if(cmd == 1) { + bits[0] = maxBits[0]; + bits[1] = maxBits[1]; + size = maxBitCounter; + + maxBits[0] = 0; + maxBits[1] = 0; + maxBitCounter = 0; + } else { + bits[0] = minBits[0]; + bits[1] = minBits[1]; + size = minBitCounter; + + minBits[0] = 0; + minBits[1] = 0; + minBitCounter = 0; + } + cmd = 0; + + dmaLen = 0; + if(size == 5 && (bits[0] >> (32 - 5)) == 0b11001) { //SET_CCNEW + if(tagState != HitagStateQuiet) { + //reset selected state + tagState = HitagStateIdle; + + //set tag in advanced mode + tagMode = HitagModeAdvanced; + + //prepare emulate DMA buffer with advanced SN reply (page 0) + dataReply->replyvalue[0] = 0b00000111; + dataReply->replytype[0] = HITAG_STARTBIT3; + dataReply->replyvalue[1] = worker->tag->pageData[0]; + dataReply->replytype[1] = HITAG_BYTE; + dataReply->replyvalue[2] = worker->tag->pageData[1]; + dataReply->replytype[2] = HITAG_BYTE; + dataReply->replyvalue[3] = worker->tag->pageData[2]; + dataReply->replytype[3] = HITAG_BYTE; + dataReply->replyvalue[4] = worker->tag->pageData[3]; + dataReply->replytype[4] = HITAG_BYTE; + + dataReply->replylength = 5; + + dmaLen = lfrfid_hitag_worker_yield_dma_buffer_AC( + dataReply, dataDMA, worker->carrierPrescaler); + } + } else if(size == 5 && (bits[0] >> (32 - 5)) == 0b00110) { //SET_CC + if(tagState != HitagStateQuiet) { + //reset selected state + tagState = HitagStateIdle; + + //do not (re)set tag in basic mode, this only happens during power on reset + + //prepare emulate DMA buffer with basic SN reply (page 0) + dataReply->replyvalue[0] = 0b00000001; + dataReply->replytype[0] = HITAG_STARTBIT; + dataReply->replyvalue[1] = worker->tag->pageData[0]; + dataReply->replytype[1] = HITAG_BYTE; + dataReply->replyvalue[2] = worker->tag->pageData[1]; + dataReply->replytype[2] = HITAG_BYTE; + dataReply->replyvalue[3] = worker->tag->pageData[2]; + dataReply->replytype[3] = HITAG_BYTE; + dataReply->replyvalue[4] = worker->tag->pageData[3]; + dataReply->replytype[4] = HITAG_BYTE; + + dataReply->replylength = 5; + + dmaLen = lfrfid_hitag_worker_yield_dma_buffer_AC( + dataReply, dataDMA, worker->carrierPrescaler); + } + } else if(size == 45 && (bits[0] >> (32 - 5)) == 0b00000) { //SELECT + //check if SN is matching + uint32_t SN_cmd = bits[0] << 5 | bits[1] >> (32 - 5); + uint32_t SN_tag = worker->tag->pageData[0] << 24 | + worker->tag->pageData[1] << 16 | + worker->tag->pageData[2] << 8 | + worker->tag->pageData[3]; + if(SN_cmd == SN_tag) { + //reply with config (page 1 from tag memory) + tagState = HitagStateSelected; + + //prepare emulate DMA buffer with basic/advanced config page reply (page 1) + if(tagMode == HitagModeBasic) { + dataReply->replyvalue[0] = 0b00000001; + dataReply->replytype[0] = HITAG_STARTBIT; + + dataReply->replyvalue[1] = + worker->tag->pageData[4 + 0]; + dataReply->replytype[1] = HITAG_BYTE; + dataReply->replyvalue[2] = + worker->tag->pageData[4 + 1]; + dataReply->replytype[2] = HITAG_BYTE; + dataReply->replyvalue[3] = + worker->tag->pageData[4 + 2]; + dataReply->replytype[3] = HITAG_BYTE; + dataReply->replyvalue[4] = + worker->tag->pageData[4 + 3]; + dataReply->replytype[4] = HITAG_BYTE; + + dataReply->replylength = 5; + } else if(tagMode == HitagModeAdvanced) { + dataReply->replyvalue[0] = 0b00111111; + dataReply->replytype[0] = HITAG_STARTBIT6; + + dataReply->replyvalue[1] = + worker->tag->pageData[4 + 0]; + dataReply->replytype[1] = HITAG_BYTE; + dataReply->replyvalue[2] = + worker->tag->pageData[4 + 1]; + dataReply->replytype[2] = HITAG_BYTE; + dataReply->replyvalue[3] = + worker->tag->pageData[4 + 2]; + dataReply->replytype[3] = HITAG_BYTE; + dataReply->replyvalue[4] = + worker->tag->pageData[4 + 3]; + dataReply->replytype[4] = HITAG_BYTE; + + uint8_t crc = CRC_PRESET; + lfrfid_hitag_worker_calc_crc( + &crc, dataReply->replyvalue[1], 8); + lfrfid_hitag_worker_calc_crc( + &crc, dataReply->replyvalue[2], 8); + lfrfid_hitag_worker_calc_crc( + &crc, dataReply->replyvalue[3], 8); + lfrfid_hitag_worker_calc_crc( + &crc, dataReply->replyvalue[4], 8); + + dataReply->replyvalue[5] = crc; + dataReply->replytype[5] = HITAG_CRC; + + dataReply->replylength = 6; + } + + dmaLen = lfrfid_hitag_worker_yield_dma_buffer_MC( + dataReply, dataDMA, worker->carrierPrescaler); + } + } else if(size == 20 && (bits[0] >> (31))) { //R/W + if(tagState == HitagStateSelected) { + //reply with requested pages in case of read public (ignore secret block/pages & ignore write cmd) + uint8_t rwCmd = bits[0] >> (32 - 4); + uint8_t addr = bits[0] >> 20 & 0xff; + uint8_t pages = 0; + switch(rwCmd) { + case(0b1100): + //read public page + if(worker->tag->pageKnown[addr] && + worker->tag->pagePublic[addr]) { + pages = 1; + } + break; + case(0b1101): + //read public block + pages = (64 - addr) % 4; + if(pages == 0) { + pages = 4; + } + for(uint8_t p = addr; p < addr + pages; p++) { + if(!worker->tag->pageKnown[p] || + !worker->tag->pagePublic[p]) { + pages = 0; + break; + } + } + break; + } + if(pages > 0) { + //pages + for(uint8_t i = 0; i < pages; i++) { + dataReply->replyvalue[1 + i * 4 + 0] = + worker->tag->pageData[(addr + i) * 4 + 0]; + dataReply->replytype[1 + i * 4 + 0] = HITAG_BYTE; + dataReply->replyvalue[1 + i * 4 + 1] = + worker->tag->pageData[(addr + i) * 4 + 1]; + dataReply->replytype[1 + i * 4 + 1] = HITAG_BYTE; + dataReply->replyvalue[1 + i * 4 + 2] = + worker->tag->pageData[(addr + i) * 4 + 2]; + dataReply->replytype[1 + i * 4 + 2] = HITAG_BYTE; + dataReply->replyvalue[1 + i * 4 + 3] = + worker->tag->pageData[(addr + i) * 4 + 3]; + dataReply->replytype[1 + i * 4 + 3] = HITAG_BYTE; + } + //startsequence & CRC + if(tagMode == HitagModeBasic) { + dataReply->replyvalue[0] = 0b00000001; + dataReply->replytype[0] = HITAG_STARTBIT; + + dataReply->replylength = 1 + pages * 4; + } else if(tagMode == HitagModeAdvanced) { + dataReply->replyvalue[0] = 0b00111111; + dataReply->replytype[0] = HITAG_STARTBIT6; + + uint8_t crc = CRC_PRESET; + + for(uint8_t i = 0; i < pages; i++) { + lfrfid_hitag_worker_calc_crc( + &crc, + dataReply->replyvalue[1 + i * 4 + 0], + 8); + lfrfid_hitag_worker_calc_crc( + &crc, + dataReply->replyvalue[1 + i * 4 + 1], + 8); + lfrfid_hitag_worker_calc_crc( + &crc, + dataReply->replyvalue[1 + i * 4 + 2], + 8); + lfrfid_hitag_worker_calc_crc( + &crc, + dataReply->replyvalue[1 + i * 4 + 3], + 8); + } + + dataReply->replyvalue[1 + pages * 4] = crc; + dataReply->replytype[1 + pages * 4] = HITAG_CRC; + + dataReply->replylength = 2 + pages * 4; + } + + dmaLen = lfrfid_hitag_worker_yield_dma_buffer_MC( + dataReply, dataDMA, worker->carrierPrescaler); + } + } + } else if(size == 20 && (bits[0] >> (32 - 4)) == 0b0111) { //HALT + //TODO validate if dummy address is a valid one (one of the default public pages (32-63)) + + //acknowledge & go silent untill next reset + if(tagState == HitagStateSelected) { + tagState = HitagStateQuiet; + + if(tagMode == HitagModeBasic) { + dataReply->replyvalue[0] = 0b00000101; + dataReply->replytype[0] = HITAG_ACK; + } else if(tagMode == HitagModeAdvanced) { + dataReply->replyvalue[0] = 0b11111101; + dataReply->replytype[0] = HITAG_ACK_ADV; + } + + dataReply->replylength = 1; + + dmaLen = lfrfid_hitag_worker_yield_dma_buffer_MC( + dataReply, dataDMA, worker->carrierPrescaler); + } + } + + if(dmaLen > 0) { //switch to reply mode + //switch carrier mode to ETR required for emulation + lfrfid_hitag_worker_carrier_in_ETR_mode(capData, prescaler); + + //respect hitag WAIT1 time + uint32_t wait1_periods = 180 / prescaler; + while(capData->capCounter - cmdIndex < wait1_periods) { + furi_delay_us(40); + } + + //just start the DMA, stopping is handled in DMA transfer complete interrupt + lfrfid_hitag_worker_pull_out_dma_start((size_t)dmaLen); + } + } + } + } + } + } + + //reset buffer + buffer_reset(buffer); + } - if(buffer_stream_get_overrun_count(capData->stream) > 0) { - FURI_LOG_E(TAG, "Read overrun, recovering"); + if(buffer_stream_get_overrun_count(capData->stream) > 0) { + FURI_LOG_E(TAG, "Read overrun, recovering"); buffer_stream_reset(capData->stream); - } - - uint32_t flags = furi_event_flag_get(worker->events); - if(FURI_BIT(flags, LFRFIDHitagWorkerSignalStop)) { - break; - } - } + } - //stop carrier capture in - lfrfid_hitag_worker_carrier_in_stop(); - - free(worker->tag); - free(dataDMA); - free(dataReply); - varint_pair_free(capData->pair); + uint32_t flags = furi_event_flag_get(worker->events); + if(FURI_BIT(flags, LFRFIDHitagWorkerSignalStop)) { + break; + } + } + + //stop carrier capture in + lfrfid_hitag_worker_carrier_in_stop(); + + free(worker->tag); + free(dataDMA); + free(dataReply); + varint_pair_free(capData->pair); buffer_stream_free(capData->stream); free(capData); - + //clear the stop flag on workder so that i can use the worker to launch a new read routine without having to restart the app - furi_event_flag_clear(worker->events, 1 << LFRFIDHitagWorkerSignalStop); + furi_event_flag_clear(worker->events, 1 << LFRFIDHitagWorkerSignalStop); return 0; } static int32_t lfrfid_hitag_worker_read_thread(void* thread_context) { - LFRFIDHitagWorker* worker = (LFRFIDHitagWorker*)thread_context; - - //initialize a tag datastructure - worker->tag = malloc(sizeof(LFRFIDHitag)); - lfrfid_hitag_worker_initialize_tag_data(worker->tag); - - //prep variables for decoder - LfRfidHitagProtocol currentProtocol = HitagProtocolAntiCollision; - uint8_t startBits = 1; - uint32_t memBlock[HITAG_BLOCKPAGES] = {0}; - uint8_t bits = 0; - bool halfBit = false; - bool finished = false; - - //prep variables for worker - uint8_t expectedBits = 33; - uint8_t readings = 0; - uint8_t currPage = 0; - bool readSucces = false; - uint8_t readThreshold = 3; - - // setup pins for reading - furi_hal_rfid_pins_read(); - - //and setup TIM1 CH1N with DMA - //fill the DMA arr & ccr buffers - /*start with looping SETCC command*/ - LfRfidHitagCMDData* dataCMD = malloc(sizeof(LfRfidHitagCMDData)); - dataCMD->CMDlength = 0; - dataCMD->CMDposition = 0; - dataCMD->CMDsubposition = 0; - dataCMD->CMDactive = false; - - LfRfidHitagCMDData* backupCMD = malloc(sizeof(LfRfidHitagCMDData)); - uint32_t value[] = {128, 0b00110, 1, 213, (uint32_t)(1.05*33*HITAG_BITPERIODS_AC)}; //always take 5% reading time buffer - uint8_t type[] = {HITAG_ON, HITAG_SET, HITAG_STOP, HITAG_ON, HITAG_ON}; - for (size_t i = 0;i<5;i++){ - backupCMD->CMDvalue[i]=value[i]; - backupCMD->CMDtype[i]=type[i]; - } - backupCMD->CMDlength=5; - backupCMD->CMDposition=0; - backupCMD->CMDsubposition=0; - backupCMD->CMDloop = true; //not really required since by definition the backup cmd is looping already in yield code - backupCMD->CMDactive = true; - - TimerDMAData* dataDMA = malloc(sizeof(TimerDMAData)); - lfrfid_hitag_worker_yield_dma_buffer_BPLM(dataCMD, backupCMD, dataDMA, 0, DMA_BUFFER_SIZE/2); - lfrfid_hitag_worker_yield_dma_buffer_BPLM(dataCMD, backupCMD, dataDMA, DMA_BUFFER_SIZE/2, DMA_BUFFER_SIZE/2); - - //start TIM1 with DMA function - lfrfid_hitag_worker_carrier_out_start( - dataDMA->timer_buffer_arr, - dataDMA->timer_buffer_ccr, - DMA_BUFFER_SIZE, - worker); - - // start capture - LfRfidHitagCaptureData* capData = malloc(sizeof(LfRfidHitagCaptureData)); - capData->stream = buffer_stream_alloc(READ_BUFFER_SIZE, READ_BUFFER_COUNT); + LFRFIDHitagWorker* worker = (LFRFIDHitagWorker*)thread_context; + + //initialize a tag datastructure + worker->tag = malloc(sizeof(LFRFIDHitag)); + lfrfid_hitag_worker_initialize_tag_data(worker->tag); + + //prep variables for decoder + LfRfidHitagProtocol currentProtocol = HitagProtocolAntiCollision; + uint8_t startBits = 1; + uint32_t memBlock[HITAG_BLOCKPAGES] = {0}; + uint8_t bits = 0; + bool halfBit = false; + bool finished = false; + + //prep variables for worker + uint8_t expectedBits = 33; + uint8_t readings = 0; + uint8_t currPage = 0; + bool readSucces = false; + uint8_t readThreshold = 3; + + // setup pins for reading + // ibutton low + furi_hal_ibutton_pin_configure(); + furi_hal_ibutton_pin_write(false); + // dont pull rfid antenna + furi_hal_gpio_init(&gpio_nfc_irq_rfid_pull, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_write(&gpio_nfc_irq_rfid_pull, false); + // carrier pin to timer out + furi_hal_gpio_init_ex( + &gpio_rfid_carrier_out, + GpioModeAltFunctionPushPull, + GpioPullNo, + GpioSpeedLow, + GpioAltFn1TIM1); + // comparator in + furi_hal_gpio_init(&gpio_rfid_data_in, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + + //and setup TIM1 CH1N with DMA + //fill the DMA arr & ccr buffers + /*start with looping SETCC command*/ + LfRfidHitagCMDData* dataCMD = malloc(sizeof(LfRfidHitagCMDData)); + dataCMD->CMDlength = 0; + dataCMD->CMDposition = 0; + dataCMD->CMDsubposition = 0; + dataCMD->CMDactive = false; + + LfRfidHitagCMDData* backupCMD = malloc(sizeof(LfRfidHitagCMDData)); + uint32_t value[] = { + 128, + 0b00110, + 1, + 213, + (uint32_t)(1.05 * 33 * HITAG_BITPERIODS_AC)}; //always take 5% reading time buffer + uint8_t type[] = {HITAG_ON, HITAG_SET, HITAG_STOP, HITAG_ON, HITAG_ON}; + for(size_t i = 0; i < 5; i++) { + backupCMD->CMDvalue[i] = value[i]; + backupCMD->CMDtype[i] = type[i]; + } + backupCMD->CMDlength = 5; + backupCMD->CMDposition = 0; + backupCMD->CMDsubposition = 0; + backupCMD->CMDloop = + true; //not really required since by definition the backup cmd is looping already in yield code + backupCMD->CMDactive = true; + + TimerDMAData* dataDMA = malloc(sizeof(TimerDMAData)); + lfrfid_hitag_worker_yield_dma_buffer_BPLM(dataCMD, backupCMD, dataDMA, 0, DMA_BUFFER_SIZE / 2); + lfrfid_hitag_worker_yield_dma_buffer_BPLM( + dataCMD, backupCMD, dataDMA, DMA_BUFFER_SIZE / 2, DMA_BUFFER_SIZE / 2); + + //start TIM1 with DMA function + lfrfid_hitag_worker_carrier_out_start( + dataDMA->timer_buffer_arr, dataDMA->timer_buffer_ccr, DMA_BUFFER_SIZE, worker); + + // start capture + LfRfidHitagCaptureData* capData = malloc(sizeof(LfRfidHitagCaptureData)); + capData->stream = buffer_stream_alloc(READ_BUFFER_SIZE, READ_BUFFER_COUNT); capData->pair = varint_pair_alloc(); - furi_hal_rfid_tim_read_capture_start(lfrfid_hitag_worker_capture_in_cc_isr, capData); - - while(1) { - Buffer* buffer = buffer_stream_receive(capData->stream, 1); - - //process data if available - if(buffer != NULL) { - size_t buffsize = buffer_get_size(buffer); - uint8_t* buffdata = buffer_get_data(buffer); - - //if scanning for tagdata, then also parse buffer data - size_t index = 0; - while(expectedBits > 0 && index < buffsize) { - uint32_t duration; - uint32_t pulse; - size_t tmp_size; - - if(!varint_pair_unpack(&buffdata[index], buffsize - index, &pulse, &duration, &tmp_size)) { - FURI_LOG_E(TAG, "can't unpack varint pair"); - break; - } else { - index += tmp_size; - - //feed pulse duration to decoder - lfrfid_hitag_worker_decoder_feed(pulse, duration, memBlock, &bits, &halfBit, &finished, currentProtocol, startBits); - - //see if i have a valid bit sequence - if (finished ){ - //bitsequence ready for validation - if (bits == expectedBits){ //eligable bitsequence detected, accept it only after x consequtive readings - //reset the backupCMD count to ensure some pause between bitsequence detection & the CMD injection - backupCMD->CMDcount = 0; - - if (readings == 0){ - for (uint8_t p=0;p<(expectedBits-1)/32;p++){ - worker->tag->pageData[(currPage+p)*4+0] = memBlock[p]>>24 & 0xff; - worker->tag->pageData[(currPage+p)*4+1] = memBlock[p]>>16 & 0xff; - worker->tag->pageData[(currPage+p)*4+2] = memBlock[p]>>8 & 0xff; - worker->tag->pageData[(currPage+p)*4+3] = memBlock[p] & 0xff; - worker->tag->pageKnown[currPage+p] = 0; - } - readings=1; - } else if (readings < readThreshold){ - bool match = true; - for (uint8_t p=0;p<(expectedBits-1)/32;p++){ - if (worker->tag->pageData[(currPage+p)*4+0] != (memBlock[p]>>24 & 0xff) || - worker->tag->pageData[(currPage+p)*4+1] != (memBlock[p]>>16 & 0xff) || - worker->tag->pageData[(currPage+p)*4+2] != (memBlock[p]>>8 & 0xff) || - worker->tag->pageData[(currPage+p)*4+3] != (memBlock[p] & 0xff)){ - match = false; - break; - } - } - if(match){ - readings++; - if (readings==readThreshold){ - //succesful page(s) reading - readSucces = true; - - for (uint8_t p=0;p<(expectedBits-1)/32;p++){ - worker->tag->pageKnown[currPage+p] = 1; - } - - readings = 0; - expectedBits = 0; //to prevent further updates to tag data - } - } else { - for (uint8_t p=0;p<(expectedBits-1)/32;p++){ - worker->tag->pageData[(currPage+p)*4+0] = memBlock[p]>>24 & 0xff; - worker->tag->pageData[(currPage+p)*4+1] = memBlock[p]>>16 & 0xff; - worker->tag->pageData[(currPage+p)*4+2] = memBlock[p]>>8 & 0xff; - worker->tag->pageData[(currPage+p)*4+3] = memBlock[p] & 0xff; - worker->tag->pageKnown[currPage+p] = 0; - } - readings=1; - } - } - } - //reset tempdata for next bitsequence readout - memset(memBlock, 0, HITAG_BLOCKPAGES*sizeof(memBlock[0])); - bits = 0; - finished = false; - } - } - } - - //reset buffer - buffer_reset(buffer); - } - - - //if bitsequence found & after pause, prep & launch the command for reading out next bit of data - if (readSucces && backupCMD->CMDcount>=7){ - readSucces=false; - backupCMD->CMDcount=0;//required since otherwise the 'relaunch' of cmd is triggered straightaway - - //update protocolData - size_t data_size = protocol_dict_get_data_size(worker->dict, LFRFIDProtocolHitag1); - lfrfid_hitag_worker_combine_tag_data(worker->tag); - protocol_dict_set_data(worker->dict, LFRFIDProtocolHitag1, worker->tag->tagData, data_size); - - if (currPage==0){ //read config page next via SELECT cmd - FURI_LOG_D(TAG, "Hitag1, [%02X %02X %02X %02X] found", worker->tag->pageData[0], worker->tag->pageData[1], worker->tag->pageData[2], worker->tag->pageData[3]); - DOLPHIN_DEED(DolphinDeedRfidReadSuccess); - worker->workerstatus = LFRFIDHitagStatusDetected; - - currentProtocol = HitagProtocolManchesterCoding; - expectedBits=33; - currPage=1; - } - else if (currPage == 1){ //process config page and start reading public blocks - lfrfid_hitag_worker_process_config_page(worker->tag); - FURI_LOG_D(TAG, "Hitag1, [%02X %02X %02X %02X] config page read", worker->tag->pageData[0], worker->tag->pageData[1], worker->tag->pageData[2], worker->tag->pageData[3]); - //find next public readible block - for (currPage=4;currPage<=HITAG_PAGES-4;currPage+=4){ - if (worker->tag->pagePublic[currPage] && (worker->tag->pageRW[currPage] & READ) ){ //public readible page/block found - expectedBits=129; - break; - } else { - FURI_LOG_D(TAG, "Hitag1, [%02X %02X %02X %02X] block %u not public/readible", worker->tag->pageData[0], worker->tag->pageData[1], worker->tag->pageData[2], worker->tag->pageData[3], currPage/4); - } - } - } - else if (currPagetag->pageData[0], worker->tag->pageData[1], worker->tag->pageData[2], worker->tag->pageData[3], currPage/4); - for (currPage+=4;currPage<=HITAG_PAGES-4;currPage+=4){ - if (worker->tag->pagePublic[currPage] && (worker->tag->pageRW[currPage] & READ) ){ //public readible page/block found - expectedBits=129; - break; - } else { - FURI_LOG_D(TAG, "Hitag1, [%02X %02X %02X %02X] block %u not public/readible", worker->tag->pageData[0], worker->tag->pageData[1], worker->tag->pageData[2], worker->tag->pageData[3], currPage/4); - } - } - } - else if (currPage==HITAG_PAGES-4){ //all public readible pages read: signal view & break out of while loop (stops the worker) - FURI_LOG_D(TAG, "Hitag1, [%02X %02X %02X %02X] block %u read", worker->tag->pageData[0], worker->tag->pageData[1], worker->tag->pageData[2], worker->tag->pageData[3], currPage/4); - - worker->workerstatus = LFRFIDHitagStatusRead; - break; - } - - if (expectedBits>0){ //inject CMD - //prep SELECT cmd bytes - uint8_t SN0 = worker->tag->pageData[0]; - uint8_t SN1 = worker->tag->pageData[1]; - uint8_t SN2 = worker->tag->pageData[2]; - uint8_t SN3 = worker->tag->pageData[3]; - uint8_t crc = CRC_PRESET; - lfrfid_hitag_worker_calc_crc(&crc, (0b00000<<(8-5)), 5); - lfrfid_hitag_worker_calc_crc(&crc, SN0, 8); - lfrfid_hitag_worker_calc_crc(&crc, SN1, 8); - lfrfid_hitag_worker_calc_crc(&crc, SN2, 8); - lfrfid_hitag_worker_calc_crc(&crc, SN3, 8); - - //prep SELECT cmd array - uint32_t value[] = {128, 0b00000, SN0, SN1, SN2, SN3, crc, 1, 213, (uint32_t)(1.05*33*HITAG_BITPERIODS_MC)}; //always take 5% reading time buffer - uint8_t type[] = {HITAG_ON, HITAG_SELECT, HITAG_BYTE, HITAG_BYTE, HITAG_BYTE, HITAG_BYTE, HITAG_CRC, HITAG_STOP, HITAG_ON, HITAG_ON}; - uint16_t len = 10; - uint16_t cnt = 1; - for (size_t i = 0;iCMDvalue[i]=value[i%len]; - dataCMD->CMDtype[i]=type[i%len]; - } - dataCMD->CMDlength=cnt*len; - dataCMD->CMDposition=0; - dataCMD->CMDsubposition=0; - dataCMD->CMDloop = false; - - if (currPage < 4*4){ //add READPAGE cmd to array - crc = CRC_PRESET; - lfrfid_hitag_worker_calc_crc(&crc, (0b1100<<(8-4)), 4); - lfrfid_hitag_worker_calc_crc(&crc, currPage, 8); - uint32_t value2[] = {128, 0b1100, currPage, crc, 1, 213, (uint32_t)(1.05*33*HITAG_BITPERIODS_MC)}; //always take 5% reading time buffer - uint8_t type2[] = {HITAG_ON, HITAG_CMD, HITAG_BYTE, HITAG_CRC, HITAG_STOP, HITAG_ON, HITAG_ON}; - uint16_t len2 = 7; - uint16_t cnt2 = 5; - for (size_t i = 0;iCMDvalue[cnt*len+i]=value2[i%len2]; - dataCMD->CMDtype[cnt*len+i]=type2[i%len2]; - } - dataCMD->CMDlength+=cnt2*len2; - } - else { //add READBLOCK cmd to array - crc = CRC_PRESET; - lfrfid_hitag_worker_calc_crc(&crc, (0b1101<<(8-4)), 4); - lfrfid_hitag_worker_calc_crc(&crc, currPage, 8); - uint32_t value2[] = {128, 0b1101, currPage, crc, 1, 213, (uint32_t)(1.05*129*HITAG_BITPERIODS_MC)}; //always take 5% reading time buffer - uint8_t type2[] = {HITAG_ON, HITAG_CMD, HITAG_BYTE, HITAG_CRC, HITAG_STOP, HITAG_ON, HITAG_ON}; - uint16_t len2 = 7; - uint16_t cnt2 = 5; - for (size_t i = 0;iCMDvalue[cnt*len+i]=value2[i%len2]; - dataCMD->CMDtype[cnt*len+i]=type2[i%len2]; - } - dataCMD->CMDlength+=cnt2*len2; - } - } - } - - //while not having x consequtive reads, relaunch the (already prepared) command - if (!readSucces && backupCMD->CMDcount>=7 && currPage > 0 && expectedBits > 0){ - backupCMD->CMDcount=0; - dataCMD->CMDposition=0; - dataCMD->CMDsubposition=0; - } - - if(buffer_stream_get_overrun_count(capData->stream) > 0 ) { - FURI_LOG_E(TAG, "Read overrun, recovering"); - buffer_stream_reset(capData->stream); - } - - //check for stop events - uint32_t flags = furi_event_flag_get(worker->events); - if(FURI_BIT(flags, LFRFIDHitagWorkerSignalStop)) { - break; - } - - //check for DMA HT/TC signals to repopulate the DMA buffer - if (worker->DMAeventCount>1){ - FURI_LOG_E(TAG, "DMA refilling can't keep up %u",worker->DMAeventCount); - } - if(FURI_BIT(flags, LFRFIDHitagWorkerSignalHalfTransfer)) { - //refill first half of dma buffer - lfrfid_hitag_worker_yield_dma_buffer_BPLM(dataCMD, backupCMD, dataDMA, 0, DMA_BUFFER_SIZE/2); - } - if(FURI_BIT(flags, LFRFIDHitagWorkerSignalTransferComplete)) { - //refill second half of dma buffer - lfrfid_hitag_worker_yield_dma_buffer_BPLM(dataCMD, backupCMD, dataDMA, DMA_BUFFER_SIZE/2, DMA_BUFFER_SIZE/2); - } - worker->DMAeventCount=0; - //clear all flags (i have processed all known/expected events) - furi_event_flag_clear(worker->events, flags); - - } - - //stop timers - furi_hal_rfid_tim_read_capture_stop(); - lfrfid_hitag_worker_carrier_out_stop(); - - //free memory - free(dataCMD); - free(backupCMD); - free(dataDMA); - varint_pair_free(capData->pair); + furi_hal_rfid_tim_read_capture_start(lfrfid_hitag_worker_capture_in_cc_isr, capData); + + while(1) { + Buffer* buffer = buffer_stream_receive(capData->stream, 1); + + //process data if available + if(buffer != NULL) { + size_t buffsize = buffer_get_size(buffer); + uint8_t* buffdata = buffer_get_data(buffer); + + //if scanning for tagdata, then also parse buffer data + size_t index = 0; + while(expectedBits > 0 && index < buffsize) { + uint32_t duration; + uint32_t pulse; + size_t tmp_size; + + if(!varint_pair_unpack( + &buffdata[index], buffsize - index, &pulse, &duration, &tmp_size)) { + FURI_LOG_E(TAG, "can't unpack varint pair"); + break; + } else { + index += tmp_size; + + //feed pulse duration to decoder + lfrfid_hitag_worker_decoder_feed( + pulse, + duration, + memBlock, + &bits, + &halfBit, + &finished, + currentProtocol, + startBits); + + //see if i have a valid bit sequence + if(finished) { + //bitsequence ready for validation + if(bits == + expectedBits) { //eligable bitsequence detected, accept it only after x consequtive readings + //reset the backupCMD count to ensure some pause between bitsequence detection & the CMD injection + backupCMD->CMDcount = 0; + + if(readings == 0) { + for(uint8_t p = 0; p < (expectedBits - 1) / 32; p++) { + worker->tag->pageData[(currPage + p) * 4 + 0] = + memBlock[p] >> 24 & 0xff; + worker->tag->pageData[(currPage + p) * 4 + 1] = + memBlock[p] >> 16 & 0xff; + worker->tag->pageData[(currPage + p) * 4 + 2] = + memBlock[p] >> 8 & 0xff; + worker->tag->pageData[(currPage + p) * 4 + 3] = memBlock[p] & + 0xff; + worker->tag->pageKnown[currPage + p] = 0; + } + readings = 1; + } else if(readings < readThreshold) { + bool match = true; + for(uint8_t p = 0; p < (expectedBits - 1) / 32; p++) { + if(worker->tag->pageData[(currPage + p) * 4 + 0] != + (memBlock[p] >> 24 & 0xff) || + worker->tag->pageData[(currPage + p) * 4 + 1] != + (memBlock[p] >> 16 & 0xff) || + worker->tag->pageData[(currPage + p) * 4 + 2] != + (memBlock[p] >> 8 & 0xff) || + worker->tag->pageData[(currPage + p) * 4 + 3] != + (memBlock[p] & 0xff)) { + match = false; + break; + } + } + if(match) { + readings++; + if(readings == readThreshold) { + //succesful page(s) reading + readSucces = true; + + for(uint8_t p = 0; p < (expectedBits - 1) / 32; p++) { + worker->tag->pageKnown[currPage + p] = 1; + } + + readings = 0; + expectedBits = 0; //to prevent further updates to tag data + } + } else { + for(uint8_t p = 0; p < (expectedBits - 1) / 32; p++) { + worker->tag->pageData[(currPage + p) * 4 + 0] = + memBlock[p] >> 24 & 0xff; + worker->tag->pageData[(currPage + p) * 4 + 1] = + memBlock[p] >> 16 & 0xff; + worker->tag->pageData[(currPage + p) * 4 + 2] = + memBlock[p] >> 8 & 0xff; + worker->tag->pageData[(currPage + p) * 4 + 3] = + memBlock[p] & 0xff; + worker->tag->pageKnown[currPage + p] = 0; + } + readings = 1; + } + } + } + //reset tempdata for next bitsequence readout + memset(memBlock, 0, HITAG_BLOCKPAGES * sizeof(memBlock[0])); + bits = 0; + finished = false; + } + } + } + + //reset buffer + buffer_reset(buffer); + } + + //if bitsequence found & after pause, prep & launch the command for reading out next bit of data + if(readSucces && backupCMD->CMDcount >= 7) { + readSucces = false; + backupCMD->CMDcount = + 0; //required since otherwise the 'relaunch' of cmd is triggered straightaway + + //update protocolData + size_t data_size = protocol_dict_get_data_size(worker->dict, LFRFIDProtocolHitag1); + lfrfid_hitag_worker_combine_tag_data(worker->tag); + protocol_dict_set_data( + worker->dict, LFRFIDProtocolHitag1, worker->tag->tagData, data_size); + + if(currPage == 0) { //read config page next via SELECT cmd + FURI_LOG_D( + TAG, + "Hitag1, [%02X %02X %02X %02X] found", + worker->tag->pageData[0], + worker->tag->pageData[1], + worker->tag->pageData[2], + worker->tag->pageData[3]); + dolphin_deed(DolphinDeedRfidReadSuccess); + worker->workerstatus = LFRFIDHitagStatusDetected; + + currentProtocol = HitagProtocolManchesterCoding; + expectedBits = 33; + currPage = 1; + } else if(currPage == 1) { //process config page and start reading public blocks + lfrfid_hitag_worker_process_config_page(worker->tag); + FURI_LOG_D( + TAG, + "Hitag1, [%02X %02X %02X %02X] config page read", + worker->tag->pageData[0], + worker->tag->pageData[1], + worker->tag->pageData[2], + worker->tag->pageData[3]); + //find next public readible block + for(currPage = 4; currPage <= HITAG_PAGES - 4; currPage += 4) { + if(worker->tag->pagePublic[currPage] && + (worker->tag->pageRW[currPage] & READ)) { //public readible page/block found + expectedBits = 129; + break; + } else { + FURI_LOG_D( + TAG, + "Hitag1, [%02X %02X %02X %02X] block %u not public/readible", + worker->tag->pageData[0], + worker->tag->pageData[1], + worker->tag->pageData[2], + worker->tag->pageData[3], + currPage / 4); + } + } + } else if(currPage < HITAG_PAGES - 4) { //scan for next public readible page + FURI_LOG_D( + TAG, + "Hitag1, [%02X %02X %02X %02X] block %u read", + worker->tag->pageData[0], + worker->tag->pageData[1], + worker->tag->pageData[2], + worker->tag->pageData[3], + currPage / 4); + for(currPage += 4; currPage <= HITAG_PAGES - 4; currPage += 4) { + if(worker->tag->pagePublic[currPage] && + (worker->tag->pageRW[currPage] & READ)) { //public readible page/block found + expectedBits = 129; + break; + } else { + FURI_LOG_D( + TAG, + "Hitag1, [%02X %02X %02X %02X] block %u not public/readible", + worker->tag->pageData[0], + worker->tag->pageData[1], + worker->tag->pageData[2], + worker->tag->pageData[3], + currPage / 4); + } + } + } else if( + currPage == + HITAG_PAGES - + 4) { //all public readible pages read: signal view & break out of while loop (stops the worker) + FURI_LOG_D( + TAG, + "Hitag1, [%02X %02X %02X %02X] block %u read", + worker->tag->pageData[0], + worker->tag->pageData[1], + worker->tag->pageData[2], + worker->tag->pageData[3], + currPage / 4); + + worker->workerstatus = LFRFIDHitagStatusRead; + break; + } + + if(expectedBits > 0) { //inject CMD + //prep SELECT cmd bytes + uint8_t SN0 = worker->tag->pageData[0]; + uint8_t SN1 = worker->tag->pageData[1]; + uint8_t SN2 = worker->tag->pageData[2]; + uint8_t SN3 = worker->tag->pageData[3]; + uint8_t crc = CRC_PRESET; + lfrfid_hitag_worker_calc_crc(&crc, (0b00000 << (8 - 5)), 5); + lfrfid_hitag_worker_calc_crc(&crc, SN0, 8); + lfrfid_hitag_worker_calc_crc(&crc, SN1, 8); + lfrfid_hitag_worker_calc_crc(&crc, SN2, 8); + lfrfid_hitag_worker_calc_crc(&crc, SN3, 8); + + //prep SELECT cmd array + uint32_t value[] = { + 128, + 0b00000, + SN0, + SN1, + SN2, + SN3, + crc, + 1, + 213, + (uint32_t)(1.05 * 33 * HITAG_BITPERIODS_MC)}; //always take 5% reading time buffer + uint8_t type[] = { + HITAG_ON, + HITAG_SELECT, + HITAG_BYTE, + HITAG_BYTE, + HITAG_BYTE, + HITAG_BYTE, + HITAG_CRC, + HITAG_STOP, + HITAG_ON, + HITAG_ON}; + uint16_t len = 10; + uint16_t cnt = 1; + for(size_t i = 0; i < cnt * len; i++) { + dataCMD->CMDvalue[i] = value[i % len]; + dataCMD->CMDtype[i] = type[i % len]; + } + dataCMD->CMDlength = cnt * len; + dataCMD->CMDposition = 0; + dataCMD->CMDsubposition = 0; + dataCMD->CMDloop = false; + + if(currPage < 4 * 4) { //add READPAGE cmd to array + crc = CRC_PRESET; + lfrfid_hitag_worker_calc_crc(&crc, (0b1100 << (8 - 4)), 4); + lfrfid_hitag_worker_calc_crc(&crc, currPage, 8); + uint32_t value2[] = { + 128, + 0b1100, + currPage, + crc, + 1, + 213, + (uint32_t)(1.05 * 33 * HITAG_BITPERIODS_MC)}; //always take 5% reading time buffer + uint8_t type2[] = { + HITAG_ON, HITAG_CMD, HITAG_BYTE, HITAG_CRC, HITAG_STOP, HITAG_ON, HITAG_ON}; + uint16_t len2 = 7; + uint16_t cnt2 = 5; + for(size_t i = 0; i < cnt2 * len2; i++) { + dataCMD->CMDvalue[cnt * len + i] = value2[i % len2]; + dataCMD->CMDtype[cnt * len + i] = type2[i % len2]; + } + dataCMD->CMDlength += cnt2 * len2; + } else { //add READBLOCK cmd to array + crc = CRC_PRESET; + lfrfid_hitag_worker_calc_crc(&crc, (0b1101 << (8 - 4)), 4); + lfrfid_hitag_worker_calc_crc(&crc, currPage, 8); + uint32_t value2[] = { + 128, + 0b1101, + currPage, + crc, + 1, + 213, + (uint32_t)(1.05 * 129 * HITAG_BITPERIODS_MC)}; //always take 5% reading time buffer + uint8_t type2[] = { + HITAG_ON, HITAG_CMD, HITAG_BYTE, HITAG_CRC, HITAG_STOP, HITAG_ON, HITAG_ON}; + uint16_t len2 = 7; + uint16_t cnt2 = 5; + for(size_t i = 0; i < cnt2 * len2; i++) { + dataCMD->CMDvalue[cnt * len + i] = value2[i % len2]; + dataCMD->CMDtype[cnt * len + i] = type2[i % len2]; + } + dataCMD->CMDlength += cnt2 * len2; + } + } + } + + //while not having x consequtive reads, relaunch the (already prepared) command + if(!readSucces && backupCMD->CMDcount >= 7 && currPage > 0 && expectedBits > 0) { + backupCMD->CMDcount = 0; + dataCMD->CMDposition = 0; + dataCMD->CMDsubposition = 0; + } + + if(buffer_stream_get_overrun_count(capData->stream) > 0) { + FURI_LOG_E(TAG, "Read overrun, recovering"); + buffer_stream_reset(capData->stream); + } + + //check for stop events + uint32_t flags = furi_event_flag_get(worker->events); + if(FURI_BIT(flags, LFRFIDHitagWorkerSignalStop)) { + break; + } + + //check for DMA HT/TC signals to repopulate the DMA buffer + if(worker->DMAeventCount > 1) { + FURI_LOG_E(TAG, "DMA refilling can't keep up %u", worker->DMAeventCount); + } + if(FURI_BIT(flags, LFRFIDHitagWorkerSignalHalfTransfer)) { + //refill first half of dma buffer + lfrfid_hitag_worker_yield_dma_buffer_BPLM( + dataCMD, backupCMD, dataDMA, 0, DMA_BUFFER_SIZE / 2); + } + if(FURI_BIT(flags, LFRFIDHitagWorkerSignalTransferComplete)) { + //refill second half of dma buffer + lfrfid_hitag_worker_yield_dma_buffer_BPLM( + dataCMD, backupCMD, dataDMA, DMA_BUFFER_SIZE / 2, DMA_BUFFER_SIZE / 2); + } + worker->DMAeventCount = 0; + //clear all flags (i have processed all known/expected events) + furi_event_flag_clear(worker->events, flags); + } + + //stop timers + furi_hal_rfid_tim_read_capture_stop(); + lfrfid_hitag_worker_carrier_out_stop(); + + //free memory + free(dataCMD); + free(backupCMD); + free(dataDMA); + varint_pair_free(capData->pair); buffer_stream_free(capData->stream); free(capData); - - //allow the view that started the thread to signal the stop, which will properly merge the threads & de-init the worker - if(currPage==HITAG_PAGES-4) { + + //allow the view that started the thread to signal the stop, which will properly merge the threads & de-init the worker + if(currPage == HITAG_PAGES - 4) { const uint32_t available_flags = (1 << LFRFIDHitagWorkerSignalStop); while(true) { uint32_t flags = furi_event_flag_wait( @@ -2144,11 +2381,11 @@ static int32_t lfrfid_hitag_worker_read_thread(void* thread_context) { } } } - - free(worker->tag); - - //clear the stop flag on workder so that i can use the same worker to launch a new routine - furi_event_flag_clear(worker->events, 1 << LFRFIDHitagWorkerSignalStop); - + + free(worker->tag); + + //clear the stop flag on workder so that i can use the same worker to launch a new routine + furi_event_flag_clear(worker->events, 1 << LFRFIDHitagWorkerSignalStop); + return 0; } diff --git a/lib/lfrfid/lfrfid_hitag_worker.h b/lib/lfrfid/lfrfid_hitag_worker.h index a9da388358b..de564484d81 100644 --- a/lib/lfrfid/lfrfid_hitag_worker.h +++ b/lib/lfrfid/lfrfid_hitag_worker.h @@ -2,8 +2,10 @@ #include #include #include +#include #include #include +#include #include //#include #include "protocols/lfrfid_protocols.h" @@ -32,16 +34,15 @@ extern "C" { #endif - typedef enum { - LFRFIDHitagWorkerSettingRead, - LFRFIDHitagWorkerSettingEmulate, + LFRFIDHitagWorkerSettingRead, + LFRFIDHitagWorkerSettingEmulate, } LFRFIDHitagWorkerSetting; typedef enum { - LFRFIDHitagStatusScanning, - LFRFIDHitagStatusDetected, - LFRFIDHitagStatusRead, + LFRFIDHitagStatusScanning, + LFRFIDHitagStatusDetected, + LFRFIDHitagStatusRead, } LFRFIDHitagStatus; typedef struct LFRFIDHitagWorker LFRFIDHitagWorker; @@ -53,7 +54,6 @@ typedef struct LFRFIDHitagWorker LFRFIDHitagWorker; */ LFRFIDHitagStatus lfrfid_hitag_worker_get_status(LFRFIDHitagWorker* worker); - /** * @brief Allocate a new LFRFIDHitagWorker instance * @@ -61,7 +61,6 @@ LFRFIDHitagStatus lfrfid_hitag_worker_get_status(LFRFIDHitagWorker* worker); */ LFRFIDHitagWorker* lfrfid_hitag_worker_alloc(ProtocolDict* dict); - /** * @brief Free a LFRFIDHitagWorker instance * @@ -75,9 +74,7 @@ void lfrfid_hitag_worker_free(LFRFIDHitagWorker* worker); * @param worker LFRFIDHitagWorker instance * @param setting read/emulate */ -void lfrfid_hitag_worker_start( - LFRFIDHitagWorker* worker, - LFRFIDHitagWorkerSetting setting); +void lfrfid_hitag_worker_start(LFRFIDHitagWorker* worker, LFRFIDHitagWorkerSetting setting); /** * @brief Stop worker @@ -86,7 +83,6 @@ void lfrfid_hitag_worker_start( */ void lfrfid_hitag_worker_stop(LFRFIDHitagWorker* worker); - #ifdef __cplusplus } #endif \ No newline at end of file diff --git a/lib/lfrfid/lfrfid_worker.c b/lib/lfrfid/lfrfid_worker.c index 669fe4fbfad..1e491c6b787 100644 --- a/lib/lfrfid/lfrfid_worker.c +++ b/lib/lfrfid/lfrfid_worker.c @@ -11,7 +11,7 @@ typedef enum { LFRFIDEventEmulate = (1 << 4), LFRFIDEventReadRaw = (1 << 5), LFRFIDEventEmulateRaw = (1 << 6), - LFRFIDEventAll = + LFRFIDEventAll = (LFRFIDEventStopThread | LFRFIDEventStopMode | LFRFIDEventRead | LFRFIDEventWrite | LFRFIDEventEmulate | LFRFIDEventReadRaw | LFRFIDEventEmulateRaw), } LFRFIDEventType; @@ -149,7 +149,7 @@ static int32_t lfrfid_worker_thread(void* thread_context) { if(flags & LFRFIDEventEmulate) worker->mode_index = LFRFIDWorkerEmulate; if(flags & LFRFIDEventReadRaw) worker->mode_index = LFRFIDWorkerReadRaw; if(flags & LFRFIDEventEmulateRaw) worker->mode_index = LFRFIDWorkerEmulateRaw; - + // do mode, if it exists if(lfrfid_worker_modes[worker->mode_index].process) { lfrfid_worker_modes[worker->mode_index].process(worker); diff --git a/lib/lfrfid/lfrfid_worker.h b/lib/lfrfid/lfrfid_worker.h index f292dc28e38..682b077a067 100644 --- a/lib/lfrfid/lfrfid_worker.h +++ b/lib/lfrfid/lfrfid_worker.h @@ -23,7 +23,7 @@ typedef enum { LFRFIDWorkerReadTypeAuto, LFRFIDWorkerReadTypeASKOnly, LFRFIDWorkerReadTypePSKOnly, - LFRFIDWorkerReadTypeRTFOnly, + LFRFIDWorkerReadTypeRTFOnly, } LFRFIDWorkerReadType; typedef enum { @@ -33,8 +33,8 @@ typedef enum { LFRFIDWorkerReadSenseCardEnd, LFRFIDWorkerReadStartASK, LFRFIDWorkerReadStartPSK, - LFRFIDWorkerReadStartRTF, - LFRFIDWorkerReadSenseHitag, //TODO combine with sense carstart? + LFRFIDWorkerReadStartRTF, + LFRFIDWorkerReadSenseHitag, //TODO combine with sense carstart? LFRFIDWorkerReadDone, } LFRFIDWorkerReadResult; @@ -48,7 +48,8 @@ typedef enum { LFRFIDWorkerEmulateRawOverrun, } LFRFIDWorkerEmulateRawResult; -typedef void (*LFRFIDWorkerReadCallback)(LFRFIDWorkerReadResult result, ProtocolId protocol, void* context); +typedef void ( + *LFRFIDWorkerReadCallback)(LFRFIDWorkerReadResult result, ProtocolId protocol, void* context); typedef void (*LFRFIDWorkerWriteCallback)(LFRFIDWorkerWriteResult result, void* context); typedef void (*LFRFIDWorkerReadRawCallback)(LFRFIDWorkerReadRawResult result, void* context); diff --git a/lib/lfrfid/lfrfid_worker_modes.c b/lib/lfrfid/lfrfid_worker_modes.c index 8d2b0535194..5da00e8d3fa 100644 --- a/lib/lfrfid/lfrfid_worker_modes.c +++ b/lib/lfrfid/lfrfid_worker_modes.c @@ -94,7 +94,7 @@ typedef enum { LFRFIDWorkerReadTimeout, } LFRFIDWorkerReadState; -static LFRFIDWorkerReadState lfrfid_worker_read_ttf( //tag talks first +static LFRFIDWorkerReadState lfrfid_worker_read_ttf( //tag talks first LFRFIDWorker* worker, LFRFIDFeature feature, uint32_t timeout, @@ -331,59 +331,61 @@ static LFRFIDWorkerReadState lfrfid_worker_read_ttf( //tag talks first return state; } -static LFRFIDWorkerReadState lfrfid_worker_read_rtf( //reader talks first - LFRFIDWorker* worker, +static LFRFIDWorkerReadState lfrfid_worker_read_rtf( //reader talks first + LFRFIDWorker* worker, LFRFIDFeature feature, uint32_t timeout, ProtocolId* result_protocol) { - UNUSED(feature); - LFRFIDWorkerReadState state = LFRFIDWorkerReadTimeout; - - FURI_LOG_D(TAG, "Start RTF"); - if(worker->read_cb) { - worker->read_cb(LFRFIDWorkerReadStartRTF, PROTOCOL_NO, worker->cb_ctx); - } - LFRFIDHitagWorker* hitag_worker = lfrfid_hitag_worker_alloc(worker->protocols); - - lfrfid_hitag_worker_start(hitag_worker, LFRFIDHitagWorkerSettingRead); - - FURI_LOG_D(TAG, "Read started"); - - //scan for hitag for a while and stay in hitag mode if card was detected - uint8_t delays = 0; - uint8_t delay_ms = 100; - bool notified = false; - while (1){ - furi_delay_ms(delay_ms); - - if (lfrfid_worker_check_for_stop(worker)){ - state = LFRFIDWorkerReadExit; - *result_protocol = PROTOCOL_NO; - break; - } - - if (lfrfid_hitag_worker_get_status(hitag_worker) == LFRFIDHitagStatusDetected){ - *result_protocol = LFRFIDProtocolHitag1; //TODO get protocol ID from hitag_worker when expanding the worker to include other hitag protocols - if (!notified && worker->read_cb){ - worker->read_cb(LFRFIDWorkerReadSenseHitag, *result_protocol, worker->cb_ctx); - notified = true; - } - } else if (lfrfid_hitag_worker_get_status(hitag_worker) == LFRFIDHitagStatusRead){ - state = LFRFIDWorkerReadOK; - *result_protocol = LFRFIDProtocolHitag1; //TODO get protocol ID from hitag_worker when expanding the worker to include other hitag protocols - break; - } else if (++delays >= timeout/delay_ms){ - state = LFRFIDWorkerReadTimeout; - break; - } - } - - lfrfid_hitag_worker_stop(hitag_worker); - lfrfid_hitag_worker_free(hitag_worker); - - FURI_LOG_D(TAG, "Read stopped"); - - return state; + UNUSED(feature); + LFRFIDWorkerReadState state = LFRFIDWorkerReadTimeout; + + FURI_LOG_D(TAG, "Start RTF"); + if(worker->read_cb) { + worker->read_cb(LFRFIDWorkerReadStartRTF, PROTOCOL_NO, worker->cb_ctx); + } + LFRFIDHitagWorker* hitag_worker = lfrfid_hitag_worker_alloc(worker->protocols); + + lfrfid_hitag_worker_start(hitag_worker, LFRFIDHitagWorkerSettingRead); + + FURI_LOG_D(TAG, "Read started"); + + //scan for hitag for a while and stay in hitag mode if card was detected + uint8_t delays = 0; + uint8_t delay_ms = 100; + bool notified = false; + while(1) { + furi_delay_ms(delay_ms); + + if(lfrfid_worker_check_for_stop(worker)) { + state = LFRFIDWorkerReadExit; + *result_protocol = PROTOCOL_NO; + break; + } + + if(lfrfid_hitag_worker_get_status(hitag_worker) == LFRFIDHitagStatusDetected) { + *result_protocol = + LFRFIDProtocolHitag1; //TODO get protocol ID from hitag_worker when expanding the worker to include other hitag protocols + if(!notified && worker->read_cb) { + worker->read_cb(LFRFIDWorkerReadSenseHitag, *result_protocol, worker->cb_ctx); + notified = true; + } + } else if(lfrfid_hitag_worker_get_status(hitag_worker) == LFRFIDHitagStatusRead) { + state = LFRFIDWorkerReadOK; + *result_protocol = + LFRFIDProtocolHitag1; //TODO get protocol ID from hitag_worker when expanding the worker to include other hitag protocols + break; + } else if(++delays >= timeout / delay_ms) { + state = LFRFIDWorkerReadTimeout; + break; + } + } + + lfrfid_hitag_worker_stop(hitag_worker); + lfrfid_hitag_worker_free(hitag_worker); + + FURI_LOG_D(TAG, "Read stopped"); + + return state; } static void lfrfid_worker_mode_read_process(LFRFIDWorker* worker) { @@ -393,24 +395,24 @@ static void lfrfid_worker_mode_read_process(LFRFIDWorker* worker) { if(worker->read_type == LFRFIDWorkerReadTypePSKOnly) { feature = LFRFIDFeaturePSK; - } else if (worker->read_type == LFRFIDWorkerReadTypeRTFOnly){ - feature = LFRFIDFeatureRTF; - } else { + } else if(worker->read_type == LFRFIDWorkerReadTypeRTFOnly) { + feature = LFRFIDFeatureRTF; + } else { feature = LFRFIDFeatureASK; - } + } if(worker->read_type == LFRFIDWorkerReadTypeAuto) { while(1) { // read for a while - - if (feature == LFRFIDFeatureASK || feature == LFRFIDFeaturePSK){ - state = lfrfid_worker_read_ttf( - worker, feature, LFRFID_WORKER_READ_SWITCH_TIME_MS, &read_result); - } else if (feature == LFRFIDFeatureRTF){ - state = lfrfid_worker_read_rtf( - worker, feature, LFRFID_WORKER_READ_SWITCH_TIME_MS, &read_result); - } - + + if(feature == LFRFIDFeatureASK || feature == LFRFIDFeaturePSK) { + state = lfrfid_worker_read_ttf( + worker, feature, LFRFID_WORKER_READ_SWITCH_TIME_MS, &read_result); + } else if(feature == LFRFIDFeatureRTF) { + state = lfrfid_worker_read_rtf( + worker, feature, LFRFID_WORKER_READ_SWITCH_TIME_MS, &read_result); + } + if(state == LFRFIDWorkerReadOK || state == LFRFIDWorkerReadExit) { break; } @@ -418,10 +420,10 @@ static void lfrfid_worker_mode_read_process(LFRFIDWorker* worker) { // switch to next feature if(feature == LFRFIDFeatureASK) { feature = LFRFIDFeaturePSK; - } else if (feature == LFRFIDFeaturePSK){ + } else if(feature == LFRFIDFeaturePSK) { feature = LFRFIDFeatureRTF; - } else if (feature == LFRFIDFeatureRTF){ - feature = LFRFIDFeatureASK; + } else if(feature == LFRFIDFeatureRTF) { + feature = LFRFIDFeatureASK; } lfrfid_worker_delay(worker, LFRFID_WORKER_READ_DROP_TIME_MS); @@ -429,15 +431,13 @@ static void lfrfid_worker_mode_read_process(LFRFIDWorker* worker) { } else { while(1) { if(worker->read_type == LFRFIDWorkerReadTypeASKOnly) { - state = lfrfid_worker_read_ttf( - worker, feature, UINT32_MAX, &read_result); - } else if (worker->read_type == LFRFIDWorkerReadTypePSKOnly) { + state = lfrfid_worker_read_ttf(worker, feature, UINT32_MAX, &read_result); + } else if(worker->read_type == LFRFIDWorkerReadTypePSKOnly) { state = lfrfid_worker_read_ttf( worker, feature, LFRFID_WORKER_READ_SWITCH_TIME_MS, &read_result); } else { - state = lfrfid_worker_read_rtf( - worker, feature, UINT32_MAX, &read_result); - } + state = lfrfid_worker_read_rtf(worker, feature, UINT32_MAX, &read_result); + } if(state == LFRFIDWorkerReadOK || state == LFRFIDWorkerReadExit) { break; @@ -472,8 +472,8 @@ static void lfrfid_worker_emulate_dma_isr(bool half, void* context) { furi_stream_buffer_send(stream, &flag, sizeof(uint32_t), 0); } -static void lfrfid_worker_emulate_ttf(LFRFIDWorker* worker){ - LFRFIDWorkerEmulateBuffer* buffer = malloc(sizeof(LFRFIDWorkerEmulateBuffer)); +static void lfrfid_worker_emulate_ttf(LFRFIDWorker* worker) { + LFRFIDWorkerEmulateBuffer* buffer = malloc(sizeof(LFRFIDWorkerEmulateBuffer)); FuriStreamBuffer* stream = furi_stream_buffer_alloc(sizeof(uint32_t), sizeof(uint32_t)); LFRFIDProtocol protocol = worker->protocol; PulseGlue* pulse_glue = pulse_glue_alloc(); @@ -561,31 +561,32 @@ static void lfrfid_worker_emulate_ttf(LFRFIDWorker* worker){ pulse_glue_free(pulse_glue); } -static void lfrfid_worker_emulate_rtf(LFRFIDWorker* worker){ - LFRFIDHitagWorker* hitag_worker = lfrfid_hitag_worker_alloc(worker->protocols); //todo, pass protocols & protocol id when expanding the worker to include other hitag protocols - - lfrfid_hitag_worker_start(hitag_worker, LFRFIDHitagWorkerSettingEmulate); - uint8_t delay_ms = 100; - while (1){ - furi_delay_ms(delay_ms); - - if (lfrfid_worker_check_for_stop(worker)){ - break; - } - } - - lfrfid_hitag_worker_stop(hitag_worker); - lfrfid_hitag_worker_free(hitag_worker); +static void lfrfid_worker_emulate_rtf(LFRFIDWorker* worker) { + LFRFIDHitagWorker* hitag_worker = lfrfid_hitag_worker_alloc( + worker->protocols); //todo, pass protocols & protocol id when expanding the worker to include other hitag protocols + + lfrfid_hitag_worker_start(hitag_worker, LFRFIDHitagWorkerSettingEmulate); + uint8_t delay_ms = 100; + while(1) { + furi_delay_ms(delay_ms); + + if(lfrfid_worker_check_for_stop(worker)) { + break; + } + } + + lfrfid_hitag_worker_stop(hitag_worker); + lfrfid_hitag_worker_free(hitag_worker); } static void lfrfid_worker_mode_emulate_process(LFRFIDWorker* worker) { LFRFIDFeature feature = protocol_dict_get_features(worker->protocols, worker->protocol); - - if (feature == LFRFIDFeatureRTF){ - lfrfid_worker_emulate_rtf(worker); - } else { - lfrfid_worker_emulate_ttf(worker); - } + + if(feature == LFRFIDFeatureRTF) { + lfrfid_worker_emulate_rtf(worker); + } else { + lfrfid_worker_emulate_ttf(worker); + } } /**************************************************************************************************/ @@ -717,7 +718,6 @@ static void lfrfid_worker_mode_emulate_raw_process(LFRFIDWorker* worker) { lfrfid_raw_worker_free(raw_worker); } - /**************************************************************************************************/ /******************************************** MODES ***********************************************/ /**************************************************************************************************/ diff --git a/lib/lfrfid/protocols/lfrfid_protocols.h b/lib/lfrfid/protocols/lfrfid_protocols.h index 954ef45c062..a95891fa464 100644 --- a/lib/lfrfid/protocols/lfrfid_protocols.h +++ b/lib/lfrfid/protocols/lfrfid_protocols.h @@ -5,7 +5,7 @@ typedef enum { LFRFIDFeatureASK = 1 << 0, /** ASK Demodulation */ LFRFIDFeaturePSK = 1 << 1, /** PSK Demodulation */ - LFRFIDFeatureRTF = 1 << 2, /** Reader Talks First: ASK Demodulation with 2 way communication */ + LFRFIDFeatureRTF = 1 << 2, /** Reader Talks First: ASK Demodulation with 2 way communication */ } LFRFIDFeature; typedef enum { diff --git a/lib/lfrfid/protocols/protocol_hitag1.c b/lib/lfrfid/protocols/protocol_hitag1.c index de9782150b8..1890ea1ad0a 100644 --- a/lib/lfrfid/protocols/protocol_hitag1.c +++ b/lib/lfrfid/protocols/protocol_hitag1.c @@ -3,15 +3,15 @@ #include "lfrfid_protocols.h" #define HITAG1_PAGES 64 -#define HITAG1_DATA_SIZE HITAG1_PAGES*4 + HITAG1_PAGES +#define HITAG1_DATA_SIZE HITAG1_PAGES * 4 + HITAG1_PAGES typedef struct { - uint8_t tagData[HITAG1_DATA_SIZE]; + uint8_t tagData[HITAG1_DATA_SIZE]; } ProtocolHitag1; ProtocolHitag1* protocol_hitag1_alloc(void) { ProtocolHitag1* protocol = malloc(sizeof(ProtocolHitag1)); - + return protocol; }; @@ -20,63 +20,63 @@ void protocol_hitag1_free(ProtocolHitag1* protocol) { }; uint8_t* protocol_hitag1_get_data(ProtocolHitag1* protocol) { - return protocol->tagData; + return protocol->tagData; }; void protocol_hitag1_decoder_start(ProtocolHitag1* protocol) { - UNUSED(protocol); - // Not applicalble, encoding & decoding is handled in lfrfid_hitag_worker... + UNUSED(protocol); + // Not applicalble, encoding & decoding is handled in lfrfid_hitag_worker... }; bool protocol_hitag1_decoder_feed(ProtocolHitag1* protocol, bool level, uint32_t duration) { UNUSED(protocol); - UNUSED(level); - UNUSED(duration); - // Not applicalble, encoding & decoding is handled in lfrfid_hitag_worker... - - bool result = false; - return result; + UNUSED(level); + UNUSED(duration); + // Not applicalble, encoding & decoding is handled in lfrfid_hitag_worker... + + bool result = false; + return result; }; bool protocol_hitag1_encoder_start(ProtocolHitag1* protocol) { UNUSED(protocol); - // Not applicalble, encoding & decoding is handled in lfrfid_hitag_worker... + // Not applicalble, encoding & decoding is handled in lfrfid_hitag_worker... return false; }; LevelDuration protocol_hitag1_encoder_yield(ProtocolHitag1* protocol) { UNUSED(protocol); - // Not applicalble, encoding & decoding is handled in lfrfid_hitag_worker... + // Not applicalble, encoding & decoding is handled in lfrfid_hitag_worker... - bool level = 0; + bool level = 0; uint32_t duration = 0; return level_duration_make(level, duration); }; bool protocol_hitag1_write_data(ProtocolHitag1* protocol, void* data) { UNUSED(protocol); - UNUSED(data); - - //this protocol cannot be simply written to card --> don't do anything, just return false + UNUSED(data); - return false; + //this protocol cannot be simply written to card --> don't do anything, just return false + + return false; }; void protocol_hitag1_render_data(ProtocolHitag1* protocol, FuriString* result) { uint8_t pages = 0; - for (uint8_t p=0;ptagData[HITAG1_PAGES*4+p]; - } - furi_string_printf( - result, - "SN: %02X %02X %02X %02X\r\n" - "Pages read: %u / 64", - protocol->tagData[0], - protocol->tagData[1], - protocol->tagData[2], - protocol->tagData[3], - pages); + for(uint8_t p = 0; p < HITAG1_PAGES; p++) { + pages += protocol->tagData[HITAG1_PAGES * 4 + p]; + } + furi_string_printf( + result, + "SN: %02X %02X %02X %02X\r\n" + "Pages read: %u / 64", + protocol->tagData[0], + protocol->tagData[1], + protocol->tagData[2], + protocol->tagData[3], + pages); }; const ProtocolBase protocol_hitag1 = { From dc6a1e9e6744f4f18cb41173c12a889b8df6e3c7 Mon Sep 17 00:00:00 2001 From: Aleksandr Kutuzov Date: Mon, 14 Aug 2023 21:44:23 +0900 Subject: [PATCH 08/14] Sync api symbols --- firmware/targets/f18/api_symbols.csv | 15 +++++++-------- firmware/targets/f7/api_symbols.csv | 3 +-- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/firmware/targets/f18/api_symbols.csv b/firmware/targets/f18/api_symbols.csv index eab140d5cf9..22476422bad 100644 --- a/firmware/targets/f18/api_symbols.csv +++ b/firmware/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,35.0,, +Version,+,36.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -568,7 +568,7 @@ Function,+,byte_input_alloc,ByteInput*, Function,+,byte_input_free,void,ByteInput* Function,+,byte_input_get_view,View*,ByteInput* Function,+,byte_input_set_header_text,void,"ByteInput*, const char*" -Function,+,byte_input_set_result_callback,void,"ByteInput*, ByteInputCallback, ByteChangedCallback, void*, uint8_t*, uint8_t" +Function,+,byte_input_set_result_callback,void,"ByteInput*, ByteInputCallback, ByteChangedCallback, void*, uint8_t*, uint16_t" Function,-,bzero,void,"void*, size_t" Function,-,calloc,void*,"size_t, size_t" Function,+,canvas_clear,void,Canvas* @@ -1038,18 +1038,18 @@ Function,+,furi_hal_cortex_timer_is_expired,_Bool,FuriHalCortexTimer Function,+,furi_hal_cortex_timer_wait,void,FuriHalCortexTimer Function,+,furi_hal_crypto_ctr,_Bool,"const uint8_t*, const uint8_t*, const uint8_t*, uint8_t*, size_t" Function,+,furi_hal_crypto_decrypt,_Bool,"const uint8_t*, uint8_t*, size_t" +Function,+,furi_hal_crypto_enclave_ensure_key,_Bool,uint8_t +Function,+,furi_hal_crypto_enclave_load_key,_Bool,"uint8_t, const uint8_t*" +Function,+,furi_hal_crypto_enclave_store_key,_Bool,"FuriHalCryptoKey*, uint8_t*" +Function,+,furi_hal_crypto_enclave_unload_key,_Bool,uint8_t +Function,+,furi_hal_crypto_enclave_verify,_Bool,"uint8_t*, uint8_t*" Function,+,furi_hal_crypto_encrypt,_Bool,"const uint8_t*, uint8_t*, size_t" Function,+,furi_hal_crypto_gcm,_Bool,"const uint8_t*, const uint8_t*, const uint8_t*, size_t, const uint8_t*, uint8_t*, size_t, uint8_t*, _Bool" Function,+,furi_hal_crypto_gcm_decrypt_and_verify,FuriHalCryptoGCMState,"const uint8_t*, const uint8_t*, const uint8_t*, size_t, const uint8_t*, uint8_t*, size_t, const uint8_t*" Function,+,furi_hal_crypto_gcm_encrypt_and_tag,FuriHalCryptoGCMState,"const uint8_t*, const uint8_t*, const uint8_t*, size_t, const uint8_t*, uint8_t*, size_t, uint8_t*" Function,-,furi_hal_crypto_init,void, Function,+,furi_hal_crypto_load_key,_Bool,"const uint8_t*, const uint8_t*" -Function,+,furi_hal_crypto_enclave_store_key,_Bool,"FuriHalCryptoKey*, uint8_t*" -Function,+,furi_hal_crypto_enclave_load_key,_Bool,"uint8_t, const uint8_t*" -Function,+,furi_hal_crypto_enclave_unload_key,_Bool,uint8_t Function,+,furi_hal_crypto_unload_key,_Bool, -Function,+,furi_hal_crypto_enclave_verify,_Bool,"uint8_t*, uint8_t*" -Function,+,furi_hal_crypto_enclave_ensure_key,_Bool,uint8_t Function,+,furi_hal_debug_disable,void, Function,+,furi_hal_debug_enable,void, Function,+,furi_hal_debug_is_gdb_session_active,_Bool, @@ -1471,7 +1471,6 @@ Function,+,gui_add_framebuffer_callback,void,"Gui*, GuiCanvasCommitCallback, voi Function,+,gui_add_view_port,void,"Gui*, ViewPort*, GuiLayer" Function,+,gui_direct_draw_acquire,Canvas*,Gui* Function,+,gui_direct_draw_release,void,Gui* -Function,-,gui_active_view_port_count,size_t,"Gui*, GuiLayer" Function,+,gui_get_framebuffer_size,size_t,const Gui* Function,+,gui_remove_framebuffer_callback,void,"Gui*, GuiCanvasCommitCallback, void*" Function,+,gui_remove_view_port,void,"Gui*, ViewPort*" diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index a65f1a4d299..37749319b56 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,35.0,, +Version,+,36.0,, Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, @@ -1642,7 +1642,6 @@ Function,+,gui_add_framebuffer_callback,void,"Gui*, GuiCanvasCommitCallback, voi Function,+,gui_add_view_port,void,"Gui*, ViewPort*, GuiLayer" Function,+,gui_direct_draw_acquire,Canvas*,Gui* Function,+,gui_direct_draw_release,void,Gui* -Function,-,gui_active_view_port_count,size_t,"Gui*, GuiLayer" Function,+,gui_get_framebuffer_size,size_t,const Gui* Function,+,gui_remove_framebuffer_callback,void,"Gui*, GuiCanvasCommitCallback, void*" Function,+,gui_remove_view_port,void,"Gui*, ViewPort*" From a4885ed04427a7b4fc43b319c6e5e9ff3e6cc0d0 Mon Sep 17 00:00:00 2001 From: Aleksandr Kutuzov Date: Tue, 15 Aug 2023 14:47:29 +0900 Subject: [PATCH 09/14] Gui: fix merge artifact in byte_input --- applications/services/gui/modules/byte_input.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/services/gui/modules/byte_input.c b/applications/services/gui/modules/byte_input.c index 80b6d1c38c8..32fcad4affc 100644 --- a/applications/services/gui/modules/byte_input.c +++ b/applications/services/gui/modules/byte_input.c @@ -25,7 +25,7 @@ typedef struct { void* callback_context; bool selected_high_nibble; - uint8_t selected_byte; + uint16_t selected_byte; int8_t selected_row; // row -2 - mini_editor, -1 - input, row 0 & 1 - keyboard uint8_t selected_column; uint16_t first_visible_byte; From 16947f96a06eb24cad9da8f0d22df21f6281b09f Mon Sep 17 00:00:00 2001 From: Daan De Witte Date: Wed, 25 Oct 2023 22:56:15 +0200 Subject: [PATCH 10/14] low level code moved to furi_hal_rfid + PVS warnings fixed --- firmware/targets/f7/api_symbols.csv | 8 +- firmware/targets/f7/furi_hal/furi_hal_rfid.c | 457 ++++++++++++++ firmware/targets/f7/furi_hal/furi_hal_rfid.h | 31 + lib/lfrfid/lfrfid_hitag_worker.c | 598 ++----------------- lib/lfrfid/lfrfid_worker_modes.c | 1 - lib/lfrfid/protocols/protocol_hitag1.c | 2 +- 6 files changed, 547 insertions(+), 550 deletions(-) diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 0a4498a6c5c..b122e40c4b9 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,39.1,, +Version,+,40.0,, Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, @@ -1331,6 +1331,12 @@ Function,-,furi_hal_rfid_init,void, Function,+,furi_hal_rfid_pin_pull_pulldown,void, Function,+,furi_hal_rfid_pin_pull_release,void, Function,+,furi_hal_rfid_pins_reset,void, +Function,+,furi_hal_rfid_rtf_carrier_in_ETR_mode,void,uint8_t +Function,+,furi_hal_rfid_rtf_carrier_in_start,void,"void*, uint8_t, uint32_t*, uint32_t*, size_t, FuriHalRfidReadCaptureCallback" +Function,+,furi_hal_rfid_rtf_carrier_in_stop,void, +Function,+,furi_hal_rfid_rtf_carrier_out_start,void, +Function,+,furi_hal_rfid_rtf_carrier_out_stop,void, +Function,+,furi_hal_rfid_rtf_pull_out_dma_start,void,size_t Function,+,furi_hal_rfid_set_read_period,void,uint32_t Function,+,furi_hal_rfid_set_read_pulse,void,uint32_t Function,+,furi_hal_rfid_tim_emulate_dma_start,void,"uint32_t*, uint32_t*, size_t, FuriHalRfidDMACallback, void*" diff --git a/firmware/targets/f7/furi_hal/furi_hal_rfid.c b/firmware/targets/f7/furi_hal/furi_hal_rfid.c index 67f11d6ff7b..0fffd13c802 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_rfid.c +++ b/firmware/targets/f7/furi_hal/furi_hal_rfid.c @@ -9,6 +9,7 @@ #include #include +// TIMER definitions #define FURI_HAL_RFID_READ_TIMER TIM1 #define FURI_HAL_RFID_READ_TIMER_BUS FuriHalBusTIM1 #define FURI_HAL_RFID_READ_TIMER_CHANNEL LL_TIM_CHANNEL_CH1N @@ -20,11 +21,26 @@ #define FURI_HAL_RFID_EMULATE_TIMER_IRQ FuriHalInterruptIdTIM2 #define FURI_HAL_RFID_EMULATE_TIMER_CHANNEL LL_TIM_CHANNEL_CH3 +#define FURI_HAL_RFID_RTF_PULL_OUT_TIMER TIM2 +//#define FURI_HAL_RFID_RTF_PULL_OUT_TIMER_BUS FuriHalBusTIM2 +#define FURI_HAL_RFID_RTF_PULL_OUT_TIMER_CHANNEL LL_TIM_CHANNEL_CH3 + +#define FURI_HAL_RFID_RTF_CARRIER_IN_TIMER TIM2 +#define FURI_HAL_RFID_RTF_CARRIER_IN_TIMER_BUS FuriHalBusTIM2 +#define FURI_HAL_RFID_RTF_CARRIER_IN_TIMER_DIR_CH LL_TIM_CHANNEL_CH1 + +#define FURI_HAL_RFID_RTF_CARRIER_IN_REFERENCE_TIMER TIM1 +#define FURI_HAL_RFID_RTF_CARRIER_IN_REFERENCE_TIMER_BUS FuriHalBusTIM1 + #define RFID_CAPTURE_TIM TIM2 #define RFID_CAPTURE_TIM_BUS FuriHalBusTIM2 #define RFID_CAPTURE_IND_CH LL_TIM_CHANNEL_CH3 #define RFID_CAPTURE_DIR_CH LL_TIM_CHANNEL_CH4 +#define CARRIER_OUT_TIMER TIM1 +#define CARRIER_OUT_TIMER_BUS FuriHalBusTIM1 +#define CARRIER_OUT_TIMER_CHANNEL LL_TIM_CHANNEL_CH1 // or LL_TIM_CHANNEL_CH1N + // Field presence detection #define FURI_HAL_RFID_FIELD_FREQUENCY_MIN 80000 #define FURI_HAL_RFID_FIELD_FREQUENCY_MAX 200000 @@ -46,6 +62,15 @@ #define RFID_DMA_CH1_DEF RFID_DMA, RFID_DMA_CH1_CHANNEL #define RFID_DMA_CH2_DEF RFID_DMA, RFID_DMA_CH2_CHANNEL +// DMA Channels definition for RTF mode +#define FURI_HAL_RFID_RTF_PULL_OUT_DMA DMA2 +#define FURI_HAL_RFID_RTF_PULL_OUT_DMA_CH1 LL_DMA_CHANNEL_1 +#define FURI_HAL_RFID_RTF_PULL_OUT_DMA_CH2 LL_DMA_CHANNEL_2 +#define FURI_HAL_RFID_RTF_PULL_OUT_DMA_CH1_IRQ FuriHalInterruptIdDma2Ch1 +#define FURI_HAL_RFID_RTF_PULL_OUT_DMA_CH1_DEF FURI_HAL_RFID_RTF_PULL_OUT_DMA, FURI_HAL_RFID_RTF_PULL_OUT_DMA_CH1 +#define FURI_HAL_RFID_RTF_PULL_OUT_DMA_CH2_DEF FURI_HAL_RFID_RTF_PULL_OUT_DMA, FURI_HAL_RFID_RTF_PULL_OUT_DMA_CH2 + + typedef struct { uint32_t counter; uint32_t set_tim_counter_cnt; @@ -56,6 +81,7 @@ typedef struct { FuriHalRfidReadCaptureCallback read_capture_callback; void* context; FuriHalRfidField field; + uint32_t prevTIMval; } FuriHalRfid; FuriHalRfid* furi_hal_rfid = NULL; @@ -72,6 +98,7 @@ void furi_hal_rfid_init() { furi_hal_rfid = malloc(sizeof(FuriHalRfid)); furi_hal_rfid->field.counter = 0; furi_hal_rfid->field.set_tim_counter_cnt = 0; + furi_hal_rfid->prevTIMval = 0; furi_hal_rfid_pins_reset(); @@ -430,6 +457,436 @@ void furi_hal_rfid_set_read_pulse(uint32_t pulse) { #endif } + + + +static void furi_hal_rfid_rtf_carrier_out_dma_isr(void* dma_context) { + if(LL_DMA_IsActiveFlag_HT1(DMA1)) { + LL_DMA_ClearFlag_HT1(DMA1); + furi_hal_rfid->dma_callback(true, dma_context); + } + + if(LL_DMA_IsActiveFlag_TC1(DMA1)) { + LL_DMA_ClearFlag_TC1(DMA1); + furi_hal_rfid->dma_callback(false, dma_context); + } +} + +void furi_hal_rfid_rtf_carrier_out_start( + uint32_t* duration, + uint32_t* pulse, + size_t length, + FuriHalRfidDMACallback callback, + void* context) { + furi_assert(furi_hal_rfid); + + // setup interrupts + furi_hal_rfid->dma_callback = callback; + + // configure timer + furi_hal_bus_enable(CARRIER_OUT_TIMER_BUS); + + LL_TIM_InitTypeDef TIM_InitStruct = {0}; + TIM_InitStruct.Prescaler = SystemCoreClock / (125000 * 8) - + 1; // sets the basis TIMER frequency to 8*freq (1MHz) for the timer + TIM_InitStruct.Autoreload = 8 - 1; //initial PWM period =125kHz + LL_TIM_Init(CARRIER_OUT_TIMER, &TIM_InitStruct); + LL_TIM_DisableARRPreload(CARRIER_OUT_TIMER); + + LL_TIM_OC_InitTypeDef TIM_OC_InitStruct = {0}; + TIM_OC_InitStruct.OCMode = LL_TIM_OCMODE_PWM1; + TIM_OC_InitStruct.OCNState = LL_TIM_OCSTATE_ENABLE; + TIM_OC_InitStruct.CompareValue = 8 / 2; //initial pulse duration of half period + LL_TIM_OC_Init(CARRIER_OUT_TIMER, CARRIER_OUT_TIMER_CHANNEL, &TIM_OC_InitStruct); + + LL_TIM_OC_SetPolarity(CARRIER_OUT_TIMER, CARRIER_OUT_TIMER_CHANNEL, LL_TIM_OCPOLARITY_HIGH); + LL_TIM_EnableDMAReq_UPDATE(CARRIER_OUT_TIMER); + + // configure DMA "mem -> ARR" channel + LL_DMA_InitTypeDef dma_config = {0}; + dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (CARRIER_OUT_TIMER->ARR); + dma_config.MemoryOrM2MDstAddress = (uint32_t)duration; + dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; + dma_config.Mode = LL_DMA_MODE_CIRCULAR; + dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; + dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; + dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; + dma_config.NbData = length; + dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM1_UP; + dma_config.Priority = LL_DMA_MODE_NORMAL; + LL_DMA_Init(DMA1, LL_DMA_CHANNEL_1, &dma_config); + LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1); + + // configure DMA "mem -> CCR1" channel + dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (CARRIER_OUT_TIMER->CCR1); + dma_config.MemoryOrM2MDstAddress = (uint32_t)pulse; + dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; + dma_config.Mode = LL_DMA_MODE_CIRCULAR; + dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; + dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; + dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; + dma_config.NbData = length; + dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM1_UP; + dma_config.Priority = LL_DMA_MODE_NORMAL; + LL_DMA_Init(DMA1, LL_DMA_CHANNEL_2, &dma_config); + LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2); + + // attach interrupt to one of DMA channels + furi_hal_interrupt_set_isr( + FuriHalInterruptIdDma1Ch1, furi_hal_rfid_rtf_carrier_out_dma_isr, context); + LL_DMA_EnableIT_TC(DMA1, LL_DMA_CHANNEL_1); + LL_DMA_EnableIT_HT(DMA1, LL_DMA_CHANNEL_1); + + // start + LL_TIM_EnableAllOutputs(CARRIER_OUT_TIMER); + + LL_TIM_SetCounter(CARRIER_OUT_TIMER, 0); + LL_TIM_EnableCounter(CARRIER_OUT_TIMER); +} + +void furi_hal_rfid_rtf_carrier_out_stop() { + LL_TIM_DisableCounter(CARRIER_OUT_TIMER); + LL_TIM_DisableAllOutputs(CARRIER_OUT_TIMER); + + furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch1, NULL, NULL); + LL_DMA_DisableIT_TC(DMA1, LL_DMA_CHANNEL_1); + LL_DMA_DisableIT_HT(DMA1, LL_DMA_CHANNEL_1); + + FURI_CRITICAL_ENTER(); + + LL_DMA_DeInit(DMA1, LL_DMA_CHANNEL_1); + LL_DMA_DeInit(DMA1, LL_DMA_CHANNEL_2); + + furi_hal_bus_disable(CARRIER_OUT_TIMER_BUS); + + FURI_CRITICAL_EXIT(); +} + +static void furi_hal_rfid_rtf_carrier_in_IC_mode(); + +static void furi_hal_rfid_rtf_carrier_in_isr(void* capture_context) { + uint32_t TIMval = 0; + //note that I'm doing 2 captures during ETR mode (pulse & duration) and sending both towards hitagworker as a duration, though this is only used for logging, no processing is done + //while in IC mode I'm only doing 1 capture (duration only) and sending this towards hitagworker as a duration used for command detection + if(LL_TIM_IsActiveFlag_CC1(FURI_HAL_RFID_RTF_CARRIER_IN_TIMER)) { + //INPUT CAPTURE trigger on channel 1 for CARRIER_IN_TIMER (TIM2) is used in command detection mode to capture duration + uint32_t newTIMval = LL_TIM_IC_GetCaptureCH1(FURI_HAL_RFID_RTF_CARRIER_IN_TIMER); + LL_TIM_ClearFlag_CC1(FURI_HAL_RFID_RTF_CARRIER_IN_TIMER); + + //ARR for TIM2 in input capture mode is UINT32_MAX so at freq of 1MHz this is 70+ minutes, instead of resetting counter, just store prev value and take difference of both + //this increases measurement accuracy, since value is then purely hardware controlled, no dependency of interrupt timing to (re)set any timer values + TIMval = newTIMval - furi_hal_rfid->prevTIMval; + furi_hal_rfid->prevTIMval = newTIMval; + } else if(LL_TIM_IsActiveFlag_UPDATE(FURI_HAL_RFID_RTF_CARRIER_IN_TIMER)) { + //UPDATE is used in reply mode (ETR +DMA setup) to capture duration (for logging/debugging only) + TIMval = LL_TIM_GetCounter(FURI_HAL_RFID_RTF_CARRIER_IN_REFERENCE_TIMER); + LL_TIM_ClearFlag_UPDATE(FURI_HAL_RFID_RTF_CARRIER_IN_TIMER); + + LL_TIM_SetCounter(FURI_HAL_RFID_RTF_CARRIER_IN_REFERENCE_TIMER, 0); + } else if(LL_TIM_IsActiveFlag_CC3(FURI_HAL_RFID_RTF_PULL_OUT_TIMER)) { + //OUTPUT COMPARE trigger on channel 3 for PULL_OUT_TIMER (TIM2) is used in reply mode (ETR + DMA setup) to also capture pulse (for logging/debugging only) + TIMval = LL_TIM_GetCounter(FURI_HAL_RFID_RTF_CARRIER_IN_REFERENCE_TIMER); + LL_TIM_ClearFlag_CC3(FURI_HAL_RFID_RTF_PULL_OUT_TIMER); + } + + if (TIMval != 0){ + furi_hal_rfid->read_capture_callback(true, TIMval, capture_context); + } +} + +static void furi_hal_rfid_rtf_pull_out_dma_stop() { + //reconfigure pull_out pin to fixed low state + //via forced OC INACTIVE MODE + LL_TIM_OC_SetMode(FURI_HAL_RFID_RTF_PULL_OUT_TIMER, FURI_HAL_RFID_RTF_PULL_OUT_TIMER_CHANNEL, LL_TIM_OCMODE_FORCED_INACTIVE); + + //disable DMA channels & requests + LL_TIM_DisableDMAReq_UPDATE(FURI_HAL_RFID_RTF_PULL_OUT_TIMER); + LL_DMA_DisableChannel(FURI_HAL_RFID_RTF_PULL_OUT_DMA_CH1_DEF); //need to disable when using normal mode? + LL_DMA_DisableChannel(FURI_HAL_RFID_RTF_PULL_OUT_DMA_CH2_DEF); //need to disable when using normal mode? + //LL_TIM_DisableAllOutputs(FURI_HAL_RFID_RTF_PULL_OUT_TIMER); //no need to disable when reconfiguring pin? + + //for logging both ccr & arr times during emulate (iso only arr) + LL_TIM_DisableIT_CC3(FURI_HAL_RFID_RTF_PULL_OUT_TIMER); + + //switch carrier detection back to input capture for stable readings (required for command detection) + furi_hal_rfid_rtf_carrier_in_IC_mode(); +} + +static void furi_hal_rfid_rtf_pull_out_dma_isr() { + // currently no HT interrupt enabled, only TC + if(LL_DMA_IsActiveFlag_TC1(FURI_HAL_RFID_RTF_PULL_OUT_DMA)) { + LL_DMA_ClearFlag_TC1(FURI_HAL_RFID_RTF_PULL_OUT_DMA); + furi_hal_rfid_rtf_pull_out_dma_stop(); + } +} + +void furi_hal_rfid_rtf_pull_out_dma_start(size_t length) { + //array pointers remain the same, but length changes so + //reset DMA length (only possible when DMA channel is disabled + //this also resets the DMA counter (the DMA length is the 'remaining counter' + LL_DMA_SetDataLength(FURI_HAL_RFID_RTF_PULL_OUT_DMA_CH1_DEF, length); + LL_DMA_SetDataLength(FURI_HAL_RFID_RTF_PULL_OUT_DMA_CH2_DEF, length); + + //enable DMA + LL_DMA_EnableChannel(FURI_HAL_RFID_RTF_PULL_OUT_DMA_CH1_DEF); + LL_DMA_EnableChannel(FURI_HAL_RFID_RTF_PULL_OUT_DMA_CH2_DEF); + + //set OC to PWM1 mode + LL_TIM_OC_SetMode( + FURI_HAL_RFID_RTF_PULL_OUT_TIMER, + FURI_HAL_RFID_RTF_PULL_OUT_TIMER_CHANNEL, + LL_TIM_OCMODE_PWM1); //during reply (dma controlled) mode + + //only enable DMA requests on timer update after enabling both channels (otherwise you risk that first DMA request only triggers one of the channels) + LL_TIM_EnableDMAReq_UPDATE(FURI_HAL_RFID_RTF_PULL_OUT_TIMER); + + //for logging both ccr & arr times during emulate (iso only arr) + LL_TIM_EnableIT_CC3(FURI_HAL_RFID_RTF_PULL_OUT_TIMER); +} + +static void furi_hal_rfid_rtf_pull_out_dma_setup( + uint32_t* duration, + uint32_t* pulse, + size_t length) { + //DMA setup + LL_TIM_DisableDMAReq_UPDATE(FURI_HAL_RFID_RTF_PULL_OUT_TIMER); //start with DMA requests disabled + + // configure DMA "mem -> ARR" channel + LL_DMA_InitTypeDef dma_config = {0}; + dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (FURI_HAL_RFID_RTF_PULL_OUT_TIMER->ARR); + dma_config.MemoryOrM2MDstAddress = (uint32_t)duration; + dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; + //dma_config.Mode = LL_DMA_MODE_CIRCULAR; //keep cycling through memory array + dma_config.Mode = LL_DMA_MODE_NORMAL; //cycle only once through memory array + dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; + dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; + dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; + dma_config.NbData = length; + dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; + dma_config.Priority = LL_DMA_MODE_NORMAL; + LL_DMA_Init(FURI_HAL_RFID_RTF_PULL_OUT_DMA_CH1_DEF, &dma_config); + + // configure DMA "mem -> CCR3" channel + dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (FURI_HAL_RFID_RTF_PULL_OUT_TIMER->CCR3); + dma_config.MemoryOrM2MDstAddress = (uint32_t)pulse; + dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; + //dma_config.Mode = LL_DMA_MODE_CIRCULAR; //keep cycling through memory array + dma_config.Mode = LL_DMA_MODE_NORMAL; //cycle only once through memory array + dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; + dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; + dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; + dma_config.NbData = length; + dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; + dma_config.Priority = LL_DMA_MODE_NORMAL; + LL_DMA_Init(FURI_HAL_RFID_RTF_PULL_OUT_DMA_CH2_DEF, &dma_config); + + // enable DMA interrupts + furi_hal_interrupt_set_isr( + FURI_HAL_RFID_RTF_PULL_OUT_DMA_CH1_IRQ, furi_hal_rfid_rtf_pull_out_dma_isr, NULL); + + //LL_DMA_EnableIT_HT(FURI_HAL_RFID_RTF_PULL_OUT_DMA_CH1_DEF); //let's try normal DMA mode and only transfer complete interrupt + LL_DMA_EnableIT_TC(FURI_HAL_RFID_RTF_PULL_OUT_DMA_CH1_DEF); +} + +void furi_hal_rfid_rtf_carrier_in_ETR_mode(uint8_t ext_prescaler) { + //ETR mode used during emulation while replying to commands from reader + //it has less accuracy than IC mode, but microcontroller cannot run DMA for pull out and input capture for carrier in at the same time + + //disable counters temporarily + LL_TIM_DisableCounter(FURI_HAL_RFID_RTF_CARRIER_IN_TIMER); + LL_TIM_DisableCounter(FURI_HAL_RFID_RTF_CARRIER_IN_REFERENCE_TIMER); + + //disable Input capture & related interrupt + LL_TIM_DisableIT_CC1(FURI_HAL_RFID_RTF_CARRIER_IN_TIMER); + LL_TIM_CC_DisableChannel(FURI_HAL_RFID_RTF_CARRIER_IN_TIMER, FURI_HAL_RFID_RTF_CARRIER_IN_TIMER_DIR_CH); + + //reset previous TIMER value + furi_hal_rfid->prevTIMval = 0; + + //switch clocksource to ETR with external prescaling via ARR + LL_TIM_SetPrescaler(FURI_HAL_RFID_RTF_CARRIER_IN_TIMER, 1 - 1); //prescaler is only applied at next update event + LL_TIM_GenerateEvent_UPDATE( + FURI_HAL_RFID_RTF_CARRIER_IN_TIMER); //prescaler is only applied at next update event //TODO: how to prevent this from creating a capture data entry + LL_TIM_SetAutoReload(FURI_HAL_RFID_RTF_CARRIER_IN_TIMER, ext_prescaler - 1); + LL_TIM_SetClockSource(FURI_HAL_RFID_RTF_CARRIER_IN_TIMER, LL_TIM_CLOCKSOURCE_EXT_MODE2); + + //reconfigure carrier_in pin to TIM2 ETR + furi_hal_gpio_init_ex( + &gpio_rfid_carrier, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedLow, GpioAltFn2TIM2); + + //reset timer counter & capture context for period calculation + LL_TIM_SetCounter(FURI_HAL_RFID_RTF_CARRIER_IN_TIMER, 0); + LL_TIM_SetCounter(FURI_HAL_RFID_RTF_CARRIER_IN_REFERENCE_TIMER, 0); + + //enable interrupt via update + LL_TIM_EnableIT_UPDATE(FURI_HAL_RFID_RTF_CARRIER_IN_TIMER); + + //re-enable counters + LL_TIM_EnableCounter(FURI_HAL_RFID_RTF_CARRIER_IN_TIMER); + LL_TIM_EnableCounter(FURI_HAL_RFID_RTF_CARRIER_IN_REFERENCE_TIMER); +} + +static void furi_hal_rfid_rtf_carrier_in_IC_mode() { + //input caputre mode used during emulation while scanning for commands from reader. Input capture yields more accurate results compared to ETR. + + //disable counters temporarily + LL_TIM_DisableCounter(FURI_HAL_RFID_RTF_CARRIER_IN_TIMER); + LL_TIM_DisableCounter(FURI_HAL_RFID_RTF_CARRIER_IN_REFERENCE_TIMER); + + //disable interrupt via update + LL_TIM_DisableIT_UPDATE(FURI_HAL_RFID_RTF_CARRIER_IN_TIMER); + + //reset previous TIMER value + furi_hal_rfid->prevTIMval = 0; + + //switch clocksource to system/64, external prescaling is handled in IC prescaler + LL_TIM_SetClockSource(FURI_HAL_RFID_RTF_CARRIER_IN_TIMER, LL_TIM_CLOCKSOURCE_INTERNAL); + LL_TIM_SetPrescaler(FURI_HAL_RFID_RTF_CARRIER_IN_TIMER, 64 - 1); //prescaler is only applied at next update event + LL_TIM_GenerateEvent_UPDATE( + FURI_HAL_RFID_RTF_CARRIER_IN_TIMER); //prescaler is only applied at next update event //TODO: how to prevent this from creating a capture data entry + LL_TIM_SetAutoReload(FURI_HAL_RFID_RTF_CARRIER_IN_TIMER, UINT32_MAX); + + //reconfigure carrier_in pin to TIM2 CH1 for input capture + furi_hal_gpio_init_ex( + &gpio_rfid_carrier, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedLow, GpioAltFn1TIM2); + + //reset timer counter & capture context for period calculation + LL_TIM_SetCounter(FURI_HAL_RFID_RTF_CARRIER_IN_TIMER, 0); + LL_TIM_SetCounter(FURI_HAL_RFID_RTF_CARRIER_IN_REFERENCE_TIMER, 0); + + //enable Input capture & related interrupt + LL_TIM_EnableIT_CC1(FURI_HAL_RFID_RTF_CARRIER_IN_TIMER); + LL_TIM_CC_EnableChannel(FURI_HAL_RFID_RTF_CARRIER_IN_TIMER, FURI_HAL_RFID_RTF_CARRIER_IN_TIMER_DIR_CH); + + //re-enable counters + LL_TIM_EnableCounter(FURI_HAL_RFID_RTF_CARRIER_IN_TIMER); + LL_TIM_EnableCounter(FURI_HAL_RFID_RTF_CARRIER_IN_REFERENCE_TIMER); +} + +void furi_hal_rfid_rtf_carrier_in_start( + void* capture_context, + uint8_t ext_prescaler, + uint32_t* duration, + uint32_t* pulse, + size_t length, + FuriHalRfidReadCaptureCallback callback) { + furi_assert(furi_hal_rfid); + furi_hal_rfid->read_capture_callback = callback; + + FURI_CRITICAL_ENTER(); + LL_DMA_DeInit(FURI_HAL_RFID_RTF_PULL_OUT_DMA_CH1_DEF); //required? + LL_DMA_DeInit(FURI_HAL_RFID_RTF_PULL_OUT_DMA_CH2_DEF); //required? + FURI_CRITICAL_EXIT(); + + furi_hal_bus_enable(FURI_HAL_RFID_RTF_CARRIER_IN_TIMER_BUS); + furi_hal_bus_enable(FURI_HAL_RFID_RTF_CARRIER_IN_REFERENCE_TIMER_BUS); + + //setup reference timer: simple setup with base freq of 1MHz and max autoreload + LL_TIM_InitTypeDef TIM_InitStruct_Ref = {0}; + TIM_InitStruct_Ref.Prescaler = + 64 - + 1; //system base freq is 64MHz, so this sets base freq for TIM to 1MHz (aka 1us period) + TIM_InitStruct_Ref.CounterMode = LL_TIM_COUNTERMODE_UP; + TIM_InitStruct_Ref.Autoreload = UINT32_MAX; + TIM_InitStruct_Ref.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1; + LL_TIM_Init(FURI_HAL_RFID_RTF_CARRIER_IN_REFERENCE_TIMER, &TIM_InitStruct_Ref); + + //setup carrier in timer for input capture + LL_TIM_InitTypeDef TIM_InitStruct = {0}; + TIM_InitStruct.Prescaler = + 64 - + 1; //system base freq is 64MHz, so this sets base freq for TIM to 1MHz (aka 1us period) + TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP; + TIM_InitStruct.Autoreload = UINT32_MAX; + TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1; + LL_TIM_Init(FURI_HAL_RFID_RTF_CARRIER_IN_TIMER, &TIM_InitStruct); + + LL_TIM_DisableARRPreload(FURI_HAL_RFID_RTF_CARRIER_IN_TIMER); + LL_TIM_SetClockSource( + FURI_HAL_RFID_RTF_CARRIER_IN_TIMER, + LL_TIM_CLOCKSOURCE_INTERNAL); //default is internal, so likely not required + LL_TIM_DisableDMAReq_TRIG(FURI_HAL_RFID_RTF_CARRIER_IN_TIMER); + LL_TIM_DisableIT_TRIG(FURI_HAL_RFID_RTF_CARRIER_IN_TIMER); + + //meanwhile already prepare the ETR + LL_TIM_ConfigETR( + FURI_HAL_RFID_RTF_CARRIER_IN_TIMER, + LL_TIM_ETR_POLARITY_INVERTED, + LL_TIM_ETR_PRESCALER_DIV1, + LL_TIM_ETR_FILTER_FDIV1); + + //INPUT CAPTURE SETUP + // Timer: channel 1 direct (from GPIO) + LL_TIM_IC_SetActiveInput( + FURI_HAL_RFID_RTF_CARRIER_IN_TIMER, FURI_HAL_RFID_RTF_CARRIER_IN_TIMER_DIR_CH, LL_TIM_ACTIVEINPUT_DIRECTTI); + //prescaling direct channel seems to be working fine (and is necessary since otherwise sd write cannot keep up) + if(ext_prescaler == 4) { + LL_TIM_IC_SetPrescaler(FURI_HAL_RFID_RTF_CARRIER_IN_TIMER, FURI_HAL_RFID_RTF_CARRIER_IN_TIMER_DIR_CH, LL_TIM_ICPSC_DIV4); + } else if(ext_prescaler == 2) { + LL_TIM_IC_SetPrescaler(FURI_HAL_RFID_RTF_CARRIER_IN_TIMER, FURI_HAL_RFID_RTF_CARRIER_IN_TIMER_DIR_CH, LL_TIM_ICPSC_DIV2); + } else { + LL_TIM_IC_SetPrescaler(FURI_HAL_RFID_RTF_CARRIER_IN_TIMER, FURI_HAL_RFID_RTF_CARRIER_IN_TIMER_DIR_CH, LL_TIM_ICPSC_DIV1); + } + LL_TIM_IC_SetPolarity(FURI_HAL_RFID_RTF_CARRIER_IN_TIMER, FURI_HAL_RFID_RTF_CARRIER_IN_TIMER_DIR_CH, LL_TIM_IC_POLARITY_RISING); + LL_TIM_IC_SetFilter(FURI_HAL_RFID_RTF_CARRIER_IN_TIMER, FURI_HAL_RFID_RTF_CARRIER_IN_TIMER_DIR_CH, LL_TIM_IC_FILTER_FDIV1); + + //set interrupt callback for capturing period + LL_TIM_EnableIT_CC1(FURI_HAL_RFID_RTF_CARRIER_IN_TIMER); + //LL_TIM_EnableIT_CC2(FURI_HAL_RFID_RTF_CARRIER_IN_TIMER); + furi_hal_interrupt_set_isr( + FuriHalInterruptIdTIM2, furi_hal_rfid_rtf_carrier_in_isr, capture_context); + + //OUTPUT COMPARE SETUP + LL_TIM_OC_InitTypeDef TIM_OC_InitStruct = {0}; + //TIM_OC_InitStruct.OCMode = LL_TIM_OCMODE_PWM1; //during emulate (dma controlled) mode + TIM_OC_InitStruct.OCMode = + LL_TIM_OCMODE_FORCED_INACTIVE; //during carrier in, put output to forced low state + TIM_OC_InitStruct.OCState = LL_TIM_OCSTATE_ENABLE; + TIM_OC_InitStruct.CompareValue = + 0; //0% this should have almost similar effect as keeping output forced inactive (there's still some micropulse emited, going high at ARR and immediately down again at CCR value) + LL_TIM_OC_Init(FURI_HAL_RFID_RTF_PULL_OUT_TIMER, FURI_HAL_RFID_RTF_PULL_OUT_TIMER_CHANNEL, &TIM_OC_InitStruct); + LL_TIM_OC_SetPolarity( + FURI_HAL_RFID_RTF_PULL_OUT_TIMER, + FURI_HAL_RFID_RTF_PULL_OUT_TIMER_CHANNEL, + LL_TIM_OCPOLARITY_HIGH); //active high (gpio goes high when pulse is high) + + //INIT DMA (do not start it yet) + furi_hal_rfid_rtf_pull_out_dma_setup(duration, pulse, length); + + LL_TIM_CC_EnableChannel(FURI_HAL_RFID_RTF_CARRIER_IN_TIMER, FURI_HAL_RFID_RTF_CARRIER_IN_TIMER_DIR_CH); + LL_TIM_CC_EnableChannel(FURI_HAL_RFID_RTF_PULL_OUT_TIMER, FURI_HAL_RFID_RTF_PULL_OUT_TIMER_CHANNEL); + LL_TIM_SetCounter(FURI_HAL_RFID_RTF_CARRIER_IN_TIMER, 0); + LL_TIM_SetCounter(FURI_HAL_RFID_RTF_CARRIER_IN_REFERENCE_TIMER, 0); + LL_TIM_EnableCounter(FURI_HAL_RFID_RTF_CARRIER_IN_TIMER); + LL_TIM_EnableCounter(FURI_HAL_RFID_RTF_CARRIER_IN_REFERENCE_TIMER); +} + +void furi_hal_rfid_rtf_carrier_in_stop() { + furi_hal_interrupt_set_isr(FuriHalInterruptIdTIM2, NULL, NULL); + furi_hal_interrupt_set_isr(FURI_HAL_RFID_RTF_PULL_OUT_DMA_CH1_IRQ, NULL, NULL); + + LL_TIM_DisableCounter(FURI_HAL_RFID_RTF_CARRIER_IN_TIMER); + LL_TIM_DisableCounter(FURI_HAL_RFID_RTF_CARRIER_IN_REFERENCE_TIMER); + LL_TIM_DisableAllOutputs(FURI_HAL_RFID_RTF_CARRIER_IN_TIMER); //used for pull pin OC + + FURI_CRITICAL_ENTER(); + LL_DMA_DeInit(FURI_HAL_RFID_RTF_PULL_OUT_DMA_CH1_DEF); + LL_DMA_DeInit(FURI_HAL_RFID_RTF_PULL_OUT_DMA_CH2_DEF); + + furi_hal_bus_disable(FURI_HAL_RFID_RTF_CARRIER_IN_TIMER_BUS); + furi_hal_bus_disable(FURI_HAL_RFID_RTF_CARRIER_IN_REFERENCE_TIMER_BUS); + + FURI_CRITICAL_EXIT(); +} + + + + + void furi_hal_rfid_comp_start() { LL_COMP_Enable(COMP1); // Magic diff --git a/firmware/targets/f7/furi_hal/furi_hal_rfid.h b/firmware/targets/f7/furi_hal/furi_hal_rfid.h index 7087ba991fa..a4770511f79 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_rfid.h +++ b/firmware/targets/f7/furi_hal/furi_hal_rfid.h @@ -76,6 +76,37 @@ void furi_hal_rfid_set_read_period(uint32_t period); */ void furi_hal_rfid_set_read_pulse(uint32_t pulse); +void furi_hal_rfid_rtf_carrier_out_stop(); + +void furi_hal_rfid_rtf_carrier_out_start(); + +/** start pull out dma timer for RTF rfid emulation mode + * + * @param length length of dma buffer + */ +void furi_hal_rfid_rtf_pull_out_dma_start(size_t length); + +/** switch timer settings to ETR during RTF rfid emulation mode + * + * @param prevTIMval pointer for storing previous timer duration value + * @param ext_prescaler prescaler to use for timer settings + */ +void furi_hal_rfid_rtf_carrier_in_ETR_mode(uint8_t ext_prescaler); + +/** start carrier in timers in RTF rfid emulation mode + */ +void furi_hal_rfid_rtf_carrier_in_start( + void* capture_context, + uint8_t ext_prescaler, + uint32_t* duration, + uint32_t* pulse, + size_t length, + FuriHalRfidReadCaptureCallback callback); + +/** stop carrier in timers in RTF rfid emulation mode + */ +void furi_hal_rfid_rtf_carrier_in_stop(); + /** Start/Enable comparator */ void furi_hal_rfid_comp_start(); diff --git a/lib/lfrfid/lfrfid_hitag_worker.c b/lib/lfrfid/lfrfid_hitag_worker.c index 4f315ef6f1f..706f0a5be2e 100644 --- a/lib/lfrfid/lfrfid_hitag_worker.c +++ b/lib/lfrfid/lfrfid_hitag_worker.c @@ -54,32 +54,6 @@ #define TAG "LFRFIDHitagWorker" -// TIMER definitions -#define CARRIER_OUT_TIMER TIM1 -#define CARRIER_OUT_TIMER_BUS FuriHalBusTIM1 -//#define CARRIER_OUT_TIMER_IRQ FuriHalInterruptIdTIM1 //TODO am i using this? -#define CARRIER_OUT_TIMER_CHANNEL LL_TIM_CHANNEL_CH1 // or LL_TIM_CHANNEL_CH1N - -#define CARRIER_IN_TIMER TIM2 -#define CARRIER_IN_TIMER_BUS FuriHalBusTIM2 -#define CARRIER_IN_TIMER_IND_CH LL_TIM_CHANNEL_CH2 //no longer used via ETR setup -#define CARRIER_IN_TIMER_DIR_CH LL_TIM_CHANNEL_CH1 //no longer used via ETR setup - -#define CARRIER_IN_REFERENCE_TIMER TIM1 -#define CARRIER_IN_REFERENCE_TIMER_BUS FuriHalBusTIM1 - -#define PULL_OUT_TIMER TIM2 -#define PULL_OUT_TIMER_BUS FuriHalBusTIM2 -#define PULL_OUT_TIMER_CHANNEL LL_TIM_CHANNEL_CH3 - -// DMA Channels definition -#define PULL_OUT_DMA DMA2 -#define PULL_OUT_DMA_CH1 LL_DMA_CHANNEL_1 -#define PULL_OUT_DMA_CH2 LL_DMA_CHANNEL_2 -#define PULL_OUT_DMA_CH1_IRQ FuriHalInterruptIdDma2Ch1 -#define PULL_OUT_DMA_CH1_DEF PULL_OUT_DMA, PULL_OUT_DMA_CH1 -#define PULL_OUT_DMA_CH2_DEF PULL_OUT_DMA, PULL_OUT_DMA_CH2 - typedef enum { HitagProtocolAntiCollision, HitagProtocolManchesterCoding, @@ -128,7 +102,6 @@ typedef struct { BufferStream* stream; VarintPair* pair; uint32_t capCounter; - uint32_t prevTIMval; } LfRfidHitagCaptureData; typedef struct { @@ -159,8 +132,6 @@ struct LFRFIDHitagWorker { static int32_t lfrfid_hitag_worker_emulate_thread(void* thread_context); static int32_t lfrfid_hitag_worker_read_thread(void* thread_context); -static void lfrfid_hitag_worker_carrier_in_IC_mode(void* capture_context); - //------------------------------------------------------------------ shared functions ------------------------------------------------------------------ LFRFIDHitagWorker* lfrfid_hitag_worker_alloc(ProtocolDict* dict) { @@ -215,9 +186,8 @@ void lfrfid_hitag_worker_stop(LFRFIDHitagWorker* worker) { furi_thread_join(worker->thread); } -//------------------------------------------------------------------ READ TAG: capture input interrupt functions ------------------------------------------------------------------ - -static void lfrfid_hitag_worker_capture_in_cc_isr(bool level, uint32_t duration, void* context) { +//------------------------------------------------------------------ READ TAG: TIMER & DMA callback functions ------------------------------------------------------------------ +static void lfrfid_hitag_worker_capture_in_cc_callback(bool level, uint32_t duration, void* context) { LfRfidHitagCaptureData* capData = context; //check if there is a new pair available: pulse + period @@ -233,481 +203,34 @@ static void lfrfid_hitag_worker_capture_in_cc_isr(bool level, uint32_t duration, } } -//------------------------------------------------------------------ READ TAG: carrier out TIMER, DMA & interrupts functions ------------------------------------------------------------------ - -static void lfrfid_hitag_worker_carrier_out_dma_isr(void* dma_context) { +//leave this in hitag_worker +static void lfrfid_hitag_worker_carrier_out_dma_callback(bool half, void* dma_context) { LFRFIDHitagWorker* worker = (LFRFIDHitagWorker*)dma_context; worker->DMAeventCount++; - - if(LL_DMA_IsActiveFlag_HT1(DMA1)) { - LL_DMA_ClearFlag_HT1(DMA1); - //halfway through DMA buffer --> first half can be repopulated - //just signal that it can be repopulated, not execute the repopulating itself, since this is an interrupt, holding up all other stuff + + if (half){ furi_event_flag_set(worker->events, 1 << LFRFIDHitagWorkerSignalHalfTransfer); - } - - if(LL_DMA_IsActiveFlag_TC1(DMA1)) { - LL_DMA_ClearFlag_TC1(DMA1); - //fully through DMA buffer --> second half can be repopulated - //just signal that it can be repopulated, not execute the repopulating itself, since this is an interrupt, holding up all other stuff + } else { furi_event_flag_set(worker->events, 1 << LFRFIDHitagWorkerSignalTransferComplete); } } -void lfrfid_hitag_worker_carrier_out_start( - uint32_t* duration, - uint32_t* pulse, - size_t length, - void* context) { - // configure timer - furi_hal_bus_enable(CARRIER_OUT_TIMER_BUS); - - LL_TIM_InitTypeDef TIM_InitStruct = {0}; - TIM_InitStruct.Prescaler = SystemCoreClock / (125000 * HITAG_BASEPERIOD) - - 1; // sets the basis TIMER frequency to 8*freq (1MHz) for the timer - TIM_InitStruct.Autoreload = - HITAG_BASEPERIOD - - 1; //initial PWM period =125kHz (ARR will be handled via DMA further on) - LL_TIM_Init(CARRIER_OUT_TIMER, &TIM_InitStruct); - LL_TIM_DisableARRPreload(CARRIER_OUT_TIMER); - - LL_TIM_OC_InitTypeDef TIM_OC_InitStruct = {0}; - TIM_OC_InitStruct.OCMode = LL_TIM_OCMODE_PWM1; - TIM_OC_InitStruct.OCNState = LL_TIM_OCSTATE_ENABLE; - TIM_OC_InitStruct.CompareValue = - HITAG_BASEPERIOD / - 2; //initial pulse duration of half period ((CCR will be handled via DMA further on) - LL_TIM_OC_Init(CARRIER_OUT_TIMER, CARRIER_OUT_TIMER_CHANNEL, &TIM_OC_InitStruct); - - LL_TIM_OC_SetPolarity(CARRIER_OUT_TIMER, CARRIER_OUT_TIMER_CHANNEL, LL_TIM_OCPOLARITY_HIGH); - LL_TIM_EnableDMAReq_UPDATE(CARRIER_OUT_TIMER); - - // configure DMA "mem -> ARR" channel - LL_DMA_InitTypeDef dma_config = {0}; - dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (CARRIER_OUT_TIMER->ARR); - dma_config.MemoryOrM2MDstAddress = (uint32_t)duration; - dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; - dma_config.Mode = LL_DMA_MODE_CIRCULAR; - dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; - dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; - dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; - dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; - dma_config.NbData = length; - dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM1_UP; - dma_config.Priority = LL_DMA_MODE_NORMAL; - LL_DMA_Init(DMA1, LL_DMA_CHANNEL_1, &dma_config); - LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1); - - // configure DMA "mem -> CCR1" channel - dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (CARRIER_OUT_TIMER->CCR1); - dma_config.MemoryOrM2MDstAddress = (uint32_t)pulse; - dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; - dma_config.Mode = LL_DMA_MODE_CIRCULAR; - dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; - dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; - dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; - dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; - dma_config.NbData = length; - dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM1_UP; - dma_config.Priority = LL_DMA_MODE_NORMAL; - LL_DMA_Init(DMA1, LL_DMA_CHANNEL_2, &dma_config); - LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2); - - // attach interrupt to one of DMA channels - furi_hal_interrupt_set_isr( - FuriHalInterruptIdDma1Ch1, lfrfid_hitag_worker_carrier_out_dma_isr, context); - LL_DMA_EnableIT_TC(DMA1, LL_DMA_CHANNEL_1); - LL_DMA_EnableIT_HT(DMA1, LL_DMA_CHANNEL_1); - - // start - LL_TIM_EnableAllOutputs(CARRIER_OUT_TIMER); - - LL_TIM_SetCounter(CARRIER_OUT_TIMER, 0); - LL_TIM_EnableCounter(CARRIER_OUT_TIMER); -} - -void lfrfid_hitag_worker_carrier_out_stop() { - LL_TIM_DisableCounter(CARRIER_OUT_TIMER); - LL_TIM_DisableAllOutputs(CARRIER_OUT_TIMER); - - furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch1, NULL, NULL); - LL_DMA_DisableIT_TC(DMA1, LL_DMA_CHANNEL_1); - LL_DMA_DisableIT_HT(DMA1, LL_DMA_CHANNEL_1); - - FURI_CRITICAL_ENTER(); - - LL_DMA_DeInit(DMA1, LL_DMA_CHANNEL_1); - LL_DMA_DeInit(DMA1, LL_DMA_CHANNEL_2); - - furi_hal_bus_disable(CARRIER_OUT_TIMER_BUS); - - FURI_CRITICAL_EXIT(); -} - -//------------------------------------------------------------------ EMULATE TAG: pull out TIMER, DMA & interrupts functions ------------------------------------------------------------------ - -static void lfrfid_hitag_worker_pull_out_dma_stop(void* capture_context) { - //reconfigure pull_out pin to fixed low state - //via forced OC INACTIVE MODE - LL_TIM_OC_SetMode(PULL_OUT_TIMER, PULL_OUT_TIMER_CHANNEL, LL_TIM_OCMODE_FORCED_INACTIVE); - //disable DMA channels & requests - LL_TIM_DisableDMAReq_UPDATE(PULL_OUT_TIMER); - LL_DMA_DisableChannel(PULL_OUT_DMA_CH1_DEF); //need to disable when using normal mode? - LL_DMA_DisableChannel(PULL_OUT_DMA_CH2_DEF); //need to disable when using normal mode? - //LL_TIM_DisableAllOutputs(PULL_OUT_TIMER); //no need to disable when reconfiguring pin? +//------------------------------------------------------------------ EMULATE TAG: TIMER callback functions)------------------------------------------------------------------ +static void lfrfid_hitag_worker_carrier_in_callback(bool level, uint32_t duration, void* capture_context){ + UNUSED(level); - //for logging both ccr & arr times during emulate (iso only arr) - LL_TIM_DisableIT_CC3(PULL_OUT_TIMER); - - //switch carrier detection back to input capture for stable readings (required for command detection) - lfrfid_hitag_worker_carrier_in_IC_mode(capture_context); -} - -static void lfrfid_hitag_worker_pull_out_dma_isr(void* capture_context) { - /* currently no HT interrupt enabled, only TC - if(LL_DMA_IsActiveFlag_HT1(PULL_OUT_DMA)) { - LL_DMA_ClearFlag_HT1(PULL_OUT_DMA); - //lfrfid_hitag_worker_pull_out_dma_stop(); - } - */ - - if(LL_DMA_IsActiveFlag_TC1(PULL_OUT_DMA)) { - LL_DMA_ClearFlag_TC1(PULL_OUT_DMA); - lfrfid_hitag_worker_pull_out_dma_stop(capture_context); - } -} - -static void lfrfid_hitag_worker_pull_out_dma_start(size_t length) { - //array pointers remain the same, but length changes so - //reset DMA length (only possible when DMA channel is disabled - //this also resets the DMA counter (the DMA length is the 'remaining counter' - LL_DMA_SetDataLength(PULL_OUT_DMA_CH1_DEF, length); - LL_DMA_SetDataLength(PULL_OUT_DMA_CH2_DEF, length); - - //enable DMA - LL_DMA_EnableChannel(PULL_OUT_DMA_CH1_DEF); - LL_DMA_EnableChannel(PULL_OUT_DMA_CH2_DEF); - - //set OC to PWM1 mode - LL_TIM_OC_SetMode( - PULL_OUT_TIMER, - PULL_OUT_TIMER_CHANNEL, - LL_TIM_OCMODE_PWM1); //during emulate (dma controlled) mode - - //only enable DMA requests on timer update after enabling both channels (otherwise you risk that first DMA request only triggers one of the channels) - LL_TIM_EnableDMAReq_UPDATE(PULL_OUT_TIMER); - - //for logging both ccr & arr times during emulate (iso only arr) - LL_TIM_EnableIT_CC3(PULL_OUT_TIMER); -} - -static void lfrfid_hitag_worker_pull_out_dma_setup( - uint32_t* duration, - uint32_t* pulse, - size_t length, - void* capture_context) { - //DMA setup - LL_TIM_DisableDMAReq_UPDATE(PULL_OUT_TIMER); //start with DMA requests disabled - - // configure DMA "mem -> ARR" channel - LL_DMA_InitTypeDef dma_config = {0}; - dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (PULL_OUT_TIMER->ARR); - dma_config.MemoryOrM2MDstAddress = (uint32_t)duration; - dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; - //dma_config.Mode = LL_DMA_MODE_CIRCULAR; //keep cycling through memory array - dma_config.Mode = LL_DMA_MODE_NORMAL; //cycle only once through memory array - dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; - dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; - dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; - dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; - dma_config.NbData = length; - dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; - dma_config.Priority = LL_DMA_MODE_NORMAL; - LL_DMA_Init(PULL_OUT_DMA_CH1_DEF, &dma_config); - - // configure DMA "mem -> CCR3" channel - dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (PULL_OUT_TIMER->CCR3); - dma_config.MemoryOrM2MDstAddress = (uint32_t)pulse; - dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; - //dma_config.Mode = LL_DMA_MODE_CIRCULAR; //keep cycling through memory array - dma_config.Mode = LL_DMA_MODE_NORMAL; //cycle only once through memory array - dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; - dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; - dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; - dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; - dma_config.NbData = length; - dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; - dma_config.Priority = LL_DMA_MODE_NORMAL; - LL_DMA_Init(PULL_OUT_DMA_CH2_DEF, &dma_config); - - // enable DMA interrupts - furi_hal_interrupt_set_isr( - PULL_OUT_DMA_CH1_IRQ, lfrfid_hitag_worker_pull_out_dma_isr, capture_context); - - //LL_DMA_EnableIT_HT(PULL_OUT_DMA_CH1_DEF); //let's try normal DMA mode and only transfer complete interrupt - LL_DMA_EnableIT_TC(PULL_OUT_DMA_CH1_DEF); -} - -//------------------------------------------------------------------ EMULATE TAG: carrier input TIMER & interrupt functions (switching input capture & ETR)------------------------------------------------------------------ - -static void lfrfid_hitag_worker_carrier_in_isr(void* capture_context) { - if(LL_TIM_IsActiveFlag_CC1(CARRIER_IN_TIMER)) { - uint32_t newTIMval = LL_TIM_IC_GetCaptureCH1(CARRIER_IN_TIMER); - LL_TIM_ClearFlag_CC1(CARRIER_IN_TIMER); - - //ARR for TIM2 in input capture mode is UINT32_MAX so at freq of 1MHz this is 70+ minutes, instead of resetting counter, just store prev value and take difference of both - //this will increase measurement accuracy, since both values are then purely hardware controlled, no dependency of interrupt timing to (re)set any timer values - //LL_TIM_SetCounter(CARRIER_IN_TIMER, 0); - - LfRfidHitagCaptureData* capData = capture_context; - capData->capCounter++; - //pack varint (as part of a pair) - //cleaner would be to pack as a varint iso varint_pair, though i'm not sure if I can get the data & size of a packed varint - varint_pair_pack(capData->pair, true, newTIMval - capData->prevTIMval); - //but send anyhow, doesn't matter that it's not a pair - buffer_stream_send_from_isr( - capData->stream, - varint_pair_get_data(capData->pair), - varint_pair_get_size(capData->pair)); - varint_pair_reset(capData->pair); - - capData->prevTIMval = newTIMval; - } else if(LL_TIM_IsActiveFlag_UPDATE(CARRIER_IN_TIMER)) { - uint32_t newTIMval = LL_TIM_GetCounter(CARRIER_IN_REFERENCE_TIMER); - LL_TIM_ClearFlag_UPDATE(CARRIER_IN_TIMER); - - //ARR for ref timer in ETR mode is UINT32_MAX so at freq of 1MHz this is 70+ minutes, instead of resetting counter, just store prev value and take difference of both - //this will increase measurement accuracy, since both values are then purely hardware controlled, no dependency of interrupt timing to (re)set any timer values - LL_TIM_SetCounter(CARRIER_IN_REFERENCE_TIMER, 0); - - LfRfidHitagCaptureData* capData = capture_context; - capData->capCounter++; - //pack varint (as part of a pair) - //cleaner would be to pack as a varint iso varint_pair, though i'm not sure if I can get the data & size of a packed varint - varint_pair_pack(capData->pair, true, newTIMval); // - capData->prevTIMval); - //but send anyhow, doesn't matter that it's not a pair - buffer_stream_send_from_isr( - capData->stream, - varint_pair_get_data(capData->pair), - varint_pair_get_size(capData->pair)); - varint_pair_reset(capData->pair); - - //capData->prevTIMval = newTIMval; - } else if(LL_TIM_IsActiveFlag_CC3(CARRIER_IN_TIMER)) { - uint32_t newTIMval = LL_TIM_GetCounter(CARRIER_IN_REFERENCE_TIMER); - LL_TIM_ClearFlag_CC3(CARRIER_IN_TIMER); - - //don't reset timer for the ccr (HIGH) timing, this way the arr (duration) timing is still full duraton timing (iso only LOW timing) - //LL_TIM_SetCounter(CARRIER_IN_REFERENCE_TIMER, 0); - - LfRfidHitagCaptureData* capData = capture_context; - capData->capCounter++; - //pack varint (as part of a pair) - //cleaner would be to pack as a varint iso varint_pair, though i'm not sure if I can get the data & size of a packed varint - varint_pair_pack(capData->pair, true, newTIMval); // - capData->prevTIMval); - //but send anyhow, doesn't matter that it's not a pair - buffer_stream_send_from_isr( - capData->stream, - varint_pair_get_data(capData->pair), - varint_pair_get_size(capData->pair)); - varint_pair_reset(capData->pair); - - //capData->prevTIMval = newTIMval; - } -} - -static void lfrfid_hitag_worker_carrier_in_ETR_mode(void* capture_context, uint8_t ext_prescaler) { - //disable counters temporarily - LL_TIM_DisableCounter(CARRIER_IN_TIMER); - LL_TIM_DisableCounter(CARRIER_IN_REFERENCE_TIMER); - - //disable Input capture & related interrupt - LL_TIM_DisableIT_CC1(CARRIER_IN_TIMER); - LL_TIM_CC_DisableChannel(CARRIER_IN_TIMER, CARRIER_IN_TIMER_DIR_CH); - - //reset capdata prev value - LfRfidHitagCaptureData* capData = capture_context; - capData->prevTIMval = 0; - - //switch clocksource to ETR with external prescaling via ARR - LL_TIM_SetPrescaler(CARRIER_IN_TIMER, 1 - 1); //prescaler is only applied at next update event - LL_TIM_GenerateEvent_UPDATE( - CARRIER_IN_TIMER); //prescaler is only applied at next update event //TODO: how to prevent this from creating a capdata entry - LL_TIM_SetAutoReload(CARRIER_IN_TIMER, ext_prescaler - 1); - LL_TIM_SetClockSource(CARRIER_IN_TIMER, LL_TIM_CLOCKSOURCE_EXT_MODE2); - - //reconfigure carrier_in pin to TIM2 ETR - furi_hal_gpio_init_ex( - &gpio_rfid_carrier, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedLow, GpioAltFn2TIM2); - - //reset timer counter & capture context for period calculation - LL_TIM_SetCounter(CARRIER_IN_TIMER, 0); - LL_TIM_SetCounter(CARRIER_IN_REFERENCE_TIMER, 0); - - //enable interrupt via update - LL_TIM_EnableIT_UPDATE(CARRIER_IN_TIMER); - - //re-enable counters - LL_TIM_EnableCounter(CARRIER_IN_TIMER); - LL_TIM_EnableCounter(CARRIER_IN_REFERENCE_TIMER); -} - -static void lfrfid_hitag_worker_carrier_in_IC_mode(void* capture_context) { - //disable counters temporarily - LL_TIM_DisableCounter(CARRIER_IN_TIMER); - LL_TIM_DisableCounter(CARRIER_IN_REFERENCE_TIMER); - - //disable interrupt via update - LL_TIM_DisableIT_UPDATE(CARRIER_IN_TIMER); - - //reset capdata prev value LfRfidHitagCaptureData* capData = capture_context; - capData->prevTIMval = 0; - - //switch clocksource to system/64, external prescaling is handled in IC prescaler - LL_TIM_SetClockSource(CARRIER_IN_TIMER, LL_TIM_CLOCKSOURCE_INTERNAL); - LL_TIM_SetPrescaler(CARRIER_IN_TIMER, 64 - 1); //prescaler is only applied at next update event - LL_TIM_GenerateEvent_UPDATE( - CARRIER_IN_TIMER); //prescaler is only applied at next update event //TODO: how to prevent this from creating a capdata entry - LL_TIM_SetAutoReload(CARRIER_IN_TIMER, UINT32_MAX); - - //reconfigure carrier_in pin to TIM2 CH1 for input capture - furi_hal_gpio_init_ex( - &gpio_rfid_carrier, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedLow, GpioAltFn1TIM2); - - //reset timer counter & capture context for period calculation - LL_TIM_SetCounter(CARRIER_IN_TIMER, 0); - LL_TIM_SetCounter(CARRIER_IN_REFERENCE_TIMER, 0); - - //enable Input capture & related interrupt - LL_TIM_EnableIT_CC1(CARRIER_IN_TIMER); - LL_TIM_CC_EnableChannel(CARRIER_IN_TIMER, CARRIER_IN_TIMER_DIR_CH); - - //re-enable counters - LL_TIM_EnableCounter(CARRIER_IN_TIMER); - LL_TIM_EnableCounter(CARRIER_IN_REFERENCE_TIMER); -} - -static void lfrfid_hitag_worker_carrier_in_start( - void* capture_context, - uint8_t ext_prescaler, - uint32_t* duration, - uint32_t* pulse, - size_t length) { - FURI_CRITICAL_ENTER(); - LL_DMA_DeInit(PULL_OUT_DMA_CH1_DEF); //required? - LL_DMA_DeInit(PULL_OUT_DMA_CH2_DEF); //required? - FURI_CRITICAL_EXIT(); - - furi_hal_bus_enable(CARRIER_IN_TIMER_BUS); - furi_hal_bus_enable(CARRIER_IN_REFERENCE_TIMER_BUS); - - //setup reference timer: simple setup with base freq of 1MHz and max autoreload - LL_TIM_InitTypeDef TIM_InitStruct_Ref = {0}; - TIM_InitStruct_Ref.Prescaler = - 64 - - 1; //system base freq is 64MHz, so this sets base freq for TIM to 1MHz (aka 1us period) - TIM_InitStruct_Ref.CounterMode = LL_TIM_COUNTERMODE_UP; - TIM_InitStruct_Ref.Autoreload = UINT32_MAX; - TIM_InitStruct_Ref.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1; - LL_TIM_Init(CARRIER_IN_REFERENCE_TIMER, &TIM_InitStruct_Ref); - - //setup carrier in timer for input capture - LL_TIM_InitTypeDef TIM_InitStruct = {0}; - TIM_InitStruct.Prescaler = - 64 - - 1; //system base freq is 64MHz, so this sets base freq for TIM to 1MHz (aka 1us period) - TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP; - TIM_InitStruct.Autoreload = UINT32_MAX; - TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1; - LL_TIM_Init(CARRIER_IN_TIMER, &TIM_InitStruct); - - LL_TIM_DisableARRPreload(CARRIER_IN_TIMER); - LL_TIM_SetClockSource( - CARRIER_IN_TIMER, - LL_TIM_CLOCKSOURCE_INTERNAL); //default is internal, so likely not required - LL_TIM_DisableDMAReq_TRIG(CARRIER_IN_TIMER); - LL_TIM_DisableIT_TRIG(CARRIER_IN_TIMER); - - //meanwhile already prepre the ETR - LL_TIM_ConfigETR( - CARRIER_IN_TIMER, - LL_TIM_ETR_POLARITY_INVERTED, - LL_TIM_ETR_PRESCALER_DIV1, - LL_TIM_ETR_FILTER_FDIV1); - - //INPUT CAPTURE SETUP - // Timer: channel 1 direct (from GPIO) - LL_TIM_IC_SetActiveInput( - CARRIER_IN_TIMER, CARRIER_IN_TIMER_DIR_CH, LL_TIM_ACTIVEINPUT_DIRECTTI); - //prescaling direct channel seems to be working fine (and is necessary since otherwise sd write cannot keep up) - if(ext_prescaler == 4) { - LL_TIM_IC_SetPrescaler(CARRIER_IN_TIMER, CARRIER_IN_TIMER_DIR_CH, LL_TIM_ICPSC_DIV4); - } else if(ext_prescaler == 2) { - LL_TIM_IC_SetPrescaler(CARRIER_IN_TIMER, CARRIER_IN_TIMER_DIR_CH, LL_TIM_ICPSC_DIV2); - } else { - LL_TIM_IC_SetPrescaler(CARRIER_IN_TIMER, CARRIER_IN_TIMER_DIR_CH, LL_TIM_ICPSC_DIV1); - } - LL_TIM_IC_SetPolarity(CARRIER_IN_TIMER, CARRIER_IN_TIMER_DIR_CH, LL_TIM_IC_POLARITY_RISING); - LL_TIM_IC_SetFilter(CARRIER_IN_TIMER, CARRIER_IN_TIMER_DIR_CH, LL_TIM_IC_FILTER_FDIV1); - /* Timer: channel 2 indirect (from channel 1) - // only measure & write to file the period (not the on & off cycle), this reduced CPU load on SD write to (hopefully) keep up with period measurements without prescaler - LL_TIM_IC_SetActiveInput(CARRIER_IN_TIMER, CARRIER_IN_TIMER_IND_CH, LL_TIM_ACTIVEINPUT_INDIRECTTI); - //presaling indirect channel doesn't really work well yet :/ - LL_TIM_IC_SetPrescaler(CARRIER_IN_TIMER, CARRIER_IN_TIMER_IND_CH, LL_TIM_ICPSC_DIV2); - LL_TIM_IC_SetPolarity(CARRIER_IN_TIMER, CARRIER_IN_TIMER_IND_CH, LL_TIM_IC_POLARITY_FALLING); - LL_TIM_IC_SetFilter(CARRIER_IN_TIMER, CARRIER_IN_TIMER_IND_CH, LL_TIM_IC_FILTER_FDIV1); - */ - - //set interrupt callback for capturing period - LL_TIM_EnableIT_CC1(CARRIER_IN_TIMER); - //LL_TIM_EnableIT_CC2(CARRIER_IN_TIMER); - furi_hal_interrupt_set_isr( - FuriHalInterruptIdTIM2, lfrfid_hitag_worker_carrier_in_isr, capture_context); - - //OUTPUT COMPARE SETUP - LL_TIM_OC_InitTypeDef TIM_OC_InitStruct = {0}; - //TIM_OC_InitStruct.OCMode = LL_TIM_OCMODE_PWM1; //during emulate (dma controlled) mode - TIM_OC_InitStruct.OCMode = - LL_TIM_OCMODE_FORCED_INACTIVE; //during carrier in put output to forced low state - TIM_OC_InitStruct.OCState = LL_TIM_OCSTATE_ENABLE; - TIM_OC_InitStruct.CompareValue = - 0; //0% this should have almost similar effect as keeping output forced inactive (there's still some micropulse emited, going high at ARR and immediately down again at CCR value) - LL_TIM_OC_Init(PULL_OUT_TIMER, PULL_OUT_TIMER_CHANNEL, &TIM_OC_InitStruct); - LL_TIM_OC_SetPolarity( - PULL_OUT_TIMER, - PULL_OUT_TIMER_CHANNEL, - LL_TIM_OCPOLARITY_HIGH); //active high (gpio goes high when pulse is high) - - //INIT DMA (do not start it yet) - lfrfid_hitag_worker_pull_out_dma_setup(duration, pulse, length, capture_context); - - LL_TIM_CC_EnableChannel(CARRIER_IN_TIMER, CARRIER_IN_TIMER_DIR_CH); - //LL_TIM_CC_EnableChannel(CARRIER_IN_TIMER, CARRIER_IN_TIMER_IND_CH); - LL_TIM_CC_EnableChannel(PULL_OUT_TIMER, PULL_OUT_TIMER_CHANNEL); - LL_TIM_SetCounter(CARRIER_IN_TIMER, 0); - LL_TIM_SetCounter(CARRIER_IN_REFERENCE_TIMER, 0); - LL_TIM_EnableCounter(CARRIER_IN_TIMER); - LL_TIM_EnableCounter(CARRIER_IN_REFERENCE_TIMER); -} - -void lfrfid_hitag_worker_carrier_in_stop() { - furi_hal_interrupt_set_isr(FuriHalInterruptIdTIM2, NULL, NULL); - furi_hal_interrupt_set_isr(PULL_OUT_DMA_CH1_IRQ, NULL, NULL); - - LL_TIM_DisableCounter(CARRIER_IN_TIMER); - LL_TIM_DisableCounter(CARRIER_IN_REFERENCE_TIMER); - LL_TIM_DisableAllOutputs(CARRIER_IN_TIMER); //used for pull pin OC - - FURI_CRITICAL_ENTER(); - LL_DMA_DeInit(PULL_OUT_DMA_CH1_DEF); - LL_DMA_DeInit(PULL_OUT_DMA_CH2_DEF); - - furi_hal_bus_disable(CARRIER_IN_TIMER_BUS); - furi_hal_bus_disable(CARRIER_IN_REFERENCE_TIMER_BUS); - - FURI_CRITICAL_EXIT(); + capData->capCounter++; + //pack varint (as part of a pair) + //cleaner would be to pack as a varint iso varint_pair, though i'm not sure if I can get the data & size of a packed varint + varint_pair_pack(capData->pair, true, duration); + //but send anyhow, doesn't matter that it's not a pair + buffer_stream_send_from_isr( + capData->stream, + varint_pair_get_data(capData->pair), + varint_pair_get_size(capData->pair)); + varint_pair_reset(capData->pair); } //------------------------------------------------------------------ hitag data parsing functions ------------------------------------------------------------------ @@ -848,27 +371,8 @@ static void lfrfid_hitag_worker_yield_dma_buffer_BPLM( data->CMDposition++; } - /*if buffer is not full, but command is fully processed: fill remainder of buffer with normal on cycles - if (stimer_buffer_ccr[s]=HITAG_BASEPERIOD/4; //25%duty cycle - dataDMA->timer_buffer_arr[s]=1*HITAG_BASEPERIOD-1; - } else { - dataDMA->timer_buffer_ccr[s]=HITAG_BASEPERIOD/2; - dataDMA->timer_buffer_arr[s]=HITAG_BASEPERIOD-1; - } - } - //other stuff to do? - } - */ - - //if buffer was full, but command not yet fully processed: set starting position for next time: - if(!itemProcessed) { - data->CMDsubposition += (len + start) - lastarrposition; - } else { - data->CMDsubposition = 0; - } + //set starting position for next time: + data->CMDsubposition += (len + start) - lastarrposition; } static uint16_t lfrfid_hitag_worker_yield_dma_buffer_MC( @@ -978,14 +482,14 @@ static void lfrfid_hitag_worker_decoder_feed( (*bits)++; } else if(*bits == startBits) { //same as previous bit (last startbit it 1, so add 1 to the memBlock) - memBlock[(*bits - startBits) / 32] |= (bit >> (*bits - startBits) % 32); //add 1 + memBlock[(*bits - startBits) / 32] |= (bit >> ((*bits - startBits) % 32)); //add 1 (*bits)++; } else { //add same as previous bit if(memBlock[(*bits - startBits - 1) / 32] & - (bit >> (*bits - startBits - 1) % 32)) { + (bit >> ((*bits - startBits - 1) % 32))) { memBlock[(*bits - startBits) / 32] |= - (bit >> (*bits - startBits) % 32); //add 1 + (bit >> ((*bits - startBits) % 32)); //add 1 (*bits)++; } else { (*bits)++; //no mem action for 0, just increase bitcounter @@ -1003,7 +507,7 @@ static void lfrfid_hitag_worker_decoder_feed( *half = true; } else if(*bits == startBits) { //assuming all startbits were 1's, this is a data 10 detection - memBlock[(*bits - startBits) / 32] |= (bit >> (*bits - startBits) % 32); //add 1 + memBlock[(*bits - startBits) / 32] |= (bit >> ((*bits - startBits) % 32)); //add 1 (*bits)++; (*bits)++; //no mem action for 0, just increase bitcounter *half = true; @@ -1011,12 +515,12 @@ static void lfrfid_hitag_worker_decoder_feed( //if we're in half of a bit, add 1, else add 10 and set half flag if(*half) { memBlock[(*bits - startBits) / 32] |= - (bit >> (*bits - startBits) % 32); //add 1 + (bit >> ((*bits - startBits) % 32)); //add 1 (*bits)++; *half = false; } else { memBlock[(*bits - startBits) / 32] |= - (bit >> (*bits - startBits) % 32); //add 1 + (bit >> ((*bits - startBits) % 32)); //add 1 (*bits)++; (*bits)++; //no mem action for 0, just increase bitcounter *half = true; @@ -1031,7 +535,7 @@ static void lfrfid_hitag_worker_decoder_feed( } else { //TODO check that prev bit is indeed 0 //add 10 to the memBlock - memBlock[(*bits - startBits) / 32] |= (bit >> (*bits - startBits) % 32); //add 1 + memBlock[(*bits - startBits) / 32] |= (bit >> ((*bits - startBits) % 32)); //add 1 (*bits)++; (*bits)++; //no mem action for 0, just increase bitcounter } @@ -1043,12 +547,12 @@ static void lfrfid_hitag_worker_decoder_feed( } else if(*half && pulse < HITAG_DURATION_M) { //second half or 0 bit + another 1 bit memBlock[(*bits - startBits) / 32] |= - (bit >> (*bits - startBits) % 32); //add 1 + (bit >> ((*bits - startBits) % 32)); //add 1 (*bits)++; } else if(!(*half) && pulse < HITAG_DURATION_S) { //another 1 bit memBlock[(*bits - startBits) / 32] |= - (bit >> (*bits - startBits) % 32); //add 1 + (bit >> ((*bits - startBits) % 32)); //add 1 (*bits)++; } else { //invalid pulse duration --> reset? @@ -1070,7 +574,7 @@ static void lfrfid_hitag_worker_decoder_feed( //if first short: add 1 to memBlock and increase half counter if(*half == false) { if(*bits >= startBits) { - memBlock[(*bits - startBits) / 32] |= (bit >> (*bits - startBits) % 32); + memBlock[(*bits - startBits) / 32] |= ((bit >> (*bits - startBits) % 32)); } (*bits)++; *half = true; @@ -1323,8 +827,7 @@ static int32_t lfrfid_hitag_worker_emulate_thread(void* thread_context) { capData->stream = buffer_stream_alloc(EMULATE_BUFFER_SIZE, EMULATE_BUFFER_COUNT); capData->pair = varint_pair_alloc(); capData->capCounter = 0; - capData->prevTIMval = 0; - + //init dmaData for emulate mode: uint16_t dmaLen = 0; TimerDMAData* dataDMA = malloc(sizeof(TimerDMAData)); @@ -1343,7 +846,7 @@ static int32_t lfrfid_hitag_worker_emulate_thread(void* thread_context) { // PA15 carrier_in to alt ftn 1 (TIM2 CH1) to use it for input capture furi_hal_gpio_init_ex( &gpio_rfid_carrier, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedLow, GpioAltFn1TIM2); - // pull to alternate function 1 (TIM2 CH3) to drive low (for read carrier) or pull antenna via DMA (for emulation) + // pull to alternate function 1 (TIM2 CH3) to drive low (for command detection) or pull antenna via DMA (for command replies) furi_hal_gpio_init_ex( &gpio_nfc_irq_rfid_pull, GpioModeAltFunctionPushPull, @@ -1352,12 +855,13 @@ static int32_t lfrfid_hitag_worker_emulate_thread(void* thread_context) { GpioAltFn1TIM2); //start capture via carrier in instead of rfid in - lfrfid_hitag_worker_carrier_in_start( + furi_hal_rfid_rtf_carrier_in_start( capData, worker->carrierPrescaler, dataDMA->timer_buffer_arr, dataDMA->timer_buffer_ccr, - dmaLen); + dmaLen, + lfrfid_hitag_worker_carrier_in_callback); uint8_t prescaler = worker->carrierPrescaler; uint8_t period = 8 * prescaler - 1; @@ -1493,7 +997,7 @@ static int32_t lfrfid_hitag_worker_emulate_thread(void* thread_context) { //update the previous (last) bit from 0 to 1 if a command was ongoing if(maxBitCounter > 0) { maxBits[(maxBitCounter - 1) / 32] |= - (one >> (maxBitCounter - 1) % 32); + (one >> ((maxBitCounter - 1) % 32)); } maxDelta -= miniMaxDelta; lastMaxIndex = miniMaxIndex; @@ -1503,11 +1007,11 @@ static int32_t lfrfid_hitag_worker_emulate_thread(void* thread_context) { //now process the (possibly updated) delta if(maxDelta >= 18) { - if(18 <= maxDelta && maxDelta <= 22) { + if(maxDelta <= 22) { maxBitCounter++; } else if(26 <= maxDelta && maxDelta <= 32) { maxBits[(maxBitCounter) / 32] |= - (one >> (maxBitCounter) % 32); + (one >> ((maxBitCounter) % 32)); maxBitCounter++; } else if(maxBitCounter > 0) { //end of bitstring caused by invalid delta @@ -1582,7 +1086,7 @@ static int32_t lfrfid_hitag_worker_emulate_thread(void* thread_context) { //update the previous (last) bit from 0 to 1 if a command was ongoing if(minBitCounter > 0) { minBits[(minBitCounter - 1) / 32] |= - (one >> (minBitCounter - 1) % 32); + (one >> ((minBitCounter - 1) % 32)); } minDelta -= miniMinDelta; lastMinIndex = miniMinIndex; @@ -1592,11 +1096,11 @@ static int32_t lfrfid_hitag_worker_emulate_thread(void* thread_context) { //now process the (possibly updated) delta if(minDelta >= 18) { - if(18 <= minDelta && minDelta <= 22) { + if(minDelta <= 22) { minBitCounter++; } else if(26 <= minDelta && minDelta <= 32) { minBits[(minBitCounter) / 32] |= - (one >> (minBitCounter) % 32); + (one >> ((minBitCounter) % 32)); minBitCounter++; } else if(minBitCounter > 0) { //end of bitstring caused by invalid delta @@ -1892,8 +1396,8 @@ static int32_t lfrfid_hitag_worker_emulate_thread(void* thread_context) { } if(dmaLen > 0) { //switch to reply mode - //switch carrier mode to ETR required for emulation - lfrfid_hitag_worker_carrier_in_ETR_mode(capData, prescaler); + //switch carrier mode to ETR required for replying + furi_hal_rfid_rtf_carrier_in_ETR_mode(prescaler); //respect hitag WAIT1 time uint32_t wait1_periods = 180 / prescaler; @@ -1902,7 +1406,7 @@ static int32_t lfrfid_hitag_worker_emulate_thread(void* thread_context) { } //just start the DMA, stopping is handled in DMA transfer complete interrupt - lfrfid_hitag_worker_pull_out_dma_start((size_t)dmaLen); + furi_hal_rfid_rtf_pull_out_dma_start((size_t)dmaLen); } } } @@ -1926,7 +1430,7 @@ static int32_t lfrfid_hitag_worker_emulate_thread(void* thread_context) { } //stop carrier capture in - lfrfid_hitag_worker_carrier_in_stop(); + furi_hal_rfid_rtf_carrier_in_stop(); free(worker->tag); free(dataDMA); @@ -2014,14 +1518,14 @@ static int32_t lfrfid_hitag_worker_read_thread(void* thread_context) { dataCMD, backupCMD, dataDMA, DMA_BUFFER_SIZE / 2, DMA_BUFFER_SIZE / 2); //start TIM1 with DMA function - lfrfid_hitag_worker_carrier_out_start( - dataDMA->timer_buffer_arr, dataDMA->timer_buffer_ccr, DMA_BUFFER_SIZE, worker); + furi_hal_rfid_rtf_carrier_out_start( + dataDMA->timer_buffer_arr, dataDMA->timer_buffer_ccr, DMA_BUFFER_SIZE, lfrfid_hitag_worker_carrier_out_dma_callback, worker); // start capture LfRfidHitagCaptureData* capData = malloc(sizeof(LfRfidHitagCaptureData)); capData->stream = buffer_stream_alloc(READ_BUFFER_SIZE, READ_BUFFER_COUNT); capData->pair = varint_pair_alloc(); - furi_hal_rfid_tim_read_capture_start(lfrfid_hitag_worker_capture_in_cc_isr, capData); + furi_hal_rfid_tim_read_capture_start(lfrfid_hitag_worker_capture_in_cc_callback, capData); while(1) { Buffer* buffer = buffer_stream_receive(capData->stream, 1); @@ -2359,7 +1863,7 @@ static int32_t lfrfid_hitag_worker_read_thread(void* thread_context) { //stop timers furi_hal_rfid_tim_read_capture_stop(); - lfrfid_hitag_worker_carrier_out_stop(); + furi_hal_rfid_rtf_carrier_out_stop(); //free memory free(dataCMD); diff --git a/lib/lfrfid/lfrfid_worker_modes.c b/lib/lfrfid/lfrfid_worker_modes.c index 095677288c9..6a879821d6b 100644 --- a/lib/lfrfid/lfrfid_worker_modes.c +++ b/lib/lfrfid/lfrfid_worker_modes.c @@ -375,7 +375,6 @@ static LFRFIDWorkerReadState lfrfid_worker_read_rtf( //reader talks first LFRFIDProtocolHitag1; //TODO get protocol ID from hitag_worker when expanding the worker to include other hitag protocols break; } else if(++delays >= timeout / delay_ms) { - state = LFRFIDWorkerReadTimeout; break; } } diff --git a/lib/lfrfid/protocols/protocol_hitag1.c b/lib/lfrfid/protocols/protocol_hitag1.c index 1890ea1ad0a..0059fd0c1bc 100644 --- a/lib/lfrfid/protocols/protocol_hitag1.c +++ b/lib/lfrfid/protocols/protocol_hitag1.c @@ -3,7 +3,7 @@ #include "lfrfid_protocols.h" #define HITAG1_PAGES 64 -#define HITAG1_DATA_SIZE HITAG1_PAGES * 4 + HITAG1_PAGES +#define HITAG1_DATA_SIZE (HITAG1_PAGES * 4 + HITAG1_PAGES) typedef struct { uint8_t tagData[HITAG1_DATA_SIZE]; From 336de4bf9e7f985232399af8002e5f5f640af9fc Mon Sep 17 00:00:00 2001 From: Aleksandr Kutuzov Date: Sun, 26 Nov 2023 16:24:37 +0900 Subject: [PATCH 11/14] Format Sources --- lib/lfrfid/lfrfid_hitag_worker.c | 23 +++--- targets/f7/furi_hal/furi_hal_rfid.c | 112 +++++++++++++++++----------- 2 files changed, 82 insertions(+), 53 deletions(-) diff --git a/lib/lfrfid/lfrfid_hitag_worker.c b/lib/lfrfid/lfrfid_hitag_worker.c index 706f0a5be2e..fcbe06e9570 100644 --- a/lib/lfrfid/lfrfid_hitag_worker.c +++ b/lib/lfrfid/lfrfid_hitag_worker.c @@ -187,7 +187,8 @@ void lfrfid_hitag_worker_stop(LFRFIDHitagWorker* worker) { } //------------------------------------------------------------------ READ TAG: TIMER & DMA callback functions ------------------------------------------------------------------ -static void lfrfid_hitag_worker_capture_in_cc_callback(bool level, uint32_t duration, void* context) { +static void + lfrfid_hitag_worker_capture_in_cc_callback(bool level, uint32_t duration, void* context) { LfRfidHitagCaptureData* capData = context; //check if there is a new pair available: pulse + period @@ -207,17 +208,17 @@ static void lfrfid_hitag_worker_capture_in_cc_callback(bool level, uint32_t dura static void lfrfid_hitag_worker_carrier_out_dma_callback(bool half, void* dma_context) { LFRFIDHitagWorker* worker = (LFRFIDHitagWorker*)dma_context; worker->DMAeventCount++; - - if (half){ + + if(half) { furi_event_flag_set(worker->events, 1 << LFRFIDHitagWorkerSignalHalfTransfer); } else { furi_event_flag_set(worker->events, 1 << LFRFIDHitagWorkerSignalTransferComplete); } } - //------------------------------------------------------------------ EMULATE TAG: TIMER callback functions)------------------------------------------------------------------ -static void lfrfid_hitag_worker_carrier_in_callback(bool level, uint32_t duration, void* capture_context){ +static void + lfrfid_hitag_worker_carrier_in_callback(bool level, uint32_t duration, void* capture_context) { UNUSED(level); LfRfidHitagCaptureData* capData = capture_context; @@ -227,9 +228,7 @@ static void lfrfid_hitag_worker_carrier_in_callback(bool level, uint32_t duratio varint_pair_pack(capData->pair, true, duration); //but send anyhow, doesn't matter that it's not a pair buffer_stream_send_from_isr( - capData->stream, - varint_pair_get_data(capData->pair), - varint_pair_get_size(capData->pair)); + capData->stream, varint_pair_get_data(capData->pair), varint_pair_get_size(capData->pair)); varint_pair_reset(capData->pair); } @@ -827,7 +826,7 @@ static int32_t lfrfid_hitag_worker_emulate_thread(void* thread_context) { capData->stream = buffer_stream_alloc(EMULATE_BUFFER_SIZE, EMULATE_BUFFER_COUNT); capData->pair = varint_pair_alloc(); capData->capCounter = 0; - + //init dmaData for emulate mode: uint16_t dmaLen = 0; TimerDMAData* dataDMA = malloc(sizeof(TimerDMAData)); @@ -1519,7 +1518,11 @@ static int32_t lfrfid_hitag_worker_read_thread(void* thread_context) { //start TIM1 with DMA function furi_hal_rfid_rtf_carrier_out_start( - dataDMA->timer_buffer_arr, dataDMA->timer_buffer_ccr, DMA_BUFFER_SIZE, lfrfid_hitag_worker_carrier_out_dma_callback, worker); + dataDMA->timer_buffer_arr, + dataDMA->timer_buffer_ccr, + DMA_BUFFER_SIZE, + lfrfid_hitag_worker_carrier_out_dma_callback, + worker); // start capture LfRfidHitagCaptureData* capData = malloc(sizeof(LfRfidHitagCaptureData)); diff --git a/targets/f7/furi_hal/furi_hal_rfid.c b/targets/f7/furi_hal/furi_hal_rfid.c index 0fffd13c802..4cbe1e7180e 100644 --- a/targets/f7/furi_hal/furi_hal_rfid.c +++ b/targets/f7/furi_hal/furi_hal_rfid.c @@ -67,9 +67,10 @@ #define FURI_HAL_RFID_RTF_PULL_OUT_DMA_CH1 LL_DMA_CHANNEL_1 #define FURI_HAL_RFID_RTF_PULL_OUT_DMA_CH2 LL_DMA_CHANNEL_2 #define FURI_HAL_RFID_RTF_PULL_OUT_DMA_CH1_IRQ FuriHalInterruptIdDma2Ch1 -#define FURI_HAL_RFID_RTF_PULL_OUT_DMA_CH1_DEF FURI_HAL_RFID_RTF_PULL_OUT_DMA, FURI_HAL_RFID_RTF_PULL_OUT_DMA_CH1 -#define FURI_HAL_RFID_RTF_PULL_OUT_DMA_CH2_DEF FURI_HAL_RFID_RTF_PULL_OUT_DMA, FURI_HAL_RFID_RTF_PULL_OUT_DMA_CH2 - +#define FURI_HAL_RFID_RTF_PULL_OUT_DMA_CH1_DEF \ + FURI_HAL_RFID_RTF_PULL_OUT_DMA, FURI_HAL_RFID_RTF_PULL_OUT_DMA_CH1 +#define FURI_HAL_RFID_RTF_PULL_OUT_DMA_CH2_DEF \ + FURI_HAL_RFID_RTF_PULL_OUT_DMA, FURI_HAL_RFID_RTF_PULL_OUT_DMA_CH2 typedef struct { uint32_t counter; @@ -81,7 +82,7 @@ typedef struct { FuriHalRfidReadCaptureCallback read_capture_callback; void* context; FuriHalRfidField field; - uint32_t prevTIMval; + uint32_t prevTIMval; } FuriHalRfid; FuriHalRfid* furi_hal_rfid = NULL; @@ -98,7 +99,7 @@ void furi_hal_rfid_init() { furi_hal_rfid = malloc(sizeof(FuriHalRfid)); furi_hal_rfid->field.counter = 0; furi_hal_rfid->field.set_tim_counter_cnt = 0; - furi_hal_rfid->prevTIMval = 0; + furi_hal_rfid->prevTIMval = 0; furi_hal_rfid_pins_reset(); @@ -457,9 +458,6 @@ void furi_hal_rfid_set_read_pulse(uint32_t pulse) { #endif } - - - static void furi_hal_rfid_rtf_carrier_out_dma_isr(void* dma_context) { if(LL_DMA_IsActiveFlag_HT1(DMA1)) { LL_DMA_ClearFlag_HT1(DMA1); @@ -482,7 +480,7 @@ void furi_hal_rfid_rtf_carrier_out_start( // setup interrupts furi_hal_rfid->dma_callback = callback; - + // configure timer furi_hal_bus_enable(CARRIER_OUT_TIMER_BUS); @@ -568,9 +566,9 @@ static void furi_hal_rfid_rtf_carrier_in_IC_mode(); static void furi_hal_rfid_rtf_carrier_in_isr(void* capture_context) { uint32_t TIMval = 0; - //note that I'm doing 2 captures during ETR mode (pulse & duration) and sending both towards hitagworker as a duration, though this is only used for logging, no processing is done - //while in IC mode I'm only doing 1 capture (duration only) and sending this towards hitagworker as a duration used for command detection - if(LL_TIM_IsActiveFlag_CC1(FURI_HAL_RFID_RTF_CARRIER_IN_TIMER)) { + //note that I'm doing 2 captures during ETR mode (pulse & duration) and sending both towards hitagworker as a duration, though this is only used for logging, no processing is done + //while in IC mode I'm only doing 1 capture (duration only) and sending this towards hitagworker as a duration used for command detection + if(LL_TIM_IsActiveFlag_CC1(FURI_HAL_RFID_RTF_CARRIER_IN_TIMER)) { //INPUT CAPTURE trigger on channel 1 for CARRIER_IN_TIMER (TIM2) is used in command detection mode to capture duration uint32_t newTIMval = LL_TIM_IC_GetCaptureCH1(FURI_HAL_RFID_RTF_CARRIER_IN_TIMER); LL_TIM_ClearFlag_CC1(FURI_HAL_RFID_RTF_CARRIER_IN_TIMER); @@ -590,8 +588,8 @@ static void furi_hal_rfid_rtf_carrier_in_isr(void* capture_context) { TIMval = LL_TIM_GetCounter(FURI_HAL_RFID_RTF_CARRIER_IN_REFERENCE_TIMER); LL_TIM_ClearFlag_CC3(FURI_HAL_RFID_RTF_PULL_OUT_TIMER); } - - if (TIMval != 0){ + + if(TIMval != 0) { furi_hal_rfid->read_capture_callback(true, TIMval, capture_context); } } @@ -599,12 +597,17 @@ static void furi_hal_rfid_rtf_carrier_in_isr(void* capture_context) { static void furi_hal_rfid_rtf_pull_out_dma_stop() { //reconfigure pull_out pin to fixed low state //via forced OC INACTIVE MODE - LL_TIM_OC_SetMode(FURI_HAL_RFID_RTF_PULL_OUT_TIMER, FURI_HAL_RFID_RTF_PULL_OUT_TIMER_CHANNEL, LL_TIM_OCMODE_FORCED_INACTIVE); + LL_TIM_OC_SetMode( + FURI_HAL_RFID_RTF_PULL_OUT_TIMER, + FURI_HAL_RFID_RTF_PULL_OUT_TIMER_CHANNEL, + LL_TIM_OCMODE_FORCED_INACTIVE); //disable DMA channels & requests LL_TIM_DisableDMAReq_UPDATE(FURI_HAL_RFID_RTF_PULL_OUT_TIMER); - LL_DMA_DisableChannel(FURI_HAL_RFID_RTF_PULL_OUT_DMA_CH1_DEF); //need to disable when using normal mode? - LL_DMA_DisableChannel(FURI_HAL_RFID_RTF_PULL_OUT_DMA_CH2_DEF); //need to disable when using normal mode? + LL_DMA_DisableChannel( + FURI_HAL_RFID_RTF_PULL_OUT_DMA_CH1_DEF); //need to disable when using normal mode? + LL_DMA_DisableChannel( + FURI_HAL_RFID_RTF_PULL_OUT_DMA_CH2_DEF); //need to disable when using normal mode? //LL_TIM_DisableAllOutputs(FURI_HAL_RFID_RTF_PULL_OUT_TIMER); //no need to disable when reconfiguring pin? //for logging both ccr & arr times during emulate (iso only arr) @@ -616,7 +619,7 @@ static void furi_hal_rfid_rtf_pull_out_dma_stop() { static void furi_hal_rfid_rtf_pull_out_dma_isr() { // currently no HT interrupt enabled, only TC - if(LL_DMA_IsActiveFlag_TC1(FURI_HAL_RFID_RTF_PULL_OUT_DMA)) { + if(LL_DMA_IsActiveFlag_TC1(FURI_HAL_RFID_RTF_PULL_OUT_DMA)) { LL_DMA_ClearFlag_TC1(FURI_HAL_RFID_RTF_PULL_OUT_DMA); furi_hal_rfid_rtf_pull_out_dma_stop(); } @@ -646,12 +649,11 @@ void furi_hal_rfid_rtf_pull_out_dma_start(size_t length) { LL_TIM_EnableIT_CC3(FURI_HAL_RFID_RTF_PULL_OUT_TIMER); } -static void furi_hal_rfid_rtf_pull_out_dma_setup( - uint32_t* duration, - uint32_t* pulse, - size_t length) { +static void + furi_hal_rfid_rtf_pull_out_dma_setup(uint32_t* duration, uint32_t* pulse, size_t length) { //DMA setup - LL_TIM_DisableDMAReq_UPDATE(FURI_HAL_RFID_RTF_PULL_OUT_TIMER); //start with DMA requests disabled + LL_TIM_DisableDMAReq_UPDATE( + FURI_HAL_RFID_RTF_PULL_OUT_TIMER); //start with DMA requests disabled // configure DMA "mem -> ARR" channel LL_DMA_InitTypeDef dma_config = {0}; @@ -695,20 +697,23 @@ static void furi_hal_rfid_rtf_pull_out_dma_setup( void furi_hal_rfid_rtf_carrier_in_ETR_mode(uint8_t ext_prescaler) { //ETR mode used during emulation while replying to commands from reader //it has less accuracy than IC mode, but microcontroller cannot run DMA for pull out and input capture for carrier in at the same time - + //disable counters temporarily LL_TIM_DisableCounter(FURI_HAL_RFID_RTF_CARRIER_IN_TIMER); LL_TIM_DisableCounter(FURI_HAL_RFID_RTF_CARRIER_IN_REFERENCE_TIMER); //disable Input capture & related interrupt LL_TIM_DisableIT_CC1(FURI_HAL_RFID_RTF_CARRIER_IN_TIMER); - LL_TIM_CC_DisableChannel(FURI_HAL_RFID_RTF_CARRIER_IN_TIMER, FURI_HAL_RFID_RTF_CARRIER_IN_TIMER_DIR_CH); + LL_TIM_CC_DisableChannel( + FURI_HAL_RFID_RTF_CARRIER_IN_TIMER, FURI_HAL_RFID_RTF_CARRIER_IN_TIMER_DIR_CH); //reset previous TIMER value furi_hal_rfid->prevTIMval = 0; //switch clocksource to ETR with external prescaling via ARR - LL_TIM_SetPrescaler(FURI_HAL_RFID_RTF_CARRIER_IN_TIMER, 1 - 1); //prescaler is only applied at next update event + LL_TIM_SetPrescaler( + FURI_HAL_RFID_RTF_CARRIER_IN_TIMER, + 1 - 1); //prescaler is only applied at next update event LL_TIM_GenerateEvent_UPDATE( FURI_HAL_RFID_RTF_CARRIER_IN_TIMER); //prescaler is only applied at next update event //TODO: how to prevent this from creating a capture data entry LL_TIM_SetAutoReload(FURI_HAL_RFID_RTF_CARRIER_IN_TIMER, ext_prescaler - 1); @@ -732,7 +737,7 @@ void furi_hal_rfid_rtf_carrier_in_ETR_mode(uint8_t ext_prescaler) { static void furi_hal_rfid_rtf_carrier_in_IC_mode() { //input caputre mode used during emulation while scanning for commands from reader. Input capture yields more accurate results compared to ETR. - + //disable counters temporarily LL_TIM_DisableCounter(FURI_HAL_RFID_RTF_CARRIER_IN_TIMER); LL_TIM_DisableCounter(FURI_HAL_RFID_RTF_CARRIER_IN_REFERENCE_TIMER); @@ -745,7 +750,9 @@ static void furi_hal_rfid_rtf_carrier_in_IC_mode() { //switch clocksource to system/64, external prescaling is handled in IC prescaler LL_TIM_SetClockSource(FURI_HAL_RFID_RTF_CARRIER_IN_TIMER, LL_TIM_CLOCKSOURCE_INTERNAL); - LL_TIM_SetPrescaler(FURI_HAL_RFID_RTF_CARRIER_IN_TIMER, 64 - 1); //prescaler is only applied at next update event + LL_TIM_SetPrescaler( + FURI_HAL_RFID_RTF_CARRIER_IN_TIMER, + 64 - 1); //prescaler is only applied at next update event LL_TIM_GenerateEvent_UPDATE( FURI_HAL_RFID_RTF_CARRIER_IN_TIMER); //prescaler is only applied at next update event //TODO: how to prevent this from creating a capture data entry LL_TIM_SetAutoReload(FURI_HAL_RFID_RTF_CARRIER_IN_TIMER, UINT32_MAX); @@ -760,7 +767,8 @@ static void furi_hal_rfid_rtf_carrier_in_IC_mode() { //enable Input capture & related interrupt LL_TIM_EnableIT_CC1(FURI_HAL_RFID_RTF_CARRIER_IN_TIMER); - LL_TIM_CC_EnableChannel(FURI_HAL_RFID_RTF_CARRIER_IN_TIMER, FURI_HAL_RFID_RTF_CARRIER_IN_TIMER_DIR_CH); + LL_TIM_CC_EnableChannel( + FURI_HAL_RFID_RTF_CARRIER_IN_TIMER, FURI_HAL_RFID_RTF_CARRIER_IN_TIMER_DIR_CH); //re-enable counters LL_TIM_EnableCounter(FURI_HAL_RFID_RTF_CARRIER_IN_TIMER); @@ -822,18 +830,35 @@ void furi_hal_rfid_rtf_carrier_in_start( //INPUT CAPTURE SETUP // Timer: channel 1 direct (from GPIO) LL_TIM_IC_SetActiveInput( - FURI_HAL_RFID_RTF_CARRIER_IN_TIMER, FURI_HAL_RFID_RTF_CARRIER_IN_TIMER_DIR_CH, LL_TIM_ACTIVEINPUT_DIRECTTI); + FURI_HAL_RFID_RTF_CARRIER_IN_TIMER, + FURI_HAL_RFID_RTF_CARRIER_IN_TIMER_DIR_CH, + LL_TIM_ACTIVEINPUT_DIRECTTI); //prescaling direct channel seems to be working fine (and is necessary since otherwise sd write cannot keep up) if(ext_prescaler == 4) { - LL_TIM_IC_SetPrescaler(FURI_HAL_RFID_RTF_CARRIER_IN_TIMER, FURI_HAL_RFID_RTF_CARRIER_IN_TIMER_DIR_CH, LL_TIM_ICPSC_DIV4); + LL_TIM_IC_SetPrescaler( + FURI_HAL_RFID_RTF_CARRIER_IN_TIMER, + FURI_HAL_RFID_RTF_CARRIER_IN_TIMER_DIR_CH, + LL_TIM_ICPSC_DIV4); } else if(ext_prescaler == 2) { - LL_TIM_IC_SetPrescaler(FURI_HAL_RFID_RTF_CARRIER_IN_TIMER, FURI_HAL_RFID_RTF_CARRIER_IN_TIMER_DIR_CH, LL_TIM_ICPSC_DIV2); + LL_TIM_IC_SetPrescaler( + FURI_HAL_RFID_RTF_CARRIER_IN_TIMER, + FURI_HAL_RFID_RTF_CARRIER_IN_TIMER_DIR_CH, + LL_TIM_ICPSC_DIV2); } else { - LL_TIM_IC_SetPrescaler(FURI_HAL_RFID_RTF_CARRIER_IN_TIMER, FURI_HAL_RFID_RTF_CARRIER_IN_TIMER_DIR_CH, LL_TIM_ICPSC_DIV1); + LL_TIM_IC_SetPrescaler( + FURI_HAL_RFID_RTF_CARRIER_IN_TIMER, + FURI_HAL_RFID_RTF_CARRIER_IN_TIMER_DIR_CH, + LL_TIM_ICPSC_DIV1); } - LL_TIM_IC_SetPolarity(FURI_HAL_RFID_RTF_CARRIER_IN_TIMER, FURI_HAL_RFID_RTF_CARRIER_IN_TIMER_DIR_CH, LL_TIM_IC_POLARITY_RISING); - LL_TIM_IC_SetFilter(FURI_HAL_RFID_RTF_CARRIER_IN_TIMER, FURI_HAL_RFID_RTF_CARRIER_IN_TIMER_DIR_CH, LL_TIM_IC_FILTER_FDIV1); - + LL_TIM_IC_SetPolarity( + FURI_HAL_RFID_RTF_CARRIER_IN_TIMER, + FURI_HAL_RFID_RTF_CARRIER_IN_TIMER_DIR_CH, + LL_TIM_IC_POLARITY_RISING); + LL_TIM_IC_SetFilter( + FURI_HAL_RFID_RTF_CARRIER_IN_TIMER, + FURI_HAL_RFID_RTF_CARRIER_IN_TIMER_DIR_CH, + LL_TIM_IC_FILTER_FDIV1); + //set interrupt callback for capturing period LL_TIM_EnableIT_CC1(FURI_HAL_RFID_RTF_CARRIER_IN_TIMER); //LL_TIM_EnableIT_CC2(FURI_HAL_RFID_RTF_CARRIER_IN_TIMER); @@ -848,7 +873,10 @@ void furi_hal_rfid_rtf_carrier_in_start( TIM_OC_InitStruct.OCState = LL_TIM_OCSTATE_ENABLE; TIM_OC_InitStruct.CompareValue = 0; //0% this should have almost similar effect as keeping output forced inactive (there's still some micropulse emited, going high at ARR and immediately down again at CCR value) - LL_TIM_OC_Init(FURI_HAL_RFID_RTF_PULL_OUT_TIMER, FURI_HAL_RFID_RTF_PULL_OUT_TIMER_CHANNEL, &TIM_OC_InitStruct); + LL_TIM_OC_Init( + FURI_HAL_RFID_RTF_PULL_OUT_TIMER, + FURI_HAL_RFID_RTF_PULL_OUT_TIMER_CHANNEL, + &TIM_OC_InitStruct); LL_TIM_OC_SetPolarity( FURI_HAL_RFID_RTF_PULL_OUT_TIMER, FURI_HAL_RFID_RTF_PULL_OUT_TIMER_CHANNEL, @@ -857,8 +885,10 @@ void furi_hal_rfid_rtf_carrier_in_start( //INIT DMA (do not start it yet) furi_hal_rfid_rtf_pull_out_dma_setup(duration, pulse, length); - LL_TIM_CC_EnableChannel(FURI_HAL_RFID_RTF_CARRIER_IN_TIMER, FURI_HAL_RFID_RTF_CARRIER_IN_TIMER_DIR_CH); - LL_TIM_CC_EnableChannel(FURI_HAL_RFID_RTF_PULL_OUT_TIMER, FURI_HAL_RFID_RTF_PULL_OUT_TIMER_CHANNEL); + LL_TIM_CC_EnableChannel( + FURI_HAL_RFID_RTF_CARRIER_IN_TIMER, FURI_HAL_RFID_RTF_CARRIER_IN_TIMER_DIR_CH); + LL_TIM_CC_EnableChannel( + FURI_HAL_RFID_RTF_PULL_OUT_TIMER, FURI_HAL_RFID_RTF_PULL_OUT_TIMER_CHANNEL); LL_TIM_SetCounter(FURI_HAL_RFID_RTF_CARRIER_IN_TIMER, 0); LL_TIM_SetCounter(FURI_HAL_RFID_RTF_CARRIER_IN_REFERENCE_TIMER, 0); LL_TIM_EnableCounter(FURI_HAL_RFID_RTF_CARRIER_IN_TIMER); @@ -883,10 +913,6 @@ void furi_hal_rfid_rtf_carrier_in_stop() { FURI_CRITICAL_EXIT(); } - - - - void furi_hal_rfid_comp_start() { LL_COMP_Enable(COMP1); // Magic From c4d4ba1caadf6bffc79f8697de59c29d50f0aada Mon Sep 17 00:00:00 2001 From: Aleksandr Kutuzov Date: Sun, 26 Nov 2023 16:44:13 +0900 Subject: [PATCH 12/14] Rfid: initialize default state in lfrfid_worker_mode_read_process --- lib/lfrfid/lfrfid_worker_modes.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/lfrfid/lfrfid_worker_modes.c b/lib/lfrfid/lfrfid_worker_modes.c index 6a879821d6b..b1b589052fe 100644 --- a/lib/lfrfid/lfrfid_worker_modes.c +++ b/lib/lfrfid/lfrfid_worker_modes.c @@ -389,7 +389,7 @@ static LFRFIDWorkerReadState lfrfid_worker_read_rtf( //reader talks first static void lfrfid_worker_mode_read_process(LFRFIDWorker* worker) { ProtocolId read_result = PROTOCOL_NO; - LFRFIDWorkerReadState state; + LFRFIDWorkerReadState state = LFRFIDWorkerReadOK; LFRFIDFeature feature; if(worker->read_type == LFRFIDWorkerReadTypePSKOnly) { From b0666b9a62dd346079642c92bc417eb4ffff7286 Mon Sep 17 00:00:00 2001 From: hedger Date: Thu, 29 Feb 2024 18:32:36 +0000 Subject: [PATCH 13/14] hal: rfid: signature fix --- targets/f7/furi_hal/furi_hal_rfid.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/targets/f7/furi_hal/furi_hal_rfid.c b/targets/f7/furi_hal/furi_hal_rfid.c index b3be9a058cd..523302da565 100644 --- a/targets/f7/furi_hal/furi_hal_rfid.c +++ b/targets/f7/furi_hal/furi_hal_rfid.c @@ -618,7 +618,8 @@ static void furi_hal_rfid_rtf_pull_out_dma_stop() { furi_hal_rfid_rtf_carrier_in_IC_mode(); } -static void furi_hal_rfid_rtf_pull_out_dma_isr() { +static void furi_hal_rfid_rtf_pull_out_dma_isr(void* context) { + UNUSED(context); // currently no HT interrupt enabled, only TC if(LL_DMA_IsActiveFlag_TC1(FURI_HAL_RFID_RTF_PULL_OUT_DMA)) { LL_DMA_ClearFlag_TC1(FURI_HAL_RFID_RTF_PULL_OUT_DMA); From 88f977ca36e91c8de55f7939679d44d272dafb07 Mon Sep 17 00:00:00 2001 From: hedger Date: Thu, 29 Feb 2024 18:50:26 +0000 Subject: [PATCH 14/14] linter fixes --- lib/lfrfid/lfrfid_hitag_worker.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/lfrfid/lfrfid_hitag_worker.c b/lib/lfrfid/lfrfid_hitag_worker.c index fcbe06e9570..81307e34af0 100644 --- a/lib/lfrfid/lfrfid_hitag_worker.c +++ b/lib/lfrfid/lfrfid_hitag_worker.c @@ -1758,7 +1758,8 @@ static int32_t lfrfid_hitag_worker_read_thread(void* thread_context) { crc, 1, 213, - (uint32_t)(1.05 * 33 * HITAG_BITPERIODS_MC)}; //always take 5% reading time buffer + (uint32_t)(1.05 * 33 * + HITAG_BITPERIODS_MC)}; //always take 5% reading time buffer uint8_t type[] = { HITAG_ON, HITAG_SELECT, @@ -1792,7 +1793,8 @@ static int32_t lfrfid_hitag_worker_read_thread(void* thread_context) { crc, 1, 213, - (uint32_t)(1.05 * 33 * HITAG_BITPERIODS_MC)}; //always take 5% reading time buffer + (uint32_t)(1.05 * 33 * + HITAG_BITPERIODS_MC)}; //always take 5% reading time buffer uint8_t type2[] = { HITAG_ON, HITAG_CMD, HITAG_BYTE, HITAG_CRC, HITAG_STOP, HITAG_ON, HITAG_ON}; uint16_t len2 = 7; @@ -1813,7 +1815,8 @@ static int32_t lfrfid_hitag_worker_read_thread(void* thread_context) { crc, 1, 213, - (uint32_t)(1.05 * 129 * HITAG_BITPERIODS_MC)}; //always take 5% reading time buffer + (uint32_t)(1.05 * 129 * + HITAG_BITPERIODS_MC)}; //always take 5% reading time buffer uint8_t type2[] = { HITAG_ON, HITAG_CMD, HITAG_BYTE, HITAG_CRC, HITAG_STOP, HITAG_ON, HITAG_ON}; uint16_t len2 = 7;