 c00776ca22
			
		
	
	
		c00776ca22
		
			
		
	
	
	
	
		
			
			* drivers: expose st25r3916 driver API * nfc poller: add start with custom callback * mf classic: rework sync API with poller custom start * mf ultralight: rework sync API with poller custom start * iso14443_3a poller: remove unused col res state * nfc: rework nfc poller custom start * mf ultralight: rename sync API * mf classic: rename sync API * iso14443-3a: rename sync API * nfc: remove async prefix in internal functions * nfc: expose internal API * nfc: fix sync api include and docs * targets: fix f18 build * nfc: rework NfcGenericEventEx type * nfc poller: add documentation * iso14443-3a poller: add documentation * felica poller: add documentation * iso14443_3b poller: add documentation * so14443_4a poller: add documentation * iso14443_4b poller: add documentation * iso15693 poller: add documentation * slix poller: add documentation * mf desfire poller: add documentation * mf ultralight poller: fix API and add documentation * mf classic poller: add documentation Co-authored-by: あく <alleteam@gmail.com>
		
			
				
	
	
		
			297 lines
		
	
	
		
			8.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			297 lines
		
	
	
		
			8.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| #include "st25tb_poller_i.h"
 | |
| 
 | |
| #include "bit_buffer.h"
 | |
| #include "core/core_defines.h"
 | |
| #include "protocols/st25tb/st25tb.h"
 | |
| #include <nfc/helpers/iso14443_crc.h>
 | |
| 
 | |
| #define TAG "ST25TBPoller"
 | |
| 
 | |
| static St25tbError st25tb_poller_process_error(NfcError error) {
 | |
|     switch(error) {
 | |
|     case NfcErrorNone:
 | |
|         return St25tbErrorNone;
 | |
|     case NfcErrorTimeout:
 | |
|         return St25tbErrorTimeout;
 | |
|     default:
 | |
|         return St25tbErrorNotPresent;
 | |
|     }
 | |
| }
 | |
| 
 | |
| static St25tbError st25tb_poller_prepare_trx(St25tbPoller* instance) {
 | |
|     furi_assert(instance);
 | |
| 
 | |
|     if(instance->state == St25tbPollerStateIdle) {
 | |
|         return st25tb_poller_activate(instance, NULL);
 | |
|     }
 | |
| 
 | |
|     return St25tbErrorNone;
 | |
| }
 | |
| 
 | |
