[FL-2529][FL-1628] New LF-RFID subsystem (#1601)
* Makefile: unit tests pack * RFID: pulse joiner and its unit test * Move pulse protocol helpers to appropriate place * Drop pulse_joiner tests * Generic protocol, protocols dictionary, unit test * Protocol dict unit test * iButton: protocols dictionary * Lib: varint * Lib: profiler * Unit test: varint * rfid: worker mockup * LFRFID: em4100 unit test * Storage: file_exist function * rfid: fsk osc * rfid: generic fsk demodulator * rfid: protocol em4100 * rfid: protocol h10301 * rfid: protocol io prox xsf * Unit test: rfid protocols * rfid: new hal * rfid: raw worker * Unit test: fix error output * rfid: worker * rfid: plain c cli * fw: migrate to scons * lfrfid: full io prox support * unit test: io prox protocol * SubGHZ: move bit defines to source * FSK oscillator: level duration compability * libs: bit manipulation library * lfrfid: ioprox protocol, use bit library and new level duration method of FSK ocillator * bit lib: unit tests * Bit lib: parity tests, remove every nth bit, copy bits * Lfrfid: awid protocol * bit lib: uint16 and uint32 getters, unit tests * lfrfid: FDX-B read, draft version * Minunit: better memeq assert * bit lib: reverse, print, print regions * Protocol dict: get protocol features, get protocol validate count * lfrfid worker: improved read * lfrfid raw worker: psk support * Cli: rfid plain C cli * protocol AWID: render * protocol em4100: render * protocol h10301: render * protocol indala26: support every indala 26 scramble * Protocol IO Prox: render * Protocol FDX-B: advanced read * lfrfid: remove unused test function * lfrfid: fix os primitives * bit lib: crc16 and unit tests * FDX-B: save data * lfrfid worker: increase stream size. Alloc raw worker only when needed. * lfrfid: indala26 emulation * lfrfid: prepare to write * lfrfid: fdx-b emulation * lfrfid: awid, ioprox write * lfrfid: write t55xx w\o validation * lfrfid: better t55xx block0 handling * lfrfid: use new t5577 functions in worker * lfrfid: improve protocol description * lfrfid: write and verify * lfrfid: delete cpp cli * lfrfid: improve worker usage * lfrfid-app: step to new worker * lfrfid: old indala (I40134) load fallback * lfrfid: indala26, recover wrong synced data * lfrfid: remove old worker * lfrfid app: dummy read screen * lfrfid app: less dummy read screen * lfrfid: generic 96-bit HID protocol (covers up to HID 37-bit) * rename * lfrfid: improve indala26 read * lfrfid: generic 192-bit HID protocol (covers all HID extended) * lfrfid: TODO about HID render * lfrfid: new protocol FDX-A * lfrfid-app: correct worker stop on exit * misc fixes * lfrfid: FDX-A and HID distinguishability has been fixed. * lfrfid: decode HID size header and render it (#1612) * lfrfid: rename HID96 and HID192 to HIDProx and HIDExt * lfrfid: extra actions scene * lfrfid: decode generic HID Proximity size lazily (#1618) * lib: stream of data buffers concept * lfrfid: raw file helper * lfrfid: changed raw worker api * lfrfid: packed varint pair * lfrfid: read stream speedup * lfrfid app: show read mode * Documentation * lfrfid app: raw read gui * lfrfid app: storage check for raw read * memleak fix * review fixes * lfrfid app: read blink color * lfrfid app: reset key name after read * review fixes * lfrfid app: fix copypasted text * review fixes * lfrfid: disable debug gpio * lfrfid: card detection events * lfrfid: change validation color from magenta to green * Update core_defines. * lfrfid: prefix fdx-b id by zeroes * lfrfid: parse up to 43-bit HID Proximity keys (#1640) * Fbt: downgrade toolchain and fix PS1 * lfrfid: fix unit tests * lfrfid app: remove printf * lfrfid: indala26, use bit 55 as data * lfrfid: indala26, better brief format * lfrfid: indala26, loading fallback * lfrfid: read timing tuning Co-authored-by: James Ide <ide@users.noreply.github.com> Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
		
							parent
							
								
									f92127c0a7
								
							
						
					
					
						commit
						9bfb641d3e
					
				| @ -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; | ||||
|  | ||||
| @ -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); | ||||
|  | ||||
| @ -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; | ||||
|  | ||||
| @ -1,50 +0,0 @@ | ||||
| #include "decoder_analyzer.h" | ||||
| #include <furi.h> | ||||
| #include <furi_hal.h> | ||||
| 
 | ||||
| // 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<uint32_t*>(calloc(data_size, sizeof(uint32_t))); | ||||
|     furi_check(data); | ||||
|     data_index = 0; | ||||
|     ready = false; | ||||
| } | ||||
| 
 | ||||
| DecoderAnalyzer::~DecoderAnalyzer() { | ||||
|     free(data); | ||||
| } | ||||
| 
 | ||||
