diff --git a/applications/main/nfc/helpers/protocol_support/felica/felica.c b/applications/main/nfc/helpers/protocol_support/felica/felica.c new file mode 100644 index 000000000000..8c9aae3c73ac --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/felica/felica.c @@ -0,0 +1,111 @@ +#include "felica.h" +#include "felica_render.h" + +#include + +#include "nfc/nfc_app_i.h" + +#include "../nfc_protocol_support_gui_common.h" + +static void nfc_scene_info_on_enter_felica(NfcApp* instance) { + const NfcDevice* device = instance->nfc_device; + const FelicaData* data = nfc_device_get_data(device, NfcProtocolFelica); + + FuriString* temp_str = furi_string_alloc(); + furi_string_cat_printf( + temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); + nfc_render_felica_info(data, NfcProtocolFormatTypeFull, temp_str); + + widget_add_text_scroll_element( + instance->widget, 0, 0, 128, 64, furi_string_get_cstr(temp_str)); + + furi_string_free(temp_str); +} + +static NfcCommand nfc_scene_read_poller_callback_felica(NfcGenericEvent event, void* context) { + furi_assert(event.protocol == NfcProtocolFelica); + + NfcApp* instance = context; + const FelicaPollerEvent* felica_event = event.data; + + if(felica_event->type == FelicaPollerEventTypeReady) { + nfc_device_set_data( + instance->nfc_device, NfcProtocolFelica, nfc_poller_get_data(instance->poller)); + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess); + return NfcCommandStop; + } + + return NfcCommandContinue; +} + +static void nfc_scene_read_on_enter_felica(NfcApp* instance) { + nfc_poller_start(instance->poller, nfc_scene_read_poller_callback_felica, instance); +} + +static void nfc_scene_read_success_on_enter_felica(NfcApp* instance) { + const NfcDevice* device = instance->nfc_device; + const FelicaData* data = nfc_device_get_data(device, NfcProtocolFelica); + + FuriString* temp_str = furi_string_alloc(); + furi_string_cat_printf( + temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); + nfc_render_felica_info(data, NfcProtocolFormatTypeShort, temp_str); + + widget_add_text_scroll_element( + instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str)); + + furi_string_free(temp_str); +} + +static bool nfc_scene_info_on_event_felica(NfcApp* instance, uint32_t event) { + if(event == GuiButtonTypeRight) { + scene_manager_next_scene(instance->scene_manager, NfcSceneNotImplemented); + return true; + } + + return false; +} + +static bool nfc_scene_saved_menu_on_event_felica(NfcApp* instance, uint32_t event) { + if(event == SubmenuIndexCommonEdit) { + scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid); + return true; + } + + return false; +} + +const NfcProtocolSupportBase nfc_protocol_support_felica = { + .features = NfcProtocolFeatureNone, // TODO: Implement better UID editing, + + .scene_info = + { + .on_enter = nfc_scene_info_on_enter_felica, + .on_event = nfc_scene_info_on_event_felica, + }, + .scene_read = + { + .on_enter = nfc_scene_read_on_enter_felica, + .on_event = NULL, + }, + .scene_read_menu = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_read_success = + { + .on_enter = nfc_scene_read_success_on_enter_felica, + .on_event = NULL, + }, + .scene_saved_menu = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_scene_saved_menu_on_event_felica, + }, + .scene_emulate = + { + .on_enter = NULL, + .on_event = NULL, + }, +}; diff --git a/applications/main/nfc/helpers/protocol_support/felica/felica.h b/applications/main/nfc/helpers/protocol_support/felica/felica.h new file mode 100644 index 000000000000..e581b6709e7a --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/felica/felica.h @@ -0,0 +1,5 @@ +#pragma once + +#include "../nfc_protocol_support_base.h" + +extern const NfcProtocolSupportBase nfc_protocol_support_felica; diff --git a/applications/main/nfc/helpers/protocol_support/felica/felica_render.c b/applications/main/nfc/helpers/protocol_support/felica/felica_render.c new file mode 100644 index 000000000000..3142b2c6dbf0 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/felica/felica_render.c @@ -0,0 +1,19 @@ +#include "felica_render.h" + +void nfc_render_felica_info( + const FelicaData* data, + NfcProtocolFormatType format_type, + FuriString* str) { + furi_string_cat_printf(str, "IDm:"); + + for(size_t i = 0; i < FELICA_IDM_SIZE; i++) { + furi_string_cat_printf(str, " %02X", data->idm.data[i]); + } + + if(format_type == NfcProtocolFormatTypeFull) { + furi_string_cat_printf(str, "\nPMm:"); + for(size_t i = 0; i < FELICA_PMM_SIZE; ++i) { + furi_string_cat_printf(str, " %02X", data->pmm.data[i]); + } + } +} diff --git a/applications/main/nfc/helpers/protocol_support/felica/felica_render.h b/applications/main/nfc/helpers/protocol_support/felica/felica_render.h new file mode 100644 index 000000000000..6d9816fc6630 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/felica/felica_render.h @@ -0,0 +1,10 @@ +#pragma once + +#include + +#include "../nfc_protocol_support_render_common.h" + +void nfc_render_felica_info( + const FelicaData* data, + NfcProtocolFormatType format_type, + FuriString* str); diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_defs.c b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_defs.c index 82d910ec5bef..4b3d15168fc3 100644 --- a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_defs.c +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_defs.c @@ -4,6 +4,7 @@ #include "iso14443_3b/iso14443_3b.h" #include "iso14443_4a/iso14443_4a.h" #include "iso15693_3/iso15693_3.h" +#include "felica/felica.h" #include "mf_ultralight/mf_ultralight.h" #include "mf_classic/mf_classic.h" #include "mf_desfire/mf_desfire.h" @@ -13,6 +14,7 @@ const NfcProtocolSupportBase* nfc_protocol_support[NfcProtocolNum] = { [NfcProtocolIso14443_3b] = &nfc_protocol_support_iso14443_3b, [NfcProtocolIso14443_4a] = &nfc_protocol_support_iso14443_4a, [NfcProtocolIso15693_3] = &nfc_protocol_support_iso15693_3, + [NfcProtocolFelica] = &nfc_protocol_support_felica, [NfcProtocolMfUltralight] = &nfc_protocol_support_mf_ultralight, [NfcProtocolMfClassic] = &nfc_protocol_support_mf_classic, [NfcProtocolMfDesfire] = &nfc_protocol_support_mf_desfire, diff --git a/applications/main/nfc/nfc_app.c b/applications/main/nfc/nfc_app.c index 9b35813e1b74..4240e61f277e 100644 --- a/applications/main/nfc/nfc_app.c +++ b/applications/main/nfc/nfc_app.c @@ -46,7 +46,6 @@ NfcApp* nfc_app_alloc() { instance->view_dispatcher, nfc_back_event_callback); instance->nfc = nfc_alloc(); - instance->scanner = nfc_scanner_alloc(instance->nfc); instance->mf_ul_auth = mf_ultralight_auth_alloc(); @@ -147,7 +146,6 @@ void nfc_app_free(NfcApp* instance) { } nfc_free(instance->nfc); - nfc_scanner_free(instance->scanner); mf_ultralight_auth_free(instance->mf_ul_auth); diff --git a/applications/main/nfc/scenes/nfc_scene_delete.c b/applications/main/nfc/scenes/nfc_scene_delete.c index d36b5fb54bf2..c1a676168ad5 100644 --- a/applications/main/nfc/scenes/nfc_scene_delete.c +++ b/applications/main/nfc/scenes/nfc_scene_delete.c @@ -35,7 +35,6 @@ void nfc_scene_delete_on_enter(void* context) { furi_string_set_str(temp_str, nfc_device_get_name(nfc->nfc_device, NfcDeviceNameTypeFull)); widget_add_string_element( nfc->widget, 64, 34, AlignCenter, AlignTop, FontSecondary, furi_string_get_cstr(temp_str)); - widget_add_string_element(nfc->widget, 64, 44, AlignCenter, AlignTop, FontSecondary, "NFC-A"); furi_string_free(temp_str); view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); diff --git a/applications/main/nfc/scenes/nfc_scene_detect.c b/applications/main/nfc/scenes/nfc_scene_detect.c index 1ff5acb3ddf1..5e7fc6528213 100644 --- a/applications/main/nfc/scenes/nfc_scene_detect.c +++ b/applications/main/nfc/scenes/nfc_scene_detect.c @@ -25,6 +25,7 @@ void nfc_scene_detect_on_enter(void* context) { nfc_app_reset_detected_protocols(instance); + instance->scanner = nfc_scanner_alloc(instance->nfc); nfc_scanner_start(instance->scanner, nfc_scene_detect_scan_callback, instance); nfc_blink_detect_start(instance); @@ -53,6 +54,7 @@ void nfc_scene_detect_on_exit(void* context) { NfcApp* instance = context; nfc_scanner_stop(instance->scanner); + nfc_scanner_free(instance->scanner); popup_reset(instance->popup); nfc_blink_stop(instance); diff --git a/applications/main/nfc/scenes/nfc_scene_select_protocol.c b/applications/main/nfc/scenes/nfc_scene_select_protocol.c index fff7ab979964..502f91772097 100644 --- a/applications/main/nfc/scenes/nfc_scene_select_protocol.c +++ b/applications/main/nfc/scenes/nfc_scene_select_protocol.c @@ -21,6 +21,7 @@ void nfc_scene_select_protocol_on_enter(void* context) { } else { prefix = "Read as"; submenu_set_header(submenu, "Multi-protocol card"); + notification_message(instance->notifications, &sequence_single_vibro); } for(size_t i = 0; i < instance->protocols_detected_num; i++) { diff --git a/firmware/targets/f7/furi_hal/f_hal_nfc.c b/firmware/targets/f7/furi_hal/f_hal_nfc.c index f9cca46ff44c..048ccbb9a5c0 100644 --- a/firmware/targets/f7/furi_hal/f_hal_nfc.c +++ b/firmware/targets/f7/furi_hal/f_hal_nfc.c @@ -531,6 +531,46 @@ FHalNfcError f_hal_nfc_set_mode(FHalNfcMode mode, FHalNfcBitrate bitrate) { 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); + + // 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 error; @@ -543,7 +583,29 @@ FHalNfcError f_hal_nfc_reset_mode() { st25r3916_direct_cmd(handle, ST25R3916_CMD_STOP); // 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, + ST25R3916_REG_BIT_RATE, + ST25R3916_REG_BIT_RATE_txrate_mask | ST25R3916_REG_BIT_RATE_rxrate_mask); + + // Write default values + st25r3916_write_reg(handle, ST25R3916_REG_RX_CONF1, 0); + st25r3916_write_reg( + handle, + ST25R3916_REG_RX_CONF2, + ST25R3916_REG_RX_CONF2_sqm_dyn | ST25R3916_REG_RX_CONF2_agc_en | + ST25R3916_REG_RX_CONF2_agc_m); + + st25r3916_write_reg( + handle, + ST25R3916_REG_CORR_CONF1, + ST25R3916_REG_CORR_CONF1_corr_s7 | ST25R3916_REG_CORR_CONF1_corr_s4 | + 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(); return error; 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 f75c51828d05..1055a16d08f3 100644 --- a/firmware/targets/f7/furi_hal/f_hal_nfc_timer.c +++ b/firmware/targets/f7/furi_hal/f_hal_nfc_timer.c @@ -44,8 +44,8 @@ static FHalNfcTimerConfig f_hal_nfc_timers[FHalNfcTimerCount] = { .pin = &gpio_ext_pa6, .timer = TIM17, .bus = FuriHalBusTIM17, - .prescaler = 15, - .freq_khz = 4000U, + .prescaler = 31, + .freq_khz = 2000U, .event = FHalNfcEventInternalTypeTimerBlockTxExpired, .irq_id = FuriHalInterruptIdTim1TrgComTim17, .irq_type = TIM1_TRG_COM_TIM17_IRQn, diff --git a/firmware/targets/furi_hal_include/f_hal_nfc.h b/firmware/targets/furi_hal_include/f_hal_nfc.h index 9c847dc5342d..d8cd41141781 100644 --- a/firmware/targets/furi_hal_include/f_hal_nfc.h +++ b/firmware/targets/furi_hal_include/f_hal_nfc.h @@ -47,7 +47,7 @@ typedef enum { FHalNfcModeIso14443bPoller, FHalNfcModeIso14443bListener, - FHalNfcModeNfcfPoller, + FHalNfcModeFelicaPoller, FHalNfcModeNfcfListener, FHalNfcModeIso15693Poller, diff --git a/lib/nfc/helpers/felica_crc.c b/lib/nfc/helpers/felica_crc.c new file mode 100644 index 000000000000..d5e0853a1095 --- /dev/null +++ b/lib/nfc/helpers/felica_crc.c @@ -0,0 +1,52 @@ +#include "felica_crc.h" + +#include + +#define FELICA_CRC_POLY (0x1021U) // Polynomial: x^16 + x^12 + x^5 + 1 +#define FELICA_CRC_INIT (0x0000U) + +uint16_t felica_crc_calculate(const uint8_t* data, size_t length) { + uint16_t crc = FELICA_CRC_INIT; + + for(size_t i = 0; i < length; i++) { + crc ^= ((uint16_t)data[i] << 8); + for(size_t j = 0; j < 8; j++) { + if(crc & 0x8000) { + crc <<= 1; + crc ^= FELICA_CRC_POLY; + } else { + crc <<= 1; + } + } + } + + return (crc << 8) | (crc >> 8); +} + +void felica_crc_append(BitBuffer* buf) { + const uint8_t* data = bit_buffer_get_data(buf); + const size_t data_size = bit_buffer_get_size_bytes(buf); + + const uint16_t crc = felica_crc_calculate(data, data_size); + bit_buffer_append_bytes(buf, (const uint8_t*)&crc, FELICA_CRC_SIZE); +} + +bool felica_crc_check(const BitBuffer* buf) { + const size_t data_size = bit_buffer_get_size_bytes(buf); + if(data_size <= FELICA_CRC_SIZE) return false; + + uint16_t crc_received; + bit_buffer_write_bytes_mid(buf, &crc_received, data_size - FELICA_CRC_SIZE, FELICA_CRC_SIZE); + + const uint8_t* data = bit_buffer_get_data(buf); + const uint16_t crc_calc = felica_crc_calculate(data, data_size - FELICA_CRC_SIZE); + + return (crc_calc == crc_received); +} + +void felica_crc_trim(BitBuffer* buf) { + const size_t data_size = bit_buffer_get_size_bytes(buf); + furi_assert(data_size > FELICA_CRC_SIZE); + + bit_buffer_set_size_bytes(buf, data_size - FELICA_CRC_SIZE); +} diff --git a/lib/nfc/helpers/felica_crc.h b/lib/nfc/helpers/felica_crc.h new file mode 100644 index 000000000000..d1dc29e74db7 --- /dev/null +++ b/lib/nfc/helpers/felica_crc.h @@ -0,0 +1,22 @@ +#pragma once + +#include +#include + +#include "bit_buffer.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define FELICA_CRC_SIZE sizeof(uint16_t) + +void felica_crc_append(BitBuffer* buf); + +bool felica_crc_check(const BitBuffer* buf); + +void felica_crc_trim(BitBuffer* buf); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/nfc.c b/lib/nfc/nfc.c index e64c308e7b83..65e041255d9f 100644 --- a/lib/nfc/nfc.c +++ b/lib/nfc/nfc.c @@ -267,6 +267,9 @@ void nfc_config(Nfc* instance, NfcMode mode) { f_hal_nfc_low_power_mode_stop(); f_hal_nfc_set_mode(FHalNfcModeIso15693Listener, FHalNfcBitrate26p48); instance->config_state = NfcConfigurationStateDone; + } else if(mode == NfcModeFelicaPoller) { + f_hal_nfc_set_mode(FHalNfcModeFelicaPoller, FHalNfcBitrate106); + instance->config_state = NfcConfigurationStateDone; } } diff --git a/lib/nfc/nfc.h b/lib/nfc/nfc.h index f5a135e17e3e..b4a023db66b4 100644 --- a/lib/nfc/nfc.h +++ b/lib/nfc/nfc.h @@ -44,7 +44,7 @@ typedef enum { NfcModeIso14443aListener, NfcModeIso14443bPoller, NfcModeIso14443bListener, - NfcModeNfcfPoller, + NfcModeFelicaPoller, NfcModeNfcfListener, NfcModeIso15693Poller, NfcModeIso15693Listener, diff --git a/lib/nfc/nfc_scanner.c b/lib/nfc/nfc_scanner.c index e4c10a919783..f32110be9381 100644 --- a/lib/nfc/nfc_scanner.c +++ b/lib/nfc/nfc_scanner.c @@ -34,10 +34,15 @@ struct NfcScanner { NfcEvent nfc_event; + NfcProtocol first_detected_protocol; + size_t base_protocols_num; size_t base_protocols_idx; NfcProtocol base_protocols[NfcProtocolNum]; + size_t detected_base_protocols_num; + NfcProtocol detected_base_protocols[NfcProtocolNum]; + size_t children_protocols_num; size_t children_protocols_idx; NfcProtocol children_protocols[NfcProtocolNum]; @@ -58,6 +63,7 @@ static void nfc_scanner_reset(NfcScanner* instance) { instance->children_protocols_num = 0; instance->detected_protocols_num = 0; + instance->detected_base_protocols_num = 0; instance->current_protocol = 0; } @@ -72,38 +78,52 @@ void nfc_scanner_state_handler_idle(NfcScanner* instance) { instance->base_protocols_num++; } } - instance->base_protocols_idx = 0; FURI_LOG_D(TAG, "Found %d base protocols", instance->base_protocols_num); + instance->first_detected_protocol = NfcProtocolInvalid; instance->state = NfcScannerStateTryBasePollers; } void nfc_scanner_state_handler_try_base_pollers(NfcScanner* instance) { - instance->current_protocol = instance->base_protocols[instance->base_protocols_idx]; + do { + instance->current_protocol = instance->base_protocols[instance->base_protocols_idx]; - NfcPoller* poller = nfc_poller_alloc(instance->nfc, instance->current_protocol); - bool protocol_detected = nfc_poller_detect(poller); - nfc_poller_free(poller); + if(instance->first_detected_protocol == instance->current_protocol) { + instance->state = NfcScannerStateFindChildrenProtocols; + break; + } - if(protocol_detected) { - instance->detected_protocols[instance->detected_protocols_num] = - instance->current_protocol; - instance->detected_protocols_num++; - } + NfcPoller* poller = nfc_poller_alloc(instance->nfc, instance->current_protocol); + bool protocol_detected = nfc_poller_detect(poller); + nfc_poller_free(poller); + + if(protocol_detected) { + instance->detected_protocols[instance->detected_protocols_num] = + instance->current_protocol; + instance->detected_protocols_num++; + + instance->detected_base_protocols[instance->detected_base_protocols_num] = + instance->current_protocol; + instance->detected_base_protocols_num++; + + if(instance->first_detected_protocol == NfcProtocolInvalid) { + instance->first_detected_protocol = instance->current_protocol; + instance->current_protocol = NfcProtocolInvalid; + } + } - if(instance->detected_protocols_num > 0) { - instance->state = NfcScannerStateFindChildrenProtocols; - } else { instance->base_protocols_idx = (instance->base_protocols_idx + 1) % instance->base_protocols_num; - } + } while(false); } void nfc_scanner_state_handler_find_children_protocols(NfcScanner* instance) { for(size_t i = 0; i < NfcProtocolNum; i++) { - if(nfc_protocol_has_parent(i, instance->current_protocol)) { - instance->children_protocols[instance->children_protocols_num] = i; - instance->children_protocols_num++; + for(size_t j = 0; j < instance->detected_base_protocols_num; j++) { + if(nfc_protocol_has_parent(i, instance->detected_base_protocols[j])) { + instance->children_protocols[instance->children_protocols_num] = i; + instance->children_protocols_num++; + } } } diff --git a/lib/nfc/protocols/felica/felica.c b/lib/nfc/protocols/felica/felica.c new file mode 100644 index 000000000000..de8b88813f35 --- /dev/null +++ b/lib/nfc/protocols/felica/felica.c @@ -0,0 +1,147 @@ +#include "felica.h" + +#include + +#include + +#define FELICA_PROTOCOL_NAME "FeliCa" +#define FELICA_DEVICE_NAME "FeliCa" + +#define FELICA_DATA_FORMAT_VERSION "Data format version" +#define FELICA_MANUFACTURE_ID "Manufacture id" +#define FELICA_MANUFACTURE_PARAMETER "Manufacture parameter" + +static const uint32_t felica_data_format_version = 1; + +const NfcDeviceBase nfc_device_felica = { + .protocol_name = FELICA_PROTOCOL_NAME, + .alloc = (NfcDeviceAlloc)felica_alloc, + .free = (NfcDeviceFree)felica_free, + .reset = (NfcDeviceReset)felica_reset, + .copy = (NfcDeviceCopy)felica_copy, + .verify = (NfcDeviceVerify)felica_verify, + .load = (NfcDeviceLoad)felica_load, + .save = (NfcDeviceSave)felica_save, + .is_equal = (NfcDeviceEqual)felica_is_equal, + .get_name = (NfcDeviceGetName)felica_get_device_name, + .get_uid = (NfcDeviceGetUid)felica_get_uid, + .set_uid = (NfcDeviceSetUid)felica_set_uid, + .get_base_data = (NfcDeviceGetBaseData)felica_get_base_data, +}; + +FelicaData* felica_alloc() { + FelicaData* data = malloc(sizeof(FelicaData)); + return data; +} + +void felica_free(FelicaData* data) { + furi_assert(data); + + free(data); +} + +void felica_reset(FelicaData* data) { + memset(data, 0, sizeof(FelicaData)); +} + +void felica_copy(FelicaData* data, const FelicaData* other) { + furi_assert(data); + furi_assert(other); + + *data = *other; +} + +bool felica_verify(FelicaData* data, const FuriString* device_type) { + UNUSED(data); + UNUSED(device_type); + + return false; +} + +bool felica_load(FelicaData* data, FlipperFormat* ff, uint32_t version) { + furi_assert(data); + + bool parsed = false; + + do { + if(version < NFC_UNIFIED_FORMAT_VERSION) break; + + uint32_t data_format_version = 0; + if(!flipper_format_read_uint32(ff, FELICA_DATA_FORMAT_VERSION, &data_format_version, 1)) + break; + if(data_format_version != felica_data_format_version) break; + if(!flipper_format_read_hex(ff, FELICA_MANUFACTURE_ID, data->idm.data, FELICA_IDM_SIZE)) + break; + if(!flipper_format_read_hex( + ff, FELICA_MANUFACTURE_PARAMETER, data->pmm.data, FELICA_PMM_SIZE)) + break; + + parsed = true; + } while(false); + + return parsed; +} + +bool felica_save(const FelicaData* data, FlipperFormat* ff) { + furi_assert(data); + + bool saved = false; + + do { + if(!flipper_format_write_comment_cstr(ff, FELICA_PROTOCOL_NAME " specific data")) break; + if(!flipper_format_write_uint32( + ff, FELICA_DATA_FORMAT_VERSION, &felica_data_format_version, 1)) + break; + if(!flipper_format_write_hex(ff, FELICA_MANUFACTURE_ID, data->idm.data, FELICA_IDM_SIZE)) + break; + if(!flipper_format_write_hex( + ff, FELICA_MANUFACTURE_PARAMETER, data->pmm.data, FELICA_PMM_SIZE)) + break; + + saved = true; + } while(false); + + return saved; +} + +bool felica_is_equal(const FelicaData* data, const FelicaData* other) { + furi_assert(data); + furi_assert(other); + + return memcmp(data, other, sizeof(FelicaData)) == 0; +} + +const char* felica_get_device_name(const FelicaData* data, NfcDeviceNameType name_type) { + UNUSED(data); + UNUSED(name_type); + + return FELICA_DEVICE_NAME; +} + +const uint8_t* felica_get_uid(const FelicaData* data, size_t* uid_len) { + furi_assert(data); + + // Consider Manufacturer ID as UID + if(uid_len) { + *uid_len = FELICA_IDM_SIZE; + } + + return data->idm.data; +} + +bool felica_set_uid(FelicaData* data, const uint8_t* uid, size_t uid_len) { + furi_assert(data); + + // Consider Manufacturer ID as UID + const bool uid_valid = uid_len == FELICA_IDM_SIZE; + if(uid_valid) { + memcpy(data->idm.data, uid, uid_len); + } + + return uid_valid; +} + +const FelicaData* felica_get_base_data(const FelicaData* data) { + UNUSED(data); + furi_crash("No base data"); +} diff --git a/lib/nfc/protocols/felica/felica.h b/lib/nfc/protocols/felica/felica.h new file mode 100644 index 000000000000..cacd489f013c --- /dev/null +++ b/lib/nfc/protocols/felica/felica.h @@ -0,0 +1,76 @@ +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define FELICA_IDM_SIZE (8U) +#define FELICA_PMM_SIZE (8U) + +#define FELICA_GUARD_TIME_US (20000U) +#define FELICA_FDT_POLL_FC (10000U) +#define FELICA_POLL_POLL_MIN_US (1280U) + +#define FELICA_SYSTEM_CODE_CODE (0xFFFFU) +#define FELICA_TIME_SLOT_1 (0x00U) +#define FELICA_TIME_SLOT_2 (0x01U) +#define FELICA_TIME_SLOT_4 (0x03U) +#define FELICA_TIME_SLOT_8 (0x07U) +#define FELICA_TIME_SLOT_16 (0x0FU) + +typedef enum { + FelicaErrorNone, + FelicaErrorNotPresent, + FelicaErrorColResFailed, + FelicaErrorBufferOverflow, + FelicaErrorCommunication, + FelicaErrorFieldOff, + FelicaErrorWrongCrc, + FelicaErrorTimeout, +} FelicaError; + +typedef struct { + uint8_t data[FELICA_IDM_SIZE]; +} FelicaIDm; + +typedef struct { + uint8_t data[FELICA_PMM_SIZE]; +} FelicaPMm; + +typedef struct { + FelicaIDm idm; + FelicaPMm pmm; +} FelicaData; + +extern const NfcDeviceBase nfc_device_felica; + +FelicaData* felica_alloc(); + +void felica_free(FelicaData* data); + +void felica_reset(FelicaData* data); + +void felica_copy(FelicaData* data, const FelicaData* other); + +bool felica_verify(FelicaData* data, const FuriString* device_type); + +bool felica_load(FelicaData* data, FlipperFormat* ff, uint32_t version); + +bool felica_save(const FelicaData* data, FlipperFormat* ff); + +bool felica_is_equal(const FelicaData* data, const FelicaData* other); + +const char* felica_get_device_name(const FelicaData* data, NfcDeviceNameType name_type); + +const uint8_t* felica_get_uid(const FelicaData* data, size_t* uid_len); + +bool felica_set_uid(FelicaData* data, const uint8_t* uid, size_t uid_len); + +const FelicaData* felica_get_base_data(const FelicaData* data); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/felica/felica_poller.c b/lib/nfc/protocols/felica/felica_poller.c new file mode 100644 index 000000000000..824043aebbaf --- /dev/null +++ b/lib/nfc/protocols/felica/felica_poller.c @@ -0,0 +1,117 @@ +#include "felica_poller_i.h" + +#include + +#include + +const FelicaData* felica_poller_get_data(FelicaPoller* instance) { + furi_assert(instance); + furi_assert(instance->data); + + return instance->data; +} + +static FelicaPoller* felica_poller_alloc(Nfc* nfc) { + furi_assert(nfc); + + FelicaPoller* instance = malloc(sizeof(FelicaPoller)); + instance->nfc = nfc; + instance->tx_buffer = bit_buffer_alloc(FELICA_POLLER_MAX_BUFFER_SIZE); + instance->rx_buffer = bit_buffer_alloc(FELICA_POLLER_MAX_BUFFER_SIZE); + + nfc_config(instance->nfc, NfcModeFelicaPoller); + nfc_set_guard_time_us(instance->nfc, FELICA_GUARD_TIME_US); + nfc_set_fdt_poll_fc(instance->nfc, FELICA_FDT_POLL_FC); + nfc_set_fdt_poll_poll_us(instance->nfc, FELICA_POLL_POLL_MIN_US); + instance->data = felica_alloc(); + + instance->felica_event.data = &instance->felica_event_data; + instance->general_event.protocol = NfcProtocolFelica; + instance->general_event.data = &instance->felica_event; + instance->general_event.instance = instance; + + return instance; +} + +static void felica_poller_free(FelicaPoller* instance) { + furi_assert(instance); + + furi_assert(instance->tx_buffer); + furi_assert(instance->rx_buffer); + furi_assert(instance->data); + + bit_buffer_free(instance->tx_buffer); + bit_buffer_free(instance->rx_buffer); + felica_free(instance->data); + free(instance); +} + +static void + felica_poller_set_callback(FelicaPoller* instance, NfcGenericCallback callback, void* context) { + furi_assert(instance); + furi_assert(callback); + + instance->callback = callback; + instance->context = context; +} + +static NfcCommand felica_poller_run(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.protocol == NfcProtocolInvalid); + furi_assert(event.data); + + FelicaPoller* instance = context; + NfcEvent* nfc_event = event.data; + NfcCommand command = NfcCommandContinue; + + if(nfc_event->type == NfcEventTypePollerReady) { + if(instance->state != FelicaPollerStateActivated) { + FelicaError error = felica_poller_async_activate(instance, instance->data); + if(error == FelicaErrorNone) { + instance->felica_event.type = FelicaPollerEventTypeReady; + instance->felica_event_data.error = error; + command = instance->callback(instance->general_event, instance->context); + } else { + instance->felica_event.type = FelicaPollerEventTypeError; + instance->felica_event_data.error = error; + command = instance->callback(instance->general_event, instance->context); + // Add delay to switch context + furi_delay_ms(100); + } + } else { + instance->felica_event.type = FelicaPollerEventTypeReady; + instance->felica_event_data.error = FelicaErrorNone; + command = instance->callback(instance->general_event, instance->context); + } + } + + return command; +} + +static bool felica_poller_detect(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.data); + furi_assert(event.instance); + furi_assert(event.protocol = NfcProtocolInvalid); + + bool protocol_detected = false; + FelicaPoller* instance = context; + NfcEvent* nfc_event = event.data; + furi_assert(instance->state == FelicaPollerStateIdle); + + if(nfc_event->type == NfcEventTypePollerReady) { + FelicaError error = felica_poller_async_activate(instance, instance->data); + protocol_detected = (error == FelicaErrorNone); + } + + return protocol_detected; +} + +const NfcPollerBase nfc_poller_felica = { + .alloc = (NfcPollerAlloc)felica_poller_alloc, + .free = (NfcPollerFree)felica_poller_free, + .set_callback = (NfcPollerSetCallback)felica_poller_set_callback, + .run = (NfcPollerRun)felica_poller_run, + .detect = (NfcPollerDetect)felica_poller_detect, + .get_data = (NfcPollerGetData)felica_poller_get_data, +}; diff --git a/lib/nfc/protocols/felica/felica_poller.h b/lib/nfc/protocols/felica/felica_poller.h new file mode 100644 index 000000000000..7d0c9525e770 --- /dev/null +++ b/lib/nfc/protocols/felica/felica_poller.h @@ -0,0 +1,30 @@ +#pragma once + +#include "felica.h" +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct FelicaPoller FelicaPoller; + +typedef enum { + FelicaPollerEventTypeError, + FelicaPollerEventTypeReady, +} FelicaPollerEventType; + +typedef struct { + FelicaError error; +} FelicaPollerEventData; + +typedef struct { + FelicaPollerEventType type; + FelicaPollerEventData* data; +} FelicaPollerEvent; + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/felica/felica_poller_defs.h b/lib/nfc/protocols/felica/felica_poller_defs.h new file mode 100644 index 000000000000..fc99dc75219b --- /dev/null +++ b/lib/nfc/protocols/felica/felica_poller_defs.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern const NfcPollerBase nfc_poller_felica; + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/felica/felica_poller_i.c b/lib/nfc/protocols/felica/felica_poller_i.c new file mode 100644 index 000000000000..48dcd2eac82c --- /dev/null +++ b/lib/nfc/protocols/felica/felica_poller_i.c @@ -0,0 +1,114 @@ +#include "felica_poller_i.h" + +#include + +#define TAG "FelicaPoller" + +static FelicaError felica_poller_process_error(NfcError error) { + switch(error) { + case NfcErrorNone: + return FelicaErrorNone; + case NfcErrorTimeout: + return FelicaErrorTimeout; + default: + return FelicaErrorNotPresent; + } +} + +static FelicaError felica_poller_frame_exchange( + FelicaPoller* instance, + const BitBuffer* tx_buffer, + BitBuffer* rx_buffer, + uint32_t fwt) { + furi_assert(instance); + + const size_t tx_bytes = bit_buffer_get_size_bytes(tx_buffer); + furi_assert(tx_bytes <= bit_buffer_get_capacity_bytes(instance->tx_buffer) - FELICA_CRC_SIZE); + + felica_crc_append(instance->tx_buffer); + + FelicaError ret = FelicaErrorNone; + + do { + NfcError error = nfc_trx(instance->nfc, instance->tx_buffer, instance->rx_buffer, fwt); + if(error != NfcErrorNone) { + ret = felica_poller_process_error(error); + break; + } + + bit_buffer_copy(rx_buffer, instance->rx_buffer); + if(!felica_crc_check(instance->rx_buffer)) { + ret = FelicaErrorWrongCrc; + break; + } + + felica_crc_trim(rx_buffer); + } while(false); + + return ret; +} + +FelicaError felica_poller_async_polling( + FelicaPoller* instance, + const FelicaPollerPollingCommand* cmd, + FelicaPollerPollingResponse* resp) { + furi_assert(instance); + furi_assert(cmd); + furi_assert(resp); + + bit_buffer_set_size_bytes(instance->tx_buffer, 2); + // Set frame len + // TODO Set length in felica_poller_frame_exchange() ? + bit_buffer_set_byte(instance->tx_buffer, 0, sizeof(FelicaPollerPollingCommand) + 2); + // Set command code + bit_buffer_set_byte(instance->tx_buffer, 1, FELICA_POLLER_CMD_POLLING_CODE); + // Set other data + bit_buffer_append_bytes( + instance->tx_buffer, (uint8_t*)cmd, sizeof(FelicaPollerPollingCommand)); + + FelicaError error = felica_poller_frame_exchange( + instance, instance->tx_buffer, instance->rx_buffer, FELICA_POLLER_POLLING_FWT); + + if(error == FelicaErrorNone) { + // TODO extract length in felica_poller_frame_exchange() ? + bit_buffer_write_bytes_mid(instance->rx_buffer, resp->idm.data, 2, sizeof(FelicaIDm)); + bit_buffer_write_bytes_mid( + instance->rx_buffer, resp->pmm.data, sizeof(FelicaIDm) + 2, sizeof(FelicaPMm)); + } + + return error; +} + +FelicaError felica_poller_async_activate(FelicaPoller* instance, FelicaData* data) { + furi_assert(instance); + + felica_reset(data); + + FelicaError ret; + + do { + bit_buffer_reset(instance->tx_buffer); + bit_buffer_reset(instance->rx_buffer); + + // Send Polling command + const FelicaPollerPollingCommand polling_cmd = { + .system_code = FELICA_SYSTEM_CODE_CODE, + .request_code = 0, + .time_slot = FELICA_TIME_SLOT_1, + }; + FelicaPollerPollingResponse polling_resp = {}; + + ret = felica_poller_async_polling(instance, &polling_cmd, &polling_resp); + + if(ret != FelicaErrorNone) { + FURI_LOG_T(TAG, "Activation failed error: %d", ret); + break; + } + + data->idm = polling_resp.idm; + data->pmm = polling_resp.pmm; + instance->state = FelicaPollerStateActivated; + } while(false); + + return ret; +} diff --git a/lib/nfc/protocols/felica/felica_poller_i.h b/lib/nfc/protocols/felica/felica_poller_i.h new file mode 100644 index 000000000000..592bfb03bd5a --- /dev/null +++ b/lib/nfc/protocols/felica/felica_poller_i.h @@ -0,0 +1,59 @@ +#pragma once + +#include "felica_poller.h" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define FELICA_POLLER_MAX_BUFFER_SIZE (256U) + +#define FELICA_POLLER_POLLING_FWT (200000U) + +#define FELICA_POLLER_CMD_POLLING_CODE (0x00U) + +typedef enum { + FelicaPollerStateIdle, + FelicaPollerStateActivated, +} FelicaPollerState; + +struct FelicaPoller { + Nfc* nfc; + FelicaPollerState state; + FelicaData* data; + BitBuffer* tx_buffer; + BitBuffer* rx_buffer; + + NfcGenericEvent general_event; + FelicaPollerEvent felica_event; + FelicaPollerEventData felica_event_data; + NfcGenericCallback callback; + void* context; +}; + +typedef struct { + uint16_t system_code; + uint8_t request_code; + uint8_t time_slot; +} FelicaPollerPollingCommand; + +typedef struct { + FelicaIDm idm; + FelicaPMm pmm; + uint8_t request_data[2]; +} FelicaPollerPollingResponse; + +const FelicaData* felica_poller_get_data(FelicaPoller* instance); + +FelicaError felica_poller_async_polling( + FelicaPoller* instance, + const FelicaPollerPollingCommand* cmd, + FelicaPollerPollingResponse* resp); + +FelicaError felica_poller_async_activate(FelicaPoller* instance, FelicaData* data); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/nfc_device_defs.c b/lib/nfc/protocols/nfc_device_defs.c index 8d1e9f3abe21..e2fa5833f301 100644 --- a/lib/nfc/protocols/nfc_device_defs.c +++ b/lib/nfc/protocols/nfc_device_defs.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -15,6 +16,7 @@ const NfcDeviceBase* nfc_devices[NfcProtocolNum] = { [NfcProtocolIso14443_3b] = &nfc_device_iso14443_3b, [NfcProtocolIso14443_4a] = &nfc_device_iso14443_4a, [NfcProtocolIso15693_3] = &nfc_device_iso15693_3, + [NfcProtocolFelica] = &nfc_device_felica, [NfcProtocolMfUltralight] = &nfc_device_mf_ultralight, [NfcProtocolMfClassic] = &nfc_device_mf_classic, [NfcProtocolMfDesfire] = &nfc_device_mf_desfire, diff --git a/lib/nfc/protocols/nfc_poller_defs.c b/lib/nfc/protocols/nfc_poller_defs.c index 4de37695b715..b4e64611b6d1 100644 --- a/lib/nfc/protocols/nfc_poller_defs.c +++ b/lib/nfc/protocols/nfc_poller_defs.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -13,6 +14,7 @@ const NfcPollerBase* nfc_pollers_api[NfcProtocolNum] = { [NfcProtocolIso14443_3b] = &nfc_poller_iso14443_3b, [NfcProtocolIso14443_4a] = &nfc_poller_iso14443_4a, [NfcProtocolIso15693_3] = &nfc_poller_iso15693_3, + [NfcProtocolFelica] = &nfc_poller_felica, [NfcProtocolMfUltralight] = &mf_ultralight_poller, [NfcProtocolMfClassic] = &mf_classic_poller, [NfcProtocolMfDesfire] = &mf_desfire_poller, diff --git a/lib/nfc/protocols/nfc_protocol.c b/lib/nfc/protocols/nfc_protocol.c index bca4441a0748..f35af7b8e648 100644 --- a/lib/nfc/protocols/nfc_protocol.c +++ b/lib/nfc/protocols/nfc_protocol.c @@ -14,7 +14,7 @@ typedef struct { * | * +------------------------+-----------+---------+ * | | | | - * ISO14443-3A ISO14443-3B NFC-F ISO15693-3 + * ISO14443-3A ISO14443-3B Felica ISO15693-3 * | | * +---------------+-------------+ SLIX2 * | | | @@ -60,6 +60,12 @@ static const NfcProtocolTreeNode nfc_protocol_nodes[NfcProtocolNum] = { .children_num = 0, .children_protocol = NULL, }, + [NfcProtocolFelica] = + { + .parent_protocol = NfcProtocolInvalid, + .children_num = 0, + .children_protocol = NULL, + }, [NfcProtocolMfUltralight] = { .parent_protocol = NfcProtocolIso14443_3a, diff --git a/lib/nfc/protocols/nfc_protocol.h b/lib/nfc/protocols/nfc_protocol.h index ee0a021ed0bf..814075533626 100644 --- a/lib/nfc/protocols/nfc_protocol.h +++ b/lib/nfc/protocols/nfc_protocol.h @@ -11,6 +11,7 @@ typedef enum { NfcProtocolIso14443_3b, NfcProtocolIso14443_4a, NfcProtocolIso15693_3, + NfcProtocolFelica, NfcProtocolMfUltralight, NfcProtocolMfClassic, NfcProtocolMfDesfire,