6 Commits
Author SHA1 Message Date
GameTec-liveandGitHub c6f37c80df Merge branch 'main' into clang-format 2025-09-02 19:44:58 +02:00
GameTec_live 07e7ed2abd fix: use proper trigger 2025-09-02 19:44:05 +02:00
GameTec_live 3b0d14759b Revert "fix: run clang format"
This reverts commit 1e79e8bd43.
2025-09-02 19:32:34 +02:00
GameTec_live 9248223a39 doc: changelog 2025-09-02 19:27:42 +02:00
GameTec_live 1e79e8bd43 fix: run clang format 2025-09-02 19:26:13 +02:00
GameTec_live e0790f677b feat: clang formatter 2025-09-02 19:26:01 +02:00
70 changed files with 1467 additions and 12696 deletions
+5
View File
@@ -0,0 +1,5 @@
BreakBeforeBinaryOperators: All
ColumnLimit: 120
BasedOnStyle: Google
BreakBeforeBraces: Stroustrup
IndentWidth: 4
+35
View File
@@ -0,0 +1,35 @@
name: clang-format Check
on:
pull_request_target:
paths:
- '**.c'
- '**.h'
- '**.cpp'
workflow_dispatch:
jobs:
check-format:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Install clang-format
run: sudo apt install -y clang-format
- name: Get files and check format
run: |
exit_code=0
while IFS= read -r -d '' f; do
if ! diff -u "$f" <(clang-format "$f"); then
echo "Formatting issue in $f"
exit_code=1
fi
done < <(find . -type f \( -name '*.c' -o -name '*.h' \) ! -path './firmware/nrf52_sdk/*' ! -path './firmware/nrf52_sdk/**' -print0)
if [ $exit_code -ne 0 ]; then
echo "Clang-format check failed."
exit 1
else
echo "All files are properly formatted."
fi
+2 -1
View File
@@ -1,10 +1,11 @@
name: Lint (pyrefly + ruff) name: Lint (pyrefly + ruff)
on: on:
pull_request: pull_request_target:
paths: paths:
- "software/**" - "software/**"
- ".github/workflows/**" - ".github/workflows/**"
- "**.py"
workflow_dispatch: workflow_dispatch:
jobs: jobs:
-5
View File
@@ -708,8 +708,3 @@ FodyWeavers.xsd
# End of https://www.toptal.com/developers/gitignore/api/visualstudio,c++,c,python,visualstudiocode,macos,windows # End of https://www.toptal.com/developers/gitignore/api/visualstudio,c++,c,python,visualstudiocode,macos,windows
software/script/tests/nonces.bin software/script/tests/nonces.bin
software/script/nonces.bin software/script/nonces.bin
.vscode/settings.json
.vscode/tasks.json
firmware/compile_commands.json
firmware/application/compile_commands.json
software/src/target_arch_detect.c
+1 -18
View File
@@ -3,25 +3,8 @@ All notable changes to this project will be documented in this file.
This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log... This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log...
## [unreleased][unreleased] ## [unreleased][unreleased]
- Added PAC/Stanley LF protocol support: read, emulate and T55xx clone (@kevihiiin, @danieltwagner) - Added clang formatter (@GameTec-live)
- Fix firmware application USB serial number (@taichunmin)
- Added ioProx LF protocol support (read, emulate and T55xx clone)
- Added `hf mfu nfcimport` to import Flipper Zero `.nfc` files into MFU/NTAG emulator slots, with `--amiibo` flag for automatic PWD/PACK derivation (@fmuk)
- Added commands to dump and clone Mifare tags
- Fix bad missing tools warning (@suut)
- Fix for FAST_READ command for nfc - mf0 tags
- Rewrite of the dynamic and static locks logic for NTAG213, NTAG215 and NTAG216; we shouldn't take into account the block lock bits
- Fixed an issue where we wouldn't be able to change CFG0 and CFG1 for NTAG213, NTAG215 and NTG216 once a password was added even if the cfg bit was reset.
- Fix for static nested key recovery (@jekkos) - Fix for static nested key recovery (@jekkos)
- Fix LEDs being stuck on after battery check (@suut)
- Add TCP support for the CLI (@suut)
- Fix build on Android in Termux (@suut)
- Fix the issue where some reader cause CU to enter a strange state (@xianglin1998)
- The transmission performance of USB has been improved (@xianglin1998)
- Added cmd for set mf1 config 'field_off_do_reset' (@xianglin1998)
- Fix Windows build (@suut)
- Added `hf 14a config` to deal with badly configured cards (@azuwis)
- New Symmetrical LED Animation Mode and Improved Minimal Mode (@WillyJL)
## [v2.1.0][2025-09-02] ## [v2.1.0][2025-09-02]
- Added UV, formatter and linter. Contribution guidelines. (@GameTec-live) - Added UV, formatter and linter. Contribution guidelines. (@GameTec-live)
+1 -2
View File
@@ -26,7 +26,6 @@ Read the [available documentation](https://github.com/RfidResearchGroup/Chameleo
* [ChameleonUltraGUI](https://github.com/GameTec-live/ChameleonUltraGUI) * [ChameleonUltraGUI](https://github.com/GameTec-live/ChameleonUltraGUI)
* [MTools BLE](https://github.com/RfidResearchGroup/ChameleonUltra/wiki/mtoolsble) * [MTools BLE](https://github.com/RfidResearchGroup/ChameleonUltra/wiki/mtoolsble)
* [Mifare Chameleon Tool (iOS only, Beta)](https://apps.apple.com/it/app/mifare-chameleon-tool/id6761231484)
# Videos # Videos
@@ -46,4 +45,4 @@ Where do you find the community?
* Devices/chameleon-ultra for usage discussions * Devices/chameleon-ultra for usage discussions
* [GameTec_live discord server](https://discord.gg/DJ2A4wxncK) * [GameTec_live discord server](https://discord.gg/DJ2A4wxncK)
###### Searching for the docs repo? Find it [here](https://github.com/RfidResearchGroup/ChameleonUltraDocs) ###### Searching for the docs repo? Find it [here](https://github.com/RfidResearchGroup/ChameleonUltraDocs)
-8
View File
@@ -28,7 +28,6 @@ SRC_FILES += \
$(PROJ_DIR)/rfid/nfctag/tag_persistence.c \ $(PROJ_DIR)/rfid/nfctag/tag_persistence.c \
$(PROJ_DIR)/rfid/nfctag/hf/crypto1_helper.c \ $(PROJ_DIR)/rfid/nfctag/hf/crypto1_helper.c \
$(PROJ_DIR)/rfid/nfctag/hf/nfc_14a.c \ $(PROJ_DIR)/rfid/nfctag/hf/nfc_14a.c \
$(PROJ_DIR)/rfid/nfctag/hf/nfc_14a_4.c \
$(PROJ_DIR)/rfid/nfctag/hf/nfc_mf1.c \ $(PROJ_DIR)/rfid/nfctag/hf/nfc_mf1.c \
$(PROJ_DIR)/rfid/nfctag/hf/nfc_mf0_ntag.c \ $(PROJ_DIR)/rfid/nfctag/hf/nfc_mf0_ntag.c \
$(PROJ_DIR)/rfid/nfctag/lf/lf_tag_em.c \ $(PROJ_DIR)/rfid/nfctag/lf/lf_tag_em.c \
@@ -37,8 +36,6 @@ SRC_FILES += \
$(PROJ_DIR)/rfid/nfctag/lf/utils/manchester.c \ $(PROJ_DIR)/rfid/nfctag/lf/utils/manchester.c \
$(PROJ_DIR)/rfid/nfctag/lf/protocols/em410x.c \ $(PROJ_DIR)/rfid/nfctag/lf/protocols/em410x.c \
$(PROJ_DIR)/rfid/nfctag/lf/protocols/hidprox.c \ $(PROJ_DIR)/rfid/nfctag/lf/protocols/hidprox.c \
$(PROJ_DIR)/rfid/nfctag/lf/protocols/pac.c \
$(PROJ_DIR)/rfid/nfctag/lf/protocols/ioprox.c \
$(PROJ_DIR)/rfid/nfctag/lf/protocols/viking.c \ $(PROJ_DIR)/rfid/nfctag/lf/protocols/viking.c \
$(PROJ_DIR)/rfid/nfctag/lf/protocols/wiegand.c \ $(PROJ_DIR)/rfid/nfctag/lf/protocols/wiegand.c \
$(PROJ_DIR)/utils/dataframe.c \ $(PROJ_DIR)/utils/dataframe.c \
@@ -343,15 +340,10 @@ ifeq (${CURRENT_DEVICE_TYPE}, ${CHAMELEON_ULTRA})
$(PROJ_DIR)/rfid/reader/hf/rc522.c \ $(PROJ_DIR)/rfid/reader/hf/rc522.c \
$(PROJ_DIR)/rfid/reader/lf/lf_125khz_radio.c \ $(PROJ_DIR)/rfid/reader/lf/lf_125khz_radio.c \
$(PROJ_DIR)/rfid/reader/lf/lf_em410x_data.c \ $(PROJ_DIR)/rfid/reader/lf/lf_em410x_data.c \
$(PROJ_DIR)/rfid/reader/lf/lf_em4x05_data.c \
$(PROJ_DIR)/rfid/reader/lf/lf_gap.c \
$(PROJ_DIR)/rfid/reader/lf/lf_reader_generic.c \
$(PROJ_DIR)/rfid/reader/lf/lf_reader_data.c \ $(PROJ_DIR)/rfid/reader/lf/lf_reader_data.c \
$(PROJ_DIR)/rfid/reader/lf/lf_reader_main.c \ $(PROJ_DIR)/rfid/reader/lf/lf_reader_main.c \
$(PROJ_DIR)/rfid/reader/lf/lf_t55xx_data.c \ $(PROJ_DIR)/rfid/reader/lf/lf_t55xx_data.c \
$(PROJ_DIR)/rfid/reader/lf/lf_hidprox_data.c \ $(PROJ_DIR)/rfid/reader/lf/lf_hidprox_data.c \
$(PROJ_DIR)/rfid/reader/lf/lf_pac_data.c \
$(PROJ_DIR)/rfid/reader/lf/lf_ioprox_data.c \
$(PROJ_DIR)/rfid/reader/lf/lf_viking_data.c \ $(PROJ_DIR)/rfid/reader/lf/lf_viking_data.c \
INC_FOLDERS +=\ INC_FOLDERS +=\
File diff suppressed because it is too large Load Diff
+38 -189
View File
@@ -41,10 +41,6 @@ NRF_LOG_MODULE_REGISTER();
#include "tag_persistence.h" #include "tag_persistence.h"
#include "settings.h" #include "settings.h"
#if defined(PROJECT_CHAMELEON_ULTRA)
#include "rc522.h"
#endif
// Defining soft timers // Defining soft timers
APP_TIMER_DEF(m_button_check_timer); // Timer for button debounce APP_TIMER_DEF(m_button_check_timer); // Timer for button debounce
@@ -60,9 +56,6 @@ static bool m_is_a_btn_release = false;
static bool m_system_off_processing = false; static bool m_system_off_processing = false;
// NFC field generator state
volatile bool m_is_field_on = false;
// cpu reset reason // cpu reset reason
static uint32_t m_reset_source; static uint32_t m_reset_source;
static uint32_t m_gpregret_val; static uint32_t m_gpregret_val;
@@ -146,41 +139,12 @@ static void gpio_te_init(void) {
APP_ERROR_CHECK(err_code); APP_ERROR_CHECK(err_code);
} }
#if defined(PROJECT_CHAMELEON_ULTRA)
static void field_generator_rainbow_loop(void) {
static uint8_t color_index = 0;
static uint32_t last_update = 0;
if (!m_is_field_on) return;
uint32_t now = app_timer_cnt_get();
if (app_timer_cnt_diff_compute(now, last_update) < APP_TIMER_TICKS(100)) {
return;
}
last_update = now;
// Rainbow colors
const uint8_t colors[] = {RGB_RED, RGB_YELLOW, RGB_GREEN, RGB_CYAN, RGB_BLUE, RGB_MAGENTA};
set_slot_light_color(colors[color_index]);
uint32_t *led_pins = hw_get_led_array();
// Light up all LEDs with current color
for (int i = 0; i < RGB_LIST_NUM; i++) {
nrf_gpio_pin_set(led_pins[i]);
}
color_index = (color_index + 1) % 6;
}
#endif
/**@brief Button Matrix Events /**@brief Button Matrix Events
*/ */
static void button_pin_handler(nrf_drv_gpiote_pin_t pin, nrf_gpiote_polarity_t action) { static void button_pin_handler(nrf_drv_gpiote_pin_t pin, nrf_gpiote_polarity_t action) {
device_mode_t mode = get_device_mode(); device_mode_t mode = get_device_mode();
// Allow button operations in both tag and reader mode // Temporarily allow only the analog card mode to respond to button operations
if (mode == DEVICE_MODE_TAG || mode == DEVICE_MODE_READER) { if (mode == DEVICE_MODE_TAG) {
static nrf_drv_gpiote_pin_t pin_static; // Use static internal variables to store the GPIO where the current event occurred static nrf_drv_gpiote_pin_t pin_static; // Use static internal variables to store the GPIO where the current event occurred
pin_static = pin; // Cache the button that currently triggers the event into an internal variable pin_static = pin; // Cache the button that currently triggers the event into an internal variable
app_timer_start(m_button_check_timer, APP_TIMER_TICKS(50), &pin_static); // Start timer anti-shake app_timer_start(m_button_check_timer, APP_TIMER_TICKS(50), &pin_static); // Start timer anti-shake
@@ -198,9 +162,7 @@ static void timer_button_event_handle(void *arg) {
NRF_LOG_INFO("BUTTON press during shutdown"); NRF_LOG_INFO("BUTTON press during shutdown");
return; return;
} }
nrf_drv_gpiote_pin_t pin = *(nrf_drv_gpiote_pin_t *)arg; nrf_drv_gpiote_pin_t pin = *(nrf_drv_gpiote_pin_t *)arg;
// Check here if the current GPIO is at the pressed level // Check here if the current GPIO is at the pressed level
if (nrf_gpio_pin_read(pin) == 1) { if (nrf_gpio_pin_read(pin) == 1) {
if (pin == BUTTON_1) { if (pin == BUTTON_1) {
@@ -301,28 +263,24 @@ static void system_off_enter(void) {
for (uint8_t i = 0; i < RGB_LIST_NUM; i++) { for (uint8_t i = 0; i < RGB_LIST_NUM; i++) {
nrf_gpio_pin_clear(p_led_array[i]); nrf_gpio_pin_clear(p_led_array[i]);
} }
// Power off animation
uint8_t animation_config = settings_get_animation_config(); uint8_t animation_config = settings_get_animation_config();
uint8_t slot = tag_emulation_get_slot();
uint8_t dir = slot > 3 ? 1 : 0;
uint8_t color = get_color_by_slot(slot);
if (m_reset_source & (NRF_POWER_RESETREAS_NFC_MASK | NRF_POWER_RESETREAS_LPCOMP_MASK)) {
if (m_reset_source & NRF_POWER_RESETREAS_NFC_MASK) {
color = 1;
} else {
color = 2;
}
}
if (animation_config == SettingsAnimationModeFull) { if (animation_config == SettingsAnimationModeFull) {
if (m_system_off_processing) rgb_marquee_sweep_from_to(color, slot, dir ? 7 : 0); uint8_t slot = tag_emulation_get_slot();
if (m_system_off_processing) rgb_marquee_sweep_fade(color, dir, 7, 99, 75); // Power off animation
if (m_system_off_processing) rgb_marquee_sweep_fade(color, !dir, 7, 75, 50); uint8_t dir = slot > 3 ? 1 : 0;
if (m_system_off_processing) rgb_marquee_sweep_fade(color, dir, 7, 50, 25); uint8_t color = get_color_by_slot(slot);
if (m_system_off_processing) rgb_marquee_sweep_fade(color, !dir, 7, 25, 0); if (m_reset_source & (NRF_POWER_RESETREAS_NFC_MASK | NRF_POWER_RESETREAS_LPCOMP_MASK)) {
} else if (animation_config == SettingsAnimationModeMinimal) { if (m_reset_source & NRF_POWER_RESETREAS_NFC_MASK) {
if (m_system_off_processing) rgb_marquee_sweep_from_to(color, slot, !dir ? 7 : 0); color = 1;
} else if (animation_config == SettingsAnimationModeSymmetric) { } else {
if (m_system_off_processing) rgb_marquee_symmetric_in(color, slot); color = 2;
}
}
if (m_system_off_processing) ledblink5(color, slot, dir ? 7 : 0);
if (m_system_off_processing) ledblink4(color, dir, 7, 99, 75);
if (m_system_off_processing) ledblink4(color, !dir, 7, 75, 50);
if (m_system_off_processing) ledblink4(color, dir, 7, 50, 25);
if (m_system_off_processing) ledblink4(color, !dir, 7, 25, 0);
} }
rgb_marquee_stop(); rgb_marquee_stop();
if (!m_system_off_processing) { if (!m_system_off_processing) {
@@ -464,13 +422,11 @@ static void check_wakeup_src(void) {
// Button wake-up boot animation // Button wake-up boot animation
uint8_t animation_config = settings_get_animation_config(); uint8_t animation_config = settings_get_animation_config();
if (animation_config == SettingsAnimationModeFull) { if (animation_config == SettingsAnimationModeFull) {
rgb_marquee_sweep_to(color, !dir, 11); ledblink2(color, !dir, 11);
rgb_marquee_sweep_to(color, dir, 11); ledblink2(color, dir, 11);
rgb_marquee_sweep_to(color, !dir, dir ? slot : 7 - slot); ledblink2(color, !dir, dir ? slot : 7 - slot);
} else if (animation_config == SettingsAnimationModeMinimal) { } else if (animation_config == SettingsAnimationModeMinimal) {
rgb_marquee_sweep_to(color, !dir, dir ? slot : 7 - slot); ledblink2(color, !dir, dir ? slot : 7 - slot);
} else if (animation_config == SettingsAnimationModeSymmetric) {
rgb_marquee_symmetric_out(color, slot);
} else { } else {
set_slot_light_color(color); set_slot_light_color(color);
} }
@@ -479,7 +435,7 @@ static void check_wakeup_src(void) {
light_up_by_slot(); light_up_by_slot();
// If no operation follows, wait for the timeout and then deep hibernate // If no operation follows, wait for the timeout and then deep hibernate
sleep_timer_start(settings_get_sleep_timeout()); sleep_timer_start(SLEEP_DELAY_MS_BUTTON_WAKEUP);
} else if ((m_reset_source & (NRF_POWER_RESETREAS_NFC_MASK | NRF_POWER_RESETREAS_LPCOMP_MASK)) || } else if ((m_reset_source & (NRF_POWER_RESETREAS_NFC_MASK | NRF_POWER_RESETREAS_LPCOMP_MASK)) ||
(m_gpregret_val & RESET_ON_LF_FIELD_EXISTS_Msk)) { (m_gpregret_val & RESET_ON_LF_FIELD_EXISTS_Msk)) {
NRF_LOG_INFO("WakeUp from rfid field"); NRF_LOG_INFO("WakeUp from rfid field");
@@ -503,12 +459,9 @@ static void check_wakeup_src(void) {
uint8_t animation_config = settings_get_animation_config(); uint8_t animation_config = settings_get_animation_config();
if (animation_config == SettingsAnimationModeFull) { if (animation_config == SettingsAnimationModeFull) {
// In the case of field wake-up, only one round of RGB is swept as the power-on animation // In the case of field wake-up, only one round of RGB is swept as the power-on animation
rgb_marquee_sweep_to(color, !dir, dir ? slot : 7 - slot); ledblink2(color, !dir, dir ? slot : 7 - slot);
} else if (animation_config == SettingsAnimationModeSymmetric) {
rgb_marquee_symmetric_out(color, slot);
} else {
set_slot_light_color(color);
} }
set_slot_light_color(color);
light_up_by_slot(); light_up_by_slot();
// We can only run tag emulation at field wakeup source. // We can only run tag emulation at field wakeup source.
@@ -535,20 +488,9 @@ static void check_wakeup_src(void) {
tag_emulation_factory_init(); tag_emulation_factory_init();
// RGB // RGB
uint8_t animation_config = settings_get_animation_config(); ledblink2(0, !dir, 11);
if (animation_config == SettingsAnimationModeFull) { ledblink2(1, dir, 11);
rgb_marquee_sweep_to(0, !dir, 11); ledblink2(2, !dir, 11);
rgb_marquee_sweep_to(1, dir, 11);
rgb_marquee_sweep_to(2, !dir, 11);
} else if (animation_config == SettingsAnimationModeMinimal) {
rgb_marquee_sweep_from_to(0, 0, 2);
rgb_marquee_sweep_from_to(1, 2, 5);
rgb_marquee_sweep_from_to(2, 5, 7);
} else if (animation_config == SettingsAnimationModeSymmetric) {
rgb_marquee_symmetric_out(0, ~0);
rgb_marquee_symmetric_in(1, ~0);
rgb_marquee_symmetric_out(2, ~0);
}
// Show RGB for slot. // Show RGB for slot.
set_slot_light_color(color); set_slot_light_color(color);
@@ -579,12 +521,6 @@ static void cycle_slot(bool dec) {
} }
// Update status only if the new card slot switch is valid // Update status only if the new card slot switch is valid
tag_emulation_change_slot(slot_new, true); // Tell the analog card module that we need to switch card slots tag_emulation_change_slot(slot_new, true); // Tell the analog card module that we need to switch card slots
// Turn off the LEDs in case we were showing the battery status
rgb_marquee_stop();
uint32_t *led_pins = hw_get_led_array();
for (int i = 0; i < RGB_LIST_NUM; i++) {
nrf_gpio_pin_clear(led_pins[i]);
}
// Go back to the color corresponding to the field enablement type // Go back to the color corresponding to the field enablement type
apply_slot_change(slot_now, slot_new); apply_slot_change(slot_now, slot_new);
} }
@@ -667,28 +603,11 @@ static void btn_fn_copy_lf(uint8_t slot, tag_specific_type_t type) {
size = LF_HIDPROX_TAG_ID_SIZE; size = LF_HIDPROX_TAG_ID_SIZE;
data = id_buffer; data = id_buffer;
break; break;
case TAG_TYPE_IOPROX:
status = scan_ioprox(id_buffer, 0);
size = LF_IOPROX_TAG_ID_SIZE;
data = id_buffer;
break;
case TAG_TYPE_EM410X: case TAG_TYPE_EM410X:
case TAG_TYPE_EM410X_ELECTRA: {
status = scan_em410x(id_buffer); status = scan_em410x(id_buffer);
tag_specific_type_t detected_type = (id_buffer[0] << 8) | id_buffer[1]; size = LF_EM410X_TAG_ID_SIZE;
tag_specific_type_t new_type = data = id_buffer + 2; // skip tag type
detected_type == TAG_TYPE_EM410X_ELECTRA ? TAG_TYPE_EM410X_ELECTRA : TAG_TYPE_EM410X;
// If we read Electra but the slot was classic (or vice versa), switch slot type automatically.
if (new_type != type) {
tag_emulation_change_type(slot, new_type);
type = new_type;
}
size = (new_type == TAG_TYPE_EM410X_ELECTRA) ? LF_EM410X_ELECTRA_TAG_ID_SIZE : LF_EM410X_TAG_ID_SIZE;
data = id_buffer + 2; // skip tag type
break; break;
}
case TAG_TYPE_VIKING: case TAG_TYPE_VIKING:
status = scan_viking(id_buffer); status = scan_viking(id_buffer);
size = LF_VIKING_TAG_ID_SIZE; size = LF_VIKING_TAG_ID_SIZE;
@@ -838,67 +757,13 @@ static void run_button_function_by_settings(settings_button_function_t sbf) {
case SettingsButtonCloneIcUid: case SettingsButtonCloneIcUid:
btn_fn_copy_ic_uid(); btn_fn_copy_ic_uid();
break; break;
case SettingsButtonNfcFieldGenerator:
if (!m_is_field_on) {
// Initialize reader hardware if not already in reader mode
device_mode_t current_mode = get_device_mode();
if (current_mode != DEVICE_MODE_READER) {
// Temporarily init reader hardware just for the field
nrf_gpio_cfg_output(READER_POWER);
nrf_gpio_pin_set(READER_POWER); // reader power enable
nrf_gpio_cfg_output(HF_ANT_SEL);
nrf_gpio_pin_clear(HF_ANT_SEL); // hf ant switch to reader mode
pcd_14a_reader_init();
bsp_delay_ms(10);
}
pcd_14a_reader_reset();
pcd_14a_reader_antenna_on();
m_is_field_on = true;
NRF_LOG_INFO("NFC field ON");
// Set initial rainbow state
set_slot_light_color(RGB_RED);
uint32_t *led_pins = hw_get_led_array();
for (int i = 0; i < RGB_LIST_NUM; i++) {
nrf_gpio_pin_set(led_pins[i]);
}
// Stop sleep timer while field is active
NRF_LOG_INFO("Stopping sleep timer for field generator");
sleep_timer_stop();
NRF_LOG_INFO("Sleep timer stopped");
} else {
pcd_14a_reader_antenna_off();
m_is_field_on = false;
NRF_LOG_INFO("NFC field OFF");
// If we're not in reader mode, clean up the hardware
device_mode_t current_mode = get_device_mode();
if (current_mode != DEVICE_MODE_READER) {
pcd_14a_reader_uninit();
nrf_gpio_pin_clear(READER_POWER); // reader power disable
nrf_gpio_pin_set(HF_ANT_SEL); // hf ant switch back to tag mode
}
// Restore normal LED
light_up_by_slot();
// Restart sleep timer
NRF_LOG_INFO("Field off, restarting sleep timer");
sleep_timer_start(SLEEP_DELAY_MS_BUTTON_CLICK);
NRF_LOG_INFO("Sleep timer restarted");
}
break;
#endif #endif
case SettingsButtonShowBattery: case SettingsButtonShowBattery:
show_battery(); show_battery();
break;
default: default:
NRF_LOG_ERROR("Unsupported button function"); NRF_LOG_ERROR("Unsupported button function")
break; break;
} }
} }
@@ -927,10 +792,8 @@ static void button_press_process(void) {
} }
// Disable led marquee for usb at button pressed. // Disable led marquee for usb at button pressed.
g_usb_led_marquee_enable = false; g_usb_led_marquee_enable = false;
// Re-delay into hibernation (unless field is on) // Re-delay into hibernation
if (!m_is_field_on) { sleep_timer_start(SLEEP_DELAY_MS_BUTTON_CLICK);
sleep_timer_start(SLEEP_DELAY_MS_BUTTON_CLICK);
}
} }
} }
@@ -949,17 +812,12 @@ static void blink_usb_led_status(void) {
} }
} else { } else {
// The light effect is enabled and can be displayed // The light effect is enabled and can be displayed
if (rgb_marquee_is_enabled()) { if (is_rgb_marquee_enable()) {
is_working = true; is_working = true;
if (g_usb_port_opened) { if (g_usb_port_opened) {
uint8_t animation_config = settings_get_animation_config(); ledblink1(color, dir);
if (animation_config == SettingsAnimationModeSymmetric) {
rgb_marquee_usb_open_symmetric(color);
} else {
rgb_marquee_usb_open_sweep(color, dir);
}
} else { } else {
rgb_marquee_usb_idle(); ledblink6();
} }
} else { } else {
if (is_working) { if (is_working) {
@@ -1029,17 +887,8 @@ int main(void) {
lesc_event_process(); lesc_event_process();
// Button event process // Button event process
button_press_process(); button_press_process();
// Led blink at usb status
#if defined(PROJECT_CHAMELEON_ULTRA) blink_usb_led_status();
// Field generator rainbow animation
field_generator_rainbow_loop();
#endif
// Led blink at usb status (only if field generator is off)
if (!m_is_field_on) {
blink_usb_led_status();
}
// Data pack process // Data pack process
data_frame_process(); data_frame_process();
// Log print process // Log print process
-5
View File
@@ -19,7 +19,6 @@
///////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////
#define STATUS_LF_TAG_OK (0x40) // Some of the low -frequency cards are successful! #define STATUS_LF_TAG_OK (0x40) // Some of the low -frequency cards are successful!
#define STATUS_LF_TAG_NO_FOUND (0x41) // Can't search for valid LF tags #define STATUS_LF_TAG_NO_FOUND (0x41) // Can't search for valid LF tags
#define STATUS_LF_TAG_LOGIN_REQUIRED (0x42) // Tag requires LOGIN before read
///////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////
// other status // other status
@@ -32,9 +31,5 @@
#define STATUS_FLASH_WRITE_FAIL (0x70) // Flash writing failed #define STATUS_FLASH_WRITE_FAIL (0x70) // Flash writing failed
#define STATUS_FLASH_READ_FAIL (0x71) // Flash read failed #define STATUS_FLASH_READ_FAIL (0x71) // Flash read failed
#define STATUS_INVALID_SLOT_TYPE (0x72) // Invalid slot type #define STATUS_INVALID_SLOT_TYPE (0x72) // Invalid slot type
#define STATUS_MEM_ERR (0x73) // Can't allocate memory or work with memory error
#define STATUS_CREATE_RESPONSE_ERR (0x74) // Can't create response for command
#define STATUS_CMD_ERR (0x75) // Execution of command failed
#endif #endif
+2 -2
View File
@@ -90,7 +90,7 @@ BLE_ADVERTISING_DEF(m_advertising);
uint16_t batt_lvl_in_milli_volts = 0; uint16_t batt_lvl_in_milli_volts = 0;
uint8_t percentage_batt_lvl = 0; uint8_t percentage_batt_lvl = 0;
static nrf_saadc_value_t adc_buf[ADC_BUF_COUNT][ADC_BUF_SIZE]; static nrf_saadc_value_t adc_buf[ADC_BUF_SIZE][ADC_BUF_COUNT];
static uint16_t m_conn_handle = BLE_CONN_HANDLE_INVALID; /**< Handle of the current connection. */ static uint16_t m_conn_handle = BLE_CONN_HANDLE_INVALID; /**< Handle of the current connection. */
static uint16_t m_ble_nus_max_data_len = BLE_GATT_ATT_MTU_DEFAULT - 3; /**< Maximum length of data (in bytes) that can be transmitted to the peer by the Nordic UART service module. */ static uint16_t m_ble_nus_max_data_len = BLE_GATT_ATT_MTU_DEFAULT - 3; /**< Maximum length of data (in bytes) that can be transmitted to the peer by the Nordic UART service module. */
lf_adc_callback_t m_lf_adc_callback = NULL; lf_adc_callback_t m_lf_adc_callback = NULL;
@@ -806,4 +806,4 @@ void unregister_lf_adc_callback(void) {
nrfx_saadc_uninit(); nrfx_saadc_uninit();
adc_configure(); adc_configure();
m_lf_adc_callback = NULL; m_lf_adc_callback = NULL;
} }
-40
View File
@@ -46,8 +46,6 @@
#define DATA_CMD_GET_BLE_PAIRING_ENABLE (1036) #define DATA_CMD_GET_BLE_PAIRING_ENABLE (1036)
#define DATA_CMD_SET_BLE_PAIRING_ENABLE (1037) #define DATA_CMD_SET_BLE_PAIRING_ENABLE (1037)
#define DATA_CMD_GET_ALL_SLOT_NICKS (1038) #define DATA_CMD_GET_ALL_SLOT_NICKS (1038)
#define DATA_CMD_GET_SLEEP_TIMEOUT (1039)
#define DATA_CMD_SET_SLEEP_TIMEOUT (1040)
// //
// ****************************************************************** // ******************************************************************
@@ -69,21 +67,12 @@
#define DATA_CMD_MF1_READ_ONE_BLOCK (2008) #define DATA_CMD_MF1_READ_ONE_BLOCK (2008)
#define DATA_CMD_MF1_WRITE_ONE_BLOCK (2009) #define DATA_CMD_MF1_WRITE_ONE_BLOCK (2009)
#define DATA_CMD_HF14A_RAW (2010) #define DATA_CMD_HF14A_RAW (2010)
#define DATA_CMD_HF14A_SCAN_KEEP (2016) /* scan+RATS, keep field alive for APDU exchange */
#define DATA_CMD_HF14A_AUTH_TRACE (2017) /* full anticoll + Crypto1 auth, every frame returned for inspection */
#define DATA_CMD_MF1_MANIPULATE_VALUE_BLOCK (2011) #define DATA_CMD_MF1_MANIPULATE_VALUE_BLOCK (2011)
#define DATA_CMD_MF1_CHECK_KEYS_OF_SECTORS (2012) #define DATA_CMD_MF1_CHECK_KEYS_OF_SECTORS (2012)
#define DATA_CMD_MF1_HARDNESTED_ACQUIRE (2013) #define DATA_CMD_MF1_HARDNESTED_ACQUIRE (2013)
#define DATA_CMD_MF1_ENC_NESTED_ACQUIRE (2014) #define DATA_CMD_MF1_ENC_NESTED_ACQUIRE (2014)
#define DATA_CMD_MF1_CHECK_KEYS_ON_BLOCK (2015) #define DATA_CMD_MF1_CHECK_KEYS_ON_BLOCK (2015)
#define DATA_CMD_HF14A_SET_FIELD_ON (2100)
#define DATA_CMD_HF14A_SET_FIELD_OFF (2101)
#define DATA_CMD_HF14A_GET_CONFIG (2200)
#define DATA_CMD_HF14A_SET_CONFIG (2201)
#define DATA_CMD_HF14A_SNIFF (2020)
// //
// ****************************************************************** // ******************************************************************
@@ -95,21 +84,10 @@
// //
#define DATA_CMD_EM410X_SCAN (3000) #define DATA_CMD_EM410X_SCAN (3000)
#define DATA_CMD_EM410X_WRITE_TO_T55XX (3001) #define DATA_CMD_EM410X_WRITE_TO_T55XX (3001)
#define DATA_CMD_EM410X_ELECTRA_WRITE_TO_T55XX (3006)
#define DATA_CMD_HIDPROX_SCAN (3002) #define DATA_CMD_HIDPROX_SCAN (3002)
#define DATA_CMD_HIDPROX_WRITE_TO_T55XX (3003) #define DATA_CMD_HIDPROX_WRITE_TO_T55XX (3003)
#define DATA_CMD_PAC_SCAN (3014)
#define DATA_CMD_PAC_WRITE_TO_T55XX (3015)
#define DATA_CMD_VIKING_SCAN (3004) #define DATA_CMD_VIKING_SCAN (3004)
#define DATA_CMD_VIKING_WRITE_TO_T55XX (3005) #define DATA_CMD_VIKING_WRITE_TO_T55XX (3005)
#define DATA_CMD_ADC_GENERIC_READ (3009)
#define DATA_CMD_GENERIC_READ (3007)
#define DATA_CMD_CORR_GENERIC_READ (3008)
#define DATA_CMD_IOPROX_SCAN (3010)
#define DATA_CMD_IOPROX_WRITE_TO_T55XX (3011)
#define DATA_CMD_IOPROX_DECODE_RAW (3012)
#define DATA_CMD_IOPROX_COMPOSE_ID (3013)
#define DATA_CMD_LF_T55XX_WRITE (3016)
// //
// ****************************************************************** // ******************************************************************
@@ -158,8 +136,6 @@
#define DATA_CMD_MF0_NTAG_GET_DETECTION_LOG (4035) #define DATA_CMD_MF0_NTAG_GET_DETECTION_LOG (4035)
#define DATA_CMD_MF0_NTAG_GET_DETECTION_ENABLE (4036) #define DATA_CMD_MF0_NTAG_GET_DETECTION_ENABLE (4036)
#define DATA_CMD_MF0_NTAG_GET_EMULATOR_CONFIG (4037) #define DATA_CMD_MF0_NTAG_GET_EMULATOR_CONFIG (4037)
#define DATA_CMD_MF1_SET_FIELD_OFF_DO_RESET (4038)
#define DATA_CMD_MF1_GET_FIELD_OFF_DO_RESET (4039)
// //
// ****************************************************************** // ******************************************************************
@@ -172,27 +148,11 @@
// //
// ****************************************************************** // ******************************************************************
/* ISO14443-4 T=CL emulation commands */
#define DATA_CMD_HF14A_4_APDU_RECV (6000) /* non-blocking poll: firmware->host APDU */
#define DATA_CMD_HF14A_4_APDU_SEND (6001) /* host->firmware APDU response */
#define DATA_CMD_HF14A_4_SET_ANTI_COLL (6002) /* set UID/ATQA/SAK/ATS */
#define DATA_CMD_HF14A_4_STATIC_RESP (6003) /* add/clear static APDU response pair */
#define DATA_CMD_HF14A_4_READER_APDU (6004) /* select+RATS+send APDU, keep field */
#define DATA_CMD_HF14A_4_EMV_SCAN (6005) /* full EMV scan in one call */
#define DATA_CMD_EM410X_SET_EMU_ID (5000) #define DATA_CMD_EM410X_SET_EMU_ID (5000)
#define DATA_CMD_EM410X_GET_EMU_ID (5001) #define DATA_CMD_EM410X_GET_EMU_ID (5001)
#define DATA_CMD_HIDPROX_SET_EMU_ID (5002) #define DATA_CMD_HIDPROX_SET_EMU_ID (5002)
#define DATA_CMD_HIDPROX_GET_EMU_ID (5003) #define DATA_CMD_HIDPROX_GET_EMU_ID (5003)
#define DATA_CMD_VIKING_SET_EMU_ID (5004) #define DATA_CMD_VIKING_SET_EMU_ID (5004)
#define DATA_CMD_VIKING_GET_EMU_ID (5005) #define DATA_CMD_VIKING_GET_EMU_ID (5005)
#define DATA_CMD_PAC_SET_EMU_ID (5006)
#define DATA_CMD_PAC_GET_EMU_ID (5007)
#define DATA_CMD_IOPROX_SET_EMU_ID (5008)
#define DATA_CMD_IOPROX_GET_EMU_ID (5009)
#define DATA_CMD_EM4X05_SCAN (3030)
#define DATA_CMD_EM4X05_READSNIFF (3032)
#define DATA_CMD_LF_SNIFF (3031)
#endif #endif
@@ -59,42 +59,9 @@ const uint16_t ats_fsdi_table[] = {
static volatile bool m_is_responded = false; static volatile bool m_is_responded = false;
// Receiving buffer // Receiving buffer
static uint8_t m_nfc_rx_buffer[MAX_NFC_RX_BUFFER_SIZE] = { 0x00 }; static uint8_t m_nfc_rx_buffer[MAX_NFC_RX_BUFFER_SIZE] = { 0x00 };
/* Optional sniff callback — fires for every received frame */
static nfc_tag_14a_sniff_cb_t m_sniff_cb = NULL;
void nfc_tag_14a_set_sniff_cb(nfc_tag_14a_sniff_cb_t cb) {
m_sniff_cb = cb;
}
void nfc_tag_14a_clear_sniff_cb(void) {
m_sniff_cb = NULL;
}
/* TX sniff: captures card→reader frames at TX_FRAMESTART */
static nfc_tag_14a_tx_sniff_cb_t m_tx_sniff_cb = NULL;
void nfc_tag_14a_set_tx_sniff_cb(nfc_tag_14a_tx_sniff_cb_t cb) {
m_tx_sniff_cb = cb;
}
void nfc_tag_14a_clear_tx_sniff_cb(void) {
m_tx_sniff_cb = NULL;
}
/* Passive sniff mode: suppress all tag TX responses so the CU does not
* participate in anticollision and avoids colliding with the real card. */
static bool m_sniff_passive = false;
void nfc_tag_14a_set_sniff_passive(bool passive) {
m_sniff_passive = passive;
}
static uint8_t m_nfc_tx_buffer[MAX_NFC_TX_BUFFER_SIZE] = { 0x00 }; static uint8_t m_nfc_tx_buffer[MAX_NFC_TX_BUFFER_SIZE] = { 0x00 };
// The N -secondary connection needs to use SAK, when the "third 'bit' in SAK is 1 is 1, the logo UID is incomplete // The N -secondary connection needs to use SAK, when the "third 'bit' in SAK is 1 is 1, the logo UID is incomplete
static uint8_t m_uid_incomplete_sak[] = { 0x04, 0xda, 0x17 }; static uint8_t m_uid_incomplete_sak[] = { 0x04, 0xda, 0x17 };
// Reset nfc peripheral after field lost?
static bool reset_if_field_lost = false; // default is 'false', Unless there is a genuine need for a reset.
/** /**
* @brief Calculate BCC * @brief Calculate BCC
@@ -356,11 +323,6 @@ void nfc_tag_14a_data_process(uint8_t *p_data) {
// Because of this error receiving event caused by this possible interference // Because of this error receiving event caused by this possible interference
return; return;
} }
/* Sniff hook — fire before any tag response logic */
if (m_sniff_cb != NULL) {
m_sniff_cb(p_data, szDataBits);
}
// Manually draw frame, separate data and strange school inspection // Manually draw frame, separate data and strange school inspection
#if !NFC_TAG_14A_RX_PARITY_AUTO_DEL_ENABLE #if !NFC_TAG_14A_RX_PARITY_AUTO_DEL_ENABLE
if (szDataBits >= 9) { if (szDataBits >= 9) {
@@ -385,11 +347,9 @@ void nfc_tag_14a_data_process(uint8_t *p_data) {
if (auto_coll_res != NULL) { if (auto_coll_res != NULL) {
// The status machine is set to the preparation state, and the next operation is to enter the card selection link // The status machine is set to the preparation state, and the next operation is to enter the card selection link
m_tag_state_14a = NFC_TAG_STATE_14A_READY; m_tag_state_14a = NFC_TAG_STATE_14A_READY;
if (!m_sniff_passive) { // After receiving the WUPA or REQA instruction, we need to reply to ATQA
// After receiving the WUPA or REQA instruction, we need to reply to ATQA nfc_tag_14a_tx_bytes(auto_coll_res->atqa, 2, false);
nfc_tag_14a_tx_bytes(auto_coll_res->atqa, 2, false); // NRF_LOG_INFO("ATQA reply.");
// NRF_LOG_INFO("ATQA reply: %02x%02x", auto_coll_res->atqa[0], auto_coll_res->atqa[1]);
}
} else { } else {
m_tag_state_14a = NFC_TAG_STATE_14A_IDLE; m_tag_state_14a = NFC_TAG_STATE_14A_IDLE;
NRF_LOG_INFO("Auto anti-collision resource no exists."); NRF_LOG_INFO("Auto anti-collision resource no exists.");
@@ -437,15 +397,6 @@ void nfc_tag_14a_data_process(uint8_t *p_data) {
m_tag_state_14a = NFC_TAG_STATE_14A_IDLE; m_tag_state_14a = NFC_TAG_STATE_14A_IDLE;
} }
return; return;
case NFC_TAG_14A_CMD_REQA:
case NFC_TAG_14A_CMD_WUPA:
// Reader is re-sending REQA/WUPA while in READY state
// This can happen if reader retries or if frame was received incorrectly
// Respond with ATQA again and stay in READY state
if (auto_coll_res != NULL) {
nfc_tag_14a_tx_bytes(auto_coll_res->atqa, 2, false);
}
return;
default: { default: {
// After receiving the wrong level instruction, directly reset the status machine // After receiving the wrong level instruction, directly reset the status machine
NRF_LOG_INFO("[MFEMUL_SELECT] Incorrect cascade level received: %02x", p_data[0]); NRF_LOG_INFO("[MFEMUL_SELECT] Incorrect cascade level received: %02x", p_data[0]);
@@ -505,9 +456,7 @@ void nfc_tag_14a_data_process(uint8_t *p_data) {
} }
// Incoming SELECT ALL for any cascade level // Incoming SELECT ALL for any cascade level
if (szDataBits == 16 && p_data[1] == 0x20) { if (szDataBits == 16 && p_data[1] == 0x20) {
if (!m_sniff_passive) { nfc_tag_14a_tx_bytes(uid, 5, false);
nfc_tag_14a_tx_bytes(uid, 5, false);
}
// NRF_LOG_INFO("[MFEMUL_SELECT] SEL Reply."); // NRF_LOG_INFO("[MFEMUL_SELECT] SEL Reply.");
break; break;
} }
@@ -521,14 +470,10 @@ void nfc_tag_14a_data_process(uint8_t *p_data) {
if (cl_finished) { if (cl_finished) {
// NRF_LOG_INFO("[MFEMUL_SELECT] m_tag_state_14a = MFEMUL_WORK"); // NRF_LOG_INFO("[MFEMUL_SELECT] m_tag_state_14a = MFEMUL_WORK");
m_tag_state_14a = NFC_TAG_STATE_14A_ACTIVE; m_tag_state_14a = NFC_TAG_STATE_14A_ACTIVE;
if (!m_sniff_passive) { nfc_tag_14a_tx_bytes(auto_coll_res->sak, 1, true);
nfc_tag_14a_tx_bytes(auto_coll_res->sak, 1, true);
}
} else { } else {
// It is necessary to continue the level, so we need to respond to a data that marks the incomplete UID in SAK // It is necessary to continue the level, so we need to respond to a data that marks the incomplete UID in SAK
if (!m_sniff_passive) { nfc_tag_14a_tx_bytes(m_uid_incomplete_sak, 3, false);
nfc_tag_14a_tx_bytes(m_uid_incomplete_sak, 3, false);
}
} }
} else { } else {
// IDLE, not our UID // IDLE, not our UID
@@ -554,10 +499,6 @@ void nfc_tag_14a_data_process(uint8_t *p_data) {
} }
// RATS instruction // RATS instruction
if (p_data[0] == NFC_TAG_14A_CMD_RATS && nfc_tag_14a_checks_crc(p_data, 4)) { if (p_data[0] == NFC_TAG_14A_CMD_RATS && nfc_tag_14a_checks_crc(p_data, 4)) {
// Reset T=CL layer state for the new session
if (m_tag_handler.cb_reset != NULL) {
m_tag_handler.cb_reset();
}
// Make sure the sub -packaging opens the support of ATS // Make sure the sub -packaging opens the support of ATS
if (auto_coll_res->ats->length > 0) { if (auto_coll_res->ats->length > 0) {
// Take out FSD and return according to the maximum FSD // Take out FSD and return according to the maximum FSD
@@ -582,42 +523,6 @@ void nfc_tag_14a_data_process(uint8_t *p_data) {
} }
} }
// Copy from nrf_nfct.c and modified for nrf52840 adapted(no verify on nrf52832)
static inline void nrf_nfct_reset(void) {
uint32_t fdm;
uint32_t int_enabled;
// Save parameter settings before the reset of the NFCT peripheral.
fdm = nrf_nfct_frame_delay_max_get();
int_enabled = nrf_nfct_int_enable_get();
// Reset the NFCT peripheral.
*(volatile uint32_t *)0x40005FFC = 0;
*(volatile uint32_t *)0x40005FFC;
*(volatile uint32_t *)0x40005FFC = 1;
// Restore parameter settings after the reset of the NFCT peripheral.
nrf_nfct_frame_delay_max_set(fdm);
// Use Window Grid frame delay mode.
nrf_nfct_frame_delay_mode_set(NRF_NFCT_FRAME_DELAY_MODE_WINDOWGRID);
/* Use SDD00001 per ISO14443-3 standard.
* Note: SDD00100 was previously used for Windows Phone compatibility
* but breaks standard readers (including Proxmark3). SDD00001 is correct. */
nrf_nfct_sensres_bit_frame_sdd_set(NRF_NFCT_SENSRES_BIT_FRAME_SDD_00001);
// Restore interrupts.
nrf_nfct_int_enable(int_enabled);
// Disable interrupts associated with data exchange.
nrf_nfct_int_disable(NRF_NFCT_INT_RXFRAMESTART_MASK |
NRF_NFCT_INT_RXFRAMEEND_MASK |
NRF_NFCT_INT_RXERROR_MASK |
NRF_NFCT_INT_TXFRAMESTART_MASK |
NRF_NFCT_INT_TXFRAMEEND_MASK);
}
static inline void nfc_fdt_reset(void) { static inline void nfc_fdt_reset(void) {
// STOP TX // STOP TX
*(volatile uint32_t *)0x40005010 = 0x01; *(volatile uint32_t *)0x40005010 = 0x01;
@@ -666,31 +571,12 @@ void nfc_tag_14a_event_callback(nrfx_nfct_evt_t const *p_event) {
TAG_FIELD_LED_OFF() TAG_FIELD_LED_OFF()
m_tag_state_14a = NFC_TAG_STATE_14A_IDLE; m_tag_state_14a = NFC_TAG_STATE_14A_IDLE;
if (reset_if_field_lost) {
// Fix a bug where certain special conditions prevent triggering TX start events and actually transmit incorrect data to the card reader.
// After more more more testing, I found that simply going into sleep mode and restarting can restore work.
// Therefore, I suspect that there may be some issues with the NFC peripheral that require a reset to resolve.
nrf_nfct_reset();
}
NRF_LOG_INFO("HF FIELD LOST"); NRF_LOG_INFO("HF FIELD LOST");
break; break;
} }
case NRFX_NFCT_EVT_TX_FRAMESTART: { case NRFX_NFCT_EVT_TX_FRAMESTART: {
// NRF_LOG_INFO("TX start.\n"); // NRF_LOG_INFO("TX start.\n");
if (m_tx_sniff_cb != NULL) { // NRF_LOG_INFO("TX config is %d.\n", nrf_nfct_tx_frame_config_get(NRF_NFCT));
uint32_t amt = NRF_NFCT->TXD.AMOUNT;
uint16_t tx_bytes = (amt >> NFCT_TXD_AMOUNT_TXDATABYTES_Pos)
& (NFCT_TXD_AMOUNT_TXDATABYTES_Msk >> NFCT_TXD_AMOUNT_TXDATABYTES_Pos);
uint16_t tx_bits_rem = (amt >> NFCT_TXD_AMOUNT_TXDATABITS_Pos)
& (NFCT_TXD_AMOUNT_TXDATABITS_Msk >> NFCT_TXD_AMOUNT_TXDATABITS_Pos);
uint16_t tx_bits = (tx_bits_rem > 0)
? ((tx_bytes - 1) * 8 + tx_bits_rem)
: (tx_bytes * 8);
if (tx_bits > 0 && tx_bytes <= MAX_NFC_TX_BUFFER_SIZE) {
m_tx_sniff_cb(m_nfc_tx_buffer, tx_bits);
}
}
break; break;
} }
case NRFX_NFCT_EVT_TX_FRAMEEND: { case NRFX_NFCT_EVT_TX_FRAMEEND: {
@@ -802,11 +688,3 @@ bool is_valid_uid_size(uint8_t uid_length) {
uid_length == NFC_TAG_14A_UID_DOUBLE_SIZE || uid_length == NFC_TAG_14A_UID_DOUBLE_SIZE ||
uid_length == NFC_TAG_14A_UID_TRIPLE_SIZE; uid_length == NFC_TAG_14A_UID_TRIPLE_SIZE;
} }
void nfc_tag_14a_set_reset_enable(bool enable) {
reset_if_field_lost = enable;
}
bool nfc_tag_14a_is_reset_enable() {
return reset_if_field_lost;
}
@@ -4,7 +4,7 @@
#include "tag_emulation.h" #include "tag_emulation.h"
#define MAX_NFC_RX_BUFFER_SIZE 257 #define MAX_NFC_RX_BUFFER_SIZE 257
#define MAX_NFC_TX_BUFFER_SIZE 512 /* must hold PCB + max APDU response */ #define MAX_NFC_TX_BUFFER_SIZE 64
#define NFC_TAG_14A_CRC_LENGTH 2 #define NFC_TAG_14A_CRC_LENGTH 2
@@ -82,27 +82,6 @@ typedef struct {
// Communication reception function that needs to be implemented // Communication reception function that needs to be implemented
typedef void (*nfc_tag_14a_reset_handler_t)(void); typedef void (*nfc_tag_14a_reset_handler_t)(void);
/* Sniff callback — called for every received frame before the tag handler.
* data : raw frame bytes (after parity strip)
* szBits : number of bits received */
typedef void (*nfc_tag_14a_sniff_cb_t)(const uint8_t *data, uint16_t szBits);
void nfc_tag_14a_set_sniff_cb(nfc_tag_14a_sniff_cb_t cb);
void nfc_tag_14a_clear_sniff_cb(void);
/* TX sniff callback — fires at TX_FRAMESTART with the frame the tag is about
* to send (card→reader direction). Same signature as the RX sniff callback.
* Install alongside nfc_tag_14a_set_sniff_cb() to capture both directions. */
typedef void (*nfc_tag_14a_tx_sniff_cb_t)(const uint8_t *data, uint16_t szBits);
void nfc_tag_14a_set_tx_sniff_cb(nfc_tag_14a_tx_sniff_cb_t cb);
void nfc_tag_14a_clear_tx_sniff_cb(void);
/* Passive sniff mode: when true, suppresses all CU anticollision responses
* (ATQA, UID, SAK) so the CU does not collide with real cards in the field.
* Enable before starting a sniff session, disable on completion. */
void nfc_tag_14a_set_sniff_passive(bool passive);
typedef void (*nfc_tag_14a_state_handler_t)(uint8_t *data, uint16_t szBits); typedef void (*nfc_tag_14a_state_handler_t)(uint8_t *data, uint16_t szBits);
typedef nfc_tag_14a_coll_res_reference_t *(*nfc_tag_14a_coll_handler_t)(void); typedef nfc_tag_14a_coll_res_reference_t *(*nfc_tag_14a_coll_handler_t)(void);
@@ -136,8 +115,4 @@ void nfc_tag_14a_tx_nbit(uint8_t data, uint32_t bits);
// Determine whether it is an effective UID length // Determine whether it is an effective UID length
bool is_valid_uid_size(uint8_t uid_length); bool is_valid_uid_size(uint8_t uid_length);
// Reset nfc peripheral after field lost
void nfc_tag_14a_set_reset_enable(bool enable);
bool nfc_tag_14a_is_reset_enable();
#endif #endif
@@ -1,446 +0,0 @@
/**
* @file nfc_14a_4.c
* @brief ISO14443-4 T=CL emulation for ChameleonUltra
*
* Implements a full ISO14443-4 tag emulator with a static APDU response
* table. The table is populated by the host before field activation, so
* the firmware can respond to an EMV reader autonomously without any USB
* communication while the RF field is active.
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <string.h>
#include "nfc_14a_4.h"
#include "nfc_14a.h"
#include "tag_emulation.h"
#include "tag_persistence.h"
#include "fds_util.h"
#include "nrf_log.h"
/* ------------------------------------------------------------------ */
/* PCB byte constants (ISO14443-4 §7) */
/* ------------------------------------------------------------------ */
#define PCB_IBLOCK_MASK 0xC0
#define PCB_IBLOCK_VAL 0x00
#define PCB_RBLOCK_MASK 0xE0
#define PCB_RBLOCK_VAL 0x80 /* R(ACK) = 0xA2/0xA3, R(NAK) = 0xB2/0xB3 */
#define PCB_SBLOCK_MASK 0xC0
#define PCB_SBLOCK_VAL 0xC0
#define PCB_BLOCK_NUM 0x01
#define PCB_CID_FOLLOWING 0x10 /* bit4: CID follows */
#define PCB_NAD_FOLLOWING 0x08 /* bit3: NAD follows */
#define PCB_CHAIN 0x20 /* bit5: chaining flag per ISO14443-4 Table 3 */
#define PCB_SBLOCK_WTX 0x30
#define PCB_SBLOCK_DESELECT 0xC2
#define WTX_VALUE 0x3B /* WTXM=59 (~3s extra wait) */
static inline bool is_iblock(uint8_t pcb) {
return (pcb & PCB_IBLOCK_MASK) == PCB_IBLOCK_VAL;
}
static inline bool is_rblock(uint8_t pcb) {
/* R-block: bit7=1, bit6=0, bit2=1, bit1=0 (mask 0xC6, value 0x82) */
return (pcb & 0xC6) == 0x82;
}
static inline bool is_sblock(uint8_t pcb) {
return (pcb & PCB_SBLOCK_MASK) == PCB_SBLOCK_VAL;
}
/* ------------------------------------------------------------------ */
/* Module state */
/* ------------------------------------------------------------------ */
static nfc_tag_14a_4_information_t *m_tag_information = NULL;
/* Shadow coll-res references into m_tag_information */
static nfc_tag_14a_coll_res_reference_t m_shadow_coll_res;
/* T=CL session state */
static uint8_t m_block_num = 0;
static bool m_cid_supported = false;
static uint8_t m_cid = 0;
static uint8_t m_apdu_buf[NFC_14A_4_MAX_APDU];
static uint16_t m_apdu_len = 0;
static bool m_apdu_pending = false;
static uint8_t m_resp_buf[NFC_14A_4_MAX_APDU];
static uint16_t m_resp_len = 0;
static bool m_response_ready = false;
/* TX scratch buffer */
static uint8_t m_tx_buf[NFC_14A_4_MAX_APDU + 4];
/* Debug counters — readable via hf 14a debug */
static uint8_t m_dbg_iblocks_rx = 0; /* I-blocks received */
static uint8_t m_dbg_iblocks_tx = 0; /* I-blocks sent */
static uint8_t m_dbg_last_rx_pcb = 0; /* PCB of last received I-block */
static uint8_t m_dbg_last_match = 0; /* last find_static_response result */
/* Static APDU response table (RAM copy, populated from m_tag_information) */
static nfc_tag_14a_4_static_response_t m_static_resp[NFC_14A_4_MAX_STATIC_RESPONSES];
static uint8_t m_static_resp_count = 0;
/* Large response overflow (RAM only, > NFC_14A_4_MAX_STATIC_RESP_LEN bytes).
* NOT persisted to flash. Must reload via emv load after power cycle. */
typedef struct {
uint8_t cmd[NFC_14A_4_MAX_STATIC_CMD_LEN];
uint8_t cmd_len;
uint8_t resp[NFC_14A_4_MAX_LARGE_RESP_LEN];
uint16_t resp_len;
} nfc_tag_14a_4_large_response_t;
static nfc_tag_14a_4_large_response_t m_large_resp[NFC_14A_4_MAX_LARGE_RESPONSES];
static uint8_t m_large_resp_count = 0;
/* ------------------------------------------------------------------ */
/* Static response table */
/* ------------------------------------------------------------------ */
void nfc_tag_14a_4_add_static_response(const uint8_t *cmd, uint8_t cmd_len,
const uint8_t *resp, uint16_t resp_len) {
if (cmd_len > NFC_14A_4_MAX_STATIC_CMD_LEN) cmd_len = NFC_14A_4_MAX_STATIC_CMD_LEN;
if (resp_len > NFC_14A_4_MAX_STATIC_RESP_LEN) {
/* Large response: RAM-only overflow table */
if (m_large_resp_count >= NFC_14A_4_MAX_LARGE_RESPONSES) return;
if (resp_len > NFC_14A_4_MAX_LARGE_RESP_LEN) resp_len = NFC_14A_4_MAX_LARGE_RESP_LEN;
nfc_tag_14a_4_large_response_t *le = &m_large_resp[m_large_resp_count++];
le->cmd_len = cmd_len;
le->resp_len = resp_len;
memcpy(le->cmd, cmd, cmd_len);
memcpy(le->resp, resp, resp_len);
return;
}
/* Normal response: flash-backed table */
if (m_static_resp_count >= NFC_14A_4_MAX_STATIC_RESPONSES) return;
nfc_tag_14a_4_static_response_t *e = &m_static_resp[m_static_resp_count++];
e->cmd_len = cmd_len;
e->resp_len = (uint8_t)resp_len;
memcpy(e->cmd, cmd, cmd_len);
memcpy(e->resp, resp, resp_len);
if (m_tag_information &&
m_tag_information->static_resp_count < NFC_14A_4_MAX_STATIC_RESPONSES) {
memcpy(&m_tag_information->static_resp[m_tag_information->static_resp_count++],
e, sizeof(*e));
}
}
void nfc_tag_14a_4_clear_static_responses(void) {
m_static_resp_count = 0;
m_large_resp_count = 0;
if (m_tag_information) {
m_tag_information->static_resp_count = 0;
}
}
static bool find_static_response(const uint8_t *apdu, uint16_t apdu_len,
uint8_t **resp_out, uint16_t *resp_len_out) {
/* Flash-backed table */
for (uint8_t i = 0; i < m_static_resp_count; i++) {
nfc_tag_14a_4_static_response_t *e = &m_static_resp[i];
if (apdu_len >= e->cmd_len &&
memcmp(apdu, e->cmd, e->cmd_len) == 0) {
*resp_out = e->resp;
*resp_len_out = e->resp_len;
return true;
}
}
/* RAM-only large response table */
for (uint8_t i = 0; i < m_large_resp_count; i++) {
nfc_tag_14a_4_large_response_t *e = &m_large_resp[i];
if (apdu_len >= e->cmd_len &&
memcmp(apdu, e->cmd, e->cmd_len) == 0) {
*resp_out = e->resp;
*resp_len_out = e->resp_len;
return true;
}
}
return false;
}
/* ------------------------------------------------------------------ */
/* TX helpers */
/* ------------------------------------------------------------------ */
static void send_iblock(const uint8_t *data, uint16_t len) {
uint8_t pcb = 0x02 | (m_block_num & 0x01);
if (m_cid_supported) pcb |= PCB_CID_FOLLOWING;
uint8_t off = 0;
m_tx_buf[off++] = pcb;
if (m_cid_supported) m_tx_buf[off++] = m_cid & 0x0F;
if (len > NFC_14A_4_MAX_APDU) len = NFC_14A_4_MAX_APDU;
memcpy(&m_tx_buf[off], data, len);
nfc_tag_14a_tx_bytes(m_tx_buf, off + len, true);
m_block_num ^= 1;
}
static void send_rack(void) {
uint8_t pcb = 0xA2 | (m_block_num & 0x01);
if (m_cid_supported) {
pcb |= PCB_CID_FOLLOWING;
uint8_t buf[2] = { pcb, m_cid & 0x0F };
nfc_tag_14a_tx_bytes(buf, 2, true);
} else {
nfc_tag_14a_tx_bytes(&pcb, 1, true);
}
}
static void send_wtx(void) {
uint8_t buf[3];
uint8_t off = 0;
buf[off++] = PCB_SBLOCK_WTX | (m_cid_supported ? PCB_CID_FOLLOWING : 0);
if (m_cid_supported) buf[off++] = m_cid & 0x0F;
buf[off++] = WTX_VALUE;
nfc_tag_14a_tx_bytes(buf, off, true);
}
/* ------------------------------------------------------------------ */
/* State handler (called from NFCT ISR on each received frame) */
/* ------------------------------------------------------------------ */
static void nfc_tag_14a_4_state_handler(uint8_t *data, uint16_t szBytes) {
if (szBytes == 0) return;
uint8_t pcb = data[0];
/* ---- S-block ---- */
if (is_sblock(pcb)) {
if ((pcb & 0xF7) == PCB_SBLOCK_DESELECT) {
/* Echo DESELECT */
nfc_tag_14a_tx_bytes(data, szBytes, true);
nfc_tag_14a_4_reset_handler();
return;
}
if ((pcb & 0x3F) == (PCB_SBLOCK_WTX & 0x3F)) {
/* Reader sending WTX — echo back with our WTXM */
uint8_t wtxm = (szBytes > 1) ? data[szBytes - 1] & 0x3F : WTX_VALUE;
uint8_t resp[3];
uint8_t off = 0;
resp[off++] = PCB_SBLOCK_WTX | (m_cid_supported ? PCB_CID_FOLLOWING : 0);
if (m_cid_supported) resp[off++] = m_cid & 0x0F;
resp[off++] = wtxm;
nfc_tag_14a_tx_bytes(resp, off, true);
/* If we now have a response ready, send it next I-block */
if (m_response_ready) {
m_response_ready = false;
send_iblock(m_resp_buf, m_resp_len);
}
return;
}
return;
}
/* ---- R-block ---- */
if (is_rblock(pcb)) {
send_rack();
return;
}
/* ---- I-block ---- */
if (is_iblock(pcb)) {
uint8_t reader_blknum = pcb & PCB_BLOCK_NUM;
bool has_cid = (pcb & PCB_CID_FOLLOWING) != 0;
bool has_nad = (pcb & PCB_NAD_FOLLOWING) != 0;
bool more_chain = (pcb & PCB_CHAIN) != 0;
uint8_t offset = 1;
if (has_cid) {
/* CID acknowledged but not used in responses (keeps protocol simpler) */
m_cid_supported = false;
offset++; /* skip CID byte */
}
if (has_nad) offset++;
if (offset >= szBytes) {
send_rack();
return;
}
uint16_t apdu_len = szBytes - offset;
if (apdu_len > NFC_14A_4_MAX_APDU) apdu_len = NFC_14A_4_MAX_APDU;
m_dbg_iblocks_rx++;
m_dbg_last_rx_pcb = pcb;
NRF_LOG_INFO("14A4 I-block #%d: reader_blk=%d m_block_num=%d apdu_len=%d",
m_dbg_iblocks_rx, reader_blknum, m_block_num, apdu_len);
/* Block number check per ISO14443-4 §7.5.3.3:
* If block number matches expected, process new APDU.
* If block number does NOT match, it is a retransmit —
* resend the last response without re-processing. */
if (reader_blknum != (m_block_num & 0x01)) {
/* Retransmit: resend last response */
if (m_resp_len > 0) {
/* Restore block num to what we sent last time and resend */
m_block_num ^= 1; /* undo the increment from last send */
send_iblock(m_resp_buf, m_resp_len);
} else {
send_rack();
}
return;
}
memcpy(m_apdu_buf, &data[offset], apdu_len);
m_apdu_len = apdu_len;
m_apdu_pending = true;
m_response_ready = false;
if (more_chain) {
send_rack();
return;
}
/* APDU complete — check static table first, then WTX */
{
uint8_t *static_resp = NULL;
uint16_t static_len = 0;
bool _found = find_static_response(m_apdu_buf, apdu_len,
&static_resp, &static_len);
m_dbg_last_match = _found ? 1 : 0;
NRF_LOG_INFO("14A4 find_static: found=%d static_len=%d resp_count=%d",
_found, static_len, m_static_resp_count);
if (_found) {
m_dbg_iblocks_tx++;
memcpy(m_resp_buf, static_resp, static_len);
m_resp_len = static_len;
send_iblock(m_resp_buf, m_resp_len);
} else if (m_response_ready) {
m_response_ready = false;
send_iblock(m_resp_buf, m_resp_len);
} else {
/* No response ready — keep reader alive with WTX */
send_wtx();
}
}
return;
}
NRF_LOG_INFO("14A-4: unknown PCB 0x%02x", pcb);
}
/* ------------------------------------------------------------------ */
/* APDU relay API (for host-driven responses) */
/* ------------------------------------------------------------------ */
bool nfc_tag_14a_4_get_pending_apdu(uint8_t *buf, uint16_t *length) {
if (!m_apdu_pending) return false;
m_apdu_pending = false;
*length = m_apdu_len;
memcpy(buf, m_apdu_buf, m_apdu_len);
return true;
}
void nfc_tag_14a_4_set_response(const uint8_t *data, uint16_t length) {
if (length > NFC_14A_4_MAX_APDU) length = NFC_14A_4_MAX_APDU;
memcpy(m_resp_buf, data, length);
m_resp_len = length;
m_response_ready = true;
}
/* ------------------------------------------------------------------ */
/* Reset handler */
/* ------------------------------------------------------------------ */
void nfc_tag_14a_4_reset_handler(void) {
m_block_num = 0;
m_cid_supported = false;
m_cid = 0;
m_apdu_pending = false;
m_response_ready = false;
m_apdu_len = 0;
m_resp_len = 0;
}
void nfc_tag_14a_4_get_debug_counters(uint8_t *rx, uint8_t *tx,
uint8_t *last_pcb, uint8_t *last_match) {
*rx = m_dbg_iblocks_rx;
*tx = m_dbg_iblocks_tx;
*last_pcb = m_dbg_last_rx_pcb;
*last_match = m_dbg_last_match;
}
/* ------------------------------------------------------------------ */
/* Anti-collision resource */
/* ------------------------------------------------------------------ */
nfc_tag_14a_coll_res_reference_t *nfc_tag_14a_4_get_coll_res(void) {
if (m_tag_information == NULL) return NULL;
m_shadow_coll_res.sak = m_tag_information->res_coll.sak;
m_shadow_coll_res.atqa = m_tag_information->res_coll.atqa;
m_shadow_coll_res.uid = m_tag_information->res_coll.uid;
m_shadow_coll_res.size = &m_tag_information->res_coll.size;
m_shadow_coll_res.ats = &m_tag_information->res_coll.ats;
return &m_shadow_coll_res;
}
/* ------------------------------------------------------------------ */
/* Data load / save / factory callbacks */
/* ------------------------------------------------------------------ */
int nfc_tag_14a_4_data_loadcb(tag_specific_type_t type, tag_data_buffer_t *buffer) {
int info_size = sizeof(nfc_tag_14a_4_information_t);
if (buffer->length < info_size) {
NRF_LOG_ERROR("14A-4 loadcb: buffer too small (%d < %d)",
buffer->length, info_size);
return info_size;
}
m_tag_information = (nfc_tag_14a_4_information_t *)buffer->buffer;
/* Populate RAM static table from persisted slot data */
m_static_resp_count = m_tag_information->static_resp_count;
if (m_static_resp_count > NFC_14A_4_MAX_STATIC_RESPONSES)
m_static_resp_count = NFC_14A_4_MAX_STATIC_RESPONSES;
memcpy(m_static_resp, m_tag_information->static_resp,
m_static_resp_count * sizeof(nfc_tag_14a_4_static_response_t));
nfc_tag_14a_handler_t handler = {
.get_coll_res = nfc_tag_14a_4_get_coll_res,
.cb_state = nfc_tag_14a_4_state_handler,
.cb_reset = nfc_tag_14a_4_reset_handler,
};
nfc_tag_14a_set_handler(&handler);
NRF_LOG_INFO("14A-4 loadcb OK: SAK=%02x uid_sz=%d static_resp=%d",
m_tag_information->res_coll.sak[0],
m_tag_information->res_coll.size,
m_static_resp_count);
return info_size;
}
int nfc_tag_14a_4_data_savecb(tag_specific_type_t type, tag_data_buffer_t *buffer) {
return sizeof(nfc_tag_14a_4_information_t);
}
bool nfc_tag_14a_4_data_factory(uint8_t slot, tag_specific_type_t tag_type) {
if (tag_type != TAG_TYPE_HF14A_4) return false;
/* Build factory defaults on stack and write directly to FDS
* (same pattern as nfc_tag_mf1_data_factory). */
nfc_tag_14a_4_information_t info;
memset(&info, 0, sizeof(info));
/* Placeholder 7-byte NXP-style UID */
info.res_coll.size = NFC_TAG_14A_UID_DOUBLE_SIZE;
info.res_coll.atqa[0] = 0x04;
info.res_coll.atqa[1] = 0x00;
info.res_coll.sak[0] = 0x20; /* ISO14443-4 */
info.res_coll.uid[0] = 0x04;
info.res_coll.uid[1] = 0x01;
info.res_coll.uid[2] = 0x02;
info.res_coll.uid[3] = 0x03;
info.res_coll.uid[4] = 0x04;
info.res_coll.uid[5] = 0x05;
info.res_coll.uid[6] = 0x06;
static const uint8_t default_ats[] = {
0x10, 0x78, 0x80, 0x70, 0x02, 0x00,
0x31, 0xC1, 0x64, 0x09, 0x97, 0x61,
0x26, 0x00, 0x90, 0x00
};
info.res_coll.ats.length = sizeof(default_ats);
memcpy(info.res_coll.ats.data, default_ats, sizeof(default_ats));
info.static_resp_count = 0;
fds_slot_record_map_t map_info;
get_fds_map_by_slot_sense_type_for_dump(slot, TAG_SENSE_HF, &map_info);
bool ret = fds_write_sync(map_info.id, map_info.key, sizeof(info), &info);
NRF_LOG_INFO("14A-4 factory slot %d: %s", slot, ret ? "OK" : "FAIL");
return ret;
}
@@ -1,71 +0,0 @@
/**
* @file nfc_14a_4.h
* @brief ISO14443-4 T=CL emulation for ChameleonUltra
*
* Implements a full ISO14443-4 tag emulator:
* - I-blocks (information, chaining, CID)
* - R-blocks (ACK/NAK retransmit)
* - S-blocks (WTX to keep reader alive, DESELECT)
* - Static APDU response table (pre-loaded before field, no USB needed
* during field exchange)
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef NFC_14A_4_H
#define NFC_14A_4_H
#include "nfc_14a.h"
#include "tag_emulation.h"
/* Maximum APDU size (FSCI=8 → FSC=256, minus PCB+CRC = 253) */
#define NFC_14A_4_MAX_APDU 260 /* max APDU in RAM; flash entries capped at 253 */
/* Static APDU response table — up to 12 pre-configured command/response pairs.
* Loaded before field activation; firmware responds autonomously without USB. */
#define NFC_14A_4_MAX_STATIC_RESPONSES 12
#define NFC_14A_4_MAX_LARGE_RESPONSES 4 /* RAM-only, for resp > 253 bytes */
#define NFC_14A_4_MAX_LARGE_RESP_LEN 260 /* max large response size */
#define NFC_14A_4_MAX_STATIC_CMD_LEN 16
#define NFC_14A_4_MAX_STATIC_RESP_LEN 253 /* max bytes in flash-backed slot */
typedef struct __attribute__((packed)) {
uint8_t cmd_len;
uint8_t cmd[NFC_14A_4_MAX_STATIC_CMD_LEN];
uint8_t resp_len;
uint8_t resp[NFC_14A_4_MAX_STATIC_RESP_LEN];
} nfc_tag_14a_4_static_response_t;
/**
* Per-slot persistent data layout stored in FDS flash.
* Anti-collision response (UID/ATQA/SAK/ATS) plus the static response table.
*/
typedef struct __attribute__((packed)) {
nfc_tag_14a_coll_res_entity_t res_coll;
uint8_t static_resp_count;
nfc_tag_14a_4_static_response_t static_resp[NFC_14A_4_MAX_STATIC_RESPONSES];
} nfc_tag_14a_4_information_t;
/* Anti-collision resource — used by get_coll_res_data in app_cmd.c */
nfc_tag_14a_coll_res_reference_t *nfc_tag_14a_4_get_coll_res(void);
/* tag_base_map callbacks */
int nfc_tag_14a_4_data_loadcb(tag_specific_type_t type, tag_data_buffer_t *buffer);
int nfc_tag_14a_4_data_savecb(tag_specific_type_t type, tag_data_buffer_t *buffer);
bool nfc_tag_14a_4_data_factory(uint8_t slot, tag_specific_type_t tag_type);
/* Static response table management (called before hw mode -e) */
void nfc_tag_14a_4_add_static_response(const uint8_t *cmd, uint8_t cmd_len,
const uint8_t *resp, uint16_t resp_len);
void nfc_tag_14a_4_clear_static_responses(void);
/* APDU relay — host-driven responses */
bool nfc_tag_14a_4_get_pending_apdu(uint8_t *buf, uint16_t *length);
void nfc_tag_14a_4_set_response(const uint8_t *data, uint16_t length);
/* Reset handler */
void nfc_tag_14a_4_reset_handler(void);
#endif /* NFC_14A_4_H */
void nfc_tag_14a_4_get_debug_counters(uint8_t *rx, uint8_t *tx, uint8_t *last_pcb, uint8_t *last_match);
@@ -675,50 +675,29 @@ static void handle_fast_read_command(uint8_t block_num, uint8_t end_block_num) {
int block_max = get_block_max_by_tag_type(m_tag_type, true); int block_max = get_block_max_by_tag_type(m_tag_type, true);
if (block_num > end_block_num || end_block_num >= block_max) { if (block_num >= end_block_num || end_block_num >= block_max) {
nfc_tag_14a_tx_nbit(NAK_INVALID_OPERATION_TBV, 4); nfc_tag_14a_tx_nbit(NAK_INVALID_OPERATION_TBV, 4);
return; return;
} }
NRF_LOG_INFO("HANDLING FAST READ %02x %02x", block_num, end_block_num); NRF_LOG_INFO("HANDLING FAST READ %02x %02x", block_num, end_block_num);
// FAST_READ is inclusive: read from block_num to end_block_num (both included)
handle_any_read(block_num, end_block_num - block_num + 1, block_max); handle_any_read(block_num, end_block_num - block_num, block_max);
} }
static bool check_ro_lock_on_page(int block_num) { static bool check_ro_lock_on_page(int block_num) {
if (block_num < 3) return true; if (block_num < 3) return true;
else if (block_num == 3) { else if (block_num == 3) return (m_tag_information->memory[2][2] & 9) != 0; // bits 0 and 3
switch (m_tag_type) { else if (block_num <= MF0ICU1_PAGES) {
case TAG_TYPE_NTAG_213:
case TAG_TYPE_NTAG_215:
case TAG_TYPE_NTAG_216:
//page 3 can be locked or not independant of BL CC bit
//the BL bit only freezes the lock bytes !
return (m_tag_information->memory[2][2] & 8) != 0;
default:
return (m_tag_information->memory[2][2] & 9) != 0;
}
// bits 0 and 3
} else if (block_num <= MF0ICU1_PAGES) {
bool locked = false; bool locked = false;
switch (m_tag_type) {
case TAG_TYPE_NTAG_213:
case TAG_TYPE_NTAG_215:
case TAG_TYPE_NTAG_216: {
// pages can be locked or not independant of BL bits
//the BL bits only freezes the lock bytes !
uint16_t lock_bits = *(uint16_t *)&m_tag_information->memory[2][2];
return ((lock_bits >> block_num) & 0x01) == 1;
}
default:
// check block locking bits
if (block_num <= 9) locked |= (m_tag_information->memory[2][2] & 2) == 2;
else locked |= (m_tag_information->memory[2][2] & 4) == 4;
locked |= (((*(uint16_t *)&m_tag_information->memory[2][2]) >> block_num) & 1) == 1; // check block locking bits
if (block_num <= 9) locked |= (m_tag_information->memory[2][2] & 2) == 2;
else locked |= (m_tag_information->memory[2][2] & 4) == 4;
return locked; locked |= (((*(uint16_t *)&m_tag_information->memory[2][2]) >> block_num) & 1) == 1;
}
return locked;
} else { } else {
uint8_t *p_lock_bytes = NULL; uint8_t *p_lock_bytes = NULL;
int user_memory_end = 0; int user_memory_end = 0;
@@ -797,42 +776,9 @@ static bool check_ro_lock_on_page(int block_num) {
bool locked_small_range = ((lock_word >> (index / dyn_lock_bit_page_cnt)) & 1) != 0; bool locked_small_range = ((lock_word >> (index / dyn_lock_bit_page_cnt)) & 1) != 0;
bool locked_large_range = ((p_lock_bytes[2] >> (index / dyn_lock_bit_page_cnt / 2)) & 1) != 0; bool locked_large_range = ((p_lock_bytes[2] >> (index / dyn_lock_bit_page_cnt / 2)) & 1) != 0;
switch (m_tag_type) {
case TAG_TYPE_NTAG_213: return locked_small_range | locked_large_range;
case TAG_TYPE_NTAG_215:
case TAG_TYPE_NTAG_216:
// For NTAG213/215/216: byte 2 contains block-locking bits (BL) which only freeze
// the lock configuration. We only check the actual lock bits (L0-L15) in bytes 0-1.
return locked_small_range;
default:
return locked_small_range | locked_large_range;
}
} else { } else {
//Check the block locking bits to see if we can touch the dynamic locks bytes for NTAG tags
if(block_num == user_memory_end)
{
switch (m_tag_type) {
case TAG_TYPE_NTAG_213:
case TAG_TYPE_NTAG_215:
case TAG_TYPE_NTAG_216: {
uint8_t block_bytes = m_tag_information->memory[user_memory_end][2];
uint16_t block_world = 0;
// Each bit in block_bytes maps to 2 bits in block_world
for (int i = 0; i < 8; i++) {
if (block_bytes & (0x01 << i)) {
block_world |= (0x0003 << (i * 2));
}
}
p_lock_bytes = m_tag_information->memory[user_memory_end];
uint16_t lock_word = (((uint16_t)p_lock_bytes[1]) << 8) | (uint16_t)p_lock_bytes[0];
return (lock_word & block_world) != 0;
}
default:
break;
}
}
// check CFGLCK bit // check CFGLCK bit
int first_cfg_page = get_first_cfg_page_by_tag_type(m_tag_type); int first_cfg_page = get_first_cfg_page_by_tag_type(m_tag_type);
uint8_t access = m_tag_information->memory[first_cfg_page + CONF_ACCESS_PAGE_OFFSET][CONF_ACCESS_BYTE]; uint8_t access = m_tag_information->memory[first_cfg_page + CONF_ACCESS_PAGE_OFFSET][CONF_ACCESS_BYTE];
@@ -847,26 +793,7 @@ static bool check_ro_lock_on_page(int block_num) {
static int handle_write_command(uint8_t block_num, uint8_t *p_data) { static int handle_write_command(uint8_t block_num, uint8_t *p_data) {
int block_max = get_block_max_by_tag_type(m_tag_type, false); int block_max = get_block_max_by_tag_type(m_tag_type, false);
bool out_of_bounds = false; if (block_num >= block_max) {
switch (m_tag_type) {
case TAG_TYPE_NTAG_213:
case TAG_TYPE_NTAG_215:
case TAG_TYPE_NTAG_216: {
int first_cfg_page = get_first_cfg_page_by_tag_type(m_tag_type);
uint8_t cfglck = m_tag_information->memory[first_cfg_page][0] & 0x40;
// For NTAG cards we need to check CFGLCK bit for config pages
bool is_config_page = (block_num >= first_cfg_page) && (block_num <= first_cfg_page + 1);
bool config_locked = (cfglck != 0) && (!m_tag_information->config.mode_uid_magic);
bool is_beyond_user_memory = (block_num >= block_max);
out_of_bounds = (is_beyond_user_memory && !is_config_page) || (config_locked && is_config_page);
break;
}
default:
out_of_bounds = block_num >= block_max;
break;
}
// Reject out-of-bounds writes (except config pages)
if (out_of_bounds) {
NRF_LOG_ERROR("Write failed: block_num %08x >= block_max %08x", block_num, block_max); NRF_LOG_ERROR("Write failed: block_num %08x >= block_max %08x", block_num, block_max);
return NAK_INVALID_OPERATION_TBV; return NAK_INVALID_OPERATION_TBV;
} }
@@ -1111,8 +1111,6 @@ int nfc_tag_mf1_data_loadcb(tag_specific_type_t type, tag_data_buffer_t *buffer)
.cb_reset = nfc_tag_mf1_reset_handler, .cb_reset = nfc_tag_mf1_reset_handler,
}; };
nfc_tag_14a_set_handler(&handler_for_14a); nfc_tag_14a_set_handler(&handler_for_14a);
NRF_LOG_INFO("HF mf1 config 'field_off_do_reset' = %d", m_tag_information->config.field_off_do_reset);
nfc_tag_14a_set_reset_enable(m_tag_information->config.field_off_do_reset);
NRF_LOG_INFO("HF mf1 data load finish."); NRF_LOG_INFO("HF mf1 data load finish.");
} else { } else {
NRF_LOG_ERROR("nfc_tag_mf1_information_t too big."); NRF_LOG_ERROR("nfc_tag_mf1_information_t too big.");
@@ -1159,12 +1157,6 @@ bool nfc_tag_mf1_data_factory(uint8_t slot, tag_specific_type_t tag_type) {
p_mf1_information->config.use_mf1_coll_res = false; p_mf1_information->config.use_mf1_coll_res = false;
p_mf1_information->config.mode_block_write = NFC_TAG_MF1_WRITE_NORMAL; p_mf1_information->config.mode_block_write = NFC_TAG_MF1_WRITE_NORMAL;
p_mf1_information->config.detection_enable = false; p_mf1_information->config.detection_enable = false;
p_mf1_information->config.field_off_do_reset = false;
// zero for reserved byte
p_mf1_information->config.reserved1 = 0x00;
p_mf1_information->config.reserved2 = 0x00;
p_mf1_information->config.reserved3 = 0x00;
// save data to flash // save data to flash
tag_sense_type_t sense_type = get_sense_type_from_tag_type(tag_type); tag_sense_type_t sense_type = get_sense_type_from_tag_type(tag_type);
@@ -1244,10 +1236,3 @@ nfc_tag_mf1_write_mode_t nfc_tag_mf1_get_write_mode(void) {
return m_tag_information->config.mode_block_write; return m_tag_information->config.mode_block_write;
} }
void nfc_tag_mf1_set_field_off_do_reset(bool enable) {
m_tag_information->config.field_off_do_reset = enable;
}
bool nfc_tag_mf1_is_field_off_do_reset(void) {
return m_tag_information->config.field_off_do_reset;
}
@@ -71,15 +71,8 @@ typedef struct {
uint8_t detection_enable: 1; uint8_t detection_enable: 1;
// Allow to write block 0 (CUID/gen2 mode) // Allow to write block 0 (CUID/gen2 mode)
uint8_t mode_gen2_magic: 1; uint8_t mode_gen2_magic: 1;
/** // reserve
* Should the NFC peripheral be reset after losing the RF field? uint8_t reserved1: 4;
* This configuration can fix the issue where some card readers cause the CU to enter a strange state of no response/incorrect response.
* Once in this state, the device must be restarted to resolve the issue.
* Alternatively, enabling this configuration for resetting the NFC after leaving the rf field can also solve the aforementioned problem.
*/
uint8_t field_off_do_reset: 1;
// reserved
uint8_t reserved1: 3;
uint8_t reserved2; uint8_t reserved2;
uint8_t reserved3; uint8_t reserved3;
} nfc_tag_mf1_configure_t; } nfc_tag_mf1_configure_t;
@@ -164,7 +157,6 @@ void nfc_tag_mf1_set_use_mf1_coll_res(bool enable);
bool nfc_tag_mf1_is_use_mf1_coll_res(void); bool nfc_tag_mf1_is_use_mf1_coll_res(void);
void nfc_tag_mf1_set_write_mode(nfc_tag_mf1_write_mode_t write_mode); void nfc_tag_mf1_set_write_mode(nfc_tag_mf1_write_mode_t write_mode);
nfc_tag_mf1_write_mode_t nfc_tag_mf1_get_write_mode(void); nfc_tag_mf1_write_mode_t nfc_tag_mf1_get_write_mode(void);
void nfc_tag_mf1_set_field_off_do_reset(bool enable);
bool nfc_tag_mf1_is_field_off_do_reset(void);
#endif #endif
@@ -5,13 +5,10 @@
#include "bsp_delay.h" #include "bsp_delay.h"
#include "fds_util.h" #include "fds_util.h"
#include "nrf_gpio.h" #include "nrf_gpio.h"
#include "nrf_soc.h"
#include "nrfx_lpcomp.h" #include "nrfx_lpcomp.h"
#include "nrfx_pwm.h" #include "nrfx_pwm.h"
#include "protocols/em410x.h" #include "protocols/em410x.h"
#include "protocols/hidprox.h" #include "protocols/hidprox.h"
#include "protocols/ioprox.h"
#include "protocols/pac.h"
#include "protocols/viking.h" #include "protocols/viking.h"
#include "syssleep.h" #include "syssleep.h"
#include "tag_emulation.h" #include "tag_emulation.h"
@@ -24,6 +21,7 @@
NRF_LOG_MODULE_REGISTER(); NRF_LOG_MODULE_REGISTER();
#define ANT_NO_MOD() nrf_gpio_pin_clear(LF_MOD) #define ANT_NO_MOD() nrf_gpio_pin_clear(LF_MOD)
#define LF_125KHZ_BROADCAST_MAX (10)
// Whether the USB light effect is allowed to enable // Whether the USB light effect is allowed to enable
extern bool g_usb_led_marquee_enable; extern bool g_usb_led_marquee_enable;
@@ -42,8 +40,7 @@ static void lf_field_lost(void) {
g_is_tag_emulating = false; // Reset the flag in the emulation g_is_tag_emulating = false; // Reset the flag in the emulation
m_is_lf_emulating = false; m_is_lf_emulating = false;
TAG_FIELD_LED_OFF() // Make sure the indicator light of the LF field status TAG_FIELD_LED_OFF() // Make sure the indicator light of the LF field status
// Re-arm LPCOMP so the next field appearance triggers lpcomp_event_handler. NRF_LPCOMP->INTENSET = LPCOMP_INTENCLR_CROSS_Msk | LPCOMP_INTENCLR_UP_Msk | LPCOMP_INTENCLR_DOWN_Msk | LPCOMP_INTENCLR_READY_Msk;
NRF_LPCOMP->INTENSET = LPCOMP_INTENSET_UP_Msk;
// call sleep_timer_start *after* unsetting g_is_tag_emulating // call sleep_timer_start *after* unsetting g_is_tag_emulating
sleep_timer_start(SLEEP_DELAY_MS_FIELD_125KHZ_LOST); // Start the timer to enter the sleep sleep_timer_start(SLEEP_DELAY_MS_FIELD_125KHZ_LOST); // Start the timer to enter the sleep
NRF_LOG_INFO("LF FIELD LOST"); NRF_LOG_INFO("LF FIELD LOST");
@@ -68,15 +65,12 @@ bool is_lf_field_exists(void) {
* priority is set to APP_IRQ_PRIORITY_HIGH). * priority is set to APP_IRQ_PRIORITY_HIGH).
*/ */
static void lpcomp_event_handler(nrf_lpcomp_event_t event) { static void lpcomp_event_handler(nrf_lpcomp_event_t event) {
// Only when the lf-frequency emulation is not launched, and the analog card is started // Only when the lf -frequency emulation is not launched, and the analog card is started
if (m_is_lf_emulating || event != NRF_LPCOMP_EVENT_UP) { if (m_is_lf_emulating || event != NRF_LPCOMP_EVENT_UP) {
return; return;
} }
sleep_timer_stop(); // turn off dormant delay sleep_timer_stop(); // turn off dormant delay
// Disable LPCOMP during emulation — LF_RSSI fluctuates during load
// modulation and would trigger spurious DOWN events with DETECT_CROSS.
// Field-loss is checked periodically via EVT_END_SEQ0 in pwm_handler.
nrfx_lpcomp_disable(); nrfx_lpcomp_disable();
// set the emulation status logo bit // set the emulation status logo bit
@@ -89,9 +83,8 @@ static void lpcomp_event_handler(nrf_lpcomp_event_t event) {
set_slot_light_color(RGB_BLUE); set_slot_light_color(RGB_BLUE);
TAG_FIELD_LED_ON() TAG_FIELD_LED_ON()
// Loop continuously — no stop/restart gaps between sequence plays. // use precise hardware timer to broadcast card id
// Field-loss is detected in pwm_handler via EVT_END_SEQ0. nrfx_pwm_simple_playback(&m_broadcast, m_pwm_seq, LF_125KHZ_BROADCAST_MAX, NRFX_PWM_FLAG_STOP);
nrfx_pwm_simple_playback(&m_broadcast, m_pwm_seq, 1, NRFX_PWM_FLAG_LOOP);
NRF_LOG_INFO("LF FIELD DETECTED"); NRF_LOG_INFO("LF FIELD DETECTED");
} }
@@ -108,23 +101,21 @@ static void lpcomp_init(void) {
} }
static void pwm_handler(nrfx_pwm_evt_type_t event_type) { static void pwm_handler(nrfx_pwm_evt_type_t event_type) {
if (event_type == NRFX_PWM_EVT_END_SEQ0) {
// Fired at end of each loop iteration — check field without stopping PWM.
// Mask UP interrupt while sampling to prevent re-entrancy.
NRF_LPCOMP->INTENCLR = LPCOMP_INTENCLR_UP_Msk;
if (!is_lf_field_exists()) {
// Field gone — stop the loop; pwm_handler will get EVT_STOPPED next.
nrfx_pwm_stop(&m_broadcast, false);
}
// Re-enable will happen either in lf_field_lost (via INTENSET) or stays
// suppressed while PWM keeps looping (we only need it after field_lost).
return;
}
if (event_type != NRFX_PWM_EVT_STOPPED) { if (event_type != NRFX_PWM_EVT_STOPPED) {
return; return;
} }
// after last broadcast, force NO_MOD on antenna to measure field.
ANT_NO_MOD(); ANT_NO_MOD();
lf_field_lost(); bsp_delay_ms(1);
// We don't need any events, but only need to detect the state of the field
NRF_LPCOMP->INTENCLR = LPCOMP_INTENCLR_CROSS_Msk | LPCOMP_INTENCLR_UP_Msk | LPCOMP_INTENCLR_DOWN_Msk | LPCOMP_INTENCLR_READY_Msk;
if (is_lf_field_exists()) {
nrfx_lpcomp_disable();
nrfx_pwm_simple_playback(&m_broadcast, m_pwm_seq, LF_125KHZ_BROADCAST_MAX, NRFX_PWM_FLAG_STOP);
} else {
lf_field_lost();
}
} }
static void pwm_init(void) { static void pwm_init(void) {
@@ -144,23 +135,6 @@ static void pwm_init(void) {
} }
static void lf_sense_enable(void) { static void lf_sense_enable(void) {
// PWM bit timing divides HFCLK by a fixed ratio. On HFINT (64 MHz RC,
// ±1.5% at 25°C after factory trim, wider over temperature) this gives a
// chip-to-chip spread that NRZ readers — which see cumulative error across
// runs of same-polarity bits with no intra-run resync — reject even when
// Manchester/FSK readers don't. Holding HFXO brings the PWM clock to
// ±40 ppm. We can't lock to the reader's carrier (tag-mode antenna taps
// on this board are envelope-only), so this is as good as it gets.
//
// Paired release in lf_sense_disable(). SD reference-counts HFXO requests,
// so this coexists with BLE. Both functions run from thread context
// (tag_mode_enter/tag_emulation_sense_end) where SVCs are safe.
sd_clock_hfclk_request();
uint32_t hfclk_running = 0;
while (!hfclk_running) {
sd_clock_hfclk_is_running(&hfclk_running);
}
lpcomp_init(); lpcomp_init();
pwm_init(); // use precise hardware pwm to broadcast card id pwm_init(); // use precise hardware pwm to broadcast card id
if (is_lf_field_exists()) { if (is_lf_field_exists()) {
@@ -173,7 +147,6 @@ static void lf_sense_disable(void) {
nrfx_lpcomp_uninit(); nrfx_lpcomp_uninit();
m_pwm_seq = NULL; m_pwm_seq = NULL;
m_is_lf_emulating = false; m_is_lf_emulating = false;
sd_clock_hfclk_release();
} }
static enum { static enum {
@@ -182,10 +155,6 @@ static enum {
LF_SENSE_STATE_ENABLE, LF_SENSE_STATE_ENABLE,
} m_lf_sense_state = LF_SENSE_STATE_NONE; } m_lf_sense_state = LF_SENSE_STATE_NONE;
static uint16_t lf_em410x_id_size(tag_specific_type_t type) {
return type == TAG_TYPE_EM410X_ELECTRA ? LF_EM410X_ELECTRA_TAG_ID_SIZE : LF_EM410X_TAG_ID_SIZE;
}
/** /**
* @brief switchLfFieldInductionToEnableTheState * @brief switchLfFieldInductionToEnableTheState
*/ */
@@ -213,14 +182,13 @@ void lf_tag_125khz_sense_switch(bool enable) {
int lf_tag_data_loadcb(tag_specific_type_t type, tag_data_buffer_t *buffer) { int lf_tag_data_loadcb(tag_specific_type_t type, tag_data_buffer_t *buffer) {
// ensure buffer size is large enough for specific tag type, // ensure buffer size is large enough for specific tag type,
// so that tag data (e.g., card numbers) can be converted to corresponding pwm sequence here. // so that tag data (e.g., card numbers) can be converted to corresponding pwm sequence here.
if ((type == TAG_TYPE_EM410X || type == TAG_TYPE_EM410X_ELECTRA) && buffer->length >= lf_em410x_id_size(type)) { if (type == TAG_TYPE_EM410X && buffer->length >= LF_EM410X_TAG_ID_SIZE) {
const protocol *p = type == TAG_TYPE_EM410X_ELECTRA ? &em410x_electra : &em410x_64;
m_tag_type = type; m_tag_type = type;
void *codec = p->alloc(); void *codec = em410x_64.alloc();
m_pwm_seq = p->modulator(codec, buffer->buffer); m_pwm_seq = em410x_64.modulator(codec, buffer->buffer);
p->free(codec); em410x_64.free(codec);
NRF_LOG_INFO("load lf em410x%s data finish.", type == TAG_TYPE_EM410X_ELECTRA ? " electra" : ""); NRF_LOG_INFO("load lf em410x data finish.");
return lf_em410x_id_size(type); return LF_EM410X_TAG_ID_SIZE;
} }
if (type == TAG_TYPE_HID_PROX && buffer->length >= LF_HIDPROX_TAG_ID_SIZE) { if (type == TAG_TYPE_HID_PROX && buffer->length >= LF_HIDPROX_TAG_ID_SIZE) {
@@ -232,15 +200,6 @@ int lf_tag_data_loadcb(tag_specific_type_t type, tag_data_buffer_t *buffer) {
return LF_HIDPROX_TAG_ID_SIZE; return LF_HIDPROX_TAG_ID_SIZE;
} }
if (type == TAG_TYPE_IOPROX && buffer->length >= LF_IOPROX_TAG_ID_SIZE) {
m_tag_type = type;
void *codec = ioprox.alloc();
m_pwm_seq = ioprox.modulator(codec, buffer->buffer);
ioprox.free(codec);
NRF_LOG_INFO("load lf ioprox data finish.");
return LF_IOPROX_TAG_ID_SIZE;
}
if (type == TAG_TYPE_VIKING && buffer->length >= LF_VIKING_TAG_ID_SIZE) { if (type == TAG_TYPE_VIKING && buffer->length >= LF_VIKING_TAG_ID_SIZE) {
m_tag_type = type; m_tag_type = type;
void *codec = viking.alloc(); void *codec = viking.alloc();
@@ -250,15 +209,6 @@ int lf_tag_data_loadcb(tag_specific_type_t type, tag_data_buffer_t *buffer) {
return LF_VIKING_TAG_ID_SIZE; return LF_VIKING_TAG_ID_SIZE;
} }
if (type == TAG_TYPE_PAC && buffer->length >= LF_PAC_TAG_ID_SIZE) {
m_tag_type = type;
void *codec = pac.alloc();
m_pwm_seq = pac.modulator(codec, buffer->buffer);
pac.free(codec);
NRF_LOG_INFO("load lf pac data finish.");
return LF_PAC_TAG_ID_SIZE;
}
NRF_LOG_ERROR("no valid data exists in buffer for tag type: %d.", type); NRF_LOG_ERROR("no valid data exists in buffer for tag type: %d.", type);
return 0; return 0;
} }
@@ -271,13 +221,7 @@ int lf_tag_data_loadcb(tag_specific_type_t type, tag_data_buffer_t *buffer) {
int lf_tag_em410x_data_savecb(tag_specific_type_t type, tag_data_buffer_t *buffer) { int lf_tag_em410x_data_savecb(tag_specific_type_t type, tag_data_buffer_t *buffer) {
// Make sure to load this tag before allowing saving // Make sure to load this tag before allowing saving
// Just save the original card package directly // Just save the original card package directly
if (m_tag_type == TAG_TYPE_EM410X) { return m_tag_type == TAG_TYPE_EM410X ? LF_EM410X_TAG_ID_SIZE : 0;
return LF_EM410X_TAG_ID_SIZE;
}
if (m_tag_type == TAG_TYPE_EM410X_ELECTRA) {
return LF_EM410X_ELECTRA_TAG_ID_SIZE;
}
return 0;
} }
/** @brief Id card deposit card number before callback /** @brief Id card deposit card number before callback
@@ -291,17 +235,6 @@ int lf_tag_hidprox_data_savecb(tag_specific_type_t type, tag_data_buffer_t *buff
return m_tag_type == TAG_TYPE_HID_PROX ? LF_HIDPROX_TAG_ID_SIZE : 0; return m_tag_type == TAG_TYPE_HID_PROX ? LF_HIDPROX_TAG_ID_SIZE : 0;
} }
/** @brief Id card deposit card number before callback
* @param type Refined tag type
* @param buffer Data buffer
* @return The length of the data that needs to be saved is that it does not save when 0
*/
int lf_tag_ioprox_data_savecb(tag_specific_type_t type, tag_data_buffer_t *buffer) {
// Make sure to load this tag before allowing saving
// Just save the original card package directly
return m_tag_type == TAG_TYPE_IOPROX ? LF_IOPROX_TAG_ID_SIZE : 0;
}
/** @brief Id card deposit card number before callback /** @brief Id card deposit card number before callback
* @param type Refined tag type * @param type Refined tag type
* @param buffer Data buffer * @param buffer Data buffer
@@ -319,7 +252,7 @@ bool lf_tag_data_factory(uint8_t slot, tag_specific_type_t tag_type, uint8_t *ta
fds_slot_record_map_t map_info; // Get the special card slot FDS record information fds_slot_record_map_t map_info; // Get the special card slot FDS record information
get_fds_map_by_slot_sense_type_for_dump(slot, sense_type, &map_info); get_fds_map_by_slot_sense_type_for_dump(slot, sense_type, &map_info);
// Call the blocked FDS to write the function, and write the data of the specified field type of the card slot into the Flash // Call the blocked FDS to write the function, and write the data of the specified field type of the card slot into the Flash
bool ret = fds_write_sync(map_info.id, map_info.key, length, (uint8_t *)tag_id); bool ret = fds_write_sync(map_info.id, map_info.key, sizeof(tag_id), (uint8_t *)tag_id);
if (ret) { if (ret) {
NRF_LOG_INFO("Factory slot data success."); NRF_LOG_INFO("Factory slot data success.");
} else { } else {
@@ -334,18 +267,9 @@ bool lf_tag_data_factory(uint8_t slot, tag_specific_type_t tag_type, uint8_t *ta
* @return Whether the format is successful, if the formatting is successful, it will return to True, otherwise False will be returned * @return Whether the format is successful, if the formatting is successful, it will return to True, otherwise False will be returned
*/ */
bool lf_tag_em410x_data_factory(uint8_t slot, tag_specific_type_t tag_type) { bool lf_tag_em410x_data_factory(uint8_t slot, tag_specific_type_t tag_type) {
static const uint8_t tag_id_base[LF_EM410X_TAG_ID_SIZE] = {0xDE, 0xAD, 0xBE, 0xEF, 0x88}; // default id, must to align(4), more word...
static const uint8_t tag_id_electra[LF_EM410X_ELECTRA_TAG_ID_SIZE] = {0xDE, 0xAD, 0xBE, 0xEF, 0x88, uint8_t tag_id[5] = {0xDE, 0xAD, 0xBE, 0xEF, 0x88};
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; return lf_tag_data_factory(slot, tag_type, tag_id, sizeof(tag_id));
switch (tag_type) {
case TAG_TYPE_EM410X_ELECTRA:
return lf_tag_data_factory(slot, tag_type, (uint8_t *)tag_id_electra, sizeof(tag_id_electra));
case TAG_TYPE_EM410X:
return lf_tag_data_factory(slot, tag_type, (uint8_t *)tag_id_base, sizeof(tag_id_base));
default:
return false;
}
} }
/** @brief Id card deposit card number before callback /** @brief Id card deposit card number before callback
@@ -359,18 +283,6 @@ bool lf_tag_hidprox_data_factory(uint8_t slot, tag_specific_type_t tag_type) {
return lf_tag_data_factory(slot, tag_type, tag_id, sizeof(tag_id)); return lf_tag_data_factory(slot, tag_type, tag_id, sizeof(tag_id));
} }
/** @brief Id card deposit card number before callback
* @param slot Card slot number
* @param tag_type Refined tag type
* @return Whether the format is successful, if the formatting is successful, it will return to True, otherwise False will be returned
*/
bool lf_tag_ioprox_data_factory(uint8_t slot, tag_specific_type_t tag_type) {
uint8_t tag_id[16] = {
0x01,0xAA,0x30,0x39,0x00,0x78,0x6A,0xA0,0x33,0x09,0xCF,0xEF,0x00,0x00,0x00,0x00
};
return lf_tag_data_factory(slot, tag_type, tag_id, sizeof(tag_id));
}
/** @brief Id card deposit card number before callback /** @brief Id card deposit card number before callback
* @param slot Card slot number * @param slot Card slot number
* @param tag_type Refined tag type * @param tag_type Refined tag type
@@ -381,13 +293,3 @@ bool lf_tag_viking_data_factory(uint8_t slot, tag_specific_type_t tag_type) {
uint8_t tag_id[4] = {0xDE, 0xAD, 0xBE, 0xEF}; uint8_t tag_id[4] = {0xDE, 0xAD, 0xBE, 0xEF};
return lf_tag_data_factory(slot, tag_type, tag_id, sizeof(tag_id)); return lf_tag_data_factory(slot, tag_type, tag_id, sizeof(tag_id));
} }
int lf_tag_pac_data_savecb(tag_specific_type_t type, tag_data_buffer_t *buffer) {
return m_tag_type == TAG_TYPE_PAC ? LF_PAC_TAG_ID_SIZE : 0;
}
bool lf_tag_pac_data_factory(uint8_t slot, tag_specific_type_t tag_type) {
// default id: 8 ASCII bytes
uint8_t tag_id[8] = {'C', 'A', 'R', 'D', '0', '0', '0', '1'};
return lf_tag_data_factory(slot, tag_type, tag_id, sizeof(tag_id));
}

Some files were not shown because too many files have changed in this diff Show More