| void DecoderAnalyzer::reset_state() { | ||||
| } | ||||
| @ -1,21 +0,0 @@ | ||||
| #pragma once | ||||
| #include <stdint.h> | ||||
| #include <atomic> | ||||
| 
 | ||||
| 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<bool> ready; | ||||
| 
 | ||||
|     static const uint32_t data_size = 2048; | ||||
|     uint32_t data_index = 0; | ||||
|     uint32_t* data; | ||||
| }; | ||||
| @ -1,72 +0,0 @@ | ||||
| #include "emmarin.h" | ||||
| #include "decoder_emmarin.h" | ||||
| #include <furi.h> | ||||
| #include <furi_hal.h> | ||||
| 
 | ||||
| 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<const uint8_t*>(&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<const uint8_t*>(&read_data), sizeof(uint64_t)); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| DecoderEMMarin::DecoderEMMarin() { | ||||
|     reset_state(); | ||||
| } | ||||
| @ -1,21 +0,0 @@ | ||||
| #pragma once | ||||
| #include <stdint.h> | ||||
| #include <atomic> | ||||
| #include <lib/toolbox/manchester_decoder.h> | ||||
| #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<bool> ready; | ||||
| 
 | ||||
|     ManchesterState manchester_saved_state; | ||||
|     ProtocolEMMarin em_marin; | ||||
| }; | ||||
| @ -1,15 +0,0 @@ | ||||
| #include "decoder_gpio_out.h" | ||||
| #include <furi.h> | ||||
| #include <furi_hal.h> | ||||
| 
 | ||||
| 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); | ||||
| } | ||||
| @ -1,14 +0,0 @@ | ||||
| #pragma once | ||||
| #include <stdint.h> | ||||
| #include <atomic> | ||||
| 
 | ||||
| class DecoderGpioOut { | ||||
| public: | ||||
|     void process_front(bool polarity, uint32_t time); | ||||
| 
 | ||||
|     DecoderGpioOut(); | ||||
|     ~DecoderGpioOut(); | ||||
| 
 | ||||
| private: | ||||
|     void reset_state(); | ||||
| }; | ||||
| @ -1,98 +0,0 @@ | ||||
| #include "decoder_hid26.h" | ||||
| #include <furi_hal.h> | ||||
| 
 | ||||
| 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<const uint8_t*>(&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<const uint8_t*>(&stored_data), sizeof(uint32_t) * 3)) { | ||||
|         ready = true; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void DecoderHID26::reset_state() { | ||||
|     last_pulse = false; | ||||
|     pulse_count = 0; | ||||
|     ready = false; | ||||
|     last_pulse_time = 0; | ||||
| } | ||||
| @ -1,24 +0,0 @@ | ||||
| #pragma once | ||||
| #include <stdint.h> | ||||
| #include <atomic> | ||||
| #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<bool> ready; | ||||
| 
 | ||||
|     void reset_state(); | ||||
|     ProtocolHID10301 hid; | ||||
| }; | ||||
| @ -1,76 +0,0 @@ | ||||
| #include "decoder_indala.h" | ||||
| #include <furi_hal.h> | ||||
| 
 | ||||
| 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<const uint8_t*>(&cursed_raw_data), | ||||
|                 sizeof(uint64_t), | ||||
|                 data, | ||||
|                 data_size); | ||||
|         } else { | ||||
|             indala.decode( | ||||
|                 reinterpret_cast<const uint8_t*>(&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<const uint8_t*>(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; | ||||
| } | ||||
| @ -1,25 +0,0 @@ | ||||
| #pragma once | ||||
| #include <stdint.h> | ||||
| #include <limits.h> | ||||
| #include <atomic> | ||||
| #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<bool> ready; | ||||
|     std::atomic<bool> cursed_data_valid; | ||||
|     ProtocolIndala40134 indala; | ||||
| }; | ||||
| @ -1,107 +0,0 @@ | ||||
| #include "decoder_ioprox.h" | ||||
| #include <furi_hal.h> | ||||
| #include <cli/cli.h> | ||||
| #include <utility> | ||||
| 
 | ||||
| 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; | ||||
| } | ||||
| @ -1,26 +0,0 @@ | ||||
| #pragma once | ||||
| #include <stdint.h> | ||||
| #include <atomic> | ||||
| #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<bool> ready; | ||||
| 
 | ||||
|     void reset_state(); | ||||
|     ProtocolIoProx ioprox; | ||||
| }; | ||||
| @ -1,15 +0,0 @@ | ||||
| #pragma once | ||||
| #include <stdint.h> | ||||
| 
 | ||||
| #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) | ||||
| @ -1,24 +0,0 @@ | ||||
| #include "encoder_emmarin.h" | ||||
| #include "protocols/protocol_emmarin.h" | ||||
| #include <furi.h> | ||||
| 
 | ||||
| void EncoderEM::init(const uint8_t* data, const uint8_t data_size) { | ||||
|     ProtocolEMMarin em_marin; | ||||
|     em_marin.encode(data, data_size, reinterpret_cast<uint8_t*>(&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; | ||||
|     } | ||||
| } | ||||
| @ -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; | ||||
| }; | ||||
| @ -1,27 +0,0 @@ | ||||
| #pragma once | ||||
| #include <stdbool.h> | ||||
| #include <stdint.h> | ||||
| 
 | ||||
