[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:
SG
2022-08-24 01:57:39 +10:00
committed by GitHub
parent f92127c0a7
commit 9bfb641d3e
179 changed files with 10234 additions and 4804 deletions

View File

@@ -1,126 +0,0 @@
#include "encoder_cyfral.h"
#include <furi_hal.h>
#define CYFRAL_DATA_SIZE sizeof(uint16_t)
#define CYFRAL_PERIOD (125 * furi_hal_cortex_instructions_per_microsecond())
#define CYFRAL_0_LOW (CYFRAL_PERIOD * 0.66f)
#define CYFRAL_0_HI (CYFRAL_PERIOD * 0.33f)
#define CYFRAL_1_LOW (CYFRAL_PERIOD * 0.33f)
#define CYFRAL_1_HI (CYFRAL_PERIOD * 0.66f)
#define CYFRAL_SET_DATA(level, len) \
*polarity = level; \
*length = len;
struct EncoderCyfral {
uint32_t data;
uint32_t index;
};
EncoderCyfral* encoder_cyfral_alloc() {
EncoderCyfral* cyfral = malloc(sizeof(EncoderCyfral));
encoder_cyfral_reset(cyfral);
return cyfral;
}
void encoder_cyfral_free(EncoderCyfral* cyfral) {
free(cyfral);
}
void encoder_cyfral_reset(EncoderCyfral* cyfral) {
cyfral->data = 0;
cyfral->index = 0;
}
uint32_t cyfral_encoder_encode(const uint16_t data) {
uint32_t value = 0;
for(int8_t i = 0; i <= 7; i++) {
switch((data >> (i * 2)) & 0b00000011) {
case 0b11:
value = value << 4;
value += 0b00000111;
break;
case 0b10:
value = value << 4;
value += 0b00001011;
break;
case 0b01:
value = value << 4;
value += 0b00001101;
break;
case 0b00:
value = value << 4;
value += 0b00001110;
break;
default:
break;
}
}
return value;
}
void encoder_cyfral_set_data(EncoderCyfral* cyfral, const uint8_t* data, size_t data_size) {
furi_assert(cyfral);
furi_check(data_size >= CYFRAL_DATA_SIZE);
uint16_t intermediate;
memcpy(&intermediate, data, CYFRAL_DATA_SIZE);
cyfral->data = cyfral_encoder_encode(intermediate);
}
void encoder_cyfral_get_pulse(EncoderCyfral* cyfral, bool* polarity, uint32_t* length) {
if(cyfral->index < 8) {
// start word (0b0001)
switch(cyfral->index) {
case 0:
CYFRAL_SET_DATA(false, CYFRAL_0_LOW);
break;
case 1:
CYFRAL_SET_DATA(true, CYFRAL_0_HI);
break;
case 2:
CYFRAL_SET_DATA(false, CYFRAL_0_LOW);
break;
case 3:
CYFRAL_SET_DATA(true, CYFRAL_0_HI);
break;
case 4:
CYFRAL_SET_DATA(false, CYFRAL_0_LOW);
break;
case 5:
CYFRAL_SET_DATA(true, CYFRAL_0_HI);
break;
case 6:
CYFRAL_SET_DATA(false, CYFRAL_1_LOW);
break;
case 7:
CYFRAL_SET_DATA(true, CYFRAL_1_HI);
break;
}
} else {
// data
uint8_t data_start_index = cyfral->index - 8;
bool clock_polarity = (data_start_index) % 2;
uint8_t bit_index = (data_start_index) / 2;
bool bit_value = ((cyfral->data >> bit_index) & 1);
if(!clock_polarity) {
if(bit_value) {
CYFRAL_SET_DATA(false, CYFRAL_1_LOW);
} else {
CYFRAL_SET_DATA(false, CYFRAL_0_LOW);
}
} else {
if(bit_value) {
CYFRAL_SET_DATA(true, CYFRAL_1_HI);
} else {
CYFRAL_SET_DATA(true, CYFRAL_0_HI);
}
}
}
cyfral->index++;
if(cyfral->index >= (9 * 4 * 2)) {
cyfral->index = 0;
}
}

View File

@@ -1,54 +0,0 @@
/**
* @file encoder_cyfral.h
*
* Cyfral pulse format encoder
*/
#pragma once
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct EncoderCyfral EncoderCyfral;
/**
* Allocate Cyfral encoder
* @return EncoderCyfral*
*/
EncoderCyfral* encoder_cyfral_alloc();
/**
* Deallocate Cyfral encoder
* @param cyfral
*/
void encoder_cyfral_free(EncoderCyfral* cyfral);
/**
* Reset Cyfral encoder
* @param cyfral
*/
void encoder_cyfral_reset(EncoderCyfral* cyfral);
/**
* Set data to be encoded to Cyfral pulse format, 2 bytes
* @param cyfral
* @param data
* @param data_size
*/
void encoder_cyfral_set_data(EncoderCyfral* cyfral, const uint8_t* data, size_t data_size);
/**
* Pop pulse from Cyfral encoder
* @param cyfral
* @param polarity
* @param length
*/
void encoder_cyfral_get_pulse(EncoderCyfral* cyfral, bool* polarity, uint32_t* length);
#ifdef __cplusplus
}
#endif

View File

@@ -1,93 +0,0 @@
#include "encoder_metakom.h"
#include <furi_hal.h>
#define METAKOM_DATA_SIZE sizeof(uint32_t)
#define METAKOM_PERIOD (125 * furi_hal_cortex_instructions_per_microsecond())
#define METAKOM_0_LOW (METAKOM_PERIOD * 0.33f)
#define METAKOM_0_HI (METAKOM_PERIOD * 0.66f)
#define METAKOM_1_LOW (METAKOM_PERIOD * 0.66f)
#define METAKOM_1_HI (METAKOM_PERIOD * 0.33f)
#define METAKOM_SET_DATA(level, len) \
*polarity = !level; \
*length = len;
struct EncoderMetakom {
uint32_t data;
uint32_t index;
};
EncoderMetakom* encoder_metakom_alloc() {
EncoderMetakom* metakom = malloc(sizeof(EncoderMetakom));
encoder_metakom_reset(metakom);
return metakom;
}
void encoder_metakom_free(EncoderMetakom* metakom) {
free(metakom);
}
void encoder_metakom_reset(EncoderMetakom* metakom) {
metakom->data = 0;
metakom->index = 0;
}
void encoder_metakom_set_data(EncoderMetakom* metakom, const uint8_t* data, size_t data_size) {
furi_assert(metakom);
furi_check(data_size >= METAKOM_DATA_SIZE);
memcpy(&metakom->data, data, METAKOM_DATA_SIZE);
}
void encoder_metakom_get_pulse(EncoderMetakom* metakom, bool* polarity, uint32_t* length) {
if(metakom->index == 0) {
// sync bit
METAKOM_SET_DATA(true, METAKOM_PERIOD);
} else if(metakom->index >= 1 && metakom->index <= 6) {
// start word (0b010)
switch(metakom->index) {
case 1:
METAKOM_SET_DATA(false, METAKOM_0_LOW);
break;
case 2:
METAKOM_SET_DATA(true, METAKOM_0_HI);
break;
case 3:
METAKOM_SET_DATA(false, METAKOM_1_LOW);
break;
case 4:
METAKOM_SET_DATA(true, METAKOM_1_HI);
break;
case 5:
METAKOM_SET_DATA(false, METAKOM_0_LOW);
break;
case 6:
METAKOM_SET_DATA(true, METAKOM_0_HI);
break;
}
} else {
// data
uint8_t data_start_index = metakom->index - 7;
bool clock_polarity = (data_start_index) % 2;
uint8_t bit_index = (data_start_index) / 2;
bool bit_value = (metakom->data >> (32 - 1 - bit_index)) & 1;
if(!clock_polarity) {
if(bit_value) {
METAKOM_SET_DATA(false, METAKOM_1_LOW);
} else {
METAKOM_SET_DATA(false, METAKOM_0_LOW);
}
} else {
if(bit_value) {
METAKOM_SET_DATA(true, METAKOM_1_HI);
} else {
METAKOM_SET_DATA(true, METAKOM_0_HI);
}
}
}
metakom->index++;
if(metakom->index >= (1 + 3 * 2 + 32 * 2)) {
metakom->index = 0;
}
}

