diff --git a/applications/nfc/nfc_worker.c b/applications/nfc/nfc_worker.c index 176b15c6..7d78fa74 100644 --- a/applications/nfc/nfc_worker.c +++ b/applications/nfc/nfc_worker.c @@ -1,6 +1,8 @@ #include "nfc_worker_i.h" #include +#include + #define TAG "NfcWorker" /***************************** NFC Worker API *******************************/ @@ -495,9 +497,11 @@ void nfc_worker_emulate_mifare_classic(NfcWorker* nfc_worker) { NfcaSignal* nfca_signal = nfca_signal_alloc(); tx_rx.nfca_signal = nfca_signal; + rfal_platform_spi_acquire(); + + furi_hal_nfc_listen_start(nfc_data); while(nfc_worker->state == NfcWorkerStateEmulateMifareClassic) { - if(furi_hal_nfc_listen( - nfc_data->uid, nfc_data->uid_len, nfc_data->atqa, nfc_data->sak, true, 300)) { + if(furi_hal_nfc_listen_rx(&tx_rx, 300)) { mf_classic_emulator(&emulator, &tx_rx); } } @@ -510,6 +514,8 @@ void nfc_worker_emulate_mifare_classic(NfcWorker* nfc_worker) { } nfca_signal_free(nfca_signal); + + rfal_platform_spi_release(); } void nfc_worker_read_mifare_desfire(NfcWorker* nfc_worker) { diff --git a/applications/unit_tests/nfc/nfc_test.c b/applications/unit_tests/nfc/nfc_test.c new file mode 100644 index 00000000..a2262735 --- /dev/null +++ b/applications/unit_tests/nfc/nfc_test.c @@ -0,0 +1,184 @@ +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "../minunit.h" + +#define TAG "NfcTest" + +#define NFC_TEST_RESOURCES_DIR "/ext/unit_tests/nfc/" +#define NFC_TEST_SIGNAL_SHORT_FILE "nfc_nfca_signal_short.nfc" +#define NFC_TEST_SIGNAL_LONG_FILE "nfc_nfca_signal_long.nfc" + +static const char* nfc_test_file_type = "Flipper NFC test"; +static const uint32_t nfc_test_file_version = 1; + +#define NFC_TEST_DATA_MAX_LEN 18 +#define NFC_TETS_TIMINGS_MAX_LEN 1350 + +typedef struct { + Storage* storage; + NfcaSignal* signal; + uint32_t test_data_len; + uint8_t test_data[NFC_TEST_DATA_MAX_LEN]; + uint32_t test_timings_len; + uint32_t test_timings[NFC_TETS_TIMINGS_MAX_LEN]; +} NfcTest; + +static NfcTest* nfc_test = NULL; + +static void nfc_test_alloc() { + nfc_test = malloc(sizeof(NfcTest)); + nfc_test->signal = nfca_signal_alloc(); + nfc_test->storage = furi_record_open("storage"); +} + +static void nfc_test_free() { + furi_assert(nfc_test); + + furi_record_close("storage"); + nfca_signal_free(nfc_test->signal); + free(nfc_test); + nfc_test = NULL; +} + +static bool nfc_test_read_signal_from_file(const char* file_name) { + bool success = false; + + FlipperFormat* file = flipper_format_file_alloc(nfc_test->storage); + string_t file_type; + string_init(file_type); + uint32_t file_version = 0; + + do { + if(!flipper_format_file_open_existing(file, file_name)) break; + if(!flipper_format_read_header(file, file_type, &file_version)) break; + if(string_cmp_str(file_type, nfc_test_file_type) || file_version != nfc_test_file_version) + break; + if(!flipper_format_read_uint32(file, "Data length", &nfc_test->test_data_len, 1)) break; + if(nfc_test->test_data_len > NFC_TEST_DATA_MAX_LEN) break; + if(!flipper_format_read_hex( + file, "Plain data", nfc_test->test_data, nfc_test->test_data_len)) + break; + if(!flipper_format_read_uint32(file, "Timings length", &nfc_test->test_timings_len, 1)) + break; + if(nfc_test->test_timings_len > NFC_TETS_TIMINGS_MAX_LEN) break; + if(!flipper_format_read_uint32( + file, "Timings", nfc_test->test_timings, nfc_test->test_timings_len)) + break; + success = true; + } while(false); + + string_clear(file_type); + flipper_format_free(file); + + return success; +} + +static bool nfc_test_digital_signal_test_encode( + const char* file_name, + uint32_t encode_max_time, + uint32_t timing_tolerance, + uint32_t timings_sum_tolerance) { + furi_assert(nfc_test); + + bool success = false; + uint32_t time = 0; + uint32_t dut_timings_sum = 0; + uint32_t ref_timings_sum = 0; + uint8_t parity[10] = {}; + + do { + // Read test data + if(!nfc_test_read_signal_from_file(file_name)) break; + + // Encode signal + FURI_CRITICAL_ENTER(); + time = DWT->CYCCNT; + nfca_signal_encode( + nfc_test->signal, nfc_test->test_data, nfc_test->test_data_len * 8, parity); + digital_signal_prepare_arr(nfc_test->signal->tx_signal); + time = (DWT->CYCCNT - time) / furi_hal_delay_instructions_per_microsecond(); + FURI_CRITICAL_EXIT(); + + // Check timings + if(time > encode_max_time) { + FURI_LOG_E( + TAG, "Encoding time: %d us while accepted value: %d us", time, encode_max_time); + break; + } + + // Check data + if(nfc_test->signal->tx_signal->edge_cnt != nfc_test->test_timings_len) { + FURI_LOG_E(TAG, "Not equal timings buffers length"); + break; + } + + uint32_t timings_diff = 0; + uint32_t* ref = nfc_test->test_timings; + uint32_t* dut = nfc_test->signal->tx_signal->reload_reg_buff; + bool timing_check_success = true; + for(size_t i = 0; i < nfc_test->test_timings_len; i++) { + timings_diff = dut[i] > ref[i] ? dut[i] - ref[i] : ref[i] - dut[i]; + dut_timings_sum += dut[i]; + ref_timings_sum += ref[i]; + if(timings_diff > timing_tolerance) { + FURI_LOG_E( + TAG, "Too big differece in %d timings. Ref: %d, DUT: %d", i, ref[i], dut[i]); + timing_check_success = false; + break; + } + } + if(!timing_check_success) break; + uint32_t sum_diff = dut_timings_sum > ref_timings_sum ? dut_timings_sum - ref_timings_sum : + ref_timings_sum - dut_timings_sum; + if(sum_diff > timings_sum_tolerance) { + FURI_LOG_E( + TAG, + "Too big difference in timings sum. Ref: %d, DUT: %d", + ref_timings_sum, + dut_timings_sum); + break; + } + + FURI_LOG_I(TAG, "Encoding time: %d us. Acceptable time: %d us", time, encode_max_time); + FURI_LOG_I( + TAG, + "Timings sum difference: %d [1/64MHZ]. Acceptable difference: %d [1/64MHz]", + sum_diff, + timings_sum_tolerance); + success = true; + } while(false); + + return success; +} + +MU_TEST(nfc_digital_signal_test) { + mu_assert( + nfc_test_digital_signal_test_encode( + NFC_TEST_RESOURCES_DIR NFC_TEST_SIGNAL_SHORT_FILE, 500, 1, 37), + "NFC short digital signal test failed\r\n"); + mu_assert( + nfc_test_digital_signal_test_encode( + NFC_TEST_RESOURCES_DIR NFC_TEST_SIGNAL_LONG_FILE, 2000, 1, 37), + "NFC long digital signal test failed\r\n"); +} + +MU_TEST_SUITE(nfc) { + nfc_test_alloc(); + + MU_RUN_TEST(nfc_digital_signal_test); + + nfc_test_free(); +} + +int run_minunit_test_nfc() { + MU_RUN_SUITE(nfc); + return MU_EXIT_CODE; +} diff --git a/applications/unit_tests/test_index.c b/applications/unit_tests/test_index.c index d8a8174c..8ff27b4d 100644 --- a/applications/unit_tests/test_index.c +++ b/applications/unit_tests/test_index.c @@ -19,6 +19,7 @@ int run_minunit_test_stream(); int run_minunit_test_storage(); int run_minunit_test_subghz(); int run_minunit_test_dirwalk(); +int run_minunit_test_nfc(); void minunit_print_progress(void) { static char progress[] = {'\\', '|', '/', '-'}; @@ -67,6 +68,7 @@ void unit_tests_cli(Cli* cli, string_t args, void* context) { test_result |= run_minunit_test_infrared_decoder_encoder(); test_result |= run_minunit_test_rpc(); test_result |= run_minunit_test_subghz(); + test_result |= run_minunit_test_nfc(); cycle_counter = (furi_hal_get_tick() - cycle_counter); diff --git a/assets/unit_tests/nfc/nfc_nfca_signal_long.nfc b/assets/unit_tests/nfc/nfc_nfca_signal_long.nfc new file mode 100644 index 00000000..fae69cb5 --- /dev/null +++ b/assets/unit_tests/nfc/nfc_nfca_signal_long.nfc @@ -0,0 +1,6 @@ +Filetype: Flipper NFC test +Version: 1 +Data length: 18 +Plain data: f1 99 41 43 a1 2f 23 01 de f3 c5 8d 91 4b 1e 50 4a c9 +Timings length: 1304 +Timings: 37 37 36 37 37 37 36 339 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 640 37 37 37 37 36 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 641 37 37 36 37 37 37 36 339 37 37 36 37 37 37 36 37 37 37 36 37 37 37 37 640 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 339 37 37 36 37 37 37 36 37 37 37 37 36 37 37 37 640 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 641 37 36 37 37 37 37 36 339 37 37 36 37 37 37 36 339 37 37 36 37 37 37 37 338 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 339 36 37 37 37 37 36 37 37 37 36 37 37 37 36 37 641 37 37 36 37 37 37 36 339 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 37 36 37 37 37 37 36 37 641 37 36 37 37 37 36 37 37 37 36 37 37 37 37 36 339 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 641 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 640 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 339 36 37 37 37 37 36 37 37 37 36 37 37 37 36 37 641 37 36 37 37 37 37 36 339 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 339 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 339 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 36 37 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 641 37 37 36 37 37 37 36 37 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 641 37 36 37 37 37 37 36 339 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 641 37 36 37 37 37 36 37 37 37 36 37 37 37 36 37 641 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 36 37 37 37 37 36 37 641 37 36 37 37 37 36 37 37 37 36 37 37 37 37 36 641 37 37 36 37 37 37 36 37 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 339 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 37 37 36 37 37 37 36 37 641 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 338 37 37 37 37 36 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 641 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 641 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 641 37 37 36 37 37 37 37 338 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 339 36 37 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 36 37 37 37 37 36 339 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 37 37 36 37 37 37 36 37 641 37 37 36 37 37 37 36 37 37 37 36 37 37 37 37 640 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 641 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 37 37 37 36 37 37 37 36 641 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 339 36 37 37 37 36 37 37 37 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 641 37 37 36 37 37 37 36 0 diff --git a/assets/unit_tests/nfc/nfc_nfca_signal_short.nfc b/assets/unit_tests/nfc/nfc_nfca_signal_short.nfc new file mode 100644 index 00000000..3b7e2d9e --- /dev/null +++ b/assets/unit_tests/nfc/nfc_nfca_signal_short.nfc @@ -0,0 +1,6 @@ +Filetype: Flipper NFC test +Version: 1 +Data length: 4 +Plain data: 14 d8 a0 c9 +Timings length: 296 +Timings: 37 37 36 37 37 37 36 641 37 37 36 37 37 37 37 338 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 641 37 36 37 37 37 36 37 339 37 36 37 37 37 37 36 339 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 338 37 37 37 37 36 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 641 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 339 37 37 36 37 37 37 37 640 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 339 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 641 37 36 37 37 37 36 37 339 37 36 37 37 37 37 36 37 37 37 36 37 37 37 36 641 37 37 36 37 37 37 37 338 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 641 36 37 37 37 37 36 37 0 diff --git a/firmware/targets/f7/furi_hal/furi_hal_nfc.c b/firmware/targets/f7/furi_hal/furi_hal_nfc.c index cba2a703..9723dea1 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_nfc.c +++ b/firmware/targets/f7/furi_hal/furi_hal_nfc.c @@ -8,6 +8,9 @@ #include #include +#include +#include +#include #define TAG "FuriHalNfc" @@ -19,6 +22,8 @@ osEventFlagsId_t event = NULL; #define EVENT_FLAG_STOP (1UL << 2) #define EVENT_FLAG_ALL (EVENT_FLAG_INTERRUPT | EVENT_FLAG_STATE_CHANGED | EVENT_FLAG_STOP) +#define FURI_HAL_NFC_UID_INCOMPLETE (0x04) + void furi_hal_nfc_init() { ReturnCode ret = rfalNfcInitialize(); if(ret == ERR_NONE) { @@ -243,6 +248,108 @@ bool furi_hal_nfc_listen( return true; } +static void furi_hal_nfc_read_fifo(uint8_t* data, uint16_t* bits) { + uint8_t fifo_status[2]; + uint8_t rx_buff[64]; + + st25r3916ReadMultipleRegisters( + ST25R3916_REG_FIFO_STATUS1, fifo_status, ST25R3916_FIFO_STATUS_LEN); + uint16_t rx_bytes = + ((((uint16_t)fifo_status[1] & ST25R3916_REG_FIFO_STATUS2_fifo_b_mask) >> + ST25R3916_REG_FIFO_STATUS2_fifo_b_shift) + << 8); + rx_bytes |= (((uint16_t)fifo_status[0]) & 0x00FFU); + st25r3916ReadFifo(rx_buff, rx_bytes); + + memcpy(data, rx_buff, rx_bytes); + *bits = rx_bytes * 8; +} + +void furi_hal_nfc_listen_sleep() { + st25r3916ExecuteCommand(ST25R3916_CMD_GOTO_SLEEP); +} + +bool furi_hal_nfc_listen_rx(FuriHalNfcTxRxContext* tx_rx, uint32_t timeout_ms) { + furi_assert(tx_rx); + + // Wait for interrupts + uint32_t start = osKernelGetTickCount(); + bool data_received = false; + while(true) { + if(furi_hal_gpio_read(&gpio_nfc_irq_rfid_pull) == true) { + st25r3916CheckForReceivedInterrupts(); + if(st25r3916GetInterrupt(ST25R3916_IRQ_MASK_RXE)) { + furi_hal_nfc_read_fifo(tx_rx->rx_data, &tx_rx->rx_bits); + data_received = true; + break; + } + continue; + } + if(osKernelGetTickCount() - start > timeout_ms) { + FURI_LOG_D(TAG, "Interrupt waiting timeout"); + break; + } + } + + return data_received; +} + +void furi_hal_nfc_listen_start(FuriHalNfcDevData* nfc_data) { + furi_assert(nfc_data); + + furi_hal_gpio_init(&gpio_nfc_irq_rfid_pull, GpioModeInput, GpioPullDown, GpioSpeedVeryHigh); + // Clear interrupts + st25r3916ClearInterrupts(); + // Mask all interrupts + st25r3916DisableInterrupts(ST25R3916_IRQ_MASK_ALL); + // RESET + st25r3916ExecuteCommand(ST25R3916_CMD_STOP); + // Setup registers + st25r3916WriteRegister( + ST25R3916_REG_OP_CONTROL, + ST25R3916_REG_OP_CONTROL_en | ST25R3916_REG_OP_CONTROL_rx_en | + ST25R3916_REG_OP_CONTROL_en_fd_auto_efd); + st25r3916WriteRegister( + ST25R3916_REG_MODE, + ST25R3916_REG_MODE_targ_targ | ST25R3916_REG_MODE_om3 | ST25R3916_REG_MODE_om0); + st25r3916WriteRegister( + ST25R3916_REG_PASSIVE_TARGET, + ST25R3916_REG_PASSIVE_TARGET_fdel_2 | ST25R3916_REG_PASSIVE_TARGET_fdel_0 | + ST25R3916_REG_PASSIVE_TARGET_d_ac_ap2p | ST25R3916_REG_PASSIVE_TARGET_d_212_424_1r); + st25r3916WriteRegister(ST25R3916_REG_MASK_RX_TIMER, 0x02); + + // Mask interrupts + uint32_t clear_irq_mask = + (ST25R3916_IRQ_MASK_RXE | ST25R3916_IRQ_MASK_RXE_PTA | ST25R3916_IRQ_MASK_WU_A_X | + ST25R3916_IRQ_MASK_WU_A); + st25r3916EnableInterrupts(clear_irq_mask); + + // Set 4 or 7 bytes UID + if(nfc_data->uid_len == 4) { + st25r3916ChangeRegisterBits( + ST25R3916_REG_AUX, ST25R3916_REG_AUX_nfc_id_mask, ST25R3916_REG_AUX_nfc_id_4bytes); + } else { + st25r3916ChangeRegisterBits( + ST25R3916_REG_AUX, ST25R3916_REG_AUX_nfc_id_mask, ST25R3916_REG_AUX_nfc_id_7bytes); + } + // Write PT Memory + uint8_t pt_memory[15] = {}; + memcpy(pt_memory, nfc_data->uid, nfc_data->uid_len); + pt_memory[10] = nfc_data->atqa[0]; + pt_memory[11] = nfc_data->atqa[1]; + if(nfc_data->uid_len == 4) { + pt_memory[12] = nfc_data->sak & ~FURI_HAL_NFC_UID_INCOMPLETE; + } else { + pt_memory[12] = nfc_data->sak | FURI_HAL_NFC_UID_INCOMPLETE; + } + pt_memory[13] = nfc_data->sak & ~FURI_HAL_NFC_UID_INCOMPLETE; + pt_memory[14] = nfc_data->sak & ~FURI_HAL_NFC_UID_INCOMPLETE; + + st25r3916WritePTMem(pt_memory, sizeof(pt_memory)); + // Go to sence + st25r3916ExecuteCommand(ST25R3916_CMD_GOTO_SENSE); +} + void rfal_interrupt_callback_handler() { osEventFlagsSet(event, EVENT_FLAG_INTERRUPT); } @@ -369,23 +476,18 @@ bool furi_hal_nfc_emulate_nfca( static bool furi_hal_nfc_transparent_tx_rx(FuriHalNfcTxRxContext* tx_rx, uint16_t timeout_ms) { furi_assert(tx_rx->nfca_signal); - platformDisableIrqCallback(); - bool ret = false; // Start transparent mode st25r3916ExecuteCommand(ST25R3916_CMD_TRANSPARENT_MODE); - // Reconfigure gpio + // Reconfigure gpio for Transparent mode furi_hal_spi_bus_handle_deinit(&furi_hal_spi_bus_handle_nfc); - furi_hal_gpio_init(&gpio_spi_r_sck, GpioModeInput, GpioPullUp, GpioSpeedLow); - furi_hal_gpio_init(&gpio_spi_r_miso, GpioModeInput, GpioPullUp, GpioSpeedLow); - furi_hal_gpio_init(&gpio_nfc_cs, GpioModeInput, GpioPullUp, GpioSpeedLow); - furi_hal_gpio_init(&gpio_spi_r_mosi, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); - furi_hal_gpio_write(&gpio_spi_r_mosi, false); // Send signal + FURI_CRITICAL_ENTER(); nfca_signal_encode(tx_rx->nfca_signal, tx_rx->tx_data, tx_rx->tx_bits, tx_rx->tx_parity); digital_signal_send(tx_rx->nfca_signal->tx_signal, &gpio_spi_r_mosi); + FURI_CRITICAL_EXIT(); furi_hal_gpio_write(&gpio_spi_r_mosi, false); // Configure gpio back to SPI and exit transparent @@ -443,7 +545,6 @@ static bool furi_hal_nfc_transparent_tx_rx(FuriHalNfcTxRxContext* tx_rx, uint16_ } st25r3916ClearInterrupts(); - platformEnableIrqCallback(); return ret; } diff --git a/firmware/targets/f7/furi_hal/furi_hal_spi_config.c b/firmware/targets/f7/furi_hal/furi_hal_spi_config.c index 0bdb2508..e01f132e 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_spi_config.c +++ b/firmware/targets/f7/furi_hal/furi_hal_spi_config.c @@ -188,6 +188,72 @@ inline static void furi_hal_spi_bus_r_handle_event_callback( } } +inline static void furi_hal_spi_bus_nfc_handle_event_callback( + FuriHalSpiBusHandle* handle, + FuriHalSpiBusHandleEvent event, + const LL_SPI_InitTypeDef* preset) { + if(event == FuriHalSpiBusHandleEventInit) { + // Configure GPIOs in normal SPI mode + furi_hal_gpio_init_ex( + handle->miso, + GpioModeAltFunctionPushPull, + GpioPullNo, + GpioSpeedVeryHigh, + GpioAltFn5SPI1); + furi_hal_gpio_init_ex( + handle->mosi, + GpioModeAltFunctionPushPull, + GpioPullNo, + GpioSpeedVeryHigh, + GpioAltFn5SPI1); + furi_hal_gpio_init_ex( + handle->sck, + GpioModeAltFunctionPushPull, + GpioPullNo, + GpioSpeedVeryHigh, + GpioAltFn5SPI1); + furi_hal_gpio_write(handle->cs, true); + furi_hal_gpio_init(handle->cs, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + } else if(event == FuriHalSpiBusHandleEventDeinit) { + // Configure GPIOs for st25r3916 Transparent mode + furi_hal_gpio_init(handle->sck, GpioModeInput, GpioPullUp, GpioSpeedLow); + furi_hal_gpio_init(handle->miso, GpioModeInput, GpioPullUp, GpioSpeedLow); + furi_hal_gpio_init(handle->cs, GpioModeInput, GpioPullUp, GpioSpeedLow); + furi_hal_gpio_write(handle->mosi, false); + furi_hal_gpio_init(handle->mosi, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + } else if(event == FuriHalSpiBusHandleEventActivate) { + LL_SPI_Init(handle->bus->spi, (LL_SPI_InitTypeDef*)preset); + LL_SPI_SetRxFIFOThreshold(handle->bus->spi, LL_SPI_RX_FIFO_TH_QUARTER); + LL_SPI_Enable(handle->bus->spi); + + furi_hal_gpio_init_ex( + handle->miso, + GpioModeAltFunctionPushPull, + GpioPullNo, + GpioSpeedVeryHigh, + GpioAltFn5SPI1); + furi_hal_gpio_init_ex( + handle->mosi, + GpioModeAltFunctionPushPull, + GpioPullNo, + GpioSpeedVeryHigh, + GpioAltFn5SPI1); + furi_hal_gpio_init_ex( + handle->sck, + GpioModeAltFunctionPushPull, + GpioPullNo, + GpioSpeedVeryHigh, + GpioAltFn5SPI1); + + } else if(event == FuriHalSpiBusHandleEventDeactivate) { + furi_hal_gpio_init(handle->miso, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(handle->mosi, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(handle->sck, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + + LL_SPI_Disable(handle->bus->spi); + } +} + static void furi_hal_spi_bus_handle_subghz_event_callback( FuriHalSpiBusHandle* handle, FuriHalSpiBusHandleEvent event) { @@ -206,7 +272,7 @@ FuriHalSpiBusHandle furi_hal_spi_bus_handle_subghz = { static void furi_hal_spi_bus_handle_nfc_event_callback( FuriHalSpiBusHandle* handle, FuriHalSpiBusHandleEvent event) { - furi_hal_spi_bus_r_handle_event_callback(handle, event, &furi_hal_spi_preset_2edge_low_8m); + furi_hal_spi_bus_nfc_handle_event_callback(handle, event, &furi_hal_spi_preset_2edge_low_8m); } FuriHalSpiBusHandle furi_hal_spi_bus_handle_nfc = { diff --git a/firmware/targets/furi_hal_include/furi_hal_nfc.h b/firmware/targets/furi_hal_include/furi_hal_nfc.h index 6e438367..a25e7c6d 100644 --- a/firmware/targets/furi_hal_include/furi_hal_nfc.h +++ b/firmware/targets/furi_hal_include/furi_hal_nfc.h @@ -163,6 +163,39 @@ bool furi_hal_nfc_listen( bool activate_after_sak, uint32_t timeout); +/** Start Target Listen mode + * @note RFAL free implementation + * + * @param nfc_data FuriHalNfcDevData instance + */ +void furi_hal_nfc_listen_start(FuriHalNfcDevData* nfc_data); + +/** Read data in Target Listen mode + * @note Must be called only after furi_hal_nfc_listen_start() + * + * @param tx_rx FuriHalNfcTxRxContext instance + * @param timeout_ms timeout im ms + * + * @return true on not empty receive + */ +bool furi_hal_nfc_listen_rx(FuriHalNfcTxRxContext* tx_rx, uint32_t timeout_ms); + +/** Set Target in Sleep state */ +void furi_hal_nfc_listen_sleep(); + +/** Emulate NFC-A Target + * @note RFAL based implementation + * + * @param uid NFC-A UID + * @param uid_len NFC-A UID length + * @param atqa NFC-A ATQA + * @param sak NFC-A SAK + * @param callback FuriHalNfcEmulateCallback instance + * @param context pointer to context for callback + * @param timeout timeout in ms + * + * @return true on success + */ bool furi_hal_nfc_emulate_nfca( uint8_t* uid, uint8_t uid_len, diff --git a/lib/ST25RFAL002/platform.c b/lib/ST25RFAL002/platform.c index c688bd59..1facfa2a 100644 --- a/lib/ST25RFAL002/platform.c +++ b/lib/ST25RFAL002/platform.c @@ -6,11 +6,13 @@ typedef struct { FuriThread* thread; volatile PlatformIrqCallback callback; + bool need_spi_lock; } RfalPlatform; static volatile RfalPlatform rfal_platform = { .thread = NULL, .callback = NULL, + .need_spi_lock = true, }; void nfc_isr(void* _ctx) { @@ -71,10 +73,30 @@ bool platformSpiTxRx(const uint8_t* txBuf, uint8_t* rxBuf, uint16_t len) { return ret; } -void platformProtectST25RComm() { +// Until we completely remove RFAL, NFC works with SPI from rfal_platform_irq_thread and nfc_worker +// threads. Some nfc features already stop using RFAL and work with SPI from nfc_worker only. +// rfal_platform_spi_acquire() and rfal_platform_spi_release() functions are used to lock SPI for a +// long term without locking it for each SPI transaction. This is needed for time critical communications. +void rfal_platform_spi_acquire() { + platformDisableIrqCallback(); + rfal_platform.need_spi_lock = false; furi_hal_spi_acquire(&furi_hal_spi_bus_handle_nfc); } -void platformUnprotectST25RComm() { +void rfal_platform_spi_release() { furi_hal_spi_release(&furi_hal_spi_bus_handle_nfc); + rfal_platform.need_spi_lock = true; + platformEnableIrqCallback(); +} + +void platformProtectST25RComm() { + if(rfal_platform.need_spi_lock) { + furi_hal_spi_acquire(&furi_hal_spi_bus_handle_nfc); + } +} + +void platformUnprotectST25RComm() { + if(rfal_platform.need_spi_lock) { + furi_hal_spi_release(&furi_hal_spi_bus_handle_nfc); + } } diff --git a/lib/ST25RFAL002/platform.h b/lib/ST25RFAL002/platform.h index 99f97ace..832e034e 100644 --- a/lib/ST25RFAL002/platform.h +++ b/lib/ST25RFAL002/platform.h @@ -19,6 +19,8 @@ void platformDisableIrqCallback(); bool platformSpiTxRx(const uint8_t* txBuf, uint8_t* rxBuf, uint16_t len); void platformProtectST25RComm(); void platformUnprotectST25RComm(); +void rfal_platform_spi_acquire(); +void rfal_platform_spi_release(); #define ST25R_SS_PIN NFC_CS_Pin #define ST25R_SS_PORT NFC_CS_GPIO_Port diff --git a/lib/digital_signal/digital_signal.c b/lib/digital_signal/digital_signal.c index 23ddaf90..46ca307a 100644 --- a/lib/digital_signal/digital_signal.c +++ b/lib/digital_signal/digital_signal.c @@ -5,14 +5,17 @@ #include #include +#pragma GCC optimize("O3,unroll-loops,Ofast") + #define F_TIM (64000000.0) -#define T_TIM (1.0 / F_TIM) +#define T_TIM 1562 //15.625 ns *100 +#define T_TIM_DIV2 781 //15.625 ns / 2 *100 DigitalSignal* digital_signal_alloc(uint32_t max_edges_cnt) { DigitalSignal* signal = malloc(sizeof(DigitalSignal)); signal->start_level = true; signal->edges_max_cnt = max_edges_cnt; - signal->edge_timings = malloc(max_edges_cnt * sizeof(float)); + signal->edge_timings = malloc(max_edges_cnt * sizeof(uint32_t)); signal->reload_reg_buff = malloc(max_edges_cnt * sizeof(uint32_t)); signal->edge_cnt = 0; @@ -48,10 +51,10 @@ bool digital_signal_append(DigitalSignal* signal_a, DigitalSignal* signal_b) { signal_a->edge_timings[signal_a->edge_cnt] += signal_b->edge_timings[0]; } } - memcpy( - &signal_a->edge_timings[signal_a->edge_cnt], - &signal_b->edge_timings[start_copy], - (signal_b->edge_cnt - start_copy) * sizeof(float)); + + for(size_t i = 0; i < signal_b->edge_cnt - start_copy; i++) { + signal_a->edge_timings[signal_a->edge_cnt + i] = signal_b->edge_timings[start_copy + i]; + } signal_a->edge_cnt += signal_b->edge_cnt - start_copy; return true; @@ -69,34 +72,33 @@ uint32_t digital_signal_get_edges_cnt(DigitalSignal* signal) { return signal->edge_cnt; } -float digital_signal_get_edge(DigitalSignal* signal, uint32_t edge_num) { +uint32_t digital_signal_get_edge(DigitalSignal* signal, uint32_t edge_num) { furi_assert(signal); furi_assert(edge_num < signal->edge_cnt); return signal->edge_timings[edge_num]; } -static void digital_signal_prepare_arr(DigitalSignal* signal) { - float t_signal = 0; - float t_current = 0; - float r = 0; - float r_int = 0; - float r_dec = 0; +void digital_signal_prepare_arr(DigitalSignal* signal) { + uint32_t t_signal_rest = signal->edge_timings[0]; + uint32_t r_count_tick_arr = 0; + uint32_t r_rest_div = 0; for(size_t i = 0; i < signal->edge_cnt - 1; i++) { - t_signal += signal->edge_timings[i]; - r = (t_signal - t_current) / T_TIM; - r_dec = modff(r, &r_int); - if(r_dec < 0.5f) { - signal->reload_reg_buff[i] = (uint32_t)r_int - 1; + r_count_tick_arr = t_signal_rest / T_TIM; + r_rest_div = t_signal_rest % T_TIM; + t_signal_rest = signal->edge_timings[i + 1] + r_rest_div; + + if(r_rest_div < T_TIM_DIV2) { + signal->reload_reg_buff[i] = r_count_tick_arr - 1; } else { - signal->reload_reg_buff[i] = (uint32_t)r_int; + signal->reload_reg_buff[i] = r_count_tick_arr; + t_signal_rest -= T_TIM; } - t_current += (signal->reload_reg_buff[i] + 1) * T_TIM; } } -bool digital_signal_send(DigitalSignal* signal, const GpioPin* gpio) { +void digital_signal_send(DigitalSignal* signal, const GpioPin* gpio) { furi_assert(signal); furi_assert(gpio); @@ -168,6 +170,4 @@ bool digital_signal_send(DigitalSignal* signal, const GpioPin* gpio) { LL_TIM_SetCounter(TIM2, 0); LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1); LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2); - - return true; } diff --git a/lib/digital_signal/digital_signal.h b/lib/digital_signal/digital_signal.h index 5e20e733..d828444c 100644 --- a/lib/digital_signal/digital_signal.h +++ b/lib/digital_signal/digital_signal.h @@ -10,7 +10,7 @@ typedef struct { bool start_level; uint32_t edge_cnt; uint32_t edges_max_cnt; - float* edge_timings; + uint32_t* edge_timings; uint32_t* reload_reg_buff; } DigitalSignal; @@ -20,10 +20,12 @@ void digital_signal_free(DigitalSignal* signal); bool digital_signal_append(DigitalSignal* signal_a, DigitalSignal* signal_b); +void digital_signal_prepare_arr(DigitalSignal* signal); + bool digital_signal_get_start_level(DigitalSignal* signal); uint32_t digital_signal_get_edges_cnt(DigitalSignal* signal); -float digital_signal_get_edge(DigitalSignal* signal, uint32_t edge_num); +uint32_t digital_signal_get_edge(DigitalSignal* signal, uint32_t edge_num); -bool digital_signal_send(DigitalSignal* signal, const GpioPin* gpio); +void digital_signal_send(DigitalSignal* signal, const GpioPin* gpio); diff --git a/lib/nfc_protocols/mifare_classic.c b/lib/nfc_protocols/mifare_classic.c index 9dbfd9d0..b6a1c23a 100644 --- a/lib/nfc_protocols/mifare_classic.c +++ b/lib/nfc_protocols/mifare_classic.c @@ -537,25 +537,23 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ // Read command while(!command_processed) { - if(!is_encrypted) { - // Read first frame - tx_rx->tx_bits = 0; - tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault; - } - if(!furi_hal_nfc_tx_rx(tx_rx, 300)) { - FURI_LOG_D( - TAG, "Error in tx rx. Tx :%d bits, Rx: %d bits", tx_rx->tx_bits, tx_rx->rx_bits); - break; - } if(!is_encrypted) { memcpy(plain_data, tx_rx->rx_data, tx_rx->rx_bits / 8); } else { + if(!furi_hal_nfc_tx_rx(tx_rx, 300)) { + FURI_LOG_D( + TAG, + "Error in tx rx. Tx :%d bits, Rx: %d bits", + tx_rx->tx_bits, + tx_rx->rx_bits); + break; + } mf_crypto1_decrypt(&emulator->crypto, tx_rx->rx_data, tx_rx->rx_bits, plain_data); } - // TODO Check crc - if(plain_data[0] == 0x50 && plain_data[1] == 00) { + if(plain_data[0] == 0x50 && plain_data[1] == 0x00) { FURI_LOG_T(TAG, "Halt received"); + furi_hal_nfc_listen_sleep(); command_processed = true; break; } else if(plain_data[0] == 0x60 || plain_data[0] == 0x61) { @@ -564,11 +562,11 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ uint8_t sector_trailer_block = mf_classic_get_sector_trailer(block); MfClassicSectorTrailer* sector_trailer = (MfClassicSectorTrailer*)emulator->data.block[sector_trailer_block].value; - if(plain_data[0] == 0x61) { - key = nfc_util_bytes2num(sector_trailer->key_b, 6); + if(plain_data[0] == 0x60) { + key = nfc_util_bytes2num(sector_trailer->key_a, 6); access_key = MfClassicKeyA; } else { - key = nfc_util_bytes2num(sector_trailer->key_a, 6); + key = nfc_util_bytes2num(sector_trailer->key_b, 6); access_key = MfClassicKeyB; } @@ -581,8 +579,12 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ if(!is_encrypted) { crypto1_word(&emulator->crypto, emulator->cuid ^ nonce, 0); memcpy(tx_rx->tx_data, nt, sizeof(nt)); + tx_rx->tx_parity[0] = 0; + for(size_t i = 0; i < sizeof(nt); i++) { + tx_rx->tx_parity[0] |= nfc_util_odd_parity8(nt[i]) << (7 - i); + } tx_rx->tx_bits = sizeof(nt) * 8; - tx_rx->tx_rx_type = FuriHalNfcTxRxTypeRxRaw; + tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent; } else { mf_crypto1_encrypt( &emulator->crypto, diff --git a/lib/nfc_protocols/nfca.c b/lib/nfc_protocols/nfca.c index e1cbdafe..c401f8cc 100755 --- a/lib/nfc_protocols/nfca.c +++ b/lib/nfc_protocols/nfca.c @@ -8,7 +8,10 @@ #define NFCA_CRC_INIT (0x6363) #define NFCA_F_SIG (13560000.0) -#define NFCA_T_SIG (1.0 / NFCA_F_SIG) +#define T_SIG 7374 //73.746ns*100 +#define T_SIG_x8 58992 //T_SIG*8 +#define T_SIG_x8_x8 471936 //T_SIG*8*8 +#define T_SIG_x8_x9 530928 //T_SIG*8*9 #define NFCA_SIGNAL_MAX_EDGES (1350) @@ -64,15 +67,15 @@ static void nfca_add_bit(DigitalSignal* signal, bool bit) { if(bit) { signal->start_level = true; for(size_t i = 0; i < 7; i++) { - signal->edge_timings[i] = 8 * NFCA_T_SIG; + signal->edge_timings[i] = T_SIG_x8; } - signal->edge_timings[7] = 9 * 8 * NFCA_T_SIG; + signal->edge_timings[7] = T_SIG_x8_x9; signal->edge_cnt = 8; } else { signal->start_level = false; - signal->edge_timings[0] = 8 * 8 * NFCA_T_SIG; + signal->edge_timings[0] = T_SIG_x8_x8; for(size_t i = 1; i < 9; i++) { - signal->edge_timings[i] = 8 * NFCA_T_SIG; + signal->edge_timings[i] = T_SIG_x8; } signal->edge_cnt = 9; }