| 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: | ||||
| }; | ||||
| @ -1,46 +0,0 @@ | ||||
| #include "encoder_hid_h10301.h" | ||||
| #include "protocols/protocol_hid_h10301.h" | ||||
| #include <furi.h> | ||||
| 
 | ||||
| void EncoderHID_H10301::init(const uint8_t* data, const uint8_t data_size) { | ||||
|     ProtocolHID10301 hid; | ||||
|     hid.encode(data, data_size, reinterpret_cast<uint8_t*>(&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; | ||||
| } | ||||
| @ -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; | ||||
| }; | ||||
| @ -1,36 +0,0 @@ | ||||
| #include "encoder_indala_40134.h" | ||||
| #include "protocols/protocol_indala_40134.h" | ||||
| #include <furi.h> | ||||
| 
 | ||||
| void EncoderIndala_40134::init(const uint8_t* data, const uint8_t data_size) { | ||||
|     ProtocolIndala40134 indala; | ||||
|     indala.encode(data, data_size, reinterpret_cast<uint8_t*>(&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; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -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; | ||||
| }; | ||||
| @ -1,32 +0,0 @@ | ||||
| #include "encoder_ioprox.h" | ||||
| #include "protocols/protocol_ioprox.h" | ||||
| #include <furi.h> | ||||
| 
 | ||||
| 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; | ||||
| } | ||||
| @ -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; | ||||
| }; | ||||
| @ -1,76 +0,0 @@ | ||||
| #include "key_info.h" | ||||
| #include <string.h> | ||||
| 
 | ||||
| const char* lfrfid_key_get_type_string(LfrfidKeyType type) { | ||||
|     switch(type) { | ||||
|     case LfrfidKeyType::KeyEM4100: | ||||
|         return "EM4100"; | ||||
|         break; | ||||
|     case LfrfidKeyType::KeyH10301: | ||||
|         return "H10301"; | ||||
|         break; | ||||
|     case LfrfidKeyType::KeyI40134: | ||||
|         return "I40134"; | ||||
|         break; | ||||
|     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; | ||||
| } | ||||
| @ -1,17 +0,0 @@ | ||||
| #pragma once | ||||
| #include <stdint.h> | ||||
| 
 | ||||
| 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); | ||||
| @ -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; | ||||
| } | ||||
| @ -1,30 +0,0 @@ | ||||
| #pragma once | ||||
| #include <stdint.h> | ||||
| 
 | ||||
