From 8c670f85543a2ac2fd54bfb38819e222b4e43229 Mon Sep 17 00:00:00 2001 From: Derek Jamison Date: Fri, 29 Aug 2025 11:00:02 -0400 Subject: [PATCH] Add LF Viking support --- firmware/application/Makefile | 2 + firmware/application/src/app_cmd.c | 48 ++++ firmware/application/src/app_main.c | 5 + firmware/application/src/data_cmd.h | 5 + .../src/rfid/nfctag/lf/lf_tag_em.c | 33 +++ .../src/rfid/nfctag/lf/lf_tag_em.h | 3 + .../src/rfid/nfctag/lf/protocols/t55xx.h | 6 + .../src/rfid/nfctag/lf/protocols/viking.c | 219 ++++++++++++++++++ .../src/rfid/nfctag/lf/protocols/viking.h | 7 + .../src/rfid/nfctag/tag_base_type.h | 3 +- .../src/rfid/nfctag/tag_emulation.c | 1 + .../src/rfid/reader/lf/lf_reader_data.h | 1 + .../src/rfid/reader/lf/lf_reader_main.c | 23 ++ .../src/rfid/reader/lf/lf_reader_main.h | 2 + .../src/rfid/reader/lf/lf_viking_data.c | 72 ++++++ software/script/chameleon_cli_unit.py | 70 +++++- software/script/chameleon_cmd.py | 47 ++++ software/script/chameleon_enum.py | 8 +- 18 files changed, 552 insertions(+), 3 deletions(-) create mode 100644 firmware/application/src/rfid/nfctag/lf/protocols/viking.c create mode 100644 firmware/application/src/rfid/nfctag/lf/protocols/viking.h create mode 100644 firmware/application/src/rfid/reader/lf/lf_viking_data.c diff --git a/firmware/application/Makefile b/firmware/application/Makefile index a9912fb..c115fd8 100644 --- a/firmware/application/Makefile +++ b/firmware/application/Makefile @@ -36,6 +36,7 @@ SRC_FILES += \ $(PROJ_DIR)/rfid/nfctag/lf/utils/manchester.c \ $(PROJ_DIR)/rfid/nfctag/lf/protocols/em410x.c \ $(PROJ_DIR)/rfid/nfctag/lf/protocols/hidprox.c \ + $(PROJ_DIR)/rfid/nfctag/lf/protocols/viking.c \ $(PROJ_DIR)/rfid/nfctag/lf/protocols/wiegand.c \ $(PROJ_DIR)/utils/dataframe.c \ $(PROJ_DIR)/utils/delayed_reset.c \ @@ -343,6 +344,7 @@ ifeq (${CURRENT_DEVICE_TYPE}, ${CHAMELEON_ULTRA}) $(PROJ_DIR)/rfid/reader/lf/lf_reader_main.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_viking_data.c \ INC_FOLDERS +=\ ${PROJ_DIR}/rfid/reader/ \ diff --git a/firmware/application/src/app_cmd.c b/firmware/application/src/app_cmd.c index 4d5e61d..092d57e 100644 --- a/firmware/application/src/app_cmd.c +++ b/firmware/application/src/app_cmd.c @@ -650,6 +650,30 @@ static data_frame_tx_t *cmd_processor_hidprox_scan(uint16_t cmd, uint16_t status } return data_frame_make(cmd, STATUS_LF_TAG_OK, sizeof(card_data), card_data); } + +static data_frame_tx_t *cmd_processor_viking_scan(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) { + uint8_t card_buffer[4] = {0x00}; + status = scan_viking(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); +} + +static data_frame_tx_t *cmd_processor_viking_write_to_t55xx(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) { + typedef struct { + uint8_t id[4]; + 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_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); +} #endif @@ -817,6 +841,26 @@ static data_frame_tx_t *cmd_processor_hidprox_get_emu_id(uint16_t cmd, uint16_t return data_frame_make(cmd, STATUS_SUCCESS, LF_HIDPROX_TAG_ID_SIZE, buffer->buffer); } +static data_frame_tx_t *cmd_processor_viking_set_emu_id(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) { + if (length != LF_VIKING_TAG_ID_SIZE) { + return data_frame_make(cmd, STATUS_PAR_ERR, 0, NULL); + } + tag_data_buffer_t *buffer = get_buffer_by_tag_type(TAG_TYPE_VIKING); + memcpy(buffer->buffer, data, LF_VIKING_TAG_ID_SIZE); + tag_emulation_load_by_buffer(TAG_TYPE_VIKING, false); + return data_frame_make(cmd, STATUS_SUCCESS, 0, NULL); +} + +static data_frame_tx_t *cmd_processor_viking_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_VIKING) { + 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_VIKING); + return data_frame_make(cmd, STATUS_SUCCESS, LF_VIKING_TAG_ID_SIZE, buffer->buffer); +} + static nfc_tag_14a_coll_res_reference_t *get_coll_res_data(bool write) { nfc_tag_14a_coll_res_reference_t *info; tag_slot_specific_type_t tag_types; @@ -1531,6 +1575,8 @@ static cmd_data_map_t m_data_cmd_map[] = { { DATA_CMD_EM410X_WRITE_TO_T55XX, before_reader_run, cmd_processor_em410x_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 }, #endif @@ -1577,6 +1623,8 @@ static cmd_data_map_t m_data_cmd_map[] = { { DATA_CMD_EM410X_GET_EMU_ID, NULL, cmd_processor_em410x_get_emu_id, NULL }, { DATA_CMD_HIDPROX_SET_EMU_ID, NULL, cmd_processor_hidprox_set_emu_id, NULL }, { DATA_CMD_HIDPROX_GET_EMU_ID, NULL, cmd_processor_hidprox_get_emu_id, NULL }, + { DATA_CMD_VIKING_SET_EMU_ID, NULL, cmd_processor_viking_set_emu_id, NULL }, + { DATA_CMD_VIKING_GET_EMU_ID, NULL, cmd_processor_viking_get_emu_id, NULL }, }; data_frame_tx_t *cmd_processor_get_device_capabilities(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) { diff --git a/firmware/application/src/app_main.c b/firmware/application/src/app_main.c index 5151236..87a3efc 100644 --- a/firmware/application/src/app_main.c +++ b/firmware/application/src/app_main.c @@ -608,6 +608,11 @@ static void btn_fn_copy_lf(uint8_t slot, tag_specific_type_t type) { 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; + data = id_buffer; + break; default: NRF_LOG_ERROR("Unsupported LF tag type") offline_status_error(); diff --git a/firmware/application/src/data_cmd.h b/firmware/application/src/data_cmd.h index 242158b..e0bf70c 100644 --- a/firmware/application/src/data_cmd.h +++ b/firmware/application/src/data_cmd.h @@ -86,6 +86,9 @@ #define DATA_CMD_EM410X_WRITE_TO_T55XX (3001) #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) + // // ****************************************************************** @@ -149,5 +152,7 @@ #define DATA_CMD_EM410X_GET_EMU_ID (5001) #define DATA_CMD_HIDPROX_SET_EMU_ID (5002) #define DATA_CMD_HIDPROX_GET_EMU_ID (5003) +#define DATA_CMD_VIKING_SET_EMU_ID (5004) +#define DATA_CMD_VIKING_GET_EMU_ID (5005) #endif diff --git a/firmware/application/src/rfid/nfctag/lf/lf_tag_em.c b/firmware/application/src/rfid/nfctag/lf/lf_tag_em.c index 30d6090..b2ce030 100644 --- a/firmware/application/src/rfid/nfctag/lf/lf_tag_em.c +++ b/firmware/application/src/rfid/nfctag/lf/lf_tag_em.c @@ -9,6 +9,7 @@ #include "nrfx_pwm.h" #include "protocols/em410x.h" #include "protocols/hidprox.h" +#include "protocols/viking.h" #include "syssleep.h" #include "tag_emulation.h" #include "tag_persistence.h" @@ -198,6 +199,16 @@ int lf_tag_data_loadcb(tag_specific_type_t type, tag_data_buffer_t *buffer) { NRF_LOG_INFO("load lf hidprox data finish."); return LF_HIDPROX_TAG_ID_SIZE; } + + if (type == TAG_TYPE_VIKING && buffer->length >= LF_VIKING_TAG_ID_SIZE) { + m_tag_type = type; + void *codec = viking.alloc(); + m_pwm_seq = viking.modulator(codec, buffer->buffer); + viking.free(codec); + NRF_LOG_INFO("load lf viking data finish."); + return LF_VIKING_TAG_ID_SIZE; + } + NRF_LOG_ERROR("no valid data exists in buffer for tag type: %d.", type); return 0; } @@ -224,6 +235,17 @@ 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; } +/** @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_viking_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_VIKING ? LF_VIKING_TAG_ID_SIZE : 0; +} + bool lf_tag_data_factory(uint8_t slot, tag_specific_type_t tag_type, uint8_t *tag_id, uint16_t length) { // write data to flash tag_sense_type_t sense_type = get_sense_type_from_tag_type(tag_type); @@ -260,3 +282,14 @@ bool lf_tag_hidprox_data_factory(uint8_t slot, tag_specific_type_t tag_type) { uint8_t tag_id[13] = {0x01, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x51, 0x45, 0x00, 0x00, 0x00}; 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_viking_data_factory(uint8_t slot, tag_specific_type_t tag_type) { + // default id + uint8_t tag_id[4] = {0xDE, 0xAD, 0xBE, 0xEF}; + return lf_tag_data_factory(slot, tag_type, tag_id, sizeof(tag_id)); +} diff --git a/firmware/application/src/rfid/nfctag/lf/lf_tag_em.h b/firmware/application/src/rfid/nfctag/lf/lf_tag_em.h index 335ee60..3dabb30 100644 --- a/firmware/application/src/rfid/nfctag/lf/lf_tag_em.h +++ b/firmware/application/src/rfid/nfctag/lf/lf_tag_em.h @@ -7,6 +7,7 @@ #define LF_EM410X_TAG_ID_SIZE 5 #define LF_HIDPROX_TAG_ID_SIZE 13 +#define LF_VIKING_TAG_ID_SIZE 4 void lf_tag_125khz_sense_switch(bool enable); int lf_tag_data_loadcb(tag_specific_type_t type, tag_data_buffer_t *buffer); @@ -14,4 +15,6 @@ int lf_tag_em410x_data_savecb(tag_specific_type_t type, tag_data_buffer_t *buffe bool lf_tag_em410x_data_factory(uint8_t slot, tag_specific_type_t tag_type); int lf_tag_hidprox_data_savecb(tag_specific_type_t type, tag_data_buffer_t *buffer); bool lf_tag_hidprox_data_factory(uint8_t slot, tag_specific_type_t tag_type); +int lf_tag_viking_data_savecb(tag_specific_type_t type, tag_data_buffer_t *buffer); +bool lf_tag_viking_data_factory(uint8_t slot, tag_specific_type_t tag_type); bool is_lf_field_exists(void); diff --git a/firmware/application/src/rfid/nfctag/lf/protocols/t55xx.h b/firmware/application/src/rfid/nfctag/lf/protocols/t55xx.h index 788d93f..31f71ae 100644 --- a/firmware/application/src/rfid/nfctag/lf/protocols/t55xx.h +++ b/firmware/application/src/rfid/nfctag/lf/protocols/t55xx.h @@ -57,6 +57,12 @@ extern "C" { T5577_PWD | \ (3 << T5577_MAXBLOCK_SHIFT)) +#define T5577_VIKING_CONFIG ( \ + T5577_BITRATE_RF_32 | \ + T5577_MODULATION_MANCHESTER | \ + T5577_PWD | \ + (2 << T5577_MAXBLOCK_SHIFT)) + void t55xx_write_data(uint32_t passwd, uint32_t *blks, uint8_t blk_count); void t55xx_reset_passwd(uint32_t old_passwd, uint32_t new_passwd); diff --git a/firmware/application/src/rfid/nfctag/lf/protocols/viking.c b/firmware/application/src/rfid/nfctag/lf/protocols/viking.c new file mode 100644 index 0000000..74d8f87 --- /dev/null +++ b/firmware/application/src/rfid/nfctag/lf/protocols/viking.c @@ -0,0 +1,219 @@ +#include "viking.h" + +#include +#include + +#include "em410x.h" +#include "nordic_common.h" +#include "nrf_pwm.h" +#include "parity.h" +#include "protocols.h" +#include "t55xx.h" +#include "tag_base_type.h" +#include "utils/manchester.h" + +#define EM_BITS_PER_ROW_COUNT (EM_COLUMN_COUNT + 1) + +#define VIKING_RAW_SIZE (64) // Preamble (24) + data (32) + checksum (8) +#define VIKING_DATA_SIZE (4) // Card data is 4 bytes +#define VIKING_HEADER (0xf20000) // Preamble... 11110010 00000000 00000000 + +#define VIKING_T55XX_BLOCK_COUNT (3) // config + 2 data blocks + +// Duration between falling edges is... +#define VIKING_READ_TIME1_BASE (0x20) // on 16, off 16 cycles +#define VIKING_READ_TIME2_BASE (0x30) // on 16, off 32 cycles (or on 32 cycles, off 16 cycles) +#define VIKING_READ_TIME3_BASE (0x40) // on 32, off 32 cycles +#define VIKING_READ_JITTER_TIME_BASE (0x07) // Jitter is just under half of 16 cycles. + +#define NRF_LOG_MODULE_NAME viking_protocol +#include "nrf_log.h" +#include "nrf_log_ctrl.h" +#include "nrf_log_default_backends.h" +NRF_LOG_MODULE_REGISTER(); + +static nrf_pwm_values_wave_form_t m_viking_pwm_seq_vals[VIKING_RAW_SIZE] = {}; + +nrf_pwm_sequence_t const m_viking_pwm_seq = { + .values.p_wave_form = m_viking_pwm_seq_vals, + .length = NRF_PWM_VALUES_LENGTH(m_viking_pwm_seq_vals), + .repeats = 0, + .end_delay = 0, +}; + +typedef struct { + uint8_t data[VIKING_DATA_SIZE]; + uint64_t raw; + uint8_t raw_length; + manchester *modem; +} viking_codec; + +static uint64_t viking_raw_data(uint8_t *uid) { + uint64_t raw = VIKING_HEADER; + uint8_t crc = 0x5A; + for (int8_t i = 0; i < VIKING_DATA_SIZE; i++) { + raw <<= 8; + raw |= uid[i]; + crc ^= uid[i]; + } + raw <<= 8; + raw |= crc; + return raw; +} + +static bool viking_get_time(uint8_t interval, uint8_t base) { + return interval >= (base - VIKING_READ_JITTER_TIME_BASE) && + interval <= (base + VIKING_READ_JITTER_TIME_BASE); +} + +static uint8_t viking_period(uint8_t interval) { + if (viking_get_time(interval, VIKING_READ_TIME1_BASE)) { // short/short + return 0; + } + if (viking_get_time(interval, VIKING_READ_TIME2_BASE)) { // short/long or long/short + return 1; + } + if (viking_get_time(interval, VIKING_READ_TIME3_BASE)) { // long/long + return 2; + } + return 3; // Not manchester (or bad signal). +} + +static viking_codec *viking_alloc(void) { + viking_codec *codec = malloc(sizeof(viking_codec)); + codec->modem = malloc(sizeof(manchester)); + codec->modem->rp = viking_period; + return codec; +}; + +static void viking_free(viking_codec *d) { + if (d->modem) { + free(d->modem); + d->modem = NULL; + } + free(d); +}; + +static uint8_t *viking_get_data(viking_codec *d) { + return d->data; +}; + +static void viking_decoder_start(viking_codec *d, uint8_t format) { + memset(d->data, 0, VIKING_DATA_SIZE); + d->raw = 0; + d->raw_length = 0; + manchester_reset(d->modem); +}; + +static bool viking_decode_feed(viking_codec *d, bool bit) { + d->raw <<= 1; + d->raw_length++; + if (bit) { + d->raw |= 0x01; + } + if (d->raw_length < (VIKING_RAW_SIZE-2)) { + return false; + } + + // Check header + uint8_t v = (d->raw >> (VIKING_RAW_SIZE - 24)) & 0xff; // Check LSB of header + if (v != ((VIKING_HEADER & 0xFF))) { + return false; + } + v = (d->raw >> (VIKING_RAW_SIZE - 16)) & 0xff; // Check mid of header + if (v != ((VIKING_HEADER >> 8) & 0xFF)) { + return false; + } + v = (d->raw >> (VIKING_RAW_SIZE - 8)) & 0xff; // Check MSB of header + if ((v & 0xFF) != ((VIKING_HEADER >> 16) & 0xFF)) { + return false; + } + + // Validate CRC + uint8_t crc = 0x5A; + for (int i = 0; i < VIKING_DATA_SIZE; i++) { + uint8_t data = (d->raw >> ((i+1)*8)) & 0xff; + crc ^= data; + d->data[VIKING_DATA_SIZE - i - 1] |= data; + } + + return crc == (d->raw & 0xff); +} + +static bool viking_decoder_feed(viking_codec *d, uint16_t interval) { + bool bits[2] = {0}; + int8_t bitlen = 0; + + // Hack: due to hardware sometimes not detecting a time2 pulse. Rather than + // reset when interval is really long, assume there was a time2 pulse. + if (interval > VIKING_READ_TIME3_BASE + VIKING_READ_JITTER_TIME_BASE) { + interval -= VIKING_READ_TIME2_BASE; + manchester_feed(d->modem, (uint8_t)VIKING_READ_TIME2_BASE, bits, &bitlen); + if (bitlen == -1) { + d->raw = 0; + d->raw_length = 0; + return false; + } + for (int i = 0; i < bitlen; i++) { + if (viking_decode_feed(d, bits[i])) { + return true; + } + } + } + + manchester_feed(d->modem, (uint8_t)interval, bits, &bitlen); + if (bitlen == -1) { + if (d->raw == 62) { + if (viking_decode_feed(d, 1)) { + return true; + } + } + d->raw = 0; + d->raw_length = 0; + return false; + } + for (int i = 0; i < bitlen; i++) { + if (viking_decode_feed(d, bits[i])) { + return true; + } + } + return false; +}; + +static const nrf_pwm_sequence_t *viking_modulator(viking_codec *d, uint8_t *buf) { + uint64_t lo = viking_raw_data(buf); + for (int i = 0; i < VIKING_RAW_SIZE; i++) { + uint16_t msb = 0x00; + if (IS_SET(lo, VIKING_RAW_SIZE - i - 1)) { + msb = (1 << 15); + } + m_viking_pwm_seq_vals[i].channel_0 = msb | 16; + m_viking_pwm_seq_vals[i].counter_top = 32; + + } + return &m_viking_pwm_seq; +}; + +// Viking card +const protocol viking = { + .tag_type = TAG_TYPE_VIKING, + .data_size = VIKING_DATA_SIZE, + .alloc = (codec_alloc)viking_alloc, + .free = (codec_free)viking_free, + .get_data = (codec_get_data)viking_get_data, + .modulator = (modulator)viking_modulator, + .decoder = + { + .start = (decoder_start)viking_decoder_start, + .feed = (decoder_feed)viking_decoder_feed, + }, +}; + +// Encode viking card number to T55xx blocks. +uint8_t viking_t55xx_writer(uint8_t *uid, uint32_t *blks) { + uint64_t raw = viking_raw_data(uid); + blks[0] = T5577_VIKING_CONFIG; + blks[1] = raw >> 32; + blks[2] = raw & 0xffffffff; + return VIKING_T55XX_BLOCK_COUNT; +} \ No newline at end of file diff --git a/firmware/application/src/rfid/nfctag/lf/protocols/viking.h b/firmware/application/src/rfid/nfctag/lf/protocols/viking.h new file mode 100644 index 0000000..7e27d1f --- /dev/null +++ b/firmware/application/src/rfid/nfctag/lf/protocols/viking.h @@ -0,0 +1,7 @@ +#pragma once + +#include "protocols.h" + +extern const protocol viking; + +uint8_t viking_t55xx_writer(uint8_t* uid, uint32_t* blks); \ No newline at end of file diff --git a/firmware/application/src/rfid/nfctag/tag_base_type.h b/firmware/application/src/rfid/nfctag/tag_base_type.h index dd5cfe5..19a0a8d 100644 --- a/firmware/application/src/rfid/nfctag/tag_base_type.h +++ b/firmware/application/src/rfid/nfctag/tag_base_type.h @@ -45,6 +45,7 @@ typedef enum { // Presco // Visa2000 // Viking + TAG_TYPE_VIKING = 170, // Noralsy // Jablotron @@ -105,7 +106,7 @@ typedef enum { } #define TAG_SPECIFIC_TYPE_LF_VALUES \ - TAG_TYPE_EM410X, TAG_TYPE_HID_PROX + TAG_TYPE_EM410X, 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, \ diff --git a/firmware/application/src/rfid/nfctag/tag_emulation.c b/firmware/application/src/rfid/nfctag/tag_emulation.c index ab17fa8..79d7493 100644 --- a/firmware/application/src/rfid/nfctag/tag_emulation.c +++ b/firmware/application/src/rfid/nfctag/tag_emulation.c @@ -91,6 +91,7 @@ static tag_base_handler_map_t tag_base_map[] = { // LF tag emulation {TAG_SENSE_LF, TAG_TYPE_EM410X, lf_tag_data_loadcb, lf_tag_em410x_data_savecb, lf_tag_em410x_data_factory, &m_tag_data_lf}, {TAG_SENSE_LF, TAG_TYPE_HID_PROX, lf_tag_data_loadcb, lf_tag_hidprox_data_savecb, lf_tag_hidprox_data_factory, &m_tag_data_lf}, + {TAG_SENSE_LF, TAG_TYPE_VIKING, lf_tag_data_loadcb, lf_tag_viking_data_savecb, lf_tag_viking_data_factory, &m_tag_data_lf}, // MF1 tag emulation {TAG_SENSE_HF, TAG_TYPE_MIFARE_Mini, nfc_tag_mf1_data_loadcb, nfc_tag_mf1_data_savecb, nfc_tag_mf1_data_factory, &m_tag_data_hf}, {TAG_SENSE_HF, TAG_TYPE_MIFARE_1024, nfc_tag_mf1_data_loadcb, nfc_tag_mf1_data_savecb, nfc_tag_mf1_data_factory, &m_tag_data_hf}, diff --git a/firmware/application/src/rfid/reader/lf/lf_reader_data.h b/firmware/application/src/rfid/reader/lf/lf_reader_data.h index ef76f50..55c6834 100644 --- a/firmware/application/src/rfid/reader/lf/lf_reader_data.h +++ b/firmware/application/src/rfid/reader/lf/lf_reader_data.h @@ -20,6 +20,7 @@ void clear_lf_counter_value(void); bool em410x_read(uint8_t *data, uint32_t timeout_ms); bool hidprox_read(uint8_t *data, uint8_t format_hint, uint32_t timeout_ms); +bool viking_read(uint8_t *data, uint32_t timeout_ms); #ifdef __cplusplus } diff --git a/firmware/application/src/rfid/reader/lf/lf_reader_main.c b/firmware/application/src/rfid/reader/lf/lf_reader_main.c index fc431b7..210c10e 100644 --- a/firmware/application/src/rfid/reader/lf/lf_reader_main.c +++ b/firmware/application/src/rfid/reader/lf/lf_reader_main.c @@ -8,6 +8,7 @@ #include "protocols/em410x.h" #include "protocols/hidprox.h" #include "protocols/t55xx.h" +#include "protocols/viking.h" #define NRF_LOG_MODULE_NAME lf_main #include "nrf_log.h" @@ -38,6 +39,16 @@ uint8_t scan_hidprox(uint8_t *data, uint8_t format_hint) { return STATUS_LF_TAG_NO_FOUND; } +/** + * Search Viking tag + */ +uint8_t scan_viking(uint8_t *uid) { + if (viking_read(uid, g_timeout_readem_ms)) { + return STATUS_LF_TAG_OK; + } + return STATUS_LF_TAG_NO_FOUND; +} + /** * Try reset t55XX tag passwords by enumerating old passwords. */ @@ -98,6 +109,18 @@ uint8_t write_hidprox_to_t55xx(uint8_t format, uint32_t fc, uint64_t cn, uint32_ return write_t55xx(blks, blk_count, new_passwd, old_passwds, old_passwd_count); } +/** + * Write viking card data to t55xx + */ +uint8_t write_viking_to_t55xx(uint8_t *uid, uint8_t *new_passwd, uint8_t *old_passwds, uint8_t old_passwd_count) { + uint32_t blks[7] = {0x00}; + uint8_t blk_count = viking_t55xx_writer(uid, blks); + if (blk_count == 0) { + return STATUS_PAR_ERR; + } + return write_t55xx(blks, blk_count, new_passwd, old_passwds, old_passwd_count); +} + /** * Set the LF card scanning timeout value (in milliseconds). */ diff --git a/firmware/application/src/rfid/reader/lf/lf_reader_main.h b/firmware/application/src/rfid/reader/lf/lf_reader_main.h index 5a2ac39..82ace89 100644 --- a/firmware/application/src/rfid/reader/lf/lf_reader_main.h +++ b/firmware/application/src/rfid/reader/lf/lf_reader_main.h @@ -10,5 +10,7 @@ void set_scan_tag_timeout(uint32_t ms); uint8_t scan_em410x(uint8_t *uid); uint8_t scan_hidprox(uint8_t *uid, uint8_t format_hint); +uint8_t scan_viking(uint8_t *uid); uint8_t write_em410x_to_t55xx(uint8_t *uid, uint8_t *newkey, uint8_t *old_keys, uint8_t old_key_count); uint8_t write_hidprox_to_t55xx(uint8_t format, uint32_t fc, uint64_t cn, uint32_t il, uint32_t oem, uint8_t *new_passwd, uint8_t *old_passwds, uint8_t old_passwd_count); +uint8_t write_viking_to_t55xx(uint8_t *uid, uint8_t *newkey, uint8_t *old_keys, uint8_t old_key_count); diff --git a/firmware/application/src/rfid/reader/lf/lf_viking_data.c b/firmware/application/src/rfid/reader/lf/lf_viking_data.c new file mode 100644 index 0000000..817cf45 --- /dev/null +++ b/firmware/application/src/rfid/reader/lf/lf_viking_data.c @@ -0,0 +1,72 @@ +#include "lf_reader_data.h" + +#include "bsp_delay.h" +#include "bsp_time.h" +#include "circular_buffer.h" +#include "lf_125khz_radio.h" +#include "lf_reader_data.h" +#include "protocols/viking.h" +#include "protocols/protocols.h" + +#define NRF_LOG_MODULE_NAME viking_reader +#include "nrf_log.h" +#include "nrf_log_ctrl.h" +#include "nrf_log_default_backends.h" +NRF_LOG_MODULE_REGISTER(); + +#define VIKING_BUFFER_SIZE (128) + +static circular_buffer cb; + +// GPIO interrupt recovery function is used to detect the descending edge +static void viking_gpio_int0_cb(void) { + uint32_t cntr = get_lf_counter_value(); + uint16_t val = 0; + if (cntr > 0xff) { + val = 0xff; + } else { + val = cntr & 0xff; + } + cb_push_back(&cb, &val); + clear_lf_counter_value(); +} + +static void init_viking_hw(void) { + register_rio_callback(viking_gpio_int0_cb); + lf_125khz_radio_gpiote_enable(); +} + +static void uninit_viking_hw(void) { + lf_125khz_radio_gpiote_disable(); + unregister_rio_callback(); +} + +bool viking_read(uint8_t *data, uint32_t timeout_ms) { + void *codec = viking.alloc(); + viking.decoder.start(codec, 0); + + cb_init(&cb, VIKING_BUFFER_SIZE, sizeof(uint16_t)); + init_viking_hw(); + start_lf_125khz_radio(); + + bool ok = false; + autotimer *p_at = bsp_obtain_timer(0); + while (!ok && NO_TIMEOUT_1MS(p_at, timeout_ms)) { + uint16_t val = 0; + while (!ok && NO_TIMEOUT_1MS(p_at, timeout_ms) && cb_pop_front(&cb, &val)) { + if (viking.decoder.feed(codec, val)) { + memcpy(data, viking.get_data(codec), viking.data_size); + ok = true; + break; + } + } + } + + bsp_return_timer(p_at); + stop_lf_125khz_radio(); + uninit_viking_hw(); + cb_free(&cb); + + viking.free(codec); + return ok; +} diff --git a/software/script/chameleon_cli_unit.py b/software/script/chameleon_cli_unit.py index 1090050..a507d7f 100644 --- a/software/script/chameleon_cli_unit.py +++ b/software/script/chameleon_cli_unit.py @@ -494,6 +494,25 @@ class LFHIDIdReadArgsUnit(DeviceRequiredUnit): def on_exec(self, args: argparse.Namespace): raise NotImplementedError() +class LFVikingIdArgsUnit(DeviceRequiredUnit): + @staticmethod + def add_card_arg(parser: ArgumentParserNoExit, required=False): + parser.add_argument("--id", type=str, required=required, help="Viking tag id", metavar="") + return parser + + def before_exec(self, args: argparse.Namespace): + if not super().before_exec(args): + return False + if args.id is None or not re.match(r"^[a-fA-F0-9]{8}$", args.id): + raise ArgsParserError("ID must include 8 HEX symbols") + return True + + def args_parser(self) -> ArgumentParserNoExit: + raise NotImplementedError("Please implement this") + + def on_exec(self, args: argparse.Namespace): + raise NotImplementedError("Please implement this") + class TagTypeArgsUnit(DeviceRequiredUnit): @staticmethod def add_type_args(parser: ArgumentParserNoExit): @@ -525,7 +544,7 @@ lf_em = lf.subgroup('em', 'EM commands') lf_em_410x = lf_em.subgroup('410x', 'EM410x commands') lf_hid = lf.subgroup('hid', 'HID commands') lf_hid_prox = lf_hid.subgroup('prox', 'HID Prox commands') - +lf_viking = lf.subgroup('viking', 'Viking commands') @root.command('clear') class RootClear(BaseCLIUnit): @@ -3206,6 +3225,30 @@ class LFHIDProxEconfig(SlotIndexArgsAndGoUnit, LFHIDIdArgsUnit): print(f" OEM: {CG}{oem}{C0}") print(f" CN: {CG}{cn}{C0}") +@lf_viking.command('read') +class LFVikingRead(ReaderRequiredUnit): + def args_parser(self) -> ArgumentParserNoExit: + parser = ArgumentParserNoExit() + parser.description = 'Scan Viking tag and print id' + return parser + + def on_exec(self, args: argparse.Namespace): + id = self.cmd.viking_scan() + print(f"Viking: {CG}{id.hex()}{C0}") + + +@lf_viking.command('write') +class LFVikingWriteT55xx(LFVikingIdArgsUnit, ReaderRequiredUnit): + def args_parser(self) -> ArgumentParserNoExit: + parser = ArgumentParserNoExit() + parser.description = 'Write Viking id to t55xx' + return self.add_card_arg(parser, required=True) + + def on_exec(self, args: argparse.Namespace): + id_hex = args.id + id_bytes = bytes.fromhex(id_hex) + self.cmd.viking_write_to_t55xx(id_bytes) + print(f" - Viking ID(8H): {id_hex} write done.") @hw_slot.command('list') class HWSlotList(DeviceRequiredUnit): @@ -3327,6 +3370,9 @@ class HWSlotList(DeviceRequiredUnit): if oem > 0: print(f' {"OEM:":40}{CY}{oem}{C0}') print(f' {"CN:":40}{CY}{cn}{C0}') + if lf_tag_type == TagSpecificType.Viking: + id = self.cmd.viking_get_emu_id() + print(f' {"ID:":40}{CY}{id.hex().upper()}{C0}') if current != selected: self.cmd.set_active_slot(selected) @@ -3463,6 +3509,28 @@ class LFEM410xEconfig(SlotIndexArgsAndGoUnit, LFEMIdArgsUnit): print(' - Get em410x tag id success.') print(f'ID: {response.hex()}') +@lf_viking.command('econfig') +class LFVikingEconfig(SlotIndexArgsAndGoUnit, LFVikingIdArgsUnit): + def args_parser(self) -> ArgumentParserNoExit: + parser = ArgumentParserNoExit() + parser.description = 'Set emulated Viking card id' + self.add_slot_args(parser) + self.add_card_arg(parser) + return parser + + def on_exec(self, args: argparse.Namespace): + if args.id is not None: + slotinfo = self.cmd.get_slot_info() + selected = SlotNumber.from_fw(self.cmd.get_active_slot()) + lf_tag_type = TagSpecificType(slotinfo[selected - 1]['lf']) + if lf_tag_type != TagSpecificType.Viking: + print(f"{CR}WARNING{C0}: Slot type not set to Viking.") + self.cmd.viking_set_emu_id(bytes.fromhex(args.id)) + print(' - Set Viking tag id success.') + else: + response = self.cmd.viking_get_emu_id() + print(' - Get Viking tag id success.') + print(f'ID: {response.hex().upper()}') @hw_slot.command('nick') class HWSlotNick(SlotIndexArgsUnit, SenseTypeArgsUnit): diff --git a/software/script/chameleon_cmd.py b/software/script/chameleon_cmd.py index 52d912b..c8bbe01 100644 --- a/software/script/chameleon_cmd.py +++ b/software/script/chameleon_cmd.py @@ -475,6 +475,31 @@ class ChameleonCMD: data = struct.pack(f'!13s4s{4*len(old_keys)}s', id_bytes, new_key, b''.join(old_keys)) return self.device.send_cmd_sync(Command.HIDPROX_WRITE_TO_T55XX, data) + @expect_response(Status.LF_TAG_OK) + def viking_scan(self): + """ + Read the card number of Viking. + + :return: + """ + resp = self.device.send_cmd_sync(Command.VIKING_SCAN) + if resp.status == Status.LF_TAG_OK: + resp.parsed = resp.data # uid + return resp + + @expect_response(Status.LF_TAG_OK) + def viking_write_to_t55xx(self, id_bytes: bytes): + """ + Write Viking card number into T55XX. + + :param id_bytes: ID card number + :return: + """ + if len(id_bytes) != 4: + raise ValueError("The id bytes length must equal 4") + data = struct.pack(f'!4s4s{4*len(old_keys)}s', id_bytes, new_key, b''.join(old_keys)) + return self.device.send_cmd_sync(Command.VIKING_WRITE_TO_T55XX, data) + @expect_response(Status.SUCCESS) def get_slot_info(self): """ @@ -610,6 +635,28 @@ class ChameleonCMD: resp.parsed = struct.unpack('>BIBIBH', resp.data[:13]) return resp + @expect_response(Status.SUCCESS) + def viking_set_emu_id(self, id: bytes): + """ + Set the card number emulated by Viking. + + :param id_bytes: byte of the card number + :return: + """ + if len(id) != 4: + raise ValueError("The id bytes length must equal 4") + data = struct.pack('4s', id) + return self.device.send_cmd_sync(Command.VIKING_SET_EMU_ID, data) + + @expect_response(Status.SUCCESS) + def viking_get_emu_id(self): + """ + Get the emulated Viking card id + """ + resp = self.device.send_cmd_sync(Command.VIKING_GET_EMU_ID) + resp.parsed = resp.data + return resp + @expect_response(Status.SUCCESS) def mf1_set_detection_enable(self, enabled: bool): """ diff --git a/software/script/chameleon_enum.py b/software/script/chameleon_enum.py index 5be7867..f6b72ed 100644 --- a/software/script/chameleon_enum.py +++ b/software/script/chameleon_enum.py @@ -78,6 +78,8 @@ class Command(enum.IntEnum): EM410X_WRITE_TO_T55XX = 3001 HIDPROX_SCAN = 3002 HIDPROX_WRITE_TO_T55XX = 3003 + VIKING_SCAN = 3004 + VIKING_WRITE_TO_T55XX = 3005 MF1_WRITE_EMU_BLOCK_DATA = 4000 HF14A_SET_ANTI_COLL_DATA = 4001 @@ -126,6 +128,8 @@ class Command(enum.IntEnum): EM410X_GET_EMU_ID = 5001 HIDPROX_SET_EMU_ID = 5002 HIDPROX_GET_EMU_ID = 5003 + VIKING_SET_EMU_ID = 5004 + VIKING_GET_EMU_ID = 5005 @enum.unique @@ -260,7 +264,7 @@ class TagSpecificType(enum.IntEnum): # PAC/Stanley # Presco # Visa2000 - # Viking + Viking = 170 # Noralsy # Jablotron @@ -347,6 +351,8 @@ class TagSpecificType(enum.IntEnum): return "EM410X/64" elif self == TagSpecificType.HIDProx: return "HIDProx" + elif self == TagSpecificType.Viking: + return "Viking" elif self == TagSpecificType.MIFARE_Mini: return "Mifare Mini" elif self == TagSpecificType.MIFARE_1024: