Author SHA1 Message Date
ambroserrg fc8cfb4e63 Update documentation for hardware upgrade
Added notices to README.md and CHANGELOG.md about the hardware upgrade
that allows restarting the Ultra by running each RGB color once,
resolving previous firmware modification issues.
2026-03-10 10:29:12 +08:00
GameTec-liveandGitHub 7846bca44b Merge pull request #364 from luu176/main
Add `hf mf autopwn` command with key saving and card dump
2026-02-24 18:35:19 +01:00
GameTec-liveandGitHub 7f71201e16 Merge pull request #363 from WillyJL/feat/rgb-marquee-improvements-upstream
New Symmetrical LED Animation Mode and Improved Minimal Mode
2026-02-19 11:57:05 +01:00
Luu dbc8ce0526 autopwn command added 2026-02-18 22:40:38 +01:00
WillyJL 24259f78b9 Update CHANGELOG.md 2026-02-18 20:58:33 +01:00
WillyJL 4bafe186b3 Symmetric animation mode for boot, shutdown, usb 2026-02-18 20:47:56 +01:00
WillyJL 402665cd75 Minimal shutdown and post-flash boot animations 2026-02-18 20:47:55 +01:00
WillyJL 9f25debe4d Fix rgb_marquee_sweep_from_to() to the left 2026-02-18 20:47:55 +01:00
WillyJL f2225767a2 Give useful names to ledblink*() functions 2026-02-18 20:47:54 +01:00
GameTec-liveandGitHub b108c84af9 Merge pull request #355 from LupusE/main
Fix broken CLI after multiple merges
2026-02-07 23:19:22 +01:00
Benjamin Moeller c7aebdf168 Fix Part2, because of multiple PR used the same ID. 2026-02-07 23:16:08 +01:00
Benjamin MøllerandGitHub 643dd03ff6 Update chameleon_enum.py
ReFix, because of link to firmware/application/src/data_cmd.h
2026-02-07 23:12:22 +01:00
Benjamin Moeller fa35c8ae3f fix double value in unique enumeration 2026-02-07 22:59:43 +01:00
Benjamin Moeller 5ee9254012 Delete invalid escape charater in message 2026-02-07 22:58:44 +01:00
GameTec-liveandGitHub dfa2680d51 Merge pull request #201 from rickNmorty2/enh-clone
Add CLI commands to dump and clone tag
2026-02-07 20:47:38 +01:00
GameTec-liveandGitHub d7ae363099 Merge pull request #343 from LupusE/main
Fix compiler error  `a label can only be part of a statement and a de…
2026-02-07 20:44:36 +01:00
GameTec-liveandGitHub 4d3479943f Merge branch 'main' into enh-clone 2026-02-07 20:43:59 +01:00
GameTec-liveandGitHub 38e3567add Merge pull request #306 from merlokk/lf_read_adc
Adds generic ADC read functionality
2026-02-07 20:42:25 +01:00
GameTec-liveandGitHub 2c7c3eeb4d Merge pull request #352 from suut/fix_bad_missing_tools_warning
Fix bad missing tools warning
2026-02-06 23:58:07 +01:00
Benjamin MøllerandGitHub 7a69e68b3f Update nfc_mf0_ntag.c (braces instead of simicolon)
Changed formatting of the fix for a more union layout, as suggested in the comments.
2026-02-06 22:36:02 +01:00
suut 688bb452aa Fix bad missing tools warning 2026-02-06 21:39:19 +01:00
GameTec-liveandGitHub 9ae0755d80 Merge pull request #332 from RickConsole/fix-hidprox-cli-args
fix hidprox UnboundLocalError cli arg error + hidprox slot set warning
2026-02-06 11:19:48 +01:00
GameTec-liveandGitHub 3755bc24ce Merge pull request #307 from azuwis/hf-14a-config
Add `hf 14a config` to deal with badly configured cards
2026-02-06 11:19:23 +01:00
Benjamin MøllerandGitHub 5d2946ffaa Merge branch 'RfidResearchGroup:main' into main 2026-02-04 13:10:05 +01:00
GameTec-liveandGitHub 41937c52a3 Merge pull request #338 from naaraxi/main
Add Electra intercom tag support with slot auto switch
2026-02-03 22:45:19 +01:00
DXLandGitHub db3e53dd08 Merge pull request #346 from YuyangisCoding/main
fix cli arg parser for FIELD_OFF_DO_RESET
2026-02-03 19:16:59 +08:00
Alexandru MazâluandGitHub 35192d9fc1 Merge branch 'RfidResearchGroup:main' into main 2026-02-03 12:49:45 +02:00
GameTec-liveandGitHub c3a35ffd9b Merge pull request #345 from suut/fix_windows_build
Fix Windows build
2026-02-03 05:54:03 +01:00
yuyangzhang eaa366b453 fix cli arg parser for FIELD_OFF_DO_RESET 2026-02-03 12:16:51 +10:00
suut 23181d84f1 Run GitHub actions 2026-02-03 00:34:05 +01:00
Rick ConsoleandGitHub 997f6bcaab Merge branch 'main' into fix-hidprox-cli-args 2026-02-02 15:58:42 -05:00
suut 6e4a564417 Fix typo in CMakeLists.txt 2026-02-02 21:25:14 +01:00
suut f6ac86cc19 Fix missing bswap64 on Windows 2026-02-02 21:22:01 +01:00
suut f1ae0e0bb0 Disable the "deprecated declaration" warning for MSVC build 2026-02-02 20:35:59 +01:00
Benjamin Moeller 54b302356a Fix compiler error a label can only be part of a statement and a declaration is not a statement 2026-02-02 17:00:21 +01:00
DXLandGitHub eeaca1604f Merge pull request #342 from tommiv/cli-field-off-reset
Added FIELD_OFF_DO_RESET support to cli hf mf econfig
2026-02-02 21:29:05 +08:00
Zhong Jianxin b967bdcd98 Add hf 14a config to deal with badly configured cards 2026-02-02 20:48:03 +08:00
Konstantin Ilchenko 613987b4f4 Shortened set_field_off_do_reset to field_off_do_reset CLI flags 2026-02-02 12:21:39 +02:00
Konstantin Ilchenko c1b9df0e5d Added FIELD_OFF_DO_RESET support to cli hf mf econfig 2026-02-02 12:18:26 +02:00
GameTec-liveandGitHub 6686acf714 Merge pull request #314 from dogty/main
Fixed FAST_READ command and handling of dynamic and static locks for …
2026-02-01 15:06:40 +01:00
GameTec-liveandGitHub d303ddb9c7 Merge pull request #316 from brewt/hidprox-hw-slot-list-formatting
Fix HIDProx formatting with `hw slot list`
2026-02-01 15:05:53 +01:00
GameTec-liveandGitHub ef76f794d0 Merge pull request #341 from MusicLeecher/main
Fix typo in hid prox econfig and add ACTProx HID card type
2026-02-01 15:04:37 +01:00
Alexandru MazâluandGitHub d1ad03a567 Merge branch 'RfidResearchGroup:main' into main 2026-01-30 23:57:30 +02:00
GameTec-liveandGitHub 13d0060ed7 Merge pull request #337 from suut/cli_android_support
TCP transport in the CLI to support Android (Termux)
2026-01-30 21:52:20 +01:00
GameTec-liveandGitHub 055ae2322e Merge branch 'main' into cli_android_support 2026-01-30 21:51:46 +01:00
Gabriel Cardoso 2f86756948 fix tipo in previous commint 2026-01-26 19:10:39 +00:00
Gabriel Cardoso 8ed2677ba2 - added ACT Prox HID format 2026-01-26 19:06:34 +00:00
Gabriel Cardoso 71e45a3194 - fix typo in hidprox econfig set/get function 2026-01-26 18:34:06 +00:00
Alexandru Mazalu c1c2b66882 Add Electra intercom tag support with slot auto switch 2026-01-24 14:45:15 +02:00
DXLandGitHub f2bea7d4ea Merge pull request #299 from xianglin1998/main
Fix some bugs & Performance improvement
2026-01-24 19:44:04 +08:00
dxl ca743273af Added cmd for set mf1 config 'field_off_do_reset' 2026-01-24 19:39:49 +08:00
dxl c0e7cb18f7 Export some functions related to mf1 emulation configuration. 2026-01-24 19:38:36 +08:00
dxl a25b4a8b64 Added MF1 configuration to enable/disable resetting NFC peripherals after leaving RF field. 2026-01-24 19:19:35 +08:00
DXLandGitHub 1543bac108 Merge branch 'RfidResearchGroup:main' into main 2026-01-24 17:54:15 +08:00
suut 8f0a9240ba Fix linter errors 2026-01-24 02:09:48 +01:00
suut b10f4ccb25 Fix build on Darwin 2026-01-24 01:59:03 +01:00
suut 0bc8287f14 Better Android detection 2026-01-24 01:48:03 +01:00
suut ec1bd33b5f Add TCP and Android (Termux) support 2026-01-24 01:30:19 +01:00
GameTec-liveandGitHub 53fe83d81a Merge pull request #331 from suut/issue-322__led_bug_after_battery_check
Fix LEDs being stuck on after battery check
2026-01-23 20:36:10 +01:00
GameTec-liveandGitHub 00de9663f3 Merge pull request #315 from brewt/color-string-em-read-fix
Fix error in `lf em 410x read` from color_string()
2026-01-21 22:56:47 +01:00
noprotoandPhilippe Teuwen 1ee7da63b1 Add initial ULCG/USCUID-UL support 2026-01-19 23:29:05 +01:00
Rick Console b272682546 fix hidprox UnboundLocalError cli arg error 2026-01-19 00:09:33 -05:00
suut 6a43ce6e05 Add information to changelog about the battery check 2026-01-18 17:53:29 +01:00
suut 9e7aa3635f Fix LEDs being stuck on after battery check
Signed-off-by: suut <suut@users.noreply.github.com>
2026-01-18 17:51:23 +01:00
dogty 11bafac176 fix(ntag): Check if the dynamic lock page can be written or not (for NTAG215, NTAG213 and NTAG216) 2025-11-03 14:46:37 +01:00
dogty 64f22dfc53 docs: updated changelog 2025-11-01 18:17:10 +01:00
dogty f6cde1629a fix(ntag): CFGLCK bit is checked before to see if CFG0 and CFG1 are writable even if a password is set (only for NTAG215, NTAG216 and NTAG213) 2025-11-01 18:17:09 +01:00
dogty 335ffee69d fix(ntag): The page lock check doesn't take into account the blocking lock bits (only for NTAG213, NTAG215 and NTA216) 2025-11-01 18:17:09 +01:00
dogty 494d397e7b fix(nfc 14a): The fast read command now handles the last block 2025-11-01 18:14:59 +01:00
dogty afacb2a565 fix: handle REQA and WUP commands for NFC tag 14a 2025-11-01 18:14:35 +01:00
Adrian YeeandGitHub efbf79e497 Fix HIDProx formatting with hw slot list
Fix HIDProx formatting to match the formatting of all other card types.
2025-10-30 16:53:33 -07:00
Adrian YeeandGitHub 7efde79235 Fix error in lf em 410x read from color_string()
Fix bug in color_string() usage introduced by 35d2f40 (bug #295).
2025-10-30 16:41:12 -07:00
GameTec-liveandGitHub 97dfe5b9a4 Merge pull request #295 from mischif/color-string
Use a function for colored strings
2025-10-13 21:44:00 +02:00
Oleg Moiseenko f5bed3c5b1 Adjusts LF ADC raw data processing
Adjusts the bit shift for ADC raw data conversion to refine the value range.
2025-10-09 09:42:43 +03:00
Oleg Moiseenko e2c6bfc9e1 Adds generic ADC read functionality
This introduces a new command to sample the ADC values from the LF antenna and returns them to the user.
2025-10-09 00:32:07 +03:00
Jeremy Brown e1b4218bb3 Fixed unnecessary formatting 2025-10-05 15:42:11 -04:00
dxl dabf1a415f Use a more thorough NFC peripheral reset strategy. 2025-09-23 11:08:27 +08:00
dxl 67357feba0 Parameter error should return STATUS_PAR_ERR 2025-09-23 00:56:55 +08:00
dxl 0858e325e5 Fix the bug where HEX printing of large data packets caused it to freeze. 2025-09-23 00:43:17 +08:00
dxl 1e8758bc1f Fixed bug that caused NFC peripheral devices to always respond with incorrect data due to special condition. 2025-09-23 00:38:01 +08:00
dxl 0448a369a3 Fixed a bug where the parameters were abnormal but the subsequent logic was still executed. 2025-09-23 00:33:05 +08:00
dxl d065a2f18e Improve the efficiency of USB packet reception. 2025-09-23 00:32:17 +08:00
Jeremy Brown d440d98d12 Cleaned up follow-up issues 2025-09-19 01:11:46 -04:00
dxl 50f72653db Fix the bug that cannot be compiled on the MSYS2 platform. 2025-09-19 12:45:32 +08:00
GameTec-liveandGitHub 8998621a47 Merge pull request #294 from RocketGod-git/feature/local-changes
Add option for NFC Field Generator
2025-09-12 19:12:27 +02:00
Jeremy Brown 35d2f40ff5 Switched to function for color stings 2025-09-11 11:52:05 -04:00
RocketGod b3b1fa9c71 Update app_main.c 2025-09-06 21:49:46 -07:00
RocketGod 0aacfcde24 Add option for NFC Field Generator
I've integrated the option for the NFC Field Generator into Python CLI and application files. TODO: GUI integration.
2025-09-06 21:46:22 -07:00
GameTec-liveandGitHub 964904fbfa Merge pull request #285 from jekkos/fix-senested-key-recovery
Fix for mf static nested key recovery
2025-09-02 19:44:25 +02:00
GameTec-liveandGitHub ea2a9410b3 Merge branch 'main' into fix-senested-key-recovery 2025-09-02 19:40:39 +02:00
jekkos 62e904f114 Fix for static nested key recovery
When runnin static nested recovery, we run into a sector auth faild
error that stops the recovery process. Add a try/execpt handler so we
can continue to try all possibitlities
2025-08-28 21:55:06 +02:00
you fcbf474e19 Update CHANGELOG.md 2024-04-24 00:50:17 +02:00
you 0686d0816d Fix default ACL (suggested by @taichunmin) 2024-04-24 00:42:33 +02:00
you 4568e8fd57 Fix dump and clone commands accordingly to PR#199 2024-04-24 00:41:13 +02:00
you 6ddda661d8 tosqash 2024-04-23 23:45:13 +02:00
you 7912bde311 Merge branch 'main' of https://github.com/RfidResearchGroup/ChameleonUltra into enh-clone 2024-04-23 23:34:17 +02:00
you 8a02d6f4f8 Add CLI command to clone a MF tag from file (bin/hex) 2024-01-28 17:55:01 +01:00
you a1a49ed953 Add CLI command to dump a MF tag to file (bin/hex) 2024-01-28 17:54:20 +01:00
you 3e6a2b505b Add fchk argument to export found keys to file 2024-01-28 17:48:18 +01:00
taichunmin e470958373 Added command to check keys of multiple sectors at once 2024-01-20 17:25:05 +08:00
44 changed files with 5481 additions and 1311 deletions
+16
View File
@@ -3,6 +3,22 @@ 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...
## [unreleased][unreleased]
- Hardware upgrade: Restarting the Ultra now only requires running each of the three RGB colors once, resolving previous firmware modification issues
- 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 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]
- Added UV, formatter and linter. Contribution guidelines. (@GameTec-live)
+4
View File
@@ -18,6 +18,10 @@ Guangdong, China: [MTools Tec](https://shop.mtoolstec.com/)
Lazada One, Singapore: [Aliexpress by RRG](https://proxgrind.aliexpress.com/store/1101312023)
# Hardware Upgrade Notice
**Important:** The Chameleon Ultra hardware has been upgraded! Restarting the device now only requires running each of the three RGB colors once (equivalent to a restart). This resolves previous issues where firmware modifications could cause the device to malfunction.
# What is it and how to use ?
Read the [available documentation](https://github.com/RfidResearchGroup/ChameleonUltra/wiki).
+1
View File
@@ -345,6 +345,7 @@ ifeq (${CURRENT_DEVICE_TYPE}, ${CHAMELEON_ULTRA})
$(PROJ_DIR)/rfid/reader/lf/lf_t55xx_data.c \
$(PROJ_DIR)/rfid/reader/lf/lf_hidprox_data.c \
$(PROJ_DIR)/rfid/reader/lf/lf_viking_data.c \
$(PROJ_DIR)/rfid/reader/lf/lf_reader_generic.c \
INC_FOLDERS +=\
${PROJ_DIR}/rfid/reader/ \
+142 -13
View File
@@ -141,7 +141,7 @@ static data_frame_tx_t *cmd_processor_get_device_settings(uint16_t cmd, uint16_t
}
static data_frame_tx_t *cmd_processor_set_animation_mode(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
if ((length != 1) || (data[0] > 2)) {
if ((length != 1) || (data[0] >= SettingsAnimationModeMAX)) {
return data_frame_make(cmd, STATUS_PAR_ERR, 0, NULL);
}
settings_set_animation_config(data[0]);
@@ -488,6 +488,35 @@ static data_frame_tx_t *cmd_processor_mf1_write_one_block(uint16_t cmd, uint16_t
return data_frame_make(cmd, status, 0, NULL);
}
#if defined(PROJECT_CHAMELEON_ULTRA)
static data_frame_tx_t *cmd_processor_hf14a_set_field_on(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
device_mode_t mode = get_device_mode();
if (mode != DEVICE_MODE_READER) {
return data_frame_make(cmd, STATUS_DEVICE_MODE_ERROR, 0, NULL);
}
// Reset and turn on the antenna
pcd_14a_reader_reset();
pcd_14a_reader_antenna_on();
return data_frame_make(cmd, STATUS_SUCCESS, 0, NULL);
}
static data_frame_tx_t *cmd_processor_hf14a_set_field_off(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
device_mode_t mode = get_device_mode();
if (mode != DEVICE_MODE_READER) {
return data_frame_make(cmd, STATUS_DEVICE_MODE_ERROR, 0, NULL);
}
// Turn off the antenna
pcd_14a_reader_antenna_off();
return data_frame_make(cmd, STATUS_SUCCESS, 0, NULL);
}
#endif
static data_frame_tx_t *cmd_processor_hf14a_raw(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
// Response Buffer
uint8_t resp[DEF_FIFO_LENGTH] = { 0x00 };
@@ -545,6 +574,21 @@ static data_frame_tx_t *cmd_processor_hf14a_raw(uint16_t cmd, uint16_t status, u
return data_frame_make(cmd, status, resp_length, resp);
}
static data_frame_tx_t *cmd_processor_hf14a_get_config(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
hf14a_config_t *hc = get_hf14a_config();
return data_frame_make(cmd, STATUS_SUCCESS, sizeof(hf14a_config_t), (uint8_t *)hc);
}
static data_frame_tx_t *cmd_processor_hf14a_set_config(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
if (length != sizeof(hf14a_config_t)) {
return data_frame_make(cmd, STATUS_PAR_ERR, 0, NULL);
}
hf14a_config_t hc;
memcpy(&hc, data, sizeof(hf14a_config_t));
set_hf14a_config(&hc);
return data_frame_make(cmd, STATUS_SUCCESS, 0, NULL);
}
static data_frame_tx_t *cmd_processor_mf1_manipulate_value_block(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
typedef struct {
uint8_t src_type;
@@ -598,12 +642,16 @@ static data_frame_tx_t *cmd_processor_mf1_manipulate_value_block(uint16_t cmd, u
}
static data_frame_tx_t *cmd_processor_em410x_scan(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
uint8_t card_buffer[7] = {0x00};
uint8_t card_buffer[2 + LF_EM410X_ELECTRA_TAG_ID_SIZE] = {0x00};
status = scan_em410x(card_buffer);
if (status != STATUS_LF_TAG_OK) {
return data_frame_make(cmd, status, 0, NULL);
}
return data_frame_make(cmd, STATUS_LF_TAG_OK, sizeof(card_buffer), card_buffer);
tag_specific_type_t tag_type = (card_buffer[0] << 8) | card_buffer[1];
uint16_t id_size = (tag_type == TAG_TYPE_EM410X_ELECTRA) ? LF_EM410X_ELECTRA_TAG_ID_SIZE : LF_EM410X_TAG_ID_SIZE;
return data_frame_make(cmd, STATUS_LF_TAG_OK, 2 + id_size, card_buffer);
}
static data_frame_tx_t *cmd_processor_em410x_write_to_t55xx(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
@@ -621,6 +669,21 @@ static data_frame_tx_t *cmd_processor_em410x_write_to_t55xx(uint16_t cmd, uint16
return data_frame_make(cmd, status, 0, NULL);
}
static data_frame_tx_t *cmd_processor_em410x_electra_write_to_t55xx(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
typedef struct {
uint8_t id[13];
uint8_t new_key[4];
uint8_t old_keys[4]; // we can have more than one... struct just to compute offsets with min 1 key
} PACKED payload_t;
payload_t *payload = (payload_t *)data;
if (length < sizeof(payload_t) || (length - offsetof(payload_t, old_keys)) % sizeof(payload->old_keys) != 0) {
return data_frame_make(cmd, STATUS_PAR_ERR, 0, NULL);
}
status = write_em410x_electra_to_t55xx(payload->id, payload->new_key, payload->old_keys, (length - offsetof(payload_t, old_keys)) / sizeof(payload->old_keys));
return data_frame_make(cmd, status, 0, NULL);
}
static data_frame_tx_t *cmd_processor_hidprox_write_to_t55xx(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
typedef struct {
uint8_t id[13];
@@ -674,6 +737,32 @@ static data_frame_tx_t *cmd_processor_viking_write_to_t55xx(uint16_t cmd, uint16
status = write_viking_to_t55xx(payload->id, payload->new_key, payload->old_keys, (length - offsetof(payload_t, old_keys)) / sizeof(payload->old_keys));
return data_frame_make(cmd, status, 0, NULL);
}
#define GENERIC_READ_LEN 800
#define GENERIC_READ_TIMEOUT_MS 500
static data_frame_tx_t *cmd_processor_generic_read(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
uint8_t *outdata = malloc(GENERIC_READ_LEN);
if (outdata == NULL) {
return data_frame_make(cmd, STATUS_MEM_ERR, 0, NULL);
}
size_t outlen = 0;
if (!raw_read_to_buffer(outdata, GENERIC_READ_LEN, GENERIC_READ_TIMEOUT_MS, &outlen)) {
free(outdata);
return data_frame_make(cmd, STATUS_CMD_ERR, 0, NULL);
};
data_frame_tx_t *frame = data_frame_make(cmd, STATUS_LF_TAG_OK, outlen, outdata);
free(outdata);
if (frame == NULL) {
return data_frame_make(cmd, STATUS_CREATE_RESPONSE_ERR, 0, NULL);
}
return frame;
}
#endif
@@ -801,24 +890,41 @@ static data_frame_tx_t *cmd_processor_wipe_fds(uint16_t cmd, uint16_t status, ui
return data_frame_make(cmd, status, 0, NULL);
}
static bool get_active_em410x_type(tag_specific_type_t *tag_type_out, uint16_t *id_size_out) {
tag_slot_specific_type_t tag_types;
tag_emulation_get_specific_types_by_slot(tag_emulation_get_slot(), &tag_types);
if (tag_types.tag_lf == TAG_TYPE_EM410X || tag_types.tag_lf == TAG_TYPE_EM410X_ELECTRA) {
*tag_type_out = tag_types.tag_lf;
*id_size_out = (tag_types.tag_lf == TAG_TYPE_EM410X_ELECTRA) ? LF_EM410X_ELECTRA_TAG_ID_SIZE : LF_EM410X_TAG_ID_SIZE;
return true;
}
return false;
}
static data_frame_tx_t *cmd_processor_em410x_set_emu_id(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
if (length != LF_EM410X_TAG_ID_SIZE) {
tag_specific_type_t tag_type;
uint16_t id_size;
if (!get_active_em410x_type(&tag_type, &id_size) || length != id_size) {
return data_frame_make(cmd, STATUS_PAR_ERR, 0, NULL);
}
tag_data_buffer_t *buffer = get_buffer_by_tag_type(TAG_TYPE_EM410X);
memcpy(buffer->buffer, data, LF_EM410X_TAG_ID_SIZE);
tag_emulation_load_by_buffer(TAG_TYPE_EM410X, false);
tag_data_buffer_t *buffer = get_buffer_by_tag_type(tag_type);
memcpy(buffer->buffer, data, id_size);
tag_emulation_load_by_buffer(tag_type, false);
return data_frame_make(cmd, STATUS_SUCCESS, 0, NULL);
}
static data_frame_tx_t *cmd_processor_em410x_get_emu_id(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
tag_slot_specific_type_t tag_types;
tag_emulation_get_specific_types_by_slot(tag_emulation_get_slot(), &tag_types);
if (tag_types.tag_lf != TAG_TYPE_EM410X) {
tag_specific_type_t tag_type;
uint16_t id_size;
if (!get_active_em410x_type(&tag_type, &id_size)) {
return data_frame_make(cmd, STATUS_PAR_ERR, 0, data); // no data in slot, don't send garbage
}
tag_data_buffer_t *buffer = get_buffer_by_tag_type(TAG_TYPE_EM410X);
return data_frame_make(cmd, STATUS_SUCCESS, LF_EM410X_TAG_ID_SIZE, buffer->buffer);
tag_data_buffer_t *buffer = get_buffer_by_tag_type(tag_type);
uint8_t resp[2 + LF_EM410X_ELECTRA_TAG_ID_SIZE] = {0x00};
resp[0] = tag_type >> 8;
resp[1] = tag_type;
memcpy(resp + 2, buffer->buffer, id_size);
return data_frame_make(cmd, STATUS_SUCCESS, 2 + id_size, resp);
}
static data_frame_tx_t *cmd_processor_hidprox_set_emu_id(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
@@ -967,7 +1073,7 @@ static data_frame_tx_t *cmd_processor_mf1_write_emu_block_data(uint16_t cmd, uin
uint8_t block_index = data[0];
uint8_t block_count = (length - 1) / NFC_TAG_MF1_DATA_SIZE;
if (block_index + block_count > NFC_TAG_MF1_BLOCK_MAX) {
status = STATUS_PAR_ERR;
return data_frame_make(cmd, STATUS_PAR_ERR, 0, NULL);
}
tag_data_buffer_t *buffer = get_buffer_by_tag_type(TAG_TYPE_MIFARE_4096);
nfc_tag_mf1_information_t *info = (nfc_tag_mf1_information_t *)buffer->buffer;
@@ -1345,6 +1451,19 @@ static data_frame_tx_t *cmd_processor_mf1_set_write_mode(uint16_t cmd, uint16_t
return data_frame_make(cmd, STATUS_SUCCESS, 0, NULL);
}
static data_frame_tx_t *cmd_processor_mf1_get_field_off_do_reset(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
uint8_t enable = nfc_tag_mf1_is_field_off_do_reset();
return data_frame_make(cmd, STATUS_SUCCESS, 1, &enable);
}
static data_frame_tx_t *cmd_processor_mf1_set_field_off_do_reset(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
if (length != 1 || data[0] >= 2) {
return data_frame_make(cmd, STATUS_PAR_ERR, 0, NULL);
}
nfc_tag_mf1_set_field_off_do_reset(data[0]);
return data_frame_make(cmd, STATUS_SUCCESS, 0, NULL);
}
static data_frame_tx_t *cmd_processor_get_enabled_slots(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
struct {
uint8_t enabled_hf;
@@ -1573,10 +1692,18 @@ static cmd_data_map_t m_data_cmd_map[] = {
{ DATA_CMD_EM410X_SCAN, before_reader_run, cmd_processor_em410x_scan, NULL },
{ DATA_CMD_EM410X_WRITE_TO_T55XX, before_reader_run, cmd_processor_em410x_write_to_t55xx, NULL },
{ DATA_CMD_EM410X_ELECTRA_WRITE_TO_T55XX,before_reader_run, cmd_processor_em410x_electra_write_to_t55xx, NULL },
{ DATA_CMD_HIDPROX_SCAN, before_reader_run, cmd_processor_hidprox_scan, NULL },
{ DATA_CMD_HIDPROX_WRITE_TO_T55XX, before_reader_run, cmd_processor_hidprox_write_to_t55xx, NULL },
{ DATA_CMD_VIKING_SCAN, before_reader_run, cmd_processor_viking_scan, NULL },
{ DATA_CMD_VIKING_WRITE_TO_T55XX, before_reader_run, cmd_processor_viking_write_to_t55xx, NULL },
{ DATA_CMD_ADC_GENERIC_READ, before_reader_run, cmd_processor_generic_read, NULL },
{ DATA_CMD_HF14A_SET_FIELD_ON, before_reader_run, cmd_processor_hf14a_set_field_on, NULL },
{ DATA_CMD_HF14A_SET_FIELD_OFF, before_reader_run, cmd_processor_hf14a_set_field_off, NULL },
{ DATA_CMD_HF14A_GET_CONFIG, NULL, cmd_processor_hf14a_get_config, NULL },
{ DATA_CMD_HF14A_SET_CONFIG, NULL, cmd_processor_hf14a_set_config, NULL },
#endif
@@ -1598,6 +1725,8 @@ static cmd_data_map_t m_data_cmd_map[] = {
{ DATA_CMD_MF1_SET_BLOCK_ANTI_COLL_MODE, NULL, cmd_processor_mf1_set_block_anti_coll_mode, NULL },
{ DATA_CMD_MF1_GET_WRITE_MODE, NULL, cmd_processor_mf1_get_write_mode, NULL },
{ DATA_CMD_MF1_SET_WRITE_MODE, NULL, cmd_processor_mf1_set_write_mode, NULL },
{ DATA_CMD_MF1_GET_FIELD_OFF_DO_RESET, NULL, cmd_processor_mf1_get_field_off_do_reset, NULL },
{ DATA_CMD_MF1_SET_FIELD_OFF_DO_RESET, NULL, cmd_processor_mf1_set_field_off_do_reset, NULL },
{ DATA_CMD_MF0_NTAG_GET_UID_MAGIC_MODE, NULL, cmd_processor_mf0_ntag_get_uid_mode, NULL },
{ DATA_CMD_MF0_NTAG_SET_UID_MAGIC_MODE, NULL, cmd_processor_mf0_ntag_set_uid_mode, NULL },
+183 -37
View File
@@ -41,6 +41,10 @@ NRF_LOG_MODULE_REGISTER();
#include "tag_persistence.h"
#include "settings.h"
#if defined(PROJECT_CHAMELEON_ULTRA)
#include "rc522.h"
#endif
// Defining soft timers
APP_TIMER_DEF(m_button_check_timer); // Timer for button debounce
@@ -56,6 +60,9 @@ static bool m_is_a_btn_release = false;
static bool m_system_off_processing = false;
// NFC field generator state
volatile bool m_is_field_on = false;
// cpu reset reason
static uint32_t m_reset_source;
static uint32_t m_gpregret_val;
@@ -139,12 +146,41 @@ static void gpio_te_init(void) {
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
*/
static void button_pin_handler(nrf_drv_gpiote_pin_t pin, nrf_gpiote_polarity_t action) {
device_mode_t mode = get_device_mode();
// Temporarily allow only the analog card mode to respond to button operations
if (mode == DEVICE_MODE_TAG) {
// Allow button operations in both tag and reader mode
if (mode == DEVICE_MODE_TAG || mode == DEVICE_MODE_READER) {
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
app_timer_start(m_button_check_timer, APP_TIMER_TICKS(50), &pin_static); // Start timer anti-shake
@@ -162,7 +198,9 @@ static void timer_button_event_handle(void *arg) {
NRF_LOG_INFO("BUTTON press during shutdown");
return;
}
nrf_drv_gpiote_pin_t pin = *(nrf_drv_gpiote_pin_t *)arg;
// Check here if the current GPIO is at the pressed level
if (nrf_gpio_pin_read(pin) == 1) {
if (pin == BUTTON_1) {
@@ -263,24 +301,28 @@ static void system_off_enter(void) {
for (uint8_t i = 0; i < RGB_LIST_NUM; i++) {
nrf_gpio_pin_clear(p_led_array[i]);
}
// Power off animation
uint8_t animation_config = settings_get_animation_config();
if (animation_config == SettingsAnimationModeFull) {
uint8_t slot = tag_emulation_get_slot();
// Power off animation
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;
}
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 (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);
}
if (animation_config == SettingsAnimationModeFull) {
if (m_system_off_processing) rgb_marquee_sweep_from_to(color, slot, dir ? 7 : 0);
if (m_system_off_processing) rgb_marquee_sweep_fade(color, dir, 7, 99, 75);
if (m_system_off_processing) rgb_marquee_sweep_fade(color, !dir, 7, 75, 50);
if (m_system_off_processing) rgb_marquee_sweep_fade(color, dir, 7, 50, 25);
if (m_system_off_processing) rgb_marquee_sweep_fade(color, !dir, 7, 25, 0);
} else if (animation_config == SettingsAnimationModeMinimal) {
if (m_system_off_processing) rgb_marquee_sweep_from_to(color, slot, !dir ? 7 : 0);
} else if (animation_config == SettingsAnimationModeSymmetric) {
if (m_system_off_processing) rgb_marquee_symmetric_in(color, slot);
}
rgb_marquee_stop();
if (!m_system_off_processing) {
@@ -422,11 +464,13 @@ static void check_wakeup_src(void) {
// Button wake-up boot animation
uint8_t animation_config = settings_get_animation_config();
if (animation_config == SettingsAnimationModeFull) {
ledblink2(color, !dir, 11);
ledblink2(color, dir, 11);
ledblink2(color, !dir, dir ? slot : 7 - slot);
rgb_marquee_sweep_to(color, !dir, 11);
rgb_marquee_sweep_to(color, dir, 11);
rgb_marquee_sweep_to(color, !dir, dir ? slot : 7 - slot);
} else if (animation_config == SettingsAnimationModeMinimal) {
ledblink2(color, !dir, dir ? slot : 7 - slot);
rgb_marquee_sweep_to(color, !dir, dir ? slot : 7 - slot);
} else if (animation_config == SettingsAnimationModeSymmetric) {
rgb_marquee_symmetric_out(color, slot);
} else {
set_slot_light_color(color);
}
@@ -459,9 +503,12 @@ static void check_wakeup_src(void) {
uint8_t animation_config = settings_get_animation_config();
if (animation_config == SettingsAnimationModeFull) {
// In the case of field wake-up, only one round of RGB is swept as the power-on animation
ledblink2(color, !dir, dir ? slot : 7 - slot);
rgb_marquee_sweep_to(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();
// We can only run tag emulation at field wakeup source.
@@ -488,9 +535,20 @@ static void check_wakeup_src(void) {
tag_emulation_factory_init();
// RGB
ledblink2(0, !dir, 11);
ledblink2(1, dir, 11);
ledblink2(2, !dir, 11);
uint8_t animation_config = settings_get_animation_config();
if (animation_config == SettingsAnimationModeFull) {
rgb_marquee_sweep_to(0, !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.
set_slot_light_color(color);
@@ -521,6 +579,12 @@ static void cycle_slot(bool dec) {
}
// 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
// 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
apply_slot_change(slot_now, slot_new);
}
@@ -604,10 +668,22 @@ static void btn_fn_copy_lf(uint8_t slot, tag_specific_type_t type) {
data = id_buffer;
break;
case TAG_TYPE_EM410X:
case TAG_TYPE_EM410X_ELECTRA: {
status = scan_em410x(id_buffer);
size = LF_EM410X_TAG_ID_SIZE;
data = id_buffer + 2; // skip tag type
tag_specific_type_t detected_type = (id_buffer[0] << 8) | id_buffer[1];
tag_specific_type_t new_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;
}
case TAG_TYPE_VIKING:
status = scan_viking(id_buffer);
size = LF_VIKING_TAG_ID_SIZE;
@@ -757,13 +833,67 @@ static void run_button_function_by_settings(settings_button_function_t sbf) {
case SettingsButtonCloneIcUid:
btn_fn_copy_ic_uid();
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
case SettingsButtonShowBattery:
show_battery();
break;
default:
NRF_LOG_ERROR("Unsupported button function")
NRF_LOG_ERROR("Unsupported button function");
break;
}
}
@@ -792,8 +922,10 @@ static void button_press_process(void) {
}
// Disable led marquee for usb at button pressed.
g_usb_led_marquee_enable = false;
// Re-delay into hibernation
sleep_timer_start(SLEEP_DELAY_MS_BUTTON_CLICK);
// Re-delay into hibernation (unless field is on)
if (!m_is_field_on) {
sleep_timer_start(SLEEP_DELAY_MS_BUTTON_CLICK);
}
}
}
@@ -812,12 +944,17 @@ static void blink_usb_led_status(void) {
}
} else {
// The light effect is enabled and can be displayed
if (is_rgb_marquee_enable()) {
if (rgb_marquee_is_enabled()) {
is_working = true;
if (g_usb_port_opened) {
ledblink1(color, dir);
uint8_t animation_config = settings_get_animation_config();
if (animation_config == SettingsAnimationModeSymmetric) {
rgb_marquee_usb_open_symmetric(color);
} else {
rgb_marquee_usb_open_sweep(color, dir);
}
} else {
ledblink6();
rgb_marquee_usb_idle();
}
} else {
if (is_working) {
@@ -887,8 +1024,17 @@ int main(void) {
lesc_event_process();
// Button event process
button_press_process();
// Led blink at usb status
blink_usb_led_status();
#if defined(PROJECT_CHAMELEON_ULTRA)
// 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_frame_process();
// Log print process
+4
View File
@@ -31,5 +31,9 @@
#define STATUS_FLASH_WRITE_FAIL (0x70) // Flash writing failed
#define STATUS_FLASH_READ_FAIL (0x71) // Flash read failed
#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
+12
View File
@@ -73,6 +73,12 @@
#define DATA_CMD_MF1_ENC_NESTED_ACQUIRE (2014)
#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)
//
// ******************************************************************
@@ -84,10 +90,14 @@
//
#define DATA_CMD_EM410X_SCAN (3000)
#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_WRITE_TO_T55XX (3003)
#define DATA_CMD_VIKING_SCAN (3004)
#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)
//
// ******************************************************************
@@ -136,6 +146,8 @@
#define DATA_CMD_MF0_NTAG_GET_DETECTION_LOG (4035)
#define DATA_CMD_MF0_NTAG_GET_DETECTION_ENABLE (4036)
#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)
//
// ******************************************************************
@@ -61,7 +61,10 @@ static volatile bool m_is_responded = false;
static uint8_t m_nfc_rx_buffer[MAX_NFC_RX_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
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
@@ -349,7 +352,7 @@ void nfc_tag_14a_data_process(uint8_t *p_data) {
m_tag_state_14a = NFC_TAG_STATE_14A_READY;
// After receiving the WUPA or REQA instruction, we need to reply to ATQA
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 {
m_tag_state_14a = NFC_TAG_STATE_14A_IDLE;
NRF_LOG_INFO("Auto anti-collision resource no exists.");
@@ -397,6 +400,15 @@ void nfc_tag_14a_data_process(uint8_t *p_data) {
m_tag_state_14a = NFC_TAG_STATE_14A_IDLE;
}
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: {
// After receiving the wrong level instruction, directly reset the status machine
NRF_LOG_INFO("[MFEMUL_SELECT] Incorrect cascade level received: %02x", p_data[0]);
@@ -523,6 +535,43 @@ 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);
/* Begin: Workaround for anomaly 25 */
/* Workaround for wrong SENSRES values require using SDD00001, but here SDD00100 is used
because it is required to operate with Windows Phone */
nrf_nfct_sensres_bit_frame_sdd_set(NRF_NFCT_SENSRES_BIT_FRAME_SDD_00100);
/* End: Workaround for anomaly 25 */
// 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) {
// STOP TX
*(volatile uint32_t *)0x40005010 = 0x01;
@@ -571,6 +620,13 @@ void nfc_tag_14a_event_callback(nrfx_nfct_evt_t const *p_event) {
TAG_FIELD_LED_OFF()
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");
break;
}
@@ -688,3 +744,11 @@ bool is_valid_uid_size(uint8_t uid_length) {
uid_length == NFC_TAG_14A_UID_DOUBLE_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;
}
@@ -115,4 +115,8 @@ void nfc_tag_14a_tx_nbit(uint8_t data, uint32_t bits);
// Determine whether it is an effective 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
@@ -675,29 +675,50 @@ 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);
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);
return;
}
NRF_LOG_INFO("HANDLING FAST READ %02x %02x", block_num, end_block_num);
handle_any_read(block_num, end_block_num - block_num, block_max);
// 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);
}
static bool check_ro_lock_on_page(int block_num) {
if (block_num < 3) return true;
else if (block_num == 3) return (m_tag_information->memory[2][2] & 9) != 0; // bits 0 and 3
else if (block_num <= MF0ICU1_PAGES) {
else if (block_num == 3) {
switch (m_tag_type) {
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;
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;
// 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;
locked |= (((*(uint16_t *)&m_tag_information->memory[2][2]) >> block_num) & 1) == 1;
return locked;
return locked;
}
} else {
uint8_t *p_lock_bytes = NULL;
int user_memory_end = 0;
@@ -776,9 +797,42 @@ 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_large_range = ((p_lock_bytes[2] >> (index / dyn_lock_bit_page_cnt / 2)) & 1) != 0;
return locked_small_range | locked_large_range;
switch (m_tag_type) {
case TAG_TYPE_NTAG_213:
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 {
//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
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];
@@ -793,7 +847,26 @@ static bool check_ro_lock_on_page(int block_num) {
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);
if (block_num >= block_max) {
bool out_of_bounds = false;
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);
return NAK_INVALID_OPERATION_TBV;
}
@@ -1111,6 +1111,8 @@ int nfc_tag_mf1_data_loadcb(tag_specific_type_t type, tag_data_buffer_t *buffer)
.cb_reset = nfc_tag_mf1_reset_handler,
};
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.");
} else {
NRF_LOG_ERROR("nfc_tag_mf1_information_t too big.");
@@ -1157,6 +1159,12 @@ 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.mode_block_write = NFC_TAG_MF1_WRITE_NORMAL;
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
tag_sense_type_t sense_type = get_sense_type_from_tag_type(tag_type);
@@ -1236,3 +1244,10 @@ nfc_tag_mf1_write_mode_t nfc_tag_mf1_get_write_mode(void) {
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,8 +71,15 @@ typedef struct {
uint8_t detection_enable: 1;
// Allow to write block 0 (CUID/gen2 mode)
uint8_t mode_gen2_magic: 1;
// reserve
uint8_t reserved1: 4;
/**
* Should the NFC peripheral be reset after losing the RF field?
* 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 reserved3;
} nfc_tag_mf1_configure_t;
@@ -157,6 +164,7 @@ void nfc_tag_mf1_set_use_mf1_coll_res(bool enable);
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);
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
@@ -155,6 +155,10 @@ static enum {
LF_SENSE_STATE_ENABLE,
} 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
*/
@@ -182,13 +186,14 @@ void lf_tag_125khz_sense_switch(bool enable) {
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,
// so that tag data (e.g., card numbers) can be converted to corresponding pwm sequence here.
if (type == TAG_TYPE_EM410X && buffer->length >= LF_EM410X_TAG_ID_SIZE) {
if ((type == TAG_TYPE_EM410X || type == TAG_TYPE_EM410X_ELECTRA) && buffer->length >= lf_em410x_id_size(type)) {
const protocol *p = type == TAG_TYPE_EM410X_ELECTRA ? &em410x_electra : &em410x_64;
m_tag_type = type;
void *codec = em410x_64.alloc();
m_pwm_seq = em410x_64.modulator(codec, buffer->buffer);
em410x_64.free(codec);
NRF_LOG_INFO("load lf em410x data finish.");
return LF_EM410X_TAG_ID_SIZE;
void *codec = p->alloc();
m_pwm_seq = p->modulator(codec, buffer->buffer);
p->free(codec);
NRF_LOG_INFO("load lf em410x%s data finish.", type == TAG_TYPE_EM410X_ELECTRA ? " electra" : "");
return lf_em410x_id_size(type);
}
if (type == TAG_TYPE_HID_PROX && buffer->length >= LF_HIDPROX_TAG_ID_SIZE) {
@@ -221,7 +226,13 @@ 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) {
// Make sure to load this tag before allowing saving
// Just save the original card package directly
return m_tag_type == TAG_TYPE_EM410X ? LF_EM410X_TAG_ID_SIZE : 0;
if (m_tag_type == TAG_TYPE_EM410X) {
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
@@ -252,7 +263,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
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
bool ret = fds_write_sync(map_info.id, map_info.key, sizeof(tag_id), (uint8_t *)tag_id);
bool ret = fds_write_sync(map_info.id, map_info.key, length, (uint8_t *)tag_id);
if (ret) {
NRF_LOG_INFO("Factory slot data success.");
} else {
@@ -267,9 +278,18 @@ 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
*/
bool lf_tag_em410x_data_factory(uint8_t slot, tag_specific_type_t tag_type) {
// default id, must to align(4), more word...
uint8_t tag_id[5] = {0xDE, 0xAD, 0xBE, 0xEF, 0x88};
return lf_tag_data_factory(slot, tag_type, tag_id, sizeof(tag_id));
static const uint8_t tag_id_base[LF_EM410X_TAG_ID_SIZE] = {0xDE, 0xAD, 0xBE, 0xEF, 0x88};
static const uint8_t tag_id_electra[LF_EM410X_ELECTRA_TAG_ID_SIZE] = {0xDE, 0xAD, 0xBE, 0xEF, 0x88,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
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
@@ -6,6 +6,7 @@
#include "tag_emulation.h"
#define LF_EM410X_TAG_ID_SIZE 5
#define LF_EM410X_ELECTRA_TAG_ID_SIZE 13
#define LF_HIDPROX_TAG_ID_SIZE 13
#define LF_VIKING_TAG_ID_SIZE 4
@@ -15,11 +15,17 @@
#define EM_BITS_PER_ROW_COUNT (EM_COLUMN_COUNT + 1)
#define EM_RAW_SIZE (64)
#define EM_DATA_SIZE (5)
#define EM_DATA_SIZE_BASE (5)
#define EM_ELECTRA_EPILOGUE_SIZE (8)
#define EM_DATA_SIZE_ELECTRA (EM_DATA_SIZE_BASE + EM_ELECTRA_EPILOGUE_SIZE)
#define EM_DATA_SIZE_MAX (EM_DATA_SIZE_ELECTRA)
#define EM_T55XX_ELECTRA_BLOCK_COUNT (5)
#define EM_ROW_COUNT (10)
#define EM_COLUMN_COUNT (4)
#define EM_HEADER (0x1ff) // 9 bits of 1
#define EM_ENCODED_DATA_HEADER (0xFF80000000000000ULL)
#define EM_T55XX_BLOCK_COUNT (3)
#define EM_READ_TIME1_BASE (0x40)
@@ -33,16 +39,25 @@
#include "nrf_log_default_backends.h"
NRF_LOG_MODULE_REGISTER();
static nrf_pwm_values_wave_form_t m_em410x_pwm_seq_vals[EM_RAW_SIZE] = {};
static nrf_pwm_values_wave_form_t m_em410x_pwm_seq_vals_base[EM_RAW_SIZE] = {};
static nrf_pwm_values_wave_form_t m_em410x_pwm_seq_vals_electra[EM_RAW_SIZE * 2] = {};
nrf_pwm_sequence_t const m_em410x_pwm_seq = {
.values.p_wave_form = m_em410x_pwm_seq_vals,
.length = NRF_PWM_VALUES_LENGTH(m_em410x_pwm_seq_vals),
nrf_pwm_sequence_t const m_em410x_pwm_seq_base = {
.values.p_wave_form = m_em410x_pwm_seq_vals_base,
.length = NRF_PWM_VALUES_LENGTH(m_em410x_pwm_seq_vals_base),
.repeats = 0,
.end_delay = 0,
};
nrf_pwm_sequence_t const m_em410x_pwm_seq_electra = {
.values.p_wave_form = m_em410x_pwm_seq_vals_electra,
.length = NRF_PWM_VALUES_LENGTH(m_em410x_pwm_seq_vals_electra),
.repeats = 0,
.end_delay = 0,
};
const protocol *em410x_protocols[] = {
&em410x_electra,
&em410x_64,
&em410x_32,
&em410x_16,
@@ -51,9 +66,11 @@ const protocol *em410x_protocols[] = {
size_t em410x_protocols_size = ARRAY_SIZE(em410x_protocols);
typedef struct {
uint8_t data[EM_DATA_SIZE];
uint8_t data[EM_DATA_SIZE_MAX];
uint64_t raw;
uint64_t epilogue;
uint8_t raw_length;
uint8_t total_length;
manchester *modem;
} em410x_codec;
@@ -80,6 +97,17 @@ uint64_t em410x_raw_data(uint8_t *uid) {
return raw;
}
uint64_t em410x_raw_epilogue(uint8_t *uid) {
uint64_t raw = 0;
for (int i = 0; i < EM_ELECTRA_EPILOGUE_SIZE; i++) {
raw <<= 8;
raw |= uid[EM_DATA_SIZE_BASE + i];
}
return raw;
}
bool em410x_get_time(uint16_t divisor, uint8_t interval, uint8_t base) {
return interval >= (base - EM_READ_JITTER_TIME_BASE) / divisor &&
interval <= (base + EM_READ_JITTER_TIME_BASE) / divisor;
@@ -142,9 +170,11 @@ void em410x_free(em410x_codec *d) {
uint8_t *em410x_get_data(em410x_codec *d) { return d->data; };
void em410x_decoder_start(em410x_codec *d, uint8_t format) {
memset(d->data, 0, EM_DATA_SIZE);
memset(d->data, 0, EM_DATA_SIZE_MAX);
d->raw = 0;
d->raw_length = 0;
d->total_length = 0;
d->epilogue = 0;
manchester_reset(d->modem);
};
@@ -202,6 +232,8 @@ bool em410x_decoder_feed(em410x_codec *d, uint16_t interval) {
if (bitlen == -1) {
d->raw = 0;
d->raw_length = 0;
d->total_length = 0;
d->epilogue = 0;
return false;
}
for (int i = 0; i < bitlen; i++) {
@@ -212,6 +244,79 @@ bool em410x_decoder_feed(em410x_codec *d, uint16_t interval) {
return false;
};
void em410x_electra_decoder_start(em410x_codec *d, uint8_t format) {
em410x_decoder_start(d, format);
}
static bool em410x_electra_decode_feed(em410x_codec *d, bool bit) {
bool carry_bit = (d->epilogue >> 63) & 0x01;
if (d->total_length < EM_RAW_SIZE + EM_RAW_SIZE) {
d->total_length++;
}
d->raw = (d->raw << 1) | carry_bit;
d->epilogue = (d->epilogue << 1) | (bit ? 1 : 0);
if (d->total_length < EM_RAW_SIZE + EM_RAW_SIZE) {
return false;
}
if ((d->raw & EM_ENCODED_DATA_HEADER) != EM_ENCODED_DATA_HEADER) {
return false;
}
if (d->raw & 0x01) {
return false;
}
uint8_t pc = 0;
for (int i = 0; i < EM_ROW_COUNT + 1; i++) {
uint8_t row = d->raw >> (EM_RAW_SIZE - 9 - (i + 1) * EM_BITS_PER_ROW_COUNT) & 0x1f;
uint8_t data = (row >> 1) & 0x0f;
pc ^= data;
if (i == 10) {
break;
}
if (!oddparity8(row)) { // row parity
return false;
}
if (i % 2) {
d->data[i >> 1] |= data;
} else {
d->data[i >> 1] = data << 4;
}
}
// if we only saw the same frame twice, treat as standard EM410X
if (d->raw == d->epilogue) {
return false;
}
for (int i = 0; i < EM_ELECTRA_EPILOGUE_SIZE; i++) {
d->data[EM_DATA_SIZE_BASE + i] = (d->epilogue >> ((EM_ELECTRA_EPILOGUE_SIZE - 1 - i) * 8)) & 0xFF;
}
return pc == 0x00;
}
bool em410x_electra_decoder_feed(em410x_codec *d, uint16_t interval) {
bool bits[2] = {0};
int8_t bitlen = 0;
manchester_feed(d->modem, (uint8_t)interval, bits, &bitlen);
if (bitlen == -1) {
em410x_decoder_start(d, 0);
return false;
}
for (int i = 0; i < bitlen; i++) {
if (em410x_electra_decode_feed(d, bits[i])) {
return true;
}
}
return false;
};
const nrf_pwm_sequence_t *em410x_modulator(em410x_codec *d, uint8_t *buf) {
uint64_t lo = em410x_raw_data(buf);
for (int i = 0; i < EM_RAW_SIZE; i++) {
@@ -219,16 +324,50 @@ const nrf_pwm_sequence_t *em410x_modulator(em410x_codec *d, uint8_t *buf) {
if (IS_SET(lo, EM_RAW_SIZE - i - 1)) {
msb = (1 << 15);
}
m_em410x_pwm_seq_vals[i].channel_0 = msb | 32;
m_em410x_pwm_seq_vals[i].counter_top = 64;
m_em410x_pwm_seq_vals_base[i].channel_0 = msb | 32;
m_em410x_pwm_seq_vals_base[i].counter_top = 64;
}
return &m_em410x_pwm_seq;
return &m_em410x_pwm_seq_base;
};
const nrf_pwm_sequence_t *em410x_electra_modulator(em410x_codec *d, uint8_t *buf) {
uint64_t data[] = {em410x_raw_data(buf), em410x_raw_epilogue(buf)};
uint16_t output_index = 0;
for (int frame = 0; frame < 2; frame++) {
for (int i = 0; i < EM_RAW_SIZE; i++) {
uint16_t msb = 0x00;
if (IS_SET(data[frame], EM_RAW_SIZE - i - 1)) {
msb = (1 << 15);
}
m_em410x_pwm_seq_vals_electra[output_index].channel_0 = msb | 32;
m_em410x_pwm_seq_vals_electra[output_index].counter_top = 64;
output_index++;
}
}
return &m_em410x_pwm_seq_electra;
};
// EM-Micro, EM410x/64 (std)
const protocol em410x_electra = {
.tag_type = TAG_TYPE_EM410X_ELECTRA,
.data_size = EM_DATA_SIZE_ELECTRA,
.alloc = (codec_alloc)em410x_64_alloc,
.free = (codec_free)em410x_free,
.get_data = (codec_get_data)em410x_get_data,
.modulator = (modulator)em410x_electra_modulator,
.decoder =
{
.start = (decoder_start)em410x_electra_decoder_start,
.feed = (decoder_feed)em410x_electra_decoder_feed,
},
};
// EM-Micro, EM410x/64 (std)
const protocol em410x_64 = {
.tag_type = TAG_TYPE_EM410X_64,
.data_size = EM_DATA_SIZE,
.data_size = EM_DATA_SIZE_BASE,
.alloc = (codec_alloc)em410x_64_alloc,
.free = (codec_free)em410x_free,
.get_data = (codec_get_data)em410x_get_data,
@@ -243,7 +382,7 @@ const protocol em410x_64 = {
// EM-Micro, EM410x/32
const protocol em410x_32 = {
.tag_type = TAG_TYPE_EM410X_32,
.data_size = EM_DATA_SIZE,
.data_size = EM_DATA_SIZE_BASE,
.alloc = (codec_alloc)em410x_32_alloc,
.free = (codec_free)em410x_free,
.get_data = (codec_get_data)em410x_get_data,
@@ -258,7 +397,7 @@ const protocol em410x_32 = {
// EM-Micro, EM410x/16
const protocol em410x_16 = {
.tag_type = TAG_TYPE_EM410X_16,
.data_size = EM_DATA_SIZE,
.data_size = EM_DATA_SIZE_BASE,
.alloc = (codec_alloc)em410x_16_alloc,
.free = (codec_free)em410x_free,
.get_data = (codec_get_data)em410x_get_data,
@@ -277,4 +416,17 @@ uint8_t em410x_t55xx_writer(uint8_t *uid, uint32_t *blks) {
blks[1] = raw >> 32;
blks[2] = raw & 0xffffffff;
return EM_T55XX_BLOCK_COUNT;
}
}
uint8_t em410x_electra_t55xx_writer(uint8_t *uid, uint32_t *blks) {
uint64_t raw_data = em410x_raw_data(uid);
uint64_t raw_epilogue = em410x_raw_epilogue(uid);
blks[0] = T5577_EM410X_ELECTRA_CONFIG;
blks[1] = raw_data >> 32;
blks[2] = raw_data & 0xffffffff;
blks[3] = raw_epilogue >> 32;
blks[4] = raw_epilogue & 0xffffffff;
return EM_T55XX_ELECTRA_BLOCK_COUNT;
}
@@ -5,8 +5,10 @@
extern const protocol em410x_64;
extern const protocol em410x_32;
extern const protocol em410x_16;
extern const protocol em410x_electra;
extern const protocol* em410x_protocols[];
extern size_t em410x_protocols_size;
uint8_t em410x_t55xx_writer(uint8_t* uid, uint32_t* blks);
uint8_t em410x_t55xx_writer(uint8_t* uid, uint32_t* blks);
uint8_t em410x_electra_t55xx_writer(uint8_t* uid, uint32_t* blks);
@@ -51,6 +51,12 @@ extern "C" {
T5577_PWD | \
(2 << T5577_MAXBLOCK_SHIFT))
#define T5577_EM410X_ELECTRA_CONFIG ( \
T5577_BITRATE_RF_64 | \
T5577_MODULATION_MANCHESTER | \
T5577_PWD | \
(4 << T5577_MAXBLOCK_SHIFT))
#define T5577_HIDPROX_CONFIG ( \
T5577_BITRATE_RF_50 | \
T5577_MODULATION_FSK2a | \
@@ -68,4 +74,4 @@ void t55xx_reset_passwd(uint32_t old_passwd, uint32_t new_passwd);
#ifdef __cplusplus
}
#endif
#endif
@@ -18,6 +18,7 @@
#define PREAMBLE_34BIT (0x009)
#define PREAMBLE_35BIT (0x005)
#define PREAMBLE_36BIT (0x003)
#define PREAMBLE_ACTP (0x095)
/**@brief Set a bit in the uint64 word.
*
@@ -548,6 +549,37 @@ static wiegand_card_t *unpack_c15001(uint64_t hi, uint64_t lo) {
return d;
}
static uint64_t pack_actprox(wiegand_card_t *card) {
if (card->oem == 0) {
card->oem = 900;
}
uint64_t bits = PREAMBLE_ACTP;
bits <<= 1;
bits = (bits << 10) | (card->oem & 0x3ff);
bits = (bits << 8) | (card->facility_code & 0xff);
bits = (bits << 16) | (card->card_number & 0xffff);
bits <<= 1;
if (evenparity32((bits >> 18) & 0x1ffff)) {
SET_BIT64(bits, 35);
}
if (oddparity32((bits >> 1) & 0x1ffff)) {
SET_BIT64(bits, 0);
}
return bits;
}
static wiegand_card_t *unpack_actprox(uint64_t hi, uint64_t lo) {
if (!(IS_SET(lo, 35) == evenparity32((lo >> 18) & 0x1ffff) &&
IS_SET(lo, 0) == oddparity32((lo >> 1) & 0x1ffff))) {
return NULL;
}
wiegand_card_t *d = wiegand_card_alloc();
d->oem = (lo >> 25) & 0x3ff;
d->facility_code = (lo >> 17) & 0xff;
d->card_number = (lo >> 1) & 0xffff;
return d;
}
static uint64_t pack_s12906(wiegand_card_t *card) {
uint64_t bits = PREAMBLE_36BIT;
bits <<= 1;
@@ -819,6 +851,7 @@ static const card_format_table_t formats[] = {
{P10004, pack_p10004, unpack_p10004, 37, {0, 0x1FFF, 0x3FFFF, 0, 0}}, // HID P10004 37-bit PCSC
{HGEN37, pack_hgeneric37, unpack_hgeneric37, 37, {1, 0, 0xFFFFFFFF, 0, 0}}, // HID Generic 37-bit
{MDI37, pack_mdi37, unpack_mdi37, 37, {1, 0xF, 0x1FFFFFFF, 0, 0}}, // PointGuard MDI 37-bit
{ACTPHID, pack_actprox, unpack_actprox, 36, {1, 0xFF, 0xFFFF, 0x3FF, 0}}, // HID ACTProx 36-bit
};
uint64_t pack(wiegand_card_t *card) {
@@ -72,6 +72,7 @@ typedef enum {
C1K48S,
AVIG56,
IR56,
ACTPHID,
} card_format_t;
// Structure for defined Wiegand card formats available for packing/unpacking
@@ -38,6 +38,7 @@ typedef enum {
TAG_TYPE_EM410X_16,
TAG_TYPE_EM410X_32,
TAG_TYPE_EM410X_64,
TAG_TYPE_EM410X_ELECTRA,
// FDX-B
// securakey
// gallagher
@@ -106,7 +107,7 @@ typedef enum {
}
#define TAG_SPECIFIC_TYPE_LF_VALUES \
TAG_TYPE_EM410X, TAG_TYPE_HID_PROX, TAG_TYPE_VIKING
TAG_TYPE_EM410X, TAG_TYPE_EM410X_ELECTRA, TAG_TYPE_HID_PROX, TAG_TYPE_VIKING
#define TAG_SPECIFIC_TYPE_HF_VALUES \
TAG_TYPE_MIFARE_Mini, TAG_TYPE_MIFARE_1024, TAG_TYPE_MIFARE_2048, \

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