 feebf2cd77
			
		
	
	
		feebf2cd77
		
			
		
	
	
	
	
		
			
			Ability to write gen1b tags (ignore 0x43) Ability to write gen1 7 byte UID tags Fix detection of non magic cards Co-authored-by: あく <alleteam@gmail.com>
		
			
				
	
	
		
			481 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			481 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| #include "nfc_magic_worker_i.h"
 | |
| 
 | |
| #include "nfc_magic_i.h"
 | |
| #include "lib/magic/common.h"
 | |
| #include "lib/magic/classic_gen1.h"
 | |
| #include "lib/magic/gen4.h"
 | |
| 
 | |
| #define TAG "NfcMagicWorker"
 | |
| 
 | |
| static void
 | |
|     nfc_magic_worker_change_state(NfcMagicWorker* nfc_magic_worker, NfcMagicWorkerState state) {
 | |
|     furi_assert(nfc_magic_worker);
 | |
| 
 | |
|     nfc_magic_worker->state = state;
 | |
| }
 | |
| 
 | |
| NfcMagicWorker* nfc_magic_worker_alloc() {
 | |
|     NfcMagicWorker* nfc_magic_worker = malloc(sizeof(NfcMagicWorker));
 | |
| 
 | |
|     // Worker thread attributes
 | |
|     nfc_magic_worker->thread =
 | |
|         furi_thread_alloc_ex("NfcMagicWorker", 8192, nfc_magic_worker_task, nfc_magic_worker);
 | |
| 
 | |
|     nfc_magic_worker->callback = NULL;
 | |
|     nfc_magic_worker->context = NULL;
 | |
| 
 | |
|     nfc_magic_worker_change_state(nfc_magic_worker, NfcMagicWorkerStateReady);
 | |
| 
 | |
|     return nfc_magic_worker;
 | |
| }
 | |
| 
 | |
| void nfc_magic_worker_free(NfcMagicWorker* nfc_magic_worker) {
 | |
|     furi_assert(nfc_magic_worker);
 | |
| 
 | |
|     furi_thread_free(nfc_magic_worker->thread);
 | |
|     free(nfc_magic_worker);
 | |
| }
 | |
| 
 | |
| void nfc_magic_worker_stop(NfcMagicWorker* nfc_magic_worker) {
 | |
|     furi_assert(nfc_magic_worker);
 | |
| 
 | |
|     nfc_magic_worker_change_state(nfc_magic_worker, NfcMagicWorkerStateStop);
 | |
|     furi_thread_join(nfc_magic_worker->thread);
 | |
| }
 | |
| 
 | |
| void nfc_magic_worker_start(
 | |
|     NfcMagicWorker* nfc_magic_worker,
 | |
|     NfcMagicWorkerState state,
 | |
|     NfcMagicDevice* magic_dev,
 | |
|     NfcDeviceData* dev_data,
 | |
|     uint32_t new_password,
 | |
|     NfcMagicWorkerCallback callback,
 | |
|     void* context) {
 | |
|     furi_assert(nfc_magic_worker);
 | |
|     furi_assert(magic_dev);
 | |
|     furi_assert(dev_data);
 | |
| 
 | |
|     nfc_magic_worker->callback = callback;
 | |
|     nfc_magic_worker->context = context;
 | |
|     nfc_magic_worker->magic_dev = magic_dev;
 | |
|     nfc_magic_worker->dev_data = dev_data;
 | |
|     nfc_magic_worker->new_password = new_password;
 | |
|     nfc_magic_worker_change_state(nfc_magic_worker, state);
 | |
|     furi_thread_start(nfc_magic_worker->thread);
 | |
| }
 | |
| 
 | |
