[FL-2529][FL-1628] New LF-RFID subsystem (#1601)
* Makefile: unit tests pack * RFID: pulse joiner and its unit test * Move pulse protocol helpers to appropriate place * Drop pulse_joiner tests * Generic protocol, protocols dictionary, unit test * Protocol dict unit test * iButton: protocols dictionary * Lib: varint * Lib: profiler * Unit test: varint * rfid: worker mockup * LFRFID: em4100 unit test * Storage: file_exist function * rfid: fsk osc * rfid: generic fsk demodulator * rfid: protocol em4100 * rfid: protocol h10301 * rfid: protocol io prox xsf * Unit test: rfid protocols * rfid: new hal * rfid: raw worker * Unit test: fix error output * rfid: worker * rfid: plain c cli * fw: migrate to scons * lfrfid: full io prox support * unit test: io prox protocol * SubGHZ: move bit defines to source * FSK oscillator: level duration compability * libs: bit manipulation library * lfrfid: ioprox protocol, use bit library and new level duration method of FSK ocillator * bit lib: unit tests * Bit lib: parity tests, remove every nth bit, copy bits * Lfrfid: awid protocol * bit lib: uint16 and uint32 getters, unit tests * lfrfid: FDX-B read, draft version * Minunit: better memeq assert * bit lib: reverse, print, print regions * Protocol dict: get protocol features, get protocol validate count * lfrfid worker: improved read * lfrfid raw worker: psk support * Cli: rfid plain C cli * protocol AWID: render * protocol em4100: render * protocol h10301: render * protocol indala26: support every indala 26 scramble * Protocol IO Prox: render * Protocol FDX-B: advanced read * lfrfid: remove unused test function * lfrfid: fix os primitives * bit lib: crc16 and unit tests * FDX-B: save data * lfrfid worker: increase stream size. Alloc raw worker only when needed. * lfrfid: indala26 emulation * lfrfid: prepare to write * lfrfid: fdx-b emulation * lfrfid: awid, ioprox write * lfrfid: write t55xx w\o validation * lfrfid: better t55xx block0 handling * lfrfid: use new t5577 functions in worker * lfrfid: improve protocol description * lfrfid: write and verify * lfrfid: delete cpp cli * lfrfid: improve worker usage * lfrfid-app: step to new worker * lfrfid: old indala (I40134) load fallback * lfrfid: indala26, recover wrong synced data * lfrfid: remove old worker * lfrfid app: dummy read screen * lfrfid app: less dummy read screen * lfrfid: generic 96-bit HID protocol (covers up to HID 37-bit) * rename * lfrfid: improve indala26 read * lfrfid: generic 192-bit HID protocol (covers all HID extended) * lfrfid: TODO about HID render * lfrfid: new protocol FDX-A * lfrfid-app: correct worker stop on exit * misc fixes * lfrfid: FDX-A and HID distinguishability has been fixed. * lfrfid: decode HID size header and render it (#1612) * lfrfid: rename HID96 and HID192 to HIDProx and HIDExt * lfrfid: extra actions scene * lfrfid: decode generic HID Proximity size lazily (#1618) * lib: stream of data buffers concept * lfrfid: raw file helper * lfrfid: changed raw worker api * lfrfid: packed varint pair * lfrfid: read stream speedup * lfrfid app: show read mode * Documentation * lfrfid app: raw read gui * lfrfid app: storage check for raw read * memleak fix * review fixes * lfrfid app: read blink color * lfrfid app: reset key name after read * review fixes * lfrfid app: fix copypasted text * review fixes * lfrfid: disable debug gpio * lfrfid: card detection events * lfrfid: change validation color from magenta to green * Update core_defines. * lfrfid: prefix fdx-b id by zeroes * lfrfid: parse up to 43-bit HID Proximity keys (#1640) * Fbt: downgrade toolchain and fix PS1 * lfrfid: fix unit tests * lfrfid app: remove printf * lfrfid: indala26, use bit 55 as data * lfrfid: indala26, better brief format * lfrfid: indala26, loading fallback * lfrfid: read timing tuning Co-authored-by: James Ide <ide@users.noreply.github.com> Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
19
lib/lfrfid/SConscript
Normal file
19
lib/lfrfid/SConscript
Normal file
@@ -0,0 +1,19 @@
|
||||
Import("env")
|
||||
|
||||
env.Append(
|
||||
LINT_SOURCES=[
|
||||
"#/lib/lfrfid",
|
||||
],
|
||||
CPPPATH=[
|
||||
"#/lib/lfrfid",
|
||||
],
|
||||
)
|
||||
|
||||
libenv = env.Clone(FW_LIB_NAME="lfrfid")
|
||||
libenv.ApplyLibFlags()
|
||||
|
||||
sources = libenv.GlobRecursive("*.c*")
|
||||
|
||||
lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources)
|
||||
libenv.Install("${LIB_DIST_DIR}", lib)
|
||||
Return("lib")
|
182
lib/lfrfid/lfrfid_dict_file.c
Normal file
182
lib/lfrfid/lfrfid_dict_file.c
Normal file
@@ -0,0 +1,182 @@
|
||||
#include "lfrfid_dict_file.h"
|
||||
#include <storage/storage.h>
|
||||
#include <flipper_format/flipper_format.h>
|
||||
#include <lfrfid/tools/bit_lib.h>
|
||||
|
||||
#define LFRFID_DICT_FILETYPE "Flipper RFID key"
|
||||
|
||||
bool lfrfid_dict_file_save(ProtocolDict* dict, ProtocolId protocol, const char* filename) {
|
||||
furi_check(protocol != PROTOCOL_NO);
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
FlipperFormat* file = flipper_format_file_alloc(storage);
|
||||
size_t data_size = protocol_dict_get_data_size(dict, protocol);
|
||||
uint8_t* data = malloc(data_size);
|
||||
bool result = false;
|
||||
|
||||
do {
|
||||
if(!flipper_format_file_open_always(file, filename)) break;
|
||||
if(!flipper_format_write_header_cstr(file, LFRFID_DICT_FILETYPE, 1)) break;
|
||||
|
||||
// TODO: write comment about protocol types into file
|
||||
|
||||
if(!flipper_format_write_string_cstr(
|
||||
file, "Key type", protocol_dict_get_name(dict, protocol)))
|
||||
break;
|
||||
|
||||
// TODO: write comment about protocol sizes into file
|
||||
|
||||
protocol_dict_get_data(dict, protocol, data, data_size);
|
||||
|
||||
if(!flipper_format_write_hex(file, "Data", data, data_size)) break;
|
||||
result = true;
|
||||
} while(false);
|
||||
|
||||
flipper_format_free(file);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
free(data);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void lfrfid_dict_protocol_indala_data(
|
||||
uint8_t* data,
|
||||
size_t data_size,
|
||||
uint8_t* protocol_data,
|
||||
size_t protocol_data_size) {
|
||||
UNUSED(data_size);
|
||||
memset(protocol_data, 0, protocol_data_size);
|
||||
|
||||
// fc
|
||||
bit_lib_set_bit(protocol_data, 24, bit_lib_get_bit(data, 0));
|
||||
bit_lib_set_bit(protocol_data, 16, bit_lib_get_bit(data, 1));
|
||||
bit_lib_set_bit(protocol_data, 11, bit_lib_get_bit(data, 2));
|
||||
bit_lib_set_bit(protocol_data, 14, bit_lib_get_bit(data, 3));
|
||||
bit_lib_set_bit(protocol_data, 15, bit_lib_get_bit(data, 4));
|
||||
bit_lib_set_bit(protocol_data, 20, bit_lib_get_bit(data, 5));
|
||||
bit_lib_set_bit(protocol_data, 6, bit_lib_get_bit(data, 6));
|
||||
bit_lib_set_bit(protocol_data, 25, bit_lib_get_bit(data, 7));
|
||||
|
||||
// cn
|
||||
bit_lib_set_bit(protocol_data, 9, bit_lib_get_bit(data, 8 + 0));
|
||||
bit_lib_set_bit(protocol_data, 12, bit_lib_get_bit(data, 8 + 1));
|
||||
bit_lib_set_bit(protocol_data, 10, bit_lib_get_bit(data, 8 + 2));
|
||||
bit_lib_set_bit(protocol_data, 7, bit_lib_get_bit(data, 8 + 3));
|
||||
bit_lib_set_bit(protocol_data, 19, bit_lib_get_bit(data, 8 + 4));
|
||||
bit_lib_set_bit(protocol_data, 3, bit_lib_get_bit(data, 8 + 5));
|
||||
bit_lib_set_bit(protocol_data, 2, bit_lib_get_bit(data, 8 + 6));
|
||||
bit_lib_set_bit(protocol_data, 18, bit_lib_get_bit(data, 8 + 7));
|
||||
bit_lib_set_bit(protocol_data, 13, bit_lib_get_bit(data, 8 + 8));
|
||||
bit_lib_set_bit(protocol_data, 0, bit_lib_get_bit(data, 8 + 9));
|
||||
bit_lib_set_bit(protocol_data, 4, bit_lib_get_bit(data, 8 + 10));
|
||||
bit_lib_set_bit(protocol_data, 21, bit_lib_get_bit(data, 8 + 11));
|
||||
bit_lib_set_bit(protocol_data, 23, bit_lib_get_bit(data, 8 + 12));
|
||||
bit_lib_set_bit(protocol_data, 26, bit_lib_get_bit(data, 8 + 13));
|
||||
bit_lib_set_bit(protocol_data, 17, bit_lib_get_bit(data, 8 + 14));
|
||||
bit_lib_set_bit(protocol_data, 8, bit_lib_get_bit(data, 8 + 15));
|
||||
|
||||
const uint32_t fc_and_card = data[0] << 16 | data[1] << 8 | data[2];
|
||||
|
||||
// indala checksum
|
||||
uint8_t checksum_sum = 0;
|
||||
checksum_sum += ((fc_and_card >> 14) & 1);
|
||||
checksum_sum += ((fc_and_card >> 12) & 1);
|
||||
checksum_sum += ((fc_and_card >> 9) & 1);
|
||||
checksum_sum += ((fc_and_card >> 8) & 1);
|
||||
checksum_sum += ((fc_and_card >> 6) & 1);
|
||||
checksum_sum += ((fc_and_card >> 5) & 1);
|
||||
checksum_sum += ((fc_and_card >> 2) & 1);
|
||||
checksum_sum += ((fc_and_card >> 0) & 1);
|
||||
checksum_sum = checksum_sum & 0b1;
|
||||
|
||||
if(checksum_sum) {
|
||||
bit_lib_set_bit(protocol_data, 27, 0);
|
||||
bit_lib_set_bit(protocol_data, 28, 1);
|
||||
} else {
|
||||
bit_lib_set_bit(protocol_data, 27, 1);
|
||||
bit_lib_set_bit(protocol_data, 28, 0);
|
||||
}
|
||||
|
||||
// wiegand parity
|
||||
uint8_t even_parity_sum = 0;
|
||||
for(int8_t i = 12; i < 24; i++) {
|
||||
if(((fc_and_card >> i) & 1) == 1) {
|
||||
even_parity_sum++;
|
||||
}
|
||||
}
|
||||
bit_lib_set_bit(protocol_data, 1, even_parity_sum % 2);
|
||||
|
||||
uint8_t odd_parity_sum = 1;
|
||||
for(int8_t i = 0; i < 12; i++) {
|
||||
if(((fc_and_card >> i) & 1) == 1) {
|
||||
odd_parity_sum++;
|
||||
}
|
||||
}
|
||||
bit_lib_set_bit(protocol_data, 5, odd_parity_sum % 2);
|
||||
}
|
||||
|
||||
static ProtocolId lfrfid_dict_protocol_fallback(
|
||||
ProtocolDict* dict,
|
||||
const char* protocol_name,
|
||||
FlipperFormat* file) {
|
||||
ProtocolId result = PROTOCOL_NO;
|
||||
if(strcmp(protocol_name, "I40134") == 0) {
|
||||
ProtocolId protocol = LFRFIDProtocolIndala26;
|
||||
|
||||
size_t data_size = 3;
|
||||
size_t protocol_data_size = protocol_dict_get_data_size(dict, protocol);
|
||||
uint8_t* data = malloc(data_size);
|
||||
uint8_t* protocol_data = malloc(protocol_data_size);
|
||||
if(flipper_format_read_hex(file, "Data", data, data_size)) {
|
||||
lfrfid_dict_protocol_indala_data(data, data_size, protocol_data, protocol_data_size);
|
||||
protocol_dict_set_data(dict, protocol, protocol_data, protocol_data_size);
|
||||
result = protocol;
|
||||
}
|
||||
free(protocol_data);
|
||||
free(data);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
ProtocolId lfrfid_dict_file_load(ProtocolDict* dict, const char* filename) {
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
FlipperFormat* file = flipper_format_file_alloc(storage);
|
||||
ProtocolId result = PROTOCOL_NO;
|
||||
uint8_t* data = malloc(protocol_dict_get_max_data_size(dict));
|
||||
string_t str_result;
|
||||
string_init(str_result);
|
||||
|
||||
do {
|
||||
if(!flipper_format_file_open_existing(file, filename)) break;
|
||||
|
||||
// header
|
||||
uint32_t version;
|
||||
if(!flipper_format_read_header(file, str_result, &version)) break;
|
||||
if(string_cmp_str(str_result, LFRFID_DICT_FILETYPE) != 0) break;
|
||||
if(version != 1) break;
|
||||
|
||||
// type
|
||||
if(!flipper_format_read_string(file, "Key type", str_result)) break;
|
||||
ProtocolId protocol;
|
||||
protocol = protocol_dict_get_protocol_by_name(dict, string_get_cstr(str_result));
|
||||
|
||||
if(protocol == PROTOCOL_NO) {
|
||||
protocol = lfrfid_dict_protocol_fallback(dict, string_get_cstr(str_result), file);
|
||||
if(protocol == PROTOCOL_NO) break;
|
||||
} else {
|
||||
// data
|
||||
size_t data_size = protocol_dict_get_data_size(dict, protocol);
|
||||
if(!flipper_format_read_hex(file, "Data", data, data_size)) break;
|
||||
protocol_dict_set_data(dict, protocol, data, data_size);
|
||||
}
|
||||
|
||||
result = protocol;
|
||||
} while(false);
|
||||
|
||||
free(data);
|
||||
string_clear(str_result);
|
||||
flipper_format_free(file);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
|
||||
return result;
|
||||
}
|
31
lib/lfrfid/lfrfid_dict_file.h
Normal file
31
lib/lfrfid/lfrfid_dict_file.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
#include <toolbox/protocols/protocol_dict.h>
|
||||
#include "protocols/lfrfid_protocols.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Save protocol from dictionary to file
|
||||
*
|
||||
* @param dict
|
||||
* @param protocol
|
||||
* @param filename
|
||||
* @return true
|
||||
* @return false
|
||||
*/
|
||||
bool lfrfid_dict_file_save(ProtocolDict* dict, ProtocolId protocol, const char* filename);
|
||||
|
||||
/**
|
||||
* @brief Load protocol from file to dictionary
|
||||
*
|
||||
* @param dict
|
||||
* @param filename
|
||||
* @return ProtocolId
|
||||
*/
|
||||
ProtocolId lfrfid_dict_file_load(ProtocolDict* dict, const char* filename);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
145
lib/lfrfid/lfrfid_raw_file.c
Normal file
145
lib/lfrfid/lfrfid_raw_file.c
Normal file
@@ -0,0 +1,145 @@
|
||||
#include "lfrfid_raw_file.h"
|
||||
#include "tools/varint_pair.h"
|
||||
#include <toolbox/stream/file_stream.h>
|
||||
#include <toolbox/varint.h>
|
||||
|
||||
#define LFRFID_RAW_FILE_MAGIC 0x4C464952
|
||||
#define LFRFID_RAW_FILE_VERSION 1
|
||||
|
||||
#define TAG "RFID RAW File"
|
||||
|
||||
typedef struct {
|
||||
uint32_t magic;
|
||||
uint32_t version;
|
||||
float frequency;
|
||||
float duty_cycle;
|
||||
uint32_t max_buffer_size;
|
||||
} LFRFIDRawFileHeader;
|
||||
|
||||
struct LFRFIDRawFile {
|
||||
Stream* stream;
|
||||
uint32_t max_buffer_size;
|
||||
|
||||
uint8_t* buffer;
|
||||
uint32_t buffer_size;
|
||||
size_t buffer_counter;
|
||||
};
|
||||
|
||||
LFRFIDRawFile* lfrfid_raw_file_alloc(Storage* storage) {
|
||||
LFRFIDRawFile* file = malloc(sizeof(LFRFIDRawFile));
|
||||
file->stream = file_stream_alloc(storage);
|
||||
file->buffer = NULL;
|
||||
return file;
|
||||
}
|
||||
|
||||
void lfrfid_raw_file_free(LFRFIDRawFile* file) {
|
||||
if(file->buffer) free(file->buffer);
|
||||
stream_free(file->stream);
|
||||
free(file);
|
||||
}
|
||||
|
||||
bool lfrfid_raw_file_open_write(LFRFIDRawFile* file, const char* file_path) {
|
||||
return file_stream_open(file->stream, file_path, FSAM_READ_WRITE, FSOM_CREATE_ALWAYS);
|
||||
}
|
||||
|
||||
bool lfrfid_raw_file_open_read(LFRFIDRawFile* file, const char* file_path) {
|
||||
return file_stream_open(file->stream, file_path, FSAM_READ, FSOM_OPEN_EXISTING);
|
||||
}
|
||||
|
||||
bool lfrfid_raw_file_write_header(
|
||||
LFRFIDRawFile* file,
|
||||
float frequency,
|
||||
float duty_cycle,
|
||||
uint32_t max_buffer_size) {
|
||||
LFRFIDRawFileHeader header = {
|
||||
.magic = LFRFID_RAW_FILE_MAGIC,
|
||||
.version = LFRFID_RAW_FILE_VERSION,
|
||||
.frequency = frequency,
|
||||
.duty_cycle = duty_cycle,
|
||||
.max_buffer_size = max_buffer_size};
|
||||
|
||||
size_t size = stream_write(file->stream, (uint8_t*)&header, sizeof(LFRFIDRawFileHeader));
|
||||
return (size == sizeof(LFRFIDRawFileHeader));
|
||||
}
|
||||
|
||||
bool lfrfid_raw_file_write_buffer(LFRFIDRawFile* file, uint8_t* buffer_data, size_t buffer_size) {
|
||||
size_t size;
|
||||
size = stream_write(file->stream, (uint8_t*)&buffer_size, sizeof(size_t));
|
||||
if(size != sizeof(size_t)) return false;
|
||||
|
||||
size = stream_write(file->stream, buffer_data, buffer_size);
|
||||
if(size != buffer_size) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool lfrfid_raw_file_read_header(LFRFIDRawFile* file, float* frequency, float* duty_cycle) {
|
||||
LFRFIDRawFileHeader header;
|
||||
size_t size = stream_read(file->stream, (uint8_t*)&header, sizeof(LFRFIDRawFileHeader));
|
||||
if(size == sizeof(LFRFIDRawFileHeader)) {
|
||||
if(header.magic == LFRFID_RAW_FILE_MAGIC && header.version == LFRFID_RAW_FILE_VERSION) {
|
||||
*frequency = header.frequency;
|
||||
*duty_cycle = header.duty_cycle;
|
||||
file->max_buffer_size = header.max_buffer_size;
|
||||
file->buffer = malloc(file->max_buffer_size);
|
||||
file->buffer_size = 0;
|
||||
file->buffer_counter = 0;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool lfrfid_raw_file_read_pair(
|
||||
LFRFIDRawFile* file,
|
||||
uint32_t* duration,
|
||||
uint32_t* pulse,
|
||||
bool* pass_end) {
|
||||
size_t length = 0;
|
||||
if(file->buffer_counter >= file->buffer_size) {
|
||||
if(stream_eof(file->stream)) {
|
||||
// rewind stream and pass header
|
||||
stream_seek(file->stream, sizeof(LFRFIDRawFileHeader), StreamOffsetFromStart);
|
||||
if(pass_end) *pass_end = true;
|
||||
}
|
||||
|
||||
length = stream_read(file->stream, (uint8_t*)&file->buffer_size, sizeof(size_t));
|
||||
if(length != sizeof(size_t)) {
|
||||
FURI_LOG_E(TAG, "read pair: failed to read size");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(file->buffer_size > file->max_buffer_size) {
|
||||
FURI_LOG_E(TAG, "read pair: buffer size is too big");
|
||||
return false;
|
||||
}
|
||||
|
||||
length = stream_read(file->stream, file->buffer, file->buffer_size);
|
||||
if(length != file->buffer_size) {
|
||||
FURI_LOG_E(TAG, "read pair: failed to read data");
|
||||
return false;
|
||||
}
|
||||
|
||||
file->buffer_counter = 0;
|
||||
}
|
||||
|
||||
size_t size = 0;
|
||||
bool result = varint_pair_unpack(
|
||||
&file->buffer[file->buffer_counter],
|
||||
(size_t)(file->buffer_size - file->buffer_counter),
|
||||
pulse,
|
||||
duration,
|
||||
&size);
|
||||
|
||||
if(result) {
|
||||
file->buffer_counter += size;
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "read pair: buffer is too small");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
95
lib/lfrfid/lfrfid_raw_file.h
Normal file
95
lib/lfrfid/lfrfid_raw_file.h
Normal file
@@ -0,0 +1,95 @@
|
||||
#pragma once
|
||||
#include <furi.h>
|
||||
#include <storage/storage.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct LFRFIDRawFile LFRFIDRawFile;
|
||||
|
||||
/**
|
||||
* @brief Allocate a new LFRFIDRawFile instance
|
||||
*
|
||||
* @param storage
|
||||
* @return LFRFIDRawFile*
|
||||
*/
|
||||
LFRFIDRawFile* lfrfid_raw_file_alloc(Storage* storage);
|
||||
|
||||
/**
|
||||
* @brief Free a LFRFIDRawFile instance
|
||||
*
|
||||
* @param file
|
||||
*/
|
||||
void lfrfid_raw_file_free(LFRFIDRawFile* file);
|
||||
|
||||
/**
|
||||
* @brief Open RAW file for writing
|
||||
*
|
||||
* @param file
|
||||
* @param file_path
|
||||
* @return bool
|
||||
*/
|
||||
bool lfrfid_raw_file_open_write(LFRFIDRawFile* file, const char* file_path);
|
||||
|
||||
/**
|
||||
* @brief Open RAW file for reading
|
||||
* @param file
|
||||
* @param file_path
|
||||
* @return bool
|
||||
*/
|
||||
bool lfrfid_raw_file_open_read(LFRFIDRawFile* file, const char* file_path);
|
||||
|
||||
/**
|
||||
* @brief Write RAW file header
|
||||
*
|
||||
* @param file
|
||||
* @param frequency
|
||||
* @param duty_cycle
|
||||
* @param max_buffer_size
|
||||
* @return bool
|
||||
*/
|
||||
bool lfrfid_raw_file_write_header(
|
||||
LFRFIDRawFile* file,
|
||||
float frequency,
|
||||
float duty_cycle,
|
||||
uint32_t max_buffer_size);
|
||||
|
||||
/**
|
||||
* @brief Write data to RAW file
|
||||
*
|
||||
* @param file
|
||||
* @param buffer_data
|
||||
* @param buffer_size
|
||||
* @return bool
|
||||
*/
|
||||
bool lfrfid_raw_file_write_buffer(LFRFIDRawFile* file, uint8_t* buffer_data, size_t buffer_size);
|
||||
|
||||
/**
|
||||
* @brief Read RAW file header
|
||||
*
|
||||
* @param file
|
||||
* @param frequency
|
||||
* @param duty_cycle
|
||||
* @return bool
|
||||
*/
|
||||
bool lfrfid_raw_file_read_header(LFRFIDRawFile* file, float* frequency, float* duty_cycle);
|
||||
|
||||
/**
|
||||
* @brief Read varint-encoded pair from RAW file
|
||||
*
|
||||
* @param file
|
||||
* @param duration
|
||||
* @param pulse
|
||||
* @param pass_end file was wrapped around, can be NULL
|
||||
* @return bool
|
||||
*/
|
||||
bool lfrfid_raw_file_read_pair(
|
||||
LFRFIDRawFile* file,
|
||||
uint32_t* duration,
|
||||
uint32_t* pulse,
|
||||
bool* pass_end);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
357
lib/lfrfid/lfrfid_raw_worker.c
Normal file
357
lib/lfrfid/lfrfid_raw_worker.c
Normal file
@@ -0,0 +1,357 @@
|
||||
#include <furi_hal_rfid.h>
|
||||
#include <toolbox/stream/file_stream.h>
|
||||
#include <toolbox/buffer_stream.h>
|
||||
#include <toolbox/varint.h>
|
||||
#include <stream_buffer.h>
|
||||
#include "lfrfid_raw_worker.h"
|
||||
#include "lfrfid_raw_file.h"
|
||||
#include "tools/varint_pair.h"
|
||||
|
||||
#define EMULATE_BUFFER_SIZE 1024
|
||||
#define RFID_DATA_BUFFER_SIZE 2048
|
||||
#define READ_DATA_BUFFER_COUNT 4
|
||||
|
||||
#define TAG_EMULATE "RAW EMULATE"
|
||||
|
||||
// emulate mode
|
||||
typedef struct {
|
||||
size_t overrun_count;
|
||||
StreamBufferHandle_t stream;
|
||||
} RfidEmulateCtx;
|
||||
|
||||
typedef struct {
|
||||
uint32_t emulate_buffer_arr[EMULATE_BUFFER_SIZE];
|
||||
uint32_t emulate_buffer_ccr[EMULATE_BUFFER_SIZE];
|
||||
RfidEmulateCtx ctx;
|
||||
} LFRFIDRawWorkerEmulateData;
|
||||
|
||||
typedef enum {
|
||||
HalfTransfer,
|
||||
TransferComplete,
|
||||
} LFRFIDRawEmulateDMAEvent;
|
||||
|
||||
// read mode
|
||||
#define READ_TEMP_DATA_SIZE 10
|
||||
|
||||
typedef struct {
|
||||
BufferStream* stream;
|
||||
VarintPair* pair;
|
||||
} LFRFIDRawWorkerReadData;
|
||||
|
||||
// main worker
|
||||
struct LFRFIDRawWorker {
|
||||
string_t file_path;
|
||||
FuriThread* thread;
|
||||
FuriEventFlag* events;
|
||||
|
||||
LFRFIDWorkerEmulateRawCallback emulate_callback;
|
||||
LFRFIDWorkerReadRawCallback read_callback;
|
||||
void* context;
|
||||
|
||||
float frequency;
|
||||
float duty_cycle;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
LFRFIDRawWorkerEventStop,
|
||||
} LFRFIDRawWorkerEvent;
|
||||
|
||||
static int32_t lfrfid_raw_read_worker_thread(void* thread_context);
|
||||
static int32_t lfrfid_raw_emulate_worker_thread(void* thread_context);
|
||||
|
||||
LFRFIDRawWorker* lfrfid_raw_worker_alloc() {
|
||||
LFRFIDRawWorker* worker = malloc(sizeof(LFRFIDRawWorker));
|
||||
|
||||
worker->thread = furi_thread_alloc();
|
||||
furi_thread_set_name(worker->thread, "lfrfid_raw_worker");
|
||||
furi_thread_set_context(worker->thread, worker);
|
||||
furi_thread_set_stack_size(worker->thread, 2048);
|
||||
|
||||
worker->events = furi_event_flag_alloc(NULL);
|
||||
|
||||
string_init(worker->file_path);
|
||||
return worker;
|
||||
}
|
||||
|
||||
void lfrfid_raw_worker_free(LFRFIDRawWorker* worker) {
|
||||
furi_thread_free(worker->thread);
|
||||
furi_event_flag_free(worker->events);
|
||||
string_clear(worker->file_path);
|
||||
free(worker);
|
||||
}
|
||||
|
||||
void lfrfid_raw_worker_start_read(
|
||||
LFRFIDRawWorker* worker,
|
||||
const char* file_path,
|
||||
float freq,
|
||||
float duty_cycle,
|
||||
LFRFIDWorkerReadRawCallback callback,
|
||||
void* context) {
|
||||
furi_check(furi_thread_get_state(worker->thread) == FuriThreadStateStopped);
|
||||
|
||||
string_set(worker->file_path, file_path);
|
||||
|
||||
worker->frequency = freq;
|
||||
worker->duty_cycle = duty_cycle;
|
||||
worker->read_callback = callback;
|
||||
worker->context = context;
|
||||
|
||||
furi_thread_set_callback(worker->thread, lfrfid_raw_read_worker_thread);
|
||||
|
||||
furi_thread_start(worker->thread);
|
||||
}
|
||||
|
||||
void lfrfid_raw_worker_start_emulate(
|
||||
LFRFIDRawWorker* worker,
|
||||
const char* file_path,
|
||||
LFRFIDWorkerEmulateRawCallback callback,
|
||||
void* context) {
|
||||
furi_check(furi_thread_get_state(worker->thread) == FuriThreadStateStopped);
|
||||
string_set(worker->file_path, file_path);
|
||||
worker->emulate_callback = callback;
|
||||
worker->context = context;
|
||||
furi_thread_set_callback(worker->thread, lfrfid_raw_emulate_worker_thread);
|
||||
furi_thread_start(worker->thread);
|
||||
}
|
||||
|
||||
void lfrfid_raw_worker_stop(LFRFIDRawWorker* worker) {
|
||||
worker->emulate_callback = NULL;
|
||||
worker->context = NULL;
|
||||
worker->read_callback = NULL;
|
||||
worker->context = NULL;
|
||||
furi_event_flag_set(worker->events, 1 << LFRFIDRawWorkerEventStop);
|
||||
furi_thread_join(worker->thread);
|
||||
}
|
||||
|
||||
static void lfrfid_raw_worker_capture(bool level, uint32_t duration, void* context) {
|
||||
LFRFIDRawWorkerReadData* ctx = context;
|
||||
|
||||
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
||||
|
||||
bool need_to_send = varint_pair_pack(ctx->pair, level, duration);
|
||||
|
||||
if(need_to_send) {
|
||||
buffer_stream_send_from_isr(
|
||||
ctx->stream,
|
||||
varint_pair_get_data(ctx->pair),
|
||||
varint_pair_get_size(ctx->pair),
|
||||
&xHigherPriorityTaskWoken);
|
||||
varint_pair_reset(ctx->pair);
|
||||
}
|
||||
|
||||
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
|
||||
}
|
||||
|
||||
static int32_t lfrfid_raw_read_worker_thread(void* thread_context) {
|
||||
LFRFIDRawWorker* worker = (LFRFIDRawWorker*)thread_context;
|
||||
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
LFRFIDRawFile* file = lfrfid_raw_file_alloc(storage);
|
||||
const char* filename = string_get_cstr(worker->file_path);
|
||||
bool file_valid = lfrfid_raw_file_open_write(file, filename);
|
||||
|
||||
LFRFIDRawWorkerReadData* data = malloc(sizeof(LFRFIDRawWorkerReadData));
|
||||
|
||||
data->stream = buffer_stream_alloc(RFID_DATA_BUFFER_SIZE, READ_DATA_BUFFER_COUNT);
|
||||
data->pair = varint_pair_alloc();
|
||||
|
||||
if(file_valid) {
|
||||
// write header
|
||||
file_valid = lfrfid_raw_file_write_header(
|
||||
file, worker->frequency, worker->duty_cycle, RFID_DATA_BUFFER_SIZE);
|
||||
}
|
||||
|
||||
if(file_valid) {
|
||||
// setup carrier
|
||||
furi_hal_rfid_pins_read();
|
||||
furi_hal_rfid_tim_read(worker->frequency, worker->duty_cycle);
|
||||
furi_hal_rfid_tim_read_start();
|
||||
|
||||
// stabilize detector
|
||||
furi_delay_ms(1500);
|
||||
|
||||
// start capture
|
||||
furi_hal_rfid_tim_read_capture_start(lfrfid_raw_worker_capture, data);
|
||||
|
||||
while(1) {
|
||||
Buffer* buffer = buffer_stream_receive(data->stream, 100);
|
||||
|
||||
if(buffer != NULL) {
|
||||
file_valid = lfrfid_raw_file_write_buffer(
|
||||
file, buffer_get_data(buffer), buffer_get_size(buffer));
|
||||
buffer_reset(buffer);
|
||||
}
|
||||
|
||||
if(!file_valid) {
|
||||
if(worker->read_callback != NULL) {
|
||||
// message file_error to worker
|
||||
worker->read_callback(LFRFIDWorkerReadRawFileError, worker->context);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if(buffer_stream_get_overrun_count(data->stream) > 0 &&
|
||||
worker->read_callback != NULL) {
|
||||
// message overrun to worker
|
||||
worker->read_callback(LFRFIDWorkerReadRawOverrun, worker->context);
|
||||
}
|
||||
|
||||
uint32_t flags = furi_event_flag_get(worker->events);
|
||||
if(FURI_BIT(flags, LFRFIDRawWorkerEventStop)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
furi_hal_rfid_tim_read_capture_stop();
|
||||
furi_hal_rfid_tim_read_stop();
|
||||
} else {
|
||||
if(worker->read_callback != NULL) {
|
||||
// message file_error to worker
|
||||
worker->read_callback(LFRFIDWorkerReadRawFileError, worker->context);
|
||||
}
|
||||
}
|
||||
|
||||
if(!file_valid) {
|
||||
const uint32_t available_flags = (1 << LFRFIDRawWorkerEventStop);
|
||||
while(true) {
|
||||
uint32_t flags = furi_event_flag_wait(
|
||||
worker->events, available_flags, FuriFlagWaitAny, FuriWaitForever);
|
||||
|
||||
if(FURI_BIT(flags, LFRFIDRawWorkerEventStop)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
varint_pair_free(data->pair);
|
||||
buffer_stream_free(data->stream);
|
||||
lfrfid_raw_file_free(file);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
free(data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rfid_emulate_dma_isr(bool half, void* context) {
|
||||
RfidEmulateCtx* ctx = context;
|
||||
|
||||
uint32_t flag = half ? HalfTransfer : TransferComplete;
|
||||
size_t len = xStreamBufferSendFromISR(ctx->stream, &flag, sizeof(uint32_t), pdFALSE);
|
||||
if(len != sizeof(uint32_t)) {
|
||||
ctx->overrun_count++;
|
||||
}
|
||||
}
|
||||
|
||||
static int32_t lfrfid_raw_emulate_worker_thread(void* thread_context) {
|
||||
LFRFIDRawWorker* worker = thread_context;
|
||||
|
||||
bool file_valid = true;
|
||||
|
||||
LFRFIDRawWorkerEmulateData* data = malloc(sizeof(LFRFIDRawWorkerEmulateData));
|
||||
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
data->ctx.overrun_count = 0;
|
||||
data->ctx.stream = xStreamBufferCreate(sizeof(uint32_t), sizeof(uint32_t));
|
||||
|
||||
LFRFIDRawFile* file = lfrfid_raw_file_alloc(storage);
|
||||
|
||||
do {
|
||||
file_valid = lfrfid_raw_file_open_read(file, string_get_cstr(worker->file_path));
|
||||
if(!file_valid) break;
|
||||
file_valid = lfrfid_raw_file_read_header(file, &worker->frequency, &worker->duty_cycle);
|
||||
if(!file_valid) break;
|
||||
|
||||
for(size_t i = 0; i < EMULATE_BUFFER_SIZE; i++) {
|
||||
file_valid = lfrfid_raw_file_read_pair(
|
||||
file, &data->emulate_buffer_arr[i], &data->emulate_buffer_ccr[i], NULL);
|
||||
if(!file_valid) break;
|
||||
data->emulate_buffer_arr[i] /= 8;
|
||||
data->emulate_buffer_arr[i] -= 1;
|
||||
data->emulate_buffer_ccr[i] /= 8;
|
||||
}
|
||||
} while(false);
|
||||
|
||||
furi_hal_rfid_tim_emulate_dma_start(
|
||||
data->emulate_buffer_arr,
|
||||
data->emulate_buffer_ccr,
|
||||
EMULATE_BUFFER_SIZE,
|
||||
rfid_emulate_dma_isr,
|
||||
&data->ctx);
|
||||
|
||||
if(!file_valid && worker->emulate_callback != NULL) {
|
||||
// message file_error to worker
|
||||
worker->emulate_callback(LFRFIDWorkerEmulateRawFileError, worker->context);
|
||||
}
|
||||
|
||||
if(file_valid) {
|
||||
uint32_t flag = 0;
|
||||
|
||||
while(true) {
|
||||
size_t size = xStreamBufferReceive(data->ctx.stream, &flag, sizeof(uint32_t), 100);
|
||||
|
||||
if(size == sizeof(uint32_t)) {
|
||||
size_t start = 0;
|
||||
if(flag == TransferComplete) {
|
||||
start = (EMULATE_BUFFER_SIZE / 2);
|
||||
}
|
||||
|
||||
for(size_t i = 0; i < (EMULATE_BUFFER_SIZE / 2); i++) {
|
||||
file_valid = lfrfid_raw_file_read_pair(
|
||||
file,
|
||||
&data->emulate_buffer_arr[start + i],
|
||||
&data->emulate_buffer_ccr[start + i],
|
||||
NULL);
|
||||
if(!file_valid) break;
|
||||
data->emulate_buffer_arr[i] /= 8;
|
||||
data->emulate_buffer_arr[i] -= 1;
|
||||
data->emulate_buffer_ccr[i] /= 8;
|
||||
}
|
||||
} else if(size != 0) {
|
||||
data->ctx.overrun_count++;
|
||||
}
|
||||
|
||||
if(!file_valid) {
|
||||
if(worker->emulate_callback != NULL) {
|
||||
// message file_error to worker
|
||||
worker->emulate_callback(LFRFIDWorkerEmulateRawFileError, worker->context);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if(data->ctx.overrun_count > 0 && worker->emulate_callback != NULL) {
|
||||
// message overrun to worker
|
||||
worker->emulate_callback(LFRFIDWorkerEmulateRawOverrun, worker->context);
|
||||
}
|
||||
|
||||
uint32_t flags = furi_event_flag_get(worker->events);
|
||||
if(FURI_BIT(flags, LFRFIDRawWorkerEventStop)) {
|
||||
break;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
furi_hal_rfid_tim_emulate_dma_stop();
|
||||
|
||||
if(!file_valid) {
|
||||
const uint32_t available_flags = (1 << LFRFIDRawWorkerEventStop);
|
||||
while(true) {
|
||||
uint32_t flags = furi_event_flag_wait(
|
||||
worker->events, available_flags, FuriFlagWaitAny, FuriWaitForever);
|
||||
|
||||
if(FURI_BIT(flags, LFRFIDRawWorkerEventStop)) {
|
||||
break;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if(data->ctx.overrun_count) {
|
||||
FURI_LOG_E(TAG_EMULATE, "overruns: %lu", data->ctx.overrun_count);
|
||||
}
|
||||
|
||||
vStreamBufferDelete(data->ctx.stream);
|
||||
lfrfid_raw_file_free(file);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
free(data);
|
||||
|
||||
return 0;
|
||||
}
|
68
lib/lfrfid/lfrfid_raw_worker.h
Normal file
68
lib/lfrfid/lfrfid_raw_worker.h
Normal file
@@ -0,0 +1,68 @@
|
||||
#pragma once
|
||||
#include <furi.h>
|
||||
#include "lfrfid_worker.h"
|
||||
#include <toolbox/protocols/protocol_dict.h>
|
||||
#include "protocols/lfrfid_protocols.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct LFRFIDRawWorker LFRFIDRawWorker;
|
||||
|
||||
/**
|
||||
* @brief Allocate a new LFRFIDRawWorker instance
|
||||
*
|
||||
* @return LFRFIDRawWorker*
|
||||
*/
|
||||
LFRFIDRawWorker* lfrfid_raw_worker_alloc();
|
||||
|
||||
/**
|
||||
* @brief Free a LFRFIDRawWorker instance
|
||||
*
|
||||
* @param worker LFRFIDRawWorker instance
|
||||
*/
|
||||
void lfrfid_raw_worker_free(LFRFIDRawWorker* worker);
|
||||
|
||||
/**
|
||||
* @brief Start reading
|
||||
*
|
||||
* @param worker LFRFIDRawWorker instance
|
||||
* @param file_path path where file will be saved
|
||||
* @param frequency HW frequency
|
||||
* @param duty_cycle HW duty cycle
|
||||
* @param callback callback for read event
|
||||
* @param context context for callback
|
||||
*/
|
||||
void lfrfid_raw_worker_start_read(
|
||||
LFRFIDRawWorker* worker,
|
||||
const char* file_path,
|
||||
float frequency,
|
||||
float duty_cycle,
|
||||
LFRFIDWorkerReadRawCallback callback,
|
||||
void* context);
|
||||
|
||||
/**
|
||||
* @brief Start emulate
|
||||
*
|
||||
* @param worker LFRFIDRawWorker instance
|
||||
* @param file_path path to file that will be emulated
|
||||
* @param callback callback for emulate event
|
||||
* @param context context for callback
|
||||
*/
|
||||
void lfrfid_raw_worker_start_emulate(
|
||||
LFRFIDRawWorker* worker,
|
||||
const char* file_path,
|
||||
LFRFIDWorkerEmulateRawCallback callback,
|
||||
void* context);
|
||||
|
||||
/**
|
||||
* @brief Stop worker
|
||||
*
|
||||
* @param worker
|
||||
*/
|
||||
void lfrfid_raw_worker_stop(LFRFIDRawWorker* worker);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
169
lib/lfrfid/lfrfid_worker.c
Normal file
169
lib/lfrfid/lfrfid_worker.c
Normal file
@@ -0,0 +1,169 @@
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <atomic.h>
|
||||
#include "lfrfid_worker_i.h"
|
||||
|
||||
typedef enum {
|
||||
LFRFIDEventStopThread = (1 << 0),
|
||||
LFRFIDEventStopMode = (1 << 1),
|
||||
LFRFIDEventRead = (1 << 2),
|
||||
LFRFIDEventWrite = (1 << 3),
|
||||
LFRFIDEventEmulate = (1 << 4),
|
||||
LFRFIDEventReadRaw = (1 << 5),
|
||||
LFRFIDEventEmulateRaw = (1 << 6),
|
||||
LFRFIDEventAll =
|
||||
(LFRFIDEventStopThread | LFRFIDEventStopMode | LFRFIDEventRead | LFRFIDEventWrite |
|
||||
LFRFIDEventEmulate | LFRFIDEventReadRaw | LFRFIDEventEmulateRaw),
|
||||
} LFRFIDEventType;
|
||||
|
||||
static int32_t lfrfid_worker_thread(void* thread_context);
|
||||
|
||||
LFRFIDWorker* lfrfid_worker_alloc(ProtocolDict* dict) {
|
||||
furi_assert(dict);
|
||||
|
||||
LFRFIDWorker* worker = malloc(sizeof(LFRFIDWorker));
|
||||
worker->mode_index = LFRFIDWorkerIdle;
|
||||
worker->read_cb = NULL;
|
||||
worker->write_cb = NULL;
|
||||
worker->cb_ctx = NULL;
|
||||
worker->raw_filename = NULL;
|
||||
worker->mode_storage = NULL;
|
||||
|
||||
worker->thread = furi_thread_alloc();
|
||||
furi_thread_set_name(worker->thread, "lfrfid_worker");
|
||||
furi_thread_set_callback(worker->thread, lfrfid_worker_thread);
|
||||
furi_thread_set_context(worker->thread, worker);
|
||||
furi_thread_set_stack_size(worker->thread, 2048);
|
||||
|
||||
worker->protocols = dict;
|
||||
|
||||
return worker;
|
||||
}
|
||||
|
||||
void lfrfid_worker_free(LFRFIDWorker* worker) {
|
||||
if(worker->raw_filename) {
|
||||
free(worker->raw_filename);
|
||||
}
|
||||
|
||||
furi_thread_free(worker->thread);
|
||||
free(worker);
|
||||
}
|
||||
|
||||
void lfrfid_worker_read_start(
|
||||
LFRFIDWorker* worker,
|
||||
LFRFIDWorkerReadType type,
|
||||
LFRFIDWorkerReadCallback callback,
|
||||
void* context) {
|
||||
furi_assert(worker->mode_index == LFRFIDWorkerIdle);
|
||||
worker->read_type = type;
|
||||
worker->read_cb = callback;
|
||||
worker->cb_ctx = context;
|
||||
furi_thread_flags_set(furi_thread_get_id(worker->thread), LFRFIDEventRead);
|
||||
}
|
||||
|
||||
void lfrfid_worker_write_start(
|
||||
LFRFIDWorker* worker,
|
||||
LFRFIDProtocol protocol,
|
||||
LFRFIDWorkerWriteCallback callback,
|
||||
void* context) {
|
||||
furi_assert(worker->mode_index == LFRFIDWorkerIdle);
|
||||
worker->protocol = protocol;
|
||||
worker->write_cb = callback;
|
||||
worker->cb_ctx = context;
|
||||
furi_thread_flags_set(furi_thread_get_id(worker->thread), LFRFIDEventWrite);
|
||||
}
|
||||
|
||||
void lfrfid_worker_emulate_start(LFRFIDWorker* worker, LFRFIDProtocol protocol) {
|
||||
furi_assert(worker->mode_index == LFRFIDWorkerIdle);
|
||||
worker->protocol = protocol;
|
||||
furi_thread_flags_set(furi_thread_get_id(worker->thread), LFRFIDEventEmulate);
|
||||
}
|
||||
|
||||
void lfrfid_worker_set_filename(LFRFIDWorker* worker, const char* filename) {
|
||||
if(worker->raw_filename) {
|
||||
free(worker->raw_filename);
|
||||
}
|
||||
|
||||
worker->raw_filename = strdup(filename);
|
||||
}
|
||||
|
||||
void lfrfid_worker_read_raw_start(
|
||||
LFRFIDWorker* worker,
|
||||
const char* filename,
|
||||
LFRFIDWorkerReadType type,
|
||||
LFRFIDWorkerReadRawCallback callback,
|
||||
void* context) {
|
||||
furi_assert(worker->mode_index == LFRFIDWorkerIdle);
|
||||
worker->read_type = type;
|
||||
worker->read_raw_cb = callback;
|
||||
worker->cb_ctx = context;
|
||||
lfrfid_worker_set_filename(worker, filename);
|
||||
furi_thread_flags_set(furi_thread_get_id(worker->thread), LFRFIDEventReadRaw);
|
||||
}
|
||||
|
||||
void lfrfid_worker_emulate_raw_start(
|
||||
LFRFIDWorker* worker,
|
||||
const char* filename,
|
||||
LFRFIDWorkerEmulateRawCallback callback,
|
||||
void* context) {
|
||||
furi_assert(worker->mode_index == LFRFIDWorkerIdle);
|
||||
lfrfid_worker_set_filename(worker, filename);
|
||||
worker->emulate_raw_cb = callback;
|
||||
worker->cb_ctx = context;
|
||||
furi_thread_flags_set(furi_thread_get_id(worker->thread), LFRFIDEventEmulateRaw);
|
||||
}
|
||||
|
||||
void lfrfid_worker_stop(LFRFIDWorker* worker) {
|
||||
furi_thread_flags_set(furi_thread_get_id(worker->thread), LFRFIDEventStopMode);
|
||||
}
|
||||
|
||||
void lfrfid_worker_start_thread(LFRFIDWorker* worker) {
|
||||
furi_thread_start(worker->thread);
|
||||
}
|
||||
|
||||
void lfrfid_worker_stop_thread(LFRFIDWorker* worker) {
|
||||
furi_assert(worker->mode_index == LFRFIDWorkerIdle);
|
||||
furi_thread_flags_set(furi_thread_get_id(worker->thread), LFRFIDEventStopThread);
|
||||
furi_thread_join(worker->thread);
|
||||
}
|
||||
|
||||
bool lfrfid_worker_check_for_stop(LFRFIDWorker* worker) {
|
||||
UNUSED(worker);
|
||||
uint32_t flags = furi_thread_flags_get();
|
||||
return (flags & LFRFIDEventStopMode);
|
||||
}
|
||||
|
||||
size_t lfrfid_worker_dict_get_data_size(LFRFIDWorker* worker, LFRFIDProtocol protocol) {
|
||||
furi_assert(worker->mode_index == LFRFIDWorkerIdle);
|
||||
return protocol_dict_get_data_size(worker->protocols, protocol);
|
||||
}
|
||||
|
||||
static int32_t lfrfid_worker_thread(void* thread_context) {
|
||||
LFRFIDWorker* worker = thread_context;
|
||||
bool running = true;
|
||||
|
||||
while(running) {
|
||||
uint32_t flags = furi_thread_flags_wait(LFRFIDEventAll, FuriFlagWaitAny, FuriWaitForever);
|
||||
if(flags != FuriFlagErrorTimeout) {
|
||||
// stop thread
|
||||
if(flags & LFRFIDEventStopThread) break;
|
||||
|
||||
// switch mode
|
||||
if(flags & LFRFIDEventRead) worker->mode_index = LFRFIDWorkerRead;
|
||||
if(flags & LFRFIDEventWrite) worker->mode_index = LFRFIDWorkerWrite;
|
||||
if(flags & LFRFIDEventEmulate) worker->mode_index = LFRFIDWorkerEmulate;
|
||||
if(flags & LFRFIDEventReadRaw) worker->mode_index = LFRFIDWorkerReadRaw;
|
||||
if(flags & LFRFIDEventEmulateRaw) worker->mode_index = LFRFIDWorkerEmulateRaw;
|
||||
|
||||
// do mode, if it exists
|
||||
if(lfrfid_worker_modes[worker->mode_index].process) {
|
||||
lfrfid_worker_modes[worker->mode_index].process(worker);
|
||||
}
|
||||
|
||||
// reset mode
|
||||
worker->mode_index = LFRFIDWorkerIdle;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
152
lib/lfrfid/lfrfid_worker.h
Normal file
152
lib/lfrfid/lfrfid_worker.h
Normal file
@@ -0,0 +1,152 @@
|
||||
/**
|
||||
* @file lfrfid_worker.h
|
||||
*
|
||||
* LFRFID worker
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <toolbox/protocols/protocol_dict.h>
|
||||
#include "protocols/lfrfid_protocols.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
LFRFIDWorkerWriteOK,
|
||||
LFRFIDWorkerWriteProtocolCannotBeWritten,
|
||||
LFRFIDWorkerWriteFobCannotBeWritten,
|
||||
LFRFIDWorkerWriteTooLongToWrite,
|
||||
} LFRFIDWorkerWriteResult;
|
||||
|
||||
typedef enum {
|
||||
LFRFIDWorkerReadTypeAuto,
|
||||
LFRFIDWorkerReadTypeASKOnly,
|
||||
LFRFIDWorkerReadTypePSKOnly,
|
||||
} LFRFIDWorkerReadType;
|
||||
|
||||
typedef enum {
|
||||
LFRFIDWorkerReadSenseStart, // TODO: not implemented
|
||||
LFRFIDWorkerReadSenseEnd, // TODO: not implemented
|
||||
LFRFIDWorkerReadSenseCardStart,
|
||||
LFRFIDWorkerReadSenseCardEnd,
|
||||
LFRFIDWorkerReadStartASK,
|
||||
LFRFIDWorkerReadStartPSK,
|
||||
LFRFIDWorkerReadDone,
|
||||
} LFRFIDWorkerReadResult;
|
||||
|
||||
typedef enum {
|
||||
LFRFIDWorkerReadRawFileError,
|
||||
LFRFIDWorkerReadRawOverrun,
|
||||
} LFRFIDWorkerReadRawResult;
|
||||
|
||||
typedef enum {
|
||||
LFRFIDWorkerEmulateRawFileError,
|
||||
LFRFIDWorkerEmulateRawOverrun,
|
||||
} LFRFIDWorkerEmulateRawResult;
|
||||
|
||||
typedef void (
|
||||
*LFRFIDWorkerReadCallback)(LFRFIDWorkerReadResult result, ProtocolId protocol, void* context);
|
||||
typedef void (*LFRFIDWorkerWriteCallback)(LFRFIDWorkerWriteResult result, void* context);
|
||||
|
||||
typedef void (*LFRFIDWorkerReadRawCallback)(LFRFIDWorkerReadRawResult result, void* context);
|
||||
typedef void (*LFRFIDWorkerEmulateRawCallback)(LFRFIDWorkerEmulateRawResult result, void* context);
|
||||
|
||||
typedef struct LFRFIDWorker LFRFIDWorker;
|
||||
|
||||
/**
|
||||
* Allocate LF-RFID worker
|
||||
* @return LFRFIDWorker*
|
||||
*/
|
||||
LFRFIDWorker* lfrfid_worker_alloc(ProtocolDict* dict);
|
||||
|
||||
/**
|
||||
* Free LF-RFID worker
|
||||
* @param worker
|
||||
*/
|
||||
void lfrfid_worker_free(LFRFIDWorker* worker);
|
||||
|
||||
/**
|
||||
* Start LF-RFID worker thread
|
||||
* @param worker
|
||||
*/
|
||||
void lfrfid_worker_start_thread(LFRFIDWorker* worker);
|
||||
|
||||
/**
|
||||
* Stop LF-RFID worker thread
|
||||
* @param worker
|
||||
*/
|
||||
void lfrfid_worker_stop_thread(LFRFIDWorker* worker);
|
||||
|
||||
/**
|
||||
* @brief Start read mode
|
||||
*
|
||||
* @param worker
|
||||
* @param type
|
||||
* @param callback
|
||||
* @param context
|
||||
*/
|
||||
void lfrfid_worker_read_start(
|
||||
LFRFIDWorker* worker,
|
||||
LFRFIDWorkerReadType type,
|
||||
LFRFIDWorkerReadCallback callback,
|
||||
void* context);
|
||||
|
||||
/**
|
||||
* @brief Start write mode
|
||||
*
|
||||
* @param worker
|
||||
* @param protocol
|
||||
* @param callback
|
||||
* @param context
|
||||
*/
|
||||
void lfrfid_worker_write_start(
|
||||
LFRFIDWorker* worker,
|
||||
LFRFIDProtocol protocol,
|
||||
LFRFIDWorkerWriteCallback callback,
|
||||
void* context);
|
||||
|
||||
/**
|
||||
* Start emulate mode
|
||||
* @param worker
|
||||
*/
|
||||
void lfrfid_worker_emulate_start(LFRFIDWorker* worker, LFRFIDProtocol protocol);
|
||||
|
||||
/**
|
||||
* @brief Start raw read mode
|
||||
*
|
||||
* @param worker
|
||||
* @param filename
|
||||
* @param type
|
||||
* @param callback
|
||||
* @param context
|
||||
*/
|
||||
void lfrfid_worker_read_raw_start(
|
||||
LFRFIDWorker* worker,
|
||||
const char* filename,
|
||||
LFRFIDWorkerReadType type,
|
||||
LFRFIDWorkerReadRawCallback callback,
|
||||
void* context);
|
||||
|
||||
/**
|
||||
* Emulate raw read mode
|
||||
* @param worker
|
||||
* @param filename
|
||||
* @param callback
|
||||
* @param context
|
||||
*/
|
||||
void lfrfid_worker_emulate_raw_start(
|
||||
LFRFIDWorker* worker,
|
||||
const char* filename,
|
||||
LFRFIDWorkerEmulateRawCallback callback,
|
||||
void* context);
|
||||
|
||||
/**
|
||||
* Stop all modes
|
||||
* @param worker
|
||||
*/
|
||||
void lfrfid_worker_stop(LFRFIDWorker* worker);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
64
lib/lfrfid/lfrfid_worker_i.h
Normal file
64
lib/lfrfid/lfrfid_worker_i.h
Normal file
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
* @file lfrfid_worker_i.h
|
||||
*
|
||||
* lfrfid worker, internal definitions
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <furi.h>
|
||||
#include "lfrfid_worker.h"
|
||||
#include "lfrfid_raw_worker.h"
|
||||
#include "protocols/lfrfid_protocols.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
void (*const process)(LFRFIDWorker* worker);
|
||||
} LFRFIDWorkerModeType;
|
||||
|
||||
typedef enum {
|
||||
LFRFIDWorkerIdle,
|
||||
LFRFIDWorkerRead,
|
||||
LFRFIDWorkerWrite,
|
||||
LFRFIDWorkerEmulate,
|
||||
LFRFIDWorkerReadRaw,
|
||||
LFRFIDWorkerEmulateRaw,
|
||||
} LFRFIDWorkerMode;
|
||||
|
||||
struct LFRFIDWorker {
|
||||
char* raw_filename;
|
||||
|
||||
LFRFIDWorkerMode mode_index;
|
||||
void* mode_storage;
|
||||
|
||||
FuriEventFlag* events;
|
||||
FuriThread* thread;
|
||||
|
||||
LFRFIDWorkerReadType read_type;
|
||||
|
||||
LFRFIDWorkerReadCallback read_cb;
|
||||
LFRFIDWorkerWriteCallback write_cb;
|
||||
LFRFIDWorkerReadRawCallback read_raw_cb;
|
||||
LFRFIDWorkerEmulateRawCallback emulate_raw_cb;
|
||||
|
||||
void* cb_ctx;
|
||||
|
||||
ProtocolDict* protocols;
|
||||
LFRFIDProtocol protocol;
|
||||
};
|
||||
|
||||
extern const LFRFIDWorkerModeType lfrfid_worker_modes[];
|
||||
|
||||
/**
|
||||
* @brief Check for stop flag
|
||||
*
|
||||
* @param worker
|
||||
* @return bool
|
||||
*/
|
||||
bool lfrfid_worker_check_for_stop(LFRFIDWorker* worker);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
624
lib/lfrfid/lfrfid_worker_modes.c
Normal file
624
lib/lfrfid/lfrfid_worker_modes.c
Normal file
@@ -0,0 +1,624 @@
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include "lfrfid_worker_i.h"
|
||||
#include "tools/t5577.h"
|
||||
#include <stream_buffer.h>
|
||||
#include <toolbox/pulse_protocols/pulse_glue.h>
|
||||
#include <toolbox/buffer_stream.h>
|
||||
#include "tools/varint_pair.h"
|
||||
#include "tools/bit_lib.h"
|
||||
|
||||
#define TAG "LFRFIDWorker"
|
||||
|
||||
/**
|
||||
* if READ_DEBUG_GPIO is defined:
|
||||
* gpio_ext_pa7 will repeat signal coming from the comparator
|
||||
* gpio_ext_pa6 will show load on the decoder
|
||||
*/
|
||||
// #define LFRFID_WORKER_READ_DEBUG_GPIO 1
|
||||
|
||||
#ifdef LFRFID_WORKER_READ_DEBUG_GPIO
|
||||
#define LFRFID_WORKER_READ_DEBUG_GPIO_VALUE &gpio_ext_pa7
|
||||
#define LFRFID_WORKER_READ_DEBUG_GPIO_LOAD &gpio_ext_pa6
|
||||
#endif
|
||||
|
||||
#define LFRFID_WORKER_READ_AVERAGE_COUNT 64
|
||||
#define LFRFID_WORKER_READ_MIN_TIME_US 16
|
||||
|
||||
#define LFRFID_WORKER_READ_DROP_TIME_MS 50
|
||||
#define LFRFID_WORKER_READ_STABILIZE_TIME_MS 450
|
||||
#define LFRFID_WORKER_READ_SWITCH_TIME_MS 1500
|
||||
|
||||
#define LFRFID_WORKER_WRITE_VERIFY_TIME_MS 1500
|
||||
#define LFRFID_WORKER_WRITE_DROP_TIME_MS 50
|
||||
#define LFRFID_WORKER_WRITE_TOO_LONG_TIME_MS 10000
|
||||
|
||||
#define LFRFID_WORKER_WRITE_MAX_UNSUCCESSFUL_READS 5
|
||||
|
||||
#define LFRFID_WORKER_READ_BUFFER_SIZE 512
|
||||
#define LFRFID_WORKER_READ_BUFFER_COUNT 8
|
||||
|
||||
#define LFRFID_WORKER_EMULATE_BUFFER_SIZE 1024
|
||||
|
||||
#define LFRFID_WORKER_DELAY_QUANT 50
|
||||
|
||||
void lfrfid_worker_delay(LFRFIDWorker* worker, uint32_t milliseconds) {
|
||||
for(uint32_t i = 0; i < (milliseconds / LFRFID_WORKER_DELAY_QUANT); i++) {
|
||||
if(lfrfid_worker_check_for_stop(worker)) break;
|
||||
furi_delay_ms(LFRFID_WORKER_DELAY_QUANT);
|
||||
}
|
||||
}
|
||||
|
||||
/**************************************************************************************************/
|
||||
/********************************************** READ **********************************************/
|
||||
/**************************************************************************************************/
|
||||
|
||||
typedef struct {
|
||||
BufferStream* stream;
|
||||
VarintPair* pair;
|
||||
bool ignore_next_pulse;
|
||||
} LFRFIDWorkerReadContext;
|
||||
|
||||
static void lfrfid_worker_read_capture(bool level, uint32_t duration, void* context) {
|
||||
LFRFIDWorkerReadContext* ctx = context;
|
||||
|
||||
// ignore pulse if last pulse was noise
|
||||
if(ctx->ignore_next_pulse) {
|
||||
ctx->ignore_next_pulse = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// ignore noise spikes
|
||||
if(duration <= LFRFID_WORKER_READ_MIN_TIME_US) {
|
||||
if(level) {
|
||||
ctx->ignore_next_pulse = true;
|
||||
}
|
||||
varint_pair_reset(ctx->pair);
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef LFRFID_WORKER_READ_DEBUG_GPIO
|
||||
furi_hal_gpio_write(LFRFID_WORKER_READ_DEBUG_GPIO_VALUE, level);
|
||||
#endif
|
||||
|
||||
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
||||
bool need_to_send = varint_pair_pack(ctx->pair, level, duration);
|
||||
if(need_to_send) {
|
||||
buffer_stream_send_from_isr(
|
||||
ctx->stream,
|
||||
varint_pair_get_data(ctx->pair),
|
||||
varint_pair_get_size(ctx->pair),
|
||||
&xHigherPriorityTaskWoken);
|
||||
varint_pair_reset(ctx->pair);
|
||||
}
|
||||
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
LFRFIDWorkerReadOK,
|
||||
LFRFIDWorkerReadExit,
|
||||
LFRFIDWorkerReadTimeout,
|
||||
} LFRFIDWorkerReadState;
|
||||
|
||||
static LFRFIDWorkerReadState lfrfid_worker_read_internal(
|
||||
LFRFIDWorker* worker,
|
||||
LFRFIDFeature feature,
|
||||
uint32_t timeout,
|
||||
ProtocolId* result_protocol) {
|
||||
LFRFIDWorkerReadState state = LFRFIDWorkerReadTimeout;
|
||||
furi_hal_rfid_pins_read();
|
||||
|
||||
if(feature & LFRFIDFeatureASK) {
|
||||
furi_hal_rfid_tim_read(125000, 0.5);
|
||||
FURI_LOG_D(TAG, "Start ASK");
|
||||
if(worker->read_cb) {
|
||||
worker->read_cb(LFRFIDWorkerReadStartASK, PROTOCOL_NO, worker->cb_ctx);
|
||||
}
|
||||
} else {
|
||||
furi_hal_rfid_tim_read(62500, 0.25);
|
||||
FURI_LOG_D(TAG, "Start PSK");
|
||||
if(worker->read_cb) {
|
||||
worker->read_cb(LFRFIDWorkerReadStartPSK, PROTOCOL_NO, worker->cb_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
furi_hal_rfid_tim_read_start();
|
||||
|
||||
// stabilize detector
|
||||
lfrfid_worker_delay(worker, LFRFID_WORKER_READ_STABILIZE_TIME_MS);
|
||||
|
||||
protocol_dict_decoders_start(worker->protocols);
|
||||
|
||||
#ifdef LFRFID_WORKER_READ_DEBUG_GPIO
|
||||
furi_hal_gpio_init_simple(LFRFID_WORKER_READ_DEBUG_GPIO_VALUE, GpioModeOutputPushPull);
|
||||
furi_hal_gpio_init_simple(LFRFID_WORKER_READ_DEBUG_GPIO_LOAD, GpioModeOutputPushPull);
|
||||
#endif
|
||||
|
||||
LFRFIDWorkerReadContext ctx;
|
||||
ctx.pair = varint_pair_alloc();
|
||||
ctx.stream =
|
||||
buffer_stream_alloc(LFRFID_WORKER_READ_BUFFER_SIZE, LFRFID_WORKER_READ_BUFFER_COUNT);
|
||||
|
||||
furi_hal_rfid_tim_read_capture_start(lfrfid_worker_read_capture, &ctx);
|
||||
|
||||
*result_protocol = PROTOCOL_NO;
|
||||
ProtocolId last_protocol = PROTOCOL_NO;
|
||||
size_t last_size = protocol_dict_get_max_data_size(worker->protocols);
|
||||
uint8_t* last_data = malloc(last_size);
|
||||
uint8_t* protocol_data = malloc(last_size);
|
||||
size_t last_read_count = 0;
|
||||
|
||||
uint32_t switch_os_tick_last = furi_get_tick();
|
||||
|
||||
uint32_t average_duration = 0;
|
||||
uint32_t average_pulse = 0;
|
||||
size_t average_index = 0;
|
||||
bool card_detected = false;
|
||||
|
||||
FURI_LOG_D(TAG, "Read started");
|
||||
while(true) {
|
||||
if(lfrfid_worker_check_for_stop(worker)) {
|
||||
state = LFRFIDWorkerReadExit;
|
||||
break;
|
||||
}
|
||||
|
||||
Buffer* buffer = buffer_stream_receive(ctx.stream, 100);
|
||||
|
||||
#ifdef LFRFID_WORKER_READ_DEBUG_GPIO
|
||||
furi_hal_gpio_write(LFRFID_WORKER_READ_DEBUG_GPIO_LOAD, true);
|
||||
#endif
|
||||
|
||||
if(buffer_stream_get_overrun_count(ctx.stream) > 0) {
|
||||
FURI_LOG_E(TAG, "Read overrun, recovering");
|
||||
buffer_stream_reset(ctx.stream);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(buffer == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
size_t size = buffer_get_size(buffer);
|
||||
uint8_t* data = buffer_get_data(buffer);
|
||||
size_t index = 0;
|
||||
|
||||
while(index < size) {
|
||||
uint32_t duration;
|
||||
uint32_t pulse;
|
||||
size_t tmp_size;
|
||||
|
||||
if(!varint_pair_unpack(&data[index], size - index, &pulse, &duration, &tmp_size)) {
|
||||
FURI_LOG_E(TAG, "can't unpack varint pair");
|
||||
break;
|
||||
} else {
|
||||
index += tmp_size;
|
||||
|
||||
average_duration += duration;
|
||||
average_pulse += pulse;
|
||||
average_index++;
|
||||
if(average_index >= LFRFID_WORKER_READ_AVERAGE_COUNT) {
|
||||
float average = (float)average_pulse / (float)average_duration;
|
||||
average_pulse = 0;
|
||||
average_duration = 0;
|
||||
average_index = 0;
|
||||
|
||||
if(worker->read_cb) {
|
||||
if(average > 0.2 && average < 0.8) {
|
||||
if(!card_detected) {
|
||||
card_detected = true;
|
||||
worker->read_cb(
|
||||
LFRFIDWorkerReadSenseStart, PROTOCOL_NO, worker->cb_ctx);
|
||||
}
|
||||
} else {
|
||||
if(card_detected) {
|
||||
card_detected = false;
|
||||
worker->read_cb(
|
||||
LFRFIDWorkerReadSenseEnd, PROTOCOL_NO, worker->cb_ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ProtocolId protocol = PROTOCOL_NO;
|
||||
|
||||
protocol = protocol_dict_decoders_feed_by_feature(
|
||||
worker->protocols, feature, true, pulse);
|
||||
if(protocol == PROTOCOL_NO) {
|
||||
protocol = protocol_dict_decoders_feed_by_feature(
|
||||
worker->protocols, feature, false, duration - pulse);
|
||||
}
|
||||
|
||||
if(protocol != PROTOCOL_NO) {
|
||||
// reset switch timer
|
||||
switch_os_tick_last = furi_get_tick();
|
||||
|
||||
size_t protocol_data_size =
|
||||
protocol_dict_get_data_size(worker->protocols, protocol);
|
||||
protocol_dict_get_data(
|
||||
worker->protocols, protocol, protocol_data, protocol_data_size);
|
||||
|
||||
// validate protocol
|
||||
if(protocol == last_protocol &&
|
||||
memcmp(last_data, protocol_data, protocol_data_size) == 0) {
|
||||
last_read_count = last_read_count + 1;
|
||||
|
||||
size_t validation_count =
|
||||
protocol_dict_get_validate_count(worker->protocols, protocol);
|
||||
|
||||
if(last_read_count >= validation_count) {
|
||||
state = LFRFIDWorkerReadOK;
|
||||
*result_protocol = protocol;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if(last_protocol == PROTOCOL_NO && worker->read_cb) {
|
||||
worker->read_cb(
|
||||
LFRFIDWorkerReadSenseCardStart, protocol, worker->cb_ctx);
|
||||
}
|
||||
|
||||
last_protocol = protocol;
|
||||
memcpy(last_data, protocol_data, protocol_data_size);
|
||||
last_read_count = 0;
|
||||
}
|
||||
|
||||
string_t string_info;
|
||||
string_init(string_info);
|
||||
for(uint8_t i = 0; i < protocol_data_size; i++) {
|
||||
if(i != 0) {
|
||||
string_cat_printf(string_info, " ");
|
||||
}
|
||||
|
||||
string_cat_printf(string_info, "%02X", protocol_data[i]);
|
||||
}
|
||||
|
||||
FURI_LOG_D(
|
||||
TAG,
|
||||
"%s, %d, [%s]",
|
||||
protocol_dict_get_name(worker->protocols, protocol),
|
||||
last_read_count,
|
||||
string_get_cstr(string_info));
|
||||
string_clear(string_info);
|
||||
|
||||
protocol_dict_decoders_start(worker->protocols);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buffer_reset(buffer);
|
||||
|
||||
#ifdef LFRFID_WORKER_READ_DEBUG_GPIO
|
||||
furi_hal_gpio_write(LFRFID_WORKER_READ_DEBUG_GPIO_LOAD, false);
|
||||
#endif
|
||||
|
||||
if(*result_protocol != PROTOCOL_NO) {
|
||||
break;
|
||||
}
|
||||
|
||||
if((furi_get_tick() - switch_os_tick_last) > timeout) {
|
||||
state = LFRFIDWorkerReadTimeout;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
FURI_LOG_D(TAG, "Read stopped");
|
||||
|
||||
if(last_protocol != PROTOCOL_NO && worker->read_cb) {
|
||||
worker->read_cb(LFRFIDWorkerReadSenseCardEnd, last_protocol, worker->cb_ctx);
|
||||
}
|
||||
|
||||
if(card_detected && worker->read_cb) {
|
||||
worker->read_cb(LFRFIDWorkerReadSenseEnd, last_protocol, worker->cb_ctx);
|
||||
}
|
||||
|
||||
furi_hal_rfid_tim_read_capture_stop();
|
||||
furi_hal_rfid_tim_read_stop();
|
||||
furi_hal_rfid_pins_reset();
|
||||
|
||||
varint_pair_free(ctx.pair);
|
||||
buffer_stream_free(ctx.stream);
|
||||
|
||||
free(protocol_data);
|
||||
free(last_data);
|
||||
|
||||
#ifdef LFRFID_WORKER_READ_DEBUG_GPIO
|
||||
furi_hal_gpio_init_simple(LFRFID_WORKER_READ_DEBUG_GPIO_VALUE, GpioModeAnalog);
|
||||
furi_hal_gpio_init_simple(LFRFID_WORKER_READ_DEBUG_GPIO_LOAD, GpioModeAnalog);
|
||||
#endif
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
static void lfrfid_worker_mode_read_process(LFRFIDWorker* worker) {
|
||||
LFRFIDFeature feature = LFRFIDFeatureASK;
|
||||
ProtocolId read_result = PROTOCOL_NO;
|
||||
LFRFIDWorkerReadState state;
|
||||
|
||||
if(worker->read_type == LFRFIDWorkerReadTypePSKOnly) {
|
||||
feature = LFRFIDFeaturePSK;
|
||||
} else {
|
||||
feature = LFRFIDFeatureASK;
|
||||
}
|
||||
|
||||
if(worker->read_type == LFRFIDWorkerReadTypeAuto) {
|
||||
while(1) {
|
||||
// read for a while
|
||||
state = lfrfid_worker_read_internal(
|
||||
worker, feature, LFRFID_WORKER_READ_SWITCH_TIME_MS, &read_result);
|
||||
|
||||
if(state == LFRFIDWorkerReadOK || state == LFRFIDWorkerReadExit) {
|
||||
break;
|
||||
}
|
||||
|
||||
// switch to next feature
|
||||
if(feature == LFRFIDFeatureASK) {
|
||||
feature = LFRFIDFeaturePSK;
|
||||
} else {
|
||||
feature = LFRFIDFeatureASK;
|
||||
}
|
||||
|
||||
lfrfid_worker_delay(worker, LFRFID_WORKER_READ_DROP_TIME_MS);
|
||||
}
|
||||
} else {
|
||||
while(1) {
|
||||
if(worker->read_type == LFRFIDWorkerReadTypeASKOnly) {
|
||||
state = lfrfid_worker_read_internal(worker, feature, UINT32_MAX, &read_result);
|
||||
} else {
|
||||
state = lfrfid_worker_read_internal(
|
||||
worker, feature, LFRFID_WORKER_READ_SWITCH_TIME_MS, &read_result);
|
||||
}
|
||||
|
||||
if(state == LFRFIDWorkerReadOK || state == LFRFIDWorkerReadExit) {
|
||||
break;
|
||||
}
|
||||
|
||||
lfrfid_worker_delay(worker, LFRFID_WORKER_READ_DROP_TIME_MS);
|
||||
}
|
||||
}
|
||||
|
||||
if(state == LFRFIDWorkerReadOK && worker->read_cb) {
|
||||
worker->read_cb(LFRFIDWorkerReadDone, read_result, worker->cb_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
/**************************************************************************************************/
|
||||
/******************************************** EMULATE *********************************************/
|
||||
/**************************************************************************************************/
|
||||
|
||||
typedef struct {
|
||||
uint32_t duration[LFRFID_WORKER_EMULATE_BUFFER_SIZE];
|
||||
uint32_t pulse[LFRFID_WORKER_EMULATE_BUFFER_SIZE];
|
||||
} LFRFIDWorkerEmulateBuffer;
|
||||
|
||||
typedef enum {
|
||||
HalfTransfer,
|
||||
TransferComplete,
|
||||
} LFRFIDWorkerEmulateDMAEvent;
|
||||
|
||||
static void lfrfid_worker_emulate_dma_isr(bool half, void* context) {
|
||||
StreamBufferHandle_t stream = context;
|
||||
uint32_t flag = half ? HalfTransfer : TransferComplete;
|
||||
xStreamBufferSendFromISR(stream, &flag, sizeof(uint32_t), pdFALSE);
|
||||
}
|
||||
|
||||
static void lfrfid_worker_mode_emulate_process(LFRFIDWorker* worker) {
|
||||
LFRFIDWorkerEmulateBuffer* buffer = malloc(sizeof(LFRFIDWorkerEmulateBuffer));
|
||||
StreamBufferHandle_t stream = xStreamBufferCreate(sizeof(uint32_t), sizeof(uint32_t));
|
||||
LFRFIDProtocol protocol = worker->protocol;
|
||||
PulseGlue* pulse_glue = pulse_glue_alloc();
|
||||
|
||||
protocol_dict_encoder_start(worker->protocols, protocol);
|
||||
|
||||
for(size_t i = 0; i < LFRFID_WORKER_EMULATE_BUFFER_SIZE; i++) {
|
||||
bool pulse_pop = false;
|
||||
while(!pulse_pop) {
|
||||
LevelDuration level_duration =
|
||||
protocol_dict_encoder_yield(worker->protocols, protocol);
|
||||
pulse_pop = pulse_glue_push(
|
||||
pulse_glue,
|
||||
level_duration_get_level(level_duration),
|
||||
level_duration_get_duration(level_duration));
|
||||
}
|
||||
uint32_t duration, pulse;
|
||||
pulse_glue_pop(pulse_glue, &duration, &pulse);
|
||||
buffer->duration[i] = duration - 1;
|
||||
buffer->pulse[i] = pulse;
|
||||
}
|
||||
|
||||
#ifdef LFRFID_WORKER_READ_DEBUG_GPIO
|
||||
furi_hal_gpio_init_simple(LFRFID_WORKER_READ_DEBUG_GPIO_LOAD, GpioModeOutputPushPull);
|
||||
#endif
|
||||
|
||||
furi_hal_rfid_tim_emulate_dma_start(
|
||||
buffer->duration,
|
||||
buffer->pulse,
|
||||
LFRFID_WORKER_EMULATE_BUFFER_SIZE,
|
||||
lfrfid_worker_emulate_dma_isr,
|
||||
stream);
|
||||
|
||||
while(true) {
|
||||
uint32_t flag = 0;
|
||||
size_t size = xStreamBufferReceive(stream, &flag, sizeof(uint32_t), 100);
|
||||
|
||||
#ifdef LFRFID_WORKER_READ_DEBUG_GPIO
|
||||
furi_hal_gpio_write(LFRFID_WORKER_READ_DEBUG_GPIO_LOAD, true);
|
||||
#endif
|
||||
|
||||
if(size == sizeof(uint32_t)) {
|
||||
size_t start = 0;
|
||||
|
||||
if(flag == HalfTransfer) {
|
||||
start = 0;
|
||||
} else if(flag == TransferComplete) {
|
||||
start = (LFRFID_WORKER_EMULATE_BUFFER_SIZE / 2);
|
||||
}
|
||||
|
||||
for(size_t i = 0; i < (LFRFID_WORKER_EMULATE_BUFFER_SIZE / 2); i++) {
|
||||
bool pulse_pop = false;
|
||||
while(!pulse_pop) {
|
||||
LevelDuration level_duration =
|
||||
protocol_dict_encoder_yield(worker->protocols, protocol);
|
||||
pulse_pop = pulse_glue_push(
|
||||
pulse_glue,
|
||||
level_duration_get_level(level_duration),
|
||||
level_duration_get_duration(level_duration));
|
||||
}
|
||||
uint32_t duration, pulse;
|
||||
pulse_glue_pop(pulse_glue, &duration, &pulse);
|
||||
buffer->duration[start + i] = duration - 1;
|
||||
buffer->pulse[start + i] = pulse;
|
||||
}
|
||||
}
|
||||
|
||||
if(lfrfid_worker_check_for_stop(worker)) {
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef LFRFID_WORKER_READ_DEBUG_GPIO
|
||||
furi_hal_gpio_write(LFRFID_WORKER_READ_DEBUG_GPIO_LOAD, false);
|
||||
#endif
|
||||
}
|
||||
|
||||
furi_hal_rfid_tim_emulate_dma_stop();
|
||||
|
||||
#ifdef LFRFID_WORKER_READ_DEBUG_GPIO
|
||||
furi_hal_gpio_init_simple(LFRFID_WORKER_READ_DEBUG_GPIO_LOAD, GpioModeAnalog);
|
||||
#endif
|
||||
|
||||
free(buffer);
|
||||
vStreamBufferDelete(stream);
|
||||
pulse_glue_free(pulse_glue);
|
||||
}
|
||||
|
||||
/**************************************************************************************************/
|
||||
/********************************************* WRITE **********************************************/
|
||||
/**************************************************************************************************/
|
||||
|
||||
static void lfrfid_worker_mode_write_process(LFRFIDWorker* worker) {
|
||||
LFRFIDProtocol protocol = worker->protocol;
|
||||
LFRFIDWriteRequest* request = malloc(sizeof(LFRFIDWriteRequest));
|
||||
request->write_type = LFRFIDWriteTypeT5577;
|
||||
|
||||
bool can_be_written = protocol_dict_get_write_data(worker->protocols, protocol, request);
|
||||
|
||||
uint32_t write_start_time = furi_get_tick();
|
||||
bool too_long = false;
|
||||
size_t unsuccessful_reads = 0;
|
||||
|
||||
size_t data_size = protocol_dict_get_data_size(worker->protocols, protocol);
|
||||
uint8_t* verify_data = malloc(data_size);
|
||||
uint8_t* read_data = malloc(data_size);
|
||||
protocol_dict_get_data(worker->protocols, protocol, verify_data, data_size);
|
||||
|
||||
if(can_be_written) {
|
||||
while(!lfrfid_worker_check_for_stop(worker)) {
|
||||
FURI_LOG_D(TAG, "Data write");
|
||||
t5577_write(&request->t5577);
|
||||
|
||||
ProtocolId read_result = PROTOCOL_NO;
|
||||
LFRFIDWorkerReadState state = lfrfid_worker_read_internal(
|
||||
worker,
|
||||
protocol_dict_get_features(worker->protocols, protocol),
|
||||
LFRFID_WORKER_WRITE_VERIFY_TIME_MS,
|
||||
&read_result);
|
||||
|
||||
if(state == LFRFIDWorkerReadOK) {
|
||||
protocol_dict_get_data(worker->protocols, protocol, read_data, data_size);
|
||||
|
||||
if(memcmp(read_data, verify_data, data_size) == 0) {
|
||||
if(worker->write_cb) {
|
||||
worker->write_cb(LFRFIDWorkerWriteOK, worker->cb_ctx);
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
unsuccessful_reads++;
|
||||
|
||||
if(unsuccessful_reads == LFRFID_WORKER_WRITE_MAX_UNSUCCESSFUL_READS) {
|
||||
if(worker->write_cb) {
|
||||
worker->write_cb(LFRFIDWorkerWriteFobCannotBeWritten, worker->cb_ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if(state == LFRFIDWorkerReadExit) {
|
||||
break;
|
||||
}
|
||||
|
||||
if(!too_long &&
|
||||
(furi_get_tick() - write_start_time) > LFRFID_WORKER_WRITE_TOO_LONG_TIME_MS) {
|
||||
too_long = true;
|
||||
if(worker->write_cb) {
|
||||
worker->write_cb(LFRFIDWorkerWriteTooLongToWrite, worker->cb_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
lfrfid_worker_delay(worker, LFRFID_WORKER_WRITE_DROP_TIME_MS);
|
||||
}
|
||||
} else {
|
||||
if(worker->write_cb) {
|
||||
worker->write_cb(LFRFIDWorkerWriteProtocolCannotBeWritten, worker->cb_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
free(request);
|
||||
free(verify_data);
|
||||
free(read_data);
|
||||
}
|
||||
|
||||
/**************************************************************************************************/
|
||||
/******************************************* READ RAW *********************************************/
|
||||
/**************************************************************************************************/
|
||||
|
||||
static void lfrfid_worker_mode_read_raw_process(LFRFIDWorker* worker) {
|
||||
LFRFIDRawWorker* raw_worker = lfrfid_raw_worker_alloc();
|
||||
|
||||
switch(worker->read_type) {
|
||||
case LFRFIDWorkerReadTypePSKOnly:
|
||||
lfrfid_raw_worker_start_read(
|
||||
raw_worker, worker->raw_filename, 62500, 0.25, worker->read_raw_cb, worker->cb_ctx);
|
||||
break;
|
||||
case LFRFIDWorkerReadTypeASKOnly:
|
||||
lfrfid_raw_worker_start_read(
|
||||
raw_worker, worker->raw_filename, 125000, 0.5, worker->read_raw_cb, worker->cb_ctx);
|
||||
break;
|
||||
default:
|
||||
furi_crash("RAW can be only PSK or ASK");
|
||||
break;
|
||||
}
|
||||
|
||||
while(!lfrfid_worker_check_for_stop(worker)) {
|
||||
furi_delay_ms(100);
|
||||
}
|
||||
|
||||
lfrfid_raw_worker_stop(raw_worker);
|
||||
lfrfid_raw_worker_free(raw_worker);
|
||||
}
|
||||
|
||||
/**************************************************************************************************/
|
||||
/***************************************** EMULATE RAW ********************************************/
|
||||
/**************************************************************************************************/
|
||||
|
||||
static void lfrfid_worker_mode_emulate_raw_process(LFRFIDWorker* worker) {
|
||||
LFRFIDRawWorker* raw_worker = lfrfid_raw_worker_alloc();
|
||||
|
||||
lfrfid_raw_worker_start_emulate(
|
||||
raw_worker, worker->raw_filename, worker->emulate_raw_cb, worker->cb_ctx);
|
||||
|
||||
while(!lfrfid_worker_check_for_stop(worker)) {
|
||||
furi_delay_ms(100);
|
||||
}
|
||||
|
||||
lfrfid_raw_worker_stop(raw_worker);
|
||||
lfrfid_raw_worker_free(raw_worker);
|
||||
}
|
||||
|
||||
/**************************************************************************************************/
|
||||
/******************************************** MODES ***********************************************/
|
||||
/**************************************************************************************************/
|
||||
|
||||
const LFRFIDWorkerModeType lfrfid_worker_modes[] = {
|
||||
[LFRFIDWorkerIdle] = {.process = NULL},
|
||||
[LFRFIDWorkerRead] = {.process = lfrfid_worker_mode_read_process},
|
||||
[LFRFIDWorkerWrite] = {.process = lfrfid_worker_mode_write_process},
|
||||
[LFRFIDWorkerEmulate] = {.process = lfrfid_worker_mode_emulate_process},
|
||||
[LFRFIDWorkerReadRaw] = {.process = lfrfid_worker_mode_read_raw_process},
|
||||
[LFRFIDWorkerEmulateRaw] = {.process = lfrfid_worker_mode_emulate_raw_process},
|
||||
};
|
22
lib/lfrfid/protocols/lfrfid_protocols.c
Normal file
22
lib/lfrfid/protocols/lfrfid_protocols.c
Normal file
@@ -0,0 +1,22 @@
|
||||
#include "lfrfid_protocols.h"
|
||||
#include "protocol_em4100.h"
|
||||
#include "protocol_h10301.h"
|
||||
#include "protocol_indala26.h"
|
||||
#include "protocol_io_prox_xsf.h"
|
||||
#include "protocol_awid.h"
|
||||
#include "protocol_fdx_a.h"
|
||||
#include "protocol_fdx_b.h"
|
||||
#include "protocol_hid_generic.h"
|
||||
#include "protocol_hid_ex_generic.h"
|
||||
|
||||
const ProtocolBase* lfrfid_protocols[] = {
|
||||
[LFRFIDProtocolEM4100] = &protocol_em4100,
|
||||
[LFRFIDProtocolH10301] = &protocol_h10301,
|
||||
[LFRFIDProtocolIndala26] = &protocol_indala26,
|
||||
[LFRFIDProtocolIOProxXSF] = &protocol_io_prox_xsf,
|
||||
[LFRFIDProtocolAwid] = &protocol_awid,
|
||||
[LFRFIDProtocolFDXA] = &protocol_fdx_a,
|
||||
[LFRFIDProtocolFDXB] = &protocol_fdx_b,
|
||||
[LFRFIDProtocolHidGeneric] = &protocol_hid_generic,
|
||||
[LFRFIDProtocolHidExGeneric] = &protocol_hid_ex_generic,
|
||||
};
|
35
lib/lfrfid/protocols/lfrfid_protocols.h
Normal file
35
lib/lfrfid/protocols/lfrfid_protocols.h
Normal file
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
#include <toolbox/protocols/protocol.h>
|
||||
#include "../tools/t5577.h"
|
||||
|
||||
typedef enum {
|
||||
LFRFIDFeatureASK = 1 << 0, /** ASK Demodulation */
|
||||
LFRFIDFeaturePSK = 1 << 1, /** PSK Demodulation */
|
||||
} LFRFIDFeature;
|
||||
|
||||
typedef enum {
|
||||
LFRFIDProtocolEM4100,
|
||||
LFRFIDProtocolH10301,
|
||||
LFRFIDProtocolIndala26,
|
||||
LFRFIDProtocolIOProxXSF,
|
||||
LFRFIDProtocolAwid,
|
||||
LFRFIDProtocolFDXA,
|
||||
LFRFIDProtocolFDXB,
|
||||
LFRFIDProtocolHidGeneric,
|
||||
LFRFIDProtocolHidExGeneric,
|
||||
|
||||
LFRFIDProtocolMax,
|
||||
} LFRFIDProtocol;
|
||||
|
||||
extern const ProtocolBase* lfrfid_protocols[];
|
||||
|
||||
typedef enum {
|
||||
LFRFIDWriteTypeT5577,
|
||||
} LFRFIDWriteType;
|
||||
|
||||
typedef struct {
|
||||
LFRFIDWriteType write_type;
|
||||
union {
|
||||
LFRFIDT5577 t5577;
|
||||
};
|
||||
} LFRFIDWriteRequest;
|
239
lib/lfrfid/protocols/protocol_awid.c
Normal file
239
lib/lfrfid/protocols/protocol_awid.c
Normal file
@@ -0,0 +1,239 @@
|
||||
#include <furi.h>
|
||||
#include <toolbox/protocols/protocol.h>
|
||||
#include <lfrfid/tools/fsk_demod.h>
|
||||
#include <lfrfid/tools/fsk_osc.h>
|
||||
#include <lfrfid/tools/bit_lib.h>
|
||||
#include "lfrfid_protocols.h"
|
||||
|
||||
#define JITTER_TIME (20)
|
||||
#define MIN_TIME (64 - JITTER_TIME)
|
||||
#define MAX_TIME (80 + JITTER_TIME)
|
||||
|
||||
#define AWID_DECODED_DATA_SIZE (9)
|
||||
|
||||
#define AWID_ENCODED_BIT_SIZE (96)
|
||||
#define AWID_ENCODED_DATA_SIZE (((AWID_ENCODED_BIT_SIZE) / 8) + 1)
|
||||
#define AWID_ENCODED_DATA_LAST (AWID_ENCODED_DATA_SIZE - 1)
|
||||
|
||||
typedef struct {
|
||||
FSKDemod* fsk_demod;
|
||||
} ProtocolAwidDecoder;
|
||||
|
||||
typedef struct {
|
||||
FSKOsc* fsk_osc;
|
||||
uint8_t encoded_index;
|
||||
} ProtocolAwidEncoder;
|
||||
|
||||
typedef struct {
|
||||
ProtocolAwidDecoder decoder;
|
||||
ProtocolAwidEncoder encoder;
|
||||
uint8_t encoded_data[AWID_ENCODED_DATA_SIZE];
|
||||
uint8_t data[AWID_DECODED_DATA_SIZE];
|
||||
} ProtocolAwid;
|
||||
|
||||
ProtocolAwid* protocol_awid_alloc(void) {
|
||||
ProtocolAwid* protocol = malloc(sizeof(ProtocolAwid));
|
||||
protocol->decoder.fsk_demod = fsk_demod_alloc(MIN_TIME, 6, MAX_TIME, 5);
|
||||
protocol->encoder.fsk_osc = fsk_osc_alloc(8, 10, 50);
|
||||
|
||||
return protocol;
|
||||
};
|
||||
|
||||
void protocol_awid_free(ProtocolAwid* protocol) {
|
||||
fsk_demod_free(protocol->decoder.fsk_demod);
|
||||
fsk_osc_free(protocol->encoder.fsk_osc);
|
||||
free(protocol);
|
||||
};
|
||||
|
||||
uint8_t* protocol_awid_get_data(ProtocolAwid* protocol) {
|
||||
return protocol->data;
|
||||
};
|
||||
|
||||
void protocol_awid_decoder_start(ProtocolAwid* protocol) {
|
||||
memset(protocol->encoded_data, 0, AWID_ENCODED_DATA_SIZE);
|
||||
};
|
||||
|
||||
static bool protocol_awid_can_be_decoded(const uint8_t* data) {
|
||||
bool result = false;
|
||||
|
||||
// Index map
|
||||
// 0 10 20 30 40 50 60
|
||||
// | | | | | | |
|
||||
// 01234567 890 1 234 5 678 9 012 3 456 7 890 1 234 5 678 9 012 3 456 7 890 1 234 5 678 9 012 3 - to 96
|
||||
// -----------------------------------------------------------------------------
|
||||
// 00000001 000 1 110 1 101 1 011 1 101 1 010 0 000 1 000 1 010 0 001 0 110 1 100 0 000 1 000 1
|
||||
// preamble bbb o bbb o bbw o fff o fff o ffc o ccc o ccc o ccc o ccc o ccc o wxx o xxx o xxx o - to 96
|
||||
// |---26 bit---| |-----117----||-------------142-------------|
|
||||
// b = format bit len, o = odd parity of last 3 bits
|
||||
// f = facility code, c = card number
|
||||
// w = wiegand parity
|
||||
// (26 bit format shown)
|
||||
|
||||
do {
|
||||
// check preamble and spacing
|
||||
if(data[0] != 0b00000001 || data[AWID_ENCODED_DATA_LAST] != 0b00000001) break;
|
||||
|
||||
// check odd parity for every 4 bits starting from the second byte
|
||||
bool parity_error = bit_lib_test_parity(data, 8, 88, BitLibParityOdd, 4);
|
||||
if(parity_error) break;
|
||||
|
||||
result = true;
|
||||
} while(false);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void protocol_awid_decode(uint8_t* encoded_data, uint8_t* decoded_data) {
|
||||
bit_lib_remove_bit_every_nth(encoded_data, 8, 88, 4);
|
||||
bit_lib_copy_bits(decoded_data, 0, 66, encoded_data, 8);
|
||||
}
|
||||
|
||||
bool protocol_awid_decoder_feed(ProtocolAwid* protocol, bool level, uint32_t duration) {
|
||||
bool value;
|
||||
uint32_t count;
|
||||
bool result = false;
|
||||
|
||||
fsk_demod_feed(protocol->decoder.fsk_demod, level, duration, &value, &count);
|
||||
if(count > 0) {
|
||||
for(size_t i = 0; i < count; i++) {
|
||||
bit_lib_push_bit(protocol->encoded_data, AWID_ENCODED_DATA_SIZE, value);
|
||||
if(protocol_awid_can_be_decoded(protocol->encoded_data)) {
|
||||
protocol_awid_decode(protocol->encoded_data, protocol->data);
|
||||
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
static void protocol_awid_encode(const uint8_t* decoded_data, uint8_t* encoded_data) {
|
||||
memset(encoded_data, 0, AWID_ENCODED_DATA_SIZE);
|
||||
|
||||
// preamble
|
||||
bit_lib_set_bits(encoded_data, 0, 0b00000001, 8);
|
||||
|
||||
for(size_t i = 0; i < 88 / 4; i++) {
|
||||
uint8_t value = bit_lib_get_bits(decoded_data, i * 3, 3) << 1;
|
||||
value |= bit_lib_test_parity_32(value, BitLibParityOdd);
|
||||
bit_lib_set_bits(encoded_data, 8 + i * 4, value, 4);
|
||||
}
|
||||
};
|
||||
|
||||
bool protocol_awid_encoder_start(ProtocolAwid* protocol) {
|
||||
protocol_awid_encode(protocol->data, (uint8_t*)protocol->encoded_data);
|
||||
protocol->encoder.encoded_index = 0;
|
||||
fsk_osc_reset(protocol->encoder.fsk_osc);
|
||||
return true;
|
||||
};
|
||||
|
||||
LevelDuration protocol_awid_encoder_yield(ProtocolAwid* protocol) {
|
||||
bool level;
|
||||
uint32_t duration;
|
||||
|
||||
bool bit = bit_lib_get_bit(protocol->encoded_data, protocol->encoder.encoded_index);
|
||||
bool advance = fsk_osc_next_half(protocol->encoder.fsk_osc, bit, &level, &duration);
|
||||
|
||||
if(advance) {
|
||||
bit_lib_increment_index(protocol->encoder.encoded_index, AWID_ENCODED_BIT_SIZE);
|
||||
}
|
||||
return level_duration_make(level, duration);
|
||||
};
|
||||
|
||||
void protocol_awid_render_data(ProtocolAwid* protocol, string_t result) {
|
||||
// Index map
|
||||
// 0 10 20 30 40 50 60
|
||||
// | | | | | | |
|
||||
// 01234567 8 90123456 7890123456789012 3 456789012345678901234567890123456
|
||||
// ------------------------------------------------------------------------
|
||||
// 00011010 1 01110101 0000000010001110 1 000000000000000000000000000000000
|
||||
// bbbbbbbb w ffffffff cccccccccccccccc w xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
// |26 bit| |-117--| |-----142------|
|
||||
// b = format bit len, o = odd parity of last 3 bits
|
||||
// f = facility code, c = card number
|
||||
// w = wiegand parity
|
||||
// (26 bit format shown)
|
||||
|
||||
uint8_t* decoded_data = protocol->data;
|
||||
uint8_t format_length = decoded_data[0];
|
||||
|
||||
string_cat_printf(result, "Format: %d\r\n", format_length);
|
||||
if(format_length == 26) {
|
||||
uint8_t facility;
|
||||
bit_lib_copy_bits(&facility, 0, 8, decoded_data, 9);
|
||||
|
||||
uint16_t card_id;
|
||||
bit_lib_copy_bits((uint8_t*)&card_id, 8, 8, decoded_data, 17);
|
||||
bit_lib_copy_bits((uint8_t*)&card_id, 0, 8, decoded_data, 25);
|
||||
string_cat_printf(result, "Facility: %d\r\n", facility);
|
||||
string_cat_printf(result, "Card: %d", card_id);
|
||||
} else {
|
||||
// print 66 bits as hex
|
||||
string_cat_printf(result, "Data: ");
|
||||
for(size_t i = 0; i < AWID_DECODED_DATA_SIZE; i++) {
|
||||
string_cat_printf(result, "%02X", decoded_data[i]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void protocol_awid_render_brief_data(ProtocolAwid* protocol, string_t result) {
|
||||
uint8_t* decoded_data = protocol->data;
|
||||
uint8_t format_length = decoded_data[0];
|
||||
|
||||
string_cat_printf(result, "Format: %d\r\n", format_length);
|
||||
if(format_length == 26) {
|
||||
uint8_t facility;
|
||||
bit_lib_copy_bits(&facility, 0, 8, decoded_data, 9);
|
||||
|
||||
uint16_t card_id;
|
||||
bit_lib_copy_bits((uint8_t*)&card_id, 8, 8, decoded_data, 17);
|
||||
bit_lib_copy_bits((uint8_t*)&card_id, 0, 8, decoded_data, 25);
|
||||
string_cat_printf(result, "ID: %03u,%05u", facility, card_id);
|
||||
} else {
|
||||
string_cat_printf(result, "Data: unknown");
|
||||
}
|
||||
};
|
||||
|
||||
bool protocol_awid_write_data(ProtocolAwid* protocol, void* data) {
|
||||
LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data;
|
||||
bool result = false;
|
||||
|
||||
protocol_awid_encode(protocol->data, (uint8_t*)protocol->encoded_data);
|
||||
|
||||
if(request->write_type == LFRFIDWriteTypeT5577) {
|
||||
request->t5577.block[0] = LFRFID_T5577_MODULATION_FSK2a | LFRFID_T5577_BITRATE_RF_50 |
|
||||
(3 << LFRFID_T5577_MAXBLOCK_SHIFT);
|
||||
request->t5577.block[1] = bit_lib_get_bits_32(protocol->encoded_data, 0, 32);
|
||||
request->t5577.block[2] = bit_lib_get_bits_32(protocol->encoded_data, 32, 32);
|
||||
request->t5577.block[3] = bit_lib_get_bits_32(protocol->encoded_data, 64, 32);
|
||||
request->t5577.blocks_to_write = 4;
|
||||
result = true;
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
const ProtocolBase protocol_awid = {
|
||||
.name = "AWID",
|
||||
.manufacturer = "AWIG",
|
||||
.data_size = AWID_DECODED_DATA_SIZE,
|
||||
.features = LFRFIDFeatureASK,
|
||||
.validate_count = 3,
|
||||
.alloc = (ProtocolAlloc)protocol_awid_alloc,
|
||||
.free = (ProtocolFree)protocol_awid_free,
|
||||
.get_data = (ProtocolGetData)protocol_awid_get_data,
|
||||
.decoder =
|
||||
{
|
||||
.start = (ProtocolDecoderStart)protocol_awid_decoder_start,
|
||||
.feed = (ProtocolDecoderFeed)protocol_awid_decoder_feed,
|
||||
},
|
||||
.encoder =
|
||||
{
|
||||
.start = (ProtocolEncoderStart)protocol_awid_encoder_start,
|
||||
.yield = (ProtocolEncoderYield)protocol_awid_encoder_yield,
|
||||
},
|
||||
.render_data = (ProtocolRenderData)protocol_awid_render_data,
|
||||
.render_brief_data = (ProtocolRenderData)protocol_awid_render_brief_data,
|
||||
.write_data = (ProtocolWriteData)protocol_awid_write_data,
|
||||
};
|
4
lib/lfrfid/protocols/protocol_awid.h
Normal file
4
lib/lfrfid/protocols/protocol_awid.h
Normal file
@@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
#include <toolbox/protocols/protocol.h>
|
||||
|
||||
extern const ProtocolBase protocol_awid;
|
292
lib/lfrfid/protocols/protocol_em4100.c
Normal file
292
lib/lfrfid/protocols/protocol_em4100.c
Normal file
@@ -0,0 +1,292 @@
|
||||
#include <furi.h>
|
||||
#include <toolbox/protocols/protocol.h>
|
||||
#include <toolbox/manchester_decoder.h>
|
||||
#include "lfrfid_protocols.h"
|
||||
|
||||
typedef uint64_t EM4100DecodedData;
|
||||
|
||||
#define EM_HEADER_POS (55)
|
||||
#define EM_HEADER_MASK (0x1FFLLU << EM_HEADER_POS)
|
||||
|
||||
#define EM_FIRST_ROW_POS (50)
|
||||
|
||||
#define EM_ROW_COUNT (10)
|
||||
#define EM_COLUMN_COUNT (4)
|
||||
#define EM_BITS_PER_ROW_COUNT (EM_COLUMN_COUNT + 1)
|
||||
|
||||
#define EM_COLUMN_POS (4)
|
||||
#define EM_STOP_POS (0)
|
||||
#define EM_STOP_MASK (0x1LLU << EM_STOP_POS)
|
||||
|
||||
#define EM_HEADER_AND_STOP_MASK (EM_HEADER_MASK | EM_STOP_MASK)
|
||||
#define EM_HEADER_AND_STOP_DATA (EM_HEADER_MASK)
|
||||
|
||||
#define EM4100_DECODED_DATA_SIZE (5)
|
||||
#define EM4100_ENCODED_DATA_SIZE (sizeof(EM4100DecodedData))
|
||||
|
||||
#define EM4100_CLOCK_PER_BIT (64)
|
||||
|
||||
#define EM_READ_SHORT_TIME (256)
|
||||
#define EM_READ_LONG_TIME (512)
|
||||
#define EM_READ_JITTER_TIME (100)
|
||||
|
||||
#define EM_READ_SHORT_TIME_LOW (EM_READ_SHORT_TIME - EM_READ_JITTER_TIME)
|
||||
#define EM_READ_SHORT_TIME_HIGH (EM_READ_SHORT_TIME + EM_READ_JITTER_TIME)
|
||||
#define EM_READ_LONG_TIME_LOW (EM_READ_LONG_TIME - EM_READ_JITTER_TIME)
|
||||
#define EM_READ_LONG_TIME_HIGH (EM_READ_LONG_TIME + EM_READ_JITTER_TIME)
|
||||
|
||||
typedef struct {
|
||||
uint8_t data[EM4100_DECODED_DATA_SIZE];
|
||||
|
||||
EM4100DecodedData encoded_data;
|
||||
uint8_t encoded_data_index;
|
||||
bool encoded_polarity;
|
||||
|
||||
ManchesterState decoder_manchester_state;
|
||||
} ProtocolEM4100;
|
||||
|
||||
ProtocolEM4100* protocol_em4100_alloc(void) {
|
||||
ProtocolEM4100* proto = malloc(sizeof(ProtocolEM4100));
|
||||
return (void*)proto;
|
||||
};
|
||||
|
||||
void protocol_em4100_free(ProtocolEM4100* proto) {
|
||||
free(proto);
|
||||
};
|
||||
|
||||
uint8_t* protocol_em4100_get_data(ProtocolEM4100* proto) {
|
||||
return proto->data;
|
||||
};
|
||||
|
||||
static void em4100_decode(
|
||||
const uint8_t* encoded_data,
|
||||
const uint8_t encoded_data_size,
|
||||
uint8_t* decoded_data,
|
||||
const uint8_t decoded_data_size) {
|
||||
furi_check(decoded_data_size >= EM4100_DECODED_DATA_SIZE);
|
||||
furi_check(encoded_data_size >= EM4100_ENCODED_DATA_SIZE);
|
||||
|
||||
uint8_t decoded_data_index = 0;
|
||||
EM4100DecodedData card_data = *((EM4100DecodedData*)(encoded_data));
|
||||
|
||||
// clean result
|
||||
memset(decoded_data, 0, decoded_data_size);
|
||||
|
||||
// header
|
||||
for(uint8_t i = 0; i < 9; i++) {
|
||||
card_data = card_data << 1;
|
||||
}
|
||||
|
||||
// nibbles
|
||||
uint8_t value = 0;
|
||||
for(uint8_t r = 0; r < EM_ROW_COUNT; r++) {
|
||||
uint8_t nibble = 0;
|
||||
for(uint8_t i = 0; i < 5; i++) {
|
||||
if(i < 4) nibble = (nibble << 1) | (card_data & (1LLU << 63) ? 1 : 0);
|
||||
card_data = card_data << 1;
|
||||
}
|
||||
value = (value << 4) | nibble;
|
||||
if(r % 2) {
|
||||
decoded_data[decoded_data_index] |= value;
|
||||
decoded_data_index++;
|
||||
value = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool em4100_can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) {
|
||||
furi_check(encoded_data_size >= EM4100_ENCODED_DATA_SIZE);
|
||||
const EM4100DecodedData* card_data = (EM4100DecodedData*)encoded_data;
|
||||
|
||||
// check header and stop bit
|
||||
if((*card_data & EM_HEADER_AND_STOP_MASK) != EM_HEADER_AND_STOP_DATA) return false;
|
||||
|
||||
// check row parity
|
||||
for(uint8_t i = 0; i < EM_ROW_COUNT; i++) {
|
||||
uint8_t parity_sum = 0;
|
||||
|
||||
for(uint8_t j = 0; j < EM_BITS_PER_ROW_COUNT; j++) {
|
||||
parity_sum += (*card_data >> (EM_FIRST_ROW_POS - i * EM_BITS_PER_ROW_COUNT + j)) & 1;
|
||||
}
|
||||
|
||||
if((parity_sum % 2)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// check columns parity
|
||||
for(uint8_t i = 0; i < EM_COLUMN_COUNT; i++) {
|
||||
uint8_t parity_sum = 0;
|
||||
|
||||
for(uint8_t j = 0; j < EM_ROW_COUNT + 1; j++) {
|
||||
parity_sum += (*card_data >> (EM_COLUMN_POS - i + j * EM_BITS_PER_ROW_COUNT)) & 1;
|
||||
}
|
||||
|
||||
if((parity_sum % 2)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void protocol_em4100_decoder_start(ProtocolEM4100* proto) {
|
||||
memset(proto->data, 0, EM4100_DECODED_DATA_SIZE);
|
||||
proto->encoded_data = 0;
|
||||
manchester_advance(
|
||||
proto->decoder_manchester_state,
|
||||
ManchesterEventReset,
|
||||
&proto->decoder_manchester_state,
|
||||
NULL);
|
||||
};
|
||||
|
||||
bool protocol_em4100_decoder_feed(ProtocolEM4100* proto, bool level, uint32_t duration) {
|
||||
bool result = false;
|
||||
|
||||
ManchesterEvent event = ManchesterEventReset;
|
||||
|
||||
if(duration > EM_READ_SHORT_TIME_LOW && duration < EM_READ_SHORT_TIME_HIGH) {
|
||||
if(!level) {
|
||||
event = ManchesterEventShortHigh;
|
||||
} else {
|
||||
event = ManchesterEventShortLow;
|
||||
}
|
||||
} else if(duration > EM_READ_LONG_TIME_LOW && duration < EM_READ_LONG_TIME_HIGH) {
|
||||
if(!level) {
|
||||
event = ManchesterEventLongHigh;
|
||||
} else {
|
||||
event = ManchesterEventLongLow;
|
||||
}
|
||||
}
|
||||
|
||||
if(event != ManchesterEventReset) {
|
||||
bool data;
|
||||
bool data_ok = manchester_advance(
|
||||
proto->decoder_manchester_state, event, &proto->decoder_manchester_state, &data);
|
||||
|
||||
if(data_ok) {
|
||||
proto->encoded_data = (proto->encoded_data << 1) | data;
|
||||
|
||||
if(em4100_can_be_decoded((uint8_t*)&proto->encoded_data, sizeof(EM4100DecodedData))) {
|
||||
em4100_decode(
|
||||
(uint8_t*)&proto->encoded_data,
|
||||
sizeof(EM4100DecodedData),
|
||||
proto->data,
|
||||
EM4100_DECODED_DATA_SIZE);
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
static void em4100_write_nibble(bool low_nibble, uint8_t data, EM4100DecodedData* encoded_data) {
|
||||
uint8_t parity_sum = 0;
|
||||
uint8_t start = 0;
|
||||
if(!low_nibble) start = 4;
|
||||
|
||||
for(int8_t i = (start + 3); i >= start; i--) {
|
||||
parity_sum += (data >> i) & 1;
|
||||
*encoded_data = (*encoded_data << 1) | ((data >> i) & 1);
|
||||
}
|
||||
|
||||
*encoded_data = (*encoded_data << 1) | ((parity_sum % 2) & 1);
|
||||
}
|
||||
|
||||
bool protocol_em4100_encoder_start(ProtocolEM4100* proto) {
|
||||
// header
|
||||
proto->encoded_data = 0b111111111;
|
||||
|
||||
// data
|
||||
for(uint8_t i = 0; i < EM4100_DECODED_DATA_SIZE; i++) {
|
||||
em4100_write_nibble(false, proto->data[i], &proto->encoded_data);
|
||||
em4100_write_nibble(true, proto->data[i], &proto->encoded_data);
|
||||
}
|
||||
|
||||
// column parity and stop bit
|
||||
uint8_t parity_sum;
|
||||
|
||||
for(uint8_t c = 0; c < EM_COLUMN_COUNT; c++) {
|
||||
parity_sum = 0;
|
||||
for(uint8_t i = 1; i <= EM_ROW_COUNT; i++) {
|
||||
uint8_t parity_bit = (proto->encoded_data >> (i * EM_BITS_PER_ROW_COUNT - 1)) & 1;
|
||||
parity_sum += parity_bit;
|
||||
}
|
||||
proto->encoded_data = (proto->encoded_data << 1) | ((parity_sum % 2) & 1);
|
||||
}
|
||||
|
||||
// stop bit
|
||||
proto->encoded_data = (proto->encoded_data << 1) | 0;
|
||||
|
||||
proto->encoded_data_index = 0;
|
||||
proto->encoded_polarity = true;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
LevelDuration protocol_em4100_encoder_yield(ProtocolEM4100* proto) {
|
||||
bool level = (proto->encoded_data >> (63 - proto->encoded_data_index)) & 1;
|
||||
uint32_t duration = EM4100_CLOCK_PER_BIT / 2;
|
||||
|
||||
if(proto->encoded_polarity) {
|
||||
proto->encoded_polarity = false;
|
||||
} else {
|
||||
level = !level;
|
||||
|
||||
proto->encoded_polarity = true;
|
||||
proto->encoded_data_index++;
|
||||
if(proto->encoded_data_index >= 64) {
|
||||
proto->encoded_data_index = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return level_duration_make(level, duration);
|
||||
};
|
||||
|
||||
bool protocol_em4100_write_data(ProtocolEM4100* protocol, void* data) {
|
||||
LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data;
|
||||
bool result = false;
|
||||
|
||||
protocol_em4100_encoder_start(protocol);
|
||||
|
||||
if(request->write_type == LFRFIDWriteTypeT5577) {
|
||||
request->t5577.block[0] =
|
||||
(LFRFID_T5577_MODULATION_MANCHESTER | LFRFID_T5577_BITRATE_RF_64 |
|
||||
(2 << LFRFID_T5577_MAXBLOCK_SHIFT));
|
||||
request->t5577.block[1] = protocol->encoded_data;
|
||||
request->t5577.block[2] = protocol->encoded_data >> 32;
|
||||
request->t5577.blocks_to_write = 3;
|
||||
result = true;
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
void protocol_em4100_render_data(ProtocolEM4100* protocol, string_t result) {
|
||||
uint8_t* data = protocol->data;
|
||||
string_printf(result, "ID: %03u,%05u", data[2], (uint16_t)((data[3] << 8) | (data[4])));
|
||||
};
|
||||
|
||||
const ProtocolBase protocol_em4100 = {
|
||||
.name = "EM4100",
|
||||
.manufacturer = "EM-Micro",
|
||||
.data_size = EM4100_DECODED_DATA_SIZE,
|
||||
.features = LFRFIDFeatureASK | LFRFIDFeaturePSK,
|
||||
.validate_count = 3,
|
||||
.alloc = (ProtocolAlloc)protocol_em4100_alloc,
|
||||
.free = (ProtocolFree)protocol_em4100_free,
|
||||
.get_data = (ProtocolGetData)protocol_em4100_get_data,
|
||||
.decoder =
|
||||
{
|
||||
.start = (ProtocolDecoderStart)protocol_em4100_decoder_start,
|
||||
.feed = (ProtocolDecoderFeed)protocol_em4100_decoder_feed,
|
||||
},
|
||||
.encoder =
|
||||
{
|
||||
.start = (ProtocolEncoderStart)protocol_em4100_encoder_start,
|
||||
.yield = (ProtocolEncoderYield)protocol_em4100_encoder_yield,
|
||||
},
|
||||
.render_data = (ProtocolRenderData)protocol_em4100_render_data,
|
||||
.render_brief_data = (ProtocolRenderData)protocol_em4100_render_data,
|
||||
.write_data = (ProtocolWriteData)protocol_em4100_write_data,
|
||||
};
|
4
lib/lfrfid/protocols/protocol_em4100.h
Normal file
4
lib/lfrfid/protocols/protocol_em4100.h
Normal file
@@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
#include <toolbox/protocols/protocol.h>
|
||||
|
||||
extern const ProtocolBase protocol_em4100;
|
239
lib/lfrfid/protocols/protocol_fdx_a.c
Normal file
239
lib/lfrfid/protocols/protocol_fdx_a.c
Normal file
@@ -0,0 +1,239 @@
|
||||
#include <furi.h>
|
||||
#include <toolbox/protocols/protocol.h>
|
||||
#include <lfrfid/tools/fsk_demod.h>
|
||||
#include <lfrfid/tools/fsk_osc.h>
|
||||
#include "lfrfid_protocols.h"
|
||||
#include <lfrfid/tools/bit_lib.h>
|
||||
|
||||
#define JITTER_TIME (20)
|
||||
#define MIN_TIME (64 - JITTER_TIME)
|
||||
#define MAX_TIME (80 + JITTER_TIME)
|
||||
|
||||
#define FDXA_DATA_SIZE 10
|
||||
#define FDXA_PREAMBLE_SIZE 2
|
||||
|
||||
#define FDXA_ENCODED_DATA_SIZE (FDXA_PREAMBLE_SIZE + FDXA_DATA_SIZE + FDXA_PREAMBLE_SIZE)
|
||||
#define FDXA_ENCODED_BIT_SIZE ((FDXA_PREAMBLE_SIZE + FDXA_DATA_SIZE) * 8)
|
||||
#define FDXA_DECODED_DATA_SIZE (5)
|
||||
#define FDXA_DECODED_BIT_SIZE ((FDXA_ENCODED_BIT_SIZE - FDXA_PREAMBLE_SIZE * 8) / 2)
|
||||
|
||||
#define FDXA_PREAMBLE_0 0x55
|
||||
#define FDXA_PREAMBLE_1 0x1D
|
||||
|
||||
typedef struct {
|
||||
FSKDemod* fsk_demod;
|
||||
} ProtocolFDXADecoder;
|
||||
|
||||
typedef struct {
|
||||
FSKOsc* fsk_osc;
|
||||
uint8_t encoded_index;
|
||||
uint32_t pulse;
|
||||
} ProtocolFDXAEncoder;
|
||||
|
||||
typedef struct {
|
||||
ProtocolFDXADecoder decoder;
|
||||
ProtocolFDXAEncoder encoder;
|
||||
uint8_t encoded_data[FDXA_ENCODED_DATA_SIZE];
|
||||
uint8_t data[FDXA_DECODED_DATA_SIZE];
|
||||
size_t protocol_size;
|
||||
} ProtocolFDXA;
|
||||
|
||||
ProtocolFDXA* protocol_fdx_a_alloc(void) {
|
||||
ProtocolFDXA* protocol = malloc(sizeof(ProtocolFDXA));
|
||||
protocol->decoder.fsk_demod = fsk_demod_alloc(MIN_TIME, 6, MAX_TIME, 5);
|
||||
protocol->encoder.fsk_osc = fsk_osc_alloc(8, 10, 50);
|
||||
|
||||
return protocol;
|
||||
};
|
||||
|
||||
void protocol_fdx_a_free(ProtocolFDXA* protocol) {
|
||||
fsk_demod_free(protocol->decoder.fsk_demod);
|
||||
fsk_osc_free(protocol->encoder.fsk_osc);
|
||||
free(protocol);
|
||||
};
|
||||
|
||||
uint8_t* protocol_fdx_a_get_data(ProtocolFDXA* protocol) {
|
||||
return protocol->data;
|
||||
};
|
||||
|
||||
void protocol_fdx_a_decoder_start(ProtocolFDXA* protocol) {
|
||||
memset(protocol->encoded_data, 0, FDXA_ENCODED_DATA_SIZE);
|
||||
};
|
||||
|
||||
static bool protocol_fdx_a_decode(const uint8_t* from, uint8_t* to) {
|
||||
size_t bit_index = 0;
|
||||
for(size_t i = FDXA_PREAMBLE_SIZE; i < (FDXA_PREAMBLE_SIZE + FDXA_DATA_SIZE); i++) {
|
||||
for(size_t n = 0; n < 4; n++) {
|
||||
uint8_t bit_pair = (from[i] >> (6 - (n * 2))) & 0b11;
|
||||
if(bit_pair == 0b01) {
|
||||
bit_lib_set_bit(to, bit_index, 0);
|
||||
} else if(bit_pair == 0b10) {
|
||||
bit_lib_set_bit(to, bit_index, 1);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
bit_index++;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool protocol_fdx_a_can_be_decoded(const uint8_t* data) {
|
||||
// check preamble
|
||||
if(data[0] != FDXA_PREAMBLE_0 || data[1] != FDXA_PREAMBLE_1 || data[12] != FDXA_PREAMBLE_0 ||
|
||||
data[13] != FDXA_PREAMBLE_1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// check for manchester encoding
|
||||
uint8_t decoded_data[FDXA_DECODED_DATA_SIZE];
|
||||
if(!protocol_fdx_a_decode(data, decoded_data)) return false;
|
||||
|
||||
uint8_t parity_sum = 0;
|
||||
for(size_t i = 0; i < FDXA_DECODED_DATA_SIZE; i++) {
|
||||
parity_sum += bit_lib_test_parity_32(decoded_data[i], BitLibParityOdd);
|
||||
decoded_data[i] &= 0x7F;
|
||||
}
|
||||
|
||||
return (parity_sum == 0);
|
||||
}
|
||||
|
||||
bool protocol_fdx_a_decoder_feed(ProtocolFDXA* protocol, bool level, uint32_t duration) {
|
||||
bool value;
|
||||
uint32_t count;
|
||||
bool result = false;
|
||||
|
||||
fsk_demod_feed(protocol->decoder.fsk_demod, level, duration, &value, &count);
|
||||
if(count > 0) {
|
||||
for(size_t i = 0; i < count; i++) {
|
||||
bit_lib_push_bit(protocol->encoded_data, FDXA_ENCODED_DATA_SIZE, value);
|
||||
if(protocol_fdx_a_can_be_decoded(protocol->encoded_data)) {
|
||||
protocol_fdx_a_decode(protocol->encoded_data, protocol->data);
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
static void protocol_fdx_a_encode(ProtocolFDXA* protocol) {
|
||||
protocol->encoded_data[0] = FDXA_PREAMBLE_0;
|
||||
protocol->encoded_data[1] = FDXA_PREAMBLE_1;
|
||||
|
||||
size_t bit_index = 0;
|
||||
for(size_t i = 0; i < FDXA_DECODED_BIT_SIZE; i++) {
|
||||
bool bit = bit_lib_get_bit(protocol->data, i);
|
||||
if(bit) {
|
||||
bit_lib_set_bit(protocol->encoded_data, 16 + bit_index, 1);
|
||||
bit_lib_set_bit(protocol->encoded_data, 16 + bit_index + 1, 0);
|
||||
} else {
|
||||
bit_lib_set_bit(protocol->encoded_data, 16 + bit_index, 0);
|
||||
bit_lib_set_bit(protocol->encoded_data, 16 + bit_index + 1, 1);
|
||||
}
|
||||
bit_index += 2;
|
||||
}
|
||||
}
|
||||
|
||||
bool protocol_fdx_a_encoder_start(ProtocolFDXA* protocol) {
|
||||
protocol->encoder.encoded_index = 0;
|
||||
protocol->encoder.pulse = 0;
|
||||
protocol_fdx_a_encode(protocol);
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
LevelDuration protocol_fdx_a_encoder_yield(ProtocolFDXA* protocol) {
|
||||
bool level = 0;
|
||||
uint32_t duration = 0;
|
||||
|
||||
// if pulse is zero, we need to output high, otherwise we need to output low
|
||||
if(protocol->encoder.pulse == 0) {
|
||||
// get bit
|
||||
uint8_t bit = bit_lib_get_bit(protocol->encoded_data, protocol->encoder.encoded_index);
|
||||
|
||||
// get pulse from oscillator
|
||||
bool advance = fsk_osc_next(protocol->encoder.fsk_osc, bit, &duration);
|
||||
|
||||
if(advance) {
|
||||
bit_lib_increment_index(protocol->encoder.encoded_index, FDXA_ENCODED_BIT_SIZE);
|
||||
}
|
||||
|
||||
// duration diveded by 2 because we need to output high and low
|
||||
duration = duration / 2;
|
||||
protocol->encoder.pulse = duration;
|
||||
level = true;
|
||||
} else {
|
||||
// output low half and reset pulse
|
||||
duration = protocol->encoder.pulse;
|
||||
protocol->encoder.pulse = 0;
|
||||
level = false;
|
||||
}
|
||||
|
||||
return level_duration_make(level, duration);
|
||||
};
|
||||
|
||||
bool protocol_fdx_a_write_data(ProtocolFDXA* protocol, void* data) {
|
||||
LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data;
|
||||
bool result = false;
|
||||
|
||||
protocol_fdx_a_encoder_start(protocol);
|
||||
|
||||
if(request->write_type == LFRFIDWriteTypeT5577) {
|
||||
request->t5577.block[0] = LFRFID_T5577_MODULATION_FSK2a | LFRFID_T5577_BITRATE_RF_50 |
|
||||
(3 << LFRFID_T5577_MAXBLOCK_SHIFT);
|
||||
request->t5577.block[1] = bit_lib_get_bits_32(protocol->encoded_data, 0, 32);
|
||||
request->t5577.block[2] = bit_lib_get_bits_32(protocol->encoded_data, 32, 32);
|
||||
request->t5577.block[3] = bit_lib_get_bits_32(protocol->encoded_data, 64, 32);
|
||||
request->t5577.blocks_to_write = 4;
|
||||
result = true;
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
void protocol_fdx_a_render_data(ProtocolFDXA* protocol, string_t result) {
|
||||
uint8_t data[FDXA_DECODED_DATA_SIZE];
|
||||
memcpy(data, protocol->data, FDXA_DECODED_DATA_SIZE);
|
||||
|
||||
uint8_t parity_sum = 0;
|
||||
for(size_t i = 0; i < FDXA_DECODED_DATA_SIZE; i++) {
|
||||
parity_sum += bit_lib_test_parity_32(data[i], BitLibParityOdd);
|
||||
data[i] &= 0x7F;
|
||||
}
|
||||
|
||||
string_printf(
|
||||
result,
|
||||
"ID: %02X%02X%02X%02X%02X\r\n"
|
||||
"Parity: %s",
|
||||
data[0],
|
||||
data[1],
|
||||
data[2],
|
||||
data[3],
|
||||
data[4],
|
||||
parity_sum == 0 ? "+" : "-");
|
||||
};
|
||||
|
||||
const ProtocolBase protocol_fdx_a = {
|
||||
.name = "FDX-A",
|
||||
.manufacturer = "FECAVA",
|
||||
.data_size = FDXA_DECODED_DATA_SIZE,
|
||||
.features = LFRFIDFeatureASK,
|
||||
.validate_count = 3,
|
||||
.alloc = (ProtocolAlloc)protocol_fdx_a_alloc,
|
||||
.free = (ProtocolFree)protocol_fdx_a_free,
|
||||
.get_data = (ProtocolGetData)protocol_fdx_a_get_data,
|
||||
.decoder =
|
||||
{
|
||||
.start = (ProtocolDecoderStart)protocol_fdx_a_decoder_start,
|
||||
.feed = (ProtocolDecoderFeed)protocol_fdx_a_decoder_feed,
|
||||
},
|
||||
.encoder =
|
||||
{
|
||||
.start = (ProtocolEncoderStart)protocol_fdx_a_encoder_start,
|
||||
.yield = (ProtocolEncoderYield)protocol_fdx_a_encoder_yield,
|
||||
},
|
||||
.render_data = (ProtocolRenderData)protocol_fdx_a_render_data,
|
||||
.render_brief_data = (ProtocolRenderData)protocol_fdx_a_render_data,
|
||||
.write_data = (ProtocolWriteData)protocol_fdx_a_write_data,
|
||||
};
|
4
lib/lfrfid/protocols/protocol_fdx_a.h
Normal file
4
lib/lfrfid/protocols/protocol_fdx_a.h
Normal file
@@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
#include <toolbox/protocols/protocol.h>
|
||||
|
||||
extern const ProtocolBase protocol_fdx_a;
|
374
lib/lfrfid/protocols/protocol_fdx_b.c
Normal file
374
lib/lfrfid/protocols/protocol_fdx_b.c
Normal file
@@ -0,0 +1,374 @@
|
||||
#include <furi.h>
|
||||
#include "toolbox/level_duration.h"
|
||||
#include "protocol_fdx_b.h"
|
||||
#include <toolbox/manchester_decoder.h>
|
||||
#include <lfrfid/tools/bit_lib.h>
|
||||
#include "lfrfid_protocols.h"
|
||||
|
||||
#define FDX_B_ENCODED_BIT_SIZE (128)
|
||||
#define FDX_B_ENCODED_BYTE_SIZE (((FDX_B_ENCODED_BIT_SIZE) / 8))
|
||||
#define FDX_B_PREAMBLE_BIT_SIZE (11)
|
||||
#define FDX_B_PREAMBLE_BYTE_SIZE (2)
|
||||
#define FDX_B_ENCODED_BYTE_FULL_SIZE (FDX_B_ENCODED_BYTE_SIZE + FDX_B_PREAMBLE_BYTE_SIZE)
|
||||
|
||||
#define FDXB_DECODED_DATA_SIZE (11)
|
||||
|
||||
#define FDX_B_SHORT_TIME (128)
|
||||
#define FDX_B_LONG_TIME (256)
|
||||
#define FDX_B_JITTER_TIME (60)
|
||||
|
||||
#define FDX_B_SHORT_TIME_LOW (FDX_B_SHORT_TIME - FDX_B_JITTER_TIME)
|
||||
#define FDX_B_SHORT_TIME_HIGH (FDX_B_SHORT_TIME + FDX_B_JITTER_TIME)
|
||||
#define FDX_B_LONG_TIME_LOW (FDX_B_LONG_TIME - FDX_B_JITTER_TIME)
|
||||
#define FDX_B_LONG_TIME_HIGH (FDX_B_LONG_TIME + FDX_B_JITTER_TIME)
|
||||
|
||||
typedef struct {
|
||||
bool last_short;
|
||||
bool last_level;
|
||||
size_t encoded_index;
|
||||
uint8_t encoded_data[FDX_B_ENCODED_BYTE_FULL_SIZE];
|
||||
uint8_t data[FDXB_DECODED_DATA_SIZE];
|
||||
} ProtocolFDXB;
|
||||
|
||||
ProtocolFDXB* protocol_fdx_b_alloc(void) {
|
||||
ProtocolFDXB* protocol = malloc(sizeof(ProtocolFDXB));
|
||||
return protocol;
|
||||
};
|
||||
|
||||
void protocol_fdx_b_free(ProtocolFDXB* protocol) {
|
||||
free(protocol);
|
||||
};
|
||||
|
||||
uint8_t* protocol_fdx_b_get_data(ProtocolFDXB* proto) {
|
||||
return proto->data;
|
||||
};
|
||||
|
||||
void protocol_fdx_b_decoder_start(ProtocolFDXB* protocol) {
|
||||
memset(protocol->encoded_data, 0, FDX_B_ENCODED_BYTE_FULL_SIZE);
|
||||
protocol->last_short = false;
|
||||
};
|
||||
|
||||
static bool protocol_fdx_b_can_be_decoded(ProtocolFDXB* protocol) {
|
||||
bool result = false;
|
||||
|
||||
/*
|
||||
msb lsb
|
||||
0 10000000000 Header pattern. 11 bits.
|
||||
11 1nnnnnnnn
|
||||
20 1nnnnnnnn 38 bit (12 digit) National code.
|
||||
29 1nnnnnnnn eg. 000000001008 (decimal).
|
||||
38 1nnnnnnnn
|
||||
47 1nnnnnncc 10 bit (3 digit) Country code.
|
||||
56 1cccccccc eg. 999 (decimal).
|
||||
65 1s------- 1 bit data block status flag.
|
||||
74 1-------a 1 bit animal application indicator.
|
||||
83 1xxxxxxxx 16 bit checksum.
|
||||
92 1xxxxxxxx
|
||||
101 1eeeeeeee 24 bits of extra data if present.
|
||||
110 1eeeeeeee eg. $123456.
|
||||
119 1eeeeeeee
|
||||
*/
|
||||
|
||||
do {
|
||||
// check 11 bits preamble
|
||||
if(bit_lib_get_bits_16(protocol->encoded_data, 0, 11) != 0b10000000000) break;
|
||||
// check next 11 bits preamble
|
||||
if(bit_lib_get_bits_16(protocol->encoded_data, 128, 11) != 0b10000000000) break;
|
||||
// check control bits
|
||||
if(!bit_lib_test_parity(protocol->encoded_data, 3, 13 * 9, BitLibParityAlways1, 9)) break;
|
||||
|
||||
// compute checksum
|
||||
uint8_t crc_data[8];
|
||||
for(size_t i = 0; i < 8; i++) {
|
||||
bit_lib_copy_bits(crc_data, i * 8, 8, protocol->encoded_data, 12 + 9 * i);
|
||||
}
|
||||
uint16_t crc_res = bit_lib_crc16(crc_data, 8, 0x1021, 0x0000, false, false, 0x0000);
|
||||
|
||||
// read checksum
|
||||
uint16_t crc_ex = 0;
|
||||
bit_lib_copy_bits((uint8_t*)&crc_ex, 8, 8, protocol->encoded_data, 84);
|
||||
bit_lib_copy_bits((uint8_t*)&crc_ex, 0, 8, protocol->encoded_data, 93);
|
||||
|
||||
// compare checksum
|
||||
if(crc_res != crc_ex) break;
|
||||
|
||||
result = true;
|
||||
} while(false);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void protocol_fdx_b_decode(ProtocolFDXB* protocol) {
|
||||
// remove parity
|
||||
bit_lib_remove_bit_every_nth(protocol->encoded_data, 3, 13 * 9, 9);
|
||||
|
||||
// remove header pattern
|
||||
for(size_t i = 0; i < 11; i++)
|
||||
bit_lib_push_bit(protocol->encoded_data, FDX_B_ENCODED_BYTE_FULL_SIZE, 0);
|
||||
|
||||
// 0 nnnnnnnn
|
||||
// 8 nnnnnnnn 38 bit (12 digit) National code.
|
||||
// 16 nnnnnnnn eg. 000000001008 (decimal).
|
||||
// 24 nnnnnnnn
|
||||
// 32 nnnnnncc 10 bit (3 digit) Country code.
|
||||
// 40 cccccccc eg. 999 (decimal).
|
||||
// 48 s------- 1 bit data block status flag.
|
||||
// 56 -------a 1 bit animal application indicator.
|
||||
// 64 xxxxxxxx 16 bit checksum.
|
||||
// 72 xxxxxxxx
|
||||
// 80 eeeeeeee 24 bits of extra data if present.
|
||||
// 88 eeeeeeee eg. $123456.
|
||||
// 92 eeeeeeee
|
||||
|
||||
// copy data without checksum
|
||||
bit_lib_copy_bits(protocol->data, 0, 64, protocol->encoded_data, 0);
|
||||
bit_lib_copy_bits(protocol->data, 64, 24, protocol->encoded_data, 80);
|
||||
|
||||
// const BitLibRegion regions_encoded[] = {
|
||||
// {'n', 0, 38},
|
||||
// {'c', 38, 10},
|
||||
// {'b', 48, 16},
|
||||
// {'x', 64, 16},
|
||||
// {'e', 80, 24},
|
||||
// };
|
||||
|
||||
// bit_lib_print_regions(regions_encoded, 5, protocol->encoded_data, FDX_B_ENCODED_BIT_SIZE);
|
||||
|
||||
// const BitLibRegion regions_decoded[] = {
|
||||
// {'n', 0, 38},
|
||||
// {'c', 38, 10},
|
||||
// {'b', 48, 16},
|
||||
// {'e', 64, 24},
|
||||
// };
|
||||
|
||||
// bit_lib_print_regions(regions_decoded, 4, protocol->data, FDXB_DECODED_DATA_SIZE * 8);
|
||||
}
|
||||
|
||||
bool protocol_fdx_b_decoder_feed(ProtocolFDXB* protocol, bool level, uint32_t duration) {
|
||||
bool result = false;
|
||||
UNUSED(level);
|
||||
|
||||
bool pushed = false;
|
||||
|
||||
// Bi-Phase Manchester decoding
|
||||
if(duration >= FDX_B_SHORT_TIME_LOW && duration <= FDX_B_SHORT_TIME_HIGH) {
|
||||
if(protocol->last_short == false) {
|
||||
protocol->last_short = true;
|
||||
} else {
|
||||
pushed = true;
|
||||
bit_lib_push_bit(protocol->encoded_data, FDX_B_ENCODED_BYTE_FULL_SIZE, false);
|
||||
protocol->last_short = false;
|
||||
}
|
||||
} else if(duration >= FDX_B_LONG_TIME_LOW && duration <= FDX_B_LONG_TIME_HIGH) {
|
||||
if(protocol->last_short == false) {
|
||||
pushed = true;
|
||||
bit_lib_push_bit(protocol->encoded_data, FDX_B_ENCODED_BYTE_FULL_SIZE, true);
|
||||
} else {
|
||||
// reset
|
||||
protocol->last_short = false;
|
||||
}
|
||||
} else {
|
||||
// reset
|
||||
protocol->last_short = false;
|
||||
}
|
||||
|
||||
if(pushed && protocol_fdx_b_can_be_decoded(protocol)) {
|
||||
protocol_fdx_b_decode(protocol);
|
||||
result = true;
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
bool protocol_fdx_b_encoder_start(ProtocolFDXB* protocol) {
|
||||
memset(protocol->encoded_data, 0, FDX_B_ENCODED_BYTE_FULL_SIZE);
|
||||
bit_lib_set_bit(protocol->encoded_data, 0, 1);
|
||||
for(size_t i = 0; i < 13; i++) {
|
||||
bit_lib_set_bit(protocol->encoded_data, 11 + 9 * i, 1);
|
||||
if(i == 8 || i == 9) continue;
|
||||
|
||||
if(i < 8) {
|
||||
bit_lib_copy_bits(protocol->encoded_data, 12 + 9 * i, 8, protocol->data, i * 8);
|
||||
} else {
|
||||
bit_lib_copy_bits(protocol->encoded_data, 12 + 9 * i, 8, protocol->data, (i - 2) * 8);
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t crc_res = bit_lib_crc16(protocol->data, 8, 0x1021, 0x0000, false, false, 0x0000);
|
||||
bit_lib_copy_bits(protocol->encoded_data, 84, 8, (uint8_t*)&crc_res, 8);
|
||||
bit_lib_copy_bits(protocol->encoded_data, 93, 8, (uint8_t*)&crc_res, 0);
|
||||
|
||||
protocol->encoded_index = 0;
|
||||
protocol->last_short = false;
|
||||
protocol->last_level = false;
|
||||
return true;
|
||||
};
|
||||
|
||||
LevelDuration protocol_fdx_b_encoder_yield(ProtocolFDXB* protocol) {
|
||||
uint32_t duration;
|
||||
protocol->last_level = !protocol->last_level;
|
||||
|
||||
bool bit = bit_lib_get_bit(protocol->encoded_data, protocol->encoded_index);
|
||||
|
||||
// Bi-Phase Manchester encoder
|
||||
if(bit) {
|
||||
// one long pulse for 1
|
||||
duration = FDX_B_LONG_TIME / 8;
|
||||
bit_lib_increment_index(protocol->encoded_index, FDX_B_ENCODED_BIT_SIZE);
|
||||
} else {
|
||||
// two short pulses for 0
|
||||
duration = FDX_B_SHORT_TIME / 8;
|
||||
if(protocol->last_short) {
|
||||
bit_lib_increment_index(protocol->encoded_index, FDX_B_ENCODED_BIT_SIZE);
|
||||
protocol->last_short = false;
|
||||
} else {
|
||||
protocol->last_short = true;
|
||||
}
|
||||
}
|
||||
|
||||
return level_duration_make(protocol->last_level, duration);
|
||||
};
|
||||
|
||||
// 0 nnnnnnnn
|
||||
// 8 nnnnnnnn 38 bit (12 digit) National code.
|
||||
// 16 nnnnnnnn eg. 000000001008 (decimal).
|
||||
// 24 nnnnnnnn
|
||||
// 32 nnnnnnnn 10 bit (3 digit) Country code.
|
||||
// 40 cccccccc eg. 999 (decimal).
|
||||
// 48 s------- 1 bit data block status flag.
|
||||
// 56 -------a 1 bit animal application indicator.
|
||||
// 64 eeeeeeee 24 bits of extra data if present.
|
||||
// 72 eeeeeeee eg. $123456.
|
||||
// 80 eeeeeeee
|
||||
|
||||
static uint64_t protocol_fdx_b_get_national_code(const uint8_t* data) {
|
||||
uint64_t national_code = bit_lib_get_bits_32(data, 0, 32);
|
||||
national_code = national_code << 32;
|
||||
national_code |= bit_lib_get_bits_32(data, 32, 6) << (32 - 6);
|
||||
bit_lib_reverse_bits((uint8_t*)&national_code, 0, 64);
|
||||
return national_code;
|
||||
}
|
||||
|
||||
static uint16_t protocol_fdx_b_get_country_code(const uint8_t* data) {
|
||||
uint16_t country_code = bit_lib_get_bits_16(data, 38, 10) << 6;
|
||||
bit_lib_reverse_bits((uint8_t*)&country_code, 0, 16);
|
||||
return country_code;
|
||||
}
|
||||
|
||||
static bool protocol_fdx_b_get_temp(const uint8_t* data, float* temp) {
|
||||
uint32_t extended = bit_lib_get_bits_32(data, 64, 24) << 8;
|
||||
bit_lib_reverse_bits((uint8_t*)&extended, 0, 32);
|
||||
|
||||
uint8_t ex_parity = (extended & 0x100) >> 8;
|
||||
uint8_t ex_temperature = extended & 0xff;
|
||||
uint8_t ex_calc_parity = bit_lib_test_parity_32(ex_temperature, BitLibParityOdd);
|
||||
bool ex_temperature_present = (ex_calc_parity == ex_parity) && !(extended & 0xe00);
|
||||
|
||||
if(ex_temperature_present) {
|
||||
float temperature_f = 74 + ex_temperature * 0.2;
|
||||
*temp = temperature_f;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void protocol_fdx_b_render_data(ProtocolFDXB* protocol, string_t result) {
|
||||
// 38 bits of national code
|
||||
uint64_t national_code = protocol_fdx_b_get_national_code(protocol->data);
|
||||
|
||||
// 10 bit of country code
|
||||
uint16_t country_code = protocol_fdx_b_get_country_code(protocol->data);
|
||||
|
||||
bool block_status = bit_lib_get_bit(protocol->data, 48);
|
||||
bool rudi_bit = bit_lib_get_bit(protocol->data, 49);
|
||||
uint8_t reserved = bit_lib_get_bits(protocol->data, 50, 5);
|
||||
uint8_t user_info = bit_lib_get_bits(protocol->data, 55, 5);
|
||||
uint8_t replacement_number = bit_lib_get_bits(protocol->data, 60, 3);
|
||||
bool animal_flag = bit_lib_get_bit(protocol->data, 63);
|
||||
|
||||
string_printf(result, "ID: %03u-%012llu\r\n", country_code, national_code);
|
||||
string_cat_printf(result, "Animal: %s, ", animal_flag ? "Yes" : "No");
|
||||
|
||||
float temperature;
|
||||
if(protocol_fdx_b_get_temp(protocol->data, &temperature)) {
|
||||
float temperature_c = (temperature - 32) / 1.8;
|
||||
string_cat_printf(
|
||||
result, "T: %.2fF, %.2fC\r\n", (double)temperature, (double)temperature_c);
|
||||
} else {
|
||||
string_cat_printf(result, "T: ---\r\n");
|
||||
}
|
||||
|
||||
string_cat_printf(
|
||||
result,
|
||||
"Bits: %X-%X-%X-%X-%X",
|
||||
block_status,
|
||||
rudi_bit,
|
||||
reserved,
|
||||
user_info,
|
||||
replacement_number);
|
||||
};
|
||||
|
||||
void protocol_fdx_b_render_brief_data(ProtocolFDXB* protocol, string_t result) {
|
||||
// 38 bits of national code
|
||||
uint64_t national_code = protocol_fdx_b_get_national_code(protocol->data);
|
||||
|
||||
// 10 bit of country code
|
||||
uint16_t country_code = protocol_fdx_b_get_country_code(protocol->data);
|
||||
|
||||
bool animal_flag = bit_lib_get_bit(protocol->data, 63);
|
||||
|
||||
string_printf(result, "ID: %03u-%012llu\r\n", country_code, national_code);
|
||||
string_cat_printf(result, "Animal: %s, ", animal_flag ? "Yes" : "No");
|
||||
|
||||
float temperature;
|
||||
if(protocol_fdx_b_get_temp(protocol->data, &temperature)) {
|
||||
float temperature_c = (temperature - 32) / 1.8;
|
||||
string_cat_printf(result, "T: %.2fC", (double)temperature_c);
|
||||
} else {
|
||||
string_cat_printf(result, "T: ---");
|
||||
}
|
||||
};
|
||||
|
||||
bool protocol_fdx_b_write_data(ProtocolFDXB* protocol, void* data) {
|
||||
LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data;
|
||||
bool result = false;
|
||||
|
||||
protocol_fdx_b_encoder_start(protocol);
|
||||
|
||||
if(request->write_type == LFRFIDWriteTypeT5577) {
|
||||
request->t5577.block[0] = LFRFID_T5577_MODULATION_DIPHASE | LFRFID_T5577_BITRATE_RF_32 |
|
||||
(4 << LFRFID_T5577_MAXBLOCK_SHIFT);
|
||||
request->t5577.block[1] = bit_lib_get_bits_32(protocol->encoded_data, 0, 32);
|
||||
request->t5577.block[2] = bit_lib_get_bits_32(protocol->encoded_data, 32, 32);
|
||||
request->t5577.block[3] = bit_lib_get_bits_32(protocol->encoded_data, 64, 32);
|
||||
request->t5577.block[4] = bit_lib_get_bits_32(protocol->encoded_data, 96, 32);
|
||||
request->t5577.blocks_to_write = 5;
|
||||
result = true;
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
const ProtocolBase protocol_fdx_b = {
|
||||
.name = "FDX-B",
|
||||
.manufacturer = "ISO",
|
||||
.data_size = FDXB_DECODED_DATA_SIZE,
|
||||
.features = LFRFIDFeatureASK,
|
||||
.validate_count = 3,
|
||||
.alloc = (ProtocolAlloc)protocol_fdx_b_alloc,
|
||||
.free = (ProtocolFree)protocol_fdx_b_free,
|
||||
.get_data = (ProtocolGetData)protocol_fdx_b_get_data,
|
||||
.decoder =
|
||||
{
|
||||
.start = (ProtocolDecoderStart)protocol_fdx_b_decoder_start,
|
||||
.feed = (ProtocolDecoderFeed)protocol_fdx_b_decoder_feed,
|
||||
},
|
||||
.encoder =
|
||||
{
|
||||
.start = (ProtocolEncoderStart)protocol_fdx_b_encoder_start,
|
||||
.yield = (ProtocolEncoderYield)protocol_fdx_b_encoder_yield,
|
||||
},
|
||||
.render_data = (ProtocolRenderData)protocol_fdx_b_render_data,
|
||||
.render_brief_data = (ProtocolRenderData)protocol_fdx_b_render_brief_data,
|
||||
.write_data = (ProtocolWriteData)protocol_fdx_b_write_data,
|
||||
};
|
4
lib/lfrfid/protocols/protocol_fdx_b.h
Normal file
4
lib/lfrfid/protocols/protocol_fdx_b.h
Normal file
@@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
#include <toolbox/protocols/protocol.h>
|
||||
|
||||
extern const ProtocolBase protocol_fdx_b;
|
386
lib/lfrfid/protocols/protocol_h10301.c
Normal file
386
lib/lfrfid/protocols/protocol_h10301.c
Normal file
@@ -0,0 +1,386 @@
|
||||
#include <furi.h>
|
||||
#include <toolbox/protocols/protocol.h>
|
||||
#include <lfrfid/tools/fsk_demod.h>
|
||||
#include <lfrfid/tools/fsk_osc.h>
|
||||
#include "lfrfid_protocols.h"
|
||||
|
||||
#define JITTER_TIME (20)
|
||||
#define MIN_TIME (64 - JITTER_TIME)
|
||||
#define MAX_TIME (80 + JITTER_TIME)
|
||||
|
||||
#define H10301_DECODED_DATA_SIZE (3)
|
||||
#define H10301_ENCODED_DATA_SIZE_U32 (3)
|
||||
#define H10301_ENCODED_DATA_SIZE (sizeof(uint32_t) * H10301_ENCODED_DATA_SIZE_U32)
|
||||
|
||||
#define H10301_BIT_SIZE (sizeof(uint32_t) * 8)
|
||||
#define H10301_BIT_MAX_SIZE (H10301_BIT_SIZE * H10301_DECODED_DATA_SIZE)
|
||||
|
||||
typedef struct {
|
||||
FSKDemod* fsk_demod;
|
||||
} ProtocolH10301Decoder;
|
||||
|
||||
typedef struct {
|
||||
FSKOsc* fsk_osc;
|
||||
uint8_t encoded_index;
|
||||
uint32_t pulse;
|
||||
} ProtocolH10301Encoder;
|
||||
|
||||
typedef struct {
|
||||
ProtocolH10301Decoder decoder;
|
||||
ProtocolH10301Encoder encoder;
|
||||
uint32_t encoded_data[H10301_ENCODED_DATA_SIZE_U32];
|
||||
uint8_t data[H10301_DECODED_DATA_SIZE];
|
||||
} ProtocolH10301;
|
||||
|
||||
ProtocolH10301* protocol_h10301_alloc(void) {
|
||||
ProtocolH10301* protocol = malloc(sizeof(ProtocolH10301));
|
||||
protocol->decoder.fsk_demod = fsk_demod_alloc(MIN_TIME, 6, MAX_TIME, 5);
|
||||
protocol->encoder.fsk_osc = fsk_osc_alloc(8, 10, 50);
|
||||
|
||||
return protocol;
|
||||
};
|
||||
|
||||
void protocol_h10301_free(ProtocolH10301* protocol) {
|
||||
fsk_demod_free(protocol->decoder.fsk_demod);
|
||||
fsk_osc_free(protocol->encoder.fsk_osc);
|
||||
free(protocol);
|
||||
};
|
||||
|
||||
uint8_t* protocol_h10301_get_data(ProtocolH10301* protocol) {
|
||||
return protocol->data;
|
||||
};
|
||||
|
||||
void protocol_h10301_decoder_start(ProtocolH10301* protocol) {
|
||||
memset(protocol->encoded_data, 0, sizeof(uint32_t) * 3);
|
||||
};
|
||||
|
||||
static void protocol_h10301_decoder_store_data(ProtocolH10301* protocol, bool data) {
|
||||
protocol->encoded_data[0] = (protocol->encoded_data[0] << 1) |
|
||||
((protocol->encoded_data[1] >> 31) & 1);
|
||||
protocol->encoded_data[1] = (protocol->encoded_data[1] << 1) |
|
||||
((protocol->encoded_data[2] >> 31) & 1);
|
||||
protocol->encoded_data[2] = (protocol->encoded_data[2] << 1) | data;
|
||||
}
|
||||
|
||||
static bool protocol_h10301_can_be_decoded(const uint32_t* card_data) {
|
||||
const uint8_t* encoded_data = (const uint8_t*)card_data;
|
||||
|
||||
// packet preamble
|
||||
// raw data
|
||||
if(*(encoded_data + 3) != 0x1D) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// encoded company/oem
|
||||
// coded with 01 = 0, 10 = 1 transitions
|
||||
// stored in word 0
|
||||
if((*card_data >> 10 & 0x3FFF) != 0x1556) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// encoded format/length
|
||||
// coded with 01 = 0, 10 = 1 transitions
|
||||
// stored in word 0 and word 1
|
||||
if((((*card_data & 0x3FF) << 12) | ((*(card_data + 1) >> 20) & 0xFFF)) != 0x155556) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// data decoding
|
||||
uint32_t result = 0;
|
||||
|
||||
// decode from word 1
|
||||
// coded with 01 = 0, 10 = 1 transitions
|
||||
for(int8_t i = 9; i >= 0; i--) {
|
||||
switch((*(card_data + 1) >> (2 * i)) & 0b11) {
|
||||
case 0b01:
|
||||
result = (result << 1) | 0;
|
||||
break;
|
||||
case 0b10:
|
||||
result = (result << 1) | 1;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// decode from word 2
|
||||
// coded with 01 = 0, 10 = 1 transitions
|
||||
for(int8_t i = 15; i >= 0; i--) {
|
||||
switch((*(card_data + 2) >> (2 * i)) & 0b11) {
|
||||
case 0b01:
|
||||
result = (result << 1) | 0;
|
||||
break;
|
||||
case 0b10:
|
||||
result = (result << 1) | 1;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// trailing parity (odd) test
|
||||
uint8_t parity_sum = 0;
|
||||
for(int8_t i = 0; i < 13; i++) {
|
||||
if(((result >> i) & 1) == 1) {
|
||||
parity_sum++;
|
||||
}
|
||||
}
|
||||
|
||||
if((parity_sum % 2) != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// leading parity (even) test
|
||||
parity_sum = 0;
|
||||
for(int8_t i = 13; i < 26; i++) {
|
||||
if(((result >> i) & 1) == 1) {
|
||||
parity_sum++;
|
||||
}
|
||||
}
|
||||
|
||||
if((parity_sum % 2) == 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void protocol_h10301_decode(const uint32_t* card_data, uint8_t* decoded_data) {
|
||||
// data decoding
|
||||
uint32_t result = 0;
|
||||
|
||||
// decode from word 1
|
||||
// coded with 01 = 0, 10 = 1 transitions
|
||||
for(int8_t i = 9; i >= 0; i--) {
|
||||
switch((*(card_data + 1) >> (2 * i)) & 0b11) {
|
||||
case 0b01:
|
||||
result = (result << 1) | 0;
|
||||
break;
|
||||
case 0b10:
|
||||
result = (result << 1) | 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// decode from word 2
|
||||
// coded with 01 = 0, 10 = 1 transitions
|
||||
for(int8_t i = 15; i >= 0; i--) {
|
||||
switch((*(card_data + 2) >> (2 * i)) & 0b11) {
|
||||
case 0b01:
|
||||
result = (result << 1) | 0;
|
||||
break;
|
||||
case 0b10:
|
||||
result = (result << 1) | 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t data[H10301_DECODED_DATA_SIZE] = {
|
||||
(uint8_t)(result >> 17), (uint8_t)(result >> 9), (uint8_t)(result >> 1)};
|
||||
|
||||
memcpy(decoded_data, &data, H10301_DECODED_DATA_SIZE);
|
||||
}
|
||||
|
||||
bool protocol_h10301_decoder_feed(ProtocolH10301* protocol, bool level, uint32_t duration) {
|
||||
bool value;
|
||||
uint32_t count;
|
||||
bool result = false;
|
||||
|
||||
fsk_demod_feed(protocol->decoder.fsk_demod, level, duration, &value, &count);
|
||||
if(count > 0) {
|
||||
for(size_t i = 0; i < count; i++) {
|
||||
protocol_h10301_decoder_store_data(protocol, value);
|
||||
if(protocol_h10301_can_be_decoded(protocol->encoded_data)) {
|
||||
protocol_h10301_decode(protocol->encoded_data, protocol->data);
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
static void protocol_h10301_write_raw_bit(bool bit, uint8_t position, uint32_t* card_data) {
|
||||
if(bit) {
|
||||
card_data[position / H10301_BIT_SIZE] |=
|
||||
1UL << (H10301_BIT_SIZE - (position % H10301_BIT_SIZE) - 1);
|
||||
} else {
|
||||
card_data[position / H10301_BIT_SIZE] &=
|
||||
~(1UL << (H10301_BIT_SIZE - (position % H10301_BIT_SIZE) - 1));
|
||||
}
|
||||
}
|
||||
|
||||
static void protocol_h10301_write_bit(bool bit, uint8_t position, uint32_t* card_data) {
|
||||
protocol_h10301_write_raw_bit(bit, position + 0, card_data);
|
||||
protocol_h10301_write_raw_bit(!bit, position + 1, card_data);
|
||||
}
|
||||
|
||||
void protocol_h10301_encode(const uint8_t* decoded_data, uint8_t* encoded_data) {
|
||||
uint32_t card_data[H10301_DECODED_DATA_SIZE] = {0, 0, 0};
|
||||
|
||||
uint32_t fc_cn = (decoded_data[0] << 16) | (decoded_data[1] << 8) | decoded_data[2];
|
||||
|
||||
// even parity sum calculation (high 12 bits of data)
|
||||
uint8_t even_parity_sum = 0;
|
||||
for(int8_t i = 12; i < 24; i++) {
|
||||
if(((fc_cn >> i) & 1) == 1) {
|
||||
even_parity_sum++;
|
||||
}
|
||||
}
|
||||
|
||||
// odd parity sum calculation (low 12 bits of data)
|
||||
uint8_t odd_parity_sum = 1;
|
||||
for(int8_t i = 0; i < 12; i++) {
|
||||
if(((fc_cn >> i) & 1) == 1) {
|
||||
odd_parity_sum++;
|
||||
}
|
||||
}
|
||||
|
||||
// 0x1D preamble
|
||||
protocol_h10301_write_raw_bit(0, 0, card_data);
|
||||
protocol_h10301_write_raw_bit(0, 1, card_data);
|
||||
protocol_h10301_write_raw_bit(0, 2, card_data);
|
||||
protocol_h10301_write_raw_bit(1, 3, card_data);
|
||||
protocol_h10301_write_raw_bit(1, 4, card_data);
|
||||
protocol_h10301_write_raw_bit(1, 5, card_data);
|
||||
protocol_h10301_write_raw_bit(0, 6, card_data);
|
||||
protocol_h10301_write_raw_bit(1, 7, card_data);
|
||||
|
||||
// company / OEM code 1
|
||||
protocol_h10301_write_bit(0, 8, card_data);
|
||||
protocol_h10301_write_bit(0, 10, card_data);
|
||||
protocol_h10301_write_bit(0, 12, card_data);
|
||||
protocol_h10301_write_bit(0, 14, card_data);
|
||||
protocol_h10301_write_bit(0, 16, card_data);
|
||||
protocol_h10301_write_bit(0, 18, card_data);
|
||||
protocol_h10301_write_bit(1, 20, card_data);
|
||||
|
||||
// card format / length 1
|
||||
protocol_h10301_write_bit(0, 22, card_data);
|
||||
protocol_h10301_write_bit(0, 24, card_data);
|
||||
protocol_h10301_write_bit(0, 26, card_data);
|
||||
protocol_h10301_write_bit(0, 28, card_data);
|
||||
protocol_h10301_write_bit(0, 30, card_data);
|
||||
protocol_h10301_write_bit(0, 32, card_data);
|
||||
protocol_h10301_write_bit(0, 34, card_data);
|
||||
protocol_h10301_write_bit(0, 36, card_data);
|
||||
protocol_h10301_write_bit(0, 38, card_data);
|
||||
protocol_h10301_write_bit(0, 40, card_data);
|
||||
protocol_h10301_write_bit(1, 42, card_data);
|
||||
|
||||
// even parity bit
|
||||
protocol_h10301_write_bit((even_parity_sum % 2), 44, card_data);
|
||||
|
||||
// data
|
||||
for(uint8_t i = 0; i < 24; i++) {
|
||||
protocol_h10301_write_bit((fc_cn >> (23 - i)) & 1, 46 + (i * 2), card_data);
|
||||
}
|
||||
|
||||
// odd parity bit
|
||||
protocol_h10301_write_bit((odd_parity_sum % 2), 94, card_data);
|
||||
|
||||
memcpy(encoded_data, &card_data, H10301_ENCODED_DATA_SIZE);
|
||||
}
|
||||
|
||||
bool protocol_h10301_encoder_start(ProtocolH10301* protocol) {
|
||||
protocol_h10301_encode(protocol->data, (uint8_t*)protocol->encoded_data);
|
||||
protocol->encoder.encoded_index = 0;
|
||||
protocol->encoder.pulse = 0;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
LevelDuration protocol_h10301_encoder_yield(ProtocolH10301* protocol) {
|
||||
bool level = 0;
|
||||
uint32_t duration = 0;
|
||||
|
||||
// if pulse is zero, we need to output high, otherwise we need to output low
|
||||
if(protocol->encoder.pulse == 0) {
|
||||
// get bit
|
||||
uint8_t bit =
|
||||
(protocol->encoded_data[protocol->encoder.encoded_index / H10301_BIT_SIZE] >>
|
||||
((H10301_BIT_SIZE - 1) - (protocol->encoder.encoded_index % H10301_BIT_SIZE))) &
|
||||
1;
|
||||
|
||||
// get pulse from oscillator
|
||||
bool advance = fsk_osc_next(protocol->encoder.fsk_osc, bit, &duration);
|
||||
|
||||
if(advance) {
|
||||
protocol->encoder.encoded_index++;
|
||||
if(protocol->encoder.encoded_index >= (H10301_BIT_MAX_SIZE)) {
|
||||
protocol->encoder.encoded_index = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// duration diveded by 2 because we need to output high and low
|
||||
duration = duration / 2;
|
||||
protocol->encoder.pulse = duration;
|
||||
level = true;
|
||||
} else {
|
||||
// output low half and reset pulse
|
||||
duration = protocol->encoder.pulse;
|
||||
protocol->encoder.pulse = 0;
|
||||
level = false;
|
||||
}
|
||||
|
||||
return level_duration_make(level, duration);
|
||||
};
|
||||
|
||||
bool protocol_h10301_write_data(ProtocolH10301* protocol, void* data) {
|
||||
LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data;
|
||||
bool result = false;
|
||||
|
||||
protocol_h10301_encoder_start(protocol);
|
||||
|
||||
if(request->write_type == LFRFIDWriteTypeT5577) {
|
||||
request->t5577.block[0] = LFRFID_T5577_MODULATION_FSK2a | LFRFID_T5577_BITRATE_RF_50 |
|
||||
(3 << LFRFID_T5577_MAXBLOCK_SHIFT);
|
||||
request->t5577.block[1] = protocol->encoded_data[0];
|
||||
request->t5577.block[2] = protocol->encoded_data[1];
|
||||
request->t5577.block[3] = protocol->encoded_data[2];
|
||||
request->t5577.blocks_to_write = 4;
|
||||
result = true;
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
void protocol_h10301_render_data(ProtocolH10301* protocol, string_t result) {
|
||||
uint8_t* data = protocol->data;
|
||||
string_printf(
|
||||
result,
|
||||
"FC: %u\r\n"
|
||||
"Card: %u",
|
||||
data[0],
|
||||
(uint16_t)((data[1] << 8) | (data[2])));
|
||||
};
|
||||
|
||||
const ProtocolBase protocol_h10301 = {
|
||||
.name = "H10301",
|
||||
.manufacturer = "HID",
|
||||
.data_size = H10301_DECODED_DATA_SIZE,
|
||||
.features = LFRFIDFeatureASK,
|
||||
.validate_count = 3,
|
||||
.alloc = (ProtocolAlloc)protocol_h10301_alloc,
|
||||
.free = (ProtocolFree)protocol_h10301_free,
|
||||
.get_data = (ProtocolGetData)protocol_h10301_get_data,
|
||||
.decoder =
|
||||
{
|
||||
.start = (ProtocolDecoderStart)protocol_h10301_decoder_start,
|
||||
.feed = (ProtocolDecoderFeed)protocol_h10301_decoder_feed,
|
||||
},
|
||||
.encoder =
|
||||
{
|
||||
.start = (ProtocolEncoderStart)protocol_h10301_encoder_start,
|
||||
.yield = (ProtocolEncoderYield)protocol_h10301_encoder_yield,
|
||||
},
|
||||
.render_data = (ProtocolRenderData)protocol_h10301_render_data,
|
||||
.render_brief_data = (ProtocolRenderData)protocol_h10301_render_data,
|
||||
.write_data = (ProtocolWriteData)protocol_h10301_write_data,
|
||||
};
|
4
lib/lfrfid/protocols/protocol_h10301.h
Normal file
4
lib/lfrfid/protocols/protocol_h10301.h
Normal file
@@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
#include <toolbox/protocols/protocol.h>
|
||||
|
||||
extern const ProtocolBase protocol_h10301;
|
219
lib/lfrfid/protocols/protocol_hid_ex_generic.c
Normal file
219
lib/lfrfid/protocols/protocol_hid_ex_generic.c
Normal file
@@ -0,0 +1,219 @@
|
||||
#include <furi.h>
|
||||
#include <toolbox/protocols/protocol.h>
|
||||
#include <lfrfid/tools/fsk_demod.h>
|
||||
#include <lfrfid/tools/fsk_osc.h>
|
||||
#include "lfrfid_protocols.h"
|
||||
#include <lfrfid/tools/bit_lib.h>
|
||||
|
||||
#define JITTER_TIME (20)
|
||||
#define MIN_TIME (64 - JITTER_TIME)
|
||||
#define MAX_TIME (80 + JITTER_TIME)
|
||||
|
||||
#define HID_DATA_SIZE 23
|
||||
#define HID_PREAMBLE_SIZE 1
|
||||
|
||||
#define HID_ENCODED_DATA_SIZE (HID_PREAMBLE_SIZE + HID_DATA_SIZE + HID_PREAMBLE_SIZE)
|
||||
#define HID_ENCODED_BIT_SIZE ((HID_PREAMBLE_SIZE + HID_DATA_SIZE) * 8)
|
||||
#define HID_DECODED_DATA_SIZE (12)
|
||||
#define HID_DECODED_BIT_SIZE ((HID_ENCODED_BIT_SIZE - HID_PREAMBLE_SIZE * 8) / 2)
|
||||
|
||||
#define HID_PREAMBLE 0x1D
|
||||
|
||||
typedef struct {
|
||||
FSKDemod* fsk_demod;
|
||||
} ProtocolHIDExDecoder;
|
||||
|
||||
typedef struct {
|
||||
FSKOsc* fsk_osc;
|
||||
uint8_t encoded_index;
|
||||
uint32_t pulse;
|
||||
} ProtocolHIDExEncoder;
|
||||
|
||||
typedef struct {
|
||||
ProtocolHIDExDecoder decoder;
|
||||
ProtocolHIDExEncoder encoder;
|
||||
uint8_t encoded_data[HID_ENCODED_DATA_SIZE];
|
||||
uint8_t data[HID_DECODED_DATA_SIZE];
|
||||
size_t protocol_size;
|
||||
} ProtocolHIDEx;
|
||||
|
||||
ProtocolHIDEx* protocol_hid_ex_generic_alloc(void) {
|
||||
ProtocolHIDEx* protocol = malloc(sizeof(ProtocolHIDEx));
|
||||
protocol->decoder.fsk_demod = fsk_demod_alloc(MIN_TIME, 6, MAX_TIME, 5);
|
||||
protocol->encoder.fsk_osc = fsk_osc_alloc(8, 10, 50);
|
||||
|
||||
return protocol;
|
||||
};
|
||||
|
||||
void protocol_hid_ex_generic_free(ProtocolHIDEx* protocol) {
|
||||
fsk_demod_free(protocol->decoder.fsk_demod);
|
||||
fsk_osc_free(protocol->encoder.fsk_osc);
|
||||
free(protocol);
|
||||
};
|
||||
|
||||
uint8_t* protocol_hid_ex_generic_get_data(ProtocolHIDEx* protocol) {
|
||||
return protocol->data;
|
||||
};
|
||||
|
||||
void protocol_hid_ex_generic_decoder_start(ProtocolHIDEx* protocol) {
|
||||
memset(protocol->encoded_data, 0, HID_ENCODED_DATA_SIZE);
|
||||
};
|
||||
|
||||
static bool protocol_hid_ex_generic_can_be_decoded(const uint8_t* data) {
|
||||
// check preamble
|
||||
if(data[0] != HID_PREAMBLE || data[HID_PREAMBLE_SIZE + HID_DATA_SIZE] != HID_PREAMBLE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// check for manchester encoding
|
||||
for(size_t i = HID_PREAMBLE_SIZE; i < (HID_PREAMBLE_SIZE + HID_DATA_SIZE); i++) {
|
||||
for(size_t n = 0; n < 4; n++) {
|
||||
uint8_t bit_pair = (data[i] >> (n * 2)) & 0b11;
|
||||
if(bit_pair == 0b11 || bit_pair == 0b00) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void protocol_hid_ex_generic_decode(const uint8_t* from, uint8_t* to) {
|
||||
size_t bit_index = 0;
|
||||
for(size_t i = HID_PREAMBLE_SIZE; i < (HID_PREAMBLE_SIZE + HID_DATA_SIZE); i++) {
|
||||
for(size_t n = 0; n < 4; n++) {
|
||||
uint8_t bit_pair = (from[i] >> (6 - (n * 2))) & 0b11;
|
||||
if(bit_pair == 0b01) {
|
||||
bit_lib_set_bit(to, bit_index, 0);
|
||||
} else if(bit_pair == 0b10) {
|
||||
bit_lib_set_bit(to, bit_index, 1);
|
||||
}
|
||||
bit_index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool protocol_hid_ex_generic_decoder_feed(ProtocolHIDEx* protocol, bool level, uint32_t duration) {
|
||||
bool value;
|
||||
uint32_t count;
|
||||
bool result = false;
|
||||
|
||||
fsk_demod_feed(protocol->decoder.fsk_demod, level, duration, &value, &count);
|
||||
if(count > 0) {
|
||||
for(size_t i = 0; i < count; i++) {
|
||||
bit_lib_push_bit(protocol->encoded_data, HID_ENCODED_DATA_SIZE, value);
|
||||
if(protocol_hid_ex_generic_can_be_decoded(protocol->encoded_data)) {
|
||||
protocol_hid_ex_generic_decode(protocol->encoded_data, protocol->data);
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
static void protocol_hid_ex_generic_encode(ProtocolHIDEx* protocol) {
|
||||
protocol->encoded_data[0] = HID_PREAMBLE;
|
||||
|
||||
size_t bit_index = 0;
|
||||
for(size_t i = 0; i < HID_DECODED_BIT_SIZE; i++) {
|
||||
bool bit = bit_lib_get_bit(protocol->data, i);
|
||||
if(bit) {
|
||||
bit_lib_set_bit(protocol->encoded_data, 8 + bit_index, 1);
|
||||
bit_lib_set_bit(protocol->encoded_data, 8 + bit_index + 1, 0);
|
||||
} else {
|
||||
bit_lib_set_bit(protocol->encoded_data, 8 + bit_index, 0);
|
||||
bit_lib_set_bit(protocol->encoded_data, 8 + bit_index + 1, 1);
|
||||
}
|
||||
bit_index += 2;
|
||||
}
|
||||
}
|
||||
|
||||
bool protocol_hid_ex_generic_encoder_start(ProtocolHIDEx* protocol) {
|
||||
protocol->encoder.encoded_index = 0;
|
||||
protocol->encoder.pulse = 0;
|
||||
protocol_hid_ex_generic_encode(protocol);
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
LevelDuration protocol_hid_ex_generic_encoder_yield(ProtocolHIDEx* protocol) {
|
||||
bool level = 0;
|
||||
uint32_t duration = 0;
|
||||
|
||||
// if pulse is zero, we need to output high, otherwise we need to output low
|
||||
if(protocol->encoder.pulse == 0) {
|
||||
// get bit
|
||||
uint8_t bit = bit_lib_get_bit(protocol->encoded_data, protocol->encoder.encoded_index);
|
||||
|
||||
// get pulse from oscillator
|
||||
bool advance = fsk_osc_next(protocol->encoder.fsk_osc, bit, &duration);
|
||||
|
||||
if(advance) {
|
||||
bit_lib_increment_index(protocol->encoder.encoded_index, HID_ENCODED_BIT_SIZE);
|
||||
}
|
||||
|
||||
// duration diveded by 2 because we need to output high and low
|
||||
duration = duration / 2;
|
||||
protocol->encoder.pulse = duration;
|
||||
level = true;
|
||||
} else {
|
||||
// output low half and reset pulse
|
||||
duration = protocol->encoder.pulse;
|
||||
protocol->encoder.pulse = 0;
|
||||
level = false;
|
||||
}
|
||||
|
||||
return level_duration_make(level, duration);
|
||||
};
|
||||
|
||||
bool protocol_hid_ex_generic_write_data(ProtocolHIDEx* protocol, void* data) {
|
||||
LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data;
|
||||
bool result = false;
|
||||
|
||||
protocol_hid_ex_generic_encoder_start(protocol);
|
||||
|
||||
if(request->write_type == LFRFIDWriteTypeT5577) {
|
||||
request->t5577.block[0] = LFRFID_T5577_MODULATION_FSK2a | LFRFID_T5577_BITRATE_RF_50 |
|
||||
(6 << LFRFID_T5577_MAXBLOCK_SHIFT);
|
||||
request->t5577.block[1] = bit_lib_get_bits_32(protocol->encoded_data, 0, 32);
|
||||
request->t5577.block[2] = bit_lib_get_bits_32(protocol->encoded_data, 32, 32);
|
||||
request->t5577.block[3] = bit_lib_get_bits_32(protocol->encoded_data, 64, 32);
|
||||
request->t5577.block[4] = bit_lib_get_bits_32(protocol->encoded_data, 96, 32);
|
||||
request->t5577.block[5] = bit_lib_get_bits_32(protocol->encoded_data, 128, 32);
|
||||
request->t5577.block[6] = bit_lib_get_bits_32(protocol->encoded_data, 160, 32);
|
||||
request->t5577.blocks_to_write = 7;
|
||||
result = true;
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
void protocol_hid_ex_generic_render_data(ProtocolHIDEx* protocol, string_t result) {
|
||||
// TODO: parser and render functions
|
||||
UNUSED(protocol);
|
||||
string_printf(result, "Generic HID Extended\r\nData: Unknown");
|
||||
};
|
||||
|
||||
const ProtocolBase protocol_hid_ex_generic = {
|
||||
.name = "HIDExt",
|
||||
.manufacturer = "Generic",
|
||||
.data_size = HID_DECODED_DATA_SIZE,
|
||||
.features = LFRFIDFeatureASK,
|
||||
.validate_count = 3,
|
||||
.alloc = (ProtocolAlloc)protocol_hid_ex_generic_alloc,
|
||||
.free = (ProtocolFree)protocol_hid_ex_generic_free,
|
||||
.get_data = (ProtocolGetData)protocol_hid_ex_generic_get_data,
|
||||
.decoder =
|
||||
{
|
||||
.start = (ProtocolDecoderStart)protocol_hid_ex_generic_decoder_start,
|
||||
.feed = (ProtocolDecoderFeed)protocol_hid_ex_generic_decoder_feed,
|
||||
},
|
||||
.encoder =
|
||||
{
|
||||
.start = (ProtocolEncoderStart)protocol_hid_ex_generic_encoder_start,
|
||||
.yield = (ProtocolEncoderYield)protocol_hid_ex_generic_encoder_yield,
|
||||
},
|
||||
.render_data = (ProtocolRenderData)protocol_hid_ex_generic_render_data,
|
||||
.render_brief_data = (ProtocolRenderData)protocol_hid_ex_generic_render_data,
|
||||
.write_data = (ProtocolWriteData)protocol_hid_ex_generic_write_data,
|
||||
};
|
4
lib/lfrfid/protocols/protocol_hid_ex_generic.h
Normal file
4
lib/lfrfid/protocols/protocol_hid_ex_generic.h
Normal file
@@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
#include <toolbox/protocols/protocol.h>
|
||||
|
||||
extern const ProtocolBase protocol_hid_ex_generic;
|
280
lib/lfrfid/protocols/protocol_hid_generic.c
Normal file
280
lib/lfrfid/protocols/protocol_hid_generic.c
Normal file
@@ -0,0 +1,280 @@
|
||||
#include <furi.h>
|
||||
#include <toolbox/protocols/protocol.h>
|
||||
#include <lfrfid/tools/fsk_demod.h>
|
||||
#include <lfrfid/tools/fsk_osc.h>
|
||||
#include "lfrfid_protocols.h"
|
||||
#include <lfrfid/tools/bit_lib.h>
|
||||
|
||||
#define JITTER_TIME (20)
|
||||
#define MIN_TIME (64 - JITTER_TIME)
|
||||
#define MAX_TIME (80 + JITTER_TIME)
|
||||
|
||||
#define HID_DATA_SIZE 11
|
||||
#define HID_PREAMBLE_SIZE 1
|
||||
#define HID_PROTOCOL_SIZE_UNKNOWN 0
|
||||
|
||||
#define HID_ENCODED_DATA_SIZE (HID_PREAMBLE_SIZE + HID_DATA_SIZE + HID_PREAMBLE_SIZE)
|
||||
#define HID_ENCODED_BIT_SIZE ((HID_PREAMBLE_SIZE + HID_DATA_SIZE) * 8)
|
||||
#define HID_DECODED_DATA_SIZE (6)
|
||||
#define HID_DECODED_BIT_SIZE ((HID_ENCODED_BIT_SIZE - HID_PREAMBLE_SIZE * 8) / 2)
|
||||
|
||||
#define HID_PREAMBLE 0x1D
|
||||
|
||||
typedef struct {
|
||||
FSKDemod* fsk_demod;
|
||||
} ProtocolHIDDecoder;
|
||||
|
||||
typedef struct {
|
||||
FSKOsc* fsk_osc;
|
||||
uint8_t encoded_index;
|
||||
uint32_t pulse;
|
||||
} ProtocolHIDEncoder;
|
||||
|
||||
typedef struct {
|
||||
ProtocolHIDDecoder decoder;
|
||||
ProtocolHIDEncoder encoder;
|
||||
uint8_t encoded_data[HID_ENCODED_DATA_SIZE];
|
||||
uint8_t data[HID_DECODED_DATA_SIZE];
|
||||
} ProtocolHID;
|
||||
|
||||
ProtocolHID* protocol_hid_generic_alloc(void) {
|
||||
ProtocolHID* protocol = malloc(sizeof(ProtocolHID));
|
||||
protocol->decoder.fsk_demod = fsk_demod_alloc(MIN_TIME, 6, MAX_TIME, 5);
|
||||
protocol->encoder.fsk_osc = fsk_osc_alloc(8, 10, 50);
|
||||
|
||||
return protocol;
|
||||
};
|
||||
|
||||
void protocol_hid_generic_free(ProtocolHID* protocol) {
|
||||
fsk_demod_free(protocol->decoder.fsk_demod);
|
||||
fsk_osc_free(protocol->encoder.fsk_osc);
|
||||
free(protocol);
|
||||
};
|
||||
|
||||
uint8_t* protocol_hid_generic_get_data(ProtocolHID* protocol) {
|
||||
return protocol->data;
|
||||
};
|
||||
|
||||
void protocol_hid_generic_decoder_start(ProtocolHID* protocol) {
|
||||
memset(protocol->encoded_data, 0, HID_ENCODED_DATA_SIZE);
|
||||
};
|
||||
|
||||
static bool protocol_hid_generic_can_be_decoded(const uint8_t* data) {
|
||||
// check preamble
|
||||
if(data[0] != HID_PREAMBLE || data[HID_PREAMBLE_SIZE + HID_DATA_SIZE] != HID_PREAMBLE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// check for manchester encoding
|
||||
for(size_t i = HID_PREAMBLE_SIZE; i < (HID_PREAMBLE_SIZE + HID_DATA_SIZE); i++) {
|
||||
for(size_t n = 0; n < 4; n++) {
|
||||
uint8_t bit_pair = (data[i] >> (n * 2)) & 0b11;
|
||||
if(bit_pair == 0b11 || bit_pair == 0b00) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void protocol_hid_generic_decode(const uint8_t* from, uint8_t* to) {
|
||||
size_t bit_index = 0;
|
||||
for(size_t i = HID_PREAMBLE_SIZE; i < (HID_PREAMBLE_SIZE + HID_DATA_SIZE); i++) {
|
||||
for(size_t n = 0; n < 4; n++) {
|
||||
uint8_t bit_pair = (from[i] >> (6 - (n * 2))) & 0b11;
|
||||
if(bit_pair == 0b01) {
|
||||
bit_lib_set_bit(to, bit_index, 0);
|
||||
} else if(bit_pair == 0b10) {
|
||||
bit_lib_set_bit(to, bit_index, 1);
|
||||
}
|
||||
bit_index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes size from the HID Proximity header:
|
||||
* - If any of the first six bits is 1, the key is composed of the bits
|
||||
* following the first 1
|
||||
* - Otherwise, if the first six bits are 0:
|
||||
* - If the seventh bit is 0, the key is composed of the remaining 37 bits.
|
||||
* - If the seventh bit is 1, the size header continues until the next 1 bit,
|
||||
* and the key is composed of however many bits remain.
|
||||
*
|
||||
* HID Proximity keys are 26 bits at minimum. If the header implies a key size
|
||||
* under 26 bits, this function returns HID_PROTOCOL_SIZE_UNKNOWN.
|
||||
*/
|
||||
static uint8_t protocol_hid_generic_decode_protocol_size(ProtocolHID* protocol) {
|
||||
for(size_t bit_index = 0; bit_index < 6; bit_index++) {
|
||||
if(bit_lib_get_bit(protocol->data, bit_index)) {
|
||||
return HID_DECODED_BIT_SIZE - bit_index - 1;
|
||||
}
|
||||
}
|
||||
|
||||
if(!bit_lib_get_bit(protocol->data, 6)) {
|
||||
return 37;
|
||||
}
|
||||
|
||||
size_t bit_index = 7;
|
||||
uint8_t size = 36;
|
||||
while(!bit_lib_get_bit(protocol->data, bit_index) && size >= 26) {
|
||||
size--;
|
||||
bit_index++;
|
||||
}
|
||||
return size < 26 ? HID_PROTOCOL_SIZE_UNKNOWN : size;
|
||||
}
|
||||
|
||||
bool protocol_hid_generic_decoder_feed(ProtocolHID* protocol, bool level, uint32_t duration) {
|
||||
bool value;
|
||||
uint32_t count;
|
||||
bool result = false;
|
||||
|
||||
fsk_demod_feed(protocol->decoder.fsk_demod, level, duration, &value, &count);
|
||||
if(count > 0) {
|
||||
for(size_t i = 0; i < count; i++) {
|
||||
bit_lib_push_bit(protocol->encoded_data, HID_ENCODED_DATA_SIZE, value);
|
||||
if(protocol_hid_generic_can_be_decoded(protocol->encoded_data)) {
|
||||
protocol_hid_generic_decode(protocol->encoded_data, protocol->data);
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
static void protocol_hid_generic_encode(ProtocolHID* protocol) {
|
||||
protocol->encoded_data[0] = HID_PREAMBLE;
|
||||
|
||||
size_t bit_index = 0;
|
||||
for(size_t i = 0; i < HID_DECODED_BIT_SIZE; i++) {
|
||||
bool bit = bit_lib_get_bit(protocol->data, i);
|
||||
if(bit) {
|
||||
bit_lib_set_bit(protocol->encoded_data, 8 + bit_index, 1);
|
||||
bit_lib_set_bit(protocol->encoded_data, 8 + bit_index + 1, 0);
|
||||
} else {
|
||||
bit_lib_set_bit(protocol->encoded_data, 8 + bit_index, 0);
|
||||
bit_lib_set_bit(protocol->encoded_data, 8 + bit_index + 1, 1);
|
||||
}
|
||||
bit_index += 2;
|
||||
}
|
||||
}
|
||||
|
||||
bool protocol_hid_generic_encoder_start(ProtocolHID* protocol) {
|
||||
protocol->encoder.encoded_index = 0;
|
||||
protocol->encoder.pulse = 0;
|
||||
protocol_hid_generic_encode(protocol);
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
LevelDuration protocol_hid_generic_encoder_yield(ProtocolHID* protocol) {
|
||||
bool level = 0;
|
||||
uint32_t duration = 0;
|
||||
|
||||
// if pulse is zero, we need to output high, otherwise we need to output low
|
||||
if(protocol->encoder.pulse == 0) {
|
||||
// get bit
|
||||
uint8_t bit = bit_lib_get_bit(protocol->encoded_data, protocol->encoder.encoded_index);
|
||||
|
||||
// get pulse from oscillator
|
||||
bool advance = fsk_osc_next(protocol->encoder.fsk_osc, bit, &duration);
|
||||
|
||||
if(advance) {
|
||||
bit_lib_increment_index(protocol->encoder.encoded_index, HID_ENCODED_BIT_SIZE);
|
||||
}
|
||||
|
||||
// duration diveded by 2 because we need to output high and low
|
||||
duration = duration / 2;
|
||||
protocol->encoder.pulse = duration;
|
||||
level = true;
|
||||
} else {
|
||||
// output low half and reset pulse
|
||||
duration = protocol->encoder.pulse;
|
||||
protocol->encoder.pulse = 0;
|
||||
level = false;
|
||||
}
|
||||
|
||||
return level_duration_make(level, duration);
|
||||
};
|
||||
|
||||
bool protocol_hid_generic_write_data(ProtocolHID* protocol, void* data) {
|
||||
LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data;
|
||||
bool result = false;
|
||||
|
||||
protocol_hid_generic_encoder_start(protocol);
|
||||
|
||||
if(request->write_type == LFRFIDWriteTypeT5577) {
|
||||
request->t5577.block[0] = LFRFID_T5577_MODULATION_FSK2a | LFRFID_T5577_BITRATE_RF_50 |
|
||||
(3 << LFRFID_T5577_MAXBLOCK_SHIFT);
|
||||
request->t5577.block[1] = bit_lib_get_bits_32(protocol->encoded_data, 0, 32);
|
||||
request->t5577.block[2] = bit_lib_get_bits_32(protocol->encoded_data, 32, 32);
|
||||
request->t5577.block[3] = bit_lib_get_bits_32(protocol->encoded_data, 64, 32);
|
||||
request->t5577.blocks_to_write = 4;
|
||||
result = true;
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
static void protocol_hid_generic_string_cat_protocol_bits(ProtocolHID* protocol, uint8_t protocol_size, string_t result) {
|
||||
// round up to the nearest nibble
|
||||
const uint8_t hex_character_count = (protocol_size + 3) / 4;
|
||||
const uint8_t protocol_bit_index = HID_DECODED_BIT_SIZE - protocol_size;
|
||||
|
||||
for(size_t i = 0; i < hex_character_count; i++) {
|
||||
uint8_t nibble =
|
||||
i == 0 ? bit_lib_get_bits(
|
||||
protocol->data, protocol_bit_index, protocol_size % 4 == 0 ? 4 : protocol_size % 4) :
|
||||
bit_lib_get_bits(protocol->data, protocol_bit_index + i * 4, 4);
|
||||
string_cat_printf(result, "%X", nibble & 0xF);
|
||||
}
|
||||
}
|
||||
|
||||
void protocol_hid_generic_render_data(ProtocolHID* protocol, string_t result) {
|
||||
const uint8_t protocol_size = protocol_hid_generic_decode_protocol_size(protocol);
|
||||
|
||||
if(protocol_size == HID_PROTOCOL_SIZE_UNKNOWN) {
|
||||
string_printf(
|
||||
result,
|
||||
"Generic HID Proximity\r\n"
|
||||
"Data: %02X%02X%02X%02X%02X%X",
|
||||
protocol->data[0],
|
||||
protocol->data[1],
|
||||
protocol->data[2],
|
||||
protocol->data[3],
|
||||
protocol->data[4],
|
||||
protocol->data[5] >> 4);
|
||||
} else {
|
||||
string_printf(
|
||||
result,
|
||||
"%hhu-bit HID Proximity\r\n"
|
||||
"Data: ",
|
||||
protocol_size);
|
||||
protocol_hid_generic_string_cat_protocol_bits(protocol, protocol_size, result);
|
||||
}
|
||||
};
|
||||
|
||||
const ProtocolBase protocol_hid_generic = {
|
||||
.name = "HIDProx",
|
||||
.manufacturer = "Generic",
|
||||
.data_size = HID_DECODED_DATA_SIZE,
|
||||
.features = LFRFIDFeatureASK,
|
||||
.validate_count = 6,
|
||||
.alloc = (ProtocolAlloc)protocol_hid_generic_alloc,
|
||||
.free = (ProtocolFree)protocol_hid_generic_free,
|
||||
.get_data = (ProtocolGetData)protocol_hid_generic_get_data,
|
||||
.decoder =
|
||||
{
|
||||
.start = (ProtocolDecoderStart)protocol_hid_generic_decoder_start,
|
||||
.feed = (ProtocolDecoderFeed)protocol_hid_generic_decoder_feed,
|
||||
},
|
||||
.encoder =
|
||||
{
|
||||
.start = (ProtocolEncoderStart)protocol_hid_generic_encoder_start,
|
||||
.yield = (ProtocolEncoderYield)protocol_hid_generic_encoder_yield,
|
||||
},
|
||||
.render_data = (ProtocolRenderData)protocol_hid_generic_render_data,
|
||||
.render_brief_data = (ProtocolRenderData)protocol_hid_generic_render_data,
|
||||
.write_data = (ProtocolWriteData)protocol_hid_generic_write_data,
|
||||
};
|
4
lib/lfrfid/protocols/protocol_hid_generic.h
Normal file
4
lib/lfrfid/protocols/protocol_hid_generic.h
Normal file
@@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
#include <toolbox/protocols/protocol.h>
|
||||
|
||||
extern const ProtocolBase protocol_hid_generic;
|
353
lib/lfrfid/protocols/protocol_indala26.c
Normal file
353
lib/lfrfid/protocols/protocol_indala26.c
Normal file
@@ -0,0 +1,353 @@
|
||||
#include <furi.h>
|
||||
#include <toolbox/protocols/protocol.h>
|
||||
#include <lfrfid/tools/bit_lib.h>
|
||||
#include "lfrfid_protocols.h"
|
||||
|
||||
#define INDALA26_PREAMBLE_BIT_SIZE (33)
|
||||
#define INDALA26_PREAMBLE_DATA_SIZE (5)
|
||||
|
||||
#define INDALA26_ENCODED_BIT_SIZE (64)
|
||||
#define INDALA26_ENCODED_DATA_SIZE \
|
||||
(((INDALA26_ENCODED_BIT_SIZE) / 8) + INDALA26_PREAMBLE_DATA_SIZE)
|
||||
#define INDALA26_ENCODED_DATA_LAST ((INDALA26_ENCODED_BIT_SIZE) / 8)
|
||||
|
||||
#define INDALA26_DECODED_BIT_SIZE (28)
|
||||
#define INDALA26_DECODED_DATA_SIZE (4)
|
||||
|
||||
#define INDALA26_US_PER_BIT (255)
|
||||
#define INDALA26_ENCODER_PULSES_PER_BIT (16)
|
||||
|
||||
typedef struct {
|
||||
uint8_t data_index;
|
||||
uint8_t bit_clock_index;
|
||||
bool last_bit;
|
||||
bool current_polarity;
|
||||
bool pulse_phase;
|
||||
} ProtocolIndalaEncoder;
|
||||
|
||||
typedef struct {
|
||||
uint8_t encoded_data[INDALA26_ENCODED_DATA_SIZE];
|
||||
uint8_t negative_encoded_data[INDALA26_ENCODED_DATA_SIZE];
|
||||
uint8_t corrupted_encoded_data[INDALA26_ENCODED_DATA_SIZE];
|
||||
uint8_t corrupted_negative_encoded_data[INDALA26_ENCODED_DATA_SIZE];
|
||||
|
||||
uint8_t data[INDALA26_DECODED_DATA_SIZE];
|
||||
ProtocolIndalaEncoder encoder;
|
||||
} ProtocolIndala;
|
||||
|
||||
ProtocolIndala* protocol_indala26_alloc(void) {
|
||||
ProtocolIndala* protocol = malloc(sizeof(ProtocolIndala));
|
||||
return protocol;
|
||||
};
|
||||
|
||||
void protocol_indala26_free(ProtocolIndala* protocol) {
|
||||
free(protocol);
|
||||
};
|
||||
|
||||
uint8_t* protocol_indala26_get_data(ProtocolIndala* protocol) {
|
||||
return protocol->data;
|
||||
};
|
||||
|
||||
void protocol_indala26_decoder_start(ProtocolIndala* protocol) {
|
||||
memset(protocol->encoded_data, 0, INDALA26_ENCODED_DATA_SIZE);
|
||||
memset(protocol->negative_encoded_data, 0, INDALA26_ENCODED_DATA_SIZE);
|
||||
memset(protocol->corrupted_encoded_data, 0, INDALA26_ENCODED_DATA_SIZE);
|
||||
memset(protocol->corrupted_negative_encoded_data, 0, INDALA26_ENCODED_DATA_SIZE);
|
||||
};
|
||||
|
||||
static bool protocol_indala26_check_preamble(uint8_t* data, size_t bit_index) {
|
||||
// Preamble 10100000 00000000 00000000 00000000 1
|
||||
if(*(uint32_t*)&data[bit_index / 8] != 0b00000000000000000000000010100000) return false;
|
||||
if(bit_lib_get_bit(data, bit_index + 32) != 1) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool protocol_indala26_can_be_decoded(uint8_t* data) {
|
||||
if(!protocol_indala26_check_preamble(data, 0)) return false;
|
||||
if(!protocol_indala26_check_preamble(data, 64)) return false;
|
||||
if(bit_lib_get_bit(data, 61) != 0) return false;
|
||||
if(bit_lib_get_bit(data, 60) != 0) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool protocol_indala26_decoder_feed_internal(bool polarity, uint32_t time, uint8_t* data) {
|
||||
time += (INDALA26_US_PER_BIT / 2);
|
||||
|
||||
size_t bit_count = (time / INDALA26_US_PER_BIT);
|
||||
bool result = false;
|
||||
|
||||
if(bit_count < INDALA26_ENCODED_BIT_SIZE) {
|
||||
for(size_t i = 0; i < bit_count; i++) {
|
||||
bit_lib_push_bit(data, INDALA26_ENCODED_DATA_SIZE, polarity);
|
||||
if(protocol_indala26_can_be_decoded(data)) {
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void protocol_indala26_decoder_save(uint8_t* data_to, const uint8_t* data_from) {
|
||||
bit_lib_copy_bits(data_to, 0, 22, data_from, 33);
|
||||
bit_lib_copy_bits(data_to, 22, 5, data_from, 55);
|
||||
bit_lib_copy_bits(data_to, 27, 2, data_from, 62);
|
||||
}
|
||||
|
||||
bool protocol_indala26_decoder_feed(ProtocolIndala* protocol, bool level, uint32_t duration) {
|
||||
bool result = false;
|
||||
|
||||
if(duration > (INDALA26_US_PER_BIT / 2)) {
|
||||
if(protocol_indala26_decoder_feed_internal(level, duration, protocol->encoded_data)) {
|
||||
protocol_indala26_decoder_save(protocol->data, protocol->encoded_data);
|
||||
FURI_LOG_D("Indala26", "Positive");
|
||||
result = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
if(protocol_indala26_decoder_feed_internal(
|
||||
!level, duration, protocol->negative_encoded_data)) {
|
||||
protocol_indala26_decoder_save(protocol->data, protocol->negative_encoded_data);
|
||||
FURI_LOG_D("Indala26", "Negative");
|
||||
result = true;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
if(duration > (INDALA26_US_PER_BIT / 4)) {
|
||||
// Try to decode wrong phase synced data
|
||||
if(level) {
|
||||
duration += 120;
|
||||
} else {
|
||||
if(duration > 120) {
|
||||
duration -= 120;
|
||||
}
|
||||
}
|
||||
|
||||
if(protocol_indala26_decoder_feed_internal(
|
||||
level, duration, protocol->corrupted_encoded_data)) {
|
||||
protocol_indala26_decoder_save(protocol->data, protocol->corrupted_encoded_data);
|
||||
FURI_LOG_D("Indala26", "Positive Corrupted");
|
||||
|
||||
result = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
if(protocol_indala26_decoder_feed_internal(
|
||||
!level, duration, protocol->corrupted_negative_encoded_data)) {
|
||||
protocol_indala26_decoder_save(
|
||||
protocol->data, protocol->corrupted_negative_encoded_data);
|
||||
FURI_LOG_D("Indala26", "Negative Corrupted");
|
||||
|
||||
result = true;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
bool protocol_indala26_encoder_start(ProtocolIndala* protocol) {
|
||||
memset(protocol->encoded_data, 0, INDALA26_ENCODED_DATA_SIZE);
|
||||
*(uint32_t*)&protocol->encoded_data[0] = 0b00000000000000000000000010100000;
|
||||
bit_lib_set_bit(protocol->encoded_data, 32, 1);
|
||||
bit_lib_copy_bits(protocol->encoded_data, 33, 22, protocol->data, 0);
|
||||
bit_lib_copy_bits(protocol->encoded_data, 55, 5, protocol->data, 22);
|
||||
bit_lib_copy_bits(protocol->encoded_data, 62, 2, protocol->data, 27);
|
||||
|
||||
protocol->encoder.last_bit =
|
||||
bit_lib_get_bit(protocol->encoded_data, INDALA26_ENCODED_BIT_SIZE - 1);
|
||||
protocol->encoder.data_index = 0;
|
||||
protocol->encoder.current_polarity = true;
|
||||
protocol->encoder.pulse_phase = true;
|
||||
protocol->encoder.bit_clock_index = 0;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
LevelDuration protocol_indala26_encoder_yield(ProtocolIndala* protocol) {
|
||||
LevelDuration level_duration;
|
||||
ProtocolIndalaEncoder* encoder = &protocol->encoder;
|
||||
|
||||
if(encoder->pulse_phase) {
|
||||
level_duration = level_duration_make(encoder->current_polarity, 1);
|
||||
encoder->pulse_phase = false;
|
||||
} else {
|
||||
level_duration = level_duration_make(!encoder->current_polarity, 1);
|
||||
encoder->pulse_phase = true;
|
||||
|
||||
encoder->bit_clock_index++;
|
||||
if(encoder->bit_clock_index >= INDALA26_ENCODER_PULSES_PER_BIT) {
|
||||
encoder->bit_clock_index = 0;
|
||||
|
||||
bool current_bit = bit_lib_get_bit(protocol->encoded_data, encoder->data_index);
|
||||
|
||||
if(current_bit != encoder->last_bit) {
|
||||
encoder->current_polarity = !encoder->current_polarity;
|
||||
}
|
||||
|
||||
encoder->last_bit = current_bit;
|
||||
|
||||
bit_lib_increment_index(encoder->data_index, INDALA26_ENCODED_BIT_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
return level_duration;
|
||||
};
|
||||
|
||||
// factory code
|
||||
static uint8_t get_fc(const uint8_t* data) {
|
||||
uint8_t fc = 0;
|
||||
|
||||
fc = fc << 1 | bit_lib_get_bit(data, 24);
|
||||
fc = fc << 1 | bit_lib_get_bit(data, 16);
|
||||
fc = fc << 1 | bit_lib_get_bit(data, 11);
|
||||
fc = fc << 1 | bit_lib_get_bit(data, 14);
|
||||
fc = fc << 1 | bit_lib_get_bit(data, 15);
|
||||
fc = fc << 1 | bit_lib_get_bit(data, 20);
|
||||
fc = fc << 1 | bit_lib_get_bit(data, 6);
|
||||
fc = fc << 1 | bit_lib_get_bit(data, 25);
|
||||
|
||||
return fc;
|
||||
}
|
||||
|
||||
// card number
|
||||
static uint16_t get_cn(const uint8_t* data) {
|
||||
uint16_t cn = 0;
|
||||
|
||||
cn = cn << 1 | bit_lib_get_bit(data, 9);
|
||||
cn = cn << 1 | bit_lib_get_bit(data, 12);
|
||||
cn = cn << 1 | bit_lib_get_bit(data, 10);
|
||||
cn = cn << 1 | bit_lib_get_bit(data, 7);
|
||||
cn = cn << 1 | bit_lib_get_bit(data, 19);
|
||||
cn = cn << 1 | bit_lib_get_bit(data, 3);
|
||||
cn = cn << 1 | bit_lib_get_bit(data, 2);
|
||||
cn = cn << 1 | bit_lib_get_bit(data, 18);
|
||||
cn = cn << 1 | bit_lib_get_bit(data, 13);
|
||||
cn = cn << 1 | bit_lib_get_bit(data, 0);
|
||||
cn = cn << 1 | bit_lib_get_bit(data, 4);
|
||||
cn = cn << 1 | bit_lib_get_bit(data, 21);
|
||||
cn = cn << 1 | bit_lib_get_bit(data, 23);
|
||||
cn = cn << 1 | bit_lib_get_bit(data, 26);
|
||||
cn = cn << 1 | bit_lib_get_bit(data, 17);
|
||||
cn = cn << 1 | bit_lib_get_bit(data, 8);
|
||||
|
||||
return cn;
|
||||
}
|
||||
|
||||
void protocol_indala26_render_data_internal(ProtocolIndala* protocol, string_t result, bool brief) {
|
||||
bool wiegand_correct = true;
|
||||
bool checksum_correct = true;
|
||||
|
||||
const uint8_t fc = get_fc(protocol->data);
|
||||
const uint16_t card = get_cn(protocol->data);
|
||||
const uint32_t fc_and_card = fc << 16 | card;
|
||||
const uint8_t checksum = bit_lib_get_bit(protocol->data, 27) << 1 |
|
||||
bit_lib_get_bit(protocol->data, 28);
|
||||
const bool even_parity = bit_lib_get_bit(protocol->data, 1);
|
||||
const bool odd_parity = bit_lib_get_bit(protocol->data, 5);
|
||||
|
||||
// indala checksum
|
||||
uint8_t checksum_sum = 0;
|
||||
checksum_sum += ((fc_and_card >> 14) & 1);
|
||||
checksum_sum += ((fc_and_card >> 12) & 1);
|
||||
checksum_sum += ((fc_and_card >> 9) & 1);
|
||||
checksum_sum += ((fc_and_card >> 8) & 1);
|
||||
checksum_sum += ((fc_and_card >> 6) & 1);
|
||||
checksum_sum += ((fc_and_card >> 5) & 1);
|
||||
checksum_sum += ((fc_and_card >> 2) & 1);
|
||||
checksum_sum += ((fc_and_card >> 0) & 1);
|
||||
checksum_sum = checksum_sum & 0b1;
|
||||
|
||||
if(checksum_sum == 1 && checksum == 0b01) {
|
||||
} else if(checksum_sum == 0 && checksum == 0b10) {
|
||||
} else {
|
||||
checksum_correct = false;
|
||||
}
|
||||
|
||||
// wiegand parity
|
||||
uint8_t even_parity_sum = 0;
|
||||
for(int8_t i = 12; i < 24; i++) {
|
||||
if(((fc_and_card >> i) & 1) == 1) {
|
||||
even_parity_sum++;
|
||||
}
|
||||
}
|
||||
if(even_parity_sum % 2 != even_parity) wiegand_correct = false;
|
||||
|
||||
uint8_t odd_parity_sum = 1;
|
||||
for(int8_t i = 0; i < 12; i++) {
|
||||
if(((fc_and_card >> i) & 1) == 1) {
|
||||
odd_parity_sum++;
|
||||
}
|
||||
}
|
||||
if(odd_parity_sum % 2 != odd_parity) wiegand_correct = false;
|
||||
|
||||
if(brief) {
|
||||
string_printf(
|
||||
result,
|
||||
"FC: %u\r\nCard: %u, Parity:%s%s",
|
||||
fc,
|
||||
card,
|
||||
(checksum_correct ? "+" : "-"),
|
||||
(wiegand_correct ? "+" : "-"));
|
||||
} else {
|
||||
string_printf(
|
||||
result,
|
||||
"FC: %u\r\n"
|
||||
"Card: %u\r\n"
|
||||
"Checksum: %s\r\n"
|
||||
"W26 Parity: %s",
|
||||
fc,
|
||||
card,
|
||||
(checksum_correct ? "+" : "-"),
|
||||
(wiegand_correct ? "+" : "-"));
|
||||
}
|
||||
}
|
||||
void protocol_indala26_render_data(ProtocolIndala* protocol, string_t result) {
|
||||
protocol_indala26_render_data_internal(protocol, result, false);
|
||||
}
|
||||
void protocol_indala26_render_brief_data(ProtocolIndala* protocol, string_t result) {
|
||||
protocol_indala26_render_data_internal(protocol, result, true);
|
||||
}
|
||||
|
||||
bool protocol_indala26_write_data(ProtocolIndala* protocol, void* data) {
|
||||
LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data;
|
||||
bool result = false;
|
||||
|
||||
protocol_indala26_encoder_start(protocol);
|
||||
|
||||
if(request->write_type == LFRFIDWriteTypeT5577) {
|
||||
request->t5577.block[0] = LFRFID_T5577_BITRATE_RF_32 | LFRFID_T5577_MODULATION_PSK1 |
|
||||
(2 << LFRFID_T5577_MAXBLOCK_SHIFT);
|
||||
request->t5577.block[1] = bit_lib_get_bits_32(protocol->encoded_data, 0, 32);
|
||||
request->t5577.block[2] = bit_lib_get_bits_32(protocol->encoded_data, 32, 32);
|
||||
request->t5577.blocks_to_write = 3;
|
||||
result = true;
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
const ProtocolBase protocol_indala26 = {
|
||||
.name = "Indala26",
|
||||
.manufacturer = "Motorola",
|
||||
.data_size = INDALA26_DECODED_DATA_SIZE,
|
||||
.features = LFRFIDFeaturePSK,
|
||||
.validate_count = 6,
|
||||
.alloc = (ProtocolAlloc)protocol_indala26_alloc,
|
||||
.free = (ProtocolFree)protocol_indala26_free,
|
||||
.get_data = (ProtocolGetData)protocol_indala26_get_data,
|
||||
.decoder =
|
||||
{
|
||||
.start = (ProtocolDecoderStart)protocol_indala26_decoder_start,
|
||||
.feed = (ProtocolDecoderFeed)protocol_indala26_decoder_feed,
|
||||
},
|
||||
.encoder =
|
||||
{
|
||||
.start = (ProtocolEncoderStart)protocol_indala26_encoder_start,
|
||||
.yield = (ProtocolEncoderYield)protocol_indala26_encoder_yield,
|
||||
},
|
||||
.render_data = (ProtocolRenderData)protocol_indala26_render_data,
|
||||
.render_brief_data = (ProtocolRenderData)protocol_indala26_render_brief_data,
|
||||
.write_data = (ProtocolWriteData)protocol_indala26_write_data,
|
||||
};
|
4
lib/lfrfid/protocols/protocol_indala26.h
Normal file
4
lib/lfrfid/protocols/protocol_indala26.h
Normal file
@@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
#include <toolbox/protocols/protocol.h>
|
||||
|
||||
extern const ProtocolBase protocol_indala26;
|
297
lib/lfrfid/protocols/protocol_io_prox_xsf.c
Normal file
297
lib/lfrfid/protocols/protocol_io_prox_xsf.c
Normal file
@@ -0,0 +1,297 @@
|
||||
#include <furi.h>
|
||||
#include <toolbox/protocols/protocol.h>
|
||||
#include <lfrfid/tools/fsk_demod.h>
|
||||
#include <lfrfid/tools/fsk_osc.h>
|
||||
#include <lfrfid/tools/bit_lib.h>
|
||||
#include "lfrfid_protocols.h"
|
||||
|
||||
#define JITTER_TIME (20)
|
||||
#define MIN_TIME (64 - JITTER_TIME)
|
||||
#define MAX_TIME (80 + JITTER_TIME)
|
||||
|
||||
#define IOPROXXSF_DECODED_DATA_SIZE (4)
|
||||
#define IOPROXXSF_ENCODED_DATA_SIZE (8)
|
||||
|
||||
#define IOPROXXSF_BIT_SIZE (8)
|
||||
#define IOPROXXSF_BIT_MAX_SIZE (IOPROXXSF_BIT_SIZE * IOPROXXSF_ENCODED_DATA_SIZE)
|
||||
|
||||
typedef struct {
|
||||
FSKDemod* fsk_demod;
|
||||
} ProtocolIOProxXSFDecoder;
|
||||
|
||||
typedef struct {
|
||||
FSKOsc* fsk_osc;
|
||||
uint8_t encoded_index;
|
||||
} ProtocolIOProxXSFEncoder;
|
||||
|
||||
typedef struct {
|
||||
ProtocolIOProxXSFEncoder encoder;
|
||||
ProtocolIOProxXSFDecoder decoder;
|
||||
uint8_t encoded_data[IOPROXXSF_ENCODED_DATA_SIZE];
|
||||
uint8_t data[IOPROXXSF_DECODED_DATA_SIZE];
|
||||
} ProtocolIOProxXSF;
|
||||
|
||||
ProtocolIOProxXSF* protocol_io_prox_xsf_alloc(void) {
|
||||
ProtocolIOProxXSF* protocol = malloc(sizeof(ProtocolIOProxXSF));
|
||||
protocol->decoder.fsk_demod = fsk_demod_alloc(MIN_TIME, 8, MAX_TIME, 6);
|
||||
protocol->encoder.fsk_osc = fsk_osc_alloc(8, 10, 64);
|
||||
return protocol;
|
||||
};
|
||||
|
||||
void protocol_io_prox_xsf_free(ProtocolIOProxXSF* protocol) {
|
||||
fsk_demod_free(protocol->decoder.fsk_demod);
|
||||
fsk_osc_free(protocol->encoder.fsk_osc);
|
||||
free(protocol);
|
||||
};
|
||||
|
||||
uint8_t* protocol_io_prox_xsf_get_data(ProtocolIOProxXSF* protocol) {
|
||||
return protocol->data;
|
||||
};
|
||||
|
||||
void protocol_io_prox_xsf_decoder_start(ProtocolIOProxXSF* protocol) {
|
||||
memset(protocol->encoded_data, 0, IOPROXXSF_ENCODED_DATA_SIZE);
|
||||
};
|
||||
|
||||
static uint8_t protocol_io_prox_xsf_compute_checksum(const uint8_t* data) {
|
||||
// Packet structure:
|
||||
//
|
||||
//0 1 2 3 4 5 6 7
|
||||
//v v v v v v v v
|
||||
//01234567 8 9ABCDEF0 1 23456789 A BCDEF012 3 456789AB C DEF01234 5 6789ABCD EF
|
||||
//00000000 0 VVVVVVVV 1 WWWWWWWW 1 XXXXXXXX 1 YYYYYYYY 1 ZZZZZZZZ 1 CHECKSUM 11
|
||||
//
|
||||
// algorithm as observed by the proxmark3 folks
|
||||
// CHECKSUM == 0xFF - (V + W + X + Y + Z)
|
||||
|
||||
uint8_t checksum = 0;
|
||||
|
||||
for(size_t i = 1; i <= 5; i++) {
|
||||
checksum += bit_lib_get_bits(data, 9 * i, 8);
|
||||
}
|
||||
|
||||
return 0xFF - checksum;
|
||||
}
|
||||
|
||||
static bool protocol_io_prox_xsf_can_be_decoded(const uint8_t* encoded_data) {
|
||||
// Packet framing
|
||||
//
|
||||
//0 1 2 3 4 5 6 7
|
||||
//v v v v v v v v
|
||||
//01234567 89ABCDEF 01234567 89ABCDEF 01234567 89ABCDEF 01234567 89ABCDEF
|
||||
//-----------------------------------------------------------------------
|
||||
//00000000 01______ _1______ __1_____ ___1____ ____1___ _____1XX XXXXXX11
|
||||
//
|
||||
// _ = variable data
|
||||
// 0 = preamble 0
|
||||
// 1 = framing 1
|
||||
// X = checksum
|
||||
|
||||
// Validate the packet preamble is there...
|
||||
if(encoded_data[0] != 0b00000000) {
|
||||
return false;
|
||||
}
|
||||
if((encoded_data[1] >> 6) != 0b01) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// ... check for known ones...
|
||||
if(bit_lib_bit_is_not_set(encoded_data[2], 6)) {
|
||||
return false;
|
||||
}
|
||||
if(bit_lib_bit_is_not_set(encoded_data[3], 5)) {
|
||||
return false;
|
||||
}
|
||||
if(bit_lib_bit_is_not_set(encoded_data[4], 4)) {
|
||||
return false;
|
||||
}
|
||||
if(bit_lib_bit_is_not_set(encoded_data[5], 3)) {
|
||||
return false;
|
||||
}
|
||||
if(bit_lib_bit_is_not_set(encoded_data[6], 2)) {
|
||||
return false;
|
||||
}
|
||||
if(bit_lib_bit_is_not_set(encoded_data[7], 1)) {
|
||||
return false;
|
||||
}
|
||||
if(bit_lib_bit_is_not_set(encoded_data[7], 0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// ... and validate our checksums.
|
||||
uint8_t checksum = protocol_io_prox_xsf_compute_checksum(encoded_data);
|
||||
uint8_t checkval = bit_lib_get_bits(encoded_data, 54, 8);
|
||||
|
||||
if(checksum != checkval) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void protocol_io_prox_xsf_decode(const uint8_t* encoded_data, uint8_t* decoded_data) {
|
||||
// Packet structure:
|
||||
// (Note: the second word seems fixed; but this may not be a guarantee;
|
||||
// it currently has no meaning.)
|
||||
//
|
||||
//0 1 2 3 4 5 6 7
|
||||
//v v v v v v v v
|
||||
//01234567 89ABCDEF 01234567 89ABCDEF 01234567 89ABCDEF 01234567 89ABCDEF
|
||||
//-----------------------------------------------------------------------
|
||||
//00000000 01111000 01FFFFFF FF1VVVVV VVV1CCCC CCCC1CCC CCCCC1XX XXXXXX11
|
||||
//
|
||||
// F = facility code
|
||||
// V = version
|
||||
// C = code
|
||||
// X = checksum
|
||||
|
||||
// Facility code
|
||||
decoded_data[0] = bit_lib_get_bits(encoded_data, 18, 8);
|
||||
|
||||
// Version code.
|
||||
decoded_data[1] = bit_lib_get_bits(encoded_data, 27, 8);
|
||||
|
||||
// Code bytes.
|
||||
decoded_data[2] = bit_lib_get_bits(encoded_data, 36, 8);
|
||||
decoded_data[3] = bit_lib_get_bits(encoded_data, 45, 8);
|
||||
}
|
||||
|
||||
bool protocol_io_prox_xsf_decoder_feed(ProtocolIOProxXSF* protocol, bool level, uint32_t duration) {
|
||||
bool result = false;
|
||||
|
||||
uint32_t count;
|
||||
bool value;
|
||||
|
||||
fsk_demod_feed(protocol->decoder.fsk_demod, level, duration, &value, &count);
|
||||
for(size_t i = 0; i < count; i++) {
|
||||
bit_lib_push_bit(protocol->encoded_data, IOPROXXSF_ENCODED_DATA_SIZE, value);
|
||||
if(protocol_io_prox_xsf_can_be_decoded(protocol->encoded_data)) {
|
||||
protocol_io_prox_xsf_decode(protocol->encoded_data, protocol->data);
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
static void protocol_io_prox_xsf_encode(const uint8_t* decoded_data, uint8_t* encoded_data) {
|
||||
// Packet to transmit:
|
||||
//
|
||||
// 0 10 20 30 40 50 60
|
||||
// v v v v v v v
|
||||
// 01234567 8 90123456 7 89012345 6 78901234 5 67890123 4 56789012 3 45678901 23
|
||||
// -----------------------------------------------------------------------------
|
||||
// 00000000 0 11110000 1 facility 1 version_ 1 code-one 1 code-two 1 checksum 11
|
||||
|
||||
// Preamble.
|
||||
bit_lib_set_bits(encoded_data, 0, 0b00000000, 8);
|
||||
bit_lib_set_bit(encoded_data, 8, 0);
|
||||
|
||||
bit_lib_set_bits(encoded_data, 9, 0b11110000, 8);
|
||||
bit_lib_set_bit(encoded_data, 17, 1);
|
||||
|
||||
// Facility code.
|
||||
bit_lib_set_bits(encoded_data, 18, decoded_data[0], 8);
|
||||
bit_lib_set_bit(encoded_data, 26, 1);
|
||||
|
||||
// Version
|
||||
bit_lib_set_bits(encoded_data, 27, decoded_data[1], 8);
|
||||
bit_lib_set_bit(encoded_data, 35, 1);
|
||||
|
||||
// Code one
|
||||
bit_lib_set_bits(encoded_data, 36, decoded_data[2], 8);
|
||||
bit_lib_set_bit(encoded_data, 44, 1);
|
||||
|
||||
// Code two
|
||||
bit_lib_set_bits(encoded_data, 45, decoded_data[3], 8);
|
||||
bit_lib_set_bit(encoded_data, 53, 1);
|
||||
|
||||
// Checksum
|
||||
bit_lib_set_bits(encoded_data, 54, protocol_io_prox_xsf_compute_checksum(encoded_data), 8);
|
||||
bit_lib_set_bit(encoded_data, 62, 1);
|
||||
bit_lib_set_bit(encoded_data, 63, 1);
|
||||
}
|
||||
|
||||
bool protocol_io_prox_xsf_encoder_start(ProtocolIOProxXSF* protocol) {
|
||||
protocol_io_prox_xsf_encode(protocol->data, protocol->encoded_data);
|
||||
protocol->encoder.encoded_index = 0;
|
||||
fsk_osc_reset(protocol->encoder.fsk_osc);
|
||||
return true;
|
||||
};
|
||||
|
||||
LevelDuration protocol_io_prox_xsf_encoder_yield(ProtocolIOProxXSF* protocol) {
|
||||
bool level;
|
||||
uint32_t duration;
|
||||
|
||||
bool bit = bit_lib_get_bit(protocol->encoded_data, protocol->encoder.encoded_index);
|
||||
bool advance = fsk_osc_next_half(protocol->encoder.fsk_osc, bit, &level, &duration);
|
||||
|
||||
if(advance) {
|
||||
bit_lib_increment_index(protocol->encoder.encoded_index, IOPROXXSF_BIT_MAX_SIZE);
|
||||
}
|
||||
return level_duration_make(level, duration);
|
||||
};
|
||||
|
||||
void protocol_io_prox_xsf_render_data(ProtocolIOProxXSF* protocol, string_t result) {
|
||||
uint8_t* data = protocol->data;
|
||||
string_printf(
|
||||
result,
|
||||
"FC: %u\r\n"
|
||||
"VС: %u\r\n"
|
||||
"Card: %u",
|
||||
data[0],
|
||||
data[1],
|
||||
(uint16_t)((data[2] << 8) | (data[3])));
|
||||
}
|
||||
|
||||
void protocol_io_prox_xsf_render_brief_data(ProtocolIOProxXSF* protocol, string_t result) {
|
||||
uint8_t* data = protocol->data;
|
||||
string_printf(
|
||||
result,
|
||||
"FC: %u, VС: %u\r\n"
|
||||
"Card: %u",
|
||||
data[0],
|
||||
data[1],
|
||||
(uint16_t)((data[2] << 8) | (data[3])));
|
||||
}
|
||||
|
||||
bool protocol_io_prox_xsf_write_data(ProtocolIOProxXSF* protocol, void* data) {
|
||||
LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data;
|
||||
bool result = false;
|
||||
|
||||
protocol_io_prox_xsf_encode(protocol->data, protocol->encoded_data);
|
||||
|
||||
if(request->write_type == LFRFIDWriteTypeT5577) {
|
||||
request->t5577.block[0] = LFRFID_T5577_MODULATION_FSK2a | LFRFID_T5577_BITRATE_RF_64 |
|
||||
(2 << LFRFID_T5577_MAXBLOCK_SHIFT);
|
||||
request->t5577.block[1] = bit_lib_get_bits_32(protocol->encoded_data, 0, 32);
|
||||
request->t5577.block[2] = bit_lib_get_bits_32(protocol->encoded_data, 32, 32);
|
||||
request->t5577.blocks_to_write = 3;
|
||||
result = true;
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
const ProtocolBase protocol_io_prox_xsf = {
|
||||
.name = "IoProxXSF",
|
||||
.manufacturer = "Kantech",
|
||||
.data_size = IOPROXXSF_DECODED_DATA_SIZE,
|
||||
.features = LFRFIDFeatureASK,
|
||||
.validate_count = 3,
|
||||
.alloc = (ProtocolAlloc)protocol_io_prox_xsf_alloc,
|
||||
.free = (ProtocolFree)protocol_io_prox_xsf_free,
|
||||
.get_data = (ProtocolGetData)protocol_io_prox_xsf_get_data,
|
||||
.decoder =
|
||||
{
|
||||
.start = (ProtocolDecoderStart)protocol_io_prox_xsf_decoder_start,
|
||||
.feed = (ProtocolDecoderFeed)protocol_io_prox_xsf_decoder_feed,
|
||||
},
|
||||
.encoder =
|
||||
{
|
||||
.start = (ProtocolEncoderStart)protocol_io_prox_xsf_encoder_start,
|
||||
.yield = (ProtocolEncoderYield)protocol_io_prox_xsf_encoder_yield,
|
||||
},
|
||||
.render_data = (ProtocolRenderData)protocol_io_prox_xsf_render_data,
|
||||
.render_brief_data = (ProtocolRenderData)protocol_io_prox_xsf_render_brief_data,
|
||||
.write_data = (ProtocolWriteData)protocol_io_prox_xsf_write_data,
|
||||
};
|
4
lib/lfrfid/protocols/protocol_io_prox_xsf.h
Normal file
4
lib/lfrfid/protocols/protocol_io_prox_xsf.h
Normal file
@@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
#include <toolbox/protocols/protocol.h>
|
||||
|
||||
extern const ProtocolBase protocol_io_prox_xsf;
|
291
lib/lfrfid/tools/bit_lib.c
Normal file
291
lib/lfrfid/tools/bit_lib.c
Normal file
@@ -0,0 +1,291 @@
|
||||
#include "bit_lib.h"
|
||||
#include <core/check.h>
|
||||
#include <stdio.h>
|
||||
|
||||
void bit_lib_push_bit(uint8_t* data, size_t data_size, bool bit) {
|
||||
size_t last_index = data_size - 1;
|
||||
|
||||
for(size_t i = 0; i < last_index; ++i) {
|
||||
data[i] = (data[i] << 1) | ((data[i + 1] >> 7) & 1);
|
||||
}
|
||||
data[last_index] = (data[last_index] << 1) | bit;
|
||||
}
|
||||
|
||||
void bit_lib_set_bit(uint8_t* data, size_t position, bool bit) {
|
||||
if(bit) {
|
||||
data[position / 8] |= 1UL << (7 - (position % 8));
|
||||
} else {
|
||||
data[position / 8] &= ~(1UL << (7 - (position % 8)));
|
||||
}
|
||||
}
|
||||
|
||||
void bit_lib_set_bits(uint8_t* data, size_t position, uint8_t byte, uint8_t length) {
|
||||
furi_check(length <= 8);
|
||||
furi_check(length > 0);
|
||||
|
||||
for(uint8_t i = 0; i < length; ++i) {
|
||||
uint8_t shift = (length - 1) - i;
|
||||
bit_lib_set_bit(data, position + i, (byte >> shift) & 1);
|
||||
}
|
||||
}
|
||||
|
||||
bool bit_lib_get_bit(const uint8_t* data, size_t position) {
|
||||
return (data[position / 8] >> (7 - (position % 8))) & 1;
|
||||
}
|
||||
|
||||
uint8_t bit_lib_get_bits(const uint8_t* data, size_t position, uint8_t length) {
|
||||
uint8_t shift = position % 8;
|
||||
if(shift == 0) {
|
||||
return data[position / 8] >> (8 - length);
|
||||
} else {
|
||||
// TODO fix read out of bounds
|
||||
uint8_t value = (data[position / 8] << (shift));
|
||||
value |= data[position / 8 + 1] >> (8 - shift);
|
||||
value = value >> (8 - length);
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t bit_lib_get_bits_16(const uint8_t* data, size_t position, uint8_t length) {
|
||||
uint16_t value = 0;
|
||||
if(length <= 8) {
|
||||
value = bit_lib_get_bits(data, position, length);
|
||||
} else {
|
||||
value = bit_lib_get_bits(data, position, 8) << (length - 8);
|
||||
value |= bit_lib_get_bits(data, position + 8, length - 8);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
uint32_t bit_lib_get_bits_32(const uint8_t* data, size_t position, uint8_t length) {
|
||||
uint32_t value = 0;
|
||||
if(length <= 8) {
|
||||
value = bit_lib_get_bits(data, position, length);
|
||||
} else if(length <= 16) {
|
||||
value = bit_lib_get_bits(data, position, 8) << (length - 8);
|
||||
value |= bit_lib_get_bits(data, position + 8, length - 8);
|
||||
} else if(length <= 24) {
|
||||
value = bit_lib_get_bits(data, position, 8) << (length - 8);
|
||||
value |= bit_lib_get_bits(data, position + 8, 8) << (length - 16);
|
||||
value |= bit_lib_get_bits(data, position + 16, length - 16);
|
||||
} else {
|
||||
value = bit_lib_get_bits(data, position, 8) << (length - 8);
|
||||
value |= bit_lib_get_bits(data, position + 8, 8) << (length - 16);
|
||||
value |= bit_lib_get_bits(data, position + 16, 8) << (length - 24);
|
||||
value |= bit_lib_get_bits(data, position + 24, length - 24);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
bool bit_lib_test_parity_32(uint32_t bits, BitLibParity parity) {
|
||||
#if !defined __GNUC__
|
||||
#error Please, implement parity test for non-GCC compilers
|
||||
#else
|
||||
switch(parity) {
|
||||
case BitLibParityEven:
|
||||
return __builtin_parity(bits);
|
||||
case BitLibParityOdd:
|
||||
return !__builtin_parity(bits);
|
||||
default:
|
||||
furi_crash("Unknown parity");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool bit_lib_test_parity(
|
||||
const uint8_t* bits,
|
||||
size_t position,
|
||||
uint8_t length,
|
||||
BitLibParity parity,
|
||||
uint8_t parity_length) {
|
||||
uint32_t parity_block;
|
||||
bool result = true;
|
||||
const size_t parity_blocks_count = length / parity_length;
|
||||
|
||||
for(size_t i = 0; i < parity_blocks_count; ++i) {
|
||||
switch(parity) {
|
||||
case BitLibParityEven:
|
||||
case BitLibParityOdd:
|
||||
parity_block = bit_lib_get_bits_32(bits, position + i * parity_length, parity_length);
|
||||
if(!bit_lib_test_parity_32(parity_block, parity)) {
|
||||
result = false;
|
||||
}
|
||||
break;
|
||||
case BitLibParityAlways0:
|
||||
if(bit_lib_get_bit(bits, position + i * parity_length + parity_length - 1)) {
|
||||
result = false;
|
||||
}
|
||||
break;
|
||||
case BitLibParityAlways1:
|
||||
if(!bit_lib_get_bit(bits, position + i * parity_length + parity_length - 1)) {
|
||||
result = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if(!result) break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t bit_lib_remove_bit_every_nth(uint8_t* data, size_t position, uint8_t length, uint8_t n) {
|
||||
size_t counter = 0;
|
||||
size_t result_counter = 0;
|
||||
uint8_t bit_buffer = 0;
|
||||
uint8_t bit_counter = 0;
|
||||
|
||||
while(counter < length) {
|
||||
if((counter + 1) % n != 0) {
|
||||
bit_buffer = (bit_buffer << 1) | bit_lib_get_bit(data, position + counter);
|
||||
bit_counter++;
|
||||
}
|
||||
|
||||
if(bit_counter == 8) {
|
||||
bit_lib_set_bits(data, position + result_counter, bit_buffer, 8);
|
||||
bit_counter = 0;
|
||||
bit_buffer = 0;
|
||||
result_counter += 8;
|
||||
}
|
||||
counter++;
|
||||
}
|
||||
|
||||
if(bit_counter != 0) {
|
||||
bit_lib_set_bits(data, position + result_counter, bit_buffer, bit_counter);
|
||||
result_counter += bit_counter;
|
||||
}
|
||||
return result_counter;
|
||||
}
|
||||
|
||||
void bit_lib_copy_bits(
|
||||
uint8_t* data,
|
||||
size_t position,
|
||||
size_t length,
|
||||
const uint8_t* source,
|
||||
size_t source_position) {
|
||||
for(size_t i = 0; i < length; ++i) {
|
||||
bit_lib_set_bit(data, position + i, bit_lib_get_bit(source, source_position + i));
|
||||
}
|
||||
}
|
||||
|
||||
void bit_lib_reverse_bits(uint8_t* data, size_t position, uint8_t length) {
|
||||
size_t i = 0;
|
||||
size_t j = length - 1;
|
||||
|
||||
while(i < j) {
|
||||
bool tmp = bit_lib_get_bit(data, position + i);
|
||||
bit_lib_set_bit(data, position + i, bit_lib_get_bit(data, position + j));
|
||||
bit_lib_set_bit(data, position + j, tmp);
|
||||
i++;
|
||||
j--;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t bit_lib_get_bit_count(uint32_t data) {
|
||||
#if defined __GNUC__
|
||||
return __builtin_popcountl(data);
|
||||
#else
|
||||
#error Please, implement popcount for non-GCC compilers
|
||||
#endif
|
||||
}
|
||||
|
||||
void bit_lib_print_bits(const uint8_t* data, size_t length) {
|
||||
for(size_t i = 0; i < length; ++i) {
|
||||
printf("%u", bit_lib_get_bit(data, i));
|
||||
}
|
||||
}
|
||||
|
||||
void bit_lib_print_regions(
|
||||
const BitLibRegion* regions,
|
||||
size_t region_count,
|
||||
const uint8_t* data,
|
||||
size_t length) {
|
||||
// print data
|
||||
bit_lib_print_bits(data, length);
|
||||
printf("\r\n");
|
||||
|
||||
// print regions
|
||||
for(size_t c = 0; c < length; ++c) {
|
||||
bool print = false;
|
||||
|
||||
for(size_t i = 0; i < region_count; i++) {
|
||||
if(regions[i].start <= c && c < regions[i].start + regions[i].length) {
|
||||
print = true;
|
||||
printf("%c", regions[i].mark);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!print) {
|
||||
printf(" ");
|
||||
}
|
||||
}
|
||||
printf("\r\n");
|
||||
|
||||
// print regions data
|
||||
for(size_t c = 0; c < length; ++c) {
|
||||
bool print = false;
|
||||
|
||||
for(size_t i = 0; i < region_count; i++) {
|
||||
if(regions[i].start <= c && c < regions[i].start + regions[i].length) {
|
||||
print = true;
|
||||
printf("%u", bit_lib_get_bit(data, c));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!print) {
|
||||
printf(" ");
|
||||
}
|
||||
}
|
||||
printf("\r\n");
|
||||
}
|
||||
|
||||
uint16_t bit_lib_reverse_16_fast(uint16_t data) {
|
||||
uint16_t result = 0;
|
||||
result |= (data & 0x8000) >> 15;
|
||||
result |= (data & 0x4000) >> 13;
|
||||
result |= (data & 0x2000) >> 11;
|
||||
result |= (data & 0x1000) >> 9;
|
||||
result |= (data & 0x0800) >> 7;
|
||||
result |= (data & 0x0400) >> 5;
|
||||
result |= (data & 0x0200) >> 3;
|
||||
result |= (data & 0x0100) >> 1;
|
||||
result |= (data & 0x0080) << 1;
|
||||
result |= (data & 0x0040) << 3;
|
||||
result |= (data & 0x0020) << 5;
|
||||
result |= (data & 0x0010) << 7;
|
||||
result |= (data & 0x0008) << 9;
|
||||
result |= (data & 0x0004) << 11;
|
||||
result |= (data & 0x0002) << 13;
|
||||
result |= (data & 0x0001) << 15;
|
||||
return result;
|
||||
}
|
||||
|
||||
uint16_t bit_lib_crc16(
|
||||
uint8_t const* data,
|
||||
size_t data_size,
|
||||
uint16_t polynom,
|
||||
uint16_t init,
|
||||
bool ref_in,
|
||||
bool ref_out,
|
||||
uint16_t xor_out) {
|
||||
uint16_t crc = init;
|
||||
|
||||
for(size_t i = 0; i < data_size; ++i) {
|
||||
uint8_t byte = data[i];
|
||||
if(ref_in) byte = bit_lib_reverse_16_fast(byte) >> 8;
|
||||
|
||||
for(size_t j = 0; j < 8; ++j) {
|
||||
bool c15 = (crc >> 15 & 1);
|
||||
bool bit = (byte >> (7 - j) & 1);
|
||||
crc <<= 1;
|
||||
if(c15 ^ bit) crc ^= polynom;
|
||||
}
|
||||
}
|
||||
|
||||
if(ref_out) crc = bit_lib_reverse_16_fast(crc);
|
||||
crc ^= xor_out;
|
||||
|
||||
return crc;
|
||||
}
|
220
lib/lfrfid/tools/bit_lib.h
Normal file
220
lib/lfrfid/tools/bit_lib.h
Normal file
@@ -0,0 +1,220 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
BitLibParityEven,
|
||||
BitLibParityOdd,
|
||||
BitLibParityAlways0,
|
||||
BitLibParityAlways1,
|
||||
} BitLibParity;
|
||||
|
||||
/** @brief Increment and wrap around a value.
|
||||
* @param index value to increment
|
||||
* @param length wrap-around range
|
||||
*/
|
||||
#define bit_lib_increment_index(index, length) (index = (((index) + 1) % (length)))
|
||||
|
||||
/** @brief Test if a bit is set.
|
||||
* @param data value to test
|
||||
* @param index bit index to test
|
||||
*/
|
||||
#define bit_lib_bit_is_set(data, index) ((data & (1 << (index))) != 0)
|
||||
|
||||
/** @brief Test if a bit is not set.
|
||||
* @param data value to test
|
||||
* @param index bit index to test
|
||||
*/
|
||||
#define bit_lib_bit_is_not_set(data, index) ((data & (1 << (index))) == 0)
|
||||
|
||||
/** @brief Push a bit into a byte array.
|
||||
* @param data array to push bit into
|
||||
* @param data_size array size
|
||||
* @param bit bit to push
|
||||
*/
|
||||
void bit_lib_push_bit(uint8_t* data, size_t data_size, bool bit);
|
||||
|
||||
/** @brief Set a bit in a byte array.
|
||||
* @param data array to set bit in
|
||||
* @param position The position of the bit to set.
|
||||
* @param bit bit value to set
|
||||
*/
|
||||
void bit_lib_set_bit(uint8_t* data, size_t position, bool bit);
|
||||
|
||||
/** @brief Set the bit at the given position to the given value.
|
||||
* @param data The data to set the bit in.
|
||||
* @param position The position of the bit to set.
|
||||
* @param byte The data to set the bit to.
|
||||
* @param length The length of the data.
|
||||
*/
|
||||
void bit_lib_set_bits(uint8_t* data, size_t position, uint8_t byte, uint8_t length);
|
||||
|
||||
/** @brief Get the bit of a byte.
|
||||
* @param data The byte to get the bits from.
|
||||
* @param position The position of the bit.
|
||||
* @return The bit.
|
||||
*/
|
||||
bool bit_lib_get_bit(const uint8_t* data, size_t position);
|
||||
|
||||
/**
|
||||
* @brief Get the bits of a data, as uint8_t.
|
||||
* @param data The data to get the bits from.
|
||||
* @param position The position of the first bit.
|
||||
* @param length The length of the bits.
|
||||
* @return The bits.
|
||||
*/
|
||||
uint8_t bit_lib_get_bits(const uint8_t* data, size_t position, uint8_t length);
|
||||
|
||||
/**
|
||||
* @brief Get the bits of a data, as uint16_t.
|
||||
* @param data The data to get the bits from.
|
||||
* @param position The position of the first bit.
|
||||
* @param length The length of the bits.
|
||||
* @return The bits.
|
||||
*/
|
||||
uint16_t bit_lib_get_bits_16(const uint8_t* data, size_t position, uint8_t length);
|
||||
|
||||
/**
|
||||
* @brief Get the bits of a data, as uint32_t.
|
||||
* @param data The data to get the bits from.
|
||||
* @param position The position of the first bit.
|
||||
* @param length The length of the bits.
|
||||
* @return The bits.
|
||||
*/
|
||||
uint32_t bit_lib_get_bits_32(const uint8_t* data, size_t position, uint8_t length);
|
||||
|
||||
/**
|
||||
* @brief Test parity of given bits
|
||||
* @param bits Bits to test parity of
|
||||
* @param parity Parity to test against
|
||||
* @return true if parity is correct, false otherwise
|
||||
*/
|
||||
bool bit_lib_test_parity_32(uint32_t bits, BitLibParity parity);
|
||||
|
||||
/**
|
||||
* @brief Test parity of bit array, check parity for every parity_length block from start
|
||||
*
|
||||
* @param data Bit array
|
||||
* @param position Start position
|
||||
* @param length Bit count
|
||||
* @param parity Parity to test against
|
||||
* @param parity_length Parity block length
|
||||
* @return true
|
||||
* @return false
|
||||
*/
|
||||
bool bit_lib_test_parity(
|
||||
const uint8_t* data,
|
||||
size_t position,
|
||||
uint8_t length,
|
||||
BitLibParity parity,
|
||||
uint8_t parity_length);
|
||||
|
||||
/**
|
||||
* @brief Remove bit every n in array and shift array left. Useful to remove parity.
|
||||
*
|
||||
* @param data Bit array
|
||||
* @param position Start position
|
||||
* @param length Bit count
|
||||
* @param n every n bit will be removed
|
||||
* @return size_t
|
||||
*/
|
||||
size_t bit_lib_remove_bit_every_nth(uint8_t* data, size_t position, uint8_t length, uint8_t n);
|
||||
|
||||
/**
|
||||
* @brief Copy bits from source to destination.
|
||||
*
|
||||
* @param data destination array
|
||||
* @param position position in destination array
|
||||
* @param length length of bits to copy
|
||||
* @param source source array
|
||||
* @param source_position position in source array
|
||||
*/
|
||||
void bit_lib_copy_bits(
|
||||
uint8_t* data,
|
||||
size_t position,
|
||||
size_t length,
|
||||
const uint8_t* source,
|
||||
size_t source_position);
|
||||
|
||||
/**
|
||||
* @brief Reverse bits in bit array
|
||||
*
|
||||
* @param data Bit array
|
||||
* @param position start position
|
||||
* @param length length of bits to reverse
|
||||
*/
|
||||
void bit_lib_reverse_bits(uint8_t* data, size_t position, uint8_t length);
|
||||
|
||||
/**
|
||||
* @brief Count 1 bits in data
|
||||
*
|
||||
* @param data
|
||||
* @return uint8_t set bit count
|
||||
*/
|
||||
uint8_t bit_lib_get_bit_count(uint32_t data);
|
||||
|
||||
/**
|
||||
* @brief Print data as bit array
|
||||
*
|
||||
* @param data
|
||||
* @param length
|
||||
*/
|
||||
void bit_lib_print_bits(const uint8_t* data, size_t length);
|
||||
|
||||
typedef struct {
|
||||
const char mark;
|
||||
const size_t start;
|
||||
const size_t length;
|
||||
} BitLibRegion;
|
||||
|
||||
/**
|
||||
* @brief Print data as bit array and mark regions. Regions needs to be sorted by start position.
|
||||
*
|
||||
* @param regions
|
||||
* @param region_count
|
||||
* @param data
|
||||
* @param length
|
||||
*/
|
||||
void bit_lib_print_regions(
|
||||
const BitLibRegion* regions,
|
||||
size_t region_count,
|
||||
const uint8_t* data,
|
||||
size_t length);
|
||||
|
||||
/**
|
||||
* @brief Reverse bits in uint16_t, faster than generic bit_lib_reverse_bits.
|
||||
*
|
||||
* @param data
|
||||
* @return uint16_t
|
||||
*/
|
||||
uint16_t bit_lib_reverse_16_fast(uint16_t data);
|
||||
|
||||
/**
|
||||
* @brief Slow, but generic CRC16 implementation
|
||||
*
|
||||
* @param data
|
||||
* @param data_size
|
||||
* @param polynom CRC polynom
|
||||
* @param init init value
|
||||
* @param ref_in true if the right bit is older
|
||||
* @param ref_out true to reverse output
|
||||
* @param xor_out xor output with this value
|
||||
* @return uint16_t
|
||||
*/
|
||||
uint16_t bit_lib_crc16(
|
||||
uint8_t const* data,
|
||||
size_t data_size,
|
||||
uint16_t polynom,
|
||||
uint16_t init,
|
||||
bool ref_in,
|
||||
bool ref_out,
|
||||
uint16_t xor_out);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
93
lib/lfrfid/tools/fsk_demod.c
Normal file
93
lib/lfrfid/tools/fsk_demod.c
Normal file
@@ -0,0 +1,93 @@
|
||||
#include <furi.h>
|
||||
#include "fsk_demod.h"
|
||||
|
||||
struct FSKDemod {
|
||||
uint32_t low_time;
|
||||
uint32_t low_pulses;
|
||||
uint32_t hi_time;
|
||||
uint32_t hi_pulses;
|
||||
|
||||
bool invert;
|
||||
uint32_t mid_time;
|
||||
uint32_t time;
|
||||
uint32_t count;
|
||||
bool last_pulse;
|
||||
};
|
||||
|
||||
FSKDemod*
|
||||
fsk_demod_alloc(uint32_t low_time, uint32_t low_pulses, uint32_t hi_time, uint32_t hi_pulses) {
|
||||
FSKDemod* demod = malloc(sizeof(FSKDemod));
|
||||
demod->invert = false;
|
||||
|
||||
if(low_time > hi_time) {
|
||||
uint32_t tmp;
|
||||
tmp = hi_time;
|
||||
hi_time = low_time;
|
||||
low_time = tmp;
|
||||
|
||||
tmp = hi_pulses;
|
||||
hi_pulses = low_pulses;
|
||||
low_pulses = tmp;
|
||||
|
||||
demod->invert = true;
|
||||
}
|
||||
|
||||
demod->low_time = low_time;
|
||||
demod->low_pulses = low_pulses;
|
||||
demod->hi_time = hi_time;
|
||||
demod->hi_pulses = hi_pulses;
|
||||
|
||||
demod->mid_time = (hi_time - low_time) / 2 + low_time;
|
||||
demod->time = 0;
|
||||
demod->count = 0;
|
||||
demod->last_pulse = false;
|
||||
|
||||
return demod;
|
||||
}
|
||||
|
||||
void fsk_demod_free(FSKDemod* demod) {
|
||||
free(demod);
|
||||
}
|
||||
|
||||
void fsk_demod_feed(FSKDemod* demod, bool polarity, uint32_t time, bool* value, uint32_t* count) {
|
||||
*count = 0;
|
||||
|
||||
if(polarity) {
|
||||
// accumulate time
|
||||
demod->time = time;
|
||||
} else {
|
||||
demod->time += time;
|
||||
|
||||
// check for valid pulse
|
||||
if(demod->time >= demod->low_time && demod->time < demod->hi_time) {
|
||||
bool pulse;
|
||||
|
||||
if(demod->time < demod->mid_time) {
|
||||
pulse = false;
|
||||
} else {
|
||||
pulse = true;
|
||||
}
|
||||
|
||||
demod->count++;
|
||||
|
||||
// check for edge transition
|
||||
if(demod->last_pulse != pulse) {
|
||||
uint32_t data_count = demod->count + 1;
|
||||
|
||||
if(demod->last_pulse) {
|
||||
data_count /= demod->hi_pulses;
|
||||
*value = !demod->invert;
|
||||
} else {
|
||||
data_count /= demod->low_pulses;
|
||||
*value = demod->invert;
|
||||
}
|
||||
|
||||
*count = data_count;
|
||||
demod->count = 0;
|
||||
demod->last_pulse = pulse;
|
||||
}
|
||||
} else {
|
||||
demod->count = 0;
|
||||
}
|
||||
}
|
||||
}
|
44
lib/lfrfid/tools/fsk_demod.h
Normal file
44
lib/lfrfid/tools/fsk_demod.h
Normal file
@@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct FSKDemod FSKDemod;
|
||||
|
||||
/**
|
||||
* @brief Allocate a new FSKDemod instance
|
||||
* FSKDemod is a demodulator that can decode FSK encoded data
|
||||
*
|
||||
* @param low_time time between rising edges for the 0 bit
|
||||
* @param low_pulses rising edges count for the 0 bit
|
||||
* @param hi_time time between rising edges for the 1 bit
|
||||
* @param hi_pulses rising edges count for the 1 bit
|
||||
* @return FSKDemod*
|
||||
*/
|
||||
FSKDemod*
|
||||
fsk_demod_alloc(uint32_t low_time, uint32_t low_pulses, uint32_t hi_time, uint32_t hi_pulses);
|
||||
|
||||
/**
|
||||
* @brief Free a FSKDemod instance
|
||||
*
|
||||
* @param fsk_demod
|
||||
*/
|
||||
void fsk_demod_free(FSKDemod* fsk_demod);
|
||||
|
||||
/**
|
||||
* @brief Feed sample to demodulator
|
||||
*
|
||||
* @param demod FSKDemod instance
|
||||
* @param polarity sample polarity
|
||||
* @param time sample time
|
||||
* @param value demodulated bit value
|
||||
* @param count demodulated bit count
|
||||
*/
|
||||
void fsk_demod_feed(FSKDemod* demod, bool polarity, uint32_t time, bool* value, uint32_t* count);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
62
lib/lfrfid/tools/fsk_ocs.c
Normal file
62
lib/lfrfid/tools/fsk_ocs.c
Normal file
@@ -0,0 +1,62 @@
|
||||
#include "fsk_osc.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
struct FSKOsc {
|
||||
uint16_t freq[2];
|
||||
uint16_t osc_phase_max;
|
||||
int32_t osc_phase_current;
|
||||
|
||||
uint32_t pulse;
|
||||
};
|
||||
|
||||
FSKOsc* fsk_osc_alloc(uint32_t freq_low, uint32_t freq_hi, uint32_t osc_phase_max) {
|
||||
FSKOsc* osc = malloc(sizeof(FSKOsc));
|
||||
osc->freq[0] = freq_low;
|
||||
osc->freq[1] = freq_hi;
|
||||
osc->osc_phase_max = osc_phase_max;
|
||||
osc->osc_phase_current = 0;
|
||||
osc->pulse = 0;
|
||||
return osc;
|
||||
}
|
||||
|
||||
void fsk_osc_free(FSKOsc* osc) {
|
||||
free(osc);
|
||||
}
|
||||
|
||||
void fsk_osc_reset(FSKOsc* osc) {
|
||||
osc->osc_phase_current = 0;
|
||||
osc->pulse = 0;
|
||||
}
|
||||
|
||||
bool fsk_osc_next(FSKOsc* osc, bool bit, uint32_t* period) {
|
||||
bool advance = false;
|
||||
*period = osc->freq[bit];
|
||||
osc->osc_phase_current += *period;
|
||||
|
||||
if(osc->osc_phase_current > osc->osc_phase_max) {
|
||||
advance = true;
|
||||
osc->osc_phase_current -= osc->osc_phase_max;
|
||||
}
|
||||
|
||||
return advance;
|
||||
}
|
||||
|
||||
bool fsk_osc_next_half(FSKOsc* osc, bool bit, bool* level, uint32_t* duration) {
|
||||
bool advance = false;
|
||||
|
||||
// if pulse is zero, we need to output high, otherwise we need to output low
|
||||
if(osc->pulse == 0) {
|
||||
uint32_t length;
|
||||
advance = fsk_osc_next(osc, bit, &length);
|
||||
*duration = length / 2;
|
||||
osc->pulse = *duration;
|
||||
*level = true;
|
||||
} else {
|
||||
// output low half and reset pulse
|
||||
*duration = osc->pulse;
|
||||
osc->pulse = 0;
|
||||
*level = false;
|
||||
}
|
||||
|
||||
return advance;
|
||||
}
|
60
lib/lfrfid/tools/fsk_osc.h
Normal file
60
lib/lfrfid/tools/fsk_osc.h
Normal file
@@ -0,0 +1,60 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct FSKOsc FSKOsc;
|
||||
|
||||
/**
|
||||
* @brief Allocate a new FSKOsc instance
|
||||
* FSKOsc is a oscillator that can provide FSK encoding
|
||||
*
|
||||
* @param freq_low
|
||||
* @param freq_hi
|
||||
* @param osc_phase_max
|
||||
* @return FSKOsc*
|
||||
*/
|
||||
FSKOsc* fsk_osc_alloc(uint32_t freq_low, uint32_t freq_hi, uint32_t osc_phase_max);
|
||||
|
||||
/**
|
||||
* @brief Free a FSKOsc instance
|
||||
*
|
||||
* @param osc
|
||||
*/
|
||||
void fsk_osc_free(FSKOsc* osc);
|
||||
|
||||
/**
|
||||
* @brief Reset ocillator
|
||||
*
|
||||
* @param osc
|
||||
*/
|
||||
void fsk_osc_reset(FSKOsc* osc);
|
||||
|
||||
/**
|
||||
* @brief Get next duration sample from oscillator
|
||||
*
|
||||
* @param osc
|
||||
* @param bit
|
||||
* @param period
|
||||
* @return bool
|
||||
*/
|
||||
bool fsk_osc_next(FSKOsc* osc, bool bit, uint32_t* period);
|
||||
|
||||
/**
|
||||
* @brief Get next half of sample from oscillator
|
||||
* Useful when encoding high and low levels separately.
|
||||
*
|
||||
* @param osc
|
||||
* @param bit
|
||||
* @param level
|
||||
* @param duration
|
||||
* @return bool
|
||||
*/
|
||||
bool fsk_osc_next_half(FSKOsc* osc, bool bit, bool* level, uint32_t* duration);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
94
lib/lfrfid/tools/t5577.c
Normal file
94
lib/lfrfid/tools/t5577.c
Normal file
@@ -0,0 +1,94 @@
|
||||
#include "t5577.h"
|
||||
#include <furi.h>
|
||||
#include <furi_hal_rfid.h>
|
||||
|
||||
#define T5577_TIMING_WAIT_TIME 400
|
||||
#define T5577_TIMING_START_GAP 30
|
||||
#define T5577_TIMING_WRITE_GAP 18
|
||||
#define T5577_TIMING_DATA_0 24
|
||||
#define T5577_TIMING_DATA_1 56
|
||||
#define T5577_TIMING_PROGRAM 700
|
||||
|
||||
#define T5577_OPCODE_PAGE_0 0b10
|
||||
#define T5577_OPCODE_PAGE_1 0b11
|
||||
#define T5577_OPCODE_RESET 0b00
|
||||
|
||||
static void t5577_start() {
|
||||
furi_hal_rfid_tim_read(125000, 0.5);
|
||||
furi_hal_rfid_pins_read();
|
||||
furi_hal_rfid_tim_read_start();
|
||||
|
||||
// do not ground the antenna
|
||||
furi_hal_rfid_pin_pull_release();
|
||||
}
|
||||
|
||||
static void t5577_stop() {
|
||||
furi_hal_rfid_tim_read_stop();
|
||||
furi_hal_rfid_tim_reset();
|
||||
furi_hal_rfid_pins_reset();
|
||||
}
|
||||
|
||||
static void t5577_write_gap(uint32_t gap_time) {
|
||||
furi_hal_rfid_tim_read_stop();
|
||||
furi_delay_us(gap_time * 8);
|
||||
furi_hal_rfid_tim_read_start();
|
||||
}
|
||||
|
||||
static void t5577_write_bit(bool value) {
|
||||
if(value) {
|
||||
furi_delay_us(T5577_TIMING_DATA_1 * 8);
|
||||
} else {
|
||||
furi_delay_us(T5577_TIMING_DATA_0 * 8);
|
||||
}
|
||||
t5577_write_gap(T5577_TIMING_WRITE_GAP);
|
||||
}
|
||||
|
||||
static void t5577_write_opcode(uint8_t value) {
|
||||
t5577_write_bit((value >> 1) & 1);
|
||||
t5577_write_bit((value >> 0) & 1);
|
||||
}
|
||||
|
||||
static void t5577_write_reset() {
|
||||
t5577_write_gap(T5577_TIMING_START_GAP);
|
||||
t5577_write_bit(1);
|
||||
t5577_write_bit(0);
|
||||
}
|
||||
|
||||
static void t5577_write_block(uint8_t block, bool lock_bit, uint32_t data) {
|
||||
furi_delay_us(T5577_TIMING_WAIT_TIME * 8);
|
||||
|
||||
// start gap
|
||||
t5577_write_gap(T5577_TIMING_START_GAP);
|
||||
|
||||
// opcode for page 0
|
||||
t5577_write_opcode(T5577_OPCODE_PAGE_0);
|
||||
|
||||
// lock bit
|
||||
t5577_write_bit(lock_bit);
|
||||
|
||||
// data
|
||||
for(uint8_t i = 0; i < 32; i++) {
|
||||
t5577_write_bit((data >> (31 - i)) & 1);
|
||||
}
|
||||
|
||||
// block address
|
||||
t5577_write_bit((block >> 2) & 1);
|
||||
t5577_write_bit((block >> 1) & 1);
|
||||
t5577_write_bit((block >> 0) & 1);
|
||||
|
||||
furi_delay_us(T5577_TIMING_PROGRAM * 8);
|
||||
|
||||
furi_delay_us(T5577_TIMING_WAIT_TIME * 8);
|
||||
t5577_write_reset();
|
||||
}
|
||||
|
||||
void t5577_write(LFRFIDT5577* data) {
|
||||
t5577_start();
|
||||
FURI_CRITICAL_ENTER();
|
||||
for(size_t i = 0; i < data->blocks_to_write; i++) {
|
||||
t5577_write_block(i, false, data->block[i]);
|
||||
}
|
||||
t5577_write_reset();
|
||||
FURI_CRITICAL_EXIT();
|
||||
t5577_stop();
|
||||
}
|
56
lib/lfrfid/tools/t5577.h
Normal file
56
lib/lfrfid/tools/t5577.h
Normal file
@@ -0,0 +1,56 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define LFRFID_T5577_BLOCK_COUNT 8
|
||||
|
||||
// T5577 block 0 definitions, thanks proxmark3!
|
||||
#define LFRFID_T5577_POR_DELAY 0x00000001
|
||||
#define LFRFID_T5577_ST_TERMINATOR 0x00000008
|
||||
#define LFRFID_T5577_PWD 0x00000010
|
||||
#define LFRFID_T5577_MAXBLOCK_SHIFT 5
|
||||
#define LFRFID_T5577_AOR 0x00000200
|
||||
#define LFRFID_T5577_PSKCF_RF_2 0
|
||||
#define LFRFID_T5577_PSKCF_RF_4 0x00000400
|
||||
#define LFRFID_T5577_PSKCF_RF_8 0x00000800
|
||||
#define LFRFID_T5577_MODULATION_DIRECT 0
|
||||
#define LFRFID_T5577_MODULATION_PSK1 0x00001000
|
||||
#define LFRFID_T5577_MODULATION_PSK2 0x00002000
|
||||
#define LFRFID_T5577_MODULATION_PSK3 0x00003000
|
||||
#define LFRFID_T5577_MODULATION_FSK1 0x00004000
|
||||
#define LFRFID_T5577_MODULATION_FSK2 0x00005000
|
||||
#define LFRFID_T5577_MODULATION_FSK1a 0x00006000
|
||||
#define LFRFID_T5577_MODULATION_FSK2a 0x00007000
|
||||
#define LFRFID_T5577_MODULATION_MANCHESTER 0x00008000
|
||||
#define LFRFID_T5577_MODULATION_BIPHASE 0x00010000
|
||||
#define LFRFID_T5577_MODULATION_DIPHASE 0x00018000
|
||||
#define LFRFID_T5577_X_MODE 0x00020000
|
||||
#define LFRFID_T5577_BITRATE_RF_8 0
|
||||
#define LFRFID_T5577_BITRATE_RF_16 0x00040000
|
||||
#define LFRFID_T5577_BITRATE_RF_32 0x00080000
|
||||
#define LFRFID_T5577_BITRATE_RF_40 0x000C0000
|
||||
#define LFRFID_T5577_BITRATE_RF_50 0x00100000
|
||||
#define LFRFID_T5577_BITRATE_RF_64 0x00140000
|
||||
#define LFRFID_T5577_BITRATE_RF_100 0x00180000
|
||||
#define LFRFID_T5577_BITRATE_RF_128 0x001C0000
|
||||
#define LFRFID_T5577_TESTMODE_DISABLED 0x60000000
|
||||
|
||||
typedef struct {
|
||||
uint32_t block[LFRFID_T5577_BLOCK_COUNT];
|
||||
uint32_t blocks_to_write;
|
||||
} LFRFIDT5577;
|
||||
|
||||
/**
|
||||
* @brief Write T5577 tag data to tag
|
||||
*
|
||||
* @param data
|
||||
*/
|
||||
void t5577_write(LFRFIDT5577* data);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
77
lib/lfrfid/tools/varint_pair.c
Normal file
77
lib/lfrfid/tools/varint_pair.c
Normal file
@@ -0,0 +1,77 @@
|
||||
#include "varint_pair.h"
|
||||
#include <toolbox/varint.h>
|
||||
|
||||
#define VARINT_PAIR_SIZE 10
|
||||
|
||||
struct VarintPair {
|
||||
size_t data_length;
|
||||
uint8_t data[VARINT_PAIR_SIZE];
|
||||
};
|
||||
|
||||
VarintPair* varint_pair_alloc() {
|
||||
VarintPair* pair = malloc(sizeof(VarintPair));
|
||||
pair->data_length = 0;
|
||||
return pair;
|
||||
}
|
||||
|
||||
void varint_pair_free(VarintPair* pair) {
|
||||
free(pair);
|
||||
}
|
||||
|
||||
bool varint_pair_pack(VarintPair* pair, bool first, uint32_t value) {
|
||||
bool result = false;
|
||||
|
||||
if(first) {
|
||||
if(pair->data_length == 0) {
|
||||
pair->data_length = varint_uint32_pack(value, pair->data);
|
||||
} else {
|
||||
pair->data_length = 0;
|
||||
}
|
||||
} else {
|
||||
if(pair->data_length > 0) {
|
||||
pair->data_length += varint_uint32_pack(value, pair->data + pair->data_length);
|
||||
result = true;
|
||||
} else {
|
||||
pair->data_length = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool varint_pair_unpack(
|
||||
uint8_t* data,
|
||||
size_t data_length,
|
||||
uint32_t* value_1,
|
||||
uint32_t* value_2,
|
||||
size_t* length) {
|
||||
size_t size = 0;
|
||||
uint32_t tmp_value_1;
|
||||
uint32_t tmp_value_2;
|
||||
|
||||
size += varint_uint32_unpack(&tmp_value_1, &data[size], data_length);
|
||||
|
||||
if(size >= data_length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size += varint_uint32_unpack(&tmp_value_2, &data[size], (size_t)(data_length - size));
|
||||
|
||||
*value_1 = tmp_value_1;
|
||||
*value_2 = tmp_value_2;
|
||||
*length = size;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t* varint_pair_get_data(VarintPair* pair) {
|
||||
return pair->data;
|
||||
}
|
||||
|
||||
size_t varint_pair_get_size(VarintPair* pair) {
|
||||
return pair->data_length;
|
||||
}
|
||||
|
||||
void varint_pair_reset(VarintPair* pair) {
|
||||
pair->data_length = 0;
|
||||
}
|
79
lib/lfrfid/tools/varint_pair.h
Normal file
79
lib/lfrfid/tools/varint_pair.h
Normal file
@@ -0,0 +1,79 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct VarintPair VarintPair;
|
||||
|
||||
/**
|
||||
* @brief Allocate a new VarintPair instance
|
||||
*
|
||||
* VarintPair is a buffer that holds pair of varint values
|
||||
* @return VarintPair*
|
||||
*/
|
||||
VarintPair* varint_pair_alloc();
|
||||
|
||||
/**
|
||||
* @brief Free a VarintPair instance
|
||||
*
|
||||
* @param pair
|
||||
*/
|
||||
void varint_pair_free(VarintPair* pair);
|
||||
|
||||
/**
|
||||
* @brief Write varint pair to buffer
|
||||
*
|
||||
* @param pair
|
||||
* @param first
|
||||
* @param value
|
||||
* @return bool pair complete and needs to be written
|
||||
*/
|
||||
bool varint_pair_pack(VarintPair* pair, bool first, uint32_t value);
|
||||
|
||||
/**
|
||||
* @brief Get pointer to varint pair buffer
|
||||
*
|
||||
* @param pair
|
||||
* @return uint8_t*
|
||||
*/
|
||||
uint8_t* varint_pair_get_data(VarintPair* pair);
|
||||
|
||||
/**
|
||||
* @brief Get size of varint pair buffer
|
||||
*
|
||||
* @param pair
|
||||
* @return size_t
|
||||
*/
|
||||
size_t varint_pair_get_size(VarintPair* pair);
|
||||
|
||||
/**
|
||||
* @brief Reset varint pair buffer
|
||||
*
|
||||
* @param pair
|
||||
*/
|
||||
void varint_pair_reset(VarintPair* pair);
|
||||
|
||||
/**
|
||||
* @brief Unpack varint pair to uint32_t pair from buffer
|
||||
*
|
||||
* @param data
|
||||
* @param data_length
|
||||
* @param value_1
|
||||
* @param value_2
|
||||
* @param length
|
||||
* @return bool
|
||||
*/
|
||||
bool varint_pair_unpack(
|
||||
uint8_t* data,
|
||||
size_t data_length,
|
||||
uint32_t* value_1,
|
||||
uint32_t* value_2,
|
||||
size_t* length);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
Reference in New Issue
Block a user