| /**
 | ||||
|  * 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; | ||||
| }; | ||||
| @ -1,150 +0,0 @@ | ||||
| #include "protocol_emmarin.h" | ||||
| #include <furi.h> | ||||
| 
 | ||||
| #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<const EMMarinCardData*>(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<const EMMarinCardData*>(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; | ||||
| } | ||||
| @ -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; | ||||
| }; | ||||
| @ -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(){}; | ||||
| }; | ||||
| @ -1,238 +0,0 @@ | ||||
| #include "protocol_hid_h10301.h" | ||||
| #include <furi.h> | ||||
| 
 | ||||
| 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<const HID10301CardData*>(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<const HID10301CardData*>(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; | ||||
| } | ||||
| @ -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; | ||||
| }; | ||||
| @ -1,237 +0,0 @@ | ||||
| #include "protocol_indala_40134.h" | ||||
| #include <furi.h> | ||||
| 
 | ||||
| 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<const Indala40134CardData*>(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<const Indala40134CardData*>(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; | ||||
| } | ||||
| @ -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; | ||||
| }; | ||||
| @ -1,193 +0,0 @@ | ||||
| #include "protocol_ioprox.h" | ||||
| #include <furi.h> | ||||
| #include <cli/cli.h> | ||||
| 
 | ||||
| /**
 | ||||
|  * 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; | ||||
| } | ||||
| @ -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); | ||||
| }; | ||||
| @ -1,95 +0,0 @@ | ||||
| #include "pulse_joiner.h" | ||||
| #include <furi.h> | ||||
| 
 | ||||
| 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}; | ||||
|     } | ||||
| } | ||||
| @ -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]; | ||||
| }; | ||||
| @ -1,65 +0,0 @@ | ||||
| #include "rfid_key.h" | ||||
| #include <core/check.h> | ||||
| #include <string.h> | ||||
| 
 | ||||
| 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; | ||||
| } | ||||
| @ -1,27 +0,0 @@ | ||||
| #pragma once | ||||
| #include "key_info.h" | ||||
| #include <array> | ||||
| 
 | ||||
| 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<uint8_t, LFRFID_KEY_SIZE> data; | ||||
|     LfrfidKeyType type; | ||||
|     char name[LFRFID_KEY_NAME_SIZE + 1]; | ||||
| }; | ||||
| @ -1,175 +0,0 @@ | ||||
| #include "rfid_reader.h" | ||||
| #include <furi.h> | ||||
| #include <furi_hal.h> | ||||
| #include <stm32wbxx_ll_cortex.h> | ||||
| 
 | ||||
| /**
 | ||||
|  * @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<RfidReader*>(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); | ||||
| } | ||||
| @ -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; | ||||
| }; | ||||
| @ -1,56 +0,0 @@ | ||||
| #include "rfid_timer_emulator.h" | ||||
| 
 | ||||
| RfidTimerEmulator::RfidTimerEmulator() { | ||||
| } | ||||
| 
 | ||||
| RfidTimerEmulator::~RfidTimerEmulator() { | ||||
|     std::map<LfrfidKeyType, EncoderGeneric*>::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<RfidTimerEmulator*>(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); | ||||
| } | ||||
| @ -1,31 +0,0 @@ | ||||
| #pragma once | ||||
| #include <furi_hal.h> | ||||
| #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 <map> | ||||
| 
 | ||||
| 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<LfrfidKeyType, EncoderGeneric*> 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); | ||||
| }; | ||||
| @ -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(); | ||||
| } | ||||
| @ -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(); | ||||
| }; | ||||
| @ -1,183 +0,0 @@ | ||||
| #include "rfid_writer.h" | ||||
| #include "protocols/protocol_ioprox.h" | ||||
| #include <furi_hal.h> | ||||
| #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<uint8_t*>(&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<uint8_t*>(&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<uint32_t*>(&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<uint8_t*>(&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(); | ||||
| } | ||||
| @ -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(); | ||||
| }; | ||||
| @ -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<void(void)> fn) { | ||||
|     list.push_back(std::make_pair(tick_count, fn)); | ||||
|     reset(); | ||||
| } | ||||
| 
 | ||||
| void TickSequencer::do_after_tick(uint32_t tick_count, std::function<void(void)> 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() { | ||||
| } | ||||
| @ -1,25 +0,0 @@ | ||||
| #pragma once | ||||
| #include "stdint.h" | ||||
| #include <list> | ||||
| #include <functional> | ||||
| 
 | ||||
| class TickSequencer { | ||||
| public: | ||||
|     TickSequencer(); | ||||
|     ~TickSequencer(); | ||||
| 
 | ||||
|     void tick(); | ||||
|     void reset(); | ||||
|     void clear(); | ||||
| 
 | ||||
|     void do_every_tick(uint32_t tick_count, std::function<void(void)> fn); | ||||
|     void do_after_tick(uint32_t tick_count, std::function<void(void)> fn); | ||||
| 
 | ||||
| private: | ||||
|     std::list<std::pair<uint32_t, std::function<void(void)> > > list; | ||||
|     std::list<std::pair<uint32_t, std::function<void(void)> > >::iterator list_it; | ||||
| 
 | ||||
|     uint32_t tick_count; | ||||
| 
 | ||||
|     void do_nothing(); | ||||
| }; | ||||
| @ -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 <toolbox/path.h> | ||||
| #include <flipper_format/flipper_format.h> | ||||
| @ -28,24 +33,44 @@ | ||||
| #include <rpc/rpc_app.h> | ||||
| 
 | ||||
| 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"); | ||||
|  | ||||
| @ -20,9 +20,15 @@ | ||||
| #include <storage/storage.h> | ||||
| #include <dialogs/dialogs.h> | ||||
| 
 | ||||
| #include "helpers/rfid_worker.h" | ||||
| #include "rpc/rpc_app.h" | ||||
| 
 | ||||
| #include <toolbox/protocols/protocol_dict.h> | ||||
| #include <lfrfid/lfrfid_dict_file.h> | ||||
| #include <lfrfid/protocols/lfrfid_protocols.h> | ||||
| #include <lfrfid/lfrfid_worker.h> | ||||
| 
 | ||||
| #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> storage; | ||||
|     RecordController<DialogsApp> 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; | ||||
| }; | ||||
|  | ||||
							
								
								
									
										575
									
								
								applications/lfrfid/lfrfid_cli.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										575
									
								
								applications/lfrfid/lfrfid_cli.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,575 @@ | ||||
| #include <furi.h> | ||||
| #include <furi_hal.h> | ||||
| #include <stdarg.h> | ||||
| #include <cli/cli.h> | ||||
| #include <lib/toolbox/args.h> | ||||
| #include <lib/lfrfid/lfrfid_worker.h> | ||||
| #include <storage/storage.h> | ||||
| #include <toolbox/stream/file_stream.h> | ||||
| 
 | ||||
| #include <toolbox/varint.h> | ||||
| 
 | ||||
| #include <toolbox/protocols/protocol_dict.h> | ||||
| #include <lfrfid/protocols/lfrfid_protocols.h> | ||||
| #include <lfrfid/lfrfid_raw_file.h> | ||||
| #include <toolbox/pulse_protocols/pulse_glue.h> | ||||
| 
 | ||||
| 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 <optional: normal | indala>\r\n"); | ||||
|     printf("rfid <write | emulate> <key_type> <key_data>\r\n"); | ||||
|     printf("rfid raw_read <ask | psk> <filename>\r\n"); | ||||
|     printf("rfid raw_emulate <filename>\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(" <FOUND %s>", 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); | ||||
| } | ||||
| @ -1,177 +0,0 @@ | ||||
| #include <furi.h> | ||||
| #include <furi_hal.h> | ||||
| #include <stdarg.h> | ||||
| #include <cli/cli.h> | ||||
| #include <lib/toolbox/args.h> | ||||
| 
 | ||||
| #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<Cli*>(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 <optional: normal | indala>\r\n"); | ||||
|     printf("rfid <write | emulate> <key_type> <key_data>\r\n"); | ||||
|     printf("\t<key_type> choose from:\r\n"); | ||||
|     printf("\tEM4100, EM-Marin (5 bytes key_data)\r\n"); | ||||
|     printf("\tH10301, HID26 (3 bytes key_data)\r\n"); | ||||
|     printf("\tI40134, Indala (3 bytes key_data)\r\n"); | ||||
|     printf("\tIoProxXSF, IoProx (4 bytes key_data)\r\n"); | ||||
|     printf("\t<key_data> 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); | ||||
| } | ||||
| @ -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<ContainerVM>(); | ||||
| @ -21,49 +20,26 @@ void LfRfidAppSceneDeleteConfirm::on_enter(LfRfidApp* app, bool /* need_restore | ||||
|     auto line_1 = container->add<StringElement>(); | ||||
|     auto line_2 = container->add<StringElement>(); | ||||
|     auto line_3 = container->add<StringElement>(); | ||||
|     auto line_4 = container->add<StringElement>(); | ||||
| 
 | ||||
|     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<ContainerVM>()->clean(); | ||||
|     string_clear(string_data); | ||||
|     string_clear(string_decrypted); | ||||
|     string_clear(string_header); | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -13,5 +13,4 @@ private: | ||||
| 
 | ||||
|     string_t string_header; | ||||
|     string_t string_data; | ||||
|     string_t string_decrypted; | ||||
| }; | ||||
|  | ||||
| @ -3,28 +3,21 @@ | ||||
| #include <dolphin/dolphin.h> | ||||
| 
 | ||||
| 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<PopupVM>(); | ||||
| 
 | ||||
|     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<PopupVM>(); | ||||
|     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<PopupVM>()->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); | ||||
| } | ||||
|  | ||||
| @ -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; | ||||
| }; | ||||
|  | ||||
							
								
								
									
										63
									
								
								applications/lfrfid/scene/lfrfid_app_scene_extra_actions.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								applications/lfrfid/scene/lfrfid_app_scene_extra_actions.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -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<SubmenuVM>(); | ||||
| 
 | ||||
|     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<SubmenuVM>(); | ||||
| } | ||||
| 
 | ||||
| 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<SubmenuVM>()->clean(); | ||||
| } | ||||
| 
 | ||||
| void LfRfidAppSceneExtraActions::submenu_callback(void* context, uint32_t index) { | ||||
|     LfRfidApp* app = static_cast<LfRfidApp*>(context); | ||||
|     LfRfidApp::Event event; | ||||
| 
 | ||||
|     event.type = LfRfidApp::EventType::MenuSelected; | ||||
|     event.payload.signed_int = index; | ||||
| 
 | ||||
|     app->view_controller.send_event(&event); | ||||
| } | ||||
							
								
								
									
										13
									
								
								applications/lfrfid/scene/lfrfid_app_scene_extra_actions.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								applications/lfrfid/scene/lfrfid_app_scene_extra_actions.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,13 @@ | ||||
| #pragma once | ||||
| #include "../lfrfid_app.h" | ||||
| 
 | ||||
| class LfRfidAppSceneExtraActions : public GenericScene<LfRfidApp> { | ||||
| 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; | ||||
| }; | ||||
							
								
								
									
										77
									
								
								applications/lfrfid/scene/lfrfid_app_scene_raw_info.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								applications/lfrfid/scene/lfrfid_app_scene_raw_info.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -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<LfRfidApp*>(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<LfRfidApp*>(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<ContainerVM>(); | ||||
| 
 | ||||
|     bool sd_exist = storage_sd_status(app->storage) == FSE_OK; | ||||
|     if(!sd_exist) { | ||||
|         auto icon = container->add<IconElement>(); | ||||
|         icon->set_icon(0, 0, &I_SDQuestion_35x43); | ||||
|         auto line = container->add<StringElement>(); | ||||
|         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<ButtonElement>(); | ||||
|         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<StringElement>(); | ||||
|         line->set_text(string_get_cstr(string_info), 0, 1, 0, AlignLeft, AlignTop, FontSecondary); | ||||
| 
 | ||||
|         auto button = container->add<ButtonElement>(); | ||||
|         button->set_type(ButtonElement::Type::Center, "OK"); | ||||
|         button->set_callback(app, ok_callback); | ||||
|     } | ||||
| 
 | ||||
|     app->view_controller.switch_to<ContainerVM>(); | ||||
| } | ||||
| 
 | ||||
| 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<ContainerVM>()->clean(); | ||||
|     string_clear(string_info); | ||||
| } | ||||
							
								
								
									
										12
									
								
								applications/lfrfid/scene/lfrfid_app_scene_raw_info.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								applications/lfrfid/scene/lfrfid_app_scene_raw_info.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | ||||
| #pragma once | ||||
| #include "../lfrfid_app.h" | ||||
| 
 | ||||
| class LfRfidAppSceneRawInfo : public GenericScene<LfRfidApp> { | ||||
| 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; | ||||
| }; | ||||
							
								
								
									
										46
									
								
								applications/lfrfid/scene/lfrfid_app_scene_raw_name.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								applications/lfrfid/scene/lfrfid_app_scene_raw_name.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,46 @@ | ||||
| 
 | ||||
| #include "lfrfid_app_scene_raw_name.h" | ||||
| #include "m-string.h" | ||||
| #include <lib/toolbox/random_name.h> | ||||
| #include <lib/toolbox/path.h> | ||||
| 
 | ||||
| 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<TextInputVM>(); | ||||
|     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<TextInputVM>(); | ||||
| } | ||||
| 
 | ||||
| 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<TextInputVM>()->clean(); | ||||
| } | ||||
| 
 | ||||
| void LfRfidAppSceneRawName::save_callback(void* context) { | ||||
|     LfRfidApp* app = static_cast<LfRfidApp*>(context); | ||||
|     LfRfidApp::Event event; | ||||
|     event.type = LfRfidApp::EventType::Next; | ||||
|     app->view_controller.send_event(&event); | ||||
| } | ||||
							
								
								
									
										12
									
								
								applications/lfrfid/scene/lfrfid_app_scene_raw_name.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								applications/lfrfid/scene/lfrfid_app_scene_raw_name.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | ||||
| #pragma once | ||||
| #include "../lfrfid_app.h" | ||||
| 
 | ||||
| class LfRfidAppSceneRawName : public GenericScene<LfRfidApp> { | ||||
| 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); | ||||
| }; | ||||
							
								
								
									
										107
									
								
								applications/lfrfid/scene/lfrfid_app_scene_raw_read.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								applications/lfrfid/scene/lfrfid_app_scene_raw_read.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,107 @@ | ||||
| #include "lfrfid_app_scene_raw_read.h" | ||||
| #include <dolphin/dolphin.h> | ||||
| 
 | ||||
| #define RAW_READ_TIME 5000 | ||||
| 
 | ||||
| static void lfrfid_read_callback(LFRFIDWorkerReadRawResult result, void* ctx) { | ||||
|     LfRfidApp* app = static_cast<LfRfidApp*>(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<LfRfidApp*>(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<PopupVM>(); | ||||
|     popup->set_icon(0, 3, &I_RFIDDolphinReceive_97x61); | ||||
|     app->view_controller.switch_to<PopupVM>(); | ||||
|     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<PopupVM>(); | ||||
| 
 | ||||
|     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<PopupVM>()->clean(); | ||||
|     lfrfid_worker_stop(app->lfworker); | ||||
|     lfrfid_worker_stop_thread(app->lfworker); | ||||
|     furi_timer_free(timer); | ||||
|     string_clear(string_file_name); | ||||
| } | ||||
							
								
								
									
										15
									
								
								applications/lfrfid/scene/lfrfid_app_scene_raw_read.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								applications/lfrfid/scene/lfrfid_app_scene_raw_read.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,15 @@ | ||||
| #pragma once | ||||
| #include "../lfrfid_app.h" | ||||
| 
 | ||||
| class LfRfidAppSceneRawRead : public GenericScene<LfRfidApp> { | ||||
| 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; | ||||
| }; | ||||
							
								
								
									
										45
									
								
								applications/lfrfid/scene/lfrfid_app_scene_raw_success.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								applications/lfrfid/scene/lfrfid_app_scene_raw_success.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -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<ContainerVM>(); | ||||
| 
 | ||||
|     auto line = container->add<StringElement>(); | ||||
|     line->set_text(string_get_cstr(string_info), 0, 1, 0, AlignLeft, AlignTop, FontSecondary); | ||||
| 
 | ||||
|     auto button = container->add<ButtonElement>(); | ||||
|     button->set_type(ButtonElement::Type::Center, "OK"); | ||||
|     button->set_callback(app, LfRfidAppSceneRawSuccess::ok_callback); | ||||
| 
 | ||||
|     app->view_controller.switch_to<ContainerVM>(); | ||||
| } | ||||
| 
 | ||||
| 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<ContainerVM>()->clean(); | ||||
|     string_clear(string_info); | ||||
| } | ||||
| 
 | ||||
| void LfRfidAppSceneRawSuccess::ok_callback(void* context) { | ||||
|     LfRfidApp* app = static_cast<LfRfidApp*>(context); | ||||
|     LfRfidApp::Event event; | ||||
|     event.type = LfRfidApp::EventType::Next; | ||||
|     app->view_controller.send_event(&event); | ||||
| } | ||||
							
								
								
									
										13
									
								
								applications/lfrfid/scene/lfrfid_app_scene_raw_success.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								applications/lfrfid/scene/lfrfid_app_scene_raw_success.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,13 @@ | ||||
| #pragma once | ||||
| #include "../lfrfid_app.h" | ||||
| 
 | ||||
| class LfRfidAppSceneRawSuccess : public GenericScene<LfRfidApp> { | ||||
| 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); | ||||
| }; | ||||
| @ -1,40 +1,100 @@ | ||||
| #include "lfrfid_app_scene_read.h" | ||||
| #include <dolphin/dolphin.h> | ||||
| 
 | ||||
| static void lfrfid_read_callback(LFRFIDWorkerReadResult result, ProtocolId protocol, void* ctx) { | ||||
|     LfRfidApp* app = static_cast<LfRfidApp*>(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<PopupVM>(); | ||||
| 
 | ||||
|     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<PopupVM>(); | ||||
|     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<PopupVM>(); | ||||
| 
 | ||||
|     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<PopupVM>()->clean(); | ||||
|     app->worker.stop_read(); | ||||
|     lfrfid_worker_stop(app->lfworker); | ||||
|     lfrfid_worker_stop_thread(app->lfworker); | ||||
| } | ||||
|  | ||||
| @ -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); | ||||
| } | ||||
|  | ||||
| @ -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<ContainerVM>(); | ||||
| 
 | ||||
| @ -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<IconElement>(); | ||||
|     icon->set_icon(3, 12, &I_RFIDBigChip_37x36); | ||||
| 
 | ||||
|     auto header = container->add<StringElement>(); | ||||
|     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<StringElement>(); | ||||
|     auto line_2l_text = container->add<StringElement>(); | ||||
|     auto line_2r_text = container->add<StringElement>(); | ||||
|     auto line_3_text = container->add<StringElement>(); | ||||
| 
 | ||||
|     auto line_1_value = container->add<StringElement>(); | ||||
|     auto line_2l_value = container->add<StringElement>(); | ||||
|     auto line_2r_value = container->add<StringElement>(); | ||||
|     auto line_3_value = container->add<StringElement>(); | ||||
| 
 | ||||
|     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<StringElement>(); | ||||
|     text->set_text(string_get_cstr(string_info), 0, 16, 0, AlignLeft, AlignTop, FontSecondary); | ||||
| 
 | ||||
|     app->view_controller.switch_to<ContainerVM>(); | ||||
| 
 | ||||
| @ -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<ContainerVM>()->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) { | ||||
|  | ||||
| @ -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; | ||||
| }; | ||||
|  | ||||
| @ -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<PopupVM>(); | ||||
|                 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<PopupVM>()->clean(); | ||||
|  | ||||
| @ -3,31 +3,29 @@ | ||||
| 
 | ||||
| void LfRfidAppSceneSaveData::on_enter(LfRfidApp* app, bool need_restore) { | ||||
|     auto byte_input = app->view_controller.get<ByteInputVM>(); | ||||
|     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<ByteInputVM>(); | ||||
| } | ||||
| 
 | ||||
| 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); | ||||
|     } | ||||
|  | ||||
| @ -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, | ||||
|     }; | ||||
| }; | ||||
|  | ||||
| @ -4,9 +4,9 @@ | ||||
| #include <lib/toolbox/path.h> | ||||
| 
 | ||||
| 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( | ||||
|  | ||||
| @ -3,12 +3,12 @@ | ||||
| void LfRfidAppSceneSaveType::on_enter(LfRfidApp* app, bool need_restore) { | ||||
|     auto submenu = app->view_controller.get<SubmenuVM>(); | ||||
| 
 | ||||
|     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<LfrfidKeyType>(i)), | ||||
|             lfrfid_key_get_type_string(static_cast<LfrfidKeyType>(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<SubmenuVM>(); | ||||
| 
 | ||||
|     // 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<LfrfidKeyType>(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<SubmenuVM>()->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); | ||||
| } | ||||
|  | ||||
| @ -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<uint8_t>(LfrfidKeyType::KeyIoProxXSF); | ||||
|     string_t submenu_name[keys_count + 1]; | ||||
|     static const uint8_t keys_count = static_cast<uint8_t>(LFRFIDProtocol::LFRFIDProtocolMax); | ||||
|     string_t submenu_name[keys_count]; | ||||
| }; | ||||
|  | ||||
| @ -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<ContainerVM>(); | ||||
| 
 | ||||
|     auto button = container->add<ButtonElement>(); | ||||
|     button->set_type(ButtonElement::Type::Left, "Back"); | ||||
|     button->set_callback(app, LfRfidAppSceneSavedInfo::back_callback); | ||||
| 
 | ||||
|     auto line_1 = container->add<StringElement>(); | ||||
|     auto line_2 = container->add<StringElement>(); | ||||
|     auto line_3 = container->add<StringElement>(); | ||||
|     auto line_4 = container->add<StringElement>(); | ||||
| 
 | ||||
|     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<ContainerVM>(); | ||||
| } | ||||
| @ -73,13 +44,5 @@ bool LfRfidAppSceneSavedInfo::on_event(LfRfidApp* /* app */, LfRfidApp::Event* / | ||||
| 
 | ||||