| static St25tbError st25tb_poller_frame_exchange(
 | |
|     St25tbPoller* instance,
 | |
|     const BitBuffer* tx_buffer,
 | |
|     BitBuffer* rx_buffer,
 | |
|     uint32_t fwt) {
 | |
|     furi_assert(instance);
 | |
| 
 | |
|     const size_t tx_bytes = bit_buffer_get_size_bytes(tx_buffer);
 | |
|     furi_assert(
 | |
|         tx_bytes <= bit_buffer_get_capacity_bytes(instance->tx_buffer) - ISO14443_CRC_SIZE);
 | |
| 
 | |
|     bit_buffer_copy(instance->tx_buffer, tx_buffer);
 | |
|     iso14443_crc_append(Iso14443CrcTypeB, instance->tx_buffer);
 | |
| 
 | |
|     St25tbError ret = St25tbErrorNone;
 | |
| 
 | |
|     do {
 | |
|         NfcError error =
 | |
|             nfc_poller_trx(instance->nfc, instance->tx_buffer, instance->rx_buffer, fwt);
 | |
|         if(error != NfcErrorNone) {
 | |
|             FURI_LOG_D(TAG, "error during trx: %d", error);
 | |
|             ret = st25tb_poller_process_error(error);
 | |
|             break;
 | |
|         }
 | |
| 
 | |
|         bit_buffer_copy(rx_buffer, instance->rx_buffer);
 | |
|         if(!iso14443_crc_check(Iso14443CrcTypeB, instance->rx_buffer)) {
 | |
|             ret = St25tbErrorWrongCrc;
 | |
|             break;
 | |
|         }
 | |
| 
 | |
|         iso14443_crc_trim(rx_buffer);
 | |
|     } while(false);
 | |
| 
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| St25tbType st25tb_get_type_from_uid(const uint8_t uid[ST25TB_UID_SIZE]) {
 | |
|     switch(uid[2] >> 2) {
 | |
|     case 0x0:
 | |
|     case 0x3:
 | |
|         return St25tbTypeX4k;
 | |
|     case 0x4:
 | |
|         return St25tbTypeX512;
 | |
|     case 0x6:
 | |
|         return St25tbType512Ac;
 | |
|     case 0x7:
 | |
|         return St25tbType04k;
 | |
|     case 0xc:
 | |
|         return St25tbType512At;
 | |
|     case 0xf:
 | |
|         return St25tbType02k;
 | |
|     default:
 | |
|         furi_crash("unsupported st25tb type");
 | |
|     }
 | |
| }
 | |
| 
 | |
| St25tbError st25tb_poller_initiate(St25tbPoller* instance, uint8_t* chip_id) {
 | |
|     // Send Initiate()
 | |
|     furi_assert(instance);
 | |
|     furi_assert(instance->nfc);
 | |
| 
 | |
|     instance->state = St25tbPollerStateInitiateInProgress;
 | |
|     bit_buffer_reset(instance->tx_buffer);
 | |
|     bit_buffer_reset(instance->rx_buffer);
 | |
|     bit_buffer_append_byte(instance->tx_buffer, 0x06);
 | |
|     bit_buffer_append_byte(instance->tx_buffer, 0x00);
 | |
| 
 | |
|     St25tbError ret;
 | |
|     do {
 | |
|         ret = st25tb_poller_frame_exchange(
 | |
|             instance, instance->tx_buffer, instance->rx_buffer, ST25TB_FDT_FC);
 | |
|         if(ret != St25tbErrorNone) {
 | |
|             break;
 | |
|         }
 | |
| 
 | |
|         if(bit_buffer_get_size_bytes(instance->rx_buffer) != 1) {
 | |
|             FURI_LOG_D(TAG, "Unexpected Initiate response size");
 | |
|             ret = St25tbErrorCommunication;
 | |
|             break;
 | |
|         }
 | |
|         if(chip_id) {
 | |
|             *chip_id = bit_buffer_get_byte(instance->rx_buffer, 0);
 | |
|         }
 | |
|     } while(false);
 | |
| 
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| St25tbError st25tb_poller_activate(St25tbPoller* instance, St25tbData* data) {
 | |
|     furi_assert(instance);
 | |
|     furi_assert(instance->nfc);
 | |
| 
 | |
|     st25tb_reset(data);
 | |
| 
 | |
|     St25tbError ret;
 | |
| 
 | |
|     do {
 | |
|         ret = st25tb_poller_initiate(instance, &data->chip_id);
 | |
|         if(ret != St25tbErrorNone) {
 | |
|             break;
 | |
|         }
 | |
| 
 | |
|         instance->state = St25tbPollerStateActivationInProgress;
 | |
| 
 | |
|         bit_buffer_reset(instance->tx_buffer);
 | |
|         bit_buffer_reset(instance->rx_buffer);
 | |
| 
 | |
|         // Send Select(Chip_ID), let's just assume that collisions won't ever happen :D
 | |
|         bit_buffer_append_byte(instance->tx_buffer, 0x0E);
 | |
|         bit_buffer_append_byte(instance->tx_buffer, data->chip_id);
 | |
| 
 | |
|         ret = st25tb_poller_frame_exchange(
 | |
|             instance, instance->tx_buffer, instance->rx_buffer, ST25TB_FDT_FC);
 | |
|         if(ret != St25tbErrorNone) {
 | |
|             instance->state = St25tbPollerStateActivationFailed;
 | |
|             break;
 | |
|         }
 | |
| 
 | |
|         if(bit_buffer_get_size_bytes(instance->rx_buffer) != 1) {
 | |
|             FURI_LOG_D(TAG, "Unexpected Select response size");
 | |
|             instance->state = St25tbPollerStateActivationFailed;
 | |
|             ret = St25tbErrorCommunication;
 | |
|             break;
 | |
|         }
 | |
| 
 | |
|         if(bit_buffer_get_byte(instance->rx_buffer, 0) != data->chip_id) {
 | |
|             FURI_LOG_D(TAG, "ChipID mismatch");
 | |
|             instance->state = St25tbPollerStateActivationFailed;
 | |
|             ret = St25tbErrorColResFailed;
 | |
|             break;
 | |
|         }
 | |
|         instance->state = St25tbPollerStateActivated;
 | |
| 
 | |
|         ret = st25tb_poller_get_uid(instance, data->uid);
 | |
|         if(ret != St25tbErrorNone) {
 | |
|             instance->state = St25tbPollerStateActivationFailed;
 | |
|             break;
 | |
|         }
 | |
|         data->type = st25tb_get_type_from_uid(data->uid);
 | |
| 
 | |
|         bool read_blocks = true;
 | |
|         for(uint8_t i = 0; i < st25tb_get_block_count(data->type); i++) {
 | |
|             ret = st25tb_poller_read_block(instance, &data->blocks[i], i);
 | |
|             if(ret != St25tbErrorNone) {
 | |
|                 read_blocks = false;
 | |
|                 break;
 | |
|             }
 | |
|         }
 | |
|         if(!read_blocks) {
 | |
|             break;
 | |
|         }
 | |
|         ret = st25tb_poller_read_block(instance, &data->system_otp_block, ST25TB_SYSTEM_OTP_BLOCK);
 | |
|     } while(false);
 | |
| 
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| St25tbError st25tb_poller_get_uid(St25tbPoller* instance, uint8_t* uid) {
 | |
|     furi_assert(instance);
 | |
|     furi_assert(instance->nfc);
 | |
| 
 | |
|     St25tbError ret;
 | |
| 
 | |
|     do {
 | |
|         bit_buffer_reset(instance->tx_buffer);
 | |
|         bit_buffer_reset(instance->rx_buffer);
 | |
| 
 | |
|         bit_buffer_append_byte(instance->tx_buffer, 0x0B);
 | |
| 
 | |
|         ret = st25tb_poller_frame_exchange(
 | |
|             instance, instance->tx_buffer, instance->rx_buffer, ST25TB_FDT_FC);
 | |
|         if(ret != St25tbErrorNone) {
 | |
|             break;
 | |
|         }
 | |
| 
 | |
|         if(bit_buffer_get_size_bytes(instance->rx_buffer) != ST25TB_UID_SIZE) {
 | |
|             FURI_LOG_D(TAG, "Unexpected Get_UID() response size");
 | |
|             instance->state = St25tbPollerStateActivationFailed;
 | |
|             ret = St25tbErrorCommunication;
 | |
|             break;
 | |
|         }
 | |
|         bit_buffer_write_bytes(instance->rx_buffer, uid, ST25TB_UID_SIZE);
 | |
|         FURI_SWAP(uid[0], uid[7]);
 | |
|         FURI_SWAP(uid[1], uid[6]);
 | |
|         FURI_SWAP(uid[2], uid[5]);
 | |
|         FURI_SWAP(uid[3], uid[4]);
 | |
|     } while(false);
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| St25tbError
 | |
|     st25tb_poller_read_block(St25tbPoller* instance, uint32_t* block, uint8_t block_number) {
 | |
|     furi_assert(instance);
 | |
|     furi_assert(instance->nfc);
 | |
|     furi_assert(block);
 | |
|     furi_assert(
 | |
|         (block_number <= st25tb_get_block_count(instance->data->type)) ||
 | |
|         block_number == ST25TB_SYSTEM_OTP_BLOCK);
 | |
|     FURI_LOG_D(TAG, "reading block %d", block_number);
 | |
|     bit_buffer_reset(instance->tx_buffer);
 | |
|     bit_buffer_reset(instance->rx_buffer);
 | |
| 
 | |
|     // Send Read_block(Addr)
 | |
|     bit_buffer_append_byte(instance->tx_buffer, 0x08);
 | |
|     bit_buffer_append_byte(instance->tx_buffer, block_number);
 | |
|     St25tbError ret;
 | |
|     do {
 | |
|         ret = st25tb_poller_frame_exchange(
 | |
|             instance, instance->tx_buffer, instance->rx_buffer, ST25TB_FDT_FC);
 | |
|         if(ret != St25tbErrorNone) {
 | |
|             break;
 | |
|         }
 | |
| 
 | |
|         if(bit_buffer_get_size_bytes(instance->rx_buffer) != ST25TB_BLOCK_SIZE) {
 | |
|             FURI_LOG_D(TAG, "Unexpected Read_block(Addr) response size");
 | |
|             ret = St25tbErrorCommunication;
 | |
|             break;
 | |
|         }
 | |
|         bit_buffer_write_bytes(instance->rx_buffer, block, ST25TB_BLOCK_SIZE);
 | |
|         FURI_LOG_D(TAG, "read result: %08lX", *block);
 | |
|     } while(false);
 | |
| 
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| St25tbError st25tb_poller_halt(St25tbPoller* instance) {
 | |
|     furi_assert(instance);
 | |
| 
 | |
|     bit_buffer_reset(instance->tx_buffer);
 | |
|     bit_buffer_reset(instance->rx_buffer);
 | |
| 
 | |
|     // Send Completion()
 | |
|     bit_buffer_append_byte(instance->tx_buffer, 0x0F);
 | |
| 
 | |
|     St25tbError ret;
 | |
| 
 | |
|     do {
 | |
|         ret = st25tb_poller_frame_exchange(
 | |
|             instance, instance->tx_buffer, instance->rx_buffer, ST25TB_FDT_FC);
 | |
|         if(ret != St25tbErrorTimeout) {
 | |
|             break;
 | |
|         }
 | |
| 
 | |
|         instance->state = St25tbPollerStateIdle;
 | |
|     } while(false);
 | |
| 
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| St25tbError st25tb_poller_send_frame(
 | |
|     St25tbPoller* instance,
 | |
|     const BitBuffer* tx_buffer,
 | |
|     BitBuffer* rx_buffer,
 | |
|     uint32_t fwt) {
 | |
|     St25tbError ret;
 | |
| 
 | |
|     do {
 | |
|         ret = st25tb_poller_prepare_trx(instance);
 | |
|         if(ret != St25tbErrorNone) break;
 | |
| 
 | |
|         ret = st25tb_poller_frame_exchange(instance, tx_buffer, rx_buffer, fwt);
 | |
|     } while(false);
 | |
| 
 | |
|     return ret;
 | |
| }
 |