diff --git a/applications/archive/helpers/archive_apps.c b/applications/archive/helpers/archive_apps.c index 9a3f825f..72084f11 100644 --- a/applications/archive/helpers/archive_apps.c +++ b/applications/archive/helpers/archive_apps.c @@ -29,23 +29,13 @@ bool archive_app_is_available(void* context, const char* path) { if(app == ArchiveAppTypeU2f) { bool file_exists = false; - Storage* fs_api = furi_record_open(RECORD_STORAGE); - File* file = storage_file_alloc(fs_api); + Storage* storage = furi_record_open(RECORD_STORAGE); - file_exists = - storage_file_open(file, ANY_PATH("u2f/key.u2f"), FSAM_READ, FSOM_OPEN_EXISTING); - if(file_exists) { - storage_file_close(file); - file_exists = - storage_file_open(file, ANY_PATH("u2f/cnt.u2f"), FSAM_READ, FSOM_OPEN_EXISTING); - if(file_exists) { - storage_file_close(file); - } + if(storage_file_exists(storage, ANY_PATH("u2f/key.u2f"))) { + file_exists = storage_file_exists(storage, ANY_PATH("u2f/cnt.u2f")); } - storage_file_free(file); furi_record_close(RECORD_STORAGE); - return file_exists; } else { return false; diff --git a/applications/archive/helpers/archive_favorites.c b/applications/archive/helpers/archive_favorites.c index 35199242..4d5b555f 100644 --- a/applications/archive/helpers/archive_favorites.c +++ b/applications/archive/helpers/archive_favorites.c @@ -82,9 +82,8 @@ uint16_t archive_favorites_count(void* context) { static bool archive_favourites_rescan() { string_t buffer; string_init(buffer); - Storage* fs_api = furi_record_open(RECORD_STORAGE); - File* file = storage_file_alloc(fs_api); - File* fav_item_file = storage_file_alloc(fs_api); + Storage* storage = furi_record_open(RECORD_STORAGE); + File* file = storage_file_alloc(storage); bool result = storage_file_open(file, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING); if(result) { @@ -101,13 +100,8 @@ static bool archive_favourites_rescan() { archive_file_append(ARCHIVE_FAV_TEMP_PATH, "%s\n", string_get_cstr(buffer)); } } else { - bool file_exists = storage_file_open( - fav_item_file, string_get_cstr(buffer), FSAM_READ, FSOM_OPEN_EXISTING); - if(file_exists) { - storage_file_close(fav_item_file); + if(storage_file_exists(storage, string_get_cstr(buffer))) { archive_file_append(ARCHIVE_FAV_TEMP_PATH, "%s\n", string_get_cstr(buffer)); - } else { - storage_file_close(fav_item_file); } } } @@ -116,12 +110,11 @@ static bool archive_favourites_rescan() { string_clear(buffer); storage_file_close(file); - storage_common_remove(fs_api, ARCHIVE_FAV_PATH); - storage_common_rename(fs_api, ARCHIVE_FAV_TEMP_PATH, ARCHIVE_FAV_PATH); - storage_common_remove(fs_api, ARCHIVE_FAV_TEMP_PATH); + storage_common_remove(storage, ARCHIVE_FAV_PATH); + storage_common_rename(storage, ARCHIVE_FAV_TEMP_PATH, ARCHIVE_FAV_PATH); + storage_common_remove(storage, ARCHIVE_FAV_TEMP_PATH); storage_file_free(file); - storage_file_free(fav_item_file); furi_record_close(RECORD_STORAGE); return result; @@ -131,9 +124,8 @@ bool archive_favorites_read(void* context) { furi_assert(context); ArchiveBrowserView* browser = context; - Storage* fs_api = furi_record_open(RECORD_STORAGE); - File* file = storage_file_alloc(fs_api); - File* fav_item_file = storage_file_alloc(fs_api); + Storage* storage = furi_record_open(RECORD_STORAGE); + File* file = storage_file_alloc(storage); string_t buffer; FileInfo file_info; @@ -163,16 +155,12 @@ bool archive_favorites_read(void* context) { need_refresh = true; } } else { - bool file_exists = storage_file_open( - fav_item_file, string_get_cstr(buffer), FSAM_READ, FSOM_OPEN_EXISTING); - if(file_exists) { - storage_common_stat(fs_api, string_get_cstr(buffer), &file_info); - storage_file_close(fav_item_file); + if(storage_file_exists(storage, string_get_cstr(buffer))) { + storage_common_stat(storage, string_get_cstr(buffer), &file_info); archive_add_file_item( browser, (file_info.flags & FSF_DIRECTORY), string_get_cstr(buffer)); file_count++; } else { - storage_file_close(fav_item_file); need_refresh = true; } } @@ -183,7 +171,6 @@ bool archive_favorites_read(void* context) { storage_file_close(file); string_clear(buffer); storage_file_free(file); - storage_file_free(fav_item_file); furi_record_close(RECORD_STORAGE); archive_set_item_count(browser, file_count); diff --git a/applications/desktop/animations/animation_manager.c b/applications/desktop/animations/animation_manager.c index d755be9c..1e2a521e 100644 --- a/applications/desktop/animations/animation_manager.c +++ b/applications/desktop/animations/animation_manager.c @@ -220,8 +220,7 @@ static bool animation_manager_check_blocking(AnimationManager* animation_manager furi_assert(blocking_animation); animation_manager->sd_shown_sd_ok = true; } else if(!animation_manager->sd_shown_no_db) { - bool db_exists = storage_common_stat(storage, EXT_PATH("Manifest"), NULL) == FSE_OK; - if(!db_exists) { + if(!storage_file_exists(storage, EXT_PATH("Manifest"))) { blocking_animation = animation_storage_find_animation(NO_DB_ANIMATION_NAME); furi_assert(blocking_animation); animation_manager->sd_shown_no_db = true; diff --git a/applications/lfrfid/helpers/decoder_analyzer.cpp b/applications/lfrfid/helpers/decoder_analyzer.cpp deleted file mode 100644 index 8d344b88..00000000 --- a/applications/lfrfid/helpers/decoder_analyzer.cpp +++ /dev/null @@ -1,50 +0,0 @@ -#include "decoder_analyzer.h" -#include -#include - -// FIXME: unused args? -bool DecoderAnalyzer::read(uint8_t* /* _data */, uint8_t /* _data_size */) { - bool result = false; - - if(ready) { - result = true; - - for(size_t i = 0; i < data_size; i++) { - printf("%lu ", data[i]); - if((i + 1) % 8 == 0) printf("\r\n"); - } - printf("\r\n--------\r\n"); - - ready = false; - } - - return result; -} - -void DecoderAnalyzer::process_front(bool polarity, uint32_t time) { - UNUSED(polarity); - if(ready) return; - - data[data_index] = time; - - if(data_index < data_size) { - data_index++; - } else { - data_index = 0; - ready = true; - } -} - -DecoderAnalyzer::DecoderAnalyzer() { - data = reinterpret_cast(calloc(data_size, sizeof(uint32_t))); - furi_check(data); - data_index = 0; - ready = false; -} - -DecoderAnalyzer::~DecoderAnalyzer() { - free(data); -} - -void DecoderAnalyzer::reset_state() { -} diff --git a/applications/lfrfid/helpers/decoder_analyzer.h b/applications/lfrfid/helpers/decoder_analyzer.h deleted file mode 100644 index eecea6ed..00000000 --- a/applications/lfrfid/helpers/decoder_analyzer.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once -#include -#include - -class DecoderAnalyzer { -public: - bool read(uint8_t* data, uint8_t data_size); - void process_front(bool polarity, uint32_t time); - - DecoderAnalyzer(); - ~DecoderAnalyzer(); - -private: - void reset_state(); - - std::atomic ready; - - static const uint32_t data_size = 2048; - uint32_t data_index = 0; - uint32_t* data; -}; diff --git a/applications/lfrfid/helpers/decoder_emmarin.cpp b/applications/lfrfid/helpers/decoder_emmarin.cpp deleted file mode 100644 index daa8e238..00000000 --- a/applications/lfrfid/helpers/decoder_emmarin.cpp +++ /dev/null @@ -1,72 +0,0 @@ -#include "emmarin.h" -#include "decoder_emmarin.h" -#include -#include - -constexpr uint32_t clocks_in_us = 64; -constexpr uint32_t short_time = 255 * clocks_in_us; -constexpr uint32_t long_time = 510 * clocks_in_us; -constexpr uint32_t jitter_time = 100 * clocks_in_us; - -constexpr uint32_t short_time_low = short_time - jitter_time; -constexpr uint32_t short_time_high = short_time + jitter_time; -constexpr uint32_t long_time_low = long_time - jitter_time; -constexpr uint32_t long_time_high = long_time + jitter_time; - -void DecoderEMMarin::reset_state() { - ready = false; - read_data = 0; - manchester_advance( - manchester_saved_state, ManchesterEventReset, &manchester_saved_state, nullptr); -} - -bool DecoderEMMarin::read(uint8_t* data, uint8_t data_size) { - bool result = false; - - if(ready) { - result = true; - em_marin.decode( - reinterpret_cast(&read_data), sizeof(uint64_t), data, data_size); - ready = false; - } - - return result; -} - -void DecoderEMMarin::process_front(bool polarity, uint32_t time) { - if(ready) return; - if(time < short_time_low) return; - - ManchesterEvent event = ManchesterEventReset; - - if(time > short_time_low && time < short_time_high) { - if(polarity) { - event = ManchesterEventShortHigh; - } else { - event = ManchesterEventShortLow; - } - } else if(time > long_time_low && time < long_time_high) { - if(polarity) { - event = ManchesterEventLongHigh; - } else { - event = ManchesterEventLongLow; - } - } - - if(event != ManchesterEventReset) { - bool data; - bool data_ok = - manchester_advance(manchester_saved_state, event, &manchester_saved_state, &data); - - if(data_ok) { - read_data = (read_data << 1) | data; - - ready = em_marin.can_be_decoded( - reinterpret_cast(&read_data), sizeof(uint64_t)); - } - } -} - -DecoderEMMarin::DecoderEMMarin() { - reset_state(); -} diff --git a/applications/lfrfid/helpers/decoder_emmarin.h b/applications/lfrfid/helpers/decoder_emmarin.h deleted file mode 100644 index a3b48d71..00000000 --- a/applications/lfrfid/helpers/decoder_emmarin.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once -#include -#include -#include -#include "protocols/protocol_emmarin.h" -class DecoderEMMarin { -public: - bool read(uint8_t* data, uint8_t data_size); - void process_front(bool polarity, uint32_t time); - - DecoderEMMarin(); - -private: - void reset_state(); - - uint64_t read_data = 0; - std::atomic ready; - - ManchesterState manchester_saved_state; - ProtocolEMMarin em_marin; -}; diff --git a/applications/lfrfid/helpers/decoder_gpio_out.cpp b/applications/lfrfid/helpers/decoder_gpio_out.cpp deleted file mode 100644 index dfb43426..00000000 --- a/applications/lfrfid/helpers/decoder_gpio_out.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include "decoder_gpio_out.h" -#include -#include - -void DecoderGpioOut::process_front(bool polarity, uint32_t /* time */) { - furi_hal_gpio_write(&gpio_ext_pa7, polarity); -} - -DecoderGpioOut::DecoderGpioOut() { - furi_hal_gpio_init_simple(&gpio_ext_pa7, GpioModeOutputPushPull); -} - -DecoderGpioOut::~DecoderGpioOut() { - furi_hal_gpio_init_simple(&gpio_ext_pa7, GpioModeAnalog); -} diff --git a/applications/lfrfid/helpers/decoder_gpio_out.h b/applications/lfrfid/helpers/decoder_gpio_out.h deleted file mode 100644 index 087720df..00000000 --- a/applications/lfrfid/helpers/decoder_gpio_out.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once -#include -#include - -class DecoderGpioOut { -public: - void process_front(bool polarity, uint32_t time); - - DecoderGpioOut(); - ~DecoderGpioOut(); - -private: - void reset_state(); -}; diff --git a/applications/lfrfid/helpers/decoder_hid26.cpp b/applications/lfrfid/helpers/decoder_hid26.cpp deleted file mode 100644 index e530b508..00000000 --- a/applications/lfrfid/helpers/decoder_hid26.cpp +++ /dev/null @@ -1,98 +0,0 @@ -#include "decoder_hid26.h" -#include - -constexpr uint32_t clocks_in_us = 64; - -constexpr uint32_t jitter_time_us = 20; -constexpr uint32_t min_time_us = 64; -constexpr uint32_t max_time_us = 80; - -constexpr uint32_t min_time = (min_time_us - jitter_time_us) * clocks_in_us; -constexpr uint32_t mid_time = ((max_time_us - min_time_us) / 2 + min_time_us) * clocks_in_us; -constexpr uint32_t max_time = (max_time_us + jitter_time_us) * clocks_in_us; - -bool DecoderHID26::read(uint8_t* data, uint8_t data_size) { - bool result = false; - furi_assert(data_size >= 3); - - if(ready) { - result = true; - hid.decode( - reinterpret_cast(&stored_data), sizeof(uint32_t) * 3, data, data_size); - ready = false; - } - - return result; -} - -void DecoderHID26::process_front(bool polarity, uint32_t time) { - if(ready) return; - - if(polarity == true) { - last_pulse_time = time; - } else { - last_pulse_time += time; - - if(last_pulse_time > min_time && last_pulse_time < max_time) { - bool pulse; - - if(last_pulse_time < mid_time) { - // 6 pulses - pulse = false; - } else { - // 5 pulses - pulse = true; - } - - if(last_pulse == pulse) { - pulse_count++; - - if(pulse) { - if(pulse_count > 4) { - pulse_count = 0; - store_data(1); - } - } else { - if(pulse_count > 5) { - pulse_count = 0; - store_data(0); - } - } - } else { - if(last_pulse) { - if(pulse_count > 2) { - store_data(1); - } - } else { - if(pulse_count > 3) { - store_data(0); - } - } - - pulse_count = 0; - last_pulse = pulse; - } - } - } -} - -DecoderHID26::DecoderHID26() { - reset_state(); -} - -void DecoderHID26::store_data(bool data) { - stored_data[0] = (stored_data[0] << 1) | ((stored_data[1] >> 31) & 1); - stored_data[1] = (stored_data[1] << 1) | ((stored_data[2] >> 31) & 1); - stored_data[2] = (stored_data[2] << 1) | data; - - if(hid.can_be_decoded(reinterpret_cast(&stored_data), sizeof(uint32_t) * 3)) { - ready = true; - } -} - -void DecoderHID26::reset_state() { - last_pulse = false; - pulse_count = 0; - ready = false; - last_pulse_time = 0; -} diff --git a/applications/lfrfid/helpers/decoder_hid26.h b/applications/lfrfid/helpers/decoder_hid26.h deleted file mode 100644 index c73ff88c..00000000 --- a/applications/lfrfid/helpers/decoder_hid26.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once -#include -#include -#include "protocols/protocol_hid_h10301.h" - -class DecoderHID26 { -public: - bool read(uint8_t* data, uint8_t data_size); - void process_front(bool polarity, uint32_t time); - DecoderHID26(); - -private: - uint32_t last_pulse_time = 0; - bool last_pulse; - uint8_t pulse_count; - - uint32_t stored_data[3] = {0, 0, 0}; - void store_data(bool data); - - std::atomic ready; - - void reset_state(); - ProtocolHID10301 hid; -}; diff --git a/applications/lfrfid/helpers/decoder_indala.cpp b/applications/lfrfid/helpers/decoder_indala.cpp deleted file mode 100644 index 100dde73..00000000 --- a/applications/lfrfid/helpers/decoder_indala.cpp +++ /dev/null @@ -1,76 +0,0 @@ -#include "decoder_indala.h" -#include - -constexpr uint32_t clocks_in_us = 64; -constexpr uint32_t us_per_bit = 255; - -bool DecoderIndala::read(uint8_t* data, uint8_t data_size) { - bool result = false; - - if(ready) { - result = true; - if(cursed_data_valid) { - indala.decode( - reinterpret_cast(&cursed_raw_data), - sizeof(uint64_t), - data, - data_size); - } else { - indala.decode( - reinterpret_cast(&raw_data), sizeof(uint64_t), data, data_size); - } - reset_state(); - } - - return result; -} - -void DecoderIndala::process_front(bool polarity, uint32_t time) { - if(ready) return; - - process_internal(polarity, time, &raw_data); - if(ready) return; - - if(polarity) { - time = time + 110; - } else { - time = time - 110; - } - - process_internal(!polarity, time, &cursed_raw_data); - if(ready) { - cursed_data_valid = true; - } -} - -void DecoderIndala::process_internal(bool polarity, uint32_t time, uint64_t* data) { - time /= clocks_in_us; - time += (us_per_bit / 2); - - uint32_t bit_count = (time / us_per_bit); - - if(bit_count < 64) { - for(uint32_t i = 0; i < bit_count; i++) { - *data = (*data << 1) | polarity; - - if((*data >> 32) == 0xa0000000ULL) { - if(indala.can_be_decoded( - reinterpret_cast(data), sizeof(uint64_t))) { - ready = true; - break; - } - } - } - } -} - -DecoderIndala::DecoderIndala() { - reset_state(); -} - -void DecoderIndala::reset_state() { - raw_data = 0; - cursed_raw_data = 0; - ready = false; - cursed_data_valid = false; -} diff --git a/applications/lfrfid/helpers/decoder_indala.h b/applications/lfrfid/helpers/decoder_indala.h deleted file mode 100644 index 5fbde7b6..00000000 --- a/applications/lfrfid/helpers/decoder_indala.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once -#include -#include -#include -#include "protocols/protocol_indala_40134.h" - -class DecoderIndala { -public: - bool read(uint8_t* data, uint8_t data_size); - void process_front(bool polarity, uint32_t time); - - void process_internal(bool polarity, uint32_t time, uint64_t* data); - - DecoderIndala(); - -private: - void reset_state(); - - uint64_t raw_data; - uint64_t cursed_raw_data; - - std::atomic ready; - std::atomic cursed_data_valid; - ProtocolIndala40134 indala; -}; diff --git a/applications/lfrfid/helpers/decoder_ioprox.cpp b/applications/lfrfid/helpers/decoder_ioprox.cpp deleted file mode 100644 index 7b44d3ce..00000000 --- a/applications/lfrfid/helpers/decoder_ioprox.cpp +++ /dev/null @@ -1,107 +0,0 @@ -#include "decoder_ioprox.h" -#include -#include -#include - -constexpr uint32_t clocks_in_us = 64; - -constexpr uint32_t jitter_time_us = 20; -constexpr uint32_t min_time_us = 64; -constexpr uint32_t max_time_us = 80; -constexpr uint32_t baud_time_us = 500; - -constexpr uint32_t min_time = (min_time_us - jitter_time_us) * clocks_in_us; -constexpr uint32_t mid_time = ((max_time_us - min_time_us) / 2 + min_time_us) * clocks_in_us; -constexpr uint32_t max_time = (max_time_us + jitter_time_us) * clocks_in_us; -constexpr uint32_t baud_time = baud_time_us * clocks_in_us; - -bool DecoderIoProx::read(uint8_t* data, uint8_t data_size) { - bool result = false; - furi_assert(data_size >= 4); - - if(ready) { - result = true; - ioprox.decode(raw_data, sizeof(raw_data), data, data_size); - ready = false; - } - - return result; -} - -void DecoderIoProx::process_front(bool is_rising_edge, uint32_t time) { - if(ready) { - return; - } - - // Always track the time that's gone by. - current_period_duration += time; - demodulation_sample_duration += time; - - // If a baud time has elapsed, we're at a sample point. - if(demodulation_sample_duration >= baud_time) { - // Start a new baud period... - demodulation_sample_duration = 0; - demodulated_value_invalid = false; - - // ... and if we didn't have any baud errors, capture a sample. - if(!demodulated_value_invalid) { - store_data(current_demodulated_value); - } - } - - // - // FSK demodulator. - // - - // If this isn't a rising edge, this isn't a pulse of interest. - // We're done. - if(!is_rising_edge) { - return; - } - - bool is_valid_low = (current_period_duration > min_time) && - (current_period_duration <= mid_time); - bool is_valid_high = (current_period_duration > mid_time) && - (current_period_duration < max_time); - - // If this is between the minimum and our threshold, this is a logical 0. - if(is_valid_low) { - current_demodulated_value = false; - } - // Otherwise, if between our threshold and the max time, it's a logical 1. - else if(is_valid_high) { - current_demodulated_value = true; - } - // Otherwise, invalidate this sample. - else { - demodulated_value_invalid = true; - } - - // We're starting a new period; track that. - current_period_duration = 0; -} - -DecoderIoProx::DecoderIoProx() { - reset_state(); -} - -void DecoderIoProx::store_data(bool data) { - for(int i = 0; i < 7; ++i) { - raw_data[i] = (raw_data[i] << 1) | ((raw_data[i + 1] >> 7) & 1); - } - raw_data[7] = (raw_data[7] << 1) | data; - - if(ioprox.can_be_decoded(raw_data, sizeof(raw_data))) { - ready = true; - } -} - -void DecoderIoProx::reset_state() { - current_demodulated_value = false; - demodulated_value_invalid = false; - - current_period_duration = 0; - demodulation_sample_duration = 0; - - ready = false; -} diff --git a/applications/lfrfid/helpers/decoder_ioprox.h b/applications/lfrfid/helpers/decoder_ioprox.h deleted file mode 100644 index aff4a477..00000000 --- a/applications/lfrfid/helpers/decoder_ioprox.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once -#include -#include -#include "protocols/protocol_ioprox.h" - -class DecoderIoProx { -public: - bool read(uint8_t* data, uint8_t data_size); - void process_front(bool polarity, uint32_t time); - DecoderIoProx(); - -private: - uint32_t current_period_duration = 0; - uint32_t demodulation_sample_duration = 0; - - bool current_demodulated_value = false; - bool demodulated_value_invalid = false; - - uint8_t raw_data[8] = {0}; - void store_data(bool data); - - std::atomic ready; - - void reset_state(); - ProtocolIoProx ioprox; -}; diff --git a/applications/lfrfid/helpers/emmarin.h b/applications/lfrfid/helpers/emmarin.h deleted file mode 100644 index ff8273bf..00000000 --- a/applications/lfrfid/helpers/emmarin.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once -#include - -#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_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) diff --git a/applications/lfrfid/helpers/encoder_emmarin.cpp b/applications/lfrfid/helpers/encoder_emmarin.cpp deleted file mode 100644 index c329ab40..00000000 --- a/applications/lfrfid/helpers/encoder_emmarin.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include "encoder_emmarin.h" -#include "protocols/protocol_emmarin.h" -#include - -void EncoderEM::init(const uint8_t* data, const uint8_t data_size) { - ProtocolEMMarin em_marin; - em_marin.encode(data, data_size, reinterpret_cast(&card_data), sizeof(uint64_t)); - - card_data_index = 0; -} - -// data transmitted as manchester encoding -// 0 - high2low -// 1 - low2high -void EncoderEM::get_next(bool* polarity, uint16_t* period, uint16_t* pulse) { - *period = clocks_per_bit; - *pulse = clocks_per_bit / 2; - *polarity = (card_data >> (63 - card_data_index)) & 1; - - card_data_index++; - if(card_data_index >= 64) { - card_data_index = 0; - } -} diff --git a/applications/lfrfid/helpers/encoder_emmarin.h b/applications/lfrfid/helpers/encoder_emmarin.h deleted file mode 100644 index 560daec1..00000000 --- a/applications/lfrfid/helpers/encoder_emmarin.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once -#include "encoder_generic.h" - -class EncoderEM : public EncoderGeneric { -public: - /** - * @brief init data to emulate - * - * @param data 1 byte FC, next 4 byte SN - * @param data_size must be 5 - */ - void init(const uint8_t* data, const uint8_t data_size) final; - - void get_next(bool* polarity, uint16_t* period, uint16_t* pulse) final; - -private: - // clock pulses per bit - static const uint8_t clocks_per_bit = 64; - - uint64_t card_data; - uint8_t card_data_index; -}; diff --git a/applications/lfrfid/helpers/encoder_generic.h b/applications/lfrfid/helpers/encoder_generic.h deleted file mode 100644 index dc6ae850..00000000 --- a/applications/lfrfid/helpers/encoder_generic.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once -#include -#include - -class EncoderGeneric { -public: - /** - * @brief init encoder - * - * @param data data array - * @param data_size data array size - */ - virtual void init(const uint8_t* data, const uint8_t data_size) = 0; - - /** - * @brief Get the next timer pulse - * - * @param polarity pulse polarity true = high2low, false = low2high - * @param period overall period time in timer clicks - * @param pulse pulse time in timer clicks - */ - virtual void get_next(bool* polarity, uint16_t* period, uint16_t* pulse) = 0; - - virtual ~EncoderGeneric(){}; - -private: -}; diff --git a/applications/lfrfid/helpers/encoder_hid_h10301.cpp b/applications/lfrfid/helpers/encoder_hid_h10301.cpp deleted file mode 100644 index 09f637fe..00000000 --- a/applications/lfrfid/helpers/encoder_hid_h10301.cpp +++ /dev/null @@ -1,46 +0,0 @@ -#include "encoder_hid_h10301.h" -#include "protocols/protocol_hid_h10301.h" -#include - -void EncoderHID_H10301::init(const uint8_t* data, const uint8_t data_size) { - ProtocolHID10301 hid; - hid.encode(data, data_size, reinterpret_cast(&card_data), sizeof(card_data) * 3); - - card_data_index = 0; -} - -void EncoderHID_H10301::write_bit(bool bit, uint8_t position) { - write_raw_bit(bit, position + 0); - write_raw_bit(!bit, position + 1); -} - -void EncoderHID_H10301::write_raw_bit(bool bit, uint8_t position) { - if(bit) { - card_data[position / 32] |= 1UL << (31 - (position % 32)); - } else { - card_data[position / 32] &= ~(1UL << (31 - (position % 32))); - } -} - -void EncoderHID_H10301::get_next(bool* polarity, uint16_t* period, uint16_t* pulse) { - uint8_t bit = (card_data[card_data_index / 32] >> (31 - (card_data_index % 32))) & 1; - - bool advance = fsk->next(bit, period); - if(advance) { - card_data_index++; - if(card_data_index >= (32 * card_data_max)) { - card_data_index = 0; - } - } - - *polarity = true; - *pulse = *period / 2; -} - -EncoderHID_H10301::EncoderHID_H10301() { - fsk = new OscFSK(8, 10, 50); -} - -EncoderHID_H10301::~EncoderHID_H10301() { - delete fsk; -} diff --git a/applications/lfrfid/helpers/encoder_hid_h10301.h b/applications/lfrfid/helpers/encoder_hid_h10301.h deleted file mode 100644 index 8cc5aa5c..00000000 --- a/applications/lfrfid/helpers/encoder_hid_h10301.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once -#include "encoder_generic.h" -#include "osc_fsk.h" - -class EncoderHID_H10301 : public EncoderGeneric { -public: - /** - * @brief init data to emulate - * - * @param data 1 byte FC, next 2 byte SN - * @param data_size must be 3 - */ - void init(const uint8_t* data, const uint8_t data_size) final; - void get_next(bool* polarity, uint16_t* period, uint16_t* pulse) final; - EncoderHID_H10301(); - ~EncoderHID_H10301(); - -private: - static const uint8_t card_data_max = 3; - uint32_t card_data[card_data_max]; - uint8_t card_data_index; - void write_bit(bool bit, uint8_t position); - void write_raw_bit(bool bit, uint8_t position); - - OscFSK* fsk; -}; diff --git a/applications/lfrfid/helpers/encoder_indala_40134.cpp b/applications/lfrfid/helpers/encoder_indala_40134.cpp deleted file mode 100644 index 764237d1..00000000 --- a/applications/lfrfid/helpers/encoder_indala_40134.cpp +++ /dev/null @@ -1,36 +0,0 @@ -#include "encoder_indala_40134.h" -#include "protocols/protocol_indala_40134.h" -#include - -void EncoderIndala_40134::init(const uint8_t* data, const uint8_t data_size) { - ProtocolIndala40134 indala; - indala.encode(data, data_size, reinterpret_cast(&card_data), sizeof(card_data)); - - last_bit = card_data & 1; - card_data_index = 0; - current_polarity = true; -} - -void EncoderIndala_40134::get_next(bool* polarity, uint16_t* period, uint16_t* pulse) { - *period = 2; - *pulse = 1; - *polarity = current_polarity; - - bit_clock_index++; - if(bit_clock_index >= clock_per_bit) { - bit_clock_index = 0; - - bool current_bit = (card_data >> (63 - card_data_index)) & 1; - - if(current_bit != last_bit) { - current_polarity = !current_polarity; - } - - last_bit = current_bit; - - card_data_index++; - if(card_data_index >= 64) { - card_data_index = 0; - } - } -} diff --git a/applications/lfrfid/helpers/encoder_indala_40134.h b/applications/lfrfid/helpers/encoder_indala_40134.h deleted file mode 100644 index eda29457..00000000 --- a/applications/lfrfid/helpers/encoder_indala_40134.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once -#include "encoder_generic.h" - -class EncoderIndala_40134 : public EncoderGeneric { -public: - /** - * @brief init data to emulate - * - * @param data indala raw data - * @param data_size must be 5 - */ - void init(const uint8_t* data, const uint8_t data_size) final; - - void get_next(bool* polarity, uint16_t* period, uint16_t* pulse) final; - -private: - uint64_t card_data; - uint8_t card_data_index; - uint8_t bit_clock_index; - bool last_bit; - bool current_polarity; - static const uint8_t clock_per_bit = 16; -}; diff --git a/applications/lfrfid/helpers/encoder_ioprox.cpp b/applications/lfrfid/helpers/encoder_ioprox.cpp deleted file mode 100644 index 41779918..00000000 --- a/applications/lfrfid/helpers/encoder_ioprox.cpp +++ /dev/null @@ -1,32 +0,0 @@ -#include "encoder_ioprox.h" -#include "protocols/protocol_ioprox.h" -#include - -void EncoderIoProx::init(const uint8_t* data, const uint8_t data_size) { - ProtocolIoProx ioprox; - ioprox.encode(data, data_size, card_data, sizeof(card_data)); - card_data_index = 0; -} - -void EncoderIoProx::get_next(bool* polarity, uint16_t* period, uint16_t* pulse) { - uint8_t bit = (card_data[card_data_index / 8] >> (7 - (card_data_index % 8))) & 1; - - bool advance = fsk->next(bit, period); - if(advance) { - card_data_index++; - if(card_data_index >= (8 * card_data_max)) { - card_data_index = 0; - } - } - - *polarity = true; - *pulse = *period / 2; -} - -EncoderIoProx::EncoderIoProx() { - fsk = new OscFSK(8, 10, 64); -} - -EncoderIoProx::~EncoderIoProx() { - delete fsk; -} diff --git a/applications/lfrfid/helpers/encoder_ioprox.h b/applications/lfrfid/helpers/encoder_ioprox.h deleted file mode 100644 index 568b4067..00000000 --- a/applications/lfrfid/helpers/encoder_ioprox.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once -#include "encoder_generic.h" -#include "osc_fsk.h" - -class EncoderIoProx : public EncoderGeneric { -public: - /** - * @brief init data to emulate - * - * @param data 1 byte FC, 1 byte Version, 2 bytes code - * @param data_size must be 4 - */ - void init(const uint8_t* data, const uint8_t data_size) final; - void get_next(bool* polarity, uint16_t* period, uint16_t* pulse) final; - EncoderIoProx(); - ~EncoderIoProx(); - -private: - static const uint8_t card_data_max = 8; - - uint8_t card_data[card_data_max]; - uint8_t card_data_index; - - OscFSK* fsk; -}; diff --git a/applications/lfrfid/helpers/key_info.cpp b/applications/lfrfid/helpers/key_info.cpp deleted file mode 100644 index 4803cd6d..00000000 --- a/applications/lfrfid/helpers/key_info.cpp +++ /dev/null @@ -1,76 +0,0 @@ -#include "key_info.h" -#include - -const char* lfrfid_key_get_type_string(LfrfidKeyType type) { - switch(type) { - case LfrfidKeyType::KeyEM4100: - return "EM4100"; - break; - case LfrfidKeyType::KeyH10301: - return "H10301"; - break; - case LfrfidKeyType::KeyI40134: - return "I40134"; - break; - case LfrfidKeyType::KeyIoProxXSF: - return "IoProxXSF"; - break; - } - - return "Unknown"; -} - -const char* lfrfid_key_get_manufacturer_string(LfrfidKeyType type) { - switch(type) { - case LfrfidKeyType::KeyEM4100: - return "EM-Marin"; - break; - case LfrfidKeyType::KeyH10301: - return "HID"; - break; - case LfrfidKeyType::KeyI40134: - return "Indala"; - break; - case LfrfidKeyType::KeyIoProxXSF: - return "Kantech"; - } - - return "Unknown"; -} - -bool lfrfid_key_get_string_type(const char* string, LfrfidKeyType* type) { - bool result = true; - - if(strcmp("EM4100", string) == 0) { - *type = LfrfidKeyType::KeyEM4100; - } else if(strcmp("H10301", string) == 0) { - *type = LfrfidKeyType::KeyH10301; - } else if(strcmp("I40134", string) == 0) { - *type = LfrfidKeyType::KeyI40134; - } else if(strcmp("IoProxXSF", string) == 0) { - *type = LfrfidKeyType::KeyIoProxXSF; - } else { - result = false; - } - - return result; -} - -uint8_t lfrfid_key_get_type_data_count(LfrfidKeyType type) { - switch(type) { - case LfrfidKeyType::KeyEM4100: - return 5; - break; - case LfrfidKeyType::KeyH10301: - return 3; - break; - case LfrfidKeyType::KeyI40134: - return 3; - break; - case LfrfidKeyType::KeyIoProxXSF: - return 4; - break; - } - - return 0; -} diff --git a/applications/lfrfid/helpers/key_info.h b/applications/lfrfid/helpers/key_info.h deleted file mode 100644 index 75a0a940..00000000 --- a/applications/lfrfid/helpers/key_info.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once -#include - -static const uint8_t LFRFID_KEY_SIZE = 8; -static const uint8_t LFRFID_KEY_NAME_SIZE = 22; - -enum class LfrfidKeyType : uint8_t { - KeyEM4100, - KeyH10301, - KeyI40134, - KeyIoProxXSF, -}; - -const char* lfrfid_key_get_type_string(LfrfidKeyType type); -const char* lfrfid_key_get_manufacturer_string(LfrfidKeyType type); -bool lfrfid_key_get_string_type(const char* string, LfrfidKeyType* type); -uint8_t lfrfid_key_get_type_data_count(LfrfidKeyType type); diff --git a/applications/lfrfid/helpers/osc_fsk.cpp b/applications/lfrfid/helpers/osc_fsk.cpp deleted file mode 100644 index 5f3b5367..00000000 --- a/applications/lfrfid/helpers/osc_fsk.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include "osc_fsk.h" - -OscFSK::OscFSK(uint16_t _freq_low, uint16_t _freq_hi, uint16_t _osc_phase_max) - : freq{_freq_low, _freq_hi} - , osc_phase_max(_osc_phase_max) { - osc_phase_current = 0; -} - -bool OscFSK::next(bool bit, uint16_t* period) { - bool advance = false; - *period = freq[bit]; - osc_phase_current += *period; - - if(osc_phase_current > osc_phase_max) { - advance = true; - osc_phase_current -= osc_phase_max; - } - - return advance; -} diff --git a/applications/lfrfid/helpers/osc_fsk.h b/applications/lfrfid/helpers/osc_fsk.h deleted file mode 100644 index eaaaa10a..00000000 --- a/applications/lfrfid/helpers/osc_fsk.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once -#include - -/** - * This code tries to fit the periods into a given number of cycles (phases) by taking cycles from the next cycle of periods. - */ -class OscFSK { -public: - /** - * Get next period - * @param bit bit value - * @param period return period - * @return bool whether to advance to the next bit - */ - bool next(bool bit, uint16_t* period); - - /** - * FSK ocillator constructor - * - * @param freq_low bit 0 freq - * @param freq_hi bit 1 freq - * @param osc_phase_max max oscillator phase - */ - OscFSK(uint16_t freq_low, uint16_t freq_hi, uint16_t osc_phase_max); - -private: - const uint16_t freq[2]; - const uint16_t osc_phase_max; - int32_t osc_phase_current; -}; diff --git a/applications/lfrfid/helpers/protocols/protocol_emmarin.cpp b/applications/lfrfid/helpers/protocols/protocol_emmarin.cpp deleted file mode 100644 index 4ac180e3..00000000 --- a/applications/lfrfid/helpers/protocols/protocol_emmarin.cpp +++ /dev/null @@ -1,150 +0,0 @@ -#include "protocol_emmarin.h" -#include - -#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) - -typedef uint64_t EMMarinCardData; - -void write_nibble(bool low_nibble, uint8_t data, EMMarinCardData* card_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; - *card_data = (*card_data << 1) | ((data >> i) & 1); - } - - *card_data = (*card_data << 1) | ((parity_sum % 2) & 1); -} - -uint8_t ProtocolEMMarin::get_encoded_data_size() { - return sizeof(EMMarinCardData); -} - -uint8_t ProtocolEMMarin::get_decoded_data_size() { - return 5; -} - -void ProtocolEMMarin::encode( - const uint8_t* decoded_data, - const uint8_t decoded_data_size, - uint8_t* encoded_data, - const uint8_t encoded_data_size) { - furi_check(decoded_data_size >= get_decoded_data_size()); - furi_check(encoded_data_size >= get_encoded_data_size()); - - EMMarinCardData card_data; - - // header - card_data = 0b111111111; - - // data - for(uint8_t i = 0; i < get_decoded_data_size(); i++) { - write_nibble(false, decoded_data[i], &card_data); - write_nibble(true, decoded_data[i], &card_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 = (card_data >> (i * EM_BITS_PER_ROW_COUNT - 1)) & 1; - parity_sum += parity_bit; - } - card_data = (card_data << 1) | ((parity_sum % 2) & 1); - } - - // stop bit - card_data = (card_data << 1) | 0; - - memcpy(encoded_data, &card_data, get_encoded_data_size()); -} - -void ProtocolEMMarin::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 >= get_decoded_data_size()); - furi_check(encoded_data_size >= get_encoded_data_size()); - - uint8_t decoded_data_index = 0; - EMMarinCardData card_data = *(reinterpret_cast(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; - } - } -} - -bool ProtocolEMMarin::can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) { - furi_check(encoded_data_size >= get_encoded_data_size()); - const EMMarinCardData* card_data = reinterpret_cast(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; -} diff --git a/applications/lfrfid/helpers/protocols/protocol_emmarin.h b/applications/lfrfid/helpers/protocols/protocol_emmarin.h deleted file mode 100644 index 7a866f90..00000000 --- a/applications/lfrfid/helpers/protocols/protocol_emmarin.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once -#include "protocol_generic.h" - -class ProtocolEMMarin : public ProtocolGeneric { -public: - uint8_t get_encoded_data_size() final; - uint8_t get_decoded_data_size() final; - - void encode( - const uint8_t* decoded_data, - const uint8_t decoded_data_size, - uint8_t* encoded_data, - const uint8_t encoded_data_size) final; - - void decode( - const uint8_t* encoded_data, - const uint8_t encoded_data_size, - uint8_t* decoded_data, - const uint8_t decoded_data_size) final; - - bool can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) final; -}; diff --git a/applications/lfrfid/helpers/protocols/protocol_generic.h b/applications/lfrfid/helpers/protocols/protocol_generic.h deleted file mode 100644 index d593f708..00000000 --- a/applications/lfrfid/helpers/protocols/protocol_generic.h +++ /dev/null @@ -1,60 +0,0 @@ -#pragma once -#include "stdint.h" -#include "stdbool.h" - -class ProtocolGeneric { -public: - /** - * @brief Get the encoded data size - * - * @return uint8_t size of encoded data in bytes - */ - virtual uint8_t get_encoded_data_size() = 0; - - /** - * @brief Get the decoded data size - * - * @return uint8_t size of decoded data in bytes - */ - virtual uint8_t get_decoded_data_size() = 0; - - /** - * @brief encode decoded data - * - * @param decoded_data - * @param decoded_data_size - * @param encoded_data - * @param encoded_data_size - */ - virtual void encode( - const uint8_t* decoded_data, - const uint8_t decoded_data_size, - uint8_t* encoded_data, - const uint8_t encoded_data_size) = 0; - - /** - * @brief decode encoded data - * - * @param encoded_data - * @param encoded_data_size - * @param decoded_data - * @param decoded_data_size - */ - virtual void decode( - const uint8_t* encoded_data, - const uint8_t encoded_data_size, - uint8_t* decoded_data, - const uint8_t decoded_data_size) = 0; - - /** - * @brief fast check that data can be correctly decoded - * - * @param encoded_data - * @param encoded_data_size - * @return true - can be correctly decoded - * @return false - cannot be correctly decoded - */ - virtual bool can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) = 0; - - virtual ~ProtocolGeneric(){}; -}; diff --git a/applications/lfrfid/helpers/protocols/protocol_hid_h10301.cpp b/applications/lfrfid/helpers/protocols/protocol_hid_h10301.cpp deleted file mode 100644 index b7183883..00000000 --- a/applications/lfrfid/helpers/protocols/protocol_hid_h10301.cpp +++ /dev/null @@ -1,238 +0,0 @@ -#include "protocol_hid_h10301.h" -#include - -typedef uint32_t HID10301CardData; -constexpr uint8_t HID10301Count = 3; -constexpr uint8_t HID10301BitSize = sizeof(HID10301CardData) * 8; - -static void write_raw_bit(bool bit, uint8_t position, HID10301CardData* card_data) { - if(bit) { - card_data[position / HID10301BitSize] |= - 1UL << (HID10301BitSize - (position % HID10301BitSize) - 1); - } else { - card_data[position / (sizeof(HID10301CardData) * 8)] &= - ~(1UL << (HID10301BitSize - (position % HID10301BitSize) - 1)); - } -} - -static void write_bit(bool bit, uint8_t position, HID10301CardData* card_data) { - write_raw_bit(bit, position + 0, card_data); - write_raw_bit(!bit, position + 1, card_data); -} - -uint8_t ProtocolHID10301::get_encoded_data_size() { - return sizeof(HID10301CardData) * HID10301Count; -} - -uint8_t ProtocolHID10301::get_decoded_data_size() { - return 3; -} - -void ProtocolHID10301::encode( - const uint8_t* decoded_data, - const uint8_t decoded_data_size, - uint8_t* encoded_data, - const uint8_t encoded_data_size) { - furi_check(decoded_data_size >= get_decoded_data_size()); - furi_check(encoded_data_size >= get_encoded_data_size()); - - HID10301CardData card_data[HID10301Count] = {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 - write_raw_bit(0, 0, card_data); - write_raw_bit(0, 1, card_data); - write_raw_bit(0, 2, card_data); - write_raw_bit(1, 3, card_data); - write_raw_bit(1, 4, card_data); - write_raw_bit(1, 5, card_data); - write_raw_bit(0, 6, card_data); - write_raw_bit(1, 7, card_data); - - // company / OEM code 1 - write_bit(0, 8, card_data); - write_bit(0, 10, card_data); - write_bit(0, 12, card_data); - write_bit(0, 14, card_data); - write_bit(0, 16, card_data); - write_bit(0, 18, card_data); - write_bit(1, 20, card_data); - - // card format / length 1 - write_bit(0, 22, card_data); - write_bit(0, 24, card_data); - write_bit(0, 26, card_data); - write_bit(0, 28, card_data); - write_bit(0, 30, card_data); - write_bit(0, 32, card_data); - write_bit(0, 34, card_data); - write_bit(0, 36, card_data); - write_bit(0, 38, card_data); - write_bit(0, 40, card_data); - write_bit(1, 42, card_data); - - // even parity bit - write_bit((even_parity_sum % 2), 44, card_data); - - // data - for(uint8_t i = 0; i < 24; i++) { - write_bit((fc_cn >> (23 - i)) & 1, 46 + (i * 2), card_data); - } - - // odd parity bit - write_bit((odd_parity_sum % 2), 94, card_data); - - memcpy(encoded_data, &card_data, get_encoded_data_size()); -} - -void ProtocolHID10301::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 >= get_decoded_data_size()); - furi_check(encoded_data_size >= get_encoded_data_size()); - - const HID10301CardData* card_data = reinterpret_cast(encoded_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[3] = {(uint8_t)(result >> 17), (uint8_t)(result >> 9), (uint8_t)(result >> 1)}; - - memcpy(decoded_data, &data, get_decoded_data_size()); -} - -bool ProtocolHID10301::can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) { - furi_check(encoded_data_size >= get_encoded_data_size()); - - const HID10301CardData* card_data = reinterpret_cast(encoded_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; -} diff --git a/applications/lfrfid/helpers/protocols/protocol_hid_h10301.h b/applications/lfrfid/helpers/protocols/protocol_hid_h10301.h deleted file mode 100644 index fbd6e0b2..00000000 --- a/applications/lfrfid/helpers/protocols/protocol_hid_h10301.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once -#include "protocol_generic.h" - -class ProtocolHID10301 : public ProtocolGeneric { -public: - uint8_t get_encoded_data_size() final; - uint8_t get_decoded_data_size() final; - - void encode( - const uint8_t* decoded_data, - const uint8_t decoded_data_size, - uint8_t* encoded_data, - const uint8_t encoded_data_size) final; - - void decode( - const uint8_t* encoded_data, - const uint8_t encoded_data_size, - uint8_t* decoded_data, - const uint8_t decoded_data_size) final; - - bool can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) final; -}; diff --git a/applications/lfrfid/helpers/protocols/protocol_indala_40134.cpp b/applications/lfrfid/helpers/protocols/protocol_indala_40134.cpp deleted file mode 100644 index 482339de..00000000 --- a/applications/lfrfid/helpers/protocols/protocol_indala_40134.cpp +++ /dev/null @@ -1,237 +0,0 @@ -#include "protocol_indala_40134.h" -#include - -typedef uint64_t Indala40134CardData; - -static void set_bit(bool bit, uint8_t position, Indala40134CardData* card_data) { - position = (sizeof(Indala40134CardData) * 8) - 1 - position; - if(bit) { - *card_data |= 1ull << position; - } else { - *card_data &= ~(1ull << position); - } -} - -static bool get_bit(uint8_t position, const Indala40134CardData* card_data) { - position = (sizeof(Indala40134CardData) * 8) - 1 - position; - return (*card_data >> position) & 1; -} - -uint8_t ProtocolIndala40134::get_encoded_data_size() { - return sizeof(Indala40134CardData); -} - -uint8_t ProtocolIndala40134::get_decoded_data_size() { - return 3; -} - -void ProtocolIndala40134::encode( - const uint8_t* decoded_data, - const uint8_t decoded_data_size, - uint8_t* encoded_data, - const uint8_t encoded_data_size) { - furi_check(decoded_data_size >= get_decoded_data_size()); - furi_check(encoded_data_size >= get_encoded_data_size()); - - uint32_t fc_and_card = (decoded_data[0] << 16) | (decoded_data[1] << 8) | decoded_data[2]; - Indala40134CardData card_data = 0; - - // preamble - set_bit(1, 0, &card_data); - set_bit(1, 2, &card_data); - set_bit(1, 32, &card_data); - - // factory code - set_bit(((fc_and_card >> 23) & 1), 57, &card_data); - set_bit(((fc_and_card >> 22) & 1), 49, &card_data); - set_bit(((fc_and_card >> 21) & 1), 44, &card_data); - set_bit(((fc_and_card >> 20) & 1), 47, &card_data); - set_bit(((fc_and_card >> 19) & 1), 48, &card_data); - set_bit(((fc_and_card >> 18) & 1), 53, &card_data); - set_bit(((fc_and_card >> 17) & 1), 39, &card_data); - set_bit(((fc_and_card >> 16) & 1), 58, &card_data); - - // card number - set_bit(((fc_and_card >> 15) & 1), 42, &card_data); - set_bit(((fc_and_card >> 14) & 1), 45, &card_data); - set_bit(((fc_and_card >> 13) & 1), 43, &card_data); - set_bit(((fc_and_card >> 12) & 1), 40, &card_data); - set_bit(((fc_and_card >> 11) & 1), 52, &card_data); - set_bit(((fc_and_card >> 10) & 1), 36, &card_data); - set_bit(((fc_and_card >> 9) & 1), 35, &card_data); - set_bit(((fc_and_card >> 8) & 1), 51, &card_data); - set_bit(((fc_and_card >> 7) & 1), 46, &card_data); - set_bit(((fc_and_card >> 6) & 1), 33, &card_data); - set_bit(((fc_and_card >> 5) & 1), 37, &card_data); - set_bit(((fc_and_card >> 4) & 1), 54, &card_data); - set_bit(((fc_and_card >> 3) & 1), 56, &card_data); - set_bit(((fc_and_card >> 2) & 1), 59, &card_data); - set_bit(((fc_and_card >> 1) & 1), 50, &card_data); - set_bit(((fc_and_card >> 0) & 1), 41, &card_data); - - // checksum - uint8_t checksum = 0; - checksum += ((fc_and_card >> 14) & 1); - checksum += ((fc_and_card >> 12) & 1); - checksum += ((fc_and_card >> 9) & 1); - checksum += ((fc_and_card >> 8) & 1); - checksum += ((fc_and_card >> 6) & 1); - checksum += ((fc_and_card >> 5) & 1); - checksum += ((fc_and_card >> 2) & 1); - checksum += ((fc_and_card >> 0) & 1); - - // wiegand parity bits - // 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_and_card >> 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_and_card >> i) & 1) == 1) { - odd_parity_sum++; - } - } - - // even parity bit - set_bit((even_parity_sum % 2), 34, &card_data); - - // odd parity bit - set_bit((odd_parity_sum % 2), 38, &card_data); - - // checksum - if((checksum & 1) == 1) { - set_bit(0, 62, &card_data); - set_bit(1, 63, &card_data); - } else { - set_bit(1, 62, &card_data); - set_bit(0, 63, &card_data); - } - - memcpy(encoded_data, &card_data, get_encoded_data_size()); -} - -// factory code -static uint8_t get_fc(const Indala40134CardData* card_data) { - uint8_t fc = 0; - - fc = fc << 1 | get_bit(57, card_data); - fc = fc << 1 | get_bit(49, card_data); - fc = fc << 1 | get_bit(44, card_data); - fc = fc << 1 | get_bit(47, card_data); - fc = fc << 1 | get_bit(48, card_data); - fc = fc << 1 | get_bit(53, card_data); - fc = fc << 1 | get_bit(39, card_data); - fc = fc << 1 | get_bit(58, card_data); - - return fc; -} - -// card number -static uint16_t get_cn(const Indala40134CardData* card_data) { - uint16_t cn = 0; - - cn = cn << 1 | get_bit(42, card_data); - cn = cn << 1 | get_bit(45, card_data); - cn = cn << 1 | get_bit(43, card_data); - cn = cn << 1 | get_bit(40, card_data); - cn = cn << 1 | get_bit(52, card_data); - cn = cn << 1 | get_bit(36, card_data); - cn = cn << 1 | get_bit(35, card_data); - cn = cn << 1 | get_bit(51, card_data); - cn = cn << 1 | get_bit(46, card_data); - cn = cn << 1 | get_bit(33, card_data); - cn = cn << 1 | get_bit(37, card_data); - cn = cn << 1 | get_bit(54, card_data); - cn = cn << 1 | get_bit(56, card_data); - cn = cn << 1 | get_bit(59, card_data); - cn = cn << 1 | get_bit(50, card_data); - cn = cn << 1 | get_bit(41, card_data); - - return cn; -} - -void ProtocolIndala40134::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 >= get_decoded_data_size()); - furi_check(encoded_data_size >= get_encoded_data_size()); - - const Indala40134CardData* card_data = - reinterpret_cast(encoded_data); - - uint8_t fc = get_fc(card_data); - uint16_t card = get_cn(card_data); - - decoded_data[0] = fc; - decoded_data[1] = card >> 8; - decoded_data[2] = card; -} - -bool ProtocolIndala40134::can_be_decoded( - const uint8_t* encoded_data, - const uint8_t encoded_data_size) { - furi_check(encoded_data_size >= get_encoded_data_size()); - bool can_be_decoded = false; - - const Indala40134CardData* card_data = - reinterpret_cast(encoded_data); - - do { - // preambula - if((*card_data >> 32) != 0xa0000000UL) break; - - // data - const uint32_t fc_and_card = get_fc(card_data) << 16 | get_cn(card_data); - - // checksum - const uint8_t checksum = get_bit(62, card_data) << 1 | get_bit(63, card_data); - 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 { - break; - } - - // wiegand parity bits - // even parity sum calculation (high 12 bits of data) - const bool even_parity = get_bit(34, card_data); - 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) break; - - // odd parity sum calculation (low 12 bits of data) - const bool odd_parity = get_bit(38, card_data); - 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) break; - - can_be_decoded = true; - } while(false); - - return can_be_decoded; -} diff --git a/applications/lfrfid/helpers/protocols/protocol_indala_40134.h b/applications/lfrfid/helpers/protocols/protocol_indala_40134.h deleted file mode 100644 index d378bb2c..00000000 --- a/applications/lfrfid/helpers/protocols/protocol_indala_40134.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once -#include "protocol_generic.h" - -class ProtocolIndala40134 : public ProtocolGeneric { -public: - uint8_t get_encoded_data_size() final; - uint8_t get_decoded_data_size() final; - - void encode( - const uint8_t* decoded_data, - const uint8_t decoded_data_size, - uint8_t* encoded_data, - const uint8_t encoded_data_size) final; - - void decode( - const uint8_t* encoded_data, - const uint8_t encoded_data_size, - uint8_t* decoded_data, - const uint8_t decoded_data_size) final; - - bool can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) final; -}; diff --git a/applications/lfrfid/helpers/protocols/protocol_ioprox.cpp b/applications/lfrfid/helpers/protocols/protocol_ioprox.cpp deleted file mode 100644 index b3b6a0e5..00000000 --- a/applications/lfrfid/helpers/protocols/protocol_ioprox.cpp +++ /dev/null @@ -1,193 +0,0 @@ -#include "protocol_ioprox.h" -#include -#include - -/** - * Writes a bit into the output buffer. - */ -static void write_bit(bool bit, uint8_t position, uint8_t* data) { - if(bit) { - data[position / 8] |= 1UL << (7 - (position % 8)); - } else { - data[position / 8] &= ~(1UL << (7 - (position % 8))); - } -} - -/** - * Writes up to eight contiguous bits into the output buffer. - */ -static void write_bits(uint8_t byte, uint8_t position, uint8_t* data, uint8_t length) { - furi_check(length <= 8); - furi_check(length > 0); - - for(uint8_t i = 0; i < length; ++i) { - uint8_t shift = 7 - i; - write_bit((byte >> shift) & 1, position + i, data); - } -} - -uint8_t ProtocolIoProx::get_encoded_data_size() { - return 8; -} - -uint8_t ProtocolIoProx::get_decoded_data_size() { - return 4; -} - -void ProtocolIoProx::encode( - const uint8_t* decoded_data, - const uint8_t decoded_data_size, - uint8_t* encoded_data, - const uint8_t encoded_data_size) { - furi_check(decoded_data_size >= get_decoded_data_size()); - furi_check(encoded_data_size >= get_encoded_data_size()); - - // 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. - write_bits(0b00000000, 0, encoded_data, 8); - write_bit(0, 8, encoded_data); - - write_bits(0b11110000, 9, encoded_data, 8); - write_bit(1, 17, encoded_data); - - // Facility code. - write_bits(decoded_data[0], 18, encoded_data, 8); - write_bit(1, 26, encoded_data); - - // Version - write_bits(decoded_data[1], 27, encoded_data, 8); - write_bit(1, 35, encoded_data); - - // Code one - write_bits(decoded_data[2], 36, encoded_data, 8); - write_bit(1, 44, encoded_data); - - // Code two - write_bits(decoded_data[3], 45, encoded_data, 8); - write_bit(1, 53, encoded_data); - - // Checksum - write_bits(compute_checksum(encoded_data, 8), 54, encoded_data, 8); - write_bit(1, 62, encoded_data); - write_bit(1, 63, encoded_data); -} - -void ProtocolIoProx::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 >= get_decoded_data_size()); - furi_check(encoded_data_size >= get_encoded_data_size()); - - // 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] = (encoded_data[2] << 2) | (encoded_data[3] >> 6); - - // Version code. - decoded_data[1] = (encoded_data[3] << 3) | (encoded_data[4] >> 5); - - // Code bytes. - decoded_data[2] = (encoded_data[4] << 4) | (encoded_data[5] >> 4); - decoded_data[3] = (encoded_data[5] << 5) | (encoded_data[6] >> 3); -} - -bool ProtocolIoProx::can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) { - furi_check(encoded_data_size >= get_encoded_data_size()); - - // 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((encoded_data[2] & 0b01000000) == 0) { - return false; - } - if((encoded_data[3] & 0b00100000) == 0) { - return false; - } - if((encoded_data[4] & 0b00010000) == 0) { - return false; - } - if((encoded_data[5] & 0b00001000) == 0) { - return false; - } - if((encoded_data[6] & 0b00000100) == 0) { - return false; - } - if((encoded_data[7] & 0b00000011) == 0) { - return false; - } - - // ... and validate our checksums. - uint8_t checksum = compute_checksum(encoded_data, 8); - uint8_t checkval = (encoded_data[6] << 6) | (encoded_data[7] >> 2); - - if(checksum != checkval) { - return false; - } - - return true; -} - -uint8_t ProtocolIoProx::compute_checksum(const uint8_t* data, const uint8_t data_size) { - furi_check(data_size == get_encoded_data_size()); - - // 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; - - checksum += (data[1] << 1) | (data[2] >> 7); // VVVVVVVVV - checksum += (data[2] << 2) | (data[3] >> 6); // WWWWWWWWW - checksum += (data[3] << 3) | (data[4] >> 5); // XXXXXXXXX - checksum += (data[4] << 4) | (data[5] >> 4); // YYYYYYYYY - checksum += (data[5] << 5) | (data[6] >> 3); // ZZZZZZZZZ - - return 0xFF - checksum; -} diff --git a/applications/lfrfid/helpers/protocols/protocol_ioprox.h b/applications/lfrfid/helpers/protocols/protocol_ioprox.h deleted file mode 100644 index 2fff53b1..00000000 --- a/applications/lfrfid/helpers/protocols/protocol_ioprox.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once -#include "protocol_generic.h" - -class ProtocolIoProx : public ProtocolGeneric { -public: - uint8_t get_encoded_data_size() final; - uint8_t get_decoded_data_size() final; - - void encode( - const uint8_t* decoded_data, - const uint8_t decoded_data_size, - uint8_t* encoded_data, - const uint8_t encoded_data_size) final; - - void decode( - const uint8_t* encoded_data, - const uint8_t encoded_data_size, - uint8_t* decoded_data, - const uint8_t decoded_data_size) final; - - bool can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) final; - -private: - /** Computes the IoProx checksum of the provided (decoded) data. */ - uint8_t compute_checksum(const uint8_t* data, const uint8_t data_size); -}; diff --git a/applications/lfrfid/helpers/pulse_joiner.cpp b/applications/lfrfid/helpers/pulse_joiner.cpp deleted file mode 100644 index c72019b1..00000000 --- a/applications/lfrfid/helpers/pulse_joiner.cpp +++ /dev/null @@ -1,95 +0,0 @@ -#include "pulse_joiner.h" -#include - -bool PulseJoiner::push_pulse(bool polarity, uint16_t period, uint16_t pulse) { - bool result = false; - furi_check((pulse_index + 1) < pulse_max); - - if(polarity == false && pulse_index == 0) { - // first negative pulse is ommited - - } else { - pulses[pulse_index].polarity = polarity; - pulses[pulse_index].time = pulse; - pulse_index++; - } - - if(period > pulse) { - pulses[pulse_index].polarity = !polarity; - pulses[pulse_index].time = period - pulse; - pulse_index++; - } - - if(pulse_index >= 4) { - // we know that first pulse is always high - // so we wait 2 edges, hi2low and next low2hi - - uint8_t edges_count = 0; - bool last_polarity = pulses[0].polarity; - - for(uint8_t i = 1; i < pulse_index; i++) { - if(pulses[i].polarity != last_polarity) { - edges_count++; - last_polarity = pulses[i].polarity; - } - } - - if(edges_count >= 2) { - result = true; - } - } - - return result; -} - -void PulseJoiner::pop_pulse(uint16_t* period, uint16_t* pulse) { - furi_check(pulse_index <= (pulse_max + 1)); - - uint16_t tmp_period = 0; - uint16_t tmp_pulse = 0; - uint8_t edges_count = 0; - bool last_polarity = pulses[0].polarity; - uint8_t next_fist_pulse = 0; - - for(uint8_t i = 0; i < pulse_max; i++) { - // count edges - if(pulses[i].polarity != last_polarity) { - edges_count++; - last_polarity = pulses[i].polarity; - } - - // wait for 2 edges - if(edges_count == 2) { - next_fist_pulse = i; - break; - } - - // sum pulse time - if(pulses[i].polarity) { - tmp_period += pulses[i].time; - tmp_pulse += pulses[i].time; - } else { - tmp_period += pulses[i].time; - } - pulse_index--; - } - - *period = tmp_period; - *pulse = tmp_pulse; - - // remove counted periods and shift data - for(uint8_t i = 0; i < pulse_max; i++) { - if((next_fist_pulse + i) < pulse_max) { - pulses[i].polarity = pulses[next_fist_pulse + i].polarity; - pulses[i].time = pulses[next_fist_pulse + i].time; - } else { - break; - } - } -} - -PulseJoiner::PulseJoiner() { - for(uint8_t i = 0; i < pulse_max; i++) { - pulses[i] = {false, 0}; - } -} diff --git a/applications/lfrfid/helpers/pulse_joiner.h b/applications/lfrfid/helpers/pulse_joiner.h deleted file mode 100644 index 1639d837..00000000 --- a/applications/lfrfid/helpers/pulse_joiner.h +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once -#include "stdint.h" - -class PulseJoiner { -public: - /** - * @brief Push timer pulse. First negative pulse is ommited. - * - * @param polarity pulse polarity: true = high2low, false = low2high - * @param period overall period time in timer clicks - * @param pulse pulse time in timer clicks - * - * @return true - next pulse can and must be popped immediatly - */ - bool push_pulse(bool polarity, uint16_t period, uint16_t pulse); - - /** - * @brief Get the next timer pulse. Call only if push_pulse returns true. - * - * @param period overall period time in timer clicks - * @param pulse pulse time in timer clicks - */ - void pop_pulse(uint16_t* period, uint16_t* pulse); - - PulseJoiner(); - -private: - struct Pulse { - bool polarity; - uint16_t time; - }; - - uint8_t pulse_index = 0; - static const uint8_t pulse_max = 6; - Pulse pulses[pulse_max]; -}; diff --git a/applications/lfrfid/helpers/rfid_key.cpp b/applications/lfrfid/helpers/rfid_key.cpp deleted file mode 100644 index 2d99d40f..00000000 --- a/applications/lfrfid/helpers/rfid_key.cpp +++ /dev/null @@ -1,65 +0,0 @@ -#include "rfid_key.h" -#include -#include - -RfidKey::RfidKey() { - clear(); -} - -RfidKey::~RfidKey() { -} - -void RfidKey::set_type(LfrfidKeyType _type) { - type = _type; -} - -void RfidKey::set_data(const uint8_t* _data, const uint8_t _data_size) { - furi_assert(_data_size <= data.size()); - for(uint8_t i = 0; i < _data_size; i++) { - data[i] = _data[i]; - } -} - -void RfidKey::set_name(const char* _name) { - strlcpy(name, _name, get_name_length()); -} - -LfrfidKeyType RfidKey::get_type() { - return type; -} - -const uint8_t* RfidKey::get_data() { - return &data[0]; -} - -const char* RfidKey::get_type_text() { - return lfrfid_key_get_type_string(type); -} - -uint8_t RfidKey::get_type_data_count() const { - return lfrfid_key_get_type_data_count(type); -} - -char* RfidKey::get_name() { - return name; -} - -uint8_t RfidKey::get_name_length() { - return LFRFID_KEY_NAME_SIZE; -} - -void RfidKey::clear() { - set_name(""); - set_type(LfrfidKeyType::KeyEM4100); - data.fill(0); -} - -RfidKey& RfidKey::operator=(const RfidKey& rhs) { - if(this == &rhs) return *this; - - set_type(rhs.type); - set_name(rhs.name); - set_data(&rhs.data[0], get_type_data_count()); - - return *this; -} diff --git a/applications/lfrfid/helpers/rfid_key.h b/applications/lfrfid/helpers/rfid_key.h deleted file mode 100644 index 29b87cf9..00000000 --- a/applications/lfrfid/helpers/rfid_key.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once -#include "key_info.h" -#include - -class RfidKey { -public: - RfidKey(); - ~RfidKey(); - - void set_type(LfrfidKeyType type); - void set_data(const uint8_t* data, const uint8_t data_size); - void set_name(const char* name); - - LfrfidKeyType get_type(); - const uint8_t* get_data(); - const char* get_type_text(); - uint8_t get_type_data_count() const; - char* get_name(); - uint8_t get_name_length(); - void clear(); - RfidKey& operator=(const RfidKey& rhs); - -private: - std::array data; - LfrfidKeyType type; - char name[LFRFID_KEY_NAME_SIZE + 1]; -}; diff --git a/applications/lfrfid/helpers/rfid_reader.cpp b/applications/lfrfid/helpers/rfid_reader.cpp deleted file mode 100644 index 029b1cb4..00000000 --- a/applications/lfrfid/helpers/rfid_reader.cpp +++ /dev/null @@ -1,175 +0,0 @@ -#include "rfid_reader.h" -#include -#include -#include - -/** - * @brief private violation assistant for RfidReader - */ -struct RfidReaderAccessor { - static void decode(RfidReader& rfid_reader, bool polarity) { - rfid_reader.decode(polarity); - } -}; - -void RfidReader::decode(bool polarity) { - uint32_t current_dwt_value = DWT->CYCCNT; - uint32_t period = current_dwt_value - last_dwt_value; - last_dwt_value = current_dwt_value; - -#ifdef RFID_GPIO_DEBUG - decoder_gpio_out.process_front(polarity, period); -#endif - - switch(type) { - case Type::Normal: - decoder_em.process_front(polarity, period); - decoder_hid26.process_front(polarity, period); - decoder_ioprox.process_front(polarity, period); - break; - case Type::Indala: - decoder_em.process_front(polarity, period); - decoder_hid26.process_front(polarity, period); - decoder_ioprox.process_front(polarity, period); - decoder_indala.process_front(polarity, period); - break; - } - - detect_ticks++; -} - -bool RfidReader::switch_timer_elapsed() { - const uint32_t seconds_to_switch = furi_kernel_get_tick_frequency() * 2.0f; - return (furi_get_tick() - switch_os_tick_last) > seconds_to_switch; -} - -void RfidReader::switch_timer_reset() { - switch_os_tick_last = furi_get_tick(); -} - -void RfidReader::switch_mode() { - switch(type) { - case Type::Normal: - type = Type::Indala; - furi_hal_rfid_change_read_config(62500.0f, 0.25f); - break; - case Type::Indala: - type = Type::Normal; - furi_hal_rfid_change_read_config(125000.0f, 0.5f); - break; - } - - switch_timer_reset(); -} - -static void comparator_trigger_callback(bool level, void* comp_ctx) { - RfidReader* _this = static_cast(comp_ctx); - - RfidReaderAccessor::decode(*_this, !level); -} - -RfidReader::RfidReader() { -} - -void RfidReader::start() { - type = Type::Normal; - - furi_hal_rfid_pins_read(); - furi_hal_rfid_tim_read(125000, 0.5); - furi_hal_rfid_tim_read_start(); - start_comparator(); - - switch_timer_reset(); - last_read_count = 0; -} - -void RfidReader::start_forced(RfidReader::Type _type) { - start(); - if(_type == Type::Indala) { - switch_mode(); - } -} - -void RfidReader::stop() { - furi_hal_rfid_pins_reset(); - furi_hal_rfid_tim_read_stop(); - furi_hal_rfid_tim_reset(); - stop_comparator(); -} - -bool RfidReader::read(LfrfidKeyType* _type, uint8_t* data, uint8_t data_size, bool switch_enable) { - bool result = false; - bool something_read = false; - - // reading - if(decoder_em.read(data, data_size)) { - *_type = LfrfidKeyType::KeyEM4100; - something_read = true; - } - - if(decoder_hid26.read(data, data_size)) { - *_type = LfrfidKeyType::KeyH10301; - something_read = true; - } - - if(decoder_ioprox.read(data, data_size)) { - *_type = LfrfidKeyType::KeyIoProxXSF; - something_read = true; - } - - if(decoder_indala.read(data, data_size)) { - *_type = LfrfidKeyType::KeyI40134; - something_read = true; - } - - // validation - if(something_read) { - switch_timer_reset(); - - if(last_read_type == *_type && memcmp(last_read_data, data, data_size) == 0) { - last_read_count = last_read_count + 1; - - if(last_read_count > 2) { - result = true; - } - } else { - last_read_type = *_type; - memcpy(last_read_data, data, data_size); - last_read_count = 0; - } - } - - // mode switching - if(switch_enable && switch_timer_elapsed()) { - switch_mode(); - last_read_count = 0; - } - - return result; -} - -bool RfidReader::detect() { - bool detected = false; - if(detect_ticks > 10) { - detected = true; - } - detect_ticks = 0; - - return detected; -} - -bool RfidReader::any_read() { - return last_read_count > 0; -} - -void RfidReader::start_comparator(void) { - furi_hal_rfid_comp_set_callback(comparator_trigger_callback, this); - last_dwt_value = DWT->CYCCNT; - - furi_hal_rfid_comp_start(); -} - -void RfidReader::stop_comparator(void) { - furi_hal_rfid_comp_stop(); - furi_hal_rfid_comp_set_callback(NULL, NULL); -} diff --git a/applications/lfrfid/helpers/rfid_reader.h b/applications/lfrfid/helpers/rfid_reader.h deleted file mode 100644 index 903bbecf..00000000 --- a/applications/lfrfid/helpers/rfid_reader.h +++ /dev/null @@ -1,59 +0,0 @@ -#pragma once -//#include "decoder_analyzer.h" -#include "decoder_gpio_out.h" -#include "decoder_emmarin.h" -#include "decoder_hid26.h" -#include "decoder_indala.h" -#include "decoder_ioprox.h" -#include "key_info.h" - -//#define RFID_GPIO_DEBUG 1 - -class RfidReader { -public: - enum class Type : uint8_t { - Normal, - Indala, - }; - - RfidReader(); - void start(); - void start_forced(RfidReader::Type type); - void stop(); - bool read(LfrfidKeyType* type, uint8_t* data, uint8_t data_size, bool switch_enable = true); - - bool detect(); - bool any_read(); - -private: - friend struct RfidReaderAccessor; - - //DecoderAnalyzer decoder_analyzer; -#ifdef RFID_GPIO_DEBUG - DecoderGpioOut decoder_gpio_out; -#endif - DecoderEMMarin decoder_em; - DecoderHID26 decoder_hid26; - DecoderIndala decoder_indala; - DecoderIoProx decoder_ioprox; - - uint32_t last_dwt_value; - - void start_comparator(void); - void stop_comparator(void); - - void decode(bool polarity); - - uint32_t detect_ticks; - - uint32_t switch_os_tick_last; - bool switch_timer_elapsed(); - void switch_timer_reset(); - void switch_mode(); - - LfrfidKeyType last_read_type; - uint8_t last_read_data[LFRFID_KEY_SIZE]; - uint8_t last_read_count; - - Type type = Type::Normal; -}; diff --git a/applications/lfrfid/helpers/rfid_timer_emulator.cpp b/applications/lfrfid/helpers/rfid_timer_emulator.cpp deleted file mode 100644 index f5337c31..00000000 --- a/applications/lfrfid/helpers/rfid_timer_emulator.cpp +++ /dev/null @@ -1,56 +0,0 @@ -#include "rfid_timer_emulator.h" - -RfidTimerEmulator::RfidTimerEmulator() { -} - -RfidTimerEmulator::~RfidTimerEmulator() { - std::map::iterator it; - - for(it = encoders.begin(); it != encoders.end(); ++it) { - delete it->second; - } - - encoders.clear(); -} - -void RfidTimerEmulator::start(LfrfidKeyType type, const uint8_t* data, uint8_t data_size) { - if(encoders.count(type)) { - current_encoder = encoders.find(type)->second; - - if(data_size >= lfrfid_key_get_type_data_count(type)) { - current_encoder->init(data, data_size); - - furi_hal_rfid_tim_emulate(125000); - furi_hal_rfid_pins_emulate(); - - furi_hal_rfid_tim_emulate_start(RfidTimerEmulator::timer_update_callback, this); - } - } else { - // not found - } -} - -void RfidTimerEmulator::stop() { - furi_hal_rfid_tim_emulate_stop(); - furi_hal_rfid_tim_reset(); - furi_hal_rfid_pins_reset(); -} - -void RfidTimerEmulator::timer_update_callback(void* ctx) { - RfidTimerEmulator* _this = static_cast(ctx); - - bool result; - bool polarity; - uint16_t period; - uint16_t pulse; - - do { - _this->current_encoder->get_next(&polarity, &period, &pulse); - result = _this->pulse_joiner.push_pulse(polarity, period, pulse); - } while(result == false); - - _this->pulse_joiner.pop_pulse(&period, &pulse); - - furi_hal_rfid_set_emulate_period(period - 1); - furi_hal_rfid_set_emulate_pulse(pulse); -} diff --git a/applications/lfrfid/helpers/rfid_timer_emulator.h b/applications/lfrfid/helpers/rfid_timer_emulator.h deleted file mode 100644 index 2129a1c7..00000000 --- a/applications/lfrfid/helpers/rfid_timer_emulator.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once -#include -#include "key_info.h" -#include "encoder_generic.h" -#include "encoder_emmarin.h" -#include "encoder_hid_h10301.h" -#include "encoder_indala_40134.h" -#include "encoder_ioprox.h" -#include "pulse_joiner.h" -#include - -class RfidTimerEmulator { -public: - RfidTimerEmulator(); - ~RfidTimerEmulator(); - void start(LfrfidKeyType type, const uint8_t* data, uint8_t data_size); - void stop(); - -private: - EncoderGeneric* current_encoder = nullptr; - - std::map encoders = { - {LfrfidKeyType::KeyEM4100, new EncoderEM()}, - {LfrfidKeyType::KeyH10301, new EncoderHID_H10301()}, - {LfrfidKeyType::KeyI40134, new EncoderIndala_40134()}, - {LfrfidKeyType::KeyIoProxXSF, new EncoderIoProx()}, - }; - - PulseJoiner pulse_joiner; - static void timer_update_callback(void* ctx); -}; diff --git a/applications/lfrfid/helpers/rfid_worker.cpp b/applications/lfrfid/helpers/rfid_worker.cpp deleted file mode 100644 index af15a340..00000000 --- a/applications/lfrfid/helpers/rfid_worker.cpp +++ /dev/null @@ -1,136 +0,0 @@ -#include "rfid_worker.h" - -RfidWorker::RfidWorker() { -} - -RfidWorker::~RfidWorker() { -} - -void RfidWorker::start_read() { - reader.start(); -} - -bool RfidWorker::read() { - static const uint8_t data_size = LFRFID_KEY_SIZE; - uint8_t data[data_size] = {0}; - LfrfidKeyType type; - - bool result = reader.read(&type, data, data_size); - - if(result) { - key.set_type(type); - key.set_data(data, data_size); - }; - - return result; -} - -bool RfidWorker::detect() { - return reader.detect(); -} - -bool RfidWorker::any_read() { - return reader.any_read(); -} - -void RfidWorker::stop_read() { - reader.stop(); -} - -void RfidWorker::start_write() { - write_result = WriteResult::Nothing; - write_sequence = new TickSequencer(); - validate_counts = 0; - - write_sequence->do_every_tick(1, std::bind(&RfidWorker::sq_write, this)); - write_sequence->do_after_tick(2, std::bind(&RfidWorker::sq_write_start_validate, this)); - write_sequence->do_every_tick(30, std::bind(&RfidWorker::sq_write_validate, this)); - write_sequence->do_every_tick(1, std::bind(&RfidWorker::sq_write_stop_validate, this)); -} - -RfidWorker::WriteResult RfidWorker::write() { - write_sequence->tick(); - return write_result; -} - -void RfidWorker::stop_write() { - delete write_sequence; - reader.stop(); -} - -void RfidWorker::start_emulate() { - emulator.start(key.get_type(), key.get_data(), key.get_type_data_count()); -} - -void RfidWorker::stop_emulate() { - emulator.stop(); -} - -void RfidWorker::sq_write() { - for(size_t i = 0; i < 5; i++) { - switch(key.get_type()) { - case LfrfidKeyType::KeyEM4100: - writer.start(); - writer.write_em(key.get_data()); - writer.stop(); - break; - case LfrfidKeyType::KeyH10301: - writer.start(); - writer.write_hid(key.get_data()); - writer.stop(); - break; - case LfrfidKeyType::KeyI40134: - writer.start(); - writer.write_indala(key.get_data()); - writer.stop(); - break; - case LfrfidKeyType::KeyIoProxXSF: - writer.start(); - writer.write_ioprox(key.get_data()); - writer.stop(); - break; - } - } -} - -void RfidWorker::sq_write_start_validate() { - switch(key.get_type()) { - case LfrfidKeyType::KeyEM4100: - case LfrfidKeyType::KeyH10301: - case LfrfidKeyType::KeyIoProxXSF: - reader.start_forced(RfidReader::Type::Normal); - break; - case LfrfidKeyType::KeyI40134: - reader.start_forced(RfidReader::Type::Indala); - break; - } -} - -void RfidWorker::sq_write_validate() { - static const uint8_t data_size = LFRFID_KEY_SIZE; - uint8_t data[data_size] = {0}; - LfrfidKeyType type; - - bool result = reader.read(&type, data, data_size); - - if(result && (write_result != WriteResult::Ok)) { - if(validate_counts > (5 * 60)) { - write_result = WriteResult::NotWritable; - } - - if(type == key.get_type()) { - if(memcmp(data, key.get_data(), key.get_type_data_count()) == 0) { - write_result = WriteResult::Ok; - validate_counts = 0; - } else { - validate_counts++; - } - } else { - validate_counts++; - } - }; -} - -void RfidWorker::sq_write_stop_validate() { - reader.stop(); -} diff --git a/applications/lfrfid/helpers/rfid_worker.h b/applications/lfrfid/helpers/rfid_worker.h deleted file mode 100644 index 2c49ad14..00000000 --- a/applications/lfrfid/helpers/rfid_worker.h +++ /dev/null @@ -1,48 +0,0 @@ -#pragma once -#include "key_info.h" -#include "rfid_reader.h" -#include "rfid_writer.h" -#include "rfid_timer_emulator.h" -#include "rfid_key.h" -#include "state_sequencer.h" - -class RfidWorker { -public: - RfidWorker(); - ~RfidWorker(); - - void start_read(); - bool read(); - bool detect(); - bool any_read(); - void stop_read(); - - enum class WriteResult : uint8_t { - Ok, - NotWritable, - Nothing, - }; - - void start_write(); - WriteResult write(); - void stop_write(); - - void start_emulate(); - void stop_emulate(); - - RfidKey key; - -private: - RfidWriter writer; - RfidReader reader; - RfidTimerEmulator emulator; - - WriteResult write_result; - TickSequencer* write_sequence; - - void sq_write(); - void sq_write_start_validate(); - void sq_write_validate(); - uint16_t validate_counts; - void sq_write_stop_validate(); -}; diff --git a/applications/lfrfid/helpers/rfid_writer.cpp b/applications/lfrfid/helpers/rfid_writer.cpp deleted file mode 100644 index 31838fde..00000000 --- a/applications/lfrfid/helpers/rfid_writer.cpp +++ /dev/null @@ -1,183 +0,0 @@ -#include "rfid_writer.h" -#include "protocols/protocol_ioprox.h" -#include -#include "protocols/protocol_emmarin.h" -#include "protocols/protocol_hid_h10301.h" -#include "protocols/protocol_indala_40134.h" - -/** - * @brief all timings are specified in field clocks (field clock = 125 kHz, 8 us) - * - */ -class T55xxTiming { -public: - constexpr static const uint16_t wait_time = 400; - constexpr static const uint8_t start_gap = 30; - constexpr static const uint8_t write_gap = 18; - constexpr static const uint8_t data_0 = 24; - constexpr static const uint8_t data_1 = 56; - constexpr static const uint16_t program = 700; -}; - -class T55xxCmd { -public: - constexpr static const uint8_t opcode_page_0 = 0b10; - constexpr static const uint8_t opcode_page_1 = 0b11; - constexpr static const uint8_t opcode_reset = 0b00; -}; - -RfidWriter::RfidWriter() { -} - -RfidWriter::~RfidWriter() { -} - -void RfidWriter::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(); -} - -void RfidWriter::stop() { - furi_hal_rfid_tim_read_stop(); - furi_hal_rfid_tim_reset(); - furi_hal_rfid_pins_reset(); -} - -void RfidWriter::write_gap(uint32_t gap_time) { - furi_hal_rfid_tim_read_stop(); - furi_delay_us(gap_time * 8); - furi_hal_rfid_tim_read_start(); -} - -void RfidWriter::write_bit(bool value) { - if(value) { - furi_delay_us(T55xxTiming::data_1 * 8); - } else { - furi_delay_us(T55xxTiming::data_0 * 8); - } - write_gap(T55xxTiming::write_gap); -} - -void RfidWriter::write_byte(uint8_t value) { - for(uint8_t i = 0; i < 8; i++) { - write_bit((value >> i) & 1); - } -} - -void RfidWriter::write_block(uint8_t page, uint8_t block, bool lock_bit, uint32_t data) { - furi_delay_us(T55xxTiming::wait_time * 8); - - // start gap - write_gap(T55xxTiming::start_gap); - - // opcode - switch(page) { - case 0: - write_bit(1); - write_bit(0); - break; - case 1: - write_bit(1); - write_bit(1); - break; - default: - furi_check(false); - break; - } - - // lock bit - write_bit(lock_bit); - - // data - for(uint8_t i = 0; i < 32; i++) { - write_bit((data >> (31 - i)) & 1); - } - - // block address - write_bit((block >> 2) & 1); - write_bit((block >> 1) & 1); - write_bit((block >> 0) & 1); - - furi_delay_us(T55xxTiming::program * 8); - - furi_delay_us(T55xxTiming::wait_time * 8); - write_reset(); -} - -void RfidWriter::write_reset() { - write_gap(T55xxTiming::start_gap); - write_bit(1); - write_bit(0); -} - -void RfidWriter::write_em(const uint8_t em_data[5]) { - ProtocolEMMarin em_card; - uint64_t em_encoded_data; - em_card.encode(em_data, 5, reinterpret_cast(&em_encoded_data), sizeof(uint64_t)); - const uint32_t em_config_block_data = 0b00000000000101001000000001000000; - - FURI_CRITICAL_ENTER(); - write_block(0, 0, false, em_config_block_data); - write_block(0, 1, false, em_encoded_data); - write_block(0, 2, false, em_encoded_data >> 32); - write_reset(); - FURI_CRITICAL_EXIT(); -} - -void RfidWriter::write_hid(const uint8_t hid_data[3]) { - ProtocolHID10301 hid_card; - uint32_t card_data[3]; - hid_card.encode(hid_data, 3, reinterpret_cast(&card_data), sizeof(card_data) * 3); - - const uint32_t hid_config_block_data = 0b00000000000100000111000001100000; - - FURI_CRITICAL_ENTER(); - write_block(0, 0, false, hid_config_block_data); - write_block(0, 1, false, card_data[0]); - write_block(0, 2, false, card_data[1]); - write_block(0, 3, false, card_data[2]); - write_reset(); - FURI_CRITICAL_EXIT(); -} - -/** Endian fixup. Translates an ioprox block into a t5577 block */ -static uint32_t ioprox_encode_block(const uint8_t block_data[4]) { - uint8_t raw_card_data[] = {block_data[3], block_data[2], block_data[1], block_data[0]}; - return *reinterpret_cast(&raw_card_data); -} - -void RfidWriter::write_ioprox(const uint8_t ioprox_data[4]) { - ProtocolIoProx ioprox_card; - - uint8_t encoded_data[8]; - ioprox_card.encode(ioprox_data, 4, encoded_data, sizeof(encoded_data)); - - const uint32_t ioprox_config_block_data = 0b00000000000101000111000001000000; - - FURI_CRITICAL_ENTER(); - write_block(0, 0, false, ioprox_config_block_data); - write_block(0, 1, false, ioprox_encode_block(&encoded_data[0])); - write_block(0, 2, false, ioprox_encode_block(&encoded_data[4])); - write_reset(); - FURI_CRITICAL_EXIT(); -} - -void RfidWriter::write_indala(const uint8_t indala_data[3]) { - ProtocolIndala40134 indala_card; - uint32_t card_data[2]; - indala_card.encode( - indala_data, 3, reinterpret_cast(&card_data), sizeof(card_data) * 2); - - const uint32_t indala_config_block_data = 0b00000000000010000001000001000000; - - FURI_CRITICAL_ENTER(); - write_block(0, 0, false, indala_config_block_data); - write_block(0, 1, false, card_data[0]); - write_block(0, 2, false, card_data[1]); - write_reset(); - FURI_CRITICAL_EXIT(); -} diff --git a/applications/lfrfid/helpers/rfid_writer.h b/applications/lfrfid/helpers/rfid_writer.h deleted file mode 100644 index 98d2bf95..00000000 --- a/applications/lfrfid/helpers/rfid_writer.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once -#include "stdint.h" - -class RfidWriter { -public: - RfidWriter(); - ~RfidWriter(); - void start(); - void stop(); - void write_em(const uint8_t em_data[5]); - void write_hid(const uint8_t hid_data[3]); - void write_ioprox(const uint8_t ioprox_data[4]); - void write_indala(const uint8_t indala_data[3]); - -private: - void write_gap(uint32_t gap_time); - void write_bit(bool value); - void write_byte(uint8_t value); - void write_block(uint8_t page, uint8_t block, bool lock_bit, uint32_t data); - void write_reset(); -}; diff --git a/applications/lfrfid/helpers/state_sequencer.cpp b/applications/lfrfid/helpers/state_sequencer.cpp deleted file mode 100644 index e6718df5..00000000 --- a/applications/lfrfid/helpers/state_sequencer.cpp +++ /dev/null @@ -1,50 +0,0 @@ -#include "state_sequencer.h" -#include "stdio.h" - -TickSequencer::TickSequencer() { -} - -TickSequencer::~TickSequencer() { -} - -void TickSequencer::tick() { - if(tick_count == list_it->first) { - tick_count = 0; - - list_it++; - if(list_it == list.end()) { - list_it = list.begin(); - } - } - - list_it->second(); - tick_count++; -} - -void TickSequencer::reset() { - list_it = list.begin(); - tick_count = 0; -} - -void TickSequencer::clear() { - list.clear(); - reset(); -} - -void TickSequencer::do_every_tick(uint32_t tick_count, std::function fn) { - list.push_back(std::make_pair(tick_count, fn)); - reset(); -} - -void TickSequencer::do_after_tick(uint32_t tick_count, std::function fn) { - if(tick_count > 1) { - list.push_back( - std::make_pair(tick_count - 1, std::bind(&TickSequencer::do_nothing, this))); - } - list.push_back(std::make_pair(1, fn)); - - reset(); -} - -void TickSequencer::do_nothing() { -} diff --git a/applications/lfrfid/helpers/state_sequencer.h b/applications/lfrfid/helpers/state_sequencer.h deleted file mode 100644 index 12512ab5..00000000 --- a/applications/lfrfid/helpers/state_sequencer.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once -#include "stdint.h" -#include -#include - -class TickSequencer { -public: - TickSequencer(); - ~TickSequencer(); - - void tick(); - void reset(); - void clear(); - - void do_every_tick(uint32_t tick_count, std::function fn); - void do_after_tick(uint32_t tick_count, std::function fn); - -private: - std::list > > list; - std::list > >::iterator list_it; - - uint32_t tick_count; - - void do_nothing(); -}; diff --git a/applications/lfrfid/lfrfid_app.cpp b/applications/lfrfid/lfrfid_app.cpp index f1a575de..9373ca8c 100644 --- a/applications/lfrfid/lfrfid_app.cpp +++ b/applications/lfrfid/lfrfid_app.cpp @@ -21,6 +21,11 @@ #include "scene/lfrfid_app_scene_delete_confirm.h" #include "scene/lfrfid_app_scene_delete_success.h" #include "scene/lfrfid_app_scene_rpc.h" +#include "scene/lfrfid_app_scene_extra_actions.h" +#include "scene/lfrfid_app_scene_raw_info.h" +#include "scene/lfrfid_app_scene_raw_name.h" +#include "scene/lfrfid_app_scene_raw_read.h" +#include "scene/lfrfid_app_scene_raw_success.h" #include #include @@ -28,24 +33,44 @@ #include const char* LfRfidApp::app_folder = ANY_PATH("lfrfid"); +const char* LfRfidApp::app_sd_folder = EXT_PATH("lfrfid"); const char* LfRfidApp::app_extension = ".rfid"; const char* LfRfidApp::app_filetype = "Flipper RFID key"; LfRfidApp::LfRfidApp() : scene_controller{this} - , notification{"notification"} - , storage{"storage"} - , dialogs{"dialogs"} + , notification{RECORD_NOTIFICATION} + , storage{RECORD_STORAGE} + , dialogs{RECORD_DIALOGS} , text_store(40) { + string_init(file_name); + string_init(raw_file_name); string_init_set_str(file_path, app_folder); + + dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax); + + size_t size = protocol_dict_get_max_data_size(dict); + new_key_data = (uint8_t*)malloc(size); + old_key_data = (uint8_t*)malloc(size); + + lfworker = lfrfid_worker_alloc(dict); } LfRfidApp::~LfRfidApp() { + string_clear(raw_file_name); + string_clear(file_name); string_clear(file_path); + protocol_dict_free(dict); + + lfrfid_worker_free(lfworker); + if(rpc_ctx) { rpc_system_app_set_callback(rpc_ctx, NULL, NULL); rpc_system_app_send_exited(rpc_ctx); } + + free(new_key_data); + free(old_key_data); } static void rpc_command_callback(RpcAppSystemEvent rpc_event, void* context) { @@ -88,7 +113,7 @@ void LfRfidApp::run(void* _args) { scene_controller.process(100, SceneType::Rpc); } else { string_set_str(file_path, args); - load_key_data(file_path, &worker.key, true); + load_key_data(file_path, true); view_controller.attach_to_gui(ViewDispatcherTypeFullscreen); scene_controller.add_scene(SceneType::Emulate, new LfRfidAppSceneEmulate()); scene_controller.process(100, SceneType::Emulate); @@ -114,11 +139,16 @@ void LfRfidApp::run(void* _args) { scene_controller.add_scene(SceneType::SavedInfo, new LfRfidAppSceneSavedInfo()); scene_controller.add_scene(SceneType::DeleteConfirm, new LfRfidAppSceneDeleteConfirm()); scene_controller.add_scene(SceneType::DeleteSuccess, new LfRfidAppSceneDeleteSuccess()); + scene_controller.add_scene(SceneType::ExtraActions, new LfRfidAppSceneExtraActions()); + scene_controller.add_scene(SceneType::RawInfo, new LfRfidAppSceneRawInfo()); + scene_controller.add_scene(SceneType::RawName, new LfRfidAppSceneRawName()); + scene_controller.add_scene(SceneType::RawRead, new LfRfidAppSceneRawRead()); + scene_controller.add_scene(SceneType::RawSuccess, new LfRfidAppSceneRawSuccess()); scene_controller.process(100); } } -bool LfRfidApp::save_key(RfidKey* key) { +bool LfRfidApp::save_key() { bool result = false; make_app_folder(); @@ -128,9 +158,9 @@ bool LfRfidApp::save_key(RfidKey* key) { string_left(file_path, filename_start); } - string_cat_printf(file_path, "/%s%s", key->get_name(), app_extension); + string_cat_printf(file_path, "/%s%s", string_get_cstr(file_name), app_extension); - result = save_key_data(file_path, key); + result = save_key_data(file_path); return result; } @@ -143,56 +173,27 @@ bool LfRfidApp::load_key_from_file_select(bool need_restore) { dialogs, file_path, file_path, app_extension, true, &I_125_10px, true); if(result) { - result = load_key_data(file_path, &worker.key, true); + result = load_key_data(file_path, true); } return result; } -bool LfRfidApp::delete_key(RfidKey* key) { - UNUSED(key); +bool LfRfidApp::delete_key() { return storage_simply_remove(storage, string_get_cstr(file_path)); } -bool LfRfidApp::load_key_data(string_t path, RfidKey* key, bool show_dialog) { - FlipperFormat* file = flipper_format_file_alloc(storage); +bool LfRfidApp::load_key_data(string_t path, bool show_dialog) { bool result = false; - string_t str_result; - string_init(str_result); do { - if(!flipper_format_file_open_existing(file, string_get_cstr(path))) break; + protocol_id = lfrfid_dict_file_load(dict, string_get_cstr(path)); + if(protocol_id == PROTOCOL_NO) break; - // header - uint32_t version; - if(!flipper_format_read_header(file, str_result, &version)) break; - if(string_cmp_str(str_result, app_filetype) != 0) break; - if(version != 1) break; - - // key type - LfrfidKeyType type; - RfidKey loaded_key; - - if(!flipper_format_read_string(file, "Key type", str_result)) break; - if(!lfrfid_key_get_string_type(string_get_cstr(str_result), &type)) break; - loaded_key.set_type(type); - - // key data - uint8_t key_data[loaded_key.get_type_data_count()] = {}; - if(!flipper_format_read_hex(file, "Data", key_data, loaded_key.get_type_data_count())) - break; - loaded_key.set_data(key_data, loaded_key.get_type_data_count()); - - path_extract_filename(path, str_result, true); - loaded_key.set_name(string_get_cstr(str_result)); - - *key = loaded_key; + path_extract_filename(path, file_name, true); result = true; } while(0); - flipper_format_free(file); - string_clear(str_result); - if((!result) && (show_dialog)) { dialog_message_show_storage_error(dialogs, "Cannot load\nkey file"); } @@ -200,27 +201,8 @@ bool LfRfidApp::load_key_data(string_t path, RfidKey* key, bool show_dialog) { return result; } -bool LfRfidApp::save_key_data(string_t path, RfidKey* key) { - FlipperFormat* file = flipper_format_file_alloc(storage); - bool result = false; - - do { - if(!flipper_format_file_open_always(file, string_get_cstr(path))) break; - if(!flipper_format_write_header_cstr(file, app_filetype, 1)) break; - if(!flipper_format_write_comment_cstr(file, "Key type can be EM4100, H10301 or I40134")) - break; - if(!flipper_format_write_string_cstr( - file, "Key type", lfrfid_key_get_type_string(key->get_type()))) - break; - if(!flipper_format_write_comment_cstr( - file, "Data size for EM4100 is 5, for H10301 is 3, for I40134 is 3")) - break; - if(!flipper_format_write_hex(file, "Data", key->get_data(), key->get_type_data_count())) - break; - result = true; - } while(0); - - flipper_format_free(file); +bool LfRfidApp::save_key_data(string_t path) { + bool result = lfrfid_dict_file_save(dict, protocol_id, string_get_cstr(path)); if(!result) { dialog_message_show_storage_error(dialogs, "Cannot save\nkey file"); diff --git a/applications/lfrfid/lfrfid_app.h b/applications/lfrfid/lfrfid_app.h index db022c9a..153218db 100644 --- a/applications/lfrfid/lfrfid_app.h +++ b/applications/lfrfid/lfrfid_app.h @@ -20,9 +20,15 @@ #include #include -#include "helpers/rfid_worker.h" #include "rpc/rpc_app.h" +#include +#include +#include +#include + +#define LFRFID_KEY_NAME_SIZE 22 + class LfRfidApp { public: enum class EventType : uint8_t { @@ -32,7 +38,19 @@ public: Stay, Retry, Exit, - EmulateStart, + ReadEventSenseStart, + ReadEventSenseEnd, + ReadEventSenseCardStart, + ReadEventSenseCardEnd, + ReadEventStartASK, + ReadEventStartPSK, + ReadEventDone, + ReadEventOverrun, + ReadEventError, + WriteEventOK, + WriteEventProtocolCannotBeWritten, + WriteEventFobCannotBeWritten, + WriteEventTooLongToWrite, RpcLoadFile, RpcSessionClose, }; @@ -57,12 +75,17 @@ public: DeleteConfirm, DeleteSuccess, Rpc, + ExtraActions, + RawInfo, + RawName, + RawRead, + RawSuccess, }; class Event { public: union { - int32_t menu_index; + int32_t signed_int; } payload; EventType type; @@ -79,8 +102,6 @@ public: RecordController storage; RecordController dialogs; - RfidWorker worker; - TextStore text_store; string_t file_path; @@ -90,15 +111,27 @@ public: void run(void* args); static const char* app_folder; + static const char* app_sd_folder; static const char* app_extension; static const char* app_filetype; - bool save_key(RfidKey* key); + bool save_key(); bool load_key_from_file_select(bool need_restore); - bool delete_key(RfidKey* key); + bool delete_key(); - bool load_key_data(string_t path, RfidKey* key, bool show_dialog); - bool save_key_data(string_t path, RfidKey* key); + bool load_key_data(string_t path, bool show_dialog); + bool save_key_data(string_t path); void make_app_folder(); + + ProtocolDict* dict; + LFRFIDWorker* lfworker; + string_t file_name; + ProtocolId protocol_id; + LFRFIDWorkerReadType read_type; + + uint8_t* old_key_data; + uint8_t* new_key_data; + + string_t raw_file_name; }; diff --git a/applications/lfrfid/lfrfid_cli.c b/applications/lfrfid/lfrfid_cli.c new file mode 100644 index 00000000..9a6930a6 --- /dev/null +++ b/applications/lfrfid/lfrfid_cli.c @@ -0,0 +1,575 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +static void lfrfid_cli(Cli* cli, string_t args, void* context); + +// app cli function +void lfrfid_on_system_start() { + Cli* cli = furi_record_open(RECORD_CLI); + cli_add_command(cli, "rfid", CliCommandFlagDefault, lfrfid_cli, NULL); + furi_record_close(RECORD_CLI); +} + +static void lfrfid_cli_print_usage() { + printf("Usage:\r\n"); + printf("rfid read \r\n"); + printf("rfid \r\n"); + printf("rfid raw_read \r\n"); + printf("rfid raw_emulate \r\n"); +}; + +typedef struct { + ProtocolId protocol; + FuriEventFlag* event; +} LFRFIDCliReadContext; + +static void lfrfid_cli_read_callback(LFRFIDWorkerReadResult result, ProtocolId proto, void* ctx) { + furi_assert(ctx); + LFRFIDCliReadContext* context = ctx; + if(result == LFRFIDWorkerReadDone) { + context->protocol = proto; + FURI_SW_MEMBARRIER(); + } + furi_event_flag_set(context->event, 1 << result); +} + +static void lfrfid_cli_read(Cli* cli, string_t args) { + string_t type_string; + string_init(type_string); + LFRFIDWorkerReadType type = LFRFIDWorkerReadTypeAuto; + + if(args_read_string_and_trim(args, type_string)) { + if(string_cmp_str(type_string, "normal") == 0 || string_cmp_str(type_string, "ask") == 0) { + // ask + type = LFRFIDWorkerReadTypeASKOnly; + } else if( + string_cmp_str(type_string, "indala") == 0 || + string_cmp_str(type_string, "psk") == 0) { + // psk + type = LFRFIDWorkerReadTypePSKOnly; + } else { + lfrfid_cli_print_usage(); + string_clear(type_string); + return; + } + } + string_clear(type_string); + + ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax); + LFRFIDWorker* worker = lfrfid_worker_alloc(dict); + LFRFIDCliReadContext context; + context.protocol = PROTOCOL_NO; + context.event = furi_event_flag_alloc(); + + lfrfid_worker_start_thread(worker); + + printf("Reading RFID...\r\nPress Ctrl+C to abort\r\n"); + + const uint32_t available_flags = (1 << LFRFIDWorkerReadDone); + + lfrfid_worker_read_start(worker, type, lfrfid_cli_read_callback, &context); + + while(true) { + uint32_t flags = + furi_event_flag_wait(context.event, available_flags, FuriFlagWaitAny, 100); + + if(flags != FuriFlagErrorTimeout) { + if(FURI_BIT(flags, LFRFIDWorkerReadDone)) { + break; + } + } + + if(cli_cmd_interrupt_received(cli)) break; + } + + lfrfid_worker_stop(worker); + lfrfid_worker_stop_thread(worker); + lfrfid_worker_free(worker); + + if(context.protocol != PROTOCOL_NO) { + printf("%s ", protocol_dict_get_name(dict, context.protocol)); + + size_t size = protocol_dict_get_data_size(dict, context.protocol); + uint8_t* data = malloc(size); + protocol_dict_get_data(dict, context.protocol, data, size); + for(size_t i = 0; i < size; i++) { + printf("%02X", data[i]); + } + printf("\r\n"); + free(data); + + string_t info; + string_init(info); + protocol_dict_render_data(dict, info, context.protocol); + if(string_size(info) > 0) { + printf("%s\r\n", string_get_cstr(info)); + } + string_clear(info); + } + + printf("Reading stopped\r\n"); + protocol_dict_free(dict); + + furi_event_flag_free(context.event); +} + +static bool lfrfid_cli_parse_args(string_t args, ProtocolDict* dict, ProtocolId* protocol) { + bool result = false; + string_t protocol_name, data_text; + string_init(protocol_name); + string_init(data_text); + size_t data_size = protocol_dict_get_max_data_size(dict); + uint8_t* data = malloc(data_size); + + do { + // load args + if(!args_read_string_and_trim(args, protocol_name) || + !args_read_string_and_trim(args, data_text)) { + lfrfid_cli_print_usage(); + break; + } + + // check protocol arg + *protocol = protocol_dict_get_protocol_by_name(dict, string_get_cstr(protocol_name)); + if(*protocol == PROTOCOL_NO) { + printf( + "Unknown protocol: %s\r\n" + "Available protocols:\r\n", + string_get_cstr(protocol_name)); + + for(ProtocolId i = 0; i < LFRFIDProtocolMax; i++) { + printf( + "\t%s, %d bytes long\r\n", + protocol_dict_get_name(dict, i), + protocol_dict_get_data_size(dict, i)); + } + break; + } + + data_size = protocol_dict_get_data_size(dict, *protocol); + + // check data arg + if(!args_read_hex_bytes(data_text, data, data_size)) { + printf( + "%s data needs to be %d bytes long\r\n", + protocol_dict_get_name(dict, *protocol), + data_size); + break; + } + + // load data to protocol + protocol_dict_set_data(dict, *protocol, data, data_size); + + result = true; + } while(false); + + free(data); + string_clear(protocol_name); + string_clear(data_text); + return result; +} + +static void lfrfid_cli_write_callback(LFRFIDWorkerWriteResult result, void* ctx) { + furi_assert(ctx); + FuriEventFlag* events = ctx; + furi_event_flag_set(events, 1 << result); +} + +static void lfrfid_cli_write(Cli* cli, string_t args) { + ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax); + ProtocolId protocol; + + if(!lfrfid_cli_parse_args(args, dict, &protocol)) { + protocol_dict_free(dict); + return; + } + + LFRFIDWorker* worker = lfrfid_worker_alloc(dict); + FuriEventFlag* event = furi_event_flag_alloc(); + + lfrfid_worker_start_thread(worker); + lfrfid_worker_write_start(worker, protocol, lfrfid_cli_write_callback, event); + + printf("Writing RFID...\r\nPress Ctrl+C to abort\r\n"); + const uint32_t available_flags = (1 << LFRFIDWorkerWriteOK) | + (1 << LFRFIDWorkerWriteProtocolCannotBeWritten) | + (1 << LFRFIDWorkerWriteFobCannotBeWritten); + + while(!cli_cmd_interrupt_received(cli)) { + uint32_t flags = furi_event_flag_wait(event, available_flags, FuriFlagWaitAny, 100); + if(flags != FuriFlagErrorTimeout) { + if(FURI_BIT(flags, LFRFIDWorkerWriteOK)) { + printf("Written!\r\n"); + break; + } + + if(FURI_BIT(flags, LFRFIDWorkerWriteProtocolCannotBeWritten)) { + printf("This protocol cannot be written.\r\n"); + break; + } + + if(FURI_BIT(flags, LFRFIDWorkerWriteFobCannotBeWritten)) { + printf("Seems this fob cannot be written.\r\n"); + } + } + } + printf("Writing stopped\r\n"); + + lfrfid_worker_stop(worker); + lfrfid_worker_stop_thread(worker); + lfrfid_worker_free(worker); + protocol_dict_free(dict); + furi_event_flag_free(event); +} + +static void lfrfid_cli_emulate(Cli* cli, string_t args) { + ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax); + ProtocolId protocol; + + if(!lfrfid_cli_parse_args(args, dict, &protocol)) { + protocol_dict_free(dict); + return; + } + + LFRFIDWorker* worker = lfrfid_worker_alloc(dict); + + lfrfid_worker_start_thread(worker); + lfrfid_worker_emulate_start(worker, protocol); + + printf("Emulating RFID...\r\nPress Ctrl+C to abort\r\n"); + while(!cli_cmd_interrupt_received(cli)) { + furi_delay_ms(100); + } + printf("Emulation stopped\r\n"); + + lfrfid_worker_stop(worker); + lfrfid_worker_stop_thread(worker); + lfrfid_worker_free(worker); + protocol_dict_free(dict); +} + +static void lfrfid_cli_raw_analyze(Cli* cli, string_t args) { + UNUSED(cli); + string_t filepath, info_string; + string_init(filepath); + string_init(info_string); + Storage* storage = furi_record_open(RECORD_STORAGE); + LFRFIDRawFile* file = lfrfid_raw_file_alloc(storage); + + do { + float frequency = 0; + float duty_cycle = 0; + + if(!args_read_probably_quoted_string_and_trim(args, filepath)) { + lfrfid_cli_print_usage(); + break; + } + + if(!lfrfid_raw_file_open_read(file, string_get_cstr(filepath))) { + printf("Failed to open file\r\n"); + break; + } + + if(!lfrfid_raw_file_read_header(file, &frequency, &duty_cycle)) { + printf("Invalid header\r\n"); + break; + } + + bool file_end = false; + uint32_t total_warns = 0; + uint32_t total_duration = 0; + uint32_t total_pulse = 0; + ProtocolId total_protocol = PROTOCOL_NO; + + ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax); + protocol_dict_decoders_start(dict); + + while(!file_end) { + uint32_t pulse = 0; + uint32_t duration = 0; + if(lfrfid_raw_file_read_pair(file, &duration, &pulse, &file_end)) { + bool warn = false; + + if(pulse > duration || pulse <= 0 || duration <= 0) { + total_warns += 1; + warn = true; + } + + string_printf(info_string, "[%ld %ld]", pulse, duration); + printf("%-16s", string_get_cstr(info_string)); + string_printf(info_string, "[%ld %ld]", pulse, duration - pulse); + printf("%-16s", string_get_cstr(info_string)); + + if(warn) { + printf(" <<----"); + } + + if(total_protocol == PROTOCOL_NO) { + total_protocol = protocol_dict_decoders_feed(dict, true, pulse); + if(total_protocol == PROTOCOL_NO) { + total_protocol = + protocol_dict_decoders_feed(dict, false, duration - pulse); + } + + if(total_protocol != PROTOCOL_NO) { + printf(" ", protocol_dict_get_name(dict, total_protocol)); + } + } + + printf("\r\n"); + + total_pulse += pulse; + total_duration += duration; + + if(total_protocol != PROTOCOL_NO) { + break; + } + } else { + printf("Failed to read pair\r\n"); + break; + } + } + + printf(" Frequency: %f\r\n", (double)frequency); + printf(" Duty Cycle: %f\r\n", (double)duty_cycle); + printf(" Warns: %ld\r\n", total_warns); + printf(" Pulse sum: %ld\r\n", total_pulse); + printf("Duration sum: %ld\r\n", total_duration); + printf(" Average: %f\r\n", (double)((float)total_pulse / (float)total_duration)); + printf(" Protocol: "); + + if(total_protocol != PROTOCOL_NO) { + size_t data_size = protocol_dict_get_data_size(dict, total_protocol); + uint8_t* data = malloc(data_size); + protocol_dict_get_data(dict, total_protocol, data, data_size); + + printf("%s [", protocol_dict_get_name(dict, total_protocol)); + for(size_t i = 0; i < data_size; i++) { + printf("%02X", data[i]); + if(i < data_size - 1) { + printf(" "); + } + } + printf("]\r\n"); + + protocol_dict_render_data(dict, info_string, total_protocol); + printf("%s\r\n", string_get_cstr(info_string)); + + free(data); + } else { + printf("not found\r\n"); + } + + protocol_dict_free(dict); + } while(false); + + string_clear(filepath); + string_clear(info_string); + lfrfid_raw_file_free(file); + furi_record_close(RECORD_STORAGE); +} + +static void lfrfid_cli_raw_read_callback(LFRFIDWorkerReadRawResult result, void* context) { + furi_assert(context); + FuriEventFlag* event = context; + furi_event_flag_set(event, 1 << result); +} + +static void lfrfid_cli_raw_read(Cli* cli, string_t args) { + UNUSED(cli); + + string_t filepath, type_string; + string_init(filepath); + string_init(type_string); + LFRFIDWorkerReadType type = LFRFIDWorkerReadTypeAuto; + + do { + if(args_read_string_and_trim(args, type_string)) { + if(string_cmp_str(type_string, "normal") == 0 || + string_cmp_str(type_string, "ask") == 0) { + // ask + type = LFRFIDWorkerReadTypeASKOnly; + } else if( + string_cmp_str(type_string, "indala") == 0 || + string_cmp_str(type_string, "psk") == 0) { + // psk + type = LFRFIDWorkerReadTypePSKOnly; + } else { + lfrfid_cli_print_usage(); + break; + } + } + + if(!args_read_probably_quoted_string_and_trim(args, filepath)) { + lfrfid_cli_print_usage(); + break; + } + + ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax); + LFRFIDWorker* worker = lfrfid_worker_alloc(dict); + FuriEventFlag* event = furi_event_flag_alloc(); + + lfrfid_worker_start_thread(worker); + + bool overrun = false; + + const uint32_t available_flags = (1 << LFRFIDWorkerReadRawFileError) | + (1 << LFRFIDWorkerReadRawOverrun); + + lfrfid_worker_read_raw_start( + worker, string_get_cstr(filepath), type, lfrfid_cli_raw_read_callback, event); + while(true) { + uint32_t flags = furi_event_flag_wait(event, available_flags, FuriFlagWaitAny, 100); + + if(flags != FuriFlagErrorTimeout) { + if(FURI_BIT(flags, LFRFIDWorkerReadRawFileError)) { + printf("File is not RFID raw file\r\n"); + break; + } + + if(FURI_BIT(flags, LFRFIDWorkerReadRawOverrun)) { + if(!overrun) { + printf("Overrun\r\n"); + overrun = true; + } + } + } + + if(cli_cmd_interrupt_received(cli)) break; + } + + if(overrun) { + printf("An overrun occurred during read\r\n"); + } + + lfrfid_worker_stop(worker); + + lfrfid_worker_stop_thread(worker); + lfrfid_worker_free(worker); + protocol_dict_free(dict); + + furi_event_flag_free(event); + + } while(false); + + string_clear(filepath); + string_clear(type_string); +} + +static void lfrfid_cli_raw_emulate_callback(LFRFIDWorkerEmulateRawResult result, void* context) { + furi_assert(context); + FuriEventFlag* event = context; + furi_event_flag_set(event, 1 << result); +} + +static void lfrfid_cli_raw_emulate(Cli* cli, string_t args) { + UNUSED(cli); + + string_t filepath; + string_init(filepath); + Storage* storage = furi_record_open(RECORD_STORAGE); + + do { + if(!args_read_probably_quoted_string_and_trim(args, filepath)) { + lfrfid_cli_print_usage(); + break; + } + + if(!storage_file_exists(storage, string_get_cstr(filepath))) { + printf("File not found: \"%s\"\r\n", string_get_cstr(filepath)); + break; + } + + ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax); + LFRFIDWorker* worker = lfrfid_worker_alloc(dict); + FuriEventFlag* event = furi_event_flag_alloc(); + + lfrfid_worker_start_thread(worker); + + bool overrun = false; + + const uint32_t available_flags = (1 << LFRFIDWorkerEmulateRawFileError) | + (1 << LFRFIDWorkerEmulateRawOverrun); + + lfrfid_worker_emulate_raw_start( + worker, string_get_cstr(filepath), lfrfid_cli_raw_emulate_callback, event); + while(true) { + uint32_t flags = furi_event_flag_wait(event, available_flags, FuriFlagWaitAny, 100); + + if(flags != FuriFlagErrorTimeout) { + if(FURI_BIT(flags, LFRFIDWorkerEmulateRawFileError)) { + printf("File is not RFID raw file\r\n"); + break; + } + + if(FURI_BIT(flags, LFRFIDWorkerEmulateRawOverrun)) { + if(!overrun) { + printf("Overrun\r\n"); + overrun = true; + } + } + } + + if(cli_cmd_interrupt_received(cli)) break; + } + + if(overrun) { + printf("An overrun occurred during emulation\r\n"); + } + + lfrfid_worker_stop(worker); + + lfrfid_worker_stop_thread(worker); + lfrfid_worker_free(worker); + protocol_dict_free(dict); + + furi_event_flag_free(event); + + } while(false); + + furi_record_close(RECORD_STORAGE); + string_clear(filepath); +} + +static void lfrfid_cli(Cli* cli, string_t args, void* context) { + UNUSED(context); + string_t cmd; + string_init(cmd); + + if(!args_read_string_and_trim(args, cmd)) { + string_clear(cmd); + lfrfid_cli_print_usage(); + return; + } + + if(string_cmp_str(cmd, "read") == 0) { + lfrfid_cli_read(cli, args); + } else if(string_cmp_str(cmd, "write") == 0) { + lfrfid_cli_write(cli, args); + } else if(string_cmp_str(cmd, "emulate") == 0) { + lfrfid_cli_emulate(cli, args); + } else if(string_cmp_str(cmd, "raw_read") == 0) { + lfrfid_cli_raw_read(cli, args); + } else if(string_cmp_str(cmd, "raw_emulate") == 0) { + lfrfid_cli_raw_emulate(cli, args); + } else if(string_cmp_str(cmd, "raw_analyze") == 0) { + lfrfid_cli_raw_analyze(cli, args); + } else { + lfrfid_cli_print_usage(); + } + + string_clear(cmd); +} \ No newline at end of file diff --git a/applications/lfrfid/lfrfid_cli.cpp b/applications/lfrfid/lfrfid_cli.cpp deleted file mode 100644 index 732197e9..00000000 --- a/applications/lfrfid/lfrfid_cli.cpp +++ /dev/null @@ -1,177 +0,0 @@ -#include -#include -#include -#include -#include - -#include "helpers/rfid_reader.h" -#include "helpers/rfid_timer_emulator.h" - -static void lfrfid_cli(Cli* cli, string_t args, void* context); - -// app cli function -extern "C" void lfrfid_on_system_start() { -#ifdef SRV_CLI - Cli* cli = static_cast(furi_record_open("cli")); - cli_add_command(cli, "rfid", CliCommandFlagDefault, lfrfid_cli, NULL); - furi_record_close("cli"); -#else - UNUSED(lfrfid_cli); -#endif -} - -void lfrfid_cli_print_usage() { - printf("Usage:\r\n"); - printf("rfid read \r\n"); - printf("rfid \r\n"); - printf("\t choose from:\r\n"); - printf("\tEM4100, EM-Marin (5 bytes key_data)\r\n"); - printf("\tH10301, HID26 (3 bytes key_data)\r\n"); - printf("\tI40134, Indala (3 bytes key_data)\r\n"); - printf("\tIoProxXSF, IoProx (4 bytes key_data)\r\n"); - printf("\t are hex-formatted\r\n"); -}; - -static bool lfrfid_cli_get_key_type(string_t data, LfrfidKeyType* type) { - bool result = false; - - if(string_cmp_str(data, "EM4100") == 0 || string_cmp_str(data, "EM-Marin") == 0) { - result = true; - *type = LfrfidKeyType::KeyEM4100; - } else if(string_cmp_str(data, "H10301") == 0 || string_cmp_str(data, "HID26") == 0) { - result = true; - *type = LfrfidKeyType::KeyH10301; - } else if(string_cmp_str(data, "I40134") == 0 || string_cmp_str(data, "Indala") == 0) { - result = true; - *type = LfrfidKeyType::KeyI40134; - } else if(string_cmp_str(data, "IoProxXSF") == 0 || string_cmp_str(data, "IoProx") == 0) { - result = true; - *type = LfrfidKeyType::KeyIoProxXSF; - } - - return result; -} - -static void lfrfid_cli_read(Cli* cli, string_t args) { - RfidReader reader; - string_t type_string; - string_init(type_string); - bool simple_mode = true; - LfrfidKeyType type; - RfidReader::Type reader_type = RfidReader::Type::Normal; - static const uint8_t data_size = LFRFID_KEY_SIZE; - uint8_t data[data_size] = {0}; - - if(args_read_string_and_trim(args, type_string)) { - simple_mode = false; - - if(string_cmp_str(type_string, "normal") == 0) { - reader_type = RfidReader::Type::Normal; - } else if(string_cmp_str(type_string, "indala") == 0) { - reader_type = RfidReader::Type::Indala; - } else { - lfrfid_cli_print_usage(); - string_clear(type_string); - return; - } - } - - if(simple_mode) { - reader.start(); - } else { - reader.start_forced(reader_type); - } - - printf("Reading RFID...\r\nPress Ctrl+C to abort\r\n"); - while(!cli_cmd_interrupt_received(cli)) { - if(reader.read(&type, data, data_size, simple_mode)) { - printf("%s", lfrfid_key_get_type_string(type)); - printf(" "); - - for(uint8_t i = 0; i < lfrfid_key_get_type_data_count(type); i++) { - printf("%02X", data[i]); - } - printf("\r\n"); - break; - } - furi_delay_ms(100); - } - - printf("Reading stopped\r\n"); - reader.stop(); - - string_clear(type_string); -} - -static void lfrfid_cli_write(Cli* cli, string_t args) { - UNUSED(cli); - UNUSED(args); - // TODO implement rfid write - printf("Not Implemented :(\r\n"); -} - -static void lfrfid_cli_emulate(Cli* cli, string_t args) { - string_t data; - string_init(data); - RfidTimerEmulator emulator; - - static const uint8_t data_size = LFRFID_KEY_SIZE; - uint8_t key_data[data_size] = {0}; - uint8_t key_data_size = 0; - LfrfidKeyType type; - - if(!args_read_string_and_trim(args, data)) { - lfrfid_cli_print_usage(); - string_clear(data); - return; - } - - if(!lfrfid_cli_get_key_type(data, &type)) { - lfrfid_cli_print_usage(); - string_clear(data); - return; - } - - key_data_size = lfrfid_key_get_type_data_count(type); - - if(!args_read_hex_bytes(args, key_data, key_data_size)) { - lfrfid_cli_print_usage(); - string_clear(data); - return; - } - - emulator.start(type, key_data, key_data_size); - - printf("Emulating RFID...\r\nPress Ctrl+C to abort\r\n"); - while(!cli_cmd_interrupt_received(cli)) { - furi_delay_ms(100); - } - printf("Emulation stopped\r\n"); - emulator.stop(); - - string_clear(data); -} - -static void lfrfid_cli(Cli* cli, string_t args, void* context) { - UNUSED(context); - string_t cmd; - string_init(cmd); - - if(!args_read_string_and_trim(args, cmd)) { - string_clear(cmd); - lfrfid_cli_print_usage(); - return; - } - - if(string_cmp_str(cmd, "read") == 0) { - lfrfid_cli_read(cli, args); - } else if(string_cmp_str(cmd, "write") == 0) { - lfrfid_cli_write(cli, args); - } else if(string_cmp_str(cmd, "emulate") == 0) { - lfrfid_cli_emulate(cli, args); - } else { - lfrfid_cli_print_usage(); - } - - string_clear(cmd); -} diff --git a/applications/lfrfid/scene/lfrfid_app_scene_delete_confirm.cpp b/applications/lfrfid/scene/lfrfid_app_scene_delete_confirm.cpp index 236ca8c2..58ff4dcd 100644 --- a/applications/lfrfid/scene/lfrfid_app_scene_delete_confirm.cpp +++ b/applications/lfrfid/scene/lfrfid_app_scene_delete_confirm.cpp @@ -5,7 +5,6 @@ void LfRfidAppSceneDeleteConfirm::on_enter(LfRfidApp* app, bool /* need_restore */) { string_init(string_data); - string_init(string_decrypted); string_init(string_header); auto container = app->view_controller.get(); @@ -21,49 +20,26 @@ void LfRfidAppSceneDeleteConfirm::on_enter(LfRfidApp* app, bool /* need_restore auto line_1 = container->add(); auto line_2 = container->add(); auto line_3 = container->add(); - auto line_4 = container->add(); - RfidKey& key = app->worker.key; - const uint8_t* data = key.get_data(); - - for(uint8_t i = 0; i < key.get_type_data_count(); i++) { + size_t size = protocol_dict_get_data_size(app->dict, app->protocol_id); + uint8_t* data = (uint8_t*)malloc(size); + protocol_dict_get_data(app->dict, app->protocol_id, data, size); + for(uint8_t i = 0; i < MIN(size, (size_t)8); i++) { if(i != 0) { string_cat_printf(string_data, " "); } + string_cat_printf(string_data, "%02X", data[i]); } + free(data); - string_printf(string_header, "Delete %s?", key.get_name()); + string_printf(string_header, "Delete %s?", string_get_cstr(app->file_name)); line_1->set_text( - string_get_cstr(string_header), 64, 19, 128 - 2, AlignCenter, AlignBottom, FontPrimary); + string_get_cstr(string_header), 64, 0, 128 - 2, AlignCenter, AlignTop, FontPrimary); line_2->set_text( - string_get_cstr(string_data), 64, 29, 0, AlignCenter, AlignBottom, FontSecondary); - - switch(key.get_type()) { - case LfrfidKeyType::KeyEM4100: - string_printf( - string_decrypted, "%03u,%05u", data[2], (uint16_t)((data[3] << 8) | (data[4]))); - - break; - case LfrfidKeyType::KeyH10301: - case LfrfidKeyType::KeyI40134: - string_printf( - string_decrypted, "FC: %u ID: %u", data[0], (uint16_t)((data[1] << 8) | (data[2]))); - break; - case LfrfidKeyType::KeyIoProxXSF: - string_printf( - string_decrypted, - "FC: %u VC: %u ID: %u", - data[0], - data[1], - (uint16_t)((data[2] << 8) | (data[3]))); - break; - } + string_get_cstr(string_data), 64, 19, 0, AlignCenter, AlignTop, FontSecondary); line_3->set_text( - string_get_cstr(string_decrypted), 64, 39, 0, AlignCenter, AlignBottom, FontSecondary); - - line_4->set_text( - lfrfid_key_get_type_string(key.get_type()), + protocol_dict_get_name(app->dict, app->protocol_id), 64, 49, 0, @@ -78,7 +54,7 @@ bool LfRfidAppSceneDeleteConfirm::on_event(LfRfidApp* app, LfRfidApp::Event* eve bool consumed = false; if(event->type == LfRfidApp::EventType::Next) { - app->delete_key(&app->worker.key); + app->delete_key(); app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::DeleteSuccess); consumed = true; } else if(event->type == LfRfidApp::EventType::Stay) { @@ -94,7 +70,6 @@ bool LfRfidAppSceneDeleteConfirm::on_event(LfRfidApp* app, LfRfidApp::Event* eve void LfRfidAppSceneDeleteConfirm::on_exit(LfRfidApp* app) { app->view_controller.get()->clean(); string_clear(string_data); - string_clear(string_decrypted); string_clear(string_header); } diff --git a/applications/lfrfid/scene/lfrfid_app_scene_delete_confirm.h b/applications/lfrfid/scene/lfrfid_app_scene_delete_confirm.h index e30764f0..f9daed54 100644 --- a/applications/lfrfid/scene/lfrfid_app_scene_delete_confirm.h +++ b/applications/lfrfid/scene/lfrfid_app_scene_delete_confirm.h @@ -13,5 +13,4 @@ private: string_t string_header; string_t string_data; - string_t string_decrypted; }; diff --git a/applications/lfrfid/scene/lfrfid_app_scene_emulate.cpp b/applications/lfrfid/scene/lfrfid_app_scene_emulate.cpp index cad4f17c..02cb011d 100644 --- a/applications/lfrfid/scene/lfrfid_app_scene_emulate.cpp +++ b/applications/lfrfid/scene/lfrfid_app_scene_emulate.cpp @@ -3,28 +3,21 @@ #include void LfRfidAppSceneEmulate::on_enter(LfRfidApp* app, bool /* need_restore */) { - string_init(data_string); - DOLPHIN_DEED(DolphinDeedRfidEmulate); - const uint8_t* data = app->worker.key.get_data(); - - for(uint8_t i = 0; i < app->worker.key.get_type_data_count(); i++) { - string_cat_printf(data_string, "%02X", data[i]); - } - auto popup = app->view_controller.get(); popup->set_header("Emulating", 89, 30, AlignCenter, AlignTop); - if(strlen(app->worker.key.get_name())) { - popup->set_text(app->worker.key.get_name(), 89, 43, AlignCenter, AlignTop); + if(string_size(app->file_name)) { + popup->set_text(string_get_cstr(app->file_name), 89, 43, AlignCenter, AlignTop); } else { - popup->set_text(string_get_cstr(data_string), 89, 43, AlignCenter, AlignTop); + popup->set_text( + protocol_dict_get_name(app->dict, app->protocol_id), 89, 43, AlignCenter, AlignTop); } popup->set_icon(0, 3, &I_RFIDDolphinSend_97x61); app->view_controller.switch_to(); - app->worker.start_emulate(); - + lfrfid_worker_start_thread(app->lfworker); + lfrfid_worker_emulate_start(app->lfworker, (LFRFIDProtocol)app->protocol_id); notification_message(app->notification, &sequence_blink_start_magenta); } @@ -37,7 +30,7 @@ bool LfRfidAppSceneEmulate::on_event(LfRfidApp* app, LfRfidApp::Event* event) { void LfRfidAppSceneEmulate::on_exit(LfRfidApp* app) { app->view_controller.get()->clean(); - app->worker.stop_emulate(); - string_clear(data_string); + lfrfid_worker_stop(app->lfworker); + lfrfid_worker_stop_thread(app->lfworker); notification_message(app->notification, &sequence_blink_stop); } diff --git a/applications/lfrfid/scene/lfrfid_app_scene_emulate.h b/applications/lfrfid/scene/lfrfid_app_scene_emulate.h index 937e49af..13d2b857 100644 --- a/applications/lfrfid/scene/lfrfid_app_scene_emulate.h +++ b/applications/lfrfid/scene/lfrfid_app_scene_emulate.h @@ -6,7 +6,4 @@ public: void on_enter(LfRfidApp* app, bool need_restore) final; bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final; void on_exit(LfRfidApp* app) final; - -private: - string_t data_string; }; diff --git a/applications/lfrfid/scene/lfrfid_app_scene_extra_actions.cpp b/applications/lfrfid/scene/lfrfid_app_scene_extra_actions.cpp new file mode 100644 index 00000000..ea4f03db --- /dev/null +++ b/applications/lfrfid/scene/lfrfid_app_scene_extra_actions.cpp @@ -0,0 +1,63 @@ +#include "lfrfid_app_scene_extra_actions.h" + +typedef enum { + SubmenuASK, + SubmenuPSK, + SubmenuRAW, +} SubmenuIndex; + +void LfRfidAppSceneExtraActions::on_enter(LfRfidApp* app, bool need_restore) { + auto submenu = app->view_controller.get(); + + submenu->add_item("Read ASK (Animal, Ordinary Card)", SubmenuASK, submenu_callback, app); + submenu->add_item("Read PSK (Indala)", SubmenuPSK, submenu_callback, app); + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + submenu->add_item("Read RAW RFID data", SubmenuRAW, submenu_callback, app); + } + + if(need_restore) { + submenu->set_selected_item(submenu_item_selected); + } + + app->view_controller.switch_to(); +} + +bool LfRfidAppSceneExtraActions::on_event(LfRfidApp* app, LfRfidApp::Event* event) { + bool consumed = false; + + if(event->type == LfRfidApp::EventType::MenuSelected) { + submenu_item_selected = event->payload.signed_int; + switch(event->payload.signed_int) { + case SubmenuASK: + app->read_type = LFRFIDWorkerReadTypeASKOnly; + app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::Read); + break; + case SubmenuPSK: + app->read_type = LFRFIDWorkerReadTypePSKOnly; + app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::Read); + break; + case SubmenuRAW: + app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::RawName); + break; + } + + consumed = true; + } + + return consumed; +} + +void LfRfidAppSceneExtraActions::on_exit(LfRfidApp* app) { + app->view_controller.get()->clean(); +} + +void LfRfidAppSceneExtraActions::submenu_callback(void* context, uint32_t index) { + LfRfidApp* app = static_cast(context); + LfRfidApp::Event event; + + event.type = LfRfidApp::EventType::MenuSelected; + event.payload.signed_int = index; + + app->view_controller.send_event(&event); +} diff --git a/applications/lfrfid/scene/lfrfid_app_scene_extra_actions.h b/applications/lfrfid/scene/lfrfid_app_scene_extra_actions.h new file mode 100644 index 00000000..dcd74614 --- /dev/null +++ b/applications/lfrfid/scene/lfrfid_app_scene_extra_actions.h @@ -0,0 +1,13 @@ +#pragma once +#include "../lfrfid_app.h" + +class LfRfidAppSceneExtraActions : public GenericScene { +public: + void on_enter(LfRfidApp* app, bool need_restore) final; + bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final; + void on_exit(LfRfidApp* app) final; + +private: + static void submenu_callback(void* context, uint32_t index); + uint32_t submenu_item_selected = 0; +}; diff --git a/applications/lfrfid/scene/lfrfid_app_scene_raw_info.cpp b/applications/lfrfid/scene/lfrfid_app_scene_raw_info.cpp new file mode 100644 index 00000000..ce3634b2 --- /dev/null +++ b/applications/lfrfid/scene/lfrfid_app_scene_raw_info.cpp @@ -0,0 +1,77 @@ +#include "lfrfid_app_scene_raw_info.h" +#include "../view/elements/button_element.h" +#include "../view/elements/icon_element.h" +#include "../view/elements/string_element.h" + +static void ok_callback(void* context) { + LfRfidApp* app = static_cast(context); + LfRfidApp::Event event; + event.type = LfRfidApp::EventType::Next; + app->view_controller.send_event(&event); +} + +static void back_callback(void* context) { + LfRfidApp* app = static_cast(context); + LfRfidApp::Event event; + event.type = LfRfidApp::EventType::Back; + app->view_controller.send_event(&event); +} + +void LfRfidAppSceneRawInfo::on_enter(LfRfidApp* app, bool /* need_restore */) { + string_init(string_info); + + auto container = app->view_controller.get(); + + bool sd_exist = storage_sd_status(app->storage) == FSE_OK; + if(!sd_exist) { + auto icon = container->add(); + icon->set_icon(0, 0, &I_SDQuestion_35x43); + auto line = container->add(); + line->set_text( + "No SD card found.\nThis function will not\nwork without\nSD card.", + 81, + 4, + 0, + AlignCenter, + AlignTop, + FontSecondary); + + auto button = container->add(); + button->set_type(ButtonElement::Type::Left, "Back"); + button->set_callback(app, back_callback); + } else { + string_printf( + string_info, + "RAW RFID data reader\r\n" + "1) Put the Flipper on your card\r\n" + "2) Press OK\r\n" + "3) Wait until data is read"); + + auto line = container->add(); + line->set_text(string_get_cstr(string_info), 0, 1, 0, AlignLeft, AlignTop, FontSecondary); + + auto button = container->add(); + button->set_type(ButtonElement::Type::Center, "OK"); + button->set_callback(app, ok_callback); + } + + app->view_controller.switch_to(); +} + +bool LfRfidAppSceneRawInfo::on_event(LfRfidApp* app, LfRfidApp::Event* event) { + bool consumed = false; + if(event->type == LfRfidApp::EventType::Next) { + app->scene_controller.switch_to_scene({LfRfidApp::SceneType::RawRead}); + consumed = true; + } else if(event->type == LfRfidApp::EventType::Back) { + app->scene_controller.search_and_switch_to_previous_scene( + {LfRfidApp::SceneType::ExtraActions}); + consumed = true; + } + return consumed; +} + +void LfRfidAppSceneRawInfo::on_exit(LfRfidApp* app) { + app->view_controller.get()->clean(); + string_clear(string_info); +} diff --git a/applications/lfrfid/scene/lfrfid_app_scene_raw_info.h b/applications/lfrfid/scene/lfrfid_app_scene_raw_info.h new file mode 100644 index 00000000..eecca143 --- /dev/null +++ b/applications/lfrfid/scene/lfrfid_app_scene_raw_info.h @@ -0,0 +1,12 @@ +#pragma once +#include "../lfrfid_app.h" + +class LfRfidAppSceneRawInfo : public GenericScene { +public: + void on_enter(LfRfidApp* app, bool need_restore) final; + bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final; + void on_exit(LfRfidApp* app) final; + +private: + string_t string_info; +}; diff --git a/applications/lfrfid/scene/lfrfid_app_scene_raw_name.cpp b/applications/lfrfid/scene/lfrfid_app_scene_raw_name.cpp new file mode 100644 index 00000000..0ad34619 --- /dev/null +++ b/applications/lfrfid/scene/lfrfid_app_scene_raw_name.cpp @@ -0,0 +1,46 @@ + +#include "lfrfid_app_scene_raw_name.h" +#include "m-string.h" +#include +#include + +void LfRfidAppSceneRawName::on_enter(LfRfidApp* app, bool /* need_restore */) { + const char* key_name = string_get_cstr(app->raw_file_name); + + bool key_name_empty = (string_size(app->raw_file_name) == 0); + if(key_name_empty) { + app->text_store.set("RfidRecord"); + } else { + app->text_store.set("%s", key_name); + } + + auto text_input = app->view_controller.get(); + text_input->set_header_text("Name the raw file"); + + text_input->set_result_callback( + save_callback, app, app->text_store.text, LFRFID_KEY_NAME_SIZE, key_name_empty); + + app->view_controller.switch_to(); +} + +bool LfRfidAppSceneRawName::on_event(LfRfidApp* app, LfRfidApp::Event* event) { + bool consumed = false; + + if(event->type == LfRfidApp::EventType::Next) { + string_set_str(app->raw_file_name, app->text_store.text); + app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::RawInfo); + } + + return consumed; +} + +void LfRfidAppSceneRawName::on_exit(LfRfidApp* app) { + app->view_controller.get()->clean(); +} + +void LfRfidAppSceneRawName::save_callback(void* context) { + LfRfidApp* app = static_cast(context); + LfRfidApp::Event event; + event.type = LfRfidApp::EventType::Next; + app->view_controller.send_event(&event); +} diff --git a/applications/lfrfid/scene/lfrfid_app_scene_raw_name.h b/applications/lfrfid/scene/lfrfid_app_scene_raw_name.h new file mode 100644 index 00000000..225d135e --- /dev/null +++ b/applications/lfrfid/scene/lfrfid_app_scene_raw_name.h @@ -0,0 +1,12 @@ +#pragma once +#include "../lfrfid_app.h" + +class LfRfidAppSceneRawName : public GenericScene { +public: + void on_enter(LfRfidApp* app, bool need_restore) final; + bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final; + void on_exit(LfRfidApp* app) final; + +private: + static void save_callback(void* context); +}; diff --git a/applications/lfrfid/scene/lfrfid_app_scene_raw_read.cpp b/applications/lfrfid/scene/lfrfid_app_scene_raw_read.cpp new file mode 100644 index 00000000..0d04e6bc --- /dev/null +++ b/applications/lfrfid/scene/lfrfid_app_scene_raw_read.cpp @@ -0,0 +1,107 @@ +#include "lfrfid_app_scene_raw_read.h" +#include + +#define RAW_READ_TIME 5000 + +static void lfrfid_read_callback(LFRFIDWorkerReadRawResult result, void* ctx) { + LfRfidApp* app = static_cast(ctx); + LfRfidApp::Event event; + + switch(result) { + case LFRFIDWorkerReadRawFileError: + event.type = LfRfidApp::EventType::ReadEventError; + break; + case LFRFIDWorkerReadRawOverrun: + event.type = LfRfidApp::EventType::ReadEventOverrun; + break; + } + + app->view_controller.send_event(&event); +} + +static void timer_callback(void* ctx) { + LfRfidApp* app = static_cast(ctx); + LfRfidApp::Event event; + event.type = LfRfidApp::EventType::ReadEventDone; + app->view_controller.send_event(&event); +} + +void LfRfidAppSceneRawRead::on_enter(LfRfidApp* app, bool /* need_restore */) { + string_init(string_file_name); + auto popup = app->view_controller.get(); + popup->set_icon(0, 3, &I_RFIDDolphinReceive_97x61); + app->view_controller.switch_to(); + lfrfid_worker_start_thread(app->lfworker); + app->make_app_folder(); + + timer = furi_timer_alloc(timer_callback, FuriTimerTypeOnce, app); + furi_timer_start(timer, RAW_READ_TIME); + string_printf( + string_file_name, "%s/%s.ask.raw", app->app_sd_folder, string_get_cstr(app->raw_file_name)); + popup->set_header("Reading\nRAW RFID\nASK", 89, 30, AlignCenter, AlignTop); + lfrfid_worker_read_raw_start( + app->lfworker, + string_get_cstr(string_file_name), + LFRFIDWorkerReadTypeASKOnly, + lfrfid_read_callback, + app); + + notification_message(app->notification, &sequence_blink_start_cyan); + + is_psk = false; + error = false; +} + +bool LfRfidAppSceneRawRead::on_event(LfRfidApp* app, LfRfidApp::Event* event) { + UNUSED(app); + bool consumed = true; + auto popup = app->view_controller.get(); + + switch(event->type) { + case LfRfidApp::EventType::ReadEventError: + error = true; + popup->set_header("Reading\nRAW RFID\nFile error", 89, 30, AlignCenter, AlignTop); + notification_message(app->notification, &sequence_blink_start_red); + furi_timer_stop(timer); + break; + case LfRfidApp::EventType::ReadEventDone: + if(!error) { + if(is_psk) { + notification_message(app->notification, &sequence_success); + app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::RawSuccess); + } else { + popup->set_header("Reading\nRAW RFID\nPSK", 89, 30, AlignCenter, AlignTop); + notification_message(app->notification, &sequence_blink_start_yellow); + lfrfid_worker_stop(app->lfworker); + string_printf( + string_file_name, + "%s/%s.psk.raw", + app->app_sd_folder, + string_get_cstr(app->raw_file_name)); + lfrfid_worker_read_raw_start( + app->lfworker, + string_get_cstr(string_file_name), + LFRFIDWorkerReadTypePSKOnly, + lfrfid_read_callback, + app); + furi_timer_start(timer, RAW_READ_TIME); + is_psk = true; + } + } + break; + default: + consumed = false; + break; + } + + return consumed; +} + +void LfRfidAppSceneRawRead::on_exit(LfRfidApp* app) { + notification_message(app->notification, &sequence_blink_stop); + app->view_controller.get()->clean(); + lfrfid_worker_stop(app->lfworker); + lfrfid_worker_stop_thread(app->lfworker); + furi_timer_free(timer); + string_clear(string_file_name); +} diff --git a/applications/lfrfid/scene/lfrfid_app_scene_raw_read.h b/applications/lfrfid/scene/lfrfid_app_scene_raw_read.h new file mode 100644 index 00000000..09ef7463 --- /dev/null +++ b/applications/lfrfid/scene/lfrfid_app_scene_raw_read.h @@ -0,0 +1,15 @@ +#pragma once +#include "../lfrfid_app.h" + +class LfRfidAppSceneRawRead : public GenericScene { +public: + void on_enter(LfRfidApp* app, bool need_restore) final; + bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final; + void on_exit(LfRfidApp* app) final; + +private: + string_t string_file_name; + FuriTimer* timer; + bool is_psk; + bool error; +}; diff --git a/applications/lfrfid/scene/lfrfid_app_scene_raw_success.cpp b/applications/lfrfid/scene/lfrfid_app_scene_raw_success.cpp new file mode 100644 index 00000000..227ab580 --- /dev/null +++ b/applications/lfrfid/scene/lfrfid_app_scene_raw_success.cpp @@ -0,0 +1,45 @@ +#include "lfrfid_app_scene_raw_success.h" +#include "../view/elements/button_element.h" +#include "../view/elements/icon_element.h" +#include "../view/elements/string_element.h" + +void LfRfidAppSceneRawSuccess::on_enter(LfRfidApp* app, bool /* need_restore */) { + string_init(string_info); + + string_printf(string_info, "RAW RFID read success!\r\n"); + string_cat_printf(string_info, "Now you can analyze files\r\n"); + string_cat_printf(string_info, "Or send them to developers"); + + auto container = app->view_controller.get(); + + auto line = container->add(); + line->set_text(string_get_cstr(string_info), 0, 1, 0, AlignLeft, AlignTop, FontSecondary); + + auto button = container->add(); + button->set_type(ButtonElement::Type::Center, "OK"); + button->set_callback(app, LfRfidAppSceneRawSuccess::ok_callback); + + app->view_controller.switch_to(); +} + +bool LfRfidAppSceneRawSuccess::on_event(LfRfidApp* app, LfRfidApp::Event* event) { + bool consumed = false; + if(event->type == LfRfidApp::EventType::Next) { + app->scene_controller.search_and_switch_to_previous_scene( + {LfRfidApp::SceneType::ExtraActions}); + consumed = true; + } + return consumed; +} + +void LfRfidAppSceneRawSuccess::on_exit(LfRfidApp* app) { + app->view_controller.get()->clean(); + string_clear(string_info); +} + +void LfRfidAppSceneRawSuccess::ok_callback(void* context) { + LfRfidApp* app = static_cast(context); + LfRfidApp::Event event; + event.type = LfRfidApp::EventType::Next; + app->view_controller.send_event(&event); +} diff --git a/applications/lfrfid/scene/lfrfid_app_scene_raw_success.h b/applications/lfrfid/scene/lfrfid_app_scene_raw_success.h new file mode 100644 index 00000000..0a0b0116 --- /dev/null +++ b/applications/lfrfid/scene/lfrfid_app_scene_raw_success.h @@ -0,0 +1,13 @@ +#pragma once +#include "../lfrfid_app.h" + +class LfRfidAppSceneRawSuccess : public GenericScene { +public: + void on_enter(LfRfidApp* app, bool need_restore) final; + bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final; + void on_exit(LfRfidApp* app) final; + +private: + string_t string_info; + static void ok_callback(void* context); +}; diff --git a/applications/lfrfid/scene/lfrfid_app_scene_read.cpp b/applications/lfrfid/scene/lfrfid_app_scene_read.cpp index 67279a16..120eb1a0 100644 --- a/applications/lfrfid/scene/lfrfid_app_scene_read.cpp +++ b/applications/lfrfid/scene/lfrfid_app_scene_read.cpp @@ -1,40 +1,100 @@ #include "lfrfid_app_scene_read.h" #include +static void lfrfid_read_callback(LFRFIDWorkerReadResult result, ProtocolId protocol, void* ctx) { + LfRfidApp* app = static_cast(ctx); + LfRfidApp::Event event; + + switch(result) { + case LFRFIDWorkerReadSenseStart: + event.type = LfRfidApp::EventType::ReadEventSenseStart; + break; + case LFRFIDWorkerReadSenseEnd: + event.type = LfRfidApp::EventType::ReadEventSenseEnd; + break; + case LFRFIDWorkerReadSenseCardStart: + event.type = LfRfidApp::EventType::ReadEventSenseCardStart; + break; + case LFRFIDWorkerReadSenseCardEnd: + event.type = LfRfidApp::EventType::ReadEventSenseCardEnd; + break; + case LFRFIDWorkerReadDone: + event.type = LfRfidApp::EventType::ReadEventDone; + break; + case LFRFIDWorkerReadStartASK: + event.type = LfRfidApp::EventType::ReadEventStartASK; + break; + case LFRFIDWorkerReadStartPSK: + event.type = LfRfidApp::EventType::ReadEventStartPSK; + break; + } + + event.payload.signed_int = protocol; + + app->view_controller.send_event(&event); +} + void LfRfidAppSceneRead::on_enter(LfRfidApp* app, bool /* need_restore */) { auto popup = app->view_controller.get(); DOLPHIN_DEED(DolphinDeedRfidRead); - popup->set_header("Reading\nLF RFID", 89, 34, AlignCenter, AlignTop); + if(app->read_type == LFRFIDWorkerReadTypePSKOnly) { + popup->set_header("Reading\nLF RFID\nPSK", 89, 30, AlignCenter, AlignTop); + } else { + popup->set_header("Reading\nLF RFID\nASK", 89, 30, AlignCenter, AlignTop); + } + popup->set_icon(0, 3, &I_RFIDDolphinReceive_97x61); app->view_controller.switch_to(); - app->worker.start_read(); + lfrfid_worker_start_thread(app->lfworker); + lfrfid_worker_read_start(app->lfworker, app->read_type, lfrfid_read_callback, app); + + notification_message(app->notification, &sequence_blink_start_cyan); } bool LfRfidAppSceneRead::on_event(LfRfidApp* app, LfRfidApp::Event* event) { - bool consumed = false; + bool consumed = true; + auto popup = app->view_controller.get(); - if(event->type == LfRfidApp::EventType::Tick) { - if(app->worker.read()) { - DOLPHIN_DEED(DolphinDeedRfidReadSuccess); - notification_message(app->notification, &sequence_success); - app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::ReadSuccess); - } else { - if(app->worker.any_read()) { - notification_message(app->notification, &sequence_blink_yellow_10); - } else if(app->worker.detect()) { - notification_message(app->notification, &sequence_blink_yellow_10); - } else { - notification_message(app->notification, &sequence_blink_cyan_10); - } - } + switch(event->type) { + case LfRfidApp::EventType::ReadEventSenseStart: + notification_message(app->notification, &sequence_blink_stop); + notification_message(app->notification, &sequence_blink_start_yellow); + break; + case LfRfidApp::EventType::ReadEventSenseCardStart: + notification_message(app->notification, &sequence_blink_stop); + notification_message(app->notification, &sequence_blink_start_green); + break; + case LfRfidApp::EventType::ReadEventSenseEnd: + case LfRfidApp::EventType::ReadEventSenseCardEnd: + notification_message(app->notification, &sequence_blink_stop); + notification_message(app->notification, &sequence_blink_start_cyan); + break; + case LfRfidApp::EventType::ReadEventDone: + app->protocol_id = event->payload.signed_int; + DOLPHIN_DEED(DolphinDeedRfidReadSuccess); + notification_message(app->notification, &sequence_success); + string_reset(app->file_name); + app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::ReadSuccess); + break; + case LfRfidApp::EventType::ReadEventStartPSK: + popup->set_header("Reading\nLF RFID\nPSK", 89, 30, AlignCenter, AlignTop); + break; + case LfRfidApp::EventType::ReadEventStartASK: + popup->set_header("Reading\nLF RFID\nASK", 89, 30, AlignCenter, AlignTop); + break; + default: + consumed = false; + break; } return consumed; } void LfRfidAppSceneRead::on_exit(LfRfidApp* app) { + notification_message(app->notification, &sequence_blink_stop); app->view_controller.get()->clean(); - app->worker.stop_read(); + lfrfid_worker_stop(app->lfworker); + lfrfid_worker_stop_thread(app->lfworker); } diff --git a/applications/lfrfid/scene/lfrfid_app_scene_read_menu.cpp b/applications/lfrfid/scene/lfrfid_app_scene_read_menu.cpp index 76c91230..aa3b3f1f 100644 --- a/applications/lfrfid/scene/lfrfid_app_scene_read_menu.cpp +++ b/applications/lfrfid/scene/lfrfid_app_scene_read_menu.cpp @@ -24,8 +24,8 @@ bool LfRfidAppSceneReadKeyMenu::on_event(LfRfidApp* app, LfRfidApp::Event* event bool consumed = false; if(event->type == LfRfidApp::EventType::MenuSelected) { - submenu_item_selected = event->payload.menu_index; - switch(event->payload.menu_index) { + submenu_item_selected = event->payload.signed_int; + switch(event->payload.signed_int) { case SubmenuWrite: app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::Write); break; @@ -54,7 +54,7 @@ void LfRfidAppSceneReadKeyMenu::submenu_callback(void* context, uint32_t index) LfRfidApp::Event event; event.type = LfRfidApp::EventType::MenuSelected; - event.payload.menu_index = index; + event.payload.signed_int = index; app->view_controller.send_event(&event); } diff --git a/applications/lfrfid/scene/lfrfid_app_scene_read_success.cpp b/applications/lfrfid/scene/lfrfid_app_scene_read_success.cpp index 010cac2c..277b43a3 100644 --- a/applications/lfrfid/scene/lfrfid_app_scene_read_success.cpp +++ b/applications/lfrfid/scene/lfrfid_app_scene_read_success.cpp @@ -4,10 +4,37 @@ #include "../view/elements/string_element.h" void LfRfidAppSceneReadSuccess::on_enter(LfRfidApp* app, bool /* need_restore */) { - string_init(string[0]); - string_init(string[1]); - string_init(string[2]); - string_init(string[3]); + string_init(string_info); + string_init(string_header); + + string_init_printf( + string_header, + "%s[%s]", + protocol_dict_get_name(app->dict, app->protocol_id), + protocol_dict_get_manufacturer(app->dict, app->protocol_id)); + + size_t size = protocol_dict_get_data_size(app->dict, app->protocol_id); + uint8_t* data = (uint8_t*)malloc(size); + protocol_dict_get_data(app->dict, app->protocol_id, data, size); + for(uint8_t i = 0; i < size; i++) { + if(i != 0) { + string_cat_printf(string_info, " "); + } + + if(i >= 9) { + string_cat_printf(string_info, "..."); + break; + } else { + string_cat_printf(string_info, "%02X", data[i]); + } + } + free(data); + + string_t render_data; + string_init(render_data); + protocol_dict_render_brief_data(app->dict, render_data, app->protocol_id); + string_cat_printf(string_info, "\r\n%s", string_get_cstr(render_data)); + string_clear(render_data); auto container = app->view_controller.get(); @@ -19,90 +46,11 @@ void LfRfidAppSceneReadSuccess::on_enter(LfRfidApp* app, bool /* need_restore */ button->set_type(ButtonElement::Type::Right, "More"); button->set_callback(app, LfRfidAppSceneReadSuccess::more_callback); - auto icon = container->add(); - icon->set_icon(3, 12, &I_RFIDBigChip_37x36); - auto header = container->add(); - header->set_text(app->worker.key.get_type_text(), 89, 3, 0, AlignCenter); + header->set_text(string_get_cstr(string_header), 0, 2, 0, AlignLeft, AlignTop, FontPrimary); - auto line_1_text = container->add(); - auto line_2l_text = container->add(); - auto line_2r_text = container->add(); - auto line_3_text = container->add(); - - auto line_1_value = container->add(); - auto line_2l_value = container->add(); - auto line_2r_value = container->add(); - auto line_3_value = container->add(); - - const uint8_t* data = app->worker.key.get_data(); - - switch(app->worker.key.get_type()) { - case LfrfidKeyType::KeyEM4100: - line_1_text->set_text("HEX:", 65, 23, 0, AlignRight, AlignBottom, FontSecondary); - line_2l_text->set_text("Mod:", 65, 35, 0, AlignRight, AlignBottom, FontSecondary); - line_3_text->set_text("ID:", 65, 47, 0, AlignRight, AlignBottom, FontSecondary); - - for(uint8_t i = 0; i < app->worker.key.get_type_data_count(); i++) { - string_cat_printf(string[0], "%02X", data[i]); - } - - string_printf(string[1], "Manchester"); - string_printf(string[2], "%03u,%05u", data[2], (uint16_t)((data[3] << 8) | (data[4]))); - - line_1_value->set_text( - string_get_cstr(string[0]), 68, 23, 0, AlignLeft, AlignBottom, FontSecondary); - line_2l_value->set_text( - string_get_cstr(string[1]), 68, 35, 0, AlignLeft, AlignBottom, FontSecondary); - line_3_value->set_text( - string_get_cstr(string[2]), 68, 47, 0, AlignLeft, AlignBottom, FontSecondary); - break; - case LfrfidKeyType::KeyH10301: - case LfrfidKeyType::KeyI40134: - line_1_text->set_text("HEX:", 65, 23, 0, AlignRight, AlignBottom, FontSecondary); - line_2l_text->set_text("FC:", 65, 35, 0, AlignRight, AlignBottom, FontSecondary); - line_3_text->set_text("Card:", 65, 47, 0, AlignRight, AlignBottom, FontSecondary); - - for(uint8_t i = 0; i < app->worker.key.get_type_data_count(); i++) { - string_cat_printf(string[0], "%02X", data[i]); - } - - string_printf(string[1], "%u", data[0]); - string_printf(string[2], "%u", (uint16_t)((data[1] << 8) | (data[2]))); - - line_1_value->set_text( - string_get_cstr(string[0]), 68, 23, 0, AlignLeft, AlignBottom, FontSecondary); - line_2l_value->set_text( - string_get_cstr(string[1]), 68, 35, 0, AlignLeft, AlignBottom, FontSecondary); - line_3_value->set_text( - string_get_cstr(string[2]), 68, 47, 0, AlignLeft, AlignBottom, FontSecondary); - break; - - case LfrfidKeyType::KeyIoProxXSF: - line_1_text->set_text("HEX:", 65, 23, 0, AlignRight, AlignBottom, FontSecondary); - line_2l_text->set_text("FC:", 65, 35, 0, AlignRight, AlignBottom, FontSecondary); - line_2r_text->set_text("VС:", 95, 35, 0, AlignRight, AlignBottom, FontSecondary); - line_3_text->set_text("Card:", 65, 47, 0, AlignRight, AlignBottom, FontSecondary); - - for(uint8_t i = 0; i < app->worker.key.get_type_data_count(); i++) { - string_cat_printf(string[0], "%02X", data[i]); - } - - string_printf(string[1], "%u", data[0]); - string_printf(string[2], "%u", (uint16_t)((data[2] << 8) | (data[3]))); - string_printf(string[3], "%u", data[1]); - - line_1_value->set_text( - string_get_cstr(string[0]), 68, 23, 0, AlignLeft, AlignBottom, FontSecondary); - line_2l_value->set_text( - string_get_cstr(string[1]), 68, 35, 0, AlignLeft, AlignBottom, FontSecondary); - line_2r_value->set_text( - string_get_cstr(string[3]), 98, 35, 0, AlignLeft, AlignBottom, FontSecondary); - line_3_value->set_text( - string_get_cstr(string[2]), 68, 47, 0, AlignLeft, AlignBottom, FontSecondary); - - break; - } + auto text = container->add(); + text->set_text(string_get_cstr(string_info), 0, 16, 0, AlignLeft, AlignTop, FontSecondary); app->view_controller.switch_to(); @@ -129,9 +77,8 @@ bool LfRfidAppSceneReadSuccess::on_event(LfRfidApp* app, LfRfidApp::Event* event void LfRfidAppSceneReadSuccess::on_exit(LfRfidApp* app) { notification_message_block(app->notification, &sequence_reset_green); app->view_controller.get()->clean(); - string_clear(string[0]); - string_clear(string[1]); - string_clear(string[2]); + string_clear(string_info); + string_clear(string_header); } void LfRfidAppSceneReadSuccess::back_callback(void* context) { diff --git a/applications/lfrfid/scene/lfrfid_app_scene_read_success.h b/applications/lfrfid/scene/lfrfid_app_scene_read_success.h index ac0e3c1b..6d90f631 100644 --- a/applications/lfrfid/scene/lfrfid_app_scene_read_success.h +++ b/applications/lfrfid/scene/lfrfid_app_scene_read_success.h @@ -11,5 +11,6 @@ private: static void back_callback(void* context); static void more_callback(void* context); - string_t string[3]; + string_t string_header; + string_t string_info; }; diff --git a/applications/lfrfid/scene/lfrfid_app_scene_rpc.cpp b/applications/lfrfid/scene/lfrfid_app_scene_rpc.cpp index 54a57c9a..c2e5ec2a 100644 --- a/applications/lfrfid/scene/lfrfid_app_scene_rpc.cpp +++ b/applications/lfrfid/scene/lfrfid_app_scene_rpc.cpp @@ -37,12 +37,13 @@ bool LfRfidAppSceneRpc::on_event(LfRfidApp* app, LfRfidApp::Event* event) { bool result = false; if(arg && !emulating) { string_set_str(app->file_path, arg); - if(app->load_key_data(app->file_path, &(app->worker.key), false)) { - app->worker.start_emulate(); + if(app->load_key_data(app->file_path, false)) { + lfrfid_worker_start_thread(app->lfworker); + lfrfid_worker_emulate_start(app->lfworker, (LFRFIDProtocol)app->protocol_id); emulating = true; auto popup = app->view_controller.get(); - app->text_store.set("emulating\n%s", app->worker.key.get_name()); + app->text_store.set("emulating\n%s", string_get_cstr(app->file_name)); popup->set_text(app->text_store.text, 89, 44, AlignCenter, AlignTop); notification_message(app->notification, &sequence_blink_start_magenta); @@ -57,7 +58,8 @@ bool LfRfidAppSceneRpc::on_event(LfRfidApp* app, LfRfidApp::Event* event) { void LfRfidAppSceneRpc::on_exit(LfRfidApp* app) { if(emulating) { - app->worker.stop_emulate(); + lfrfid_worker_stop(app->lfworker); + lfrfid_worker_stop_thread(app->lfworker); notification_message(app->notification, &sequence_blink_stop); } app->view_controller.get()->clean(); diff --git a/applications/lfrfid/scene/lfrfid_app_scene_save_data.cpp b/applications/lfrfid/scene/lfrfid_app_scene_save_data.cpp index 3a13e683..c506cd72 100644 --- a/applications/lfrfid/scene/lfrfid_app_scene_save_data.cpp +++ b/applications/lfrfid/scene/lfrfid_app_scene_save_data.cpp @@ -3,31 +3,29 @@ void LfRfidAppSceneSaveData::on_enter(LfRfidApp* app, bool need_restore) { auto byte_input = app->view_controller.get(); - RfidKey& key = app->worker.key; - - if(need_restore) printf("restored\r\n"); + size_t size = protocol_dict_get_data_size(app->dict, app->protocol_id); if(need_restore) { - key.set_data(old_key_data, key.get_type_data_count()); + protocol_dict_set_data(app->dict, app->protocol_id, app->old_key_data, size); } else { - memcpy(old_key_data, key.get_data(), key.get_type_data_count()); + protocol_dict_get_data(app->dict, app->protocol_id, app->old_key_data, size); } - memcpy(new_key_data, key.get_data(), key.get_type_data_count()); + protocol_dict_get_data(app->dict, app->protocol_id, app->new_key_data, size); + byte_input->set_header_text("Enter the data in hex"); - byte_input->set_result_callback( - save_callback, NULL, app, new_key_data, app->worker.key.get_type_data_count()); + byte_input->set_result_callback(save_callback, NULL, app, app->new_key_data, size); app->view_controller.switch_to(); } bool LfRfidAppSceneSaveData::on_event(LfRfidApp* app, LfRfidApp::Event* event) { bool consumed = false; - RfidKey& key = app->worker.key; if(event->type == LfRfidApp::EventType::Next) { - key.set_data(new_key_data, key.get_type_data_count()); + size_t size = protocol_dict_get_data_size(app->dict, app->protocol_id); + protocol_dict_set_data(app->dict, app->protocol_id, app->new_key_data, size); DOLPHIN_DEED(DolphinDeedRfidAdd); app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::SaveName); } diff --git a/applications/lfrfid/scene/lfrfid_app_scene_save_data.h b/applications/lfrfid/scene/lfrfid_app_scene_save_data.h index 6458ae64..d03cae12 100644 --- a/applications/lfrfid/scene/lfrfid_app_scene_save_data.h +++ b/applications/lfrfid/scene/lfrfid_app_scene_save_data.h @@ -9,25 +9,4 @@ public: private: static void save_callback(void* context); - uint8_t old_key_data[LFRFID_KEY_SIZE] = { - 0xAA, - 0xAA, - 0xAA, - 0xAA, - 0xAA, - 0xAA, - 0xAA, - 0xAA, - }; - - uint8_t new_key_data[LFRFID_KEY_SIZE] = { - 0xBB, - 0xBB, - 0xBB, - 0xBB, - 0xBB, - 0xBB, - 0xBB, - 0xBB, - }; }; diff --git a/applications/lfrfid/scene/lfrfid_app_scene_save_name.cpp b/applications/lfrfid/scene/lfrfid_app_scene_save_name.cpp index d7ba2c9e..ed58b645 100644 --- a/applications/lfrfid/scene/lfrfid_app_scene_save_name.cpp +++ b/applications/lfrfid/scene/lfrfid_app_scene_save_name.cpp @@ -4,9 +4,9 @@ #include void LfRfidAppSceneSaveName::on_enter(LfRfidApp* app, bool /* need_restore */) { - const char* key_name = app->worker.key.get_name(); + const char* key_name = string_get_cstr(app->file_name); - bool key_name_empty = !strcmp(key_name, ""); + bool key_name_empty = (string_size(app->file_name) == 0); if(key_name_empty) { string_set_str(app->file_path, app->app_folder); set_random_name(app->text_store.text, app->text_store.text_size); @@ -18,11 +18,7 @@ void LfRfidAppSceneSaveName::on_enter(LfRfidApp* app, bool /* need_restore */) { text_input->set_header_text("Name the card"); text_input->set_result_callback( - save_callback, - app, - app->text_store.text, - app->worker.key.get_name_length(), - key_name_empty); + save_callback, app, app->text_store.text, LFRFID_KEY_NAME_SIZE, key_name_empty); string_t folder_path; string_init(folder_path); @@ -42,13 +38,13 @@ bool LfRfidAppSceneSaveName::on_event(LfRfidApp* app, LfRfidApp::Event* event) { bool consumed = false; if(event->type == LfRfidApp::EventType::Next) { - if(strlen(app->worker.key.get_name())) { - app->delete_key(&app->worker.key); + if(string_size(app->file_name) > 0) { + app->delete_key(); } - app->worker.key.set_name(app->text_store.text); + string_set_str(app->file_name, app->text_store.text); - if(app->save_key(&app->worker.key)) { + if(app->save_key()) { app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::SaveSuccess); } else { app->scene_controller.search_and_switch_to_previous_scene( diff --git a/applications/lfrfid/scene/lfrfid_app_scene_save_type.cpp b/applications/lfrfid/scene/lfrfid_app_scene_save_type.cpp index 334bb1a0..b017e7b0 100644 --- a/applications/lfrfid/scene/lfrfid_app_scene_save_type.cpp +++ b/applications/lfrfid/scene/lfrfid_app_scene_save_type.cpp @@ -3,12 +3,12 @@ void LfRfidAppSceneSaveType::on_enter(LfRfidApp* app, bool need_restore) { auto submenu = app->view_controller.get(); - for(uint8_t i = 0; i <= keys_count; i++) { + for(uint8_t i = 0; i < keys_count; i++) { string_init_printf( submenu_name[i], "%s %s", - lfrfid_key_get_manufacturer_string(static_cast(i)), - lfrfid_key_get_type_string(static_cast(i))); + protocol_dict_get_manufacturer(app->dict, i), + protocol_dict_get_name(app->dict, i)); submenu->add_item(string_get_cstr(submenu_name[i]), i, submenu_callback, app); } @@ -19,15 +19,15 @@ void LfRfidAppSceneSaveType::on_enter(LfRfidApp* app, bool need_restore) { app->view_controller.switch_to(); // clear key name - app->worker.key.set_name(""); + string_reset(app->file_name); } bool LfRfidAppSceneSaveType::on_event(LfRfidApp* app, LfRfidApp::Event* event) { bool consumed = false; if(event->type == LfRfidApp::EventType::MenuSelected) { - submenu_item_selected = event->payload.menu_index; - app->worker.key.set_type(static_cast(event->payload.menu_index)); + submenu_item_selected = event->payload.signed_int; + app->protocol_id = event->payload.signed_int; app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::SaveData); consumed = true; } @@ -37,7 +37,7 @@ bool LfRfidAppSceneSaveType::on_event(LfRfidApp* app, LfRfidApp::Event* event) { void LfRfidAppSceneSaveType::on_exit(LfRfidApp* app) { app->view_controller.get()->clean(); - for(uint8_t i = 0; i <= keys_count; i++) { + for(uint8_t i = 0; i < keys_count; i++) { string_clear(submenu_name[i]); } } @@ -47,7 +47,7 @@ void LfRfidAppSceneSaveType::submenu_callback(void* context, uint32_t index) { LfRfidApp::Event event; event.type = LfRfidApp::EventType::MenuSelected; - event.payload.menu_index = index; + event.payload.signed_int = index; app->view_controller.send_event(&event); } diff --git a/applications/lfrfid/scene/lfrfid_app_scene_save_type.h b/applications/lfrfid/scene/lfrfid_app_scene_save_type.h index 847c0dab..e4c1be3e 100644 --- a/applications/lfrfid/scene/lfrfid_app_scene_save_type.h +++ b/applications/lfrfid/scene/lfrfid_app_scene_save_type.h @@ -10,6 +10,6 @@ public: private: static void submenu_callback(void* context, uint32_t index); uint32_t submenu_item_selected = 0; - static const uint8_t keys_count = static_cast(LfrfidKeyType::KeyIoProxXSF); - string_t submenu_name[keys_count + 1]; + static const uint8_t keys_count = static_cast(LFRFIDProtocol::LFRFIDProtocolMax); + string_t submenu_name[keys_count]; }; diff --git a/applications/lfrfid/scene/lfrfid_app_scene_saved_info.cpp b/applications/lfrfid/scene/lfrfid_app_scene_saved_info.cpp index dd4a3d4e..614dd505 100644 --- a/applications/lfrfid/scene/lfrfid_app_scene_saved_info.cpp +++ b/applications/lfrfid/scene/lfrfid_app_scene_saved_info.cpp @@ -4,65 +4,36 @@ #include "../view/elements/string_element.h" void LfRfidAppSceneSavedInfo::on_enter(LfRfidApp* app, bool /* need_restore */) { - string_init(string_data); - string_init(string_decrypted); + string_init(string_info); + + string_printf( + string_info, + "%s [%s]\r\n", + string_get_cstr(app->file_name), + protocol_dict_get_name(app->dict, app->protocol_id)); + + size_t size = protocol_dict_get_data_size(app->dict, app->protocol_id); + uint8_t* data = (uint8_t*)malloc(size); + protocol_dict_get_data(app->dict, app->protocol_id, data, size); + for(uint8_t i = 0; i < size; i++) { + if(i != 0) { + string_cat_printf(string_info, " "); + } + + string_cat_printf(string_info, "%02X", data[i]); + } + free(data); + + string_t render_data; + string_init(render_data); + protocol_dict_render_data(app->dict, render_data, app->protocol_id); + string_cat_printf(string_info, "\r\n%s", string_get_cstr(render_data)); + string_clear(render_data); auto container = app->view_controller.get(); - auto button = container->add(); - button->set_type(ButtonElement::Type::Left, "Back"); - button->set_callback(app, LfRfidAppSceneSavedInfo::back_callback); - auto line_1 = container->add(); - auto line_2 = container->add(); - auto line_3 = container->add(); - auto line_4 = container->add(); - - RfidKey& key = app->worker.key; - const uint8_t* data = key.get_data(); - - for(uint8_t i = 0; i < key.get_type_data_count(); i++) { - if(i != 0) { - string_cat_printf(string_data, " "); - } - string_cat_printf(string_data, "%02X", data[i]); - } - - line_1->set_text(key.get_name(), 64, 17, 128 - 2, AlignCenter, AlignBottom, FontSecondary); - line_2->set_text( - string_get_cstr(string_data), 64, 29, 0, AlignCenter, AlignBottom, FontPrimary); - - switch(key.get_type()) { - case LfrfidKeyType::KeyEM4100: - string_printf( - string_decrypted, "%03u,%05u", data[2], (uint16_t)((data[3] << 8) | (data[4]))); - - break; - case LfrfidKeyType::KeyH10301: - case LfrfidKeyType::KeyI40134: - string_printf( - string_decrypted, "FC: %u ID: %u", data[0], (uint16_t)((data[1] << 8) | (data[2]))); - break; - case LfrfidKeyType::KeyIoProxXSF: - string_printf( - string_decrypted, - "FC: %u VC: %u ID: %u", - data[0], - data[1], - (uint16_t)((data[2] << 8) | (data[3]))); - break; - } - line_3->set_text( - string_get_cstr(string_decrypted), 64, 39, 0, AlignCenter, AlignBottom, FontSecondary); - - line_4->set_text( - lfrfid_key_get_type_string(key.get_type()), - 64, - 49, - 0, - AlignCenter, - AlignBottom, - FontSecondary); + line_1->set_text(string_get_cstr(string_info), 0, 1, 0, AlignLeft, AlignTop, FontSecondary); app->view_controller.switch_to(); } @@ -73,13 +44,5 @@ bool LfRfidAppSceneSavedInfo::on_event(LfRfidApp* /* app */, LfRfidApp::Event* / void LfRfidAppSceneSavedInfo::on_exit(LfRfidApp* app) { app->view_controller.get()->clean(); - string_clear(string_data); - string_clear(string_decrypted); -} - -void LfRfidAppSceneSavedInfo::back_callback(void* context) { - LfRfidApp* app = static_cast(context); - LfRfidApp::Event event; - event.type = LfRfidApp::EventType::Back; - app->view_controller.send_event(&event); + string_clear(string_info); } diff --git a/applications/lfrfid/scene/lfrfid_app_scene_saved_info.h b/applications/lfrfid/scene/lfrfid_app_scene_saved_info.h index 5aa33e8a..b0b588bc 100644 --- a/applications/lfrfid/scene/lfrfid_app_scene_saved_info.h +++ b/applications/lfrfid/scene/lfrfid_app_scene_saved_info.h @@ -8,8 +8,5 @@ public: void on_exit(LfRfidApp* app) final; private: - static void back_callback(void* context); - - string_t string_data; - string_t string_decrypted; + string_t string_info; }; diff --git a/applications/lfrfid/scene/lfrfid_app_scene_saved_key_menu.cpp b/applications/lfrfid/scene/lfrfid_app_scene_saved_key_menu.cpp index e6677fe8..e7a38d8a 100644 --- a/applications/lfrfid/scene/lfrfid_app_scene_saved_key_menu.cpp +++ b/applications/lfrfid/scene/lfrfid_app_scene_saved_key_menu.cpp @@ -28,8 +28,8 @@ bool LfRfidAppSceneSavedKeyMenu::on_event(LfRfidApp* app, LfRfidApp::Event* even bool consumed = false; if(event->type == LfRfidApp::EventType::MenuSelected) { - submenu_item_selected = event->payload.menu_index; - switch(event->payload.menu_index) { + submenu_item_selected = event->payload.signed_int; + switch(event->payload.signed_int) { case SubmenuEmulate: app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::Emulate); break; @@ -61,7 +61,7 @@ void LfRfidAppSceneSavedKeyMenu::submenu_callback(void* context, uint32_t index) LfRfidApp::Event event; event.type = LfRfidApp::EventType::MenuSelected; - event.payload.menu_index = index; + event.payload.signed_int = index; app->view_controller.send_event(&event); } diff --git a/applications/lfrfid/scene/lfrfid_app_scene_start.cpp b/applications/lfrfid/scene/lfrfid_app_scene_start.cpp index f5afad5c..5005c9af 100644 --- a/applications/lfrfid/scene/lfrfid_app_scene_start.cpp +++ b/applications/lfrfid/scene/lfrfid_app_scene_start.cpp @@ -4,6 +4,7 @@ typedef enum { SubmenuRead, SubmenuSaved, SubmenuAddManually, + SubmenuExtraActions, } SubmenuIndex; void LfRfidAppSceneStart::on_enter(LfRfidApp* app, bool need_restore) { @@ -12,6 +13,7 @@ void LfRfidAppSceneStart::on_enter(LfRfidApp* app, bool need_restore) { submenu->add_item("Read", SubmenuRead, submenu_callback, app); submenu->add_item("Saved", SubmenuSaved, submenu_callback, app); submenu->add_item("Add Manually", SubmenuAddManually, submenu_callback, app); + submenu->add_item("Extra Actions", SubmenuExtraActions, submenu_callback, app); if(need_restore) { submenu->set_selected_item(submenu_item_selected); @@ -20,15 +22,17 @@ void LfRfidAppSceneStart::on_enter(LfRfidApp* app, bool need_restore) { app->view_controller.switch_to(); // clear key - app->worker.key.clear(); + string_reset(app->file_name); + app->protocol_id = PROTOCOL_NO; + app->read_type = LFRFIDWorkerReadTypeAuto; } bool LfRfidAppSceneStart::on_event(LfRfidApp* app, LfRfidApp::Event* event) { bool consumed = false; if(event->type == LfRfidApp::EventType::MenuSelected) { - submenu_item_selected = event->payload.menu_index; - switch(event->payload.menu_index) { + submenu_item_selected = event->payload.signed_int; + switch(event->payload.signed_int) { case SubmenuRead: app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::Read); break; @@ -38,6 +42,9 @@ bool LfRfidAppSceneStart::on_event(LfRfidApp* app, LfRfidApp::Event* event) { case SubmenuAddManually: app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::SaveType); break; + case SubmenuExtraActions: + app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::ExtraActions); + break; } consumed = true; } @@ -54,7 +61,7 @@ void LfRfidAppSceneStart::submenu_callback(void* context, uint32_t index) { LfRfidApp::Event event; event.type = LfRfidApp::EventType::MenuSelected; - event.payload.menu_index = index; + event.payload.signed_int = index; app->view_controller.send_event(&event); } diff --git a/applications/lfrfid/scene/lfrfid_app_scene_write.cpp b/applications/lfrfid/scene/lfrfid_app_scene_write.cpp index 274ba315..8e04d8e8 100644 --- a/applications/lfrfid/scene/lfrfid_app_scene_write.cpp +++ b/applications/lfrfid/scene/lfrfid_app_scene_write.cpp @@ -1,66 +1,79 @@ #include "lfrfid_app_scene_write.h" -void LfRfidAppSceneWrite::on_enter(LfRfidApp* app, bool /* need_restore */) { - card_not_supported = false; - string_init(data_string); +static void lfrfid_write_callback(LFRFIDWorkerWriteResult result, void* ctx) { + LfRfidApp* app = static_cast(ctx); + LfRfidApp::Event event; - const uint8_t* data = app->worker.key.get_data(); - - for(uint8_t i = 0; i < app->worker.key.get_type_data_count(); i++) { - string_cat_printf(data_string, "%02X", data[i]); + switch(result) { + case LFRFIDWorkerWriteOK: + event.type = LfRfidApp::EventType::WriteEventOK; + break; + case LFRFIDWorkerWriteProtocolCannotBeWritten: + event.type = LfRfidApp::EventType::WriteEventProtocolCannotBeWritten; + break; + case LFRFIDWorkerWriteFobCannotBeWritten: + event.type = LfRfidApp::EventType::WriteEventFobCannotBeWritten; + break; + case LFRFIDWorkerWriteTooLongToWrite: + event.type = LfRfidApp::EventType::WriteEventTooLongToWrite; + break; } + app->view_controller.send_event(&event); +} + +void LfRfidAppSceneWrite::on_enter(LfRfidApp* app, bool /* need_restore */) { auto popup = app->view_controller.get(); popup->set_header("Writing", 89, 30, AlignCenter, AlignTop); - if(strlen(app->worker.key.get_name())) { - popup->set_text(app->worker.key.get_name(), 89, 43, AlignCenter, AlignTop); + if(string_size(app->file_name)) { + popup->set_text(string_get_cstr(app->file_name), 89, 43, AlignCenter, AlignTop); } else { - popup->set_text(string_get_cstr(data_string), 89, 43, AlignCenter, AlignTop); + popup->set_text( + protocol_dict_get_name(app->dict, app->protocol_id), 89, 43, AlignCenter, AlignTop); } popup->set_icon(0, 3, &I_RFIDDolphinSend_97x61); app->view_controller.switch_to(); - app->worker.start_write(); + lfrfid_worker_start_thread(app->lfworker); + lfrfid_worker_write_start( + app->lfworker, (LFRFIDProtocol)app->protocol_id, lfrfid_write_callback, app); + notification_message(app->notification, &sequence_blink_start_magenta); } bool LfRfidAppSceneWrite::on_event(LfRfidApp* app, LfRfidApp::Event* event) { - bool consumed = false; + bool consumed = true; + auto popup = app->view_controller.get(); - if(event->type == LfRfidApp::EventType::Tick) { - RfidWorker::WriteResult result = app->worker.write(); - - switch(result) { - case RfidWorker::WriteResult::Nothing: - notification_message(app->notification, &sequence_blink_magenta_10); - break; - case RfidWorker::WriteResult::Ok: - notification_message(app->notification, &sequence_success); - app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::WriteSuccess); - break; - case RfidWorker::WriteResult::NotWritable: - if(!card_not_supported) { - auto popup = app->view_controller.get(); - popup->set_icon(72, 17, &I_DolphinCommon_56x48); - popup->set_header("Still trying to write...", 64, 3, AlignCenter, AlignTop); - popup->set_text( - "Make sure this\ncard is writable\nand not\nprotected.", - 3, - 17, - AlignLeft, - AlignTop); - card_not_supported = true; - } - notification_message(app->notification, &sequence_blink_yellow_10); - break; - } + switch(event->type) { + case LfRfidApp::EventType::WriteEventOK: + notification_message(app->notification, &sequence_success); + app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::WriteSuccess); + break; + case LfRfidApp::EventType::WriteEventProtocolCannotBeWritten: + popup->set_icon(72, 17, &I_DolphinCommon_56x48); + popup->set_header("Error", 64, 3, AlignCenter, AlignTop); + popup->set_text("This protocol\ncannot be written", 3, 17, AlignLeft, AlignTop); + notification_message(app->notification, &sequence_blink_start_red); + break; + case LfRfidApp::EventType::WriteEventFobCannotBeWritten: + case LfRfidApp::EventType::WriteEventTooLongToWrite: + popup->set_icon(72, 17, &I_DolphinCommon_56x48); + popup->set_header("Still trying to write...", 64, 3, AlignCenter, AlignTop); + popup->set_text( + "Make sure this\ncard is writable\nand not\nprotected.", 3, 17, AlignLeft, AlignTop); + notification_message(app->notification, &sequence_blink_start_yellow); + break; + default: + consumed = false; } return consumed; } void LfRfidAppSceneWrite::on_exit(LfRfidApp* app) { + notification_message(app->notification, &sequence_blink_stop); app->view_controller.get()->clean(); - app->worker.stop_write(); - string_clear(data_string); + lfrfid_worker_stop(app->lfworker); + lfrfid_worker_stop_thread(app->lfworker); } diff --git a/applications/lfrfid/scene/lfrfid_app_scene_write.h b/applications/lfrfid/scene/lfrfid_app_scene_write.h index 3abadeba..7564eff9 100644 --- a/applications/lfrfid/scene/lfrfid_app_scene_write.h +++ b/applications/lfrfid/scene/lfrfid_app_scene_write.h @@ -6,8 +6,4 @@ public: void on_enter(LfRfidApp* app, bool need_restore) final; bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final; void on_exit(LfRfidApp* app) final; - -private: - string_t data_string; - bool card_not_supported; }; diff --git a/applications/storage/storage.h b/applications/storage/storage.h index 55a951d1..1a7c9349 100644 --- a/applications/storage/storage.h +++ b/applications/storage/storage.h @@ -136,6 +136,15 @@ bool storage_file_sync(File* file); */ bool storage_file_eof(File* file); +/** + * @brief Check that file exists + * + * @param storage + * @param path + * @return true if file exists + */ +bool storage_file_exists(Storage* storage, const char* path); + /******************* Dir Functions *******************/ /** Opens a directory to get objects from it diff --git a/applications/storage/storage_external_api.c b/applications/storage/storage_external_api.c index b32080df..80cafb28 100644 --- a/applications/storage/storage_external_api.c +++ b/applications/storage/storage_external_api.c @@ -240,6 +240,18 @@ bool storage_file_eof(File* file) { return S_RETURN_BOOL; } +bool storage_file_exists(Storage* storage, const char* path) { + bool exist = false; + FileInfo fileinfo; + FS_Error error = storage_common_stat(storage, path, &fileinfo); + + if(error == FSE_OK && !(fileinfo.flags & FSF_DIRECTORY)) { + exist = true; + } + + return exist; +} + /****************** DIR ******************/ static bool storage_dir_open_internal(File* file, const char* path) { diff --git a/applications/unit_tests/lfrfid/bit_lib_test.c b/applications/unit_tests/lfrfid/bit_lib_test.c new file mode 100644 index 00000000..72661570 --- /dev/null +++ b/applications/unit_tests/lfrfid/bit_lib_test.c @@ -0,0 +1,473 @@ +#include +#include "../minunit.h" +#include + +MU_TEST(test_bit_lib_increment_index) { + uint32_t index = 0; + + // test increment + for(uint32_t i = 0; i < 31; ++i) { + bit_lib_increment_index(index, 32); + mu_assert_int_eq(i + 1, index); + } + + // test wrap around + for(uint32_t i = 0; i < 512; ++i) { + bit_lib_increment_index(index, 32); + mu_assert_int_less_than(32, index); + } +} + +MU_TEST(test_bit_lib_is_set) { + uint32_t value = 0x0000FFFF; + + for(uint32_t i = 0; i < 16; ++i) { + mu_check(bit_lib_bit_is_set(value, i)); + mu_check(!bit_lib_bit_is_not_set(value, i)); + } + + for(uint32_t i = 16; i < 32; ++i) { + mu_check(!bit_lib_bit_is_set(value, i)); + mu_check(bit_lib_bit_is_not_set(value, i)); + } +} + +MU_TEST(test_bit_lib_push) { +#define TEST_BIT_LIB_PUSH_DATA_SIZE 4 + uint8_t data[TEST_BIT_LIB_PUSH_DATA_SIZE] = {0}; + uint8_t expected_data_1[TEST_BIT_LIB_PUSH_DATA_SIZE] = {0x00, 0x00, 0x0F, 0xFF}; + uint8_t expected_data_2[TEST_BIT_LIB_PUSH_DATA_SIZE] = {0x00, 0xFF, 0xF0, 0x00}; + uint8_t expected_data_3[TEST_BIT_LIB_PUSH_DATA_SIZE] = {0xFF, 0x00, 0x00, 0xFF}; + uint8_t expected_data_4[TEST_BIT_LIB_PUSH_DATA_SIZE] = {0xFF, 0xFF, 0xFF, 0xFF}; + uint8_t expected_data_5[TEST_BIT_LIB_PUSH_DATA_SIZE] = {0x00, 0x00, 0x00, 0x00}; + uint8_t expected_data_6[TEST_BIT_LIB_PUSH_DATA_SIZE] = {0xCC, 0xCC, 0xCC, 0xCC}; + + for(uint32_t i = 0; i < 12; ++i) { + bit_lib_push_bit(data, TEST_BIT_LIB_PUSH_DATA_SIZE, true); + } + mu_assert_mem_eq(expected_data_1, data, TEST_BIT_LIB_PUSH_DATA_SIZE); + + for(uint32_t i = 0; i < 12; ++i) { + bit_lib_push_bit(data, TEST_BIT_LIB_PUSH_DATA_SIZE, false); + } + mu_assert_mem_eq(expected_data_2, data, TEST_BIT_LIB_PUSH_DATA_SIZE); + + for(uint32_t i = 0; i < 4; ++i) { + bit_lib_push_bit(data, TEST_BIT_LIB_PUSH_DATA_SIZE, false); + } + for(uint32_t i = 0; i < 8; ++i) { + bit_lib_push_bit(data, TEST_BIT_LIB_PUSH_DATA_SIZE, true); + } + mu_assert_mem_eq(expected_data_3, data, TEST_BIT_LIB_PUSH_DATA_SIZE); + + for(uint32_t i = 0; i < TEST_BIT_LIB_PUSH_DATA_SIZE * 8; ++i) { + bit_lib_push_bit(data, TEST_BIT_LIB_PUSH_DATA_SIZE, true); + } + mu_assert_mem_eq(expected_data_4, data, TEST_BIT_LIB_PUSH_DATA_SIZE); + + for(uint32_t i = 0; i < TEST_BIT_LIB_PUSH_DATA_SIZE * 8; ++i) { + bit_lib_push_bit(data, TEST_BIT_LIB_PUSH_DATA_SIZE, false); + } + mu_assert_mem_eq(expected_data_5, data, TEST_BIT_LIB_PUSH_DATA_SIZE); + + for(uint32_t i = 0; i < TEST_BIT_LIB_PUSH_DATA_SIZE * 2; ++i) { + bit_lib_push_bit(data, TEST_BIT_LIB_PUSH_DATA_SIZE, true); + bit_lib_push_bit(data, TEST_BIT_LIB_PUSH_DATA_SIZE, true); + bit_lib_push_bit(data, TEST_BIT_LIB_PUSH_DATA_SIZE, false); + bit_lib_push_bit(data, TEST_BIT_LIB_PUSH_DATA_SIZE, false); + } + mu_assert_mem_eq(expected_data_6, data, TEST_BIT_LIB_PUSH_DATA_SIZE); +} + +MU_TEST(test_bit_lib_set_bit) { + uint8_t value[2] = {0x00, 0xFF}; + bit_lib_set_bit(value, 15, false); + mu_assert_mem_eq(value, ((uint8_t[]){0x00, 0xFE}), 2); + bit_lib_set_bit(value, 14, false); + mu_assert_mem_eq(value, ((uint8_t[]){0x00, 0xFC}), 2); + bit_lib_set_bit(value, 13, false); + mu_assert_mem_eq(value, ((uint8_t[]){0x00, 0xF8}), 2); + bit_lib_set_bit(value, 12, false); + mu_assert_mem_eq(value, ((uint8_t[]){0x00, 0xF0}), 2); + bit_lib_set_bit(value, 11, false); + mu_assert_mem_eq(value, ((uint8_t[]){0x00, 0xE0}), 2); + bit_lib_set_bit(value, 10, false); + mu_assert_mem_eq(value, ((uint8_t[]){0x00, 0xC0}), 2); + bit_lib_set_bit(value, 9, false); + mu_assert_mem_eq(value, ((uint8_t[]){0x00, 0x80}), 2); + bit_lib_set_bit(value, 8, false); + mu_assert_mem_eq(value, ((uint8_t[]){0x00, 0x00}), 2); + + bit_lib_set_bit(value, 7, true); + mu_assert_mem_eq(value, ((uint8_t[]){0x01, 0x00}), 2); + bit_lib_set_bit(value, 6, true); + mu_assert_mem_eq(value, ((uint8_t[]){0x03, 0x00}), 2); + bit_lib_set_bit(value, 5, true); + mu_assert_mem_eq(value, ((uint8_t[]){0x07, 0x00}), 2); + bit_lib_set_bit(value, 4, true); + mu_assert_mem_eq(value, ((uint8_t[]){0x0F, 0x00}), 2); + bit_lib_set_bit(value, 3, true); + mu_assert_mem_eq(value, ((uint8_t[]){0x1F, 0x00}), 2); + bit_lib_set_bit(value, 2, true); + mu_assert_mem_eq(value, ((uint8_t[]){0x3F, 0x00}), 2); + bit_lib_set_bit(value, 1, true); + mu_assert_mem_eq(value, ((uint8_t[]){0x7F, 0x00}), 2); + bit_lib_set_bit(value, 0, true); + mu_assert_mem_eq(value, ((uint8_t[]){0xFF, 0x00}), 2); +} + +MU_TEST(test_bit_lib_set_bits) { + uint8_t value[2] = {0b00000000, 0b11111111}; + // set 4 bits to 0b0100 from 12 index + bit_lib_set_bits(value, 12, 0b0100, 4); + // [0100] + mu_assert_mem_eq(value, ((uint8_t[]){0b00000000, 0b11110100}), 2); + + // set 2 bits to 0b11 from 11 index + bit_lib_set_bits(value, 11, 0b11, 2); + // [11] + mu_assert_mem_eq(value, ((uint8_t[]){0b00000000, 0b11111100}), 2); + + // set 3 bits to 0b111 from 0 index + bit_lib_set_bits(value, 0, 0b111, 3); + // [111] + mu_assert_mem_eq(value, ((uint8_t[]){0b11100000, 0b11111100}), 2); + + // set 8 bits to 0b11111000 from 3 index + bit_lib_set_bits(value, 3, 0b11111000, 8); + // [11111 000] + mu_assert_mem_eq(value, ((uint8_t[]){0b11111111, 0b00011100}), 2); +} + +MU_TEST(test_bit_lib_get_bit) { + uint8_t value[2] = {0b00000000, 0b11111111}; + for(uint32_t i = 0; i < 8; ++i) { + mu_check(bit_lib_get_bit(value, i) == false); + } + for(uint32_t i = 8; i < 16; ++i) { + mu_check(bit_lib_get_bit(value, i) == true); + } +} + +MU_TEST(test_bit_lib_get_bits) { + uint8_t value[2] = {0b00000000, 0b11111111}; + mu_assert_int_eq(0b00000000, bit_lib_get_bits(value, 0, 8)); + mu_assert_int_eq(0b00000001, bit_lib_get_bits(value, 1, 8)); + mu_assert_int_eq(0b00000011, bit_lib_get_bits(value, 2, 8)); + mu_assert_int_eq(0b00000111, bit_lib_get_bits(value, 3, 8)); + mu_assert_int_eq(0b00001111, bit_lib_get_bits(value, 4, 8)); + mu_assert_int_eq(0b00011111, bit_lib_get_bits(value, 5, 8)); + mu_assert_int_eq(0b00111111, bit_lib_get_bits(value, 6, 8)); + mu_assert_int_eq(0b01111111, bit_lib_get_bits(value, 7, 8)); + mu_assert_int_eq(0b11111111, bit_lib_get_bits(value, 8, 8)); +} + +MU_TEST(test_bit_lib_get_bits_16) { + uint8_t value[2] = {0b00001001, 0b10110001}; + mu_assert_int_eq(0b0, bit_lib_get_bits_16(value, 0, 1)); + mu_assert_int_eq(0b00, bit_lib_get_bits_16(value, 0, 2)); + mu_assert_int_eq(0b000, bit_lib_get_bits_16(value, 0, 3)); + mu_assert_int_eq(0b0000, bit_lib_get_bits_16(value, 0, 4)); + mu_assert_int_eq(0b00001, bit_lib_get_bits_16(value, 0, 5)); + mu_assert_int_eq(0b000010, bit_lib_get_bits_16(value, 0, 6)); + mu_assert_int_eq(0b0000100, bit_lib_get_bits_16(value, 0, 7)); + mu_assert_int_eq(0b00001001, bit_lib_get_bits_16(value, 0, 8)); + mu_assert_int_eq(0b000010011, bit_lib_get_bits_16(value, 0, 9)); + mu_assert_int_eq(0b0000100110, bit_lib_get_bits_16(value, 0, 10)); + mu_assert_int_eq(0b00001001101, bit_lib_get_bits_16(value, 0, 11)); + mu_assert_int_eq(0b000010011011, bit_lib_get_bits_16(value, 0, 12)); + mu_assert_int_eq(0b0000100110110, bit_lib_get_bits_16(value, 0, 13)); + mu_assert_int_eq(0b00001001101100, bit_lib_get_bits_16(value, 0, 14)); + mu_assert_int_eq(0b000010011011000, bit_lib_get_bits_16(value, 0, 15)); + mu_assert_int_eq(0b0000100110110001, bit_lib_get_bits_16(value, 0, 16)); +} + +MU_TEST(test_bit_lib_get_bits_32) { + uint8_t value[4] = {0b00001001, 0b10110001, 0b10001100, 0b01100010}; + mu_assert_int_eq(0b0, bit_lib_get_bits_32(value, 0, 1)); + mu_assert_int_eq(0b00, bit_lib_get_bits_32(value, 0, 2)); + mu_assert_int_eq(0b000, bit_lib_get_bits_32(value, 0, 3)); + mu_assert_int_eq(0b0000, bit_lib_get_bits_32(value, 0, 4)); + mu_assert_int_eq(0b00001, bit_lib_get_bits_32(value, 0, 5)); + mu_assert_int_eq(0b000010, bit_lib_get_bits_32(value, 0, 6)); + mu_assert_int_eq(0b0000100, bit_lib_get_bits_32(value, 0, 7)); + mu_assert_int_eq(0b00001001, bit_lib_get_bits_32(value, 0, 8)); + mu_assert_int_eq(0b000010011, bit_lib_get_bits_32(value, 0, 9)); + mu_assert_int_eq(0b0000100110, bit_lib_get_bits_32(value, 0, 10)); + mu_assert_int_eq(0b00001001101, bit_lib_get_bits_32(value, 0, 11)); + mu_assert_int_eq(0b000010011011, bit_lib_get_bits_32(value, 0, 12)); + mu_assert_int_eq(0b0000100110110, bit_lib_get_bits_32(value, 0, 13)); + mu_assert_int_eq(0b00001001101100, bit_lib_get_bits_32(value, 0, 14)); + mu_assert_int_eq(0b000010011011000, bit_lib_get_bits_32(value, 0, 15)); + mu_assert_int_eq(0b0000100110110001, bit_lib_get_bits_32(value, 0, 16)); + mu_assert_int_eq(0b00001001101100011, bit_lib_get_bits_32(value, 0, 17)); + mu_assert_int_eq(0b000010011011000110, bit_lib_get_bits_32(value, 0, 18)); + mu_assert_int_eq(0b0000100110110001100, bit_lib_get_bits_32(value, 0, 19)); + mu_assert_int_eq(0b00001001101100011000, bit_lib_get_bits_32(value, 0, 20)); + mu_assert_int_eq(0b000010011011000110001, bit_lib_get_bits_32(value, 0, 21)); + mu_assert_int_eq(0b0000100110110001100011, bit_lib_get_bits_32(value, 0, 22)); + mu_assert_int_eq(0b00001001101100011000110, bit_lib_get_bits_32(value, 0, 23)); + mu_assert_int_eq(0b000010011011000110001100, bit_lib_get_bits_32(value, 0, 24)); + mu_assert_int_eq(0b0000100110110001100011000, bit_lib_get_bits_32(value, 0, 25)); + mu_assert_int_eq(0b00001001101100011000110001, bit_lib_get_bits_32(value, 0, 26)); + mu_assert_int_eq(0b000010011011000110001100011, bit_lib_get_bits_32(value, 0, 27)); + mu_assert_int_eq(0b0000100110110001100011000110, bit_lib_get_bits_32(value, 0, 28)); + mu_assert_int_eq(0b00001001101100011000110001100, bit_lib_get_bits_32(value, 0, 29)); + mu_assert_int_eq(0b000010011011000110001100011000, bit_lib_get_bits_32(value, 0, 30)); + mu_assert_int_eq(0b0000100110110001100011000110001, bit_lib_get_bits_32(value, 0, 31)); + mu_assert_int_eq(0b00001001101100011000110001100010, bit_lib_get_bits_32(value, 0, 32)); +} + +MU_TEST(test_bit_lib_test_parity_u32) { + // test even parity + mu_assert_int_eq(bit_lib_test_parity_32(0b00000000, BitLibParityEven), 0); + mu_assert_int_eq(bit_lib_test_parity_32(0b00000001, BitLibParityEven), 1); + mu_assert_int_eq(bit_lib_test_parity_32(0b00000010, BitLibParityEven), 1); + mu_assert_int_eq(bit_lib_test_parity_32(0b00000011, BitLibParityEven), 0); + mu_assert_int_eq(bit_lib_test_parity_32(0b00000100, BitLibParityEven), 1); + mu_assert_int_eq(bit_lib_test_parity_32(0b00000101, BitLibParityEven), 0); + mu_assert_int_eq(bit_lib_test_parity_32(0b00000110, BitLibParityEven), 0); + mu_assert_int_eq(bit_lib_test_parity_32(0b00000111, BitLibParityEven), 1); + mu_assert_int_eq(bit_lib_test_parity_32(0b00001000, BitLibParityEven), 1); + mu_assert_int_eq(bit_lib_test_parity_32(0b00001001, BitLibParityEven), 0); + mu_assert_int_eq(bit_lib_test_parity_32(0b00001010, BitLibParityEven), 0); + mu_assert_int_eq(bit_lib_test_parity_32(0b00001011, BitLibParityEven), 1); + mu_assert_int_eq(bit_lib_test_parity_32(0b00001100, BitLibParityEven), 0); + mu_assert_int_eq(bit_lib_test_parity_32(0b00001101, BitLibParityEven), 1); + mu_assert_int_eq(bit_lib_test_parity_32(0b00001110, BitLibParityEven), 1); + mu_assert_int_eq(bit_lib_test_parity_32(0b00001111, BitLibParityEven), 0); + mu_assert_int_eq(bit_lib_test_parity_32(0b00010000, BitLibParityEven), 1); + + // test odd parity + mu_assert_int_eq(bit_lib_test_parity_32(0b00000000, BitLibParityOdd), 1); + mu_assert_int_eq(bit_lib_test_parity_32(0b00000001, BitLibParityOdd), 0); + mu_assert_int_eq(bit_lib_test_parity_32(0b00000010, BitLibParityOdd), 0); + mu_assert_int_eq(bit_lib_test_parity_32(0b00000011, BitLibParityOdd), 1); + mu_assert_int_eq(bit_lib_test_parity_32(0b00000100, BitLibParityOdd), 0); + mu_assert_int_eq(bit_lib_test_parity_32(0b00000101, BitLibParityOdd), 1); + mu_assert_int_eq(bit_lib_test_parity_32(0b00000110, BitLibParityOdd), 1); + mu_assert_int_eq(bit_lib_test_parity_32(0b00000111, BitLibParityOdd), 0); + mu_assert_int_eq(bit_lib_test_parity_32(0b00001000, BitLibParityOdd), 0); + mu_assert_int_eq(bit_lib_test_parity_32(0b00001001, BitLibParityOdd), 1); + mu_assert_int_eq(bit_lib_test_parity_32(0b00001010, BitLibParityOdd), 1); + mu_assert_int_eq(bit_lib_test_parity_32(0b00001011, BitLibParityOdd), 0); + mu_assert_int_eq(bit_lib_test_parity_32(0b00001100, BitLibParityOdd), 1); + mu_assert_int_eq(bit_lib_test_parity_32(0b00001101, BitLibParityOdd), 0); + mu_assert_int_eq(bit_lib_test_parity_32(0b00001110, BitLibParityOdd), 0); + mu_assert_int_eq(bit_lib_test_parity_32(0b00001111, BitLibParityOdd), 1); + mu_assert_int_eq(bit_lib_test_parity_32(0b00010000, BitLibParityOdd), 0); +} + +MU_TEST(test_bit_lib_test_parity) { + // next data contains valid parity for 1-3 nibble and invalid for 4 nibble + uint8_t data_always_0_parity[2] = {0b11101110, 0b11101111}; + uint8_t data_always_1_parity[2] = {0b00010001, 0b00010000}; + uint8_t data_always_odd_parity[2] = {0b00000011, 0b11110111}; + uint8_t data_always_even_parity[2] = {0b00010111, 0b10110011}; + + // test alawys 0 parity + mu_check(bit_lib_test_parity(data_always_0_parity, 0, 12, BitLibParityAlways0, 4)); + mu_check(bit_lib_test_parity(data_always_0_parity, 4, 8, BitLibParityAlways0, 4)); + mu_check(bit_lib_test_parity(data_always_0_parity, 8, 4, BitLibParityAlways0, 4)); + mu_check(bit_lib_test_parity(data_always_1_parity, 12, 4, BitLibParityAlways0, 4)); + + mu_check(!bit_lib_test_parity(data_always_0_parity, 0, 16, BitLibParityAlways0, 4)); + mu_check(!bit_lib_test_parity(data_always_0_parity, 4, 12, BitLibParityAlways0, 4)); + mu_check(!bit_lib_test_parity(data_always_0_parity, 8, 8, BitLibParityAlways0, 4)); + mu_check(!bit_lib_test_parity(data_always_0_parity, 12, 4, BitLibParityAlways0, 4)); + + // test alawys 1 parity + mu_check(bit_lib_test_parity(data_always_1_parity, 0, 12, BitLibParityAlways1, 4)); + mu_check(bit_lib_test_parity(data_always_1_parity, 4, 8, BitLibParityAlways1, 4)); + mu_check(bit_lib_test_parity(data_always_1_parity, 8, 4, BitLibParityAlways1, 4)); + mu_check(bit_lib_test_parity(data_always_0_parity, 12, 4, BitLibParityAlways1, 4)); + + mu_check(!bit_lib_test_parity(data_always_1_parity, 0, 16, BitLibParityAlways1, 4)); + mu_check(!bit_lib_test_parity(data_always_1_parity, 4, 12, BitLibParityAlways1, 4)); + mu_check(!bit_lib_test_parity(data_always_1_parity, 8, 8, BitLibParityAlways1, 4)); + mu_check(!bit_lib_test_parity(data_always_1_parity, 12, 4, BitLibParityAlways1, 4)); + + // test odd parity + mu_check(bit_lib_test_parity(data_always_odd_parity, 0, 12, BitLibParityOdd, 4)); + mu_check(bit_lib_test_parity(data_always_odd_parity, 4, 8, BitLibParityOdd, 4)); + mu_check(bit_lib_test_parity(data_always_odd_parity, 8, 4, BitLibParityOdd, 4)); + mu_check(bit_lib_test_parity(data_always_even_parity, 12, 4, BitLibParityOdd, 4)); + + mu_check(!bit_lib_test_parity(data_always_odd_parity, 0, 16, BitLibParityOdd, 4)); + mu_check(!bit_lib_test_parity(data_always_odd_parity, 4, 12, BitLibParityOdd, 4)); + mu_check(!bit_lib_test_parity(data_always_odd_parity, 8, 8, BitLibParityOdd, 4)); + mu_check(!bit_lib_test_parity(data_always_odd_parity, 12, 4, BitLibParityOdd, 4)); + + // test even parity + mu_check(bit_lib_test_parity(data_always_even_parity, 0, 12, BitLibParityEven, 4)); + mu_check(bit_lib_test_parity(data_always_even_parity, 4, 8, BitLibParityEven, 4)); + mu_check(bit_lib_test_parity(data_always_even_parity, 8, 4, BitLibParityEven, 4)); + mu_check(bit_lib_test_parity(data_always_odd_parity, 12, 4, BitLibParityEven, 4)); + + mu_check(!bit_lib_test_parity(data_always_even_parity, 0, 16, BitLibParityEven, 4)); + mu_check(!bit_lib_test_parity(data_always_even_parity, 4, 12, BitLibParityEven, 4)); + mu_check(!bit_lib_test_parity(data_always_even_parity, 8, 8, BitLibParityEven, 4)); + mu_check(!bit_lib_test_parity(data_always_even_parity, 12, 4, BitLibParityEven, 4)); +} + +MU_TEST(test_bit_lib_remove_bit_every_nth) { + // TODO: more tests + uint8_t data_i[1] = {0b00001111}; + uint8_t data_o[1] = {0b00011111}; + size_t length; + + length = bit_lib_remove_bit_every_nth(data_i, 0, 8, 3); + mu_assert_int_eq(6, length); + mu_assert_mem_eq(data_o, data_i, 1); +} + +MU_TEST(test_bit_lib_reverse_bits) { + uint8_t data_1_i[2] = {0b11001010, 0b00011111}; + uint8_t data_1_o[2] = {0b11111000, 0b01010011}; + + // reverse bits [0..15] + bit_lib_reverse_bits(data_1_i, 0, 16); + mu_assert_mem_eq(data_1_o, data_1_i, 2); + + uint8_t data_2_i[2] = {0b11001010, 0b00011111}; + uint8_t data_2_o[2] = {0b11001000, 0b01011111}; + + // reverse bits [4..11] + bit_lib_reverse_bits(data_2_i, 4, 8); + mu_assert_mem_eq(data_2_o, data_2_i, 2); +} + +MU_TEST(test_bit_lib_copy_bits) { + uint8_t data_1_i[2] = {0b11001010, 0b00011111}; + uint8_t data_1_o[2] = {0}; + + // data_1_o[0..15] = data_1_i[0..15] + bit_lib_copy_bits(data_1_o, 0, 16, data_1_i, 0); + mu_assert_mem_eq(data_1_i, data_1_o, 2); + + memset(data_1_o, 0, 2); + // data_1_o[4..11] = data_1_i[0..7] + bit_lib_copy_bits(data_1_o, 4, 8, data_1_i, 0); + mu_assert_mem_eq(((uint8_t[]){0b00001100, 0b10100000}), data_1_o, 2); +} + +MU_TEST(test_bit_lib_get_bit_count) { + mu_assert_int_eq(0, bit_lib_get_bit_count(0)); + mu_assert_int_eq(1, bit_lib_get_bit_count(0b1)); + mu_assert_int_eq(1, bit_lib_get_bit_count(0b10)); + mu_assert_int_eq(2, bit_lib_get_bit_count(0b11)); + mu_assert_int_eq(4, bit_lib_get_bit_count(0b11000011)); + mu_assert_int_eq(6, bit_lib_get_bit_count(0b11000011000011)); + mu_assert_int_eq(8, bit_lib_get_bit_count(0b11111111)); + mu_assert_int_eq(16, bit_lib_get_bit_count(0b11111110000000000000000111111111)); + mu_assert_int_eq(32, bit_lib_get_bit_count(0b11111111111111111111111111111111)); +} + +MU_TEST(test_bit_lib_reverse_16_fast) { + mu_assert_int_eq(0b0000000000000000, bit_lib_reverse_16_fast(0b0000000000000000)); + mu_assert_int_eq(0b1000000000000000, bit_lib_reverse_16_fast(0b0000000000000001)); + mu_assert_int_eq(0b1100000000000000, bit_lib_reverse_16_fast(0b0000000000000011)); + mu_assert_int_eq(0b0000100000001001, bit_lib_reverse_16_fast(0b1001000000010000)); +} + +MU_TEST(test_bit_lib_crc16) { + uint8_t data[9] = {'1', '2', '3', '4', '5', '6', '7', '8', '9'}; + uint8_t data_size = 9; + + // Algorithm + // Check Poly Init RefIn RefOut XorOut + // CRC-16/CCITT-FALSE + // 0x29B1 0x1021 0xFFFF false false 0x0000 + mu_assert_int_eq(0x29B1, bit_lib_crc16(data, data_size, 0x1021, 0xFFFF, false, false, 0x0000)); + // CRC-16/ARC + // 0xBB3D 0x8005 0x0000 true true 0x0000 + mu_assert_int_eq(0xBB3D, bit_lib_crc16(data, data_size, 0x8005, 0x0000, true, true, 0x0000)); + // CRC-16/AUG-CCITT + // 0xE5CC 0x1021 0x1D0F false false 0x0000 + mu_assert_int_eq(0xE5CC, bit_lib_crc16(data, data_size, 0x1021, 0x1D0F, false, false, 0x0000)); + // CRC-16/BUYPASS + // 0xFEE8 0x8005 0x0000 false false 0x0000 + mu_assert_int_eq(0xFEE8, bit_lib_crc16(data, data_size, 0x8005, 0x0000, false, false, 0x0000)); + // CRC-16/CDMA2000 + // 0x4C06 0xC867 0xFFFF false false 0x0000 + mu_assert_int_eq(0x4C06, bit_lib_crc16(data, data_size, 0xC867, 0xFFFF, false, false, 0x0000)); + // CRC-16/DDS-110 + // 0x9ECF 0x8005 0x800D false false 0x0000 + mu_assert_int_eq(0x9ECF, bit_lib_crc16(data, data_size, 0x8005, 0x800D, false, false, 0x0000)); + // CRC-16/DECT-R + // 0x007E 0x0589 0x0000 false false 0x0001 + mu_assert_int_eq(0x007E, bit_lib_crc16(data, data_size, 0x0589, 0x0000, false, false, 0x0001)); + // CRC-16/DECT-X + // 0x007F 0x0589 0x0000 false false 0x0000 + mu_assert_int_eq(0x007F, bit_lib_crc16(data, data_size, 0x0589, 0x0000, false, false, 0x0000)); + // CRC-16/DNP + // 0xEA82 0x3D65 0x0000 true true 0xFFFF + mu_assert_int_eq(0xEA82, bit_lib_crc16(data, data_size, 0x3D65, 0x0000, true, true, 0xFFFF)); + // CRC-16/EN-13757 + // 0xC2B7 0x3D65 0x0000 false false 0xFFFF + mu_assert_int_eq(0xC2B7, bit_lib_crc16(data, data_size, 0x3D65, 0x0000, false, false, 0xFFFF)); + // CRC-16/GENIBUS + // 0xD64E 0x1021 0xFFFF false false 0xFFFF + mu_assert_int_eq(0xD64E, bit_lib_crc16(data, data_size, 0x1021, 0xFFFF, false, false, 0xFFFF)); + // CRC-16/MAXIM + // 0x44C2 0x8005 0x0000 true true 0xFFFF + mu_assert_int_eq(0x44C2, bit_lib_crc16(data, data_size, 0x8005, 0x0000, true, true, 0xFFFF)); + // CRC-16/MCRF4XX + // 0x6F91 0x1021 0xFFFF true true 0x0000 + mu_assert_int_eq(0x6F91, bit_lib_crc16(data, data_size, 0x1021, 0xFFFF, true, true, 0x0000)); + // CRC-16/RIELLO + // 0x63D0 0x1021 0xB2AA true true 0x0000 + mu_assert_int_eq(0x63D0, bit_lib_crc16(data, data_size, 0x1021, 0xB2AA, true, true, 0x0000)); + // CRC-16/T10-DIF + // 0xD0DB 0x8BB7 0x0000 false false 0x0000 + mu_assert_int_eq(0xD0DB, bit_lib_crc16(data, data_size, 0x8BB7, 0x0000, false, false, 0x0000)); + // CRC-16/TELEDISK + // 0x0FB3 0xA097 0x0000 false false 0x0000 + mu_assert_int_eq(0x0FB3, bit_lib_crc16(data, data_size, 0xA097, 0x0000, false, false, 0x0000)); + // CRC-16/TMS37157 + // 0x26B1 0x1021 0x89EC true true 0x0000 + mu_assert_int_eq(0x26B1, bit_lib_crc16(data, data_size, 0x1021, 0x89EC, true, true, 0x0000)); + // CRC-16/USB + // 0xB4C8 0x8005 0xFFFF true true 0xFFFF + mu_assert_int_eq(0xB4C8, bit_lib_crc16(data, data_size, 0x8005, 0xFFFF, true, true, 0xFFFF)); + // CRC-A + // 0xBF05 0x1021 0xC6C6 true true 0x0000 + mu_assert_int_eq(0xBF05, bit_lib_crc16(data, data_size, 0x1021, 0xC6C6, true, true, 0x0000)); + // CRC-16/KERMIT + // 0x2189 0x1021 0x0000 true true 0x0000 + mu_assert_int_eq(0x2189, bit_lib_crc16(data, data_size, 0x1021, 0x0000, true, true, 0x0000)); + // CRC-16/MODBUS + // 0x4B37 0x8005 0xFFFF true true 0x0000 + mu_assert_int_eq(0x4B37, bit_lib_crc16(data, data_size, 0x8005, 0xFFFF, true, true, 0x0000)); + // CRC-16/X-25 + // 0x906E 0x1021 0xFFFF true true 0xFFFF + mu_assert_int_eq(0x906E, bit_lib_crc16(data, data_size, 0x1021, 0xFFFF, true, true, 0xFFFF)); + // CRC-16/XMODEM + // 0x31C3 0x1021 0x0000 false false 0x0000 + mu_assert_int_eq(0x31C3, bit_lib_crc16(data, data_size, 0x1021, 0x0000, false, false, 0x0000)); +} + +MU_TEST_SUITE(test_bit_lib) { + MU_RUN_TEST(test_bit_lib_increment_index); + MU_RUN_TEST(test_bit_lib_is_set); + MU_RUN_TEST(test_bit_lib_push); + MU_RUN_TEST(test_bit_lib_set_bit); + MU_RUN_TEST(test_bit_lib_set_bits); + MU_RUN_TEST(test_bit_lib_get_bit); + MU_RUN_TEST(test_bit_lib_get_bits); + MU_RUN_TEST(test_bit_lib_get_bits_16); + MU_RUN_TEST(test_bit_lib_get_bits_32); + MU_RUN_TEST(test_bit_lib_test_parity_u32); + MU_RUN_TEST(test_bit_lib_test_parity); + MU_RUN_TEST(test_bit_lib_remove_bit_every_nth); + MU_RUN_TEST(test_bit_lib_copy_bits); + MU_RUN_TEST(test_bit_lib_reverse_bits); + MU_RUN_TEST(test_bit_lib_get_bit_count); + MU_RUN_TEST(test_bit_lib_reverse_16_fast); + MU_RUN_TEST(test_bit_lib_crc16); +} + +int run_minunit_test_bit_lib() { + MU_RUN_SUITE(test_bit_lib); + return MU_EXIT_CODE; +} \ No newline at end of file diff --git a/applications/unit_tests/lfrfid/lfrfid_protocols.c b/applications/unit_tests/lfrfid/lfrfid_protocols.c new file mode 100644 index 00000000..4401cbb4 --- /dev/null +++ b/applications/unit_tests/lfrfid/lfrfid_protocols.c @@ -0,0 +1,464 @@ +#include +#include "../minunit.h" +#include +#include +#include + +#define LF_RFID_READ_TIMING_MULTIPLIER 8 + +#define EM_TEST_DATA \ + { 0x58, 0x00, 0x85, 0x64, 0x02 } +#define EM_TEST_DATA_SIZE 5 +#define EM_TEST_EMULATION_TIMINGS_COUNT (64 * 2) + +const int8_t em_test_timings[EM_TEST_EMULATION_TIMINGS_COUNT] = { + 32, -32, 32, -32, 32, -32, 32, -32, 32, -32, 32, -32, 32, -32, 32, -32, 32, -32, -32, + 32, 32, -32, -32, 32, 32, -32, -32, 32, 32, -32, -32, 32, -32, 32, -32, 32, 32, -32, + -32, 32, -32, 32, -32, 32, -32, 32, -32, 32, -32, 32, -32, 32, -32, 32, -32, 32, -32, + 32, 32, -32, -32, 32, -32, 32, -32, 32, 32, -32, -32, 32, 32, -32, -32, 32, 32, -32, + -32, 32, -32, 32, 32, -32, 32, -32, -32, 32, -32, 32, -32, 32, 32, -32, -32, 32, -32, + 32, 32, -32, -32, 32, -32, 32, -32, 32, -32, 32, -32, 32, -32, 32, -32, 32, 32, -32, + -32, 32, 32, -32, -32, 32, -32, 32, -32, 32, -32, 32, -32, 32, +}; + +#define HID10301_TEST_DATA \ + { 0x8D, 0x48, 0xA8 } +#define HID10301_TEST_DATA_SIZE 3 +#define HID10301_TEST_EMULATION_TIMINGS_COUNT (541 * 2) + +const int8_t hid10301_test_timings[HID10301_TEST_EMULATION_TIMINGS_COUNT] = { + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, + 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, + 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, + 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, + 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, + 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, + 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, + 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, + 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, + 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, + 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, + 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, + 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, + 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, + 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, + 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, +}; + +#define IOPROX_XSF_TEST_DATA \ + { 0x65, 0x01, 0x05, 0x39 } +#define IOPROX_XSF_TEST_DATA_SIZE 4 +#define IOPROX_XSF_TEST_EMULATION_TIMINGS_COUNT (468 * 2) + +const int8_t ioprox_xsf_test_timings[IOPROX_XSF_TEST_EMULATION_TIMINGS_COUNT] = { + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, + 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, + 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, + 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, + 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, + 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, + 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, + 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, + 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, + 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, + 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, + 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, +}; + +#define INDALA26_EMULATION_TIMINGS_COUNT (1024 * 2) +#define INDALA26_TEST_DATA \ + { 0x3B, 0x73, 0x64, 0xA8 } +#define INDALA26_TEST_DATA_SIZE 4 + +const int8_t indala26_test_timings[INDALA26_EMULATION_TIMINGS_COUNT] = { + 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, + 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, + 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, + 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, + 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, + 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, + 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, + 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, + 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, + 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, + 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, + 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, + 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, + 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, + 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, + 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, + 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, + 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, + 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, + 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, + 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, +}; + +MU_TEST(test_lfrfid_protocol_em_read_simple) { + ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax); + mu_assert_int_eq(EM_TEST_DATA_SIZE, protocol_dict_get_data_size(dict, LFRFIDProtocolEM4100)); + mu_assert_string_eq("EM4100", protocol_dict_get_name(dict, LFRFIDProtocolEM4100)); + mu_assert_string_eq("EM-Micro", protocol_dict_get_manufacturer(dict, LFRFIDProtocolEM4100)); + + const uint8_t data[EM_TEST_DATA_SIZE] = EM_TEST_DATA; + + protocol_dict_decoders_start(dict); + + ProtocolId protocol = PROTOCOL_NO; + PulseGlue* pulse_glue = pulse_glue_alloc(); + + for(size_t i = 0; i < EM_TEST_EMULATION_TIMINGS_COUNT * 10; i++) { + bool pulse_pop = pulse_glue_push( + pulse_glue, + em_test_timings[i % EM_TEST_EMULATION_TIMINGS_COUNT] >= 0, + abs(em_test_timings[i % EM_TEST_EMULATION_TIMINGS_COUNT]) * + LF_RFID_READ_TIMING_MULTIPLIER); + + if(pulse_pop) { + uint32_t length, period; + pulse_glue_pop(pulse_glue, &length, &period); + + protocol = protocol_dict_decoders_feed(dict, true, period); + if(protocol != PROTOCOL_NO) break; + + protocol = protocol_dict_decoders_feed(dict, false, length - period); + if(protocol != PROTOCOL_NO) break; + } + } + + pulse_glue_free(pulse_glue); + + mu_assert_int_eq(LFRFIDProtocolEM4100, protocol); + uint8_t received_data[EM_TEST_DATA_SIZE] = {0}; + protocol_dict_get_data(dict, protocol, received_data, EM_TEST_DATA_SIZE); + + mu_assert_mem_eq(data, received_data, EM_TEST_DATA_SIZE); + + protocol_dict_free(dict); +} + +MU_TEST(test_lfrfid_protocol_em_emulate_simple) { + ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax); + mu_assert_int_eq(EM_TEST_DATA_SIZE, protocol_dict_get_data_size(dict, LFRFIDProtocolEM4100)); + mu_assert_string_eq("EM4100", protocol_dict_get_name(dict, LFRFIDProtocolEM4100)); + mu_assert_string_eq("EM-Micro", protocol_dict_get_manufacturer(dict, LFRFIDProtocolEM4100)); + + const uint8_t data[EM_TEST_DATA_SIZE] = EM_TEST_DATA; + + protocol_dict_set_data(dict, LFRFIDProtocolEM4100, data, EM_TEST_DATA_SIZE); + mu_check(protocol_dict_encoder_start(dict, LFRFIDProtocolEM4100)); + + for(size_t i = 0; i < EM_TEST_EMULATION_TIMINGS_COUNT; i++) { + LevelDuration level_duration = protocol_dict_encoder_yield(dict, LFRFIDProtocolEM4100); + + if(level_duration_get_level(level_duration)) { + mu_assert_int_eq(em_test_timings[i], level_duration_get_duration(level_duration)); + } else { + mu_assert_int_eq(em_test_timings[i], -level_duration_get_duration(level_duration)); + } + } + + protocol_dict_free(dict); +} + +MU_TEST(test_lfrfid_protocol_h10301_read_simple) { + ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax); + mu_assert_int_eq( + HID10301_TEST_DATA_SIZE, protocol_dict_get_data_size(dict, LFRFIDProtocolH10301)); + mu_assert_string_eq("H10301", protocol_dict_get_name(dict, LFRFIDProtocolH10301)); + mu_assert_string_eq("HID", protocol_dict_get_manufacturer(dict, LFRFIDProtocolH10301)); + + const uint8_t data[HID10301_TEST_DATA_SIZE] = HID10301_TEST_DATA; + + protocol_dict_decoders_start(dict); + + ProtocolId protocol = PROTOCOL_NO; + PulseGlue* pulse_glue = pulse_glue_alloc(); + + for(size_t i = 0; i < HID10301_TEST_EMULATION_TIMINGS_COUNT * 10; i++) { + bool pulse_pop = pulse_glue_push( + pulse_glue, + hid10301_test_timings[i % HID10301_TEST_EMULATION_TIMINGS_COUNT] >= 0, + abs(hid10301_test_timings[i % HID10301_TEST_EMULATION_TIMINGS_COUNT]) * + LF_RFID_READ_TIMING_MULTIPLIER); + + if(pulse_pop) { + uint32_t length, period; + pulse_glue_pop(pulse_glue, &length, &period); + + protocol = protocol_dict_decoders_feed(dict, true, period); + if(protocol != PROTOCOL_NO) break; + + protocol = protocol_dict_decoders_feed(dict, false, length - period); + if(protocol != PROTOCOL_NO) break; + } + } + + pulse_glue_free(pulse_glue); + + mu_assert_int_eq(LFRFIDProtocolH10301, protocol); + uint8_t received_data[HID10301_TEST_DATA_SIZE] = {0}; + protocol_dict_get_data(dict, protocol, received_data, HID10301_TEST_DATA_SIZE); + + mu_assert_mem_eq(data, received_data, HID10301_TEST_DATA_SIZE); + + protocol_dict_free(dict); +} + +MU_TEST(test_lfrfid_protocol_h10301_emulate_simple) { + ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax); + mu_assert_int_eq( + HID10301_TEST_DATA_SIZE, protocol_dict_get_data_size(dict, LFRFIDProtocolH10301)); + mu_assert_string_eq("H10301", protocol_dict_get_name(dict, LFRFIDProtocolH10301)); + mu_assert_string_eq("HID", protocol_dict_get_manufacturer(dict, LFRFIDProtocolH10301)); + + const uint8_t data[HID10301_TEST_DATA_SIZE] = HID10301_TEST_DATA; + + protocol_dict_set_data(dict, LFRFIDProtocolH10301, data, HID10301_TEST_DATA_SIZE); + mu_check(protocol_dict_encoder_start(dict, LFRFIDProtocolH10301)); + + for(size_t i = 0; i < HID10301_TEST_EMULATION_TIMINGS_COUNT; i++) { + LevelDuration level_duration = protocol_dict_encoder_yield(dict, LFRFIDProtocolH10301); + + if(level_duration_get_level(level_duration)) { + mu_assert_int_eq( + hid10301_test_timings[i], level_duration_get_duration(level_duration)); + } else { + mu_assert_int_eq( + hid10301_test_timings[i], -level_duration_get_duration(level_duration)); + } + } + + protocol_dict_free(dict); +} + +MU_TEST(test_lfrfid_protocol_ioprox_xsf_read_simple) { + ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax); + mu_assert_int_eq( + IOPROX_XSF_TEST_DATA_SIZE, protocol_dict_get_data_size(dict, LFRFIDProtocolIOProxXSF)); + mu_assert_string_eq("IoProxXSF", protocol_dict_get_name(dict, LFRFIDProtocolIOProxXSF)); + mu_assert_string_eq("Kantech", protocol_dict_get_manufacturer(dict, LFRFIDProtocolIOProxXSF)); + + const uint8_t data[IOPROX_XSF_TEST_DATA_SIZE] = IOPROX_XSF_TEST_DATA; + + protocol_dict_decoders_start(dict); + + ProtocolId protocol = PROTOCOL_NO; + PulseGlue* pulse_glue = pulse_glue_alloc(); + + for(size_t i = 0; i < IOPROX_XSF_TEST_EMULATION_TIMINGS_COUNT * 10; i++) { + bool pulse_pop = pulse_glue_push( + pulse_glue, + ioprox_xsf_test_timings[i % IOPROX_XSF_TEST_EMULATION_TIMINGS_COUNT] >= 0, + abs(ioprox_xsf_test_timings[i % IOPROX_XSF_TEST_EMULATION_TIMINGS_COUNT]) * + LF_RFID_READ_TIMING_MULTIPLIER); + + if(pulse_pop) { + uint32_t length, period; + pulse_glue_pop(pulse_glue, &length, &period); + + protocol = protocol_dict_decoders_feed(dict, true, period); + if(protocol != PROTOCOL_NO) break; + + protocol = protocol_dict_decoders_feed(dict, false, length - period); + if(protocol != PROTOCOL_NO) break; + } + } + + pulse_glue_free(pulse_glue); + + mu_assert_int_eq(LFRFIDProtocolIOProxXSF, protocol); + uint8_t received_data[IOPROX_XSF_TEST_DATA_SIZE] = {0}; + protocol_dict_get_data(dict, protocol, received_data, IOPROX_XSF_TEST_DATA_SIZE); + + mu_assert_mem_eq(data, received_data, IOPROX_XSF_TEST_DATA_SIZE); + + protocol_dict_free(dict); +} + +MU_TEST(test_lfrfid_protocol_ioprox_xsf_emulate_simple) { + ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax); + mu_assert_int_eq( + IOPROX_XSF_TEST_DATA_SIZE, protocol_dict_get_data_size(dict, LFRFIDProtocolIOProxXSF)); + mu_assert_string_eq("IoProxXSF", protocol_dict_get_name(dict, LFRFIDProtocolIOProxXSF)); + mu_assert_string_eq("Kantech", protocol_dict_get_manufacturer(dict, LFRFIDProtocolIOProxXSF)); + + const uint8_t data[IOPROX_XSF_TEST_DATA_SIZE] = IOPROX_XSF_TEST_DATA; + + protocol_dict_set_data(dict, LFRFIDProtocolIOProxXSF, data, IOPROX_XSF_TEST_DATA_SIZE); + mu_check(protocol_dict_encoder_start(dict, LFRFIDProtocolIOProxXSF)); + + for(size_t i = 0; i < IOPROX_XSF_TEST_EMULATION_TIMINGS_COUNT; i++) { + LevelDuration level_duration = protocol_dict_encoder_yield(dict, LFRFIDProtocolIOProxXSF); + + if(level_duration_get_level(level_duration)) { + mu_assert_int_eq( + ioprox_xsf_test_timings[i], level_duration_get_duration(level_duration)); + } else { + mu_assert_int_eq( + ioprox_xsf_test_timings[i], -level_duration_get_duration(level_duration)); + } + } + + protocol_dict_free(dict); +} + +MU_TEST(test_lfrfid_protocol_inadala26_emulate_simple) { + ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax); + mu_assert_int_eq( + INDALA26_TEST_DATA_SIZE, protocol_dict_get_data_size(dict, LFRFIDProtocolIndala26)); + mu_assert_string_eq("Indala26", protocol_dict_get_name(dict, LFRFIDProtocolIndala26)); + mu_assert_string_eq("Motorola", protocol_dict_get_manufacturer(dict, LFRFIDProtocolIndala26)); + + const uint8_t data[INDALA26_TEST_DATA_SIZE] = INDALA26_TEST_DATA; + + protocol_dict_set_data(dict, LFRFIDProtocolIndala26, data, INDALA26_TEST_DATA_SIZE); + mu_check(protocol_dict_encoder_start(dict, LFRFIDProtocolIndala26)); + + for(size_t i = 0; i < INDALA26_EMULATION_TIMINGS_COUNT; i++) { + LevelDuration level_duration = protocol_dict_encoder_yield(dict, LFRFIDProtocolIndala26); + + if(level_duration_get_level(level_duration)) { + mu_assert_int_eq( + indala26_test_timings[i], level_duration_get_duration(level_duration)); + } else { + mu_assert_int_eq( + indala26_test_timings[i], -level_duration_get_duration(level_duration)); + } + } + + protocol_dict_free(dict); +} + +MU_TEST_SUITE(test_lfrfid_protocols_suite) { + MU_RUN_TEST(test_lfrfid_protocol_em_read_simple); + MU_RUN_TEST(test_lfrfid_protocol_em_emulate_simple); + + MU_RUN_TEST(test_lfrfid_protocol_h10301_read_simple); + MU_RUN_TEST(test_lfrfid_protocol_h10301_emulate_simple); + + MU_RUN_TEST(test_lfrfid_protocol_ioprox_xsf_read_simple); + MU_RUN_TEST(test_lfrfid_protocol_ioprox_xsf_emulate_simple); + + MU_RUN_TEST(test_lfrfid_protocol_inadala26_emulate_simple); +} + +int run_minunit_test_lfrfid_protocols() { + MU_RUN_SUITE(test_lfrfid_protocols_suite); + return MU_EXIT_CODE; +} \ No newline at end of file diff --git a/applications/unit_tests/minunit.h b/applications/unit_tests/minunit.h index d1efd13e..17eb7b3f 100644 --- a/applications/unit_tests/minunit.h +++ b/applications/unit_tests/minunit.h @@ -151,46 +151,46 @@ void minunit_print_fail(const char* error); #define MU_EXIT_CODE minunit_fail /* Assertions */ -#define mu_check(test) \ - MU__SAFE_BLOCK( \ - minunit_assert++; if(!(test)) { \ - snprintf( \ - minunit_last_message, \ - MINUNIT_MESSAGE_LEN, \ - "%s failed:\n\t%s:%d: %s", \ - __func__, \ - __FILE__, \ - __LINE__, \ - #test); \ - minunit_status = 1; \ - return; \ +#define mu_check(test) \ + MU__SAFE_BLOCK( \ + minunit_assert++; if(!(test)) { \ + snprintf( \ + minunit_last_message, \ + MINUNIT_MESSAGE_LEN, \ + "%s failed:\r\n\t%s:%d: %s", \ + __func__, \ + __FILE__, \ + __LINE__, \ + #test); \ + minunit_status = 1; \ + return; \ } else { minunit_print_progress(); }) -#define mu_fail(message) \ - MU__SAFE_BLOCK(minunit_assert++; snprintf( \ - minunit_last_message, \ - MINUNIT_MESSAGE_LEN, \ - "%s failed:\n\t%s:%d: %s", \ - __func__, \ - __FILE__, \ - __LINE__, \ - message); \ - minunit_status = 1; \ +#define mu_fail(message) \ + MU__SAFE_BLOCK(minunit_assert++; snprintf( \ + minunit_last_message, \ + MINUNIT_MESSAGE_LEN, \ + "%s failed:\r\n\t%s:%d: %s", \ + __func__, \ + __FILE__, \ + __LINE__, \ + message); \ + minunit_status = 1; \ return;) -#define mu_assert(test, message) \ - MU__SAFE_BLOCK( \ - minunit_assert++; if(!(test)) { \ - snprintf( \ - minunit_last_message, \ - MINUNIT_MESSAGE_LEN, \ - "%s failed:\n\t%s:%d: %s", \ - __func__, \ - __FILE__, \ - __LINE__, \ - message); \ - minunit_status = 1; \ - return; \ +#define mu_assert(test, message) \ + MU__SAFE_BLOCK( \ + minunit_assert++; if(!(test)) { \ + snprintf( \ + minunit_last_message, \ + MINUNIT_MESSAGE_LEN, \ + "%s failed:\r\n\t%s:%d: %s", \ + __func__, \ + __FILE__, \ + __LINE__, \ + message); \ + minunit_status = 1; \ + return; \ } else { minunit_print_progress(); }) #define mu_assert_int_eq(expected, result) \ @@ -201,7 +201,7 @@ void minunit_print_fail(const char* error); snprintf( \ minunit_last_message, \ MINUNIT_MESSAGE_LEN, \ - "%s failed:\n\t%s:%d: %d expected but was %d", \ + "%s failed:\r\n\t%s:%d: %d expected but was %d", \ __func__, \ __FILE__, \ __LINE__, \ @@ -219,7 +219,7 @@ void minunit_print_fail(const char* error); snprintf( \ minunit_last_message, \ MINUNIT_MESSAGE_LEN, \ - "%s failed:\n\t%s:%d: expected different results but both were %d", \ + "%s failed:\r\n\t%s:%d: expected different results but both were %d", \ __func__, \ __FILE__, \ __LINE__, \ @@ -236,7 +236,7 @@ void minunit_print_fail(const char* error); snprintf( \ minunit_last_message, \ MINUNIT_MESSAGE_LEN, \ - "%s failed:\n\t%s:%d: %d <= %d", \ + "%s failed:\r\n\t%s:%d: %d <= %d", \ __func__, \ __FILE__, \ __LINE__, \ @@ -254,7 +254,7 @@ void minunit_print_fail(const char* error); snprintf( \ minunit_last_message, \ MINUNIT_MESSAGE_LEN, \ - "%s failed:\n\t%s:%d: %d >= %d", \ + "%s failed:\r\n\t%s:%d: %d >= %d", \ __func__, \ __FILE__, \ __LINE__, \ @@ -274,7 +274,7 @@ void minunit_print_fail(const char* error); snprintf( \ minunit_last_message, \ MINUNIT_MESSAGE_LEN, \ - "%s failed:\n\t%s:%d: %d was not between (inclusive) %d and %d", \ + "%s failed:\r\n\t%s:%d: %d was not between (inclusive) %d and %d", \ __func__, \ __FILE__, \ __LINE__, \ @@ -302,7 +302,7 @@ void minunit_print_fail(const char* error); snprintf( \ minunit_last_message, \ MINUNIT_MESSAGE_LEN, \ - "%s failed:\n\t%s:%d: expected to be one of %s but was %d", \ + "%s failed:\r\n\t%s:%d: expected to be one of %s but was %d", \ __func__, \ __FILE__, \ __LINE__, \ @@ -321,7 +321,7 @@ void minunit_print_fail(const char* error); snprintf( \ minunit_last_message, \ MINUNIT_MESSAGE_LEN, \ - "%s failed:\n\t%s:%d: %.*g expected but was %.*g", \ + "%s failed:\r\n\t%s:%d: %.*g expected but was %.*g", \ __func__, \ __FILE__, \ __LINE__, \ @@ -341,7 +341,7 @@ void minunit_print_fail(const char* error); snprintf( \ minunit_last_message, \ MINUNIT_MESSAGE_LEN, \ - "%s failed:\n\t%s:%d: %f <= %f", \ + "%s failed:\r\n\t%s:%d: %f <= %f", \ __func__, \ __FILE__, \ __LINE__, \ @@ -359,7 +359,7 @@ void minunit_print_fail(const char* error); snprintf( \ minunit_last_message, \ MINUNIT_MESSAGE_LEN, \ - "%s failed:\n\t%s:%d: %f >= %f", \ + "%s failed:\r\n\t%s:%d: %f >= %f", \ __func__, \ __FILE__, \ __LINE__, \ @@ -379,7 +379,7 @@ void minunit_print_fail(const char* error); snprintf( \ minunit_last_message, \ MINUNIT_MESSAGE_LEN, \ - "%s failed:\n\t%s:%d: %f was not between (inclusive) %f and %f", \ + "%s failed:\r\n\t%s:%d: %f was not between (inclusive) %f and %f", \ __func__, \ __FILE__, \ __LINE__, \ @@ -400,7 +400,7 @@ void minunit_print_fail(const char* error); snprintf( \ minunit_last_message, \ MINUNIT_MESSAGE_LEN, \ - "%s failed:\n\t%s:%d: '%s' expected but was '%s'", \ + "%s failed:\r\n\t%s:%d: '%s' expected but was '%s'", \ __func__, \ __FILE__, \ __LINE__, \ @@ -410,13 +410,41 @@ void minunit_print_fail(const char* error); return; \ } else { minunit_print_progress(); }) +#define mu_assert_mem_eq(expected, result, size) \ + MU__SAFE_BLOCK( \ + const void* minunit_tmp_e = expected; const void* minunit_tmp_r = result; \ + minunit_assert++; \ + if(memcmp(minunit_tmp_e, minunit_tmp_r, size)) { \ + snprintf( \ + minunit_last_message, \ + MINUNIT_MESSAGE_LEN, \ + "%s failed:\r\n\t%s:%d: mem not equal\r\n\tEXP RES", \ + __func__, \ + __FILE__, \ + __LINE__); \ + for(size_t __index = 0; __index < size; __index++) { \ + if(strlen(minunit_last_message) > MINUNIT_MESSAGE_LEN - 20) break; \ + uint8_t __e = ((uint8_t*)minunit_tmp_e)[__index]; \ + uint8_t __r = ((uint8_t*)minunit_tmp_r)[__index]; \ + snprintf( \ + minunit_last_message + strlen(minunit_last_message), \ + MINUNIT_MESSAGE_LEN - strlen(minunit_last_message), \ + "\r\n\t%02X %s %02X", \ + __e, \ + ((__e == __r) ? ".." : "!="), \ + __r); \ + } \ + minunit_status = 1; \ + return; \ + } else { minunit_print_progress(); }) + #define mu_assert_null(result) \ MU__SAFE_BLOCK( \ minunit_assert++; if(result == NULL) { minunit_print_progress(); } else { \ snprintf( \ minunit_last_message, \ MINUNIT_MESSAGE_LEN, \ - "%s failed:\n\t%s:%d: Expected result was not NULL", \ + "%s failed:\r\n\t%s:%d: Expected result was not NULL", \ __func__, \ __FILE__, \ __LINE__); \ @@ -430,7 +458,7 @@ void minunit_print_fail(const char* error); snprintf( \ minunit_last_message, \ MINUNIT_MESSAGE_LEN, \ - "%s failed:\n\t%s:%d: Expected result was not NULL", \ + "%s failed:\r\n\t%s:%d: Expected result was not NULL", \ __func__, \ __FILE__, \ __LINE__); \ @@ -438,32 +466,32 @@ void minunit_print_fail(const char* error); return; \ }) -#define mu_assert_pointers_eq(pointer1, pointer2) \ - MU__SAFE_BLOCK( \ - minunit_assert++; if(pointer1 == pointer2) { minunit_print_progress(); } else { \ - snprintf( \ - minunit_last_message, \ - MINUNIT_MESSAGE_LEN, \ - "%s failed:\n\t%s:%d: Expected the pointers to point to the same memory location", \ - __func__, \ - __FILE__, \ - __LINE__); \ - minunit_status = 1; \ - return; \ +#define mu_assert_pointers_eq(pointer1, pointer2) \ + MU__SAFE_BLOCK( \ + minunit_assert++; if(pointer1 == pointer2) { minunit_print_progress(); } else { \ + snprintf( \ + minunit_last_message, \ + MINUNIT_MESSAGE_LEN, \ + "%s failed:\r\n\t%s:%d: Expected the pointers to point to the same memory location", \ + __func__, \ + __FILE__, \ + __LINE__); \ + minunit_status = 1; \ + return; \ }) -#define mu_assert_pointers_not_eq(pointer1, pointer2) \ - MU__SAFE_BLOCK( \ - minunit_assert++; if(pointer1 != pointer2) { minunit_print_progress(); } else { \ - snprintf( \ - minunit_last_message, \ - MINUNIT_MESSAGE_LEN, \ - "%s failed:\n\t%s:%d: Expected the pointers to point to the same memory location", \ - __func__, \ - __FILE__, \ - __LINE__); \ - minunit_status = 1; \ - return; \ +#define mu_assert_pointers_not_eq(pointer1, pointer2) \ + MU__SAFE_BLOCK( \ + minunit_assert++; if(pointer1 != pointer2) { minunit_print_progress(); } else { \ + snprintf( \ + minunit_last_message, \ + MINUNIT_MESSAGE_LEN, \ + "%s failed:\r\n\t%s:%d: Expected the pointers to point to the same memory location", \ + __func__, \ + __FILE__, \ + __LINE__); \ + minunit_status = 1; \ + return; \ }) /* diff --git a/applications/unit_tests/protocol_dict/protocol_dict_test.c b/applications/unit_tests/protocol_dict/protocol_dict_test.c new file mode 100644 index 00000000..73e77ec9 --- /dev/null +++ b/applications/unit_tests/protocol_dict/protocol_dict_test.c @@ -0,0 +1,222 @@ +#include +#include "../minunit.h" +#include + +typedef enum { + TestDictProtocol0, + TestDictProtocol1, + + TestDictProtocolMax, +} TestDictProtocols; + +/*********************** PROTOCOL 0 START ***********************/ + +typedef struct { + uint32_t data; + size_t encoder_counter; +} Protocol0Data; + +static const uint32_t protocol_0_decoder_result = 0xDEADBEEF; + +static void* protocol_0_alloc() { + void* data = malloc(sizeof(Protocol0Data)); + return data; +} + +static void protocol_0_free(Protocol0Data* data) { + free(data); +} + +static uint8_t* protocol_0_get_data(Protocol0Data* data) { + return (uint8_t*)&data->data; +} + +static void protocol_0_decoder_start(Protocol0Data* data) { + data->data = 0; +} + +static bool protocol_0_decoder_feed(Protocol0Data* data, bool level, uint32_t duration) { + if(level && duration == 666) { + data->data = protocol_0_decoder_result; + return true; + } else { + return false; + } +} + +static bool protocol_0_encoder_start(Protocol0Data* data) { + data->encoder_counter = 0; + return true; +} + +static LevelDuration protocol_0_encoder_yield(Protocol0Data* data) { + data->encoder_counter++; + return level_duration_make(data->encoder_counter % 2, data->data); +} + +/*********************** PROTOCOL 1 START ***********************/ + +typedef struct { + uint64_t data; + size_t encoder_counter; +} Protocol1Data; + +static const uint64_t protocol_1_decoder_result = 0x1234567890ABCDEF; + +static void* protocol_1_alloc() { + void* data = malloc(sizeof(Protocol1Data)); + return data; +} + +static void protocol_1_free(Protocol1Data* data) { + free(data); +} + +static uint8_t* protocol_1_get_data(Protocol1Data* data) { + return (uint8_t*)&data->data; +} + +static void protocol_1_decoder_start(Protocol1Data* data) { + data->data = 0; +} + +static bool protocol_1_decoder_feed(Protocol1Data* data, bool level, uint32_t duration) { + if(level && duration == 543) { + data->data = 0x1234567890ABCDEF; + return true; + } else { + return false; + } +} + +static bool protocol_1_encoder_start(Protocol1Data* data) { + data->encoder_counter = 0; + return true; +} + +static LevelDuration protocol_1_encoder_yield(Protocol1Data* data) { + data->encoder_counter++; + return level_duration_make(!(data->encoder_counter % 2), 100); +} + +/*********************** PROTOCOLS DESCRIPTION ***********************/ +static const ProtocolBase protocol_0 = { + .name = "Protocol 0", + .manufacturer = "Manufacturer 0", + .data_size = 4, + .alloc = (ProtocolAlloc)protocol_0_alloc, + .free = (ProtocolFree)protocol_0_free, + .get_data = (ProtocolGetData)protocol_0_get_data, + .decoder = + { + .start = (ProtocolDecoderStart)protocol_0_decoder_start, + .feed = (ProtocolDecoderFeed)protocol_0_decoder_feed, + }, + .encoder = + { + .start = (ProtocolEncoderStart)protocol_0_encoder_start, + .yield = (ProtocolEncoderYield)protocol_0_encoder_yield, + }, +}; + +static const ProtocolBase protocol_1 = { + .name = "Protocol 1", + .manufacturer = "Manufacturer 1", + .data_size = 8, + .alloc = (ProtocolAlloc)protocol_1_alloc, + .free = (ProtocolFree)protocol_1_free, + .get_data = (ProtocolGetData)protocol_1_get_data, + .decoder = + { + .start = (ProtocolDecoderStart)protocol_1_decoder_start, + .feed = (ProtocolDecoderFeed)protocol_1_decoder_feed, + }, + .encoder = + { + .start = (ProtocolEncoderStart)protocol_1_encoder_start, + .yield = (ProtocolEncoderYield)protocol_1_encoder_yield, + }, +}; + +static const ProtocolBase* test_protocols_base[] = { + [TestDictProtocol0] = &protocol_0, + [TestDictProtocol1] = &protocol_1, +}; + +MU_TEST(test_protocol_dict) { + ProtocolDict* dict = protocol_dict_alloc(test_protocols_base, TestDictProtocolMax); + size_t max_data_size = protocol_dict_get_max_data_size(dict); + mu_assert_int_eq(8, max_data_size); + uint8_t* data = malloc(max_data_size); + + protocol_dict_decoders_start(dict); + ProtocolId protocol_id = PROTOCOL_NO; + + for(size_t i = 0; i < 100; i++) { + protocol_id = protocol_dict_decoders_feed(dict, i % 2, 100); + mu_assert_int_eq(PROTOCOL_NO, protocol_id); + } + + // trigger protocol 1 + protocol_id = protocol_dict_decoders_feed(dict, true, 543); + mu_assert_int_eq(TestDictProtocol1, protocol_id); + + mu_assert_string_eq("Protocol 1", protocol_dict_get_name(dict, protocol_id)); + mu_assert_string_eq("Manufacturer 1", protocol_dict_get_manufacturer(dict, protocol_id)); + + size_t data_size = protocol_dict_get_data_size(dict, protocol_id); + mu_assert_int_eq(8, data_size); + + protocol_dict_get_data(dict, protocol_id, data, data_size); + mu_assert_mem_eq(&protocol_1_decoder_result, data, data_size); + + // trigger protocol 0 + protocol_id = protocol_dict_decoders_feed(dict, true, 666); + mu_assert_int_eq(TestDictProtocol0, protocol_id); + + mu_assert_string_eq("Protocol 0", protocol_dict_get_name(dict, protocol_id)); + mu_assert_string_eq("Manufacturer 0", protocol_dict_get_manufacturer(dict, protocol_id)); + + data_size = protocol_dict_get_data_size(dict, protocol_id); + mu_assert_int_eq(4, data_size); + + protocol_dict_get_data(dict, protocol_id, data, data_size); + mu_assert_mem_eq(&protocol_0_decoder_result, data, data_size); + + protocol_dict_decoders_start(dict); + + protocol_id = TestDictProtocol0; + + const uint8_t protocol_0_test_data[4] = {100, 0, 0, 0}; + protocol_dict_set_data(dict, protocol_id, protocol_0_test_data, 4); + + mu_check(protocol_dict_encoder_start(dict, protocol_id)); + + LevelDuration level; + level = protocol_dict_encoder_yield(dict, protocol_id); + mu_assert_int_eq(true, level_duration_get_level(level)); + mu_assert_int_eq(100, level_duration_get_duration(level)); + level = protocol_dict_encoder_yield(dict, protocol_id); + mu_assert_int_eq(false, level_duration_get_level(level)); + mu_assert_int_eq(100, level_duration_get_duration(level)); + level = protocol_dict_encoder_yield(dict, protocol_id); + mu_assert_int_eq(true, level_duration_get_level(level)); + mu_assert_int_eq(100, level_duration_get_duration(level)); + + mu_check(protocol_dict_encoder_start(dict, protocol_id)); + level = protocol_dict_encoder_yield(dict, protocol_id); + mu_assert_int_eq(true, level_duration_get_level(level)); + mu_assert_int_eq(100, level_duration_get_duration(level)); + + protocol_dict_free(dict); + free(data); +} + +MU_TEST_SUITE(test_protocol_dict_suite) { + MU_RUN_TEST(test_protocol_dict); +} + +int run_minunit_test_protocol_dict() { + MU_RUN_SUITE(test_protocol_dict_suite); + return MU_EXIT_CODE; +} \ No newline at end of file diff --git a/applications/unit_tests/test_index.c b/applications/unit_tests/test_index.c index e5282246..81d891b2 100644 --- a/applications/unit_tests/test_index.c +++ b/applications/unit_tests/test_index.c @@ -19,7 +19,10 @@ int run_minunit_test_stream(); int run_minunit_test_storage(); int run_minunit_test_subghz(); int run_minunit_test_dirwalk(); +int run_minunit_test_protocol_dict(); +int run_minunit_test_lfrfid_protocols(); int run_minunit_test_nfc(); +int run_minunit_test_bit_lib(); typedef int (*UnitTestEntry)(); @@ -39,6 +42,9 @@ const UnitTest unit_tests[] = { {.name = "subghz", .entry = run_minunit_test_subghz}, {.name = "infrared", .entry = run_minunit_test_infrared}, {.name = "nfc", .entry = run_minunit_test_nfc}, + {.name = "protocol_dict", .entry = run_minunit_test_protocol_dict}, + {.name = "lfrfid", .entry = run_minunit_test_lfrfid_protocols}, + {.name = "bit_lib", .entry = run_minunit_test_bit_lib}, }; void minunit_print_progress() { diff --git a/applications/unit_tests/varint/varint_test.c b/applications/unit_tests/varint/varint_test.c new file mode 100644 index 00000000..8faab136 --- /dev/null +++ b/applications/unit_tests/varint/varint_test.c @@ -0,0 +1,88 @@ +#include +#include +#include "../minunit.h" +#include +#include + +MU_TEST(test_varint_basic_u) { + mu_assert_int_eq(1, varint_uint32_length(0)); + mu_assert_int_eq(5, varint_uint32_length(UINT32_MAX)); + + uint8_t data[8] = {}; + uint32_t out_value; + + mu_assert_int_eq(1, varint_uint32_pack(0, data)); + mu_assert_int_eq(1, varint_uint32_unpack(&out_value, data, 8)); + mu_assert_int_eq(0, out_value); + + mu_assert_int_eq(5, varint_uint32_pack(UINT32_MAX, data)); + mu_assert_int_eq(5, varint_uint32_unpack(&out_value, data, 8)); + mu_assert_int_eq(UINT32_MAX, out_value); +} + +MU_TEST(test_varint_basic_i) { + mu_assert_int_eq(5, varint_int32_length(INT32_MIN / 2)); + mu_assert_int_eq(1, varint_int32_length(0)); + mu_assert_int_eq(5, varint_int32_length(INT32_MAX / 2)); + + mu_assert_int_eq(2, varint_int32_length(127)); + mu_assert_int_eq(2, varint_int32_length(-127)); + + uint8_t data[8] = {}; + int32_t out_value; + mu_assert_int_eq(1, varint_int32_pack(0, data)); + mu_assert_int_eq(1, varint_int32_unpack(&out_value, data, 8)); + mu_assert_int_eq(0, out_value); + + mu_assert_int_eq(2, varint_int32_pack(127, data)); + mu_assert_int_eq(2, varint_int32_unpack(&out_value, data, 8)); + mu_assert_int_eq(127, out_value); + + mu_assert_int_eq(2, varint_int32_pack(-127, data)); + mu_assert_int_eq(2, varint_int32_unpack(&out_value, data, 8)); + mu_assert_int_eq(-127, out_value); + + mu_assert_int_eq(5, varint_int32_pack(INT32_MAX, data)); + mu_assert_int_eq(5, varint_int32_unpack(&out_value, data, 8)); + mu_assert_int_eq(INT32_MAX, out_value); + + mu_assert_int_eq(5, varint_int32_pack(INT32_MIN / 2 + 1, data)); + mu_assert_int_eq(5, varint_int32_unpack(&out_value, data, 8)); + mu_assert_int_eq(INT32_MIN / 2 + 1, out_value); +} + +MU_TEST(test_varint_rand_u) { + uint8_t data[8] = {}; + uint32_t out_value; + + for(size_t i = 0; i < 200000; i++) { + uint32_t rand_value = rand(); + mu_assert_int_eq( + varint_uint32_pack(rand_value, data), varint_uint32_unpack(&out_value, data, 8)); + mu_assert_int_eq(rand_value, out_value); + } +} + +MU_TEST(test_varint_rand_i) { + uint8_t data[8] = {}; + int32_t out_value; + + for(size_t i = 0; i < 200000; i++) { + int32_t rand_value = rand() + (INT32_MIN / 2 + 1); + mu_assert_int_eq( + varint_int32_pack(rand_value, data), varint_int32_unpack(&out_value, data, 8)); + mu_assert_int_eq(rand_value, out_value); + } +} + +MU_TEST_SUITE(test_varint_suite) { + MU_RUN_TEST(test_varint_basic_u); + MU_RUN_TEST(test_varint_basic_i); + MU_RUN_TEST(test_varint_rand_u); + MU_RUN_TEST(test_varint_rand_i); +} + +int run_minunit_test_varint() { + MU_RUN_SUITE(test_varint_suite); + return MU_EXIT_CODE; +} \ No newline at end of file diff --git a/applications/updater/util/update_task.c b/applications/updater/util/update_task.c index 6864076d..b0477319 100644 --- a/applications/updater/util/update_task.c +++ b/applications/updater/util/update_task.c @@ -170,8 +170,7 @@ static bool update_task_check_file_exists(UpdateTask* update_task, string_t file string_t tmp_path; string_init_set(tmp_path, update_task->update_path); path_append(tmp_path, string_get_cstr(filename)); - bool exists = - (storage_common_stat(update_task->storage, string_get_cstr(tmp_path), NULL) == FSE_OK); + bool exists = storage_file_exists(update_task->storage, string_get_cstr(tmp_path)); string_clear(tmp_path); return exists; } diff --git a/firmware.scons b/firmware.scons index 74554307..f47e9ff2 100644 --- a/firmware.scons +++ b/firmware.scons @@ -200,6 +200,7 @@ fwelf = fwenv["FW_ELF"] = fwenv.Program( "misc", "mbedtls", "loclass", + "lfrfid", # 2nd round "flipperformat", "toolbox", diff --git a/firmware/targets/f7/furi_hal/furi_hal_rfid.c b/firmware/targets/f7/furi_hal/furi_hal_rfid.c index 507c53bf..0ade85e0 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_rfid.c +++ b/firmware/targets/f7/furi_hal/furi_hal_rfid.c @@ -6,6 +6,7 @@ #include #include +#include #define FURI_HAL_RFID_READ_TIMER TIM1 #define FURI_HAL_RFID_READ_TIMER_CHANNEL LL_TIM_CHANNEL_CH1N @@ -16,8 +17,14 @@ #define FURI_HAL_RFID_EMULATE_TIMER_IRQ FuriHalInterruptIdTIM2 #define FURI_HAL_RFID_EMULATE_TIMER_CHANNEL LL_TIM_CHANNEL_CH3 +#define RFID_CAPTURE_TIM TIM2 +#define RFID_CAPTURE_IND_CH LL_TIM_CHANNEL_CH3 +#define RFID_CAPTURE_DIR_CH LL_TIM_CHANNEL_CH4 + typedef struct { FuriHalRfidEmulateCallback callback; + FuriHalRfidDMACallback dma_callback; + FuriHalRfidReadCaptureCallback read_capture_callback; void* context; } FuriHalRfid; @@ -212,6 +219,185 @@ void furi_hal_rfid_tim_emulate_stop() { furi_hal_interrupt_set_isr(FURI_HAL_RFID_EMULATE_TIMER_IRQ, NULL, NULL); } +static void furi_hal_capture_dma_isr(void* context) { + UNUSED(context); + + // Channel 3, positive level + if(LL_TIM_IsActiveFlag_CC3(RFID_CAPTURE_TIM)) { + LL_TIM_ClearFlag_CC3(RFID_CAPTURE_TIM); + furi_hal_rfid->read_capture_callback( + true, LL_TIM_IC_GetCaptureCH3(RFID_CAPTURE_TIM), furi_hal_rfid->context); + } + + // Channel 4, overall level + if(LL_TIM_IsActiveFlag_CC4(RFID_CAPTURE_TIM)) { + LL_TIM_ClearFlag_CC4(RFID_CAPTURE_TIM); + LL_TIM_SetCounter(RFID_CAPTURE_TIM, 0); + furi_hal_rfid->read_capture_callback( + false, LL_TIM_IC_GetCaptureCH4(RFID_CAPTURE_TIM), furi_hal_rfid->context); + } +} + +void furi_hal_rfid_tim_read_capture_start(FuriHalRfidReadCaptureCallback callback, void* context) { + FURI_CRITICAL_ENTER(); + LL_TIM_DeInit(RFID_CAPTURE_TIM); + FURI_CRITICAL_EXIT(); + + furi_assert(furi_hal_rfid); + + furi_hal_rfid->read_capture_callback = callback; + furi_hal_rfid->context = context; + + // Timer: base + LL_TIM_InitTypeDef TIM_InitStruct = {0}; + TIM_InitStruct.Prescaler = 64 - 1; + TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP; + TIM_InitStruct.Autoreload = UINT32_MAX; + TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1; + LL_TIM_Init(RFID_CAPTURE_TIM, &TIM_InitStruct); + + // Timer: advanced + LL_TIM_SetClockSource(RFID_CAPTURE_TIM, LL_TIM_CLOCKSOURCE_INTERNAL); + LL_TIM_DisableARRPreload(RFID_CAPTURE_TIM); + LL_TIM_SetTriggerInput(RFID_CAPTURE_TIM, LL_TIM_TS_TI2FP2); + LL_TIM_SetSlaveMode(RFID_CAPTURE_TIM, LL_TIM_SLAVEMODE_DISABLED); + LL_TIM_SetTriggerOutput(RFID_CAPTURE_TIM, LL_TIM_TRGO_RESET); + LL_TIM_EnableMasterSlaveMode(RFID_CAPTURE_TIM); + LL_TIM_DisableDMAReq_TRIG(RFID_CAPTURE_TIM); + LL_TIM_DisableIT_TRIG(RFID_CAPTURE_TIM); + LL_TIM_SetRemap(RFID_CAPTURE_TIM, LL_TIM_TIM2_TI4_RMP_COMP1); + + // Timer: channel 3 indirect + LL_TIM_IC_SetActiveInput(RFID_CAPTURE_TIM, RFID_CAPTURE_IND_CH, LL_TIM_ACTIVEINPUT_INDIRECTTI); + LL_TIM_IC_SetPrescaler(RFID_CAPTURE_TIM, RFID_CAPTURE_IND_CH, LL_TIM_ICPSC_DIV1); + LL_TIM_IC_SetPolarity(RFID_CAPTURE_TIM, RFID_CAPTURE_IND_CH, LL_TIM_IC_POLARITY_FALLING); + LL_TIM_IC_SetFilter(RFID_CAPTURE_TIM, RFID_CAPTURE_IND_CH, LL_TIM_IC_FILTER_FDIV1); + + // Timer: channel 4 direct + LL_TIM_IC_SetActiveInput(RFID_CAPTURE_TIM, RFID_CAPTURE_DIR_CH, LL_TIM_ACTIVEINPUT_DIRECTTI); + LL_TIM_IC_SetPrescaler(RFID_CAPTURE_TIM, RFID_CAPTURE_DIR_CH, LL_TIM_ICPSC_DIV1); + LL_TIM_IC_SetPolarity(RFID_CAPTURE_TIM, RFID_CAPTURE_DIR_CH, LL_TIM_IC_POLARITY_RISING); + LL_TIM_IC_SetFilter(RFID_CAPTURE_TIM, RFID_CAPTURE_DIR_CH, LL_TIM_IC_FILTER_FDIV1); + + furi_hal_interrupt_set_isr(FURI_HAL_RFID_EMULATE_TIMER_IRQ, furi_hal_capture_dma_isr, NULL); + + LL_TIM_EnableIT_CC3(RFID_CAPTURE_TIM); + LL_TIM_EnableIT_CC4(RFID_CAPTURE_TIM); + LL_TIM_CC_EnableChannel(RFID_CAPTURE_TIM, RFID_CAPTURE_IND_CH); + LL_TIM_CC_EnableChannel(RFID_CAPTURE_TIM, RFID_CAPTURE_DIR_CH); + LL_TIM_SetCounter(RFID_CAPTURE_TIM, 0); + LL_TIM_EnableCounter(RFID_CAPTURE_TIM); + + furi_hal_rfid_comp_start(); +} + +void furi_hal_rfid_tim_read_capture_stop() { + furi_hal_rfid_comp_stop(); + + furi_hal_interrupt_set_isr(FURI_HAL_RFID_EMULATE_TIMER_IRQ, NULL, NULL); + + FURI_CRITICAL_ENTER(); + LL_TIM_DeInit(RFID_CAPTURE_TIM); + FURI_CRITICAL_EXIT(); +} + +static void furi_hal_rfid_dma_isr() { + if(LL_DMA_IsActiveFlag_HT1(DMA1)) { + LL_DMA_ClearFlag_HT1(DMA1); + furi_hal_rfid->dma_callback(true, furi_hal_rfid->context); + } + + if(LL_DMA_IsActiveFlag_TC1(DMA1)) { + LL_DMA_ClearFlag_TC1(DMA1); + furi_hal_rfid->dma_callback(false, furi_hal_rfid->context); + } +} + +void furi_hal_rfid_tim_emulate_dma_start( + uint32_t* duration, + uint32_t* pulse, + size_t length, + FuriHalRfidDMACallback callback, + void* context) { + furi_assert(furi_hal_rfid); + + // setup interrupts + furi_hal_rfid->dma_callback = callback; + furi_hal_rfid->context = context; + + // setup pins + furi_hal_rfid_pins_emulate(); + + // configure timer + furi_hal_rfid_tim_emulate(125000); + LL_TIM_OC_SetPolarity( + FURI_HAL_RFID_EMULATE_TIMER, FURI_HAL_RFID_EMULATE_TIMER_CHANNEL, LL_TIM_OCPOLARITY_HIGH); + LL_TIM_EnableDMAReq_UPDATE(FURI_HAL_RFID_EMULATE_TIMER); + + // configure DMA "mem -> ARR" channel + LL_DMA_InitTypeDef dma_config = {0}; + dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (FURI_HAL_RFID_EMULATE_TIMER->ARR); + dma_config.MemoryOrM2MDstAddress = (uint32_t)duration; + dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; + dma_config.Mode = LL_DMA_MODE_CIRCULAR; + dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; + dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; + dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; + dma_config.NbData = length; + dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; + dma_config.Priority = LL_DMA_MODE_NORMAL; + LL_DMA_Init(DMA1, LL_DMA_CHANNEL_1, &dma_config); + LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1); + + // configure DMA "mem -> CCR3" channel +#if FURI_HAL_RFID_EMULATE_TIMER_CHANNEL == LL_TIM_CHANNEL_CH3 + dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (FURI_HAL_RFID_EMULATE_TIMER->CCR3); +#else +#error Update this code. Would you kindly? +#endif + dma_config.MemoryOrM2MDstAddress = (uint32_t)pulse; + dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; + dma_config.Mode = LL_DMA_MODE_CIRCULAR; + dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; + dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; + dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; + dma_config.NbData = length; + dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; + dma_config.Priority = LL_DMA_MODE_NORMAL; + LL_DMA_Init(DMA1, LL_DMA_CHANNEL_2, &dma_config); + LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2); + + // attach interrupt to one of DMA channels + furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch1, furi_hal_rfid_dma_isr, NULL); + LL_DMA_EnableIT_TC(DMA1, LL_DMA_CHANNEL_1); + LL_DMA_EnableIT_HT(DMA1, LL_DMA_CHANNEL_1); + + // start + LL_TIM_EnableAllOutputs(FURI_HAL_RFID_EMULATE_TIMER); + + LL_TIM_SetCounter(FURI_HAL_RFID_EMULATE_TIMER, 0); + LL_TIM_EnableCounter(FURI_HAL_RFID_EMULATE_TIMER); +} + +void furi_hal_rfid_tim_emulate_dma_stop() { + LL_TIM_DisableCounter(FURI_HAL_RFID_EMULATE_TIMER); + LL_TIM_DisableAllOutputs(FURI_HAL_RFID_EMULATE_TIMER); + + furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch1, NULL, NULL); + LL_DMA_DisableIT_TC(DMA1, LL_DMA_CHANNEL_1); + LL_DMA_DisableIT_HT(DMA1, LL_DMA_CHANNEL_1); + + FURI_CRITICAL_ENTER(); + + LL_DMA_DeInit(DMA1, LL_DMA_CHANNEL_1); + LL_DMA_DeInit(DMA1, LL_DMA_CHANNEL_2); + LL_TIM_DeInit(FURI_HAL_RFID_EMULATE_TIMER); + + FURI_CRITICAL_EXIT(); +} + void furi_hal_rfid_tim_reset() { FURI_CRITICAL_ENTER(); diff --git a/firmware/targets/furi_hal_include/furi_hal_rfid.h b/firmware/targets/furi_hal_include/furi_hal_rfid.h index d26ba53f..36563c1d 100644 --- a/firmware/targets/furi_hal_include/furi_hal_rfid.h +++ b/firmware/targets/furi_hal_include/furi_hal_rfid.h @@ -7,6 +7,7 @@ #include #include +#include #ifdef __cplusplus extern "C" { @@ -63,6 +64,23 @@ typedef void (*FuriHalRfidEmulateCallback)(void* context); */ void furi_hal_rfid_tim_emulate_start(FuriHalRfidEmulateCallback callback, void* context); +typedef void (*FuriHalRfidReadCaptureCallback)(bool level, uint32_t duration, void* context); + +void furi_hal_rfid_tim_read_capture_start(FuriHalRfidReadCaptureCallback callback, void* context); + +void furi_hal_rfid_tim_read_capture_stop(); + +typedef void (*FuriHalRfidDMACallback)(bool half, void* context); + +void furi_hal_rfid_tim_emulate_dma_start( + uint32_t* duration, + uint32_t* pulse, + size_t length, + FuriHalRfidDMACallback callback, + void* context); + +void furi_hal_rfid_tim_emulate_dma_stop(); + /** Stop emulation timer */ void furi_hal_rfid_tim_emulate_stop(); diff --git a/furi/core/core_defines.h b/furi/core/core_defines.h index 9ed05b21..801810f6 100644 --- a/furi/core/core_defines.h +++ b/furi/core/core_defines.h @@ -92,6 +92,8 @@ extern "C" { #define FURI_BIT_CLEAR(x, n) ((x) &= ~(1 << (n))) #endif +#define FURI_SW_MEMBARRIER() asm volatile("" : : : "memory") + #ifdef __cplusplus } #endif diff --git a/lib/SConscript b/lib/SConscript index 5e5bb2ea..4498c4c9 100644 --- a/lib/SConscript +++ b/lib/SConscript @@ -77,6 +77,7 @@ libs = env.BuildModules( "appframe", "misc", "loclass", + "lfrfid", ], ) diff --git a/lib/flipper_format/flipper_format.h b/lib/flipper_format/flipper_format.h index 6163dee0..9e82bb31 100644 --- a/lib/flipper_format/flipper_format.h +++ b/lib/flipper_format/flipper_format.h @@ -41,7 +41,7 @@ * Writing: * * ~~~~~~~~~~~~~~~~~~~~~ - * FlipperFormat format = flipper_format_file_alloc(storage); + * FlipperFormat* format = flipper_format_file_alloc(storage); * * do { * const uint32_t version = 1; @@ -66,7 +66,7 @@ * Reading: * * ~~~~~~~~~~~~~~~~~~~~~ - * FlipperFormat file = flipper_format_file_alloc(storage); + * FlipperFormat* file = flipper_format_file_alloc(storage); * * do { * uint32_t version = 1; diff --git a/lib/lfrfid/SConscript b/lib/lfrfid/SConscript new file mode 100644 index 00000000..8d354288 --- /dev/null +++ b/lib/lfrfid/SConscript @@ -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") diff --git a/lib/lfrfid/lfrfid_dict_file.c b/lib/lfrfid/lfrfid_dict_file.c new file mode 100644 index 00000000..bb6af39a --- /dev/null +++ b/lib/lfrfid/lfrfid_dict_file.c @@ -0,0 +1,182 @@ +#include "lfrfid_dict_file.h" +#include +#include +#include + +#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; +} \ No newline at end of file diff --git a/lib/lfrfid/lfrfid_dict_file.h b/lib/lfrfid/lfrfid_dict_file.h new file mode 100644 index 00000000..077bb0ba --- /dev/null +++ b/lib/lfrfid/lfrfid_dict_file.h @@ -0,0 +1,31 @@ +#pragma once +#include +#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 \ No newline at end of file diff --git a/lib/lfrfid/lfrfid_raw_file.c b/lib/lfrfid/lfrfid_raw_file.c new file mode 100644 index 00000000..27c6f247 --- /dev/null +++ b/lib/lfrfid/lfrfid_raw_file.c @@ -0,0 +1,145 @@ +#include "lfrfid_raw_file.h" +#include "tools/varint_pair.h" +#include +#include + +#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; +} \ No newline at end of file diff --git a/lib/lfrfid/lfrfid_raw_file.h b/lib/lfrfid/lfrfid_raw_file.h new file mode 100644 index 00000000..3f2f14e0 --- /dev/null +++ b/lib/lfrfid/lfrfid_raw_file.h @@ -0,0 +1,95 @@ +#pragma once +#include +#include + +#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 diff --git a/lib/lfrfid/lfrfid_raw_worker.c b/lib/lfrfid/lfrfid_raw_worker.c new file mode 100644 index 00000000..4050a8ca --- /dev/null +++ b/lib/lfrfid/lfrfid_raw_worker.c @@ -0,0 +1,357 @@ +#include +#include +#include +#include +#include +#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; +} \ No newline at end of file diff --git a/lib/lfrfid/lfrfid_raw_worker.h b/lib/lfrfid/lfrfid_raw_worker.h new file mode 100644 index 00000000..1195dd58 --- /dev/null +++ b/lib/lfrfid/lfrfid_raw_worker.h @@ -0,0 +1,68 @@ +#pragma once +#include +#include "lfrfid_worker.h" +#include +#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 \ No newline at end of file diff --git a/lib/lfrfid/lfrfid_worker.c b/lib/lfrfid/lfrfid_worker.c new file mode 100644 index 00000000..8b4f8b6a --- /dev/null +++ b/lib/lfrfid/lfrfid_worker.c @@ -0,0 +1,169 @@ +#include +#include +#include +#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; +} \ No newline at end of file diff --git a/lib/lfrfid/lfrfid_worker.h b/lib/lfrfid/lfrfid_worker.h new file mode 100644 index 00000000..def9f89a --- /dev/null +++ b/lib/lfrfid/lfrfid_worker.h @@ -0,0 +1,152 @@ +/** + * @file lfrfid_worker.h + * + * LFRFID worker + */ + +#pragma once +#include +#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 diff --git a/lib/lfrfid/lfrfid_worker_i.h b/lib/lfrfid/lfrfid_worker_i.h new file mode 100644 index 00000000..33c0bff0 --- /dev/null +++ b/lib/lfrfid/lfrfid_worker_i.h @@ -0,0 +1,64 @@ +/** + * @file lfrfid_worker_i.h + * + * lfrfid worker, internal definitions + */ + +#pragma once +#include +#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 diff --git a/lib/lfrfid/lfrfid_worker_modes.c b/lib/lfrfid/lfrfid_worker_modes.c new file mode 100644 index 00000000..f41a7194 --- /dev/null +++ b/lib/lfrfid/lfrfid_worker_modes.c @@ -0,0 +1,624 @@ +#include +#include +#include "lfrfid_worker_i.h" +#include "tools/t5577.h" +#include +#include +#include +#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}, +}; \ No newline at end of file diff --git a/lib/lfrfid/protocols/lfrfid_protocols.c b/lib/lfrfid/protocols/lfrfid_protocols.c new file mode 100644 index 00000000..5df01b19 --- /dev/null +++ b/lib/lfrfid/protocols/lfrfid_protocols.c @@ -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, +}; \ No newline at end of file diff --git a/lib/lfrfid/protocols/lfrfid_protocols.h b/lib/lfrfid/protocols/lfrfid_protocols.h new file mode 100644 index 00000000..4b8f6573 --- /dev/null +++ b/lib/lfrfid/protocols/lfrfid_protocols.h @@ -0,0 +1,35 @@ +#pragma once +#include +#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; \ No newline at end of file diff --git a/lib/lfrfid/protocols/protocol_awid.c b/lib/lfrfid/protocols/protocol_awid.c new file mode 100644 index 00000000..243b5ede --- /dev/null +++ b/lib/lfrfid/protocols/protocol_awid.c @@ -0,0 +1,239 @@ +#include +#include +#include +#include +#include +#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, +}; \ No newline at end of file diff --git a/lib/lfrfid/protocols/protocol_awid.h b/lib/lfrfid/protocols/protocol_awid.h new file mode 100644 index 00000000..51a4ea52 --- /dev/null +++ b/lib/lfrfid/protocols/protocol_awid.h @@ -0,0 +1,4 @@ +#pragma once +#include + +extern const ProtocolBase protocol_awid; \ No newline at end of file diff --git a/lib/lfrfid/protocols/protocol_em4100.c b/lib/lfrfid/protocols/protocol_em4100.c new file mode 100644 index 00000000..92721fcd --- /dev/null +++ b/lib/lfrfid/protocols/protocol_em4100.c @@ -0,0 +1,292 @@ +#include +#include +#include +#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, +}; \ No newline at end of file diff --git a/lib/lfrfid/protocols/protocol_em4100.h b/lib/lfrfid/protocols/protocol_em4100.h new file mode 100644 index 00000000..6e1e25b9 --- /dev/null +++ b/lib/lfrfid/protocols/protocol_em4100.h @@ -0,0 +1,4 @@ +#pragma once +#include + +extern const ProtocolBase protocol_em4100; \ No newline at end of file diff --git a/lib/lfrfid/protocols/protocol_fdx_a.c b/lib/lfrfid/protocols/protocol_fdx_a.c new file mode 100644 index 00000000..23f9e285 --- /dev/null +++ b/lib/lfrfid/protocols/protocol_fdx_a.c @@ -0,0 +1,239 @@ +#include +#include +#include +#include +#include "lfrfid_protocols.h" +#include + +#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, +}; \ No newline at end of file diff --git a/lib/lfrfid/protocols/protocol_fdx_a.h b/lib/lfrfid/protocols/protocol_fdx_a.h new file mode 100644 index 00000000..35554488 --- /dev/null +++ b/lib/lfrfid/protocols/protocol_fdx_a.h @@ -0,0 +1,4 @@ +#pragma once +#include + +extern const ProtocolBase protocol_fdx_a; \ No newline at end of file diff --git a/lib/lfrfid/protocols/protocol_fdx_b.c b/lib/lfrfid/protocols/protocol_fdx_b.c new file mode 100644 index 00000000..f68a884e --- /dev/null +++ b/lib/lfrfid/protocols/protocol_fdx_b.c @@ -0,0 +1,374 @@ +#include +#include "toolbox/level_duration.h" +#include "protocol_fdx_b.h" +#include +#include +#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, +}; \ No newline at end of file diff --git a/lib/lfrfid/protocols/protocol_fdx_b.h b/lib/lfrfid/protocols/protocol_fdx_b.h new file mode 100644 index 00000000..549c862e --- /dev/null +++ b/lib/lfrfid/protocols/protocol_fdx_b.h @@ -0,0 +1,4 @@ +#pragma once +#include + +extern const ProtocolBase protocol_fdx_b; \ No newline at end of file diff --git a/lib/lfrfid/protocols/protocol_h10301.c b/lib/lfrfid/protocols/protocol_h10301.c new file mode 100644 index 00000000..f30f75fa --- /dev/null +++ b/lib/lfrfid/protocols/protocol_h10301.c @@ -0,0 +1,386 @@ +#include +#include +#include +#include +#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, +}; \ No newline at end of file diff --git a/lib/lfrfid/protocols/protocol_h10301.h b/lib/lfrfid/protocols/protocol_h10301.h new file mode 100644 index 00000000..b7ee5ad5 --- /dev/null +++ b/lib/lfrfid/protocols/protocol_h10301.h @@ -0,0 +1,4 @@ +#pragma once +#include + +extern const ProtocolBase protocol_h10301; \ No newline at end of file diff --git a/lib/lfrfid/protocols/protocol_hid_ex_generic.c b/lib/lfrfid/protocols/protocol_hid_ex_generic.c new file mode 100644 index 00000000..e0a85266 --- /dev/null +++ b/lib/lfrfid/protocols/protocol_hid_ex_generic.c @@ -0,0 +1,219 @@ +#include +#include +#include +#include +#include "lfrfid_protocols.h" +#include + +#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, +}; \ No newline at end of file diff --git a/lib/lfrfid/protocols/protocol_hid_ex_generic.h b/lib/lfrfid/protocols/protocol_hid_ex_generic.h new file mode 100644 index 00000000..9c4ddfff --- /dev/null +++ b/lib/lfrfid/protocols/protocol_hid_ex_generic.h @@ -0,0 +1,4 @@ +#pragma once +#include + +extern const ProtocolBase protocol_hid_ex_generic; \ No newline at end of file diff --git a/lib/lfrfid/protocols/protocol_hid_generic.c b/lib/lfrfid/protocols/protocol_hid_generic.c new file mode 100644 index 00000000..2516d681 --- /dev/null +++ b/lib/lfrfid/protocols/protocol_hid_generic.c @@ -0,0 +1,280 @@ +#include +#include +#include +#include +#include "lfrfid_protocols.h" +#include + +#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, +}; diff --git a/lib/lfrfid/protocols/protocol_hid_generic.h b/lib/lfrfid/protocols/protocol_hid_generic.h new file mode 100644 index 00000000..22e78a4d --- /dev/null +++ b/lib/lfrfid/protocols/protocol_hid_generic.h @@ -0,0 +1,4 @@ +#pragma once +#include + +extern const ProtocolBase protocol_hid_generic; \ No newline at end of file diff --git a/lib/lfrfid/protocols/protocol_indala26.c b/lib/lfrfid/protocols/protocol_indala26.c new file mode 100644 index 00000000..136ececf --- /dev/null +++ b/lib/lfrfid/protocols/protocol_indala26.c @@ -0,0 +1,353 @@ +#include +#include +#include +#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, +}; \ No newline at end of file diff --git a/lib/lfrfid/protocols/protocol_indala26.h b/lib/lfrfid/protocols/protocol_indala26.h new file mode 100644 index 00000000..c0c61784 --- /dev/null +++ b/lib/lfrfid/protocols/protocol_indala26.h @@ -0,0 +1,4 @@ +#pragma once +#include + +extern const ProtocolBase protocol_indala26; diff --git a/lib/lfrfid/protocols/protocol_io_prox_xsf.c b/lib/lfrfid/protocols/protocol_io_prox_xsf.c new file mode 100644 index 00000000..66b1610b --- /dev/null +++ b/lib/lfrfid/protocols/protocol_io_prox_xsf.c @@ -0,0 +1,297 @@ +#include +#include +#include +#include +#include +#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, +}; \ No newline at end of file diff --git a/lib/lfrfid/protocols/protocol_io_prox_xsf.h b/lib/lfrfid/protocols/protocol_io_prox_xsf.h new file mode 100644 index 00000000..af94fb46 --- /dev/null +++ b/lib/lfrfid/protocols/protocol_io_prox_xsf.h @@ -0,0 +1,4 @@ +#pragma once +#include + +extern const ProtocolBase protocol_io_prox_xsf; diff --git a/lib/lfrfid/tools/bit_lib.c b/lib/lfrfid/tools/bit_lib.c new file mode 100644 index 00000000..b57bda8a --- /dev/null +++ b/lib/lfrfid/tools/bit_lib.c @@ -0,0 +1,291 @@ +#include "bit_lib.h" +#include +#include + +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; +} \ No newline at end of file diff --git a/lib/lfrfid/tools/bit_lib.h b/lib/lfrfid/tools/bit_lib.h new file mode 100644 index 00000000..24aae0e9 --- /dev/null +++ b/lib/lfrfid/tools/bit_lib.h @@ -0,0 +1,220 @@ +#pragma once +#include +#include +#include + +#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 \ No newline at end of file diff --git a/lib/lfrfid/tools/fsk_demod.c b/lib/lfrfid/tools/fsk_demod.c new file mode 100644 index 00000000..a5155d88 --- /dev/null +++ b/lib/lfrfid/tools/fsk_demod.c @@ -0,0 +1,93 @@ +#include +#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; + } + } +} diff --git a/lib/lfrfid/tools/fsk_demod.h b/lib/lfrfid/tools/fsk_demod.h new file mode 100644 index 00000000..d816b0da --- /dev/null +++ b/lib/lfrfid/tools/fsk_demod.h @@ -0,0 +1,44 @@ +#pragma once +#include +#include + +#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 diff --git a/lib/lfrfid/tools/fsk_ocs.c b/lib/lfrfid/tools/fsk_ocs.c new file mode 100644 index 00000000..1fd46cf1 --- /dev/null +++ b/lib/lfrfid/tools/fsk_ocs.c @@ -0,0 +1,62 @@ +#include "fsk_osc.h" +#include + +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; +} \ No newline at end of file diff --git a/lib/lfrfid/tools/fsk_osc.h b/lib/lfrfid/tools/fsk_osc.h new file mode 100644 index 00000000..ed7d436c --- /dev/null +++ b/lib/lfrfid/tools/fsk_osc.h @@ -0,0 +1,60 @@ +#pragma once +#include +#include + +#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 \ No newline at end of file diff --git a/lib/lfrfid/tools/t5577.c b/lib/lfrfid/tools/t5577.c new file mode 100644 index 00000000..3444afea --- /dev/null +++ b/lib/lfrfid/tools/t5577.c @@ -0,0 +1,94 @@ +#include "t5577.h" +#include +#include + +#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(); +} \ No newline at end of file diff --git a/lib/lfrfid/tools/t5577.h b/lib/lfrfid/tools/t5577.h new file mode 100644 index 00000000..6d53b5dc --- /dev/null +++ b/lib/lfrfid/tools/t5577.h @@ -0,0 +1,56 @@ +#pragma once +#include +#include + +#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 \ No newline at end of file diff --git a/lib/lfrfid/tools/varint_pair.c b/lib/lfrfid/tools/varint_pair.c new file mode 100644 index 00000000..c59ba55b --- /dev/null +++ b/lib/lfrfid/tools/varint_pair.c @@ -0,0 +1,77 @@ +#include "varint_pair.h" +#include + +#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; +} diff --git a/lib/lfrfid/tools/varint_pair.h b/lib/lfrfid/tools/varint_pair.h new file mode 100644 index 00000000..3c838642 --- /dev/null +++ b/lib/lfrfid/tools/varint_pair.h @@ -0,0 +1,79 @@ +#pragma once +#include +#include +#include + +#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 \ No newline at end of file diff --git a/lib/one_wire/ibutton/encoder/encoder_cyfral.c b/lib/one_wire/ibutton/encoder/encoder_cyfral.c deleted file mode 100644 index 0b506b2b..00000000 --- a/lib/one_wire/ibutton/encoder/encoder_cyfral.c +++ /dev/null @@ -1,126 +0,0 @@ -#include "encoder_cyfral.h" -#include - -#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; - } -} diff --git a/lib/one_wire/ibutton/encoder/encoder_cyfral.h b/lib/one_wire/ibutton/encoder/encoder_cyfral.h deleted file mode 100644 index 4fc7be5e..00000000 --- a/lib/one_wire/ibutton/encoder/encoder_cyfral.h +++ /dev/null @@ -1,54 +0,0 @@ -/** - * @file encoder_cyfral.h - * - * Cyfral pulse format encoder - */ - -#pragma once -#include -#include -#include - -#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 diff --git a/lib/one_wire/ibutton/encoder/encoder_metakom.c b/lib/one_wire/ibutton/encoder/encoder_metakom.c deleted file mode 100644 index 5b978ebe..00000000 --- a/lib/one_wire/ibutton/encoder/encoder_metakom.c +++ /dev/null @@ -1,93 +0,0 @@ -#include "encoder_metakom.h" -#include - -#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; - } -} diff --git a/lib/one_wire/ibutton/encoder/encoder_metakom.h b/lib/one_wire/ibutton/encoder/encoder_metakom.h deleted file mode 100644 index 50ff11a2..00000000 --- a/lib/one_wire/ibutton/encoder/encoder_metakom.h +++ /dev/null @@ -1,54 +0,0 @@ -/** - * @file encoder_metakom.h - * - * Metakom pulse format encoder - */ - -#pragma once -#include -#include -#include - -#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 diff --git a/lib/one_wire/ibutton/ibutton_key.h b/lib/one_wire/ibutton/ibutton_key.h index f66537d7..d682555a 100644 --- a/lib/one_wire/ibutton/ibutton_key.h +++ b/lib/one_wire/ibutton/ibutton_key.h @@ -5,6 +5,7 @@ */ #pragma once +#include #ifdef __cplusplus extern "C" { diff --git a/lib/one_wire/ibutton/ibutton_worker.c b/lib/one_wire/ibutton/ibutton_worker.c index 755ed32f..26982bcb 100644 --- a/lib/one_wire/ibutton/ibutton_worker.c +++ b/lib/one_wire/ibutton/ibutton_worker.c @@ -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); diff --git a/lib/one_wire/ibutton/ibutton_worker_i.h b/lib/one_wire/ibutton/ibutton_worker_i.h index 28588443..2396fbd6 100644 --- a/lib/one_wire/ibutton/ibutton_worker_i.h +++ b/lib/one_wire/ibutton/ibutton_worker_i.h @@ -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 +#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[]; diff --git a/lib/one_wire/ibutton/ibutton_worker_modes.c b/lib/one_wire/ibutton/ibutton_worker_modes.c index 78e05d0e..d585e27f 100644 --- a/lib/one_wire/ibutton/ibutton_worker_modes.c +++ b/lib/one_wire/ibutton/ibutton_worker_modes.c @@ -2,6 +2,7 @@ #include #include "ibutton_worker_i.h" #include "ibutton_key_command.h" +#include 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); } diff --git a/lib/one_wire/ibutton/protocols/ibutton_protocols.c b/lib/one_wire/ibutton/protocols/ibutton_protocols.c new file mode 100644 index 00000000..b07d68b3 --- /dev/null +++ b/lib/one_wire/ibutton/protocols/ibutton_protocols.c @@ -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, +}; \ No newline at end of file diff --git a/lib/one_wire/ibutton/protocols/ibutton_protocols.h b/lib/one_wire/ibutton/protocols/ibutton_protocols.h new file mode 100644 index 00000000..cdaa3ab6 --- /dev/null +++ b/lib/one_wire/ibutton/protocols/ibutton_protocols.h @@ -0,0 +1,11 @@ +#pragma once +#include "toolbox/protocols/protocol.h" + +typedef enum { + iButtonProtocolCyfral, + iButtonProtocolMetakom, + + iButtonProtocolMax, +} iButtonProtocol; + +extern const ProtocolBase* ibutton_protocols[]; \ No newline at end of file diff --git a/lib/one_wire/ibutton/protocols/protocol_cyfral.c b/lib/one_wire/ibutton/protocols/protocol_cyfral.c new file mode 100644 index 00000000..51c42824 --- /dev/null +++ b/lib/one_wire/ibutton/protocols/protocol_cyfral.c @@ -0,0 +1,344 @@ +#include +#include +#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, + }, +}; \ No newline at end of file diff --git a/lib/one_wire/ibutton/protocols/protocol_cyfral.h b/lib/one_wire/ibutton/protocols/protocol_cyfral.h new file mode 100644 index 00000000..97a98e48 --- /dev/null +++ b/lib/one_wire/ibutton/protocols/protocol_cyfral.h @@ -0,0 +1,4 @@ +#pragma once +#include "toolbox/protocols/protocol.h" + +extern const ProtocolBase protocol_cyfral; \ No newline at end of file diff --git a/lib/one_wire/ibutton/pulse_protocols/protocol_metakom.c b/lib/one_wire/ibutton/protocols/protocol_metakom.c similarity index 51% rename from lib/one_wire/ibutton/pulse_protocols/protocol_metakom.c rename to lib/one_wire/ibutton/protocols/protocol_metakom.c index 9df9e20b..00f16e45 100644 --- a/lib/one_wire/ibutton/pulse_protocols/protocol_metakom.c +++ b/lib/one_wire/ibutton/protocols/protocol_metakom.c @@ -1,9 +1,14 @@ +#include +#include #include "protocol_metakom.h" -#include -#include -#include -#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, + }, +}; \ No newline at end of file diff --git a/lib/one_wire/ibutton/protocols/protocol_metakom.h b/lib/one_wire/ibutton/protocols/protocol_metakom.h new file mode 100644 index 00000000..5e44a2a8 --- /dev/null +++ b/lib/one_wire/ibutton/protocols/protocol_metakom.h @@ -0,0 +1,4 @@ +#pragma once +#include "toolbox/protocols/protocol.h" + +extern const ProtocolBase protocol_metakom; \ No newline at end of file diff --git a/lib/one_wire/ibutton/pulse_protocols/protocol_cyfral.c b/lib/one_wire/ibutton/pulse_protocols/protocol_cyfral.c deleted file mode 100644 index 7c289790..00000000 --- a/lib/one_wire/ibutton/pulse_protocols/protocol_cyfral.c +++ /dev/null @@ -1,256 +0,0 @@ -#include "protocol_cyfral.h" -#include -#include -#include -#include - -#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; - } -} diff --git a/lib/one_wire/ibutton/pulse_protocols/protocol_cyfral.h b/lib/one_wire/ibutton/pulse_protocols/protocol_cyfral.h deleted file mode 100644 index 10305da1..00000000 --- a/lib/one_wire/ibutton/pulse_protocols/protocol_cyfral.h +++ /dev/null @@ -1,38 +0,0 @@ -/** - * @file protocol_cyfral.h - * - * Cyfral pulse format decoder - */ - -#pragma once -#include -#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 diff --git a/lib/one_wire/ibutton/pulse_protocols/protocol_metakom.h b/lib/one_wire/ibutton/pulse_protocols/protocol_metakom.h deleted file mode 100644 index fdc45769..00000000 --- a/lib/one_wire/ibutton/pulse_protocols/protocol_metakom.h +++ /dev/null @@ -1,38 +0,0 @@ -/** - * @file protocol_metakom.h - * - * Metakom pulse format decoder - */ - -#pragma once -#include -#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 diff --git a/lib/one_wire/pulse_protocols/pulse_decoder.c b/lib/one_wire/pulse_protocols/pulse_decoder.c deleted file mode 100644 index c7d3b09e..00000000 --- a/lib/one_wire/pulse_protocols/pulse_decoder.c +++ /dev/null @@ -1,66 +0,0 @@ -#include -#include "pulse_decoder.h" -#include -#include - -#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); -} diff --git a/lib/one_wire/pulse_protocols/pulse_decoder.h b/lib/one_wire/pulse_protocols/pulse_decoder.h deleted file mode 100644 index dbaef52b..00000000 --- a/lib/one_wire/pulse_protocols/pulse_decoder.h +++ /dev/null @@ -1,70 +0,0 @@ -/** - * @file pulse_decoder.h - * - * Generic pulse protocol decoder library - */ - -#pragma once -#include -#include -#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 diff --git a/lib/one_wire/pulse_protocols/pulse_protocol.c b/lib/one_wire/pulse_protocols/pulse_protocol.c deleted file mode 100644 index 76feba11..00000000 --- a/lib/one_wire/pulse_protocols/pulse_protocol.c +++ /dev/null @@ -1,67 +0,0 @@ -#include "pulse_protocol.h" -#include -#include - -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); - } -} diff --git a/lib/one_wire/pulse_protocols/pulse_protocol.h b/lib/one_wire/pulse_protocols/pulse_protocol.h deleted file mode 100644 index bfce0e76..00000000 --- a/lib/one_wire/pulse_protocols/pulse_protocol.h +++ /dev/null @@ -1,122 +0,0 @@ -/** - * @file pulse_protocol.h - * - * Generic pulse protocol decoder library, protocol interface - */ - -#pragma once -#include -#include -#include - -#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 diff --git a/lib/subghz/protocols/keeloq_common.c b/lib/subghz/protocols/keeloq_common.c index 7e864a32..0f8c763d 100644 --- a/lib/subghz/protocols/keeloq_common.c +++ b/lib/subghz/protocols/keeloq_common.c @@ -5,6 +5,10 @@ #include #include +#define bit(x, n) (((x) >> (n)) & 1) +#define g5(x, a, b, c, d, e) \ + (bit(x, a) + bit(x, b) * 2 + bit(x, c) * 4 + bit(x, d) * 8 + bit(x, e) * 16) + /** Simple Learning Encrypt * @param data - 0xBSSSCCCC, B(4bit) key, S(10bit) serial&0x3FF, C(16bit) counter * @param key - manufacture (64bit) diff --git a/lib/subghz/protocols/keeloq_common.h b/lib/subghz/protocols/keeloq_common.h index 50d447ed..aa07a7f5 100644 --- a/lib/subghz/protocols/keeloq_common.h +++ b/lib/subghz/protocols/keeloq_common.h @@ -11,9 +11,6 @@ * */ #define KEELOQ_NLF 0x3A5C742E -#define bit(x, n) (((x) >> (n)) & 1) -#define g5(x, a, b, c, d, e) \ - (bit(x, a) + bit(x, b) * 2 + bit(x, c) * 4 + bit(x, d) * 8 + bit(x, e) * 16) /* * KeeLoq learning types diff --git a/lib/toolbox/buffer_stream.c b/lib/toolbox/buffer_stream.c new file mode 100644 index 00000000..66d21096 --- /dev/null +++ b/lib/toolbox/buffer_stream.c @@ -0,0 +1,145 @@ +#include "buffer_stream.h" +#include + +struct Buffer { + volatile bool occupied; + volatile size_t size; + uint8_t* data; + size_t max_data_size; +}; + +struct BufferStream { + size_t stream_overrun_count; + StreamBufferHandle_t stream; + + size_t index; + Buffer* buffers; + size_t max_buffers_count; +}; + +bool buffer_write(Buffer* buffer, const uint8_t* data, size_t size) { + if(buffer->occupied) { + return false; + } + if((buffer->size + size) > buffer->max_data_size) { + return false; + } + memcpy(buffer->data + buffer->size, data, size); + buffer->size += size; + return true; +} + +uint8_t* buffer_get_data(Buffer* buffer) { + return buffer->data; +} + +size_t buffer_get_size(Buffer* buffer) { + return buffer->size; +} + +void buffer_reset(Buffer* buffer) { + buffer->occupied = false; + buffer->size = 0; +} + +BufferStream* buffer_stream_alloc(size_t buffer_size, size_t buffers_count) { + furi_assert(buffer_size > 0); + furi_assert(buffers_count > 0); + BufferStream* buffer_stream = malloc(sizeof(BufferStream)); + buffer_stream->max_buffers_count = buffers_count; + buffer_stream->buffers = malloc(sizeof(Buffer) * buffer_stream->max_buffers_count); + for(size_t i = 0; i < buffer_stream->max_buffers_count; i++) { + buffer_stream->buffers[i].occupied = false; + buffer_stream->buffers[i].size = 0; + buffer_stream->buffers[i].data = malloc(buffer_size); + buffer_stream->buffers[i].max_data_size = buffer_size; + } + buffer_stream->stream = xStreamBufferCreate( + sizeof(BufferStream*) * buffer_stream->max_buffers_count, sizeof(BufferStream*)); + buffer_stream->stream_overrun_count = 0; + buffer_stream->index = 0; + + return buffer_stream; +} + +void buffer_stream_free(BufferStream* buffer_stream) { + for(size_t i = 0; i < buffer_stream->max_buffers_count; i++) { + free(buffer_stream->buffers[i].data); + } + vStreamBufferDelete(buffer_stream->stream); + free(buffer_stream->buffers); + free(buffer_stream); +} + +static inline int8_t buffer_stream_get_free_buffer(BufferStream* buffer_stream) { + int8_t id = -1; + for(size_t i = 0; i < buffer_stream->max_buffers_count; i++) { + if(buffer_stream->buffers[i].occupied == false) { + id = i; + break; + } + } + + return id; +} + +bool buffer_stream_send_from_isr( + BufferStream* buffer_stream, + const uint8_t* data, + size_t size, + BaseType_t* const task_woken) { + Buffer* buffer = &buffer_stream->buffers[buffer_stream->index]; + bool result = true; + + // write to buffer + if(!buffer_write(buffer, data, size)) { + // if buffer is full - send it + buffer->occupied = true; + // we always have space for buffer in stream + xStreamBufferSendFromISR(buffer_stream->stream, &buffer, sizeof(Buffer*), task_woken); + + // get new buffer from the pool + int8_t index = buffer_stream_get_free_buffer(buffer_stream); + + // check that we have valid buffer + if(index == -1) { + // no free buffer + buffer_stream->stream_overrun_count++; + result = false; + } else { + // write to new buffer + buffer_stream->index = index; + buffer = &buffer_stream->buffers[buffer_stream->index]; + buffer_write(buffer, data, size); + } + } + + return result; +} + +Buffer* buffer_stream_receive(BufferStream* buffer_stream, TickType_t timeout) { + Buffer* buffer; + size_t size = xStreamBufferReceive(buffer_stream->stream, &buffer, sizeof(Buffer*), timeout); + + if(size == sizeof(Buffer*)) { + return buffer; + } else { + return NULL; + } +} + +size_t buffer_stream_get_overrun_count(BufferStream* buffer_stream) { + return buffer_stream->stream_overrun_count; +} + +void buffer_stream_reset(BufferStream* buffer_stream) { + FURI_CRITICAL_ENTER(); + BaseType_t xReturn = xStreamBufferReset(buffer_stream->stream); + furi_assert(xReturn == pdPASS); + UNUSED(xReturn); + buffer_stream->stream_overrun_count = 0; + for(size_t i = 0; i < buffer_stream->max_buffers_count; i++) { + buffer_reset(&buffer_stream->buffers[i]); + } + FURI_CRITICAL_EXIT(); +} \ No newline at end of file diff --git a/lib/toolbox/buffer_stream.h b/lib/toolbox/buffer_stream.h new file mode 100644 index 00000000..d4c3cddf --- /dev/null +++ b/lib/toolbox/buffer_stream.h @@ -0,0 +1,94 @@ +/** + * @file buffer_stream.h + * + * This file implements the concept of a buffer stream. + * Data is written to the buffer until the buffer is full. + * Then the buffer pointer is written to the stream, and the new write buffer is taken from the buffer pool. + * After the buffer has been read by the receiving thread, it is sent to the free buffer pool. + * + * This will speed up sending large chunks of data between threads, compared to using a stream directly. + */ +#pragma once +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct Buffer Buffer; + +/** + * @brief Get buffer data pointer + * @param buffer + * @return uint8_t* + */ +uint8_t* buffer_get_data(Buffer* buffer); + +/** + * @brief Get buffer size + * @param buffer + * @return size_t + */ +size_t buffer_get_size(Buffer* buffer); + +/** + * @brief Reset buffer and send to free buffer pool + * @param buffer + */ +void buffer_reset(Buffer* buffer); + +typedef struct BufferStream BufferStream; + +/** + * @brief Allocate a new BufferStream instance + * @param buffer_size + * @param buffers_count + * @return BufferStream* + */ +BufferStream* buffer_stream_alloc(size_t buffer_size, size_t buffers_count); + +/** + * @brief Free a BufferStream instance + * @param buffer_stream + */ +void buffer_stream_free(BufferStream* buffer_stream); + +/** + * @brief Write data to buffer stream, from ISR context + * Data will be written to the buffer until the buffer is full, and only then will the buffer be sent. + * @param buffer_stream + * @param data + * @param size + * @param task_woken + * @return bool + */ +bool buffer_stream_send_from_isr( + BufferStream* buffer_stream, + const uint8_t* data, + size_t size, + BaseType_t* const task_woken); + +/** + * @brief Receive buffer from stream + * @param buffer_stream + * @param timeout + * @return Buffer* + */ +Buffer* buffer_stream_receive(BufferStream* buffer_stream, TickType_t timeout); + +/** + * @brief Get stream overrun count + * @param buffer_stream + * @return size_t + */ +size_t buffer_stream_get_overrun_count(BufferStream* buffer_stream); + +/** + * @brief Reset stream and buffer pool + * @param buffer_stream + */ +void buffer_stream_reset(BufferStream* buffer_stream); + +#ifdef __cplusplus +} +#endif diff --git a/lib/toolbox/profiler.c b/lib/toolbox/profiler.c new file mode 100644 index 00000000..96f38dce --- /dev/null +++ b/lib/toolbox/profiler.c @@ -0,0 +1,87 @@ +#include "profiler.h" +#include +#include +#include +#include + +typedef struct { + uint32_t start; + uint32_t length; + uint32_t count; +} ProfilerRecord; + +DICT_DEF2(ProfilerRecordDict, const char*, M_CSTR_OPLIST, ProfilerRecord, M_POD_OPLIST) +#define M_OPL_ProfilerRecord_t() DICT_OPLIST(ProfilerRecord, M_CSTR_OPLIST, M_POD_OPLIST) + +struct Profiler { + ProfilerRecordDict_t records; +}; + +Profiler* profiler_alloc() { + Profiler* profiler = malloc(sizeof(Profiler)); + ProfilerRecordDict_init(profiler->records); + return profiler; +} + +void profiler_free(Profiler* profiler) { + ProfilerRecordDict_clear(profiler->records); + free(profiler); +} + +void profiler_prealloc(Profiler* profiler, const char* key) { + ProfilerRecord record = { + .start = 0, + .length = 0, + .count = 0, + }; + + ProfilerRecordDict_set_at(profiler->records, key, record); +} + +void profiler_start(Profiler* profiler, const char* key) { + ProfilerRecord* record = ProfilerRecordDict_get(profiler->records, key); + if(record == NULL) { + profiler_prealloc(profiler, key); + record = ProfilerRecordDict_get(profiler->records, key); + } + + furi_check(record->start == 0); + record->start = DWT->CYCCNT; +} + +void profiler_stop(Profiler* profiler, const char* key) { + ProfilerRecord* record = ProfilerRecordDict_get(profiler->records, key); + furi_check(record != NULL); + + record->length += DWT->CYCCNT - record->start; + record->start = 0; + record->count++; +} + +void profiler_dump(Profiler* profiler) { + printf("Profiler:\r\n"); + + ProfilerRecordDict_it_t it; + for(ProfilerRecordDict_it(it, profiler->records); !ProfilerRecordDict_end_p(it); + ProfilerRecordDict_next(it)) { + const ProfilerRecordDict_itref_t* itref = ProfilerRecordDict_cref(it); + + uint32_t count = itref->value.count; + + uint32_t clocks = itref->value.length; + double us = (double)clocks / (double)64.0; + double ms = (double)clocks / (double)64000.0; + double s = (double)clocks / (double)64000000.0; + + printf("\t%s[%lu]: %f s, %f ms, %f us, %lu clk\r\n", itref->key, count, s, ms, us, clocks); + + if(count > 1) { + us /= (double)count; + ms /= (double)count; + s /= (double)count; + clocks /= count; + + printf("\t%s[1]: %f s, %f ms, %f us, %lu clk\r\n", itref->key, s, ms, us, clocks); + } + } +} diff --git a/lib/toolbox/profiler.h b/lib/toolbox/profiler.h new file mode 100644 index 00000000..84014633 --- /dev/null +++ b/lib/toolbox/profiler.h @@ -0,0 +1,23 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct Profiler Profiler; + +Profiler* profiler_alloc(); + +void profiler_free(Profiler* profiler); + +void profiler_prealloc(Profiler* profiler, const char* key); + +void profiler_start(Profiler* profiler, const char* key); + +void profiler_stop(Profiler* profiler, const char* key); + +void profiler_dump(Profiler* profiler); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/lib/toolbox/protocols/protocol.h b/lib/toolbox/protocols/protocol.h new file mode 100644 index 00000000..56bb1b74 --- /dev/null +++ b/lib/toolbox/protocols/protocol.h @@ -0,0 +1,46 @@ +#pragma once +#include +#include +#include +#include +#include + +typedef void* (*ProtocolAlloc)(void); +typedef void (*ProtocolFree)(void* protocol); +typedef uint8_t* (*ProtocolGetData)(void* protocol); + +typedef void (*ProtocolDecoderStart)(void* protocol); +typedef bool (*ProtocolDecoderFeed)(void* protocol, bool level, uint32_t duration); + +typedef bool (*ProtocolEncoderStart)(void* protocol); +typedef LevelDuration (*ProtocolEncoderYield)(void* protocol); + +typedef void (*ProtocolRenderData)(void* protocol, string_t result); +typedef bool (*ProtocolWriteData)(void* protocol, void* data); + +typedef struct { + ProtocolDecoderStart start; + ProtocolDecoderFeed feed; +} ProtocolDecoder; + +typedef struct { + ProtocolEncoderStart start; + ProtocolEncoderYield yield; +} ProtocolEncoder; + +typedef struct { + const size_t data_size; + const char* name; + const char* manufacturer; + const uint32_t features; + const uint8_t validate_count; + + ProtocolAlloc alloc; + ProtocolFree free; + ProtocolGetData get_data; + ProtocolDecoder decoder; + ProtocolEncoder encoder; + ProtocolRenderData render_data; + ProtocolRenderData render_brief_data; + ProtocolWriteData write_data; +} ProtocolBase; \ No newline at end of file diff --git a/lib/toolbox/protocols/protocol_dict.c b/lib/toolbox/protocols/protocol_dict.c new file mode 100644 index 00000000..2a022cc4 --- /dev/null +++ b/lib/toolbox/protocols/protocol_dict.c @@ -0,0 +1,226 @@ +#include +#include "protocol_dict.h" + +struct ProtocolDict { + const ProtocolBase** base; + size_t count; + void** data; +}; + +ProtocolDict* protocol_dict_alloc(const ProtocolBase** protocols, size_t count) { + ProtocolDict* dict = malloc(sizeof(ProtocolDict)); + dict->base = protocols; + dict->count = count; + dict->data = malloc(sizeof(void*) * dict->count); + + for(size_t i = 0; i < dict->count; i++) { + dict->data[i] = dict->base[i]->alloc(); + } + + return dict; +} + +void protocol_dict_free(ProtocolDict* dict) { + for(size_t i = 0; i < dict->count; i++) { + dict->base[i]->free(dict->data[i]); + } + + free(dict->data); + free(dict); +} + +void protocol_dict_set_data( + ProtocolDict* dict, + size_t protocol_index, + const uint8_t* data, + size_t data_size) { + furi_assert(protocol_index < dict->count); + furi_assert(dict->base[protocol_index]->get_data != NULL); + uint8_t* protocol_data = dict->base[protocol_index]->get_data(dict->data[protocol_index]); + size_t protocol_data_size = dict->base[protocol_index]->data_size; + furi_check(data_size >= protocol_data_size); + memcpy(protocol_data, data, protocol_data_size); +} + +void protocol_dict_get_data( + ProtocolDict* dict, + size_t protocol_index, + uint8_t* data, + size_t data_size) { + furi_assert(protocol_index < dict->count); + furi_assert(dict->base[protocol_index]->get_data != NULL); + uint8_t* protocol_data = dict->base[protocol_index]->get_data(dict->data[protocol_index]); + size_t protocol_data_size = dict->base[protocol_index]->data_size; + furi_check(data_size >= protocol_data_size); + memcpy(data, protocol_data, protocol_data_size); +} + +size_t protocol_dict_get_data_size(ProtocolDict* dict, size_t protocol_index) { + furi_assert(protocol_index < dict->count); + return dict->base[protocol_index]->data_size; +} + +size_t protocol_dict_get_max_data_size(ProtocolDict* dict) { + size_t max_data_size = 0; + for(size_t i = 0; i < dict->count; i++) { + size_t data_size = dict->base[i]->data_size; + if(data_size > max_data_size) { + max_data_size = data_size; + } + } + + return max_data_size; +} + +const char* protocol_dict_get_name(ProtocolDict* dict, size_t protocol_index) { + furi_assert(protocol_index < dict->count); + return dict->base[protocol_index]->name; +} + +const char* protocol_dict_get_manufacturer(ProtocolDict* dict, size_t protocol_index) { + furi_assert(protocol_index < dict->count); + return dict->base[protocol_index]->manufacturer; +} + +void protocol_dict_decoders_start(ProtocolDict* dict) { + for(size_t i = 0; i < dict->count; i++) { + ProtocolDecoderStart fn = dict->base[i]->decoder.start; + + if(fn) { + fn(dict->data[i]); + } + } +} + +uint32_t protocol_dict_get_features(ProtocolDict* dict, size_t protocol_index) { + furi_assert(protocol_index < dict->count); + return dict->base[protocol_index]->features; +} + +ProtocolId protocol_dict_decoders_feed(ProtocolDict* dict, bool level, uint32_t duration) { + bool done = false; + ProtocolId ready_protocol_id = PROTOCOL_NO; + + for(size_t i = 0; i < dict->count; i++) { + ProtocolDecoderFeed fn = dict->base[i]->decoder.feed; + + if(fn) { + if(fn(dict->data[i], level, duration)) { + if(!done) { + ready_protocol_id = i; + done = true; + } + } + } + } + + return ready_protocol_id; +} + +ProtocolId protocol_dict_decoders_feed_by_feature( + ProtocolDict* dict, + uint32_t feature, + bool level, + uint32_t duration) { + bool done = false; + ProtocolId ready_protocol_id = PROTOCOL_NO; + + for(size_t i = 0; i < dict->count; i++) { + uint32_t features = dict->base[i]->features; + if(features & feature) { + ProtocolDecoderFeed fn = dict->base[i]->decoder.feed; + + if(fn) { + if(fn(dict->data[i], level, duration)) { + if(!done) { + ready_protocol_id = i; + done = true; + } + } + } + } + } + + return ready_protocol_id; +} + +ProtocolId protocol_dict_decoders_feed_by_id( + ProtocolDict* dict, + size_t protocol_index, + bool level, + uint32_t duration) { + furi_assert(protocol_index < dict->count); + + ProtocolId ready_protocol_id = PROTOCOL_NO; + ProtocolDecoderFeed fn = dict->base[protocol_index]->decoder.feed; + + if(fn) { + if(fn(dict->data[protocol_index], level, duration)) { + ready_protocol_id = protocol_index; + } + } + + return ready_protocol_id; +} + +bool protocol_dict_encoder_start(ProtocolDict* dict, size_t protocol_index) { + furi_assert(protocol_index < dict->count); + ProtocolEncoderStart fn = dict->base[protocol_index]->encoder.start; + + if(fn) { + return fn(dict->data[protocol_index]); + } else { + return false; + } +} + +LevelDuration protocol_dict_encoder_yield(ProtocolDict* dict, size_t protocol_index) { + furi_assert(protocol_index < dict->count); + ProtocolEncoderYield fn = dict->base[protocol_index]->encoder.yield; + + if(fn) { + return fn(dict->data[protocol_index]); + } else { + return level_duration_reset(); + } +} + +void protocol_dict_render_data(ProtocolDict* dict, string_t result, size_t protocol_index) { + furi_assert(protocol_index < dict->count); + ProtocolRenderData fn = dict->base[protocol_index]->render_data; + + if(fn) { + return fn(dict->data[protocol_index], result); + } +} + +void protocol_dict_render_brief_data(ProtocolDict* dict, string_t result, size_t protocol_index) { + furi_assert(protocol_index < dict->count); + ProtocolRenderData fn = dict->base[protocol_index]->render_brief_data; + + if(fn) { + return fn(dict->data[protocol_index], result); + } +} + +uint32_t protocol_dict_get_validate_count(ProtocolDict* dict, size_t protocol_index) { + furi_assert(protocol_index < dict->count); + return dict->base[protocol_index]->validate_count; +} + +ProtocolId protocol_dict_get_protocol_by_name(ProtocolDict* dict, const char* name) { + for(size_t i = 0; i < dict->count; i++) { + if(strcmp(name, protocol_dict_get_name(dict, i)) == 0) { + return i; + } + } + return PROTOCOL_NO; +} + +bool protocol_dict_get_write_data(ProtocolDict* dict, size_t protocol_index, void* data) { + furi_assert(protocol_index < dict->count); + ProtocolWriteData fn = dict->base[protocol_index]->write_data; + + furi_assert(fn); + return fn(dict->data[protocol_index], data); +} \ No newline at end of file diff --git a/lib/toolbox/protocols/protocol_dict.h b/lib/toolbox/protocols/protocol_dict.h new file mode 100644 index 00000000..3037ddd5 --- /dev/null +++ b/lib/toolbox/protocols/protocol_dict.h @@ -0,0 +1,73 @@ +#pragma once +#include "protocol.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct ProtocolDict ProtocolDict; + +typedef int32_t ProtocolId; + +#define PROTOCOL_NO (-1) +#define PROTOCOL_ALL_FEATURES (0xFFFFFFFF) + +ProtocolDict* protocol_dict_alloc(const ProtocolBase** protocols, size_t protocol_count); + +void protocol_dict_free(ProtocolDict* dict); + +void protocol_dict_set_data( + ProtocolDict* dict, + size_t protocol_index, + const uint8_t* data, + size_t data_size); + +void protocol_dict_get_data( + ProtocolDict* dict, + size_t protocol_index, + uint8_t* data, + size_t data_size); + +size_t protocol_dict_get_data_size(ProtocolDict* dict, size_t protocol_index); + +size_t protocol_dict_get_max_data_size(ProtocolDict* dict); + +const char* protocol_dict_get_name(ProtocolDict* dict, size_t protocol_index); + +const char* protocol_dict_get_manufacturer(ProtocolDict* dict, size_t protocol_index); + +void protocol_dict_decoders_start(ProtocolDict* dict); + +uint32_t protocol_dict_get_features(ProtocolDict* dict, size_t protocol_index); + +ProtocolId protocol_dict_decoders_feed(ProtocolDict* dict, bool level, uint32_t duration); + +ProtocolId protocol_dict_decoders_feed_by_feature( + ProtocolDict* dict, + uint32_t feature, + bool level, + uint32_t duration); + +ProtocolId protocol_dict_decoders_feed_by_id( + ProtocolDict* dict, + size_t protocol_index, + bool level, + uint32_t duration); + +bool protocol_dict_encoder_start(ProtocolDict* dict, size_t protocol_index); + +LevelDuration protocol_dict_encoder_yield(ProtocolDict* dict, size_t protocol_index); + +void protocol_dict_render_data(ProtocolDict* dict, string_t result, size_t protocol_index); + +void protocol_dict_render_brief_data(ProtocolDict* dict, string_t result, size_t protocol_index); + +uint32_t protocol_dict_get_validate_count(ProtocolDict* dict, size_t protocol_index); + +ProtocolId protocol_dict_get_protocol_by_name(ProtocolDict* dict, const char* name); + +bool protocol_dict_get_write_data(ProtocolDict* dict, size_t protocol_index, void* data); + +#ifdef __cplusplus +} +#endif diff --git a/lib/toolbox/pulse_joiner.c b/lib/toolbox/pulse_joiner.c new file mode 100644 index 00000000..b6206486 --- /dev/null +++ b/lib/toolbox/pulse_joiner.c @@ -0,0 +1,117 @@ +#include "pulse_joiner.h" +#include + +#define PULSE_MAX_COUNT 6 + +typedef struct { + bool polarity; + uint16_t time; +} Pulse; + +struct PulseJoiner { + size_t pulse_index; + Pulse pulses[PULSE_MAX_COUNT]; +}; + +PulseJoiner* pulse_joiner_alloc() { + PulseJoiner* pulse_joiner = malloc(sizeof(PulseJoiner)); + + pulse_joiner->pulse_index = 0; + for(uint8_t i = 0; i < PULSE_MAX_COUNT; i++) { + pulse_joiner->pulses[i].polarity = false; + pulse_joiner->pulses[i].time = 0; + } + + return pulse_joiner; +} + +void pulse_joiner_free(PulseJoiner* pulse_joiner) { + free(pulse_joiner); +} + +bool pulse_joiner_push_pulse(PulseJoiner* pulse_joiner, bool polarity, size_t period, size_t pulse) { + bool result = false; + furi_check((pulse_joiner->pulse_index + 1) < PULSE_MAX_COUNT); + + if(polarity == false && pulse_joiner->pulse_index == 0) { + // first negative pulse is omitted + + } else { + pulse_joiner->pulses[pulse_joiner->pulse_index].polarity = polarity; + pulse_joiner->pulses[pulse_joiner->pulse_index].time = pulse; + pulse_joiner->pulse_index++; + } + + if(period > pulse) { + pulse_joiner->pulses[pulse_joiner->pulse_index].polarity = !polarity; + pulse_joiner->pulses[pulse_joiner->pulse_index].time = period - pulse; + pulse_joiner->pulse_index++; + } + + if(pulse_joiner->pulse_index >= 4) { + // we know that first pulse is always high + // so we wait 2 edges, hi2low and next low2hi + + uint8_t edges_count = 0; + bool last_polarity = pulse_joiner->pulses[0].polarity; + + for(uint8_t i = 1; i < pulse_joiner->pulse_index; i++) { + if(pulse_joiner->pulses[i].polarity != last_polarity) { + edges_count++; + last_polarity = pulse_joiner->pulses[i].polarity; + } + } + + if(edges_count >= 2) { + result = true; + } + } + + return result; +} + +void pulse_joiner_pop_pulse(PulseJoiner* pulse_joiner, size_t* period, size_t* pulse) { + furi_check(pulse_joiner->pulse_index <= (PULSE_MAX_COUNT + 1)); + + uint16_t tmp_period = 0; + uint16_t tmp_pulse = 0; + uint8_t edges_count = 0; + bool last_polarity = pulse_joiner->pulses[0].polarity; + uint8_t next_fist_pulse = 0; + + for(uint8_t i = 0; i < PULSE_MAX_COUNT; i++) { + // count edges + if(pulse_joiner->pulses[i].polarity != last_polarity) { + edges_count++; + last_polarity = pulse_joiner->pulses[i].polarity; + } + + // wait for 2 edges + if(edges_count == 2) { + next_fist_pulse = i; + break; + } + + // sum pulse time + if(pulse_joiner->pulses[i].polarity) { + tmp_period += pulse_joiner->pulses[i].time; + tmp_pulse += pulse_joiner->pulses[i].time; + } else { + tmp_period += pulse_joiner->pulses[i].time; + } + pulse_joiner->pulse_index--; + } + + *period = tmp_period; + *pulse = tmp_pulse; + + // remove counted periods and shift data + for(uint8_t i = 0; i < PULSE_MAX_COUNT; i++) { + if((next_fist_pulse + i) < PULSE_MAX_COUNT) { + pulse_joiner->pulses[i].polarity = pulse_joiner->pulses[next_fist_pulse + i].polarity; + pulse_joiner->pulses[i].time = pulse_joiner->pulses[next_fist_pulse + i].time; + } else { + break; + } + } +} \ No newline at end of file diff --git a/lib/toolbox/pulse_joiner.h b/lib/toolbox/pulse_joiner.h new file mode 100644 index 00000000..25f702e7 --- /dev/null +++ b/lib/toolbox/pulse_joiner.h @@ -0,0 +1,46 @@ +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct PulseJoiner PulseJoiner; + +/** + * @brief Alloc PulseJoiner + * + * @return PulseJoiner* + */ +PulseJoiner* pulse_joiner_alloc(); + +/** + * @brief Free PulseJoiner + * + * @param pulse_joiner + */ +void pulse_joiner_free(PulseJoiner* pulse_joiner); + +/** + * @brief Push timer pulse. First negative pulse is ommited. + * + * @param polarity pulse polarity: true = high2low, false = low2high + * @param period overall period time in timer clicks + * @param pulse pulse time in timer clicks + * + * @return true - next pulse can and must be popped immediatly + */ +bool pulse_joiner_push_pulse(PulseJoiner* pulse_joiner, bool polarity, size_t period, size_t pulse); + +/** + * @brief Get the next timer pulse. Call only if push_pulse returns true. + * + * @param period overall period time in timer clicks + * @param pulse pulse time in timer clicks + */ +void pulse_joiner_pop_pulse(PulseJoiner* pulse_joiner, size_t* period, size_t* pulse); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/lib/one_wire/pulse_protocols/pulse_glue.c b/lib/toolbox/pulse_protocols/pulse_glue.c similarity index 100% rename from lib/one_wire/pulse_protocols/pulse_glue.c rename to lib/toolbox/pulse_protocols/pulse_glue.c diff --git a/lib/one_wire/pulse_protocols/pulse_glue.h b/lib/toolbox/pulse_protocols/pulse_glue.h similarity index 100% rename from lib/one_wire/pulse_protocols/pulse_glue.h rename to lib/toolbox/pulse_protocols/pulse_glue.h diff --git a/lib/toolbox/varint.c b/lib/toolbox/varint.c new file mode 100644 index 00000000..ee2f5c3a --- /dev/null +++ b/lib/toolbox/varint.c @@ -0,0 +1,76 @@ +#include "varint.h" + +size_t varint_uint32_pack(uint32_t value, uint8_t* output) { + uint8_t* start = output; + while(value >= 0x80) { + *output++ = (value | 0x80); + value >>= 7; + } + *output++ = value; + return output - start; +} + +size_t varint_uint32_unpack(uint32_t* value, const uint8_t* input, size_t input_size) { + size_t i; + uint32_t parsed = 0; + + for(i = 0; i < input_size; i++) { + parsed |= (input[i] & 0x7F) << (7 * i); + + if(!(input[i] & 0x80)) { + break; + } + } + + *value = parsed; + + return i + 1; +} + +size_t varint_uint32_length(uint32_t value) { + size_t size = 0; + while(value >= 0x80) { + value >>= 7; + size++; + } + size++; + + return size; +} + +size_t varint_int32_pack(int32_t value, uint8_t* output) { + uint32_t v; + + if(value >= 0) { + v = value * 2; + } else { + v = (value * -2) - 1; + } + + return varint_uint32_pack(v, output); +} + +size_t varint_int32_unpack(int32_t* value, const uint8_t* input, size_t input_size) { + uint32_t v; + size_t size = varint_uint32_unpack(&v, input, input_size); + + if(v & 1) { + *value = (int32_t)(v + 1) / (-2); + } else { + *value = v / 2; + } + + return size; +} + +size_t varint_int32_length(int32_t value) { + uint32_t v; + + if(value >= 0) { + v = value * 2; + } else { + v = (value * -2) - 1; + } + + return varint_uint32_length(v); +} \ No newline at end of file diff --git a/lib/toolbox/varint.h b/lib/toolbox/varint.h new file mode 100644 index 00000000..bf4681d4 --- /dev/null +++ b/lib/toolbox/varint.h @@ -0,0 +1,35 @@ +#pragma once +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Pack uint32 to varint + * @param value value from UINT32_MIN to UINT32_MAX + * @param output output array, need to be at least 5 bytes long + * @return size_t + */ +size_t varint_uint32_pack(uint32_t value, uint8_t* output); + +size_t varint_uint32_unpack(uint32_t* value, const uint8_t* input, size_t input_size); + +size_t varint_uint32_length(uint32_t value); + +/** + * Pack int32 to varint + * @param value value from (INT32_MIN / 2 + 1) to INT32_MAX + * @param output output array, need to be at least 5 bytes long + * @return size_t + */ +size_t varint_int32_pack(int32_t value, uint8_t* output); + +size_t varint_int32_unpack(int32_t* value, const uint8_t* input, size_t input_size); + +size_t varint_int32_length(int32_t value); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/scripts/toolchain/fbtenv.sh b/scripts/toolchain/fbtenv.sh index d9d3cbe8..b06c582a 100755 --- a/scripts/toolchain/fbtenv.sh +++ b/scripts/toolchain/fbtenv.sh @@ -5,7 +5,7 @@ # public variables DEFAULT_SCRIPT_PATH="$(pwd -P)"; SCRIPT_PATH="${SCRIPT_PATH:-$DEFAULT_SCRIPT_PATH}"; -FBT_TOOLCHAIN_VERSION="${FBT_TOOLCHAIN_VERSION:-"9"}"; +FBT_TOOLCHAIN_VERSION="${FBT_TOOLCHAIN_VERSION:-"8"}"; FBT_TOOLCHAIN_PATH="${FBT_TOOLCHAIN_PATH:-$SCRIPT_PATH}"; fbtenv_check_sourced() @@ -199,7 +199,7 @@ fbtenv_main() fbtenv_check_script_path || return 1; fbtenv_get_kernel_type || return 1; fbtenv_check_download_toolchain || return 1; - PS1="[FBT]$PS1"; + PS1="[FBT]${PS1-}"; PATH="$TOOLCHAIN_ARCH_DIR/python/bin:$PATH"; PATH="$TOOLCHAIN_ARCH_DIR/bin:$PATH"; PATH="$TOOLCHAIN_ARCH_DIR/protobuf/bin:$PATH";