| void LfRfidAppSceneSavedInfo::on_exit(LfRfidApp* app) { | ||||
|     app->view_controller.get<ContainerVM>()->clean(); | ||||
|     string_clear(string_data); | ||||
|     string_clear(string_decrypted); | ||||
| } | ||||
| 
 | ||||
| void LfRfidAppSceneSavedInfo::back_callback(void* context) { | ||||
|     LfRfidApp* app = static_cast<LfRfidApp*>(context); | ||||
|     LfRfidApp::Event event; | ||||
|     event.type = LfRfidApp::EventType::Back; | ||||
|     app->view_controller.send_event(&event); | ||||
|     string_clear(string_info); | ||||
| } | ||||
|  | ||||
| @ -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; | ||||
| }; | ||||
|  | ||||
| @ -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); | ||||
| } | ||||
|  | ||||
| @ -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<SubmenuVM>(); | ||||
| 
 | ||||
|     // 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); | ||||
| } | ||||
|  | ||||
| @ -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<LfRfidApp*>(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<PopupVM>(); | ||||
| 
 | ||||
|     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<PopupVM>(); | ||||
|     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<PopupVM>(); | ||||
| 
 | ||||
|     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<PopupVM>(); | ||||
|                 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<PopupVM>()->clean(); | ||||
|     app->worker.stop_write(); | ||||
|     string_clear(data_string); | ||||
|     lfrfid_worker_stop(app->lfworker); | ||||
|     lfrfid_worker_stop_thread(app->lfworker); | ||||
| } | ||||
|  | ||||
| @ -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; | ||||
| }; | ||||
|  | ||||
| @ -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
 | ||||
