[FL-1063} LF-RFID Cli (#515)

* App lfrfid: update emulator to process external data.
* App lfrfid: cleanup emulator
* App lfrfid: cli interface
* Lib: arguments parser lib

Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
SG 2021-06-10 21:53:59 +10:00 committed by GitHub
parent ced7d6315d
commit 4ad5245969
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 372 additions and 63 deletions

View file

@ -45,6 +45,7 @@ void irda_cli_init();
void nfc_cli_init();
void subghz_cli_init();
void bt_cli_init();
void lfrfid_cli_init();
const FlipperApplication FLIPPER_SERVICES[] = {
#ifdef SRV_CLI
@ -211,6 +212,9 @@ const FlipperOnStartHook FLIPPER_ON_SYSTEM_START[] = {
#ifdef APP_SUBGHZ
subghz_cli_init,
#endif
#ifdef APP_LF_RFID
lfrfid_cli_init,
#endif
#ifdef SRV_BT
bt_cli_init,
#endif

View file

@ -0,0 +1,33 @@
#include "key-info.h"
const char* lfrfid_key_get_type_string(LfrfidKeyType type) {
switch(type) {
case LfrfidKeyType::KeyEM4100:
return "EM4100";
break;
case LfrfidKeyType::KeyH10301:
return "H10301";
break;
case LfrfidKeyType::KeyI40134:
return "I40134";
break;
}
return "Unknown";
}
uint8_t lfrfid_key_get_type_data_count(LfrfidKeyType type) {
switch(type) {
case LfrfidKeyType::KeyEM4100:
return 5;
break;
case LfrfidKeyType::KeyH10301:
return 3;
break;
case LfrfidKeyType::KeyI40134:
return 3;
break;
}
return 0;
}

View file

@ -4,7 +4,10 @@
static const uint8_t LFRFID_KEY_SIZE = 8;
enum class LfrfidKeyType : uint8_t {
KeyEmarine,
KeyHID,
KeyIndala,
};
KeyEM4100,
KeyH10301,
KeyI40134,
};
const char* lfrfid_key_get_type_string(LfrfidKeyType type);
uint8_t lfrfid_key_get_type_data_count(LfrfidKeyType type);

View file

@ -71,12 +71,12 @@ bool RfidReader::read(LfrfidKeyType* type, uint8_t* data, uint8_t data_size) {
bool result = false;
if(decoder_em.read(data, data_size)) {
*type = LfrfidKeyType::KeyEmarine;
*type = LfrfidKeyType::KeyEM4100;
result = true;
}
if(decoder_hid26.read(data, data_size)) {
*type = LfrfidKeyType::KeyHID;
*type = LfrfidKeyType::KeyH10301;
result = true;
}

View file

@ -6,7 +6,7 @@ RfidTimerEmulator::RfidTimerEmulator() {
}
RfidTimerEmulator::~RfidTimerEmulator() {
std::map<Type, EncoderGeneric*>::iterator it;
std::map<LfrfidKeyType, EncoderGeneric*>::iterator it;
for(it = encoders.begin(); it != encoders.end(); ++it) {
delete it->second;
@ -14,38 +14,28 @@ RfidTimerEmulator::~RfidTimerEmulator() {
}
}
void RfidTimerEmulator::start(Type type) {
void RfidTimerEmulator::start(LfrfidKeyType type, const uint8_t* data, uint8_t data_size) {
if(encoders.count(type)) {
current_encoder = encoders.find(type)->second;
uint8_t em_data[5] = {0x53, 0x00, 0x5F, 0xB3, 0xC2};
uint8_t hid_data[3] = {0xED, 0x87, 0x70};
uint8_t indala_data[3] = {0x1F, 0x2E, 0x3D};
switch(type) {
case Type::EM:
current_encoder->init(em_data, 5);
break;
case Type::HID_H10301:
current_encoder->init(hid_data, 3);
break;
case Type::Indala_40134:
current_encoder->init(indala_data, 3);
break;
if(lfrfid_key_get_type_data_count(type) == data_size) {
current_encoder->init(data, data_size);
api_hal_rfid_tim_emulate(125000);
api_hal_rfid_pins_emulate();
api_interrupt_add(timer_update_callback, InterruptTypeTimerUpdate, this);
// TODO make api for interrupts priority
for(size_t i = WWDG_IRQn; i <= DMAMUX1_OVR_IRQn; i++) {
HAL_NVIC_SetPriority(static_cast<IRQn_Type>(i), 15, 0);
}
HAL_NVIC_SetPriority(TIM1_UP_TIM16_IRQn, 5, 0);
HAL_NVIC_EnableIRQ(TIM1_UP_TIM16_IRQn);
api_hal_rfid_tim_emulate_start();
}
api_hal_rfid_tim_emulate(125000);
api_hal_rfid_pins_emulate();
api_interrupt_add(timer_update_callback, InterruptTypeTimerUpdate, this);
for(size_t i = WWDG_IRQn; i <= DMAMUX1_OVR_IRQn; i++) {
HAL_NVIC_SetPriority(static_cast<IRQn_Type>(i), 15, 0);
}
HAL_NVIC_SetPriority(TIM1_UP_TIM16_IRQn, 5, 0);
HAL_NVIC_EnableIRQ(TIM1_UP_TIM16_IRQn);
api_hal_rfid_tim_emulate_start();
} else {
// not found
}

View file

@ -10,24 +10,18 @@
class RfidTimerEmulator {
public:
enum class Type : uint8_t {
EM,
HID_H10301,
Indala_40134,
};
RfidTimerEmulator();
~RfidTimerEmulator();
void start(Type type);
void start(LfrfidKeyType type, const uint8_t* data, uint8_t data_size);
void stop();
private:
EncoderGeneric* current_encoder = nullptr;
std::map<Type, EncoderGeneric*> encoders = {
{Type::EM, new EncoderEM()},
{Type::HID_H10301, new EncoderHID_H10301()},
{Type::Indala_40134, new EncoderIndala_40134()},
std::map<LfrfidKeyType, EncoderGeneric*> encoders = {
{LfrfidKeyType::KeyEM4100, new EncoderEM()},
{LfrfidKeyType::KeyH10301, new EncoderHID_H10301()},
{LfrfidKeyType::KeyI40134, new EncoderIndala_40134()},
};
PulseJoiner pulse_joiner;

View file

@ -0,0 +1,142 @@
#include <furi.h>
#include <api-hal.h>
#include <stdarg.h>
#include <cli/cli.h>
#include <args.h>
#include "helpers/rfid-reader.h"
#include "helpers/rfid-timer-emulator.h"
void lfrfid_cli(Cli* cli, string_t args, void* context);
// app cli function
extern "C" void lfrfid_cli_init() {
Cli* cli = static_cast<Cli*>(furi_record_open("cli"));
cli_add_command(cli, "rfid", lfrfid_cli, NULL);
furi_record_close("cli");
}
void lfrfid_cli_print_usage() {
printf("Usage:\r\n");
printf("rfid read\r\n");
printf("rfid <write | emulate> <key_type> <key_data>\r\n");
printf("\t<key_type> choose from:\r\n");
printf("\tEM4100, EM-Marin (5 bytes key_data)\r\n");
printf("\tH10301, HID26 (3 bytes key_data)\r\n");
printf("\tI40134, Indala (3 bytes key_data)\r\n");
printf("\t<key_data> are hex-formatted\r\n");
};
bool lfrfid_cli_get_key_type(string_t data, LfrfidKeyType* type) {
bool result = false;
if(string_cmp_str(data, "EM4100") == 0 || string_cmp_str(data, "EM-Marin") == 0) {
result = true;
*type = LfrfidKeyType::KeyEM4100;
} else if(string_cmp_str(data, "H10301") == 0 || string_cmp_str(data, "HID26") == 0) {
result = true;
*type = LfrfidKeyType::KeyH10301;
} else if(string_cmp_str(data, "I40134") == 0 || string_cmp_str(data, "Indala") == 0) {
result = true;
*type = LfrfidKeyType::KeyI40134;
}
return result;
}
void lfrfid_cli_read(Cli* cli) {
RfidReader reader;
reader.start(RfidReader::Type::Normal);
static const uint8_t data_size = LFRFID_KEY_SIZE;
uint8_t data[data_size] = {0};
LfrfidKeyType type;
printf("Reading RFID...\r\nPress Ctrl+C to abort\r\n");
while(!cli_cmd_interrupt_received(cli)) {
if(reader.read(&type, data, data_size)) {
printf(lfrfid_key_get_type_string(type));
printf(" ");
for(uint8_t i = 0; i < lfrfid_key_get_type_data_count(type); i++) {
printf("%02X", data[i]);
}
printf("\r\n");
break;
}
delay(100);
}
printf("Reading stopped\r\n");
reader.stop();
}
void lfrfid_cli_write(Cli* cli, string_t args) {
// TODO implement rfid write
printf("Not implemented :(\r\n");
}
void lfrfid_cli_emulate(Cli* cli, string_t args) {
string_t data;
string_init(data);
RfidTimerEmulator emulator;
static const uint8_t data_size = LFRFID_KEY_SIZE;
uint8_t key_data[data_size] = {0};
uint8_t key_data_size = 0;
LfrfidKeyType type;
if(!args_read_string_and_trim(args, data)) {
lfrfid_cli_print_usage();
string_clear(data);
return;
}
if(!lfrfid_cli_get_key_type(data, &type)) {
lfrfid_cli_print_usage();
string_clear(data);
return;
}
key_data_size = lfrfid_key_get_type_data_count(type);
if(!args_read_hex_bytes(args, key_data, key_data_size)) {
lfrfid_cli_print_usage();
string_clear(data);
return;
}
emulator.start(type, key_data, key_data_size);
printf("Emulating RFID...\r\nPress Ctrl+C to abort\r\n");
while(!cli_cmd_interrupt_received(cli)) {
delay(100);
}
printf("Emulation stopped\r\n");
emulator.stop();
string_clear(data);
}
void lfrfid_cli(Cli* cli, string_t args, void* context) {
string_t cmd;
string_init(cmd);
if(!args_read_string_and_trim(args, cmd)) {
string_clear(cmd);
lfrfid_cli_print_usage();
return;
}
if(string_cmp_str(cmd, "read") == 0) {
lfrfid_cli_read(cli);
} else if(string_cmp_str(cmd, "write") == 0) {
lfrfid_cli_write(cli, args);
} else if(string_cmp_str(cmd, "emulate") == 0) {
lfrfid_cli_emulate(cli, args);
} else {
lfrfid_cli_print_usage();
}
string_clear(cmd);
}

View file

@ -14,7 +14,7 @@ void LfrfidSceneEmulateEMMarine::on_enter(LfrfidApp* app) {
popup_set_text(popup, app->get_text_store(), 64, 22, AlignCenter, AlignTop);
view_manager->switch_to(LfrfidAppViewManager::ViewType::Popup);
app->get_emulator()->start(RfidTimerEmulator::Type::EM);
app->get_emulator()->start(LfrfidKeyType::KeyEM4100, data, 5);
}
bool LfrfidSceneEmulateEMMarine::on_event(LfrfidApp* app, LfrfidEvent* event) {

View file

@ -9,4 +9,5 @@ public:
void on_exit(LfrfidApp* app) final;
private:
const uint8_t data[5] = {0x53, 0x00, 0x5F, 0xB3, 0xC2};
};

View file

@ -14,7 +14,7 @@ void LfrfidSceneEmulateHID::on_enter(LfrfidApp* app) {
popup_set_text(popup, app->get_text_store(), 64, 22, AlignCenter, AlignTop);
view_manager->switch_to(LfrfidAppViewManager::ViewType::Popup);
app->get_emulator()->start(RfidTimerEmulator::Type::HID_H10301);
app->get_emulator()->start(LfrfidKeyType::KeyH10301, data, 3);
}
bool LfrfidSceneEmulateHID::on_event(LfrfidApp* app, LfrfidEvent* event) {

View file

@ -9,4 +9,5 @@ public:
void on_exit(LfrfidApp* app) final;
private:
const uint8_t data[3] = {0xED, 0x87, 0x70};
};

View file

@ -14,7 +14,7 @@ void LfrfidSceneEmulateIndala::on_enter(LfrfidApp* app) {
popup_set_text(popup, app->get_text_store(), 64, 22, AlignCenter, AlignTop);
view_manager->switch_to(LfrfidAppViewManager::ViewType::Popup);
app->get_emulator()->start(RfidTimerEmulator::Type::Indala_40134);
app->get_emulator()->start(LfrfidKeyType::KeyI40134, data, 3);
}
bool LfrfidSceneEmulateIndala::on_event(LfrfidApp* app, LfrfidEvent* event) {

View file

@ -9,4 +9,5 @@ public:
void on_exit(LfrfidApp* app) final;
private:
const uint8_t data[3] = {0x1F, 0x2E, 0x3D};
};

View file

@ -35,7 +35,7 @@ bool LfrfidSceneReadNormal::on_event(LfrfidApp* app, LfrfidEvent* event) {
}
switch(type) {
case LfrfidKeyType::KeyEmarine:
case LfrfidKeyType::KeyEM4100:
app->set_text_store(
"[EM] %02X %02X %02X %02X %02X\n"
"count: %u",
@ -46,7 +46,7 @@ bool LfrfidSceneReadNormal::on_event(LfrfidApp* app, LfrfidEvent* event) {
data[4],
success_reads);
break;
case LfrfidKeyType::KeyHID:
case LfrfidKeyType::KeyH10301:
app->set_text_store(
"[HID26] %02X %02X %02X\n"
"count: %u",
@ -55,7 +55,7 @@ bool LfrfidSceneReadNormal::on_event(LfrfidApp* app, LfrfidEvent* event) {
data[2],
success_reads);
break;
case LfrfidKeyType::KeyIndala:
case LfrfidKeyType::KeyI40134:
app->set_text_store(
"[IND] %02X %02X %02X\n"
"count: %u",

View file

@ -38,7 +38,7 @@ bool LfrfidSceneWrite::on_event(LfrfidApp* app, LfrfidEvent* event) {
LfrfidKeyType type;
app->get_reader()->read(&type, data, LFRFID_KEY_SIZE);
if(type == LfrfidKeyType::KeyEmarine) {
if(type == LfrfidKeyType::KeyEM4100) {
if(memcmp(em_data, data, 5) == 0) {
readed = true;
}

76
lib/args/args.c Normal file
View file

@ -0,0 +1,76 @@
#include "args.h"
size_t args_get_first_word_length(string_t args) {
size_t ws = string_search_char(args, ' ');
if(ws == STRING_FAILURE) {
ws = strlen(string_get_cstr(args));
}
return ws;
}
size_t args_length(string_t args) {
return strlen(string_get_cstr(args));
}
bool args_read_string_and_trim(string_t args, string_t word) {
size_t cmd_length = args_get_first_word_length(args);
if(cmd_length == 0) {
return false;
}
string_set_n(word, args, 0, cmd_length);
string_right(args, cmd_length);
string_strim(args);
return true;
}
bool args_char_to_hex_nibble(char c, uint8_t* nibble) {
if((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f')) {
if((c >= '0' && c <= '9')) {
*nibble = c - '0';
} else if((c >= 'A' && c <= 'F')) {
*nibble = c - 'A' + 10;
} else {
*nibble = c - 'a' + 10;
}
return true;
} else {
return false;
}
}
bool args_char_to_hex(char hi_nibble, char low_nibble, uint8_t* byte) {
uint8_t hi_nibble_value = 0;
uint8_t low_nibble_value = 0;
bool result = false;
if(args_char_to_hex_nibble(hi_nibble, &hi_nibble_value)) {
if(args_char_to_hex_nibble(low_nibble, &low_nibble_value)) {
result = true;
*byte = (hi_nibble_value << 4) | low_nibble_value;
}
}
return result;
}
bool args_read_hex_bytes(string_t args, uint8_t* bytes, uint8_t bytes_count) {
bool result = true;
const char* str_pointer = string_get_cstr(args);
if(args_get_first_word_length(args) == (bytes_count * 2)) {
for(uint8_t i = 0; i < bytes_count; i++) {
if(!args_char_to_hex(str_pointer[i * 2], str_pointer[i * 2 + 1], &(bytes[i]))) {
result = false;
break;
}
}
} else {
result = false;
}
return result;
}

70
lib/args/args.h Normal file
View file

@ -0,0 +1,70 @@
#pragma once
#include "m-string.h"
#include "stdint.h"
#include "stdbool.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Extract first word from arguments string and trim arguments string
*
* @param args arguments string
* @param word first word, output
* @return true - success
* @return false - arguments string does not contain anything
*/
bool args_read_string_and_trim(string_t args, string_t word);
/**
* @brief Convert hex ASCII values to byte array
*
* @param args arguments string
* @param bytes byte array pointer, output
* @param bytes_count needed bytes count
* @return true - success
* @return false - arguments string does not contain enough values, or contain non-hex ASCII values
*/
bool args_read_hex_bytes(string_t args, uint8_t* bytes, uint8_t bytes_count);
/************************************ HELPERS ***************************************/
/**
* @brief Get length of first word from arguments string
*
* @param args arguments string
* @return size_t length of first word
*/
size_t args_get_first_word_length(string_t args);
/**
* @brief Get length of arguments string
*
* @param args arguments string
* @return size_t length of arguments string
*/
size_t args_length(string_t args);
/**
* @brief Convert ASCII hex value to nibble
*
* @param c ASCII character
* @param nibble nibble pointer, output
* @return bool conversion status
*/
bool args_char_to_hex_nibble(char c, uint8_t* nibble);
/**
* @brief Convert ASCII hex values to byte
*
* @param hi_nibble ASCII hi nibble character
* @param low_nibble ASCII low nibble character
* @param byte byte pointer, output
* @return bool conversion status
*/
bool args_char_to_hex(char hi_nibble, char low_nibble, uint8_t* byte);
#ifdef __cplusplus
}
#endif

View file

@ -66,15 +66,6 @@ CFLAGS += -I$(LIB_DIR)/app-template
CFLAGS += -I$(LIB_DIR)/fnv1a-hash
C_SOURCES += $(LIB_DIR)/fnv1a-hash/fnv1a-hash.c
# build onewire/cyfral library only if
# we build iButton application
ifeq ($(APP_IBUTTON), 1)
# onewire library
APP_ONEWIRE = 1
endif
APP_ONEWIRE ?= 0
ifeq ($(APP_ONEWIRE), 1)
# onewire library
ONEWIRE_DIR = $(LIB_DIR)/onewire
CFLAGS += -I$(ONEWIRE_DIR)
@ -84,7 +75,6 @@ CPP_SOURCES += $(wildcard $(ONEWIRE_DIR)/*.cpp)
CYFRAL_DIR = $(LIB_DIR)/cyfral
CFLAGS += -I$(CYFRAL_DIR)
CPP_SOURCES += $(wildcard $(CYFRAL_DIR)/*.cpp)
endif
# common apps api
CFLAGS += -I$(LIB_DIR)/common-api
@ -101,3 +91,7 @@ C_SOURCES += $(LIB_DIR)/version/version.c
CFLAGS += -I$(LIB_DIR)/irda
C_SOURCES += $(wildcard $(LIB_DIR)/irda/*.c)
C_SOURCES += $(wildcard $(LIB_DIR)/irda/*/*.c)
#args lib
CFLAGS += -I$(LIB_DIR)/args
C_SOURCES += $(wildcard $(LIB_DIR)/args/*.c)