| int32_t nfc_magic_worker_task(void* context) {
 | |
|     NfcMagicWorker* nfc_magic_worker = context;
 | |
| 
 | |
|     if(nfc_magic_worker->state == NfcMagicWorkerStateCheck) {
 | |
|         nfc_magic_worker_check(nfc_magic_worker);
 | |
|     } else if(nfc_magic_worker->state == NfcMagicWorkerStateWrite) {
 | |
|         nfc_magic_worker_write(nfc_magic_worker);
 | |
|     } else if(nfc_magic_worker->state == NfcMagicWorkerStateRekey) {
 | |
|         nfc_magic_worker_rekey(nfc_magic_worker);
 | |
|     } else if(nfc_magic_worker->state == NfcMagicWorkerStateWipe) {
 | |
|         nfc_magic_worker_wipe(nfc_magic_worker);
 | |
|     }
 | |
| 
 | |
|     nfc_magic_worker_change_state(nfc_magic_worker, NfcMagicWorkerStateReady);
 | |
| 
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| void nfc_magic_worker_write(NfcMagicWorker* nfc_magic_worker) {
 | |
|     bool card_found_notified = false;
 | |
|     bool done = false;
 | |
|     FuriHalNfcDevData nfc_data = {};
 | |
|     NfcMagicDevice* magic_dev = nfc_magic_worker->magic_dev;
 | |
|     NfcDeviceData* dev_data = nfc_magic_worker->dev_data;
 | |
|     NfcProtocol dev_protocol = dev_data->protocol;
 | |
| 
 | |
|     while(nfc_magic_worker->state == NfcMagicWorkerStateWrite) {
 | |
|         do {
 | |
|             if(magic_dev->type == MagicTypeClassicGen1) {
 | |
|                 if(furi_hal_nfc_detect(&nfc_data, 200)) {
 | |
|                     magic_deactivate();
 | |
|                     magic_activate();
 | |
|                     if(!magic_gen1_wupa()) {
 | |
|                         FURI_LOG_E(TAG, "No card response to WUPA (not a magic card)");
 | |
|                         nfc_magic_worker->callback(
 | |
|                             NfcMagicWorkerEventWrongCard, nfc_magic_worker->context);
 | |
|                         done = true;
 | |
|                         break;
 | |
|                     }
 | |
|                     magic_deactivate();
 | |
|                 }
 | |
|                 magic_activate();
 | |
|                 if(magic_gen1_wupa()) {
 | |
|                     magic_gen1_data_access_cmd();
 | |
| 
 | |
|                     MfClassicData* mfc_data = &dev_data->mf_classic_data;
 | |
|                     for(size_t i = 0; i < 64; i++) {
 | |
|                         FURI_LOG_D(TAG, "Writing block %d", i);
 | |
|                         if(!magic_gen1_write_blk(i, &mfc_data->block[i])) {
 | |
|                             FURI_LOG_E(TAG, "Failed to write %d block", i);
 | |
|                             done = true;
 | |
|                             nfc_magic_worker->callback(
 | |
|                                 NfcMagicWorkerEventFail, nfc_magic_worker->context);
 | |
|                             break;
 | |
|                         }
 | |
|                     }
 | |
| 
 | |
|                     done = true;
 | |
|                     nfc_magic_worker->callback(
 | |
|                         NfcMagicWorkerEventSuccess, nfc_magic_worker->context);
 | |
|                     break;
 | |
|                 }
 | |
|             } else if(magic_dev->type == MagicTypeGen4) {
 | |
|                 if(furi_hal_nfc_detect(&nfc_data, 200)) {
 | |
|                     uint8_t gen4_config[28];
 | |
|                     uint32_t password = magic_dev->password;
 | |
| 
 | |
|                     uint32_t cuid;
 | |
|                     if(dev_protocol == NfcDeviceProtocolMifareClassic) {
 | |
|                         gen4_config[0] = 0x00;
 | |
|                         gen4_config[27] = 0x00;
 | |
|                     } else if(dev_protocol == NfcDeviceProtocolMifareUl) {
 | |
|                         MfUltralightData* mf_ul_data = &dev_data->mf_ul_data;
 | |
|                         gen4_config[0] = 0x01;
 | |
|                         switch(mf_ul_data->type) {
 | |
|                         case MfUltralightTypeUL11:
 | |
|                         case MfUltralightTypeUL21:
 | |
|                         // UL-C?
 | |
|                         // UL?
 | |
|                         default:
 | |
|                             gen4_config[27] = MagicGen4UltralightModeUL_EV1;
 | |
|                             break;
 | |
|                         case MfUltralightTypeNTAG203:
 | |
|                         case MfUltralightTypeNTAG213:
 | |
|                         case MfUltralightTypeNTAG215:
 | |
|                         case MfUltralightTypeNTAG216:
 | |
|                         case MfUltralightTypeNTAGI2C1K:
 | |
|                         case MfUltralightTypeNTAGI2C2K:
 | |
|                         case MfUltralightTypeNTAGI2CPlus1K:
 | |
|                         case MfUltralightTypeNTAGI2CPlus2K:
 | |
|                             gen4_config[27] = MagicGen4UltralightModeNTAG;
 | |
|                             break;
 | |
|                         }
 | |
|                     }
 | |
| 
 | |
|                     if(dev_data->nfc_data.uid_len == 4) {
 | |
|                         gen4_config[1] = MagicGen4UIDLengthSingle;
 | |
|                     } else if(dev_data->nfc_data.uid_len == 7) {
 | |
|                         gen4_config[1] = MagicGen4UIDLengthDouble;
 | |
|                     } else {
 | |
|                         FURI_LOG_E(TAG, "Unexpected UID length %d", dev_data->nfc_data.uid_len);
 | |
|                         nfc_magic_worker->callback(
 | |
|                             NfcMagicWorkerEventFail, nfc_magic_worker->context);
 | |
|                         done = true;
 | |
|                         break;
 | |
|                     }
 | |
| 
 | |
|                     gen4_config[2] = (uint8_t)(password >> 24);
 | |
|                     gen4_config[3] = (uint8_t)(password >> 16);
 | |
|                     gen4_config[4] = (uint8_t)(password >> 8);
 | |
|                     gen4_config[5] = (uint8_t)password;
 | |
| 
 | |
|                     if(dev_protocol == NfcDeviceProtocolMifareUl) {
 | |
|                         gen4_config[6] = MagicGen4ShadowModeHighSpeedIgnore;
 | |
|                     } else {
 | |
|                         gen4_config[6] = MagicGen4ShadowModeIgnore;
 | |
|                     }
 | |
|                     gen4_config[7] = 0x00;
 | |
|                     memset(gen4_config + 8, 0, 16);
 | |
|                     gen4_config[24] = dev_data->nfc_data.atqa[0];
 | |
|                     gen4_config[25] = dev_data->nfc_data.atqa[1];
 | |
|                     gen4_config[26] = dev_data->nfc_data.sak;
 | |
| 
 | |
|                     furi_hal_nfc_sleep();
 | |
|                     furi_hal_nfc_activate_nfca(200, &cuid);
 | |
|                     if(!magic_gen4_set_cfg(password, gen4_config, sizeof(gen4_config), false)) {
 | |
|                         nfc_magic_worker->callback(
 | |
|                             NfcMagicWorkerEventFail, nfc_magic_worker->context);
 | |
|                         done = true;
 | |
|                         break;
 | |
|                     }
 | |
|                     if(dev_protocol == NfcDeviceProtocolMifareClassic) {
 | |
|                         MfClassicData* mfc_data = &dev_data->mf_classic_data;
 | |
|                         size_t block_count = 64;
 | |
|                         if(mfc_data->type == MfClassicType4k) block_count = 256;
 | |
|                         for(size_t i = 0; i < block_count; i++) {
 | |
|                             FURI_LOG_D(TAG, "Writing block %d", i);
 | |
|                             if(!magic_gen4_write_blk(password, i, mfc_data->block[i].value)) {
 | |
|                                 FURI_LOG_E(TAG, "Failed to write %d block", i);
 | |
|                                 nfc_magic_worker->callback(
 | |
|                                     NfcMagicWorkerEventFail, nfc_magic_worker->context);
 | |
|                                 done = true;
 | |
|                                 break;
 | |
|                             }
 | |
|                         }
 | |
|                     } else if(dev_protocol == NfcDeviceProtocolMifareUl) {
 | |
|                         MfUltralightData* mf_ul_data = &dev_data->mf_ul_data;
 | |
|                         for(size_t i = 0; (i * 4) < mf_ul_data->data_read; i++) {
 | |
|                             size_t data_offset = i * 4;
 | |
|                             FURI_LOG_D(
 | |
|                                 TAG,
 | |
|                                 "Writing page %zu (%zu/%u)",
 | |
|                                 i,
 | |
|                                 data_offset,
 | |
|                                 mf_ul_data->data_read);
 | |
|                             uint8_t* block = mf_ul_data->data + data_offset;
 | |
|                             if(!magic_gen4_write_blk(password, i, block)) {
 | |
|                                 FURI_LOG_E(TAG, "Failed to write %zu page", i);
 | |
|                                 nfc_magic_worker->callback(
 | |
|                                     NfcMagicWorkerEventFail, nfc_magic_worker->context);
 | |
|                                 done = true;
 | |
|                                 break;
 | |
|                             }
 | |
|                         }
 | |
| 
 | |
|                         uint8_t buffer[16] = {0};
 | |
| 
 | |
|                         for(size_t i = 0; i < 8; i++) {
 | |
|                             memcpy(buffer, &mf_ul_data->signature[i * 4], 4); //-V1086
 | |
|                             if(!magic_gen4_write_blk(password, 0xF2 + i, buffer)) {
 | |
|                                 FURI_LOG_E(TAG, "Failed to write signature block %d", i);
 | |
|                                 nfc_magic_worker->callback(
 | |
|                                     NfcMagicWorkerEventFail, nfc_magic_worker->context);
 | |
|                                 done = true;
 | |
|                                 break;
 | |
|                             }
 | |
|                         }
 | |
| 
 | |
|                         buffer[0] = mf_ul_data->version.header;
 | |
|                         buffer[1] = mf_ul_data->version.vendor_id;
 | |
|                         buffer[2] = mf_ul_data->version.prod_type;
 | |
|                         buffer[3] = mf_ul_data->version.prod_subtype;
 | |
|                         if(!magic_gen4_write_blk(password, 0xFA, buffer)) {
 | |
|                             FURI_LOG_E(TAG, "Failed to write version block 0");
 | |
|                             nfc_magic_worker->callback(
 | |
|                                 NfcMagicWorkerEventFail, nfc_magic_worker->context);
 | |
|                             done = true;
 | |
|                             break;
 | |
|                         }
 | |
| 
 | |
|                         buffer[0] = mf_ul_data->version.prod_ver_major;
 | |
|                         buffer[1] = mf_ul_data->version.prod_ver_minor;
 | |
|                         buffer[2] = mf_ul_data->version.storage_size;
 | |
|                         buffer[3] = mf_ul_data->version.protocol_type;
 | |
|                         if(!magic_gen4_write_blk(password, 0xFB, buffer)) {
 | |
|                             FURI_LOG_E(TAG, "Failed to write version block 1");
 | |
|                             nfc_magic_worker->callback(
 | |
|                                 NfcMagicWorkerEventFail, nfc_magic_worker->context);
 | |
|                             done = true;
 | |
|                             break;
 | |
|                         }
 | |
|                     }
 | |
| 
 | |
|                     nfc_magic_worker->callback(
 | |
|                         NfcMagicWorkerEventSuccess, nfc_magic_worker->context);
 | |
|                     done = true;
 | |
|                     break;
 | |
|                 }
 | |
|             }
 | |
|         } while(false);
 | |
| 
 | |
|         if(done) break;
 | |
| 
 | |
|         if(card_found_notified) {
 | |
|             nfc_magic_worker->callback(
 | |
|                 NfcMagicWorkerEventNoCardDetected, nfc_magic_worker->context);
 | |
|             card_found_notified = false;
 | |
|         }
 | |
| 
 | |
|         furi_delay_ms(300);
 | |
|     }
 | |
|     magic_deactivate();
 | |
| }
 | |
| 
 | |
| void nfc_magic_worker_check(NfcMagicWorker* nfc_magic_worker) {
 | |
|     FuriHalNfcDevData nfc_data = {};
 | |
|     NfcMagicDevice* magic_dev = nfc_magic_worker->magic_dev;
 | |
|     bool card_found_notified = false;
 | |
|     uint8_t gen4_config[MAGIC_GEN4_CONFIG_LEN];
 | |
| 
 | |
|     while(nfc_magic_worker->state == NfcMagicWorkerStateCheck) {
 | |
|         magic_activate();
 | |
|         if(magic_gen1_wupa()) {
 | |
|             magic_dev->type = MagicTypeClassicGen1;
 | |
|             if(!card_found_notified) {
 | |
|                 nfc_magic_worker->callback(
 | |
|                     NfcMagicWorkerEventCardDetected, nfc_magic_worker->context);
 | |
|                 card_found_notified = true;
 | |
|             }
 | |
| 
 | |
|             if(furi_hal_nfc_detect(&nfc_data, 200)) {
 | |
|                 magic_dev->cuid = nfc_data.cuid;
 | |
|                 magic_dev->uid_len = nfc_data.uid_len;
 | |
|             } else {
 | |
|                 // wrong BCC
 | |
|                 magic_dev->uid_len = 4;
 | |
|             }
 | |
|             nfc_magic_worker->callback(NfcMagicWorkerEventSuccess, nfc_magic_worker->context);
 | |
|             break;
 | |
|         } else {
 | |
|             magic_deactivate();
 | |
|             magic_activate();
 | |
|             if(furi_hal_nfc_detect(&nfc_data, 200)) {
 | |
|                 magic_dev->cuid = nfc_data.cuid;
 | |
|                 magic_dev->uid_len = nfc_data.uid_len;
 | |
|                 if(magic_gen4_get_cfg(magic_dev->password, gen4_config)) {
 | |
|                     magic_dev->type = MagicTypeGen4;
 | |
|                     if(!card_found_notified) {
 | |
|                         nfc_magic_worker->callback(
 | |
|                             NfcMagicWorkerEventCardDetected, nfc_magic_worker->context);
 | |
|                         card_found_notified = true;
 | |
|                     }
 | |
| 
 | |
|                     nfc_magic_worker->callback(
 | |
|                         NfcMagicWorkerEventSuccess, nfc_magic_worker->context);
 | |
|                 } else {
 | |
|                     nfc_magic_worker->callback(
 | |
|                         NfcMagicWorkerEventWrongCard, nfc_magic_worker->context);
 | |
|                     card_found_notified = true;
 | |
|                 }
 | |
|                 break;
 | |
|             } else {
 | |
|                 if(card_found_notified) {
 | |
|                     nfc_magic_worker->callback(
 | |
|                         NfcMagicWorkerEventNoCardDetected, nfc_magic_worker->context);
 | |
|                     card_found_notified = false;
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         magic_deactivate();
 | |
|         furi_delay_ms(300);
 | |
|     }
 | |
| 
 | |
|     magic_deactivate();
 | |
| }
 | |
| 
 | |
| void nfc_magic_worker_rekey(NfcMagicWorker* nfc_magic_worker) {
 | |
|     NfcMagicDevice* magic_dev = nfc_magic_worker->magic_dev;
 | |
|     bool card_found_notified = false;
 | |
| 
 | |
|     if(magic_dev->type != MagicTypeGen4) {
 | |
|         nfc_magic_worker->callback(NfcMagicWorkerEventCardDetected, nfc_magic_worker->context);
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     while(nfc_magic_worker->state == NfcMagicWorkerStateRekey) {
 | |
|         magic_activate();
 | |
|         uint32_t cuid;
 | |
|         furi_hal_nfc_activate_nfca(200, &cuid);
 | |
|         if(cuid != magic_dev->cuid) {
 | |
|             if(card_found_notified) {
 | |
|                 nfc_magic_worker->callback(
 | |
|                     NfcMagicWorkerEventNoCardDetected, nfc_magic_worker->context);
 | |
|                 card_found_notified = false;
 | |
|             }
 | |
|             continue;
 | |
|         }
 | |
| 
 | |
|         nfc_magic_worker->callback(NfcMagicWorkerEventCardDetected, nfc_magic_worker->context);
 | |
|         card_found_notified = true;
 | |
| 
 | |
|         if(magic_gen4_set_pwd(magic_dev->password, nfc_magic_worker->new_password)) {
 | |
|             magic_dev->password = nfc_magic_worker->new_password;
 | |
|             nfc_magic_worker->callback(NfcMagicWorkerEventSuccess, nfc_magic_worker->context);
 | |
|             break;
 | |
|         }
 | |
| 
 | |
|         if(card_found_notified) { //-V547
 | |
|             nfc_magic_worker->callback(
 | |
|                 NfcMagicWorkerEventNoCardDetected, nfc_magic_worker->context);
 | |
|             card_found_notified = false;
 | |
|         }
 | |
|         furi_delay_ms(300);
 | |
|     }
 | |
|     magic_deactivate();
 | |
| }
 | |
| 
 | |
| void nfc_magic_worker_wipe(NfcMagicWorker* nfc_magic_worker) {
 | |
|     NfcMagicDevice* magic_dev = nfc_magic_worker->magic_dev;
 | |
|     bool card_found_notified = false;
 | |
|     bool card_wiped = false;
 | |
| 
 | |
|     MfClassicBlock block;
 | |
|     memset(&block, 0, sizeof(MfClassicBlock));
 | |
|     MfClassicBlock empty_block;
 | |
|     memset(&empty_block, 0, sizeof(MfClassicBlock));
 | |
|     MfClassicBlock trailer_block;
 | |
|     memset(&trailer_block, 0xff, sizeof(MfClassicBlock));
 | |
| 
 | |
|     block.value[0] = 0x01;
 | |
|     block.value[1] = 0x02;
 | |
|     block.value[2] = 0x03;
 | |
|     block.value[3] = 0x04;
 | |
|     block.value[4] = 0x04;
 | |
|     block.value[5] = 0x08;
 | |
|     block.value[6] = 0x04;
 | |
| 
 | |
|     trailer_block.value[7] = 0x07;
 | |
|     trailer_block.value[8] = 0x80;
 | |
|     trailer_block.value[9] = 0x69;
 | |
| 
 | |
|     while(nfc_magic_worker->state == NfcMagicWorkerStateWipe) {
 | |
|         do {
 | |
|             magic_deactivate();
 | |
|             furi_delay_ms(300);
 | |
|             if(!magic_activate()) break;
 | |
|             if(magic_dev->type == MagicTypeClassicGen1) {
 | |
|                 if(!magic_gen1_wupa()) break;
 | |
|                 if(!card_found_notified) {
 | |
|                     nfc_magic_worker->callback(
 | |
|                         NfcMagicWorkerEventCardDetected, nfc_magic_worker->context);
 | |
|                     card_found_notified = true;
 | |
|                 }
 | |
| 
 | |
|                 if(!magic_gen1_data_access_cmd()) break;
 | |
|                 if(!magic_gen1_write_blk(0, &block)) break;
 | |
| 
 | |
|                 for(size_t i = 1; i < 64; i++) {
 | |
|                     FURI_LOG_D(TAG, "Wiping block %d", i);
 | |
|                     bool success = false;
 | |
|                     if((i | 0x03) == i) {
 | |
|                         success = magic_gen1_write_blk(i, &trailer_block);
 | |
|                     } else {
 | |
|                         success = magic_gen1_write_blk(i, &empty_block);
 | |
|                     }
 | |
| 
 | |
|                     if(!success) {
 | |
|                         FURI_LOG_E(TAG, "Failed to write %d block", i);
 | |
|                         nfc_magic_worker->callback(
 | |
|                             NfcMagicWorkerEventFail, nfc_magic_worker->context);
 | |
|                         break;
 | |
|                     }
 | |
|                 }
 | |
| 
 | |
|                 card_wiped = true;
 | |
|                 nfc_magic_worker->callback(NfcMagicWorkerEventSuccess, nfc_magic_worker->context);
 | |
|             } else if(magic_dev->type == MagicTypeGen4) {
 | |
|                 uint32_t cuid;
 | |
|                 if(!furi_hal_nfc_activate_nfca(200, &cuid)) break;
 | |
|                 if(cuid != magic_dev->cuid) break;
 | |
|                 if(!card_found_notified) {
 | |
|                     nfc_magic_worker->callback(
 | |
|                         NfcMagicWorkerEventCardDetected, nfc_magic_worker->context);
 | |
|                     card_found_notified = true;
 | |
|                 }
 | |
| 
 | |
|                 if(!magic_gen4_wipe(magic_dev->password)) break;
 | |
| 
 | |
|                 card_wiped = true;
 | |
|                 nfc_magic_worker->callback(NfcMagicWorkerEventSuccess, nfc_magic_worker->context);
 | |
|             }
 | |
|         } while(false);
 | |
| 
 | |
|         if(card_wiped) break;
 | |
| 
 | |
|         if(card_found_notified) {
 | |
|             nfc_magic_worker->callback(
 | |
|                 NfcMagicWorkerEventNoCardDetected, nfc_magic_worker->context);
 | |
|             card_found_notified = false;
 | |
|         }
 | |
|     }
 | |
|     magic_deactivate();
 | |
| }
 |