|  | ||||
| @ -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) { | ||||
|  | ||||
							
								
								
									
										473
									
								
								applications/unit_tests/lfrfid/bit_lib_test.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										473
									
								
								applications/unit_tests/lfrfid/bit_lib_test.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,473 @@ | ||||
| #include <furi.h> | ||||
| #include "../minunit.h" | ||||
| #include <lfrfid/tools/bit_lib.h> | ||||
| 
 | ||||
| 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; | ||||
| } | ||||
							
								
								
									
										464
									
								
								applications/unit_tests/lfrfid/lfrfid_protocols.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										464
									
								
								applications/unit_tests/lfrfid/lfrfid_protocols.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,464 @@ | ||||
| #include <furi.h> | ||||
| #include "../minunit.h" | ||||
| #include <toolbox/protocols/protocol_dict.h> | ||||
| #include <lfrfid/protocols/lfrfid_protocols.h> | ||||
| #include <toolbox/pulse_protocols/pulse_glue.h> | ||||
| 
 | ||||
| #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; | ||||
| } | ||||
| @ -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;                                                                                  \ | ||||
|         }) | ||||
| 
 | ||||
| /*
 | ||||
|  | ||||
							
								
								
									
										222
									
								
								applications/unit_tests/protocol_dict/protocol_dict_test.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										222
									
								
								applications/unit_tests/protocol_dict/protocol_dict_test.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,222 @@ | ||||
| #include <furi.h> | ||||
| #include "../minunit.h" | ||||
| #include <toolbox/protocols/protocol_dict.h> | ||||
| 
 | ||||
| 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; | ||||
| } | ||||
| @ -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() { | ||||
|  | ||||
							
								
								
									
										88
									
								
								applications/unit_tests/varint/varint_test.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								applications/unit_tests/varint/varint_test.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,88 @@ | ||||
| #include <furi.h> | ||||
| #include <furi_hal.h> | ||||
| #include "../minunit.h" | ||||
| #include <toolbox/varint.h> | ||||
| #include <toolbox/profiler.h> | ||||
| 
 | ||||
| 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; | ||||
| } | ||||
| @ -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; | ||||
| } | ||||
|  | ||||
| @ -200,6 +200,7 @@ fwelf = fwenv["FW_ELF"] = fwenv.Program( | ||||
|         "misc", | ||||
|         "mbedtls", | ||||
|         "loclass", | ||||
|         "lfrfid", | ||||
|         # 2nd round | ||||
|         "flipperformat", | ||||
|         "toolbox", | ||||
|  | ||||
| @ -6,6 +6,7 @@ | ||||
| 
 | ||||
| #include <stm32wbxx_ll_tim.h> | ||||
| #include <stm32wbxx_ll_comp.h> | ||||
| #include <stm32wbxx_ll_dma.h> | ||||
| 
 | ||||
| #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(); | ||||
| 
 | ||||
|  | ||||
| @ -7,6 +7,7 @@ | ||||
| 
 | ||||
| #include <stdint.h> | ||||
| #include <stdbool.h> | ||||
| #include <stddef.h> | ||||
| 
 | ||||
| #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(); | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
| @ -77,6 +77,7 @@ libs = env.BuildModules( | ||||
|         "appframe", | ||||
|         "misc", | ||||
|         "loclass", | ||||
|         "lfrfid", | ||||
|     ], | ||||
| ) | ||||
| 
 | ||||
|  | ||||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 SG
						SG