mirror of
https://github.com/RfidResearchGroup/ChameleonUltra.git
synced 2026-10-15 06:50:22 +00:00
1364 lines
63 KiB
C
1364 lines
63 KiB
C
#include "fds_util.h"
|
|
#include "bsp_time.h"
|
|
#include "bsp_delay.h"
|
|
#include "usb_main.h"
|
|
#include "rfid_main.h"
|
|
#include "ble_main.h"
|
|
#include "syssleep.h"
|
|
#include "tag_emulation.h"
|
|
#include "hex_utils.h"
|
|
#include "data_cmd.h"
|
|
#include "app_cmd.h"
|
|
#include "app_status.h"
|
|
#include "tag_persistence.h"
|
|
#include "nrf_pwr_mgmt.h"
|
|
#include "settings.h"
|
|
#include "delayed_reset.h"
|
|
#include "netdata.h"
|
|
|
|
|
|
#define NRF_LOG_MODULE_NAME app_cmd
|
|
#include "nrf_log.h"
|
|
#include "nrf_log_ctrl.h"
|
|
#include "nrf_log_default_backends.h"
|
|
NRF_LOG_MODULE_REGISTER();
|
|
|
|
|
|
static void change_slot_auto(uint8_t slot) {
|
|
device_mode_t mode = get_device_mode();
|
|
tag_emulation_change_slot(slot, mode != DEVICE_MODE_READER);
|
|
light_up_by_slot();
|
|
set_slot_light_color(RGB_RED);
|
|
}
|
|
|
|
static data_frame_tx_t *cmd_processor_get_app_version(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
struct {
|
|
uint8_t version_major;
|
|
uint8_t version_minor;
|
|
} PACKED payload;
|
|
payload.version_major = APP_FW_VER_MAJOR;
|
|
payload.version_minor = APP_FW_VER_MINOR;
|
|
return data_frame_make(cmd, STATUS_SUCCESS, sizeof(payload), (uint8_t *)&payload);
|
|
}
|
|
|
|
|
|
static data_frame_tx_t *cmd_processor_get_git_version(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
return data_frame_make(cmd, STATUS_SUCCESS, strlen(GIT_VERSION), (uint8_t *)GIT_VERSION);
|
|
}
|
|
|
|
|
|
static data_frame_tx_t *cmd_processor_get_device_model(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
uint8_t resp_data = hw_get_device_type();
|
|
return data_frame_make(cmd, STATUS_SUCCESS, sizeof(resp_data), &resp_data);
|
|
}
|
|
|
|
|
|
static data_frame_tx_t *cmd_processor_change_device_mode(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
if ((length != 1) || (data[0] > 1)) {
|
|
return data_frame_make(cmd, STATUS_PAR_ERR, 0, NULL);
|
|
}
|
|
|
|
if (data[0] == 1) {
|
|
#if defined(PROJECT_CHAMELEON_ULTRA)
|
|
reader_mode_enter();
|
|
return data_frame_make(cmd, STATUS_SUCCESS, 0, NULL);
|
|
#else
|
|
return data_frame_make(cmd, STATUS_NOT_IMPLEMENTED, 0, NULL);
|
|
#endif
|
|
} else {
|
|
#if defined(PROJECT_CHAMELEON_ULTRA)
|
|
tag_mode_enter();
|
|
#endif
|
|
return data_frame_make(cmd, STATUS_SUCCESS, 0, NULL);
|
|
}
|
|
}
|
|
|
|
static data_frame_tx_t *cmd_processor_get_device_mode(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
uint8_t resp_data = (get_device_mode() == DEVICE_MODE_READER);
|
|
return data_frame_make(cmd, STATUS_SUCCESS, sizeof(resp_data), &resp_data);
|
|
}
|
|
|
|
static data_frame_tx_t *cmd_processor_enter_bootloader(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
// restart to boot
|
|
#define BOOTLOADER_DFU_GPREGRET_MASK (0xB0)
|
|
#define BOOTLOADER_DFU_START_BIT_MASK (0x01)
|
|
#define BOOTLOADER_DFU_START (BOOTLOADER_DFU_GPREGRET_MASK | BOOTLOADER_DFU_START_BIT_MASK)
|
|
APP_ERROR_CHECK(sd_power_gpregret_clr(0, 0xffffffff));
|
|
APP_ERROR_CHECK(sd_power_gpregret_set(0, BOOTLOADER_DFU_START));
|
|
nrf_pwr_mgmt_shutdown(NRF_PWR_MGMT_SHUTDOWN_GOTO_DFU);
|
|
// Never into here...
|
|
while (1) __NOP();
|
|
// For the compiler to be happy...
|
|
return data_frame_make(cmd, STATUS_SUCCESS, 0, NULL);
|
|
}
|
|
|
|
static data_frame_tx_t *cmd_processor_get_device_chip_id(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
struct {
|
|
uint32_t chip_HSW;
|
|
uint32_t chip_LSW;
|
|
} PACKED payload;
|
|
payload.chip_LSW = U32HTONL(NRF_FICR->DEVICEID[0]);
|
|
payload.chip_HSW = U32HTONL(NRF_FICR->DEVICEID[1]);
|
|
return data_frame_make(cmd, STATUS_SUCCESS, sizeof(payload), (uint8_t *)&payload);
|
|
}
|
|
|
|
static data_frame_tx_t *cmd_processor_get_device_address(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
// The FICR value is a just a random number, with no knowledge
|
|
// of the Bluetooth Specification requirements for random addresses.
|
|
// So we need to set a Bluetooth LE random address as a static address.
|
|
// See: https://github.com/zephyrproject-rtos/zephyr/blob/7b6b1328a0cb96fe313a5e2bfc57047471df236e/subsys/bluetooth/controller/hci/nordic/hci_vendor.c#L29
|
|
|
|
struct {
|
|
uint16_t device_address_HSW;
|
|
uint32_t device_address_LSW;
|
|
} PACKED payload;
|
|
payload.device_address_LSW = U32HTONL(NRF_FICR->DEVICEADDR[0]);
|
|
payload.device_address_HSW = U16HTONS(NRF_FICR->DEVICEADDR[1] | 0xC000);
|
|
return data_frame_make(cmd, STATUS_SUCCESS, sizeof(payload), (uint8_t *)&payload);
|
|
}
|
|
|
|
static data_frame_tx_t *cmd_processor_save_settings(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
status = settings_save_config();
|
|
return data_frame_make(cmd, status, 0, NULL);
|
|
}
|
|
|
|
static data_frame_tx_t *cmd_processor_reset_settings(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
settings_init_config();
|
|
status = settings_save_config();
|
|
return data_frame_make(cmd, status, 0, NULL);
|
|
}
|
|
|
|
static data_frame_tx_t *cmd_processor_get_device_settings(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
uint8_t settings[7 + BLE_PAIRING_KEY_LEN] = {};
|
|
settings[0] = SETTINGS_CURRENT_VERSION; // current version
|
|
settings[1] = settings_get_animation_config(); // animation mode
|
|
settings[2] = settings_get_button_press_config('A'); // short A button press mode
|
|
settings[3] = settings_get_button_press_config('B'); // short B button press mode
|
|
settings[4] = settings_get_long_button_press_config('A'); // long A button press mode
|
|
settings[5] = settings_get_long_button_press_config('B'); // long B button press mode
|
|
settings[6] = settings_get_ble_pairing_enable(); // is device require pairing
|
|
memcpy(settings + 7, settings_get_ble_connect_key(), BLE_PAIRING_KEY_LEN);
|
|
return data_frame_make(cmd, STATUS_SUCCESS, 7 + BLE_PAIRING_KEY_LEN, settings);
|
|
}
|
|
|
|
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)) {
|
|
return data_frame_make(cmd, STATUS_PAR_ERR, 0, NULL);
|
|
}
|
|
settings_set_animation_config(data[0]);
|
|
return data_frame_make(cmd, STATUS_SUCCESS, 0, NULL);
|
|
}
|
|
|
|
static data_frame_tx_t *cmd_processor_get_animation_mode(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
uint8_t animation_mode = settings_get_animation_config();
|
|
return data_frame_make(cmd, STATUS_SUCCESS, 1, &animation_mode);
|
|
}
|
|
|
|
static data_frame_tx_t *cmd_processor_get_battery_info(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
struct {
|
|
uint16_t voltage;
|
|
uint8_t percent;
|
|
} PACKED payload;
|
|
payload.voltage = U16HTONS(batt_lvl_in_milli_volts);
|
|
payload.percent = percentage_batt_lvl;
|
|
return data_frame_make(cmd, STATUS_SUCCESS, sizeof(payload), (uint8_t *)&payload);
|
|
}
|
|
|
|
static data_frame_tx_t *cmd_processor_get_button_press_config(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
if ((length != 1) || (!is_settings_button_type_valid(data[0]))) {
|
|
return data_frame_make(cmd, STATUS_PAR_ERR, 0, NULL);
|
|
}
|
|
uint8_t button_press_config = settings_get_button_press_config(data[0]);
|
|
return data_frame_make(cmd, STATUS_SUCCESS, sizeof(button_press_config), &button_press_config);
|
|
}
|
|
|
|
static data_frame_tx_t *cmd_processor_set_button_press_config(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
if ((length != 2) || (!is_settings_button_type_valid(data[0]))) {
|
|
return data_frame_make(cmd, STATUS_PAR_ERR, 0, NULL);
|
|
}
|
|
settings_set_button_press_config(data[0], data[1]);
|
|
return data_frame_make(cmd, STATUS_SUCCESS, 0, NULL);
|
|
}
|
|
|
|
static data_frame_tx_t *cmd_processor_get_long_button_press_config(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
if ((length != 1) || (!is_settings_button_type_valid(data[0]))) {
|
|
return data_frame_make(cmd, STATUS_PAR_ERR, 0, NULL);
|
|
}
|
|
uint8_t button_press_config = settings_get_long_button_press_config(data[0]);
|
|
return data_frame_make(cmd, STATUS_SUCCESS, sizeof(button_press_config), &button_press_config);
|
|
}
|
|
|
|
static data_frame_tx_t *cmd_processor_set_long_button_press_config(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
if ((length != 2) || (!is_settings_button_type_valid(data[0]))) {
|
|
return data_frame_make(cmd, STATUS_PAR_ERR, 0, NULL);
|
|
}
|
|
settings_set_long_button_press_config(data[0], data[1]);
|
|
return data_frame_make(cmd, STATUS_SUCCESS, 0, NULL);
|
|
}
|
|
|
|
static data_frame_tx_t *cmd_processor_get_ble_pairing_enable(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
uint8_t is_enable = settings_get_ble_pairing_enable();
|
|
return data_frame_make(cmd, STATUS_SUCCESS, 1, &is_enable);
|
|
}
|
|
|
|
static data_frame_tx_t *cmd_processor_set_ble_pairing_enable(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
if (length != 1 && data[0] > 1) {
|
|
return data_frame_make(cmd, STATUS_PAR_ERR, 0, NULL);
|
|
}
|
|
settings_set_ble_pairing_enable(data[0]);
|
|
return data_frame_make(cmd, STATUS_SUCCESS, 0, NULL);
|
|
}
|
|
|
|
#if defined(PROJECT_CHAMELEON_ULTRA)
|
|
|
|
static data_frame_tx_t *cmd_processor_hf14a_scan(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
picc_14a_tag_t taginfo;
|
|
status = pcd_14a_reader_scan_auto(&taginfo);
|
|
if (status != STATUS_HF_TAG_OK) {
|
|
return data_frame_make(cmd, status, 0, NULL);
|
|
}
|
|
// uidlen[1]|uid[uidlen]|atqa[2]|sak[1]|atslen[1]|ats[atslen]
|
|
// dynamic length, so no struct
|
|
uint8_t payload[1 + sizeof(taginfo.uid) + sizeof(taginfo.atqa) + sizeof(taginfo.sak) + 1 + 254];
|
|
uint16_t offset = 0;
|
|
payload[offset++] = taginfo.uid_len;
|
|
memcpy(&payload[offset], taginfo.uid, taginfo.uid_len);
|
|
offset += taginfo.uid_len;
|
|
memcpy(&payload[offset], taginfo.atqa, sizeof(taginfo.atqa));
|
|
offset += sizeof(taginfo.atqa);
|
|
payload[offset++] = taginfo.sak;
|
|
payload[offset++] = taginfo.ats_len;
|
|
memcpy(&payload[offset], taginfo.ats, taginfo.ats_len);
|
|
offset += taginfo.ats_len;
|
|
return data_frame_make(cmd, STATUS_HF_TAG_OK, offset, payload);
|
|
}
|
|
|
|
static data_frame_tx_t *cmd_processor_mf1_detect_support(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
status = check_std_mifare_nt_support();
|
|
return data_frame_make(cmd, status, 0, NULL);
|
|
}
|
|
|
|
static data_frame_tx_t *cmd_processor_mf1_detect_prng(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
uint8_t type;
|
|
status = check_prng_type((mf1_prng_type_t *)&type);
|
|
if (status != STATUS_HF_TAG_OK) {
|
|
return data_frame_make(cmd, status, 0, NULL);
|
|
}
|
|
return data_frame_make(cmd, STATUS_HF_TAG_OK, sizeof(type), &type);
|
|
}
|
|
|
|
// We have a reusable payload structure.
|
|
typedef struct {
|
|
uint8_t type_known;
|
|
uint8_t block_known;
|
|
uint8_t key_known[6];
|
|
uint8_t type_target;
|
|
uint8_t block_target;
|
|
} PACKED nested_common_payload_t;
|
|
|
|
static data_frame_tx_t *cmd_processor_mf1_static_nested_acquire(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
mf1_static_nested_core_t sncs;
|
|
if (length != sizeof(nested_common_payload_t)) {
|
|
return data_frame_make(cmd, STATUS_PAR_ERR, 0, NULL);
|
|
}
|
|
|
|
nested_common_payload_t *payload = (nested_common_payload_t *)data;
|
|
status = static_nested_recover_key(bytes_to_num(payload->key_known, 6), payload->block_known, payload->type_known, payload->block_target, payload->type_target, &sncs);
|
|
if (status != STATUS_HF_TAG_OK) {
|
|
return data_frame_make(cmd, status, 0, NULL);
|
|
}
|
|
// mf1_static_nested_core_t is PACKED and comprises only bytes so we can use it directly
|
|
return data_frame_make(cmd, STATUS_HF_TAG_OK, sizeof(sncs), (uint8_t *)(&sncs));
|
|
}
|
|
|
|
static data_frame_tx_t *cmd_processor_mf1_darkside_acquire(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
if (length != 4) {
|
|
return data_frame_make(cmd, STATUS_PAR_ERR, 0, NULL);
|
|
}
|
|
struct {
|
|
uint8_t darkside_status;
|
|
// DarksideCore_t is PACKED and comprises only bytes so we can use it directly
|
|
DarksideCore_t dc;
|
|
} PACKED payload;
|
|
status = darkside_recover_key(data[1], data[0], data[2], data[3], &payload.dc, (mf1_darkside_status_t *)&payload.darkside_status);
|
|
if (status != STATUS_HF_TAG_OK) {
|
|
return data_frame_make(cmd, status, 0, NULL);
|
|
}
|
|
if (payload.darkside_status != DARKSIDE_OK) {
|
|
return data_frame_make(cmd, STATUS_HF_TAG_OK, sizeof(payload.darkside_status), &payload.darkside_status);
|
|
}
|
|
return data_frame_make(cmd, STATUS_HF_TAG_OK, sizeof(payload), (uint8_t *)&payload);
|
|
}
|
|
|
|
static data_frame_tx_t *cmd_processor_mf1_detect_nt_dist(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
typedef struct {
|
|
uint8_t type_known;
|
|
uint8_t block_known;
|
|
uint8_t key_known[6];
|
|
} PACKED payload_t;
|
|
if (length != sizeof(payload_t)) {
|
|
return data_frame_make(cmd, STATUS_PAR_ERR, 0, NULL);
|
|
}
|
|
|
|
struct {
|
|
uint8_t uid[4];
|
|
uint32_t distance;
|
|
} PACKED payload_resp;
|
|
|
|
payload_t *payload = (payload_t *)data;
|
|
uint32_t distance;
|
|
status = nested_distance_detect(payload->block_known, payload->type_known, payload->key_known, payload_resp.uid, &distance);
|
|
if (status != STATUS_HF_TAG_OK) {
|
|
return data_frame_make(cmd, status, 0, NULL);
|
|
}
|
|
payload_resp.distance = U32HTONL(distance);
|
|
return data_frame_make(cmd, STATUS_HF_TAG_OK, sizeof(payload_resp), (uint8_t *)&payload_resp);
|
|
}
|
|
|
|
static data_frame_tx_t *cmd_processor_mf1_nested_acquire(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
mf1_nested_core_t ncs[SETS_NR];
|
|
if (length != sizeof(nested_common_payload_t)) {
|
|
return data_frame_make(cmd, STATUS_PAR_ERR, 0, NULL);
|
|
}
|
|
|
|
nested_common_payload_t *payload = (nested_common_payload_t *)data;
|
|
status = nested_recover_key(bytes_to_num(payload->key_known, 6), payload->block_known, payload->type_known, payload->block_target, payload->type_target, ncs);
|
|
if (status != STATUS_HF_TAG_OK) {
|
|
return data_frame_make(cmd, status, 0, NULL);
|
|
}
|
|
// mf1_nested_core_t is PACKED and comprises only bytes so we can use it directly
|
|
return data_frame_make(cmd, STATUS_HF_TAG_OK, sizeof(ncs), (uint8_t *)(&ncs));
|
|
}
|
|
|
|
static data_frame_tx_t *cmd_processor_mf1_auth_one_key_block(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
typedef struct {
|
|
uint8_t type;
|
|
uint8_t block;
|
|
uint8_t key[6];
|
|
} PACKED payload_t;
|
|
if (length != sizeof(payload_t)) {
|
|
return data_frame_make(cmd, STATUS_PAR_ERR, 0, NULL);
|
|
}
|
|
|
|
payload_t *payload = (payload_t *)data;
|
|
status = auth_key_use_522_hw(payload->block, payload->type, payload->key);
|
|
pcd_14a_reader_mf1_unauth();
|
|
return data_frame_make(cmd, status, 0, NULL);
|
|
}
|
|
|
|
static data_frame_tx_t *cmd_processor_mf1_check_keys_of_sectors(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
if (length < 16 || (length - 10) % 6 != 0) {
|
|
return data_frame_make(cmd, STATUS_PAR_ERR, 0, NULL);
|
|
}
|
|
|
|
// init
|
|
mf1_toolbox_check_keys_of_sectors_in_t in = {
|
|
.mask = *(mf1_toolbox_check_keys_of_sectors_mask_t *) &data[0],
|
|
.keys_len = (length - 10) / 6,
|
|
.keys = (mf1_key_t *) &data[10]
|
|
};
|
|
mf1_toolbox_check_keys_of_sectors_out_t out;
|
|
status = mf1_toolbox_check_keys_of_sectors(&in, &out);
|
|
|
|
return data_frame_make(cmd, status, sizeof(out), (uint8_t *)&out);
|
|
}
|
|
|
|
static data_frame_tx_t *cmd_processor_mf1_read_one_block(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
typedef struct {
|
|
uint8_t type;
|
|
uint8_t block;
|
|
uint8_t key[6];
|
|
} PACKED payload_t;
|
|
if (length != sizeof(payload_t)) {
|
|
return data_frame_make(cmd, STATUS_PAR_ERR, 0, NULL);
|
|
}
|
|
|
|
payload_t *payload = (payload_t *)data;
|
|
uint8_t block[16] = { 0x00 };
|
|
status = auth_key_use_522_hw(payload->block, payload->type, payload->key);
|
|
if (status != STATUS_HF_TAG_OK) {
|
|
return data_frame_make(cmd, status, 0, NULL);
|
|
}
|
|
status = pcd_14a_reader_mf1_read(payload->block, block);
|
|
if (status != STATUS_HF_TAG_OK) {
|
|
return data_frame_make(cmd, status, 0, NULL);
|
|
}
|
|
return data_frame_make(cmd, status, sizeof(block), block);
|
|
}
|
|
|
|
static data_frame_tx_t *cmd_processor_mf1_write_one_block(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
typedef struct {
|
|
uint8_t type;
|
|
uint8_t block;
|
|
uint8_t key[6];
|
|
uint8_t block_data[16];
|
|
} PACKED payload_t;
|
|
if (length != sizeof(payload_t)) {
|
|
return data_frame_make(cmd, STATUS_PAR_ERR, 0, NULL);
|
|
}
|
|
|
|
payload_t *payload = (payload_t *)data;
|
|
status = auth_key_use_522_hw(payload->block, payload->type, payload->key);
|
|
if (status != STATUS_HF_TAG_OK) {
|
|
return data_frame_make(cmd, status, 0, NULL);
|
|
}
|
|
status = pcd_14a_reader_mf1_write(payload->block, payload->block_data);
|
|
return data_frame_make(cmd, status, 0, NULL);
|
|
}
|
|
|
|
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 };
|
|
uint16_t resp_length = 0;
|
|
|
|
typedef struct {
|
|
struct { // LSB -> MSB
|
|
uint8_t reserved : 2;
|
|
|
|
uint8_t check_response_crc : 1;
|
|
uint8_t keep_rf_field : 1;
|
|
uint8_t auto_select : 1;
|
|
uint8_t append_crc : 1;
|
|
uint8_t wait_response : 1;
|
|
uint8_t activate_rf_field : 1;
|
|
} options;
|
|
|
|
// U16NTOHS
|
|
uint16_t resp_timeout;
|
|
uint16_t data_bitlength;
|
|
|
|
uint8_t data_buffer[0]; // We can have a lot of data or no data. struct just to compute offsets with min options.
|
|
} PACKED payload_t;
|
|
payload_t *payload = (payload_t *)data;
|
|
if (length < sizeof(payload_t)) {
|
|
return data_frame_make(cmd, STATUS_PAR_ERR, 0, NULL);
|
|
}
|
|
|
|
NRF_LOG_INFO("activate_rf_field = %d", payload->options.activate_rf_field);
|
|
NRF_LOG_INFO("wait_response = %d", payload->options.wait_response);
|
|
NRF_LOG_INFO("append_crc = %d", payload->options.append_crc);
|
|
NRF_LOG_INFO("auto_select = %d", payload->options.auto_select);
|
|
NRF_LOG_INFO("keep_rf_field = %d", payload->options.keep_rf_field);
|
|
NRF_LOG_INFO("check_response_crc = %d", payload->options.check_response_crc);
|
|
NRF_LOG_INFO("reserved = %d", payload->options.reserved);
|
|
|
|
status = pcd_14a_reader_raw_cmd(
|
|
payload->options.activate_rf_field,
|
|
payload->options.wait_response,
|
|
payload->options.append_crc,
|
|
payload->options.auto_select,
|
|
payload->options.keep_rf_field,
|
|
payload->options.check_response_crc,
|
|
|
|
U16NTOHS(payload->resp_timeout),
|
|
|
|
U16NTOHS(payload->data_bitlength),
|
|
payload->data_buffer,
|
|
|
|
resp,
|
|
&resp_length,
|
|
U8ARR_BIT_LEN(resp)
|
|
);
|
|
|
|
return data_frame_make(cmd, status, resp_length, resp);
|
|
}
|
|
|
|
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;
|
|
uint8_t src_block;
|
|
uint8_t src_key[6];
|
|
uint8_t operator;
|
|
uint32_t operand;
|
|
uint8_t dst_type;
|
|
uint8_t dst_block;
|
|
uint8_t dst_key[6];
|
|
} PACKED payload_t;
|
|
if (length != sizeof(payload_t)) {
|
|
return data_frame_make(cmd, STATUS_PAR_ERR, 0, NULL);
|
|
}
|
|
|
|
payload_t *payload = (payload_t *)data;
|
|
|
|
// scan tag
|
|
picc_14a_tag_t taginfo;
|
|
if (pcd_14a_reader_scan_auto(&taginfo) != STATUS_HF_TAG_OK) {
|
|
return data_frame_make(cmd, STATUS_HF_TAG_NO, 0, NULL);
|
|
}
|
|
|
|
// auth src
|
|
status = pcd_14a_reader_mf1_auth(&taginfo, payload->src_type, payload->src_block, payload->src_key);
|
|
if (status != STATUS_HF_TAG_OK) {
|
|
return data_frame_make(cmd, status, 0, NULL);
|
|
}
|
|
|
|
// value block operation
|
|
status = pcd_14a_reader_mf1_manipulate_value_block(payload->operator, payload->src_block, (int32_t) U32NTOHL(payload->operand));
|
|
if (status != STATUS_HF_TAG_OK) {
|
|
return data_frame_make(cmd, status, 0, NULL);
|
|
}
|
|
|
|
// auth dst if needed
|
|
if (payload->src_block != payload->dst_block || payload->src_type != payload->dst_type) {
|
|
status = pcd_14a_reader_mf1_auth(&taginfo, payload->dst_type, payload->dst_block, payload->dst_key);
|
|
if (status != STATUS_HF_TAG_OK) {
|
|
return data_frame_make(cmd, status, 0, NULL);
|
|
}
|
|
}
|
|
|
|
// transfer value block
|
|
status = pcd_14a_reader_mf1_transfer_value_block(payload->dst_block);
|
|
if (status != STATUS_HF_TAG_OK) {
|
|
return data_frame_make(cmd, status, 0, NULL);
|
|
}
|
|
|
|
return data_frame_make(cmd, status, 0, NULL);
|
|
}
|
|
|
|
static data_frame_tx_t *cmd_processor_em410x_scan(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
uint8_t id_buffer[5] = { 0x00 };
|
|
status = PcdScanEM410X(id_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(id_buffer), id_buffer);
|
|
}
|
|
|
|
static data_frame_tx_t *cmd_processor_em410x_write_to_t55XX(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
typedef struct {
|
|
uint8_t id[5];
|
|
uint8_t old_key[4];
|
|
uint8_t new_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, new_keys)) % sizeof(payload->new_keys) != 0) {
|
|
return data_frame_make(cmd, STATUS_PAR_ERR, 0, NULL);
|
|
}
|
|
|
|
status = PcdWriteT55XX(payload->id, payload->old_key, payload->new_keys, (length - offsetof(payload_t, new_keys)) / sizeof(payload->new_keys));
|
|
return data_frame_make(cmd, status, 0, NULL);
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
static data_frame_tx_t *cmd_processor_set_active_slot(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
if (length != 1 || data[0] >= TAG_MAX_SLOT_NUM) {
|
|
return data_frame_make(cmd, STATUS_PAR_ERR, 0, NULL);
|
|
}
|
|
change_slot_auto(data[0]);
|
|
return data_frame_make(cmd, STATUS_SUCCESS, 0, NULL);
|
|
}
|
|
|
|
static data_frame_tx_t *cmd_processor_set_slot_tag_type(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
typedef struct {
|
|
uint8_t num_slot;
|
|
uint16_t tag_type;
|
|
} PACKED payload_t;
|
|
if (length != sizeof(payload_t)) {
|
|
return data_frame_make(cmd, STATUS_PAR_ERR, 0, NULL);
|
|
}
|
|
|
|
payload_t *payload = (payload_t *)data;
|
|
tag_specific_type_t tag_type = U16NTOHS(payload->tag_type);
|
|
if (payload->num_slot >= TAG_MAX_SLOT_NUM || !is_tag_specific_type_valid(tag_type)) {
|
|
return data_frame_make(cmd, STATUS_PAR_ERR, 0, NULL);
|
|
}
|
|
tag_emulation_change_type(payload->num_slot, tag_type);
|
|
return data_frame_make(cmd, STATUS_SUCCESS, 0, NULL);
|
|
}
|
|
|
|
static data_frame_tx_t *cmd_processor_delete_slot_sense_type(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
typedef struct {
|
|
uint8_t num_slot;
|
|
uint8_t sense_type;
|
|
} PACKED payload_t;
|
|
payload_t *payload = (payload_t *)data;
|
|
if ((length != sizeof(payload_t)) ||
|
|
(payload->num_slot >= TAG_MAX_SLOT_NUM) ||
|
|
(payload->sense_type != TAG_SENSE_HF && payload->sense_type != TAG_SENSE_LF)) {
|
|
return data_frame_make(cmd, STATUS_PAR_ERR, 0, NULL);
|
|
}
|
|
|
|
tag_emulation_delete_data(payload->num_slot, payload->sense_type);
|
|
return data_frame_make(cmd, STATUS_SUCCESS, 0, NULL);
|
|
}
|
|
|
|
static data_frame_tx_t *cmd_processor_set_slot_data_default(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
typedef struct {
|
|
uint8_t num_slot;
|
|
uint16_t tag_type;
|
|
} PACKED payload_t;
|
|
if (length != sizeof(payload_t)) {
|
|
return data_frame_make(cmd, STATUS_PAR_ERR, 0, NULL);
|
|
}
|
|
|
|
payload_t *payload = (payload_t *)data;
|
|
tag_specific_type_t tag_type = U16NTOHS(payload->tag_type);
|
|
if (payload->num_slot >= TAG_MAX_SLOT_NUM || !is_tag_specific_type_valid(tag_type)) {
|
|
return data_frame_make(cmd, STATUS_PAR_ERR, 0, NULL);
|
|
}
|
|
status = tag_emulation_factory_data(payload->num_slot, tag_type) ? STATUS_SUCCESS : STATUS_NOT_IMPLEMENTED;
|
|
return data_frame_make(cmd, status, 0, NULL);
|
|
}
|
|
|
|
static data_frame_tx_t *cmd_processor_set_slot_enable(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
typedef struct {
|
|
uint8_t slot_index;
|
|
uint8_t sense_type;
|
|
uint8_t enabled;
|
|
} PACKED payload_t;
|
|
|
|
payload_t *payload = (payload_t *)data;
|
|
if (length != sizeof(payload_t) ||
|
|
payload->slot_index >= TAG_MAX_SLOT_NUM ||
|
|
(payload->sense_type != TAG_SENSE_HF && payload->sense_type != TAG_SENSE_LF) ||
|
|
payload->enabled > 1) {
|
|
return data_frame_make(cmd, STATUS_PAR_ERR, 0, NULL);
|
|
}
|
|
|
|
uint8_t slot_now = payload->slot_index;
|
|
tag_emulation_slot_set_enable(slot_now, payload->sense_type, payload->enabled);
|
|
if ((!payload->enabled) &&
|
|
(!tag_emulation_slot_is_enabled(slot_now, payload->sense_type == TAG_SENSE_HF ? TAG_SENSE_LF : TAG_SENSE_HF))) {
|
|
// HF and LF disabled, need to change slot
|
|
uint8_t slot_prev = tag_emulation_slot_find_next(slot_now);
|
|
NRF_LOG_INFO("slot_now = %d, slot_prev = %d", slot_now, slot_prev);
|
|
if (slot_prev == slot_now) {
|
|
set_slot_light_color(RGB_MAGENTA);
|
|
} else {
|
|
change_slot_auto(slot_prev);
|
|
}
|
|
}
|
|
return data_frame_make(cmd, STATUS_SUCCESS, 0, NULL);
|
|
}
|
|
|
|
static data_frame_tx_t *cmd_processor_slot_data_config_save(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
tag_emulation_save();
|
|
return data_frame_make(cmd, STATUS_SUCCESS, 0, NULL);
|
|
}
|
|
|
|
static data_frame_tx_t *cmd_processor_get_active_slot(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
uint8_t slot = tag_emulation_get_slot();
|
|
return data_frame_make(cmd, STATUS_SUCCESS, 1, &slot);
|
|
}
|
|
|
|
static data_frame_tx_t *cmd_processor_get_slot_info(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
struct {
|
|
uint16_t hf_tag_type;
|
|
uint16_t lf_tag_type;
|
|
} PACKED payload[8];
|
|
|
|
tag_slot_specific_type_t tag_types;
|
|
for (uint8_t slot = 0; slot < 8; slot++) {
|
|
tag_emulation_get_specific_types_by_slot(slot, &tag_types);
|
|
payload[slot].hf_tag_type = U16HTONS(tag_types.tag_hf);
|
|
payload[slot].lf_tag_type = U16HTONS(tag_types.tag_lf);
|
|
}
|
|
|
|
return data_frame_make(cmd, STATUS_SUCCESS, sizeof(payload), (uint8_t *)&payload);
|
|
}
|
|
|
|
static data_frame_tx_t *cmd_processor_wipe_fds(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
bool success = fds_wipe();
|
|
status = success ? STATUS_SUCCESS : STATUS_FLASH_WRITE_FAIL;
|
|
delayed_reset(50);
|
|
return data_frame_make(cmd, status, 0, NULL);
|
|
}
|
|
|
|
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) {
|
|
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);
|
|
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) {
|
|
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);
|
|
uint8_t responseData[LF_EM410X_TAG_ID_SIZE];
|
|
memcpy(responseData, buffer->buffer, LF_EM410X_TAG_ID_SIZE);
|
|
return data_frame_make(cmd, STATUS_SUCCESS, LF_EM410X_TAG_ID_SIZE, responseData);
|
|
}
|
|
|
|
static data_frame_tx_t *cmd_processor_hf14a_get_anti_coll_data(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_hf == TAG_TYPE_UNDEFINED) {
|
|
return data_frame_make(cmd, STATUS_SUCCESS, 0, NULL); // no data in slot, don't send garbage
|
|
}
|
|
nfc_tag_14a_coll_res_reference_t *info = get_saved_mifare_coll_res();
|
|
// uidlen[1]|uid[uidlen]|atqa[2]|sak[1]|atslen[1]|ats[atslen]
|
|
// dynamic length, so no struct
|
|
uint8_t payload[1 + *info->size + 2 + 1 + 1 + 254];
|
|
uint16_t offset = 0;
|
|
payload[offset++] = *info->size;
|
|
memcpy(&payload[offset], info->uid, *info->size);
|
|
offset += *info->size;
|
|
memcpy(&payload[offset], info->atqa, 2);
|
|
offset += 2;
|
|
payload[offset++] = *info->sak;
|
|
if (info->ats->length > 0) {
|
|
payload[offset++] = info->ats->length;
|
|
memcpy(&payload[offset], info->ats->data, info->ats->length);
|
|
offset += info->ats->length;
|
|
} else {
|
|
payload[offset++] = 0;
|
|
}
|
|
return data_frame_make(cmd, STATUS_SUCCESS, offset, payload);
|
|
}
|
|
|
|
static data_frame_tx_t *cmd_processor_mf1_set_detection_enable(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
if (length != 1 || data[0] > 1) {
|
|
return data_frame_make(cmd, STATUS_PAR_ERR, 0, NULL);
|
|
}
|
|
nfc_tag_mf1_detection_log_clear();
|
|
nfc_tag_mf1_set_detection_enable(data[0]);
|
|
return data_frame_make(cmd, STATUS_SUCCESS, 0, NULL);
|
|
}
|
|
|
|
static data_frame_tx_t *cmd_processor_mf1_get_detection_enable(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
uint8_t is_enable = nfc_tag_mf1_is_detection_enable();
|
|
return data_frame_make(cmd, STATUS_SUCCESS, 1, (uint8_t *)(&is_enable));
|
|
}
|
|
|
|
static data_frame_tx_t *cmd_processor_mf1_get_detection_count(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
uint32_t count = nfc_tag_mf1_detection_log_count();
|
|
if (count == 0xFFFFFFFF) {
|
|
count = 0;
|
|
}
|
|
uint32_t payload = U32HTONL(count);
|
|
return data_frame_make(cmd, STATUS_SUCCESS, sizeof(uint32_t), (uint8_t *)&payload);
|
|
}
|
|
|
|
static data_frame_tx_t *cmd_processor_mf1_get_detection_log(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
uint32_t count;
|
|
uint32_t index;
|
|
uint8_t *resp = NULL;
|
|
nfc_tag_mf1_auth_log_t *logs = mf1_get_auth_log(&count);
|
|
if (length != 4 || count == 0xFFFFFFFF) {
|
|
return data_frame_make(cmd, STATUS_PAR_ERR, 0, NULL);
|
|
}
|
|
index = U32NTOHL(*(uint32_t *)data);
|
|
// NRF_LOG_INFO("index = %d", index);
|
|
if (index >= count) {
|
|
return data_frame_make(cmd, STATUS_PAR_ERR, 0, NULL);
|
|
}
|
|
resp = (uint8_t *)(logs + index);
|
|
length = MIN(count - index, NETDATA_MAX_DATA_LENGTH / sizeof(nfc_tag_mf1_auth_log_t)) * sizeof(nfc_tag_mf1_auth_log_t);
|
|
return data_frame_make(cmd, STATUS_SUCCESS, length, resp);
|
|
}
|
|
|
|
static data_frame_tx_t *cmd_processor_mf1_write_emu_block_data(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
if (length == 0 || (((length - 1) % NFC_TAG_MF1_DATA_SIZE) != 0)) {
|
|
return data_frame_make(cmd, STATUS_PAR_ERR, 0, NULL);
|
|
}
|
|
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;
|
|
}
|
|
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;
|
|
for (int i = 1, j = block_index; i < length; i += NFC_TAG_MF1_DATA_SIZE, j++) {
|
|
uint8_t *p_block = &data[i];
|
|
memcpy(info->memory[j], p_block, NFC_TAG_MF1_DATA_SIZE);
|
|
}
|
|
return data_frame_make(cmd, STATUS_SUCCESS, 0, NULL);
|
|
}
|
|
|
|
static data_frame_tx_t *cmd_processor_mf1_read_emu_block_data(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
if ((length != 2) || (data[1] < 1) || (data[1] > 32) || (data[0] + data[1] > NFC_TAG_MF1_BLOCK_MAX)) {
|
|
return data_frame_make(cmd, STATUS_PAR_ERR, 0, NULL);
|
|
}
|
|
uint8_t block_index = data[0];
|
|
uint8_t block_count = data[1];
|
|
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;
|
|
uint16_t result_length = block_count * NFC_TAG_MF1_DATA_SIZE;
|
|
uint8_t result_buffer[result_length];
|
|
for (int i = 0, j = block_index; i < result_length; i += NFC_TAG_MF1_DATA_SIZE, j++) {
|
|
memcpy(&result_buffer[i], info->memory[j], NFC_TAG_MF1_DATA_SIZE);
|
|
}
|
|
return data_frame_make(cmd, STATUS_SUCCESS, result_length, result_buffer);
|
|
}
|
|
|
|
static data_frame_tx_t *cmd_processor_mf0_ntag_write_emu_page_data(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
uint8_t byte;
|
|
uint8_t active_slot = tag_emulation_get_slot();
|
|
|
|
tag_slot_specific_type_t active_slot_tag_types;
|
|
tag_emulation_get_specific_types_by_slot(active_slot, &active_slot_tag_types);
|
|
|
|
int nr_pages = nfc_tag_mf0_ntag_get_nr_pages_by_tag_type(active_slot_tag_types.tag_hf);
|
|
// This means wrong slot type.
|
|
if (nr_pages <= 0) return data_frame_make(cmd, STATUS_INVALID_SLOT_TYPE, 0, data);
|
|
|
|
if (length < 2) {
|
|
byte = nr_pages;
|
|
return data_frame_make(cmd, STATUS_INVALID_PARAMS, 1, &byte);
|
|
}
|
|
|
|
int page_index = data[0];
|
|
int pages_count = data[1];
|
|
int byte_length = (int)pages_count * NFC_TAG_MF0_NTAG_DATA_SIZE;
|
|
|
|
if (pages_count == 0) return data_frame_make(cmd, STATUS_SUCCESS, 0, NULL);
|
|
else if (
|
|
(page_index >= ((int)nr_pages))
|
|
|| (pages_count > (((int)nr_pages) - page_index))
|
|
|| (((int)length - 2) < byte_length)
|
|
) {
|
|
byte = nr_pages;
|
|
return data_frame_make(cmd, STATUS_INVALID_PARAMS, 1, &byte);
|
|
}
|
|
|
|
tag_data_buffer_t *buffer = get_buffer_by_tag_type(active_slot_tag_types.tag_hf);
|
|
nfc_tag_mf0_ntag_information_t *info = (nfc_tag_mf0_ntag_information_t *)buffer->buffer;
|
|
|
|
memcpy(&info->memory[page_index][0], &data[2], byte_length);
|
|
|
|
return data_frame_make(cmd, STATUS_SUCCESS, 0, NULL);
|
|
}
|
|
|
|
static data_frame_tx_t *cmd_processor_mf0_ntag_read_emu_page_data(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
uint8_t byte;
|
|
uint8_t active_slot = tag_emulation_get_slot();
|
|
|
|
tag_slot_specific_type_t active_slot_tag_types;
|
|
tag_emulation_get_specific_types_by_slot(active_slot, &active_slot_tag_types);
|
|
|
|
int nr_pages = nfc_tag_mf0_ntag_get_nr_pages_by_tag_type(active_slot_tag_types.tag_hf);
|
|
// This means wrong slot type.
|
|
if (nr_pages <= 0) return data_frame_make(cmd, STATUS_INVALID_SLOT_TYPE, 0, data);
|
|
|
|
if (length < 2) {
|
|
byte = nr_pages;
|
|
return data_frame_make(cmd, STATUS_INVALID_PARAMS, 1, &byte);
|
|
}
|
|
|
|
int page_index = data[0];
|
|
int pages_count = data[1];
|
|
|
|
if (pages_count == 0) return data_frame_make(cmd, STATUS_SUCCESS, 0, NULL);
|
|
else if ((page_index >= ((int)nr_pages)) || (pages_count > (((int)nr_pages) - page_index))) {
|
|
byte = nr_pages;
|
|
return data_frame_make(cmd, STATUS_INVALID_PARAMS, 1, &byte);
|
|
}
|
|
|
|
tag_data_buffer_t *buffer = get_buffer_by_tag_type(active_slot_tag_types.tag_hf);
|
|
nfc_tag_mf0_ntag_information_t *info = (nfc_tag_mf0_ntag_information_t *)buffer->buffer;
|
|
|
|
return data_frame_make(cmd, STATUS_SUCCESS, pages_count * NFC_TAG_MF0_NTAG_DATA_SIZE, &info->memory[page_index][0]);
|
|
}
|
|
|
|
static data_frame_tx_t *cmd_processor_mf0_ntag_get_version_data(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
uint8_t *version_data = nfc_tag_mf0_ntag_get_version_data();
|
|
if (version_data == NULL) return data_frame_make(cmd, STATUS_INVALID_SLOT_TYPE, 0, NULL);
|
|
|
|
return data_frame_make(cmd, STATUS_SUCCESS, NFC_TAG_MF0_NTAG_VER_SIZE, version_data);
|
|
}
|
|
|
|
static data_frame_tx_t *cmd_processor_mf0_ntag_set_version_data(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
if (length != 8) return data_frame_make(cmd, STATUS_INVALID_PARAMS, 0, NULL);
|
|
|
|
uint8_t *version_data = nfc_tag_mf0_ntag_get_version_data();
|
|
if (version_data == NULL) return data_frame_make(cmd, STATUS_INVALID_SLOT_TYPE, 0, NULL);
|
|
memcpy(version_data, data, NFC_TAG_MF0_NTAG_VER_SIZE);
|
|
|
|
return data_frame_make(cmd, STATUS_SUCCESS, 0, NULL);
|
|
}
|
|
|
|
static data_frame_tx_t *cmd_processor_mf0_ntag_get_signature_data(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
uint8_t *signature_data = nfc_tag_mf0_ntag_get_signature_data();
|
|
if (signature_data == NULL) return data_frame_make(cmd, STATUS_INVALID_SLOT_TYPE, 0, NULL);
|
|
|
|
return data_frame_make(cmd, STATUS_SUCCESS, NFC_TAG_MF0_NTAG_SIG_SIZE, signature_data);
|
|
}
|
|
|
|
static data_frame_tx_t *cmd_processor_mf0_ntag_set_signature_data(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
if (length != NFC_TAG_MF0_NTAG_SIG_SIZE) return data_frame_make(cmd, STATUS_INVALID_PARAMS, 0, NULL);
|
|
|
|
uint8_t *signature_data = nfc_tag_mf0_ntag_get_signature_data();
|
|
if (signature_data == NULL) return data_frame_make(cmd, STATUS_INVALID_SLOT_TYPE, 0, NULL);
|
|
memcpy(signature_data, data, NFC_TAG_MF0_NTAG_SIG_SIZE);
|
|
|
|
return data_frame_make(cmd, STATUS_SUCCESS, 0, NULL);
|
|
}
|
|
|
|
static data_frame_tx_t *cmd_processor_mf0_ntag_get_counter_data(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
if (length != 1) return data_frame_make(cmd, STATUS_INVALID_PARAMS, 0, NULL);
|
|
|
|
uint8_t index = data[0] & 0x7F;
|
|
uint8_t *counter_data = nfc_tag_mf0_ntag_get_counter_data_by_index(index);
|
|
if (counter_data == NULL) return data_frame_make(cmd, STATUS_INVALID_SLOT_TYPE, 0, NULL);
|
|
|
|
bool tearing = (counter_data[MF0_NTAG_AUTHLIM_OFF_IN_CTR] & MF0_NTAG_AUTHLIM_MASK_IN_CTR) != 0;
|
|
|
|
uint8_t response[4];
|
|
memcpy(response, counter_data, 3);
|
|
response[3] = tearing ? 0x00 : 0xBD;
|
|
|
|
return data_frame_make(cmd, STATUS_SUCCESS, 4, response);
|
|
}
|
|
|
|
static data_frame_tx_t *cmd_processor_mf0_ntag_set_counter_data(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
if (length != 4) return data_frame_make(cmd, STATUS_INVALID_PARAMS, 0, NULL);
|
|
|
|
uint8_t index = data[0] & 0x7F;
|
|
uint8_t *counter_data = nfc_tag_mf0_ntag_get_counter_data_by_index(index);
|
|
if (counter_data == NULL) return data_frame_make(cmd, STATUS_INVALID_SLOT_TYPE, 0, NULL);
|
|
|
|
// clear tearing event flag
|
|
if ((data[0] & 0x80) == 0x80) {
|
|
counter_data[MF0_NTAG_AUTHLIM_OFF_IN_CTR] &= ~MF0_NTAG_TEARING_MASK_IN_AUTHLIM;
|
|
}
|
|
|
|
// copy the actual counter value
|
|
memcpy(counter_data, &data[1], 3);
|
|
|
|
return data_frame_make(cmd, STATUS_SUCCESS, 0, NULL);
|
|
}
|
|
|
|
static data_frame_tx_t *cmd_processor_mf0_ntag_reset_auth_cnt(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
// all tags with counters support auth
|
|
uint8_t *counter_data = nfc_tag_mf0_ntag_get_counter_data_by_index(0);
|
|
if (counter_data == NULL) return data_frame_make(cmd, STATUS_INVALID_SLOT_TYPE, 0, NULL);
|
|
|
|
uint8_t old_value = counter_data[MF0_NTAG_AUTHLIM_OFF_IN_CTR] & MF0_NTAG_AUTHLIM_MASK_IN_CTR;
|
|
counter_data[MF0_NTAG_AUTHLIM_OFF_IN_CTR] &= ~MF0_NTAG_AUTHLIM_MASK_IN_CTR;
|
|
|
|
return data_frame_make(cmd, STATUS_SUCCESS, 1, &old_value);
|
|
}
|
|
|
|
static data_frame_tx_t *cmd_processor_hf14a_set_anti_coll_data(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
// uidlen[1]|uid[uidlen]|atqa[2]|sak[1]|atslen[1]|ats[atslen]
|
|
// dynamic length, so no struct
|
|
if ((length < 1) || \
|
|
(!is_valid_uid_size(data[0])) || \
|
|
(length < 1 + data[0] + 2 + 1 + 1) || \
|
|
(length < 1 + data[0] + 2 + 1 + 1 + data[1 + data[0] + 2 + 1])) {
|
|
return data_frame_make(cmd, STATUS_PAR_ERR, 0, NULL);
|
|
}
|
|
nfc_tag_14a_coll_res_reference_t *info = get_mifare_coll_res();
|
|
uint16_t offset = 0;
|
|
*(info->size) = (nfc_tag_14a_uid_size)data[offset];
|
|
offset++;
|
|
memcpy(info->uid, &data[offset], *(info->size));
|
|
offset += *(info->size);
|
|
memcpy(info->atqa, &data[offset], 2);
|
|
offset += 2;
|
|
info->sak[0] = data[offset];
|
|
offset ++;
|
|
info->ats->length = data[offset];
|
|
offset ++;
|
|
memcpy(info->ats->data, &data[offset], info->ats->length);
|
|
offset += info->ats->length;
|
|
return data_frame_make(cmd, STATUS_SUCCESS, 0, NULL);
|
|
}
|
|
|
|
static data_frame_tx_t *cmd_processor_set_slot_tag_nick(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
if (length < 3 || length > 34) {
|
|
return data_frame_make(cmd, STATUS_PAR_ERR, 0, NULL);
|
|
}
|
|
uint8_t slot = data[0];
|
|
uint8_t sense_type = data[1];
|
|
fds_slot_record_map_t map_info;
|
|
if (slot >= TAG_MAX_SLOT_NUM || (sense_type != TAG_SENSE_HF && sense_type != TAG_SENSE_LF)) {
|
|
return data_frame_make(cmd, STATUS_PAR_ERR, 0, NULL);
|
|
}
|
|
get_fds_map_by_slot_sense_type_for_nick(slot, sense_type, &map_info);
|
|
|
|
uint8_t buffer[36];
|
|
buffer[0] = length - 2;
|
|
memcpy(buffer + 1, data + 2, buffer[0]);
|
|
|
|
bool ret = fds_write_sync(map_info.id, map_info.key, sizeof(buffer), buffer);
|
|
if (!ret) {
|
|
return data_frame_make(cmd, STATUS_FLASH_WRITE_FAIL, 0, NULL);
|
|
}
|
|
return data_frame_make(cmd, STATUS_SUCCESS, 0, NULL);
|
|
}
|
|
|
|
static data_frame_tx_t *cmd_processor_get_slot_tag_nick(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
if (length != 2) {
|
|
return data_frame_make(cmd, STATUS_PAR_ERR, 0, NULL);
|
|
}
|
|
uint8_t slot = data[0];
|
|
uint8_t sense_type = data[1];
|
|
uint8_t buffer[36];
|
|
fds_slot_record_map_t map_info;
|
|
|
|
if (slot >= TAG_MAX_SLOT_NUM || (sense_type != TAG_SENSE_HF && sense_type != TAG_SENSE_LF)) {
|
|
return data_frame_make(cmd, STATUS_PAR_ERR, 0, NULL);
|
|
}
|
|
get_fds_map_by_slot_sense_type_for_nick(slot, sense_type, &map_info);
|
|
uint16_t buffer_length = sizeof(buffer);
|
|
bool ret = fds_read_sync(map_info.id, map_info.key, &buffer_length, buffer);
|
|
if (!ret) {
|
|
return data_frame_make(cmd, STATUS_FLASH_READ_FAIL, 0, NULL);
|
|
}
|
|
return data_frame_make(cmd, STATUS_SUCCESS, buffer[0], &buffer[1]);
|
|
}
|
|
|
|
static data_frame_tx_t *cmd_processor_delete_slot_tag_nick(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
if (length != 2) {
|
|
return data_frame_make(cmd, STATUS_PAR_ERR, 0, NULL);
|
|
}
|
|
uint8_t slot = data[0];
|
|
uint8_t sense_type = data[1];
|
|
fds_slot_record_map_t map_info;
|
|
|
|
if (slot >= TAG_MAX_SLOT_NUM || (sense_type != TAG_SENSE_HF && sense_type != TAG_SENSE_LF)) {
|
|
return data_frame_make(cmd, STATUS_PAR_ERR, 0, NULL);
|
|
}
|
|
get_fds_map_by_slot_sense_type_for_nick(slot, sense_type, &map_info);
|
|
bool ret = fds_delete_sync(map_info.id, map_info.key);
|
|
if (!ret) {
|
|
return data_frame_make(cmd, STATUS_FLASH_WRITE_FAIL, 0, NULL);
|
|
}
|
|
return data_frame_make(cmd, STATUS_SUCCESS, 0, NULL);
|
|
}
|
|
|
|
static data_frame_tx_t *cmd_processor_mf1_get_emulator_config(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
uint8_t mf1_info[5] = {};
|
|
mf1_info[0] = nfc_tag_mf1_is_detection_enable();
|
|
mf1_info[1] = nfc_tag_mf1_is_gen1a_magic_mode();
|
|
mf1_info[2] = nfc_tag_mf1_is_gen2_magic_mode();
|
|
mf1_info[3] = nfc_tag_mf1_is_use_mf1_coll_res();
|
|
mf1_info[4] = nfc_tag_mf1_get_write_mode();
|
|
return data_frame_make(cmd, STATUS_SUCCESS, 5, mf1_info);
|
|
}
|
|
|
|
static data_frame_tx_t *cmd_processor_mf1_get_gen1a_mode(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
uint8_t mode = nfc_tag_mf1_is_gen1a_magic_mode();
|
|
return data_frame_make(cmd, STATUS_SUCCESS, 1, &mode);
|
|
}
|
|
|
|
static data_frame_tx_t *cmd_processor_mf1_set_gen1a_mode(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
if (length != 1 && data[0] > 1) {
|
|
return data_frame_make(cmd, STATUS_PAR_ERR, 0, NULL);
|
|
}
|
|
nfc_tag_mf1_set_gen1a_magic_mode(data[0]);
|
|
return data_frame_make(cmd, STATUS_SUCCESS, 0, NULL);
|
|
}
|
|
|
|
static data_frame_tx_t *cmd_processor_mf1_get_gen2_mode(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
uint8_t mode = nfc_tag_mf1_is_gen2_magic_mode();
|
|
return data_frame_make(cmd, STATUS_SUCCESS, 1, &mode);
|
|
}
|
|
|
|
static data_frame_tx_t *cmd_processor_mf1_set_gen2_mode(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
if (length != 1 && data[0] > 1) {
|
|
return data_frame_make(cmd, STATUS_PAR_ERR, 0, NULL);
|
|
}
|
|
nfc_tag_mf1_set_gen2_magic_mode(data[0]);
|
|
return data_frame_make(cmd, STATUS_SUCCESS, 0, NULL);
|
|
}
|
|
|
|
static data_frame_tx_t *cmd_processor_mf1_get_block_anti_coll_mode(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
uint8_t mode = nfc_tag_mf1_is_use_mf1_coll_res();
|
|
return data_frame_make(cmd, STATUS_SUCCESS, 1, &mode);
|
|
}
|
|
|
|
static data_frame_tx_t *cmd_processor_mf1_set_block_anti_coll_mode(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
if (length != 1 && data[0] > 1) {
|
|
return data_frame_make(cmd, STATUS_PAR_ERR, 0, NULL);
|
|
}
|
|
nfc_tag_mf1_set_use_mf1_coll_res(data[0]);
|
|
return data_frame_make(cmd, STATUS_SUCCESS, 0, NULL);
|
|
}
|
|
|
|
static data_frame_tx_t *cmd_processor_mf1_get_write_mode(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
uint8_t mode = nfc_tag_mf1_get_write_mode();
|
|
return data_frame_make(cmd, STATUS_SUCCESS, 1, &mode);
|
|
}
|
|
|
|
static data_frame_tx_t *cmd_processor_mf1_set_write_mode(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
if (length != 1 || data[0] > 3) {
|
|
return data_frame_make(cmd, STATUS_PAR_ERR, 0, NULL);
|
|
}
|
|
nfc_tag_mf1_set_write_mode(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;
|
|
uint8_t enabled_lf;
|
|
} PACKED payload[8];
|
|
for (uint8_t slot = 0; slot < 8; slot++) {
|
|
payload[slot].enabled_hf = tag_emulation_slot_is_enabled(slot, TAG_SENSE_HF);
|
|
payload[slot].enabled_lf = tag_emulation_slot_is_enabled(slot, TAG_SENSE_LF);
|
|
}
|
|
return data_frame_make(cmd, STATUS_SUCCESS, sizeof(payload), (uint8_t *)&payload);
|
|
}
|
|
|
|
static data_frame_tx_t *cmd_processor_get_ble_connect_key(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
return data_frame_make(cmd, STATUS_SUCCESS, BLE_PAIRING_KEY_LEN, settings_get_ble_connect_key());
|
|
}
|
|
|
|
static data_frame_tx_t *cmd_processor_set_ble_connect_key(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
if (length != BLE_PAIRING_KEY_LEN) {
|
|
return data_frame_make(cmd, STATUS_PAR_ERR, 0, NULL);
|
|
}
|
|
// Must be 6 ASCII characters, can only be 0-9.
|
|
bool is_valid_key = true;
|
|
for (uint8_t i = 0; i < BLE_PAIRING_KEY_LEN; i++) {
|
|
if (data[i] < '0' || data[i] > '9') {
|
|
is_valid_key = false;
|
|
break;
|
|
}
|
|
}
|
|
if (!is_valid_key) {
|
|
return data_frame_make(cmd, STATUS_PAR_ERR, 0, NULL);
|
|
}
|
|
// Key is valid, we can update to config
|
|
settings_set_ble_connect_key(data);
|
|
return data_frame_make(cmd, STATUS_SUCCESS, 0, NULL);
|
|
}
|
|
|
|
static data_frame_tx_t *cmd_processor_delete_all_ble_bonds(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
advertising_stop();
|
|
delete_bonds_all();
|
|
return data_frame_make(cmd, STATUS_SUCCESS, 0, NULL);
|
|
}
|
|
|
|
#if defined(PROJECT_CHAMELEON_ULTRA)
|
|
|
|
|
|
/**
|
|
* before reader run, reset reader and on antenna,
|
|
* we must to wait some time, to init picc(power).
|
|
*/
|
|
static data_frame_tx_t *before_reader_run(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);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/**
|
|
* before reader run, reset reader and on antenna,
|
|
* we must to wait some time, to init picc(power).
|
|
*/
|
|
static data_frame_tx_t *before_hf_reader_run(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
data_frame_tx_t *ret = before_reader_run(cmd, status, length, data);
|
|
if (ret == NULL) {
|
|
pcd_14a_reader_reset();
|
|
pcd_14a_reader_antenna_on();
|
|
bsp_delay_ms(8);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* after reader run, off antenna, to keep battery.
|
|
*/
|
|
static data_frame_tx_t *after_hf_reader_run(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
pcd_14a_reader_antenna_off();
|
|
return NULL;
|
|
}
|
|
|
|
#endif
|
|
|
|
// fct will be defined after m_data_cmd_map because we need to know its size
|
|
data_frame_tx_t *cmd_processor_get_device_capabilities(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data);
|
|
|
|
static data_frame_tx_t *cmd_processor_mf0_ntag_get_uid_mode(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
int rc = nfc_tag_mf0_ntag_get_uid_mode();
|
|
|
|
if (rc < 0) return data_frame_make(cmd, STATUS_PAR_ERR, 0, NULL);
|
|
else {
|
|
uint8_t res = rc;
|
|
return data_frame_make(cmd, STATUS_SUCCESS, 1, &res);
|
|
}
|
|
}
|
|
|
|
static data_frame_tx_t *cmd_processor_mf0_ntag_set_uid_mode(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
if (length != 1 || !nfc_tag_mf0_ntag_set_uid_mode(data[0] != 0)) {
|
|
return data_frame_make(cmd, STATUS_PAR_ERR, 0, NULL);
|
|
}
|
|
|
|
return data_frame_make(cmd, STATUS_SUCCESS, 0, NULL);
|
|
}
|
|
|
|
/**
|
|
* (cmd -> processor) function map, the map struct is:
|
|
* cmd code before process cmd processor after process
|
|
*/
|
|
static cmd_data_map_t m_data_cmd_map[] = {
|
|
{ DATA_CMD_GET_APP_VERSION, NULL, cmd_processor_get_app_version, NULL },
|
|
{ DATA_CMD_CHANGE_DEVICE_MODE, NULL, cmd_processor_change_device_mode, NULL },
|
|
{ DATA_CMD_GET_DEVICE_MODE, NULL, cmd_processor_get_device_mode, NULL },
|
|
{ DATA_CMD_SET_ACTIVE_SLOT, NULL, cmd_processor_set_active_slot, NULL },
|
|
{ DATA_CMD_SET_SLOT_TAG_TYPE, NULL, cmd_processor_set_slot_tag_type, NULL },
|
|
{ DATA_CMD_SET_SLOT_DATA_DEFAULT, NULL, cmd_processor_set_slot_data_default, NULL },
|
|
{ DATA_CMD_SET_SLOT_ENABLE, NULL, cmd_processor_set_slot_enable, NULL },
|
|
{ DATA_CMD_SET_SLOT_TAG_NICK, NULL, cmd_processor_set_slot_tag_nick, NULL },
|
|
{ DATA_CMD_GET_SLOT_TAG_NICK, NULL, cmd_processor_get_slot_tag_nick, NULL },
|
|
{ DATA_CMD_SLOT_DATA_CONFIG_SAVE, NULL, cmd_processor_slot_data_config_save, NULL },
|
|
{ DATA_CMD_ENTER_BOOTLOADER, NULL, cmd_processor_enter_bootloader, NULL },
|
|
{ DATA_CMD_GET_DEVICE_CHIP_ID, NULL, cmd_processor_get_device_chip_id, NULL },
|
|
{ DATA_CMD_GET_DEVICE_ADDRESS, NULL, cmd_processor_get_device_address, NULL },
|
|
{ DATA_CMD_SAVE_SETTINGS, NULL, cmd_processor_save_settings, NULL },
|
|
{ DATA_CMD_RESET_SETTINGS, NULL, cmd_processor_reset_settings, NULL },
|
|
{ DATA_CMD_SET_ANIMATION_MODE, NULL, cmd_processor_set_animation_mode, NULL },
|
|
{ DATA_CMD_GET_ANIMATION_MODE, NULL, cmd_processor_get_animation_mode, NULL },
|
|
{ DATA_CMD_GET_GIT_VERSION, NULL, cmd_processor_get_git_version, NULL },
|
|
{ DATA_CMD_GET_ACTIVE_SLOT, NULL, cmd_processor_get_active_slot, NULL },
|
|
{ DATA_CMD_GET_SLOT_INFO, NULL, cmd_processor_get_slot_info, NULL },
|
|
{ DATA_CMD_WIPE_FDS, NULL, cmd_processor_wipe_fds, NULL },
|
|
{ DATA_CMD_DELETE_SLOT_TAG_NICK, NULL, cmd_processor_delete_slot_tag_nick, NULL },
|
|
{ DATA_CMD_GET_ENABLED_SLOTS, NULL, cmd_processor_get_enabled_slots, NULL },
|
|
{ DATA_CMD_DELETE_SLOT_SENSE_TYPE, NULL, cmd_processor_delete_slot_sense_type, NULL },
|
|
{ DATA_CMD_GET_BATTERY_INFO, NULL, cmd_processor_get_battery_info, NULL },
|
|
{ DATA_CMD_GET_BUTTON_PRESS_CONFIG, NULL, cmd_processor_get_button_press_config, NULL },
|
|
{ DATA_CMD_SET_BUTTON_PRESS_CONFIG, NULL, cmd_processor_set_button_press_config, NULL },
|
|
{ DATA_CMD_GET_LONG_BUTTON_PRESS_CONFIG, NULL, cmd_processor_get_long_button_press_config, NULL },
|
|
{ DATA_CMD_SET_LONG_BUTTON_PRESS_CONFIG, NULL, cmd_processor_set_long_button_press_config, NULL },
|
|
{ DATA_CMD_GET_BLE_PAIRING_KEY, NULL, cmd_processor_get_ble_connect_key, NULL },
|
|
{ DATA_CMD_SET_BLE_PAIRING_KEY, NULL, cmd_processor_set_ble_connect_key, NULL },
|
|
{ DATA_CMD_DELETE_ALL_BLE_BONDS, NULL, cmd_processor_delete_all_ble_bonds, NULL },
|
|
{ DATA_CMD_GET_DEVICE_MODEL, NULL, cmd_processor_get_device_model, NULL },
|
|
{ DATA_CMD_GET_DEVICE_SETTINGS, NULL, cmd_processor_get_device_settings, NULL },
|
|
{ DATA_CMD_GET_DEVICE_CAPABILITIES, NULL, cmd_processor_get_device_capabilities, NULL },
|
|
{ DATA_CMD_GET_BLE_PAIRING_ENABLE, NULL, cmd_processor_get_ble_pairing_enable, NULL },
|
|
{ DATA_CMD_SET_BLE_PAIRING_ENABLE, NULL, cmd_processor_set_ble_pairing_enable, NULL },
|
|
|
|
#if defined(PROJECT_CHAMELEON_ULTRA)
|
|
|
|
{ DATA_CMD_HF14A_SCAN, before_hf_reader_run, cmd_processor_hf14a_scan, after_hf_reader_run },
|
|
{ DATA_CMD_MF1_DETECT_SUPPORT, before_hf_reader_run, cmd_processor_mf1_detect_support, after_hf_reader_run },
|
|
{ DATA_CMD_MF1_DETECT_PRNG, before_hf_reader_run, cmd_processor_mf1_detect_prng, after_hf_reader_run },
|
|
{ DATA_CMD_MF1_STATIC_NESTED_ACQUIRE, before_hf_reader_run, cmd_processor_mf1_static_nested_acquire, after_hf_reader_run },
|
|
{ DATA_CMD_MF1_DARKSIDE_ACQUIRE, before_hf_reader_run, cmd_processor_mf1_darkside_acquire, after_hf_reader_run },
|
|
{ DATA_CMD_MF1_DETECT_NT_DIST, before_hf_reader_run, cmd_processor_mf1_detect_nt_dist, after_hf_reader_run },
|
|
{ DATA_CMD_MF1_NESTED_ACQUIRE, before_hf_reader_run, cmd_processor_mf1_nested_acquire, after_hf_reader_run },
|
|
|
|
{ DATA_CMD_MF1_AUTH_ONE_KEY_BLOCK, before_hf_reader_run, cmd_processor_mf1_auth_one_key_block, after_hf_reader_run },
|
|
{ DATA_CMD_MF1_READ_ONE_BLOCK, before_hf_reader_run, cmd_processor_mf1_read_one_block, after_hf_reader_run },
|
|
{ DATA_CMD_MF1_WRITE_ONE_BLOCK, before_hf_reader_run, cmd_processor_mf1_write_one_block, after_hf_reader_run },
|
|
{ DATA_CMD_HF14A_RAW, before_reader_run, cmd_processor_hf14a_raw, NULL },
|
|
{ DATA_CMD_MF1_MANIPULATE_VALUE_BLOCK, before_hf_reader_run, cmd_processor_mf1_manipulate_value_block, after_hf_reader_run },
|
|
{ DATA_CMD_MF1_CHECK_KEYS_OF_SECTORS, before_hf_reader_run, cmd_processor_mf1_check_keys_of_sectors, after_hf_reader_run },
|
|
|
|
{ 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 },
|
|
|
|
#endif
|
|
|
|
{ DATA_CMD_MF1_WRITE_EMU_BLOCK_DATA, NULL, cmd_processor_mf1_write_emu_block_data, NULL },
|
|
{ DATA_CMD_HF14A_SET_ANTI_COLL_DATA, NULL, cmd_processor_hf14a_set_anti_coll_data, NULL },
|
|
|
|
{ DATA_CMD_MF1_SET_DETECTION_ENABLE, NULL, cmd_processor_mf1_set_detection_enable, NULL },
|
|
{ DATA_CMD_MF1_GET_DETECTION_COUNT, NULL, cmd_processor_mf1_get_detection_count, NULL },
|
|
{ DATA_CMD_MF1_GET_DETECTION_LOG, NULL, cmd_processor_mf1_get_detection_log, NULL },
|
|
{ DATA_CMD_MF1_GET_DETECTION_ENABLE, NULL, cmd_processor_mf1_get_detection_enable, NULL },
|
|
{ DATA_CMD_MF1_READ_EMU_BLOCK_DATA, NULL, cmd_processor_mf1_read_emu_block_data, NULL },
|
|
{ DATA_CMD_MF1_GET_EMULATOR_CONFIG, NULL, cmd_processor_mf1_get_emulator_config, NULL },
|
|
{ DATA_CMD_MF1_GET_GEN1A_MODE, NULL, cmd_processor_mf1_get_gen1a_mode, NULL },
|
|
{ DATA_CMD_MF1_SET_GEN1A_MODE, NULL, cmd_processor_mf1_set_gen1a_mode, NULL },
|
|
{ DATA_CMD_MF1_GET_GEN2_MODE, NULL, cmd_processor_mf1_get_gen2_mode, NULL },
|
|
{ DATA_CMD_MF1_SET_GEN2_MODE, NULL, cmd_processor_mf1_set_gen2_mode, NULL },
|
|
{ DATA_CMD_MF1_GET_BLOCK_ANTI_COLL_MODE, NULL, cmd_processor_mf1_get_block_anti_coll_mode, NULL },
|
|
{ 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_HF14A_GET_ANTI_COLL_DATA, NULL, cmd_processor_hf14a_get_anti_coll_data, 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 },
|
|
{ DATA_CMD_MF0_NTAG_READ_EMU_PAGE_DATA, NULL, cmd_processor_mf0_ntag_read_emu_page_data, NULL },
|
|
{ DATA_CMD_MF0_NTAG_WRITE_EMU_PAGE_DATA, NULL, cmd_processor_mf0_ntag_write_emu_page_data, NULL },
|
|
{ DATA_CMD_MF0_NTAG_GET_VERSION_DATA, NULL, cmd_processor_mf0_ntag_get_version_data, NULL },
|
|
{ DATA_CMD_MF0_NTAG_SET_VERSION_DATA, NULL, cmd_processor_mf0_ntag_set_version_data, NULL },
|
|
{ DATA_CMD_MF0_NTAG_GET_SIGNATURE_DATA, NULL, cmd_processor_mf0_ntag_get_signature_data, NULL },
|
|
{ DATA_CMD_MF0_NTAG_SET_SIGNATURE_DATA, NULL, cmd_processor_mf0_ntag_set_signature_data, NULL },
|
|
{ DATA_CMD_MF0_NTAG_GET_COUNTER_DATA, NULL, cmd_processor_mf0_ntag_get_counter_data, NULL },
|
|
{ DATA_CMD_MF0_NTAG_SET_COUNTER_DATA, NULL, cmd_processor_mf0_ntag_set_counter_data, NULL },
|
|
{ DATA_CMD_MF0_NTAG_RESET_AUTH_CNT, NULL, cmd_processor_mf0_ntag_reset_auth_cnt, NULL },
|
|
|
|
{ DATA_CMD_EM410X_SET_EMU_ID, NULL, cmd_processor_em410x_set_emu_id, NULL },
|
|
{ DATA_CMD_EM410X_GET_EMU_ID, NULL, cmd_processor_em410x_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) {
|
|
size_t count = ARRAYLEN(m_data_cmd_map);
|
|
uint16_t commands[count];
|
|
memset(commands, 0, count * sizeof(uint16_t));
|
|
|
|
for (size_t i = 0; i < count; i++) {
|
|
commands[i] = U16HTONS(m_data_cmd_map[i].cmd);
|
|
}
|
|
|
|
return data_frame_make(cmd, STATUS_SUCCESS, count * sizeof(uint16_t), (uint8_t *)commands);
|
|
}
|
|
|
|
/**
|
|
* @brief Auto select source to response
|
|
*
|
|
* @param resp data
|
|
*/
|
|
static void auto_response_data(data_frame_tx_t *resp) {
|
|
// TODO Please select the reply source automatically according to the message source,
|
|
// and do not reply by checking the validity of the link layer by layer
|
|
if (is_usb_working()) {
|
|
usb_cdc_write(resp->buffer, resp->length);
|
|
} else if (is_nus_working()) {
|
|
nus_data_response(resp->buffer, resp->length);
|
|
} else {
|
|
NRF_LOG_ERROR("No connection valid found at response client.");
|
|
}
|
|
}
|
|
|
|
|
|
/**@brief Function to process data frame(cmd)
|
|
*/
|
|
void on_data_frame_received(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
|
data_frame_tx_t *response = NULL;
|
|
bool is_cmd_support = false;
|
|
for (int i = 0; i < ARRAY_SIZE(m_data_cmd_map); i++) {
|
|
if (m_data_cmd_map[i].cmd == cmd) {
|
|
is_cmd_support = true;
|
|
if (m_data_cmd_map[i].cmd_before != NULL) {
|
|
data_frame_tx_t *before_resp = m_data_cmd_map[i].cmd_before(cmd, status, length, data);
|
|
if (before_resp != NULL) {
|
|
// some problem found before run cmd.
|
|
response = before_resp;
|
|
break;
|
|
}
|
|
}
|
|
if (m_data_cmd_map[i].cmd_processor != NULL) response = m_data_cmd_map[i].cmd_processor(cmd, status, length, data);
|
|
if (m_data_cmd_map[i].cmd_after != NULL) {
|
|
data_frame_tx_t *after_resp = m_data_cmd_map[i].cmd_after(cmd, status, length, data);
|
|
if (after_resp != NULL) {
|
|
// some problem found after run cmd.
|
|
response = after_resp;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (is_cmd_support) {
|
|
// check and response
|
|
if (response != NULL) {
|
|
auto_response_data(response);
|
|
}
|
|
} else {
|
|
// response cmd unsupported.
|
|
response = data_frame_make(cmd, STATUS_INVALID_CMD, 0, NULL);
|
|
auto_response_data(response);
|
|
NRF_LOG_INFO("Data frame cmd invalid: %d,", cmd);
|
|
}
|
|
}
|