View File

@@ -1,54 +0,0 @@
/**
* @file encoder_metakom.h
*
* Metakom pulse format encoder
*/
#pragma once
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct EncoderMetakom EncoderMetakom;
/**
* Allocate Metakom encoder
* @return EncoderMetakom*
*/
EncoderMetakom* encoder_metakom_alloc();
/**
* Deallocate Metakom encoder
* @param metakom
*/
void encoder_metakom_free(EncoderMetakom* metakom);
/**
* Reset Metakom encoder
* @param metakom
*/
void encoder_metakom_reset(EncoderMetakom* metakom);
/**
* Set data to be encoded to Metakom pulse format, 4 bytes
* @param metakom
* @param data
* @param data_size
*/
void encoder_metakom_set_data(EncoderMetakom* metakom, const uint8_t* data, size_t data_size);
/**
* Pop pulse from Metakom encoder
* @param cyfral
* @param polarity
* @param length
*/
void encoder_metakom_get_pulse(EncoderMetakom* metakom, bool* polarity, uint32_t* length);
#ifdef __cplusplus
}
#endif

View File

@@ -5,6 +5,7 @@
*/
#pragma once
#include <stdint.h>
#ifdef __cplusplus
extern "C" {

View File

@@ -29,34 +29,21 @@ iButtonWorker* ibutton_worker_alloc() {
worker->slave = onewire_slave_alloc();
worker->writer = ibutton_writer_alloc(worker->host);
worker->device = onewire_device_alloc(0, 0, 0, 0, 0, 0, 0, 0);
worker->pulse_decoder = pulse_decoder_alloc();
worker->protocol_cyfral = protocol_cyfral_alloc();
worker->protocol_metakom = protocol_metakom_alloc();
worker->messages = furi_message_queue_alloc(1, sizeof(iButtonMessage));
worker->mode_index = iButtonWorkerIdle;
worker->last_dwt_value = 0;
worker->read_cb = NULL;
worker->write_cb = NULL;
worker->emulate_cb = NULL;
worker->cb_ctx = NULL;
worker->encoder_cyfral = encoder_cyfral_alloc();
worker->encoder_metakom = encoder_metakom_alloc();
worker->thread = furi_thread_alloc();
furi_thread_set_name(worker->thread, "ibutton_worker");
furi_thread_set_callback(worker->thread, ibutton_worker_thread);
furi_thread_set_context(worker->thread, worker);
furi_thread_set_stack_size(worker->thread, 2048);
pulse_decoder_add_protocol(
worker->pulse_decoder,
protocol_cyfral_get_protocol(worker->protocol_cyfral),
PulseProtocolCyfral);
pulse_decoder_add_protocol(
worker->pulse_decoder,
protocol_metakom_get_protocol(worker->protocol_metakom),
PulseProtocolMetakom);
worker->protocols = protocol_dict_alloc(ibutton_protocols, iButtonProtocolMax);
return worker;
}
@@ -113,10 +100,6 @@ void ibutton_worker_stop(iButtonWorker* worker) {
}
void ibutton_worker_free(iButtonWorker* worker) {
pulse_decoder_free(worker->pulse_decoder);
protocol_metakom_free(worker->protocol_metakom);
protocol_cyfral_free(worker->protocol_cyfral);
ibutton_writer_free(worker->writer);
onewire_slave_free(worker->slave);
@@ -124,8 +107,7 @@ void ibutton_worker_free(iButtonWorker* worker) {
onewire_host_free(worker->host);
onewire_device_free(worker->device);
encoder_cyfral_free(worker->encoder_cyfral);
encoder_metakom_free(worker->encoder_metakom);
protocol_dict_free(worker->protocols);
furi_message_queue_free(worker->messages);

View File

@@ -10,21 +10,13 @@
#include "../one_wire_host.h"
#include "../one_wire_slave.h"
#include "../one_wire_device.h"
#include "../pulse_protocols/pulse_decoder.h"
#include "pulse_protocols/protocol_cyfral.h"
#include "pulse_protocols/protocol_metakom.h"
#include "encoder/encoder_cyfral.h"
#include "encoder/encoder_metakom.h"
#include <toolbox/protocols/protocol_dict.h>
#include "protocols/ibutton_protocols.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
PulseProtocolCyfral,
PulseProtocolMetakom,
} PulseProtocols;
typedef struct {
const uint32_t quant;
void (*const start)(iButtonWorker* worker);
@@ -39,11 +31,6 @@ typedef enum {
iButtonWorkerEmulate = 3,
} iButtonWorkerMode;
typedef enum {
iButtonEmulateModeCyfral,
iButtonEmulateModeMetakom,
} iButtonEmulateMode;
struct iButtonWorker {
iButtonKey* key_p;
uint8_t* key_data;
@@ -55,19 +42,13 @@ struct iButtonWorker {
FuriMessageQueue* messages;
FuriThread* thread;
PulseDecoder* pulse_decoder;
ProtocolCyfral* protocol_cyfral;
ProtocolMetakom* protocol_metakom;
uint32_t last_dwt_value;
iButtonWorkerReadCallback read_cb;
iButtonWorkerWriteCallback write_cb;
iButtonWorkerEmulateCallback emulate_cb;
void* cb_ctx;
EncoderCyfral* encoder_cyfral;
EncoderMetakom* encoder_metakom;
iButtonEmulateMode emulate_mode;
ProtocolDict* protocols;
iButtonProtocol protocol_to_encode;
};
extern const iButtonWorkerModeType ibutton_worker_modes[];

View File

@@ -2,6 +2,7 @@
#include <furi_hal.h>
#include "ibutton_worker_i.h"
#include "ibutton_key_command.h"
#include <stream_buffer.h>
void ibutton_worker_mode_idle_start(iButtonWorker* worker);
void ibutton_worker_mode_idle_tick(iButtonWorker* worker);
@@ -62,59 +63,86 @@ void ibutton_worker_mode_idle_stop(iButtonWorker* worker) {
/*********************** READ ***********************/
typedef struct {
uint32_t last_dwt_value;
StreamBufferHandle_t stream;
} iButtonReadContext;
void ibutton_worker_comparator_callback(bool level, void* context) {
iButtonWorker* worker = context;
iButtonReadContext* read_context = context;
uint32_t current_dwt_value = DWT->CYCCNT;
pulse_decoder_process_pulse(
worker->pulse_decoder, level, current_dwt_value - worker->last_dwt_value);
LevelDuration data =
level_duration_make(level, current_dwt_value - read_context->last_dwt_value);
xStreamBufferSend(read_context->stream, &data, sizeof(LevelDuration), 0);
worker->last_dwt_value = current_dwt_value;
read_context->last_dwt_value = current_dwt_value;
}
bool ibutton_worker_read_comparator(iButtonWorker* worker) {
bool result = false;
pulse_decoder_reset(worker->pulse_decoder);
protocol_dict_decoders_start(worker->protocols);
furi_hal_rfid_pins_reset();
// pulldown pull pin, we sense the signal through the analog part of the RFID schematic
furi_hal_rfid_pin_pull_pulldown();
furi_hal_rfid_comp_set_callback(ibutton_worker_comparator_callback, worker);
worker->last_dwt_value = DWT->CYCCNT;
iButtonReadContext read_context = {
.last_dwt_value = DWT->CYCCNT,
.stream = xStreamBufferCreate(sizeof(LevelDuration) * 512, 1),
};
furi_hal_rfid_comp_set_callback(ibutton_worker_comparator_callback, &read_context);
furi_hal_rfid_comp_start();
// TODO: rework with thread events, "pulse_decoder_get_decoded_index_with_timeout"
furi_delay_ms(100);
int32_t decoded_index = pulse_decoder_get_decoded_index(worker->pulse_decoder);
if(decoded_index >= 0) {
pulse_decoder_get_data(
worker->pulse_decoder, decoded_index, worker->key_data, ibutton_key_get_max_size());
}
uint32_t tick_start = furi_get_tick();
while(true) {
LevelDuration level;
size_t ret = xStreamBufferReceive(read_context.stream, &level, sizeof(LevelDuration), 100);
switch(decoded_index) {
case PulseProtocolCyfral:
furi_check(worker->key_p != NULL);
ibutton_key_set_type(worker->key_p, iButtonKeyCyfral);
ibutton_key_set_data(worker->key_p, worker->key_data, ibutton_key_get_max_size());
result = true;
break;
case PulseProtocolMetakom:
furi_check(worker->key_p != NULL);
ibutton_key_set_type(worker->key_p, iButtonKeyMetakom);
ibutton_key_set_data(worker->key_p, worker->key_data, ibutton_key_get_max_size());
result = true;
break;
break;
default:
break;
if((furi_get_tick() - tick_start) > 100) {
break;
}
if(ret > 0) {
ProtocolId decoded_index = protocol_dict_decoders_feed(
worker->protocols,
level_duration_get_level(level),
level_duration_get_duration(level));
if(decoded_index == PROTOCOL_NO) continue;
protocol_dict_get_data(
worker->protocols, decoded_index, worker->key_data, ibutton_key_get_max_size());
switch(decoded_index) {
case iButtonProtocolCyfral:
furi_check(worker->key_p != NULL);
ibutton_key_set_type(worker->key_p, iButtonKeyCyfral);
ibutton_key_set_data(worker->key_p, worker->key_data, ibutton_key_get_max_size());
result = true;
break;
case iButtonProtocolMetakom:
furi_check(worker->key_p != NULL);
ibutton_key_set_type(worker->key_p, iButtonKeyMetakom);
ibutton_key_set_data(worker->key_p, worker->key_data, ibutton_key_get_max_size());
result = true;
break;
break;
default:
break;
}
}
}
furi_hal_rfid_comp_stop();
furi_hal_rfid_comp_set_callback(NULL, NULL);
furi_hal_rfid_pins_reset();
vStreamBufferDelete(read_context.stream);
return result;
}
@@ -207,21 +235,12 @@ void ibutton_worker_emulate_timer_cb(void* context) {
furi_assert(context);
iButtonWorker* worker = context;
bool polarity;
uint32_t length;
LevelDuration level =
protocol_dict_encoder_yield(worker->protocols, worker->protocol_to_encode);
switch(worker->emulate_mode) {
case iButtonEmulateModeCyfral:
encoder_cyfral_get_pulse(worker->encoder_cyfral, &polarity, &length);
break;
case iButtonEmulateModeMetakom:
encoder_metakom_get_pulse(worker->encoder_metakom, &polarity, &length);
break;
}
furi_hal_ibutton_emulate_set_next(level_duration_get_duration(level));
furi_hal_ibutton_emulate_set_next(length);
if(polarity) {
if(level_duration_get_level(level)) {
furi_hal_ibutton_pin_high();
} else {
furi_hal_ibutton_pin_low();
@@ -238,17 +257,16 @@ void ibutton_worker_emulate_timer_start(iButtonWorker* worker) {
return;
break;
case iButtonKeyCyfral:
worker->emulate_mode = iButtonEmulateModeCyfral;
encoder_cyfral_reset(worker->encoder_cyfral);
encoder_cyfral_set_data(worker->encoder_cyfral, key_id, key_size);
worker->protocol_to_encode = iButtonProtocolCyfral;
break;
case iButtonKeyMetakom:
worker->emulate_mode = iButtonEmulateModeMetakom;
encoder_metakom_reset(worker->encoder_metakom);
encoder_metakom_set_data(worker->encoder_metakom, key_id, key_size);
worker->protocol_to_encode = iButtonProtocolMetakom;
break;
}
protocol_dict_set_data(worker->protocols, worker->protocol_to_encode, key_id, key_size);
protocol_dict_encoder_start(worker->protocols, worker->protocol_to_encode);
furi_hal_ibutton_start_drive();
furi_hal_ibutton_emulate_start(0, ibutton_worker_emulate_timer_cb, worker);
}

View File

@@ -0,0 +1,8 @@
#include "ibutton_protocols.h"
#include "protocol_cyfral.h"
#include "protocol_metakom.h"
const ProtocolBase* ibutton_protocols[] = {
[iButtonProtocolCyfral] = &protocol_cyfral,
[iButtonProtocolMetakom] = &protocol_metakom,
};

View File

@@ -0,0 +1,11 @@
#pragma once
#include "toolbox/protocols/protocol.h"
typedef enum {
iButtonProtocolCyfral,
iButtonProtocolMetakom,
iButtonProtocolMax,
} iButtonProtocol;
extern const ProtocolBase* ibutton_protocols[];

View File

@@ -0,0 +1,344 @@
#include <furi.h>
#include <furi_hal.h>
#include "protocol_cyfral.h"
#define CYFRAL_DATA_SIZE sizeof(uint16_t)
#define CYFRAL_PERIOD (125 * furi_hal_cortex_instructions_per_microsecond())
#define CYFRAL_0_LOW (CYFRAL_PERIOD * 0.66f)
#define CYFRAL_0_HI (CYFRAL_PERIOD * 0.33f)
#define CYFRAL_1_LOW (CYFRAL_PERIOD * 0.33f)
#define CYFRAL_1_HI (CYFRAL_PERIOD * 0.66f)
#define CYFRAL_MAX_PERIOD_US 230
typedef enum {
CYFRAL_BIT_WAIT_FRONT_HIGH,
CYFRAL_BIT_WAIT_FRONT_LOW,
} CyfralBitState;
typedef enum {
CYFRAL_WAIT_START_NIBBLE,
CYFRAL_READ_NIBBLE,
CYFRAL_READ_STOP_NIBBLE,
} CyfralState;
typedef struct {
CyfralState state;
CyfralBitState bit_state;
// high + low period time
uint32_t period_time;
// temporary nibble storage
uint8_t nibble;
// data valid flag
// MUST be checked only in READ_STOP_NIBBLE state
bool data_valid;
// nibble index, we expect 8 nibbles
uint8_t index;
// bit index in nibble, 4 bit per nibble
uint8_t bit_index;
// max period, 230us x clock per us
uint32_t max_period;
} ProtocolCyfralDecoder;
typedef struct {
uint32_t data;
uint32_t index;
} ProtocolCyfralEncoder;
typedef struct {
uint16_t data;
ProtocolCyfralDecoder decoder;
ProtocolCyfralEncoder encoder;
} ProtocolCyfral;
static void* protocol_cyfral_alloc(void) {
ProtocolCyfral* proto = malloc(sizeof(ProtocolCyfral));
return (void*)proto;
}
static void protocol_cyfral_free(ProtocolCyfral* proto) {
free(proto);
}
static uint8_t* protocol_cyfral_get_data(ProtocolCyfral* proto) {
return (uint8_t*)&proto->data;
}
static void protocol_cyfral_decoder_start(ProtocolCyfral* proto) {
ProtocolCyfralDecoder* cyfral = &proto->decoder;
cyfral->state = CYFRAL_WAIT_START_NIBBLE;
cyfral->bit_state = CYFRAL_BIT_WAIT_FRONT_LOW;
cyfral->period_time = 0;
cyfral->bit_index = 0;
cyfral->index = 0;
cyfral->nibble = 0;
cyfral->data_valid = true;
cyfral->max_period = CYFRAL_MAX_PERIOD_US * furi_hal_cortex_instructions_per_microsecond();
proto->data = 0;
}
static bool protocol_cyfral_decoder_process_bit(
ProtocolCyfralDecoder* cyfral,
bool polarity,
uint32_t length,
bool* bit_ready,
bool* bit_value) {
bool result = true;
*bit_ready = false;
// bit start from low
switch(cyfral->bit_state) {
case CYFRAL_BIT_WAIT_FRONT_LOW:
if(polarity == true) {
cyfral->period_time += length;
*bit_ready = true;
if(cyfral->period_time <= cyfral->max_period) {
if((cyfral->period_time / 2) > length) {
*bit_value = false;
} else {
*bit_value = true;
}
} else {
result = false;
}
cyfral->bit_state = CYFRAL_BIT_WAIT_FRONT_HIGH;
} else {
result = false;
}
break;
case CYFRAL_BIT_WAIT_FRONT_HIGH:
if(polarity == false) {
cyfral->period_time = length;
cyfral->bit_state = CYFRAL_BIT_WAIT_FRONT_LOW;
} else {
result = false;
}
break;
}
return result;
}
static bool protocol_cyfral_decoder_feed(ProtocolCyfral* proto, bool level, uint32_t duration) {
ProtocolCyfralDecoder* cyfral = &proto->decoder;
bool bit_ready;
bool bit_value;
bool decoded = false;
switch(cyfral->state) {
case CYFRAL_WAIT_START_NIBBLE:
// wait for start word
if(protocol_cyfral_decoder_process_bit(cyfral, level, duration, &bit_ready, &bit_value)) {
if(bit_ready) {
cyfral->nibble = ((cyfral->nibble << 1) | bit_value) & 0x0F;
if(cyfral->nibble == 0b0001) {
cyfral->nibble = 0;
cyfral->state = CYFRAL_READ_NIBBLE;
}
}
} else {
protocol_cyfral_decoder_start(proto);
}
break;
case CYFRAL_READ_NIBBLE:
// read nibbles
if(protocol_cyfral_decoder_process_bit(cyfral, level, duration, &bit_ready, &bit_value)) {
if(bit_ready) {
cyfral->nibble = (cyfral->nibble << 1) | bit_value;
cyfral->bit_index++;
//convert every nibble to 2-bit index
if(cyfral->bit_index == 4) {
switch(cyfral->nibble) {
case 0b1110:
proto->data = (proto->data << 2) | 0b11;
break;
case 0b1101:
proto->data = (proto->data << 2) | 0b10;
break;
case 0b1011:
proto->data = (proto->data << 2) | 0b01;
break;
case 0b0111:
proto->data = (proto->data << 2) | 0b00;
break;
default:
cyfral->data_valid = false;
break;
}
cyfral->nibble = 0;
cyfral->bit_index = 0;
cyfral->index++;
}
// succefully read 8 nibbles
if(cyfral->index == 8) {
cyfral->state = CYFRAL_READ_STOP_NIBBLE;
}
}
} else {
protocol_cyfral_decoder_start(proto);
}
break;
case CYFRAL_READ_STOP_NIBBLE:
// read stop nibble
if(protocol_cyfral_decoder_process_bit(cyfral, level, duration, &bit_ready, &bit_value)) {
if(bit_ready) {
cyfral->nibble = ((cyfral->nibble << 1) | bit_value) & 0x0F;
cyfral->bit_index++;
switch(cyfral->bit_index) {
case 0:
case 1:
case 2:
case 3:
break;
case 4:
if(cyfral->nibble == 0b0001) {
// validate data
if(cyfral->data_valid) {
decoded = true;
} else {
protocol_cyfral_decoder_start(proto);
}
} else {
protocol_cyfral_decoder_start(proto);
}
break;
default:
protocol_cyfral_decoder_start(proto);
break;
}
}
} else {
protocol_cyfral_decoder_start(proto);
}
break;
}
return decoded;
}
static uint32_t protocol_cyfral_encoder_encode(const uint16_t data) {
uint32_t value = 0;
for(int8_t i = 0; i <= 7; i++) {
switch((data >> (i * 2)) & 0b00000011) {
case 0b11:
value = value << 4;
value += 0b00000111;
break;
case 0b10:
value = value << 4;
value += 0b00001011;
break;
case 0b01:
value = value << 4;
value += 0b00001101;
break;
case 0b00:
value = value << 4;
value += 0b00001110;
break;
default:
break;
}
}
return value;
}
static bool protocol_cyfral_encoder_start(ProtocolCyfral* proto) {
proto->encoder.index = 0;
proto->encoder.data = protocol_cyfral_encoder_encode(proto->data);
return true;
}
static LevelDuration protocol_cyfral_encoder_yield(ProtocolCyfral* proto) {
LevelDuration result;
if(proto->encoder.index < 8) {
// start word (0b0001)
switch(proto->encoder.index) {
case 0:
result = level_duration_make(false, CYFRAL_0_LOW);
break;
case 1:
result = level_duration_make(true, CYFRAL_0_HI);
break;
case 2:
result = level_duration_make(false, CYFRAL_0_LOW);
break;
case 3:
result = level_duration_make(true, CYFRAL_0_HI);
break;
case 4:
result = level_duration_make(false, CYFRAL_0_LOW);
break;
case 5:
result = level_duration_make(true, CYFRAL_0_HI);
break;
case 6:
result = level_duration_make(false, CYFRAL_1_LOW);
break;
case 7:
result = level_duration_make(true, CYFRAL_1_HI);
break;
}
} else {
// data
uint8_t data_start_index = proto->encoder.index - 8;
bool clock_polarity = (data_start_index) % 2;
uint8_t bit_index = (data_start_index) / 2;
bool bit_value = ((proto->encoder.data >> bit_index) & 1);
if(!clock_polarity) {
if(bit_value) {
result = level_duration_make(false, CYFRAL_1_LOW);
} else {
result = level_duration_make(false, CYFRAL_0_LOW);
}
} else {
if(bit_value) {
result = level_duration_make(true, CYFRAL_1_HI);
} else {
result = level_duration_make(true, CYFRAL_0_HI);
}
}
}
proto->encoder.index++;
if(proto->encoder.index >= (9 * 4 * 2)) {
proto->encoder.index = 0;
}
return result;
}
const ProtocolBase protocol_cyfral = {
.name = "Cyfral",
.manufacturer = "Cyfral",
.data_size = CYFRAL_DATA_SIZE,
.alloc = (ProtocolAlloc)protocol_cyfral_alloc,
.free = (ProtocolFree)protocol_cyfral_free,
.get_data = (ProtocolGetData)protocol_cyfral_get_data,
.decoder =
{
.start = (ProtocolDecoderStart)protocol_cyfral_decoder_start,
.feed = (ProtocolDecoderFeed)protocol_cyfral_decoder_feed,
},
.encoder =
{
.start = (ProtocolEncoderStart)protocol_cyfral_encoder_start,
.yield = (ProtocolEncoderYield)protocol_cyfral_encoder_yield,
},
};

View File

@@ -0,0 +1,4 @@
#pragma once
#include "toolbox/protocols/protocol.h"
extern const ProtocolBase protocol_cyfral;

View File

@@ -1,9 +1,14 @@
#include <furi.h>
#include <furi_hal.h>
#include "protocol_metakom.h"
#include <stdlib.h>
#include <string.h>
#include <core/check.h>
#define METAKOM_DATA_SIZE 4
#define METAKOM_DATA_SIZE sizeof(uint32_t)
#define METAKOM_PERIOD (125 * furi_hal_cortex_instructions_per_microsecond())
#define METAKOM_0_LOW (METAKOM_PERIOD * 0.33f)
#define METAKOM_0_HI (METAKOM_PERIOD * 0.66f)
#define METAKOM_1_LOW (METAKOM_PERIOD * 0.66f)
#define METAKOM_1_HI (METAKOM_PERIOD * 0.33f)
#define METAKOM_PERIOD_SAMPLE_COUNT 10
typedef enum {
@@ -19,79 +24,49 @@ typedef enum {
METAKOM_BIT_WAIT_FRONT_LOW,
} MetakomBitState;
struct ProtocolMetakom {
PulseProtocol* protocol;
typedef struct {
// high + low period time
uint32_t period_time;
uint32_t low_time_storage;
uint8_t period_sample_index;
uint32_t period_sample_data[METAKOM_PERIOD_SAMPLE_COUNT];
// ready flag
// TODO: atomic access
bool ready;
uint8_t tmp_data;
uint8_t tmp_counter;
uint32_t key_data;
uint8_t key_data_index;
MetakomBitState bit_state;
MetakomState state;
};
} ProtocolMetakomDecoder;
static void metakom_pulse(void* context, bool polarity, uint32_t length);
static void metakom_reset(void* context);
static void metakom_get_data(void* context, uint8_t* data, size_t length);
static bool metakom_decoded(void* context);
typedef struct {
uint32_t index;
} ProtocolMetakomEncoder;
ProtocolMetakom* protocol_metakom_alloc() {
ProtocolMetakom* metakom = malloc(sizeof(ProtocolMetakom));
metakom_reset(metakom);
typedef struct {
uint32_t data;
metakom->protocol = pulse_protocol_alloc();
ProtocolMetakomDecoder decoder;
ProtocolMetakomEncoder encoder;
} ProtocolMetakom;
pulse_protocol_set_context(metakom->protocol, metakom);
pulse_protocol_set_pulse_cb(metakom->protocol, metakom_pulse);
pulse_protocol_set_reset_cb(metakom->protocol, metakom_reset);
pulse_protocol_set_get_data_cb(metakom->protocol, metakom_get_data);
pulse_protocol_set_decoded_cb(metakom->protocol, metakom_decoded);
return metakom;
static ProtocolMetakom* protocol_metakom_alloc(void) {
ProtocolMetakom* proto = malloc(sizeof(ProtocolMetakom));
return (void*)proto;
}
void protocol_metakom_free(ProtocolMetakom* metakom) {
furi_assert(metakom);
pulse_protocol_free(metakom->protocol);
free(metakom);
static void protocol_metakom_free(ProtocolMetakom* proto) {
free(proto);
}
PulseProtocol* protocol_metakom_get_protocol(ProtocolMetakom* metakom) {
furi_assert(metakom);
return metakom->protocol;
static uint8_t* protocol_metakom_get_data(ProtocolMetakom* proto) {
return (uint8_t*)&proto->data;
}
static void metakom_get_data(void* context, uint8_t* data, size_t length) {
furi_assert(context);
furi_check(length >= METAKOM_DATA_SIZE);
ProtocolMetakom* metakom = context;
memcpy(data, &metakom->key_data, METAKOM_DATA_SIZE);
}
static void protocol_metakom_decoder_start(ProtocolMetakom* proto) {
ProtocolMetakomDecoder* metakom = &proto->decoder;
static bool metakom_decoded(void* context) {
furi_assert(context);
ProtocolMetakom* metakom = context;
bool decoded = metakom->ready;
return decoded;
}
static void metakom_reset(void* context) {
furi_assert(context);
ProtocolMetakom* metakom = context;
metakom->ready = false;
metakom->period_sample_index = 0;
metakom->period_time = 0;
metakom->tmp_counter = 0;
@@ -101,9 +76,10 @@ static void metakom_reset(void* context) {
};
metakom->state = METAKOM_WAIT_PERIOD_SYNC;
metakom->bit_state = METAKOM_BIT_WAIT_FRONT_LOW;
metakom->key_data = 0;
metakom->key_data_index = 0;
metakom->low_time_storage = 0;
proto->data = 0;
}
static bool metakom_parity_check(uint8_t data) {
@@ -122,7 +98,7 @@ static bool metakom_parity_check(uint8_t data) {
}
static bool metakom_process_bit(
ProtocolMetakom* metakom,
ProtocolMetakomDecoder* metakom,
bool polarity,
uint32_t time,
uint32_t* high_time,
@@ -149,18 +125,17 @@ static bool metakom_process_bit(
return result;
}
static void metakom_pulse(void* context, bool polarity, uint32_t time) {
furi_assert(context);
ProtocolMetakom* metakom = context;
static bool protocol_metakom_decoder_feed(ProtocolMetakom* proto, bool level, uint32_t duration) {
ProtocolMetakomDecoder* metakom = &proto->decoder;
if(metakom->ready) return;
bool ready = false;
uint32_t high_time = 0;
uint32_t low_time = 0;
switch(metakom->state) {
case METAKOM_WAIT_PERIOD_SYNC:
if(metakom_process_bit(metakom, polarity, time, &high_time, &low_time)) {
if(metakom_process_bit(metakom, level, duration, &high_time, &low_time)) {
metakom->period_sample_data[metakom->period_sample_index] = high_time + low_time;
metakom->period_sample_index++;
@@ -176,7 +151,7 @@ static void metakom_pulse(void* context, bool polarity, uint32_t time) {
break;
case METAKOM_WAIT_START_BIT:
if(metakom_process_bit(metakom, polarity, time, &high_time, &low_time)) {
if(metakom_process_bit(metakom, level, duration, &high_time, &low_time)) {
metakom->tmp_counter++;
if(high_time > metakom->period_time) {
metakom->tmp_counter = 0;
@@ -184,13 +159,13 @@ static void metakom_pulse(void* context, bool polarity, uint32_t time) {
}
if(metakom->tmp_counter > 40) {
metakom_reset(metakom);
protocol_metakom_decoder_start(proto);
}
}
break;
case METAKOM_WAIT_START_WORD:
if(metakom_process_bit(metakom, polarity, time, &high_time, &low_time)) {
if(metakom_process_bit(metakom, level, duration, &high_time, &low_time)) {
if(low_time < (metakom->period_time / 2)) {
metakom->tmp_data = (metakom->tmp_data << 1) | 0b0;
} else {
@@ -204,13 +179,13 @@ static void metakom_pulse(void* context, bool polarity, uint32_t time) {
metakom->tmp_data = 0;
metakom->state = METAKOM_READ_WORD;
} else {
metakom_reset(metakom);
protocol_metakom_decoder_start(proto);
}
}
}
break;
case METAKOM_READ_WORD:
if(metakom_process_bit(metakom, polarity, time, &high_time, &low_time)) {
if(metakom_process_bit(metakom, level, duration, &high_time, &low_time)) {
if(low_time < (metakom->period_time / 2)) {
metakom->tmp_data = (metakom->tmp_data << 1) | 0b0;
} else {
@@ -220,7 +195,7 @@ static void metakom_pulse(void* context, bool polarity, uint32_t time) {
if(metakom->tmp_counter == 8) {
if(metakom_parity_check(metakom->tmp_data)) {
metakom->key_data = (metakom->key_data << 8) | metakom->tmp_data;
proto->data = (proto->data << 8) | metakom->tmp_data;
metakom->key_data_index++;
metakom->tmp_data = 0;
metakom->tmp_counter = 0;
@@ -230,17 +205,17 @@ static void metakom_pulse(void* context, bool polarity, uint32_t time) {
if(high_time > metakom->period_time) {
metakom->state = METAKOM_READ_STOP_WORD;
} else {
metakom_reset(metakom);
protocol_metakom_decoder_start(proto);
}
}
} else {
metakom_reset(metakom);
protocol_metakom_decoder_start(proto);
}
}
}
break;
case METAKOM_READ_STOP_WORD:
if(metakom_process_bit(metakom, polarity, time, &high_time, &low_time)) {
if(metakom_process_bit(metakom, level, duration, &high_time, &low_time)) {
if(low_time < (metakom->period_time / 2)) {
metakom->tmp_data = (metakom->tmp_data << 1) | 0b0;
} else {
@@ -250,12 +225,96 @@ static void metakom_pulse(void* context, bool polarity, uint32_t time) {
if(metakom->tmp_counter == 3) {
if(metakom->tmp_data == 0b010) {
metakom->ready = true;
ready = true;
} else {
metakom_reset(metakom);
protocol_metakom_decoder_start(proto);
}
}
}
break;
}
return ready;
}
static bool protocol_metakom_encoder_start(ProtocolMetakom* proto) {
proto->encoder.index = 0;
return true;
}
static LevelDuration protocol_metakom_encoder_yield(ProtocolMetakom* proto) {
LevelDuration result;
if(proto->encoder.index == 0) {
// sync bit
result = level_duration_make(false, METAKOM_PERIOD);
} else if(proto->encoder.index >= 1 && proto->encoder.index <= 6) {
// start word (0b010)
switch(proto->encoder.index) {
case 1:
result = level_duration_make(true, METAKOM_0_LOW);
break;
case 2:
result = level_duration_make(false, METAKOM_0_HI);
break;
case 3:
result = level_duration_make(true, METAKOM_1_LOW);
break;
case 4:
result = level_duration_make(false, METAKOM_1_HI);
break;
case 5:
result = level_duration_make(true, METAKOM_0_LOW);
break;
case 6:
result = level_duration_make(false, METAKOM_0_HI);
break;
}
} else {
// data
uint8_t data_start_index = proto->encoder.index - 7;
bool clock_polarity = (data_start_index) % 2;
uint8_t bit_index = (data_start_index) / 2;
bool bit_value = (proto->data >> (32 - 1 - bit_index)) & 1;
if(!clock_polarity) {
if(bit_value) {
result = level_duration_make(true, METAKOM_1_LOW);
} else {
result = level_duration_make(true, METAKOM_0_LOW);
}
} else {
if(bit_value) {
result = level_duration_make(false, METAKOM_1_HI);
} else {
result = level_duration_make(false, METAKOM_0_HI);
}
}
}
proto->encoder.index++;
if(proto->encoder.index >= (1 + 3 * 2 + 32 * 2)) {
proto->encoder.index = 0;
}
return result;
}
const ProtocolBase protocol_metakom = {
.name = "Metakom",
.manufacturer = "Metakom",
.data_size = METAKOM_DATA_SIZE,
.alloc = (ProtocolAlloc)protocol_metakom_alloc,
.free = (ProtocolFree)protocol_metakom_free,
.get_data = (ProtocolGetData)protocol_metakom_get_data,
.decoder =
{
.start = (ProtocolDecoderStart)protocol_metakom_decoder_start,
.feed = (ProtocolDecoderFeed)protocol_metakom_decoder_feed,
},
.encoder =
{
.start = (ProtocolEncoderStart)protocol_metakom_encoder_start,
.yield = (ProtocolEncoderYield)protocol_metakom_encoder_yield,
},
};

View File

@@ -0,0 +1,4 @@
#pragma once
#include "toolbox/protocols/protocol.h"
extern const ProtocolBase protocol_metakom;

View File

@@ -1,256 +0,0 @@
#include "protocol_cyfral.h"
#include <stdlib.h>
#include <string.h>
#include <furi.h>
#include <furi_hal.h>
#define CYFRAL_DATA_SIZE 2
#define CYFRAL_MAX_PERIOD_US 230
typedef enum {
CYFRAL_BIT_WAIT_FRONT_HIGH,
CYFRAL_BIT_WAIT_FRONT_LOW,
} CyfralBitState;
typedef enum {
CYFRAL_WAIT_START_NIBBLE,
CYFRAL_READ_NIBBLE,
CYFRAL_READ_STOP_NIBBLE,
} CyfralState;
struct ProtocolCyfral {
PulseProtocol* protocol;
CyfralState state;
CyfralBitState bit_state;
// ready flag, key is read and valid
// TODO: atomic access
bool ready;
// key data storage
uint16_t key_data;
// high + low period time
uint32_t period_time;
// temporary nibble storage
uint8_t nibble;
// data valid flag
// MUST be checked only in READ_STOP_NIBBLE state
bool data_valid;
// nibble index, we expect 8 nibbles
uint8_t index;
// bit index in nibble, 4 bit per nibble
uint8_t bit_index;
// max period, 230us x clock per us
uint32_t max_period;
};
static void cyfral_pulse(void* context, bool polarity, uint32_t length);
static void cyfral_reset(void* context);
static void cyfral_get_data(void* context, uint8_t* data, size_t length);
static bool cyfral_decoded(void* context);
ProtocolCyfral* protocol_cyfral_alloc() {
ProtocolCyfral* cyfral = malloc(sizeof(ProtocolCyfral));
cyfral_reset(cyfral);
cyfral->protocol = pulse_protocol_alloc();
pulse_protocol_set_context(cyfral->protocol, cyfral);
pulse_protocol_set_pulse_cb(cyfral->protocol, cyfral_pulse);
pulse_protocol_set_reset_cb(cyfral->protocol, cyfral_reset);
pulse_protocol_set_get_data_cb(cyfral->protocol, cyfral_get_data);
pulse_protocol_set_decoded_cb(cyfral->protocol, cyfral_decoded);
return cyfral;
}
void protocol_cyfral_free(ProtocolCyfral* cyfral) {
furi_assert(cyfral);
pulse_protocol_free(cyfral->protocol);
free(cyfral);
}
PulseProtocol* protocol_cyfral_get_protocol(ProtocolCyfral* cyfral) {
furi_assert(cyfral);
return cyfral->protocol;
}
static void cyfral_get_data(void* context, uint8_t* data, size_t length) {
furi_assert(context);
furi_check(length >= CYFRAL_DATA_SIZE);
ProtocolCyfral* cyfral = context;
memcpy(data, &cyfral->key_data, CYFRAL_DATA_SIZE);
}
static bool cyfral_decoded(void* context) {
furi_assert(context);
ProtocolCyfral* cyfral = context;
bool decoded = cyfral->ready;
return decoded;
}
static void cyfral_reset(void* context) {
furi_assert(context);
ProtocolCyfral* cyfral = context;
cyfral->state = CYFRAL_WAIT_START_NIBBLE;
cyfral->bit_state = CYFRAL_BIT_WAIT_FRONT_LOW;
cyfral->period_time = 0;
cyfral->bit_index = 0;
cyfral->ready = false;
cyfral->index = 0;
cyfral->key_data = 0;
cyfral->nibble = 0;
cyfral->data_valid = true;
cyfral->max_period = CYFRAL_MAX_PERIOD_US * furi_hal_cortex_instructions_per_microsecond();
}
static bool cyfral_process_bit(
ProtocolCyfral* cyfral,
bool polarity,
uint32_t length,
bool* bit_ready,
bool* bit_value) {
bool result = true;
*bit_ready = false;
// bit start from low
switch(cyfral->bit_state) {
case CYFRAL_BIT_WAIT_FRONT_LOW:
if(polarity == true) {
cyfral->period_time += length;
*bit_ready = true;
if(cyfral->period_time <= cyfral->max_period) {
if((cyfral->period_time / 2) > length) {
*bit_value = false;
} else {
*bit_value = true;
}
} else {
result = false;
}
cyfral->bit_state = CYFRAL_BIT_WAIT_FRONT_HIGH;
} else {
result = false;
}
break;
case CYFRAL_BIT_WAIT_FRONT_HIGH:
if(polarity == false) {
cyfral->period_time = length;
cyfral->bit_state = CYFRAL_BIT_WAIT_FRONT_LOW;
} else {
result = false;
}
break;
}
return result;
}
static void cyfral_pulse(void* context, bool polarity, uint32_t length) {
furi_assert(context);
ProtocolCyfral* cyfral = context;
bool bit_ready;
bool bit_value;
if(cyfral->ready) return;
switch(cyfral->state) {
case CYFRAL_WAIT_START_NIBBLE:
// wait for start word
if(cyfral_process_bit(cyfral, polarity, length, &bit_ready, &bit_value)) {
if(bit_ready) {
cyfral->nibble = ((cyfral->nibble << 1) | bit_value) & 0x0F;
if(cyfral->nibble == 0b0001) {
cyfral->nibble = 0;
cyfral->state = CYFRAL_READ_NIBBLE;
}
}
} else {
cyfral_reset(cyfral);
}
break;
case CYFRAL_READ_NIBBLE:
// read nibbles
if(cyfral_process_bit(cyfral, polarity, length, &bit_ready, &bit_value)) {
if(bit_ready) {
cyfral->nibble = (cyfral->nibble << 1) | bit_value;
cyfral->bit_index++;
//convert every nibble to 2-bit index
if(cyfral->bit_index == 4) {
switch(cyfral->nibble) {
case 0b1110:
cyfral->key_data = (cyfral->key_data << 2) | 0b11;
break;
case 0b1101:
cyfral->key_data = (cyfral->key_data << 2) | 0b10;
break;
case 0b1011:
cyfral->key_data = (cyfral->key_data << 2) | 0b01;
break;
case 0b0111:
cyfral->key_data = (cyfral->key_data << 2) | 0b00;
break;
default:
cyfral->data_valid = false;
break;
}
cyfral->nibble = 0;
cyfral->bit_index = 0;
cyfral->index++;
}
// succefully read 8 nibbles
if(cyfral->index == 8) {
cyfral->state = CYFRAL_READ_STOP_NIBBLE;
}
}
} else {
cyfral_reset(cyfral);
}
break;
case CYFRAL_READ_STOP_NIBBLE:
// read stop nibble
if(cyfral_process_bit(cyfral, polarity, length, &bit_ready, &bit_value)) {
if(bit_ready) {
cyfral->nibble = ((cyfral->nibble << 1) | bit_value) & 0x0F;
cyfral->bit_index++;
switch(cyfral->bit_index) {
case 0:
case 1:
case 2:
case 3:
break;
case 4:
if(cyfral->nibble == 0b0001) {
// validate data
if(cyfral->data_valid) {
cyfral->ready = true;
} else {
cyfral_reset(cyfral);
}
} else {
cyfral_reset(cyfral);
}
break;
default:
cyfral_reset(cyfral);
break;
}
}
} else {
cyfral_reset(cyfral);
}
break;
}
}

View File

@@ -1,38 +0,0 @@
/**
* @file protocol_cyfral.h
*
* Cyfral pulse format decoder
*/
#pragma once
#include <stdint.h>
#include "../../pulse_protocols/pulse_protocol.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct ProtocolCyfral ProtocolCyfral;
/**
* Allocate decoder
* @return ProtocolCyfral*
*/
ProtocolCyfral* protocol_cyfral_alloc();
/**
* Deallocate decoder
* @param cyfral
*/
void protocol_cyfral_free(ProtocolCyfral* cyfral);
/**
* Get protocol interface
* @param cyfral
* @return PulseProtocol*
*/
PulseProtocol* protocol_cyfral_get_protocol(ProtocolCyfral* cyfral);
#ifdef __cplusplus
}
#endif

View File

@@ -1,38 +0,0 @@
/**
* @file protocol_metakom.h
*
* Metakom pulse format decoder
*/
#pragma once
#include <stdint.h>
#include "../../pulse_protocols/pulse_protocol.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct ProtocolMetakom ProtocolMetakom;
/**
* Allocate decoder
* @return ProtocolMetakom*
*/
ProtocolMetakom* protocol_metakom_alloc();
/**
* Free decoder
* @param metakom
*/
void protocol_metakom_free(ProtocolMetakom* metakom);
/**
* Get protocol interface
* @param metakom
* @return PulseProtocol*
*/
PulseProtocol* protocol_metakom_get_protocol(ProtocolMetakom* metakom);
#ifdef __cplusplus
}
#endif

View File

@@ -1,66 +0,0 @@
#include <stdlib.h>
#include "pulse_decoder.h"
#include <string.h>
#include <core/check.h>
#define MAX_PROTOCOL 5
struct PulseDecoder {
PulseProtocol* protocols[MAX_PROTOCOL];
};
PulseDecoder* pulse_decoder_alloc() {
PulseDecoder* decoder = malloc(sizeof(PulseDecoder));
memset(decoder, 0, sizeof(PulseDecoder));
return decoder;
}
void pulse_decoder_free(PulseDecoder* reader) {
furi_assert(reader);
free(reader);
}
void pulse_decoder_add_protocol(PulseDecoder* reader, PulseProtocol* protocol, int32_t index) {
furi_check(index < MAX_PROTOCOL);
furi_check(reader->protocols[index] == NULL);
reader->protocols[index] = protocol;
}
void pulse_decoder_process_pulse(PulseDecoder* reader, bool polarity, uint32_t length) {
furi_assert(reader);
for(size_t index = 0; index < MAX_PROTOCOL; index++) {
if(reader->protocols[index] != NULL) {
pulse_protocol_process_pulse(reader->protocols[index], polarity, length);
}
}
}
int32_t pulse_decoder_get_decoded_index(PulseDecoder* reader) {
furi_assert(reader);
int32_t decoded = -1;
for(size_t index = 0; index < MAX_PROTOCOL; index++) {
if(reader->protocols[index] != NULL) {
if(pulse_protocol_decoded(reader->protocols[index])) {
decoded = index;
break;
}
}
}
return decoded;
}
void pulse_decoder_reset(PulseDecoder* reader) {
furi_assert(reader);
for(size_t index = 0; index < MAX_PROTOCOL; index++) {
if(reader->protocols[index] != NULL) {
pulse_protocol_reset(reader->protocols[index]);
}
}
}
void pulse_decoder_get_data(PulseDecoder* reader, int32_t index, uint8_t* data, size_t length) {
furi_assert(reader);
furi_check(reader->protocols[index] != NULL);
pulse_protocol_get_data(reader->protocols[index], data, length);
}

View File

@@ -1,70 +0,0 @@
/**
* @file pulse_decoder.h
*
* Generic pulse protocol decoder library
*/
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include "pulse_protocol.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct PulseDecoder PulseDecoder;
/**
* Allocate decoder
* @return PulseDecoder*
*/
PulseDecoder* pulse_decoder_alloc();
/**
* Deallocate decoder
* @param decoder
*/
void pulse_decoder_free(PulseDecoder* decoder);
/**
* Add protocol to decoder
* @param decoder
* @param protocol protocol implementation
* @param index protocol index, should not be repeated
*/
void pulse_decoder_add_protocol(PulseDecoder* decoder, PulseProtocol* protocol, int32_t index);
/**
* Push and process pulse with decoder
* @param decoder
* @param polarity
* @param length
*/
void pulse_decoder_process_pulse(PulseDecoder* decoder, bool polarity, uint32_t length);
/**
* Get indec of decoded protocol
* @param decoder
* @return int32_t, -1 if nothing decoded, or index of decoded protocol
*/
int32_t pulse_decoder_get_decoded_index(PulseDecoder* decoder);
/**
* Reset all protocols in decoder
* @param decoder
*/
void pulse_decoder_reset(PulseDecoder* decoder);
/**
* Get decoded data from protocol
* @param decoder
* @param index
* @param data
* @param length
*/
void pulse_decoder_get_data(PulseDecoder* decoder, int32_t index, uint8_t* data, size_t length);
#ifdef __cplusplus
}
#endif

View File

@@ -1,55 +0,0 @@
#include "pulse_glue.h"
struct PulseGlue {
int32_t hi_period;
int32_t low_period;
int32_t next_hi_period;
};
PulseGlue* pulse_glue_alloc() {
PulseGlue* pulse_glue = malloc(sizeof(PulseGlue));
pulse_glue_reset(pulse_glue);
return pulse_glue;
}
void pulse_glue_free(PulseGlue* pulse_glue) {
free(pulse_glue);
}
void pulse_glue_reset(PulseGlue* pulse_glue) {
pulse_glue->hi_period = 0;
pulse_glue->low_period = 0;
pulse_glue->next_hi_period = 0;
}
bool pulse_glue_push(PulseGlue* pulse_glue, bool polarity, uint32_t length) {
bool pop_ready = false;
if(polarity) {
if(pulse_glue->low_period == 0) {
// stage 1, accumulate hi period
pulse_glue->hi_period += length;
} else {
// stage 3, accumulate next hi period and be ready for pulse_glue_pop
pulse_glue->next_hi_period = length;
// data is ready
pop_ready = true;
}
} else {
if(pulse_glue->hi_period != 0) {
// stage 2, accumulate low period
pulse_glue->low_period += length;
}
}
return pop_ready;
}
void pulse_glue_pop(PulseGlue* pulse_glue, uint32_t* length, uint32_t* period) {
*length = pulse_glue->hi_period + pulse_glue->low_period;
*period = pulse_glue->hi_period;
pulse_glue->hi_period = pulse_glue->next_hi_period;
pulse_glue->low_period = 0;
pulse_glue->next_hi_period = 0;
}

View File

@@ -1,26 +0,0 @@
/**
* @file pulse_glue.h
*
* Simple tool to glue separated pulses to corret
*/
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct PulseGlue PulseGlue;
PulseGlue* pulse_glue_alloc();
void pulse_glue_free(PulseGlue* pulse_glue);
void pulse_glue_reset(PulseGlue* pulse_glue);
bool pulse_glue_push(PulseGlue* pulse_glue, bool polarity, uint32_t length);
void pulse_glue_pop(PulseGlue* pulse_glue, uint32_t* length, uint32_t* period);
#ifdef __cplusplus
}
#endif

View File

@@ -1,67 +0,0 @@
#include "pulse_protocol.h"
#include <stdlib.h>
#include <string.h>
struct PulseProtocol {
void* context;
PulseProtocolPulseCallback pulse_cb;
PulseProtocolResetCallback reset_cb;
PulseProtocolGetDataCallback get_data_cb;
PulseProtocolDecodedCallback decoded_cb;
};
PulseProtocol* pulse_protocol_alloc() {
PulseProtocol* protocol = malloc(sizeof(PulseProtocol));
memset(protocol, 0, sizeof(PulseProtocol));
return protocol;
}
void pulse_protocol_set_context(PulseProtocol* protocol, void* context) {
protocol->context = context;
}
void pulse_protocol_set_pulse_cb(PulseProtocol* protocol, PulseProtocolPulseCallback callback) {
protocol->pulse_cb = callback;
}
void pulse_protocol_set_reset_cb(PulseProtocol* protocol, PulseProtocolResetCallback callback) {
protocol->reset_cb = callback;
}
void pulse_protocol_set_get_data_cb(PulseProtocol* protocol, PulseProtocolGetDataCallback callback) {
protocol->get_data_cb = callback;
}
void pulse_protocol_set_decoded_cb(PulseProtocol* protocol, PulseProtocolDecodedCallback callback) {
protocol->decoded_cb = callback;
}
void pulse_protocol_free(PulseProtocol* protocol) {
free(protocol);
}
void pulse_protocol_process_pulse(PulseProtocol* protocol, bool polarity, uint32_t length) {
if(protocol->pulse_cb != NULL) {
protocol->pulse_cb(protocol->context, polarity, length);
}
}
void pulse_protocol_reset(PulseProtocol* protocol) {
if(protocol->reset_cb != NULL) {
protocol->reset_cb(protocol->context);
}
}
bool pulse_protocol_decoded(PulseProtocol* protocol) {
bool result = false;
if(protocol->decoded_cb != NULL) {
result = protocol->decoded_cb(protocol->context);
}
return result;
}
void pulse_protocol_get_data(PulseProtocol* protocol, uint8_t* data, size_t length) {
if(protocol->get_data_cb != NULL) {
protocol->get_data_cb(protocol->context, data, length);
}
}

View File

@@ -1,122 +0,0 @@
/**
* @file pulse_protocol.h
*
* Generic pulse protocol decoder library, protocol interface
*/
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* Anonymous PulseProtocol struct
*/
typedef struct PulseProtocol PulseProtocol;
/**
* Process pulse callback
*/
typedef void (*PulseProtocolPulseCallback)(void* context, bool polarity, uint32_t length);
/**
* Reset protocol callback
*/
typedef void (*PulseProtocolResetCallback)(void* context);
/**
* Get decoded data callback
*/
typedef void (*PulseProtocolGetDataCallback)(void* context, uint8_t* data, size_t length);
/**
* Is protocol decoded callback
*/
typedef bool (*PulseProtocolDecodedCallback)(void* context);
/**
* Allocate protocol
* @return PulseProtocol*
*/
PulseProtocol* pulse_protocol_alloc();
/**
* Deallocate protocol
* @param protocol
*/
void pulse_protocol_free(PulseProtocol* protocol);
/**
* Set context for callbacks
* @param protocol
* @param context
*/
void pulse_protocol_set_context(PulseProtocol* protocol, void* context);
/**
* Set "Process pulse" callback. Called from the decoder when a new pulse is received.
* @param protocol
* @param callback
*/
void pulse_protocol_set_pulse_cb(PulseProtocol* protocol, PulseProtocolPulseCallback callback);
/**
* Set "Reset protocol" callback. Called from the decoder when the decoder is reset.
* @param protocol
* @param callback
*/
void pulse_protocol_set_reset_cb(PulseProtocol* protocol, PulseProtocolResetCallback callback);
/**
* Set "Get decoded data" callback. Called from the decoder when the decoder wants to get decoded data.
* @param protocol
* @param callback
*/
void pulse_protocol_set_get_data_cb(PulseProtocol* protocol, PulseProtocolGetDataCallback callback);
/**
* Set "Is protocol decoded" callback. Called from the decoder when the decoder wants to know if a protocol has been decoded.
* @param protocol
* @param callback
*/
void pulse_protocol_set_decoded_cb(PulseProtocol* protocol, PulseProtocolDecodedCallback callback);
/**
* Part of decoder interface.
* @param protocol
* @param polarity
* @param length
*/
void pulse_protocol_process_pulse(PulseProtocol* protocol, bool polarity, uint32_t length);
/**
* Part of decoder interface.
* @param protocol
* @return true
* @return false
*/
bool pulse_protocol_decoded(PulseProtocol* protocol);
/**
* Part of decoder interface.
* @param protocol
* @return true
* @return false
*/
void pulse_protocol_get_data(PulseProtocol* protocol, uint8_t* data, size_t length);
/**
* Part of decoder interface.
* @param protocol
* @return true
* @return false
*/
void pulse_protocol_reset(PulseProtocol* protocol);
#ifdef __cplusplus
}
#endif