diff --git a/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3.c b/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3.c index 59f0e55a858b..57a73cc0ca3f 100644 --- a/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3.c +++ b/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3.c @@ -2,6 +2,7 @@ #include "iso15693_3_render.h" #include +#include #include "nfc/nfc_app_i.h" @@ -58,6 +59,38 @@ static void nfc_scene_read_success_on_enter_iso15693_3(NfcApp* instance) { furi_string_free(temp_str); } +static NfcCommand + nfc_scene_emulate_listener_callback_iso15693_3(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.protocol == NfcProtocolIso15693_3); + furi_assert(event.data); + + NfcApp* nfc = context; + Iso15693_3ListenerEvent* iso15693_3_event = event.data; + + if(iso15693_3_event->type == Iso15693_3ListenerEventTypeCustomCommand) { + furi_string_cat_printf(nfc->text_box_store, "R:"); + for(size_t i = 0; i < bit_buffer_get_size_bytes(iso15693_3_event->data->buffer); i++) { + furi_string_cat_printf( + nfc->text_box_store, + " %02X", + bit_buffer_get_byte(iso15693_3_event->data->buffer, i)); + } + furi_string_push_back(nfc->text_box_store, '\n'); + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventListenerUpdate); + } + + return NfcCommandContinue; +} + +static void nfc_scene_emulate_on_enter_iso15693_3(NfcApp* instance) { + const Iso15693_3Data* data = nfc_device_get_data(instance->nfc_device, NfcProtocolIso15693_3); + + instance->listener = nfc_listener_alloc(instance->nfc, NfcProtocolIso15693_3, data); + nfc_listener_start( + instance->listener, nfc_scene_emulate_listener_callback_iso15693_3, instance); +} + static bool nfc_scene_info_on_event_iso15693_3(NfcApp* instance, uint32_t event) { if(event == GuiButtonTypeRight) { scene_manager_next_scene(instance->scene_manager, NfcSceneNotImplemented); @@ -77,7 +110,8 @@ static bool nfc_scene_saved_menu_on_event_iso15693_3(NfcApp* instance, uint32_t } const NfcProtocolSupportBase nfc_protocol_support_iso15693_3 = { - .features = NfcProtocolFeatureNone, // TODO: Implement better UID editing, + .features = + NfcProtocolFeatureEmulateFull, // | NfcProtocolFeatureEditUid, // TODO: Implement better UID editing .scene_info = { @@ -106,7 +140,7 @@ const NfcProtocolSupportBase nfc_protocol_support_iso15693_3 = { }, .scene_emulate = { - .on_enter = NULL, + .on_enter = nfc_scene_emulate_on_enter_iso15693_3, .on_event = NULL, }, }; diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 637718068308..2486c0b72cdd 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -182,6 +182,7 @@ Header,+,lib/one_wire/one_wire_host.h,, Header,+,lib/one_wire/one_wire_slave.h,, Header,+,lib/print/wrappers.h,, Header,+,lib/pulse_reader/pulse_reader.h,, +Header,+,lib/signal_reader/signal_reader.h,, Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_adc.h,, Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_bus.h,, Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_comp.h,, @@ -870,24 +871,28 @@ Function,-,expm1f,float,float Function,-,expm1l,long double,long double Function,-,f_hal_nfc_abort,FHalNfcError, Function,-,f_hal_nfc_acquire,FHalNfcError, -Function,-,f_hal_nfc_event_start,FHalNfcError, +Function,+,f_hal_nfc_event_start,FHalNfcError, Function,-,f_hal_nfc_init,FHalNfcError, Function,-,f_hal_nfc_is_hal_ready,FHalNfcError, -Function,-,f_hal_nfc_listen_reset,FHalNfcError, -Function,-,f_hal_nfc_listen_start,FHalNfcError, Function,-,f_hal_nfc_listener_disable_auto_col_res,FHalNfcError, +Function,+,f_hal_nfc_listener_reset,FHalNfcError, +Function,+,f_hal_nfc_listener_rx,FHalNfcError,"uint8_t*, size_t, size_t*" +Function,+,f_hal_nfc_listener_rx_start,FHalNfcError, Function,-,f_hal_nfc_listener_sleep,FHalNfcError, +Function,+,f_hal_nfc_listener_start,FHalNfcError, Function,-,f_hal_nfc_listener_tx,FHalNfcError,"const uint8_t*, size_t" +Function,-,f_hal_nfc_listener_wait_event,FHalNfcEvent,uint32_t Function,-,f_hal_nfc_low_power_mode_start,FHalNfcError, Function,-,f_hal_nfc_low_power_mode_stop,FHalNfcError, Function,-,f_hal_nfc_poller_field_on,FHalNfcError, Function,-,f_hal_nfc_poller_rx,FHalNfcError,"uint8_t*, size_t, size_t*" Function,-,f_hal_nfc_poller_tx,FHalNfcError,"const uint8_t*, size_t" Function,-,f_hal_nfc_poller_tx_custom_parity,FHalNfcError,"const uint8_t*, size_t" +Function,-,f_hal_nfc_poller_wait_event,FHalNfcEvent,uint32_t Function,-,f_hal_nfc_release,FHalNfcError, Function,-,f_hal_nfc_reset_mode,FHalNfcError, Function,-,f_hal_nfc_set_mask_receive_timer,void,uint32_t -Function,-,f_hal_nfc_set_mode,FHalNfcError,"FHalNfcMode, FHalNfcBitrate" +Function,+,f_hal_nfc_set_mode,FHalNfcError,"FHalNfcMode, FHalNfcTech" Function,-,f_hal_nfc_timer_block_tx_is_running,_Bool, Function,-,f_hal_nfc_timer_block_tx_start,void,uint32_t Function,-,f_hal_nfc_timer_block_tx_start_us,void,uint32_t @@ -895,9 +900,6 @@ Function,-,f_hal_nfc_timer_block_tx_stop,void, Function,-,f_hal_nfc_timer_fwt_start,void,uint32_t Function,-,f_hal_nfc_timer_fwt_stop,void, Function,-,f_hal_nfc_trx_reset,FHalNfcError, -Function,-,f_hal_nfc_wait_event,FHalNfcEvent,uint32_t -Function,-,f_hal_nfca_listener_deinit,FHalNfcError, -Function,-,f_hal_nfca_listener_init,FHalNfcError, Function,-,f_hal_nfca_listener_tx_custom_parity,FHalNfcError,"const uint8_t*, const _Bool*, size_t" Function,-,f_hal_nfca_receive_sdd_frame,FHalNfcError,"uint8_t*, size_t, size_t*" Function,-,f_hal_nfca_send_sdd_frame,FHalNfcError,"const uint8_t*, size_t" @@ -2668,6 +2670,13 @@ Function,+,sha256_finish,void,"sha256_context*, unsigned char[32]" Function,+,sha256_process,void,sha256_context* Function,+,sha256_start,void,sha256_context* Function,+,sha256_update,void,"sha256_context*, const unsigned char*, unsigned int" +Function,+,signal_reader_alloc,SignalReader*,"const GpioPin*, uint32_t" +Function,+,signal_reader_free,void,SignalReader* +Function,+,signal_reader_set_polarity,void,"SignalReader*, SignalReaderPolarity" +Function,+,signal_reader_set_pull,void,"SignalReader*, GpioPull" +Function,+,signal_reader_set_sample_rate,void,"SignalReader*, SignalReaderTimeUnit, uint32_t" +Function,+,signal_reader_start,void,"SignalReader*, SignalReaderCallback, void*" +Function,+,signal_reader_stop,void,SignalReader* Function,+,simple_array_alloc,SimpleArray*,const SimpleArrayConfig* Function,+,simple_array_cget,const SimpleArrayElement*,"const SimpleArray*, uint32_t" Function,+,simple_array_cget_data,const SimpleArrayData*,const SimpleArray* diff --git a/firmware/targets/f7/furi_hal/f_hal_nfc.c b/firmware/targets/f7/furi_hal/f_hal_nfc.c index 59373f2a4201..3047341ecbe0 100644 --- a/firmware/targets/f7/furi_hal/f_hal_nfc.c +++ b/firmware/targets/f7/furi_hal/f_hal_nfc.c @@ -7,7 +7,14 @@ #define TAG "FHalNfc" -static FuriMutex* f_hal_nfc_mutex = NULL; +static const FHalNfcTechBase* f_hal_nfc_tech[FHalNfcTechNum] = { + [FHalNfcTechIso14443a] = &f_hal_nfc_iso14443a, + [FHalNfcTechIso14443b] = &f_hal_nfc_iso14443b, + [FHalNfcTechIso15693] = &f_hal_nfc_iso15693, + [FHalNfcTechFelica] = &f_hal_nfc_felica, +}; + +FHalNfc f_hal_nfc; static FHalNfcError f_hal_nfc_turn_on_osc(FuriHalSpiBusHandle* handle) { FHalNfcError error = FHalNfcErrorNone; @@ -59,9 +66,9 @@ FHalNfcError f_hal_nfc_is_hal_ready() { } FHalNfcError f_hal_nfc_init() { - furi_assert(f_hal_nfc_mutex == NULL); + furi_assert(f_hal_nfc.mutex == NULL); - f_hal_nfc_mutex = furi_mutex_alloc(FuriMutexTypeNormal); + f_hal_nfc.mutex = furi_mutex_alloc(FuriMutexTypeNormal); FHalNfcError error = FHalNfcErrorNone; f_hal_nfc_event_init(); @@ -234,16 +241,16 @@ FHalNfcError f_hal_nfc_init() { } static bool f_hal_nfc_is_mine() { - return (furi_mutex_get_owner(f_hal_nfc_mutex) == furi_thread_get_current_id()); + return (furi_mutex_get_owner(f_hal_nfc.mutex) == furi_thread_get_current_id()); } FHalNfcError f_hal_nfc_acquire() { - furi_check(f_hal_nfc_mutex); + furi_check(f_hal_nfc.mutex); furi_hal_spi_acquire(&furi_hal_spi_bus_handle_nfc); FHalNfcError error = FHalNfcErrorNone; - if(furi_mutex_acquire(f_hal_nfc_mutex, 100) != FuriStatusOk) { + if(furi_mutex_acquire(f_hal_nfc.mutex, 100) != FuriStatusOk) { furi_hal_spi_release(&furi_hal_spi_bus_handle_nfc); error = FHalNfcErrorBusy; } @@ -252,9 +259,9 @@ FHalNfcError f_hal_nfc_acquire() { } FHalNfcError f_hal_nfc_release() { - furi_check(f_hal_nfc_mutex); + furi_check(f_hal_nfc.mutex); furi_check(f_hal_nfc_is_mine()); - furi_check(furi_mutex_release(f_hal_nfc_mutex) == FuriStatusOk); + furi_check(furi_mutex_release(f_hal_nfc.mutex) == FuriStatusOk); furi_hal_spi_release(&furi_hal_spi_bus_handle_nfc); @@ -298,7 +305,7 @@ FHalNfcError f_hal_nfc_low_power_mode_stop() { return error; } -static void f_hal_nfc_configure_poller_common(FuriHalSpiBusHandle* handle) { +static FHalNfcError f_hal_nfc_poller_init_common(FuriHalSpiBusHandle* handle) { // Disable wake up st25r3916_clear_reg_bits(handle, ST25R3916_REG_OP_CONTROL, ST25R3916_REG_OP_CONTROL_wu); // Enable correlator @@ -315,264 +322,41 @@ static void f_hal_nfc_configure_poller_common(FuriHalSpiBusHandle* handle) { st25r3916_write_reg(handle, ST25R3916_REG_OVERSHOOT_CONF2, 0x00); st25r3916_write_reg(handle, ST25R3916_REG_UNDERSHOOT_CONF1, 0x00); st25r3916_write_reg(handle, ST25R3916_REG_UNDERSHOOT_CONF2, 0x00); -} - -// TODO: Refactor this function to be more modular and readable -FHalNfcError f_hal_nfc_set_mode(FHalNfcMode mode, FHalNfcBitrate bitrate) { - FHalNfcError error = FHalNfcErrorNone; - FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; - - if(mode == FHalNfcModeIso14443aPoller || mode == FHalNfcModeIso14443aListener) { - if(mode == FHalNfcModeIso14443aPoller) { - // Poller configuration - f_hal_nfc_configure_poller_common(handle); - // Enable ISO14443A mode, OOK modulation - st25r3916_change_reg_bits( - handle, - ST25R3916_REG_MODE, - ST25R3916_REG_MODE_om_mask | ST25R3916_REG_MODE_tr_am, - ST25R3916_REG_MODE_om_iso14443a | ST25R3916_REG_MODE_tr_am_ook); - - // Overshoot protection - is this necessary here? - st25r3916_change_reg_bits(handle, ST25R3916_REG_OVERSHOOT_CONF1, 0xff, 0x40); - st25r3916_change_reg_bits(handle, ST25R3916_REG_OVERSHOOT_CONF2, 0xff, 0x03); - st25r3916_change_reg_bits(handle, ST25R3916_REG_UNDERSHOOT_CONF1, 0xff, 0x40); - st25r3916_change_reg_bits(handle, ST25R3916_REG_UNDERSHOOT_CONF2, 0xff, 0x03); - - } else { - // Listener configuration - f_hal_nfca_listener_init(); - st25r3916_write_reg( - handle, - ST25R3916_REG_OP_CONTROL, - ST25R3916_REG_OP_CONTROL_en | ST25R3916_REG_OP_CONTROL_rx_en | - ST25R3916_REG_OP_CONTROL_en_fd_auto_efd); - st25r3916_write_reg( - handle, ST25R3916_REG_MODE, ST25R3916_REG_MODE_targ_targ | ST25R3916_REG_MODE_om0); - st25r3916_write_reg( - handle, - 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); - - st25r3916_write_reg(handle, ST25R3916_REG_MASK_RX_TIMER, 0x02); - } - if(bitrate == FHalNfcBitrate106) { - // Bitrate-dependent NFC-A settings - - // 1st stage zero = 600kHz, 3rd stage zero = 200 kHz - st25r3916_write_reg(handle, ST25R3916_REG_RX_CONF1, ST25R3916_REG_RX_CONF1_z600k); - // AGC enabled, ratio 6:1, squelch after TX - st25r3916_write_reg( - handle, - ST25R3916_REG_RX_CONF2, - ST25R3916_REG_RX_CONF2_agc6_3 | ST25R3916_REG_RX_CONF2_agc_m | - ST25R3916_REG_RX_CONF2_agc_en | ST25R3916_REG_RX_CONF2_sqm_dyn); - // HF operation, full gain on AM and PM channels - st25r3916_write_reg(handle, ST25R3916_REG_RX_CONF3, 0x00); - // No gain reduction on AM and PM channels - st25r3916_write_reg(handle, ST25R3916_REG_RX_CONF4, 0x00); - // Correlator config - st25r3916_write_reg( - handle, - ST25R3916_REG_CORR_CONF1, - ST25R3916_REG_CORR_CONF1_corr_s0 | ST25R3916_REG_CORR_CONF1_corr_s4 | - ST25R3916_REG_CORR_CONF1_corr_s6); - // Sleep mode disable, 424kHz mode off - st25r3916_write_reg(handle, ST25R3916_REG_CORR_CONF2, 0x00); - } - - } else if(mode == FHalNfcModeIso14443bPoller /* TODO: Listener support */) { - f_hal_nfc_configure_poller_common(handle); - // Enable ISO14443B mode, AM modulation - st25r3916_change_reg_bits( - handle, - ST25R3916_REG_MODE, - ST25R3916_REG_MODE_om_mask | ST25R3916_REG_MODE_tr_am, - ST25R3916_REG_MODE_om_iso14443b | ST25R3916_REG_MODE_tr_am_am); - - // 10% ASK modulation - st25r3916_change_reg_bits( - handle, - ST25R3916_REG_TX_DRIVER, - ST25R3916_REG_TX_DRIVER_am_mod_mask, - ST25R3916_REG_TX_DRIVER_am_mod_10percent); - - // Use regulator AM, resistive AM disabled - st25r3916_clear_reg_bits( - handle, - ST25R3916_REG_AUX_MOD, - ST25R3916_REG_AUX_MOD_dis_reg_am | ST25R3916_REG_AUX_MOD_res_am); - - // EGT = 0 etu - // SOF = 10 etu LOW + 2 etu HIGH - // EOF = 10 etu - st25r3916_change_reg_bits( - handle, - ST25R3916_REG_ISO14443B_1, - ST25R3916_REG_ISO14443B_1_egt_mask | ST25R3916_REG_ISO14443B_1_sof_mask | - ST25R3916_REG_ISO14443B_1_eof, - (0U << ST25R3916_REG_ISO14443B_1_egt_shift) | ST25R3916_REG_ISO14443B_1_sof_0_10etu | - ST25R3916_REG_ISO14443B_1_sof_1_2etu | ST25R3916_REG_ISO14443B_1_eof_10etu); - - // TR1 = 80 / fs - // B' mode off (no_sof & no_eof = 0) - st25r3916_change_reg_bits( - handle, - ST25R3916_REG_ISO14443B_2, - ST25R3916_REG_ISO14443B_2_tr1_mask | ST25R3916_REG_ISO14443B_2_no_sof | - ST25R3916_REG_ISO14443B_2_no_eof, - ST25R3916_REG_ISO14443B_2_tr1_80fs80fs); - - if(bitrate == FHalNfcBitrate106) { - // Bitrate-dependent NFC-B settings - - // 1st stage zero = 60kHz, 3rd stage zero = 200 kHz - st25r3916_write_reg(handle, ST25R3916_REG_RX_CONF1, ST25R3916_REG_RX_CONF1_h200); - - // Enable AGC - // AGC Ratio 6 - // AGC algorithm with RESET (recommended for ISO14443-B) - // AGC operation during complete receive period - // Squelch ratio 6/3 (recommended for ISO14443-B) - // Squelch automatic activation on TX end - st25r3916_write_reg( - handle, - ST25R3916_REG_RX_CONF2, - ST25R3916_REG_RX_CONF2_agc6_3 | ST25R3916_REG_RX_CONF2_agc_alg | - ST25R3916_REG_RX_CONF2_agc_m | ST25R3916_REG_RX_CONF2_agc_en | - ST25R3916_REG_RX_CONF2_pulz_61 | ST25R3916_REG_RX_CONF2_sqm_dyn); - - // HF operation, full gain on AM and PM channels - st25r3916_write_reg(handle, ST25R3916_REG_RX_CONF3, 0x00); - // No gain reduction on AM and PM channels - st25r3916_write_reg(handle, ST25R3916_REG_RX_CONF4, 0x00); - - // Subcarrier end detector enabled - // Subcarrier end detection level = 66% - // BPSK start 33 pilot pulses - // AM & PM summation before digitizing on - st25r3916_write_reg( - handle, - ST25R3916_REG_CORR_CONF1, - ST25R3916_REG_CORR_CONF1_corr_s0 | ST25R3916_REG_CORR_CONF1_corr_s1 | - ST25R3916_REG_CORR_CONF1_corr_s3 | ST25R3916_REG_CORR_CONF1_corr_s4); - // Sleep mode disable, 424kHz mode off - st25r3916_write_reg(handle, ST25R3916_REG_CORR_CONF2, 0x00); - } - - } else if(mode == FHalNfcModeIso15693Poller || mode == FHalNfcModeIso15693Listener) { - if(mode == FHalNfcModeIso15693Poller) { - // Poller configuration - f_hal_nfc_configure_poller_common(handle); - // Enable Subcarrier Stream mode, OOK modulation - st25r3916_change_reg_bits( - handle, - ST25R3916_REG_MODE, - ST25R3916_REG_MODE_om_mask | ST25R3916_REG_MODE_tr_am, - ST25R3916_REG_MODE_om_subcarrier_stream | ST25R3916_REG_MODE_tr_am_ook); - - // Subcarrier 424 kHz mode - // 8 sub-carrier pulses in report period - st25r3916_write_reg( - handle, - ST25R3916_REG_STREAM_MODE, - ST25R3916_REG_STREAM_MODE_scf_sc424 | ST25R3916_REG_STREAM_MODE_stx_106 | - ST25R3916_REG_STREAM_MODE_scp_8pulses); - - // Use regulator AM, resistive AM disabled - st25r3916_clear_reg_bits( - handle, - ST25R3916_REG_AUX_MOD, - ST25R3916_REG_AUX_MOD_dis_reg_am | ST25R3916_REG_AUX_MOD_res_am); - - } else { - // Listener configuration - f_hal_nfca_listener_init(); - // TODO: Implement listener config - } - - if(bitrate == FHalNfcBitrate26p48) { - // Bitrate-dependent NFC-V settings - - // 1st stage zero = 12 kHz, 3rd stage zero = 80 kHz, low-pass = 600 kHz - st25r3916_write_reg( - handle, - ST25R3916_REG_RX_CONF1, - ST25R3916_REG_RX_CONF1_z12k | ST25R3916_REG_RX_CONF1_h80 | - ST25R3916_REG_RX_CONF1_lp_600khz); - - // Enable AGC - // AGC Ratio 6 - // AGC algorithm with RESET (recommended for ISO15693) - // AGC operation during complete receive period - // Squelch automatic activation on TX end - st25r3916_write_reg( - handle, - ST25R3916_REG_RX_CONF2, - ST25R3916_REG_RX_CONF2_agc6_3 | ST25R3916_REG_RX_CONF2_agc_m | - ST25R3916_REG_RX_CONF2_agc_en | ST25R3916_REG_RX_CONF2_sqm_dyn); - - // HF operation, full gain on AM and PM channels - st25r3916_write_reg(handle, ST25R3916_REG_RX_CONF3, 0x00); - // No gain reduction on AM and PM channels - st25r3916_write_reg(handle, ST25R3916_REG_RX_CONF4, 0x00); - - // Collision detection level 53% - // AM & PM summation before digitizing on - st25r3916_write_reg( - handle, - ST25R3916_REG_CORR_CONF1, - ST25R3916_REG_CORR_CONF1_corr_s0 | ST25R3916_REG_CORR_CONF1_corr_s1 | - ST25R3916_REG_CORR_CONF1_corr_s4); - // 424 kHz subcarrier stream mode on - st25r3916_write_reg( - handle, ST25R3916_REG_CORR_CONF2, ST25R3916_REG_CORR_CONF2_corr_s8); - } - } else if(mode == FHalNfcModeFelicaPoller) { - f_hal_nfc_configure_poller_common(handle); - // Enable Felica mode, AM modulation - st25r3916_change_reg_bits( - handle, - ST25R3916_REG_MODE, - ST25R3916_REG_MODE_om_mask | ST25R3916_REG_MODE_tr_am, - ST25R3916_REG_MODE_om_felica | ST25R3916_REG_MODE_tr_am_am); + return FHalNfcErrorNone; +} - // 10% ASK modulation - st25r3916_change_reg_bits( - handle, - ST25R3916_REG_TX_DRIVER, - ST25R3916_REG_TX_DRIVER_am_mod_mask, - ST25R3916_REG_TX_DRIVER_am_mod_10percent); +static FHalNfcError f_hal_nfc_listener_init_common(FuriHalSpiBusHandle* handle) { + UNUSED(handle); + // TODO: Common listener configuration + return FHalNfcErrorNone; +} - // Use regulator AM, resistive AM disabled - st25r3916_clear_reg_bits( - handle, - ST25R3916_REG_AUX_MOD, - ST25R3916_REG_AUX_MOD_dis_reg_am | ST25R3916_REG_AUX_MOD_res_am); +FHalNfcError f_hal_nfc_set_mode(FHalNfcMode mode, FHalNfcTech tech) { + furi_assert(mode < FHalNfcModeNum); + furi_assert(tech < FHalNfcTechNum); - st25r3916_change_reg_bits( - handle, - ST25R3916_REG_BIT_RATE, - ST25R3916_REG_BIT_RATE_txrate_mask | ST25R3916_REG_BIT_RATE_rxrate_mask, - ST25R3916_REG_BIT_RATE_txrate_212 | ST25R3916_REG_BIT_RATE_rxrate_212); + FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; - // Receive configuration - st25r3916_write_reg( - handle, - ST25R3916_REG_RX_CONF1, - ST25R3916_REG_RX_CONF1_lp0 | ST25R3916_REG_RX_CONF1_hz_12_80khz); + FHalNfcError error = FHalNfcErrorNone; - // Correlator setup - st25r3916_write_reg( - handle, - ST25R3916_REG_CORR_CONF1, - ST25R3916_REG_CORR_CONF1_corr_s6 | ST25R3916_REG_CORR_CONF1_corr_s4 | - ST25R3916_REG_CORR_CONF1_corr_s3); + if(mode == FHalNfcModePoller) { + do { + error = f_hal_nfc_poller_init_common(handle); + if(error != FHalNfcErrorNone) break; + error = f_hal_nfc_tech[tech]->poller.init(handle); + } while(false); + + } else if(mode == FHalNfcModeListener) { + do { + error = f_hal_nfc_listener_init_common(handle); + if(error != FHalNfcErrorNone) break; + error = f_hal_nfc_tech[tech]->listener.init(handle); + } while(false); } + f_hal_nfc.mode = mode; + f_hal_nfc.tech = tech; return error; } @@ -584,7 +368,6 @@ FHalNfcError f_hal_nfc_reset_mode() { // Set default value in mode register st25r3916_write_reg(handle, ST25R3916_REG_MODE, ST25R3916_REG_MODE_om0); st25r3916_write_reg(handle, ST25R3916_REG_STREAM_MODE, 0); - st25r3916_clear_reg_bits(handle, ST25R3916_REG_AUX, ST25R3916_REG_AUX_no_crc_rx); st25r3916_clear_reg_bits( handle, @@ -606,7 +389,14 @@ FHalNfcError f_hal_nfc_reset_mode() { ST25R3916_REG_CORR_CONF1_corr_s1 | ST25R3916_REG_CORR_CONF1_corr_s0); st25r3916_write_reg(handle, ST25R3916_REG_CORR_CONF2, 0); - f_hal_nfca_listener_deinit(); + const FHalNfcMode mode = f_hal_nfc.mode; + const FHalNfcTech tech = f_hal_nfc.tech; + + if(mode == FHalNfcModePoller) { + error = f_hal_nfc_tech[tech]->poller.deinit(handle); + } else if(mode == FHalNfcModeListener) { + error = f_hal_nfc_tech[tech]->listener.deinit(handle); + } return error; } @@ -664,11 +454,13 @@ FHalNfcError f_hal_nfc_poller_tx_custom_parity(const uint8_t* tx_data, size_t tx return err; } -FHalNfcError f_hal_nfc_poller_tx(const uint8_t* tx_data, size_t tx_bits) { +FHalNfcError f_hal_nfc_poller_tx_common( + FuriHalSpiBusHandle* handle, + const uint8_t* tx_data, + size_t tx_bits) { furi_assert(tx_data); FHalNfcError err = FHalNfcErrorNone; - FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; // Prepare tx st25r3916_direct_cmd(handle, ST25R3916_CMD_CLEAR_FIFO); @@ -694,25 +486,76 @@ FHalNfcError f_hal_nfc_poller_tx(const uint8_t* tx_data, size_t tx_bits) { return err; } -FHalNfcError f_hal_nfc_listener_tx(const uint8_t* tx_data, size_t tx_bits) { - furi_assert(tx_data); - - FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; +FHalNfcError + f_hal_nfc_common_fifo_tx(FuriHalSpiBusHandle* handle, const uint8_t* tx_data, size_t tx_bits) { + FHalNfcError err = FHalNfcErrorNone; st25r3916_direct_cmd(handle, ST25R3916_CMD_CLEAR_FIFO); - st25r3916_write_fifo(handle, tx_data, tx_bits); st25r3916_direct_cmd(handle, ST25R3916_CMD_TRANSMIT_WITHOUT_CRC); - bool tx_end = f_hal_nfc_event_wait_for_specific_irq(handle, ST25R3916_IRQ_MASK_TXE, 10); - return tx_end ? FHalNfcErrorNone : FHalNfcErrorCommunicationTimeout; + return err; +} + +FHalNfcError f_hal_nfc_poller_tx(const uint8_t* tx_data, size_t tx_bits) { + furi_assert(f_hal_nfc.mode == FHalNfcModePoller); + furi_assert(f_hal_nfc.tech < FHalNfcTechNum); + FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + + return f_hal_nfc_tech[f_hal_nfc.tech]->poller.tx(handle, tx_data, tx_bits); } FHalNfcError f_hal_nfc_poller_rx(uint8_t* rx_data, size_t rx_data_size, size_t* rx_bits) { - furi_assert(rx_data); - furi_assert(rx_bits); + furi_assert(f_hal_nfc.mode == FHalNfcModePoller); + furi_assert(f_hal_nfc.tech < FHalNfcTechNum); + FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + + return f_hal_nfc_tech[f_hal_nfc.tech]->poller.rx(handle, rx_data, rx_data_size, rx_bits); +} + +FHalNfcEvent f_hal_nfc_poller_wait_event(uint32_t timeout_ms) { + furi_assert(f_hal_nfc.mode == FHalNfcModePoller); + furi_assert(f_hal_nfc.tech < FHalNfcTechNum); + + return f_hal_nfc_tech[f_hal_nfc.tech]->poller.wait_event(timeout_ms); +} + +FHalNfcEvent f_hal_nfc_listener_wait_event(uint32_t timeout_ms) { + furi_assert(f_hal_nfc.mode == FHalNfcModeListener); + furi_assert(f_hal_nfc.tech < FHalNfcTechNum); + + return f_hal_nfc_tech[f_hal_nfc.tech]->listener.wait_event(timeout_ms); +} + +FHalNfcError f_hal_nfc_listener_tx(const uint8_t* tx_data, size_t tx_bits) { + furi_assert(tx_data); + + furi_assert(f_hal_nfc.mode == FHalNfcModeListener); + furi_assert(f_hal_nfc.tech < FHalNfcTechNum); FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + return f_hal_nfc_tech[f_hal_nfc.tech]->listener.tx(handle, tx_data, tx_bits); +} + +FHalNfcError f_hal_nfc_common_listener_rx_start(FuriHalSpiBusHandle* handle) { + UNUSED(handle); + /* Empty implementation */ + return FHalNfcErrorNone; +} + +FHalNfcError f_hal_nfc_listener_rx_start() { + furi_assert(f_hal_nfc.mode == FHalNfcModeListener); + furi_assert(f_hal_nfc.tech < FHalNfcTechNum); + + FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + return f_hal_nfc_tech[f_hal_nfc.tech]->listener.rx_start(handle); +} + +FHalNfcError f_hal_nfc_common_fifo_rx( + FuriHalSpiBusHandle* handle, + uint8_t* rx_data, + size_t rx_data_size, + size_t* rx_bits) { FHalNfcError error = FHalNfcErrorNone; if(!st25r3916_read_fifo(handle, rx_data, rx_data_size, rx_bits)) { @@ -722,6 +565,17 @@ FHalNfcError f_hal_nfc_poller_rx(uint8_t* rx_data, size_t rx_data_size, size_t* return error; } +FHalNfcError f_hal_nfc_listener_rx(uint8_t* rx_data, size_t rx_data_size, size_t* rx_bits) { + furi_assert(rx_data); + furi_assert(rx_bits); + + furi_assert(f_hal_nfc.mode == FHalNfcModeListener); + furi_assert(f_hal_nfc.tech < FHalNfcTechNum); + + FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + return f_hal_nfc_tech[f_hal_nfc.tech]->listener.rx(handle, rx_data, rx_data_size, rx_bits); +} + FHalNfcError f_hal_nfc_trx_reset() { FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; @@ -730,7 +584,7 @@ FHalNfcError f_hal_nfc_trx_reset() { return FHalNfcErrorNone; } -FHalNfcError f_hal_nfc_listen_start() { +FHalNfcError f_hal_nfc_listener_start() { FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; st25r3916_direct_cmd(handle, ST25R3916_CMD_STOP); @@ -752,7 +606,7 @@ FHalNfcError f_hal_nfc_listen_start() { return FHalNfcErrorNone; } -FHalNfcError f_hal_nfc_listen_reset() { +FHalNfcError f_hal_nfc_listener_reset() { FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; st25r3916_direct_cmd(handle, ST25R3916_CMD_UNMASK_RECEIVE_DATA); diff --git a/firmware/targets/f7/furi_hal/f_hal_nfc_event.c b/firmware/targets/f7/furi_hal/f_hal_nfc_event.c index f28967ffb869..622038fefa4e 100644 --- a/firmware/targets/f7/furi_hal/f_hal_nfc_event.c +++ b/firmware/targets/f7/furi_hal/f_hal_nfc_event.c @@ -15,7 +15,7 @@ FHalNfcError f_hal_nfc_event_start() { return FHalNfcErrorNone; } -void f_hal_nfc_set_event(FHalNfcEventInternalType event) { +void f_hal_nfc_event_set(FHalNfcEventInternalType event) { furi_assert(f_hal_nfc_event); furi_assert(f_hal_nfc_event->thread); @@ -23,11 +23,11 @@ void f_hal_nfc_set_event(FHalNfcEventInternalType event) { } FHalNfcError f_hal_nfc_abort() { - f_hal_nfc_set_event(FHalNfcEventInternalTypeAbort); + f_hal_nfc_event_set(FHalNfcEventInternalTypeAbort); return FHalNfcErrorNone; } -FHalNfcEvent f_hal_nfc_wait_event(uint32_t timeout_ms) { +FHalNfcEvent f_hal_nfc_wait_event_common(uint32_t timeout_ms) { furi_assert(f_hal_nfc_event); furi_assert(f_hal_nfc_event->thread); diff --git a/firmware/targets/f7/furi_hal/f_hal_nfc_felica.c b/firmware/targets/f7/furi_hal/f_hal_nfc_felica.c new file mode 100644 index 000000000000..683da8534ee4 --- /dev/null +++ b/firmware/targets/f7/furi_hal/f_hal_nfc_felica.c @@ -0,0 +1,63 @@ +#include "f_hal_nfc_i.h" + +static FHalNfcError f_hal_nfc_felica_poller_init(FuriHalSpiBusHandle* handle) { + // Enable Felica mode, AM modulation + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_MODE, + ST25R3916_REG_MODE_om_mask | ST25R3916_REG_MODE_tr_am, + ST25R3916_REG_MODE_om_felica | ST25R3916_REG_MODE_tr_am_am); + + // 10% ASK modulation + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_TX_DRIVER, + ST25R3916_REG_TX_DRIVER_am_mod_mask, + ST25R3916_REG_TX_DRIVER_am_mod_10percent); + + // Use regulator AM, resistive AM disabled + st25r3916_clear_reg_bits( + handle, + ST25R3916_REG_AUX_MOD, + ST25R3916_REG_AUX_MOD_dis_reg_am | ST25R3916_REG_AUX_MOD_res_am); + + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_BIT_RATE, + ST25R3916_REG_BIT_RATE_txrate_mask | ST25R3916_REG_BIT_RATE_rxrate_mask, + ST25R3916_REG_BIT_RATE_txrate_212 | ST25R3916_REG_BIT_RATE_rxrate_212); + + // Receive configuration + st25r3916_write_reg( + handle, + ST25R3916_REG_RX_CONF1, + ST25R3916_REG_RX_CONF1_lp0 | ST25R3916_REG_RX_CONF1_hz_12_80khz); + + // Correlator setup + st25r3916_write_reg( + handle, + ST25R3916_REG_CORR_CONF1, + ST25R3916_REG_CORR_CONF1_corr_s6 | ST25R3916_REG_CORR_CONF1_corr_s4 | + ST25R3916_REG_CORR_CONF1_corr_s3); + + return FHalNfcErrorNone; +} + +static FHalNfcError f_hal_nfc_felica_poller_deinit(FuriHalSpiBusHandle* handle) { + UNUSED(handle); + + return FHalNfcErrorNone; +} + +const FHalNfcTechBase f_hal_nfc_felica = { + .poller = + { + .init = f_hal_nfc_felica_poller_init, + .deinit = f_hal_nfc_felica_poller_deinit, + .wait_event = f_hal_nfc_wait_event_common, + .tx = f_hal_nfc_poller_tx_common, + .rx = f_hal_nfc_common_fifo_rx, + }, + + .listener = {0}, +}; diff --git a/firmware/targets/f7/furi_hal/f_hal_nfc_i.h b/firmware/targets/f7/furi_hal/f_hal_nfc_i.h index ed06e4c9b8bd..834ac219a399 100644 --- a/firmware/targets/f7/furi_hal/f_hal_nfc_i.h +++ b/firmware/targets/f7/furi_hal/f_hal_nfc_i.h @@ -15,22 +15,30 @@ typedef enum { FHalNfcEventInternalTypeIrq = (1U << 1), FHalNfcEventInternalTypeTimerFwtExpired = (1U << 2), FHalNfcEventInternalTypeTimerBlockTxExpired = (1U << 3), + FHalNfcEventInternalTypeTransparentDataReceived = (1U << 4), } FHalNfcEventInternalType; -#define F_HAL_NFC_EVENT_INTERNAL_ALL \ - ((FHalNfcEventInternalTypeAbort | FHalNfcEventInternalTypeIrq | \ - FHalNfcEventInternalTypeTimerFwtExpired | FHalNfcEventInternalTypeTimerBlockTxExpired)) +#define F_HAL_NFC_EVENT_INTERNAL_ALL \ + ((FHalNfcEventInternalTypeAbort | FHalNfcEventInternalTypeIrq | \ + FHalNfcEventInternalTypeTimerFwtExpired | FHalNfcEventInternalTypeTimerBlockTxExpired | \ + FHalNfcEventInternalTypeTransparentDataReceived)) typedef struct { FuriThreadId thread; void* context; } FHalNfcEventInternal; -extern FHalNfcEventInternal* f_hal_nfc; +typedef struct { + FuriMutex* mutex; + FHalNfcMode mode; + FHalNfcTech tech; +} FHalNfc; + +extern FHalNfc f_hal_nfc; void f_hal_nfc_event_init(); -void f_hal_nfc_set_event(FHalNfcEventInternalType event); +void f_hal_nfc_event_set(FHalNfcEventInternalType event); void f_hal_nfc_init_gpio_isr(); @@ -47,6 +55,58 @@ bool f_hal_nfc_event_wait_for_specific_irq( uint32_t mask, uint32_t timeout_ms); +// Common technology methods +FHalNfcEvent f_hal_nfc_wait_event_common(uint32_t timeout_ms); +FHalNfcError f_hal_nfc_common_listener_rx_start(FuriHalSpiBusHandle* handle); +FHalNfcError + f_hal_nfc_common_fifo_tx(FuriHalSpiBusHandle* handle, const uint8_t* tx_data, size_t tx_bits); +FHalNfcError f_hal_nfc_common_fifo_rx( + FuriHalSpiBusHandle* handle, + uint8_t* rx_data, + size_t rx_data_size, + size_t* rx_bits); + +FHalNfcError + f_hal_nfc_poller_tx_common(FuriHalSpiBusHandle* handle, const uint8_t* tx_data, size_t tx_bits); + +// Technology specific API +typedef FHalNfcError (*FHalNfcChipConfig)(FuriHalSpiBusHandle* handle); +typedef FHalNfcError ( + *FHalNfcTx)(FuriHalSpiBusHandle* handle, const uint8_t* tx_data, size_t tx_bits); +typedef FHalNfcError (*FHalNfcRx)( + FuriHalSpiBusHandle* handle, + uint8_t* rx_data, + size_t rx_data_size, + size_t* rx_bits); +typedef FHalNfcEvent (*FHalNfcWaitEvent)(uint32_t timeout_ms); + +typedef struct { + FHalNfcChipConfig init; + FHalNfcChipConfig deinit; + FHalNfcWaitEvent wait_event; + FHalNfcTx tx; + FHalNfcRx rx; +} FHalNfcTechPollerBase; + +typedef struct { + FHalNfcChipConfig init; + FHalNfcChipConfig deinit; + FHalNfcWaitEvent wait_event; + FHalNfcChipConfig rx_start; + FHalNfcTx tx; + FHalNfcRx rx; +} FHalNfcTechListenerBase; + +typedef struct { + FHalNfcTechPollerBase poller; + FHalNfcTechListenerBase listener; +} FHalNfcTechBase; + +extern const FHalNfcTechBase f_hal_nfc_iso14443a; +extern const FHalNfcTechBase f_hal_nfc_iso14443b; +extern const FHalNfcTechBase f_hal_nfc_iso15693; +extern const FHalNfcTechBase f_hal_nfc_felica; + #ifdef __cplusplus } #endif diff --git a/firmware/targets/f7/furi_hal/f_hal_nfc_irq.c b/firmware/targets/f7/furi_hal/f_hal_nfc_irq.c index f51d1370ffce..95f6c4129280 100644 --- a/firmware/targets/f7/furi_hal/f_hal_nfc_irq.c +++ b/firmware/targets/f7/furi_hal/f_hal_nfc_irq.c @@ -4,7 +4,7 @@ #include static void f_hal_nfc_int_callback() { - f_hal_nfc_set_event(FHalNfcEventInternalTypeIrq); + f_hal_nfc_event_set(FHalNfcEventInternalTypeIrq); } uint32_t f_hal_nfc_get_irq(FuriHalSpiBusHandle* handle) { diff --git a/firmware/targets/f7/furi_hal/f_hal_nfca.c b/firmware/targets/f7/furi_hal/f_hal_nfc_iso14443a.c similarity index 51% rename from firmware/targets/f7/furi_hal/f_hal_nfca.c rename to firmware/targets/f7/furi_hal/f_hal_nfc_iso14443a.c index fbd705663a16..c80f9c9960cd 100644 --- a/firmware/targets/f7/furi_hal/f_hal_nfca.c +++ b/firmware/targets/f7/furi_hal/f_hal_nfc_iso14443a.c @@ -1,15 +1,96 @@ #include #include -#include -#include -#include #include -#define TAG "FuriHalNfcA" +#include + +#define TAG "FuriHalIso14443a" static Iso14443_3aSignal* iso14443_3a_signal = NULL; +static FHalNfcError f_hal_nfc_iso14443a_common_init(FuriHalSpiBusHandle* handle) { + // Common NFC-A settings, 106 kbps + + // 1st stage zero = 600kHz, 3rd stage zero = 200 kHz + st25r3916_write_reg(handle, ST25R3916_REG_RX_CONF1, ST25R3916_REG_RX_CONF1_z600k); + // AGC enabled, ratio 3:1, squelch after TX + st25r3916_write_reg( + handle, + ST25R3916_REG_RX_CONF2, + ST25R3916_REG_RX_CONF2_agc6_3 | ST25R3916_REG_RX_CONF2_agc_m | + ST25R3916_REG_RX_CONF2_agc_en | ST25R3916_REG_RX_CONF2_sqm_dyn); + // HF operation, full gain on AM and PM channels + st25r3916_write_reg(handle, ST25R3916_REG_RX_CONF3, 0x00); + // No gain reduction on AM and PM channels + st25r3916_write_reg(handle, ST25R3916_REG_RX_CONF4, 0x00); + // Correlator config + st25r3916_write_reg( + handle, + ST25R3916_REG_CORR_CONF1, + ST25R3916_REG_CORR_CONF1_corr_s0 | ST25R3916_REG_CORR_CONF1_corr_s4 | + ST25R3916_REG_CORR_CONF1_corr_s6); + // Sleep mode disable, 424kHz mode off + st25r3916_write_reg(handle, ST25R3916_REG_CORR_CONF2, 0x00); + + return FHalNfcErrorNone; +} + +static FHalNfcError f_hal_nfc_iso14443a_poller_init(FuriHalSpiBusHandle* handle) { + // Enable ISO14443A mode, OOK modulation + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_MODE, + ST25R3916_REG_MODE_om_mask | ST25R3916_REG_MODE_tr_am, + ST25R3916_REG_MODE_om_iso14443a | ST25R3916_REG_MODE_tr_am_ook); + + // Overshoot protection - is this necessary here? + st25r3916_change_reg_bits(handle, ST25R3916_REG_OVERSHOOT_CONF1, 0xff, 0x40); + st25r3916_change_reg_bits(handle, ST25R3916_REG_OVERSHOOT_CONF2, 0xff, 0x03); + st25r3916_change_reg_bits(handle, ST25R3916_REG_UNDERSHOOT_CONF1, 0xff, 0x40); + st25r3916_change_reg_bits(handle, ST25R3916_REG_UNDERSHOOT_CONF2, 0xff, 0x03); + + return f_hal_nfc_iso14443a_common_init(handle); +} + +static FHalNfcError f_hal_nfc_iso14443a_poller_deinit(FuriHalSpiBusHandle* handle) { + UNUSED(handle); + return FHalNfcErrorNone; +} + +static FHalNfcError f_hal_nfc_iso14443a_listener_init(FuriHalSpiBusHandle* handle) { + furi_check(iso14443_3a_signal == NULL); + iso14443_3a_signal = iso14443_3a_signal_alloc(&gpio_spi_r_mosi); + + st25r3916_write_reg( + handle, + ST25R3916_REG_OP_CONTROL, + ST25R3916_REG_OP_CONTROL_en | ST25R3916_REG_OP_CONTROL_rx_en | + ST25R3916_REG_OP_CONTROL_en_fd_auto_efd); + st25r3916_write_reg( + handle, ST25R3916_REG_MODE, ST25R3916_REG_MODE_targ_targ | ST25R3916_REG_MODE_om0); + st25r3916_write_reg( + handle, + 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); + + st25r3916_write_reg(handle, ST25R3916_REG_MASK_RX_TIMER, 0x02); + + return f_hal_nfc_iso14443a_common_init(handle); +} + +static FHalNfcError f_hal_nfc_iso14443a_listener_deinit(FuriHalSpiBusHandle* handle) { + UNUSED(handle); + + if(iso14443_3a_signal) { + iso14443_3a_signal_free(iso14443_3a_signal); + iso14443_3a_signal = NULL; + } + + return FHalNfcErrorNone; +} + FHalNfcError f_hal_nfca_send_short_frame(FHalNfcaShortFrame frame) { FHalNfcError error = FHalNfcErrorNone; @@ -103,23 +184,25 @@ FHalNfcError return error; } -// TODO change this - -FHalNfcError f_hal_nfca_listener_init() { - furi_check(iso14443_3a_signal == NULL); +FHalNfcError f_hal_iso4443a_listener_tx( + FuriHalSpiBusHandle* handle, + const uint8_t* tx_data, + size_t tx_bits) { + FHalNfcError error = FHalNfcErrorNone; - iso14443_3a_signal = iso14443_3a_signal_alloc(&gpio_spi_r_mosi); + do { + error = f_hal_nfc_common_fifo_tx(handle, tx_data, tx_bits); + if(error != FHalNfcErrorNone) break; - return FHalNfcErrorNone; -} + bool tx_end = f_hal_nfc_event_wait_for_specific_irq(handle, ST25R3916_IRQ_MASK_TXE, 10); + if(!tx_end) { + error = FHalNfcErrorCommunicationTimeout; + break; + } -FHalNfcError f_hal_nfca_listener_deinit() { - if(iso14443_3a_signal) { - iso14443_3a_signal_free(iso14443_3a_signal); - iso14443_3a_signal = NULL; - } + } while(false); - return FHalNfcErrorNone; + return error; } FHalNfcError f_hal_nfca_listener_tx_custom_parity( @@ -150,3 +233,24 @@ FHalNfcError f_hal_nfca_listener_tx_custom_parity( // TODO handle field off return FHalNfcErrorNone; } + +const FHalNfcTechBase f_hal_nfc_iso14443a = { + .poller = + { + .init = f_hal_nfc_iso14443a_poller_init, + .deinit = f_hal_nfc_iso14443a_poller_deinit, + .wait_event = f_hal_nfc_wait_event_common, + .tx = f_hal_nfc_poller_tx_common, + .rx = f_hal_nfc_common_fifo_rx, + }, + + .listener = + { + .init = f_hal_nfc_iso14443a_listener_init, + .deinit = f_hal_nfc_iso14443a_listener_deinit, + .wait_event = f_hal_nfc_wait_event_common, + .rx_start = f_hal_nfc_common_listener_rx_start, + .tx = f_hal_iso4443a_listener_tx, + .rx = f_hal_nfc_common_fifo_rx, + }, +}; diff --git a/firmware/targets/f7/furi_hal/f_hal_nfc_iso14443b.c b/firmware/targets/f7/furi_hal/f_hal_nfc_iso14443b.c new file mode 100644 index 000000000000..a66efbfa8144 --- /dev/null +++ b/firmware/targets/f7/furi_hal/f_hal_nfc_iso14443b.c @@ -0,0 +1,102 @@ +#include "f_hal_nfc_i.h" + +static FHalNfcError f_hal_nfc_iso14443b_common_init(FuriHalSpiBusHandle* handle) { + // Common NFC-B settings, 106kbps + + // 1st stage zero = 60kHz, 3rd stage zero = 200 kHz + st25r3916_write_reg(handle, ST25R3916_REG_RX_CONF1, ST25R3916_REG_RX_CONF1_h200); + + // Enable AGC + // AGC Ratio 6 + // AGC algorithm with RESET (recommended for ISO14443-B) + // AGC operation during complete receive period + // Squelch ratio 6/3 (recommended for ISO14443-B) + // Squelch automatic activation on TX end + st25r3916_write_reg( + handle, + ST25R3916_REG_RX_CONF2, + ST25R3916_REG_RX_CONF2_agc6_3 | ST25R3916_REG_RX_CONF2_agc_alg | + ST25R3916_REG_RX_CONF2_agc_m | ST25R3916_REG_RX_CONF2_agc_en | + ST25R3916_REG_RX_CONF2_pulz_61 | ST25R3916_REG_RX_CONF2_sqm_dyn); + + // HF operation, full gain on AM and PM channels + st25r3916_write_reg(handle, ST25R3916_REG_RX_CONF3, 0x00); + // No gain reduction on AM and PM channels + st25r3916_write_reg(handle, ST25R3916_REG_RX_CONF4, 0x00); + + // Subcarrier end detector enabled + // Subcarrier end detection level = 66% + // BPSK start 33 pilot pulses + // AM & PM summation before digitizing on + st25r3916_write_reg( + handle, + ST25R3916_REG_CORR_CONF1, + ST25R3916_REG_CORR_CONF1_corr_s0 | ST25R3916_REG_CORR_CONF1_corr_s1 | + ST25R3916_REG_CORR_CONF1_corr_s3 | ST25R3916_REG_CORR_CONF1_corr_s4); + // Sleep mode disable, 424kHz mode off + st25r3916_write_reg(handle, ST25R3916_REG_CORR_CONF2, 0x00); + + return FHalNfcErrorNone; +} + +static FHalNfcError f_hal_nfc_iso14443b_poller_init(FuriHalSpiBusHandle* handle) { + // Enable ISO14443B mode, AM modulation + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_MODE, + ST25R3916_REG_MODE_om_mask | ST25R3916_REG_MODE_tr_am, + ST25R3916_REG_MODE_om_iso14443b | ST25R3916_REG_MODE_tr_am_am); + + // 10% ASK modulation + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_TX_DRIVER, + ST25R3916_REG_TX_DRIVER_am_mod_mask, + ST25R3916_REG_TX_DRIVER_am_mod_10percent); + + // Use regulator AM, resistive AM disabled + st25r3916_clear_reg_bits( + handle, + ST25R3916_REG_AUX_MOD, + ST25R3916_REG_AUX_MOD_dis_reg_am | ST25R3916_REG_AUX_MOD_res_am); + + // EGT = 0 etu + // SOF = 10 etu LOW + 2 etu HIGH + // EOF = 10 etu + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_ISO14443B_1, + ST25R3916_REG_ISO14443B_1_egt_mask | ST25R3916_REG_ISO14443B_1_sof_mask | + ST25R3916_REG_ISO14443B_1_eof, + (0U << ST25R3916_REG_ISO14443B_1_egt_shift) | ST25R3916_REG_ISO14443B_1_sof_0_10etu | + ST25R3916_REG_ISO14443B_1_sof_1_2etu | ST25R3916_REG_ISO14443B_1_eof_10etu); + + // TR1 = 80 / fs + // B' mode off (no_sof & no_eof = 0) + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_ISO14443B_2, + ST25R3916_REG_ISO14443B_2_tr1_mask | ST25R3916_REG_ISO14443B_2_no_sof | + ST25R3916_REG_ISO14443B_2_no_eof, + ST25R3916_REG_ISO14443B_2_tr1_80fs80fs); + + return f_hal_nfc_iso14443b_common_init(handle); +} + +static FHalNfcError f_hal_nfc_iso14443b_poller_deinit(FuriHalSpiBusHandle* handle) { + UNUSED(handle); + return FHalNfcErrorNone; +} + +const FHalNfcTechBase f_hal_nfc_iso14443b = { + .poller = + { + .init = f_hal_nfc_iso14443b_poller_init, + .deinit = f_hal_nfc_iso14443b_poller_deinit, + .wait_event = f_hal_nfc_wait_event_common, + .tx = f_hal_nfc_poller_tx_common, + .rx = f_hal_nfc_common_fifo_rx, + }, + + .listener = {0}, +}; diff --git a/firmware/targets/f7/furi_hal/f_hal_nfc_iso15693.c b/firmware/targets/f7/furi_hal/f_hal_nfc_iso15693.c new file mode 100644 index 000000000000..9cffba652cfb --- /dev/null +++ b/firmware/targets/f7/furi_hal/f_hal_nfc_iso15693.c @@ -0,0 +1,434 @@ +#include "f_hal_nfc_i.h" + +#include +#include + +#include + +#define F_HAL_NFC_ISO15693_MAX_FRAME_SIZE (1024U) +#define F_HAL_NFC_ISO15693_POLLER_MAX_BUFFER_SIZE (64) + +#define F_HAL_NFC_ISO15693_RESP_SOF_SIZE (5) +#define F_HAL_NFC_ISO15693_RESP_EOF_SIZE (5) +#define F_HAL_NFC_ISO15693_RESP_SOF_MASK (0x1FU) +#define F_HAL_NFC_ISO15693_RESP_SOF_PATTERN (0x17U) +#define F_HAL_NFC_ISO15693_RESP_EOF_PATTERN (0x1DU) + +#define F_HAL_NFC_ISO15693_RESP_PATTERN_MASK (0x03U) +#define F_HAL_NFC_ISO15693_RESP_PATTERN_0 (0x01U) +#define F_HAL_NFC_ISO15693_RESP_PATTERN_1 (0x02U) + +#define BITS_IN_BYTE (8U) + +#define TAG "FuriHalIso15693" + +typedef struct { + Iso15693Signal* signal; + Iso15693Parser* parser; +} FHalNfcIso15693Listener; + +typedef struct { + // 4 bits per data bit on transmit + uint8_t fifo_buf[F_HAL_NFC_ISO15693_POLLER_MAX_BUFFER_SIZE * 4]; + size_t fifo_buf_bits; + uint8_t frame_buf[F_HAL_NFC_ISO15693_POLLER_MAX_BUFFER_SIZE * 2]; + size_t frame_buf_bits; +} FHalNfcIso15693Poller; + +static FHalNfcIso15693Listener* f_hal_nfc_iso15693_listener = NULL; +static FHalNfcIso15693Poller* f_hal_nfc_iso15693_poller = NULL; + +static FHalNfcIso15693Listener* f_hal_nfc_iso15693_listener_alloc() { + FHalNfcIso15693Listener* instance = malloc(sizeof(FHalNfcIso15693Listener)); + + instance->signal = iso15693_signal_alloc(&gpio_spi_r_mosi); + instance->parser = iso15693_parser_alloc(&gpio_spi_r_miso, F_HAL_NFC_ISO15693_MAX_FRAME_SIZE); + + return instance; +} + +static void f_hal_nfc_iso15693_listener_free(FHalNfcIso15693Listener* instance) { + furi_assert(instance); + + iso15693_signal_free(instance->signal); + iso15693_parser_free(instance->parser); + + free(instance); +} + +static FHalNfcIso15693Poller* f_hal_nfc_iso15693_poller_alloc() { + FHalNfcIso15693Poller* instance = malloc(sizeof(FHalNfcIso15693Poller)); + + return instance; +} + +static void f_hal_nfc_iso15693_poller_free(FHalNfcIso15693Poller* instance) { + furi_assert(instance); + + free(instance); +} + +static FHalNfcError f_hal_nfc_iso15693_common_init(FuriHalSpiBusHandle* handle) { + // Common NFC-V settings, 26.48 kbps + + // 1st stage zero = 12 kHz, 3rd stage zero = 80 kHz, low-pass = 600 kHz + st25r3916_write_reg( + handle, + ST25R3916_REG_RX_CONF1, + ST25R3916_REG_RX_CONF1_z12k | ST25R3916_REG_RX_CONF1_h80 | + ST25R3916_REG_RX_CONF1_lp_600khz); + + // Enable AGC + // AGC Ratio 6 + // AGC algorithm with RESET (recommended for ISO15693) + // AGC operation during complete receive period + // Squelch automatic activation on TX end + st25r3916_write_reg( + handle, + ST25R3916_REG_RX_CONF2, + ST25R3916_REG_RX_CONF2_agc6_3 | ST25R3916_REG_RX_CONF2_agc_m | + ST25R3916_REG_RX_CONF2_agc_en | ST25R3916_REG_RX_CONF2_sqm_dyn); + + // HF operation, full gain on AM and PM channels + st25r3916_write_reg(handle, ST25R3916_REG_RX_CONF3, 0x00); + // No gain reduction on AM and PM channels + st25r3916_write_reg(handle, ST25R3916_REG_RX_CONF4, 0x00); + + // Collision detection level 53% + // AM & PM summation before digitizing on + st25r3916_write_reg( + handle, + ST25R3916_REG_CORR_CONF1, + ST25R3916_REG_CORR_CONF1_corr_s0 | ST25R3916_REG_CORR_CONF1_corr_s1 | + ST25R3916_REG_CORR_CONF1_corr_s4); + // 424 kHz subcarrier stream mode on + st25r3916_write_reg(handle, ST25R3916_REG_CORR_CONF2, ST25R3916_REG_CORR_CONF2_corr_s8); + return FHalNfcErrorNone; +} + +static FHalNfcError f_hal_nfc_iso15693_poller_init(FuriHalSpiBusHandle* handle) { + furi_assert(f_hal_nfc_iso15693_poller == NULL); + + f_hal_nfc_iso15693_poller = f_hal_nfc_iso15693_poller_alloc(); + + // Enable Subcarrier Stream mode, OOK modulation + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_MODE, + ST25R3916_REG_MODE_om_mask | ST25R3916_REG_MODE_tr_am, + ST25R3916_REG_MODE_om_subcarrier_stream | ST25R3916_REG_MODE_tr_am_ook); + + // Subcarrier 424 kHz mode + // 8 sub-carrier pulses in report period + st25r3916_write_reg( + handle, + ST25R3916_REG_STREAM_MODE, + ST25R3916_REG_STREAM_MODE_scf_sc424 | ST25R3916_REG_STREAM_MODE_stx_106 | + ST25R3916_REG_STREAM_MODE_scp_8pulses); + + // Use regulator AM, resistive AM disabled + st25r3916_clear_reg_bits( + handle, + ST25R3916_REG_AUX_MOD, + ST25R3916_REG_AUX_MOD_dis_reg_am | ST25R3916_REG_AUX_MOD_res_am); + + return f_hal_nfc_iso15693_common_init(handle); +} + +static FHalNfcError f_hal_nfc_iso15693_poller_deinit(FuriHalSpiBusHandle* handle) { + UNUSED(handle); + furi_assert(f_hal_nfc_iso15693_poller); + + f_hal_nfc_iso15693_poller_free(f_hal_nfc_iso15693_poller); + f_hal_nfc_iso15693_poller = NULL; + + return FHalNfcErrorNone; +} + +static void iso15693_3_poller_encode_frame( + const uint8_t* tx_data, + size_t tx_bits, + uint8_t* frame_buf, + size_t frame_buf_size, + size_t* frame_buf_bits) { + static const uint8_t bit_patterns_1_out_of_4[] = {0x02, 0x08, 0x20, 0x80}; + size_t frame_buf_size_calc = (tx_bits / 2) + 2; + furi_assert(frame_buf_size >= frame_buf_size_calc); + + // Add SOF 1 out of 4 + frame_buf[0] = 0x21; + + size_t byte_pos = 1; + for(size_t i = 0; i < tx_bits / BITS_IN_BYTE; ++i) { + for(size_t j = 0; j < BITS_IN_BYTE; j += (BITS_IN_BYTE) / 4) { + const uint8_t bit_pair = (tx_data[i] >> j) & 0x03; + frame_buf[byte_pos++] = bit_patterns_1_out_of_4[bit_pair]; + } + } + // Add EOF + frame_buf[byte_pos++] = 0x04; + *frame_buf_bits = byte_pos * BITS_IN_BYTE; +} + +static bool iso15693_3_poller_decode_frame( + const uint8_t* buf, + size_t buf_bits, + uint8_t* buf_decoded, + size_t buf_decoded_size, + size_t* buf_decoded_bits) { + bool decoded = false; + size_t bit_pos = 0; + memset(buf_decoded, 0, buf_decoded_size); + + do { + if(buf_bits == 0) break; + // Check SOF + if((buf[0] & F_HAL_NFC_ISO15693_RESP_SOF_MASK) != F_HAL_NFC_ISO15693_RESP_SOF_PATTERN) + break; + + // 2 response bits = 1 data bit + for(uint32_t i = F_HAL_NFC_ISO15693_RESP_SOF_SIZE; + i < buf_bits - F_HAL_NFC_ISO15693_RESP_SOF_SIZE; + i += BITS_IN_BYTE / 4) { + const size_t byte_index = i / BITS_IN_BYTE; + const size_t bit_offset = i % BITS_IN_BYTE; + const uint8_t resp_byte = (buf[byte_index] >> bit_offset) | + (buf[byte_index + 1] << (BITS_IN_BYTE - bit_offset)); + + // Check EOF + if(resp_byte == F_HAL_NFC_ISO15693_RESP_EOF_PATTERN) { + decoded = true; + break; + } + + const uint8_t bit_pattern = resp_byte & F_HAL_NFC_ISO15693_RESP_PATTERN_MASK; + + if(bit_pattern == F_HAL_NFC_ISO15693_RESP_PATTERN_0) { + bit_pos++; + } else if(bit_pattern == F_HAL_NFC_ISO15693_RESP_PATTERN_1) { + buf_decoded[bit_pos / BITS_IN_BYTE] |= 1 << (bit_pos % BITS_IN_BYTE); + bit_pos++; + } else { + break; + } + if(bit_pos / BITS_IN_BYTE > buf_decoded_size) { + break; + } + } + + } while(false); + + if(decoded) { + *buf_decoded_bits = bit_pos; + } + + return decoded; +} + +static FHalNfcError f_hal_nfc_iso15693_poller_tx( + FuriHalSpiBusHandle* handle, + const uint8_t* tx_data, + size_t tx_bits) { + FHalNfcIso15693Poller* instance = f_hal_nfc_iso15693_poller; + iso15693_3_poller_encode_frame( + tx_data, + tx_bits, + instance->frame_buf, + sizeof(instance->frame_buf), + &instance->frame_buf_bits); + return f_hal_nfc_poller_tx_common(handle, instance->frame_buf, instance->frame_buf_bits); +} + +static FHalNfcError f_hal_nfc_iso15693_poller_rx( + FuriHalSpiBusHandle* handle, + uint8_t* rx_data, + size_t rx_data_size, + size_t* rx_bits) { + FHalNfcError error = FHalNfcErrorNone; + FHalNfcIso15693Poller* instance = f_hal_nfc_iso15693_poller; + + do { + error = f_hal_nfc_common_fifo_rx( + handle, instance->fifo_buf, sizeof(instance->fifo_buf), &instance->fifo_buf_bits); + if(error != FHalNfcErrorNone) break; + + if(!iso15693_3_poller_decode_frame( + instance->fifo_buf, + instance->fifo_buf_bits, + instance->frame_buf, + sizeof(instance->frame_buf), + &instance->frame_buf_bits)) { + error = FHalNfcErrorDataFormat; + break; + } + if(rx_data_size < instance->frame_buf_bits / BITS_IN_BYTE) { + error = FHalNfcErrorBufferOverflow; + break; + } + + memcpy(rx_data, instance->frame_buf, instance->frame_buf_bits / BITS_IN_BYTE); + *rx_bits = instance->frame_buf_bits; + } while(false); + + return error; +} + +static FHalNfcError f_hal_nfc_iso15693_listener_init(FuriHalSpiBusHandle* handle) { + furi_assert(f_hal_nfc_iso15693_listener == NULL); + + f_hal_nfc_iso15693_listener = f_hal_nfc_iso15693_listener_alloc(); + + // Set default operation mode + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_MODE, + ST25R3916_REG_MODE_om_mask | ST25R3916_REG_MODE_tr_am, + ST25R3916_REG_MODE_om_targ_nfca | ST25R3916_REG_MODE_tr_am_ook); + + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_OP_CONTROL, + ST25R3916_REG_OP_CONTROL_rx_en, + ST25R3916_REG_OP_CONTROL_rx_en); + + // Enable passive target mode + st25r3916_change_reg_bits( + handle, ST25R3916_REG_MODE, ST25R3916_REG_MODE_targ, ST25R3916_REG_MODE_targ_targ); + + return f_hal_nfc_iso15693_common_init(handle); +} + +static FHalNfcError f_hal_nfc_iso15693_listener_deinit(FuriHalSpiBusHandle* handle) { + UNUSED(handle); + furi_assert(f_hal_nfc_iso15693_listener); + + f_hal_nfc_iso15693_listener_free(f_hal_nfc_iso15693_listener); + f_hal_nfc_iso15693_listener = NULL; + + return FHalNfcErrorNone; +} + +static void f_hal_nfc_iso15693_listener_transparent_mode_enter(FuriHalSpiBusHandle* handle) { + st25r3916_direct_cmd(handle, ST25R3916_CMD_TRANSPARENT_MODE); + + furi_hal_spi_bus_handle_deinit(handle); + f_hal_nfc_deinit_gpio_isr(); +} + +static void f_hal_nfc_iso15693_listener_transparent_mode_exit(FuriHalSpiBusHandle* handle) { + // Configure gpio back to SPI and exit transparent mode + f_hal_nfc_init_gpio_isr(); + furi_hal_spi_bus_handle_init(handle); + + st25r3916_direct_cmd(handle, ST25R3916_CMD_UNMASK_RECEIVE_DATA); +} + +static FHalNfcError + f_hal_nfc_iso15693_listener_tx_transparent(const uint8_t* data, size_t data_size) { + iso15693_signal_tx( + f_hal_nfc_iso15693_listener->signal, Iso15693SignalDataRateHi, data, data_size); + + return FHalNfcErrorNone; +} + +static FHalNfcError f_hal_nfc_iso15693_listener_rx_start(FuriHalSpiBusHandle* handle) { + UNUSED(handle); + return FHalNfcErrorNone; +} + +static void f_hal_nfc_iso15693_parser_callback(Iso15693ParserEvent event, void* context) { + furi_assert(context); + + if(event == Iso15693ParserEventDataReceived) { + FuriThreadId thread_id = context; + furi_thread_flags_set(thread_id, FHalNfcEventInternalTypeTransparentDataReceived); + } +} + +static FHalNfcEvent f_hal_nfc_iso15693_wait_event(uint32_t timeout_ms) { + FHalNfcEvent event = 0; + FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + + f_hal_nfc_iso15693_listener_transparent_mode_enter(handle); + FuriThreadId thread_id = furi_thread_get_current_id(); + iso15693_parser_start( + f_hal_nfc_iso15693_listener->parser, f_hal_nfc_iso15693_parser_callback, thread_id); + + while(true) { + uint32_t flag = furi_thread_flags_wait( + FHalNfcEventInternalTypeAbort | FHalNfcEventInternalTypeTransparentDataReceived, + FuriFlagWaitAny, + timeout_ms); + furi_thread_flags_clear(flag); + + if(flag & FHalNfcEventInternalTypeAbort) { + event = FHalNfcEventAbortRequest; + break; + } + if(flag & FHalNfcEventInternalTypeTransparentDataReceived) { + if(iso15693_parser_run(f_hal_nfc_iso15693_listener->parser)) { + event = FHalNfcEventRxEnd; + break; + } + } + } + + iso15693_parser_stop(f_hal_nfc_iso15693_listener->parser); + f_hal_nfc_iso15693_listener_transparent_mode_exit(handle); + + return event; +} + +static FHalNfcError f_hal_nfc_iso15693_listener_tx( + FuriHalSpiBusHandle* handle, + const uint8_t* tx_data, + size_t tx_bits) { + furi_assert(f_hal_nfc_iso15693_listener); + + FHalNfcError error = FHalNfcErrorNone; + f_hal_nfc_iso15693_listener_transparent_mode_enter(handle); + + error = f_hal_nfc_iso15693_listener_tx_transparent(tx_data, tx_bits / BITS_IN_BYTE); + + f_hal_nfc_iso15693_listener_transparent_mode_exit(handle); + + return error; +} + +static FHalNfcError f_hal_nfc_iso15693_listener_rx( + FuriHalSpiBusHandle* handle, + uint8_t* rx_data, + size_t rx_data_size, + size_t* rx_bits) { + furi_assert(f_hal_nfc_iso15693_listener); + UNUSED(handle); + + if(rx_data_size < iso15693_parser_get_data_size_bytes(f_hal_nfc_iso15693_listener->parser)) { + return FHalNfcErrorBufferOverflow; + } + + iso15693_parser_get_data(f_hal_nfc_iso15693_listener->parser, rx_data, rx_data_size, rx_bits); + + return FHalNfcErrorNone; +} + +const FHalNfcTechBase f_hal_nfc_iso15693 = { + .poller = + { + .init = f_hal_nfc_iso15693_poller_init, + .deinit = f_hal_nfc_iso15693_poller_deinit, + .wait_event = f_hal_nfc_wait_event_common, + .tx = f_hal_nfc_iso15693_poller_tx, + .rx = f_hal_nfc_iso15693_poller_rx, + }, + + .listener = + { + .init = f_hal_nfc_iso15693_listener_init, + .deinit = f_hal_nfc_iso15693_listener_deinit, + .wait_event = f_hal_nfc_iso15693_wait_event, + .rx_start = f_hal_nfc_iso15693_listener_rx_start, + .tx = f_hal_nfc_iso15693_listener_tx, + .rx = f_hal_nfc_iso15693_listener_rx, + }, +}; diff --git a/firmware/targets/f7/furi_hal/f_hal_nfc_timer.c b/firmware/targets/f7/furi_hal/f_hal_nfc_timer.c index 1055a16d08f3..be6c3b1d59be 100644 --- a/firmware/targets/f7/furi_hal/f_hal_nfc_timer.c +++ b/firmware/targets/f7/furi_hal/f_hal_nfc_timer.c @@ -57,7 +57,7 @@ static void f_hal_nfc_timer_irq_callback(void* context) { FHalNfcTimerConfig* timer = context; if(LL_TIM_IsActiveFlag_UPDATE(timer->timer)) { LL_TIM_ClearFlag_UPDATE(timer->timer); - f_hal_nfc_set_event(timer->event); + f_hal_nfc_event_set(timer->event); furi_hal_gpio_write(timer->pin, false); } } diff --git a/firmware/targets/f7/target.json b/firmware/targets/f7/target.json index 0c083b68ddba..f58200c4688d 100644 --- a/firmware/targets/f7/target.json +++ b/firmware/targets/f7/target.json @@ -30,6 +30,7 @@ "nfc", "digital_signal", "pulse_reader", + "signal_reader", "microtar", "usb_stm32", "st25rfal002", diff --git a/firmware/targets/furi_hal_include/f_hal_nfc.h b/firmware/targets/furi_hal_include/f_hal_nfc.h index d8cd41141781..f7af6c19df91 100644 --- a/firmware/targets/furi_hal_include/f_hal_nfc.h +++ b/firmware/targets/furi_hal_include/f_hal_nfc.h @@ -38,28 +38,24 @@ typedef enum { FHalNfcErrorIsrTimeout, FHalNfcErrorCommunicationTimeout, FHalNfcErrorBufferOverflow, + FHalNfcErrorDataFormat, } FHalNfcError; typedef enum { - FHalNfcModeIso14443aPoller, - FHalNfcModeIso14443aListener, - - FHalNfcModeIso14443bPoller, - FHalNfcModeIso14443bListener, - - FHalNfcModeFelicaPoller, - FHalNfcModeNfcfListener, - - FHalNfcModeIso15693Poller, - FHalNfcModeIso15693Listener, + FHalNfcModePoller, + FHalNfcModeListener, FHalNfcModeNum, } FHalNfcMode; typedef enum { - FHalNfcBitrate26p48, - FHalNfcBitrate106, -} FHalNfcBitrate; + FHalNfcTechIso14443a, + FHalNfcTechIso14443b, + FHalNfcTechIso15693, + FHalNfcTechFelica, + + FHalNfcTechNum, +} FHalNfcTech; typedef enum { FHalNfcaShortFrameAllReq, @@ -97,7 +93,7 @@ FHalNfcError f_hal_nfc_low_power_mode_stop(); * * @return FHalNfcError */ -FHalNfcError f_hal_nfc_set_mode(FHalNfcMode mode, FHalNfcBitrate bitrate); +FHalNfcError f_hal_nfc_set_mode(FHalNfcMode mode, FHalNfcTech tech); FHalNfcError f_hal_nfc_reset_mode(); @@ -112,7 +108,9 @@ FHalNfcError f_hal_nfc_acquire(); FHalNfcError f_hal_nfc_release(); -FHalNfcError f_hal_nfc_event_start(); +FHalNfcEvent f_hal_nfc_poller_wait_event(uint32_t timeout_ms); + +FHalNfcEvent f_hal_nfc_listener_wait_event(uint32_t timeout_ms); FHalNfcError f_hal_nfc_poller_tx(const uint8_t* tx_data, size_t tx_bits); @@ -122,19 +120,23 @@ FHalNfcError f_hal_nfc_poller_rx(uint8_t* rx_data, size_t rx_data_size, size_t* FHalNfcError f_hal_nfc_listener_tx(const uint8_t* tx_data, size_t tx_bits); -FHalNfcError f_hal_nfc_trx_reset(); +FHalNfcError f_hal_nfc_listener_rx_start(); -FHalNfcError f_hal_nfc_listen_start(); +FHalNfcError f_hal_nfc_listener_rx(uint8_t* rx_data, size_t rx_data_size, size_t* rx_bits); -FHalNfcError f_hal_nfc_listen_reset(); +FHalNfcError f_hal_nfc_listener_start(); + +FHalNfcError f_hal_nfc_listener_reset(); FHalNfcError f_hal_nfc_listener_sleep(); FHalNfcError f_hal_nfc_listener_disable_auto_col_res(); +FHalNfcError f_hal_nfc_trx_reset(); + FHalNfcError f_hal_nfc_abort(); -FHalNfcEvent f_hal_nfc_wait_event(uint32_t timeout_ms); +FHalNfcError f_hal_nfc_event_start(); void f_hal_nfc_timer_fwt_start(uint32_t time_fc); @@ -158,11 +160,6 @@ FHalNfcError f_hal_nfca_send_sdd_frame(const uint8_t* tx_data, size_t tx_bits); FHalNfcError f_hal_nfca_receive_sdd_frame(uint8_t* rx_data, size_t rx_data_size, size_t* rx_bits); -// TODO virtual methods? -FHalNfcError f_hal_nfca_listener_init(); - -FHalNfcError f_hal_nfca_listener_deinit(); - FHalNfcError furi_hal_nfca_set_col_res_data(uint8_t* uid, uint8_t uid_len, uint8_t* atqa, uint8_t sak); diff --git a/lib/SConscript b/lib/SConscript index ab78c6ea4d44..9dfa35671a3b 100644 --- a/lib/SConscript +++ b/lib/SConscript @@ -5,6 +5,7 @@ env.Append( Dir("app-scened-template"), Dir("digital_signal"), Dir("pulse_reader"), + Dir("signal_reader"), Dir("drivers"), Dir("flipper_format"), Dir("infrared"), @@ -97,6 +98,7 @@ libs = env.BuildModules( "nfc", "digital_signal", "pulse_reader", + "signal_reader", "appframe", "misc", "lfrfid", diff --git a/lib/digital_signal/presets/nfc/iso15693_signal.c b/lib/digital_signal/presets/nfc/iso15693_signal.c new file mode 100644 index 000000000000..d140c7593c68 --- /dev/null +++ b/lib/digital_signal/presets/nfc/iso15693_signal.c @@ -0,0 +1,196 @@ +#include "iso15693_signal.h" + +#include + +#define BITS_IN_BYTE (8U) + +#define ISO15693_SIGNAL_COEFF_HI (1U) +#define ISO15693_SIGNAL_COEFF_LO (4U) + +#define ISO15693_SIGNAL_ZERO_EDGES (16U) +#define ISO15693_SIGNAL_ONE_EDGES (ISO15693_SIGNAL_ZERO_EDGES + 1U) +#define ISO15693_SIGNAL_EOF_EDGES (64U) +#define ISO15693_SIGNAL_SOF_EDGES (ISO15693_SIGNAL_EOF_EDGES + 1U) +#define ISO15693_SIGNAL_EDGES (1350U) + +#define ISO15693_SIGNAL_FC (13.56e6) +#define ISO15693_SIGNAL_FC_16 (16.0e11 / ISO15693_SIGNAL_FC) +#define ISO15693_SIGNAL_FC_256 (256.0e11 / ISO15693_SIGNAL_FC) +#define ISO15693_SIGNAL_FC_768 (768.0e11 / ISO15693_SIGNAL_FC) + +typedef enum { + Iso15693SignalIndexSof, + Iso15693SignalIndexEof, + Iso15693SignalIndexOne, + Iso15693SignalIndexZero, + Iso15693SignalIndexNum, +} Iso15693SignalIndex; + +typedef DigitalSignal* Iso15693SignalBank[Iso15693SignalIndexNum]; + +struct Iso15693Signal { + DigitalSequence* tx_sequence; + Iso15693SignalBank banks[Iso15693SignalDataRateNum]; +}; + +// Add an unmodulated signal for the length of Fc / 256 * k (where k = 1 or 4) +static void iso15693_add_silence(DigitalSignal* signal, Iso15693SignalDataRate data_rate) { + const uint32_t k = data_rate == Iso15693SignalDataRateHi ? ISO15693_SIGNAL_COEFF_HI : + ISO15693_SIGNAL_COEFF_LO; + digital_signal_add_pulse(signal, ISO15693_SIGNAL_FC_256 * k, false); +} + +// Add 8 * k subcarrier pulses of Fc / 16 (where k = 1 or 4) +static void iso15693_add_subcarrier(DigitalSignal* signal, Iso15693SignalDataRate data_rate) { + const uint32_t k = data_rate == Iso15693SignalDataRateHi ? ISO15693_SIGNAL_COEFF_HI : + ISO15693_SIGNAL_COEFF_LO; + for(uint32_t i = 0; i < ISO15693_SIGNAL_ZERO_EDGES * k; ++i) { + digital_signal_add_pulse(signal, ISO15693_SIGNAL_FC_16, !(i % 2)); + } +} + +static void iso15693_add_bit(DigitalSignal* signal, Iso15693SignalDataRate data_rate, bool bit) { + if(bit) { + iso15693_add_silence(signal, data_rate); + iso15693_add_subcarrier(signal, data_rate); + } else { + iso15693_add_subcarrier(signal, data_rate); + iso15693_add_silence(signal, data_rate); + } +} + +static inline void iso15693_add_sof(DigitalSignal* signal, Iso15693SignalDataRate data_rate) { + for(uint32_t i = 0; i < ISO15693_SIGNAL_FC_768 / ISO15693_SIGNAL_FC_256; ++i) { + iso15693_add_silence(signal, data_rate); + } + + for(uint32_t i = 0; i < ISO15693_SIGNAL_FC_768 / ISO15693_SIGNAL_FC_256; ++i) { + iso15693_add_subcarrier(signal, data_rate); + } + + iso15693_add_bit(signal, data_rate, true); +} + +static inline void iso15693_add_eof(DigitalSignal* signal, Iso15693SignalDataRate data_rate) { + iso15693_add_bit(signal, data_rate, false); + + for(uint32_t i = 0; i < ISO15693_SIGNAL_FC_768 / ISO15693_SIGNAL_FC_256; ++i) { + iso15693_add_subcarrier(signal, data_rate); + } + + for(uint32_t i = 0; i < ISO15693_SIGNAL_FC_768 / ISO15693_SIGNAL_FC_256; ++i) { + iso15693_add_silence(signal, data_rate); + } +} + +static inline uint32_t + iso15693_get_sequence_index(Iso15693SignalIndex index, Iso15693SignalDataRate data_rate) { + return index + data_rate * Iso15693SignalIndexNum; +} + +static inline void + iso15693_add_byte(Iso15693Signal* instance, Iso15693SignalDataRate data_rate, uint8_t byte) { + for(size_t i = 0; i < BITS_IN_BYTE; i++) { + const uint8_t bit = byte & (1U << i); + digital_sequence_add( + instance->tx_sequence, + iso15693_get_sequence_index( + bit ? Iso15693SignalIndexOne : Iso15693SignalIndexZero, data_rate)); + } +} + +static inline void iso15693_signal_encode( + Iso15693Signal* instance, + Iso15693SignalDataRate data_rate, + const uint8_t* tx_data, + size_t tx_data_size) { + digital_sequence_add( + instance->tx_sequence, iso15693_get_sequence_index(Iso15693SignalIndexSof, data_rate)); + + for(size_t i = 0; i < tx_data_size; i++) { + iso15693_add_byte(instance, data_rate, tx_data[i]); + } + + digital_sequence_add( + instance->tx_sequence, iso15693_get_sequence_index(Iso15693SignalIndexEof, data_rate)); +} + +static void iso15693_signal_bank_fill(Iso15693Signal* instance, Iso15693SignalDataRate data_rate) { + const uint32_t k = data_rate == Iso15693SignalDataRateHi ? ISO15693_SIGNAL_COEFF_HI : + ISO15693_SIGNAL_COEFF_LO; + DigitalSignal** bank = instance->banks[data_rate]; + + // FIXME: possibly a couple of wasted edges when k > 1 + bank[Iso15693SignalIndexSof] = digital_signal_alloc(ISO15693_SIGNAL_SOF_EDGES * k); + bank[Iso15693SignalIndexEof] = digital_signal_alloc(ISO15693_SIGNAL_EOF_EDGES * k); + bank[Iso15693SignalIndexOne] = digital_signal_alloc(ISO15693_SIGNAL_ONE_EDGES * k); + bank[Iso15693SignalIndexZero] = digital_signal_alloc(ISO15693_SIGNAL_ZERO_EDGES * k); + + iso15693_add_sof(bank[Iso15693SignalIndexSof], data_rate); + iso15693_add_eof(bank[Iso15693SignalIndexEof], data_rate); + iso15693_add_bit(bank[Iso15693SignalIndexOne], data_rate, true); + iso15693_add_bit(bank[Iso15693SignalIndexZero], data_rate, false); +} + +static void + iso15693_signal_bank_clear(Iso15693Signal* instance, Iso15693SignalDataRate data_rate) { + DigitalSignal** bank = instance->banks[data_rate]; + + for(uint32_t i = 0; i < Iso15693SignalIndexNum; ++i) { + digital_signal_free(bank[i]); + } +} + +static void + iso15693_signal_bank_register(Iso15693Signal* instance, Iso15693SignalDataRate data_rate) { + for(uint32_t i = 0; i < Iso15693SignalIndexNum; ++i) { + digital_sequence_set_signal( + instance->tx_sequence, + iso15693_get_sequence_index(i, data_rate), + instance->banks[data_rate][i]); + } +} + +Iso15693Signal* iso15693_signal_alloc(const GpioPin* pin) { + furi_assert(pin); + + Iso15693Signal* instance = malloc(sizeof(Iso15693Signal)); + + instance->tx_sequence = digital_sequence_alloc(BITS_IN_BYTE * 255 + 2, pin); + + for(uint32_t i = 0; i < Iso15693SignalDataRateNum; ++i) { + iso15693_signal_bank_fill(instance, i); + iso15693_signal_bank_register(instance, i); + } + + return instance; +} + +void iso15693_signal_free(Iso15693Signal* instance) { + furi_assert(instance); + + digital_sequence_free(instance->tx_sequence); + + for(uint32_t i = 0; i < Iso15693SignalDataRateNum; ++i) { + iso15693_signal_bank_clear(instance, i); + } + + free(instance); +} + +void iso15693_signal_tx( + Iso15693Signal* instance, + Iso15693SignalDataRate data_rate, + const uint8_t* tx_data, + size_t tx_data_size) { + furi_assert(instance); + furi_assert(data_rate < Iso15693SignalDataRateNum); + furi_assert(tx_data); + furi_assert(tx_data_size / BITS_IN_BYTE); + + FURI_CRITICAL_ENTER(); + digital_sequence_clear(instance->tx_sequence); + iso15693_signal_encode(instance, data_rate, tx_data, tx_data_size); + digital_sequence_send(instance->tx_sequence); + FURI_CRITICAL_EXIT(); +} diff --git a/lib/digital_signal/presets/nfc/iso15693_signal.h b/lib/digital_signal/presets/nfc/iso15693_signal.h new file mode 100644 index 000000000000..e5f2c3ca7fb2 --- /dev/null +++ b/lib/digital_signal/presets/nfc/iso15693_signal.h @@ -0,0 +1,32 @@ +#pragma once + +#include + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct Iso15693Signal Iso15693Signal; + +typedef enum { + Iso15693SignalDataRateHi, + Iso15693SignalDataRateLo, + Iso15693SignalDataRateNum, +} Iso15693SignalDataRate; + +Iso15693Signal* iso15693_signal_alloc(const GpioPin* pin); + +void iso15693_signal_free(Iso15693Signal* instance); + +void iso15693_signal_tx( + Iso15693Signal* instance, + Iso15693SignalDataRate data_rate, + const uint8_t* tx_data, + size_t tx_data_size); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/helpers/iso13239_crc.h b/lib/nfc/helpers/iso13239_crc.h index 7de2cd8a0f6c..c71ec6befe49 100644 --- a/lib/nfc/helpers/iso13239_crc.h +++ b/lib/nfc/helpers/iso13239_crc.h @@ -3,7 +3,7 @@ #include #include -#include "bit_buffer.h" +#include #ifdef __cplusplus extern "C" { diff --git a/lib/nfc/helpers/iso14443_crc.h b/lib/nfc/helpers/iso14443_crc.h index c9ad5397fc3e..14a63841e71f 100644 --- a/lib/nfc/helpers/iso14443_crc.h +++ b/lib/nfc/helpers/iso14443_crc.h @@ -3,7 +3,7 @@ #include #include -#include "bit_buffer.h" +#include #ifdef __cplusplus extern "C" { diff --git a/lib/nfc/nfc.c b/lib/nfc/nfc.c index ea620fe26057..e8b5f502b021 100644 --- a/lib/nfc/nfc.c +++ b/lib/nfc/nfc.c @@ -12,7 +12,7 @@ typedef enum { NfcStateIdle, NfcStateFieldOn, - NfcStateListenStarted, + NfcStateListenerStarted, NfcStatePollerReady, NfcStatePollerReset, } NfcState; @@ -82,8 +82,8 @@ static int32_t nfc_worker_listener(void* context) { furi_assert(instance->callback); furi_assert(instance->config_state == NfcConfigurationStateDone); - f_hal_nfc_listen_start(); - instance->state = NfcStateListenStarted; + f_hal_nfc_listener_start(); + instance->state = NfcStateListenerStarted; f_hal_nfc_event_start(); @@ -93,15 +93,17 @@ static int32_t nfc_worker_listener(void* context) { NfcCommand command = NfcCommandContinue; while(true) { - FHalNfcEvent event = f_hal_nfc_wait_event(F_HAL_NFC_EVENT_WAIT_FOREVER); + FHalNfcEvent event = f_hal_nfc_listener_wait_event(F_HAL_NFC_EVENT_WAIT_FOREVER); if(event & FHalNfcEventAbortRequest) { nfc_event.type = NfcEventTypeUserAbort; instance->callback(nfc_event, instance->context); break; } if(event & FHalNfcEventFieldOn) { + // FURI_LOG_D(TAG, "Field ON"); nfc_event.type = NfcEventTypeFieldOn; instance->callback(nfc_event, instance->context); + f_hal_nfc_listener_rx_start(); } if(event & FHalNfcEventFieldOff) { nfc_event.type = NfcEventTypeFieldOff; @@ -115,7 +117,7 @@ static int32_t nfc_worker_listener(void* context) { } if(event & FHalNfcEventRxEnd) { nfc_event.type = NfcEventTypeRxEnd; - f_hal_nfc_poller_rx( + f_hal_nfc_listener_rx( instance->rx_buffer, sizeof(instance->rx_buffer), &instance->rx_bits); bit_buffer_copy_bits(event_data.buffer, instance->rx_buffer, instance->rx_bits); command = instance->callback(nfc_event, instance->context); @@ -147,7 +149,7 @@ bool nfc_worker_poller_start_handler(Nfc* instance) { f_hal_nfc_poller_field_on(); if(instance->guard_time_us) { f_hal_nfc_timer_block_tx_start_us(instance->guard_time_us); - FHalNfcEvent event = f_hal_nfc_wait_event(F_HAL_NFC_EVENT_WAIT_FOREVER); + FHalNfcEvent event = f_hal_nfc_poller_wait_event(F_HAL_NFC_EVENT_WAIT_FOREVER); furi_assert(event & FHalNfcEventTimerBlockTxExpired); } instance->poller_state = NfcPollerStateReady; @@ -231,7 +233,7 @@ Nfc* nfc_alloc() { void nfc_free(Nfc* instance) { furi_assert(instance); // TODO REWORK!!! - if(instance->state == NfcStateListenStarted) { + if(instance->state == NfcStateListenerStarted) { f_hal_nfc_abort(); furi_thread_join(instance->worker_thread); } @@ -249,24 +251,24 @@ void nfc_config(Nfc* instance, NfcMode mode) { f_hal_nfc_reset_mode(); instance->config_state = NfcConfigurationStateIdle; } else if(mode == NfcModeIso14443aPoller) { - f_hal_nfc_set_mode(FHalNfcModeIso14443aPoller, FHalNfcBitrate106); + f_hal_nfc_set_mode(FHalNfcModePoller, FHalNfcTechIso14443a); instance->config_state = NfcConfigurationStateDone; } else if(mode == NfcModeIso14443aListener) { f_hal_nfc_low_power_mode_stop(); - f_hal_nfc_set_mode(FHalNfcModeIso14443aListener, FHalNfcBitrate106); + f_hal_nfc_set_mode(FHalNfcModeListener, FHalNfcTechIso14443a); instance->config_state = NfcConfigurationStateDone; } else if(mode == NfcModeIso14443bPoller) { - f_hal_nfc_set_mode(FHalNfcModeIso14443bPoller, FHalNfcBitrate106); + f_hal_nfc_set_mode(FHalNfcModePoller, FHalNfcTechIso14443b); instance->config_state = NfcConfigurationStateDone; } else if(mode == NfcModeIso15693Poller) { - f_hal_nfc_set_mode(FHalNfcModeIso15693Poller, FHalNfcBitrate26p48); + f_hal_nfc_set_mode(FHalNfcModePoller, FHalNfcTechIso15693); instance->config_state = NfcConfigurationStateDone; } else if(mode == NfcModeIso15693Listener) { f_hal_nfc_low_power_mode_stop(); - f_hal_nfc_set_mode(FHalNfcModeIso15693Listener, FHalNfcBitrate26p48); + f_hal_nfc_set_mode(FHalNfcModeListener, FHalNfcTechIso15693); instance->config_state = NfcConfigurationStateDone; } else if(mode == NfcModeFelicaPoller) { - f_hal_nfc_set_mode(FHalNfcModeFelicaPoller, FHalNfcBitrate106); + f_hal_nfc_set_mode(FHalNfcModePoller, FHalNfcTechFelica); instance->config_state = NfcConfigurationStateDone; } } @@ -347,7 +349,7 @@ void nfc_stop(Nfc* instance) { NfcError nfc_listener_sleep(Nfc* instance) { furi_assert(instance); - furi_assert(instance->state == NfcStateListenStarted); + furi_assert(instance->state == NfcStateListenerStarted); f_hal_nfc_listener_sleep(); @@ -374,7 +376,7 @@ static NfcError nfc_poller_trx_state_machine(Nfc* instance, uint32_t fwt_fc) { NfcError error = NfcErrorNone; while(true) { - event = f_hal_nfc_wait_event(F_HAL_NFC_EVENT_WAIT_FOREVER); + event = f_hal_nfc_poller_wait_event(F_HAL_NFC_EVENT_WAIT_FOREVER); if(event & FHalNfcEventTimerBlockTxExpired) { if(instance->comm_state == NfcCommStateWaitBlockTxTimer) { instance->comm_state = NfcCommStateReadyTx; diff --git a/lib/nfc/nfc.h b/lib/nfc/nfc.h index b4a023db66b4..0c7e63e96e55 100644 --- a/lib/nfc/nfc.h +++ b/lib/nfc/nfc.h @@ -1,6 +1,6 @@ #pragma once -#include +#include #ifdef __cplusplus extern "C" { diff --git a/lib/nfc/protocols/felica/felica.h b/lib/nfc/protocols/felica/felica.h index cacd489f013c..bd305b932601 100644 --- a/lib/nfc/protocols/felica/felica.h +++ b/lib/nfc/protocols/felica/felica.h @@ -1,6 +1,6 @@ #pragma once -#include +#include #include #ifdef __cplusplus diff --git a/lib/nfc/protocols/felica/felica_poller_i.h b/lib/nfc/protocols/felica/felica_poller_i.h index 592bfb03bd5a..d458c9bb4628 100644 --- a/lib/nfc/protocols/felica/felica_poller_i.h +++ b/lib/nfc/protocols/felica/felica_poller_i.h @@ -2,7 +2,7 @@ #include "felica_poller.h" -#include +#include #ifdef __cplusplus extern "C" { diff --git a/lib/nfc/protocols/iso14443_3a/iso14443_3a.h b/lib/nfc/protocols/iso14443_3a/iso14443_3a.h index a899472f160e..ca7e4d9c0fcc 100644 --- a/lib/nfc/protocols/iso14443_3a/iso14443_3a.h +++ b/lib/nfc/protocols/iso14443_3a/iso14443_3a.h @@ -1,6 +1,6 @@ #pragma once -#include +#include #include #ifdef __cplusplus diff --git a/lib/nfc/protocols/iso14443_3a/iso14443_3a_poller_i.h b/lib/nfc/protocols/iso14443_3a/iso14443_3a_poller_i.h index 99a2372bc96e..08483eb0009c 100644 --- a/lib/nfc/protocols/iso14443_3a/iso14443_3a_poller_i.h +++ b/lib/nfc/protocols/iso14443_3a/iso14443_3a_poller_i.h @@ -2,7 +2,7 @@ #include "iso14443_3a_poller.h" -#include +#include #ifdef __cplusplus extern "C" { diff --git a/lib/nfc/protocols/iso14443_3b/iso14443_3b.h b/lib/nfc/protocols/iso14443_3b/iso14443_3b.h index f222abd9db2e..b9b88788829a 100644 --- a/lib/nfc/protocols/iso14443_3b/iso14443_3b.h +++ b/lib/nfc/protocols/iso14443_3b/iso14443_3b.h @@ -1,6 +1,6 @@ #pragma once -#include +#include #include #ifdef __cplusplus diff --git a/lib/nfc/protocols/iso14443_3b/iso14443_3b_poller_i.h b/lib/nfc/protocols/iso14443_3b/iso14443_3b_poller_i.h index 898378ac3604..ce42eb40ed35 100644 --- a/lib/nfc/protocols/iso14443_3b/iso14443_3b_poller_i.h +++ b/lib/nfc/protocols/iso14443_3b/iso14443_3b_poller_i.h @@ -2,8 +2,6 @@ #include "iso14443_3b_poller.h" -#include - #ifdef __cplusplus extern "C" { #endif diff --git a/lib/nfc/protocols/iso15693_3/iso15693_3.c b/lib/nfc/protocols/iso15693_3/iso15693_3.c index 612743524609..a776e6f1a680 100644 --- a/lib/nfc/protocols/iso15693_3/iso15693_3.c +++ b/lib/nfc/protocols/iso15693_3/iso15693_3.c @@ -303,3 +303,17 @@ const Iso15693_3Data* iso15693_3_get_base_data(const Iso15693_3Data* data) { UNUSED(data); furi_crash("No base data"); } + +bool iso15693_3_is_block_locked(const Iso15693_3Data* data, uint8_t block_num) { + furi_assert(data); + furi_assert(block_num < data->system_info.block_count); + + return *(const uint8_t*)simple_array_cget(data->block_security, block_num); +} + +void iso15693_3_set_block_locked(Iso15693_3Data* data, uint8_t block_num, bool locked) { + furi_assert(data); + furi_assert(block_num < data->system_info.block_count); + + *(uint8_t*)simple_array_get(data->block_security, block_num) = locked ? 1 : 0; +} diff --git a/lib/nfc/protocols/iso15693_3/iso15693_3.h b/lib/nfc/protocols/iso15693_3/iso15693_3.h index 5d11d881db0c..870a3c19cad5 100644 --- a/lib/nfc/protocols/iso15693_3/iso15693_3.h +++ b/lib/nfc/protocols/iso15693_3/iso15693_3.h @@ -12,6 +12,7 @@ extern "C" { #define ISO15693_3_GUARD_TIME_US (5000U) #define ISO15693_3_FDT_POLL_FC (4202U) +#define ISO15693_3_FDT_LISTEN_FC (4320U) #define ISO15693_3_POLL_POLL_MIN_US (1500U) /* true: modulating releases load, false: modulating adds load resistor to field coil */ @@ -41,13 +42,14 @@ extern "C" { #define ISO15693_3_REQ_FLAG_T4_SELECTED (1U << 4) #define ISO15693_3_REQ_FLAG_T4_ADDRESSED (1U << 5) -#define ISO15693_3_REQ_FLAG_T4_CUSTOM (1U << 6) +#define ISO15693_3_REQ_FLAG_T4_OPTION (1U << 6) #define ISO15693_3_REQ_FLAG_T5_AFI_PRESENT (1U << 4) #define ISO15693_3_REQ_FLAG_T5_N_SLOTS_16 (0U << 5) #define ISO15693_3_REQ_FLAG_T5_N_SLOTS_1 (1U << 5) -#define ISO15693_3_REQ_FLAG_T5_CUSTOM (1U << 6) +#define ISO15693_3_REQ_FLAG_T5_OPTION (1U << 6) +#define ISO15693_3_RESP_FLAG_NONE (0U) #define ISO15693_3_RESP_FLAG_ERROR (1U << 0) #define ISO15693_3_RESP_FLAG_EXTENSION (1U << 3) @@ -63,13 +65,16 @@ extern "C" { #define ISO15693_3_RESP_ERROR_CUSTOM_START (0xA0U) #define ISO15693_3_RESP_ERROR_CUSTOM_END (0xDFU) +#define ISO15693_3_CMD_MANDATORY_START (0x01U) #define ISO15693_3_CMD_INVENTORY (0x01U) #define ISO15693_3_CMD_STAY_QUIET (0x02U) +#define ISO15693_3_CMD_MANDATORY_RFU (0x03U) +#define ISO15693_3_CMD_OPTIONAL_START (0x20U) #define ISO15693_3_CMD_READ_BLOCK (0x20U) #define ISO15693_3_CMD_WRITE_BLOCK (0x21U) #define ISO15693_3_CMD_LOCK_BLOCK (0x22U) -#define ISO15693_3_CMD_READ_BLOCKS (0x23U) -#define ISO15693_3_CMD_WRITE_BLOCKS (0x24U) +#define ISO15693_3_CMD_READ_MULTI_BLOCKS (0x23U) +#define ISO15693_3_CMD_WRITE_MULTI_BLOCKS (0x24U) #define ISO15693_3_CMD_SELECT (0x25U) #define ISO15693_3_CMD_RESET_TO_READY (0x26U) #define ISO15693_3_CMD_WRITE_AFI (0x27U) @@ -78,6 +83,7 @@ extern "C" { #define ISO15693_3_CMD_LOCK_DSFID (0x2AU) #define ISO15693_3_CMD_GET_SYS_INFO (0x2BU) #define ISO15693_3_CMD_GET_BLOCKS_SECURITY (0x2CU) +#define ISO15693_3_CMD_OPTIONAL_RFU (0x2DU) #define ISO15693_3_SYSINFO_FLAG_DSFID (1U << 0) #define ISO15693_3_SYSINFO_FLAG_AFI (1U << 1) @@ -149,6 +155,14 @@ bool iso15693_3_set_uid(Iso15693_3Data* data, const uint8_t* uid, size_t uid_len const Iso15693_3Data* iso15693_3_get_base_data(const Iso15693_3Data* data); +// Getters and tests + +bool iso15693_3_is_block_locked(const Iso15693_3Data* data, uint8_t block_num); + +// Setters + +void iso15693_3_set_block_locked(Iso15693_3Data* data, uint8_t block_num, bool locked); + extern const NfcDeviceBase nfc_device_iso15693_3; #ifdef __cplusplus diff --git a/lib/nfc/protocols/iso15693_3/iso15693_3_i.c b/lib/nfc/protocols/iso15693_3/iso15693_3_i.c index 1f3b1352fea4..c75d19261584 100644 --- a/lib/nfc/protocols/iso15693_3/iso15693_3_i.c +++ b/lib/nfc/protocols/iso15693_3/iso15693_3_i.c @@ -210,3 +210,47 @@ Iso15693_3Error iso15693_3_get_block_security_response_parse( return ret; } + +void iso15693_3_append_uid(const Iso15693_3Data* data, BitBuffer* buf) { + for(size_t i = 0; i < ISO15693_3_UID_SIZE; ++i) { + // Reverse the UID + bit_buffer_append_byte(buf, data->uid[ISO15693_3_UID_SIZE - i - 1]); + } +} + +void iso15693_3_append_block(const Iso15693_3Data* data, uint8_t block_num, BitBuffer* buf) { + furi_assert(block_num < data->system_info.block_count); + + const uint32_t block_offset = block_num * data->system_info.block_size; + const uint8_t* block_data = simple_array_cget(data->block_data, block_offset); + + bit_buffer_append_bytes(buf, block_data, data->system_info.block_size); +} + +void iso15693_3_set_block_data( + Iso15693_3Data* data, + uint8_t block_num, + const uint8_t* block_data, + size_t block_data_size) { + furi_assert(block_num < data->system_info.block_count); + furi_assert(block_data_size == data->system_info.block_size); + + const uint32_t block_offset = block_num * data->system_info.block_size; + uint8_t* block = simple_array_get(data->block_data, block_offset); + + memcpy(block, block_data, block_data_size); +} + +void iso15693_3_append_block_security( + const Iso15693_3Data* data, + uint8_t block_num, + BitBuffer* buf) { + bit_buffer_append_byte(buf, *(uint8_t*)simple_array_cget(data->block_security, block_num)); +} + +bool iso15693_3_is_equal_uid(const Iso15693_3Data* data, const uint8_t* uid) { + for(size_t i = 0; i < ISO15693_3_UID_SIZE; ++i) { + if(data->uid[i] != uid[ISO15693_3_UID_SIZE - i - 1]) return false; + } + return true; +} diff --git a/lib/nfc/protocols/iso15693_3/iso15693_3_i.h b/lib/nfc/protocols/iso15693_3/iso15693_3_i.h index 4cab9199a807..01280e180822 100644 --- a/lib/nfc/protocols/iso15693_3/iso15693_3_i.h +++ b/lib/nfc/protocols/iso15693_3/iso15693_3_i.h @@ -2,7 +2,7 @@ #include "iso15693_3.h" -#include +#include #ifdef __cplusplus extern "C" { @@ -33,6 +33,24 @@ Iso15693_3Error iso15693_3_get_block_security_response_parse( uint16_t block_count, const BitBuffer* buf); +void iso15693_3_append_uid(const Iso15693_3Data* data, BitBuffer* buf); + +void iso15693_3_append_block(const Iso15693_3Data* data, uint8_t block_num, BitBuffer* buf); + +void iso15693_3_set_block_data( + Iso15693_3Data* data, + uint8_t block_num, + const uint8_t* block_data, + size_t block_data_size); + +void iso15693_3_append_block_security( + const Iso15693_3Data* data, + uint8_t block_num, + BitBuffer* buf); + +// NOTE: the uid parameter has reversed byte order with respect to data +bool iso15693_3_is_equal_uid(const Iso15693_3Data* data, const uint8_t* uid); + #ifdef __cplusplus } #endif diff --git a/lib/nfc/protocols/iso15693_3/iso15693_3_listener.c b/lib/nfc/protocols/iso15693_3/iso15693_3_listener.c index e69de29bb2d1..c47734310b14 100644 --- a/lib/nfc/protocols/iso15693_3/iso15693_3_listener.c +++ b/lib/nfc/protocols/iso15693_3/iso15693_3_listener.c @@ -0,0 +1,111 @@ +#include "iso15693_3_listener_i.h" + +#include + +#include +#include +#include + +#define TAG "Iso15693_3Listener" + +#define ISO15693_3_LISTENER_BUFFER_SIZE (64U) + +Iso15693_3Listener* iso15693_3_listener_alloc(Nfc* nfc, const Iso15693_3Data* data) { + furi_assert(nfc); + + Iso15693_3Listener* instance = malloc(sizeof(Iso15693_3Listener)); + instance->nfc = nfc; + instance->data = iso15693_3_alloc(); + iso15693_3_copy(instance->data, data); + + instance->tx_buffer = bit_buffer_alloc(ISO15693_3_LISTENER_BUFFER_SIZE); + + instance->iso15693_3_event.data = &instance->iso15693_3_event_data; + instance->generic_event.protocol = NfcProtocolIso15693_3; + instance->generic_event.instance = instance; + instance->generic_event.data = &instance->iso15693_3_event; + + nfc_set_fdt_listen_fc(instance->nfc, ISO15693_3_FDT_LISTEN_FC); + nfc_config(instance->nfc, NfcModeIso15693Listener); + + return instance; +} + +void iso15693_3_listener_free(Iso15693_3Listener* instance) { + furi_assert(instance); + + bit_buffer_free(instance->tx_buffer); + iso15693_3_free(instance->data); + + free(instance); +} + +void iso15693_3_listener_set_callback( + Iso15693_3Listener* instance, + NfcGenericCallback callback, + void* context) { + furi_assert(instance); + + instance->callback = callback; + instance->context = context; +} + +const Iso15693_3Data* iso15693_3_listener_get_data(Iso15693_3Listener* instance) { + furi_assert(instance); + furi_assert(instance->data); + + return instance->data; +} + +NfcCommand iso15693_3_listener_run(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.protocol == NfcProtocolInvalid); + furi_assert(event.data); + + Iso15693_3Listener* instance = context; + NfcEvent* nfc_event = event.data; + NfcCommand command = NfcCommandContinue; + + if(nfc_event->type == NfcEventTypeFieldOn) { + iso15693_3_listener_ready(instance); + } else if(nfc_event->type == NfcEventTypeFieldOff) { + if(instance->callback) { + instance->iso15693_3_event.type = Iso15693_3ListenerEventTypeFieldOff; + command = instance->callback(instance->generic_event, instance->context); + } + iso15693_3_listener_sleep(instance); + } else if(nfc_event->type == NfcEventTypeRxEnd) { + BitBuffer* request_buf = nfc_event->data.buffer; + if(iso13239_crc_check(Iso13239CrcTypeDefault, request_buf)) { + iso13239_crc_trim(request_buf); + const Iso15693_3Error error = + iso15693_3_listener_process_request(instance, request_buf); + if(error == Iso15693_3ErrorNotSupported) { + instance->iso15693_3_event.type = Iso15693_3ListenerEventTypeCustomCommand; + command = instance->callback(instance->generic_event, instance->context); + } + } else if(bit_buffer_get_size(request_buf) == 0) { + // Special case: Single EOF + if(instance->session_state.wait_for_eof) { + iso15693_3_listener_send_frame(instance, instance->tx_buffer); + instance->session_state.wait_for_eof = false; + } else if(instance->callback) { + instance->iso15693_3_event.type = Iso15693_3ListenerEventTypeSingleEof; + command = instance->callback(instance->generic_event, instance->context); + } + } else { + FURI_LOG_D( + TAG, "Wrong CRC, buffer size: %zu", bit_buffer_get_size(nfc_event->data.buffer)); + } + } + + return command; +} + +const NfcListenerBase nfc_listener_iso15693_3 = { + .alloc = (NfcListenerAlloc)iso15693_3_listener_alloc, + .free = (NfcListenerFree)iso15693_3_listener_free, + .set_callback = (NfcListenerSetCallback)iso15693_3_listener_set_callback, + .get_data = (NfcListenerGetData)iso15693_3_listener_get_data, + .run = (NfcListenerRun)iso15693_3_listener_run, +}; diff --git a/lib/nfc/protocols/iso15693_3/iso15693_3_listener.h b/lib/nfc/protocols/iso15693_3/iso15693_3_listener.h index cd74574a9381..c69d18db4e7f 100644 --- a/lib/nfc/protocols/iso15693_3/iso15693_3_listener.h +++ b/lib/nfc/protocols/iso15693_3/iso15693_3_listener.h @@ -1,9 +1,30 @@ #pragma once +#include + +#include "iso15693_3.h" + #ifdef __cplusplus extern "C" { #endif +typedef struct Iso15693_3Listener Iso15693_3Listener; + +typedef enum { + Iso15693_3ListenerEventTypeFieldOff, + Iso15693_3ListenerEventTypeCustomCommand, + Iso15693_3ListenerEventTypeSingleEof, +} Iso15693_3ListenerEventType; + +typedef struct { + BitBuffer* buffer; +} Iso15693_3ListenerEventData; + +typedef struct { + Iso15693_3ListenerEventType type; + Iso15693_3ListenerEventData* data; +} Iso15693_3ListenerEvent; + #ifdef __cplusplus } #endif diff --git a/lib/nfc/protocols/iso15693_3/iso15693_3_listener_defs.h b/lib/nfc/protocols/iso15693_3/iso15693_3_listener_defs.h index cd74574a9381..0543b6f9299a 100644 --- a/lib/nfc/protocols/iso15693_3/iso15693_3_listener_defs.h +++ b/lib/nfc/protocols/iso15693_3/iso15693_3_listener_defs.h @@ -1,9 +1,13 @@ #pragma once +#include + #ifdef __cplusplus extern "C" { #endif +extern const NfcListenerBase nfc_listener_iso15693_3; + #ifdef __cplusplus } #endif diff --git a/lib/nfc/protocols/iso15693_3/iso15693_3_listener_i.c b/lib/nfc/protocols/iso15693_3/iso15693_3_listener_i.c index e69de29bb2d1..ad7e33d00c90 100644 --- a/lib/nfc/protocols/iso15693_3/iso15693_3_listener_i.c +++ b/lib/nfc/protocols/iso15693_3/iso15693_3_listener_i.c @@ -0,0 +1,726 @@ +#include "iso15693_3_listener_i.h" + +#include + +#define TAG "Iso15693_3Listener" + +typedef Iso15693_3Error (*Iso15693_3RequestHandler)( + Iso15693_3Listener* instance, + const uint8_t* data, + size_t data_size, + uint8_t flags); + +static Iso15693_3Error iso15693_3_listener_inventory_handler( + Iso15693_3Listener* instance, + const uint8_t* data, + size_t data_size, + uint8_t flags) { + Iso15693_3Error error = Iso15693_3ErrorNone; + + do { + const bool afi_flag = flags & ISO15693_3_REQ_FLAG_T5_AFI_PRESENT; + const size_t data_size_min = sizeof(uint8_t) * (afi_flag ? 2 : 1); + + if(data_size < data_size_min) { + error = Iso15693_3ErrorFormat; + break; + } + + if(afi_flag) { + const uint8_t afi = *data++; + // When AFI flag is set, ignore non-matching requests + if(afi != instance->data->system_info.afi) break; + } + + const uint8_t mask_len = *data++; + const size_t data_size_required = data_size_min + mask_len; + + if(data_size != data_size_required) { + error = Iso15693_3ErrorFormat; + break; + } + + if(mask_len != 0) { + // TODO: Take mask_len and mask_value into account (if present) + } + + bit_buffer_append_byte(instance->tx_buffer, instance->data->system_info.dsfid); // DSFID + iso15693_3_append_uid(instance->data, instance->tx_buffer); // UID + } while(false); + + instance->session_state.no_reply = (error != Iso15693_3ErrorNone); + return error; +} + +static Iso15693_3Error iso15693_3_listener_stay_quiet_handler( + Iso15693_3Listener* instance, + const uint8_t* data, + size_t data_size, + uint8_t flags) { + UNUSED(data); + UNUSED(data_size); + UNUSED(flags); + + instance->state = Iso15693_3ListenerStateQuiet; + instance->session_state.no_reply = true; + return Iso15693_3ErrorNone; +} + +static Iso15693_3Error iso15693_3_listener_read_block_handler( + Iso15693_3Listener* instance, + const uint8_t* data, + size_t data_size, + uint8_t flags) { + Iso15693_3Error error = Iso15693_3ErrorNone; + + do { + typedef struct { + uint8_t block_num; + } Iso15693_3ReadBlockRequestLayout; + + const Iso15693_3ReadBlockRequestLayout* request = + (const Iso15693_3ReadBlockRequestLayout*)data; + + if(data_size != sizeof(Iso15693_3ReadBlockRequestLayout)) { + error = Iso15693_3ErrorFormat; + break; + } + + const uint32_t block_index = request->block_num; + const uint32_t block_count_max = instance->data->system_info.block_count; + + if(block_index >= block_count_max) { + error = Iso15693_3ErrorInternal; + break; + } + + if(flags & ISO15693_3_REQ_FLAG_T4_OPTION) { + iso15693_3_append_block_security( + instance->data, block_index, instance->tx_buffer); // Block security (optional) + } + + iso15693_3_append_block(instance->data, block_index, instance->tx_buffer); // Block data + } while(false); + + return error; +} + +static Iso15693_3Error iso15693_3_listener_write_block_handler( + Iso15693_3Listener* instance, + const uint8_t* data, + size_t data_size, + uint8_t flags) { + Iso15693_3Error error = Iso15693_3ErrorNone; + + do { + typedef struct { + uint8_t block_num; + uint8_t block_data[]; + } Iso15693_3WriteBlockRequestLayout; + + const Iso15693_3WriteBlockRequestLayout* request = + (const Iso15693_3WriteBlockRequestLayout*)data; + + instance->session_state.wait_for_eof = flags & ISO15693_3_REQ_FLAG_T4_OPTION; + + if(data_size <= sizeof(Iso15693_3WriteBlockRequestLayout)) { + error = Iso15693_3ErrorFormat; + break; + } + + const uint32_t block_index = request->block_num; + const uint32_t block_count_max = instance->data->system_info.block_count; + const uint32_t block_size_max = instance->data->system_info.block_size; + const size_t block_size_received = data_size - sizeof(Iso15693_3WriteBlockRequestLayout); + + if(block_index >= block_count_max) { + error = Iso15693_3ErrorInternal; + break; + } else if(block_size_received != block_size_max) { + error = Iso15693_3ErrorInternal; + break; + } else if(iso15693_3_is_block_locked(instance->data, block_index)) { + error = Iso15693_3ErrorInternal; + break; + } + + iso15693_3_set_block_data( + instance->data, block_index, request->block_data, block_size_received); + } while(false); + + return error; +} + +static Iso15693_3Error iso15693_3_listener_lock_block_handler( + Iso15693_3Listener* instance, + const uint8_t* data, + size_t data_size, + uint8_t flags) { + Iso15693_3Error error = Iso15693_3ErrorNone; + + do { + typedef struct { + uint8_t block_num; + } Iso15693_3LockBlockRequestLayout; + + const Iso15693_3LockBlockRequestLayout* request = + (const Iso15693_3LockBlockRequestLayout*)data; + + instance->session_state.wait_for_eof = flags & ISO15693_3_REQ_FLAG_T4_OPTION; + + if(data_size != sizeof(Iso15693_3LockBlockRequestLayout)) { + error = Iso15693_3ErrorFormat; + break; + } + + const uint32_t block_index = request->block_num; + const uint32_t block_count_max = instance->data->system_info.block_count; + + if(block_index >= block_count_max) { + error = Iso15693_3ErrorInternal; + break; + } else if(iso15693_3_is_block_locked(instance->data, block_index)) { + error = Iso15693_3ErrorInternal; + break; + } + + iso15693_3_set_block_locked(instance->data, block_index, true); + } while(false); + + return error; +} + +static Iso15693_3Error iso15693_3_listener_read_multi_blocks_handler( + Iso15693_3Listener* instance, + const uint8_t* data, + size_t data_size, + uint8_t flags) { + Iso15693_3Error error = Iso15693_3ErrorNone; + + do { + typedef struct { + uint8_t first_block_num; + uint8_t block_count; + } Iso15693_3ReadMultiBlocksRequestLayout; + + const Iso15693_3ReadMultiBlocksRequestLayout* request = + (const Iso15693_3ReadMultiBlocksRequestLayout*)data; + + if(data_size != sizeof(Iso15693_3ReadMultiBlocksRequestLayout)) { + error = Iso15693_3ErrorFormat; + break; + } + + const uint32_t block_index_start = request->first_block_num; + const uint32_t block_index_end = block_index_start + request->block_count; + + const uint32_t block_count = request->block_count + 1; + const uint32_t block_count_max = instance->data->system_info.block_count; + const uint32_t block_count_available = block_count_max - block_index_start; + + if(block_count > block_count_available) { + error = Iso15693_3ErrorInternal; + break; + } + + for(uint32_t i = block_index_start; i <= block_index_end; ++i) { + if(flags & ISO15693_3_REQ_FLAG_T4_OPTION) { + iso15693_3_append_block_security( + instance->data, i, instance->tx_buffer); // Block security (optional) + } + iso15693_3_append_block(instance->data, i, instance->tx_buffer); // Block data + } + } while(false); + + return error; +} + +static Iso15693_3Error iso15693_3_listener_write_multi_blocks_handler( + Iso15693_3Listener* instance, + const uint8_t* data, + size_t data_size, + uint8_t flags) { + Iso15693_3Error error = Iso15693_3ErrorNone; + + do { + typedef struct { + uint8_t first_block_num; + uint8_t block_count; + uint8_t block_data[]; + } Iso15693_3WriteMultiBlocksRequestLayout; + + const Iso15693_3WriteMultiBlocksRequestLayout* request = + (const Iso15693_3WriteMultiBlocksRequestLayout*)data; + + instance->session_state.wait_for_eof = flags & ISO15693_3_REQ_FLAG_T4_OPTION; + + if(data_size <= sizeof(Iso15693_3WriteMultiBlocksRequestLayout)) { + error = Iso15693_3ErrorFormat; + break; + } + + const uint32_t block_index_start = request->first_block_num; + const uint32_t block_index_end = block_index_start + request->block_count; + + const uint32_t block_count = request->block_count + 1; + const uint32_t block_count_max = instance->data->system_info.block_count; + const uint32_t block_count_available = block_count_max - block_index_start; + + const size_t block_data_size = data_size - sizeof(Iso15693_3WriteMultiBlocksRequestLayout); + const size_t block_size = block_data_size / block_count; + const size_t block_size_max = instance->data->system_info.block_size; + + if(block_count > block_count_available) { + error = Iso15693_3ErrorInternal; + break; + } else if(block_size != block_size_max) { + error = Iso15693_3ErrorInternal; + break; + } + + for(uint32_t i = block_index_start; i <= block_index_end; ++i) { + if(iso15693_3_is_block_locked(instance->data, i)) { + error = Iso15693_3ErrorInternal; + break; + } + } + + if(error != Iso15693_3ErrorNone) break; + + for(uint32_t i = block_index_start; i < block_count + request->first_block_num; ++i) { + const uint8_t* block_data = &request->block_data[block_size * i]; + iso15693_3_set_block_data(instance->data, i, block_data, block_size); + } + } while(false); + + return error; +} + +static Iso15693_3Error iso15693_3_listener_select_handler( + Iso15693_3Listener* instance, + const uint8_t* data, + size_t data_size, + uint8_t flags) { + UNUSED(data); + UNUSED(data_size); + + Iso15693_3Error error = Iso15693_3ErrorNone; + + do { + if(!(flags & ISO15693_3_REQ_FLAG_T4_ADDRESSED)) { + instance->session_state.no_reply = true; + error = Iso15693_3ErrorUnknown; + break; + } + + instance->state = Iso15693_3ListenerStateSelected; + } while(false); + + return error; +} + +static Iso15693_3Error iso15693_3_listener_reset_to_ready_handler( + Iso15693_3Listener* instance, + const uint8_t* data, + size_t data_size, + uint8_t flags) { + UNUSED(data); + UNUSED(data_size); + UNUSED(flags); + + instance->state = Iso15693_3ListenerStateReady; + return Iso15693_3ErrorNone; +} + +static Iso15693_3Error iso15693_3_listener_write_afi_handler( + Iso15693_3Listener* instance, + const uint8_t* data, + size_t data_size, + uint8_t flags) { + Iso15693_3Error error = Iso15693_3ErrorNone; + + do { + typedef struct { + uint8_t afi; + } Iso15693_3WriteAfiRequestLayout; + + const Iso15693_3WriteAfiRequestLayout* request = + (const Iso15693_3WriteAfiRequestLayout*)data; + + instance->session_state.wait_for_eof = flags & ISO15693_3_REQ_FLAG_T4_OPTION; + + if(data_size <= sizeof(Iso15693_3WriteAfiRequestLayout)) { + error = Iso15693_3ErrorFormat; + break; + } else if(instance->data->system_info.flags & ISO15693_3_SYSINFO_LOCK_AFI) { + error = Iso15693_3ErrorInternal; + break; + } + + instance->data->system_info.afi = request->afi; + } while(false); + + return error; +} + +static Iso15693_3Error iso15693_3_listener_lock_afi_handler( + Iso15693_3Listener* instance, + const uint8_t* data, + size_t data_size, + uint8_t flags) { + UNUSED(data); + UNUSED(data_size); + + Iso15693_3Error error = Iso15693_3ErrorNone; + + do { + instance->session_state.wait_for_eof = flags & ISO15693_3_REQ_FLAG_T4_OPTION; + + if(instance->data->system_info.flags & ISO15693_3_SYSINFO_LOCK_AFI) { + error = Iso15693_3ErrorInternal; + break; + } + + instance->data->system_info.flags |= ISO15693_3_SYSINFO_LOCK_AFI; + } while(false); + + return error; +} + +static Iso15693_3Error iso15693_3_listener_write_dsfid_handler( + Iso15693_3Listener* instance, + const uint8_t* data, + size_t data_size, + uint8_t flags) { + Iso15693_3Error error = Iso15693_3ErrorNone; + + do { + typedef struct { + uint8_t dsfid; + } Iso15693_3WriteDsfidRequestLayout; + + const Iso15693_3WriteDsfidRequestLayout* request = + (const Iso15693_3WriteDsfidRequestLayout*)data; + + instance->session_state.wait_for_eof = flags & ISO15693_3_REQ_FLAG_T4_OPTION; + + if(data_size <= sizeof(Iso15693_3WriteDsfidRequestLayout)) { + error = Iso15693_3ErrorFormat; + break; + } else if(instance->data->system_info.flags & ISO15693_3_SYSINFO_LOCK_DSFID) { + error = Iso15693_3ErrorInternal; + break; + } + + instance->data->system_info.dsfid = request->dsfid; + } while(false); + + return error; +} + +static Iso15693_3Error iso15693_3_listener_lock_dsfid_handler( + Iso15693_3Listener* instance, + const uint8_t* data, + size_t data_size, + uint8_t flags) { + UNUSED(data); + UNUSED(data_size); + + Iso15693_3Error error = Iso15693_3ErrorNone; + + do { + instance->session_state.wait_for_eof = flags & ISO15693_3_REQ_FLAG_T4_OPTION; + + if(instance->data->system_info.flags & ISO15693_3_SYSINFO_LOCK_DSFID) { + error = Iso15693_3ErrorInternal; + break; + } + + instance->data->system_info.flags |= ISO15693_3_SYSINFO_LOCK_DSFID; + } while(false); + + return error; +} + +static Iso15693_3Error iso15693_3_listener_get_system_info_handler( + Iso15693_3Listener* instance, + const uint8_t* data, + size_t data_size, + uint8_t flags) { + UNUSED(data); + UNUSED(data_size); + UNUSED(flags); + + Iso15693_3Error error = Iso15693_3ErrorNone; + + do { + const uint8_t system_flags = instance->data->system_info.flags; + bit_buffer_append_byte(instance->tx_buffer, system_flags); // System info flags + + iso15693_3_append_uid(instance->data, instance->tx_buffer); // UID + + if(system_flags & ISO15693_3_SYSINFO_FLAG_DSFID) { + bit_buffer_append_byte(instance->tx_buffer, instance->data->system_info.dsfid); + } + if(system_flags & ISO15693_3_SYSINFO_FLAG_AFI) { + bit_buffer_append_byte(instance->tx_buffer, instance->data->system_info.afi); + } + if(system_flags & ISO15693_3_SYSINFO_FLAG_MEMORY) { + const uint8_t memory_info[2] = { + instance->data->system_info.block_count - 1, + instance->data->system_info.block_size - 1, + }; + bit_buffer_append_bytes(instance->tx_buffer, memory_info, COUNT_OF(memory_info)); + } + if(system_flags & ISO15693_3_SYSINFO_FLAG_IC_REF) { + bit_buffer_append_byte(instance->tx_buffer, instance->data->system_info.ic_ref); + } + + } while(false); + + return error; +} + +static Iso15693_3Error iso15693_3_listener_get_multi_blocks_security_handler( + Iso15693_3Listener* instance, + const uint8_t* data, + size_t data_size, + uint8_t flags) { + UNUSED(flags); + + Iso15693_3Error error = Iso15693_3ErrorNone; + + do { + typedef struct { + uint8_t first_block_num; + uint8_t block_count; + } Iso15693_3GetMultiBlocksSecurityRequestLayout; + + const Iso15693_3GetMultiBlocksSecurityRequestLayout* request = + (const Iso15693_3GetMultiBlocksSecurityRequestLayout*)data; + + if(data_size < sizeof(Iso15693_3GetMultiBlocksSecurityRequestLayout)) { + error = Iso15693_3ErrorFormat; + break; + } + + const uint32_t block_index_start = request->first_block_num; + const uint32_t block_index_end = block_index_start + request->block_count; + + const uint32_t block_count_max = instance->data->system_info.block_count; + + if(block_index_end >= block_count_max) { + error = Iso15693_3ErrorInternal; + break; + } + + for(uint32_t i = block_index_start; i <= block_index_end; ++i) { + bit_buffer_append_byte( + instance->tx_buffer, iso15693_3_is_block_locked(instance->data, i) ? 1 : 0); + } + } while(false); + + return error; +} + +static const Iso15693_3RequestHandler iso15693_3_request_handlers[] = { + // Mandatory commands + iso15693_3_listener_inventory_handler, + iso15693_3_listener_stay_quiet_handler, + // Optional commands + iso15693_3_listener_read_block_handler, + iso15693_3_listener_write_block_handler, + iso15693_3_listener_lock_block_handler, + iso15693_3_listener_read_multi_blocks_handler, + iso15693_3_listener_write_multi_blocks_handler, + iso15693_3_listener_select_handler, + iso15693_3_listener_reset_to_ready_handler, + iso15693_3_listener_write_afi_handler, + iso15693_3_listener_lock_afi_handler, + iso15693_3_listener_write_dsfid_handler, + iso15693_3_listener_lock_dsfid_handler, + iso15693_3_listener_get_system_info_handler, + iso15693_3_listener_get_multi_blocks_security_handler, +}; + +static inline Iso15693_3Error iso15693_3_listener_handle_request( + Iso15693_3Listener* instance, + const uint8_t* data, + size_t data_size, + uint8_t command, + uint8_t flags) { + Iso15693_3Error error = Iso15693_3ErrorNone; + + do { + uint8_t command_index; + + if(command < ISO15693_3_CMD_MANDATORY_RFU) { + command_index = command - ISO15693_3_CMD_MANDATORY_START; + } else if(command >= ISO15693_3_CMD_OPTIONAL_START && command < ISO15693_3_CMD_OPTIONAL_RFU) { + command_index = command - ISO15693_3_CMD_MANDATORY_START - + ISO15693_3_CMD_OPTIONAL_START + ISO15693_3_CMD_MANDATORY_RFU; + } else { + error = Iso15693_3ErrorNotSupported; + break; + } + + bit_buffer_reset(instance->tx_buffer); + bit_buffer_append_byte(instance->tx_buffer, ISO15693_3_RESP_FLAG_NONE); + + error = iso15693_3_request_handlers[command_index](instance, data, data_size, flags); + + Iso15693_3ListenerSessionState* session_state = &instance->session_state; + + // Several commands may not require an answer + if(session_state->no_reply) { + session_state->no_reply = false; + error = Iso15693_3ErrorNone; + break; + } + + // TODO: Move it to a separate function + if(error != Iso15693_3ErrorNone) { + bit_buffer_reset(instance->tx_buffer); + bit_buffer_append_byte(instance->tx_buffer, ISO15693_3_RESP_FLAG_ERROR); + bit_buffer_append_byte(instance->tx_buffer, ISO15693_3_RESP_ERROR_UNKNOWN); + } + + if(!session_state->wait_for_eof) { + error = iso15693_3_listener_send_frame(instance, instance->tx_buffer); + } + + } while(false); + + return error; +} + +Iso15693_3Error iso15693_3_listener_ready(Iso15693_3Listener* instance) { + furi_assert(instance); + instance->state = Iso15693_3ListenerStateReady; + return Iso15693_3ErrorNone; +} + +Iso15693_3Error iso15693_3_listener_sleep(Iso15693_3Listener* instance) { + furi_assert(instance); + instance->state = Iso15693_3ListenerStateIdle; + return Iso15693_3ErrorNone; +} + +static Iso15693_3Error iso15693_3_listener_process_nfc_error(NfcError error) { + Iso15693_3Error ret = Iso15693_3ErrorNone; + + if(error == NfcErrorNone) { + ret = Iso15693_3ErrorNone; + } else if(error == NfcErrorTimeout) { + ret = Iso15693_3ErrorTimeout; + } else { + ret = Iso15693_3ErrorFieldOff; + } + + return ret; +} + +Iso15693_3Error + iso15693_3_listener_send_frame(Iso15693_3Listener* instance, const BitBuffer* tx_buffer) { + furi_assert(instance); + furi_assert(tx_buffer); + + bit_buffer_copy(instance->tx_buffer, tx_buffer); + iso13239_crc_append(Iso13239CrcTypeDefault, instance->tx_buffer); + + NfcError error = nfc_listener_tx(instance->nfc, instance->tx_buffer); + return iso15693_3_listener_process_nfc_error(error); +} + +Iso15693_3Error + iso15693_3_listener_process_request(Iso15693_3Listener* instance, const BitBuffer* rx_buffer) { + Iso15693_3Error error = Iso15693_3ErrorNone; + + do { + typedef struct { + uint8_t flags; + uint8_t command; + uint8_t data[]; + } Iso15693_3RequestLayout; + + const size_t buf_size = bit_buffer_get_size_bytes(rx_buffer); + const size_t buf_size_min = sizeof(Iso15693_3RequestLayout); + + if(buf_size < buf_size_min) { + error = Iso15693_3ErrorFormat; + break; + } + + const Iso15693_3RequestLayout* request = + (const Iso15693_3RequestLayout*)bit_buffer_get_data(rx_buffer); + + const bool inventory_flag = request->flags & ISO15693_3_REQ_FLAG_INVENTORY_T5; + + if(!inventory_flag) { + const bool selected_mode = request->flags & ISO15693_3_REQ_FLAG_T4_SELECTED; + const bool addressed_mode = request->flags & ISO15693_3_REQ_FLAG_T4_ADDRESSED; + + if(selected_mode && addressed_mode) { + // A request mode can be either addressed or selected, but not both + break; + } else if(instance->state == Iso15693_3ListenerStateQuiet) { + // If the card is quiet, ignore non-addressed commands + if(!addressed_mode) break; + } else if(instance->state != Iso15693_3ListenerStateSelected) { + // If the card is not selected, ignore selected commands + if(selected_mode) break; + } + + const uint8_t* data; + size_t data_size; + + if(addressed_mode) { + // In addressed mode, UID must be included in each command + const size_t buf_size_min_addr = buf_size_min + ISO15693_3_UID_SIZE; + + if(buf_size < buf_size_min_addr) { + error = Iso15693_3ErrorFormat; + break; + } else if(!iso15693_3_is_equal_uid(instance->data, request->data)) { + // In addressed mode, ignore all commands with non-matching UID + if(instance->state == Iso15693_3ListenerStateSelected && + request->command == ISO15693_3_CMD_SELECT) { + // Special case, reset to ready on reception of a + // SELECT command with non-matching UID + // TODO: Find a neater way to do this? + instance->state = Iso15693_3ListenerStateReady; + } + break; + } + + data = &request->data[ISO15693_3_UID_SIZE]; + data_size = buf_size - buf_size_min_addr; + + } else { + data = request->data; + data_size = buf_size - buf_size_min; + } + + error = iso15693_3_listener_handle_request( + instance, data, data_size, request->command, request->flags); + + } else { + // If the card is quiet, ignore INVENTORY commands + if(instance->state == Iso15693_3ListenerStateQuiet) { + break; + } + + // Only the INVENTORY command is allowed with this flag set + if(request->command != ISO15693_3_CMD_INVENTORY) { + error = Iso15693_3ErrorUnknown; + break; + } + + error = iso15693_3_listener_handle_request( + instance, request->data, buf_size - buf_size_min, request->command, request->flags); + } + + } while(false); + + return error; +} diff --git a/lib/nfc/protocols/iso15693_3/iso15693_3_listener_i.h b/lib/nfc/protocols/iso15693_3/iso15693_3_listener_i.h index cd74574a9381..e6ecdfb35a1c 100644 --- a/lib/nfc/protocols/iso15693_3/iso15693_3_listener_i.h +++ b/lib/nfc/protocols/iso15693_3/iso15693_3_listener_i.h @@ -1,9 +1,52 @@ #pragma once +#include + +#include "iso15693_3_listener.h" + +#include "iso15693_3_i.h" + #ifdef __cplusplus extern "C" { #endif +typedef enum { + Iso15693_3ListenerStateIdle, + Iso15693_3ListenerStateReady, + Iso15693_3ListenerStateSelected, + Iso15693_3ListenerStateQuiet, +} Iso15693_3ListenerState; + +typedef struct { + bool wait_for_eof; + bool no_reply; +} Iso15693_3ListenerSessionState; + +struct Iso15693_3Listener { + Nfc* nfc; + Iso15693_3Data* data; + Iso15693_3ListenerState state; + Iso15693_3ListenerSessionState session_state; + + BitBuffer* tx_buffer; + + NfcGenericEvent generic_event; + Iso15693_3ListenerEvent iso15693_3_event; + Iso15693_3ListenerEventData iso15693_3_event_data; + NfcGenericCallback callback; + void* context; +}; + +Iso15693_3Error iso15693_3_listener_ready(Iso15693_3Listener* instance); + +Iso15693_3Error iso15693_3_listener_sleep(Iso15693_3Listener* instance); + +Iso15693_3Error + iso15693_3_listener_send_frame(Iso15693_3Listener* instance, const BitBuffer* tx_buffer); + +Iso15693_3Error + iso15693_3_listener_process_request(Iso15693_3Listener* instance, const BitBuffer* rx_buffer); + #ifdef __cplusplus } #endif diff --git a/lib/nfc/protocols/iso15693_3/iso15693_3_poller.c b/lib/nfc/protocols/iso15693_3/iso15693_3_poller.c index ce4b084c72da..ffe27bc78459 100644 --- a/lib/nfc/protocols/iso15693_3/iso15693_3_poller.c +++ b/lib/nfc/protocols/iso15693_3/iso15693_3_poller.c @@ -20,10 +20,6 @@ static Iso15693_3Poller* iso15693_3_poller_alloc(Nfc* nfc) { instance->nfc = nfc; instance->tx_buffer = bit_buffer_alloc(ISO15693_3_POLLER_MAX_BUFFER_SIZE); instance->rx_buffer = bit_buffer_alloc(ISO15693_3_POLLER_MAX_BUFFER_SIZE); - // 4 bits per data bit on transmit - instance->tx_frame_buffer = bit_buffer_alloc(ISO15693_3_POLLER_MAX_BUFFER_SIZE * 4); - // 2 bits per data bit on receive - instance->rx_frame_buffer = bit_buffer_alloc(ISO15693_3_POLLER_MAX_BUFFER_SIZE * 2); nfc_config(instance->nfc, NfcModeIso15693Poller); nfc_set_guard_time_us(instance->nfc, ISO15693_3_GUARD_TIME_US); @@ -44,14 +40,10 @@ static void iso15693_3_poller_free(Iso15693_3Poller* instance) { furi_assert(instance->tx_buffer); furi_assert(instance->rx_buffer); - furi_assert(instance->tx_frame_buffer); - furi_assert(instance->rx_frame_buffer); furi_assert(instance->data); bit_buffer_free(instance->tx_buffer); bit_buffer_free(instance->rx_buffer); - bit_buffer_free(instance->tx_frame_buffer); - bit_buffer_free(instance->rx_frame_buffer); iso15693_3_free(instance->data); free(instance); } diff --git a/lib/nfc/protocols/iso15693_3/iso15693_3_poller_i.c b/lib/nfc/protocols/iso15693_3/iso15693_3_poller_i.c index 12aff5759918..23a053608c66 100644 --- a/lib/nfc/protocols/iso15693_3/iso15693_3_poller_i.c +++ b/lib/nfc/protocols/iso15693_3/iso15693_3_poller_i.c @@ -6,16 +6,6 @@ #define BITS_IN_BYTE (8) -#define ISO15693_RESP_SOF_SIZE (5) -#define ISO15693_RESP_EOF_SIZE (5) -#define ISO15693_RESP_SOF_MASK (0x1FU) -#define ISO15693_RESP_SOF_PATTERN (0x17U) -#define ISO15693_RESP_EOF_PATTERN (0x1DU) - -#define ISO15693_RESP_PATTERN_MASK (0x03U) -#define ISO15693_RESP_PATTERN_0 (0x01U) -#define ISO15693_RESP_PATTERN_1 (0x02U) - #define ISO15693_3_POLLER_NUM_BLOCKS_PER_QUERY (32U) static Iso15693_3Error iso15693_3_poller_process_nfc_error(NfcError error) { @@ -52,63 +42,6 @@ static Iso15693_3Error iso15693_3_poller_prepare_trx(Iso15693_3Poller* instance) return Iso15693_3ErrorNone; } -static void iso15693_3_poller_encode_byte(uint8_t data, BitBuffer* out) { - static const uint8_t bit_patterns_1_out_of_4[] = {0x02, 0x08, 0x20, 0x80}; - - for(uint32_t i = 0; i < BITS_IN_BYTE; i += (BITS_IN_BYTE) / 4) { - const uint8_t bit_pair = (data >> i) & 0x03; - bit_buffer_append_byte(out, bit_patterns_1_out_of_4[bit_pair]); - } -} - -static void iso15693_3_poller_encode_frame(const BitBuffer* data, BitBuffer* frame_data) { - bit_buffer_append_byte(frame_data, 0x21); // Add SOF 1 out of 4 - - for(size_t i = 0; i < bit_buffer_get_size_bytes(data); ++i) { - iso15693_3_poller_encode_byte(bit_buffer_get_byte(data, i), frame_data); - } - - bit_buffer_append_byte(frame_data, 0x04); // Add EOF -} - -static bool iso15693_3_poller_decode_frame(BitBuffer* data, const BitBuffer* frame_data) { - bool decoded = false; - - do { - if(bit_buffer_get_size(frame_data) == 0) break; - // Check SOF - if((bit_buffer_get_byte(frame_data, 0) & ISO15693_RESP_SOF_MASK) != - ISO15693_RESP_SOF_PATTERN) - break; - - // 2 response bits = 1 data bit - for(uint32_t i = ISO15693_RESP_SOF_SIZE; - i < bit_buffer_get_size(frame_data) - ISO15693_RESP_SOF_SIZE; - i += BITS_IN_BYTE / 4) { - const uint8_t resp_byte = bit_buffer_get_byte_from_bit(frame_data, i); - - // Check EOF - if(resp_byte == ISO15693_RESP_EOF_PATTERN) { - decoded = true; - break; - } - - const uint8_t bit_pattern = resp_byte & ISO15693_RESP_PATTERN_MASK; - - if(bit_pattern == ISO15693_RESP_PATTERN_0) { - bit_buffer_append_bit(data, false); - } else if(bit_pattern == ISO15693_RESP_PATTERN_1) { - bit_buffer_append_bit(data, true); - } else { - break; - } - } - - } while(false); - - return decoded; -} - static Iso15693_3Error iso15693_3_poller_frame_exchange( Iso15693_3Poller* instance, const BitBuffer* tx_buffer, @@ -128,23 +61,12 @@ static Iso15693_3Error iso15693_3_poller_frame_exchange( bit_buffer_copy(instance->tx_buffer, tx_buffer); iso13239_crc_append(Iso13239CrcTypeDefault, instance->tx_buffer); - bit_buffer_reset(instance->tx_frame_buffer); - bit_buffer_reset(instance->rx_frame_buffer); - - iso15693_3_poller_encode_frame(instance->tx_buffer, instance->tx_frame_buffer); - - NfcError error = - nfc_trx(instance->nfc, instance->tx_frame_buffer, instance->rx_frame_buffer, fwt); + NfcError error = nfc_trx(instance->nfc, instance->tx_buffer, instance->rx_buffer, fwt); if(error != NfcErrorNone) { ret = iso15693_3_poller_process_nfc_error(error); break; } - if(!iso15693_3_poller_decode_frame(instance->rx_buffer, instance->rx_frame_buffer)) { - ret = Iso15693_3ErrorFraming; - break; - } - if(!iso13239_crc_check(Iso13239CrcTypeDefault, instance->rx_buffer)) { ret = Iso15693_3ErrorWrongCrc; break; diff --git a/lib/nfc/protocols/iso15693_3/iso15693_3_poller_i.h b/lib/nfc/protocols/iso15693_3/iso15693_3_poller_i.h index 6b6073132a20..154ee684c97c 100644 --- a/lib/nfc/protocols/iso15693_3/iso15693_3_poller_i.h +++ b/lib/nfc/protocols/iso15693_3/iso15693_3_poller_i.h @@ -4,7 +4,7 @@ #include "iso15693_3_i.h" -#include +#include #ifdef __cplusplus extern "C" { @@ -25,8 +25,6 @@ struct Iso15693_3Poller { Iso15693_3Data* data; BitBuffer* tx_buffer; BitBuffer* rx_buffer; - BitBuffer* tx_frame_buffer; - BitBuffer* rx_frame_buffer; NfcGenericEvent general_event; Iso15693_3PollerEvent iso15693_3_event; diff --git a/lib/nfc/protocols/mf_classic/crypto1.h b/lib/nfc/protocols/mf_classic/crypto1.h index 4273ac82c5b5..f2bdb272b0e6 100644 --- a/lib/nfc/protocols/mf_classic/crypto1.h +++ b/lib/nfc/protocols/mf_classic/crypto1.h @@ -1,6 +1,6 @@ #pragma once -#include +#include #ifdef __cplusplus extern "C" { diff --git a/lib/nfc/protocols/nfc_listener_defs.c b/lib/nfc/protocols/nfc_listener_defs.c index 0a44454f0934..094757f0832c 100644 --- a/lib/nfc/protocols/nfc_listener_defs.c +++ b/lib/nfc/protocols/nfc_listener_defs.c @@ -2,12 +2,15 @@ #include #include +#include #include #include const NfcListenerBase* nfc_listeners_api[NfcProtocolNum] = { [NfcProtocolIso14443_3a] = &nfc_listener_iso14443_3a, + [NfcProtocolIso14443_3b] = NULL, [NfcProtocolIso14443_4a] = &nfc_listener_iso14443_4a, + [NfcProtocolIso15693_3] = &nfc_listener_iso15693_3, [NfcProtocolMfUltralight] = &mf_ultralight_listener, [NfcProtocolMfClassic] = &mf_classic_listener, [NfcProtocolMfDesfire] = NULL, diff --git a/lib/signal_reader/SConscript b/lib/signal_reader/SConscript new file mode 100644 index 000000000000..ea7314420189 --- /dev/null +++ b/lib/signal_reader/SConscript @@ -0,0 +1,20 @@ +Import("env") + +env.Append( + CPPPATH=[ + "#/lib/signal_reader", + ], + SDK_HEADERS=[ + File("signal_reader.h"), + ], +) + +libenv = env.Clone(FW_LIB_NAME="signal_reader") +libenv.ApplyLibFlags() +libenv.Append(CCFLAGS=["-O3", "-funroll-loops", "-Ofast"]) + +sources = libenv.GlobRecursive("*.c*") + +lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources) +libenv.Install("${LIB_DIST_DIR}", lib) +Return("lib") diff --git a/lib/signal_reader/parsers/iso15693/iso15693_parser.c b/lib/signal_reader/parsers/iso15693/iso15693_parser.c new file mode 100644 index 000000000000..80652e57b08c --- /dev/null +++ b/lib/signal_reader/parsers/iso15693/iso15693_parser.c @@ -0,0 +1,334 @@ +#include "iso15693_parser.h" + +#include + +#include + +#define ISO15693_PARSER_BITSTREAM_BUFF_SIZE (8) +#define ISO15693_PARSER_BITRATE_F64MHZ (603U) + +#define TAG "Iso15693Parser" + +typedef enum { + Iso15693ParserStateParseSoF, + Iso15693ParserStateParse1OutOf4, + Iso15693ParserStateParse1OutOf256, + + Iso15693ParserStateNum, +} Iso15693ParserState; + +struct Iso15693Parser { + Iso15693ParserState state; + + SignalReader* signal_reader; + + uint8_t bitstream_buff[ISO15693_PARSER_BITSTREAM_BUFF_SIZE]; + size_t bitstream_idx; + uint8_t last_byte; + + bool signal_detected; + bool bit_offset_calculated; + uint8_t bit_offset; + size_t byte_idx; + size_t bytes_to_process; + + uint8_t next_byte; + uint16_t next_byte_part; + bool zero_found; + + BitBuffer* parsed_frame; + bool frame_parsed; + + Iso15693ParserCallback callback; + void* context; +}; + +typedef enum { + Iso15693ParserCommandProcessed, + Iso15693ParserCommandWaitData, + Iso15693ParserCommandFail, + Iso15693ParserCommandSuccess, +} Iso15693ParserCommand; + +typedef Iso15693ParserCommand (*Iso15693ParserStateHandler)(Iso15693Parser* instance); + +Iso15693Parser* iso15693_parser_alloc(const GpioPin* pin, size_t max_frame_size) { + Iso15693Parser* instance = malloc(sizeof(Iso15693Parser)); + instance->parsed_frame = bit_buffer_alloc(max_frame_size); + + instance->signal_reader = signal_reader_alloc(pin, ISO15693_PARSER_BITSTREAM_BUFF_SIZE); + signal_reader_set_sample_rate( + instance->signal_reader, SignalReaderTimeUnit64Mhz, ISO15693_PARSER_BITRATE_F64MHZ); + signal_reader_set_pull(instance->signal_reader, GpioPullDown); + signal_reader_set_polarity(instance->signal_reader, SignalReaderPolarityInverted); + + return instance; +} + +void iso15693_parser_free(Iso15693Parser* instance) { + furi_assert(instance); + + bit_buffer_free(instance->parsed_frame); + signal_reader_free(instance->signal_reader); + free(instance); +} + +void iso15693_parser_reset(Iso15693Parser* instance) { + furi_assert(instance); + + instance->state = Iso15693ParserStateParseSoF; + memset(instance->bitstream_buff, 0x00, sizeof(instance->bitstream_buff)); + instance->bitstream_idx = 0; + + instance->next_byte = 0; + instance->next_byte_part = 0; + + instance->bit_offset = 0; + instance->byte_idx = 0; + instance->bytes_to_process = 0; + instance->signal_detected = false; + instance->bit_offset_calculated = false; + + instance->last_byte = 0x00; + instance->zero_found = false; + + bit_buffer_reset(instance->parsed_frame); + instance->frame_parsed = false; +} + +static void signal_reader_callback(SignalReaderEvent event, void* context) { + furi_assert(context); + furi_assert(event.data->data); + furi_assert(event.data->len == ISO15693_PARSER_BITSTREAM_BUFF_SIZE / 2); + + Iso15693Parser* instance = context; + furi_assert(instance->callback); + + if(!instance->signal_detected) { + size_t i = 0; + for(i = 0; i < event.data->len; i++) { + if(event.data->data[i] != 0x00) { + break; + } + } + if(i != event.data->len) { + memcpy(instance->bitstream_buff, &event.data->data[i], event.data->len - i); + instance->bytes_to_process = event.data->len - i; + instance->signal_detected = true; + } + } else { + memcpy( + &instance->bitstream_buff[instance->bytes_to_process], + event.data->data, + event.data->len); + instance->bytes_to_process += event.data->len; + } + if(instance->bytes_to_process >= ISO15693_PARSER_BITSTREAM_BUFF_SIZE / 4) { + instance->callback(Iso15693ParserEventDataReceived, instance->context); + } +} + +static void iso15693_parser_start_signal_reader(Iso15693Parser* instance) { + iso15693_parser_reset(instance); + signal_reader_start(instance->signal_reader, signal_reader_callback, instance); +} + +void iso15693_parser_start( + Iso15693Parser* instance, + Iso15693ParserCallback callback, + void* context) { + furi_assert(instance); + furi_assert(callback); + + instance->callback = callback; + instance->context = context; + iso15693_parser_start_signal_reader(instance); +} + +void iso15693_parser_stop(Iso15693Parser* instance) { + furi_assert(instance); + + signal_reader_stop(instance->signal_reader); +} +static void iso15693_parser_prepare_buff(Iso15693Parser* instance) { + if(!instance->bit_offset_calculated) { + for(size_t i = 0; i < 8; i++) { + if(FURI_BIT(instance->bitstream_buff[0], i)) { + instance->bit_offset = i; + break; + } + } + if(instance->bit_offset == 7) { + if(FURI_BIT(instance->bitstream_buff[1], 0) == 1) { + instance->bit_offset = 0; + for(size_t i = 0; i < instance->bytes_to_process - 1; i++) { + instance->bitstream_buff[i] = instance->bitstream_buff[i + 1]; + instance->bytes_to_process--; + } + } + } else { + if(FURI_BIT(instance->bitstream_buff[0], instance->bit_offset + 1) == 1) { + instance->bit_offset++; + } + } + + for(size_t i = 0; i < instance->bytes_to_process - 1; i++) { + instance->bitstream_buff[i] = + (instance->bitstream_buff[i] >> instance->bit_offset) | + (instance->bitstream_buff[i + 1] << (8 - instance->bit_offset)); + } + instance->last_byte = instance->bitstream_buff[instance->bytes_to_process - 1]; + instance->bytes_to_process--; + instance->bit_offset_calculated = true; + } else { + for(size_t i = 0; i < instance->bytes_to_process; i++) { + uint8_t next_byte = instance->bitstream_buff[i]; + instance->bitstream_buff[i] = (instance->last_byte >> instance->bit_offset) | + (next_byte << (8 - instance->bit_offset)); + instance->last_byte = next_byte; + } + } +} + +static Iso15693ParserCommand iso15693_parser_parse_sof(Iso15693Parser* instance) { + Iso15693ParserCommand command = Iso15693ParserCommandProcessed; + const uint8_t sof_1_out_of_4 = 0x21; + const uint8_t sof_1_out_of_256 = 0x81; + const uint8_t eof = 0x01; + + if(instance->bitstream_buff[0] == sof_1_out_of_4) { + instance->state = Iso15693ParserStateParse1OutOf4; + instance->byte_idx = 1; + } else if(instance->bitstream_buff[0] == sof_1_out_of_256) { + instance->state = Iso15693ParserStateParse1OutOf256; + instance->byte_idx = 1; + } else if(instance->bitstream_buff[0] == eof) { + instance->frame_parsed = true; + command = Iso15693ParserCommandSuccess; + } else { + command = Iso15693ParserCommandFail; + } + + return command; +} + +static Iso15693ParserCommand iso15693_parser_parse_1_out_of_4(Iso15693Parser* instance) { + Iso15693ParserCommand command = Iso15693ParserCommandWaitData; + const uint8_t bit_patterns_1_out_of_4[] = {0x02, 0x08, 0x20, 0x80}; + const uint8_t eof = 0x04; + + for(size_t i = instance->byte_idx; i < instance->bytes_to_process; i++) { + // Check EoF + if(instance->next_byte_part == 0) { + if(instance->bitstream_buff[i] == eof) { + instance->frame_parsed = true; + command = Iso15693ParserCommandSuccess; + break; + } + } + + // Check next pattern + size_t j = 0; + for(j = 0; j < COUNT_OF(bit_patterns_1_out_of_4); j++) { + if(instance->bitstream_buff[i] == bit_patterns_1_out_of_4[j]) { + instance->next_byte |= j << (instance->next_byte_part * 2); + instance->next_byte_part++; + if(instance->next_byte_part == 4) { + instance->next_byte_part = 0; + bit_buffer_append_byte(instance->parsed_frame, instance->next_byte); + instance->next_byte = 0; + } + break; + } + } + if(j == COUNT_OF(bit_patterns_1_out_of_4)) { + command = Iso15693ParserCommandFail; + break; + } + } + instance->bytes_to_process = 0; + instance->byte_idx = 0; + + return command; +} + +static Iso15693ParserCommand iso15693_parser_parse_1_out_of_256(Iso15693Parser* instance) { + Iso15693ParserCommand command = Iso15693ParserCommandWaitData; + const uint8_t eof = 0x04; + + for(size_t i = instance->byte_idx; i < instance->bytes_to_process; i++) { + // Check EoF + if(instance->next_byte_part == 0) { + if(instance->bitstream_buff[i] == eof) { + instance->frame_parsed = true; + command = Iso15693ParserCommandSuccess; + break; + } + } + + if(instance->zero_found) { + if(instance->bitstream_buff[i] != 0x00) { + command = Iso15693ParserCommandFail; + break; + } + } else { + if(instance->bitstream_buff[i] != 0x00) { + for(size_t j = 0; j < 8; j++) { + if(FURI_BIT(instance->bitstream_buff[i], j) == 1) { + bit_buffer_append_byte( + instance->parsed_frame, instance->next_byte_part * 4 + j / 2); + } + } + } + } + instance->next_byte_part = (instance->next_byte_part + 1) % 64; + } + instance->bytes_to_process = 0; + instance->byte_idx = 0; + + return command; +} + +static const Iso15693ParserStateHandler iso15693_parser_state_handlers[Iso15693ParserStateNum] = { + [Iso15693ParserStateParseSoF] = iso15693_parser_parse_sof, + [Iso15693ParserStateParse1OutOf4] = iso15693_parser_parse_1_out_of_4, + [Iso15693ParserStateParse1OutOf256] = iso15693_parser_parse_1_out_of_256, +}; + +bool iso15693_parser_run(Iso15693Parser* instance) { + if(instance->bytes_to_process) { + iso15693_parser_prepare_buff(instance); + + Iso15693ParserCommand command = Iso15693ParserCommandProcessed; + while(command == Iso15693ParserCommandProcessed) { + command = iso15693_parser_state_handlers[instance->state](instance); + } + + if(command == Iso15693ParserCommandFail) { + FURI_LOG_D(TAG, "Frame parse failed"); + iso15693_parser_stop(instance); + iso15693_parser_start_signal_reader(instance); + } + } + + return instance->frame_parsed; +} + +size_t iso15693_parser_get_data_size_bytes(Iso15693Parser* instance) { + furi_assert(instance); + + return bit_buffer_get_size_bytes(instance->parsed_frame); +} + +void iso15693_parser_get_data( + Iso15693Parser* instance, + uint8_t* buff, + size_t buff_size, + size_t* data_bits) { + furi_assert(instance); + furi_assert(buff); + furi_assert(data_bits); + + bit_buffer_write_bytes(instance->parsed_frame, buff, buff_size); + *data_bits = bit_buffer_get_size(instance->parsed_frame); +} diff --git a/lib/signal_reader/parsers/iso15693/iso15693_parser.h b/lib/signal_reader/parsers/iso15693/iso15693_parser.h new file mode 100644 index 000000000000..3017a96d79ac --- /dev/null +++ b/lib/signal_reader/parsers/iso15693/iso15693_parser.h @@ -0,0 +1,42 @@ +#pragma once + +#include "../../signal_reader.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct Iso15693Parser Iso15693Parser; + +typedef enum { + Iso15693ParserEventDataReceived, +} Iso15693ParserEvent; + +typedef void (*Iso15693ParserCallback)(Iso15693ParserEvent event, void* context); + +Iso15693Parser* iso15693_parser_alloc(const GpioPin* pin, size_t max_frame_size); + +void iso15693_parser_free(Iso15693Parser* instance); + +void iso15693_parser_reset(Iso15693Parser* instance); + +void iso15693_parser_start( + Iso15693Parser* instance, + Iso15693ParserCallback callback, + void* context); + +void iso15693_parser_stop(Iso15693Parser* instance); + +bool iso15693_parser_run(Iso15693Parser* instance); + +size_t iso15693_parser_get_data_size_bytes(Iso15693Parser* instance); + +void iso15693_parser_get_data( + Iso15693Parser* instance, + uint8_t* buff, + size_t buff_size, + size_t* data_bits); + +#ifdef __cplusplus +} +#endif diff --git a/lib/signal_reader/signal_reader.c b/lib/signal_reader/signal_reader.c new file mode 100644 index 000000000000..b6758eebd605 --- /dev/null +++ b/lib/signal_reader/signal_reader.c @@ -0,0 +1,289 @@ +#include "signal_reader.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#define SIGNAL_READER_DMA DMA2 + +#define SIGNAL_READER_CAPTURE_TIM (TIM16) +#define SIGNAL_READER_CAPTURE_TIM_CHANNEL LL_TIM_CHANNEL_CH1 + +#define SIGNAL_READER_DMA_GPIO LL_DMA_CHANNEL_2 +#define SIGNAL_READER_DMA_GPIO_IRQ FuriHalInterruptIdDma2Ch2 +#define SIGNAL_READER_DMA_GPIO_DEF SIGNAL_READER_DMA, SIGNAL_READER_DMA_GPIO + +#define SIGNAL_READER_DMA_CNT_SYNC LL_DMA_CHANNEL_5 +#define SIGNAL_READER_DMA_CNT_SYNC_IRQ FuriHalInterruptIdDma2Ch5 +#define SIGNAL_READER_DMA_CNT_SYNC_DEF SIGNAL_READER_DMA, SIGNAL_READER_DMA_CNT_SYNC + +struct SignalReader { + size_t buffer_size; + const GpioPin* pin; + GpioPull pull; + SignalReaderPolarity polarity; + uint16_t* gpio_buffer; + uint8_t* bitstream_buffer; + + uint32_t tim_cnt_compensation; + uint32_t tim_arr; + + SignalReaderEvent event; + SignalReaderEventData event_data; + + SignalReaderCallback callback; + void* context; +}; + +#define GPIO_PIN_MAP(pin, prefix) \ + (((pin) == (LL_GPIO_PIN_0)) ? prefix##0 : \ + ((pin) == (LL_GPIO_PIN_1)) ? prefix##1 : \ + ((pin) == (LL_GPIO_PIN_2)) ? prefix##2 : \ + ((pin) == (LL_GPIO_PIN_3)) ? prefix##3 : \ + ((pin) == (LL_GPIO_PIN_4)) ? prefix##4 : \ + ((pin) == (LL_GPIO_PIN_5)) ? prefix##5 : \ + ((pin) == (LL_GPIO_PIN_6)) ? prefix##6 : \ + ((pin) == (LL_GPIO_PIN_7)) ? prefix##7 : \ + ((pin) == (LL_GPIO_PIN_8)) ? prefix##8 : \ + ((pin) == (LL_GPIO_PIN_9)) ? prefix##9 : \ + ((pin) == (LL_GPIO_PIN_10)) ? prefix##10 : \ + ((pin) == (LL_GPIO_PIN_11)) ? prefix##11 : \ + ((pin) == (LL_GPIO_PIN_12)) ? prefix##12 : \ + ((pin) == (LL_GPIO_PIN_13)) ? prefix##13 : \ + ((pin) == (LL_GPIO_PIN_14)) ? prefix##14 : \ + prefix##15) + +#define GET_DMAMUX_EXTI_LINE(pin) GPIO_PIN_MAP(pin, LL_DMAMUX_REQ_GEN_EXTI_LINE) + +SignalReader* signal_reader_alloc(const GpioPin* gpio_pin, uint32_t size) { + SignalReader* instance = malloc(sizeof(SignalReader)); + + instance->pin = gpio_pin; + instance->pull = GpioPullNo; + + instance->buffer_size = size; + instance->gpio_buffer = malloc(sizeof(uint16_t) * size * 8); + instance->bitstream_buffer = malloc(size); + + instance->event.data = &instance->event_data; + + return instance; +} + +void signal_reader_free(SignalReader* instance) { + furi_assert(instance); + furi_assert(instance->gpio_buffer); + furi_assert(instance->bitstream_buffer); + + free(instance->gpio_buffer); + free(instance->bitstream_buffer); + free(instance); +} + +void signal_reader_set_pull(SignalReader* instance, GpioPull pull) { + furi_assert(instance); + + instance->pull = pull; +} + +void signal_reader_set_polarity(SignalReader* instance, SignalReaderPolarity polarity) { + furi_assert(instance); + + instance->polarity = polarity; +} + +void signal_reader_set_sample_rate( + SignalReader* instance, + SignalReaderTimeUnit time_unit, + uint32_t time) { + furi_assert(instance); + UNUSED(time_unit); + + instance->tim_arr = time; +} + +static void furi_hal_sw_digital_pin_dma_rx_isr(void* context) { + SignalReader* instance = context; + + uint16_t* gpio_buff_start = NULL; + uint8_t* bitstream_buff_start = NULL; + + if(LL_DMA_IsActiveFlag_HT2(SIGNAL_READER_DMA)) { + LL_DMA_ClearFlag_HT2(SIGNAL_READER_DMA); + instance->event.type = SignalReaderEventTypeHalfBufferFilled; + gpio_buff_start = instance->gpio_buffer; + bitstream_buff_start = instance->bitstream_buffer; + + if(instance->callback) { + furi_assert(gpio_buff_start); + furi_assert(bitstream_buff_start); + + for(size_t i = 0; i < instance->buffer_size * 4; i++) { + if((i % 8) == 0) { + bitstream_buff_start[i / 8] = 0; + } + uint8_t bit = 0; + if(instance->polarity == SignalReaderPolarityNormal) { + bit = (gpio_buff_start[i] & instance->pin->pin) == instance->pin->pin; + } else { + bit = (gpio_buff_start[i] & instance->pin->pin) == 0; + } + bitstream_buff_start[i / 8] |= bit << (i % 8); + } + instance->event_data.data = bitstream_buff_start; + instance->event_data.len = instance->buffer_size / 2; + instance->callback(instance->event, instance->context); + } + } + if(LL_DMA_IsActiveFlag_TC2(SIGNAL_READER_DMA)) { + LL_DMA_ClearFlag_TC2(SIGNAL_READER_DMA); + instance->event.type = SignalReaderEventTypeFullBufferFilled; + gpio_buff_start = &instance->gpio_buffer[instance->buffer_size * 4]; + bitstream_buff_start = &instance->bitstream_buffer[instance->buffer_size / 2]; + + if(instance->callback) { + furi_assert(gpio_buff_start); + furi_assert(bitstream_buff_start); + + for(size_t i = 0; i < instance->buffer_size * 4; i++) { + if((i % 8) == 0) { + bitstream_buff_start[i / 8] = 0; + } + uint8_t bit = 0; + if(instance->polarity == SignalReaderPolarityNormal) { + bit = (gpio_buff_start[i] & instance->pin->pin) == instance->pin->pin; + } else { + bit = (gpio_buff_start[i] & instance->pin->pin) == 0; + } + bitstream_buff_start[i / 8] |= bit << (i % 8); + } + instance->event_data.data = bitstream_buff_start; + instance->event_data.len = instance->buffer_size / 2; + instance->callback(instance->event, instance->context); + } + } +} + +void signal_reader_start(SignalReader* instance, SignalReaderCallback callback, void* context) { + furi_assert(instance); + furi_assert(callback); + + instance->callback = callback; + instance->context = context; + + // EXTI delay compensation + instance->tim_cnt_compensation = 9; + + furi_hal_bus_enable(FuriHalBusTIM16); + + // Capture timer config + LL_TIM_SetPrescaler(SIGNAL_READER_CAPTURE_TIM, 0); + LL_TIM_SetCounterMode(SIGNAL_READER_CAPTURE_TIM, LL_TIM_COUNTERMODE_UP); + LL_TIM_SetAutoReload(SIGNAL_READER_CAPTURE_TIM, instance->tim_arr); + LL_TIM_SetClockDivision(SIGNAL_READER_CAPTURE_TIM, LL_TIM_CLOCKDIVISION_DIV1); + + LL_TIM_DisableARRPreload(SIGNAL_READER_CAPTURE_TIM); + LL_TIM_SetClockSource(SIGNAL_READER_CAPTURE_TIM, LL_TIM_CLOCKSOURCE_INTERNAL); + + // Configure TIM channel CC1 + LL_TIM_OC_InitTypeDef TIM_OC_InitStruct = {}; + TIM_OC_InitStruct.OCMode = LL_TIM_OCMODE_FROZEN; + TIM_OC_InitStruct.OCState = LL_TIM_OCSTATE_DISABLE; + TIM_OC_InitStruct.OCNState = LL_TIM_OCSTATE_DISABLE; + TIM_OC_InitStruct.CompareValue = (instance->tim_arr / 2); + TIM_OC_InitStruct.OCPolarity = LL_TIM_OCPOLARITY_HIGH; + LL_TIM_OC_Init( + SIGNAL_READER_CAPTURE_TIM, SIGNAL_READER_CAPTURE_TIM_CHANNEL, &TIM_OC_InitStruct); + LL_TIM_OC_DisableFast(SIGNAL_READER_CAPTURE_TIM, SIGNAL_READER_CAPTURE_TIM_CHANNEL); + + LL_TIM_SetTriggerOutput(SIGNAL_READER_CAPTURE_TIM, LL_TIM_TRGO_RESET); + LL_TIM_DisableMasterSlaveMode(SIGNAL_READER_CAPTURE_TIM); + + // Start + LL_TIM_GenerateEvent_UPDATE(SIGNAL_READER_CAPTURE_TIM); + + /* We need the EXTI to be configured as interrupt generating line, but no ISR registered */ + furi_hal_gpio_init( + instance->pin, GpioModeInterruptRiseFall, instance->pull, GpioSpeedVeryHigh); + furi_delay_ms(10); + + /* Set DMAMUX request generation signal ID on specified DMAMUX channel */ + LL_DMAMUX_SetRequestSignalID( + DMAMUX1, LL_DMAMUX_REQ_GEN_0, GET_DMAMUX_EXTI_LINE(instance->pin->pin)); + /* Set the polarity of the signal on which the DMA request is generated */ + LL_DMAMUX_SetRequestGenPolarity(DMAMUX1, LL_DMAMUX_REQ_GEN_0, LL_DMAMUX_REQ_GEN_POL_RISING); + /* Set the number of DMA requests that will be authorized after a generation event */ + LL_DMAMUX_SetGenRequestNb(DMAMUX1, LL_DMAMUX_REQ_GEN_0, 1); + + // Configure DMA Sync + LL_DMA_SetMemoryAddress( + SIGNAL_READER_DMA_CNT_SYNC_DEF, (uint32_t)&instance->tim_cnt_compensation); + LL_DMA_SetPeriphAddress( + SIGNAL_READER_DMA_CNT_SYNC_DEF, (uint32_t) & (SIGNAL_READER_CAPTURE_TIM->CNT)); + LL_DMA_ConfigTransfer( + SIGNAL_READER_DMA_CNT_SYNC_DEF, + LL_DMA_DIRECTION_MEMORY_TO_PERIPH | LL_DMA_MODE_CIRCULAR | LL_DMA_PERIPH_NOINCREMENT | + LL_DMA_MEMORY_NOINCREMENT | LL_DMA_PDATAALIGN_HALFWORD | LL_DMA_MDATAALIGN_HALFWORD | + LL_DMA_PRIORITY_VERYHIGH); + LL_DMA_SetDataLength(SIGNAL_READER_DMA_CNT_SYNC_DEF, 1); + LL_DMA_SetPeriphRequest(SIGNAL_READER_DMA_CNT_SYNC_DEF, LL_DMAMUX_REQ_GENERATOR0); + + // Configure DMA Rx pin + LL_DMA_SetMemoryAddress(SIGNAL_READER_DMA_GPIO_DEF, (uint32_t)instance->gpio_buffer); + LL_DMA_SetPeriphAddress(SIGNAL_READER_DMA_GPIO_DEF, (uint32_t) & (instance->pin->port->IDR)); + LL_DMA_ConfigTransfer( + SIGNAL_READER_DMA_GPIO_DEF, + LL_DMA_DIRECTION_PERIPH_TO_MEMORY | LL_DMA_MODE_CIRCULAR | LL_DMA_PERIPH_NOINCREMENT | + LL_DMA_MEMORY_INCREMENT | LL_DMA_PDATAALIGN_HALFWORD | LL_DMA_MDATAALIGN_HALFWORD | + LL_DMA_PRIORITY_HIGH); + LL_DMA_SetDataLength(SIGNAL_READER_DMA_GPIO_DEF, instance->buffer_size * 8); + LL_DMA_SetPeriphRequest(SIGNAL_READER_DMA_GPIO_DEF, LL_DMAMUX_REQ_TIM16_CH1); + + // Configure DMA Channel CC1 + LL_TIM_EnableDMAReq_CC1(SIGNAL_READER_CAPTURE_TIM); + LL_TIM_CC_EnableChannel(SIGNAL_READER_CAPTURE_TIM, SIGNAL_READER_CAPTURE_TIM_CHANNEL); + + // Start DMA irq, higher priority than normal + furi_hal_interrupt_set_isr_ex( + SIGNAL_READER_DMA_GPIO_IRQ, 14, furi_hal_sw_digital_pin_dma_rx_isr, instance); + + // Start DMA Sync timer + LL_DMA_EnableChannel(SIGNAL_READER_DMA_CNT_SYNC_DEF); + LL_DMAMUX_EnableRequestGen(DMAMUX1, LL_DMAMUX_REQ_GEN_0); + + // Start DMA Rx pin + LL_DMA_EnableChannel(SIGNAL_READER_DMA_GPIO_DEF); + // Strat timer + LL_TIM_SetCounter(SIGNAL_READER_CAPTURE_TIM, 0); + LL_TIM_EnableCounter(SIGNAL_READER_CAPTURE_TIM); + + // Need to clear flags before enabling DMA !!!! + if(LL_DMA_IsActiveFlag_TC2(SIGNAL_READER_DMA)) LL_DMA_ClearFlag_TC1(SIGNAL_READER_DMA); + if(LL_DMA_IsActiveFlag_TE2(SIGNAL_READER_DMA)) LL_DMA_ClearFlag_TE1(SIGNAL_READER_DMA); + LL_DMA_EnableIT_TC(SIGNAL_READER_DMA_GPIO_DEF); + LL_DMA_EnableIT_HT(SIGNAL_READER_DMA_GPIO_DEF); +} + +void signal_reader_stop(SignalReader* instance) { + furi_assert(instance); + + furi_hal_interrupt_set_isr(SIGNAL_READER_DMA_GPIO_IRQ, NULL, NULL); + + // Deinit DMA Rx pin + LL_DMA_DeInit(SIGNAL_READER_DMA_GPIO_DEF); + // Deinit DMA Sync timer + LL_DMA_DeInit(SIGNAL_READER_DMA_CNT_SYNC_DEF); + + furi_hal_bus_disable(FuriHalBusTIM16); + + memset(instance->gpio_buffer, 0, sizeof(uint16_t) * instance->buffer_size * 8); + memset(instance->bitstream_buffer, 0, instance->buffer_size); +} diff --git a/lib/signal_reader/signal_reader.h b/lib/signal_reader/signal_reader.h new file mode 100644 index 000000000000..2213465c37c2 --- /dev/null +++ b/lib/signal_reader/signal_reader.h @@ -0,0 +1,60 @@ +#pragma once + +#include +#include +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + SignalReaderEventTypeHalfBufferFilled, + SignalReaderEventTypeFullBufferFilled, +} SignalReaderEventType; + +typedef struct { + uint8_t* data; + size_t len; +} SignalReaderEventData; + +typedef struct { + SignalReaderEventType type; + SignalReaderEventData* data; +} SignalReaderEvent; + +typedef enum { + SignalReaderTimeUnit64Mhz, +} SignalReaderTimeUnit; + +typedef enum { + SignalReaderPolarityNormal, + SignalReaderPolarityInverted, +} SignalReaderPolarity; + +typedef void (*SignalReaderCallback)(SignalReaderEvent event, void* context); + +typedef struct SignalReader SignalReader; + +SignalReader* signal_reader_alloc(const GpioPin* gpio_pin, uint32_t size); + +void signal_reader_free(SignalReader* instance); + +void signal_reader_set_pull(SignalReader* instance, GpioPull pull); + +void signal_reader_set_polarity(SignalReader* instance, SignalReaderPolarity polarity); + +void signal_reader_set_sample_rate( + SignalReader* instance, + SignalReaderTimeUnit time_unit, + uint32_t time); + +void signal_reader_start(SignalReader* instance, SignalReaderCallback callback, void* context); + +void signal_reader_stop(SignalReader* instance); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/helpers/bit_buffer.c b/lib/toolbox/bit_buffer.c similarity index 100% rename from lib/nfc/helpers/bit_buffer.c rename to lib/toolbox/bit_buffer.c diff --git a/lib/nfc/helpers/bit_buffer.h b/lib/toolbox/bit_buffer.h similarity index 100% rename from lib/nfc/helpers/bit_buffer.h rename to lib/toolbox/bit_buffer.h