diff --git a/.drone.yml b/.drone.yml index 7499ccbee..15c5dfce1 100644 --- a/.drone.yml +++ b/.drone.yml @@ -44,6 +44,7 @@ steps: - cp assets/resources/nfc/assets/mf_classic_dict.nfc sd-card/nfc/assets/mf_classic_dict.nfc - cp assets/resources/infrared/assets/tv.ir sd-card/infrared/assets/tv.ir - cp assets/resources/infrared/assets/ac.ir sd-card/infrared/assets/ac.ir + - cp assets/resources/infrared/assets/projectors.ir sd-card/infrared/assets/projectors.ir - cp assets/resources/infrared/assets/audio.ir sd-card/infrared/assets/audio.ir - cp assets/resources/unirf/unirf_map_example.txt sd-card/unirf/unirf_map_example.txt - cp assets/resources/Manifest sd-card/Manifest diff --git a/.gitignore b/.gitignore index a2e0be12d..8f4ab0e1f 100644 --- a/.gitignore +++ b/.gitignore @@ -51,3 +51,7 @@ build/ # openocd output file openocd.log + +# PVS Studio temporary files +.PVS-Studio/ +PVS-Studio.log diff --git a/.pvsconfig b/.pvsconfig new file mode 100644 index 000000000..d17eaa5a0 --- /dev/null +++ b/.pvsconfig @@ -0,0 +1,22 @@ +# MLib macros we can't do much about. +//-V:M_EACH:1048,1044 +//-V:ARRAY_DEF:760,747,568,776,729,712,654 +//-V:LIST_DEF:760,747,568,712,729,654,776 +//-V:BPTREE_DEF2:779,1086,557,773,512 +//-V:DICT_DEF2:779,524,776,760,1044,1001,729,590,568,747,685 +//-V:ALGO_DEF:1048,747,1044 + +# Non-severe malloc/null pointer deref warnings +//-V::522:2,3 + +# Warning about headers with copyleft license +//-V::1042 + +# Potentially null argument warnings +//-V:memset:575 +//-V:memcpy:575 +//-V:strcpy:575 +//-V:strchr:575 + +# For loop warning on M_FOREACH +//-V:for:1044 diff --git a/.pvsoptions b/.pvsoptions new file mode 100644 index 000000000..4c80ab667 --- /dev/null +++ b/.pvsoptions @@ -0,0 +1 @@ +--rules-config .pvsconfig -e lib/fatfs -e lib/fnv1a-hash -e lib/FreeRTOS-Kernel -e lib/heatshrink -e lib/libusb_stm32 -e lib/littlefs -e lib/mbedtls -e lib/micro-ecc -e lib/microtar -e lib/mlib -e lib/qrcode -e lib/ST25RFAL002 -e lib/STM32CubeWB -e lib/u8g2 -e */arm-none-eabi/* diff --git a/CHANGELOG.md b/CHANGELOG.md index 88ad7cdba..64ed851a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,13 +1,23 @@ ### New changes -* Wifi Marauder app update (by @0xchocolate) -* Updated Universal remote assets (by @Amec0e) -* Fixed music player -* Fixed typos in subghz encoders -* OFW: New NFC info screens -* OFW: U2F fixes +* New universal remote for projectors +* OFW: New LF-RFID subsystem (New protocols, Animal tags support) +* Updated universal remote assets (by @Amec0e) +* Renamed UniRF Remix -> Sub-GHz Remote +* Replaced Hex/Dec converter with Multi Converter plugin [(by theisolinearchip)](https://github.com/theisolinearchip/flipperzero_stuff) +* New update screen, readme pictures (by @Svaarich) +* Fixed crash if Center button is pressed on the "update success" screen via screensharing +* Temporary disabled one log call in picopass plugin to fix crash/freeze on Read screen +* OFW: Picopass load/info/delete +* OFW: SubGhz: add protocol Magellen +* OFW: Fix mifare ultralight/ntag unlock +* OFW: Dolphin level thresholds update +* OFW: Add MFC 1/4K 4/7bUID to "Add Manually" +* OFW: Other fixes and changes **Note: Prefer installing using web updater or by self update package, all needed assets will be installed** +**Build naming has been changed - all same as before but `cg - codegrabber` changed to `un - unleashed`** + Self-update package (update from microSD) - `flipper-z-f7-update-(version).zip` DFU for update using qFlipper - `flipper-z-f7-full-(version).dfu` diff --git a/ReadMe.md b/ReadMe.md index 927369e07..b9cc9e257 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -1,6 +1,8 @@ -# Flipper Zero Unleashed Firmware - -fzCUSTOM +

+ +fzCUSTOM + +

Welcome to Flipper Zero's Custom Firmware repo! Our goal is to make any features possible in this device without any limitations! @@ -30,6 +32,9 @@ Our Discord Community: * Picopass/iClass plugin included in releases * Recompiled IR TV Universal Remote for ALL buttons * Universal A/C and Audio(soundbars, etc.) remote +* Universal remote for Projectors +* BadUSB keyboard layouts +* Customizable Flipper name * Other small fixes and changes throughout See changelog in releases for latest updates! @@ -57,13 +62,12 @@ See changelog in releases for latest updates! - ESP8266 Deauther plugin [(by SequoiaSan)](https://github.com/SequoiaSan/FlipperZero-Wifi-ESP8266-Deauther-Module) - WiFi Scanner plugin [(by SequoiaSan)](https://github.com/SequoiaSan/FlipperZero-WiFi-Scanner_Module) -- Dec/Hex Converter plugin [(by theisolinearchip)](https://github.com/theisolinearchip/flipperzero_stuff/tree/main/applications/dec_hex_converter) +- MultiConverter plugin [(by theisolinearchip)](https://github.com/theisolinearchip/flipperzero_stuff) - WAV player plugin (fixed) [(OFW: DrZlo13)](https://github.com/flipperdevices/flipperzero-firmware/tree/zlo/wav-player) - UPC-A Barcode generator plugin [(by McAzzaMan)](https://github.com/McAzzaMan/flipperzero-firmware/tree/UPC-A_Barcode_Generator/applications/barcode_generator) - GPIO: Sentry Safe plugin [(by H4ckd4ddy)](https://github.com/H4ckd4ddy/flipperzero-sentry-safe-plugin) - ESP32: WiFi Marauder companion plugin [(by 0xchocolate)](https://github.com/0xchocolate/flipperzero-firmware-with-wifi-marauder-companion) - NRF24: Sniffer & MouseJacker (with changes) [(by mothball187)](https://github.com/mothball187/flipperzero-nrf24/tree/main/mousejacker) -- HID Analyzer [(by Ownasaurus)](https://github.com/Ownasaurus/flipperzero-firmware/tree/hid-analyzer/applications/hid_analyzer) - Simple Clock (fixed) !! New version WIP, wait for updates !! [(Original by CompaqDisc)](https://gist.github.com/CompaqDisc/4e329c501bd03c1e801849b81f48ea61) - UniversalRF Remix (with changes)(only RAW subghz files) [(by ESurge)(Original UniversalRF by jimilinuxguy)](https://github.com/ESurge/flipperzero-firmware-unirfremix) - Tetris (with fixes) [(by jeffplang)](https://github.com/jeffplang/flipperzero-firmware/tree/tetris_game/applications/tetris_game) @@ -87,10 +91,12 @@ See changelog in releases for latest updates! ### **Plugins** -## [- Configure UniversalRF Remix App](https://github.com/Eng1n33r/flipperzero-firmware/blob/dev/documentation/UniRFRemix.md) +## [- Configure Sub-GHz Remote App](https://github.com/Eng1n33r/flipperzero-firmware/blob/dev/documentation/SubGHzRemotePlugin.md) ## [- Barcode Generator](https://github.com/Eng1n33r/flipperzero-firmware/blob/dev/documentation/BarcodeGenerator.md) +## [- Multi Converter](https://github.com/Eng1n33r/flipperzero-firmware/blob/dev/documentation/MultiConverter.md) + ## [- WAV Player sample files & how to convert](https://github.com/UberGuidoZ/Flipper/tree/main/Wav_Player#readme) ### **Plugins that works with external hardware** @@ -142,7 +148,6 @@ See changelog in releases for latest updates! - `assets` - Assets used by applications and services - `furi` - Furi Core: os level primitives and helpers - `debug` - Debug tool: GDB-plugins, SVD-file and etc -- `docker` - Docker image sources (used for firmware build automation) - `documentation` - Documentation generation system configs and input files - `firmware` - Firmware source code - `lib` - Our and 3rd party libraries, drivers and etc... diff --git a/applications/archive/helpers/archive_apps.c b/applications/archive/helpers/archive_apps.c index 9a3f825f1..72084f113 100644 --- a/applications/archive/helpers/archive_apps.c +++ b/applications/archive/helpers/archive_apps.c @@ -29,23 +29,13 @@ bool archive_app_is_available(void* context, const char* path) { if(app == ArchiveAppTypeU2f) { bool file_exists = false; - Storage* fs_api = furi_record_open(RECORD_STORAGE); - File* file = storage_file_alloc(fs_api); + Storage* storage = furi_record_open(RECORD_STORAGE); - file_exists = - storage_file_open(file, ANY_PATH("u2f/key.u2f"), FSAM_READ, FSOM_OPEN_EXISTING); - if(file_exists) { - storage_file_close(file); - file_exists = - storage_file_open(file, ANY_PATH("u2f/cnt.u2f"), FSAM_READ, FSOM_OPEN_EXISTING); - if(file_exists) { - storage_file_close(file); - } + if(storage_file_exists(storage, ANY_PATH("u2f/key.u2f"))) { + file_exists = storage_file_exists(storage, ANY_PATH("u2f/cnt.u2f")); } - storage_file_free(file); furi_record_close(RECORD_STORAGE); - return file_exists; } else { return false; diff --git a/applications/archive/helpers/archive_browser.c b/applications/archive/helpers/archive_browser.c index 54759dadc..2dfb9484b 100644 --- a/applications/archive/helpers/archive_browser.c +++ b/applications/archive/helpers/archive_browser.c @@ -77,14 +77,24 @@ static void archive_long_load_cb(void* context) { }); } -void archive_file_browser_set_callbacks(ArchiveBrowserView* browser) { +static void archive_file_browser_set_path( + ArchiveBrowserView* browser, + string_t path, + const char* filter_ext, + bool skip_assets) { furi_assert(browser); - - file_browser_worker_set_callback_context(browser->worker, browser); - file_browser_worker_set_folder_callback(browser->worker, archive_folder_open_cb); - file_browser_worker_set_list_callback(browser->worker, archive_list_load_cb); - file_browser_worker_set_item_callback(browser->worker, archive_list_item_cb); - file_browser_worker_set_long_load_callback(browser->worker, archive_long_load_cb); + if(!browser->worker_running) { + browser->worker = file_browser_worker_alloc(path, filter_ext, skip_assets); + file_browser_worker_set_callback_context(browser->worker, browser); + file_browser_worker_set_folder_callback(browser->worker, archive_folder_open_cb); + file_browser_worker_set_list_callback(browser->worker, archive_list_load_cb); + file_browser_worker_set_item_callback(browser->worker, archive_list_item_cb); + file_browser_worker_set_long_load_callback(browser->worker, archive_long_load_cb); + browser->worker_running = true; + } else { + furi_assert(browser->worker); + file_browser_worker_set_config(browser->worker, path, filter_ext, skip_assets); + } } bool archive_is_item_in_array(ArchiveBrowserViewModel* model, uint32_t idx) { @@ -438,8 +448,8 @@ void archive_switch_tab(ArchiveBrowserView* browser, InputKey key) { tab = archive_get_tab(browser); if(archive_is_dir_exists(browser->path)) { bool skip_assets = (strcmp(archive_get_tab_ext(tab), "*") == 0) ? false : true; - file_browser_worker_set_config( - browser->worker, browser->path, archive_get_tab_ext(tab), skip_assets); + archive_file_browser_set_path( + browser, browser->path, archive_get_tab_ext(tab), skip_assets); tab_empty = false; // Empty check will be performed later } else { tab_empty = true; diff --git a/applications/archive/helpers/archive_browser.h b/applications/archive/helpers/archive_browser.h index d6c79817a..ad64a9845 100644 --- a/applications/archive/helpers/archive_browser.h +++ b/applications/archive/helpers/archive_browser.h @@ -87,4 +87,3 @@ void archive_switch_tab(ArchiveBrowserView* browser, InputKey key); void archive_enter_dir(ArchiveBrowserView* browser, string_t name); void archive_leave_dir(ArchiveBrowserView* browser); void archive_refresh_dir(ArchiveBrowserView* browser); -void archive_file_browser_set_callbacks(ArchiveBrowserView* browser); diff --git a/applications/archive/helpers/archive_favorites.c b/applications/archive/helpers/archive_favorites.c index 35199242e..4d5b555f5 100644 --- a/applications/archive/helpers/archive_favorites.c +++ b/applications/archive/helpers/archive_favorites.c @@ -82,9 +82,8 @@ uint16_t archive_favorites_count(void* context) { static bool archive_favourites_rescan() { string_t buffer; string_init(buffer); - Storage* fs_api = furi_record_open(RECORD_STORAGE); - File* file = storage_file_alloc(fs_api); - File* fav_item_file = storage_file_alloc(fs_api); + Storage* storage = furi_record_open(RECORD_STORAGE); + File* file = storage_file_alloc(storage); bool result = storage_file_open(file, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING); if(result) { @@ -101,13 +100,8 @@ static bool archive_favourites_rescan() { archive_file_append(ARCHIVE_FAV_TEMP_PATH, "%s\n", string_get_cstr(buffer)); } } else { - bool file_exists = storage_file_open( - fav_item_file, string_get_cstr(buffer), FSAM_READ, FSOM_OPEN_EXISTING); - if(file_exists) { - storage_file_close(fav_item_file); + if(storage_file_exists(storage, string_get_cstr(buffer))) { archive_file_append(ARCHIVE_FAV_TEMP_PATH, "%s\n", string_get_cstr(buffer)); - } else { - storage_file_close(fav_item_file); } } } @@ -116,12 +110,11 @@ static bool archive_favourites_rescan() { string_clear(buffer); storage_file_close(file); - storage_common_remove(fs_api, ARCHIVE_FAV_PATH); - storage_common_rename(fs_api, ARCHIVE_FAV_TEMP_PATH, ARCHIVE_FAV_PATH); - storage_common_remove(fs_api, ARCHIVE_FAV_TEMP_PATH); + storage_common_remove(storage, ARCHIVE_FAV_PATH); + storage_common_rename(storage, ARCHIVE_FAV_TEMP_PATH, ARCHIVE_FAV_PATH); + storage_common_remove(storage, ARCHIVE_FAV_TEMP_PATH); storage_file_free(file); - storage_file_free(fav_item_file); furi_record_close(RECORD_STORAGE); return result; @@ -131,9 +124,8 @@ bool archive_favorites_read(void* context) { furi_assert(context); ArchiveBrowserView* browser = context; - Storage* fs_api = furi_record_open(RECORD_STORAGE); - File* file = storage_file_alloc(fs_api); - File* fav_item_file = storage_file_alloc(fs_api); + Storage* storage = furi_record_open(RECORD_STORAGE); + File* file = storage_file_alloc(storage); string_t buffer; FileInfo file_info; @@ -163,16 +155,12 @@ bool archive_favorites_read(void* context) { need_refresh = true; } } else { - bool file_exists = storage_file_open( - fav_item_file, string_get_cstr(buffer), FSAM_READ, FSOM_OPEN_EXISTING); - if(file_exists) { - storage_common_stat(fs_api, string_get_cstr(buffer), &file_info); - storage_file_close(fav_item_file); + if(storage_file_exists(storage, string_get_cstr(buffer))) { + storage_common_stat(storage, string_get_cstr(buffer), &file_info); archive_add_file_item( browser, (file_info.flags & FSF_DIRECTORY), string_get_cstr(buffer)); file_count++; } else { - storage_file_close(fav_item_file); need_refresh = true; } } @@ -183,7 +171,6 @@ bool archive_favorites_read(void* context) { storage_file_close(file); string_clear(buffer); storage_file_free(file); - storage_file_free(fav_item_file); furi_record_close(RECORD_STORAGE); archive_set_item_count(browser, file_count); diff --git a/applications/archive/views/archive_browser_view.c b/applications/archive/views/archive_browser_view.c index 810d5c8f7..174071ad4 100644 --- a/applications/archive/views/archive_browser_view.c +++ b/applications/archive/views/archive_browser_view.c @@ -370,18 +370,15 @@ ArchiveBrowserView* browser_alloc() { return true; }); - browser->worker = file_browser_worker_alloc(browser->path, "*", false); - archive_file_browser_set_callbacks(browser); - - file_browser_worker_set_callback_context(browser->worker, browser); - return browser; } void browser_free(ArchiveBrowserView* browser) { furi_assert(browser); - file_browser_worker_free(browser->worker); + if(browser->worker_running) { + file_browser_worker_free(browser->worker); + } with_view_model( browser->view, (ArchiveBrowserViewModel * model) { diff --git a/applications/archive/views/archive_browser_view.h b/applications/archive/views/archive_browser_view.h index 2de04a166..5c649c389 100644 --- a/applications/archive/views/archive_browser_view.h +++ b/applications/archive/views/archive_browser_view.h @@ -74,6 +74,7 @@ typedef enum { struct ArchiveBrowserView { View* view; BrowserWorker* worker; + bool worker_running; ArchiveBrowserViewCallback callback; void* context; string_t path; diff --git a/applications/bt/bt_cli.c b/applications/bt/bt_cli.c index 3aa1bc752..79500fac4 100644 --- a/applications/bt/bt_cli.c +++ b/applications/bt/bt_cli.c @@ -3,7 +3,7 @@ #include #include -#include "ble.h" +#include #include "bt_settings.h" #include "bt_service/bt.h" diff --git a/applications/bt/bt_hid_app/bt_hid.c b/applications/bt/bt_hid_app/bt_hid.c old mode 100755 new mode 100644 index b6f00e939..0827bd0ad --- a/applications/bt/bt_hid_app/bt_hid.c +++ b/applications/bt/bt_hid_app/bt_hid.c @@ -89,8 +89,7 @@ BtHid* bt_hid_app_alloc() { app->submenu, "Keynote", BtHidSubmenuIndexKeynote, bt_hid_submenu_callback, app); submenu_add_item( app->submenu, "Keyboard", BtHidSubmenuIndexKeyboard, bt_hid_submenu_callback, app); - submenu_add_item( - app->submenu, "Media Player", BtHidSubmenuIndexMedia, bt_hid_submenu_callback, app); + submenu_add_item(app->submenu, "Media", BtHidSubmenuIndexMedia, bt_hid_submenu_callback, app); submenu_add_item(app->submenu, "Mouse", BtHidSubmenuIndexMouse, bt_hid_submenu_callback, app); view_set_previous_callback(submenu_get_view(app->submenu), bt_hid_exit); view_dispatcher_add_view( @@ -134,7 +133,8 @@ BtHid* bt_hid_app_alloc() { app->view_dispatcher, BtHidViewMouse, bt_hid_mouse_get_view(app->bt_hid_mouse)); // TODO switch to menu after Media is done - view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewKeynote); + app->view_id = BtHidViewSubmenu; + view_dispatcher_switch_to_view(app->view_dispatcher, app->view_id); return app; } diff --git a/applications/bt/bt_hid_app/views/bt_hid_keynote.c b/applications/bt/bt_hid_app/views/bt_hid_keynote.c index 60a1ebc08..ea4ee16fa 100755 --- a/applications/bt/bt_hid_app/views/bt_hid_keynote.c +++ b/applications/bt/bt_hid_app/views/bt_hid_keynote.c @@ -43,7 +43,10 @@ static void bt_hid_keynote_draw_callback(Canvas* canvas, void* context) { } canvas_set_font(canvas, FontPrimary); elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Keynote"); + + canvas_draw_icon(canvas, 68, 2, &I_Pin_back_arrow_10x8); canvas_set_font(canvas, FontSecondary); + elements_multiline_text_aligned(canvas, 127, 3, AlignRight, AlignTop, "Hold to exit"); // Up canvas_draw_icon(canvas, 21, 24, &I_Button_18x18); @@ -97,8 +100,8 @@ static void bt_hid_keynote_draw_callback(Canvas* canvas, void* context) { elements_slightly_rounded_box(canvas, 66, 47, 60, 13); canvas_set_color(canvas, ColorWhite); } - canvas_draw_icon(canvas, 110, 49, &I_Ok_btn_9x9); - elements_multiline_text_aligned(canvas, 76, 56, AlignLeft, AlignBottom, "Back"); + canvas_draw_icon(canvas, 74, 49, &I_Pin_back_arrow_10x8); + elements_multiline_text_aligned(canvas, 91, 57, AlignLeft, AlignBottom, "Back"); } static void bt_hid_keynote_process(BtHidKeynote* bt_hid_keynote, InputEvent* event) { diff --git a/applications/bt/bt_hid_app/views/bt_hid_media.c b/applications/bt/bt_hid_app/views/bt_hid_media.c index b384f47cf..258ea0a40 100755 --- a/applications/bt/bt_hid_app/views/bt_hid_media.c +++ b/applications/bt/bt_hid_app/views/bt_hid_media.c @@ -49,7 +49,9 @@ static void bt_hid_media_draw_callback(Canvas* canvas, void* context) { // Up if(model->up_pressed) { + canvas_set_bitmap_mode(canvas, 1); canvas_draw_icon(canvas, 93, 9, &I_Pressed_Button_13x13); + canvas_set_bitmap_mode(canvas, 0); canvas_set_color(canvas, ColorWhite); } canvas_draw_icon(canvas, 96, 12, &I_Volup_8x6); @@ -57,7 +59,9 @@ static void bt_hid_media_draw_callback(Canvas* canvas, void* context) { // Down if(model->down_pressed) { + canvas_set_bitmap_mode(canvas, 1); canvas_draw_icon(canvas, 93, 41, &I_Pressed_Button_13x13); + canvas_set_bitmap_mode(canvas, 0); canvas_set_color(canvas, ColorWhite); } canvas_draw_icon(canvas, 96, 45, &I_Voldwn_6x6); @@ -65,7 +69,9 @@ static void bt_hid_media_draw_callback(Canvas* canvas, void* context) { // Left if(model->left_pressed) { + canvas_set_bitmap_mode(canvas, 1); canvas_draw_icon(canvas, 77, 25, &I_Pressed_Button_13x13); + canvas_set_bitmap_mode(canvas, 0); canvas_set_color(canvas, ColorWhite); } bt_hid_media_draw_arrow(canvas, 82, 31, CanvasDirectionRightToLeft); @@ -74,7 +80,9 @@ static void bt_hid_media_draw_callback(Canvas* canvas, void* context) { // Right if(model->right_pressed) { + canvas_set_bitmap_mode(canvas, 1); canvas_draw_icon(canvas, 109, 25, &I_Pressed_Button_13x13); + canvas_set_bitmap_mode(canvas, 0); canvas_set_color(canvas, ColorWhite); } bt_hid_media_draw_arrow(canvas, 112, 31, CanvasDirectionLeftToRight); @@ -89,6 +97,12 @@ static void bt_hid_media_draw_callback(Canvas* canvas, void* context) { bt_hid_media_draw_arrow(canvas, 96, 31, CanvasDirectionLeftToRight); canvas_draw_line(canvas, 100, 29, 100, 33); canvas_draw_line(canvas, 102, 29, 102, 33); + canvas_set_color(canvas, ColorBlack); + + // Exit + canvas_draw_icon(canvas, 0, 54, &I_Pin_back_arrow_10x8); + canvas_set_font(canvas, FontSecondary); + elements_multiline_text_aligned(canvas, 13, 62, AlignLeft, AlignBottom, "Hold to exit"); } static void bt_hid_media_process_press(BtHidMedia* bt_hid_media, InputEvent* event) { diff --git a/applications/bt/bt_hid_app/views/bt_hid_mouse.c b/applications/bt/bt_hid_app/views/bt_hid_mouse.c index fb1537a2c..f9d84f9fb 100644 --- a/applications/bt/bt_hid_app/views/bt_hid_mouse.c +++ b/applications/bt/bt_hid_app/views/bt_hid_mouse.c @@ -36,7 +36,11 @@ static void bt_hid_mouse_draw_callback(Canvas* canvas, void* context) { canvas_set_font(canvas, FontSecondary); if(model->left_mouse_held == true) { - elements_multiline_text_aligned(canvas, 0, 60, AlignLeft, AlignBottom, "Selecting..."); + elements_multiline_text_aligned(canvas, 0, 62, AlignLeft, AlignBottom, "Selecting..."); + } else { + canvas_draw_icon(canvas, 0, 54, &I_Pin_back_arrow_10x8); + canvas_set_font(canvas, FontSecondary); + elements_multiline_text_aligned(canvas, 13, 62, AlignLeft, AlignBottom, "Hold to exit"); } // Keypad circles @@ -44,7 +48,9 @@ static void bt_hid_mouse_draw_callback(Canvas* canvas, void* context) { // Up if(model->up_pressed) { + canvas_set_bitmap_mode(canvas, 1); canvas_draw_icon(canvas, 81, 9, &I_Pressed_Button_13x13); + canvas_set_bitmap_mode(canvas, 0); canvas_set_color(canvas, ColorWhite); } canvas_draw_icon(canvas, 84, 10, &I_Pin_arrow_up7x9); @@ -52,7 +58,9 @@ static void bt_hid_mouse_draw_callback(Canvas* canvas, void* context) { // Down if(model->down_pressed) { + canvas_set_bitmap_mode(canvas, 1); canvas_draw_icon(canvas, 81, 41, &I_Pressed_Button_13x13); + canvas_set_bitmap_mode(canvas, 0); canvas_set_color(canvas, ColorWhite); } canvas_draw_icon(canvas, 84, 43, &I_Pin_arrow_down_7x9); @@ -60,7 +68,9 @@ static void bt_hid_mouse_draw_callback(Canvas* canvas, void* context) { // Left if(model->left_pressed) { + canvas_set_bitmap_mode(canvas, 1); canvas_draw_icon(canvas, 65, 25, &I_Pressed_Button_13x13); + canvas_set_bitmap_mode(canvas, 0); canvas_set_color(canvas, ColorWhite); } canvas_draw_icon(canvas, 67, 28, &I_Pin_arrow_left_9x7); @@ -68,7 +78,9 @@ static void bt_hid_mouse_draw_callback(Canvas* canvas, void* context) { // Right if(model->right_pressed) { + canvas_set_bitmap_mode(canvas, 1); canvas_draw_icon(canvas, 97, 25, &I_Pressed_Button_13x13); + canvas_set_bitmap_mode(canvas, 0); canvas_set_color(canvas, ColorWhite); } canvas_draw_icon(canvas, 99, 28, &I_Pin_arrow_right_9x7); @@ -76,18 +88,17 @@ static void bt_hid_mouse_draw_callback(Canvas* canvas, void* context) { // Ok if(model->left_mouse_pressed) { - canvas_draw_icon(canvas, 81, 25, &I_Pressed_Button_13x13); - canvas_set_color(canvas, ColorWhite); + canvas_draw_icon(canvas, 81, 25, &I_Ok_btn_pressed_13x13); + } else { + canvas_draw_icon(canvas, 83, 27, &I_Left_mouse_icon_9x9); } - canvas_draw_icon(canvas, 83, 27, &I_Ok_btn_9x9); - canvas_set_color(canvas, ColorBlack); // Back if(model->right_mouse_pressed) { - canvas_draw_icon(canvas, 108, 48, &I_Pressed_Button_13x13); - canvas_set_color(canvas, ColorWhite); + canvas_draw_icon(canvas, 108, 48, &I_Ok_btn_pressed_13x13); + } else { + canvas_draw_icon(canvas, 110, 50, &I_Right_mouse_icon_9x9); } - canvas_draw_icon(canvas, 110, 50, &I_Ok_btn_9x9); } static void bt_hid_mouse_process(BtHidMouse* bt_hid_mouse, InputEvent* event) { diff --git a/applications/dec_hex_converter/dec_hex_converter.c b/applications/dec_hex_converter/dec_hex_converter.c deleted file mode 100644 index 98a090a82..000000000 --- a/applications/dec_hex_converter/dec_hex_converter.c +++ /dev/null @@ -1,404 +0,0 @@ -#include -#include -#include -#include - -#define DEC_HEX_CONVERTER_NUMBER_DIGITS 9 -#define DEC_HEX_CONVERTER_KEYS 18 -#define DEC_HEX_CONVERTER_KEY_DEL 16 -// #define DEC_HEX_CONVERTER_KEY_SWAP 17 // actually not used... - -#define DEC_HEX_CONVERTER_CHAR_DEL '<' -#define DEC_HEX_CONVERTER_CHAR_SWAP 's' -#define DEC_HEX_CONVERTER_CHAR_MODE '#' -#define DEC_HEX_CONVERTER_CHAR_OVERFLOW '#' - -#define DEC_HEX_CONVERTER_KEY_FRAME_MARGIN 3 -#define DEC_HEX_CONVERTER_KEY_CHAR_HEIGHT 8 - -#define DEC_HEX_MAX_SUPORTED_DEC_INT 999999999 - -typedef enum { - EventTypeKey, -} EventType; - -typedef struct { - InputEvent input; - EventType type; -} DecHexConverterEvent; - -typedef enum { - ModeDec, - ModeHex, -} Mode; - -// setting up one char array next to the other one causes the canvas_draw_str to display both of them -// when addressing the first one if there's no string terminator or similar indicator. Adding a \0 seems -// to work fine to prevent that, so add a final last char outside the size constants (added on init -// and NEVER changed nor referenced again) -// -// (as a reference, canvas_draw_str ends up calling u8g2_DrawStr from u8g2_font.c, -// that finally ends up calling u8g2_draw_string) -typedef struct { - char dec_number[DEC_HEX_CONVERTER_NUMBER_DIGITS + 1]; - char hex_number[DEC_HEX_CONVERTER_NUMBER_DIGITS + 1]; - Mode mode; // dec / hex - int8_t cursor; // position on keyboard (includes digit letters and other options) - int8_t digit_pos; // current digit on selected mode -} DecHexConverterState; - -// move cursor left / right (TODO: implement menu nav in a more "standard" and reusable way?) -void dec_hex_converter_logic_move_cursor_lr( - DecHexConverterState* const dec_hex_converter_state, - int8_t d) { - dec_hex_converter_state->cursor += d; - - if(dec_hex_converter_state->cursor > DEC_HEX_CONVERTER_KEYS - 1) - dec_hex_converter_state->cursor = 0; - else if(dec_hex_converter_state->cursor < 0) - dec_hex_converter_state->cursor = DEC_HEX_CONVERTER_KEYS - 1; - - // if we're moving left / right to the letters keys on ModeDec just go to the closest available key - if(dec_hex_converter_state->mode == ModeDec) { - if(dec_hex_converter_state->cursor == 10) - dec_hex_converter_state->cursor = 16; - else if(dec_hex_converter_state->cursor == 15) - dec_hex_converter_state->cursor = 9; - } -} - -// move cursor up / down; there're two lines, so we basically toggle -void dec_hex_converter_logic_move_cursor_ud(DecHexConverterState* const dec_hex_converter_state) { - if(dec_hex_converter_state->cursor < 9) { - // move to second line ("down") - dec_hex_converter_state->cursor += 9; - - // if we're reaching the letter keys while ModeDec, just move left / right for the first available key - if(dec_hex_converter_state->mode == ModeDec && - (dec_hex_converter_state->cursor >= 10 && dec_hex_converter_state->cursor <= 15)) { - if(dec_hex_converter_state->cursor <= 12) - dec_hex_converter_state->cursor = 9; - else - dec_hex_converter_state->cursor = 16; - } - } else { - // move to first line ("up") - dec_hex_converter_state->cursor -= 9; - } -} - -// fetch number from current mode and modifies the destination one, RM dnt stel pls -// (if destination is shorter than the output value, overried with "-" chars or something similar) -void dec_hex_converter_logic_convert_number(DecHexConverterState* const dec_hex_converter_state) { - char* s_ptr; - char* d_ptr; - - char dest[DEC_HEX_CONVERTER_NUMBER_DIGITS]; - int i = 0; // current index on destination array - - if(dec_hex_converter_state->mode == ModeDec) { - // DEC to HEX cannot overflow if they're, at least, the same size - - s_ptr = dec_hex_converter_state->dec_number; - d_ptr = dec_hex_converter_state->hex_number; - - int a = atoi(s_ptr); - int r; - while(a != 0) { - r = a % 16; - dest[i] = r + (r < 10 ? '0' : ('A' - 10)); - a /= 16; - i++; - } - - } else { - s_ptr = dec_hex_converter_state->hex_number; - d_ptr = dec_hex_converter_state->dec_number; - - int a = strtol(s_ptr, NULL, 16); - if(a > DEC_HEX_MAX_SUPORTED_DEC_INT) { - // draw all "###" if there's an overflow - for(int j = 0; j < DEC_HEX_CONVERTER_NUMBER_DIGITS; j++) { - d_ptr[j] = DEC_HEX_CONVERTER_CHAR_OVERFLOW; - } - return; - } else { - while(a > 0) { - dest[i++] = (a % 10) + '0'; - a /= 10; - } - } - } - - // dest is reversed, copy to destination pointer and append empty chars at the end - for(int j = 0; j < DEC_HEX_CONVERTER_NUMBER_DIGITS; j++) { - if(i >= 1) - d_ptr[j] = dest[--i]; - else - d_ptr[j] = ' '; - } -} - -// change from DEC to HEX or HEX to DEC, set the digit_pos to the last position not empty on the destination mode -void dec_hex_converter_logic_swap_mode(DecHexConverterState* const dec_hex_converter_state) { - char* n_ptr; - if(dec_hex_converter_state->mode == ModeDec) { - dec_hex_converter_state->mode = ModeHex; - n_ptr = dec_hex_converter_state->hex_number; - } else { - dec_hex_converter_state->mode = ModeDec; - n_ptr = dec_hex_converter_state->dec_number; - } - - dec_hex_converter_state->digit_pos = DEC_HEX_CONVERTER_NUMBER_DIGITS; - for(int i = 0; i < DEC_HEX_CONVERTER_NUMBER_DIGITS; i++) { - if(n_ptr[i] == ' ') { - dec_hex_converter_state->digit_pos = i; - break; - } - } -} - -// removes the number on current digit on current mode -static void - dec_hex_converter_logic_del_number(DecHexConverterState* const dec_hex_converter_state) { - if(dec_hex_converter_state->digit_pos > 0) dec_hex_converter_state->digit_pos--; - - if(dec_hex_converter_state->mode == ModeDec) { - dec_hex_converter_state->dec_number[dec_hex_converter_state->digit_pos] = ' '; - } else { - dec_hex_converter_state->hex_number[dec_hex_converter_state->digit_pos] = ' '; - } -} - -// append a number to the digit on the current mode -static void dec_hex_converter_logic_add_number( - DecHexConverterState* const dec_hex_converter_state, - int8_t number) { - // ignore HEX values on DEC mode (probably button nav will be disabled too, so cannot reach); - // also do not add anything if we're out of bound - if((number > 9 && dec_hex_converter_state->mode == ModeDec) || - dec_hex_converter_state->digit_pos >= DEC_HEX_CONVERTER_NUMBER_DIGITS) - return; - - char* s_ptr; - - if(dec_hex_converter_state->mode == ModeDec) { - s_ptr = dec_hex_converter_state->dec_number; - } else { - s_ptr = dec_hex_converter_state->hex_number; - } - - if(number < 10) { - s_ptr[dec_hex_converter_state->digit_pos] = number + '0'; - } else { - s_ptr[dec_hex_converter_state->digit_pos] = (number - 10) + 'A'; // A-F (HEX only) - } - - dec_hex_converter_state->digit_pos++; -} - -// --------------- - -static void dec_hex_converter_render_callback(Canvas* const canvas, void* ctx) { - const DecHexConverterState* dec_hex_converter_state = acquire_mutex((ValueMutex*)ctx, 25); - if(dec_hex_converter_state == NULL) { - return; - } - - canvas_set_color(canvas, ColorBlack); - - // DEC - canvas_set_font(canvas, FontPrimary); - canvas_draw_str(canvas, 2, 10, "DEC: "); - - canvas_set_font(canvas, FontPrimary); - canvas_draw_str(canvas, 2 + 30, 10, dec_hex_converter_state->dec_number); - - // HEX - canvas_set_font(canvas, FontPrimary); - canvas_draw_str(canvas, 2, 10 + 12, "HEX: "); - - canvas_set_font(canvas, FontPrimary); - canvas_draw_str(canvas, 2 + 30, 10 + 12, dec_hex_converter_state->hex_number); - - // current mode indicator - // char buffer[4]; - // snprintf(buffer, sizeof(buffer), "%u", dec_hex_converter_state->digit_pos); // debug: show digit position instead of selected mode - if(dec_hex_converter_state->mode == ModeDec) { - canvas_draw_glyph(canvas, 128 - 10, 10, DEC_HEX_CONVERTER_CHAR_MODE); - } else { - canvas_draw_glyph(canvas, 128 - 10, 10 + 12, DEC_HEX_CONVERTER_CHAR_MODE); - } - - // draw the line - canvas_draw_line(canvas, 2, 25, 128 - 3, 25); - - // draw the keyboard - uint8_t _x = 5; - uint8_t _y = 25 + 15; // line + 10 - - for(int i = 0; i < DEC_HEX_CONVERTER_KEYS; i++) { - char g; - if(i < 10) - g = (i + '0'); - else if(i < 16) - g = ((i - 10) + 'A'); - else if(i == 16) - g = DEC_HEX_CONVERTER_CHAR_DEL; // '<' - else - g = DEC_HEX_CONVERTER_CHAR_SWAP; // 's' - - uint8_t g_w = canvas_glyph_width(canvas, g); - - // disable letters on DEC mode (but keep the previous width for visual purposes - show "blank keys") - if(dec_hex_converter_state->mode == ModeDec && i > 9 && i < 16) g = ' '; - - if(dec_hex_converter_state->cursor == i) { - canvas_draw_box( - canvas, - _x - DEC_HEX_CONVERTER_KEY_FRAME_MARGIN, - _y - (DEC_HEX_CONVERTER_KEY_CHAR_HEIGHT + DEC_HEX_CONVERTER_KEY_FRAME_MARGIN), - DEC_HEX_CONVERTER_KEY_FRAME_MARGIN + g_w + DEC_HEX_CONVERTER_KEY_FRAME_MARGIN, - DEC_HEX_CONVERTER_KEY_CHAR_HEIGHT + DEC_HEX_CONVERTER_KEY_FRAME_MARGIN * 2); - canvas_set_color(canvas, ColorWhite); - canvas_draw_glyph(canvas, _x, _y, g); - canvas_set_color(canvas, ColorBlack); - } else { - canvas_draw_frame( - canvas, - _x - DEC_HEX_CONVERTER_KEY_FRAME_MARGIN, - _y - (DEC_HEX_CONVERTER_KEY_CHAR_HEIGHT + DEC_HEX_CONVERTER_KEY_FRAME_MARGIN), - DEC_HEX_CONVERTER_KEY_FRAME_MARGIN + g_w + DEC_HEX_CONVERTER_KEY_FRAME_MARGIN, - DEC_HEX_CONVERTER_KEY_CHAR_HEIGHT + DEC_HEX_CONVERTER_KEY_FRAME_MARGIN * 2); - canvas_draw_glyph(canvas, _x, _y, g); - } - - if(i < 8) { - _x += g_w + DEC_HEX_CONVERTER_KEY_FRAME_MARGIN * 2 + 2; - } else if(i == 8) { - _y += (DEC_HEX_CONVERTER_KEY_CHAR_HEIGHT + DEC_HEX_CONVERTER_KEY_FRAME_MARGIN * 2) + 3; - _x = 7; // some padding at the beginning on second line - } else { - _x += g_w + DEC_HEX_CONVERTER_KEY_FRAME_MARGIN * 2 + 1; - } - } - - release_mutex((ValueMutex*)ctx, dec_hex_converter_state); -} - -static void - dec_hex_converter_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { - furi_assert(event_queue); - - DecHexConverterEvent event = {.type = EventTypeKey, .input = *input_event}; - furi_message_queue_put(event_queue, &event, FuriWaitForever); -} - -static void dec_hex_converter_init(DecHexConverterState* const dec_hex_converter_state) { - dec_hex_converter_state->mode = ModeDec; - dec_hex_converter_state->digit_pos = 0; - - dec_hex_converter_state->dec_number[DEC_HEX_CONVERTER_NUMBER_DIGITS] = '\0'; // null terminator - dec_hex_converter_state->hex_number[DEC_HEX_CONVERTER_NUMBER_DIGITS] = '\0'; // null terminator - - for(int i = 0; i < DEC_HEX_CONVERTER_NUMBER_DIGITS; i++) { - dec_hex_converter_state->dec_number[i] = ' '; - dec_hex_converter_state->hex_number[i] = ' '; - } -} - -// main entry point -int32_t dec_hex_converter_app(void* p) { - UNUSED(p); - - // get event queue - FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(DecHexConverterEvent)); - - // allocate state - DecHexConverterState* dec_hex_converter_state = malloc(sizeof(DecHexConverterState)); - - // set mutex for plugin state (different threads can access it) - ValueMutex state_mutex; - if(!init_mutex(&state_mutex, dec_hex_converter_state, sizeof(dec_hex_converter_state))) { - FURI_LOG_E("DecHexConverter", "cannot create mutex\r\n"); - furi_message_queue_free(event_queue); - free(dec_hex_converter_state); - return 255; - } - - // register callbacks for drawing and input processing - ViewPort* view_port = view_port_alloc(); - view_port_draw_callback_set(view_port, dec_hex_converter_render_callback, &state_mutex); - view_port_input_callback_set(view_port, dec_hex_converter_input_callback, event_queue); - - // open GUI and register view_port - Gui* gui = furi_record_open(RECORD_GUI); - gui_add_view_port(gui, view_port, GuiLayerFullscreen); - - dec_hex_converter_init(dec_hex_converter_state); - - // main loop - DecHexConverterEvent event; - for(bool processing = true; processing;) { - FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); - DecHexConverterState* dec_hex_converter_state = - (DecHexConverterState*)acquire_mutex_block(&state_mutex); - - if(event_status == FuriStatusOk) { - // press events - if(event.type == EventTypeKey) { - if(event.input.type == InputTypePress) { - switch(event.input.key) { - default: - break; - case InputKeyUp: - case InputKeyDown: - dec_hex_converter_logic_move_cursor_ud(dec_hex_converter_state); - break; - case InputKeyRight: - dec_hex_converter_logic_move_cursor_lr(dec_hex_converter_state, 1); - break; - case InputKeyLeft: - dec_hex_converter_logic_move_cursor_lr(dec_hex_converter_state, -1); - break; - case InputKeyOk: - if(dec_hex_converter_state->cursor < DEC_HEX_CONVERTER_KEY_DEL) { - // positions from 0 to 15 works as regular numbers (DEC / HEX where applicable) - // (logic won't allow add numbers > 9 on ModeDec) - dec_hex_converter_logic_add_number( - dec_hex_converter_state, dec_hex_converter_state->cursor); - } else if(dec_hex_converter_state->cursor == DEC_HEX_CONVERTER_KEY_DEL) { - // del - dec_hex_converter_logic_del_number(dec_hex_converter_state); - } else { - // swap - dec_hex_converter_logic_swap_mode(dec_hex_converter_state); - } - - dec_hex_converter_logic_convert_number(dec_hex_converter_state); - break; - case InputKeyBack: - processing = false; - break; - } - } - } - } else { - // event timeout - } - - view_port_update(view_port); - release_mutex(&state_mutex, dec_hex_converter_state); - } - - view_port_enabled_set(view_port, false); - gui_remove_view_port(gui, view_port); - furi_record_close(RECORD_GUI); - view_port_free(view_port); - furi_message_queue_free(event_queue); - delete_mutex(&state_mutex); - free(dec_hex_converter_state); - - return 0; -} \ No newline at end of file diff --git a/applications/desktop/animations/animation_manager.c b/applications/desktop/animations/animation_manager.c index d755be9c0..1e2a521e1 100644 --- a/applications/desktop/animations/animation_manager.c +++ b/applications/desktop/animations/animation_manager.c @@ -220,8 +220,7 @@ static bool animation_manager_check_blocking(AnimationManager* animation_manager furi_assert(blocking_animation); animation_manager->sd_shown_sd_ok = true; } else if(!animation_manager->sd_shown_no_db) { - bool db_exists = storage_common_stat(storage, EXT_PATH("Manifest"), NULL) == FSE_OK; - if(!db_exists) { + if(!storage_file_exists(storage, EXT_PATH("Manifest"))) { blocking_animation = animation_storage_find_animation(NO_DB_ANIMATION_NAME); furi_assert(blocking_animation); animation_manager->sd_shown_no_db = true; diff --git a/applications/desktop/helpers/slideshow.c b/applications/desktop/helpers/slideshow.c index 63bd42b55..b4d85cb90 100644 --- a/applications/desktop/helpers/slideshow.c +++ b/applications/desktop/helpers/slideshow.c @@ -94,6 +94,10 @@ bool slideshow_is_loaded(Slideshow* slideshow) { return slideshow->loaded; } +bool slideshow_is_one_page(Slideshow* slideshow) { + return slideshow->loaded && (slideshow->icon.frame_count == 1); +} + bool slideshow_advance(Slideshow* slideshow) { uint8_t next_frame = slideshow->current_frame + 1; if(next_frame < slideshow->icon.frame_count) { diff --git a/applications/desktop/helpers/slideshow.h b/applications/desktop/helpers/slideshow.h index eeaac0e8b..9083e0dcf 100644 --- a/applications/desktop/helpers/slideshow.h +++ b/applications/desktop/helpers/slideshow.h @@ -9,6 +9,7 @@ Slideshow* slideshow_alloc(); void slideshow_free(Slideshow* slideshow); bool slideshow_load(Slideshow* slideshow, const char* fspath); bool slideshow_is_loaded(Slideshow* slideshow); +bool slideshow_is_one_page(Slideshow* slideshow); void slideshow_goback(Slideshow* slideshow); bool slideshow_advance(Slideshow* slideshow); void slideshow_draw(Slideshow* slideshow, Canvas* canvas, uint8_t x, uint8_t y); diff --git a/applications/desktop/views/desktop_view_debug.c b/applications/desktop/views/desktop_view_debug.c index 69c82bdbe..c965432f1 100644 --- a/applications/desktop/views/desktop_view_debug.c +++ b/applications/desktop/views/desktop_view_debug.c @@ -23,11 +23,12 @@ void desktop_debug_render(Canvas* canvas, void* model) { const Version* ver; char buffer[64]; - static const char* headers[] = {"FW Version Info:", "Dolphin Info:"}; + static const char* headers[] = {"Device Info:", "Dolphin Info:"}; canvas_set_color(canvas, ColorBlack); canvas_set_font(canvas, FontPrimary); - canvas_draw_str(canvas, 2, 9 + STATUS_BAR_Y_SHIFT, headers[m->screen]); + canvas_draw_str_aligned( + canvas, 64, 1 + STATUS_BAR_Y_SHIFT, AlignCenter, AlignTop, headers[m->screen]); canvas_set_font(canvas, FontSecondary); if(m->screen != DesktopViewStatsMeta) { @@ -44,7 +45,7 @@ void desktop_debug_render(Canvas* canvas, void* model) { furi_hal_version_get_hw_region_name(), furi_hal_region_get_name(), my_name ? my_name : "Unknown"); - canvas_draw_str(canvas, 5, 19 + STATUS_BAR_Y_SHIFT, buffer); + canvas_draw_str(canvas, 0, 19 + STATUS_BAR_Y_SHIFT, buffer); ver = furi_hal_version_get_firmware_version(); const BleGlueC2Info* c2_ver = NULL; @@ -52,7 +53,7 @@ void desktop_debug_render(Canvas* canvas, void* model) { c2_ver = ble_glue_get_c2_info(); #endif if(!ver) { - canvas_draw_str(canvas, 5, 29 + STATUS_BAR_Y_SHIFT, "No info"); + canvas_draw_str(canvas, 0, 30 + STATUS_BAR_Y_SHIFT, "No info"); return; } @@ -62,7 +63,7 @@ void desktop_debug_render(Canvas* canvas, void* model) { "%s [%s]", version_get_version(ver), version_get_builddate(ver)); - canvas_draw_str(canvas, 5, 28 + STATUS_BAR_Y_SHIFT, buffer); + canvas_draw_str(canvas, 0, 30 + STATUS_BAR_Y_SHIFT, buffer); snprintf( buffer, @@ -72,11 +73,11 @@ void desktop_debug_render(Canvas* canvas, void* model) { version_get_githash(ver), version_get_gitbranchnum(ver), c2_ver ? c2_ver->StackTypeString : ""); - canvas_draw_str(canvas, 5, 39 + STATUS_BAR_Y_SHIFT, buffer); + canvas_draw_str(canvas, 0, 40 + STATUS_BAR_Y_SHIFT, buffer); snprintf( buffer, sizeof(buffer), "[%d] %s", version_get_target(ver), version_get_gitbranch(ver)); - canvas_draw_str(canvas, 5, 50 + STATUS_BAR_Y_SHIFT, buffer); + canvas_draw_str(canvas, 0, 50 + STATUS_BAR_Y_SHIFT, buffer); } else { Dolphin* dolphin = furi_record_open(RECORD_DOLPHIN); diff --git a/applications/desktop/views/desktop_view_slideshow.c b/applications/desktop/views/desktop_view_slideshow.c index 26ae95eae..3462d2f08 100644 --- a/applications/desktop/views/desktop_view_slideshow.c +++ b/applications/desktop/views/desktop_view_slideshow.c @@ -35,8 +35,9 @@ static bool desktop_view_slideshow_input(InputEvent* event, void* context) { furi_assert(event); DesktopSlideshowView* instance = context; + DesktopSlideshowViewModel* model = view_get_model(instance->view); + bool update_view = false; if(event->type == InputTypeShort) { - DesktopSlideshowViewModel* model = view_get_model(instance->view); bool end_slideshow = false; switch(event->key) { case InputKeyLeft: @@ -54,15 +55,18 @@ static bool desktop_view_slideshow_input(InputEvent* event, void* context) { if(end_slideshow) { instance->callback(DesktopSlideshowCompleted, instance->context); } - view_commit_model(instance->view, true); + update_view = true; } else if(event->key == InputKeyOk) { if(event->type == InputTypePress) { furi_timer_start(instance->timer, DESKTOP_SLIDESHOW_POWEROFF_SHORT); } else if(event->type == InputTypeRelease) { furi_timer_stop(instance->timer); - furi_timer_start(instance->timer, DESKTOP_SLIDESHOW_POWEROFF_LONG); + /*if(!slideshow_is_one_page(model->slideshow)) { + furi_timer_start(instance->timer, DESKTOP_SLIDESHOW_POWEROFF_LONG); + }*/ } } + view_commit_model(instance->view, update_view); return true; } @@ -79,12 +83,12 @@ static void desktop_view_slideshow_enter(void* context) { instance->timer = furi_timer_alloc(desktop_first_start_timer_callback, FuriTimerTypeOnce, instance); - furi_timer_start(instance->timer, DESKTOP_SLIDESHOW_POWEROFF_LONG); - DesktopSlideshowViewModel* model = view_get_model(instance->view); model->slideshow = slideshow_alloc(); if(!slideshow_load(model->slideshow, SLIDESHOW_FS_PATH)) { instance->callback(DesktopSlideshowCompleted, instance->context); + } else if(!slideshow_is_one_page(model->slideshow)) { + furi_timer_start(instance->timer, DESKTOP_SLIDESHOW_POWEROFF_LONG); } view_commit_model(instance->view, false); } diff --git a/applications/dolphin/helpers/dolphin_state.c b/applications/dolphin/helpers/dolphin_state.c index 76f38a5fd..95e2f42f4 100644 --- a/applications/dolphin/helpers/dolphin_state.c +++ b/applications/dolphin/helpers/dolphin_state.c @@ -14,8 +14,8 @@ #define DOLPHIN_STATE_PATH INT_PATH(DOLPHIN_STATE_FILE_NAME) #define DOLPHIN_STATE_HEADER_MAGIC 0xD0 #define DOLPHIN_STATE_HEADER_VERSION 0x01 -#define LEVEL2_THRESHOLD 735 -#define LEVEL3_THRESHOLD 2940 +#define LEVEL2_THRESHOLD 300 +#define LEVEL3_THRESHOLD 1800 #define BUTTHURT_MAX 14 #define BUTTHURT_MIN 0 diff --git a/applications/gui/modules/file_browser_worker.c b/applications/gui/modules/file_browser_worker.c index d705e5c3a..36df6cc83 100644 --- a/applications/gui/modules/file_browser_worker.c +++ b/applications/gui/modules/file_browser_worker.c @@ -99,6 +99,11 @@ static bool browser_folder_check_and_switch(string_t path) { FileInfo file_info; Storage* storage = furi_record_open(RECORD_STORAGE); bool is_root = false; + + if(string_search_rchar(path, '/') == 0) { + is_root = true; + } + while(1) { // Check if folder is existing and navigate back if not if(storage_common_stat(storage, string_get_cstr(path), &file_info) == FSE_OK) { diff --git a/applications/gui/modules/widget.h b/applications/gui/modules/widget.h index 587fa3c65..03586165c 100755 --- a/applications/gui/modules/widget.h +++ b/applications/gui/modules/widget.h @@ -115,8 +115,8 @@ void widget_add_text_box_element( * @param[in] text Formatted text. Default format: align left, Secondary font. * The following formats are available: * "\e#Bold text" - sets bold font before until next '\n' symbol - * "\ecBold text" - sets center horizontal align before until next '\n' symbol - * "\erBold text" - sets right horizontal align before until next '\n' symbol + * "\ecCenter-aligned text" - sets center horizontal align until the next '\n' symbol + * "\erRight-aligned text" - sets right horizontal align until the next '\n' symbol */ void widget_add_text_scroll_element( Widget* widget, diff --git a/applications/hid_analyzer/application.fam b/applications/hid_analyzer/application.fam deleted file mode 100644 index 5005190cc..000000000 --- a/applications/hid_analyzer/application.fam +++ /dev/null @@ -1,9 +0,0 @@ -App( - appid="hid_analyzer", - name="HID Analyzer", - apptype=FlipperAppType.PLUGIN, - entry_point="hid_analyzer_app", - cdefines=["APP_HID_ANALYZER"], - stack_size=2 * 1024, - order=40, -) diff --git a/applications/hid_analyzer/helpers/decoder_hid.cpp b/applications/hid_analyzer/helpers/decoder_hid.cpp deleted file mode 100644 index 176bfe0b9..000000000 --- a/applications/hid_analyzer/helpers/decoder_hid.cpp +++ /dev/null @@ -1,98 +0,0 @@ -#include "decoder_hid.h" -#include - -constexpr uint32_t clocks_in_us = 64; - -constexpr uint32_t jitter_time_us = 20; -constexpr uint32_t min_time_us = 64; -constexpr uint32_t max_time_us = 80; - -constexpr uint32_t min_time = (min_time_us - jitter_time_us) * clocks_in_us; -constexpr uint32_t mid_time = ((max_time_us - min_time_us) / 2 + min_time_us) * clocks_in_us; -constexpr uint32_t max_time = (max_time_us + jitter_time_us) * clocks_in_us; - -bool DecoderHID::read(uint8_t* data, uint8_t data_size) { - bool result = false; - furi_assert(data_size >= 3); - - if(ready) { - result = true; - hid.decode( - reinterpret_cast(&stored_data), sizeof(uint32_t) * 3, data, data_size); - ready = false; - } - - return result; -} - -void DecoderHID::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; - } - } - } -} - -DecoderHID::DecoderHID() { - reset_state(); -} - -void DecoderHID::store_data(bool data) { - stored_data[0] = (stored_data[0] << 1) | ((stored_data[1] >> 31) & 1); - stored_data[1] = (stored_data[1] << 1) | ((stored_data[2] >> 31) & 1); - stored_data[2] = (stored_data[2] << 1) | data; - - if(hid.can_be_decoded(reinterpret_cast(&stored_data), sizeof(uint32_t) * 3)) { - ready = true; - } -} - -void DecoderHID::reset_state() { - last_pulse = false; - pulse_count = 0; - ready = false; - last_pulse_time = 0; -} diff --git a/applications/hid_analyzer/helpers/decoder_hid.h b/applications/hid_analyzer/helpers/decoder_hid.h deleted file mode 100644 index 5dcb6c265..000000000 --- a/applications/hid_analyzer/helpers/decoder_hid.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once -#include -#include -#include "protocols/protocol_hid.h" - -class DecoderHID { -public: - bool read(uint8_t* data, uint8_t data_size); - void process_front(bool polarity, uint32_t time); - DecoderHID(); - -private: - uint32_t last_pulse_time = 0; - bool last_pulse; - uint8_t pulse_count; - - uint32_t stored_data[3] = {0, 0, 0}; - void store_data(bool data); - - std::atomic ready; - - void reset_state(); - ProtocolHID hid; -}; diff --git a/applications/hid_analyzer/helpers/hid_reader.cpp b/applications/hid_analyzer/helpers/hid_reader.cpp deleted file mode 100644 index 890d7c651..000000000 --- a/applications/hid_analyzer/helpers/hid_reader.cpp +++ /dev/null @@ -1,143 +0,0 @@ -#include "hid_reader.h" -#include -#include -#include - -/** - * @brief private violation assistant for HIDReader - */ -struct HIDReaderAccessor { - static void decode(HIDReader& hid_reader, bool polarity) { - hid_reader.decode(polarity); - } -}; - -void HIDReader::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; - - decoder_hid.process_front(polarity, period); - - detect_ticks++; -} - -bool HIDReader::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 HIDReader::switch_timer_reset() { - switch_os_tick_last = furi_get_tick(); -} - -void HIDReader::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) { - HIDReader* _this = static_cast(comp_ctx); - - HIDReaderAccessor::decode(*_this, !level); -} - -HIDReader::HIDReader() { -} - -void HIDReader::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 HIDReader::start_forced(HIDReader::Type _type) { - start(); - if(_type == Type::Indala) { - switch_mode(); - } -} - -void HIDReader::stop() { - furi_hal_rfid_pins_reset(); - furi_hal_rfid_tim_read_stop(); - furi_hal_rfid_tim_reset(); - stop_comparator(); -} - -bool HIDReader::read(LfrfidKeyType* _type, uint8_t* data, uint8_t data_size, bool switch_enable) { - bool result = false; - bool something_read = false; - - if(decoder_hid.read(data, data_size)) { - *_type = LfrfidKeyType::KeyH10301; // should be an OK temp - 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 HIDReader::detect() { - bool detected = false; - if(detect_ticks > 10) { - detected = true; - } - detect_ticks = 0; - - return detected; -} - -bool HIDReader::any_read() { - return last_read_count > 0; -} - -void HIDReader::start_comparator(void) { - furi_hal_rfid_comp_set_callback(comparator_trigger_callback, this); - last_dwt_value = DWT->CYCCNT; - - furi_hal_rfid_comp_start(); -} - -void HIDReader::stop_comparator(void) { - furi_hal_rfid_comp_stop(); - furi_hal_rfid_comp_set_callback(NULL, NULL); -} diff --git a/applications/hid_analyzer/helpers/hid_reader.h b/applications/hid_analyzer/helpers/hid_reader.h deleted file mode 100644 index 99dbeb08d..000000000 --- a/applications/hid_analyzer/helpers/hid_reader.h +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once -#include "decoder_hid.h" -#include "key_info.h" - -//#define RFID_GPIO_DEBUG 1 - -class HIDReader { -public: - enum class Type : uint8_t { - Normal, - Indala, - }; - - HIDReader(); - void start(); - void start_forced(HIDReader::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 HIDReaderAccessor; - - DecoderHID decoder_hid; - - uint32_t last_dwt_value; - - void start_comparator(void); - void stop_comparator(void); - - void decode(bool polarity); - - uint32_t detect_ticks; - - uint32_t switch_os_tick_last; - bool switch_timer_elapsed(); - void switch_timer_reset(); - void switch_mode(); - - LfrfidKeyType last_read_type; - uint8_t last_read_data[LFRFID_KEY_SIZE]; - uint8_t last_read_count; - - Type type = Type::Normal; -}; diff --git a/applications/hid_analyzer/helpers/hid_worker.cpp b/applications/hid_analyzer/helpers/hid_worker.cpp deleted file mode 100644 index 42230858c..000000000 --- a/applications/hid_analyzer/helpers/hid_worker.cpp +++ /dev/null @@ -1,38 +0,0 @@ -#include "hid_worker.h" - -HIDWorker::HIDWorker() { -} - -HIDWorker::~HIDWorker() { -} - -void HIDWorker::start_read() { - reader.start(); -} - -bool HIDWorker::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 HIDWorker::detect() { - return reader.detect(); -} - -bool HIDWorker::any_read() { - return reader.any_read(); -} - -void HIDWorker::stop_read() { - reader.stop(); -} diff --git a/applications/hid_analyzer/helpers/hid_worker.h b/applications/hid_analyzer/helpers/hid_worker.h deleted file mode 100644 index e6674bd1b..000000000 --- a/applications/hid_analyzer/helpers/hid_worker.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once -#include "key_info.h" -#include "rfid_key.h" -#include "hid_reader.h" - -class HIDWorker { -public: - HIDWorker(); - ~HIDWorker(); - - void start_read(); - bool read(); - bool detect(); - bool any_read(); - void stop_read(); - - RfidKey key; - -private: - HIDReader reader; -}; diff --git a/applications/hid_analyzer/helpers/key_info.h b/applications/hid_analyzer/helpers/key_info.h deleted file mode 100644 index e465011d0..000000000 --- a/applications/hid_analyzer/helpers/key_info.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once -#include - -static const uint8_t LFRFID_KEY_SIZE = 8; -static const uint8_t LFRFID_KEY_NAME_SIZE = 22; - -enum class LfrfidKeyType : uint8_t { - KeyEM4100, - KeyH10301, - KeyI40134, -}; - -const char* lfrfid_key_get_type_string(LfrfidKeyType type); -const char* lfrfid_key_get_manufacturer_string(LfrfidKeyType type); -bool lfrfid_key_get_string_type(const char* string, LfrfidKeyType* type); -uint8_t lfrfid_key_get_type_data_count(LfrfidKeyType type); diff --git a/applications/hid_analyzer/helpers/osc_fsk.h b/applications/hid_analyzer/helpers/osc_fsk.h deleted file mode 100644 index eaaaa10ad..000000000 --- a/applications/hid_analyzer/helpers/osc_fsk.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once -#include - -/** - * This code tries to fit the periods into a given number of cycles (phases) by taking cycles from the next cycle of periods. - */ -class OscFSK { -public: - /** - * Get next period - * @param bit bit value - * @param period return period - * @return bool whether to advance to the next bit - */ - bool next(bool bit, uint16_t* period); - - /** - * FSK ocillator constructor - * - * @param freq_low bit 0 freq - * @param freq_hi bit 1 freq - * @param osc_phase_max max oscillator phase - */ - OscFSK(uint16_t freq_low, uint16_t freq_hi, uint16_t osc_phase_max); - -private: - const uint16_t freq[2]; - const uint16_t osc_phase_max; - int32_t osc_phase_current; -}; diff --git a/applications/hid_analyzer/helpers/protocols/protocol_generic.h b/applications/hid_analyzer/helpers/protocols/protocol_generic.h deleted file mode 100644 index d593f7089..000000000 --- a/applications/hid_analyzer/helpers/protocols/protocol_generic.h +++ /dev/null @@ -1,60 +0,0 @@ -#pragma once -#include "stdint.h" -#include "stdbool.h" - -class ProtocolGeneric { -public: - /** - * @brief Get the encoded data size - * - * @return uint8_t size of encoded data in bytes - */ - virtual uint8_t get_encoded_data_size() = 0; - - /** - * @brief Get the decoded data size - * - * @return uint8_t size of decoded data in bytes - */ - virtual uint8_t get_decoded_data_size() = 0; - - /** - * @brief encode decoded data - * - * @param decoded_data - * @param decoded_data_size - * @param encoded_data - * @param encoded_data_size - */ - virtual void encode( - const uint8_t* decoded_data, - const uint8_t decoded_data_size, - uint8_t* encoded_data, - const uint8_t encoded_data_size) = 0; - - /** - * @brief decode encoded data - * - * @param encoded_data - * @param encoded_data_size - * @param decoded_data - * @param decoded_data_size - */ - virtual void decode( - const uint8_t* encoded_data, - const uint8_t encoded_data_size, - uint8_t* decoded_data, - const uint8_t decoded_data_size) = 0; - - /** - * @brief fast check that data can be correctly decoded - * - * @param encoded_data - * @param encoded_data_size - * @return true - can be correctly decoded - * @return false - cannot be correctly decoded - */ - virtual bool can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) = 0; - - virtual ~ProtocolGeneric(){}; -}; diff --git a/applications/hid_analyzer/helpers/protocols/protocol_hid.cpp b/applications/hid_analyzer/helpers/protocols/protocol_hid.cpp deleted file mode 100644 index e60eafa29..000000000 --- a/applications/hid_analyzer/helpers/protocols/protocol_hid.cpp +++ /dev/null @@ -1,155 +0,0 @@ -#include "protocol_hid.h" -#include - -typedef uint32_t HIDCardData; -constexpr uint8_t HIDCount = 3; -constexpr uint8_t HIDBitSize = sizeof(HIDCardData) * 8; - -uint8_t ProtocolHID::get_encoded_data_size() { - return sizeof(HIDCardData) * HIDCount; -} - -uint8_t ProtocolHID::get_decoded_data_size() { - return 3; -} - -void ProtocolHID::encode( - const uint8_t* decoded_data, - const uint8_t decoded_data_size, - uint8_t* encoded_data, - const uint8_t encoded_data_size) { - UNUSED(decoded_data); - UNUSED(decoded_data_size); - UNUSED(encoded_data); - UNUSED(encoded_data_size); - // bob! -} - -void ProtocolHID::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()); - - // header check - int16_t second1pos = find_second_1(encoded_data); - - if((*(encoded_data + 1) & 0b1100) != 0x08) { - *decoded_data = 37; - } else { - *decoded_data = (36 - (second1pos - 8)); - } -} - -int16_t ProtocolHID::find_second_1(const uint8_t* encoded_data) { - if((*(encoded_data + 1) & 0b11) == 0b10) { - return 8; - } else { - for(int8_t i = 3; i >= 0; i--) { - if(((*(encoded_data + 0) >> (2 * i)) & 0b11) == 0b10) { - return (12 - i); - } - } - for(int8_t i = 3; i >= 0; i--) { - if(((*(encoded_data + 7) >> (2 * i)) & 0b11) == 0b10) { - return (16 - i); - } - } - for(int8_t i = 3; i >= 2; i--) { - if(((*(encoded_data + 6) >> (2 * i)) & 0b11) == 0b10) { - return (20 - i); - } - } - } - - return -1; -} - -bool ProtocolHID::can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) { - furi_check(encoded_data_size >= get_encoded_data_size()); - - const HIDCardData* card_data = reinterpret_cast(encoded_data); - - // header check - int16_t second1pos = -1; - // packet pre-preamble - if(*(encoded_data + 3) != 0x1D) { - return false; - } - - // packet preamble - if(*(encoded_data + 2) != 0x55) { // first four 0s mandatory in preamble - return false; - } - - if((*(encoded_data + 1) & 0xF0) != 0x50) { // next two 0s mandatory in preamble - return false; - } - - if((*(encoded_data + 1) & 0b1100) != 0x08) { // if it's not a 1... - // either it's a 37-bit or invalid - // so just continue with the manchester encoding checks - } else { // it is a 1. so it could be anywhere between 26 and 36 bit encoding. or invalid. - // we need to find the location of the second 1 - second1pos = find_second_1(encoded_data); - } - - if(second1pos == -1) { - // we're 37 bit or invalid - } - - // data decoding. ensure all is properly manchester encoded - uint32_t result = 0; - - // decode from word 0 - // coded with 01 = 0, 10 = 1 transitions - for(int8_t i = 11; i >= 0; i--) { - switch((*(card_data + 0) >> (2 * i)) & 0b11) { - case 0b01: - result = (result << 1) | 0; - break; - case 0b10: - result = (result << 1) | 1; - break; - default: - return false; - break; - } - } - - // decode from word 1 - // coded with 01 = 0, 10 = 1 transitions - for(int8_t i = 15; 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; - } - } - - return true; -} diff --git a/applications/hid_analyzer/helpers/protocols/protocol_hid.h b/applications/hid_analyzer/helpers/protocols/protocol_hid.h deleted file mode 100644 index 0e2636d5b..000000000 --- a/applications/hid_analyzer/helpers/protocols/protocol_hid.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once -#include "protocol_generic.h" - -class ProtocolHID : 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: - int16_t find_second_1(const uint8_t* encoded_data); -}; diff --git a/applications/hid_analyzer/helpers/pulse_joiner.h b/applications/hid_analyzer/helpers/pulse_joiner.h deleted file mode 100644 index 1639d8371..000000000 --- a/applications/hid_analyzer/helpers/pulse_joiner.h +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once -#include "stdint.h" - -class PulseJoiner { -public: - /** - * @brief Push timer pulse. First negative pulse is ommited. - * - * @param polarity pulse polarity: true = high2low, false = low2high - * @param period overall period time in timer clicks - * @param pulse pulse time in timer clicks - * - * @return true - next pulse can and must be popped immediatly - */ - bool push_pulse(bool polarity, uint16_t period, uint16_t pulse); - - /** - * @brief Get the next timer pulse. Call only if push_pulse returns true. - * - * @param period overall period time in timer clicks - * @param pulse pulse time in timer clicks - */ - void pop_pulse(uint16_t* period, uint16_t* pulse); - - PulseJoiner(); - -private: - struct Pulse { - bool polarity; - uint16_t time; - }; - - uint8_t pulse_index = 0; - static const uint8_t pulse_max = 6; - Pulse pulses[pulse_max]; -}; diff --git a/applications/hid_analyzer/helpers/rfid_key.h b/applications/hid_analyzer/helpers/rfid_key.h deleted file mode 100644 index 29b87cf9c..000000000 --- a/applications/hid_analyzer/helpers/rfid_key.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once -#include "key_info.h" -#include - -class RfidKey { -public: - RfidKey(); - ~RfidKey(); - - void set_type(LfrfidKeyType type); - void set_data(const uint8_t* data, const uint8_t data_size); - void set_name(const char* name); - - LfrfidKeyType get_type(); - const uint8_t* get_data(); - const char* get_type_text(); - uint8_t get_type_data_count() const; - char* get_name(); - uint8_t get_name_length(); - void clear(); - RfidKey& operator=(const RfidKey& rhs); - -private: - std::array data; - LfrfidKeyType type; - char name[LFRFID_KEY_NAME_SIZE + 1]; -}; diff --git a/applications/hid_analyzer/helpers/rfid_timer_emulator.h b/applications/hid_analyzer/helpers/rfid_timer_emulator.h deleted file mode 100644 index 874a4c3dd..000000000 --- a/applications/hid_analyzer/helpers/rfid_timer_emulator.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once -#include -#include "key_info.h" -#include "encoder_generic.h" -#include "encoder_emmarin.h" -#include "encoder_hid_h10301.h" -#include "encoder_indala_40134.h" -#include "pulse_joiner.h" -#include - -class RfidTimerEmulator { -public: - RfidTimerEmulator(); - ~RfidTimerEmulator(); - void start(LfrfidKeyType type, const uint8_t* data, uint8_t data_size); - void stop(); - -private: - EncoderGeneric* current_encoder = nullptr; - - std::map encoders = { - {LfrfidKeyType::KeyEM4100, new EncoderEM()}, - {LfrfidKeyType::KeyH10301, new EncoderHID_H10301()}, - {LfrfidKeyType::KeyI40134, new EncoderIndala_40134()}, - }; - - PulseJoiner pulse_joiner; - static void timer_update_callback(void* ctx); -}; diff --git a/applications/hid_analyzer/helpers/rfid_writer.h b/applications/hid_analyzer/helpers/rfid_writer.h deleted file mode 100644 index 38329877b..000000000 --- a/applications/hid_analyzer/helpers/rfid_writer.h +++ /dev/null @@ -1,20 +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_indala(const uint8_t indala_data[3]); - -private: - void write_gap(uint32_t gap_time); - void write_bit(bool value); - void write_byte(uint8_t value); - void write_block(uint8_t page, uint8_t block, bool lock_bit, uint32_t data); - void write_reset(); -}; diff --git a/applications/hid_analyzer/helpers/state_sequencer.h b/applications/hid_analyzer/helpers/state_sequencer.h deleted file mode 100644 index 12512ab51..000000000 --- a/applications/hid_analyzer/helpers/state_sequencer.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once -#include "stdint.h" -#include -#include - -class TickSequencer { -public: - TickSequencer(); - ~TickSequencer(); - - void tick(); - void reset(); - void clear(); - - void do_every_tick(uint32_t tick_count, std::function fn); - void do_after_tick(uint32_t tick_count, std::function fn); - -private: - std::list > > list; - std::list > >::iterator list_it; - - uint32_t tick_count; - - void do_nothing(); -}; diff --git a/applications/hid_analyzer/hid_analyzer_app.cpp b/applications/hid_analyzer/hid_analyzer_app.cpp deleted file mode 100644 index e8d7fd541..000000000 --- a/applications/hid_analyzer/hid_analyzer_app.cpp +++ /dev/null @@ -1,23 +0,0 @@ -#include "hid_analyzer_app.h" -#include "scene/hid_analyzer_app_scene_read.h" -#include "scene/hid_analyzer_app_scene_read_success.h" - -HIDApp::HIDApp() - : scene_controller{this} - , notification{"notification"} - , storage{"storage"} - , dialogs{"dialogs"} - , text_store(40) { -} - -HIDApp::~HIDApp() { -} - -void HIDApp::run(void* _args) { - UNUSED(_args); - - view_controller.attach_to_gui(ViewDispatcherTypeFullscreen); - scene_controller.add_scene(SceneType::Read, new HIDAppSceneRead()); - scene_controller.add_scene(SceneType::ReadSuccess, new HIDAppSceneReadSuccess()); - scene_controller.process(100, SceneType::Read); -} \ No newline at end of file diff --git a/applications/hid_analyzer/hid_analyzer_app.h b/applications/hid_analyzer/hid_analyzer_app.h deleted file mode 100644 index 40374663a..000000000 --- a/applications/hid_analyzer/hid_analyzer_app.h +++ /dev/null @@ -1,65 +0,0 @@ -#pragma once - -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include "view/container_vm.h" - -#include -#include -#include - -#include "helpers/hid_worker.h" - -class HIDApp { -public: - enum class EventType : uint8_t { - GENERIC_EVENT_ENUM_VALUES, - Next, - MenuSelected, - Stay, - Retry, - }; - - enum class SceneType : uint8_t { - GENERIC_SCENE_ENUM_VALUES, - Read, - ReadSuccess, - }; - - class Event { - public: - union { - int32_t menu_index; - } payload; - - EventType type; - }; - - HIDApp(); - ~HIDApp(); - - void run(void* args); - - // private: - SceneController, HIDApp> scene_controller; - ViewController - view_controller; - RecordController notification; - RecordController storage; - RecordController dialogs; - TextStore text_store; - - HIDWorker worker; -}; \ No newline at end of file diff --git a/applications/hid_analyzer/hid_analyzer_app_launcher.cpp b/applications/hid_analyzer/hid_analyzer_app_launcher.cpp deleted file mode 100644 index 93e210d6c..000000000 --- a/applications/hid_analyzer/hid_analyzer_app_launcher.cpp +++ /dev/null @@ -1,10 +0,0 @@ -#include "hid_analyzer_app.h" - -// app enter function -extern "C" int32_t hid_analyzer_app(void* args) { - HIDApp* app = new HIDApp(); - app->run(args); - delete app; - - return 0; -} diff --git a/applications/hid_analyzer/scene/hid_analyzer_app_scene_read.cpp b/applications/hid_analyzer/scene/hid_analyzer_app_scene_read.cpp deleted file mode 100644 index 8e7801614..000000000 --- a/applications/hid_analyzer/scene/hid_analyzer_app_scene_read.cpp +++ /dev/null @@ -1,40 +0,0 @@ -#include "hid_analyzer_app_scene_read.h" -#include - -void HIDAppSceneRead::on_enter(HIDApp* app, bool /* need_restore */) { - auto popup = app->view_controller.get(); - - DOLPHIN_DEED(DolphinDeedRfidRead); - popup->set_header("Searching for\nLF HID RFID", 89, 34, AlignCenter, AlignTop); - popup->set_icon(0, 3, &I_RFIDDolphinReceive_97x61); - - app->view_controller.switch_to(); - app->worker.start_read(); -} - -bool HIDAppSceneRead::on_event(HIDApp* app, HIDApp::Event* event) { - bool consumed = false; - - if(event->type == HIDApp::EventType::Tick) { - if(app->worker.read()) { - DOLPHIN_DEED(DolphinDeedRfidReadSuccess); - notification_message(app->notification, &sequence_success); - app->scene_controller.switch_to_next_scene(HIDApp::SceneType::ReadSuccess); - } else { - if(app->worker.any_read()) { - notification_message(app->notification, &sequence_blink_green_10); - } else if(app->worker.detect()) { - notification_message(app->notification, &sequence_blink_cyan_10); - } else { - notification_message(app->notification, &sequence_blink_cyan_10); - } - } - } - - return consumed; -} - -void HIDAppSceneRead::on_exit(HIDApp* app) { - app->view_controller.get()->clean(); - app->worker.stop_read(); -} diff --git a/applications/hid_analyzer/scene/hid_analyzer_app_scene_read.h b/applications/hid_analyzer/scene/hid_analyzer_app_scene_read.h deleted file mode 100644 index c486f390e..000000000 --- a/applications/hid_analyzer/scene/hid_analyzer_app_scene_read.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once -#include "../hid_analyzer_app.h" - -class HIDAppSceneRead : public GenericScene { -public: - void on_enter(HIDApp* app, bool need_restore) final; - bool on_event(HIDApp* app, HIDApp::Event* event) final; - void on_exit(HIDApp* app) final; -}; diff --git a/applications/hid_analyzer/scene/hid_analyzer_app_scene_read_success.cpp b/applications/hid_analyzer/scene/hid_analyzer_app_scene_read_success.cpp deleted file mode 100644 index 30bb63939..000000000 --- a/applications/hid_analyzer/scene/hid_analyzer_app_scene_read_success.cpp +++ /dev/null @@ -1,78 +0,0 @@ -#include "hid_analyzer_app_scene_read_success.h" -#include "../view/elements/button_element.h" -#include "../view/elements/icon_element.h" -#include "../view/elements/string_element.h" - -void HIDAppSceneReadSuccess::on_enter(HIDApp* app, bool /* need_restore */) { - string_init(string[0]); - string_init(string[1]); - string_init(string[2]); - - auto container = app->view_controller.get(); - - auto button = container->add(); - button->set_type(ButtonElement::Type::Left, "Retry"); - button->set_callback(app, HIDAppSceneReadSuccess::back_callback); - - auto icon = container->add(); - icon->set_icon(3, 12, &I_RFIDBigChip_37x36); - - auto header = container->add(); - header->set_text("HID", 89, 3, 0, AlignCenter); - - // auto line_1_text = container->add(); - auto line_2_text = container->add(); - // auto line_3_text = container->add(); - - // auto line_1_value = container->add(); - auto line_2_value = container->add(); - // auto line_3_value = container->add(); - - const uint8_t* data = app->worker.key.get_data(); - - // line_1_text->set_text("Hi:", 65, 23, 0, AlignRight, AlignBottom, FontSecondary); - line_2_text->set_text("Bit:", 65, 35, 0, AlignRight, AlignBottom, FontSecondary); - // line_3_text->set_text("Bye:", 65, 47, 0, AlignRight, AlignBottom, FontSecondary); - - string_printf(string[1], "%u", data[0]); - - // line_1_value->set_text( - // string_get_cstr(string[0]), 68, 23, 0, AlignLeft, AlignBottom, FontSecondary); - line_2_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); - - app->view_controller.switch_to(); - - notification_message_block(app->notification, &sequence_set_green_255); -} - -bool HIDAppSceneReadSuccess::on_event(HIDApp* app, HIDApp::Event* event) { - bool consumed = false; - - if(event->type == HIDApp::EventType::Retry) { - app->scene_controller.search_and_switch_to_previous_scene({HIDApp::SceneType::Read}); - consumed = true; - } else if(event->type == HIDApp::EventType::Back) { - app->scene_controller.search_and_switch_to_previous_scene({HIDApp::SceneType::Read}); - consumed = true; - } - - return consumed; -} - -void HIDAppSceneReadSuccess::on_exit(HIDApp* app) { - notification_message_block(app->notification, &sequence_reset_green); - app->view_controller.get()->clean(); - string_clear(string[0]); - string_clear(string[1]); - string_clear(string[2]); -} - -void HIDAppSceneReadSuccess::back_callback(void* context) { - HIDApp* app = static_cast(context); - HIDApp::Event event; - event.type = HIDApp::EventType::Retry; - app->view_controller.send_event(&event); -} diff --git a/applications/hid_analyzer/scene/hid_analyzer_app_scene_read_success.h b/applications/hid_analyzer/scene/hid_analyzer_app_scene_read_success.h deleted file mode 100644 index 2668a57bd..000000000 --- a/applications/hid_analyzer/scene/hid_analyzer_app_scene_read_success.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once -#include "../hid_analyzer_app.h" - -class HIDAppSceneReadSuccess : public GenericScene { -public: - void on_enter(HIDApp* app, bool need_restore) final; - bool on_event(HIDApp* app, HIDApp::Event* event) final; - void on_exit(HIDApp* app) final; - -private: - static void back_callback(void* context); - - string_t string[3]; -}; diff --git a/applications/hid_analyzer/view/container_vm.h b/applications/hid_analyzer/view/container_vm.h deleted file mode 100644 index 011baa2e9..000000000 --- a/applications/hid_analyzer/view/container_vm.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once -#include - -class ContainerVM : public GenericViewModule { -public: - ContainerVM(); - ~ContainerVM() final; - View* get_view() final; - void clean() final; - - template T* add(); - -private: - View* view; - static void view_draw_callback(Canvas* canvas, void* model); - static bool view_input_callback(InputEvent* event, void* context); -}; diff --git a/applications/hid_analyzer/view/elements/button_element.h b/applications/hid_analyzer/view/elements/button_element.h deleted file mode 100644 index eb9644277..000000000 --- a/applications/hid_analyzer/view/elements/button_element.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once -#include "generic_element.h" - -typedef void (*ButtonElementCallback)(void* context); - -class ButtonElement : public GenericElement { -public: - ButtonElement(); - ~ButtonElement() final; - void draw(Canvas* canvas) final; - bool input(InputEvent* event) final; - - enum class Type : uint8_t { - Left, - Center, - Right, - }; - - void set_type(Type type, const char* text); - void set_callback(void* context, ButtonElementCallback callback); - -private: - Type type = Type::Left; - const char* text = nullptr; - - void* context = nullptr; - ButtonElementCallback callback = nullptr; -}; diff --git a/applications/hid_analyzer/view/elements/generic_element.h b/applications/hid_analyzer/view/elements/generic_element.h deleted file mode 100644 index f5a58b2d9..000000000 --- a/applications/hid_analyzer/view/elements/generic_element.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once -#include -#include - -class GenericElement { -public: - GenericElement(){}; - virtual ~GenericElement(){}; - virtual void draw(Canvas* canvas) = 0; - virtual bool input(InputEvent* event) = 0; - - // TODO that must be accessible only to ContainerVMData - void set_parent_view(View* view); - - // TODO that must be accessible only to inheritors - void lock_model(); - void unlock_model(bool need_redraw); - -private: - View* view = nullptr; -}; diff --git a/applications/hid_analyzer/view/elements/icon_element.h b/applications/hid_analyzer/view/elements/icon_element.h deleted file mode 100644 index a08202741..000000000 --- a/applications/hid_analyzer/view/elements/icon_element.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once -#include "generic_element.h" - -class IconElement : public GenericElement { -public: - IconElement(); - ~IconElement() final; - void draw(Canvas* canvas) final; - bool input(InputEvent* event) final; - - void set_icon(uint8_t x = 0, uint8_t y = 0, const Icon* icon = NULL); - -private: - const Icon* icon = NULL; - uint8_t x = 0; - uint8_t y = 0; -}; diff --git a/applications/hid_analyzer/view/elements/string_element.h b/applications/hid_analyzer/view/elements/string_element.h deleted file mode 100644 index 173fdd601..000000000 --- a/applications/hid_analyzer/view/elements/string_element.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once -#include "generic_element.h" - -class StringElement : public GenericElement { -public: - StringElement(); - ~StringElement() final; - void draw(Canvas* canvas) final; - bool input(InputEvent* event) final; - - void set_text( - const char* text = NULL, - uint8_t x = 0, - uint8_t y = 0, - uint8_t fit_width = 0, - Align horizontal = AlignLeft, - Align vertical = AlignTop, - Font font = FontPrimary); - -private: - const char* text = NULL; - uint8_t x = 0; - uint8_t y = 0; - uint8_t fit_width = 0; - Align horizontal = AlignLeft; - Align vertical = AlignTop; - Font font = FontPrimary; -}; diff --git a/applications/infrared/scenes/infrared_scene_config.h b/applications/infrared/scenes/infrared_scene_config.h index 111fd2d31..27eabe225 100644 --- a/applications/infrared/scenes/infrared_scene_config.h +++ b/applications/infrared/scenes/infrared_scene_config.h @@ -17,6 +17,7 @@ ADD_SCENE(infrared, universal, Universal) ADD_SCENE(infrared, universal_tv, UniversalTV) ADD_SCENE(infrared, universal_ac, UniversalAC) ADD_SCENE(infrared, universal_audio, UniversalAudio) +ADD_SCENE(infrared, universal_projector, UniversalProjector) ADD_SCENE(infrared, debug, Debug) ADD_SCENE(infrared, error_databases, ErrorDatabases) ADD_SCENE(infrared, rpc, Rpc) diff --git a/applications/infrared/scenes/infrared_scene_universal.c b/applications/infrared/scenes/infrared_scene_universal.c index 8c6fe22b2..36c7d86e1 100644 --- a/applications/infrared/scenes/infrared_scene_universal.c +++ b/applications/infrared/scenes/infrared_scene_universal.c @@ -3,6 +3,7 @@ typedef enum { SubmenuIndexUniversalTV, SubmenuIndexUniversalAudio, + SubmenuIndexUniversalProjector, SubmenuIndexUniversalAirConditioner, } SubmenuIndex; @@ -30,6 +31,13 @@ void infrared_scene_universal_on_enter(void* context) { infrared_scene_universal_submenu_callback, context); + submenu_add_item( + submenu, + "Projectors", + SubmenuIndexUniversalProjector, + infrared_scene_universal_submenu_callback, + context); + submenu_add_item( submenu, "ACs", @@ -52,6 +60,9 @@ bool infrared_scene_universal_on_event(void* context, SceneManagerEvent event) { } else if(event.event == SubmenuIndexUniversalAudio) { scene_manager_next_scene(scene_manager, InfraredSceneUniversalAudio); consumed = true; + } else if(event.event == SubmenuIndexUniversalProjector) { + scene_manager_next_scene(scene_manager, InfraredSceneUniversalProjector); + consumed = true; } else if(event.event == SubmenuIndexUniversalAirConditioner) { scene_manager_next_scene(scene_manager, InfraredSceneUniversalAC); consumed = true; diff --git a/applications/infrared/scenes/infrared_scene_universal_projector.c b/applications/infrared/scenes/infrared_scene_universal_projector.c new file mode 100644 index 000000000..11a0077f5 --- /dev/null +++ b/applications/infrared/scenes/infrared_scene_universal_projector.c @@ -0,0 +1,86 @@ +#include "../infrared_i.h" + +#include "common/infrared_scene_universal_common.h" + +void infrared_scene_universal_projector_on_enter(void* context) { + infrared_scene_universal_common_on_enter(context); + + Infrared* infrared = context; + ButtonPanel* button_panel = infrared->button_panel; + InfraredBruteForce* brute_force = infrared->brute_force; + + infrared_brute_force_set_db_filename(brute_force, EXT_PATH("infrared/assets/projectors.ir")); + //TODO Improve Projectors universal remote + button_panel_reserve(button_panel, 2, 2); + uint32_t i = 0; + button_panel_add_item( + button_panel, + i, + 0, + 0, + 3, + 19, + &I_Power_25x27, + &I_Power_hvr_25x27, + infrared_scene_universal_common_item_callback, + context); + infrared_brute_force_add_record(brute_force, i++, "POWER"); + button_panel_add_item( + button_panel, + i, + 1, + 0, + 36, + 19, + &I_Mute_25x27, + &I_Mute_hvr_25x27, + infrared_scene_universal_common_item_callback, + context); + infrared_brute_force_add_record(brute_force, i++, "MUTE"); + button_panel_add_item( + button_panel, + i, + 0, + 1, + 3, + 64, + &I_Vol_up_25x27, + &I_Vol_up_hvr_25x27, + infrared_scene_universal_common_item_callback, + context); + infrared_brute_force_add_record(brute_force, i++, "VOL+"); + button_panel_add_item( + button_panel, + i, + 1, + 1, + 36, + 64, + &I_Vol_down_25x27, + &I_Vol_down_hvr_25x27, + infrared_scene_universal_common_item_callback, + context); + infrared_brute_force_add_record(brute_force, i++, "VOL-"); + + button_panel_add_label(button_panel, 10, 11, FontPrimary, "Projector"); + button_panel_add_label(button_panel, 17, 60, FontSecondary, "Volume"); + + view_set_orientation(view_stack_get_view(infrared->view_stack), ViewOrientationVertical); + view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewStack); + + infrared_show_loading_popup(infrared, true); + bool success = infrared_brute_force_calculate_messages(brute_force); + infrared_show_loading_popup(infrared, false); + + if(!success) { + scene_manager_next_scene(infrared->scene_manager, InfraredSceneErrorDatabases); + } +} + +bool infrared_scene_universal_projector_on_event(void* context, SceneManagerEvent event) { + return infrared_scene_universal_common_on_event(context, event); +} + +void infrared_scene_universal_projector_on_exit(void* context) { + infrared_scene_universal_common_on_exit(context); +} diff --git a/applications/lfrfid/helpers/decoder_analyzer.cpp b/applications/lfrfid/helpers/decoder_analyzer.cpp deleted file mode 100644 index 8d344b880..000000000 --- a/applications/lfrfid/helpers/decoder_analyzer.cpp +++ /dev/null @@ -1,50 +0,0 @@ -#include "decoder_analyzer.h" -#include -#include - -// FIXME: unused args? -bool DecoderAnalyzer::read(uint8_t* /* _data */, uint8_t /* _data_size */) { - bool result = false; - - if(ready) { - result = true; - - for(size_t i = 0; i < data_size; i++) { - printf("%lu ", data[i]); - if((i + 1) % 8 == 0) printf("\r\n"); - } - printf("\r\n--------\r\n"); - - ready = false; - } - - return result; -} - -void DecoderAnalyzer::process_front(bool polarity, uint32_t time) { - UNUSED(polarity); - if(ready) return; - - data[data_index] = time; - - if(data_index < data_size) { - data_index++; - } else { - data_index = 0; - ready = true; - } -} - -DecoderAnalyzer::DecoderAnalyzer() { - data = reinterpret_cast(calloc(data_size, sizeof(uint32_t))); - furi_check(data); - data_index = 0; - ready = false; -} - -DecoderAnalyzer::~DecoderAnalyzer() { - free(data); -} - -void DecoderAnalyzer::reset_state() { -} diff --git a/applications/lfrfid/helpers/decoder_analyzer.h b/applications/lfrfid/helpers/decoder_analyzer.h deleted file mode 100644 index eecea6edd..000000000 --- a/applications/lfrfid/helpers/decoder_analyzer.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once -#include -#include - -class DecoderAnalyzer { -public: - bool read(uint8_t* data, uint8_t data_size); - void process_front(bool polarity, uint32_t time); - - DecoderAnalyzer(); - ~DecoderAnalyzer(); - -private: - void reset_state(); - - std::atomic ready; - - static const uint32_t data_size = 2048; - uint32_t data_index = 0; - uint32_t* data; -}; diff --git a/applications/lfrfid/helpers/decoder_emmarin.cpp b/applications/lfrfid/helpers/decoder_emmarin.cpp deleted file mode 100644 index daa8e238c..000000000 --- a/applications/lfrfid/helpers/decoder_emmarin.cpp +++ /dev/null @@ -1,72 +0,0 @@ -#include "emmarin.h" -#include "decoder_emmarin.h" -#include -#include - -constexpr uint32_t clocks_in_us = 64; -constexpr uint32_t short_time = 255 * clocks_in_us; -constexpr uint32_t long_time = 510 * clocks_in_us; -constexpr uint32_t jitter_time = 100 * clocks_in_us; - -constexpr uint32_t short_time_low = short_time - jitter_time; -constexpr uint32_t short_time_high = short_time + jitter_time; -constexpr uint32_t long_time_low = long_time - jitter_time; -constexpr uint32_t long_time_high = long_time + jitter_time; - -void DecoderEMMarin::reset_state() { - ready = false; - read_data = 0; - manchester_advance( - manchester_saved_state, ManchesterEventReset, &manchester_saved_state, nullptr); -} - -bool DecoderEMMarin::read(uint8_t* data, uint8_t data_size) { - bool result = false; - - if(ready) { - result = true; - em_marin.decode( - reinterpret_cast(&read_data), sizeof(uint64_t), data, data_size); - ready = false; - } - - return result; -} - -void DecoderEMMarin::process_front(bool polarity, uint32_t time) { - if(ready) return; - if(time < short_time_low) return; - - ManchesterEvent event = ManchesterEventReset; - - if(time > short_time_low && time < short_time_high) { - if(polarity) { - event = ManchesterEventShortHigh; - } else { - event = ManchesterEventShortLow; - } - } else if(time > long_time_low && time < long_time_high) { - if(polarity) { - event = ManchesterEventLongHigh; - } else { - event = ManchesterEventLongLow; - } - } - - if(event != ManchesterEventReset) { - bool data; - bool data_ok = - manchester_advance(manchester_saved_state, event, &manchester_saved_state, &data); - - if(data_ok) { - read_data = (read_data << 1) | data; - - ready = em_marin.can_be_decoded( - reinterpret_cast(&read_data), sizeof(uint64_t)); - } - } -} - -DecoderEMMarin::DecoderEMMarin() { - reset_state(); -} diff --git a/applications/lfrfid/helpers/decoder_emmarin.h b/applications/lfrfid/helpers/decoder_emmarin.h deleted file mode 100644 index a3b48d71c..000000000 --- a/applications/lfrfid/helpers/decoder_emmarin.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once -#include -#include -#include -#include "protocols/protocol_emmarin.h" -class DecoderEMMarin { -public: - bool read(uint8_t* data, uint8_t data_size); - void process_front(bool polarity, uint32_t time); - - DecoderEMMarin(); - -private: - void reset_state(); - - uint64_t read_data = 0; - std::atomic ready; - - ManchesterState manchester_saved_state; - ProtocolEMMarin em_marin; -}; diff --git a/applications/lfrfid/helpers/decoder_gpio_out.cpp b/applications/lfrfid/helpers/decoder_gpio_out.cpp deleted file mode 100644 index dfb434267..000000000 --- a/applications/lfrfid/helpers/decoder_gpio_out.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include "decoder_gpio_out.h" -#include -#include - -void DecoderGpioOut::process_front(bool polarity, uint32_t /* time */) { - furi_hal_gpio_write(&gpio_ext_pa7, polarity); -} - -DecoderGpioOut::DecoderGpioOut() { - furi_hal_gpio_init_simple(&gpio_ext_pa7, GpioModeOutputPushPull); -} - -DecoderGpioOut::~DecoderGpioOut() { - furi_hal_gpio_init_simple(&gpio_ext_pa7, GpioModeAnalog); -} diff --git a/applications/lfrfid/helpers/decoder_gpio_out.h b/applications/lfrfid/helpers/decoder_gpio_out.h deleted file mode 100644 index 087720dfd..000000000 --- a/applications/lfrfid/helpers/decoder_gpio_out.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once -#include -#include - -class DecoderGpioOut { -public: - void process_front(bool polarity, uint32_t time); - - DecoderGpioOut(); - ~DecoderGpioOut(); - -private: - void reset_state(); -}; diff --git a/applications/lfrfid/helpers/decoder_hid26.cpp b/applications/lfrfid/helpers/decoder_hid26.cpp deleted file mode 100644 index e530b5089..000000000 --- a/applications/lfrfid/helpers/decoder_hid26.cpp +++ /dev/null @@ -1,98 +0,0 @@ -#include "decoder_hid26.h" -#include - -constexpr uint32_t clocks_in_us = 64; - -constexpr uint32_t jitter_time_us = 20; -constexpr uint32_t min_time_us = 64; -constexpr uint32_t max_time_us = 80; - -constexpr uint32_t min_time = (min_time_us - jitter_time_us) * clocks_in_us; -constexpr uint32_t mid_time = ((max_time_us - min_time_us) / 2 + min_time_us) * clocks_in_us; -constexpr uint32_t max_time = (max_time_us + jitter_time_us) * clocks_in_us; - -bool DecoderHID26::read(uint8_t* data, uint8_t data_size) { - bool result = false; - furi_assert(data_size >= 3); - - if(ready) { - result = true; - hid.decode( - reinterpret_cast(&stored_data), sizeof(uint32_t) * 3, data, data_size); - ready = false; - } - - return result; -} - -void DecoderHID26::process_front(bool polarity, uint32_t time) { - if(ready) return; - - if(polarity == true) { - last_pulse_time = time; - } else { - last_pulse_time += time; - - if(last_pulse_time > min_time && last_pulse_time < max_time) { - bool pulse; - - if(last_pulse_time < mid_time) { - // 6 pulses - pulse = false; - } else { - // 5 pulses - pulse = true; - } - - if(last_pulse == pulse) { - pulse_count++; - - if(pulse) { - if(pulse_count > 4) { - pulse_count = 0; - store_data(1); - } - } else { - if(pulse_count > 5) { - pulse_count = 0; - store_data(0); - } - } - } else { - if(last_pulse) { - if(pulse_count > 2) { - store_data(1); - } - } else { - if(pulse_count > 3) { - store_data(0); - } - } - - pulse_count = 0; - last_pulse = pulse; - } - } - } -} - -DecoderHID26::DecoderHID26() { - reset_state(); -} - -void DecoderHID26::store_data(bool data) { - stored_data[0] = (stored_data[0] << 1) | ((stored_data[1] >> 31) & 1); - stored_data[1] = (stored_data[1] << 1) | ((stored_data[2] >> 31) & 1); - stored_data[2] = (stored_data[2] << 1) | data; - - if(hid.can_be_decoded(reinterpret_cast(&stored_data), sizeof(uint32_t) * 3)) { - ready = true; - } -} - -void DecoderHID26::reset_state() { - last_pulse = false; - pulse_count = 0; - ready = false; - last_pulse_time = 0; -} diff --git a/applications/lfrfid/helpers/decoder_hid26.h b/applications/lfrfid/helpers/decoder_hid26.h deleted file mode 100644 index c73ff88c4..000000000 --- a/applications/lfrfid/helpers/decoder_hid26.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once -#include -#include -#include "protocols/protocol_hid_h10301.h" - -class DecoderHID26 { -public: - bool read(uint8_t* data, uint8_t data_size); - void process_front(bool polarity, uint32_t time); - DecoderHID26(); - -private: - uint32_t last_pulse_time = 0; - bool last_pulse; - uint8_t pulse_count; - - uint32_t stored_data[3] = {0, 0, 0}; - void store_data(bool data); - - std::atomic ready; - - void reset_state(); - ProtocolHID10301 hid; -}; diff --git a/applications/lfrfid/helpers/decoder_indala.cpp b/applications/lfrfid/helpers/decoder_indala.cpp deleted file mode 100644 index 100dde73b..000000000 --- a/applications/lfrfid/helpers/decoder_indala.cpp +++ /dev/null @@ -1,76 +0,0 @@ -#include "decoder_indala.h" -#include - -constexpr uint32_t clocks_in_us = 64; -constexpr uint32_t us_per_bit = 255; - -bool DecoderIndala::read(uint8_t* data, uint8_t data_size) { - bool result = false; - - if(ready) { - result = true; - if(cursed_data_valid) { - indala.decode( - reinterpret_cast(&cursed_raw_data), - sizeof(uint64_t), - data, - data_size); - } else { - indala.decode( - reinterpret_cast(&raw_data), sizeof(uint64_t), data, data_size); - } - reset_state(); - } - - return result; -} - -void DecoderIndala::process_front(bool polarity, uint32_t time) { - if(ready) return; - - process_internal(polarity, time, &raw_data); - if(ready) return; - - if(polarity) { - time = time + 110; - } else { - time = time - 110; - } - - process_internal(!polarity, time, &cursed_raw_data); - if(ready) { - cursed_data_valid = true; - } -} - -void DecoderIndala::process_internal(bool polarity, uint32_t time, uint64_t* data) { - time /= clocks_in_us; - time += (us_per_bit / 2); - - uint32_t bit_count = (time / us_per_bit); - - if(bit_count < 64) { - for(uint32_t i = 0; i < bit_count; i++) { - *data = (*data << 1) | polarity; - - if((*data >> 32) == 0xa0000000ULL) { - if(indala.can_be_decoded( - reinterpret_cast(data), sizeof(uint64_t))) { - ready = true; - break; - } - } - } - } -} - -DecoderIndala::DecoderIndala() { - reset_state(); -} - -void DecoderIndala::reset_state() { - raw_data = 0; - cursed_raw_data = 0; - ready = false; - cursed_data_valid = false; -} diff --git a/applications/lfrfid/helpers/decoder_indala.h b/applications/lfrfid/helpers/decoder_indala.h deleted file mode 100644 index 5fbde7b6b..000000000 --- a/applications/lfrfid/helpers/decoder_indala.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once -#include -#include -#include -#include "protocols/protocol_indala_40134.h" - -class DecoderIndala { -public: - bool read(uint8_t* data, uint8_t data_size); - void process_front(bool polarity, uint32_t time); - - void process_internal(bool polarity, uint32_t time, uint64_t* data); - - DecoderIndala(); - -private: - void reset_state(); - - uint64_t raw_data; - uint64_t cursed_raw_data; - - std::atomic ready; - std::atomic cursed_data_valid; - ProtocolIndala40134 indala; -}; diff --git a/applications/lfrfid/helpers/decoder_ioprox.cpp b/applications/lfrfid/helpers/decoder_ioprox.cpp deleted file mode 100644 index 7b44d3cea..000000000 --- a/applications/lfrfid/helpers/decoder_ioprox.cpp +++ /dev/null @@ -1,107 +0,0 @@ -#include "decoder_ioprox.h" -#include -#include -#include - -constexpr uint32_t clocks_in_us = 64; - -constexpr uint32_t jitter_time_us = 20; -constexpr uint32_t min_time_us = 64; -constexpr uint32_t max_time_us = 80; -constexpr uint32_t baud_time_us = 500; - -constexpr uint32_t min_time = (min_time_us - jitter_time_us) * clocks_in_us; -constexpr uint32_t mid_time = ((max_time_us - min_time_us) / 2 + min_time_us) * clocks_in_us; -constexpr uint32_t max_time = (max_time_us + jitter_time_us) * clocks_in_us; -constexpr uint32_t baud_time = baud_time_us * clocks_in_us; - -bool DecoderIoProx::read(uint8_t* data, uint8_t data_size) { - bool result = false; - furi_assert(data_size >= 4); - - if(ready) { - result = true; - ioprox.decode(raw_data, sizeof(raw_data), data, data_size); - ready = false; - } - - return result; -} - -void DecoderIoProx::process_front(bool is_rising_edge, uint32_t time) { - if(ready) { - return; - } - - // Always track the time that's gone by. - current_period_duration += time; - demodulation_sample_duration += time; - - // If a baud time has elapsed, we're at a sample point. - if(demodulation_sample_duration >= baud_time) { - // Start a new baud period... - demodulation_sample_duration = 0; - demodulated_value_invalid = false; - - // ... and if we didn't have any baud errors, capture a sample. - if(!demodulated_value_invalid) { - store_data(current_demodulated_value); - } - } - - // - // FSK demodulator. - // - - // If this isn't a rising edge, this isn't a pulse of interest. - // We're done. - if(!is_rising_edge) { - return; - } - - bool is_valid_low = (current_period_duration > min_time) && - (current_period_duration <= mid_time); - bool is_valid_high = (current_period_duration > mid_time) && - (current_period_duration < max_time); - - // If this is between the minimum and our threshold, this is a logical 0. - if(is_valid_low) { - current_demodulated_value = false; - } - // Otherwise, if between our threshold and the max time, it's a logical 1. - else if(is_valid_high) { - current_demodulated_value = true; - } - // Otherwise, invalidate this sample. - else { - demodulated_value_invalid = true; - } - - // We're starting a new period; track that. - current_period_duration = 0; -} - -DecoderIoProx::DecoderIoProx() { - reset_state(); -} - -void DecoderIoProx::store_data(bool data) { - for(int i = 0; i < 7; ++i) { - raw_data[i] = (raw_data[i] << 1) | ((raw_data[i + 1] >> 7) & 1); - } - raw_data[7] = (raw_data[7] << 1) | data; - - if(ioprox.can_be_decoded(raw_data, sizeof(raw_data))) { - ready = true; - } -} - -void DecoderIoProx::reset_state() { - current_demodulated_value = false; - demodulated_value_invalid = false; - - current_period_duration = 0; - demodulation_sample_duration = 0; - - ready = false; -} diff --git a/applications/lfrfid/helpers/decoder_ioprox.h b/applications/lfrfid/helpers/decoder_ioprox.h deleted file mode 100644 index aff4a4778..000000000 --- a/applications/lfrfid/helpers/decoder_ioprox.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once -#include -#include -#include "protocols/protocol_ioprox.h" - -class DecoderIoProx { -public: - bool read(uint8_t* data, uint8_t data_size); - void process_front(bool polarity, uint32_t time); - DecoderIoProx(); - -private: - uint32_t current_period_duration = 0; - uint32_t demodulation_sample_duration = 0; - - bool current_demodulated_value = false; - bool demodulated_value_invalid = false; - - uint8_t raw_data[8] = {0}; - void store_data(bool data); - - std::atomic ready; - - void reset_state(); - ProtocolIoProx ioprox; -}; diff --git a/applications/lfrfid/helpers/emmarin.h b/applications/lfrfid/helpers/emmarin.h deleted file mode 100644 index ff8273bf7..000000000 --- a/applications/lfrfid/helpers/emmarin.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once -#include - -#define EM_HEADER_POS 55 -#define EM_HEADER_MASK (0x1FFLLU << EM_HEADER_POS) - -#define EM_FIRST_ROW_POS 50 -#define EM_ROW_COUNT 10 - -#define EM_COLUMN_POS 4 -#define EM_STOP_POS 0 -#define EM_STOP_MASK (0x1LLU << EM_STOP_POS) - -#define EM_HEADER_AND_STOP_MASK (EM_HEADER_MASK | EM_STOP_MASK) -#define EM_HEADER_AND_STOP_DATA (EM_HEADER_MASK) diff --git a/applications/lfrfid/helpers/encoder_emmarin.cpp b/applications/lfrfid/helpers/encoder_emmarin.cpp deleted file mode 100644 index c329ab400..000000000 --- a/applications/lfrfid/helpers/encoder_emmarin.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include "encoder_emmarin.h" -#include "protocols/protocol_emmarin.h" -#include - -void EncoderEM::init(const uint8_t* data, const uint8_t data_size) { - ProtocolEMMarin em_marin; - em_marin.encode(data, data_size, reinterpret_cast(&card_data), sizeof(uint64_t)); - - card_data_index = 0; -} - -// data transmitted as manchester encoding -// 0 - high2low -// 1 - low2high -void EncoderEM::get_next(bool* polarity, uint16_t* period, uint16_t* pulse) { - *period = clocks_per_bit; - *pulse = clocks_per_bit / 2; - *polarity = (card_data >> (63 - card_data_index)) & 1; - - card_data_index++; - if(card_data_index >= 64) { - card_data_index = 0; - } -} diff --git a/applications/lfrfid/helpers/encoder_emmarin.h b/applications/lfrfid/helpers/encoder_emmarin.h deleted file mode 100644 index 560daec1b..000000000 --- a/applications/lfrfid/helpers/encoder_emmarin.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once -#include "encoder_generic.h" - -class EncoderEM : public EncoderGeneric { -public: - /** - * @brief init data to emulate - * - * @param data 1 byte FC, next 4 byte SN - * @param data_size must be 5 - */ - void init(const uint8_t* data, const uint8_t data_size) final; - - void get_next(bool* polarity, uint16_t* period, uint16_t* pulse) final; - -private: - // clock pulses per bit - static const uint8_t clocks_per_bit = 64; - - uint64_t card_data; - uint8_t card_data_index; -}; diff --git a/applications/lfrfid/helpers/encoder_generic.h b/applications/lfrfid/helpers/encoder_generic.h deleted file mode 100644 index dc6ae8503..000000000 --- a/applications/lfrfid/helpers/encoder_generic.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once -#include -#include - -class EncoderGeneric { -public: - /** - * @brief init encoder - * - * @param data data array - * @param data_size data array size - */ - virtual void init(const uint8_t* data, const uint8_t data_size) = 0; - - /** - * @brief Get the next timer pulse - * - * @param polarity pulse polarity true = high2low, false = low2high - * @param period overall period time in timer clicks - * @param pulse pulse time in timer clicks - */ - virtual void get_next(bool* polarity, uint16_t* period, uint16_t* pulse) = 0; - - virtual ~EncoderGeneric(){}; - -private: -}; diff --git a/applications/lfrfid/helpers/encoder_hid_h10301.cpp b/applications/lfrfid/helpers/encoder_hid_h10301.cpp deleted file mode 100644 index 09f637fee..000000000 --- a/applications/lfrfid/helpers/encoder_hid_h10301.cpp +++ /dev/null @@ -1,46 +0,0 @@ -#include "encoder_hid_h10301.h" -#include "protocols/protocol_hid_h10301.h" -#include - -void EncoderHID_H10301::init(const uint8_t* data, const uint8_t data_size) { - ProtocolHID10301 hid; - hid.encode(data, data_size, reinterpret_cast(&card_data), sizeof(card_data) * 3); - - card_data_index = 0; -} - -void EncoderHID_H10301::write_bit(bool bit, uint8_t position) { - write_raw_bit(bit, position + 0); - write_raw_bit(!bit, position + 1); -} - -void EncoderHID_H10301::write_raw_bit(bool bit, uint8_t position) { - if(bit) { - card_data[position / 32] |= 1UL << (31 - (position % 32)); - } else { - card_data[position / 32] &= ~(1UL << (31 - (position % 32))); - } -} - -void EncoderHID_H10301::get_next(bool* polarity, uint16_t* period, uint16_t* pulse) { - uint8_t bit = (card_data[card_data_index / 32] >> (31 - (card_data_index % 32))) & 1; - - bool advance = fsk->next(bit, period); - if(advance) { - card_data_index++; - if(card_data_index >= (32 * card_data_max)) { - card_data_index = 0; - } - } - - *polarity = true; - *pulse = *period / 2; -} - -EncoderHID_H10301::EncoderHID_H10301() { - fsk = new OscFSK(8, 10, 50); -} - -EncoderHID_H10301::~EncoderHID_H10301() { - delete fsk; -} diff --git a/applications/lfrfid/helpers/encoder_hid_h10301.h b/applications/lfrfid/helpers/encoder_hid_h10301.h deleted file mode 100644 index 8cc5aa5cb..000000000 --- a/applications/lfrfid/helpers/encoder_hid_h10301.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once -#include "encoder_generic.h" -#include "osc_fsk.h" - -class EncoderHID_H10301 : public EncoderGeneric { -public: - /** - * @brief init data to emulate - * - * @param data 1 byte FC, next 2 byte SN - * @param data_size must be 3 - */ - void init(const uint8_t* data, const uint8_t data_size) final; - void get_next(bool* polarity, uint16_t* period, uint16_t* pulse) final; - EncoderHID_H10301(); - ~EncoderHID_H10301(); - -private: - static const uint8_t card_data_max = 3; - uint32_t card_data[card_data_max]; - uint8_t card_data_index; - void write_bit(bool bit, uint8_t position); - void write_raw_bit(bool bit, uint8_t position); - - OscFSK* fsk; -}; diff --git a/applications/lfrfid/helpers/encoder_indala_40134.cpp b/applications/lfrfid/helpers/encoder_indala_40134.cpp deleted file mode 100644 index 764237d1f..000000000 --- a/applications/lfrfid/helpers/encoder_indala_40134.cpp +++ /dev/null @@ -1,36 +0,0 @@ -#include "encoder_indala_40134.h" -#include "protocols/protocol_indala_40134.h" -#include - -void EncoderIndala_40134::init(const uint8_t* data, const uint8_t data_size) { - ProtocolIndala40134 indala; - indala.encode(data, data_size, reinterpret_cast(&card_data), sizeof(card_data)); - - last_bit = card_data & 1; - card_data_index = 0; - current_polarity = true; -} - -void EncoderIndala_40134::get_next(bool* polarity, uint16_t* period, uint16_t* pulse) { - *period = 2; - *pulse = 1; - *polarity = current_polarity; - - bit_clock_index++; - if(bit_clock_index >= clock_per_bit) { - bit_clock_index = 0; - - bool current_bit = (card_data >> (63 - card_data_index)) & 1; - - if(current_bit != last_bit) { - current_polarity = !current_polarity; - } - - last_bit = current_bit; - - card_data_index++; - if(card_data_index >= 64) { - card_data_index = 0; - } - } -} diff --git a/applications/lfrfid/helpers/encoder_indala_40134.h b/applications/lfrfid/helpers/encoder_indala_40134.h deleted file mode 100644 index eda29457f..000000000 --- a/applications/lfrfid/helpers/encoder_indala_40134.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once -#include "encoder_generic.h" - -class EncoderIndala_40134 : public EncoderGeneric { -public: - /** - * @brief init data to emulate - * - * @param data indala raw data - * @param data_size must be 5 - */ - void init(const uint8_t* data, const uint8_t data_size) final; - - void get_next(bool* polarity, uint16_t* period, uint16_t* pulse) final; - -private: - uint64_t card_data; - uint8_t card_data_index; - uint8_t bit_clock_index; - bool last_bit; - bool current_polarity; - static const uint8_t clock_per_bit = 16; -}; diff --git a/applications/lfrfid/helpers/encoder_ioprox.cpp b/applications/lfrfid/helpers/encoder_ioprox.cpp deleted file mode 100644 index 417799186..000000000 --- a/applications/lfrfid/helpers/encoder_ioprox.cpp +++ /dev/null @@ -1,32 +0,0 @@ -#include "encoder_ioprox.h" -#include "protocols/protocol_ioprox.h" -#include - -void EncoderIoProx::init(const uint8_t* data, const uint8_t data_size) { - ProtocolIoProx ioprox; - ioprox.encode(data, data_size, card_data, sizeof(card_data)); - card_data_index = 0; -} - -void EncoderIoProx::get_next(bool* polarity, uint16_t* period, uint16_t* pulse) { - uint8_t bit = (card_data[card_data_index / 8] >> (7 - (card_data_index % 8))) & 1; - - bool advance = fsk->next(bit, period); - if(advance) { - card_data_index++; - if(card_data_index >= (8 * card_data_max)) { - card_data_index = 0; - } - } - - *polarity = true; - *pulse = *period / 2; -} - -EncoderIoProx::EncoderIoProx() { - fsk = new OscFSK(8, 10, 64); -} - -EncoderIoProx::~EncoderIoProx() { - delete fsk; -} diff --git a/applications/lfrfid/helpers/encoder_ioprox.h b/applications/lfrfid/helpers/encoder_ioprox.h deleted file mode 100644 index 568b40671..000000000 --- a/applications/lfrfid/helpers/encoder_ioprox.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once -#include "encoder_generic.h" -#include "osc_fsk.h" - -class EncoderIoProx : public EncoderGeneric { -public: - /** - * @brief init data to emulate - * - * @param data 1 byte FC, 1 byte Version, 2 bytes code - * @param data_size must be 4 - */ - void init(const uint8_t* data, const uint8_t data_size) final; - void get_next(bool* polarity, uint16_t* period, uint16_t* pulse) final; - EncoderIoProx(); - ~EncoderIoProx(); - -private: - static const uint8_t card_data_max = 8; - - uint8_t card_data[card_data_max]; - uint8_t card_data_index; - - OscFSK* fsk; -}; diff --git a/applications/lfrfid/helpers/key_info.cpp b/applications/lfrfid/helpers/key_info.cpp deleted file mode 100644 index 4803cd6dc..000000000 --- a/applications/lfrfid/helpers/key_info.cpp +++ /dev/null @@ -1,76 +0,0 @@ -#include "key_info.h" -#include - -const char* lfrfid_key_get_type_string(LfrfidKeyType type) { - switch(type) { - case LfrfidKeyType::KeyEM4100: - return "EM4100"; - break; - case LfrfidKeyType::KeyH10301: - return "H10301"; - break; - case LfrfidKeyType::KeyI40134: - return "I40134"; - break; - case LfrfidKeyType::KeyIoProxXSF: - return "IoProxXSF"; - break; - } - - return "Unknown"; -} - -const char* lfrfid_key_get_manufacturer_string(LfrfidKeyType type) { - switch(type) { - case LfrfidKeyType::KeyEM4100: - return "EM-Marin"; - break; - case LfrfidKeyType::KeyH10301: - return "HID"; - break; - case LfrfidKeyType::KeyI40134: - return "Indala"; - break; - case LfrfidKeyType::KeyIoProxXSF: - return "Kantech"; - } - - return "Unknown"; -} - -bool lfrfid_key_get_string_type(const char* string, LfrfidKeyType* type) { - bool result = true; - - if(strcmp("EM4100", string) == 0) { - *type = LfrfidKeyType::KeyEM4100; - } else if(strcmp("H10301", string) == 0) { - *type = LfrfidKeyType::KeyH10301; - } else if(strcmp("I40134", string) == 0) { - *type = LfrfidKeyType::KeyI40134; - } else if(strcmp("IoProxXSF", string) == 0) { - *type = LfrfidKeyType::KeyIoProxXSF; - } else { - result = false; - } - - return result; -} - -uint8_t lfrfid_key_get_type_data_count(LfrfidKeyType type) { - switch(type) { - case LfrfidKeyType::KeyEM4100: - return 5; - break; - case LfrfidKeyType::KeyH10301: - return 3; - break; - case LfrfidKeyType::KeyI40134: - return 3; - break; - case LfrfidKeyType::KeyIoProxXSF: - return 4; - break; - } - - return 0; -} diff --git a/applications/lfrfid/helpers/key_info.h b/applications/lfrfid/helpers/key_info.h deleted file mode 100644 index 75a0a9406..000000000 --- a/applications/lfrfid/helpers/key_info.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once -#include - -static const uint8_t LFRFID_KEY_SIZE = 8; -static const uint8_t LFRFID_KEY_NAME_SIZE = 22; - -enum class LfrfidKeyType : uint8_t { - KeyEM4100, - KeyH10301, - KeyI40134, - KeyIoProxXSF, -}; - -const char* lfrfid_key_get_type_string(LfrfidKeyType type); -const char* lfrfid_key_get_manufacturer_string(LfrfidKeyType type); -bool lfrfid_key_get_string_type(const char* string, LfrfidKeyType* type); -uint8_t lfrfid_key_get_type_data_count(LfrfidKeyType type); diff --git a/applications/lfrfid/helpers/osc_fsk.cpp b/applications/lfrfid/helpers/osc_fsk.cpp deleted file mode 100644 index 5f3b5367b..000000000 --- a/applications/lfrfid/helpers/osc_fsk.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include "osc_fsk.h" - -OscFSK::OscFSK(uint16_t _freq_low, uint16_t _freq_hi, uint16_t _osc_phase_max) - : freq{_freq_low, _freq_hi} - , osc_phase_max(_osc_phase_max) { - osc_phase_current = 0; -} - -bool OscFSK::next(bool bit, uint16_t* period) { - bool advance = false; - *period = freq[bit]; - osc_phase_current += *period; - - if(osc_phase_current > osc_phase_max) { - advance = true; - osc_phase_current -= osc_phase_max; - } - - return advance; -} diff --git a/applications/lfrfid/helpers/osc_fsk.h b/applications/lfrfid/helpers/osc_fsk.h deleted file mode 100644 index eaaaa10ad..000000000 --- a/applications/lfrfid/helpers/osc_fsk.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once -#include - -/** - * This code tries to fit the periods into a given number of cycles (phases) by taking cycles from the next cycle of periods. - */ -class OscFSK { -public: - /** - * Get next period - * @param bit bit value - * @param period return period - * @return bool whether to advance to the next bit - */ - bool next(bool bit, uint16_t* period); - - /** - * FSK ocillator constructor - * - * @param freq_low bit 0 freq - * @param freq_hi bit 1 freq - * @param osc_phase_max max oscillator phase - */ - OscFSK(uint16_t freq_low, uint16_t freq_hi, uint16_t osc_phase_max); - -private: - const uint16_t freq[2]; - const uint16_t osc_phase_max; - int32_t osc_phase_current; -}; diff --git a/applications/lfrfid/helpers/protocols/protocol_emmarin.cpp b/applications/lfrfid/helpers/protocols/protocol_emmarin.cpp deleted file mode 100644 index 4ac180e30..000000000 --- a/applications/lfrfid/helpers/protocols/protocol_emmarin.cpp +++ /dev/null @@ -1,150 +0,0 @@ -#include "protocol_emmarin.h" -#include - -#define EM_HEADER_POS 55 -#define EM_HEADER_MASK (0x1FFLLU << EM_HEADER_POS) - -#define EM_FIRST_ROW_POS 50 - -#define EM_ROW_COUNT 10 -#define EM_COLUMN_COUNT 4 -#define EM_BITS_PER_ROW_COUNT (EM_COLUMN_COUNT + 1) - -#define EM_COLUMN_POS 4 -#define EM_STOP_POS 0 -#define EM_STOP_MASK (0x1LLU << EM_STOP_POS) - -#define EM_HEADER_AND_STOP_MASK (EM_HEADER_MASK | EM_STOP_MASK) -#define EM_HEADER_AND_STOP_DATA (EM_HEADER_MASK) - -typedef uint64_t EMMarinCardData; - -void write_nibble(bool low_nibble, uint8_t data, EMMarinCardData* card_data) { - uint8_t parity_sum = 0; - uint8_t start = 0; - if(!low_nibble) start = 4; - - for(int8_t i = (start + 3); i >= start; i--) { - parity_sum += (data >> i) & 1; - *card_data = (*card_data << 1) | ((data >> i) & 1); - } - - *card_data = (*card_data << 1) | ((parity_sum % 2) & 1); -} - -uint8_t ProtocolEMMarin::get_encoded_data_size() { - return sizeof(EMMarinCardData); -} - -uint8_t ProtocolEMMarin::get_decoded_data_size() { - return 5; -} - -void ProtocolEMMarin::encode( - const uint8_t* decoded_data, - const uint8_t decoded_data_size, - uint8_t* encoded_data, - const uint8_t encoded_data_size) { - furi_check(decoded_data_size >= get_decoded_data_size()); - furi_check(encoded_data_size >= get_encoded_data_size()); - - EMMarinCardData card_data; - - // header - card_data = 0b111111111; - - // data - for(uint8_t i = 0; i < get_decoded_data_size(); i++) { - write_nibble(false, decoded_data[i], &card_data); - write_nibble(true, decoded_data[i], &card_data); - } - - // column parity and stop bit - uint8_t parity_sum; - - for(uint8_t c = 0; c < EM_COLUMN_COUNT; c++) { - parity_sum = 0; - for(uint8_t i = 1; i <= EM_ROW_COUNT; i++) { - uint8_t parity_bit = (card_data >> (i * EM_BITS_PER_ROW_COUNT - 1)) & 1; - parity_sum += parity_bit; - } - card_data = (card_data << 1) | ((parity_sum % 2) & 1); - } - - // stop bit - card_data = (card_data << 1) | 0; - - memcpy(encoded_data, &card_data, get_encoded_data_size()); -} - -void ProtocolEMMarin::decode( - const uint8_t* encoded_data, - const uint8_t encoded_data_size, - uint8_t* decoded_data, - const uint8_t decoded_data_size) { - furi_check(decoded_data_size >= get_decoded_data_size()); - furi_check(encoded_data_size >= get_encoded_data_size()); - - uint8_t decoded_data_index = 0; - EMMarinCardData card_data = *(reinterpret_cast(encoded_data)); - - // clean result - memset(decoded_data, 0, decoded_data_size); - - // header - for(uint8_t i = 0; i < 9; i++) { - card_data = card_data << 1; - } - - // nibbles - uint8_t value = 0; - for(uint8_t r = 0; r < EM_ROW_COUNT; r++) { - uint8_t nibble = 0; - for(uint8_t i = 0; i < 5; i++) { - if(i < 4) nibble = (nibble << 1) | (card_data & (1LLU << 63) ? 1 : 0); - card_data = card_data << 1; - } - value = (value << 4) | nibble; - if(r % 2) { - decoded_data[decoded_data_index] |= value; - decoded_data_index++; - value = 0; - } - } -} - -bool ProtocolEMMarin::can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) { - furi_check(encoded_data_size >= get_encoded_data_size()); - const EMMarinCardData* card_data = reinterpret_cast(encoded_data); - - // check header and stop bit - if((*card_data & EM_HEADER_AND_STOP_MASK) != EM_HEADER_AND_STOP_DATA) return false; - - // check row parity - for(uint8_t i = 0; i < EM_ROW_COUNT; i++) { - uint8_t parity_sum = 0; - - for(uint8_t j = 0; j < EM_BITS_PER_ROW_COUNT; j++) { - parity_sum += (*card_data >> (EM_FIRST_ROW_POS - i * EM_BITS_PER_ROW_COUNT + j)) & 1; - } - - if((parity_sum % 2)) { - return false; - } - } - - // check columns parity - for(uint8_t i = 0; i < EM_COLUMN_COUNT; i++) { - uint8_t parity_sum = 0; - - for(uint8_t j = 0; j < EM_ROW_COUNT + 1; j++) { - parity_sum += (*card_data >> (EM_COLUMN_POS - i + j * EM_BITS_PER_ROW_COUNT)) & 1; - } - - if((parity_sum % 2)) { - return false; - } - } - - return true; -} diff --git a/applications/lfrfid/helpers/protocols/protocol_emmarin.h b/applications/lfrfid/helpers/protocols/protocol_emmarin.h deleted file mode 100644 index 7a866f909..000000000 --- a/applications/lfrfid/helpers/protocols/protocol_emmarin.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once -#include "protocol_generic.h" - -class ProtocolEMMarin : public ProtocolGeneric { -public: - uint8_t get_encoded_data_size() final; - uint8_t get_decoded_data_size() final; - - void encode( - const uint8_t* decoded_data, - const uint8_t decoded_data_size, - uint8_t* encoded_data, - const uint8_t encoded_data_size) final; - - void decode( - const uint8_t* encoded_data, - const uint8_t encoded_data_size, - uint8_t* decoded_data, - const uint8_t decoded_data_size) final; - - bool can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) final; -}; diff --git a/applications/lfrfid/helpers/protocols/protocol_generic.h b/applications/lfrfid/helpers/protocols/protocol_generic.h deleted file mode 100644 index d593f7089..000000000 --- a/applications/lfrfid/helpers/protocols/protocol_generic.h +++ /dev/null @@ -1,60 +0,0 @@ -#pragma once -#include "stdint.h" -#include "stdbool.h" - -class ProtocolGeneric { -public: - /** - * @brief Get the encoded data size - * - * @return uint8_t size of encoded data in bytes - */ - virtual uint8_t get_encoded_data_size() = 0; - - /** - * @brief Get the decoded data size - * - * @return uint8_t size of decoded data in bytes - */ - virtual uint8_t get_decoded_data_size() = 0; - - /** - * @brief encode decoded data - * - * @param decoded_data - * @param decoded_data_size - * @param encoded_data - * @param encoded_data_size - */ - virtual void encode( - const uint8_t* decoded_data, - const uint8_t decoded_data_size, - uint8_t* encoded_data, - const uint8_t encoded_data_size) = 0; - - /** - * @brief decode encoded data - * - * @param encoded_data - * @param encoded_data_size - * @param decoded_data - * @param decoded_data_size - */ - virtual void decode( - const uint8_t* encoded_data, - const uint8_t encoded_data_size, - uint8_t* decoded_data, - const uint8_t decoded_data_size) = 0; - - /** - * @brief fast check that data can be correctly decoded - * - * @param encoded_data - * @param encoded_data_size - * @return true - can be correctly decoded - * @return false - cannot be correctly decoded - */ - virtual bool can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) = 0; - - virtual ~ProtocolGeneric(){}; -}; diff --git a/applications/lfrfid/helpers/protocols/protocol_hid_h10301.cpp b/applications/lfrfid/helpers/protocols/protocol_hid_h10301.cpp deleted file mode 100644 index b71838838..000000000 --- a/applications/lfrfid/helpers/protocols/protocol_hid_h10301.cpp +++ /dev/null @@ -1,238 +0,0 @@ -#include "protocol_hid_h10301.h" -#include - -typedef uint32_t HID10301CardData; -constexpr uint8_t HID10301Count = 3; -constexpr uint8_t HID10301BitSize = sizeof(HID10301CardData) * 8; - -static void write_raw_bit(bool bit, uint8_t position, HID10301CardData* card_data) { - if(bit) { - card_data[position / HID10301BitSize] |= - 1UL << (HID10301BitSize - (position % HID10301BitSize) - 1); - } else { - card_data[position / (sizeof(HID10301CardData) * 8)] &= - ~(1UL << (HID10301BitSize - (position % HID10301BitSize) - 1)); - } -} - -static void write_bit(bool bit, uint8_t position, HID10301CardData* card_data) { - write_raw_bit(bit, position + 0, card_data); - write_raw_bit(!bit, position + 1, card_data); -} - -uint8_t ProtocolHID10301::get_encoded_data_size() { - return sizeof(HID10301CardData) * HID10301Count; -} - -uint8_t ProtocolHID10301::get_decoded_data_size() { - return 3; -} - -void ProtocolHID10301::encode( - const uint8_t* decoded_data, - const uint8_t decoded_data_size, - uint8_t* encoded_data, - const uint8_t encoded_data_size) { - furi_check(decoded_data_size >= get_decoded_data_size()); - furi_check(encoded_data_size >= get_encoded_data_size()); - - HID10301CardData card_data[HID10301Count] = {0, 0, 0}; - - uint32_t fc_cn = (decoded_data[0] << 16) | (decoded_data[1] << 8) | decoded_data[2]; - - // even parity sum calculation (high 12 bits of data) - uint8_t even_parity_sum = 0; - for(int8_t i = 12; i < 24; i++) { - if(((fc_cn >> i) & 1) == 1) { - even_parity_sum++; - } - } - - // odd parity sum calculation (low 12 bits of data) - uint8_t odd_parity_sum = 1; - for(int8_t i = 0; i < 12; i++) { - if(((fc_cn >> i) & 1) == 1) { - odd_parity_sum++; - } - } - - // 0x1D preamble - write_raw_bit(0, 0, card_data); - write_raw_bit(0, 1, card_data); - write_raw_bit(0, 2, card_data); - write_raw_bit(1, 3, card_data); - write_raw_bit(1, 4, card_data); - write_raw_bit(1, 5, card_data); - write_raw_bit(0, 6, card_data); - write_raw_bit(1, 7, card_data); - - // company / OEM code 1 - write_bit(0, 8, card_data); - write_bit(0, 10, card_data); - write_bit(0, 12, card_data); - write_bit(0, 14, card_data); - write_bit(0, 16, card_data); - write_bit(0, 18, card_data); - write_bit(1, 20, card_data); - - // card format / length 1 - write_bit(0, 22, card_data); - write_bit(0, 24, card_data); - write_bit(0, 26, card_data); - write_bit(0, 28, card_data); - write_bit(0, 30, card_data); - write_bit(0, 32, card_data); - write_bit(0, 34, card_data); - write_bit(0, 36, card_data); - write_bit(0, 38, card_data); - write_bit(0, 40, card_data); - write_bit(1, 42, card_data); - - // even parity bit - write_bit((even_parity_sum % 2), 44, card_data); - - // data - for(uint8_t i = 0; i < 24; i++) { - write_bit((fc_cn >> (23 - i)) & 1, 46 + (i * 2), card_data); - } - - // odd parity bit - write_bit((odd_parity_sum % 2), 94, card_data); - - memcpy(encoded_data, &card_data, get_encoded_data_size()); -} - -void ProtocolHID10301::decode( - const uint8_t* encoded_data, - const uint8_t encoded_data_size, - uint8_t* decoded_data, - const uint8_t decoded_data_size) { - furi_check(decoded_data_size >= get_decoded_data_size()); - furi_check(encoded_data_size >= get_encoded_data_size()); - - const HID10301CardData* card_data = reinterpret_cast(encoded_data); - - // data decoding - uint32_t result = 0; - - // decode from word 1 - // coded with 01 = 0, 10 = 1 transitions - for(int8_t i = 9; i >= 0; i--) { - switch((*(card_data + 1) >> (2 * i)) & 0b11) { - case 0b01: - result = (result << 1) | 0; - break; - case 0b10: - result = (result << 1) | 1; - break; - default: - break; - } - } - - // decode from word 2 - // coded with 01 = 0, 10 = 1 transitions - for(int8_t i = 15; i >= 0; i--) { - switch((*(card_data + 2) >> (2 * i)) & 0b11) { - case 0b01: - result = (result << 1) | 0; - break; - case 0b10: - result = (result << 1) | 1; - break; - default: - break; - } - } - - uint8_t data[3] = {(uint8_t)(result >> 17), (uint8_t)(result >> 9), (uint8_t)(result >> 1)}; - - memcpy(decoded_data, &data, get_decoded_data_size()); -} - -bool ProtocolHID10301::can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) { - furi_check(encoded_data_size >= get_encoded_data_size()); - - const HID10301CardData* card_data = reinterpret_cast(encoded_data); - - // packet preamble - // raw data - if(*(encoded_data + 3) != 0x1D) { - return false; - } - - // encoded company/oem - // coded with 01 = 0, 10 = 1 transitions - // stored in word 0 - if((*card_data >> 10 & 0x3FFF) != 0x1556) { - return false; - } - - // encoded format/length - // coded with 01 = 0, 10 = 1 transitions - // stored in word 0 and word 1 - if((((*card_data & 0x3FF) << 12) | ((*(card_data + 1) >> 20) & 0xFFF)) != 0x155556) { - return false; - } - - // data decoding - uint32_t result = 0; - - // decode from word 1 - // coded with 01 = 0, 10 = 1 transitions - for(int8_t i = 9; i >= 0; i--) { - switch((*(card_data + 1) >> (2 * i)) & 0b11) { - case 0b01: - result = (result << 1) | 0; - break; - case 0b10: - result = (result << 1) | 1; - break; - default: - return false; - break; - } - } - - // decode from word 2 - // coded with 01 = 0, 10 = 1 transitions - for(int8_t i = 15; i >= 0; i--) { - switch((*(card_data + 2) >> (2 * i)) & 0b11) { - case 0b01: - result = (result << 1) | 0; - break; - case 0b10: - result = (result << 1) | 1; - break; - default: - return false; - break; - } - } - - // trailing parity (odd) test - uint8_t parity_sum = 0; - for(int8_t i = 0; i < 13; i++) { - if(((result >> i) & 1) == 1) { - parity_sum++; - } - } - - if((parity_sum % 2) != 1) { - return false; - } - - // leading parity (even) test - parity_sum = 0; - for(int8_t i = 13; i < 26; i++) { - if(((result >> i) & 1) == 1) { - parity_sum++; - } - } - - if((parity_sum % 2) == 1) { - return false; - } - - return true; -} diff --git a/applications/lfrfid/helpers/protocols/protocol_hid_h10301.h b/applications/lfrfid/helpers/protocols/protocol_hid_h10301.h deleted file mode 100644 index fbd6e0b2b..000000000 --- a/applications/lfrfid/helpers/protocols/protocol_hid_h10301.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once -#include "protocol_generic.h" - -class ProtocolHID10301 : public ProtocolGeneric { -public: - uint8_t get_encoded_data_size() final; - uint8_t get_decoded_data_size() final; - - void encode( - const uint8_t* decoded_data, - const uint8_t decoded_data_size, - uint8_t* encoded_data, - const uint8_t encoded_data_size) final; - - void decode( - const uint8_t* encoded_data, - const uint8_t encoded_data_size, - uint8_t* decoded_data, - const uint8_t decoded_data_size) final; - - bool can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) final; -}; diff --git a/applications/lfrfid/helpers/protocols/protocol_indala_40134.cpp b/applications/lfrfid/helpers/protocols/protocol_indala_40134.cpp deleted file mode 100644 index 482339def..000000000 --- a/applications/lfrfid/helpers/protocols/protocol_indala_40134.cpp +++ /dev/null @@ -1,237 +0,0 @@ -#include "protocol_indala_40134.h" -#include - -typedef uint64_t Indala40134CardData; - -static void set_bit(bool bit, uint8_t position, Indala40134CardData* card_data) { - position = (sizeof(Indala40134CardData) * 8) - 1 - position; - if(bit) { - *card_data |= 1ull << position; - } else { - *card_data &= ~(1ull << position); - } -} - -static bool get_bit(uint8_t position, const Indala40134CardData* card_data) { - position = (sizeof(Indala40134CardData) * 8) - 1 - position; - return (*card_data >> position) & 1; -} - -uint8_t ProtocolIndala40134::get_encoded_data_size() { - return sizeof(Indala40134CardData); -} - -uint8_t ProtocolIndala40134::get_decoded_data_size() { - return 3; -} - -void ProtocolIndala40134::encode( - const uint8_t* decoded_data, - const uint8_t decoded_data_size, - uint8_t* encoded_data, - const uint8_t encoded_data_size) { - furi_check(decoded_data_size >= get_decoded_data_size()); - furi_check(encoded_data_size >= get_encoded_data_size()); - - uint32_t fc_and_card = (decoded_data[0] << 16) | (decoded_data[1] << 8) | decoded_data[2]; - Indala40134CardData card_data = 0; - - // preamble - set_bit(1, 0, &card_data); - set_bit(1, 2, &card_data); - set_bit(1, 32, &card_data); - - // factory code - set_bit(((fc_and_card >> 23) & 1), 57, &card_data); - set_bit(((fc_and_card >> 22) & 1), 49, &card_data); - set_bit(((fc_and_card >> 21) & 1), 44, &card_data); - set_bit(((fc_and_card >> 20) & 1), 47, &card_data); - set_bit(((fc_and_card >> 19) & 1), 48, &card_data); - set_bit(((fc_and_card >> 18) & 1), 53, &card_data); - set_bit(((fc_and_card >> 17) & 1), 39, &card_data); - set_bit(((fc_and_card >> 16) & 1), 58, &card_data); - - // card number - set_bit(((fc_and_card >> 15) & 1), 42, &card_data); - set_bit(((fc_and_card >> 14) & 1), 45, &card_data); - set_bit(((fc_and_card >> 13) & 1), 43, &card_data); - set_bit(((fc_and_card >> 12) & 1), 40, &card_data); - set_bit(((fc_and_card >> 11) & 1), 52, &card_data); - set_bit(((fc_and_card >> 10) & 1), 36, &card_data); - set_bit(((fc_and_card >> 9) & 1), 35, &card_data); - set_bit(((fc_and_card >> 8) & 1), 51, &card_data); - set_bit(((fc_and_card >> 7) & 1), 46, &card_data); - set_bit(((fc_and_card >> 6) & 1), 33, &card_data); - set_bit(((fc_and_card >> 5) & 1), 37, &card_data); - set_bit(((fc_and_card >> 4) & 1), 54, &card_data); - set_bit(((fc_and_card >> 3) & 1), 56, &card_data); - set_bit(((fc_and_card >> 2) & 1), 59, &card_data); - set_bit(((fc_and_card >> 1) & 1), 50, &card_data); - set_bit(((fc_and_card >> 0) & 1), 41, &card_data); - - // checksum - uint8_t checksum = 0; - checksum += ((fc_and_card >> 14) & 1); - checksum += ((fc_and_card >> 12) & 1); - checksum += ((fc_and_card >> 9) & 1); - checksum += ((fc_and_card >> 8) & 1); - checksum += ((fc_and_card >> 6) & 1); - checksum += ((fc_and_card >> 5) & 1); - checksum += ((fc_and_card >> 2) & 1); - checksum += ((fc_and_card >> 0) & 1); - - // wiegand parity bits - // even parity sum calculation (high 12 bits of data) - uint8_t even_parity_sum = 0; - for(int8_t i = 12; i < 24; i++) { - if(((fc_and_card >> i) & 1) == 1) { - even_parity_sum++; - } - } - - // odd parity sum calculation (low 12 bits of data) - uint8_t odd_parity_sum = 1; - for(int8_t i = 0; i < 12; i++) { - if(((fc_and_card >> i) & 1) == 1) { - odd_parity_sum++; - } - } - - // even parity bit - set_bit((even_parity_sum % 2), 34, &card_data); - - // odd parity bit - set_bit((odd_parity_sum % 2), 38, &card_data); - - // checksum - if((checksum & 1) == 1) { - set_bit(0, 62, &card_data); - set_bit(1, 63, &card_data); - } else { - set_bit(1, 62, &card_data); - set_bit(0, 63, &card_data); - } - - memcpy(encoded_data, &card_data, get_encoded_data_size()); -} - -// factory code -static uint8_t get_fc(const Indala40134CardData* card_data) { - uint8_t fc = 0; - - fc = fc << 1 | get_bit(57, card_data); - fc = fc << 1 | get_bit(49, card_data); - fc = fc << 1 | get_bit(44, card_data); - fc = fc << 1 | get_bit(47, card_data); - fc = fc << 1 | get_bit(48, card_data); - fc = fc << 1 | get_bit(53, card_data); - fc = fc << 1 | get_bit(39, card_data); - fc = fc << 1 | get_bit(58, card_data); - - return fc; -} - -// card number -static uint16_t get_cn(const Indala40134CardData* card_data) { - uint16_t cn = 0; - - cn = cn << 1 | get_bit(42, card_data); - cn = cn << 1 | get_bit(45, card_data); - cn = cn << 1 | get_bit(43, card_data); - cn = cn << 1 | get_bit(40, card_data); - cn = cn << 1 | get_bit(52, card_data); - cn = cn << 1 | get_bit(36, card_data); - cn = cn << 1 | get_bit(35, card_data); - cn = cn << 1 | get_bit(51, card_data); - cn = cn << 1 | get_bit(46, card_data); - cn = cn << 1 | get_bit(33, card_data); - cn = cn << 1 | get_bit(37, card_data); - cn = cn << 1 | get_bit(54, card_data); - cn = cn << 1 | get_bit(56, card_data); - cn = cn << 1 | get_bit(59, card_data); - cn = cn << 1 | get_bit(50, card_data); - cn = cn << 1 | get_bit(41, card_data); - - return cn; -} - -void ProtocolIndala40134::decode( - const uint8_t* encoded_data, - const uint8_t encoded_data_size, - uint8_t* decoded_data, - const uint8_t decoded_data_size) { - furi_check(decoded_data_size >= get_decoded_data_size()); - furi_check(encoded_data_size >= get_encoded_data_size()); - - const Indala40134CardData* card_data = - reinterpret_cast(encoded_data); - - uint8_t fc = get_fc(card_data); - uint16_t card = get_cn(card_data); - - decoded_data[0] = fc; - decoded_data[1] = card >> 8; - decoded_data[2] = card; -} - -bool ProtocolIndala40134::can_be_decoded( - const uint8_t* encoded_data, - const uint8_t encoded_data_size) { - furi_check(encoded_data_size >= get_encoded_data_size()); - bool can_be_decoded = false; - - const Indala40134CardData* card_data = - reinterpret_cast(encoded_data); - - do { - // preambula - if((*card_data >> 32) != 0xa0000000UL) break; - - // data - const uint32_t fc_and_card = get_fc(card_data) << 16 | get_cn(card_data); - - // checksum - const uint8_t checksum = get_bit(62, card_data) << 1 | get_bit(63, card_data); - uint8_t checksum_sum = 0; - checksum_sum += ((fc_and_card >> 14) & 1); - checksum_sum += ((fc_and_card >> 12) & 1); - checksum_sum += ((fc_and_card >> 9) & 1); - checksum_sum += ((fc_and_card >> 8) & 1); - checksum_sum += ((fc_and_card >> 6) & 1); - checksum_sum += ((fc_and_card >> 5) & 1); - checksum_sum += ((fc_and_card >> 2) & 1); - checksum_sum += ((fc_and_card >> 0) & 1); - checksum_sum = checksum_sum & 0b1; - - if(checksum_sum == 1 && checksum == 0b01) { - } else if(checksum_sum == 0 && checksum == 0b10) { - } else { - break; - } - - // wiegand parity bits - // even parity sum calculation (high 12 bits of data) - const bool even_parity = get_bit(34, card_data); - uint8_t even_parity_sum = 0; - for(int8_t i = 12; i < 24; i++) { - if(((fc_and_card >> i) & 1) == 1) { - even_parity_sum++; - } - } - if(even_parity_sum % 2 != even_parity) break; - - // odd parity sum calculation (low 12 bits of data) - const bool odd_parity = get_bit(38, card_data); - uint8_t odd_parity_sum = 1; - for(int8_t i = 0; i < 12; i++) { - if(((fc_and_card >> i) & 1) == 1) { - odd_parity_sum++; - } - } - if(odd_parity_sum % 2 != odd_parity) break; - - can_be_decoded = true; - } while(false); - - return can_be_decoded; -} diff --git a/applications/lfrfid/helpers/protocols/protocol_indala_40134.h b/applications/lfrfid/helpers/protocols/protocol_indala_40134.h deleted file mode 100644 index d378bb2ce..000000000 --- a/applications/lfrfid/helpers/protocols/protocol_indala_40134.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once -#include "protocol_generic.h" - -class ProtocolIndala40134 : public ProtocolGeneric { -public: - uint8_t get_encoded_data_size() final; - uint8_t get_decoded_data_size() final; - - void encode( - const uint8_t* decoded_data, - const uint8_t decoded_data_size, - uint8_t* encoded_data, - const uint8_t encoded_data_size) final; - - void decode( - const uint8_t* encoded_data, - const uint8_t encoded_data_size, - uint8_t* decoded_data, - const uint8_t decoded_data_size) final; - - bool can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) final; -}; diff --git a/applications/lfrfid/helpers/protocols/protocol_ioprox.cpp b/applications/lfrfid/helpers/protocols/protocol_ioprox.cpp deleted file mode 100644 index b3b6a0e59..000000000 --- a/applications/lfrfid/helpers/protocols/protocol_ioprox.cpp +++ /dev/null @@ -1,193 +0,0 @@ -#include "protocol_ioprox.h" -#include -#include - -/** - * Writes a bit into the output buffer. - */ -static void write_bit(bool bit, uint8_t position, uint8_t* data) { - if(bit) { - data[position / 8] |= 1UL << (7 - (position % 8)); - } else { - data[position / 8] &= ~(1UL << (7 - (position % 8))); - } -} - -/** - * Writes up to eight contiguous bits into the output buffer. - */ -static void write_bits(uint8_t byte, uint8_t position, uint8_t* data, uint8_t length) { - furi_check(length <= 8); - furi_check(length > 0); - - for(uint8_t i = 0; i < length; ++i) { - uint8_t shift = 7 - i; - write_bit((byte >> shift) & 1, position + i, data); - } -} - -uint8_t ProtocolIoProx::get_encoded_data_size() { - return 8; -} - -uint8_t ProtocolIoProx::get_decoded_data_size() { - return 4; -} - -void ProtocolIoProx::encode( - const uint8_t* decoded_data, - const uint8_t decoded_data_size, - uint8_t* encoded_data, - const uint8_t encoded_data_size) { - furi_check(decoded_data_size >= get_decoded_data_size()); - furi_check(encoded_data_size >= get_encoded_data_size()); - - // Packet to transmit: - // - // 0 10 20 30 40 50 60 - // v v v v v v v - // 01234567 8 90123456 7 89012345 6 78901234 5 67890123 4 56789012 3 45678901 23 - // ----------------------------------------------------------------------------- - // 00000000 0 11110000 1 facility 1 version_ 1 code-one 1 code-two 1 checksum 11 - - // Preamble. - write_bits(0b00000000, 0, encoded_data, 8); - write_bit(0, 8, encoded_data); - - write_bits(0b11110000, 9, encoded_data, 8); - write_bit(1, 17, encoded_data); - - // Facility code. - write_bits(decoded_data[0], 18, encoded_data, 8); - write_bit(1, 26, encoded_data); - - // Version - write_bits(decoded_data[1], 27, encoded_data, 8); - write_bit(1, 35, encoded_data); - - // Code one - write_bits(decoded_data[2], 36, encoded_data, 8); - write_bit(1, 44, encoded_data); - - // Code two - write_bits(decoded_data[3], 45, encoded_data, 8); - write_bit(1, 53, encoded_data); - - // Checksum - write_bits(compute_checksum(encoded_data, 8), 54, encoded_data, 8); - write_bit(1, 62, encoded_data); - write_bit(1, 63, encoded_data); -} - -void ProtocolIoProx::decode( - const uint8_t* encoded_data, - const uint8_t encoded_data_size, - uint8_t* decoded_data, - const uint8_t decoded_data_size) { - furi_check(decoded_data_size >= get_decoded_data_size()); - furi_check(encoded_data_size >= get_encoded_data_size()); - - // Packet structure: - // (Note: the second word seems fixed; but this may not be a guarantee; - // it currently has no meaning.) - // - //0 1 2 3 4 5 6 7 - //v v v v v v v v - //01234567 89ABCDEF 01234567 89ABCDEF 01234567 89ABCDEF 01234567 89ABCDEF - //----------------------------------------------------------------------- - //00000000 01111000 01FFFFFF FF1VVVVV VVV1CCCC CCCC1CCC CCCCC1XX XXXXXX11 - // - // F = facility code - // V = version - // C = code - // X = checksum - - // Facility code - decoded_data[0] = (encoded_data[2] << 2) | (encoded_data[3] >> 6); - - // Version code. - decoded_data[1] = (encoded_data[3] << 3) | (encoded_data[4] >> 5); - - // Code bytes. - decoded_data[2] = (encoded_data[4] << 4) | (encoded_data[5] >> 4); - decoded_data[3] = (encoded_data[5] << 5) | (encoded_data[6] >> 3); -} - -bool ProtocolIoProx::can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) { - furi_check(encoded_data_size >= get_encoded_data_size()); - - // Packet framing - // - //0 1 2 3 4 5 6 7 - //v v v v v v v v - //01234567 89ABCDEF 01234567 89ABCDEF 01234567 89ABCDEF 01234567 89ABCDEF - //----------------------------------------------------------------------- - //00000000 01______ _1______ __1_____ ___1____ ____1___ _____1XX XXXXXX11 - // - // _ = variable data - // 0 = preamble 0 - // 1 = framing 1 - // X = checksum - - // Validate the packet preamble is there... - if(encoded_data[0] != 0b00000000) { - return false; - } - if((encoded_data[1] >> 6) != 0b01) { - return false; - } - - // ... check for known ones... - if((encoded_data[2] & 0b01000000) == 0) { - return false; - } - if((encoded_data[3] & 0b00100000) == 0) { - return false; - } - if((encoded_data[4] & 0b00010000) == 0) { - return false; - } - if((encoded_data[5] & 0b00001000) == 0) { - return false; - } - if((encoded_data[6] & 0b00000100) == 0) { - return false; - } - if((encoded_data[7] & 0b00000011) == 0) { - return false; - } - - // ... and validate our checksums. - uint8_t checksum = compute_checksum(encoded_data, 8); - uint8_t checkval = (encoded_data[6] << 6) | (encoded_data[7] >> 2); - - if(checksum != checkval) { - return false; - } - - return true; -} - -uint8_t ProtocolIoProx::compute_checksum(const uint8_t* data, const uint8_t data_size) { - furi_check(data_size == get_encoded_data_size()); - - // Packet structure: - // - //0 1 2 3 4 5 6 7 - //v v v v v v v v - //01234567 8 9ABCDEF0 1 23456789 A BCDEF012 3 456789AB C DEF01234 5 6789ABCD EF - //00000000 0 VVVVVVVV 1 WWWWWWWW 1 XXXXXXXX 1 YYYYYYYY 1 ZZZZZZZZ 1 CHECKSUM 11 - // - // algorithm as observed by the proxmark3 folks - // CHECKSUM == 0xFF - (V + W + X + Y + Z) - - uint8_t checksum = 0; - - checksum += (data[1] << 1) | (data[2] >> 7); // VVVVVVVVV - checksum += (data[2] << 2) | (data[3] >> 6); // WWWWWWWWW - checksum += (data[3] << 3) | (data[4] >> 5); // XXXXXXXXX - checksum += (data[4] << 4) | (data[5] >> 4); // YYYYYYYYY - checksum += (data[5] << 5) | (data[6] >> 3); // ZZZZZZZZZ - - return 0xFF - checksum; -} diff --git a/applications/lfrfid/helpers/protocols/protocol_ioprox.h b/applications/lfrfid/helpers/protocols/protocol_ioprox.h deleted file mode 100644 index 2fff53b17..000000000 --- a/applications/lfrfid/helpers/protocols/protocol_ioprox.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once -#include "protocol_generic.h" - -class ProtocolIoProx : public ProtocolGeneric { -public: - uint8_t get_encoded_data_size() final; - uint8_t get_decoded_data_size() final; - - void encode( - const uint8_t* decoded_data, - const uint8_t decoded_data_size, - uint8_t* encoded_data, - const uint8_t encoded_data_size) final; - - void decode( - const uint8_t* encoded_data, - const uint8_t encoded_data_size, - uint8_t* decoded_data, - const uint8_t decoded_data_size) final; - - bool can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) final; - -private: - /** Computes the IoProx checksum of the provided (decoded) data. */ - uint8_t compute_checksum(const uint8_t* data, const uint8_t data_size); -}; diff --git a/applications/lfrfid/helpers/pulse_joiner.cpp b/applications/lfrfid/helpers/pulse_joiner.cpp deleted file mode 100644 index c72019b14..000000000 --- a/applications/lfrfid/helpers/pulse_joiner.cpp +++ /dev/null @@ -1,95 +0,0 @@ -#include "pulse_joiner.h" -#include - -bool PulseJoiner::push_pulse(bool polarity, uint16_t period, uint16_t pulse) { - bool result = false; - furi_check((pulse_index + 1) < pulse_max); - - if(polarity == false && pulse_index == 0) { - // first negative pulse is ommited - - } else { - pulses[pulse_index].polarity = polarity; - pulses[pulse_index].time = pulse; - pulse_index++; - } - - if(period > pulse) { - pulses[pulse_index].polarity = !polarity; - pulses[pulse_index].time = period - pulse; - pulse_index++; - } - - if(pulse_index >= 4) { - // we know that first pulse is always high - // so we wait 2 edges, hi2low and next low2hi - - uint8_t edges_count = 0; - bool last_polarity = pulses[0].polarity; - - for(uint8_t i = 1; i < pulse_index; i++) { - if(pulses[i].polarity != last_polarity) { - edges_count++; - last_polarity = pulses[i].polarity; - } - } - - if(edges_count >= 2) { - result = true; - } - } - - return result; -} - -void PulseJoiner::pop_pulse(uint16_t* period, uint16_t* pulse) { - furi_check(pulse_index <= (pulse_max + 1)); - - uint16_t tmp_period = 0; - uint16_t tmp_pulse = 0; - uint8_t edges_count = 0; - bool last_polarity = pulses[0].polarity; - uint8_t next_fist_pulse = 0; - - for(uint8_t i = 0; i < pulse_max; i++) { - // count edges - if(pulses[i].polarity != last_polarity) { - edges_count++; - last_polarity = pulses[i].polarity; - } - - // wait for 2 edges - if(edges_count == 2) { - next_fist_pulse = i; - break; - } - - // sum pulse time - if(pulses[i].polarity) { - tmp_period += pulses[i].time; - tmp_pulse += pulses[i].time; - } else { - tmp_period += pulses[i].time; - } - pulse_index--; - } - - *period = tmp_period; - *pulse = tmp_pulse; - - // remove counted periods and shift data - for(uint8_t i = 0; i < pulse_max; i++) { - if((next_fist_pulse + i) < pulse_max) { - pulses[i].polarity = pulses[next_fist_pulse + i].polarity; - pulses[i].time = pulses[next_fist_pulse + i].time; - } else { - break; - } - } -} - -PulseJoiner::PulseJoiner() { - for(uint8_t i = 0; i < pulse_max; i++) { - pulses[i] = {false, 0}; - } -} diff --git a/applications/lfrfid/helpers/pulse_joiner.h b/applications/lfrfid/helpers/pulse_joiner.h deleted file mode 100644 index 1639d8371..000000000 --- a/applications/lfrfid/helpers/pulse_joiner.h +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once -#include "stdint.h" - -class PulseJoiner { -public: - /** - * @brief Push timer pulse. First negative pulse is ommited. - * - * @param polarity pulse polarity: true = high2low, false = low2high - * @param period overall period time in timer clicks - * @param pulse pulse time in timer clicks - * - * @return true - next pulse can and must be popped immediatly - */ - bool push_pulse(bool polarity, uint16_t period, uint16_t pulse); - - /** - * @brief Get the next timer pulse. Call only if push_pulse returns true. - * - * @param period overall period time in timer clicks - * @param pulse pulse time in timer clicks - */ - void pop_pulse(uint16_t* period, uint16_t* pulse); - - PulseJoiner(); - -private: - struct Pulse { - bool polarity; - uint16_t time; - }; - - uint8_t pulse_index = 0; - static const uint8_t pulse_max = 6; - Pulse pulses[pulse_max]; -}; diff --git a/applications/lfrfid/helpers/rfid_key.cpp b/applications/lfrfid/helpers/rfid_key.cpp deleted file mode 100644 index 2d99d40f2..000000000 --- a/applications/lfrfid/helpers/rfid_key.cpp +++ /dev/null @@ -1,65 +0,0 @@ -#include "rfid_key.h" -#include -#include - -RfidKey::RfidKey() { - clear(); -} - -RfidKey::~RfidKey() { -} - -void RfidKey::set_type(LfrfidKeyType _type) { - type = _type; -} - -void RfidKey::set_data(const uint8_t* _data, const uint8_t _data_size) { - furi_assert(_data_size <= data.size()); - for(uint8_t i = 0; i < _data_size; i++) { - data[i] = _data[i]; - } -} - -void RfidKey::set_name(const char* _name) { - strlcpy(name, _name, get_name_length()); -} - -LfrfidKeyType RfidKey::get_type() { - return type; -} - -const uint8_t* RfidKey::get_data() { - return &data[0]; -} - -const char* RfidKey::get_type_text() { - return lfrfid_key_get_type_string(type); -} - -uint8_t RfidKey::get_type_data_count() const { - return lfrfid_key_get_type_data_count(type); -} - -char* RfidKey::get_name() { - return name; -} - -uint8_t RfidKey::get_name_length() { - return LFRFID_KEY_NAME_SIZE; -} - -void RfidKey::clear() { - set_name(""); - set_type(LfrfidKeyType::KeyEM4100); - data.fill(0); -} - -RfidKey& RfidKey::operator=(const RfidKey& rhs) { - if(this == &rhs) return *this; - - set_type(rhs.type); - set_name(rhs.name); - set_data(&rhs.data[0], get_type_data_count()); - - return *this; -} diff --git a/applications/lfrfid/helpers/rfid_key.h b/applications/lfrfid/helpers/rfid_key.h deleted file mode 100644 index 29b87cf9c..000000000 --- a/applications/lfrfid/helpers/rfid_key.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once -#include "key_info.h" -#include - -class RfidKey { -public: - RfidKey(); - ~RfidKey(); - - void set_type(LfrfidKeyType type); - void set_data(const uint8_t* data, const uint8_t data_size); - void set_name(const char* name); - - LfrfidKeyType get_type(); - const uint8_t* get_data(); - const char* get_type_text(); - uint8_t get_type_data_count() const; - char* get_name(); - uint8_t get_name_length(); - void clear(); - RfidKey& operator=(const RfidKey& rhs); - -private: - std::array data; - LfrfidKeyType type; - char name[LFRFID_KEY_NAME_SIZE + 1]; -}; diff --git a/applications/lfrfid/helpers/rfid_reader.cpp b/applications/lfrfid/helpers/rfid_reader.cpp deleted file mode 100644 index 029b1cb4b..000000000 --- a/applications/lfrfid/helpers/rfid_reader.cpp +++ /dev/null @@ -1,175 +0,0 @@ -#include "rfid_reader.h" -#include -#include -#include - -/** - * @brief private violation assistant for RfidReader - */ -struct RfidReaderAccessor { - static void decode(RfidReader& rfid_reader, bool polarity) { - rfid_reader.decode(polarity); - } -}; - -void RfidReader::decode(bool polarity) { - uint32_t current_dwt_value = DWT->CYCCNT; - uint32_t period = current_dwt_value - last_dwt_value; - last_dwt_value = current_dwt_value; - -#ifdef RFID_GPIO_DEBUG - decoder_gpio_out.process_front(polarity, period); -#endif - - switch(type) { - case Type::Normal: - decoder_em.process_front(polarity, period); - decoder_hid26.process_front(polarity, period); - decoder_ioprox.process_front(polarity, period); - break; - case Type::Indala: - decoder_em.process_front(polarity, period); - decoder_hid26.process_front(polarity, period); - decoder_ioprox.process_front(polarity, period); - decoder_indala.process_front(polarity, period); - break; - } - - detect_ticks++; -} - -bool RfidReader::switch_timer_elapsed() { - const uint32_t seconds_to_switch = furi_kernel_get_tick_frequency() * 2.0f; - return (furi_get_tick() - switch_os_tick_last) > seconds_to_switch; -} - -void RfidReader::switch_timer_reset() { - switch_os_tick_last = furi_get_tick(); -} - -void RfidReader::switch_mode() { - switch(type) { - case Type::Normal: - type = Type::Indala; - furi_hal_rfid_change_read_config(62500.0f, 0.25f); - break; - case Type::Indala: - type = Type::Normal; - furi_hal_rfid_change_read_config(125000.0f, 0.5f); - break; - } - - switch_timer_reset(); -} - -static void comparator_trigger_callback(bool level, void* comp_ctx) { - RfidReader* _this = static_cast(comp_ctx); - - RfidReaderAccessor::decode(*_this, !level); -} - -RfidReader::RfidReader() { -} - -void RfidReader::start() { - type = Type::Normal; - - furi_hal_rfid_pins_read(); - furi_hal_rfid_tim_read(125000, 0.5); - furi_hal_rfid_tim_read_start(); - start_comparator(); - - switch_timer_reset(); - last_read_count = 0; -} - -void RfidReader::start_forced(RfidReader::Type _type) { - start(); - if(_type == Type::Indala) { - switch_mode(); - } -} - -void RfidReader::stop() { - furi_hal_rfid_pins_reset(); - furi_hal_rfid_tim_read_stop(); - furi_hal_rfid_tim_reset(); - stop_comparator(); -} - -bool RfidReader::read(LfrfidKeyType* _type, uint8_t* data, uint8_t data_size, bool switch_enable) { - bool result = false; - bool something_read = false; - - // reading - if(decoder_em.read(data, data_size)) { - *_type = LfrfidKeyType::KeyEM4100; - something_read = true; - } - - if(decoder_hid26.read(data, data_size)) { - *_type = LfrfidKeyType::KeyH10301; - something_read = true; - } - - if(decoder_ioprox.read(data, data_size)) { - *_type = LfrfidKeyType::KeyIoProxXSF; - something_read = true; - } - - if(decoder_indala.read(data, data_size)) { - *_type = LfrfidKeyType::KeyI40134; - something_read = true; - } - - // validation - if(something_read) { - switch_timer_reset(); - - if(last_read_type == *_type && memcmp(last_read_data, data, data_size) == 0) { - last_read_count = last_read_count + 1; - - if(last_read_count > 2) { - result = true; - } - } else { - last_read_type = *_type; - memcpy(last_read_data, data, data_size); - last_read_count = 0; - } - } - - // mode switching - if(switch_enable && switch_timer_elapsed()) { - switch_mode(); - last_read_count = 0; - } - - return result; -} - -bool RfidReader::detect() { - bool detected = false; - if(detect_ticks > 10) { - detected = true; - } - detect_ticks = 0; - - return detected; -} - -bool RfidReader::any_read() { - return last_read_count > 0; -} - -void RfidReader::start_comparator(void) { - furi_hal_rfid_comp_set_callback(comparator_trigger_callback, this); - last_dwt_value = DWT->CYCCNT; - - furi_hal_rfid_comp_start(); -} - -void RfidReader::stop_comparator(void) { - furi_hal_rfid_comp_stop(); - furi_hal_rfid_comp_set_callback(NULL, NULL); -} diff --git a/applications/lfrfid/helpers/rfid_reader.h b/applications/lfrfid/helpers/rfid_reader.h deleted file mode 100644 index 903bbecf9..000000000 --- a/applications/lfrfid/helpers/rfid_reader.h +++ /dev/null @@ -1,59 +0,0 @@ -#pragma once -//#include "decoder_analyzer.h" -#include "decoder_gpio_out.h" -#include "decoder_emmarin.h" -#include "decoder_hid26.h" -#include "decoder_indala.h" -#include "decoder_ioprox.h" -#include "key_info.h" - -//#define RFID_GPIO_DEBUG 1 - -class RfidReader { -public: - enum class Type : uint8_t { - Normal, - Indala, - }; - - RfidReader(); - void start(); - void start_forced(RfidReader::Type type); - void stop(); - bool read(LfrfidKeyType* type, uint8_t* data, uint8_t data_size, bool switch_enable = true); - - bool detect(); - bool any_read(); - -private: - friend struct RfidReaderAccessor; - - //DecoderAnalyzer decoder_analyzer; -#ifdef RFID_GPIO_DEBUG - DecoderGpioOut decoder_gpio_out; -#endif - DecoderEMMarin decoder_em; - DecoderHID26 decoder_hid26; - DecoderIndala decoder_indala; - DecoderIoProx decoder_ioprox; - - uint32_t last_dwt_value; - - void start_comparator(void); - void stop_comparator(void); - - void decode(bool polarity); - - uint32_t detect_ticks; - - uint32_t switch_os_tick_last; - bool switch_timer_elapsed(); - void switch_timer_reset(); - void switch_mode(); - - LfrfidKeyType last_read_type; - uint8_t last_read_data[LFRFID_KEY_SIZE]; - uint8_t last_read_count; - - Type type = Type::Normal; -}; diff --git a/applications/lfrfid/helpers/rfid_timer_emulator.cpp b/applications/lfrfid/helpers/rfid_timer_emulator.cpp deleted file mode 100644 index f5337c31d..000000000 --- a/applications/lfrfid/helpers/rfid_timer_emulator.cpp +++ /dev/null @@ -1,56 +0,0 @@ -#include "rfid_timer_emulator.h" - -RfidTimerEmulator::RfidTimerEmulator() { -} - -RfidTimerEmulator::~RfidTimerEmulator() { - std::map::iterator it; - - for(it = encoders.begin(); it != encoders.end(); ++it) { - delete it->second; - } - - encoders.clear(); -} - -void RfidTimerEmulator::start(LfrfidKeyType type, const uint8_t* data, uint8_t data_size) { - if(encoders.count(type)) { - current_encoder = encoders.find(type)->second; - - if(data_size >= lfrfid_key_get_type_data_count(type)) { - current_encoder->init(data, data_size); - - furi_hal_rfid_tim_emulate(125000); - furi_hal_rfid_pins_emulate(); - - furi_hal_rfid_tim_emulate_start(RfidTimerEmulator::timer_update_callback, this); - } - } else { - // not found - } -} - -void RfidTimerEmulator::stop() { - furi_hal_rfid_tim_emulate_stop(); - furi_hal_rfid_tim_reset(); - furi_hal_rfid_pins_reset(); -} - -void RfidTimerEmulator::timer_update_callback(void* ctx) { - RfidTimerEmulator* _this = static_cast(ctx); - - bool result; - bool polarity; - uint16_t period; - uint16_t pulse; - - do { - _this->current_encoder->get_next(&polarity, &period, &pulse); - result = _this->pulse_joiner.push_pulse(polarity, period, pulse); - } while(result == false); - - _this->pulse_joiner.pop_pulse(&period, &pulse); - - furi_hal_rfid_set_emulate_period(period - 1); - furi_hal_rfid_set_emulate_pulse(pulse); -} diff --git a/applications/lfrfid/helpers/rfid_timer_emulator.h b/applications/lfrfid/helpers/rfid_timer_emulator.h deleted file mode 100644 index 2129a1c7f..000000000 --- a/applications/lfrfid/helpers/rfid_timer_emulator.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once -#include -#include "key_info.h" -#include "encoder_generic.h" -#include "encoder_emmarin.h" -#include "encoder_hid_h10301.h" -#include "encoder_indala_40134.h" -#include "encoder_ioprox.h" -#include "pulse_joiner.h" -#include - -class RfidTimerEmulator { -public: - RfidTimerEmulator(); - ~RfidTimerEmulator(); - void start(LfrfidKeyType type, const uint8_t* data, uint8_t data_size); - void stop(); - -private: - EncoderGeneric* current_encoder = nullptr; - - std::map encoders = { - {LfrfidKeyType::KeyEM4100, new EncoderEM()}, - {LfrfidKeyType::KeyH10301, new EncoderHID_H10301()}, - {LfrfidKeyType::KeyI40134, new EncoderIndala_40134()}, - {LfrfidKeyType::KeyIoProxXSF, new EncoderIoProx()}, - }; - - PulseJoiner pulse_joiner; - static void timer_update_callback(void* ctx); -}; diff --git a/applications/lfrfid/helpers/rfid_worker.cpp b/applications/lfrfid/helpers/rfid_worker.cpp deleted file mode 100644 index af15a340f..000000000 --- a/applications/lfrfid/helpers/rfid_worker.cpp +++ /dev/null @@ -1,136 +0,0 @@ -#include "rfid_worker.h" - -RfidWorker::RfidWorker() { -} - -RfidWorker::~RfidWorker() { -} - -void RfidWorker::start_read() { - reader.start(); -} - -bool RfidWorker::read() { - static const uint8_t data_size = LFRFID_KEY_SIZE; - uint8_t data[data_size] = {0}; - LfrfidKeyType type; - - bool result = reader.read(&type, data, data_size); - - if(result) { - key.set_type(type); - key.set_data(data, data_size); - }; - - return result; -} - -bool RfidWorker::detect() { - return reader.detect(); -} - -bool RfidWorker::any_read() { - return reader.any_read(); -} - -void RfidWorker::stop_read() { - reader.stop(); -} - -void RfidWorker::start_write() { - write_result = WriteResult::Nothing; - write_sequence = new TickSequencer(); - validate_counts = 0; - - write_sequence->do_every_tick(1, std::bind(&RfidWorker::sq_write, this)); - write_sequence->do_after_tick(2, std::bind(&RfidWorker::sq_write_start_validate, this)); - write_sequence->do_every_tick(30, std::bind(&RfidWorker::sq_write_validate, this)); - write_sequence->do_every_tick(1, std::bind(&RfidWorker::sq_write_stop_validate, this)); -} - -RfidWorker::WriteResult RfidWorker::write() { - write_sequence->tick(); - return write_result; -} - -void RfidWorker::stop_write() { - delete write_sequence; - reader.stop(); -} - -void RfidWorker::start_emulate() { - emulator.start(key.get_type(), key.get_data(), key.get_type_data_count()); -} - -void RfidWorker::stop_emulate() { - emulator.stop(); -} - -void RfidWorker::sq_write() { - for(size_t i = 0; i < 5; i++) { - switch(key.get_type()) { - case LfrfidKeyType::KeyEM4100: - writer.start(); - writer.write_em(key.get_data()); - writer.stop(); - break; - case LfrfidKeyType::KeyH10301: - writer.start(); - writer.write_hid(key.get_data()); - writer.stop(); - break; - case LfrfidKeyType::KeyI40134: - writer.start(); - writer.write_indala(key.get_data()); - writer.stop(); - break; - case LfrfidKeyType::KeyIoProxXSF: - writer.start(); - writer.write_ioprox(key.get_data()); - writer.stop(); - break; - } - } -} - -void RfidWorker::sq_write_start_validate() { - switch(key.get_type()) { - case LfrfidKeyType::KeyEM4100: - case LfrfidKeyType::KeyH10301: - case LfrfidKeyType::KeyIoProxXSF: - reader.start_forced(RfidReader::Type::Normal); - break; - case LfrfidKeyType::KeyI40134: - reader.start_forced(RfidReader::Type::Indala); - break; - } -} - -void RfidWorker::sq_write_validate() { - static const uint8_t data_size = LFRFID_KEY_SIZE; - uint8_t data[data_size] = {0}; - LfrfidKeyType type; - - bool result = reader.read(&type, data, data_size); - - if(result && (write_result != WriteResult::Ok)) { - if(validate_counts > (5 * 60)) { - write_result = WriteResult::NotWritable; - } - - if(type == key.get_type()) { - if(memcmp(data, key.get_data(), key.get_type_data_count()) == 0) { - write_result = WriteResult::Ok; - validate_counts = 0; - } else { - validate_counts++; - } - } else { - validate_counts++; - } - }; -} - -void RfidWorker::sq_write_stop_validate() { - reader.stop(); -} diff --git a/applications/lfrfid/helpers/rfid_worker.h b/applications/lfrfid/helpers/rfid_worker.h deleted file mode 100644 index 2c49ad14e..000000000 --- a/applications/lfrfid/helpers/rfid_worker.h +++ /dev/null @@ -1,48 +0,0 @@ -#pragma once -#include "key_info.h" -#include "rfid_reader.h" -#include "rfid_writer.h" -#include "rfid_timer_emulator.h" -#include "rfid_key.h" -#include "state_sequencer.h" - -class RfidWorker { -public: - RfidWorker(); - ~RfidWorker(); - - void start_read(); - bool read(); - bool detect(); - bool any_read(); - void stop_read(); - - enum class WriteResult : uint8_t { - Ok, - NotWritable, - Nothing, - }; - - void start_write(); - WriteResult write(); - void stop_write(); - - void start_emulate(); - void stop_emulate(); - - RfidKey key; - -private: - RfidWriter writer; - RfidReader reader; - RfidTimerEmulator emulator; - - WriteResult write_result; - TickSequencer* write_sequence; - - void sq_write(); - void sq_write_start_validate(); - void sq_write_validate(); - uint16_t validate_counts; - void sq_write_stop_validate(); -}; diff --git a/applications/lfrfid/helpers/rfid_writer.cpp b/applications/lfrfid/helpers/rfid_writer.cpp deleted file mode 100644 index 31838fde0..000000000 --- a/applications/lfrfid/helpers/rfid_writer.cpp +++ /dev/null @@ -1,183 +0,0 @@ -#include "rfid_writer.h" -#include "protocols/protocol_ioprox.h" -#include -#include "protocols/protocol_emmarin.h" -#include "protocols/protocol_hid_h10301.h" -#include "protocols/protocol_indala_40134.h" - -/** - * @brief all timings are specified in field clocks (field clock = 125 kHz, 8 us) - * - */ -class T55xxTiming { -public: - constexpr static const uint16_t wait_time = 400; - constexpr static const uint8_t start_gap = 30; - constexpr static const uint8_t write_gap = 18; - constexpr static const uint8_t data_0 = 24; - constexpr static const uint8_t data_1 = 56; - constexpr static const uint16_t program = 700; -}; - -class T55xxCmd { -public: - constexpr static const uint8_t opcode_page_0 = 0b10; - constexpr static const uint8_t opcode_page_1 = 0b11; - constexpr static const uint8_t opcode_reset = 0b00; -}; - -RfidWriter::RfidWriter() { -} - -RfidWriter::~RfidWriter() { -} - -void RfidWriter::start() { - furi_hal_rfid_tim_read(125000, 0.5); - furi_hal_rfid_pins_read(); - furi_hal_rfid_tim_read_start(); - - // do not ground the antenna - furi_hal_rfid_pin_pull_release(); -} - -void RfidWriter::stop() { - furi_hal_rfid_tim_read_stop(); - furi_hal_rfid_tim_reset(); - furi_hal_rfid_pins_reset(); -} - -void RfidWriter::write_gap(uint32_t gap_time) { - furi_hal_rfid_tim_read_stop(); - furi_delay_us(gap_time * 8); - furi_hal_rfid_tim_read_start(); -} - -void RfidWriter::write_bit(bool value) { - if(value) { - furi_delay_us(T55xxTiming::data_1 * 8); - } else { - furi_delay_us(T55xxTiming::data_0 * 8); - } - write_gap(T55xxTiming::write_gap); -} - -void RfidWriter::write_byte(uint8_t value) { - for(uint8_t i = 0; i < 8; i++) { - write_bit((value >> i) & 1); - } -} - -void RfidWriter::write_block(uint8_t page, uint8_t block, bool lock_bit, uint32_t data) { - furi_delay_us(T55xxTiming::wait_time * 8); - - // start gap - write_gap(T55xxTiming::start_gap); - - // opcode - switch(page) { - case 0: - write_bit(1); - write_bit(0); - break; - case 1: - write_bit(1); - write_bit(1); - break; - default: - furi_check(false); - break; - } - - // lock bit - write_bit(lock_bit); - - // data - for(uint8_t i = 0; i < 32; i++) { - write_bit((data >> (31 - i)) & 1); - } - - // block address - write_bit((block >> 2) & 1); - write_bit((block >> 1) & 1); - write_bit((block >> 0) & 1); - - furi_delay_us(T55xxTiming::program * 8); - - furi_delay_us(T55xxTiming::wait_time * 8); - write_reset(); -} - -void RfidWriter::write_reset() { - write_gap(T55xxTiming::start_gap); - write_bit(1); - write_bit(0); -} - -void RfidWriter::write_em(const uint8_t em_data[5]) { - ProtocolEMMarin em_card; - uint64_t em_encoded_data; - em_card.encode(em_data, 5, reinterpret_cast(&em_encoded_data), sizeof(uint64_t)); - const uint32_t em_config_block_data = 0b00000000000101001000000001000000; - - FURI_CRITICAL_ENTER(); - write_block(0, 0, false, em_config_block_data); - write_block(0, 1, false, em_encoded_data); - write_block(0, 2, false, em_encoded_data >> 32); - write_reset(); - FURI_CRITICAL_EXIT(); -} - -void RfidWriter::write_hid(const uint8_t hid_data[3]) { - ProtocolHID10301 hid_card; - uint32_t card_data[3]; - hid_card.encode(hid_data, 3, reinterpret_cast(&card_data), sizeof(card_data) * 3); - - const uint32_t hid_config_block_data = 0b00000000000100000111000001100000; - - FURI_CRITICAL_ENTER(); - write_block(0, 0, false, hid_config_block_data); - write_block(0, 1, false, card_data[0]); - write_block(0, 2, false, card_data[1]); - write_block(0, 3, false, card_data[2]); - write_reset(); - FURI_CRITICAL_EXIT(); -} - -/** Endian fixup. Translates an ioprox block into a t5577 block */ -static uint32_t ioprox_encode_block(const uint8_t block_data[4]) { - uint8_t raw_card_data[] = {block_data[3], block_data[2], block_data[1], block_data[0]}; - return *reinterpret_cast(&raw_card_data); -} - -void RfidWriter::write_ioprox(const uint8_t ioprox_data[4]) { - ProtocolIoProx ioprox_card; - - uint8_t encoded_data[8]; - ioprox_card.encode(ioprox_data, 4, encoded_data, sizeof(encoded_data)); - - const uint32_t ioprox_config_block_data = 0b00000000000101000111000001000000; - - FURI_CRITICAL_ENTER(); - write_block(0, 0, false, ioprox_config_block_data); - write_block(0, 1, false, ioprox_encode_block(&encoded_data[0])); - write_block(0, 2, false, ioprox_encode_block(&encoded_data[4])); - write_reset(); - FURI_CRITICAL_EXIT(); -} - -void RfidWriter::write_indala(const uint8_t indala_data[3]) { - ProtocolIndala40134 indala_card; - uint32_t card_data[2]; - indala_card.encode( - indala_data, 3, reinterpret_cast(&card_data), sizeof(card_data) * 2); - - const uint32_t indala_config_block_data = 0b00000000000010000001000001000000; - - FURI_CRITICAL_ENTER(); - write_block(0, 0, false, indala_config_block_data); - write_block(0, 1, false, card_data[0]); - write_block(0, 2, false, card_data[1]); - write_reset(); - FURI_CRITICAL_EXIT(); -} diff --git a/applications/lfrfid/helpers/rfid_writer.h b/applications/lfrfid/helpers/rfid_writer.h deleted file mode 100644 index 98d2bf955..000000000 --- a/applications/lfrfid/helpers/rfid_writer.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once -#include "stdint.h" - -class RfidWriter { -public: - RfidWriter(); - ~RfidWriter(); - void start(); - void stop(); - void write_em(const uint8_t em_data[5]); - void write_hid(const uint8_t hid_data[3]); - void write_ioprox(const uint8_t ioprox_data[4]); - void write_indala(const uint8_t indala_data[3]); - -private: - void write_gap(uint32_t gap_time); - void write_bit(bool value); - void write_byte(uint8_t value); - void write_block(uint8_t page, uint8_t block, bool lock_bit, uint32_t data); - void write_reset(); -}; diff --git a/applications/lfrfid/helpers/state_sequencer.cpp b/applications/lfrfid/helpers/state_sequencer.cpp deleted file mode 100644 index e6718df5c..000000000 --- a/applications/lfrfid/helpers/state_sequencer.cpp +++ /dev/null @@ -1,50 +0,0 @@ -#include "state_sequencer.h" -#include "stdio.h" - -TickSequencer::TickSequencer() { -} - -TickSequencer::~TickSequencer() { -} - -void TickSequencer::tick() { - if(tick_count == list_it->first) { - tick_count = 0; - - list_it++; - if(list_it == list.end()) { - list_it = list.begin(); - } - } - - list_it->second(); - tick_count++; -} - -void TickSequencer::reset() { - list_it = list.begin(); - tick_count = 0; -} - -void TickSequencer::clear() { - list.clear(); - reset(); -} - -void TickSequencer::do_every_tick(uint32_t tick_count, std::function fn) { - list.push_back(std::make_pair(tick_count, fn)); - reset(); -} - -void TickSequencer::do_after_tick(uint32_t tick_count, std::function fn) { - if(tick_count > 1) { - list.push_back( - std::make_pair(tick_count - 1, std::bind(&TickSequencer::do_nothing, this))); - } - list.push_back(std::make_pair(1, fn)); - - reset(); -} - -void TickSequencer::do_nothing() { -} diff --git a/applications/lfrfid/helpers/state_sequencer.h b/applications/lfrfid/helpers/state_sequencer.h deleted file mode 100644 index 12512ab51..000000000 --- a/applications/lfrfid/helpers/state_sequencer.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once -#include "stdint.h" -#include -#include - -class TickSequencer { -public: - TickSequencer(); - ~TickSequencer(); - - void tick(); - void reset(); - void clear(); - - void do_every_tick(uint32_t tick_count, std::function fn); - void do_after_tick(uint32_t tick_count, std::function fn); - -private: - std::list > > list; - std::list > >::iterator list_it; - - uint32_t tick_count; - - void do_nothing(); -}; diff --git a/applications/lfrfid/lfrfid_app.cpp b/applications/lfrfid/lfrfid_app.cpp index f1a575de5..9373ca8c7 100644 --- a/applications/lfrfid/lfrfid_app.cpp +++ b/applications/lfrfid/lfrfid_app.cpp @@ -21,6 +21,11 @@ #include "scene/lfrfid_app_scene_delete_confirm.h" #include "scene/lfrfid_app_scene_delete_success.h" #include "scene/lfrfid_app_scene_rpc.h" +#include "scene/lfrfid_app_scene_extra_actions.h" +#include "scene/lfrfid_app_scene_raw_info.h" +#include "scene/lfrfid_app_scene_raw_name.h" +#include "scene/lfrfid_app_scene_raw_read.h" +#include "scene/lfrfid_app_scene_raw_success.h" #include #include @@ -28,24 +33,44 @@ #include const char* LfRfidApp::app_folder = ANY_PATH("lfrfid"); +const char* LfRfidApp::app_sd_folder = EXT_PATH("lfrfid"); const char* LfRfidApp::app_extension = ".rfid"; const char* LfRfidApp::app_filetype = "Flipper RFID key"; LfRfidApp::LfRfidApp() : scene_controller{this} - , notification{"notification"} - , storage{"storage"} - , dialogs{"dialogs"} + , notification{RECORD_NOTIFICATION} + , storage{RECORD_STORAGE} + , dialogs{RECORD_DIALOGS} , text_store(40) { + string_init(file_name); + string_init(raw_file_name); string_init_set_str(file_path, app_folder); + + dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax); + + size_t size = protocol_dict_get_max_data_size(dict); + new_key_data = (uint8_t*)malloc(size); + old_key_data = (uint8_t*)malloc(size); + + lfworker = lfrfid_worker_alloc(dict); } LfRfidApp::~LfRfidApp() { + string_clear(raw_file_name); + string_clear(file_name); string_clear(file_path); + protocol_dict_free(dict); + + lfrfid_worker_free(lfworker); + if(rpc_ctx) { rpc_system_app_set_callback(rpc_ctx, NULL, NULL); rpc_system_app_send_exited(rpc_ctx); } + + free(new_key_data); + free(old_key_data); } static void rpc_command_callback(RpcAppSystemEvent rpc_event, void* context) { @@ -88,7 +113,7 @@ void LfRfidApp::run(void* _args) { scene_controller.process(100, SceneType::Rpc); } else { string_set_str(file_path, args); - load_key_data(file_path, &worker.key, true); + load_key_data(file_path, true); view_controller.attach_to_gui(ViewDispatcherTypeFullscreen); scene_controller.add_scene(SceneType::Emulate, new LfRfidAppSceneEmulate()); scene_controller.process(100, SceneType::Emulate); @@ -114,11 +139,16 @@ void LfRfidApp::run(void* _args) { scene_controller.add_scene(SceneType::SavedInfo, new LfRfidAppSceneSavedInfo()); scene_controller.add_scene(SceneType::DeleteConfirm, new LfRfidAppSceneDeleteConfirm()); scene_controller.add_scene(SceneType::DeleteSuccess, new LfRfidAppSceneDeleteSuccess()); + scene_controller.add_scene(SceneType::ExtraActions, new LfRfidAppSceneExtraActions()); + scene_controller.add_scene(SceneType::RawInfo, new LfRfidAppSceneRawInfo()); + scene_controller.add_scene(SceneType::RawName, new LfRfidAppSceneRawName()); + scene_controller.add_scene(SceneType::RawRead, new LfRfidAppSceneRawRead()); + scene_controller.add_scene(SceneType::RawSuccess, new LfRfidAppSceneRawSuccess()); scene_controller.process(100); } } -bool LfRfidApp::save_key(RfidKey* key) { +bool LfRfidApp::save_key() { bool result = false; make_app_folder(); @@ -128,9 +158,9 @@ bool LfRfidApp::save_key(RfidKey* key) { string_left(file_path, filename_start); } - string_cat_printf(file_path, "/%s%s", key->get_name(), app_extension); + string_cat_printf(file_path, "/%s%s", string_get_cstr(file_name), app_extension); - result = save_key_data(file_path, key); + result = save_key_data(file_path); return result; } @@ -143,56 +173,27 @@ bool LfRfidApp::load_key_from_file_select(bool need_restore) { dialogs, file_path, file_path, app_extension, true, &I_125_10px, true); if(result) { - result = load_key_data(file_path, &worker.key, true); + result = load_key_data(file_path, true); } return result; } -bool LfRfidApp::delete_key(RfidKey* key) { - UNUSED(key); +bool LfRfidApp::delete_key() { return storage_simply_remove(storage, string_get_cstr(file_path)); } -bool LfRfidApp::load_key_data(string_t path, RfidKey* key, bool show_dialog) { - FlipperFormat* file = flipper_format_file_alloc(storage); +bool LfRfidApp::load_key_data(string_t path, bool show_dialog) { bool result = false; - string_t str_result; - string_init(str_result); do { - if(!flipper_format_file_open_existing(file, string_get_cstr(path))) break; + protocol_id = lfrfid_dict_file_load(dict, string_get_cstr(path)); + if(protocol_id == PROTOCOL_NO) break; - // header - uint32_t version; - if(!flipper_format_read_header(file, str_result, &version)) break; - if(string_cmp_str(str_result, app_filetype) != 0) break; - if(version != 1) break; - - // key type - LfrfidKeyType type; - RfidKey loaded_key; - - if(!flipper_format_read_string(file, "Key type", str_result)) break; - if(!lfrfid_key_get_string_type(string_get_cstr(str_result), &type)) break; - loaded_key.set_type(type); - - // key data - uint8_t key_data[loaded_key.get_type_data_count()] = {}; - if(!flipper_format_read_hex(file, "Data", key_data, loaded_key.get_type_data_count())) - break; - loaded_key.set_data(key_data, loaded_key.get_type_data_count()); - - path_extract_filename(path, str_result, true); - loaded_key.set_name(string_get_cstr(str_result)); - - *key = loaded_key; + path_extract_filename(path, file_name, true); result = true; } while(0); - flipper_format_free(file); - string_clear(str_result); - if((!result) && (show_dialog)) { dialog_message_show_storage_error(dialogs, "Cannot load\nkey file"); } @@ -200,27 +201,8 @@ bool LfRfidApp::load_key_data(string_t path, RfidKey* key, bool show_dialog) { return result; } -bool LfRfidApp::save_key_data(string_t path, RfidKey* key) { - FlipperFormat* file = flipper_format_file_alloc(storage); - bool result = false; - - do { - if(!flipper_format_file_open_always(file, string_get_cstr(path))) break; - if(!flipper_format_write_header_cstr(file, app_filetype, 1)) break; - if(!flipper_format_write_comment_cstr(file, "Key type can be EM4100, H10301 or I40134")) - break; - if(!flipper_format_write_string_cstr( - file, "Key type", lfrfid_key_get_type_string(key->get_type()))) - break; - if(!flipper_format_write_comment_cstr( - file, "Data size for EM4100 is 5, for H10301 is 3, for I40134 is 3")) - break; - if(!flipper_format_write_hex(file, "Data", key->get_data(), key->get_type_data_count())) - break; - result = true; - } while(0); - - flipper_format_free(file); +bool LfRfidApp::save_key_data(string_t path) { + bool result = lfrfid_dict_file_save(dict, protocol_id, string_get_cstr(path)); if(!result) { dialog_message_show_storage_error(dialogs, "Cannot save\nkey file"); diff --git a/applications/lfrfid/lfrfid_app.h b/applications/lfrfid/lfrfid_app.h index db022c9aa..153218dbd 100644 --- a/applications/lfrfid/lfrfid_app.h +++ b/applications/lfrfid/lfrfid_app.h @@ -20,9 +20,15 @@ #include #include -#include "helpers/rfid_worker.h" #include "rpc/rpc_app.h" +#include +#include +#include +#include + +#define LFRFID_KEY_NAME_SIZE 22 + class LfRfidApp { public: enum class EventType : uint8_t { @@ -32,7 +38,19 @@ public: Stay, Retry, Exit, - EmulateStart, + ReadEventSenseStart, + ReadEventSenseEnd, + ReadEventSenseCardStart, + ReadEventSenseCardEnd, + ReadEventStartASK, + ReadEventStartPSK, + ReadEventDone, + ReadEventOverrun, + ReadEventError, + WriteEventOK, + WriteEventProtocolCannotBeWritten, + WriteEventFobCannotBeWritten, + WriteEventTooLongToWrite, RpcLoadFile, RpcSessionClose, }; @@ -57,12 +75,17 @@ public: DeleteConfirm, DeleteSuccess, Rpc, + ExtraActions, + RawInfo, + RawName, + RawRead, + RawSuccess, }; class Event { public: union { - int32_t menu_index; + int32_t signed_int; } payload; EventType type; @@ -79,8 +102,6 @@ public: RecordController storage; RecordController dialogs; - RfidWorker worker; - TextStore text_store; string_t file_path; @@ -90,15 +111,27 @@ public: void run(void* args); static const char* app_folder; + static const char* app_sd_folder; static const char* app_extension; static const char* app_filetype; - bool save_key(RfidKey* key); + bool save_key(); bool load_key_from_file_select(bool need_restore); - bool delete_key(RfidKey* key); + bool delete_key(); - bool load_key_data(string_t path, RfidKey* key, bool show_dialog); - bool save_key_data(string_t path, RfidKey* key); + bool load_key_data(string_t path, bool show_dialog); + bool save_key_data(string_t path); void make_app_folder(); + + ProtocolDict* dict; + LFRFIDWorker* lfworker; + string_t file_name; + ProtocolId protocol_id; + LFRFIDWorkerReadType read_type; + + uint8_t* old_key_data; + uint8_t* new_key_data; + + string_t raw_file_name; }; diff --git a/applications/lfrfid/lfrfid_cli.c b/applications/lfrfid/lfrfid_cli.c new file mode 100644 index 000000000..9a6930a67 --- /dev/null +++ b/applications/lfrfid/lfrfid_cli.c @@ -0,0 +1,575 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +static void lfrfid_cli(Cli* cli, string_t args, void* context); + +// app cli function +void lfrfid_on_system_start() { + Cli* cli = furi_record_open(RECORD_CLI); + cli_add_command(cli, "rfid", CliCommandFlagDefault, lfrfid_cli, NULL); + furi_record_close(RECORD_CLI); +} + +static void lfrfid_cli_print_usage() { + printf("Usage:\r\n"); + printf("rfid read \r\n"); + printf("rfid \r\n"); + printf("rfid raw_read \r\n"); + printf("rfid raw_emulate \r\n"); +}; + +typedef struct { + ProtocolId protocol; + FuriEventFlag* event; +} LFRFIDCliReadContext; + +static void lfrfid_cli_read_callback(LFRFIDWorkerReadResult result, ProtocolId proto, void* ctx) { + furi_assert(ctx); + LFRFIDCliReadContext* context = ctx; + if(result == LFRFIDWorkerReadDone) { + context->protocol = proto; + FURI_SW_MEMBARRIER(); + } + furi_event_flag_set(context->event, 1 << result); +} + +static void lfrfid_cli_read(Cli* cli, string_t args) { + string_t type_string; + string_init(type_string); + LFRFIDWorkerReadType type = LFRFIDWorkerReadTypeAuto; + + if(args_read_string_and_trim(args, type_string)) { + if(string_cmp_str(type_string, "normal") == 0 || string_cmp_str(type_string, "ask") == 0) { + // ask + type = LFRFIDWorkerReadTypeASKOnly; + } else if( + string_cmp_str(type_string, "indala") == 0 || + string_cmp_str(type_string, "psk") == 0) { + // psk + type = LFRFIDWorkerReadTypePSKOnly; + } else { + lfrfid_cli_print_usage(); + string_clear(type_string); + return; + } + } + string_clear(type_string); + + ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax); + LFRFIDWorker* worker = lfrfid_worker_alloc(dict); + LFRFIDCliReadContext context; + context.protocol = PROTOCOL_NO; + context.event = furi_event_flag_alloc(); + + lfrfid_worker_start_thread(worker); + + printf("Reading RFID...\r\nPress Ctrl+C to abort\r\n"); + + const uint32_t available_flags = (1 << LFRFIDWorkerReadDone); + + lfrfid_worker_read_start(worker, type, lfrfid_cli_read_callback, &context); + + while(true) { + uint32_t flags = + furi_event_flag_wait(context.event, available_flags, FuriFlagWaitAny, 100); + + if(flags != FuriFlagErrorTimeout) { + if(FURI_BIT(flags, LFRFIDWorkerReadDone)) { + break; + } + } + + if(cli_cmd_interrupt_received(cli)) break; + } + + lfrfid_worker_stop(worker); + lfrfid_worker_stop_thread(worker); + lfrfid_worker_free(worker); + + if(context.protocol != PROTOCOL_NO) { + printf("%s ", protocol_dict_get_name(dict, context.protocol)); + + size_t size = protocol_dict_get_data_size(dict, context.protocol); + uint8_t* data = malloc(size); + protocol_dict_get_data(dict, context.protocol, data, size); + for(size_t i = 0; i < size; i++) { + printf("%02X", data[i]); + } + printf("\r\n"); + free(data); + + string_t info; + string_init(info); + protocol_dict_render_data(dict, info, context.protocol); + if(string_size(info) > 0) { + printf("%s\r\n", string_get_cstr(info)); + } + string_clear(info); + } + + printf("Reading stopped\r\n"); + protocol_dict_free(dict); + + furi_event_flag_free(context.event); +} + +static bool lfrfid_cli_parse_args(string_t args, ProtocolDict* dict, ProtocolId* protocol) { + bool result = false; + string_t protocol_name, data_text; + string_init(protocol_name); + string_init(data_text); + size_t data_size = protocol_dict_get_max_data_size(dict); + uint8_t* data = malloc(data_size); + + do { + // load args + if(!args_read_string_and_trim(args, protocol_name) || + !args_read_string_and_trim(args, data_text)) { + lfrfid_cli_print_usage(); + break; + } + + // check protocol arg + *protocol = protocol_dict_get_protocol_by_name(dict, string_get_cstr(protocol_name)); + if(*protocol == PROTOCOL_NO) { + printf( + "Unknown protocol: %s\r\n" + "Available protocols:\r\n", + string_get_cstr(protocol_name)); + + for(ProtocolId i = 0; i < LFRFIDProtocolMax; i++) { + printf( + "\t%s, %d bytes long\r\n", + protocol_dict_get_name(dict, i), + protocol_dict_get_data_size(dict, i)); + } + break; + } + + data_size = protocol_dict_get_data_size(dict, *protocol); + + // check data arg + if(!args_read_hex_bytes(data_text, data, data_size)) { + printf( + "%s data needs to be %d bytes long\r\n", + protocol_dict_get_name(dict, *protocol), + data_size); + break; + } + + // load data to protocol + protocol_dict_set_data(dict, *protocol, data, data_size); + + result = true; + } while(false); + + free(data); + string_clear(protocol_name); + string_clear(data_text); + return result; +} + +static void lfrfid_cli_write_callback(LFRFIDWorkerWriteResult result, void* ctx) { + furi_assert(ctx); + FuriEventFlag* events = ctx; + furi_event_flag_set(events, 1 << result); +} + +static void lfrfid_cli_write(Cli* cli, string_t args) { + ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax); + ProtocolId protocol; + + if(!lfrfid_cli_parse_args(args, dict, &protocol)) { + protocol_dict_free(dict); + return; + } + + LFRFIDWorker* worker = lfrfid_worker_alloc(dict); + FuriEventFlag* event = furi_event_flag_alloc(); + + lfrfid_worker_start_thread(worker); + lfrfid_worker_write_start(worker, protocol, lfrfid_cli_write_callback, event); + + printf("Writing RFID...\r\nPress Ctrl+C to abort\r\n"); + const uint32_t available_flags = (1 << LFRFIDWorkerWriteOK) | + (1 << LFRFIDWorkerWriteProtocolCannotBeWritten) | + (1 << LFRFIDWorkerWriteFobCannotBeWritten); + + while(!cli_cmd_interrupt_received(cli)) { + uint32_t flags = furi_event_flag_wait(event, available_flags, FuriFlagWaitAny, 100); + if(flags != FuriFlagErrorTimeout) { + if(FURI_BIT(flags, LFRFIDWorkerWriteOK)) { + printf("Written!\r\n"); + break; + } + + if(FURI_BIT(flags, LFRFIDWorkerWriteProtocolCannotBeWritten)) { + printf("This protocol cannot be written.\r\n"); + break; + } + + if(FURI_BIT(flags, LFRFIDWorkerWriteFobCannotBeWritten)) { + printf("Seems this fob cannot be written.\r\n"); + } + } + } + printf("Writing stopped\r\n"); + + lfrfid_worker_stop(worker); + lfrfid_worker_stop_thread(worker); + lfrfid_worker_free(worker); + protocol_dict_free(dict); + furi_event_flag_free(event); +} + +static void lfrfid_cli_emulate(Cli* cli, string_t args) { + ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax); + ProtocolId protocol; + + if(!lfrfid_cli_parse_args(args, dict, &protocol)) { + protocol_dict_free(dict); + return; + } + + LFRFIDWorker* worker = lfrfid_worker_alloc(dict); + + lfrfid_worker_start_thread(worker); + lfrfid_worker_emulate_start(worker, protocol); + + printf("Emulating RFID...\r\nPress Ctrl+C to abort\r\n"); + while(!cli_cmd_interrupt_received(cli)) { + furi_delay_ms(100); + } + printf("Emulation stopped\r\n"); + + lfrfid_worker_stop(worker); + lfrfid_worker_stop_thread(worker); + lfrfid_worker_free(worker); + protocol_dict_free(dict); +} + +static void lfrfid_cli_raw_analyze(Cli* cli, string_t args) { + UNUSED(cli); + string_t filepath, info_string; + string_init(filepath); + string_init(info_string); + Storage* storage = furi_record_open(RECORD_STORAGE); + LFRFIDRawFile* file = lfrfid_raw_file_alloc(storage); + + do { + float frequency = 0; + float duty_cycle = 0; + + if(!args_read_probably_quoted_string_and_trim(args, filepath)) { + lfrfid_cli_print_usage(); + break; + } + + if(!lfrfid_raw_file_open_read(file, string_get_cstr(filepath))) { + printf("Failed to open file\r\n"); + break; + } + + if(!lfrfid_raw_file_read_header(file, &frequency, &duty_cycle)) { + printf("Invalid header\r\n"); + break; + } + + bool file_end = false; + uint32_t total_warns = 0; + uint32_t total_duration = 0; + uint32_t total_pulse = 0; + ProtocolId total_protocol = PROTOCOL_NO; + + ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax); + protocol_dict_decoders_start(dict); + + while(!file_end) { + uint32_t pulse = 0; + uint32_t duration = 0; + if(lfrfid_raw_file_read_pair(file, &duration, &pulse, &file_end)) { + bool warn = false; + + if(pulse > duration || pulse <= 0 || duration <= 0) { + total_warns += 1; + warn = true; + } + + string_printf(info_string, "[%ld %ld]", pulse, duration); + printf("%-16s", string_get_cstr(info_string)); + string_printf(info_string, "[%ld %ld]", pulse, duration - pulse); + printf("%-16s", string_get_cstr(info_string)); + + if(warn) { + printf(" <<----"); + } + + if(total_protocol == PROTOCOL_NO) { + total_protocol = protocol_dict_decoders_feed(dict, true, pulse); + if(total_protocol == PROTOCOL_NO) { + total_protocol = + protocol_dict_decoders_feed(dict, false, duration - pulse); + } + + if(total_protocol != PROTOCOL_NO) { + printf(" ", protocol_dict_get_name(dict, total_protocol)); + } + } + + printf("\r\n"); + + total_pulse += pulse; + total_duration += duration; + + if(total_protocol != PROTOCOL_NO) { + break; + } + } else { + printf("Failed to read pair\r\n"); + break; + } + } + + printf(" Frequency: %f\r\n", (double)frequency); + printf(" Duty Cycle: %f\r\n", (double)duty_cycle); + printf(" Warns: %ld\r\n", total_warns); + printf(" Pulse sum: %ld\r\n", total_pulse); + printf("Duration sum: %ld\r\n", total_duration); + printf(" Average: %f\r\n", (double)((float)total_pulse / (float)total_duration)); + printf(" Protocol: "); + + if(total_protocol != PROTOCOL_NO) { + size_t data_size = protocol_dict_get_data_size(dict, total_protocol); + uint8_t* data = malloc(data_size); + protocol_dict_get_data(dict, total_protocol, data, data_size); + + printf("%s [", protocol_dict_get_name(dict, total_protocol)); + for(size_t i = 0; i < data_size; i++) { + printf("%02X", data[i]); + if(i < data_size - 1) { + printf(" "); + } + } + printf("]\r\n"); + + protocol_dict_render_data(dict, info_string, total_protocol); + printf("%s\r\n", string_get_cstr(info_string)); + + free(data); + } else { + printf("not found\r\n"); + } + + protocol_dict_free(dict); + } while(false); + + string_clear(filepath); + string_clear(info_string); + lfrfid_raw_file_free(file); + furi_record_close(RECORD_STORAGE); +} + +static void lfrfid_cli_raw_read_callback(LFRFIDWorkerReadRawResult result, void* context) { + furi_assert(context); + FuriEventFlag* event = context; + furi_event_flag_set(event, 1 << result); +} + +static void lfrfid_cli_raw_read(Cli* cli, string_t args) { + UNUSED(cli); + + string_t filepath, type_string; + string_init(filepath); + string_init(type_string); + LFRFIDWorkerReadType type = LFRFIDWorkerReadTypeAuto; + + do { + if(args_read_string_and_trim(args, type_string)) { + if(string_cmp_str(type_string, "normal") == 0 || + string_cmp_str(type_string, "ask") == 0) { + // ask + type = LFRFIDWorkerReadTypeASKOnly; + } else if( + string_cmp_str(type_string, "indala") == 0 || + string_cmp_str(type_string, "psk") == 0) { + // psk + type = LFRFIDWorkerReadTypePSKOnly; + } else { + lfrfid_cli_print_usage(); + break; + } + } + + if(!args_read_probably_quoted_string_and_trim(args, filepath)) { + lfrfid_cli_print_usage(); + break; + } + + ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax); + LFRFIDWorker* worker = lfrfid_worker_alloc(dict); + FuriEventFlag* event = furi_event_flag_alloc(); + + lfrfid_worker_start_thread(worker); + + bool overrun = false; + + const uint32_t available_flags = (1 << LFRFIDWorkerReadRawFileError) | + (1 << LFRFIDWorkerReadRawOverrun); + + lfrfid_worker_read_raw_start( + worker, string_get_cstr(filepath), type, lfrfid_cli_raw_read_callback, event); + while(true) { + uint32_t flags = furi_event_flag_wait(event, available_flags, FuriFlagWaitAny, 100); + + if(flags != FuriFlagErrorTimeout) { + if(FURI_BIT(flags, LFRFIDWorkerReadRawFileError)) { + printf("File is not RFID raw file\r\n"); + break; + } + + if(FURI_BIT(flags, LFRFIDWorkerReadRawOverrun)) { + if(!overrun) { + printf("Overrun\r\n"); + overrun = true; + } + } + } + + if(cli_cmd_interrupt_received(cli)) break; + } + + if(overrun) { + printf("An overrun occurred during read\r\n"); + } + + lfrfid_worker_stop(worker); + + lfrfid_worker_stop_thread(worker); + lfrfid_worker_free(worker); + protocol_dict_free(dict); + + furi_event_flag_free(event); + + } while(false); + + string_clear(filepath); + string_clear(type_string); +} + +static void lfrfid_cli_raw_emulate_callback(LFRFIDWorkerEmulateRawResult result, void* context) { + furi_assert(context); + FuriEventFlag* event = context; + furi_event_flag_set(event, 1 << result); +} + +static void lfrfid_cli_raw_emulate(Cli* cli, string_t args) { + UNUSED(cli); + + string_t filepath; + string_init(filepath); + Storage* storage = furi_record_open(RECORD_STORAGE); + + do { + if(!args_read_probably_quoted_string_and_trim(args, filepath)) { + lfrfid_cli_print_usage(); + break; + } + + if(!storage_file_exists(storage, string_get_cstr(filepath))) { + printf("File not found: \"%s\"\r\n", string_get_cstr(filepath)); + break; + } + + ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax); + LFRFIDWorker* worker = lfrfid_worker_alloc(dict); + FuriEventFlag* event = furi_event_flag_alloc(); + + lfrfid_worker_start_thread(worker); + + bool overrun = false; + + const uint32_t available_flags = (1 << LFRFIDWorkerEmulateRawFileError) | + (1 << LFRFIDWorkerEmulateRawOverrun); + + lfrfid_worker_emulate_raw_start( + worker, string_get_cstr(filepath), lfrfid_cli_raw_emulate_callback, event); + while(true) { + uint32_t flags = furi_event_flag_wait(event, available_flags, FuriFlagWaitAny, 100); + + if(flags != FuriFlagErrorTimeout) { + if(FURI_BIT(flags, LFRFIDWorkerEmulateRawFileError)) { + printf("File is not RFID raw file\r\n"); + break; + } + + if(FURI_BIT(flags, LFRFIDWorkerEmulateRawOverrun)) { + if(!overrun) { + printf("Overrun\r\n"); + overrun = true; + } + } + } + + if(cli_cmd_interrupt_received(cli)) break; + } + + if(overrun) { + printf("An overrun occurred during emulation\r\n"); + } + + lfrfid_worker_stop(worker); + + lfrfid_worker_stop_thread(worker); + lfrfid_worker_free(worker); + protocol_dict_free(dict); + + furi_event_flag_free(event); + + } while(false); + + furi_record_close(RECORD_STORAGE); + string_clear(filepath); +} + +static void lfrfid_cli(Cli* cli, string_t args, void* context) { + UNUSED(context); + string_t cmd; + string_init(cmd); + + if(!args_read_string_and_trim(args, cmd)) { + string_clear(cmd); + lfrfid_cli_print_usage(); + return; + } + + if(string_cmp_str(cmd, "read") == 0) { + lfrfid_cli_read(cli, args); + } else if(string_cmp_str(cmd, "write") == 0) { + lfrfid_cli_write(cli, args); + } else if(string_cmp_str(cmd, "emulate") == 0) { + lfrfid_cli_emulate(cli, args); + } else if(string_cmp_str(cmd, "raw_read") == 0) { + lfrfid_cli_raw_read(cli, args); + } else if(string_cmp_str(cmd, "raw_emulate") == 0) { + lfrfid_cli_raw_emulate(cli, args); + } else if(string_cmp_str(cmd, "raw_analyze") == 0) { + lfrfid_cli_raw_analyze(cli, args); + } else { + lfrfid_cli_print_usage(); + } + + string_clear(cmd); +} \ No newline at end of file diff --git a/applications/lfrfid/lfrfid_cli.cpp b/applications/lfrfid/lfrfid_cli.cpp deleted file mode 100644 index 732197e95..000000000 --- a/applications/lfrfid/lfrfid_cli.cpp +++ /dev/null @@ -1,177 +0,0 @@ -#include -#include -#include -#include -#include - -#include "helpers/rfid_reader.h" -#include "helpers/rfid_timer_emulator.h" - -static void lfrfid_cli(Cli* cli, string_t args, void* context); - -// app cli function -extern "C" void lfrfid_on_system_start() { -#ifdef SRV_CLI - Cli* cli = static_cast(furi_record_open("cli")); - cli_add_command(cli, "rfid", CliCommandFlagDefault, lfrfid_cli, NULL); - furi_record_close("cli"); -#else - UNUSED(lfrfid_cli); -#endif -} - -void lfrfid_cli_print_usage() { - printf("Usage:\r\n"); - printf("rfid read \r\n"); - printf("rfid \r\n"); - printf("\t choose from:\r\n"); - printf("\tEM4100, EM-Marin (5 bytes key_data)\r\n"); - printf("\tH10301, HID26 (3 bytes key_data)\r\n"); - printf("\tI40134, Indala (3 bytes key_data)\r\n"); - printf("\tIoProxXSF, IoProx (4 bytes key_data)\r\n"); - printf("\t are hex-formatted\r\n"); -}; - -static bool lfrfid_cli_get_key_type(string_t data, LfrfidKeyType* type) { - bool result = false; - - if(string_cmp_str(data, "EM4100") == 0 || string_cmp_str(data, "EM-Marin") == 0) { - result = true; - *type = LfrfidKeyType::KeyEM4100; - } else if(string_cmp_str(data, "H10301") == 0 || string_cmp_str(data, "HID26") == 0) { - result = true; - *type = LfrfidKeyType::KeyH10301; - } else if(string_cmp_str(data, "I40134") == 0 || string_cmp_str(data, "Indala") == 0) { - result = true; - *type = LfrfidKeyType::KeyI40134; - } else if(string_cmp_str(data, "IoProxXSF") == 0 || string_cmp_str(data, "IoProx") == 0) { - result = true; - *type = LfrfidKeyType::KeyIoProxXSF; - } - - return result; -} - -static void lfrfid_cli_read(Cli* cli, string_t args) { - RfidReader reader; - string_t type_string; - string_init(type_string); - bool simple_mode = true; - LfrfidKeyType type; - RfidReader::Type reader_type = RfidReader::Type::Normal; - static const uint8_t data_size = LFRFID_KEY_SIZE; - uint8_t data[data_size] = {0}; - - if(args_read_string_and_trim(args, type_string)) { - simple_mode = false; - - if(string_cmp_str(type_string, "normal") == 0) { - reader_type = RfidReader::Type::Normal; - } else if(string_cmp_str(type_string, "indala") == 0) { - reader_type = RfidReader::Type::Indala; - } else { - lfrfid_cli_print_usage(); - string_clear(type_string); - return; - } - } - - if(simple_mode) { - reader.start(); - } else { - reader.start_forced(reader_type); - } - - printf("Reading RFID...\r\nPress Ctrl+C to abort\r\n"); - while(!cli_cmd_interrupt_received(cli)) { - if(reader.read(&type, data, data_size, simple_mode)) { - printf("%s", lfrfid_key_get_type_string(type)); - printf(" "); - - for(uint8_t i = 0; i < lfrfid_key_get_type_data_count(type); i++) { - printf("%02X", data[i]); - } - printf("\r\n"); - break; - } - furi_delay_ms(100); - } - - printf("Reading stopped\r\n"); - reader.stop(); - - string_clear(type_string); -} - -static void lfrfid_cli_write(Cli* cli, string_t args) { - UNUSED(cli); - UNUSED(args); - // TODO implement rfid write - printf("Not Implemented :(\r\n"); -} - -static void lfrfid_cli_emulate(Cli* cli, string_t args) { - string_t data; - string_init(data); - RfidTimerEmulator emulator; - - static const uint8_t data_size = LFRFID_KEY_SIZE; - uint8_t key_data[data_size] = {0}; - uint8_t key_data_size = 0; - LfrfidKeyType type; - - if(!args_read_string_and_trim(args, data)) { - lfrfid_cli_print_usage(); - string_clear(data); - return; - } - - if(!lfrfid_cli_get_key_type(data, &type)) { - lfrfid_cli_print_usage(); - string_clear(data); - return; - } - - key_data_size = lfrfid_key_get_type_data_count(type); - - if(!args_read_hex_bytes(args, key_data, key_data_size)) { - lfrfid_cli_print_usage(); - string_clear(data); - return; - } - - emulator.start(type, key_data, key_data_size); - - printf("Emulating RFID...\r\nPress Ctrl+C to abort\r\n"); - while(!cli_cmd_interrupt_received(cli)) { - furi_delay_ms(100); - } - printf("Emulation stopped\r\n"); - emulator.stop(); - - string_clear(data); -} - -static void lfrfid_cli(Cli* cli, string_t args, void* context) { - UNUSED(context); - string_t cmd; - string_init(cmd); - - if(!args_read_string_and_trim(args, cmd)) { - string_clear(cmd); - lfrfid_cli_print_usage(); - return; - } - - if(string_cmp_str(cmd, "read") == 0) { - lfrfid_cli_read(cli, args); - } else if(string_cmp_str(cmd, "write") == 0) { - lfrfid_cli_write(cli, args); - } else if(string_cmp_str(cmd, "emulate") == 0) { - lfrfid_cli_emulate(cli, args); - } else { - lfrfid_cli_print_usage(); - } - - string_clear(cmd); -} diff --git a/applications/lfrfid/scene/lfrfid_app_scene_delete_confirm.cpp b/applications/lfrfid/scene/lfrfid_app_scene_delete_confirm.cpp index 236ca8c29..58ff4dcdf 100644 --- a/applications/lfrfid/scene/lfrfid_app_scene_delete_confirm.cpp +++ b/applications/lfrfid/scene/lfrfid_app_scene_delete_confirm.cpp @@ -5,7 +5,6 @@ void LfRfidAppSceneDeleteConfirm::on_enter(LfRfidApp* app, bool /* need_restore */) { string_init(string_data); - string_init(string_decrypted); string_init(string_header); auto container = app->view_controller.get(); @@ -21,49 +20,26 @@ void LfRfidAppSceneDeleteConfirm::on_enter(LfRfidApp* app, bool /* need_restore auto line_1 = container->add(); auto line_2 = container->add(); auto line_3 = container->add(); - auto line_4 = container->add(); - RfidKey& key = app->worker.key; - const uint8_t* data = key.get_data(); - - for(uint8_t i = 0; i < key.get_type_data_count(); i++) { + size_t size = protocol_dict_get_data_size(app->dict, app->protocol_id); + uint8_t* data = (uint8_t*)malloc(size); + protocol_dict_get_data(app->dict, app->protocol_id, data, size); + for(uint8_t i = 0; i < MIN(size, (size_t)8); i++) { if(i != 0) { string_cat_printf(string_data, " "); } + string_cat_printf(string_data, "%02X", data[i]); } + free(data); - string_printf(string_header, "Delete %s?", key.get_name()); + string_printf(string_header, "Delete %s?", string_get_cstr(app->file_name)); line_1->set_text( - string_get_cstr(string_header), 64, 19, 128 - 2, AlignCenter, AlignBottom, FontPrimary); + string_get_cstr(string_header), 64, 0, 128 - 2, AlignCenter, AlignTop, FontPrimary); line_2->set_text( - string_get_cstr(string_data), 64, 29, 0, AlignCenter, AlignBottom, FontSecondary); - - switch(key.get_type()) { - case LfrfidKeyType::KeyEM4100: - string_printf( - string_decrypted, "%03u,%05u", data[2], (uint16_t)((data[3] << 8) | (data[4]))); - - break; - case LfrfidKeyType::KeyH10301: - case LfrfidKeyType::KeyI40134: - string_printf( - string_decrypted, "FC: %u ID: %u", data[0], (uint16_t)((data[1] << 8) | (data[2]))); - break; - case LfrfidKeyType::KeyIoProxXSF: - string_printf( - string_decrypted, - "FC: %u VC: %u ID: %u", - data[0], - data[1], - (uint16_t)((data[2] << 8) | (data[3]))); - break; - } + string_get_cstr(string_data), 64, 19, 0, AlignCenter, AlignTop, FontSecondary); line_3->set_text( - string_get_cstr(string_decrypted), 64, 39, 0, AlignCenter, AlignBottom, FontSecondary); - - line_4->set_text( - lfrfid_key_get_type_string(key.get_type()), + protocol_dict_get_name(app->dict, app->protocol_id), 64, 49, 0, @@ -78,7 +54,7 @@ bool LfRfidAppSceneDeleteConfirm::on_event(LfRfidApp* app, LfRfidApp::Event* eve bool consumed = false; if(event->type == LfRfidApp::EventType::Next) { - app->delete_key(&app->worker.key); + app->delete_key(); app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::DeleteSuccess); consumed = true; } else if(event->type == LfRfidApp::EventType::Stay) { @@ -94,7 +70,6 @@ bool LfRfidAppSceneDeleteConfirm::on_event(LfRfidApp* app, LfRfidApp::Event* eve void LfRfidAppSceneDeleteConfirm::on_exit(LfRfidApp* app) { app->view_controller.get()->clean(); string_clear(string_data); - string_clear(string_decrypted); string_clear(string_header); } diff --git a/applications/lfrfid/scene/lfrfid_app_scene_delete_confirm.h b/applications/lfrfid/scene/lfrfid_app_scene_delete_confirm.h index e30764f02..f9daed543 100644 --- a/applications/lfrfid/scene/lfrfid_app_scene_delete_confirm.h +++ b/applications/lfrfid/scene/lfrfid_app_scene_delete_confirm.h @@ -13,5 +13,4 @@ private: string_t string_header; string_t string_data; - string_t string_decrypted; }; diff --git a/applications/lfrfid/scene/lfrfid_app_scene_emulate.cpp b/applications/lfrfid/scene/lfrfid_app_scene_emulate.cpp index cad4f17c7..02cb011d1 100644 --- a/applications/lfrfid/scene/lfrfid_app_scene_emulate.cpp +++ b/applications/lfrfid/scene/lfrfid_app_scene_emulate.cpp @@ -3,28 +3,21 @@ #include void LfRfidAppSceneEmulate::on_enter(LfRfidApp* app, bool /* need_restore */) { - string_init(data_string); - DOLPHIN_DEED(DolphinDeedRfidEmulate); - const uint8_t* data = app->worker.key.get_data(); - - for(uint8_t i = 0; i < app->worker.key.get_type_data_count(); i++) { - string_cat_printf(data_string, "%02X", data[i]); - } - auto popup = app->view_controller.get(); popup->set_header("Emulating", 89, 30, AlignCenter, AlignTop); - if(strlen(app->worker.key.get_name())) { - popup->set_text(app->worker.key.get_name(), 89, 43, AlignCenter, AlignTop); + if(string_size(app->file_name)) { + popup->set_text(string_get_cstr(app->file_name), 89, 43, AlignCenter, AlignTop); } else { - popup->set_text(string_get_cstr(data_string), 89, 43, AlignCenter, AlignTop); + popup->set_text( + protocol_dict_get_name(app->dict, app->protocol_id), 89, 43, AlignCenter, AlignTop); } popup->set_icon(0, 3, &I_RFIDDolphinSend_97x61); app->view_controller.switch_to(); - app->worker.start_emulate(); - + lfrfid_worker_start_thread(app->lfworker); + lfrfid_worker_emulate_start(app->lfworker, (LFRFIDProtocol)app->protocol_id); notification_message(app->notification, &sequence_blink_start_magenta); } @@ -37,7 +30,7 @@ bool LfRfidAppSceneEmulate::on_event(LfRfidApp* app, LfRfidApp::Event* event) { void LfRfidAppSceneEmulate::on_exit(LfRfidApp* app) { app->view_controller.get()->clean(); - app->worker.stop_emulate(); - string_clear(data_string); + lfrfid_worker_stop(app->lfworker); + lfrfid_worker_stop_thread(app->lfworker); notification_message(app->notification, &sequence_blink_stop); } diff --git a/applications/lfrfid/scene/lfrfid_app_scene_emulate.h b/applications/lfrfid/scene/lfrfid_app_scene_emulate.h index 937e49af9..13d2b857d 100644 --- a/applications/lfrfid/scene/lfrfid_app_scene_emulate.h +++ b/applications/lfrfid/scene/lfrfid_app_scene_emulate.h @@ -6,7 +6,4 @@ public: void on_enter(LfRfidApp* app, bool need_restore) final; bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final; void on_exit(LfRfidApp* app) final; - -private: - string_t data_string; }; diff --git a/applications/lfrfid/scene/lfrfid_app_scene_extra_actions.cpp b/applications/lfrfid/scene/lfrfid_app_scene_extra_actions.cpp new file mode 100644 index 000000000..ea4f03dbb --- /dev/null +++ b/applications/lfrfid/scene/lfrfid_app_scene_extra_actions.cpp @@ -0,0 +1,63 @@ +#include "lfrfid_app_scene_extra_actions.h" + +typedef enum { + SubmenuASK, + SubmenuPSK, + SubmenuRAW, +} SubmenuIndex; + +void LfRfidAppSceneExtraActions::on_enter(LfRfidApp* app, bool need_restore) { + auto submenu = app->view_controller.get(); + + submenu->add_item("Read ASK (Animal, Ordinary Card)", SubmenuASK, submenu_callback, app); + submenu->add_item("Read PSK (Indala)", SubmenuPSK, submenu_callback, app); + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + submenu->add_item("Read RAW RFID data", SubmenuRAW, submenu_callback, app); + } + + if(need_restore) { + submenu->set_selected_item(submenu_item_selected); + } + + app->view_controller.switch_to(); +} + +bool LfRfidAppSceneExtraActions::on_event(LfRfidApp* app, LfRfidApp::Event* event) { + bool consumed = false; + + if(event->type == LfRfidApp::EventType::MenuSelected) { + submenu_item_selected = event->payload.signed_int; + switch(event->payload.signed_int) { + case SubmenuASK: + app->read_type = LFRFIDWorkerReadTypeASKOnly; + app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::Read); + break; + case SubmenuPSK: + app->read_type = LFRFIDWorkerReadTypePSKOnly; + app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::Read); + break; + case SubmenuRAW: + app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::RawName); + break; + } + + consumed = true; + } + + return consumed; +} + +void LfRfidAppSceneExtraActions::on_exit(LfRfidApp* app) { + app->view_controller.get()->clean(); +} + +void LfRfidAppSceneExtraActions::submenu_callback(void* context, uint32_t index) { + LfRfidApp* app = static_cast(context); + LfRfidApp::Event event; + + event.type = LfRfidApp::EventType::MenuSelected; + event.payload.signed_int = index; + + app->view_controller.send_event(&event); +} diff --git a/applications/lfrfid/scene/lfrfid_app_scene_extra_actions.h b/applications/lfrfid/scene/lfrfid_app_scene_extra_actions.h new file mode 100644 index 000000000..dcd746146 --- /dev/null +++ b/applications/lfrfid/scene/lfrfid_app_scene_extra_actions.h @@ -0,0 +1,13 @@ +#pragma once +#include "../lfrfid_app.h" + +class LfRfidAppSceneExtraActions : public GenericScene { +public: + void on_enter(LfRfidApp* app, bool need_restore) final; + bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final; + void on_exit(LfRfidApp* app) final; + +private: + static void submenu_callback(void* context, uint32_t index); + uint32_t submenu_item_selected = 0; +}; diff --git a/applications/lfrfid/scene/lfrfid_app_scene_raw_info.cpp b/applications/lfrfid/scene/lfrfid_app_scene_raw_info.cpp new file mode 100644 index 000000000..ce3634b2a --- /dev/null +++ b/applications/lfrfid/scene/lfrfid_app_scene_raw_info.cpp @@ -0,0 +1,77 @@ +#include "lfrfid_app_scene_raw_info.h" +#include "../view/elements/button_element.h" +#include "../view/elements/icon_element.h" +#include "../view/elements/string_element.h" + +static void ok_callback(void* context) { + LfRfidApp* app = static_cast(context); + LfRfidApp::Event event; + event.type = LfRfidApp::EventType::Next; + app->view_controller.send_event(&event); +} + +static void back_callback(void* context) { + LfRfidApp* app = static_cast(context); + LfRfidApp::Event event; + event.type = LfRfidApp::EventType::Back; + app->view_controller.send_event(&event); +} + +void LfRfidAppSceneRawInfo::on_enter(LfRfidApp* app, bool /* need_restore */) { + string_init(string_info); + + auto container = app->view_controller.get(); + + bool sd_exist = storage_sd_status(app->storage) == FSE_OK; + if(!sd_exist) { + auto icon = container->add(); + icon->set_icon(0, 0, &I_SDQuestion_35x43); + auto line = container->add(); + line->set_text( + "No SD card found.\nThis function will not\nwork without\nSD card.", + 81, + 4, + 0, + AlignCenter, + AlignTop, + FontSecondary); + + auto button = container->add(); + button->set_type(ButtonElement::Type::Left, "Back"); + button->set_callback(app, back_callback); + } else { + string_printf( + string_info, + "RAW RFID data reader\r\n" + "1) Put the Flipper on your card\r\n" + "2) Press OK\r\n" + "3) Wait until data is read"); + + auto line = container->add(); + line->set_text(string_get_cstr(string_info), 0, 1, 0, AlignLeft, AlignTop, FontSecondary); + + auto button = container->add(); + button->set_type(ButtonElement::Type::Center, "OK"); + button->set_callback(app, ok_callback); + } + + app->view_controller.switch_to(); +} + +bool LfRfidAppSceneRawInfo::on_event(LfRfidApp* app, LfRfidApp::Event* event) { + bool consumed = false; + if(event->type == LfRfidApp::EventType::Next) { + app->scene_controller.switch_to_scene({LfRfidApp::SceneType::RawRead}); + consumed = true; + } else if(event->type == LfRfidApp::EventType::Back) { + app->scene_controller.search_and_switch_to_previous_scene( + {LfRfidApp::SceneType::ExtraActions}); + consumed = true; + } + return consumed; +} + +void LfRfidAppSceneRawInfo::on_exit(LfRfidApp* app) { + app->view_controller.get()->clean(); + string_clear(string_info); +} diff --git a/applications/lfrfid/scene/lfrfid_app_scene_raw_info.h b/applications/lfrfid/scene/lfrfid_app_scene_raw_info.h new file mode 100644 index 000000000..eecca1436 --- /dev/null +++ b/applications/lfrfid/scene/lfrfid_app_scene_raw_info.h @@ -0,0 +1,12 @@ +#pragma once +#include "../lfrfid_app.h" + +class LfRfidAppSceneRawInfo : public GenericScene { +public: + void on_enter(LfRfidApp* app, bool need_restore) final; + bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final; + void on_exit(LfRfidApp* app) final; + +private: + string_t string_info; +}; diff --git a/applications/lfrfid/scene/lfrfid_app_scene_raw_name.cpp b/applications/lfrfid/scene/lfrfid_app_scene_raw_name.cpp new file mode 100644 index 000000000..0ad346198 --- /dev/null +++ b/applications/lfrfid/scene/lfrfid_app_scene_raw_name.cpp @@ -0,0 +1,46 @@ + +#include "lfrfid_app_scene_raw_name.h" +#include "m-string.h" +#include +#include + +void LfRfidAppSceneRawName::on_enter(LfRfidApp* app, bool /* need_restore */) { + const char* key_name = string_get_cstr(app->raw_file_name); + + bool key_name_empty = (string_size(app->raw_file_name) == 0); + if(key_name_empty) { + app->text_store.set("RfidRecord"); + } else { + app->text_store.set("%s", key_name); + } + + auto text_input = app->view_controller.get(); + text_input->set_header_text("Name the raw file"); + + text_input->set_result_callback( + save_callback, app, app->text_store.text, LFRFID_KEY_NAME_SIZE, key_name_empty); + + app->view_controller.switch_to(); +} + +bool LfRfidAppSceneRawName::on_event(LfRfidApp* app, LfRfidApp::Event* event) { + bool consumed = false; + + if(event->type == LfRfidApp::EventType::Next) { + string_set_str(app->raw_file_name, app->text_store.text); + app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::RawInfo); + } + + return consumed; +} + +void LfRfidAppSceneRawName::on_exit(LfRfidApp* app) { + app->view_controller.get()->clean(); +} + +void LfRfidAppSceneRawName::save_callback(void* context) { + LfRfidApp* app = static_cast(context); + LfRfidApp::Event event; + event.type = LfRfidApp::EventType::Next; + app->view_controller.send_event(&event); +} diff --git a/applications/lfrfid/scene/lfrfid_app_scene_raw_name.h b/applications/lfrfid/scene/lfrfid_app_scene_raw_name.h new file mode 100644 index 000000000..225d135e5 --- /dev/null +++ b/applications/lfrfid/scene/lfrfid_app_scene_raw_name.h @@ -0,0 +1,12 @@ +#pragma once +#include "../lfrfid_app.h" + +class LfRfidAppSceneRawName : public GenericScene { +public: + void on_enter(LfRfidApp* app, bool need_restore) final; + bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final; + void on_exit(LfRfidApp* app) final; + +private: + static void save_callback(void* context); +}; diff --git a/applications/lfrfid/scene/lfrfid_app_scene_raw_read.cpp b/applications/lfrfid/scene/lfrfid_app_scene_raw_read.cpp new file mode 100644 index 000000000..0d04e6bc7 --- /dev/null +++ b/applications/lfrfid/scene/lfrfid_app_scene_raw_read.cpp @@ -0,0 +1,107 @@ +#include "lfrfid_app_scene_raw_read.h" +#include + +#define RAW_READ_TIME 5000 + +static void lfrfid_read_callback(LFRFIDWorkerReadRawResult result, void* ctx) { + LfRfidApp* app = static_cast(ctx); + LfRfidApp::Event event; + + switch(result) { + case LFRFIDWorkerReadRawFileError: + event.type = LfRfidApp::EventType::ReadEventError; + break; + case LFRFIDWorkerReadRawOverrun: + event.type = LfRfidApp::EventType::ReadEventOverrun; + break; + } + + app->view_controller.send_event(&event); +} + +static void timer_callback(void* ctx) { + LfRfidApp* app = static_cast(ctx); + LfRfidApp::Event event; + event.type = LfRfidApp::EventType::ReadEventDone; + app->view_controller.send_event(&event); +} + +void LfRfidAppSceneRawRead::on_enter(LfRfidApp* app, bool /* need_restore */) { + string_init(string_file_name); + auto popup = app->view_controller.get(); + popup->set_icon(0, 3, &I_RFIDDolphinReceive_97x61); + app->view_controller.switch_to(); + lfrfid_worker_start_thread(app->lfworker); + app->make_app_folder(); + + timer = furi_timer_alloc(timer_callback, FuriTimerTypeOnce, app); + furi_timer_start(timer, RAW_READ_TIME); + string_printf( + string_file_name, "%s/%s.ask.raw", app->app_sd_folder, string_get_cstr(app->raw_file_name)); + popup->set_header("Reading\nRAW RFID\nASK", 89, 30, AlignCenter, AlignTop); + lfrfid_worker_read_raw_start( + app->lfworker, + string_get_cstr(string_file_name), + LFRFIDWorkerReadTypeASKOnly, + lfrfid_read_callback, + app); + + notification_message(app->notification, &sequence_blink_start_cyan); + + is_psk = false; + error = false; +} + +bool LfRfidAppSceneRawRead::on_event(LfRfidApp* app, LfRfidApp::Event* event) { + UNUSED(app); + bool consumed = true; + auto popup = app->view_controller.get(); + + switch(event->type) { + case LfRfidApp::EventType::ReadEventError: + error = true; + popup->set_header("Reading\nRAW RFID\nFile error", 89, 30, AlignCenter, AlignTop); + notification_message(app->notification, &sequence_blink_start_red); + furi_timer_stop(timer); + break; + case LfRfidApp::EventType::ReadEventDone: + if(!error) { + if(is_psk) { + notification_message(app->notification, &sequence_success); + app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::RawSuccess); + } else { + popup->set_header("Reading\nRAW RFID\nPSK", 89, 30, AlignCenter, AlignTop); + notification_message(app->notification, &sequence_blink_start_yellow); + lfrfid_worker_stop(app->lfworker); + string_printf( + string_file_name, + "%s/%s.psk.raw", + app->app_sd_folder, + string_get_cstr(app->raw_file_name)); + lfrfid_worker_read_raw_start( + app->lfworker, + string_get_cstr(string_file_name), + LFRFIDWorkerReadTypePSKOnly, + lfrfid_read_callback, + app); + furi_timer_start(timer, RAW_READ_TIME); + is_psk = true; + } + } + break; + default: + consumed = false; + break; + } + + return consumed; +} + +void LfRfidAppSceneRawRead::on_exit(LfRfidApp* app) { + notification_message(app->notification, &sequence_blink_stop); + app->view_controller.get()->clean(); + lfrfid_worker_stop(app->lfworker); + lfrfid_worker_stop_thread(app->lfworker); + furi_timer_free(timer); + string_clear(string_file_name); +} diff --git a/applications/lfrfid/scene/lfrfid_app_scene_raw_read.h b/applications/lfrfid/scene/lfrfid_app_scene_raw_read.h new file mode 100644 index 000000000..09ef74639 --- /dev/null +++ b/applications/lfrfid/scene/lfrfid_app_scene_raw_read.h @@ -0,0 +1,15 @@ +#pragma once +#include "../lfrfid_app.h" + +class LfRfidAppSceneRawRead : public GenericScene { +public: + void on_enter(LfRfidApp* app, bool need_restore) final; + bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final; + void on_exit(LfRfidApp* app) final; + +private: + string_t string_file_name; + FuriTimer* timer; + bool is_psk; + bool error; +}; diff --git a/applications/lfrfid/scene/lfrfid_app_scene_raw_success.cpp b/applications/lfrfid/scene/lfrfid_app_scene_raw_success.cpp new file mode 100644 index 000000000..227ab580a --- /dev/null +++ b/applications/lfrfid/scene/lfrfid_app_scene_raw_success.cpp @@ -0,0 +1,45 @@ +#include "lfrfid_app_scene_raw_success.h" +#include "../view/elements/button_element.h" +#include "../view/elements/icon_element.h" +#include "../view/elements/string_element.h" + +void LfRfidAppSceneRawSuccess::on_enter(LfRfidApp* app, bool /* need_restore */) { + string_init(string_info); + + string_printf(string_info, "RAW RFID read success!\r\n"); + string_cat_printf(string_info, "Now you can analyze files\r\n"); + string_cat_printf(string_info, "Or send them to developers"); + + auto container = app->view_controller.get(); + + auto line = container->add(); + line->set_text(string_get_cstr(string_info), 0, 1, 0, AlignLeft, AlignTop, FontSecondary); + + auto button = container->add(); + button->set_type(ButtonElement::Type::Center, "OK"); + button->set_callback(app, LfRfidAppSceneRawSuccess::ok_callback); + + app->view_controller.switch_to(); +} + +bool LfRfidAppSceneRawSuccess::on_event(LfRfidApp* app, LfRfidApp::Event* event) { + bool consumed = false; + if(event->type == LfRfidApp::EventType::Next) { + app->scene_controller.search_and_switch_to_previous_scene( + {LfRfidApp::SceneType::ExtraActions}); + consumed = true; + } + return consumed; +} + +void LfRfidAppSceneRawSuccess::on_exit(LfRfidApp* app) { + app->view_controller.get()->clean(); + string_clear(string_info); +} + +void LfRfidAppSceneRawSuccess::ok_callback(void* context) { + LfRfidApp* app = static_cast(context); + LfRfidApp::Event event; + event.type = LfRfidApp::EventType::Next; + app->view_controller.send_event(&event); +} diff --git a/applications/lfrfid/scene/lfrfid_app_scene_raw_success.h b/applications/lfrfid/scene/lfrfid_app_scene_raw_success.h new file mode 100644 index 000000000..0a0b0116b --- /dev/null +++ b/applications/lfrfid/scene/lfrfid_app_scene_raw_success.h @@ -0,0 +1,13 @@ +#pragma once +#include "../lfrfid_app.h" + +class LfRfidAppSceneRawSuccess : public GenericScene { +public: + void on_enter(LfRfidApp* app, bool need_restore) final; + bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final; + void on_exit(LfRfidApp* app) final; + +private: + string_t string_info; + static void ok_callback(void* context); +}; diff --git a/applications/lfrfid/scene/lfrfid_app_scene_read.cpp b/applications/lfrfid/scene/lfrfid_app_scene_read.cpp index 67279a163..120eb1a07 100644 --- a/applications/lfrfid/scene/lfrfid_app_scene_read.cpp +++ b/applications/lfrfid/scene/lfrfid_app_scene_read.cpp @@ -1,40 +1,100 @@ #include "lfrfid_app_scene_read.h" #include +static void lfrfid_read_callback(LFRFIDWorkerReadResult result, ProtocolId protocol, void* ctx) { + LfRfidApp* app = static_cast(ctx); + LfRfidApp::Event event; + + switch(result) { + case LFRFIDWorkerReadSenseStart: + event.type = LfRfidApp::EventType::ReadEventSenseStart; + break; + case LFRFIDWorkerReadSenseEnd: + event.type = LfRfidApp::EventType::ReadEventSenseEnd; + break; + case LFRFIDWorkerReadSenseCardStart: + event.type = LfRfidApp::EventType::ReadEventSenseCardStart; + break; + case LFRFIDWorkerReadSenseCardEnd: + event.type = LfRfidApp::EventType::ReadEventSenseCardEnd; + break; + case LFRFIDWorkerReadDone: + event.type = LfRfidApp::EventType::ReadEventDone; + break; + case LFRFIDWorkerReadStartASK: + event.type = LfRfidApp::EventType::ReadEventStartASK; + break; + case LFRFIDWorkerReadStartPSK: + event.type = LfRfidApp::EventType::ReadEventStartPSK; + break; + } + + event.payload.signed_int = protocol; + + app->view_controller.send_event(&event); +} + void LfRfidAppSceneRead::on_enter(LfRfidApp* app, bool /* need_restore */) { auto popup = app->view_controller.get(); DOLPHIN_DEED(DolphinDeedRfidRead); - popup->set_header("Reading\nLF RFID", 89, 34, AlignCenter, AlignTop); + if(app->read_type == LFRFIDWorkerReadTypePSKOnly) { + popup->set_header("Reading\nLF RFID\nPSK", 89, 30, AlignCenter, AlignTop); + } else { + popup->set_header("Reading\nLF RFID\nASK", 89, 30, AlignCenter, AlignTop); + } + popup->set_icon(0, 3, &I_RFIDDolphinReceive_97x61); app->view_controller.switch_to(); - app->worker.start_read(); + lfrfid_worker_start_thread(app->lfworker); + lfrfid_worker_read_start(app->lfworker, app->read_type, lfrfid_read_callback, app); + + notification_message(app->notification, &sequence_blink_start_cyan); } bool LfRfidAppSceneRead::on_event(LfRfidApp* app, LfRfidApp::Event* event) { - bool consumed = false; + bool consumed = true; + auto popup = app->view_controller.get(); - if(event->type == LfRfidApp::EventType::Tick) { - if(app->worker.read()) { - DOLPHIN_DEED(DolphinDeedRfidReadSuccess); - notification_message(app->notification, &sequence_success); - app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::ReadSuccess); - } else { - if(app->worker.any_read()) { - notification_message(app->notification, &sequence_blink_yellow_10); - } else if(app->worker.detect()) { - notification_message(app->notification, &sequence_blink_yellow_10); - } else { - notification_message(app->notification, &sequence_blink_cyan_10); - } - } + switch(event->type) { + case LfRfidApp::EventType::ReadEventSenseStart: + notification_message(app->notification, &sequence_blink_stop); + notification_message(app->notification, &sequence_blink_start_yellow); + break; + case LfRfidApp::EventType::ReadEventSenseCardStart: + notification_message(app->notification, &sequence_blink_stop); + notification_message(app->notification, &sequence_blink_start_green); + break; + case LfRfidApp::EventType::ReadEventSenseEnd: + case LfRfidApp::EventType::ReadEventSenseCardEnd: + notification_message(app->notification, &sequence_blink_stop); + notification_message(app->notification, &sequence_blink_start_cyan); + break; + case LfRfidApp::EventType::ReadEventDone: + app->protocol_id = event->payload.signed_int; + DOLPHIN_DEED(DolphinDeedRfidReadSuccess); + notification_message(app->notification, &sequence_success); + string_reset(app->file_name); + app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::ReadSuccess); + break; + case LfRfidApp::EventType::ReadEventStartPSK: + popup->set_header("Reading\nLF RFID\nPSK", 89, 30, AlignCenter, AlignTop); + break; + case LfRfidApp::EventType::ReadEventStartASK: + popup->set_header("Reading\nLF RFID\nASK", 89, 30, AlignCenter, AlignTop); + break; + default: + consumed = false; + break; } return consumed; } void LfRfidAppSceneRead::on_exit(LfRfidApp* app) { + notification_message(app->notification, &sequence_blink_stop); app->view_controller.get()->clean(); - app->worker.stop_read(); + lfrfid_worker_stop(app->lfworker); + lfrfid_worker_stop_thread(app->lfworker); } diff --git a/applications/lfrfid/scene/lfrfid_app_scene_read_menu.cpp b/applications/lfrfid/scene/lfrfid_app_scene_read_menu.cpp index 76c912306..aa3b3f1fb 100644 --- a/applications/lfrfid/scene/lfrfid_app_scene_read_menu.cpp +++ b/applications/lfrfid/scene/lfrfid_app_scene_read_menu.cpp @@ -24,8 +24,8 @@ bool LfRfidAppSceneReadKeyMenu::on_event(LfRfidApp* app, LfRfidApp::Event* event bool consumed = false; if(event->type == LfRfidApp::EventType::MenuSelected) { - submenu_item_selected = event->payload.menu_index; - switch(event->payload.menu_index) { + submenu_item_selected = event->payload.signed_int; + switch(event->payload.signed_int) { case SubmenuWrite: app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::Write); break; @@ -54,7 +54,7 @@ void LfRfidAppSceneReadKeyMenu::submenu_callback(void* context, uint32_t index) LfRfidApp::Event event; event.type = LfRfidApp::EventType::MenuSelected; - event.payload.menu_index = index; + event.payload.signed_int = index; app->view_controller.send_event(&event); } diff --git a/applications/lfrfid/scene/lfrfid_app_scene_read_success.cpp b/applications/lfrfid/scene/lfrfid_app_scene_read_success.cpp index 010cac2cf..277b43a3e 100644 --- a/applications/lfrfid/scene/lfrfid_app_scene_read_success.cpp +++ b/applications/lfrfid/scene/lfrfid_app_scene_read_success.cpp @@ -4,10 +4,37 @@ #include "../view/elements/string_element.h" void LfRfidAppSceneReadSuccess::on_enter(LfRfidApp* app, bool /* need_restore */) { - string_init(string[0]); - string_init(string[1]); - string_init(string[2]); - string_init(string[3]); + string_init(string_info); + string_init(string_header); + + string_init_printf( + string_header, + "%s[%s]", + protocol_dict_get_name(app->dict, app->protocol_id), + protocol_dict_get_manufacturer(app->dict, app->protocol_id)); + + size_t size = protocol_dict_get_data_size(app->dict, app->protocol_id); + uint8_t* data = (uint8_t*)malloc(size); + protocol_dict_get_data(app->dict, app->protocol_id, data, size); + for(uint8_t i = 0; i < size; i++) { + if(i != 0) { + string_cat_printf(string_info, " "); + } + + if(i >= 9) { + string_cat_printf(string_info, "..."); + break; + } else { + string_cat_printf(string_info, "%02X", data[i]); + } + } + free(data); + + string_t render_data; + string_init(render_data); + protocol_dict_render_brief_data(app->dict, render_data, app->protocol_id); + string_cat_printf(string_info, "\r\n%s", string_get_cstr(render_data)); + string_clear(render_data); auto container = app->view_controller.get(); @@ -19,90 +46,11 @@ void LfRfidAppSceneReadSuccess::on_enter(LfRfidApp* app, bool /* need_restore */ button->set_type(ButtonElement::Type::Right, "More"); button->set_callback(app, LfRfidAppSceneReadSuccess::more_callback); - auto icon = container->add(); - icon->set_icon(3, 12, &I_RFIDBigChip_37x36); - auto header = container->add(); - header->set_text(app->worker.key.get_type_text(), 89, 3, 0, AlignCenter); + header->set_text(string_get_cstr(string_header), 0, 2, 0, AlignLeft, AlignTop, FontPrimary); - auto line_1_text = container->add(); - auto line_2l_text = container->add(); - auto line_2r_text = container->add(); - auto line_3_text = container->add(); - - auto line_1_value = container->add(); - auto line_2l_value = container->add(); - auto line_2r_value = container->add(); - auto line_3_value = container->add(); - - const uint8_t* data = app->worker.key.get_data(); - - switch(app->worker.key.get_type()) { - case LfrfidKeyType::KeyEM4100: - line_1_text->set_text("HEX:", 65, 23, 0, AlignRight, AlignBottom, FontSecondary); - line_2l_text->set_text("Mod:", 65, 35, 0, AlignRight, AlignBottom, FontSecondary); - line_3_text->set_text("ID:", 65, 47, 0, AlignRight, AlignBottom, FontSecondary); - - for(uint8_t i = 0; i < app->worker.key.get_type_data_count(); i++) { - string_cat_printf(string[0], "%02X", data[i]); - } - - string_printf(string[1], "Manchester"); - string_printf(string[2], "%03u,%05u", data[2], (uint16_t)((data[3] << 8) | (data[4]))); - - line_1_value->set_text( - string_get_cstr(string[0]), 68, 23, 0, AlignLeft, AlignBottom, FontSecondary); - line_2l_value->set_text( - string_get_cstr(string[1]), 68, 35, 0, AlignLeft, AlignBottom, FontSecondary); - line_3_value->set_text( - string_get_cstr(string[2]), 68, 47, 0, AlignLeft, AlignBottom, FontSecondary); - break; - case LfrfidKeyType::KeyH10301: - case LfrfidKeyType::KeyI40134: - line_1_text->set_text("HEX:", 65, 23, 0, AlignRight, AlignBottom, FontSecondary); - line_2l_text->set_text("FC:", 65, 35, 0, AlignRight, AlignBottom, FontSecondary); - line_3_text->set_text("Card:", 65, 47, 0, AlignRight, AlignBottom, FontSecondary); - - for(uint8_t i = 0; i < app->worker.key.get_type_data_count(); i++) { - string_cat_printf(string[0], "%02X", data[i]); - } - - string_printf(string[1], "%u", data[0]); - string_printf(string[2], "%u", (uint16_t)((data[1] << 8) | (data[2]))); - - line_1_value->set_text( - string_get_cstr(string[0]), 68, 23, 0, AlignLeft, AlignBottom, FontSecondary); - line_2l_value->set_text( - string_get_cstr(string[1]), 68, 35, 0, AlignLeft, AlignBottom, FontSecondary); - line_3_value->set_text( - string_get_cstr(string[2]), 68, 47, 0, AlignLeft, AlignBottom, FontSecondary); - break; - - case LfrfidKeyType::KeyIoProxXSF: - line_1_text->set_text("HEX:", 65, 23, 0, AlignRight, AlignBottom, FontSecondary); - line_2l_text->set_text("FC:", 65, 35, 0, AlignRight, AlignBottom, FontSecondary); - line_2r_text->set_text("VС:", 95, 35, 0, AlignRight, AlignBottom, FontSecondary); - line_3_text->set_text("Card:", 65, 47, 0, AlignRight, AlignBottom, FontSecondary); - - for(uint8_t i = 0; i < app->worker.key.get_type_data_count(); i++) { - string_cat_printf(string[0], "%02X", data[i]); - } - - string_printf(string[1], "%u", data[0]); - string_printf(string[2], "%u", (uint16_t)((data[2] << 8) | (data[3]))); - string_printf(string[3], "%u", data[1]); - - line_1_value->set_text( - string_get_cstr(string[0]), 68, 23, 0, AlignLeft, AlignBottom, FontSecondary); - line_2l_value->set_text( - string_get_cstr(string[1]), 68, 35, 0, AlignLeft, AlignBottom, FontSecondary); - line_2r_value->set_text( - string_get_cstr(string[3]), 98, 35, 0, AlignLeft, AlignBottom, FontSecondary); - line_3_value->set_text( - string_get_cstr(string[2]), 68, 47, 0, AlignLeft, AlignBottom, FontSecondary); - - break; - } + auto text = container->add(); + text->set_text(string_get_cstr(string_info), 0, 16, 0, AlignLeft, AlignTop, FontSecondary); app->view_controller.switch_to(); @@ -129,9 +77,8 @@ bool LfRfidAppSceneReadSuccess::on_event(LfRfidApp* app, LfRfidApp::Event* event void LfRfidAppSceneReadSuccess::on_exit(LfRfidApp* app) { notification_message_block(app->notification, &sequence_reset_green); app->view_controller.get()->clean(); - string_clear(string[0]); - string_clear(string[1]); - string_clear(string[2]); + string_clear(string_info); + string_clear(string_header); } void LfRfidAppSceneReadSuccess::back_callback(void* context) { diff --git a/applications/lfrfid/scene/lfrfid_app_scene_read_success.h b/applications/lfrfid/scene/lfrfid_app_scene_read_success.h index ac0e3c1b5..6d90f6310 100644 --- a/applications/lfrfid/scene/lfrfid_app_scene_read_success.h +++ b/applications/lfrfid/scene/lfrfid_app_scene_read_success.h @@ -11,5 +11,6 @@ private: static void back_callback(void* context); static void more_callback(void* context); - string_t string[3]; + string_t string_header; + string_t string_info; }; diff --git a/applications/lfrfid/scene/lfrfid_app_scene_rpc.cpp b/applications/lfrfid/scene/lfrfid_app_scene_rpc.cpp index 54a57c9a2..c2e5ec2a6 100644 --- a/applications/lfrfid/scene/lfrfid_app_scene_rpc.cpp +++ b/applications/lfrfid/scene/lfrfid_app_scene_rpc.cpp @@ -37,12 +37,13 @@ bool LfRfidAppSceneRpc::on_event(LfRfidApp* app, LfRfidApp::Event* event) { bool result = false; if(arg && !emulating) { string_set_str(app->file_path, arg); - if(app->load_key_data(app->file_path, &(app->worker.key), false)) { - app->worker.start_emulate(); + if(app->load_key_data(app->file_path, false)) { + lfrfid_worker_start_thread(app->lfworker); + lfrfid_worker_emulate_start(app->lfworker, (LFRFIDProtocol)app->protocol_id); emulating = true; auto popup = app->view_controller.get(); - app->text_store.set("emulating\n%s", app->worker.key.get_name()); + app->text_store.set("emulating\n%s", string_get_cstr(app->file_name)); popup->set_text(app->text_store.text, 89, 44, AlignCenter, AlignTop); notification_message(app->notification, &sequence_blink_start_magenta); @@ -57,7 +58,8 @@ bool LfRfidAppSceneRpc::on_event(LfRfidApp* app, LfRfidApp::Event* event) { void LfRfidAppSceneRpc::on_exit(LfRfidApp* app) { if(emulating) { - app->worker.stop_emulate(); + lfrfid_worker_stop(app->lfworker); + lfrfid_worker_stop_thread(app->lfworker); notification_message(app->notification, &sequence_blink_stop); } app->view_controller.get()->clean(); diff --git a/applications/lfrfid/scene/lfrfid_app_scene_save_data.cpp b/applications/lfrfid/scene/lfrfid_app_scene_save_data.cpp index 3a13e6838..c506cd729 100644 --- a/applications/lfrfid/scene/lfrfid_app_scene_save_data.cpp +++ b/applications/lfrfid/scene/lfrfid_app_scene_save_data.cpp @@ -3,31 +3,29 @@ void LfRfidAppSceneSaveData::on_enter(LfRfidApp* app, bool need_restore) { auto byte_input = app->view_controller.get(); - RfidKey& key = app->worker.key; - - if(need_restore) printf("restored\r\n"); + size_t size = protocol_dict_get_data_size(app->dict, app->protocol_id); if(need_restore) { - key.set_data(old_key_data, key.get_type_data_count()); + protocol_dict_set_data(app->dict, app->protocol_id, app->old_key_data, size); } else { - memcpy(old_key_data, key.get_data(), key.get_type_data_count()); + protocol_dict_get_data(app->dict, app->protocol_id, app->old_key_data, size); } - memcpy(new_key_data, key.get_data(), key.get_type_data_count()); + protocol_dict_get_data(app->dict, app->protocol_id, app->new_key_data, size); + byte_input->set_header_text("Enter the data in hex"); - byte_input->set_result_callback( - save_callback, NULL, app, new_key_data, app->worker.key.get_type_data_count()); + byte_input->set_result_callback(save_callback, NULL, app, app->new_key_data, size); app->view_controller.switch_to(); } bool LfRfidAppSceneSaveData::on_event(LfRfidApp* app, LfRfidApp::Event* event) { bool consumed = false; - RfidKey& key = app->worker.key; if(event->type == LfRfidApp::EventType::Next) { - key.set_data(new_key_data, key.get_type_data_count()); + size_t size = protocol_dict_get_data_size(app->dict, app->protocol_id); + protocol_dict_set_data(app->dict, app->protocol_id, app->new_key_data, size); DOLPHIN_DEED(DolphinDeedRfidAdd); app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::SaveName); } diff --git a/applications/lfrfid/scene/lfrfid_app_scene_save_data.h b/applications/lfrfid/scene/lfrfid_app_scene_save_data.h index 6458ae649..d03cae125 100644 --- a/applications/lfrfid/scene/lfrfid_app_scene_save_data.h +++ b/applications/lfrfid/scene/lfrfid_app_scene_save_data.h @@ -9,25 +9,4 @@ public: private: static void save_callback(void* context); - uint8_t old_key_data[LFRFID_KEY_SIZE] = { - 0xAA, - 0xAA, - 0xAA, - 0xAA, - 0xAA, - 0xAA, - 0xAA, - 0xAA, - }; - - uint8_t new_key_data[LFRFID_KEY_SIZE] = { - 0xBB, - 0xBB, - 0xBB, - 0xBB, - 0xBB, - 0xBB, - 0xBB, - 0xBB, - }; }; diff --git a/applications/lfrfid/scene/lfrfid_app_scene_save_name.cpp b/applications/lfrfid/scene/lfrfid_app_scene_save_name.cpp index d7ba2c9ed..ed58b6453 100644 --- a/applications/lfrfid/scene/lfrfid_app_scene_save_name.cpp +++ b/applications/lfrfid/scene/lfrfid_app_scene_save_name.cpp @@ -4,9 +4,9 @@ #include void LfRfidAppSceneSaveName::on_enter(LfRfidApp* app, bool /* need_restore */) { - const char* key_name = app->worker.key.get_name(); + const char* key_name = string_get_cstr(app->file_name); - bool key_name_empty = !strcmp(key_name, ""); + bool key_name_empty = (string_size(app->file_name) == 0); if(key_name_empty) { string_set_str(app->file_path, app->app_folder); set_random_name(app->text_store.text, app->text_store.text_size); @@ -18,11 +18,7 @@ void LfRfidAppSceneSaveName::on_enter(LfRfidApp* app, bool /* need_restore */) { text_input->set_header_text("Name the card"); text_input->set_result_callback( - save_callback, - app, - app->text_store.text, - app->worker.key.get_name_length(), - key_name_empty); + save_callback, app, app->text_store.text, LFRFID_KEY_NAME_SIZE, key_name_empty); string_t folder_path; string_init(folder_path); @@ -42,13 +38,13 @@ bool LfRfidAppSceneSaveName::on_event(LfRfidApp* app, LfRfidApp::Event* event) { bool consumed = false; if(event->type == LfRfidApp::EventType::Next) { - if(strlen(app->worker.key.get_name())) { - app->delete_key(&app->worker.key); + if(string_size(app->file_name) > 0) { + app->delete_key(); } - app->worker.key.set_name(app->text_store.text); + string_set_str(app->file_name, app->text_store.text); - if(app->save_key(&app->worker.key)) { + if(app->save_key()) { app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::SaveSuccess); } else { app->scene_controller.search_and_switch_to_previous_scene( diff --git a/applications/lfrfid/scene/lfrfid_app_scene_save_type.cpp b/applications/lfrfid/scene/lfrfid_app_scene_save_type.cpp index 334bb1a03..b017e7b05 100644 --- a/applications/lfrfid/scene/lfrfid_app_scene_save_type.cpp +++ b/applications/lfrfid/scene/lfrfid_app_scene_save_type.cpp @@ -3,12 +3,12 @@ void LfRfidAppSceneSaveType::on_enter(LfRfidApp* app, bool need_restore) { auto submenu = app->view_controller.get(); - for(uint8_t i = 0; i <= keys_count; i++) { + for(uint8_t i = 0; i < keys_count; i++) { string_init_printf( submenu_name[i], "%s %s", - lfrfid_key_get_manufacturer_string(static_cast(i)), - lfrfid_key_get_type_string(static_cast(i))); + protocol_dict_get_manufacturer(app->dict, i), + protocol_dict_get_name(app->dict, i)); submenu->add_item(string_get_cstr(submenu_name[i]), i, submenu_callback, app); } @@ -19,15 +19,15 @@ void LfRfidAppSceneSaveType::on_enter(LfRfidApp* app, bool need_restore) { app->view_controller.switch_to(); // clear key name - app->worker.key.set_name(""); + string_reset(app->file_name); } bool LfRfidAppSceneSaveType::on_event(LfRfidApp* app, LfRfidApp::Event* event) { bool consumed = false; if(event->type == LfRfidApp::EventType::MenuSelected) { - submenu_item_selected = event->payload.menu_index; - app->worker.key.set_type(static_cast(event->payload.menu_index)); + submenu_item_selected = event->payload.signed_int; + app->protocol_id = event->payload.signed_int; app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::SaveData); consumed = true; } @@ -37,7 +37,7 @@ bool LfRfidAppSceneSaveType::on_event(LfRfidApp* app, LfRfidApp::Event* event) { void LfRfidAppSceneSaveType::on_exit(LfRfidApp* app) { app->view_controller.get()->clean(); - for(uint8_t i = 0; i <= keys_count; i++) { + for(uint8_t i = 0; i < keys_count; i++) { string_clear(submenu_name[i]); } } @@ -47,7 +47,7 @@ void LfRfidAppSceneSaveType::submenu_callback(void* context, uint32_t index) { LfRfidApp::Event event; event.type = LfRfidApp::EventType::MenuSelected; - event.payload.menu_index = index; + event.payload.signed_int = index; app->view_controller.send_event(&event); } diff --git a/applications/lfrfid/scene/lfrfid_app_scene_save_type.h b/applications/lfrfid/scene/lfrfid_app_scene_save_type.h index 847c0dabb..e4c1be3e6 100644 --- a/applications/lfrfid/scene/lfrfid_app_scene_save_type.h +++ b/applications/lfrfid/scene/lfrfid_app_scene_save_type.h @@ -10,6 +10,6 @@ public: private: static void submenu_callback(void* context, uint32_t index); uint32_t submenu_item_selected = 0; - static const uint8_t keys_count = static_cast(LfrfidKeyType::KeyIoProxXSF); - string_t submenu_name[keys_count + 1]; + static const uint8_t keys_count = static_cast(LFRFIDProtocol::LFRFIDProtocolMax); + string_t submenu_name[keys_count]; }; diff --git a/applications/lfrfid/scene/lfrfid_app_scene_saved_info.cpp b/applications/lfrfid/scene/lfrfid_app_scene_saved_info.cpp index dd4a3d4eb..614dd505c 100644 --- a/applications/lfrfid/scene/lfrfid_app_scene_saved_info.cpp +++ b/applications/lfrfid/scene/lfrfid_app_scene_saved_info.cpp @@ -4,65 +4,36 @@ #include "../view/elements/string_element.h" void LfRfidAppSceneSavedInfo::on_enter(LfRfidApp* app, bool /* need_restore */) { - string_init(string_data); - string_init(string_decrypted); + string_init(string_info); + + string_printf( + string_info, + "%s [%s]\r\n", + string_get_cstr(app->file_name), + protocol_dict_get_name(app->dict, app->protocol_id)); + + size_t size = protocol_dict_get_data_size(app->dict, app->protocol_id); + uint8_t* data = (uint8_t*)malloc(size); + protocol_dict_get_data(app->dict, app->protocol_id, data, size); + for(uint8_t i = 0; i < size; i++) { + if(i != 0) { + string_cat_printf(string_info, " "); + } + + string_cat_printf(string_info, "%02X", data[i]); + } + free(data); + + string_t render_data; + string_init(render_data); + protocol_dict_render_data(app->dict, render_data, app->protocol_id); + string_cat_printf(string_info, "\r\n%s", string_get_cstr(render_data)); + string_clear(render_data); auto container = app->view_controller.get(); - auto button = container->add(); - button->set_type(ButtonElement::Type::Left, "Back"); - button->set_callback(app, LfRfidAppSceneSavedInfo::back_callback); - auto line_1 = container->add(); - auto line_2 = container->add(); - auto line_3 = container->add(); - auto line_4 = container->add(); - - RfidKey& key = app->worker.key; - const uint8_t* data = key.get_data(); - - for(uint8_t i = 0; i < key.get_type_data_count(); i++) { - if(i != 0) { - string_cat_printf(string_data, " "); - } - string_cat_printf(string_data, "%02X", data[i]); - } - - line_1->set_text(key.get_name(), 64, 17, 128 - 2, AlignCenter, AlignBottom, FontSecondary); - line_2->set_text( - string_get_cstr(string_data), 64, 29, 0, AlignCenter, AlignBottom, FontPrimary); - - switch(key.get_type()) { - case LfrfidKeyType::KeyEM4100: - string_printf( - string_decrypted, "%03u,%05u", data[2], (uint16_t)((data[3] << 8) | (data[4]))); - - break; - case LfrfidKeyType::KeyH10301: - case LfrfidKeyType::KeyI40134: - string_printf( - string_decrypted, "FC: %u ID: %u", data[0], (uint16_t)((data[1] << 8) | (data[2]))); - break; - case LfrfidKeyType::KeyIoProxXSF: - string_printf( - string_decrypted, - "FC: %u VC: %u ID: %u", - data[0], - data[1], - (uint16_t)((data[2] << 8) | (data[3]))); - break; - } - line_3->set_text( - string_get_cstr(string_decrypted), 64, 39, 0, AlignCenter, AlignBottom, FontSecondary); - - line_4->set_text( - lfrfid_key_get_type_string(key.get_type()), - 64, - 49, - 0, - AlignCenter, - AlignBottom, - FontSecondary); + line_1->set_text(string_get_cstr(string_info), 0, 1, 0, AlignLeft, AlignTop, FontSecondary); app->view_controller.switch_to(); } @@ -73,13 +44,5 @@ bool LfRfidAppSceneSavedInfo::on_event(LfRfidApp* /* app */, LfRfidApp::Event* / void LfRfidAppSceneSavedInfo::on_exit(LfRfidApp* app) { app->view_controller.get()->clean(); - string_clear(string_data); - string_clear(string_decrypted); -} - -void LfRfidAppSceneSavedInfo::back_callback(void* context) { - LfRfidApp* app = static_cast(context); - LfRfidApp::Event event; - event.type = LfRfidApp::EventType::Back; - app->view_controller.send_event(&event); + string_clear(string_info); } diff --git a/applications/lfrfid/scene/lfrfid_app_scene_saved_info.h b/applications/lfrfid/scene/lfrfid_app_scene_saved_info.h index 5aa33e8ad..b0b588bcb 100644 --- a/applications/lfrfid/scene/lfrfid_app_scene_saved_info.h +++ b/applications/lfrfid/scene/lfrfid_app_scene_saved_info.h @@ -8,8 +8,5 @@ public: void on_exit(LfRfidApp* app) final; private: - static void back_callback(void* context); - - string_t string_data; - string_t string_decrypted; + string_t string_info; }; diff --git a/applications/lfrfid/scene/lfrfid_app_scene_saved_key_menu.cpp b/applications/lfrfid/scene/lfrfid_app_scene_saved_key_menu.cpp index e6677fe8d..e7a38d8ad 100644 --- a/applications/lfrfid/scene/lfrfid_app_scene_saved_key_menu.cpp +++ b/applications/lfrfid/scene/lfrfid_app_scene_saved_key_menu.cpp @@ -28,8 +28,8 @@ bool LfRfidAppSceneSavedKeyMenu::on_event(LfRfidApp* app, LfRfidApp::Event* even bool consumed = false; if(event->type == LfRfidApp::EventType::MenuSelected) { - submenu_item_selected = event->payload.menu_index; - switch(event->payload.menu_index) { + submenu_item_selected = event->payload.signed_int; + switch(event->payload.signed_int) { case SubmenuEmulate: app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::Emulate); break; @@ -61,7 +61,7 @@ void LfRfidAppSceneSavedKeyMenu::submenu_callback(void* context, uint32_t index) LfRfidApp::Event event; event.type = LfRfidApp::EventType::MenuSelected; - event.payload.menu_index = index; + event.payload.signed_int = index; app->view_controller.send_event(&event); } diff --git a/applications/lfrfid/scene/lfrfid_app_scene_start.cpp b/applications/lfrfid/scene/lfrfid_app_scene_start.cpp index f5afad5c9..5005c9afb 100644 --- a/applications/lfrfid/scene/lfrfid_app_scene_start.cpp +++ b/applications/lfrfid/scene/lfrfid_app_scene_start.cpp @@ -4,6 +4,7 @@ typedef enum { SubmenuRead, SubmenuSaved, SubmenuAddManually, + SubmenuExtraActions, } SubmenuIndex; void LfRfidAppSceneStart::on_enter(LfRfidApp* app, bool need_restore) { @@ -12,6 +13,7 @@ void LfRfidAppSceneStart::on_enter(LfRfidApp* app, bool need_restore) { submenu->add_item("Read", SubmenuRead, submenu_callback, app); submenu->add_item("Saved", SubmenuSaved, submenu_callback, app); submenu->add_item("Add Manually", SubmenuAddManually, submenu_callback, app); + submenu->add_item("Extra Actions", SubmenuExtraActions, submenu_callback, app); if(need_restore) { submenu->set_selected_item(submenu_item_selected); @@ -20,15 +22,17 @@ void LfRfidAppSceneStart::on_enter(LfRfidApp* app, bool need_restore) { app->view_controller.switch_to(); // clear key - app->worker.key.clear(); + string_reset(app->file_name); + app->protocol_id = PROTOCOL_NO; + app->read_type = LFRFIDWorkerReadTypeAuto; } bool LfRfidAppSceneStart::on_event(LfRfidApp* app, LfRfidApp::Event* event) { bool consumed = false; if(event->type == LfRfidApp::EventType::MenuSelected) { - submenu_item_selected = event->payload.menu_index; - switch(event->payload.menu_index) { + submenu_item_selected = event->payload.signed_int; + switch(event->payload.signed_int) { case SubmenuRead: app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::Read); break; @@ -38,6 +42,9 @@ bool LfRfidAppSceneStart::on_event(LfRfidApp* app, LfRfidApp::Event* event) { case SubmenuAddManually: app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::SaveType); break; + case SubmenuExtraActions: + app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::ExtraActions); + break; } consumed = true; } @@ -54,7 +61,7 @@ void LfRfidAppSceneStart::submenu_callback(void* context, uint32_t index) { LfRfidApp::Event event; event.type = LfRfidApp::EventType::MenuSelected; - event.payload.menu_index = index; + event.payload.signed_int = index; app->view_controller.send_event(&event); } diff --git a/applications/lfrfid/scene/lfrfid_app_scene_write.cpp b/applications/lfrfid/scene/lfrfid_app_scene_write.cpp index 274ba3158..8e04d8e8d 100644 --- a/applications/lfrfid/scene/lfrfid_app_scene_write.cpp +++ b/applications/lfrfid/scene/lfrfid_app_scene_write.cpp @@ -1,66 +1,79 @@ #include "lfrfid_app_scene_write.h" -void LfRfidAppSceneWrite::on_enter(LfRfidApp* app, bool /* need_restore */) { - card_not_supported = false; - string_init(data_string); +static void lfrfid_write_callback(LFRFIDWorkerWriteResult result, void* ctx) { + LfRfidApp* app = static_cast(ctx); + LfRfidApp::Event event; - const uint8_t* data = app->worker.key.get_data(); - - for(uint8_t i = 0; i < app->worker.key.get_type_data_count(); i++) { - string_cat_printf(data_string, "%02X", data[i]); + switch(result) { + case LFRFIDWorkerWriteOK: + event.type = LfRfidApp::EventType::WriteEventOK; + break; + case LFRFIDWorkerWriteProtocolCannotBeWritten: + event.type = LfRfidApp::EventType::WriteEventProtocolCannotBeWritten; + break; + case LFRFIDWorkerWriteFobCannotBeWritten: + event.type = LfRfidApp::EventType::WriteEventFobCannotBeWritten; + break; + case LFRFIDWorkerWriteTooLongToWrite: + event.type = LfRfidApp::EventType::WriteEventTooLongToWrite; + break; } + app->view_controller.send_event(&event); +} + +void LfRfidAppSceneWrite::on_enter(LfRfidApp* app, bool /* need_restore */) { auto popup = app->view_controller.get(); popup->set_header("Writing", 89, 30, AlignCenter, AlignTop); - if(strlen(app->worker.key.get_name())) { - popup->set_text(app->worker.key.get_name(), 89, 43, AlignCenter, AlignTop); + if(string_size(app->file_name)) { + popup->set_text(string_get_cstr(app->file_name), 89, 43, AlignCenter, AlignTop); } else { - popup->set_text(string_get_cstr(data_string), 89, 43, AlignCenter, AlignTop); + popup->set_text( + protocol_dict_get_name(app->dict, app->protocol_id), 89, 43, AlignCenter, AlignTop); } popup->set_icon(0, 3, &I_RFIDDolphinSend_97x61); app->view_controller.switch_to(); - app->worker.start_write(); + lfrfid_worker_start_thread(app->lfworker); + lfrfid_worker_write_start( + app->lfworker, (LFRFIDProtocol)app->protocol_id, lfrfid_write_callback, app); + notification_message(app->notification, &sequence_blink_start_magenta); } bool LfRfidAppSceneWrite::on_event(LfRfidApp* app, LfRfidApp::Event* event) { - bool consumed = false; + bool consumed = true; + auto popup = app->view_controller.get(); - if(event->type == LfRfidApp::EventType::Tick) { - RfidWorker::WriteResult result = app->worker.write(); - - switch(result) { - case RfidWorker::WriteResult::Nothing: - notification_message(app->notification, &sequence_blink_magenta_10); - break; - case RfidWorker::WriteResult::Ok: - notification_message(app->notification, &sequence_success); - app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::WriteSuccess); - break; - case RfidWorker::WriteResult::NotWritable: - if(!card_not_supported) { - auto popup = app->view_controller.get(); - popup->set_icon(72, 17, &I_DolphinCommon_56x48); - popup->set_header("Still trying to write...", 64, 3, AlignCenter, AlignTop); - popup->set_text( - "Make sure this\ncard is writable\nand not\nprotected.", - 3, - 17, - AlignLeft, - AlignTop); - card_not_supported = true; - } - notification_message(app->notification, &sequence_blink_yellow_10); - break; - } + switch(event->type) { + case LfRfidApp::EventType::WriteEventOK: + notification_message(app->notification, &sequence_success); + app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::WriteSuccess); + break; + case LfRfidApp::EventType::WriteEventProtocolCannotBeWritten: + popup->set_icon(72, 17, &I_DolphinCommon_56x48); + popup->set_header("Error", 64, 3, AlignCenter, AlignTop); + popup->set_text("This protocol\ncannot be written", 3, 17, AlignLeft, AlignTop); + notification_message(app->notification, &sequence_blink_start_red); + break; + case LfRfidApp::EventType::WriteEventFobCannotBeWritten: + case LfRfidApp::EventType::WriteEventTooLongToWrite: + popup->set_icon(72, 17, &I_DolphinCommon_56x48); + popup->set_header("Still trying to write...", 64, 3, AlignCenter, AlignTop); + popup->set_text( + "Make sure this\ncard is writable\nand not\nprotected.", 3, 17, AlignLeft, AlignTop); + notification_message(app->notification, &sequence_blink_start_yellow); + break; + default: + consumed = false; } return consumed; } void LfRfidAppSceneWrite::on_exit(LfRfidApp* app) { + notification_message(app->notification, &sequence_blink_stop); app->view_controller.get()->clean(); - app->worker.stop_write(); - string_clear(data_string); + lfrfid_worker_stop(app->lfworker); + lfrfid_worker_stop_thread(app->lfworker); } diff --git a/applications/lfrfid/scene/lfrfid_app_scene_write.h b/applications/lfrfid/scene/lfrfid_app_scene_write.h index 3abadebab..7564eff9d 100644 --- a/applications/lfrfid/scene/lfrfid_app_scene_write.h +++ b/applications/lfrfid/scene/lfrfid_app_scene_write.h @@ -6,8 +6,4 @@ public: void on_enter(LfRfidApp* app, bool need_restore) final; bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final; void on_exit(LfRfidApp* app) final; - -private: - string_t data_string; - bool card_not_supported; }; diff --git a/applications/meta/application.fam b/applications/meta/application.fam index e238ea761..d8c152783 100644 --- a/applications/meta/application.fam +++ b/applications/meta/application.fam @@ -68,7 +68,6 @@ App( apptype=FlipperAppType.METAPACKAGE, provides=[ "picopass", - "hid_analyzer", "barcode_generator", "mouse_jacker", "nrf_sniff", diff --git a/applications/dec_hex_converter/application.fam b/applications/multi_converter/application.fam similarity index 58% rename from applications/dec_hex_converter/application.fam rename to applications/multi_converter/application.fam index 062c40b26..9325a3a35 100644 --- a/applications/dec_hex_converter/application.fam +++ b/applications/multi_converter/application.fam @@ -1,8 +1,8 @@ App( - appid="dec_hex_converter", - name="Dec/Hex Converter", + appid="multi_converter", + name="Multi Converter", apptype=FlipperAppType.PLUGIN, - entry_point="dec_hex_converter_app", + entry_point="multi_converter_app", cdefines=["APP_DEC_HEX_CONVERTER"], requires=["gui"], stack_size=1 * 1024, diff --git a/applications/multi_converter/multi_converter.c b/applications/multi_converter/multi_converter.c new file mode 100644 index 000000000..69b3c71f6 --- /dev/null +++ b/applications/multi_converter/multi_converter.c @@ -0,0 +1,162 @@ +#include +#include +#include +#include + +#include "multi_converter_definitions.h" +#include "multi_converter_mode_display.h" +#include "multi_converter_mode_select.h" + +static void multi_converter_render_callback(Canvas* const canvas, void* ctx) { + + const MultiConverterState* multi_converter_state = acquire_mutex((ValueMutex*)ctx, 25); + if(multi_converter_state == NULL) { + return; + } + + if (multi_converter_state->mode == ModeDisplay) { + multi_converter_mode_display_draw(canvas, multi_converter_state); + } else { + multi_converter_mode_select_draw(canvas, multi_converter_state); + } + + release_mutex((ValueMutex*)ctx, multi_converter_state); +} + +static void multi_converter_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { + furi_assert(event_queue); + + MultiConverterEvent event = {.type = EventTypeKey, .input = *input_event}; + furi_message_queue_put(event_queue, &event, FuriWaitForever); +} + +static void multi_converter_init(MultiConverterState* const multi_converter_state) { + // initial default values + + multi_converter_state->buffer_orig[MULTI_CONVERTER_NUMBER_DIGITS] = '\0'; + multi_converter_state->buffer_dest[MULTI_CONVERTER_NUMBER_DIGITS] = '\0'; // null terminators + + multi_converter_state->unit_type_orig = UnitTypeDec; + multi_converter_state->unit_type_dest = UnitTypeHex; + + multi_converter_state->keyboard_lock = 0; + + // init the display view + multi_converter_mode_display_reset(multi_converter_state); + + // init the select view + multi_converter_mode_select_reset(multi_converter_state); + + // set ModeDisplay as the current mode + multi_converter_state->mode = ModeDisplay; +} + +// main entry point +int32_t multi_converter_app(void* p) { + UNUSED(p); + + // get event queue + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(MultiConverterEvent)); + + // allocate state + MultiConverterState* multi_converter_state = malloc(sizeof(MultiConverterState)); + + // set mutex for plugin state (different threads can access it) + ValueMutex state_mutex; + if(!init_mutex(&state_mutex, multi_converter_state, sizeof(multi_converter_state))) { + FURI_LOG_E("MultiConverter", "cannot create mutex\r\n"); + free(multi_converter_state); + return 255; + } + + // register callbacks for drawing and input processing + ViewPort* view_port = view_port_alloc(); + view_port_draw_callback_set(view_port, multi_converter_render_callback, &state_mutex); + view_port_input_callback_set(view_port, multi_converter_input_callback, event_queue); + + // open GUI and register view_port + Gui* gui = furi_record_open("gui"); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + multi_converter_init(multi_converter_state); + + // main loop + MultiConverterEvent event; + for (bool processing = true; processing;) { + FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); + MultiConverterState* multi_converter_state = (MultiConverterState*)acquire_mutex_block(&state_mutex); + + if (event_status == FuriStatusOk) { + // press events + if (event.type == EventTypeKey && !multi_converter_state->keyboard_lock) { + if (multi_converter_state->mode == ModeDisplay) { + + if (event.input.key == InputKeyBack) { + if (event.input.type == InputTypePress) processing = false; + } else if (event.input.key == InputKeyOk) { // the "ok" press can be short or long + MultiConverterModeTrigger t = None; + + if (event.input.type == InputTypeLong) t = multi_converter_mode_display_ok(1, multi_converter_state); + else if (event.input.type == InputTypeShort) t = multi_converter_mode_display_ok(0, multi_converter_state); + + if (t == Reset) { + multi_converter_mode_select_reset(multi_converter_state); + multi_converter_state->mode = ModeSelector; + } + } else { + if (event.input.type == InputTypePress) multi_converter_mode_display_navigation(event.input.key, multi_converter_state); + } + + } else { // ModeSelect + if (event.input.type == InputTypePress) { + switch (event.input.key) { + default: + break; + case InputKeyBack: + case InputKeyOk: { + MultiConverterModeTrigger t = multi_converter_mode_select_exit(event.input.key == InputKeyOk ? 1 : 0, multi_converter_state); + + if (t == Reset) { + multi_converter_mode_display_reset(multi_converter_state); + } else if (t == Convert) { + multi_converter_mode_display_convert(multi_converter_state); + } + + multi_converter_state->keyboard_lock = 1; + multi_converter_state->mode = ModeDisplay; + break; + } + case InputKeyLeft: + case InputKeyRight: + multi_converter_mode_select_switch(multi_converter_state); + break; + case InputKeyUp: + multi_converter_mode_select_change_unit(-1, multi_converter_state); + break; + case InputKeyDown: + multi_converter_mode_select_change_unit(1, multi_converter_state); + break; + } + } + } + } else if (multi_converter_state->keyboard_lock) { + multi_converter_state->keyboard_lock = 0; + } + } else { + // event timeout + } + + view_port_update(view_port); + release_mutex(&state_mutex, multi_converter_state); + } + + view_port_enabled_set(view_port, false); + gui_remove_view_port(gui, view_port); + furi_record_close("gui"); + view_port_free(view_port); + furi_message_queue_free(event_queue); + delete_mutex(&state_mutex); + free(multi_converter_state); + + return 0; +} \ No newline at end of file diff --git a/applications/multi_converter/multi_converter_definitions.h b/applications/multi_converter/multi_converter_definitions.h new file mode 100644 index 000000000..fe904342d --- /dev/null +++ b/applications/multi_converter/multi_converter_definitions.h @@ -0,0 +1,82 @@ +#pragma once + +#define MULTI_CONVERTER_NUMBER_DIGITS 9 + +typedef enum { + EventTypeKey, +} EventType; + +typedef struct { + InputEvent input; + EventType type; +} MultiConverterEvent; + +typedef enum { + ModeDisplay, + ModeSelector, +} MultiConverterMode; + +typedef enum { + None, + Reset, + Convert, +} MultiConverterModeTrigger; + +// new units goes here, used as index to the main multi_converter_available_units array (multi_converter_units.h) +typedef enum { + UnitTypeDec, + UnitTypeHex, + UnitTypeBin, + + UnitTypeCelsius, + UnitTypeFahernheit, + UnitTypeKelvin, + + UnitTypeKilometers, + UnitTypeMeters, + UnitTypeCentimeters, + UnitTypeMiles, + UnitTypeFeet, + UnitTypeInches, + + UnitTypeDegree, + UnitTypeRadian, +} MultiConverterUnitType; + +typedef struct { + MultiConverterUnitType selected_unit_type_orig; + MultiConverterUnitType selected_unit_type_dest; + uint8_t select_orig; +} MultiConverterModeSelect; + +typedef struct { + uint8_t cursor; // cursor position when typing + int8_t key; // hover key + uint8_t comma; // comma already added? (only one comma allowed) + uint8_t negative; // is negative? +} MultiConverterModeDisplay; + +typedef struct MultiConverterUnit MultiConverterUnit; +typedef struct MultiConverterState MultiConverterState; + +struct MultiConverterUnit { + uint8_t allow_comma; + uint8_t allow_negative; + uint8_t max_number_keys; + char mini_name[4]; + char name[12]; + void (*convert_function)(MultiConverterState * const); + uint8_t (*allowed_function)(MultiConverterUnitType); +}; + +struct MultiConverterState { + char buffer_orig[MULTI_CONVERTER_NUMBER_DIGITS + 1]; + char buffer_dest[MULTI_CONVERTER_NUMBER_DIGITS + 1]; + MultiConverterUnitType unit_type_orig; + MultiConverterUnitType unit_type_dest; + MultiConverterMode mode; + MultiConverterModeDisplay display; + MultiConverterModeSelect select; + uint8_t keyboard_lock; // used to create a small lock when switching from SELECT to DISPLAY modes + // (debouncing, basically; otherwise it switch modes twice 'cause it's too fast!) +}; diff --git a/applications/multi_converter/multi_converter_mode_display.c b/applications/multi_converter/multi_converter_mode_display.c new file mode 100644 index 000000000..2462cf60c --- /dev/null +++ b/applications/multi_converter/multi_converter_mode_display.c @@ -0,0 +1,284 @@ +#include "multi_converter_mode_display.h" + +#define MULTI_CONVERTER_DISPLAY_KEYS 18 // [0] to [F] + [BACK] + [SELECT] + +#define MULTI_CONVERTER_DISPLAY_KEY_NEGATIVE 0 // long press +#define MULTI_CONVERTER_DISPLAY_KEY_COMMA 1 // long press +#define MULTI_CONVERTER_DISPLAY_KEY_DEL 16 +#define MULTI_CONVERTER_DISPLAY_KEY_SELECT 17 + +#define MULTI_CONVERTER_DISPLAY_CHAR_COMMA '.' +#define MULTI_CONVERTER_DISPLAY_CHAR_NEGATIVE '-' +#define MULTI_CONVERTER_DISPLAY_CHAR_DEL '<' +#define MULTI_CONVERTER_DISPLAY_CHAR_SELECT '#' +#define MULTI_CONVERTER_DISPLAY_CHAR_BLANK ' ' + +#define MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN 3 +#define MULTI_CONVERTER_DISPLAY_KEY_CHAR_HEIGHT 8 + + +void multi_converter_mode_display_convert(MultiConverterState* const multi_converter_state) { + + // 1.- if origin == destination (in theory user won't be allowed to choose the same options, but it's kinda "valid"...) + // just copy buffer_orig to buffer_dest and that's it + + if (multi_converter_state->unit_type_orig == multi_converter_state->unit_type_dest) { + memcpy(multi_converter_state->buffer_dest, multi_converter_state->buffer_orig, MULTI_CONVERTER_NUMBER_DIGITS); + return; + } + + // 2.- origin_buffer has not null functions + if (multi_converter_get_unit(multi_converter_state->unit_type_orig).convert_function == NULL || multi_converter_get_unit(multi_converter_state->unit_type_orig).allowed_function == NULL) return; + + // 3.- valid destination type (using allowed_destinations function) + if (!multi_converter_get_unit(multi_converter_state->unit_type_orig).allowed_function(multi_converter_state->unit_type_dest)) return; + + multi_converter_get_unit(multi_converter_state->unit_type_orig).convert_function(multi_converter_state); + +} + +void multi_converter_mode_display_draw(Canvas* const canvas, const MultiConverterState* multi_converter_state) { + + canvas_set_color(canvas, ColorBlack); + + // ORIGIN + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 2, 10, multi_converter_get_unit(multi_converter_state->unit_type_orig).mini_name); + + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 2 + 30, 10, multi_converter_state->buffer_orig); + + // DESTINATION + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 2, 10 + 12, multi_converter_get_unit(multi_converter_state->unit_type_dest).mini_name); + + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 2 + 30, 10 + 12, multi_converter_state->buffer_dest); + + // SEPARATOR_LINE + canvas_draw_line(canvas, 2, 25, 128 - 3, 25); + + // KEYBOARD + uint8_t _x = 5; + uint8_t _y = 25 + 15; // line + 10 + + for (int i = 0; i < MULTI_CONVERTER_DISPLAY_KEYS; i++) { + + char g; + if (i < 10) g = (i + '0'); + else if (i < 16) g = ((i - 10) + 'A'); + else if (i == MULTI_CONVERTER_DISPLAY_KEY_DEL) g = MULTI_CONVERTER_DISPLAY_CHAR_DEL; + else g = MULTI_CONVERTER_DISPLAY_CHAR_SELECT; + + uint8_t g_w = canvas_glyph_width(canvas, g); + + if (i < 16 && i > multi_converter_get_unit(multi_converter_state->unit_type_orig).max_number_keys-1) { + // some units won't use the full [0] - [F] keyboard, in those situations just hide the char + // (won't be selectable anyway, so no worries here; this is just about drawing stuff) + g = MULTI_CONVERTER_DISPLAY_CHAR_BLANK; + } + + // currently hover key is highlighted + if ((multi_converter_state->display).key == i) { + canvas_draw_box(canvas, + _x - MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN, + _y - (MULTI_CONVERTER_DISPLAY_KEY_CHAR_HEIGHT + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN), + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN + g_w + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN, + MULTI_CONVERTER_DISPLAY_KEY_CHAR_HEIGHT + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN * 2 + ); + canvas_set_color(canvas, ColorWhite); + } else { + canvas_draw_frame(canvas, + _x - MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN, + _y - (MULTI_CONVERTER_DISPLAY_KEY_CHAR_HEIGHT + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN), + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN + g_w + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN, + MULTI_CONVERTER_DISPLAY_KEY_CHAR_HEIGHT + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN * 2 + ); + } + + // draw key + canvas_draw_glyph(canvas, _x, _y, g); + + // certain keys have long_press features, draw whatever they're using there too + if (i == MULTI_CONVERTER_DISPLAY_KEY_NEGATIVE) { + canvas_draw_box(canvas, + _x + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN + g_w - 4, + _y + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN - 2, + 4, + 2 + ); + } else if (i == MULTI_CONVERTER_DISPLAY_KEY_COMMA) { + canvas_draw_box(canvas, + _x + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN + g_w - 2, + _y + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN - 2, + 2, + 2 + ); + } + + // back to black + canvas_set_color(canvas, ColorBlack); + + if (i < 8) { + _x += g_w + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN * 2 + 2; + } else if (i == 8) { + _y += (MULTI_CONVERTER_DISPLAY_KEY_CHAR_HEIGHT + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN * 2) + 3; + _x = 8; // some padding at the beginning on second line + } else { + _x += g_w + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN * 2 + 1; + } + } + +} + +void multi_converter_mode_display_navigation(InputKey key, MultiConverterState* const multi_converter_state) { + + // first move to keyboard position, then check if the ORIGIN allows that specific key, if not jump to the "closest one" + switch (key) { + + default: + break; + + case InputKeyUp: + case InputKeyDown: + if ((multi_converter_state->display).key >= 9) (multi_converter_state->display).key -= 9; + else (multi_converter_state->display).key += 9; + break; + + case InputKeyLeft: + case InputKeyRight: + + (multi_converter_state->display).key += (key == InputKeyLeft ? -1 : 1); + + if ((multi_converter_state->display).key > MULTI_CONVERTER_DISPLAY_KEYS-1) (multi_converter_state->display).key = 0; + else if ((multi_converter_state->display).key < 0) (multi_converter_state->display).key = MULTI_CONVERTER_DISPLAY_KEYS-1; + break; + } + + // if destination key is disabled by max_number_keys, move to the closest one + // (this could be improved with more accurate keys movements, probably...) + if (multi_converter_get_unit(multi_converter_state->unit_type_orig).max_number_keys >= 16) return; // weird, since this means "do not show any number on the keyboard, but just in case..." + + int8_t i = -1; + if (key == InputKeyRight || key == InputKeyDown) i = 1; + + while ((multi_converter_state->display).key < 16 && (multi_converter_state->display).key > multi_converter_get_unit(multi_converter_state->unit_type_orig).max_number_keys-1) { + (multi_converter_state->display).key += i; + if ((multi_converter_state->display).key > MULTI_CONVERTER_DISPLAY_KEYS-1) (multi_converter_state->display).key = 0; + else if ((multi_converter_state->display).key < 0) (multi_converter_state->display).key = MULTI_CONVERTER_DISPLAY_KEYS-1; + } + +} + +void multi_converter_mode_display_reset(MultiConverterState* const multi_converter_state) { + + // clean the buffers + for (int i = 0; i < MULTI_CONVERTER_NUMBER_DIGITS; i++) { + multi_converter_state->buffer_orig[i] = MULTI_CONVERTER_DISPLAY_CHAR_BLANK; + multi_converter_state->buffer_dest[i] = MULTI_CONVERTER_DISPLAY_CHAR_BLANK; + } + + // reset the display flags and index + multi_converter_state->display.cursor = 0; + multi_converter_state->display.key = 0; + multi_converter_state->display.comma = 0; + multi_converter_state->display.negative = 0; +} + +void multi_converter_mode_display_toggle_negative(MultiConverterState* const multi_converter_state) { + if (multi_converter_get_unit(multi_converter_state->unit_type_orig).allow_negative) { + + if (!(multi_converter_state->display).negative) { + // shift origin buffer one to right + add the "-" sign (last digit will be lost) + for (int i = MULTI_CONVERTER_NUMBER_DIGITS-1; i > 0; i--) { + // we could avoid the blanks, but nevermind + multi_converter_state->buffer_orig[i] = multi_converter_state->buffer_orig[i-1]; + } + multi_converter_state->buffer_orig[0] = MULTI_CONVERTER_DISPLAY_CHAR_NEGATIVE; + + // only increment cursor if we're not out of bound + if ((multi_converter_state->display).cursor < MULTI_CONVERTER_NUMBER_DIGITS) (multi_converter_state->display).cursor++; + } else { + // shift origin buffer one to left, append ' ' on the end + for (int i = 0; i < MULTI_CONVERTER_NUMBER_DIGITS-1; i++) { + if (multi_converter_state->buffer_orig[i] == MULTI_CONVERTER_DISPLAY_CHAR_BLANK) break; + + multi_converter_state->buffer_orig[i] = multi_converter_state->buffer_orig[i+1]; + } + multi_converter_state->buffer_orig[MULTI_CONVERTER_NUMBER_DIGITS-1] = MULTI_CONVERTER_DISPLAY_CHAR_BLANK; + + (multi_converter_state->display).cursor--; + } + + // toggle flag + (multi_converter_state->display).negative ^= 1; + } +} + +void multi_converter_mode_display_add_comma(MultiConverterState* const multi_converter_state) { + if ( + !multi_converter_get_unit(multi_converter_state->unit_type_orig).allow_comma || + (multi_converter_state->display).comma || + !(multi_converter_state->display).cursor || + ((multi_converter_state->display).cursor == (MULTI_CONVERTER_NUMBER_DIGITS - 1)) + ) return; // maybe not allowerd; or one comma already in place; also cannot add commas as first or last chars + + // set flag to one + (multi_converter_state->display).comma = 1; + + multi_converter_state->buffer_orig[(multi_converter_state->display).cursor] = MULTI_CONVERTER_DISPLAY_CHAR_COMMA; + (multi_converter_state->display).cursor++; +} + +void multi_converter_mode_display_add_number(MultiConverterState* const multi_converter_state) { + if ((multi_converter_state->display).key > multi_converter_get_unit(multi_converter_state->unit_type_orig).max_number_keys-1) return; + + if ((multi_converter_state->display).key < 10) { + multi_converter_state->buffer_orig[(multi_converter_state->display).cursor] = (multi_converter_state->display).key + '0'; + } else { + multi_converter_state->buffer_orig[(multi_converter_state->display).cursor] = ((multi_converter_state->display).key - 10) + 'A'; + } + + (multi_converter_state->display).cursor++; +} + +MultiConverterModeTrigger multi_converter_mode_display_ok(uint8_t long_press, MultiConverterState* const multi_converter_state) { + + if ((multi_converter_state->display).key < MULTI_CONVERTER_DISPLAY_KEY_DEL) { + if ((multi_converter_state->display).cursor >= MULTI_CONVERTER_NUMBER_DIGITS) return None; // limit reached, ignore + + // long press on 0 toggle NEGATIVE if allowed, on 1 adds COMMA if allowed + if (long_press) { + + if ((multi_converter_state->display).key == MULTI_CONVERTER_DISPLAY_KEY_NEGATIVE) { + // toggle negative + multi_converter_mode_display_toggle_negative(multi_converter_state); + } else if ((multi_converter_state->display).key == MULTI_CONVERTER_DISPLAY_KEY_COMMA) { + // add comma + multi_converter_mode_display_add_comma(multi_converter_state); + } + + } else { + // regular keys + multi_converter_mode_display_add_number(multi_converter_state); + } + + multi_converter_mode_display_convert(multi_converter_state); + + } else if ((multi_converter_state->display).key == MULTI_CONVERTER_DISPLAY_KEY_DEL) { + if ((multi_converter_state->display).cursor > 0) (multi_converter_state->display).cursor--; + + if (multi_converter_state->buffer_orig[(multi_converter_state->display).cursor] == MULTI_CONVERTER_DISPLAY_CHAR_COMMA) (multi_converter_state->display).comma = 0; + if (multi_converter_state->buffer_orig[(multi_converter_state->display).cursor] == MULTI_CONVERTER_DISPLAY_CHAR_NEGATIVE) (multi_converter_state->display).negative = 0; + + multi_converter_state->buffer_orig[(multi_converter_state->display).cursor] = MULTI_CONVERTER_DISPLAY_CHAR_BLANK; + + multi_converter_mode_display_convert(multi_converter_state); + + } else { // MULTI_CONVERTER_DISPLAY_KEY_SELECT + return Reset; + } + + return None; + +} \ No newline at end of file diff --git a/applications/multi_converter/multi_converter_mode_display.h b/applications/multi_converter/multi_converter_mode_display.h new file mode 100644 index 000000000..7c23e230b --- /dev/null +++ b/applications/multi_converter/multi_converter_mode_display.h @@ -0,0 +1,55 @@ +#pragma once + +#include +#include + +#include "multi_converter_definitions.h" +#include "multi_converter_units.h" + +// +// performs a unit conversion from origin to source buffers, if there's any error, overflow or +// non-compatible format (which shouldn't happen, but just in case) abort conversion and outputs +// some "?" strings on the buffer or something similar +// +void multi_converter_mode_display_convert(MultiConverterState* const multi_converter_state); + +// +// draw the main DISPLAY view with the current multi_converter_state values +// +void multi_converter_mode_display_draw(Canvas* const canvas, const MultiConverterState* multi_converter_state); + +// +// keyboard navigation on DISPLAY mode (NAVIGATION only, no BACK nor OK - InputKey guaranteed to be left/right/up/down) +// +void multi_converter_mode_display_navigation(InputKey key, MultiConverterState* const multi_converter_state); + +// +// reset the DISPLAY mode with the current units, cleaning the buffers and different flags; +// call this when exiting the SELECT mode / changing the units +// +void multi_converter_mode_display_reset(MultiConverterState* const multi_converter_state); + +// +// toggle the negative flag on current selected buffer ONLY if the unit allows negative numbers +// (adding negative number may crop the last char on the buffer; it cannot be recovered) +// +void multi_converter_mode_display_toggle_negative(MultiConverterState* const multi_converter_state); + +// +// add a comma/dot/decimal separator/whatever on current selected buffer ONLY if the unit allows it +// (only ONE comma allowed, not in the beginning nor end) +// +void multi_converter_mode_display_add_comma(MultiConverterState* const multi_converter_state); + +// +// add a regular number to the buffer if it's <= the max_number_keys from the unit (not necessary +// since the draw and navigation functions won't allow a trigger for an invalid number, but still +// to keep the "checks" policy on each "add key" function...) +// +void multi_converter_mode_display_add_number(MultiConverterState* const multi_converter_state); + +// +// handle the OK action when selecting a specific key on the keyboard (add a number, a symbol, change mode...) +// returns a ModeTrigger enum value: may or may not let to a mode change on the main loop (WON'T change the mode here) +// +MultiConverterModeTrigger multi_converter_mode_display_ok(uint8_t long_press, MultiConverterState* const multi_converter_state); \ No newline at end of file diff --git a/applications/multi_converter/multi_converter_mode_select.c b/applications/multi_converter/multi_converter_mode_select.c new file mode 100644 index 000000000..c30a361e5 --- /dev/null +++ b/applications/multi_converter/multi_converter_mode_select.c @@ -0,0 +1,160 @@ +#include "multi_converter_mode_select.h" + +#define MULTI_CONVERTER_LIST_ENTRIES_COUNT 3 + +#define MULTI_CONVERTER_INFO_STRING_FROM "FROM:" +#define MULTI_CONVERTER_INFO_STRING_TO "TO:" +#define MULTI_CONVERTER_INFO_STRING_OK "OK: Change" +#define MULTI_CONVERTER_INFO_STRING_BACK "BACK: Cancel" + +void multi_converter_mode_select_draw_destination_offset(uint8_t x, uint8_t y, int8_t d, Canvas* const canvas, const MultiConverterState* multi_converter_state) { + int i = 1; + while (i < MULTI_CONVERTER_AVAILABLE_UNITS) { // in case there's no match, to avoid an endless loop (in theory shouldn't happen, but...) + int ut = multi_converter_get_unit_type_offset((multi_converter_state->select).selected_unit_type_dest, i * d); + if ( + multi_converter_available_units[(multi_converter_state->select).selected_unit_type_orig].allowed_function(ut) && + (multi_converter_state->select).selected_unit_type_orig != ut + ) { + canvas_draw_str(canvas, x, y, multi_converter_available_units[ut].name); + break; + } + i++; + } +} + +void multi_converter_mode_select_draw_selected_unit(uint8_t x, uint8_t y, MultiConverterUnitType unit_type, Canvas* const canvas) { + canvas_draw_box(canvas, x - 2 , y - 10, canvas_string_width(canvas, multi_converter_available_units[unit_type].name) + 4, 13); + canvas_set_color(canvas, ColorWhite); + canvas_draw_str(canvas, x, y, multi_converter_available_units[unit_type].name); + canvas_set_color(canvas, ColorBlack); +} + +void multi_converter_mode_select_draw(Canvas* const canvas, const MultiConverterState* multi_converter_state) { + + int y = 10; + int x = 10; + + canvas_set_color(canvas, ColorBlack); + + // FROM + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, x, y, MULTI_CONVERTER_INFO_STRING_FROM); + + canvas_set_font(canvas, FontSecondary); + + // offset -1 + y += 12; + + canvas_draw_str(canvas, x, y, multi_converter_available_units[ multi_converter_get_unit_type_offset((multi_converter_state->select).selected_unit_type_orig, -1) ].name); + + // current selected element + y += 12; + + multi_converter_mode_select_draw_selected_unit(x, y, (multi_converter_state->select).selected_unit_type_orig, canvas); + + if ((multi_converter_state->select).select_orig) canvas_draw_str(canvas, x - 6, y, ">"); + + // offset +1 + y += 12; + + canvas_draw_str(canvas, x, y, multi_converter_available_units[ multi_converter_get_unit_type_offset((multi_converter_state->select).selected_unit_type_orig, 1) ].name); + + // TO + y = 10; + x = 70; + + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, x, y, MULTI_CONVERTER_INFO_STRING_TO); + + canvas_set_font(canvas, FontSecondary); + + // offset -1: go back from current selected destination and find the first one valid (even if it's itself) + y += 12; + + multi_converter_mode_select_draw_destination_offset(x, y, -1, canvas, multi_converter_state); + + // current selected element + y += 12; + + multi_converter_mode_select_draw_selected_unit(x, y, (multi_converter_state->select).selected_unit_type_dest, canvas); + + if (!(multi_converter_state->select).select_orig) canvas_draw_str(canvas, x - 6, y, ">"); + + // offset +1: same but on the opposite direction + y += 12; + + multi_converter_mode_select_draw_destination_offset(x, y, 1, canvas, multi_converter_state); + + // OK / CANCEL + + canvas_set_color(canvas, ColorBlack); + canvas_draw_box(canvas, 0, 64 - 12, canvas_string_width(canvas, MULTI_CONVERTER_INFO_STRING_OK) + 4, 12); + canvas_draw_box(canvas, 128 - 4 - canvas_string_width(canvas, MULTI_CONVERTER_INFO_STRING_BACK), 64 - 12, canvas_string_width(canvas, "BACK: Cancel") + 4, 12); + + canvas_set_color(canvas, ColorWhite); + canvas_draw_str(canvas, 2, 64 - 3, MULTI_CONVERTER_INFO_STRING_OK); + canvas_draw_str(canvas, 128 - 2 - canvas_string_width(canvas, MULTI_CONVERTER_INFO_STRING_BACK), 64 - 3, MULTI_CONVERTER_INFO_STRING_BACK); +} + +void multi_converter_mode_select_reset(MultiConverterState* const multi_converter_state) { + + // initial pre-selected values are equal to the current selected values + (multi_converter_state->select).selected_unit_type_orig = multi_converter_state->unit_type_orig; + (multi_converter_state->select).selected_unit_type_dest = multi_converter_state->unit_type_dest; + + (multi_converter_state->select).select_orig = 1; +} + +MultiConverterModeTrigger multi_converter_mode_select_exit(uint8_t save_changes, MultiConverterState* const multi_converter_state) { + if (save_changes) { + + multi_converter_state->unit_type_dest = (multi_converter_state->select).selected_unit_type_dest; + + if (multi_converter_state->unit_type_orig == (multi_converter_state->select).selected_unit_type_orig) { + // if the ORIGIN unit didn't changed, just trigger the convert + + return Convert; + } else { + multi_converter_state->unit_type_orig = (multi_converter_state->select).selected_unit_type_orig; + multi_converter_state->unit_type_dest = (multi_converter_state->select).selected_unit_type_dest; + + return Reset; + } + + } + + return None; +} + +void multi_converter_mode_select_switch(MultiConverterState* const multi_converter_state) { + (multi_converter_state->select).select_orig ^= 1; +} + +void multi_converter_mode_select_change_unit(int8_t direction, MultiConverterState* const multi_converter_state) { + + MultiConverterUnitType d; + if ((multi_converter_state->select).select_orig) { + (multi_converter_state->select).selected_unit_type_orig = multi_converter_get_unit_type_offset((multi_converter_state->select).selected_unit_type_orig, direction); + d = (multi_converter_state->select).selected_unit_type_dest; + } else { + d = ((multi_converter_state->select).selected_unit_type_dest + direction) % MULTI_CONVERTER_AVAILABLE_UNITS; + } + + // check each unit with the ORIGIN allowed_function() to make sure we're selecting a valid DESTINATION + // (when changing the ORIGIN unit the DIRECTION in which we'll switch the DESTINATION will be the SAME); + // also notice that ORIGIN must be DIFFERENT than DESTINATION + int i = 0; + while (i < MULTI_CONVERTER_AVAILABLE_UNITS) { + if ( + multi_converter_available_units[(multi_converter_state->select).selected_unit_type_orig].allowed_function(d) && + (multi_converter_state->select).selected_unit_type_orig != d + ) { + (multi_converter_state->select).selected_unit_type_dest = d; + break; + } + + d = multi_converter_get_unit_type_offset(d, direction); + i++; + } + +} diff --git a/applications/multi_converter/multi_converter_mode_select.h b/applications/multi_converter/multi_converter_mode_select.h new file mode 100644 index 000000000..2e4a9ec85 --- /dev/null +++ b/applications/multi_converter/multi_converter_mode_select.h @@ -0,0 +1,58 @@ +#pragma once + +#include +#include +#include + +#include "multi_converter_definitions.h" +#include "multi_converter_units.h" + +// +// aux draw function for units offsets and draw stuff +// +void multi_converter_mode_select_draw_destination_offset(uint8_t x, uint8_t y, int8_t d, Canvas* const canvas, const MultiConverterState* multi_converter_state); + +void multi_converter_mode_select_draw_selected_unit(uint8_t x, uint8_t y, MultiConverterUnitType unit_type, Canvas* const canvas); + +// +// draw the main SELECT view with the current multi_converter_state values +// +void multi_converter_mode_select_draw(Canvas* const canvas, const MultiConverterState* multi_converter_state); + +// +// reset the SELECT mode view, showing as "pre-selected" the current working units +// +void multi_converter_mode_select_reset(MultiConverterState* const multi_converter_state); + +// +// exit from SELECT mode and go back to display view, if save_changes == 1 use the current SELECT view info +// to modify the current selected units and reset the views properly (usually if the ORIGIN unit has been +// changed, reset everything; otherwise just trigger the convert function with a new DESTINATION) +// +// currently this function DON'T CHECK invalid unit relations (the navigation and display functions will +// prevent weird behaviours, so for now we're trusting the selected_unit_orig/dest_type values) +// +// returns an enum code MultiConverterDisplayTrigger based on doing nothing (cancel), triggering the display +// convert method or reseting the whole display mode (when fully changing the units) +// +// notice the MODE CHANGE itself is not done here but in the main loop (outside the call) via the ModeTrigger enum element +// +MultiConverterModeTrigger multi_converter_mode_select_exit(uint8_t save_changes, MultiConverterState* const multi_converter_state); + +// +// switch between selecting the ORIGIN or the DESTINATION unit on DISPLAY mode (since there're only +// two options, both left/right arrow keys acts as toggles, no "direction" required) +// +void multi_converter_mode_select_switch(MultiConverterState* const multi_converter_state); + +// +// change the selected unit on SELECTED mode, using the select_orig flag to check if we're switching the +// ORIGIN or the DESTINATION unit; the DIRECTION (up or down to travel the array) is set as a param +// +// when switching the ORIGIN one, reset the DESTINATION to the first valid unit (if the current one is not +// valid anymore); when switching the DESTINATION one, an allowed_function() check is performed in order to +// properly set a valid destination unit. +// +// (notice the draw step also perform which units are valid to display, so no worries about that here) +// +void multi_converter_mode_select_change_unit(int8_t direction, MultiConverterState* const multi_converter_state); diff --git a/applications/multi_converter/multi_converter_units.c b/applications/multi_converter/multi_converter_units.c new file mode 100644 index 000000000..281284923 --- /dev/null +++ b/applications/multi_converter/multi_converter_units.c @@ -0,0 +1,230 @@ +#include "multi_converter_units.h" + +#define MULTI_CONVERTER_CHAR_OVERFLOW '#' +#define MULTI_CONVERTER_MAX_SUPORTED_INT 999999999 + +#define multi_converter_unit_set_overflow(b) for (int _i = 0; _i < MULTI_CONVERTER_NUMBER_DIGITS; _i++) b[_i] = MULTI_CONVERTER_CHAR_OVERFLOW; + +// +// DEC / HEX / BIN conversion +// +void multi_converter_unit_dec_hex_bin_convert(MultiConverterState* const multi_converter_state) { + + char dest[MULTI_CONVERTER_NUMBER_DIGITS]; + + int i = 0; + uint8_t overflow = 0; + + int a = 0; + int r = 0; + uint8_t f = 1; + + switch(multi_converter_state->unit_type_orig) { + default: + break; + case UnitTypeDec: { + a = atoi(multi_converter_state->buffer_orig); + f = (multi_converter_state->unit_type_dest == UnitTypeHex ? 16 : 2); + + break; + } + case UnitTypeHex: + a = strtol(multi_converter_state->buffer_orig, NULL, 16); + f = (multi_converter_state->unit_type_dest == UnitTypeDec ? 10 : 2); + + break; + case UnitTypeBin: + a = strtol(multi_converter_state->buffer_orig, NULL, 2); + f = (multi_converter_state->unit_type_dest == UnitTypeDec ? 10 : 16); + + break; + } + + while (a > 0) { + r = a % f; + dest[i] = r + (r < 10 ? '0' : ('A' - 10) ); + a /= f; + if (i++ >= MULTI_CONVERTER_NUMBER_DIGITS) { + overflow = 1; + break; + } + } + + if (overflow) { + multi_converter_unit_set_overflow(multi_converter_state->buffer_dest); + } else { + // copy DEST (reversed) to destination and append empty chars at the end + for (int j = 0; j < MULTI_CONVERTER_NUMBER_DIGITS; j++) { + if (i >= 1) multi_converter_state->buffer_dest[j] = dest[--i]; + else multi_converter_state->buffer_dest[j] = ' '; + } + } + +} + +uint8_t multi_converter_unit_dec_hex_bin_allowed(MultiConverterUnitType unit_type) { + return (unit_type == UnitTypeDec || unit_type == UnitTypeHex || unit_type == UnitTypeBin); +} + +// +// CEL / FAR / KEL +// +void multi_converter_unit_temperature_convert(MultiConverterState* const multi_converter_state) { + + double a = strtof(multi_converter_state->buffer_orig, NULL); + uint8_t overflow = 0; + + switch(multi_converter_state->unit_type_orig) { + default: + break; + case UnitTypeCelsius: + if (multi_converter_state->unit_type_dest == UnitTypeFahernheit) { + // celsius to fahrenheit + a = (a * ((double) 1.8)) + 32; + } else { // UnitTypeKelvin + a += ((double) 273.15); + } + + break; + case UnitTypeFahernheit: + // fahrenheit to celsius, always + a = (a - 32) / ((double) 1.8); + if (multi_converter_state->unit_type_dest == UnitTypeKelvin) { + // if kelvin, add + a += ((double) 273.15); + } + + break; + case UnitTypeKelvin: + // kelvin to celsius, always + a -= ((double) 273.15); + if (multi_converter_state->unit_type_dest == UnitTypeFahernheit) { + // if fahernheit, convert + a = (a * ((double) 1.8)) + 32; + } + + break; + } + + if (overflow) { + multi_converter_unit_set_overflow(multi_converter_state->buffer_dest); + } else { + + int ret = snprintf(multi_converter_state->buffer_dest, MULTI_CONVERTER_NUMBER_DIGITS + 1, "%.3lf", a); + + if (ret < 0) multi_converter_unit_set_overflow(multi_converter_state->buffer_dest); + } + +} + +uint8_t multi_converter_unit_temperature_allowed(MultiConverterUnitType unit_type) { + return (unit_type == UnitTypeCelsius || unit_type == UnitTypeFahernheit || unit_type == UnitTypeKelvin); +} + +// +// KM / M / CM / MILES / FEET / INCHES +// + +void multi_converter_unit_distance_convert(MultiConverterState* const multi_converter_state) { + double a = strtof(multi_converter_state->buffer_orig, NULL); + uint8_t overflow = 0; + + switch(multi_converter_state->unit_type_orig) { + default: + break; + case UnitTypeKilometers: + if (multi_converter_state->unit_type_dest == UnitTypeMeters) a *= ((double) 1000); + else if (multi_converter_state->unit_type_dest == UnitTypeCentimeters) a *= ((double) 100000); + else if (multi_converter_state->unit_type_dest == UnitTypeMiles) a *= ((double) 0.6213711); + else if (multi_converter_state->unit_type_dest == UnitTypeFeet) a *= ((double) 3280.839895013); + else if (multi_converter_state->unit_type_dest == UnitTypeInches) a *= ((double) 39370.078740157); + break; + case UnitTypeMeters: + if (multi_converter_state->unit_type_dest == UnitTypeKilometers) a /= ((double) 1000); + else if (multi_converter_state->unit_type_dest == UnitTypeCentimeters) a *= ((double) 100); + else if (multi_converter_state->unit_type_dest == UnitTypeMiles) a *= ((double) 0.0006213711); + else if (multi_converter_state->unit_type_dest == UnitTypeFeet) a *= ((double) 3.280839895013); + else if (multi_converter_state->unit_type_dest == UnitTypeInches) a *= ((double) 39.370078740157); + break; + case UnitTypeCentimeters: + if (multi_converter_state->unit_type_dest == UnitTypeKilometers) a /= ((double) 100000); + else if (multi_converter_state->unit_type_dest == UnitTypeMeters) a /= ((double) 100); + else if (multi_converter_state->unit_type_dest == UnitTypeMiles) a *= ((double) 0.000006213711); + else if (multi_converter_state->unit_type_dest == UnitTypeFeet) a *= ((double) 0.03280839895013); + else if (multi_converter_state->unit_type_dest == UnitTypeInches) a *= ((double) 0.39370078740157); + break; + + case UnitTypeMiles: + if (multi_converter_state->unit_type_dest == UnitTypeKilometers) a *= ((double) 1.609344); + else if (multi_converter_state->unit_type_dest == UnitTypeMeters) a *= ((double) 1609.344); + else if (multi_converter_state->unit_type_dest == UnitTypeCentimeters) a *= ((double) 160934.4); + else if (multi_converter_state->unit_type_dest == UnitTypeFeet) a *= ((double) 5280); + else if (multi_converter_state->unit_type_dest == UnitTypeInches) a *= ((double) 63360); + break; + case UnitTypeFeet: + if (multi_converter_state->unit_type_dest == UnitTypeKilometers) a *= ((double) 0.0003048); + else if (multi_converter_state->unit_type_dest == UnitTypeMeters) a *= ((double) 0.3048); + else if (multi_converter_state->unit_type_dest == UnitTypeCentimeters) a *= ((double) 30.48); + else if (multi_converter_state->unit_type_dest == UnitTypeMiles) a *= ((double) 0.000189393939394); + else if (multi_converter_state->unit_type_dest == UnitTypeInches) a *= ((double) 12); + break; + case UnitTypeInches: + if (multi_converter_state->unit_type_dest == UnitTypeKilometers) a *= ((double) 0.0000254); + else if (multi_converter_state->unit_type_dest == UnitTypeMeters) a *= ((double) 0.0254); + else if (multi_converter_state->unit_type_dest == UnitTypeCentimeters) a *= ((double) 2.54); + else if (multi_converter_state->unit_type_dest == UnitTypeMiles) a *= ((double) 0.0000157828282828); + else if (multi_converter_state->unit_type_dest == UnitTypeFeet) a *= ((double) 0.0833333333333); + break; + + } + + if (overflow) { + multi_converter_unit_set_overflow(multi_converter_state->buffer_dest); + } else { + + int ret = snprintf(multi_converter_state->buffer_dest, MULTI_CONVERTER_NUMBER_DIGITS + 1, "%lf", a); + + if (ret < 0) multi_converter_unit_set_overflow(multi_converter_state->buffer_dest); + } +} + +uint8_t multi_converter_unit_distance_allowed(MultiConverterUnitType unit_type) { + return ( + unit_type == UnitTypeKilometers || unit_type == UnitTypeMeters || unit_type == UnitTypeCentimeters || + unit_type == UnitTypeMiles || unit_type == UnitTypeFeet || unit_type == UnitTypeInches + ); +} + +// +// DEG / RAD +// + +void multi_converter_unit_angle_convert(MultiConverterState* const multi_converter_state) { + double a = strtof(multi_converter_state->buffer_orig, NULL); + uint8_t overflow = 0; + + switch(multi_converter_state->unit_type_orig) { + default: + break; + case UnitTypeDegree: + if (multi_converter_state->unit_type_dest == UnitTypeRadian) a *= ((double) 0.0174532925199); + break; + + case UnitTypeRadian: + if (multi_converter_state->unit_type_dest == UnitTypeDegree) a *= ((double) 57.2957795131); + break; + } + + if (overflow) { + multi_converter_unit_set_overflow(multi_converter_state->buffer_dest); + } else { + + int ret = snprintf(multi_converter_state->buffer_dest, MULTI_CONVERTER_NUMBER_DIGITS + 1, "%lf", a); + + if (ret < 0) multi_converter_unit_set_overflow(multi_converter_state->buffer_dest); + } +} + +uint8_t multi_converter_unit_angle_allowed(MultiConverterUnitType unit_type) { + return (unit_type == UnitTypeDegree || unit_type == UnitTypeRadian); +} \ No newline at end of file diff --git a/applications/multi_converter/multi_converter_units.h b/applications/multi_converter/multi_converter_units.h new file mode 100644 index 000000000..da281627e --- /dev/null +++ b/applications/multi_converter/multi_converter_units.h @@ -0,0 +1,71 @@ +#pragma once + +#include +#include + +#include "multi_converter_definitions.h" + +#define MULTI_CONVERTER_AVAILABLE_UNITS 14 + +#define multi_converter_get_unit(unit_type) multi_converter_available_units[unit_type] +#define multi_converter_get_unit_type_offset(unit_type, offset) (((unit_type + offset) % MULTI_CONVERTER_AVAILABLE_UNITS + MULTI_CONVERTER_AVAILABLE_UNITS) % MULTI_CONVERTER_AVAILABLE_UNITS) +// the modulo operation will fail with extremely large values on the units array + +// DEC / HEX / BIN +void multi_converter_unit_dec_hex_bin_convert(MultiConverterState* const multi_converter_state); +uint8_t multi_converter_unit_dec_hex_bin_allowed(MultiConverterUnitType); + +// CEL / FAR / KEL +void multi_converter_unit_temperature_convert(MultiConverterState* const multi_converter_state); +uint8_t multi_converter_unit_temperature_allowed(MultiConverterUnitType); + +// KM / M / CM / MILES / FEET / INCHES +void multi_converter_unit_distance_convert(MultiConverterState* const multi_converter_state); +uint8_t multi_converter_unit_distance_allowed(MultiConverterUnitType); + +// DEG / RAD +void multi_converter_unit_angle_convert(MultiConverterState* const multi_converter_state); +uint8_t multi_converter_unit_angle_allowed(MultiConverterUnitType unit_type); + +// +// each unit is made of comma? + negative? + keyboard_length + mini_name + name + convert function + allowed function +// (setting functions as NULL will cause convert / select options to be ignored) +// +static const MultiConverterUnit multi_converter_unit_dec = { 0, 0, 10, "DEC\0", "Decimal\0", multi_converter_unit_dec_hex_bin_convert, multi_converter_unit_dec_hex_bin_allowed }; +static const MultiConverterUnit multi_converter_unit_hex = { 0, 0, 16, "HEX\0", "Hexadecimal\0", multi_converter_unit_dec_hex_bin_convert, multi_converter_unit_dec_hex_bin_allowed }; +static const MultiConverterUnit multi_converter_unit_bin = { 0, 0, 2, "BIN\0", "Binary\0", multi_converter_unit_dec_hex_bin_convert, multi_converter_unit_dec_hex_bin_allowed }; + +static const MultiConverterUnit multi_converter_unit_cel = { 1, 1, 10, "CEL\0", "Celsius\0", multi_converter_unit_temperature_convert, multi_converter_unit_temperature_allowed }; +static const MultiConverterUnit multi_converter_unit_far = { 1, 1, 10, "FAR\0", "Fahernheit\0", multi_converter_unit_temperature_convert, multi_converter_unit_temperature_allowed }; +static const MultiConverterUnit multi_converter_unit_kel = { 1, 1, 10, "KEL\0", "Kelvin\0", multi_converter_unit_temperature_convert, multi_converter_unit_temperature_allowed }; + +static const MultiConverterUnit multi_converter_unit_km = { 1, 0, 10, "KM\0", "Kilometers\0", multi_converter_unit_distance_convert, multi_converter_unit_distance_allowed }; +static const MultiConverterUnit multi_converter_unit_m = { 1, 0, 10, "M\0", "Meters\0", multi_converter_unit_distance_convert, multi_converter_unit_distance_allowed }; +static const MultiConverterUnit multi_converter_unit_cm = { 1, 0, 10, "CM\0", "Centimeters\0", multi_converter_unit_distance_convert, multi_converter_unit_distance_allowed }; +static const MultiConverterUnit multi_converter_unit_mi = { 1, 0, 10, "MI\0", "Miles\0", multi_converter_unit_distance_convert, multi_converter_unit_distance_allowed }; +static const MultiConverterUnit multi_converter_unit_ft = { 1, 0, 10, "FT\0", "Feet\0", multi_converter_unit_distance_convert, multi_converter_unit_distance_allowed }; +static const MultiConverterUnit multi_converter_unit_in = { 1, 0, 10, " \"\0", "Inches\0", multi_converter_unit_distance_convert, multi_converter_unit_distance_allowed }; + +static const MultiConverterUnit multi_converter_unit_deg = { 1, 0, 10, "DEG\0", "Degree\0", multi_converter_unit_angle_convert, multi_converter_unit_angle_allowed }; +static const MultiConverterUnit multi_converter_unit_rad = { 1, 0, 10, "RAD\0", "Radian\0", multi_converter_unit_angle_convert, multi_converter_unit_angle_allowed }; + +// index order set by the MultiConverterUnitType enum element (multi_converter_definitions.h) +static const MultiConverterUnit multi_converter_available_units[MULTI_CONVERTER_AVAILABLE_UNITS] = { + [UnitTypeDec] = multi_converter_unit_dec, + [UnitTypeHex] = multi_converter_unit_hex, + [UnitTypeBin] = multi_converter_unit_bin, + + [UnitTypeCelsius] = multi_converter_unit_cel, + [UnitTypeFahernheit] = multi_converter_unit_far, + [UnitTypeKelvin] = multi_converter_unit_kel, + + [UnitTypeKilometers] = multi_converter_unit_km, + [UnitTypeMeters] = multi_converter_unit_m, + [UnitTypeCentimeters] = multi_converter_unit_cm, + [UnitTypeMiles] = multi_converter_unit_mi, + [UnitTypeFeet] = multi_converter_unit_ft, + [UnitTypeInches] = multi_converter_unit_in, + + [UnitTypeDegree] = multi_converter_unit_deg, + [UnitTypeRadian] = multi_converter_unit_rad, +}; \ No newline at end of file diff --git a/applications/nfc/helpers/nfc_generators.c b/applications/nfc/helpers/nfc_generators.c index 3ec78a127..b94adbd7b 100644 --- a/applications/nfc/helpers/nfc_generators.c +++ b/applications/nfc/helpers/nfc_generators.c @@ -25,6 +25,39 @@ static void nfc_generate_mf_ul_uid(uint8_t* uid) { uid[6] |= 0x80; } +static void nfc_generate_mf_classic_uid(uint8_t* uid, uint8_t length) { + uid[0] = NXP_MANUFACTURER_ID; + furi_hal_random_fill_buf(&uid[1], length - 1); +} + +static void nfc_generate_mf_classic_block_0(uint8_t* block, uint8_t uid_len) { + // Block length is always 16 bytes, and the UID can be either 4 or 7 bytes + furi_assert(uid_len == 4 || uid_len == 7); + furi_assert(block); + nfc_generate_mf_classic_uid(block, uid_len); + for(int i = uid_len; i < 16; i++) { + block[i] = 0xFF; + } +} + +static void nfc_generate_mf_classic_sector_trailer(MfClassicData* data, uint8_t block) { + // All keys are set to FFFF FFFF FFFFh at chip delivery and the bytes 6, 7 and 8 are set to FF0780h. + MfClassicSectorTrailer* sec_tr = (MfClassicSectorTrailer*)data->block[block].value; + sec_tr->access_bits[0] = 0xFF; + sec_tr->access_bits[1] = 0x07; + sec_tr->access_bits[2] = 0x80; + sec_tr->access_bits[3] = 0x69; // Nice + + memset(sec_tr->key_a, 0xff, sizeof(sec_tr->key_a)); + memset(sec_tr->key_b, 0xff, sizeof(sec_tr->key_b)); + + mf_classic_set_block_read(data, block, &data->block[block]); + mf_classic_set_key_found( + data, mf_classic_get_sector_by_block(block), MfClassicKeyA, 0xFFFFFFFFFFFF); + mf_classic_set_key_found( + data, mf_classic_get_sector_by_block(block), MfClassicKeyB, 0xFFFFFFFFFFFF); +} + static void nfc_generate_mf_ul_common(NfcDeviceData* data) { data->nfc_data.type = FuriHalNfcTypeA; data->nfc_data.interface = FuriHalNfcInterfaceRf; @@ -36,6 +69,19 @@ static void nfc_generate_mf_ul_common(NfcDeviceData* data) { data->protocol = NfcDeviceProtocolMifareUl; } +static void + nfc_generate_mf_classic_common(NfcDeviceData* data, uint8_t uid_len, MfClassicType type) { + data->nfc_data.type = FuriHalNfcTypeA; + data->nfc_data.interface = FuriHalNfcInterfaceRf; + data->nfc_data.uid_len = uid_len; + nfc_generate_mf_classic_block_0(data->mf_classic_data.block[0].value, uid_len); + data->nfc_data.atqa[0] = 0x44; + data->nfc_data.atqa[1] = 0x00; + data->nfc_data.sak = 0x08; + data->protocol = NfcDeviceProtocolMifareClassic; + data->mf_classic_data.type = type; +} + static void nfc_generate_calc_bcc(uint8_t* uid, uint8_t* bcc0, uint8_t* bcc1) { *bcc0 = 0x88 ^ uid[0] ^ uid[1] ^ uid[2]; *bcc1 = uid[3] ^ uid[4] ^ uid[5] ^ uid[6]; @@ -268,70 +314,161 @@ static void nfc_generate_ntag_i2c_plus_2k(NfcDeviceData* data) { mful->version.storage_size = 0x15; } +static void nfc_generate_mf_classic(NfcDeviceData* data, uint8_t uid_len, MfClassicType type) { + nfc_generate_common_start(data); + nfc_generate_mf_classic_common(data, uid_len, type); + + // Set the UID + data->nfc_data.uid[0] = NXP_MANUFACTURER_ID; + for(int i = 1; i < uid_len; i++) { + data->nfc_data.uid[i] = data->mf_classic_data.block[0].value[i]; + } + + MfClassicData* mfc = &data->mf_classic_data; + mf_classic_set_block_read(mfc, 0, &mfc->block[0]); + + if(type == MfClassicType4k) { + // Set every block to 0xFF + for(uint16_t i = 1; i < 256; i += 1) { + if(mf_classic_is_sector_trailer(i)) { + nfc_generate_mf_classic_sector_trailer(mfc, i); + } else { + memset(&mfc->block[i].value, 0xFF, 16); + } + mf_classic_set_block_read(mfc, i, &mfc->block[i]); + } + } else if(type == MfClassicType1k) { + // Set every block to 0xFF + for(uint16_t i = 1; i < MF_CLASSIC_1K_TOTAL_SECTORS_NUM * 4; i += 1) { + if(mf_classic_is_sector_trailer(i)) { + nfc_generate_mf_classic_sector_trailer(mfc, i); + } else { + memset(&mfc->block[i].value, 0xFF, 16); + } + mf_classic_set_block_read(mfc, i, &mfc->block[i]); + } + } + + mfc->type = type; +} + +static void nfc_generate_mf_classic_1k_4b_uid(NfcDeviceData* data) { + nfc_generate_mf_classic(data, 4, MfClassicType1k); +} + +static void nfc_generate_mf_classic_1k_7b_uid(NfcDeviceData* data) { + nfc_generate_mf_classic(data, 7, MfClassicType1k); +} + +static void nfc_generate_mf_classic_4k_4b_uid(NfcDeviceData* data) { + nfc_generate_mf_classic(data, 4, MfClassicType4k); +} + +static void nfc_generate_mf_classic_4k_7b_uid(NfcDeviceData* data) { + nfc_generate_mf_classic(data, 7, MfClassicType4k); +} + static const NfcGenerator mf_ul_generator = { .name = "Mifare Ultralight", .generator_func = nfc_generate_mf_ul_orig, - .next_scene = NfcSceneMfUltralightMenu}; + .next_scene = NfcSceneMfUltralightMenu, +}; static const NfcGenerator mf_ul_11_generator = { .name = "Mifare Ultralight EV1 11", .generator_func = nfc_generate_mf_ul_11, - .next_scene = NfcSceneMfUltralightMenu}; + .next_scene = NfcSceneMfUltralightMenu, +}; static const NfcGenerator mf_ul_h11_generator = { .name = "Mifare Ultralight EV1 H11", .generator_func = nfc_generate_mf_ul_h11, - .next_scene = NfcSceneMfUltralightMenu}; + .next_scene = NfcSceneMfUltralightMenu, +}; static const NfcGenerator mf_ul_21_generator = { .name = "Mifare Ultralight EV1 21", .generator_func = nfc_generate_mf_ul_21, - .next_scene = NfcSceneMfUltralightMenu}; + .next_scene = NfcSceneMfUltralightMenu, +}; static const NfcGenerator mf_ul_h21_generator = { .name = "Mifare Ultralight EV1 H21", .generator_func = nfc_generate_mf_ul_h21, - .next_scene = NfcSceneMfUltralightMenu}; + .next_scene = NfcSceneMfUltralightMenu, +}; static const NfcGenerator ntag203_generator = { .name = "NTAG203", .generator_func = nfc_generate_mf_ul_ntag203, - .next_scene = NfcSceneMfUltralightMenu}; + .next_scene = NfcSceneMfUltralightMenu, +}; static const NfcGenerator ntag213_generator = { .name = "NTAG213", .generator_func = nfc_generate_ntag213, - .next_scene = NfcSceneMfUltralightMenu}; + .next_scene = NfcSceneMfUltralightMenu, +}; static const NfcGenerator ntag215_generator = { .name = "NTAG215", .generator_func = nfc_generate_ntag215, - .next_scene = NfcSceneMfUltralightMenu}; + .next_scene = NfcSceneMfUltralightMenu, +}; static const NfcGenerator ntag216_generator = { .name = "NTAG216", .generator_func = nfc_generate_ntag216, - .next_scene = NfcSceneMfUltralightMenu}; + .next_scene = NfcSceneMfUltralightMenu, +}; static const NfcGenerator ntag_i2c_1k_generator = { .name = "NTAG I2C 1k", .generator_func = nfc_generate_ntag_i2c_1k, - .next_scene = NfcSceneMfUltralightMenu}; + .next_scene = NfcSceneMfUltralightMenu, +}; static const NfcGenerator ntag_i2c_2k_generator = { .name = "NTAG I2C 2k", .generator_func = nfc_generate_ntag_i2c_2k, - .next_scene = NfcSceneMfUltralightMenu}; + .next_scene = NfcSceneMfUltralightMenu, +}; static const NfcGenerator ntag_i2c_plus_1k_generator = { .name = "NTAG I2C Plus 1k", .generator_func = nfc_generate_ntag_i2c_plus_1k, - .next_scene = NfcSceneMfUltralightMenu}; + .next_scene = NfcSceneMfUltralightMenu, +}; static const NfcGenerator ntag_i2c_plus_2k_generator = { .name = "NTAG I2C Plus 2k", .generator_func = nfc_generate_ntag_i2c_plus_2k, - .next_scene = NfcSceneMfUltralightMenu}; + .next_scene = NfcSceneMfUltralightMenu, +}; + +static const NfcGenerator mifare_classic_1k_4b_uid_generator = { + .name = "Mifare Classic 1k 4byte UID", + .generator_func = nfc_generate_mf_classic_1k_4b_uid, + .next_scene = NfcSceneMfClassicMenu, +}; + +static const NfcGenerator mifare_classic_1k_7b_uid_generator = { + .name = "Mifare Classic 1k 7byte UID", + .generator_func = nfc_generate_mf_classic_1k_7b_uid, + .next_scene = NfcSceneMfClassicMenu, +}; + +static const NfcGenerator mifare_classic_4k_4b_uid_generator = { + .name = "Mifare Classic 4k 4byte UID", + .generator_func = nfc_generate_mf_classic_4k_4b_uid, + .next_scene = NfcSceneMfClassicMenu, +}; + +static const NfcGenerator mifare_classic_4k_7b_uid_generator = { + .name = "Mifare Classic 4k 7byte UID", + .generator_func = nfc_generate_mf_classic_4k_7b_uid, + .next_scene = NfcSceneMfClassicMenu, +}; const NfcGenerator* const nfc_generators[] = { &mf_ul_generator, @@ -347,5 +484,9 @@ const NfcGenerator* const nfc_generators[] = { &ntag_i2c_2k_generator, &ntag_i2c_plus_1k_generator, &ntag_i2c_plus_2k_generator, + &mifare_classic_1k_4b_uid_generator, + &mifare_classic_1k_7b_uid_generator, + &mifare_classic_4k_4b_uid_generator, + &mifare_classic_4k_7b_uid_generator, NULL, }; diff --git a/applications/picopass/application.fam b/applications/picopass/application.fam index d1f6b93ca..520e2a35a 100644 --- a/applications/picopass/application.fam +++ b/applications/picopass/application.fam @@ -5,6 +5,6 @@ App( entry_point="picopass_app", cdefines=["APP_PICOPASS"], requires=["storage", "gui"], - stack_size=1 * 1024, + stack_size=4 * 1024, order=30, ) diff --git a/applications/picopass/picopass.c b/applications/picopass/picopass.c index df327eaa8..805f6760e 100644 --- a/applications/picopass/picopass.c +++ b/applications/picopass/picopass.c @@ -56,6 +56,11 @@ Picopass* picopass_alloc() { view_dispatcher_add_view( picopass->view_dispatcher, PicopassViewPopup, popup_get_view(picopass->popup)); + // Loading + picopass->loading = loading_alloc(); + view_dispatcher_add_view( + picopass->view_dispatcher, PicopassViewLoading, loading_get_view(picopass->loading)); + // Text Input picopass->text_input = text_input_alloc(); view_dispatcher_add_view( @@ -86,6 +91,10 @@ void picopass_free(Picopass* picopass) { view_dispatcher_remove_view(picopass->view_dispatcher, PicopassViewPopup); popup_free(picopass->popup); + // Loading + view_dispatcher_remove_view(picopass->view_dispatcher, PicopassViewLoading); + loading_free(picopass->loading); + // TextInput view_dispatcher_remove_view(picopass->view_dispatcher, PicopassViewTextInput); text_input_free(picopass->text_input); @@ -148,6 +157,20 @@ void picopass_blink_stop(Picopass* picopass) { notification_message(picopass->notifications, &picopass_sequence_blink_stop); } +void picopass_show_loading_popup(void* context, bool show) { + Picopass* picopass = context; + TaskHandle_t timer_task = xTaskGetHandle(configTIMER_SERVICE_TASK_NAME); + + if(show) { + // Raise timer priority so that animations can play + vTaskPrioritySet(timer_task, configMAX_PRIORITIES - 1); + view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewLoading); + } else { + // Restore default timer priority + vTaskPrioritySet(timer_task, configTIMER_TASK_PRIORITY); + } +} + int32_t picopass_app(void* p) { UNUSED(p); Picopass* picopass = picopass_alloc(); diff --git a/applications/picopass/picopass_device.c b/applications/picopass/picopass_device.c index 9b422edd3..4cd6faaab 100644 --- a/applications/picopass/picopass_device.c +++ b/applications/picopass/picopass_device.c @@ -8,6 +8,9 @@ static const char* picopass_file_header = "Flipper Picopass device"; static const uint32_t picopass_file_version = 1; +const uint8_t picopass_iclass_decryptionkey[] = + {0xb4, 0x21, 0x2c, 0xca, 0xb7, 0xed, 0x21, 0x0f, 0x7b, 0x93, 0xd4, 0x59, 0x39, 0xc7, 0xdd, 0x36}; + PicopassDevice* picopass_device_alloc() { PicopassDevice* picopass_dev = malloc(sizeof(PicopassDevice)); picopass_dev->dev_data.pacs.legacy = false; @@ -15,6 +18,7 @@ PicopassDevice* picopass_device_alloc() { picopass_dev->dev_data.pacs.pin_length = 0; picopass_dev->storage = furi_record_open(RECORD_STORAGE); picopass_dev->dialogs = furi_record_open(RECORD_DIALOGS); + string_init(picopass_dev->load_path); return picopass_dev; } @@ -111,7 +115,7 @@ static bool picopass_device_save_file( } while(0); if(!saved) { - dialog_message_show_storage_error(dev->dialogs, "Can not save\nkey file"); + dialog_message_show_storage_error(dev->dialogs, "Can not save\nfile"); } string_clear(temp_str); flipper_format_free(file); @@ -128,11 +132,83 @@ bool picopass_device_save(PicopassDevice* dev, const char* dev_name) { return false; } +static bool picopass_device_load_data(PicopassDevice* dev, string_t path, bool show_dialog) { + bool parsed = false; + FlipperFormat* file = flipper_format_file_alloc(dev->storage); + PicopassBlock* AA1 = dev->dev_data.AA1; + PicopassPacs* pacs = &dev->dev_data.pacs; + string_t temp_str; + string_init(temp_str); + bool deprecated_version = false; + + if(dev->loading_cb) { + dev->loading_cb(dev->loading_cb_ctx, true); + } + + do { + if(!flipper_format_file_open_existing(file, string_get_cstr(path))) break; + + // Read and verify file header + uint32_t version = 0; + if(!flipper_format_read_header(file, temp_str, &version)) break; + if(string_cmp_str(temp_str, picopass_file_header) || (version != picopass_file_version)) { + deprecated_version = true; + break; + } + + // Parse header blocks + bool block_read = true; + for(size_t i = 0; i < 6; i++) { + string_printf(temp_str, "Block %d", i); + if(!flipper_format_read_hex( + file, string_get_cstr(temp_str), AA1[i].data, PICOPASS_BLOCK_LEN)) { + block_read = false; + break; + } + } + + size_t app_limit = AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[0]; + for(size_t i = 6; i < app_limit; i++) { + string_printf(temp_str, "Block %d", i); + if(!flipper_format_read_hex( + file, string_get_cstr(temp_str), AA1[i].data, PICOPASS_BLOCK_LEN)) { + block_read = false; + break; + } + } + if(!block_read) break; + + if(picopass_device_parse_credential(AA1, pacs) != ERR_NONE) break; + if(picopass_device_parse_wiegand(pacs->credential, &pacs->record) != ERR_NONE) break; + + parsed = true; + } while(false); + + if(dev->loading_cb) { + dev->loading_cb(dev->loading_cb_ctx, false); + } + + if((!parsed) && (show_dialog)) { + if(deprecated_version) { + dialog_message_show_storage_error(dev->dialogs, "File format deprecated"); + } else { + dialog_message_show_storage_error(dev->dialogs, "Can not parse\nfile"); + } + } + + string_clear(temp_str); + flipper_format_free(file); + + return parsed; +} + void picopass_device_clear(PicopassDevice* dev) { furi_assert(dev); picopass_device_data_clear(&dev->dev_data); memset(&dev->dev_data, 0, sizeof(dev->dev_data)); + dev->format = PicopassDeviceSaveFormatHF; + string_reset(dev->load_path); } void picopass_device_free(PicopassDevice* picopass_dev) { @@ -144,6 +220,36 @@ void picopass_device_free(PicopassDevice* picopass_dev) { free(picopass_dev); } +bool picopass_file_select(PicopassDevice* dev) { + furi_assert(dev); + + // Input events and views are managed by file_browser + string_t picopass_app_folder; + string_init_set_str(picopass_app_folder, PICOPASS_APP_FOLDER); + bool res = dialog_file_browser_show( + dev->dialogs, + dev->load_path, + picopass_app_folder, + PICOPASS_APP_EXTENSION, + true, + &I_Nfc_10px, + true); + string_clear(picopass_app_folder); + if(res) { + string_t filename; + string_init(filename); + path_extract_filename(dev->load_path, filename, true); + strncpy(dev->dev_name, string_get_cstr(filename), PICOPASS_DEV_NAME_MAX_LEN); + res = picopass_device_load_data(dev, dev->load_path, true); + if(res) { + picopass_device_set_name(dev, dev->dev_name); + } + string_clear(filename); + } + + return res; +} + void picopass_device_data_clear(PicopassDeviceData* dev_data) { for(size_t i = 0; i < PICOPASS_MAX_APP_LIMIT; i++) { memset(dev_data->AA1[i].data, 0, sizeof(dev_data->AA1[i].data)); @@ -152,3 +258,122 @@ void picopass_device_data_clear(PicopassDeviceData* dev_data) { dev_data->pacs.se_enabled = false; dev_data->pacs.pin_length = 0; } + +bool picopass_device_delete(PicopassDevice* dev, bool use_load_path) { + furi_assert(dev); + + bool deleted = false; + string_t file_path; + string_init(file_path); + + do { + // Delete original file + if(use_load_path && !string_empty_p(dev->load_path)) { + string_set(file_path, dev->load_path); + } else { + string_printf( + file_path, "%s/%s%s", PICOPASS_APP_FOLDER, dev->dev_name, PICOPASS_APP_EXTENSION); + } + if(!storage_simply_remove(dev->storage, string_get_cstr(file_path))) break; + deleted = true; + } while(0); + + if(!deleted) { + dialog_message_show_storage_error(dev->dialogs, "Can not remove file"); + } + + string_clear(file_path); + return deleted; +} + +void picopass_device_set_loading_callback( + PicopassDevice* dev, + PicopassLoadingCallback callback, + void* context) { + furi_assert(dev); + + dev->loading_cb = callback; + dev->loading_cb_ctx = context; +} + +ReturnCode picopass_device_decrypt(uint8_t* enc_data, uint8_t* dec_data) { + uint8_t key[32] = {0}; + memcpy(key, picopass_iclass_decryptionkey, sizeof(picopass_iclass_decryptionkey)); + mbedtls_des3_context ctx; + mbedtls_des3_init(&ctx); + mbedtls_des3_set2key_dec(&ctx, key); + mbedtls_des3_crypt_ecb(&ctx, enc_data, dec_data); + mbedtls_des3_free(&ctx); + return ERR_NONE; +} + +ReturnCode picopass_device_parse_credential(PicopassBlock* AA1, PicopassPacs* pacs) { + ReturnCode err; + + // Thank you proxmark! + pacs->legacy = (memcmp(AA1[5].data, "\xff\xff\xff\xff\xff\xff\xff\xff", 8) == 0); + pacs->se_enabled = (memcmp(AA1[5].data, "\xff\xff\xff\x00\x06\xff\xff\xff", 8) == 0); + + pacs->biometrics = AA1[6].data[4]; + pacs->pin_length = AA1[6].data[6] & 0x0F; + pacs->encryption = AA1[6].data[7]; + + if(pacs->encryption == PicopassDeviceEncryption3DES) { + FURI_LOG_D(TAG, "3DES Encrypted"); + err = picopass_device_decrypt(AA1[7].data, pacs->credential); + if(err != ERR_NONE) { + FURI_LOG_E(TAG, "decrypt error %d", err); + return err; + } + + err = picopass_device_decrypt(AA1[8].data, pacs->pin0); + if(err != ERR_NONE) { + FURI_LOG_E(TAG, "decrypt error %d", err); + return err; + } + + err = picopass_device_decrypt(AA1[9].data, pacs->pin1); + if(err != ERR_NONE) { + FURI_LOG_E(TAG, "decrypt error %d", err); + return err; + } + } else if(pacs->encryption == PicopassDeviceEncryptionNone) { + FURI_LOG_D(TAG, "No Encryption"); + memcpy(pacs->credential, AA1[7].data, PICOPASS_BLOCK_LEN); + memcpy(pacs->pin0, AA1[8].data, PICOPASS_BLOCK_LEN); + memcpy(pacs->pin1, AA1[9].data, PICOPASS_BLOCK_LEN); + } else if(pacs->encryption == PicopassDeviceEncryptionDES) { + FURI_LOG_D(TAG, "DES Encrypted"); + } else { + FURI_LOG_D(TAG, "Unknown encryption"); + } + + return ERR_NONE; +} + +ReturnCode picopass_device_parse_wiegand(uint8_t* data, PicopassWiegandRecord* record) { + uint32_t* halves = (uint32_t*)data; + if(halves[0] == 0) { + uint8_t leading0s = __builtin_clz(REVERSE_BYTES_U32(halves[1])); + record->bitLength = 31 - leading0s; + } else { + uint8_t leading0s = __builtin_clz(REVERSE_BYTES_U32(halves[0])); + record->bitLength = 63 - leading0s; + } + FURI_LOG_D(TAG, "bitLength: %d", record->bitLength); + + if(record->bitLength == 26) { + uint8_t* v4 = data + 4; + uint32_t bot = v4[3] | (v4[2] << 8) | (v4[1] << 16) | (v4[0] << 24); + + record->CardNumber = (bot >> 1) & 0xFFFF; + record->FacilityCode = (bot >> 17) & 0xFF; + FURI_LOG_D(TAG, "FC:%u CN: %u\n", record->FacilityCode, record->CardNumber); + record->valid = true; + } else { + record->CardNumber = 0; + record->FacilityCode = 0; + record->valid = false; + } + return ERR_NONE; +} diff --git a/applications/picopass/picopass_device.h b/applications/picopass/picopass_device.h index 89e031ca7..0415b8794 100644 --- a/applications/picopass/picopass_device.h +++ b/applications/picopass/picopass_device.h @@ -7,6 +7,10 @@ #include +#include +#include +#include + #define PICOPASS_DEV_NAME_MAX_LEN 22 #define PICOPASS_READER_DATA_MAX_SIZE 64 #define PICOPASS_BLOCK_LEN 8 @@ -20,6 +24,8 @@ #define PICOPASS_APP_EXTENSION ".picopass" #define PICOPASS_APP_SHADOW_EXTENSION ".pas" +typedef void (*PicopassLoadingCallback)(void* context, bool state); + typedef enum { PicopassDeviceEncryptionUnknown = 0, PicopassDeviceEncryptionNone = 0x14, @@ -67,6 +73,9 @@ typedef struct { char dev_name[PICOPASS_DEV_NAME_MAX_LEN + 1]; string_t load_path; PicopassDeviceSaveFormat format; + PicopassLoadingCallback loading_cb; + void* loading_cb_ctx; + } PicopassDevice; PicopassDevice* picopass_device_alloc(); @@ -77,6 +86,18 @@ void picopass_device_set_name(PicopassDevice* dev, const char* name); bool picopass_device_save(PicopassDevice* dev, const char* dev_name); +bool picopass_file_select(PicopassDevice* dev); + void picopass_device_data_clear(PicopassDeviceData* dev_data); void picopass_device_clear(PicopassDevice* dev); + +bool picopass_device_delete(PicopassDevice* dev, bool use_load_path); + +void picopass_device_set_loading_callback( + PicopassDevice* dev, + PicopassLoadingCallback callback, + void* context); + +ReturnCode picopass_device_parse_credential(PicopassBlock* AA1, PicopassPacs* pacs); +ReturnCode picopass_device_parse_wiegand(uint8_t* data, PicopassWiegandRecord* record); diff --git a/applications/picopass/picopass_i.h b/applications/picopass/picopass_i.h index dbf4f8be5..d295f53ac 100644 --- a/applications/picopass/picopass_i.h +++ b/applications/picopass/picopass_i.h @@ -14,6 +14,7 @@ #include #include +#include #include #include @@ -55,6 +56,7 @@ struct Picopass { // Common Views Submenu* submenu; Popup* popup; + Loading* loading; TextInput* text_input; Widget* widget; }; @@ -62,6 +64,7 @@ struct Picopass { typedef enum { PicopassViewMenu, PicopassViewPopup, + PicopassViewLoading, PicopassViewTextInput, PicopassViewWidget, } PicopassView; @@ -75,3 +78,5 @@ void picopass_text_store_clear(Picopass* picopass); void picopass_blink_start(Picopass* picopass); void picopass_blink_stop(Picopass* picopass); + +void picopass_show_loading_popup(void* context, bool show); diff --git a/applications/picopass/picopass_worker.c b/applications/picopass/picopass_worker.c index 3079a98c4..560548a18 100644 --- a/applications/picopass/picopass_worker.c +++ b/applications/picopass/picopass_worker.c @@ -1,23 +1,9 @@ #include "picopass_worker_i.h" -#include - -#include -#include -#include -#include -#include - -#include -#include -#include - -#include #define TAG "PicopassWorker" const uint8_t picopass_iclass_key[] = {0xaf, 0xa7, 0x85, 0xa7, 0xda, 0xb3, 0x33, 0x78}; -const uint8_t picopass_iclass_decryptionkey[] = - {0xb4, 0x21, 0x2c, 0xca, 0xb7, 0xed, 0x21, 0x0f, 0x7b, 0x93, 0xd4, 0x59, 0x39, 0xc7, 0xdd, 0x36}; +const uint8_t picopass_factory_key[] = {0x76, 0x65, 0x54, 0x43, 0x32, 0x21, 0x10, 0x00}; static void picopass_worker_enable_field() { st25r3916TxRxOn(); @@ -31,44 +17,6 @@ static ReturnCode picopass_worker_disable_field(ReturnCode rc) { return rc; } -static ReturnCode picopass_worker_decrypt(uint8_t* enc_data, uint8_t* dec_data) { - uint8_t key[32] = {0}; - memcpy(key, picopass_iclass_decryptionkey, sizeof(picopass_iclass_decryptionkey)); - mbedtls_des3_context ctx; - mbedtls_des3_init(&ctx); - mbedtls_des3_set2key_dec(&ctx, key); - mbedtls_des3_crypt_ecb(&ctx, enc_data, dec_data); - mbedtls_des3_free(&ctx); - return ERR_NONE; -} - -static ReturnCode picopass_worker_parse_wiegand(uint8_t* data, PicopassWiegandRecord* record) { - uint32_t* halves = (uint32_t*)data; - if(halves[0] == 0) { - uint8_t leading0s = __builtin_clz(REVERSE_BYTES_U32(halves[1])); - record->bitLength = 31 - leading0s; - } else { - uint8_t leading0s = __builtin_clz(REVERSE_BYTES_U32(halves[0])); - record->bitLength = 63 - leading0s; - } - FURI_LOG_D(TAG, "bitLength: %d", record->bitLength); - - if(record->bitLength == 26) { - uint8_t* v4 = data + 4; - uint32_t bot = v4[3] | (v4[2] << 8) | (v4[1] << 16) | (v4[0] << 24); - - record->CardNumber = (bot >> 1) & 0xFFFF; - record->FacilityCode = (bot >> 17) & 0xFF; - FURI_LOG_D(TAG, "FC:%u CN: %u\n", record->FacilityCode, record->CardNumber); - record->valid = true; - } else { - record->CardNumber = 0; - record->FacilityCode = 0; - record->valid = false; - } - return ERR_NONE; -} - /***************************** Picopass Worker API *******************************/ PicopassWorker* picopass_worker_alloc() { @@ -157,7 +105,7 @@ ReturnCode picopass_detect_card(int timeout) { err = rfalPicoPassPollerCheckPresence(); if(err != ERR_RF_COLLISION) { - FURI_LOG_E(TAG, "rfalPicoPassPollerCheckPresence error %d", err); + //FURI_LOG_E(TAG, "rfalPicoPassPollerCheckPresence error %d", err); return err; } @@ -272,46 +220,15 @@ void picopass_worker_detect(PicopassWorker* picopass_worker) { FURI_LOG_E(TAG, "picopass_read_card error %d", err); } - // Thank you proxmark! - pacs->legacy = (memcmp(AA1[5].data, "\xff\xff\xff\xff\xff\xff\xff\xff", 8) == 0); - pacs->se_enabled = (memcmp(AA1[5].data, "\xff\xff\xff\x00\x06\xff\xff\xff", 8) == 0); - - pacs->biometrics = AA1[6].data[4]; - pacs->pin_length = AA1[6].data[6] & 0x0F; - pacs->encryption = AA1[6].data[7]; - - if(pacs->encryption == PicopassDeviceEncryption3DES) { - FURI_LOG_D(TAG, "3DES Encrypted"); - err = picopass_worker_decrypt(AA1[7].data, pacs->credential); - if(err != ERR_NONE) { - FURI_LOG_E(TAG, "decrypt error %d", err); - break; - } - - err = picopass_worker_decrypt(AA1[8].data, pacs->pin0); - if(err != ERR_NONE) { - FURI_LOG_E(TAG, "decrypt error %d", err); - break; - } - - err = picopass_worker_decrypt(AA1[9].data, pacs->pin1); - if(err != ERR_NONE) { - FURI_LOG_E(TAG, "decrypt error %d", err); - break; - } - } else if(pacs->encryption == PicopassDeviceEncryptionNone) { - FURI_LOG_D(TAG, "No Encryption"); - memcpy(pacs->credential, AA1[7].data, PICOPASS_BLOCK_LEN); - memcpy(pacs->pin0, AA1[8].data, PICOPASS_BLOCK_LEN); - memcpy(pacs->pin1, AA1[9].data, PICOPASS_BLOCK_LEN); - } else if(pacs->encryption == PicopassDeviceEncryptionDES) { - FURI_LOG_D(TAG, "DES Encrypted"); - } else { - FURI_LOG_D(TAG, "Unknown encryption"); - break; + err = picopass_device_parse_credential(AA1, pacs); + if(err != ERR_NONE) { + FURI_LOG_E(TAG, "picopass_device_parse_credential error %d", err); } - picopass_worker_parse_wiegand(pacs->credential, &pacs->record); + err = picopass_device_parse_wiegand(pacs->credential, &pacs->record); + if(err != ERR_NONE) { + FURI_LOG_E(TAG, "picopass_device_parse_wiegand error %d", err); + } // Notify caller and exit if(picopass_worker->callback) { diff --git a/applications/picopass/picopass_worker_i.h b/applications/picopass/picopass_worker_i.h index 2610d5e7f..789951900 100644 --- a/applications/picopass/picopass_worker_i.h +++ b/applications/picopass/picopass_worker_i.h @@ -6,6 +6,15 @@ #include #include +#include + +#include +#include +#include +#include + +#include + struct PicopassWorker { FuriThread* thread; Storage* storage; diff --git a/applications/picopass/scenes/picopass_scene_config.h b/applications/picopass/scenes/picopass_scene_config.h index 0a3e73f29..87745378b 100755 --- a/applications/picopass/scenes/picopass_scene_config.h +++ b/applications/picopass/scenes/picopass_scene_config.h @@ -5,3 +5,7 @@ ADD_SCENE(picopass, card_menu, CardMenu) ADD_SCENE(picopass, save_name, SaveName) ADD_SCENE(picopass, save_success, SaveSuccess) ADD_SCENE(picopass, saved_menu, SavedMenu) +ADD_SCENE(picopass, file_select, FileSelect) +ADD_SCENE(picopass, device_info, DeviceInfo) +ADD_SCENE(picopass, delete, Delete) +ADD_SCENE(picopass, delete_success, DeleteSuccess) diff --git a/applications/picopass/scenes/picopass_scene_delete.c b/applications/picopass/scenes/picopass_scene_delete.c new file mode 100644 index 000000000..fb23cb5d4 --- /dev/null +++ b/applications/picopass/scenes/picopass_scene_delete.c @@ -0,0 +1,58 @@ +#include "../picopass_i.h" + +void picopass_scene_delete_widget_callback(GuiButtonType result, InputType type, void* context) { + Picopass* picopass = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(picopass->view_dispatcher, result); + } +} + +void picopass_scene_delete_on_enter(void* context) { + Picopass* picopass = context; + + // Setup Custom Widget view + char temp_str[64]; + snprintf(temp_str, sizeof(temp_str), "\e#Delete %s?\e#", picopass->dev->dev_name); + widget_add_text_box_element( + picopass->widget, 0, 0, 128, 23, AlignCenter, AlignCenter, temp_str, false); + widget_add_button_element( + picopass->widget, + GuiButtonTypeLeft, + "Back", + picopass_scene_delete_widget_callback, + picopass); + widget_add_button_element( + picopass->widget, + GuiButtonTypeRight, + "Delete", + picopass_scene_delete_widget_callback, + picopass); + + view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewWidget); +} + +bool picopass_scene_delete_on_event(void* context, SceneManagerEvent event) { + Picopass* picopass = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeLeft) { + return scene_manager_previous_scene(picopass->scene_manager); + } else if(event.event == GuiButtonTypeRight) { + if(picopass_device_delete(picopass->dev, true)) { + scene_manager_next_scene(picopass->scene_manager, PicopassSceneDeleteSuccess); + } else { + scene_manager_search_and_switch_to_previous_scene( + picopass->scene_manager, PicopassSceneStart); + } + consumed = true; + } + } + return consumed; +} + +void picopass_scene_delete_on_exit(void* context) { + Picopass* picopass = context; + + widget_reset(picopass->widget); +} diff --git a/applications/picopass/scenes/picopass_scene_delete_success.c b/applications/picopass/scenes/picopass_scene_delete_success.c new file mode 100755 index 000000000..f2a36a7fb --- /dev/null +++ b/applications/picopass/scenes/picopass_scene_delete_success.c @@ -0,0 +1,40 @@ +#include "../picopass_i.h" + +void picopass_scene_delete_success_popup_callback(void* context) { + Picopass* picopass = context; + view_dispatcher_send_custom_event(picopass->view_dispatcher, PicopassCustomEventViewExit); +} + +void picopass_scene_delete_success_on_enter(void* context) { + Picopass* picopass = context; + + // Setup view + Popup* popup = picopass->popup; + popup_set_icon(popup, 0, 2, &I_DolphinMafia_115x62); + popup_set_header(popup, "Deleted", 83, 19, AlignLeft, AlignBottom); + popup_set_timeout(popup, 1500); + popup_set_context(popup, picopass); + popup_set_callback(popup, picopass_scene_delete_success_popup_callback); + popup_enable_timeout(popup); + view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewPopup); +} + +bool picopass_scene_delete_success_on_event(void* context, SceneManagerEvent event) { + Picopass* picopass = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == PicopassCustomEventViewExit) { + consumed = scene_manager_search_and_switch_to_previous_scene( + picopass->scene_manager, PicopassSceneStart); + } + } + return consumed; +} + +void picopass_scene_delete_success_on_exit(void* context) { + Picopass* picopass = context; + + // Clear view + popup_reset(picopass->popup); +} diff --git a/applications/picopass/scenes/picopass_scene_device_info.c b/applications/picopass/scenes/picopass_scene_device_info.c new file mode 100644 index 000000000..38891b673 --- /dev/null +++ b/applications/picopass/scenes/picopass_scene_device_info.c @@ -0,0 +1,82 @@ +#include "../picopass_i.h" +#include + +void picopass_scene_device_info_widget_callback( + GuiButtonType result, + InputType type, + void* context) { + Picopass* picopass = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(picopass->view_dispatcher, result); + } +} + +void picopass_scene_device_info_on_enter(void* context) { + Picopass* picopass = context; + + string_t credential_str; + string_t wiegand_str; + string_init(credential_str); + string_init(wiegand_str); + + DOLPHIN_DEED(DolphinDeedNfcReadSuccess); + + // Setup view + PicopassPacs* pacs = &picopass->dev->dev_data.pacs; + Widget* widget = picopass->widget; + + size_t bytesLength = 1 + pacs->record.bitLength / 8; + string_set_str(credential_str, ""); + for(uint8_t i = PICOPASS_BLOCK_LEN - bytesLength; i < PICOPASS_BLOCK_LEN; i++) { + string_cat_printf(credential_str, " %02X", pacs->credential[i]); + } + + if(pacs->record.valid) { + string_cat_printf( + wiegand_str, "FC: %u CN: %u", pacs->record.FacilityCode, pacs->record.CardNumber); + } else { + string_cat_printf(wiegand_str, "%d bits", pacs->record.bitLength); + } + + widget_add_string_element( + widget, 64, 12, AlignCenter, AlignCenter, FontPrimary, string_get_cstr(wiegand_str)); + widget_add_string_element( + widget, 64, 32, AlignCenter, AlignCenter, FontSecondary, string_get_cstr(credential_str)); + + string_clear(credential_str); + string_clear(wiegand_str); + + widget_add_button_element( + picopass->widget, + GuiButtonTypeLeft, + "Back", + picopass_scene_device_info_widget_callback, + picopass); + + view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewWidget); +} + +bool picopass_scene_device_info_on_event(void* context, SceneManagerEvent event) { + Picopass* picopass = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeLeft) { + consumed = scene_manager_previous_scene(picopass->scene_manager); + } else if(event.event == PicopassCustomEventViewExit) { + view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewWidget); + consumed = true; + } + } else if(event.type == SceneManagerEventTypeBack) { + view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewWidget); + consumed = true; + } + return consumed; +} + +void picopass_scene_device_info_on_exit(void* context) { + Picopass* picopass = context; + + // Clear views + widget_reset(picopass->widget); +} diff --git a/applications/picopass/scenes/picopass_scene_file_select.c b/applications/picopass/scenes/picopass_scene_file_select.c new file mode 100644 index 000000000..b3d4c3d73 --- /dev/null +++ b/applications/picopass/scenes/picopass_scene_file_select.c @@ -0,0 +1,25 @@ +#include "../picopass_i.h" +#include "picopass/picopass_device.h" + +void picopass_scene_file_select_on_enter(void* context) { + Picopass* picopass = context; + // Process file_select return + picopass_device_set_loading_callback(picopass->dev, picopass_show_loading_popup, picopass); + if(picopass_file_select(picopass->dev)) { + scene_manager_next_scene(picopass->scene_manager, PicopassSceneSavedMenu); + } else { + scene_manager_search_and_switch_to_previous_scene( + picopass->scene_manager, PicopassSceneStart); + } + picopass_device_set_loading_callback(picopass->dev, NULL, picopass); +} + +bool picopass_scene_file_select_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void picopass_scene_file_select_on_exit(void* context) { + UNUSED(context); +} diff --git a/applications/picopass/scenes/picopass_scene_saved_menu.c b/applications/picopass/scenes/picopass_scene_saved_menu.c index 232cf26a9..8f0ce40ba 100644 --- a/applications/picopass/scenes/picopass_scene_saved_menu.c +++ b/applications/picopass/scenes/picopass_scene_saved_menu.c @@ -1,5 +1,11 @@ #include "../picopass_i.h" +enum SubmenuIndex { + SubmenuIndexDelete, + SubmenuIndexInfo, + SubmenuIndexWrite, +}; + void picopass_scene_saved_menu_submenu_callback(void* context, uint32_t index) { Picopass* picopass = context; @@ -8,6 +14,16 @@ void picopass_scene_saved_menu_submenu_callback(void* context, uint32_t index) { void picopass_scene_saved_menu_on_enter(void* context) { Picopass* picopass = context; + Submenu* submenu = picopass->submenu; + + submenu_add_item( + submenu, + "Delete", + SubmenuIndexDelete, + picopass_scene_saved_menu_submenu_callback, + picopass); + submenu_add_item( + submenu, "Info", SubmenuIndexInfo, picopass_scene_saved_menu_submenu_callback, picopass); submenu_set_selected_item( picopass->submenu, @@ -23,6 +39,14 @@ bool picopass_scene_saved_menu_on_event(void* context, SceneManagerEvent event) if(event.type == SceneManagerEventTypeCustom) { scene_manager_set_scene_state( picopass->scene_manager, PicopassSceneSavedMenu, event.event); + + if(event.event == SubmenuIndexDelete) { + scene_manager_next_scene(picopass->scene_manager, PicopassSceneDelete); + consumed = true; + } else if(event.event == SubmenuIndexInfo) { + scene_manager_next_scene(picopass->scene_manager, PicopassSceneDeviceInfo); + consumed = true; + } } return consumed; diff --git a/applications/picopass/scenes/picopass_scene_start.c b/applications/picopass/scenes/picopass_scene_start.c index 7f42fb133..76c18a22a 100644 --- a/applications/picopass/scenes/picopass_scene_start.c +++ b/applications/picopass/scenes/picopass_scene_start.c @@ -17,6 +17,8 @@ void picopass_scene_start_on_enter(void* context) { Submenu* submenu = picopass->submenu; submenu_add_item( submenu, "Read Card", SubmenuIndexRead, picopass_scene_start_submenu_callback, picopass); + submenu_add_item( + submenu, "Saved", SubmenuIndexSaved, picopass_scene_start_submenu_callback, picopass); submenu_set_selected_item( submenu, scene_manager_get_scene_state(picopass->scene_manager, PicopassSceneStart)); @@ -32,6 +34,9 @@ bool picopass_scene_start_on_event(void* context, SceneManagerEvent event) { if(event.event == SubmenuIndexRead) { scene_manager_next_scene(picopass->scene_manager, PicopassSceneReadCard); consumed = true; + } else if(event.event == SubmenuIndexSaved) { + scene_manager_next_scene(picopass->scene_manager, PicopassSceneFileSelect); + consumed = true; } scene_manager_set_scene_state(picopass->scene_manager, PicopassSceneStart, event.event); } diff --git a/applications/power/power_service/power.c b/applications/power/power_service/power.c index 9036ae1ce..3a2c6cf3b 100644 --- a/applications/power/power_service/power.c +++ b/applications/power/power_service/power.c @@ -55,13 +55,14 @@ Power* power_alloc() { // Gui power->view_dispatcher = view_dispatcher_alloc(); - power->popup = popup_alloc(); - popup_set_header( - power->popup, "Disconnect USB for safe\nshutdown", 64, 26, AlignCenter, AlignTop); - view_dispatcher_add_view(power->view_dispatcher, PowerViewPopup, popup_get_view(power->popup)); power->power_off = power_off_alloc(); view_dispatcher_add_view( power->view_dispatcher, PowerViewOff, power_off_get_view(power->power_off)); + power->power_unplug_usb = power_unplug_usb_alloc(); + view_dispatcher_add_view( + power->view_dispatcher, + PowerViewUnplugUsb, + power_unplug_usb_get_view(power->power_unplug_usb)); view_dispatcher_attach_to_gui( power->view_dispatcher, power->gui, ViewDispatcherTypeFullscreen); @@ -78,8 +79,9 @@ void power_free(Power* power) { // Gui view_dispatcher_remove_view(power->view_dispatcher, PowerViewOff); power_off_free(power->power_off); - view_dispatcher_remove_view(power->view_dispatcher, PowerViewPopup); - popup_free(power->popup); + view_dispatcher_remove_view(power->view_dispatcher, PowerViewUnplugUsb); + power_unplug_usb_free(power->power_unplug_usb); + view_port_free(power->battery_view_port); // State diff --git a/applications/power/power_service/power_api.c b/applications/power/power_service/power_api.c index d26fb3b4f..8185b7cd5 100644 --- a/applications/power/power_service/power_api.c +++ b/applications/power/power_service/power_api.c @@ -8,8 +8,8 @@ void power_off(Power* power) { furi_hal_power_off(); // Notify user if USB is plugged view_dispatcher_send_to_front(power->view_dispatcher); - view_dispatcher_switch_to_view(power->view_dispatcher, PowerViewPopup); - furi_delay_ms(10); + view_dispatcher_switch_to_view(power->view_dispatcher, PowerViewUnplugUsb); + furi_delay_ms(100); furi_halt("Disconnect USB for safe shutdown"); } diff --git a/applications/power/power_service/power_i.h b/applications/power/power_service/power_i.h index c7181d0a1..66ced885b 100755 --- a/applications/power/power_service/power_i.h +++ b/applications/power/power_service/power_i.h @@ -8,6 +8,7 @@ #include #include "views/power_off.h" +#include "views/power_unplug_usb.h" #include @@ -21,8 +22,8 @@ typedef enum { struct Power { ViewDispatcher* view_dispatcher; - Popup* popup; PowerOff* power_off; + PowerUnplugUsb* power_unplug_usb; ViewPort* battery_view_port; Gui* gui; @@ -42,6 +43,6 @@ struct Power { }; typedef enum { - PowerViewPopup, PowerViewOff, + PowerViewUnplugUsb, } PowerView; diff --git a/applications/power/power_service/views/power_unplug_usb.c b/applications/power/power_service/views/power_unplug_usb.c new file mode 100644 index 000000000..5632cd8b0 --- /dev/null +++ b/applications/power/power_service/views/power_unplug_usb.c @@ -0,0 +1,43 @@ +#include "power_unplug_usb.h" +#include +#include + +struct PowerUnplugUsb { + View* view; +}; + +static void power_unplug_usb_draw_callback(Canvas* canvas, void* _model) { + UNUSED(_model); + + canvas_set_color(canvas, ColorBlack); + canvas_draw_icon(canvas, 0, 0, &I_Unplug_bg_top_128x14); + canvas_draw_box(canvas, 0, 14, 128, (64 - 10 - 14)); + canvas_draw_icon(canvas, 0, (64 - 10), &I_Unplug_bg_bottom_128x10); + + canvas_set_color(canvas, ColorWhite); + canvas_set_font(canvas, FontPrimary); + elements_multiline_text_aligned( + canvas, 64, 32, AlignCenter, AlignCenter, "It's now safe to unplug\nthe USB cable"); +} + +PowerUnplugUsb* power_unplug_usb_alloc() { + PowerUnplugUsb* power_unplug_usb = malloc(sizeof(PowerUnplugUsb)); + + power_unplug_usb->view = view_alloc(); + view_set_context(power_unplug_usb->view, power_unplug_usb); + view_set_draw_callback(power_unplug_usb->view, power_unplug_usb_draw_callback); + view_set_input_callback(power_unplug_usb->view, NULL); + + return power_unplug_usb; +} + +void power_unplug_usb_free(PowerUnplugUsb* power_unplug_usb) { + furi_assert(power_unplug_usb); + view_free(power_unplug_usb->view); + free(power_unplug_usb); +} + +View* power_unplug_usb_get_view(PowerUnplugUsb* power_unplug_usb) { + furi_assert(power_unplug_usb); + return power_unplug_usb->view; +} diff --git a/applications/power/power_service/views/power_unplug_usb.h b/applications/power/power_service/views/power_unplug_usb.h new file mode 100644 index 000000000..e85c6d03b --- /dev/null +++ b/applications/power/power_service/views/power_unplug_usb.h @@ -0,0 +1,11 @@ +#pragma once + +typedef struct PowerUnplugUsb PowerUnplugUsb; + +#include + +PowerUnplugUsb* power_unplug_usb_alloc(); + +void power_unplug_usb_free(PowerUnplugUsb* power_unplug_usb); + +View* power_unplug_usb_get_view(PowerUnplugUsb* power_unplug_usb); diff --git a/applications/rpc/rpc.c b/applications/rpc/rpc.c index f241a6786..d767a928d 100644 --- a/applications/rpc/rpc.c +++ b/applications/rpc/rpc.c @@ -80,13 +80,11 @@ struct Rpc { FuriMutex* busy_mutex; }; -static bool content_callback(pb_istream_t* stream, const pb_field_t* field, void** arg); - static void rpc_close_session_process(const PB_Main* request, void* context) { furi_assert(request); + furi_assert(context); RpcSession* session = (RpcSession*)context; - furi_assert(session); rpc_send_and_release_empty(session, request->command_id, PB_CommandStatus_OK); furi_mutex_acquire(session->callbacks_mutex, FuriWaitForever); @@ -98,264 +96,6 @@ static void rpc_close_session_process(const PB_Main* request, void* context) { furi_mutex_release(session->callbacks_mutex); } -static size_t rpc_sprintf_msg_file( - string_t str, - const char* prefix, - const PB_Storage_File* msg_file, - size_t msg_files_size) { - size_t cnt = 0; - - for(size_t i = 0; i < msg_files_size; ++i, ++msg_file) { - string_cat_printf( - str, - "%s[%c] size: %5ld", - prefix, - msg_file->type == PB_Storage_File_FileType_DIR ? 'd' : 'f', - msg_file->size); - - if(msg_file->name) { - string_cat_printf(str, " \'%s\'", msg_file->name); - } - - if(msg_file->data && msg_file->data->size) { - string_cat_printf( - str, - " (%d):\'%.*s%s\'", - msg_file->data->size, - MIN(msg_file->data->size, 30), - msg_file->data->bytes, - msg_file->data->size > 30 ? "..." : ""); - } - - string_cat_printf(str, "\r\n"); - } - - return cnt; -} - -void rpc_print_data(const char* prefix, uint8_t* buffer, size_t size) { - string_t str; - string_init(str); - string_reserve(str, 100 + size * 5); - - string_cat_printf(str, "\r\n%s DEC(%d): {", prefix, size); - for(size_t i = 0; i < size; ++i) { - string_cat_printf(str, "%d, ", buffer[i]); - } - string_cat_printf(str, "}\r\n"); - - printf("%s", string_get_cstr(str)); - string_reset(str); - string_reserve(str, 100 + size * 3); - - string_cat_printf(str, "%s HEX(%d): {", prefix, size); - for(size_t i = 0; i < size; ++i) { - string_cat_printf(str, "%02X", buffer[i]); - } - string_cat_printf(str, "}\r\n\r\n"); - - printf("%s", string_get_cstr(str)); - string_clear(str); -} - -void rpc_print_message(const PB_Main* message) { - string_t str; - string_init(str); - - string_cat_printf( - str, - "PB_Main: {\r\n\tresult: %d cmd_id: %ld (%s)\r\n", - message->command_status, - message->command_id, - message->has_next ? "has_next" : "last"); - switch(message->which_content) { - default: - /* not implemented yet */ - string_cat_printf(str, "\tNOT_IMPLEMENTED (%d) {\r\n", message->which_content); - break; - case PB_Main_stop_session_tag: - string_cat_printf(str, "\tstop_session {\r\n"); - break; - case PB_Main_app_start_request_tag: { - string_cat_printf(str, "\tapp_start {\r\n"); - const char* name = message->content.app_start_request.name; - const char* args = message->content.app_start_request.args; - if(name) { - string_cat_printf(str, "\t\tname: %s\r\n", name); - } - if(args) { - string_cat_printf(str, "\t\targs: %s\r\n", args); - } - break; - } - case PB_Main_app_lock_status_request_tag: { - string_cat_printf(str, "\tapp_lock_status_request {\r\n"); - break; - } - case PB_Main_app_lock_status_response_tag: { - string_cat_printf(str, "\tapp_lock_status_response {\r\n"); - bool lock_status = message->content.app_lock_status_response.locked; - string_cat_printf(str, "\t\tlocked: %s\r\n", lock_status ? "true" : "false"); - break; - } - case PB_Main_storage_md5sum_request_tag: { - string_cat_printf(str, "\tmd5sum_request {\r\n"); - const char* path = message->content.storage_md5sum_request.path; - if(path) { - string_cat_printf(str, "\t\tpath: %s\r\n", path); - } - break; - } - case PB_Main_storage_md5sum_response_tag: { - string_cat_printf(str, "\tmd5sum_response {\r\n"); - const char* path = message->content.storage_md5sum_response.md5sum; - if(path) { - string_cat_printf(str, "\t\tmd5sum: %s\r\n", path); - } - break; - } - case PB_Main_system_ping_request_tag: - string_cat_printf(str, "\tping_request {\r\n"); - break; - case PB_Main_system_ping_response_tag: - string_cat_printf(str, "\tping_response {\r\n"); - break; - case PB_Main_system_device_info_request_tag: - string_cat_printf(str, "\tdevice_info_request {\r\n"); - break; - case PB_Main_system_device_info_response_tag: - string_cat_printf(str, "\tdevice_info_response {\r\n"); - string_cat_printf( - str, - "\t\t%s: %s\r\n", - message->content.system_device_info_response.key, - message->content.system_device_info_response.value); - break; - case PB_Main_storage_mkdir_request_tag: - string_cat_printf(str, "\tmkdir {\r\n"); - break; - case PB_Main_storage_delete_request_tag: { - string_cat_printf(str, "\tdelete {\r\n"); - const char* path = message->content.storage_delete_request.path; - if(path) { - string_cat_printf(str, "\t\tpath: %s\r\n", path); - } - break; - } - case PB_Main_empty_tag: - string_cat_printf(str, "\tempty {\r\n"); - break; - case PB_Main_storage_info_request_tag: { - string_cat_printf(str, "\tinfo_request {\r\n"); - const char* path = message->content.storage_info_request.path; - if(path) { - string_cat_printf(str, "\t\tpath: %s\r\n", path); - } - break; - } - case PB_Main_storage_info_response_tag: { - string_cat_printf(str, "\tinfo_response {\r\n"); - string_cat_printf( - str, "\t\ttotal_space: %lu\r\n", message->content.storage_info_response.total_space); - string_cat_printf( - str, "\t\tfree_space: %lu\r\n", message->content.storage_info_response.free_space); - break; - } - case PB_Main_storage_stat_request_tag: { - string_cat_printf(str, "\tstat_request {\r\n"); - const char* path = message->content.storage_stat_request.path; - if(path) { - string_cat_printf(str, "\t\tpath: %s\r\n", path); - } - break; - } - case PB_Main_storage_stat_response_tag: { - string_cat_printf(str, "\tstat_response {\r\n"); - if(message->content.storage_stat_response.has_file) { - const PB_Storage_File* msg_file = &message->content.storage_stat_response.file; - rpc_sprintf_msg_file(str, "\t\t\t", msg_file, 1); - } - break; - } - case PB_Main_storage_list_request_tag: { - string_cat_printf(str, "\tlist_request {\r\n"); - const char* path = message->content.storage_list_request.path; - if(path) { - string_cat_printf(str, "\t\tpath: %s\r\n", path); - } - break; - } - case PB_Main_storage_read_request_tag: { - string_cat_printf(str, "\tread_request {\r\n"); - const char* path = message->content.storage_read_request.path; - if(path) { - string_cat_printf(str, "\t\tpath: %s\r\n", path); - } - break; - } - case PB_Main_storage_write_request_tag: { - string_cat_printf(str, "\twrite_request {\r\n"); - const char* path = message->content.storage_write_request.path; - if(path) { - string_cat_printf(str, "\t\tpath: %s\r\n", path); - } - if(message->content.storage_write_request.has_file) { - const PB_Storage_File* msg_file = &message->content.storage_write_request.file; - rpc_sprintf_msg_file(str, "\t\t\t", msg_file, 1); - } - break; - } - case PB_Main_storage_read_response_tag: - string_cat_printf(str, "\tread_response {\r\n"); - if(message->content.storage_read_response.has_file) { - const PB_Storage_File* msg_file = &message->content.storage_read_response.file; - rpc_sprintf_msg_file(str, "\t\t\t", msg_file, 1); - } - break; - case PB_Main_storage_list_response_tag: { - const PB_Storage_File* msg_file = message->content.storage_list_response.file; - size_t msg_file_count = message->content.storage_list_response.file_count; - string_cat_printf(str, "\tlist_response {\r\n"); - rpc_sprintf_msg_file(str, "\t\t", msg_file, msg_file_count); - break; - } - case PB_Main_storage_rename_request_tag: { - string_cat_printf(str, "\trename_request {\r\n"); - string_cat_printf( - str, "\t\told_path: %s\r\n", message->content.storage_rename_request.old_path); - string_cat_printf( - str, "\t\tnew_path: %s\r\n", message->content.storage_rename_request.new_path); - break; - } - case PB_Main_gui_start_screen_stream_request_tag: - string_cat_printf(str, "\tstart_screen_stream {\r\n"); - break; - case PB_Main_gui_stop_screen_stream_request_tag: - string_cat_printf(str, "\tstop_screen_stream {\r\n"); - break; - case PB_Main_gui_screen_frame_tag: - string_cat_printf(str, "\tscreen_frame {\r\n"); - break; - case PB_Main_gui_send_input_event_request_tag: - string_cat_printf(str, "\tsend_input_event {\r\n"); - string_cat_printf( - str, "\t\tkey: %d\r\n", message->content.gui_send_input_event_request.key); - string_cat_printf( - str, "\t\type: %d\r\n", message->content.gui_send_input_event_request.type); - break; - case PB_Main_gui_start_virtual_display_request_tag: - string_cat_printf(str, "\tstart_virtual_display {\r\n"); - break; - case PB_Main_gui_stop_virtual_display_request_tag: - string_cat_printf(str, "\tstop_virtual_display {\r\n"); - break; - } - string_cat_printf(str, "\t}\r\n}\r\n"); - printf("%s", string_get_cstr(str)); - - string_clear(str); -} - void rpc_session_set_context(RpcSession* session, void* context) { furi_assert(session); @@ -409,6 +149,9 @@ void rpc_session_set_terminated_callback( size_t rpc_session_feed(RpcSession* session, uint8_t* encoded_bytes, size_t size, TickType_t timeout) { furi_assert(session); + furi_assert(encoded_bytes); + furi_assert(size > 0); + size_t bytes_sent = xStreamBufferSend(session->stream, encoded_bytes, size, timeout); furi_thread_flags_set(furi_thread_get_id(session->thread), RpcEvtNewData); @@ -422,6 +165,8 @@ size_t rpc_session_get_available_size(RpcSession* session) { } bool rpc_pb_stream_read(pb_istream_t* istream, pb_byte_t* buf, size_t count) { + furi_assert(istream); + furi_assert(buf); RpcSession* session = istream->state; furi_assert(session); furi_assert(istream->bytes_left); @@ -462,16 +207,17 @@ bool rpc_pb_stream_read(pb_istream_t* istream, pb_byte_t* buf, size_t count) { } #if SRV_RPC_DEBUG - rpc_print_data("INPUT", buf, bytes_received); + rpc_debug_print_data("INPUT", buf, bytes_received); #endif return (count == bytes_received); } -static bool content_callback(pb_istream_t* stream, const pb_field_t* field, void** arg) { +static bool rpc_pb_content_callback(pb_istream_t* stream, const pb_field_t* field, void** arg) { furi_assert(stream); RpcSession* session = stream->state; furi_assert(session); + furi_assert(field); RpcHandler* handler = RpcHandlerDict_get(session->handlers, field->tag); @@ -502,7 +248,7 @@ static int32_t rpc_session_worker(void* context) { if(pb_decode_ex(&istream, &PB_Main_msg, session->decoded_message, PB_DECODE_DELIMITED)) { #if SRV_RPC_DEBUG FURI_LOG_I(TAG, "INPUT:"); - rpc_print_message(session->decoded_message); + rpc_debug_print_message(session->decoded_message); #endif RpcHandler* handler = RpcHandlerDict_get(session->handlers, session->decoded_message->which_content); @@ -610,7 +356,7 @@ RpcSession* rpc_session_open(Rpc* rpc) { RpcHandlerDict_init(session->handlers); session->decoded_message = malloc(sizeof(PB_Main)); - session->decoded_message->cb_content.funcs.decode = content_callback; + session->decoded_message->cb_content.funcs.decode = rpc_pb_content_callback; session->decoded_message->cb_content.arg = session; session->system_contexts = malloc(COUNT_OF(rpc_systems) * sizeof(void*)); @@ -678,7 +424,7 @@ void rpc_send(RpcSession* session, PB_Main* message) { #if SRV_RPC_DEBUG FURI_LOG_I(TAG, "OUTPUT:"); - rpc_print_message(message); + rpc_debug_print_message(message); #endif bool result = pb_encode_ex(&ostream, &PB_Main_msg, message, PB_ENCODE_DELIMITED); @@ -690,7 +436,7 @@ void rpc_send(RpcSession* session, PB_Main* message) { pb_encode_ex(&ostream, &PB_Main_msg, message, PB_ENCODE_DELIMITED); #if SRV_RPC_DEBUG - rpc_print_data("OUTPUT", buffer, ostream.bytes_written); + rpc_debug_print_data("OUTPUT", buffer, ostream.bytes_written); #endif furi_mutex_acquire(session->callbacks_mutex, FuriWaitForever); diff --git a/applications/rpc/rpc_app.c b/applications/rpc/rpc_app.c index 555cec8cf..b8b34170e 100644 --- a/applications/rpc/rpc_app.c +++ b/applications/rpc/rpc_app.c @@ -299,6 +299,7 @@ void* rpc_system_app_alloc(RpcSession* session) { void rpc_system_app_free(void* context) { RpcAppSystem* rpc_app = context; + furi_assert(rpc_app); RpcSession* session = rpc_app->session; furi_assert(session); diff --git a/applications/rpc/rpc_cli.c b/applications/rpc/rpc_cli.c index efc672193..8cb0f76ac 100644 --- a/applications/rpc/rpc_cli.c +++ b/applications/rpc/rpc_cli.c @@ -14,23 +14,23 @@ typedef struct { #define CLI_READ_BUFFER_SIZE 64 -static void rpc_send_bytes_callback(void* context, uint8_t* bytes, size_t bytes_len) { +static void rpc_cli_send_bytes_callback(void* context, uint8_t* bytes, size_t bytes_len) { furi_assert(context); furi_assert(bytes); - furi_assert(bytes_len); + furi_assert(bytes_len > 0); CliRpc* cli_rpc = context; cli_write(cli_rpc->cli, bytes, bytes_len); } -static void rpc_session_close_callback(void* context) { +static void rpc_cli_session_close_callback(void* context) { furi_assert(context); CliRpc* cli_rpc = context; cli_rpc->session_close_request = true; } -static void rpc_session_terminated_callback(void* context) { +static void rpc_cli_session_terminated_callback(void* context) { furi_check(context); CliRpc* cli_rpc = context; @@ -39,6 +39,8 @@ static void rpc_session_terminated_callback(void* context) { void rpc_cli_command_start_session(Cli* cli, string_t args, void* context) { UNUSED(args); + furi_assert(cli); + furi_assert(context); Rpc* rpc = context; uint32_t mem_before = memmgr_get_free_heap(); @@ -55,9 +57,9 @@ void rpc_cli_command_start_session(Cli* cli, string_t args, void* context) { CliRpc cli_rpc = {.cli = cli, .session_close_request = false}; cli_rpc.terminate_semaphore = furi_semaphore_alloc(1, 0); rpc_session_set_context(rpc_session, &cli_rpc); - rpc_session_set_send_bytes_callback(rpc_session, rpc_send_bytes_callback); - rpc_session_set_close_callback(rpc_session, rpc_session_close_callback); - rpc_session_set_terminated_callback(rpc_session, rpc_session_terminated_callback); + rpc_session_set_send_bytes_callback(rpc_session, rpc_cli_send_bytes_callback); + rpc_session_set_close_callback(rpc_session, rpc_cli_session_close_callback); + rpc_session_set_terminated_callback(rpc_session, rpc_cli_session_terminated_callback); uint8_t* buffer = malloc(CLI_READ_BUFFER_SIZE); size_t size_received = 0; diff --git a/applications/rpc/rpc_debug.c b/applications/rpc/rpc_debug.c new file mode 100644 index 000000000..9c04bd89a --- /dev/null +++ b/applications/rpc/rpc_debug.c @@ -0,0 +1,260 @@ +#include "rpc_i.h" +#include + +static size_t rpc_debug_print_file_msg( + string_t str, + const char* prefix, + const PB_Storage_File* msg_file, + size_t msg_files_size) { + size_t cnt = 0; + + for(size_t i = 0; i < msg_files_size; ++i, ++msg_file) { + string_cat_printf( + str, + "%s[%c] size: %5ld", + prefix, + msg_file->type == PB_Storage_File_FileType_DIR ? 'd' : 'f', + msg_file->size); + + if(msg_file->name) { + string_cat_printf(str, " \'%s\'", msg_file->name); + } + + if(msg_file->data && msg_file->data->size) { + string_cat_printf( + str, + " (%d):\'%.*s%s\'", + msg_file->data->size, + MIN(msg_file->data->size, 30), + msg_file->data->bytes, + msg_file->data->size > 30 ? "..." : ""); + } + + string_cat_printf(str, "\r\n"); + } + + return cnt; +} + +void rpc_debug_print_data(const char* prefix, uint8_t* buffer, size_t size) { + string_t str; + string_init(str); + string_reserve(str, 100 + size * 5); + + string_cat_printf(str, "\r\n%s DEC(%d): {", prefix, size); + for(size_t i = 0; i < size; ++i) { + string_cat_printf(str, "%d, ", buffer[i]); + } + string_cat_printf(str, "}\r\n"); + + printf("%s", string_get_cstr(str)); + string_reset(str); + string_reserve(str, 100 + size * 3); + + string_cat_printf(str, "%s HEX(%d): {", prefix, size); + for(size_t i = 0; i < size; ++i) { + string_cat_printf(str, "%02X", buffer[i]); + } + string_cat_printf(str, "}\r\n\r\n"); + + printf("%s", string_get_cstr(str)); + string_clear(str); +} + +void rpc_debug_print_message(const PB_Main* message) { + string_t str; + string_init(str); + + string_cat_printf( + str, + "PB_Main: {\r\n\tresult: %d cmd_id: %ld (%s)\r\n", + message->command_status, + message->command_id, + message->has_next ? "has_next" : "last"); + switch(message->which_content) { + default: + /* not implemented yet */ + string_cat_printf(str, "\tNOT_IMPLEMENTED (%d) {\r\n", message->which_content); + break; + case PB_Main_stop_session_tag: + string_cat_printf(str, "\tstop_session {\r\n"); + break; + case PB_Main_app_start_request_tag: { + string_cat_printf(str, "\tapp_start {\r\n"); + const char* name = message->content.app_start_request.name; + const char* args = message->content.app_start_request.args; + if(name) { + string_cat_printf(str, "\t\tname: %s\r\n", name); + } + if(args) { + string_cat_printf(str, "\t\targs: %s\r\n", args); + } + break; + } + case PB_Main_app_lock_status_request_tag: { + string_cat_printf(str, "\tapp_lock_status_request {\r\n"); + break; + } + case PB_Main_app_lock_status_response_tag: { + string_cat_printf(str, "\tapp_lock_status_response {\r\n"); + bool lock_status = message->content.app_lock_status_response.locked; + string_cat_printf(str, "\t\tlocked: %s\r\n", lock_status ? "true" : "false"); + break; + } + case PB_Main_storage_md5sum_request_tag: { + string_cat_printf(str, "\tmd5sum_request {\r\n"); + const char* path = message->content.storage_md5sum_request.path; + if(path) { + string_cat_printf(str, "\t\tpath: %s\r\n", path); + } + break; + } + case PB_Main_storage_md5sum_response_tag: { + string_cat_printf(str, "\tmd5sum_response {\r\n"); + const char* path = message->content.storage_md5sum_response.md5sum; + if(path) { + string_cat_printf(str, "\t\tmd5sum: %s\r\n", path); + } + break; + } + case PB_Main_system_ping_request_tag: + string_cat_printf(str, "\tping_request {\r\n"); + break; + case PB_Main_system_ping_response_tag: + string_cat_printf(str, "\tping_response {\r\n"); + break; + case PB_Main_system_device_info_request_tag: + string_cat_printf(str, "\tdevice_info_request {\r\n"); + break; + case PB_Main_system_device_info_response_tag: + string_cat_printf(str, "\tdevice_info_response {\r\n"); + string_cat_printf( + str, + "\t\t%s: %s\r\n", + message->content.system_device_info_response.key, + message->content.system_device_info_response.value); + break; + case PB_Main_storage_mkdir_request_tag: + string_cat_printf(str, "\tmkdir {\r\n"); + break; + case PB_Main_storage_delete_request_tag: { + string_cat_printf(str, "\tdelete {\r\n"); + const char* path = message->content.storage_delete_request.path; + if(path) { + string_cat_printf(str, "\t\tpath: %s\r\n", path); + } + break; + } + case PB_Main_empty_tag: + string_cat_printf(str, "\tempty {\r\n"); + break; + case PB_Main_storage_info_request_tag: { + string_cat_printf(str, "\tinfo_request {\r\n"); + const char* path = message->content.storage_info_request.path; + if(path) { + string_cat_printf(str, "\t\tpath: %s\r\n", path); + } + break; + } + case PB_Main_storage_info_response_tag: { + string_cat_printf(str, "\tinfo_response {\r\n"); + string_cat_printf( + str, "\t\ttotal_space: %lu\r\n", message->content.storage_info_response.total_space); + string_cat_printf( + str, "\t\tfree_space: %lu\r\n", message->content.storage_info_response.free_space); + break; + } + case PB_Main_storage_stat_request_tag: { + string_cat_printf(str, "\tstat_request {\r\n"); + const char* path = message->content.storage_stat_request.path; + if(path) { + string_cat_printf(str, "\t\tpath: %s\r\n", path); + } + break; + } + case PB_Main_storage_stat_response_tag: { + string_cat_printf(str, "\tstat_response {\r\n"); + if(message->content.storage_stat_response.has_file) { + const PB_Storage_File* msg_file = &message->content.storage_stat_response.file; + rpc_debug_print_file_msg(str, "\t\t\t", msg_file, 1); + } + break; + } + case PB_Main_storage_list_request_tag: { + string_cat_printf(str, "\tlist_request {\r\n"); + const char* path = message->content.storage_list_request.path; + if(path) { + string_cat_printf(str, "\t\tpath: %s\r\n", path); + } + break; + } + case PB_Main_storage_read_request_tag: { + string_cat_printf(str, "\tread_request {\r\n"); + const char* path = message->content.storage_read_request.path; + if(path) { + string_cat_printf(str, "\t\tpath: %s\r\n", path); + } + break; + } + case PB_Main_storage_write_request_tag: { + string_cat_printf(str, "\twrite_request {\r\n"); + const char* path = message->content.storage_write_request.path; + if(path) { + string_cat_printf(str, "\t\tpath: %s\r\n", path); + } + if(message->content.storage_write_request.has_file) { + const PB_Storage_File* msg_file = &message->content.storage_write_request.file; + rpc_debug_print_file_msg(str, "\t\t\t", msg_file, 1); + } + break; + } + case PB_Main_storage_read_response_tag: + string_cat_printf(str, "\tread_response {\r\n"); + if(message->content.storage_read_response.has_file) { + const PB_Storage_File* msg_file = &message->content.storage_read_response.file; + rpc_debug_print_file_msg(str, "\t\t\t", msg_file, 1); + } + break; + case PB_Main_storage_list_response_tag: { + const PB_Storage_File* msg_file = message->content.storage_list_response.file; + size_t msg_file_count = message->content.storage_list_response.file_count; + string_cat_printf(str, "\tlist_response {\r\n"); + rpc_debug_print_file_msg(str, "\t\t", msg_file, msg_file_count); + break; + } + case PB_Main_storage_rename_request_tag: { + string_cat_printf(str, "\trename_request {\r\n"); + string_cat_printf( + str, "\t\told_path: %s\r\n", message->content.storage_rename_request.old_path); + string_cat_printf( + str, "\t\tnew_path: %s\r\n", message->content.storage_rename_request.new_path); + break; + } + case PB_Main_gui_start_screen_stream_request_tag: + string_cat_printf(str, "\tstart_screen_stream {\r\n"); + break; + case PB_Main_gui_stop_screen_stream_request_tag: + string_cat_printf(str, "\tstop_screen_stream {\r\n"); + break; + case PB_Main_gui_screen_frame_tag: + string_cat_printf(str, "\tscreen_frame {\r\n"); + break; + case PB_Main_gui_send_input_event_request_tag: + string_cat_printf(str, "\tsend_input_event {\r\n"); + string_cat_printf( + str, "\t\tkey: %d\r\n", message->content.gui_send_input_event_request.key); + string_cat_printf( + str, "\t\type: %d\r\n", message->content.gui_send_input_event_request.type); + break; + case PB_Main_gui_start_virtual_display_request_tag: + string_cat_printf(str, "\tstart_virtual_display {\r\n"); + break; + case PB_Main_gui_stop_virtual_display_request_tag: + string_cat_printf(str, "\tstop_virtual_display {\r\n"); + break; + } + string_cat_printf(str, "\t}\r\n}\r\n"); + printf("%s", string_get_cstr(str)); + + string_clear(str); +} diff --git a/applications/rpc/rpc_gpio.c b/applications/rpc/rpc_gpio.c index 614e775a1..09e738505 100644 --- a/applications/rpc/rpc_gpio.c +++ b/applications/rpc/rpc_gpio.c @@ -57,7 +57,6 @@ static void rpc_system_gpio_set_pin_mode(const PB_Main* request, void* context) furi_assert(request->which_content == PB_Main_gpio_set_pin_mode_tag); RpcSession* session = context; - furi_assert(session); PB_Gpio_SetPinMode cmd = request->content.gpio_set_pin_mode; const GpioPin* pin = rpc_pin_to_hal_pin(cmd.pin); @@ -77,7 +76,6 @@ static void rpc_system_gpio_write_pin(const PB_Main* request, void* context) { furi_assert(request->which_content == PB_Main_gpio_write_pin_tag); RpcSession* session = context; - furi_assert(session); PB_Gpio_WritePin cmd = request->content.gpio_write_pin; const GpioPin* pin = rpc_pin_to_hal_pin(cmd.pin); @@ -105,7 +103,6 @@ static void rpc_system_gpio_read_pin(const PB_Main* request, void* context) { furi_assert(request->which_content == PB_Main_gpio_read_pin_tag); RpcSession* session = context; - furi_assert(session); PB_Gpio_ReadPin cmd = request->content.gpio_read_pin; const GpioPin* pin = rpc_pin_to_hal_pin(cmd.pin); @@ -133,7 +130,6 @@ void rpc_system_gpio_get_pin_mode(const PB_Main* request, void* context) { furi_assert(request->which_content == PB_Main_gpio_get_pin_mode_tag); RpcSession* session = context; - furi_assert(session); PB_Gpio_GetPinMode cmd = request->content.gpio_get_pin_mode; const GpioPin* pin = rpc_pin_to_hal_pin(cmd.pin); @@ -170,7 +166,6 @@ void rpc_system_gpio_set_input_pull(const PB_Main* request, void* context) { furi_assert(request->which_content == PB_Main_gpio_set_input_pull_tag); RpcSession* session = context; - furi_assert(session); PB_Gpio_SetInputPull cmd = request->content.gpio_set_input_pull; const GpioPin* pin = rpc_pin_to_hal_pin(cmd.pin); diff --git a/applications/rpc/rpc_i.h b/applications/rpc/rpc_i.h index e512ad397..9ffd054a0 100644 --- a/applications/rpc/rpc_i.h +++ b/applications/rpc/rpc_i.h @@ -35,7 +35,9 @@ void rpc_system_gui_free(void* ctx); void* rpc_system_gpio_alloc(RpcSession* session); void rpc_system_gpio_free(void* ctx); -void rpc_print_message(const PB_Main* message); +void rpc_debug_print_message(const PB_Main* message); +void rpc_debug_print_data(const char* prefix, uint8_t* buffer, size_t size); + void rpc_cli_command_start_session(Cli* cli, string_t args, void* context); PB_CommandStatus rpc_system_storage_get_error(FS_Error fs_error); diff --git a/applications/rpc/rpc_storage.c b/applications/rpc/rpc_storage.c index ad6191b2f..1b545b414 100644 --- a/applications/rpc/rpc_storage.c +++ b/applications/rpc/rpc_storage.c @@ -37,6 +37,7 @@ static void rpc_system_storage_reset_state( RpcSession* session, bool send_error) { furi_assert(rpc_storage); + furi_assert(session); if(rpc_storage->state != RpcStorageStateIdle) { if(send_error) { @@ -177,6 +178,8 @@ static void rpc_system_storage_stat_process(const PB_Main* request, void* contex } static void rpc_system_storage_list_root(const PB_Main* request, void* context) { + furi_assert(request); + furi_assert(context); RpcStorageSystem* rpc_storage = context; RpcSession* session = rpc_storage->session; furi_assert(session); @@ -411,6 +414,8 @@ static void rpc_system_storage_write_process(const PB_Main* request, void* conte } static bool rpc_system_storage_is_dir_is_empty(Storage* fs_api, const char* path) { + furi_assert(fs_api); + furi_assert(path); FileInfo fileinfo; bool is_dir_is_empty = true; FS_Error error = storage_common_stat(fs_api, path, &fileinfo); @@ -605,6 +610,7 @@ static void rpc_system_storage_rename_process(const PB_Main* request, void* cont static void rpc_system_storage_backup_create_process(const PB_Main* request, void* context) { furi_assert(request); furi_assert(request->which_content == PB_Main_storage_backup_create_request_tag); + furi_assert(context); FURI_LOG_D(TAG, "BackupCreate"); @@ -626,6 +632,7 @@ static void rpc_system_storage_backup_create_process(const PB_Main* request, voi static void rpc_system_storage_backup_restore_process(const PB_Main* request, void* context) { furi_assert(request); furi_assert(request->which_content == PB_Main_storage_backup_restore_request_tag); + furi_assert(context); FURI_LOG_D(TAG, "BackupRestore"); @@ -695,6 +702,7 @@ void* rpc_system_storage_alloc(RpcSession* session) { } void rpc_system_storage_free(void* context) { + furi_assert(context); RpcStorageSystem* rpc_storage = context; RpcSession* session = rpc_storage->session; furi_assert(session); diff --git a/applications/rpc/rpc_system.c b/applications/rpc/rpc_system.c index 0538aa64d..1681bb17e 100644 --- a/applications/rpc/rpc_system.c +++ b/applications/rpc/rpc_system.c @@ -77,6 +77,7 @@ static void rpc_system_system_device_info_callback( furi_assert(key); furi_assert(value); RpcSystemContext* ctx = context; + furi_assert(ctx); furi_assert(key); furi_assert(value); @@ -233,6 +234,7 @@ static void rpc_system_system_power_info_callback( furi_assert(key); furi_assert(value); RpcSystemContext* ctx = context; + furi_assert(ctx); furi_assert(key); furi_assert(value); @@ -297,6 +299,8 @@ static void rpc_system_system_update_request_process(const PB_Main* request, voi #endif void* rpc_system_system_alloc(RpcSession* session) { + furi_assert(session); + RpcHandler rpc_handler = { .message_handler = NULL, .decode_submessage = NULL, diff --git a/applications/storage/storage.h b/applications/storage/storage.h index 55a951d12..1a7c93495 100644 --- a/applications/storage/storage.h +++ b/applications/storage/storage.h @@ -136,6 +136,15 @@ bool storage_file_sync(File* file); */ bool storage_file_eof(File* file); +/** + * @brief Check that file exists + * + * @param storage + * @param path + * @return true if file exists + */ +bool storage_file_exists(Storage* storage, const char* path); + /******************* Dir Functions *******************/ /** Opens a directory to get objects from it diff --git a/applications/storage/storage_external_api.c b/applications/storage/storage_external_api.c index b32080dfc..80cafb282 100644 --- a/applications/storage/storage_external_api.c +++ b/applications/storage/storage_external_api.c @@ -240,6 +240,18 @@ bool storage_file_eof(File* file) { return S_RETURN_BOOL; } +bool storage_file_exists(Storage* storage, const char* path) { + bool exist = false; + FileInfo fileinfo; + FS_Error error = storage_common_stat(storage, path, &fileinfo); + + if(error == FSE_OK && !(fileinfo.flags & FSF_DIRECTORY)) { + exist = true; + } + + return exist; +} + /****************** DIR ******************/ static bool storage_dir_open_internal(File* file, const char* path) { diff --git a/applications/subghz/subghz_setting.c b/applications/subghz/subghz_setting.c index 23d17ddf5..daa3c018b 100644 --- a/applications/subghz/subghz_setting.c +++ b/applications/subghz/subghz_setting.c @@ -319,15 +319,15 @@ const char* subghz_setting_get_preset_name(SubGhzSetting* instance, size_t idx) int subghz_setting_get_inx_preset_by_name(SubGhzSetting* instance, const char* preset_name) { furi_assert(instance); size_t idx = 0; - for - M_EACH(item, instance->preset->data, SubGhzSettingCustomPresetItemArray_t) { - if(strcmp(string_get_cstr(item->custom_preset_name), preset_name) == 0) { - return idx; - } - idx++; - } - furi_crash("SubGhz: No name preset."); - return -1; + for + M_EACH(item, instance->preset->data, SubGhzSettingCustomPresetItemArray_t) { + if(strcmp(string_get_cstr(item->custom_preset_name), preset_name) == 0) { + return idx; + } + idx++; + } + furi_crash("SubGhz: No name preset."); + return -1; } bool subghz_setting_load_custom_preset( diff --git a/applications/unirfremix/application.fam b/applications/unirfremix/application.fam index d8eaaab89..fd3553947 100644 --- a/applications/unirfremix/application.fam +++ b/applications/unirfremix/application.fam @@ -1,6 +1,6 @@ App( appid="unirfremix", - name="UniRF Remix", + name="Sub-GHz Remote", apptype=FlipperAppType.APP, entry_point="unirfremix_app", cdefines=["APP_UNIRFREMIX"], @@ -9,6 +9,6 @@ App( "dialogs", ], icon="A_UniRFRemix_14", - stack_size=2 * 1024, + stack_size=4 * 1024, order=11, ) diff --git a/applications/unit_tests/lfrfid/bit_lib_test.c b/applications/unit_tests/lfrfid/bit_lib_test.c new file mode 100644 index 000000000..726615703 --- /dev/null +++ b/applications/unit_tests/lfrfid/bit_lib_test.c @@ -0,0 +1,473 @@ +#include +#include "../minunit.h" +#include + +MU_TEST(test_bit_lib_increment_index) { + uint32_t index = 0; + + // test increment + for(uint32_t i = 0; i < 31; ++i) { + bit_lib_increment_index(index, 32); + mu_assert_int_eq(i + 1, index); + } + + // test wrap around + for(uint32_t i = 0; i < 512; ++i) { + bit_lib_increment_index(index, 32); + mu_assert_int_less_than(32, index); + } +} + +MU_TEST(test_bit_lib_is_set) { + uint32_t value = 0x0000FFFF; + + for(uint32_t i = 0; i < 16; ++i) { + mu_check(bit_lib_bit_is_set(value, i)); + mu_check(!bit_lib_bit_is_not_set(value, i)); + } + + for(uint32_t i = 16; i < 32; ++i) { + mu_check(!bit_lib_bit_is_set(value, i)); + mu_check(bit_lib_bit_is_not_set(value, i)); + } +} + +MU_TEST(test_bit_lib_push) { +#define TEST_BIT_LIB_PUSH_DATA_SIZE 4 + uint8_t data[TEST_BIT_LIB_PUSH_DATA_SIZE] = {0}; + uint8_t expected_data_1[TEST_BIT_LIB_PUSH_DATA_SIZE] = {0x00, 0x00, 0x0F, 0xFF}; + uint8_t expected_data_2[TEST_BIT_LIB_PUSH_DATA_SIZE] = {0x00, 0xFF, 0xF0, 0x00}; + uint8_t expected_data_3[TEST_BIT_LIB_PUSH_DATA_SIZE] = {0xFF, 0x00, 0x00, 0xFF}; + uint8_t expected_data_4[TEST_BIT_LIB_PUSH_DATA_SIZE] = {0xFF, 0xFF, 0xFF, 0xFF}; + uint8_t expected_data_5[TEST_BIT_LIB_PUSH_DATA_SIZE] = {0x00, 0x00, 0x00, 0x00}; + uint8_t expected_data_6[TEST_BIT_LIB_PUSH_DATA_SIZE] = {0xCC, 0xCC, 0xCC, 0xCC}; + + for(uint32_t i = 0; i < 12; ++i) { + bit_lib_push_bit(data, TEST_BIT_LIB_PUSH_DATA_SIZE, true); + } + mu_assert_mem_eq(expected_data_1, data, TEST_BIT_LIB_PUSH_DATA_SIZE); + + for(uint32_t i = 0; i < 12; ++i) { + bit_lib_push_bit(data, TEST_BIT_LIB_PUSH_DATA_SIZE, false); + } + mu_assert_mem_eq(expected_data_2, data, TEST_BIT_LIB_PUSH_DATA_SIZE); + + for(uint32_t i = 0; i < 4; ++i) { + bit_lib_push_bit(data, TEST_BIT_LIB_PUSH_DATA_SIZE, false); + } + for(uint32_t i = 0; i < 8; ++i) { + bit_lib_push_bit(data, TEST_BIT_LIB_PUSH_DATA_SIZE, true); + } + mu_assert_mem_eq(expected_data_3, data, TEST_BIT_LIB_PUSH_DATA_SIZE); + + for(uint32_t i = 0; i < TEST_BIT_LIB_PUSH_DATA_SIZE * 8; ++i) { + bit_lib_push_bit(data, TEST_BIT_LIB_PUSH_DATA_SIZE, true); + } + mu_assert_mem_eq(expected_data_4, data, TEST_BIT_LIB_PUSH_DATA_SIZE); + + for(uint32_t i = 0; i < TEST_BIT_LIB_PUSH_DATA_SIZE * 8; ++i) { + bit_lib_push_bit(data, TEST_BIT_LIB_PUSH_DATA_SIZE, false); + } + mu_assert_mem_eq(expected_data_5, data, TEST_BIT_LIB_PUSH_DATA_SIZE); + + for(uint32_t i = 0; i < TEST_BIT_LIB_PUSH_DATA_SIZE * 2; ++i) { + bit_lib_push_bit(data, TEST_BIT_LIB_PUSH_DATA_SIZE, true); + bit_lib_push_bit(data, TEST_BIT_LIB_PUSH_DATA_SIZE, true); + bit_lib_push_bit(data, TEST_BIT_LIB_PUSH_DATA_SIZE, false); + bit_lib_push_bit(data, TEST_BIT_LIB_PUSH_DATA_SIZE, false); + } + mu_assert_mem_eq(expected_data_6, data, TEST_BIT_LIB_PUSH_DATA_SIZE); +} + +MU_TEST(test_bit_lib_set_bit) { + uint8_t value[2] = {0x00, 0xFF}; + bit_lib_set_bit(value, 15, false); + mu_assert_mem_eq(value, ((uint8_t[]){0x00, 0xFE}), 2); + bit_lib_set_bit(value, 14, false); + mu_assert_mem_eq(value, ((uint8_t[]){0x00, 0xFC}), 2); + bit_lib_set_bit(value, 13, false); + mu_assert_mem_eq(value, ((uint8_t[]){0x00, 0xF8}), 2); + bit_lib_set_bit(value, 12, false); + mu_assert_mem_eq(value, ((uint8_t[]){0x00, 0xF0}), 2); + bit_lib_set_bit(value, 11, false); + mu_assert_mem_eq(value, ((uint8_t[]){0x00, 0xE0}), 2); + bit_lib_set_bit(value, 10, false); + mu_assert_mem_eq(value, ((uint8_t[]){0x00, 0xC0}), 2); + bit_lib_set_bit(value, 9, false); + mu_assert_mem_eq(value, ((uint8_t[]){0x00, 0x80}), 2); + bit_lib_set_bit(value, 8, false); + mu_assert_mem_eq(value, ((uint8_t[]){0x00, 0x00}), 2); + + bit_lib_set_bit(value, 7, true); + mu_assert_mem_eq(value, ((uint8_t[]){0x01, 0x00}), 2); + bit_lib_set_bit(value, 6, true); + mu_assert_mem_eq(value, ((uint8_t[]){0x03, 0x00}), 2); + bit_lib_set_bit(value, 5, true); + mu_assert_mem_eq(value, ((uint8_t[]){0x07, 0x00}), 2); + bit_lib_set_bit(value, 4, true); + mu_assert_mem_eq(value, ((uint8_t[]){0x0F, 0x00}), 2); + bit_lib_set_bit(value, 3, true); + mu_assert_mem_eq(value, ((uint8_t[]){0x1F, 0x00}), 2); + bit_lib_set_bit(value, 2, true); + mu_assert_mem_eq(value, ((uint8_t[]){0x3F, 0x00}), 2); + bit_lib_set_bit(value, 1, true); + mu_assert_mem_eq(value, ((uint8_t[]){0x7F, 0x00}), 2); + bit_lib_set_bit(value, 0, true); + mu_assert_mem_eq(value, ((uint8_t[]){0xFF, 0x00}), 2); +} + +MU_TEST(test_bit_lib_set_bits) { + uint8_t value[2] = {0b00000000, 0b11111111}; + // set 4 bits to 0b0100 from 12 index + bit_lib_set_bits(value, 12, 0b0100, 4); + // [0100] + mu_assert_mem_eq(value, ((uint8_t[]){0b00000000, 0b11110100}), 2); + + // set 2 bits to 0b11 from 11 index + bit_lib_set_bits(value, 11, 0b11, 2); + // [11] + mu_assert_mem_eq(value, ((uint8_t[]){0b00000000, 0b11111100}), 2); + + // set 3 bits to 0b111 from 0 index + bit_lib_set_bits(value, 0, 0b111, 3); + // [111] + mu_assert_mem_eq(value, ((uint8_t[]){0b11100000, 0b11111100}), 2); + + // set 8 bits to 0b11111000 from 3 index + bit_lib_set_bits(value, 3, 0b11111000, 8); + // [11111 000] + mu_assert_mem_eq(value, ((uint8_t[]){0b11111111, 0b00011100}), 2); +} + +MU_TEST(test_bit_lib_get_bit) { + uint8_t value[2] = {0b00000000, 0b11111111}; + for(uint32_t i = 0; i < 8; ++i) { + mu_check(bit_lib_get_bit(value, i) == false); + } + for(uint32_t i = 8; i < 16; ++i) { + mu_check(bit_lib_get_bit(value, i) == true); + } +} + +MU_TEST(test_bit_lib_get_bits) { + uint8_t value[2] = {0b00000000, 0b11111111}; + mu_assert_int_eq(0b00000000, bit_lib_get_bits(value, 0, 8)); + mu_assert_int_eq(0b00000001, bit_lib_get_bits(value, 1, 8)); + mu_assert_int_eq(0b00000011, bit_lib_get_bits(value, 2, 8)); + mu_assert_int_eq(0b00000111, bit_lib_get_bits(value, 3, 8)); + mu_assert_int_eq(0b00001111, bit_lib_get_bits(value, 4, 8)); + mu_assert_int_eq(0b00011111, bit_lib_get_bits(value, 5, 8)); + mu_assert_int_eq(0b00111111, bit_lib_get_bits(value, 6, 8)); + mu_assert_int_eq(0b01111111, bit_lib_get_bits(value, 7, 8)); + mu_assert_int_eq(0b11111111, bit_lib_get_bits(value, 8, 8)); +} + +MU_TEST(test_bit_lib_get_bits_16) { + uint8_t value[2] = {0b00001001, 0b10110001}; + mu_assert_int_eq(0b0, bit_lib_get_bits_16(value, 0, 1)); + mu_assert_int_eq(0b00, bit_lib_get_bits_16(value, 0, 2)); + mu_assert_int_eq(0b000, bit_lib_get_bits_16(value, 0, 3)); + mu_assert_int_eq(0b0000, bit_lib_get_bits_16(value, 0, 4)); + mu_assert_int_eq(0b00001, bit_lib_get_bits_16(value, 0, 5)); + mu_assert_int_eq(0b000010, bit_lib_get_bits_16(value, 0, 6)); + mu_assert_int_eq(0b0000100, bit_lib_get_bits_16(value, 0, 7)); + mu_assert_int_eq(0b00001001, bit_lib_get_bits_16(value, 0, 8)); + mu_assert_int_eq(0b000010011, bit_lib_get_bits_16(value, 0, 9)); + mu_assert_int_eq(0b0000100110, bit_lib_get_bits_16(value, 0, 10)); + mu_assert_int_eq(0b00001001101, bit_lib_get_bits_16(value, 0, 11)); + mu_assert_int_eq(0b000010011011, bit_lib_get_bits_16(value, 0, 12)); + mu_assert_int_eq(0b0000100110110, bit_lib_get_bits_16(value, 0, 13)); + mu_assert_int_eq(0b00001001101100, bit_lib_get_bits_16(value, 0, 14)); + mu_assert_int_eq(0b000010011011000, bit_lib_get_bits_16(value, 0, 15)); + mu_assert_int_eq(0b0000100110110001, bit_lib_get_bits_16(value, 0, 16)); +} + +MU_TEST(test_bit_lib_get_bits_32) { + uint8_t value[4] = {0b00001001, 0b10110001, 0b10001100, 0b01100010}; + mu_assert_int_eq(0b0, bit_lib_get_bits_32(value, 0, 1)); + mu_assert_int_eq(0b00, bit_lib_get_bits_32(value, 0, 2)); + mu_assert_int_eq(0b000, bit_lib_get_bits_32(value, 0, 3)); + mu_assert_int_eq(0b0000, bit_lib_get_bits_32(value, 0, 4)); + mu_assert_int_eq(0b00001, bit_lib_get_bits_32(value, 0, 5)); + mu_assert_int_eq(0b000010, bit_lib_get_bits_32(value, 0, 6)); + mu_assert_int_eq(0b0000100, bit_lib_get_bits_32(value, 0, 7)); + mu_assert_int_eq(0b00001001, bit_lib_get_bits_32(value, 0, 8)); + mu_assert_int_eq(0b000010011, bit_lib_get_bits_32(value, 0, 9)); + mu_assert_int_eq(0b0000100110, bit_lib_get_bits_32(value, 0, 10)); + mu_assert_int_eq(0b00001001101, bit_lib_get_bits_32(value, 0, 11)); + mu_assert_int_eq(0b000010011011, bit_lib_get_bits_32(value, 0, 12)); + mu_assert_int_eq(0b0000100110110, bit_lib_get_bits_32(value, 0, 13)); + mu_assert_int_eq(0b00001001101100, bit_lib_get_bits_32(value, 0, 14)); + mu_assert_int_eq(0b000010011011000, bit_lib_get_bits_32(value, 0, 15)); + mu_assert_int_eq(0b0000100110110001, bit_lib_get_bits_32(value, 0, 16)); + mu_assert_int_eq(0b00001001101100011, bit_lib_get_bits_32(value, 0, 17)); + mu_assert_int_eq(0b000010011011000110, bit_lib_get_bits_32(value, 0, 18)); + mu_assert_int_eq(0b0000100110110001100, bit_lib_get_bits_32(value, 0, 19)); + mu_assert_int_eq(0b00001001101100011000, bit_lib_get_bits_32(value, 0, 20)); + mu_assert_int_eq(0b000010011011000110001, bit_lib_get_bits_32(value, 0, 21)); + mu_assert_int_eq(0b0000100110110001100011, bit_lib_get_bits_32(value, 0, 22)); + mu_assert_int_eq(0b00001001101100011000110, bit_lib_get_bits_32(value, 0, 23)); + mu_assert_int_eq(0b000010011011000110001100, bit_lib_get_bits_32(value, 0, 24)); + mu_assert_int_eq(0b0000100110110001100011000, bit_lib_get_bits_32(value, 0, 25)); + mu_assert_int_eq(0b00001001101100011000110001, bit_lib_get_bits_32(value, 0, 26)); + mu_assert_int_eq(0b000010011011000110001100011, bit_lib_get_bits_32(value, 0, 27)); + mu_assert_int_eq(0b0000100110110001100011000110, bit_lib_get_bits_32(value, 0, 28)); + mu_assert_int_eq(0b00001001101100011000110001100, bit_lib_get_bits_32(value, 0, 29)); + mu_assert_int_eq(0b000010011011000110001100011000, bit_lib_get_bits_32(value, 0, 30)); + mu_assert_int_eq(0b0000100110110001100011000110001, bit_lib_get_bits_32(value, 0, 31)); + mu_assert_int_eq(0b00001001101100011000110001100010, bit_lib_get_bits_32(value, 0, 32)); +} + +MU_TEST(test_bit_lib_test_parity_u32) { + // test even parity + mu_assert_int_eq(bit_lib_test_parity_32(0b00000000, BitLibParityEven), 0); + mu_assert_int_eq(bit_lib_test_parity_32(0b00000001, BitLibParityEven), 1); + mu_assert_int_eq(bit_lib_test_parity_32(0b00000010, BitLibParityEven), 1); + mu_assert_int_eq(bit_lib_test_parity_32(0b00000011, BitLibParityEven), 0); + mu_assert_int_eq(bit_lib_test_parity_32(0b00000100, BitLibParityEven), 1); + mu_assert_int_eq(bit_lib_test_parity_32(0b00000101, BitLibParityEven), 0); + mu_assert_int_eq(bit_lib_test_parity_32(0b00000110, BitLibParityEven), 0); + mu_assert_int_eq(bit_lib_test_parity_32(0b00000111, BitLibParityEven), 1); + mu_assert_int_eq(bit_lib_test_parity_32(0b00001000, BitLibParityEven), 1); + mu_assert_int_eq(bit_lib_test_parity_32(0b00001001, BitLibParityEven), 0); + mu_assert_int_eq(bit_lib_test_parity_32(0b00001010, BitLibParityEven), 0); + mu_assert_int_eq(bit_lib_test_parity_32(0b00001011, BitLibParityEven), 1); + mu_assert_int_eq(bit_lib_test_parity_32(0b00001100, BitLibParityEven), 0); + mu_assert_int_eq(bit_lib_test_parity_32(0b00001101, BitLibParityEven), 1); + mu_assert_int_eq(bit_lib_test_parity_32(0b00001110, BitLibParityEven), 1); + mu_assert_int_eq(bit_lib_test_parity_32(0b00001111, BitLibParityEven), 0); + mu_assert_int_eq(bit_lib_test_parity_32(0b00010000, BitLibParityEven), 1); + + // test odd parity + mu_assert_int_eq(bit_lib_test_parity_32(0b00000000, BitLibParityOdd), 1); + mu_assert_int_eq(bit_lib_test_parity_32(0b00000001, BitLibParityOdd), 0); + mu_assert_int_eq(bit_lib_test_parity_32(0b00000010, BitLibParityOdd), 0); + mu_assert_int_eq(bit_lib_test_parity_32(0b00000011, BitLibParityOdd), 1); + mu_assert_int_eq(bit_lib_test_parity_32(0b00000100, BitLibParityOdd), 0); + mu_assert_int_eq(bit_lib_test_parity_32(0b00000101, BitLibParityOdd), 1); + mu_assert_int_eq(bit_lib_test_parity_32(0b00000110, BitLibParityOdd), 1); + mu_assert_int_eq(bit_lib_test_parity_32(0b00000111, BitLibParityOdd), 0); + mu_assert_int_eq(bit_lib_test_parity_32(0b00001000, BitLibParityOdd), 0); + mu_assert_int_eq(bit_lib_test_parity_32(0b00001001, BitLibParityOdd), 1); + mu_assert_int_eq(bit_lib_test_parity_32(0b00001010, BitLibParityOdd), 1); + mu_assert_int_eq(bit_lib_test_parity_32(0b00001011, BitLibParityOdd), 0); + mu_assert_int_eq(bit_lib_test_parity_32(0b00001100, BitLibParityOdd), 1); + mu_assert_int_eq(bit_lib_test_parity_32(0b00001101, BitLibParityOdd), 0); + mu_assert_int_eq(bit_lib_test_parity_32(0b00001110, BitLibParityOdd), 0); + mu_assert_int_eq(bit_lib_test_parity_32(0b00001111, BitLibParityOdd), 1); + mu_assert_int_eq(bit_lib_test_parity_32(0b00010000, BitLibParityOdd), 0); +} + +MU_TEST(test_bit_lib_test_parity) { + // next data contains valid parity for 1-3 nibble and invalid for 4 nibble + uint8_t data_always_0_parity[2] = {0b11101110, 0b11101111}; + uint8_t data_always_1_parity[2] = {0b00010001, 0b00010000}; + uint8_t data_always_odd_parity[2] = {0b00000011, 0b11110111}; + uint8_t data_always_even_parity[2] = {0b00010111, 0b10110011}; + + // test alawys 0 parity + mu_check(bit_lib_test_parity(data_always_0_parity, 0, 12, BitLibParityAlways0, 4)); + mu_check(bit_lib_test_parity(data_always_0_parity, 4, 8, BitLibParityAlways0, 4)); + mu_check(bit_lib_test_parity(data_always_0_parity, 8, 4, BitLibParityAlways0, 4)); + mu_check(bit_lib_test_parity(data_always_1_parity, 12, 4, BitLibParityAlways0, 4)); + + mu_check(!bit_lib_test_parity(data_always_0_parity, 0, 16, BitLibParityAlways0, 4)); + mu_check(!bit_lib_test_parity(data_always_0_parity, 4, 12, BitLibParityAlways0, 4)); + mu_check(!bit_lib_test_parity(data_always_0_parity, 8, 8, BitLibParityAlways0, 4)); + mu_check(!bit_lib_test_parity(data_always_0_parity, 12, 4, BitLibParityAlways0, 4)); + + // test alawys 1 parity + mu_check(bit_lib_test_parity(data_always_1_parity, 0, 12, BitLibParityAlways1, 4)); + mu_check(bit_lib_test_parity(data_always_1_parity, 4, 8, BitLibParityAlways1, 4)); + mu_check(bit_lib_test_parity(data_always_1_parity, 8, 4, BitLibParityAlways1, 4)); + mu_check(bit_lib_test_parity(data_always_0_parity, 12, 4, BitLibParityAlways1, 4)); + + mu_check(!bit_lib_test_parity(data_always_1_parity, 0, 16, BitLibParityAlways1, 4)); + mu_check(!bit_lib_test_parity(data_always_1_parity, 4, 12, BitLibParityAlways1, 4)); + mu_check(!bit_lib_test_parity(data_always_1_parity, 8, 8, BitLibParityAlways1, 4)); + mu_check(!bit_lib_test_parity(data_always_1_parity, 12, 4, BitLibParityAlways1, 4)); + + // test odd parity + mu_check(bit_lib_test_parity(data_always_odd_parity, 0, 12, BitLibParityOdd, 4)); + mu_check(bit_lib_test_parity(data_always_odd_parity, 4, 8, BitLibParityOdd, 4)); + mu_check(bit_lib_test_parity(data_always_odd_parity, 8, 4, BitLibParityOdd, 4)); + mu_check(bit_lib_test_parity(data_always_even_parity, 12, 4, BitLibParityOdd, 4)); + + mu_check(!bit_lib_test_parity(data_always_odd_parity, 0, 16, BitLibParityOdd, 4)); + mu_check(!bit_lib_test_parity(data_always_odd_parity, 4, 12, BitLibParityOdd, 4)); + mu_check(!bit_lib_test_parity(data_always_odd_parity, 8, 8, BitLibParityOdd, 4)); + mu_check(!bit_lib_test_parity(data_always_odd_parity, 12, 4, BitLibParityOdd, 4)); + + // test even parity + mu_check(bit_lib_test_parity(data_always_even_parity, 0, 12, BitLibParityEven, 4)); + mu_check(bit_lib_test_parity(data_always_even_parity, 4, 8, BitLibParityEven, 4)); + mu_check(bit_lib_test_parity(data_always_even_parity, 8, 4, BitLibParityEven, 4)); + mu_check(bit_lib_test_parity(data_always_odd_parity, 12, 4, BitLibParityEven, 4)); + + mu_check(!bit_lib_test_parity(data_always_even_parity, 0, 16, BitLibParityEven, 4)); + mu_check(!bit_lib_test_parity(data_always_even_parity, 4, 12, BitLibParityEven, 4)); + mu_check(!bit_lib_test_parity(data_always_even_parity, 8, 8, BitLibParityEven, 4)); + mu_check(!bit_lib_test_parity(data_always_even_parity, 12, 4, BitLibParityEven, 4)); +} + +MU_TEST(test_bit_lib_remove_bit_every_nth) { + // TODO: more tests + uint8_t data_i[1] = {0b00001111}; + uint8_t data_o[1] = {0b00011111}; + size_t length; + + length = bit_lib_remove_bit_every_nth(data_i, 0, 8, 3); + mu_assert_int_eq(6, length); + mu_assert_mem_eq(data_o, data_i, 1); +} + +MU_TEST(test_bit_lib_reverse_bits) { + uint8_t data_1_i[2] = {0b11001010, 0b00011111}; + uint8_t data_1_o[2] = {0b11111000, 0b01010011}; + + // reverse bits [0..15] + bit_lib_reverse_bits(data_1_i, 0, 16); + mu_assert_mem_eq(data_1_o, data_1_i, 2); + + uint8_t data_2_i[2] = {0b11001010, 0b00011111}; + uint8_t data_2_o[2] = {0b11001000, 0b01011111}; + + // reverse bits [4..11] + bit_lib_reverse_bits(data_2_i, 4, 8); + mu_assert_mem_eq(data_2_o, data_2_i, 2); +} + +MU_TEST(test_bit_lib_copy_bits) { + uint8_t data_1_i[2] = {0b11001010, 0b00011111}; + uint8_t data_1_o[2] = {0}; + + // data_1_o[0..15] = data_1_i[0..15] + bit_lib_copy_bits(data_1_o, 0, 16, data_1_i, 0); + mu_assert_mem_eq(data_1_i, data_1_o, 2); + + memset(data_1_o, 0, 2); + // data_1_o[4..11] = data_1_i[0..7] + bit_lib_copy_bits(data_1_o, 4, 8, data_1_i, 0); + mu_assert_mem_eq(((uint8_t[]){0b00001100, 0b10100000}), data_1_o, 2); +} + +MU_TEST(test_bit_lib_get_bit_count) { + mu_assert_int_eq(0, bit_lib_get_bit_count(0)); + mu_assert_int_eq(1, bit_lib_get_bit_count(0b1)); + mu_assert_int_eq(1, bit_lib_get_bit_count(0b10)); + mu_assert_int_eq(2, bit_lib_get_bit_count(0b11)); + mu_assert_int_eq(4, bit_lib_get_bit_count(0b11000011)); + mu_assert_int_eq(6, bit_lib_get_bit_count(0b11000011000011)); + mu_assert_int_eq(8, bit_lib_get_bit_count(0b11111111)); + mu_assert_int_eq(16, bit_lib_get_bit_count(0b11111110000000000000000111111111)); + mu_assert_int_eq(32, bit_lib_get_bit_count(0b11111111111111111111111111111111)); +} + +MU_TEST(test_bit_lib_reverse_16_fast) { + mu_assert_int_eq(0b0000000000000000, bit_lib_reverse_16_fast(0b0000000000000000)); + mu_assert_int_eq(0b1000000000000000, bit_lib_reverse_16_fast(0b0000000000000001)); + mu_assert_int_eq(0b1100000000000000, bit_lib_reverse_16_fast(0b0000000000000011)); + mu_assert_int_eq(0b0000100000001001, bit_lib_reverse_16_fast(0b1001000000010000)); +} + +MU_TEST(test_bit_lib_crc16) { + uint8_t data[9] = {'1', '2', '3', '4', '5', '6', '7', '8', '9'}; + uint8_t data_size = 9; + + // Algorithm + // Check Poly Init RefIn RefOut XorOut + // CRC-16/CCITT-FALSE + // 0x29B1 0x1021 0xFFFF false false 0x0000 + mu_assert_int_eq(0x29B1, bit_lib_crc16(data, data_size, 0x1021, 0xFFFF, false, false, 0x0000)); + // CRC-16/ARC + // 0xBB3D 0x8005 0x0000 true true 0x0000 + mu_assert_int_eq(0xBB3D, bit_lib_crc16(data, data_size, 0x8005, 0x0000, true, true, 0x0000)); + // CRC-16/AUG-CCITT + // 0xE5CC 0x1021 0x1D0F false false 0x0000 + mu_assert_int_eq(0xE5CC, bit_lib_crc16(data, data_size, 0x1021, 0x1D0F, false, false, 0x0000)); + // CRC-16/BUYPASS + // 0xFEE8 0x8005 0x0000 false false 0x0000 + mu_assert_int_eq(0xFEE8, bit_lib_crc16(data, data_size, 0x8005, 0x0000, false, false, 0x0000)); + // CRC-16/CDMA2000 + // 0x4C06 0xC867 0xFFFF false false 0x0000 + mu_assert_int_eq(0x4C06, bit_lib_crc16(data, data_size, 0xC867, 0xFFFF, false, false, 0x0000)); + // CRC-16/DDS-110 + // 0x9ECF 0x8005 0x800D false false 0x0000 + mu_assert_int_eq(0x9ECF, bit_lib_crc16(data, data_size, 0x8005, 0x800D, false, false, 0x0000)); + // CRC-16/DECT-R + // 0x007E 0x0589 0x0000 false false 0x0001 + mu_assert_int_eq(0x007E, bit_lib_crc16(data, data_size, 0x0589, 0x0000, false, false, 0x0001)); + // CRC-16/DECT-X + // 0x007F 0x0589 0x0000 false false 0x0000 + mu_assert_int_eq(0x007F, bit_lib_crc16(data, data_size, 0x0589, 0x0000, false, false, 0x0000)); + // CRC-16/DNP + // 0xEA82 0x3D65 0x0000 true true 0xFFFF + mu_assert_int_eq(0xEA82, bit_lib_crc16(data, data_size, 0x3D65, 0x0000, true, true, 0xFFFF)); + // CRC-16/EN-13757 + // 0xC2B7 0x3D65 0x0000 false false 0xFFFF + mu_assert_int_eq(0xC2B7, bit_lib_crc16(data, data_size, 0x3D65, 0x0000, false, false, 0xFFFF)); + // CRC-16/GENIBUS + // 0xD64E 0x1021 0xFFFF false false 0xFFFF + mu_assert_int_eq(0xD64E, bit_lib_crc16(data, data_size, 0x1021, 0xFFFF, false, false, 0xFFFF)); + // CRC-16/MAXIM + // 0x44C2 0x8005 0x0000 true true 0xFFFF + mu_assert_int_eq(0x44C2, bit_lib_crc16(data, data_size, 0x8005, 0x0000, true, true, 0xFFFF)); + // CRC-16/MCRF4XX + // 0x6F91 0x1021 0xFFFF true true 0x0000 + mu_assert_int_eq(0x6F91, bit_lib_crc16(data, data_size, 0x1021, 0xFFFF, true, true, 0x0000)); + // CRC-16/RIELLO + // 0x63D0 0x1021 0xB2AA true true 0x0000 + mu_assert_int_eq(0x63D0, bit_lib_crc16(data, data_size, 0x1021, 0xB2AA, true, true, 0x0000)); + // CRC-16/T10-DIF + // 0xD0DB 0x8BB7 0x0000 false false 0x0000 + mu_assert_int_eq(0xD0DB, bit_lib_crc16(data, data_size, 0x8BB7, 0x0000, false, false, 0x0000)); + // CRC-16/TELEDISK + // 0x0FB3 0xA097 0x0000 false false 0x0000 + mu_assert_int_eq(0x0FB3, bit_lib_crc16(data, data_size, 0xA097, 0x0000, false, false, 0x0000)); + // CRC-16/TMS37157 + // 0x26B1 0x1021 0x89EC true true 0x0000 + mu_assert_int_eq(0x26B1, bit_lib_crc16(data, data_size, 0x1021, 0x89EC, true, true, 0x0000)); + // CRC-16/USB + // 0xB4C8 0x8005 0xFFFF true true 0xFFFF + mu_assert_int_eq(0xB4C8, bit_lib_crc16(data, data_size, 0x8005, 0xFFFF, true, true, 0xFFFF)); + // CRC-A + // 0xBF05 0x1021 0xC6C6 true true 0x0000 + mu_assert_int_eq(0xBF05, bit_lib_crc16(data, data_size, 0x1021, 0xC6C6, true, true, 0x0000)); + // CRC-16/KERMIT + // 0x2189 0x1021 0x0000 true true 0x0000 + mu_assert_int_eq(0x2189, bit_lib_crc16(data, data_size, 0x1021, 0x0000, true, true, 0x0000)); + // CRC-16/MODBUS + // 0x4B37 0x8005 0xFFFF true true 0x0000 + mu_assert_int_eq(0x4B37, bit_lib_crc16(data, data_size, 0x8005, 0xFFFF, true, true, 0x0000)); + // CRC-16/X-25 + // 0x906E 0x1021 0xFFFF true true 0xFFFF + mu_assert_int_eq(0x906E, bit_lib_crc16(data, data_size, 0x1021, 0xFFFF, true, true, 0xFFFF)); + // CRC-16/XMODEM + // 0x31C3 0x1021 0x0000 false false 0x0000 + mu_assert_int_eq(0x31C3, bit_lib_crc16(data, data_size, 0x1021, 0x0000, false, false, 0x0000)); +} + +MU_TEST_SUITE(test_bit_lib) { + MU_RUN_TEST(test_bit_lib_increment_index); + MU_RUN_TEST(test_bit_lib_is_set); + MU_RUN_TEST(test_bit_lib_push); + MU_RUN_TEST(test_bit_lib_set_bit); + MU_RUN_TEST(test_bit_lib_set_bits); + MU_RUN_TEST(test_bit_lib_get_bit); + MU_RUN_TEST(test_bit_lib_get_bits); + MU_RUN_TEST(test_bit_lib_get_bits_16); + MU_RUN_TEST(test_bit_lib_get_bits_32); + MU_RUN_TEST(test_bit_lib_test_parity_u32); + MU_RUN_TEST(test_bit_lib_test_parity); + MU_RUN_TEST(test_bit_lib_remove_bit_every_nth); + MU_RUN_TEST(test_bit_lib_copy_bits); + MU_RUN_TEST(test_bit_lib_reverse_bits); + MU_RUN_TEST(test_bit_lib_get_bit_count); + MU_RUN_TEST(test_bit_lib_reverse_16_fast); + MU_RUN_TEST(test_bit_lib_crc16); +} + +int run_minunit_test_bit_lib() { + MU_RUN_SUITE(test_bit_lib); + return MU_EXIT_CODE; +} \ No newline at end of file diff --git a/applications/unit_tests/lfrfid/lfrfid_protocols.c b/applications/unit_tests/lfrfid/lfrfid_protocols.c new file mode 100644 index 000000000..4401cbb4d --- /dev/null +++ b/applications/unit_tests/lfrfid/lfrfid_protocols.c @@ -0,0 +1,464 @@ +#include +#include "../minunit.h" +#include +#include +#include + +#define LF_RFID_READ_TIMING_MULTIPLIER 8 + +#define EM_TEST_DATA \ + { 0x58, 0x00, 0x85, 0x64, 0x02 } +#define EM_TEST_DATA_SIZE 5 +#define EM_TEST_EMULATION_TIMINGS_COUNT (64 * 2) + +const int8_t em_test_timings[EM_TEST_EMULATION_TIMINGS_COUNT] = { + 32, -32, 32, -32, 32, -32, 32, -32, 32, -32, 32, -32, 32, -32, 32, -32, 32, -32, -32, + 32, 32, -32, -32, 32, 32, -32, -32, 32, 32, -32, -32, 32, -32, 32, -32, 32, 32, -32, + -32, 32, -32, 32, -32, 32, -32, 32, -32, 32, -32, 32, -32, 32, -32, 32, -32, 32, -32, + 32, 32, -32, -32, 32, -32, 32, -32, 32, 32, -32, -32, 32, 32, -32, -32, 32, 32, -32, + -32, 32, -32, 32, 32, -32, 32, -32, -32, 32, -32, 32, -32, 32, 32, -32, -32, 32, -32, + 32, 32, -32, -32, 32, -32, 32, -32, 32, -32, 32, -32, 32, -32, 32, -32, 32, 32, -32, + -32, 32, 32, -32, -32, 32, -32, 32, -32, 32, -32, 32, -32, 32, +}; + +#define HID10301_TEST_DATA \ + { 0x8D, 0x48, 0xA8 } +#define HID10301_TEST_DATA_SIZE 3 +#define HID10301_TEST_EMULATION_TIMINGS_COUNT (541 * 2) + +const int8_t hid10301_test_timings[HID10301_TEST_EMULATION_TIMINGS_COUNT] = { + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, + 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, + 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, + 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, + 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, + 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, + 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, + 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, + 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, + 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, + 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, + 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, + 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, + 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, + 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, + 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, +}; + +#define IOPROX_XSF_TEST_DATA \ + { 0x65, 0x01, 0x05, 0x39 } +#define IOPROX_XSF_TEST_DATA_SIZE 4 +#define IOPROX_XSF_TEST_EMULATION_TIMINGS_COUNT (468 * 2) + +const int8_t ioprox_xsf_test_timings[IOPROX_XSF_TEST_EMULATION_TIMINGS_COUNT] = { + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, + 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, + 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, + 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, + 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, + 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, + 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, + 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, + 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, + 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, + 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, + 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, +}; + +#define INDALA26_EMULATION_TIMINGS_COUNT (1024 * 2) +#define INDALA26_TEST_DATA \ + { 0x3B, 0x73, 0x64, 0xA8 } +#define INDALA26_TEST_DATA_SIZE 4 + +const int8_t indala26_test_timings[INDALA26_EMULATION_TIMINGS_COUNT] = { + 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, + 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, + 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, + 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, + 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, + 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, + 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, + 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, + 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, + 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, + 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, + 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, + 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, + 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, + 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, + 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, + 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, + 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, + 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, + 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, + 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, +}; + +MU_TEST(test_lfrfid_protocol_em_read_simple) { + ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax); + mu_assert_int_eq(EM_TEST_DATA_SIZE, protocol_dict_get_data_size(dict, LFRFIDProtocolEM4100)); + mu_assert_string_eq("EM4100", protocol_dict_get_name(dict, LFRFIDProtocolEM4100)); + mu_assert_string_eq("EM-Micro", protocol_dict_get_manufacturer(dict, LFRFIDProtocolEM4100)); + + const uint8_t data[EM_TEST_DATA_SIZE] = EM_TEST_DATA; + + protocol_dict_decoders_start(dict); + + ProtocolId protocol = PROTOCOL_NO; + PulseGlue* pulse_glue = pulse_glue_alloc(); + + for(size_t i = 0; i < EM_TEST_EMULATION_TIMINGS_COUNT * 10; i++) { + bool pulse_pop = pulse_glue_push( + pulse_glue, + em_test_timings[i % EM_TEST_EMULATION_TIMINGS_COUNT] >= 0, + abs(em_test_timings[i % EM_TEST_EMULATION_TIMINGS_COUNT]) * + LF_RFID_READ_TIMING_MULTIPLIER); + + if(pulse_pop) { + uint32_t length, period; + pulse_glue_pop(pulse_glue, &length, &period); + + protocol = protocol_dict_decoders_feed(dict, true, period); + if(protocol != PROTOCOL_NO) break; + + protocol = protocol_dict_decoders_feed(dict, false, length - period); + if(protocol != PROTOCOL_NO) break; + } + } + + pulse_glue_free(pulse_glue); + + mu_assert_int_eq(LFRFIDProtocolEM4100, protocol); + uint8_t received_data[EM_TEST_DATA_SIZE] = {0}; + protocol_dict_get_data(dict, protocol, received_data, EM_TEST_DATA_SIZE); + + mu_assert_mem_eq(data, received_data, EM_TEST_DATA_SIZE); + + protocol_dict_free(dict); +} + +MU_TEST(test_lfrfid_protocol_em_emulate_simple) { + ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax); + mu_assert_int_eq(EM_TEST_DATA_SIZE, protocol_dict_get_data_size(dict, LFRFIDProtocolEM4100)); + mu_assert_string_eq("EM4100", protocol_dict_get_name(dict, LFRFIDProtocolEM4100)); + mu_assert_string_eq("EM-Micro", protocol_dict_get_manufacturer(dict, LFRFIDProtocolEM4100)); + + const uint8_t data[EM_TEST_DATA_SIZE] = EM_TEST_DATA; + + protocol_dict_set_data(dict, LFRFIDProtocolEM4100, data, EM_TEST_DATA_SIZE); + mu_check(protocol_dict_encoder_start(dict, LFRFIDProtocolEM4100)); + + for(size_t i = 0; i < EM_TEST_EMULATION_TIMINGS_COUNT; i++) { + LevelDuration level_duration = protocol_dict_encoder_yield(dict, LFRFIDProtocolEM4100); + + if(level_duration_get_level(level_duration)) { + mu_assert_int_eq(em_test_timings[i], level_duration_get_duration(level_duration)); + } else { + mu_assert_int_eq(em_test_timings[i], -level_duration_get_duration(level_duration)); + } + } + + protocol_dict_free(dict); +} + +MU_TEST(test_lfrfid_protocol_h10301_read_simple) { + ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax); + mu_assert_int_eq( + HID10301_TEST_DATA_SIZE, protocol_dict_get_data_size(dict, LFRFIDProtocolH10301)); + mu_assert_string_eq("H10301", protocol_dict_get_name(dict, LFRFIDProtocolH10301)); + mu_assert_string_eq("HID", protocol_dict_get_manufacturer(dict, LFRFIDProtocolH10301)); + + const uint8_t data[HID10301_TEST_DATA_SIZE] = HID10301_TEST_DATA; + + protocol_dict_decoders_start(dict); + + ProtocolId protocol = PROTOCOL_NO; + PulseGlue* pulse_glue = pulse_glue_alloc(); + + for(size_t i = 0; i < HID10301_TEST_EMULATION_TIMINGS_COUNT * 10; i++) { + bool pulse_pop = pulse_glue_push( + pulse_glue, + hid10301_test_timings[i % HID10301_TEST_EMULATION_TIMINGS_COUNT] >= 0, + abs(hid10301_test_timings[i % HID10301_TEST_EMULATION_TIMINGS_COUNT]) * + LF_RFID_READ_TIMING_MULTIPLIER); + + if(pulse_pop) { + uint32_t length, period; + pulse_glue_pop(pulse_glue, &length, &period); + + protocol = protocol_dict_decoders_feed(dict, true, period); + if(protocol != PROTOCOL_NO) break; + + protocol = protocol_dict_decoders_feed(dict, false, length - period); + if(protocol != PROTOCOL_NO) break; + } + } + + pulse_glue_free(pulse_glue); + + mu_assert_int_eq(LFRFIDProtocolH10301, protocol); + uint8_t received_data[HID10301_TEST_DATA_SIZE] = {0}; + protocol_dict_get_data(dict, protocol, received_data, HID10301_TEST_DATA_SIZE); + + mu_assert_mem_eq(data, received_data, HID10301_TEST_DATA_SIZE); + + protocol_dict_free(dict); +} + +MU_TEST(test_lfrfid_protocol_h10301_emulate_simple) { + ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax); + mu_assert_int_eq( + HID10301_TEST_DATA_SIZE, protocol_dict_get_data_size(dict, LFRFIDProtocolH10301)); + mu_assert_string_eq("H10301", protocol_dict_get_name(dict, LFRFIDProtocolH10301)); + mu_assert_string_eq("HID", protocol_dict_get_manufacturer(dict, LFRFIDProtocolH10301)); + + const uint8_t data[HID10301_TEST_DATA_SIZE] = HID10301_TEST_DATA; + + protocol_dict_set_data(dict, LFRFIDProtocolH10301, data, HID10301_TEST_DATA_SIZE); + mu_check(protocol_dict_encoder_start(dict, LFRFIDProtocolH10301)); + + for(size_t i = 0; i < HID10301_TEST_EMULATION_TIMINGS_COUNT; i++) { + LevelDuration level_duration = protocol_dict_encoder_yield(dict, LFRFIDProtocolH10301); + + if(level_duration_get_level(level_duration)) { + mu_assert_int_eq( + hid10301_test_timings[i], level_duration_get_duration(level_duration)); + } else { + mu_assert_int_eq( + hid10301_test_timings[i], -level_duration_get_duration(level_duration)); + } + } + + protocol_dict_free(dict); +} + +MU_TEST(test_lfrfid_protocol_ioprox_xsf_read_simple) { + ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax); + mu_assert_int_eq( + IOPROX_XSF_TEST_DATA_SIZE, protocol_dict_get_data_size(dict, LFRFIDProtocolIOProxXSF)); + mu_assert_string_eq("IoProxXSF", protocol_dict_get_name(dict, LFRFIDProtocolIOProxXSF)); + mu_assert_string_eq("Kantech", protocol_dict_get_manufacturer(dict, LFRFIDProtocolIOProxXSF)); + + const uint8_t data[IOPROX_XSF_TEST_DATA_SIZE] = IOPROX_XSF_TEST_DATA; + + protocol_dict_decoders_start(dict); + + ProtocolId protocol = PROTOCOL_NO; + PulseGlue* pulse_glue = pulse_glue_alloc(); + + for(size_t i = 0; i < IOPROX_XSF_TEST_EMULATION_TIMINGS_COUNT * 10; i++) { + bool pulse_pop = pulse_glue_push( + pulse_glue, + ioprox_xsf_test_timings[i % IOPROX_XSF_TEST_EMULATION_TIMINGS_COUNT] >= 0, + abs(ioprox_xsf_test_timings[i % IOPROX_XSF_TEST_EMULATION_TIMINGS_COUNT]) * + LF_RFID_READ_TIMING_MULTIPLIER); + + if(pulse_pop) { + uint32_t length, period; + pulse_glue_pop(pulse_glue, &length, &period); + + protocol = protocol_dict_decoders_feed(dict, true, period); + if(protocol != PROTOCOL_NO) break; + + protocol = protocol_dict_decoders_feed(dict, false, length - period); + if(protocol != PROTOCOL_NO) break; + } + } + + pulse_glue_free(pulse_glue); + + mu_assert_int_eq(LFRFIDProtocolIOProxXSF, protocol); + uint8_t received_data[IOPROX_XSF_TEST_DATA_SIZE] = {0}; + protocol_dict_get_data(dict, protocol, received_data, IOPROX_XSF_TEST_DATA_SIZE); + + mu_assert_mem_eq(data, received_data, IOPROX_XSF_TEST_DATA_SIZE); + + protocol_dict_free(dict); +} + +MU_TEST(test_lfrfid_protocol_ioprox_xsf_emulate_simple) { + ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax); + mu_assert_int_eq( + IOPROX_XSF_TEST_DATA_SIZE, protocol_dict_get_data_size(dict, LFRFIDProtocolIOProxXSF)); + mu_assert_string_eq("IoProxXSF", protocol_dict_get_name(dict, LFRFIDProtocolIOProxXSF)); + mu_assert_string_eq("Kantech", protocol_dict_get_manufacturer(dict, LFRFIDProtocolIOProxXSF)); + + const uint8_t data[IOPROX_XSF_TEST_DATA_SIZE] = IOPROX_XSF_TEST_DATA; + + protocol_dict_set_data(dict, LFRFIDProtocolIOProxXSF, data, IOPROX_XSF_TEST_DATA_SIZE); + mu_check(protocol_dict_encoder_start(dict, LFRFIDProtocolIOProxXSF)); + + for(size_t i = 0; i < IOPROX_XSF_TEST_EMULATION_TIMINGS_COUNT; i++) { + LevelDuration level_duration = protocol_dict_encoder_yield(dict, LFRFIDProtocolIOProxXSF); + + if(level_duration_get_level(level_duration)) { + mu_assert_int_eq( + ioprox_xsf_test_timings[i], level_duration_get_duration(level_duration)); + } else { + mu_assert_int_eq( + ioprox_xsf_test_timings[i], -level_duration_get_duration(level_duration)); + } + } + + protocol_dict_free(dict); +} + +MU_TEST(test_lfrfid_protocol_inadala26_emulate_simple) { + ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax); + mu_assert_int_eq( + INDALA26_TEST_DATA_SIZE, protocol_dict_get_data_size(dict, LFRFIDProtocolIndala26)); + mu_assert_string_eq("Indala26", protocol_dict_get_name(dict, LFRFIDProtocolIndala26)); + mu_assert_string_eq("Motorola", protocol_dict_get_manufacturer(dict, LFRFIDProtocolIndala26)); + + const uint8_t data[INDALA26_TEST_DATA_SIZE] = INDALA26_TEST_DATA; + + protocol_dict_set_data(dict, LFRFIDProtocolIndala26, data, INDALA26_TEST_DATA_SIZE); + mu_check(protocol_dict_encoder_start(dict, LFRFIDProtocolIndala26)); + + for(size_t i = 0; i < INDALA26_EMULATION_TIMINGS_COUNT; i++) { + LevelDuration level_duration = protocol_dict_encoder_yield(dict, LFRFIDProtocolIndala26); + + if(level_duration_get_level(level_duration)) { + mu_assert_int_eq( + indala26_test_timings[i], level_duration_get_duration(level_duration)); + } else { + mu_assert_int_eq( + indala26_test_timings[i], -level_duration_get_duration(level_duration)); + } + } + + protocol_dict_free(dict); +} + +MU_TEST_SUITE(test_lfrfid_protocols_suite) { + MU_RUN_TEST(test_lfrfid_protocol_em_read_simple); + MU_RUN_TEST(test_lfrfid_protocol_em_emulate_simple); + + MU_RUN_TEST(test_lfrfid_protocol_h10301_read_simple); + MU_RUN_TEST(test_lfrfid_protocol_h10301_emulate_simple); + + MU_RUN_TEST(test_lfrfid_protocol_ioprox_xsf_read_simple); + MU_RUN_TEST(test_lfrfid_protocol_ioprox_xsf_emulate_simple); + + MU_RUN_TEST(test_lfrfid_protocol_inadala26_emulate_simple); +} + +int run_minunit_test_lfrfid_protocols() { + MU_RUN_SUITE(test_lfrfid_protocols_suite); + return MU_EXIT_CODE; +} \ No newline at end of file diff --git a/applications/unit_tests/minunit.h b/applications/unit_tests/minunit.h index d1efd13e6..17eb7b3f1 100644 --- a/applications/unit_tests/minunit.h +++ b/applications/unit_tests/minunit.h @@ -151,46 +151,46 @@ void minunit_print_fail(const char* error); #define MU_EXIT_CODE minunit_fail /* Assertions */ -#define mu_check(test) \ - MU__SAFE_BLOCK( \ - minunit_assert++; if(!(test)) { \ - snprintf( \ - minunit_last_message, \ - MINUNIT_MESSAGE_LEN, \ - "%s failed:\n\t%s:%d: %s", \ - __func__, \ - __FILE__, \ - __LINE__, \ - #test); \ - minunit_status = 1; \ - return; \ +#define mu_check(test) \ + MU__SAFE_BLOCK( \ + minunit_assert++; if(!(test)) { \ + snprintf( \ + minunit_last_message, \ + MINUNIT_MESSAGE_LEN, \ + "%s failed:\r\n\t%s:%d: %s", \ + __func__, \ + __FILE__, \ + __LINE__, \ + #test); \ + minunit_status = 1; \ + return; \ } else { minunit_print_progress(); }) -#define mu_fail(message) \ - MU__SAFE_BLOCK(minunit_assert++; snprintf( \ - minunit_last_message, \ - MINUNIT_MESSAGE_LEN, \ - "%s failed:\n\t%s:%d: %s", \ - __func__, \ - __FILE__, \ - __LINE__, \ - message); \ - minunit_status = 1; \ +#define mu_fail(message) \ + MU__SAFE_BLOCK(minunit_assert++; snprintf( \ + minunit_last_message, \ + MINUNIT_MESSAGE_LEN, \ + "%s failed:\r\n\t%s:%d: %s", \ + __func__, \ + __FILE__, \ + __LINE__, \ + message); \ + minunit_status = 1; \ return;) -#define mu_assert(test, message) \ - MU__SAFE_BLOCK( \ - minunit_assert++; if(!(test)) { \ - snprintf( \ - minunit_last_message, \ - MINUNIT_MESSAGE_LEN, \ - "%s failed:\n\t%s:%d: %s", \ - __func__, \ - __FILE__, \ - __LINE__, \ - message); \ - minunit_status = 1; \ - return; \ +#define mu_assert(test, message) \ + MU__SAFE_BLOCK( \ + minunit_assert++; if(!(test)) { \ + snprintf( \ + minunit_last_message, \ + MINUNIT_MESSAGE_LEN, \ + "%s failed:\r\n\t%s:%d: %s", \ + __func__, \ + __FILE__, \ + __LINE__, \ + message); \ + minunit_status = 1; \ + return; \ } else { minunit_print_progress(); }) #define mu_assert_int_eq(expected, result) \ @@ -201,7 +201,7 @@ void minunit_print_fail(const char* error); snprintf( \ minunit_last_message, \ MINUNIT_MESSAGE_LEN, \ - "%s failed:\n\t%s:%d: %d expected but was %d", \ + "%s failed:\r\n\t%s:%d: %d expected but was %d", \ __func__, \ __FILE__, \ __LINE__, \ @@ -219,7 +219,7 @@ void minunit_print_fail(const char* error); snprintf( \ minunit_last_message, \ MINUNIT_MESSAGE_LEN, \ - "%s failed:\n\t%s:%d: expected different results but both were %d", \ + "%s failed:\r\n\t%s:%d: expected different results but both were %d", \ __func__, \ __FILE__, \ __LINE__, \ @@ -236,7 +236,7 @@ void minunit_print_fail(const char* error); snprintf( \ minunit_last_message, \ MINUNIT_MESSAGE_LEN, \ - "%s failed:\n\t%s:%d: %d <= %d", \ + "%s failed:\r\n\t%s:%d: %d <= %d", \ __func__, \ __FILE__, \ __LINE__, \ @@ -254,7 +254,7 @@ void minunit_print_fail(const char* error); snprintf( \ minunit_last_message, \ MINUNIT_MESSAGE_LEN, \ - "%s failed:\n\t%s:%d: %d >= %d", \ + "%s failed:\r\n\t%s:%d: %d >= %d", \ __func__, \ __FILE__, \ __LINE__, \ @@ -274,7 +274,7 @@ void minunit_print_fail(const char* error); snprintf( \ minunit_last_message, \ MINUNIT_MESSAGE_LEN, \ - "%s failed:\n\t%s:%d: %d was not between (inclusive) %d and %d", \ + "%s failed:\r\n\t%s:%d: %d was not between (inclusive) %d and %d", \ __func__, \ __FILE__, \ __LINE__, \ @@ -302,7 +302,7 @@ void minunit_print_fail(const char* error); snprintf( \ minunit_last_message, \ MINUNIT_MESSAGE_LEN, \ - "%s failed:\n\t%s:%d: expected to be one of %s but was %d", \ + "%s failed:\r\n\t%s:%d: expected to be one of %s but was %d", \ __func__, \ __FILE__, \ __LINE__, \ @@ -321,7 +321,7 @@ void minunit_print_fail(const char* error); snprintf( \ minunit_last_message, \ MINUNIT_MESSAGE_LEN, \ - "%s failed:\n\t%s:%d: %.*g expected but was %.*g", \ + "%s failed:\r\n\t%s:%d: %.*g expected but was %.*g", \ __func__, \ __FILE__, \ __LINE__, \ @@ -341,7 +341,7 @@ void minunit_print_fail(const char* error); snprintf( \ minunit_last_message, \ MINUNIT_MESSAGE_LEN, \ - "%s failed:\n\t%s:%d: %f <= %f", \ + "%s failed:\r\n\t%s:%d: %f <= %f", \ __func__, \ __FILE__, \ __LINE__, \ @@ -359,7 +359,7 @@ void minunit_print_fail(const char* error); snprintf( \ minunit_last_message, \ MINUNIT_MESSAGE_LEN, \ - "%s failed:\n\t%s:%d: %f >= %f", \ + "%s failed:\r\n\t%s:%d: %f >= %f", \ __func__, \ __FILE__, \ __LINE__, \ @@ -379,7 +379,7 @@ void minunit_print_fail(const char* error); snprintf( \ minunit_last_message, \ MINUNIT_MESSAGE_LEN, \ - "%s failed:\n\t%s:%d: %f was not between (inclusive) %f and %f", \ + "%s failed:\r\n\t%s:%d: %f was not between (inclusive) %f and %f", \ __func__, \ __FILE__, \ __LINE__, \ @@ -400,7 +400,7 @@ void minunit_print_fail(const char* error); snprintf( \ minunit_last_message, \ MINUNIT_MESSAGE_LEN, \ - "%s failed:\n\t%s:%d: '%s' expected but was '%s'", \ + "%s failed:\r\n\t%s:%d: '%s' expected but was '%s'", \ __func__, \ __FILE__, \ __LINE__, \ @@ -410,13 +410,41 @@ void minunit_print_fail(const char* error); return; \ } else { minunit_print_progress(); }) +#define mu_assert_mem_eq(expected, result, size) \ + MU__SAFE_BLOCK( \ + const void* minunit_tmp_e = expected; const void* minunit_tmp_r = result; \ + minunit_assert++; \ + if(memcmp(minunit_tmp_e, minunit_tmp_r, size)) { \ + snprintf( \ + minunit_last_message, \ + MINUNIT_MESSAGE_LEN, \ + "%s failed:\r\n\t%s:%d: mem not equal\r\n\tEXP RES", \ + __func__, \ + __FILE__, \ + __LINE__); \ + for(size_t __index = 0; __index < size; __index++) { \ + if(strlen(minunit_last_message) > MINUNIT_MESSAGE_LEN - 20) break; \ + uint8_t __e = ((uint8_t*)minunit_tmp_e)[__index]; \ + uint8_t __r = ((uint8_t*)minunit_tmp_r)[__index]; \ + snprintf( \ + minunit_last_message + strlen(minunit_last_message), \ + MINUNIT_MESSAGE_LEN - strlen(minunit_last_message), \ + "\r\n\t%02X %s %02X", \ + __e, \ + ((__e == __r) ? ".." : "!="), \ + __r); \ + } \ + minunit_status = 1; \ + return; \ + } else { minunit_print_progress(); }) + #define mu_assert_null(result) \ MU__SAFE_BLOCK( \ minunit_assert++; if(result == NULL) { minunit_print_progress(); } else { \ snprintf( \ minunit_last_message, \ MINUNIT_MESSAGE_LEN, \ - "%s failed:\n\t%s:%d: Expected result was not NULL", \ + "%s failed:\r\n\t%s:%d: Expected result was not NULL", \ __func__, \ __FILE__, \ __LINE__); \ @@ -430,7 +458,7 @@ void minunit_print_fail(const char* error); snprintf( \ minunit_last_message, \ MINUNIT_MESSAGE_LEN, \ - "%s failed:\n\t%s:%d: Expected result was not NULL", \ + "%s failed:\r\n\t%s:%d: Expected result was not NULL", \ __func__, \ __FILE__, \ __LINE__); \ @@ -438,32 +466,32 @@ void minunit_print_fail(const char* error); return; \ }) -#define mu_assert_pointers_eq(pointer1, pointer2) \ - MU__SAFE_BLOCK( \ - minunit_assert++; if(pointer1 == pointer2) { minunit_print_progress(); } else { \ - snprintf( \ - minunit_last_message, \ - MINUNIT_MESSAGE_LEN, \ - "%s failed:\n\t%s:%d: Expected the pointers to point to the same memory location", \ - __func__, \ - __FILE__, \ - __LINE__); \ - minunit_status = 1; \ - return; \ +#define mu_assert_pointers_eq(pointer1, pointer2) \ + MU__SAFE_BLOCK( \ + minunit_assert++; if(pointer1 == pointer2) { minunit_print_progress(); } else { \ + snprintf( \ + minunit_last_message, \ + MINUNIT_MESSAGE_LEN, \ + "%s failed:\r\n\t%s:%d: Expected the pointers to point to the same memory location", \ + __func__, \ + __FILE__, \ + __LINE__); \ + minunit_status = 1; \ + return; \ }) -#define mu_assert_pointers_not_eq(pointer1, pointer2) \ - MU__SAFE_BLOCK( \ - minunit_assert++; if(pointer1 != pointer2) { minunit_print_progress(); } else { \ - snprintf( \ - minunit_last_message, \ - MINUNIT_MESSAGE_LEN, \ - "%s failed:\n\t%s:%d: Expected the pointers to point to the same memory location", \ - __func__, \ - __FILE__, \ - __LINE__); \ - minunit_status = 1; \ - return; \ +#define mu_assert_pointers_not_eq(pointer1, pointer2) \ + MU__SAFE_BLOCK( \ + minunit_assert++; if(pointer1 != pointer2) { minunit_print_progress(); } else { \ + snprintf( \ + minunit_last_message, \ + MINUNIT_MESSAGE_LEN, \ + "%s failed:\r\n\t%s:%d: Expected the pointers to point to the same memory location", \ + __func__, \ + __FILE__, \ + __LINE__); \ + minunit_status = 1; \ + return; \ }) /* diff --git a/applications/unit_tests/protocol_dict/protocol_dict_test.c b/applications/unit_tests/protocol_dict/protocol_dict_test.c new file mode 100644 index 000000000..73e77ec90 --- /dev/null +++ b/applications/unit_tests/protocol_dict/protocol_dict_test.c @@ -0,0 +1,222 @@ +#include +#include "../minunit.h" +#include + +typedef enum { + TestDictProtocol0, + TestDictProtocol1, + + TestDictProtocolMax, +} TestDictProtocols; + +/*********************** PROTOCOL 0 START ***********************/ + +typedef struct { + uint32_t data; + size_t encoder_counter; +} Protocol0Data; + +static const uint32_t protocol_0_decoder_result = 0xDEADBEEF; + +static void* protocol_0_alloc() { + void* data = malloc(sizeof(Protocol0Data)); + return data; +} + +static void protocol_0_free(Protocol0Data* data) { + free(data); +} + +static uint8_t* protocol_0_get_data(Protocol0Data* data) { + return (uint8_t*)&data->data; +} + +static void protocol_0_decoder_start(Protocol0Data* data) { + data->data = 0; +} + +static bool protocol_0_decoder_feed(Protocol0Data* data, bool level, uint32_t duration) { + if(level && duration == 666) { + data->data = protocol_0_decoder_result; + return true; + } else { + return false; + } +} + +static bool protocol_0_encoder_start(Protocol0Data* data) { + data->encoder_counter = 0; + return true; +} + +static LevelDuration protocol_0_encoder_yield(Protocol0Data* data) { + data->encoder_counter++; + return level_duration_make(data->encoder_counter % 2, data->data); +} + +/*********************** PROTOCOL 1 START ***********************/ + +typedef struct { + uint64_t data; + size_t encoder_counter; +} Protocol1Data; + +static const uint64_t protocol_1_decoder_result = 0x1234567890ABCDEF; + +static void* protocol_1_alloc() { + void* data = malloc(sizeof(Protocol1Data)); + return data; +} + +static void protocol_1_free(Protocol1Data* data) { + free(data); +} + +static uint8_t* protocol_1_get_data(Protocol1Data* data) { + return (uint8_t*)&data->data; +} + +static void protocol_1_decoder_start(Protocol1Data* data) { + data->data = 0; +} + +static bool protocol_1_decoder_feed(Protocol1Data* data, bool level, uint32_t duration) { + if(level && duration == 543) { + data->data = 0x1234567890ABCDEF; + return true; + } else { + return false; + } +} + +static bool protocol_1_encoder_start(Protocol1Data* data) { + data->encoder_counter = 0; + return true; +} + +static LevelDuration protocol_1_encoder_yield(Protocol1Data* data) { + data->encoder_counter++; + return level_duration_make(!(data->encoder_counter % 2), 100); +} + +/*********************** PROTOCOLS DESCRIPTION ***********************/ +static const ProtocolBase protocol_0 = { + .name = "Protocol 0", + .manufacturer = "Manufacturer 0", + .data_size = 4, + .alloc = (ProtocolAlloc)protocol_0_alloc, + .free = (ProtocolFree)protocol_0_free, + .get_data = (ProtocolGetData)protocol_0_get_data, + .decoder = + { + .start = (ProtocolDecoderStart)protocol_0_decoder_start, + .feed = (ProtocolDecoderFeed)protocol_0_decoder_feed, + }, + .encoder = + { + .start = (ProtocolEncoderStart)protocol_0_encoder_start, + .yield = (ProtocolEncoderYield)protocol_0_encoder_yield, + }, +}; + +static const ProtocolBase protocol_1 = { + .name = "Protocol 1", + .manufacturer = "Manufacturer 1", + .data_size = 8, + .alloc = (ProtocolAlloc)protocol_1_alloc, + .free = (ProtocolFree)protocol_1_free, + .get_data = (ProtocolGetData)protocol_1_get_data, + .decoder = + { + .start = (ProtocolDecoderStart)protocol_1_decoder_start, + .feed = (ProtocolDecoderFeed)protocol_1_decoder_feed, + }, + .encoder = + { + .start = (ProtocolEncoderStart)protocol_1_encoder_start, + .yield = (ProtocolEncoderYield)protocol_1_encoder_yield, + }, +}; + +static const ProtocolBase* test_protocols_base[] = { + [TestDictProtocol0] = &protocol_0, + [TestDictProtocol1] = &protocol_1, +}; + +MU_TEST(test_protocol_dict) { + ProtocolDict* dict = protocol_dict_alloc(test_protocols_base, TestDictProtocolMax); + size_t max_data_size = protocol_dict_get_max_data_size(dict); + mu_assert_int_eq(8, max_data_size); + uint8_t* data = malloc(max_data_size); + + protocol_dict_decoders_start(dict); + ProtocolId protocol_id = PROTOCOL_NO; + + for(size_t i = 0; i < 100; i++) { + protocol_id = protocol_dict_decoders_feed(dict, i % 2, 100); + mu_assert_int_eq(PROTOCOL_NO, protocol_id); + } + + // trigger protocol 1 + protocol_id = protocol_dict_decoders_feed(dict, true, 543); + mu_assert_int_eq(TestDictProtocol1, protocol_id); + + mu_assert_string_eq("Protocol 1", protocol_dict_get_name(dict, protocol_id)); + mu_assert_string_eq("Manufacturer 1", protocol_dict_get_manufacturer(dict, protocol_id)); + + size_t data_size = protocol_dict_get_data_size(dict, protocol_id); + mu_assert_int_eq(8, data_size); + + protocol_dict_get_data(dict, protocol_id, data, data_size); + mu_assert_mem_eq(&protocol_1_decoder_result, data, data_size); + + // trigger protocol 0 + protocol_id = protocol_dict_decoders_feed(dict, true, 666); + mu_assert_int_eq(TestDictProtocol0, protocol_id); + + mu_assert_string_eq("Protocol 0", protocol_dict_get_name(dict, protocol_id)); + mu_assert_string_eq("Manufacturer 0", protocol_dict_get_manufacturer(dict, protocol_id)); + + data_size = protocol_dict_get_data_size(dict, protocol_id); + mu_assert_int_eq(4, data_size); + + protocol_dict_get_data(dict, protocol_id, data, data_size); + mu_assert_mem_eq(&protocol_0_decoder_result, data, data_size); + + protocol_dict_decoders_start(dict); + + protocol_id = TestDictProtocol0; + + const uint8_t protocol_0_test_data[4] = {100, 0, 0, 0}; + protocol_dict_set_data(dict, protocol_id, protocol_0_test_data, 4); + + mu_check(protocol_dict_encoder_start(dict, protocol_id)); + + LevelDuration level; + level = protocol_dict_encoder_yield(dict, protocol_id); + mu_assert_int_eq(true, level_duration_get_level(level)); + mu_assert_int_eq(100, level_duration_get_duration(level)); + level = protocol_dict_encoder_yield(dict, protocol_id); + mu_assert_int_eq(false, level_duration_get_level(level)); + mu_assert_int_eq(100, level_duration_get_duration(level)); + level = protocol_dict_encoder_yield(dict, protocol_id); + mu_assert_int_eq(true, level_duration_get_level(level)); + mu_assert_int_eq(100, level_duration_get_duration(level)); + + mu_check(protocol_dict_encoder_start(dict, protocol_id)); + level = protocol_dict_encoder_yield(dict, protocol_id); + mu_assert_int_eq(true, level_duration_get_level(level)); + mu_assert_int_eq(100, level_duration_get_duration(level)); + + protocol_dict_free(dict); + free(data); +} + +MU_TEST_SUITE(test_protocol_dict_suite) { + MU_RUN_TEST(test_protocol_dict); +} + +int run_minunit_test_protocol_dict() { + MU_RUN_SUITE(test_protocol_dict_suite); + return MU_EXIT_CODE; +} \ No newline at end of file diff --git a/applications/unit_tests/rpc/rpc_test.c b/applications/unit_tests/rpc/rpc_test.c index d31311af6..6ee2aed65 100644 --- a/applications/unit_tests/rpc/rpc_test.c +++ b/applications/unit_tests/rpc/rpc_test.c @@ -215,7 +215,7 @@ static void test_rpc_print_message_list(MsgList_t msg_list) { MsgList_reverse(msg_list); for M_EACH(msg, msg_list, MsgList_t) { - rpc_print_message(msg); + rpc_debug_print_message(msg); } MsgList_reverse(msg_list); #else diff --git a/applications/unit_tests/subghz/subghz_test.c b/applications/unit_tests/subghz/subghz_test.c index f91d27234..04f442f6c 100644 --- a/applications/unit_tests/subghz/subghz_test.c +++ b/applications/unit_tests/subghz/subghz_test.c @@ -13,7 +13,7 @@ #define CAME_ATOMO_DIR_NAME EXT_PATH("subghz/assets/came_atomo") #define NICE_FLOR_S_DIR_NAME EXT_PATH("subghz/assets/nice_flor_s") #define TEST_RANDOM_DIR_NAME EXT_PATH("unit_tests/subghz/test_random_raw.sub") -#define TEST_RANDOM_COUNT_PARSE 188 +#define TEST_RANDOM_COUNT_PARSE 196 #define TEST_TIMEOUT 10000 static SubGhzEnvironment* environment_handler; @@ -412,6 +412,13 @@ MU_TEST(subghz_decoder_honeywell_wdb_test) { "Test decoder " SUBGHZ_PROTOCOL_HONEYWELL_WDB_NAME " error\r\n"); } +MU_TEST(subghz_decoder_magellen_test) { + mu_assert( + subghz_decoder_test( + EXT_PATH("unit_tests/subghz/magellen_raw.sub"), SUBGHZ_PROTOCOL_MAGELLEN_NAME), + "Test decoder " SUBGHZ_PROTOCOL_MAGELLEN_NAME " error\r\n"); +} + //test encoders MU_TEST(subghz_encoder_princeton_test) { mu_assert( @@ -515,6 +522,12 @@ MU_TEST(subghz_encoder_honeywell_wdb_test) { "Test encoder " SUBGHZ_PROTOCOL_HONEYWELL_WDB_NAME " error\r\n"); } +MU_TEST(subghz_encoder_magellen_test) { + mu_assert( + subghz_encoder_test(EXT_PATH("unit_tests/subghz/magellen.sub")), + "Test encoder " SUBGHZ_PROTOCOL_MAGELLEN_NAME " error\r\n"); +} + MU_TEST(subghz_random_test) { mu_assert(subghz_decode_random_test(TEST_RANDOM_DIR_NAME), "Random test error\r\n"); } @@ -552,6 +565,7 @@ MU_TEST_SUITE(subghz) { MU_RUN_TEST(subghz_decoder_doitrand_test); MU_RUN_TEST(subghz_decoder_phoenix_v2_test); MU_RUN_TEST(subghz_decoder_honeywell_wdb_test); + MU_RUN_TEST(subghz_decoder_magellen_test); MU_RUN_TEST(subghz_encoder_princeton_test); MU_RUN_TEST(subghz_encoder_came_test); @@ -570,6 +584,7 @@ MU_TEST_SUITE(subghz) { MU_RUN_TEST(subghz_encoder_doitrand_test); MU_RUN_TEST(subghz_encoder_phoenix_v2_test); MU_RUN_TEST(subghz_encoder_honeywell_wdb_test); + MU_RUN_TEST(subghz_encoder_magellen_test); MU_RUN_TEST(subghz_random_test); subghz_test_deinit(); diff --git a/applications/unit_tests/test_index.c b/applications/unit_tests/test_index.c index e52822465..81d891b2b 100644 --- a/applications/unit_tests/test_index.c +++ b/applications/unit_tests/test_index.c @@ -19,7 +19,10 @@ int run_minunit_test_stream(); int run_minunit_test_storage(); int run_minunit_test_subghz(); int run_minunit_test_dirwalk(); +int run_minunit_test_protocol_dict(); +int run_minunit_test_lfrfid_protocols(); int run_minunit_test_nfc(); +int run_minunit_test_bit_lib(); typedef int (*UnitTestEntry)(); @@ -39,6 +42,9 @@ const UnitTest unit_tests[] = { {.name = "subghz", .entry = run_minunit_test_subghz}, {.name = "infrared", .entry = run_minunit_test_infrared}, {.name = "nfc", .entry = run_minunit_test_nfc}, + {.name = "protocol_dict", .entry = run_minunit_test_protocol_dict}, + {.name = "lfrfid", .entry = run_minunit_test_lfrfid_protocols}, + {.name = "bit_lib", .entry = run_minunit_test_bit_lib}, }; void minunit_print_progress() { diff --git a/applications/unit_tests/varint/varint_test.c b/applications/unit_tests/varint/varint_test.c new file mode 100644 index 000000000..8faab1368 --- /dev/null +++ b/applications/unit_tests/varint/varint_test.c @@ -0,0 +1,88 @@ +#include +#include +#include "../minunit.h" +#include +#include + +MU_TEST(test_varint_basic_u) { + mu_assert_int_eq(1, varint_uint32_length(0)); + mu_assert_int_eq(5, varint_uint32_length(UINT32_MAX)); + + uint8_t data[8] = {}; + uint32_t out_value; + + mu_assert_int_eq(1, varint_uint32_pack(0, data)); + mu_assert_int_eq(1, varint_uint32_unpack(&out_value, data, 8)); + mu_assert_int_eq(0, out_value); + + mu_assert_int_eq(5, varint_uint32_pack(UINT32_MAX, data)); + mu_assert_int_eq(5, varint_uint32_unpack(&out_value, data, 8)); + mu_assert_int_eq(UINT32_MAX, out_value); +} + +MU_TEST(test_varint_basic_i) { + mu_assert_int_eq(5, varint_int32_length(INT32_MIN / 2)); + mu_assert_int_eq(1, varint_int32_length(0)); + mu_assert_int_eq(5, varint_int32_length(INT32_MAX / 2)); + + mu_assert_int_eq(2, varint_int32_length(127)); + mu_assert_int_eq(2, varint_int32_length(-127)); + + uint8_t data[8] = {}; + int32_t out_value; + mu_assert_int_eq(1, varint_int32_pack(0, data)); + mu_assert_int_eq(1, varint_int32_unpack(&out_value, data, 8)); + mu_assert_int_eq(0, out_value); + + mu_assert_int_eq(2, varint_int32_pack(127, data)); + mu_assert_int_eq(2, varint_int32_unpack(&out_value, data, 8)); + mu_assert_int_eq(127, out_value); + + mu_assert_int_eq(2, varint_int32_pack(-127, data)); + mu_assert_int_eq(2, varint_int32_unpack(&out_value, data, 8)); + mu_assert_int_eq(-127, out_value); + + mu_assert_int_eq(5, varint_int32_pack(INT32_MAX, data)); + mu_assert_int_eq(5, varint_int32_unpack(&out_value, data, 8)); + mu_assert_int_eq(INT32_MAX, out_value); + + mu_assert_int_eq(5, varint_int32_pack(INT32_MIN / 2 + 1, data)); + mu_assert_int_eq(5, varint_int32_unpack(&out_value, data, 8)); + mu_assert_int_eq(INT32_MIN / 2 + 1, out_value); +} + +MU_TEST(test_varint_rand_u) { + uint8_t data[8] = {}; + uint32_t out_value; + + for(size_t i = 0; i < 200000; i++) { + uint32_t rand_value = rand(); + mu_assert_int_eq( + varint_uint32_pack(rand_value, data), varint_uint32_unpack(&out_value, data, 8)); + mu_assert_int_eq(rand_value, out_value); + } +} + +MU_TEST(test_varint_rand_i) { + uint8_t data[8] = {}; + int32_t out_value; + + for(size_t i = 0; i < 200000; i++) { + int32_t rand_value = rand() + (INT32_MIN / 2 + 1); + mu_assert_int_eq( + varint_int32_pack(rand_value, data), varint_int32_unpack(&out_value, data, 8)); + mu_assert_int_eq(rand_value, out_value); + } +} + +MU_TEST_SUITE(test_varint_suite) { + MU_RUN_TEST(test_varint_basic_u); + MU_RUN_TEST(test_varint_basic_i); + MU_RUN_TEST(test_varint_rand_u); + MU_RUN_TEST(test_varint_rand_i); +} + +int run_minunit_test_varint() { + MU_RUN_SUITE(test_varint_suite); + return MU_EXIT_CODE; +} \ No newline at end of file diff --git a/applications/updater/util/update_task.c b/applications/updater/util/update_task.c index 6864076d6..b04773197 100644 --- a/applications/updater/util/update_task.c +++ b/applications/updater/util/update_task.c @@ -170,8 +170,7 @@ static bool update_task_check_file_exists(UpdateTask* update_task, string_t file string_t tmp_path; string_init_set(tmp_path, update_task->update_path); path_append(tmp_path, string_get_cstr(filename)); - bool exists = - (storage_common_stat(update_task->storage, string_get_cstr(tmp_path), NULL) == FSE_OK); + bool exists = storage_file_exists(update_task->storage, string_get_cstr(tmp_path)); string_clear(tmp_path); return exists; } diff --git a/assets/ReadMe.md b/assets/ReadMe.md index 2cd99d56b..2d493b4fe 100644 --- a/assets/ReadMe.md +++ b/assets/ReadMe.md @@ -9,12 +9,6 @@ ./fbt icons proto dolphin_internal dolphin_blocking dolphin_ext resources ``` -# Compiling with Docker-Compose - -```bash -docker-compose exec dev ./fbt icons proto dolphin_internal dolphin_blocking dolphin_ext resources -``` - # Asset naming rules ## Images and Animations diff --git a/assets/icons/BLE/BLE_HID/Ble_connected_15x15.png b/assets/icons/BLE/BLE_HID/Ble_connected_15x15.png index 58776828e..64dab9b53 100644 Binary files a/assets/icons/BLE/BLE_HID/Ble_connected_15x15.png and b/assets/icons/BLE/BLE_HID/Ble_connected_15x15.png differ diff --git a/assets/icons/BLE/BLE_HID/Ble_disconnected_15x15.png b/assets/icons/BLE/BLE_HID/Ble_disconnected_15x15.png index bfc1e7d7f..0858bb93f 100644 Binary files a/assets/icons/BLE/BLE_HID/Ble_disconnected_15x15.png and b/assets/icons/BLE/BLE_HID/Ble_disconnected_15x15.png differ diff --git a/assets/icons/BLE/BLE_HID/Left_mouse_icon_9x9.png b/assets/icons/BLE/BLE_HID/Left_mouse_icon_9x9.png new file mode 100644 index 000000000..c533d8572 Binary files /dev/null and b/assets/icons/BLE/BLE_HID/Left_mouse_icon_9x9.png differ diff --git a/assets/icons/BLE/BLE_HID/Ok_btn_pressed_13x13.png b/assets/icons/BLE/BLE_HID/Ok_btn_pressed_13x13.png new file mode 100644 index 000000000..6b46ba3a8 Binary files /dev/null and b/assets/icons/BLE/BLE_HID/Ok_btn_pressed_13x13.png differ diff --git a/assets/icons/BLE/BLE_HID/Right_mouse_icon_9x9.png b/assets/icons/BLE/BLE_HID/Right_mouse_icon_9x9.png new file mode 100644 index 000000000..446d7176c Binary files /dev/null and b/assets/icons/BLE/BLE_HID/Right_mouse_icon_9x9.png differ diff --git a/assets/icons/Power/Unplug_bg_bottom_128x10.png b/assets/icons/Power/Unplug_bg_bottom_128x10.png new file mode 100644 index 000000000..35d73ba76 Binary files /dev/null and b/assets/icons/Power/Unplug_bg_bottom_128x10.png differ diff --git a/assets/icons/Power/Unplug_bg_top_128x14.png b/assets/icons/Power/Unplug_bg_top_128x14.png new file mode 100644 index 000000000..bafa2c494 Binary files /dev/null and b/assets/icons/Power/Unplug_bg_top_128x14.png differ diff --git a/assets/resources/Manifest b/assets/resources/Manifest index adac70b34..35898f60e 100644 --- a/assets/resources/Manifest +++ b/assets/resources/Manifest @@ -1,5 +1,5 @@ V:0 -T:1660773536 +T:1661280310 D:badusb D:dolphin D:infrared @@ -241,9 +241,10 @@ F:33b8fde22f34ef556b64b77164bc19b0:578:dolphin/L3_Lab_research_128x54/frame_8.bm F:f267f0654781049ca323b11bb4375519:581:dolphin/L3_Lab_research_128x54/frame_9.bm F:41106c0cbc5144f151b2b2d3daaa0527:727:dolphin/L3_Lab_research_128x54/meta.txt D:infrared/assets -F:cedef6d481ec8a10072168bfe82b9ec4:60995:infrared/assets/ac.ir -F:92f0d9326b08834472e04b4748eb19b3:47287:infrared/assets/audio.ir -F:005cd139f858369893c0008f7abbad43:127677:infrared/assets/tv.ir +F:ed61e6f4adc7972bde0e7b5028effac3:101928:infrared/assets/ac.ir +F:229a87b90c1890c5ec6d9e3b0f4695fb:48062:infrared/assets/audio.ir +F:63ca357ca0b85a6ad900539e7b6a0fed:5086:infrared/assets/projectors.ir +F:0370c1333f1e0d8d690b5afbfc623a60:127656:infrared/assets/tv.ir F:a157a80f5a668700403d870c23b9567d:470:music_player/Marble_Machine.fmf D:nfc/assets F:81dc04c7b181f94b644079a71476dff4:4742:nfc/assets/aid.nfc diff --git a/assets/resources/infrared/assets/ac.ir b/assets/resources/infrared/assets/ac.ir index fece63b33..28c8c03e4 100644 --- a/assets/resources/infrared/assets/ac.ir +++ b/assets/resources/infrared/assets/ac.ir @@ -4,7 +4,7 @@ Version: 1 # Universal AC IR codes - Brute force updated by jaroslavmraz # Fixes and tweaks provided by MX (MasterX) # -# BETA version - Aug 14, 2022 +# BETA version - Aug 23, 2022 # # Old SAMSUNG AC # @@ -579,7 +579,333 @@ type: parsed protocol: NECext address: 01 08 00 00 command: 3F 00 00 00 +# +#########################NEW LIST######################## # +# file: Flipper-IRDB/ACs/Admiral/Admiral_AC.ir +# +name: POWER +type: parsed +protocol: NEC +address: 20 00 00 00 +command: 02 00 00 00 +# +name: MODE +type: parsed +protocol: NEC +address: 20 00 00 00 +command: 09 00 00 00 +# +name: TEMP+ +type: parsed +protocol: NEC +address: 20 00 00 00 +command: 0A 00 00 00 +# +name: TEMP- +type: parsed +protocol: NEC +address: 20 00 00 00 +command: 11 00 00 00 +# +# file: Flipper-IRDB/ACs/Airmet/Airmet_ac.ir +# +name: POWER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8917 4491 587 565 534 1665 594 559 540 1659 589 1688 540 534 585 1666 562 1664 564 1661 587 564 535 591 538 1661 567 585 534 1665 563 588 541 559 560 1665 563 563 566 559 540 586 533 1692 536 564 565 534 585 567 532 1667 592 534 565 587 542 531 588 564 535 591 538 561 568 558 540 585 534 565 564 1687 541 1658 560 1667 592 1660 558 1667 561 1664 584 567 542 558 561 564 565 561 538 588 541 558 561 1690 538 1661 567 1659 589 1662 566 1659 559 1667 591 559 540 587 532 541 588 564 535 591 538 561 558 567 542 584 535 565 564 1687 541 532 587 1664 564 1662 566 585 534 1691 537 589 540 +# +name: POWER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8971 4489 568 584 535 564 565 561 537 588 541 558 561 565 534 592 537 562 567 1659 559 566 563 563 536 1664 584 567 542 584 535 1664 564 587 542 558 561 564 535 591 538 562 567 558 541 585 534 566 563 562 537 589 540 559 560 566 533 593 536 563 566 560 539 587 532 567 562 564 535 591 538 1661 567 1658 590 1662 566 1659 559 1667 592 1660 558 567 562 563 536 590 539 561 558 567 542 584 535 1664 564 1662 586 1665 563 1662 566 1659 589 1663 565 560 559 566 533 593 536 564 565 560 538 587 542 557 562 564 535 591 538 1661 567 585 534 1666 562 1663 585 566 533 1667 592 560 538 +# +# file: Flipper-IRDB/ACs/Amcor/Amcor_AC.ir +# +name: POWER +type: parsed +protocol: NEC +address: 80 00 00 00 +command: 9B 00 00 00 +# +# file: Flipper-IRDB/ACs/Ariston/Ariston_AC_A-MW09-IGX.ir +# +name: POWER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4481 4414 595 1596 594 527 562 1602 588 1604 586 535 565 530 570 1594 596 526 563 532 568 1596 594 528 561 534 566 1598 592 1600 590 531 569 1596 594 527 562 1603 587 1604 586 1605 648 1543 594 527 562 1603 587 1604 586 1605 595 525 564 531 569 526 563 532 568 1596 594 528 561 534 566 1598 592 1600 590 1601 589 532 568 527 562 533 567 528 561 534 566 529 560 535 565 530 570 1594 596 1596 594 1597 593 1598 592 1599 591 5252 4503 4418 590 1601 589 532 568 1597 593 1598 592 529 560 535 565 1600 590 531 569 526 563 1602 588 533 567 529 560 1604 596 1595 595 526 563 1602 588 533 567 1598 592 1599 591 1600 590 1601 589 532 568 1598 592 1599 591 1600 590 531 569 527 562 532 568 528 561 1603 587 534 566 530 559 1605 595 1596 594 1597 593 528 561 534 566 529 560 535 565 530 570 525 564 531 569 527 562 1602 588 1603 587 1604 596 1595 595 1596 594 +# +# file: Flipper-IRDB/ACs/Bonaire/Bonaire_DurangoAC.ir +# +name: MODE +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 1303 438 1287 429 418 1254 1308 434 1281 434 423 1250 444 1255 439 1259 1303 439 418 1254 440 1258 446 8172 1313 428 1276 411 446 1252 1310 404 1311 403 444 1255 439 1259 445 1253 1309 405 442 1257 447 1250 444 8173 1302 412 1303 410 447 1251 1300 439 1276 410 447 1251 443 1255 439 1259 1303 437 420 1250 496 1202 439 8177 1308 434 1281 433 414 1256 1306 435 1280 434 413 1284 420 1251 443 1255 1307 433 414 1283 421 1250 444 8173 1302 438 1277 436 421 1276 1275 438 1277 436 421 1275 419 1252 442 1256 1306 434 413 1284 420 1250 444 8173 1302 438 1277 436 421 1276 1275 437 1278 435 412 1285 419 1278 416 1281 1281 432 415 1282 412 1285 419 +# +# file: Flipper-IRDB/ACs/Botti/Botti_BL-168DLR.ir +# +name: POWER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9577 4536 622 576 621 577 620 577 620 578 619 578 619 578 619 579 618 1667 625 1660 622 1664 618 1668 624 1662 620 1666 616 1670 622 1664 618 580 617 581 616 1669 623 575 622 575 622 1664 618 580 617 580 617 581 616 1669 623 575 622 1664 618 1668 624 573 624 1662 620 1666 616 1670 622 39798 9556 2250 626 96841 9564 2245 620 96843 9560 2253 622 96836 9569 2244 621 96844 9571 2244 621 96843 9573 2247 619 96844 9572 2248 617 96847 9570 2248 617 96848 9568 2250 626 96833 9564 2251 625 96834 9564 2252 624 96843 9595 2223 653 96809 9597 2221 644 96822 9604 2215 650 96814 9591 2226 650 96813 9591 2225 650 96811 9600 2218 647 96816 9597 2221 654 96812 9601 2219 646 96815 9598 2220 645 96813 9601 2216 649 96815 9599 2219 657 96809 9565 2252 624 96842 9564 2254 622 96841 9565 2254 622 96838 9568 2249 616 96844 9571 2247 619 96842 9574 2245 620 96840 9566 2251 625 96838 9567 2251 625 96835 9570 2250 615 96848 9566 2254 621 96843 9572 2247 618 96847 9567 2253 623 96842 9572 2249 616 96843 9572 2247 618 96843 9572 2249 616 96849 9576 2245 621 96842 9574 2246 619 96842 9575 2246 619 96843 9573 2248 617 96843 9572 2249 616 96847 9571 2249 617 96839 9569 2251 625 96837 9569 2251 624 96837 9568 2250 626 96835 9572 2248 618 96844 9572 2246 619 96843 9564 2253 623 96834 9572 2245 620 96835 9569 2246 619 +# +# file: Flipper-IRDB/ACs/Corlitec/Cortlitec_portable_ac.ir +# +name: POWER +type: parsed +protocol: NECext +address: 10 E7 00 00 +command: 05 FA 00 00 +# +# file: Flipper-IRDB/ACs/Daikin/Daikin_AC_industrial_TB.ir +# +name: POWER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 5055 2191 335 1799 360 752 338 714 365 716 364 1800 359 723 367 715 365 718 361 720 359 1805 365 747 332 1802 357 1806 364 719 360 1803 367 1798 361 1803 367 1797 362 1802 368 744 335 1799 360 721 369 714 365 716 363 719 360 721 369 713 366 1798 361 1802 368 715 364 717 362 720 359 723 367 715 364 1799 360 722 368 714 365 717 363 719 361 722 368 714 365 716 363 719 360 721 369 713 366 716 363 718 361 721 359 723 367 1797 362 1802 368 1796 363 1801 358 754 336 716 363 719 361 29583 5057 2160 366 1798 361 750 329 723 367 715 364 1800 359 753 337 715 364 717 363 720 359 1804 366 747 332 1801 358 1806 364 718 361 1803 356 1798 1802 368 1797 362 1802 368 714 365 1799 360 722 368 714 365 717 362 719 360 722 368 714 365 1798 361 1803 367 715 364 718 362 721 359 723 367 715 364 718 361 720 359 723 367 715 364 717 362 720 360 1804 366 1799 360 721 369 714 365 1798 361 1803 367 1798 361 720 359 723 367 715 364 718 361 720 360 723 367 715 364 717 362 720 359 722 368 714 365 717 362 719 360 722 368 1796 363 719 360 721 369 714 365 716 363 719 361 721 369 713 366 716 363 718 361 721 358 723 367 716 363 718 362 720 359 723 367 715 364 718 361 720 359 723 367 715 364 1799 360 1804 366 1799 360 721 369 714 365 716 363 1801 358 754 336 1798 361 720 360 1805 365 748 331 720 359 723 367 715 364 717 363 720 360 722 368 714 365 717 362 720 359 722 368 714 365 717 363 719 361 722 368 714 365 1798 361 751 339 713 366 716 363 1801 359 1805 365 1800 359 1805 365 1800 359 1805 365 1799 360 +# +# file: Flipper-IRDB/ACs/DeLonghi/Delonghi_portable_Pinguino-Air-to-Air-PAC-N81_ac.ir +# +name: POWER +type: parsed +protocol: NECext +address: 48 12 00 00 +command: 88 08 00 00 +# +# file: Flipper-IRDB/ACs/Friedrich/Friedrich.ir +# +name: POWER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 5624 5582 567 553 568 550 571 550 561 557 564 584 537 554 567 1672 571 1671 562 554 567 553 568 1696 537 1676 567 1673 570 1668 565 554 567 556 565 1671 562 557 564 1676 567 1671 562 584 537 555 566 1673 570 552 569 1666 567 1671 562 1676 567 1671 572 548 563 556 565 1672 571 554 567 547 564 556 565 1674 569 549 562 558 563 1676 567 551 570 554 568 548 563 557 564 555 566 554 567 1672 571 1667 566 1674 569 1673 570 545 566 554 567 1671 572 1667 566 554 567 1672 561 557 564 1677 566 +# +# file: Flipper-IRDB/ACs/Frigidaire/Frigidaire_AC.ir +# +name: POWER +type: parsed +protocol: NECext +address: 01 FF 00 00 +command: 0A F5 00 00 +# +# file: Flipper-IRDB/ACs/Fujitsu/Fujitsu_AC.ir +# +name: POWER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3302 1618 435 393 436 391 438 1207 431 396 433 1213 435 392 437 391 438 388 441 1206 432 1214 434 392 437 389 440 388 431 1215 433 1213 435 392 437 390 439 388 431 396 433 395 434 392 437 390 439 388 431 396 433 394 435 392 437 390 439 388 441 1205 433 395 434 392 437 389 440 388 431 396 433 394 435 392 437 1209 439 388 431 397 432 394 435 393 436 1210 438 387 432 396 433 394 435 392 437 390 439 388 431 1215 433 394 435 1211 437 1208 440 1205 433 1213 435 1211 437 1209 439 +# +# file: Flipper-IRDB/ACs/Hisense/Hisense_window_AC.ir +# +name: POWER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8961 4409 563 541 570 533 568 535 566 537 564 539 562 1649 564 1648 565 539 562 541 570 533 568 536 565 1645 568 1644 569 1643 570 534 567 536 565 1646 567 1645 568 536 565 565 536 1648 565 540 572 558 543 560 541 562 539 564 537 566 546 558 543 560 541 563 538 565 536 567 544 559 542 562 539 564 537 566 546 558 543 561 540 563 538 565 536 568 544 560 541 562 539 564 537 566 546 558 543 561 540 563 538 565 536 568 544 560 541 562 539 564 537 567 545 559 542 561 540 563 538 566 545 558 543 561 540 563 538 565 536 568 544 560 541 562 539 565 536 567 544 559 542 561 540 564 537 566 545 558 543 561 540 563 538 565 536 568 543 560 541 562 539 564 537 566 545 532 569 534 567 537 564 539 562 541 571 560 541 536 565 538 563 540 572 532 569 535 566 537 564 539 573 531 570 534 567 536 565 539 562 541 571 533 568 535 566 538 563 540 572 532 569 535 566 537 564 540 572 532 569 534 567 537 564 539 573 532 569 534 567 536 565 539 562 541 571 533 568 536 565 538 563 541 571 533 568 535 566 538 563 540 572 532 569 535 566 537 564 540 572 532 569 534 567 537 564 539 562 542 570 534 567 536 565 539 562 541 570 534 567 536 565 539 562 542 570 534 567 536 565 539 562 542 570 534 567 536 565 539 562 542 570 534 567 536 565 539 562 542 570 1641 572 1640 563 543 569 535 566 538 563 540 561 543 569 535 566 538 563 540 561 543 569 535 566 538 563 541 571 533 568 536 565 539 562 541 571 534 567 537 564 539 562 542 570 534 567 537 564 1646 567 1646 567 539 562 542 570 534 567 537 564 540 561 542 590 +# +# file: Flipper-IRDB/ACs/Kenmore/Kenmore_AC.ir +# +name: TEMP- +type: parsed +protocol: NECext +address: 08 F5 00 00 +command: 0D F2 00 00 +# +# file: Flipper-IRDB/ACs/LG/LG_AC_2.ir +# +name: POWER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8455 4196 542 1566 541 539 519 535 513 541 517 1565 541 538 520 534 514 540 518 1563 544 1565 542 538 520 533 515 539 519 535 513 541 517 536 522 532 516 538 520 533 515 539 519 535 513 1569 538 542 516 1566 541 539 519 534 514 540 518 1564 542 +# +# file: Flipper-IRDB/ACs/LG/LG_AC_LP1417GSR.ir +# +name: POWER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8912 4459 593 1636 593 1640 599 525 600 528 597 533 602 532 593 544 601 1637 592 523 591 1639 600 1636 593 535 600 529 596 538 597 541 594 534 601 1629 600 1634 595 531 1719 1649 532 593 541 594 543 602 526 599 519 595 1640 599 525 600 529 596 1649 601 534 601 537 598 529 596 522 603 519 595 529 596 533 602 528 597 538 597 540 595 532 593 523 602 518 596 527 598 528 597 535 600 533 592 545 601 509 595 7882 598 519 595 524 601 521 593 535 600 530 595 538 597 1650 600 1637 602 514 600 517 597 526 599 527 598 532 593 541 594 544 601 525 600 515 599 519 595 527 598 529 596 534 601 532 593 545 600 526 599 519 595 551 574 524 601 526 599 533 602 532 593 545 600 526 599 517 597 523 602 522 603 525 600 532 593 541 594 543 602 525 600 516 598 522 592 530 595 533 602 529 596 539 596 541 594 532 593 524 601 518 596 527 598 528 597 533 602 531 594 543 592 533 602 1625 593 526 599 523 602 1636 603 1639 600 533 592 1657 593 1628 601 7882 597 521 593 527 598 526 599 529 596 535 600 535 600 538 597 531 594 1634 595 1641 598 527 598 531 594 535 600 532 593 542 593 535 600 518 596 524 601 521 593 533 602 528 597 537 598 539 596 528 597 520 594 525 600 521 593 533 602 528 597 536 599 537 598 527 598 517 597 522 603 521 593 1644 595 1646 593 1651 599 537 598 526 599 516 598 520 594 527 598 528 597 532 593 539 596 540 595 531 594 1638 601 1633 596 528 597 1645 594 1652 598 1649 601 535 600 509 595 +# +# file: Flipper-IRDB/ACs/LG/LP1015WNR.ir +# +name: POWER +type: parsed +protocol: NECext +address: 10 E7 00 00 +command: 06 F9 00 00 +# +# file: Flipper-IRDB/ACs/Midea/Midea_AC_MAW05R1WBL.ir +# +name: POWER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4454 4377 594 1560 591 486 589 1565 597 480 595 482 593 484 591 485 590 1564 598 1557 594 1560 591 1563 588 489 597 480 595 482 593 483 592 485 590 487 588 1565 597 1558 593 1562 589 487 588 489 597 1558 593 1561 590 1564 598 1557 594 1560 591 1563 588 1567 595 1560 591 1563 588 1566 596 1559 592 1562 589 1565 597 1558 593 1562 589 1565 597 1557 594 1561 590 486 590 488 598 479 596 1558 593 484 591 1563 588 489 597 1557 594 5161 4459 4371 601 476 589 1565 597 481 594 1560 592 1562 589 1565 597 1558 593 484 591 485 590 487 589 488 598 1556 595 1559 592 1563 588 1566 596 1559 592 1562 589 488 587 489 597 480 595 1559 592 1562 589 488 598 479 596 480 595 482 593 484 591 485 601 476 589 488 598 480 595 481 594 483 592 484 591 486 590 487 588 489 597 480 595 481 594 483 592 1562 589 1565 597 1558 593 483 592 1562 589 488 587 1567 595 482 593 +# +name: POWER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3439 1755 439 1262 435 1286 432 438 441 423 456 418 441 1282 436 433 436 431 438 1285 433 1314 435 408 461 1293 435 408 440 426 433 1289 460 1296 412 455 414 1312 406 1319 409 429 440 429 461 1267 441 426 464 406 442 1281 437 430 439 427 432 436 464 405 433 432 437 458 411 456 434 422 437 428 462 412 436 428 441 422 457 411 437 456 413 454 405 436 433 434 435 427 432 435 465 404 434 435 434 431 459 413 435 429 440 422 437 433 457 1270 438 1288 461 410 438 426 464 404 434 1293 435 1315 413 423 436 1287 462 407 431 434 435 435 434 433 467 415 433 1318 410 1286 463 409 439 1284 455 1301 438 410 438 426 464 1262 456 1269 439 429 461 410 469 407 462 1268 440 1285 433 461 408 456 413 1284 465 1261 457 414 455 1271 468 1277 431 1321 438 405 433 460 409 430 439 426 464 407 462 407 441 450 409 434 435 430 439 452 438 431 438 405 464 405 464 405 464 409 460 409 439 430 439 427 442 423 436 460 430 415 433 431 438 425 434 444 435 456 413 432 437 427 442 425 434 436 464 429 430 439 430 417 442 428 441 428 462 411 437 452 438 405 433 434 435 456 413 427 442 425 434 455 414 427 432 435 434 433 436 427 463 406 432 437 442 449 430 1268 460 1265 463 1262 466 405 464 406 442 422 437 1289 439 428 441 17052 3577 1746 407 1321 407 1294 434 460 409 431 459 439 409 1288 440 429 440 431 438 1309 430 1272 456 415 464 1289 408 433 457 412 436 1289 439 1284 465 404 434 1292 436 1289 439 428 441 450 409 1315 413 427 442 451 408 1294 434 432 437 430 439 428 462 406 432 432 458 414 455 414 434 430 439 428 441 426 433 436 433 432 437 454 405 438 441 428 431 434 456 418 441 448 431 411 437 429 440 425 434 433 436 458 411 425 434 433 436 457 412 1287 462 1263 455 419 461 408 440 430 439 1310 429 1272 435 460 409 1288 440 429 461 408 441 425 465 405 464 407 441 1286 432 1291 437 432 437 1315 413 1314 414 450 440 406 442 1283 456 1270 458 413 456 413 456 413 435 1297 431 1294 465 404 434 433 436 1314 414 1285 433 433 436 1288 440 1285 464 1264 433 433 436 453 416 425 434 460 409 434 435 432 437 454 415 423 436 431 459 424 435 430 460 413 435 430 439 423 456 413 435 431 438 455 414 427 432 435 434 455 414 426 464 406 432 437 442 422 457 414 434 431 438 424 435 435 455 414 455 416 432 433 436 457 412 426 464 410 438 453 437 412 436 437 463 409 439 425 434 455 414 426 433 437 432 433 436 433 436 457 433 412 436 453 437 406 432 435 465 1266 462 1263 434 1318 410 426 464 410 438 426 433 1293 435 434 435 +# +# file: Flipper-IRDB/ACs/Mitsubishi/Mitsubishi_SRK35ZS-W.ir +# +name: POWER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3199 1594 380 414 380 1208 379 415 389 405 389 1201 386 407 386 1202 385 409 384 408 385 1203 384 1203 384 1204 383 410 383 1206 381 412 382 1208 379 1207 380 1209 389 404 390 406 387 405 389 406 387 1201 386 1202 385 408 385 1204 383 409 384 1204 383 1205 382 412 382 411 382 413 380 1206 382 414 380 1208 379 414 379 416 388 1201 386 1201 386 1201 386 408 385 1202 385 1204 383 1204 383 1206 381 1205 382 1207 380 1207 380 1208 390 405 388 405 389 406 387 406 387 406 387 407 386 408 385 1202 385 1204 383 1203 384 411 382 1205 382 1205 382 1207 380 1207 381 414 390 404 379 414 380 1210 388 406 387 408 385 406 387 408 385 1202 385 1203 384 409 384 1205 382 1205 382 1206 381 1206 381 1208 379 415 389 404 389 1199 388 407 386 406 387 407 386 408 385 409 384 1202 385 1204 383 1204 383 1204 383 1205 382 412 381 1208 379 1208 379 415 389 404 390 406 387 406 387 407 386 1200 387 409 384 408 385 1202 385 1205 383 410 383 1205 382 1205 382 412 381 1207 380 1207 380 415 389 404 379 1209 389 406 387 405 388 1200 387 408 385 408 385 1202 385 1204 383 1204 383 1204 383 1208 379 1207 380 1207 380 1208 379 416 388 405 388 407 386 407 386 407 386 407 386 407 386 408 385 408 385 1204 383 1204 383 1205 382 1205 382 1207 380 1208 379 414 379 1210 388 406 387 406 387 406 387 407 386 407 386 409 384 1203 384 +# +name: TEMP+ +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3198 1595 389 404 379 1209 389 405 388 406 387 1200 387 407 386 1202 385 409 384 410 383 1203 384 1204 383 1179 408 413 380 1207 380 414 379 1209 389 1172 415 1200 387 406 387 409 384 408 385 409 384 1204 383 1205 382 410 383 1206 381 412 381 1206 381 1183 415 404 389 405 388 406 387 1200 387 406 387 1202 385 408 385 409 384 1204 383 1204 383 1205 382 412 381 1206 381 1181 406 414 390 1199 388 1173 414 1175 412 1175 412 1177 410 409 384 409 384 1205 382 410 383 411 382 412 381 412 382 412 381 1207 380 1209 389 405 389 1199 388 1174 413 1175 412 1175 412 1177 410 410 383 409 384 1205 382 412 381 412 381 413 380 413 380 1208 379 1207 380 414 379 1209 389 1173 414 1176 411 1177 410 1178 409 409 384 410 383 1206 381 411 382 412 381 412 382 413 381 413 380 1208 379 1208 379 1183 415 1173 414 1175 412 407 386 1203 384 1177 410 410 383 412 381 412 381 412 381 412 381 1207 380 414 379 414 379 1208 390 1199 388 405 388 1199 388 1200 387 409 384 1202 385 1177 410 410 383 412 381 1206 381 411 382 413 380 1207 380 413 380 414 380 1208 390 1199 388 1175 412 1175 412 1176 411 1179 408 1178 409 1179 408 412 381 413 380 414 379 414 379 415 389 404 379 414 379 416 388 405 389 1201 386 1200 387 1201 386 1177 410 1177 410 1179 408 411 382 1208 379 413 380 413 381 413 380 415 389 404 389 405 389 1199 388 +# +name: TEMP- +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3200 1592 382 412 381 1206 381 414 379 414 379 1208 379 416 388 1200 387 406 387 407 386 1202 385 1176 411 1178 409 410 383 1179 408 411 382 1182 416 1172 415 1172 415 404 389 406 387 407 386 408 385 1201 386 1177 410 409 384 1179 408 412 381 1179 408 1181 406 413 380 413 380 414 379 1209 389 405 388 1199 388 406 387 408 385 1202 385 1179 408 1178 409 412 381 1180 407 1182 405 414 379 1182 416 1174 413 1174 413 1176 411 1176 411 410 383 410 383 1204 383 412 381 412 381 412 381 413 380 1209 389 1172 415 1174 413 406 387 1174 413 1177 410 1177 410 1179 408 410 383 412 381 413 380 1206 381 413 380 414 379 414 379 415 389 1199 388 1175 412 407 386 1177 410 1178 409 1178 409 1179 408 1180 407 413 380 414 379 1208 379 415 389 405 388 407 386 406 387 407 386 1201 386 1177 410 1178 409 1179 408 1180 407 412 381 1180 407 1182 405 416 388 405 388 406 387 407 386 407 386 1201 386 409 384 409 384 1204 383 1179 408 411 382 1180 407 1182 405 416 388 1172 415 1173 414 406 387 407 386 1201 386 409 384 410 383 1203 384 410 383 412 381 1206 381 1183 415 1173 414 1173 414 1174 413 1175 412 1178 409 1178 409 410 383 411 382 411 382 412 381 412 381 413 380 413 380 416 388 403 380 1208 379 1184 414 1175 412 1175 412 1176 411 1177 410 411 382 1178 409 411 382 412 381 413 380 413 380 413 380 413 380 1208 379 +# +name: MODE +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3197 1595 390 404 379 1210 388 406 388 406 388 1201 386 407 386 1202 386 408 385 409 384 1205 383 1204 383 1206 382 412 381 1206 381 413 381 1207 381 1208 380 1209 389 405 389 406 387 405 389 407 387 1200 388 1201 386 408 385 1203 384 412 382 1205 383 1204 384 412 382 412 382 412 382 1207 381 413 381 1208 380 414 380 414 380 1209 389 1199 389 1201 386 1200 387 1202 385 408 385 409 384 1204 384 1205 383 1205 382 1205 383 412 382 413 380 1207 381 1209 389 406 388 405 389 405 389 405 389 1200 387 1203 384 1201 386 409 384 1203 384 1205 382 1205 383 1205 383 411 383 412 382 412 382 1207 380 414 380 415 389 404 379 417 387 1199 389 1199 389 1201 386 1204 384 1203 384 1203 384 1203 384 1204 384 411 382 412 382 412 382 412 382 413 380 413 381 414 380 414 380 1209 389 1199 389 1199 389 1200 387 1201 386 408 385 1203 385 1203 384 409 384 412 381 410 383 413 381 411 383 1206 382 413 381 413 380 1207 381 1209 389 404 379 1209 389 1199 389 407 386 1201 386 1202 385 408 385 410 384 1203 384 410 383 411 383 1204 384 412 382 412 382 1206 381 1207 380 1208 379 1211 387 1199 389 1201 386 1201 386 1202 385 408 385 410 383 409 385 409 385 410 384 410 384 410 383 411 383 412 382 1207 381 1206 382 1209 389 1197 380 1210 388 1199 389 406 387 1200 387 408 385 407 386 408 385 409 384 408 385 409 384 1204 384 +# +# file: Flipper-IRDB/ACs/Panasonic/Panasonic_CWA75C4179.ir +# +name: POWER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3472 1744 416 457 415 1306 418 463 419 466 416 473 419 474 418 478 414 457 415 458 414 463 419 462 420 465 417 472 420 1316 418 479 413 458 414 459 413 464 418 463 419 466 416 473 419 1318 416 1324 420 1295 418 454 418 459 413 1312 422 463 419 470 422 471 421 475 417 454 418 455 417 460 422 459 413 472 420 469 413 480 412 485 417 453 419 454 418 459 413 468 414 471 421 468 414 479 413 484 418 452 420 453 419 458 414 468 414 471 421 467 415 478 414 483 419 451 421 453 419 1301 412 1312 422 463 419 470 422 471 421 476 416 433 418 10535 3469 1746 414 459 413 1308 415 465 417 468 414 475 417 476 416 481 421 449 413 461 421 455 417 465 417 467 415 474 418 1319 415 482 420 450 422 451 421 457 415 466 416 469 413 476 416 1320 414 1327 417 1297 416 457 415 462 420 1305 419 466 416 473 419 474 418 478 414 457 415 458 414 463 419 462 420 465 417 472 420 473 419 477 415 456 416 1301 412 464 418 463 419 1310 413 1319 415 1321 413 485 417 453 419 454 418 459 413 1312 422 1307 416 472 420 1317 416 480 412 459 413 460 412 465 417 464 418 467 415 474 418 475 417 479 413 1302 422 1295 418 1302 421 1303 421 1308 415 473 419 1318 416 481 421 1293 420 1296 417 460 412 1313 421 1307 416 473 419 473 419 478 414 457 415 458 414 463 419 462 420 465 417 472 420 473 419 477 415 456 416 457 415 1306 418 1307 416 1312 422 467 415 478 414 483 419 451 421 452 420 457 415 467 415 469 413 476 416 1321 413 1328 416 1298 415 458 414 463 419 462 420 465 417 472 420 472 420 477 415 456 416 457 415 462 420 461 421 464 418 470 422 471 421 476 416 454 418 1299 414 463 419 461 421 464 418 471 421 472 420 477 415 1299 414 459 413 464 418 463 419 466 416 473 419 473 419 478 414 457 415 458 414 463 419 462 420 465 417 472 420 473 419 477 415 456 416 457 415 1306 417 1307 416 468 414 1319 415 478 414 483 419 430 421 +# +name: POWER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3473 1716 444 455 417 1304 420 461 421 464 418 471 421 471 421 476 416 455 417 456 416 461 421 460 412 473 419 469 413 1324 420 477 415 455 417 457 415 462 420 461 421 463 419 470 422 1315 419 1321 413 1302 422 451 421 456 416 1309 414 470 422 467 415 478 414 482 420 451 421 452 420 457 415 466 416 469 413 476 416 477 415 481 421 450 412 461 421 456 416 465 417 468 414 474 418 475 417 480 412 458 414 459 413 464 418 463 419 466 416 473 419 474 418 478 414 457 415 458 414 1307 417 1308 416 469 413 476 416 477 415 481 421 428 413 10540 3465 1751 419 454 418 1302 422 459 413 472 420 469 413 480 412 485 417 453 419 454 418 459 413 468 414 471 421 468 414 1322 422 475 417 454 418 455 417 460 412 469 413 472 420 468 414 1323 421 1320 414 1300 413 460 412 465 417 1307 417 468 414 475 417 476 416 481 421 449 413 460 422 455 417 464 418 467 415 474 418 474 418 479 413 458 414 459 413 464 418 463 419 1309 415 1318 416 1321 413 484 418 452 420 453 419 458 414 1311 413 1316 418 471 421 1315 419 478 414 457 415 458 414 463 419 462 420 465 417 472 420 472 420 477 415 1299 414 1302 422 1299 414 1310 414 1315 419 470 422 1315 419 478 414 1300 413 1303 421 456 416 1309 415 1314 420 469 413 480 412 485 417 453 419 454 418 459 413 468 414 471 421 468 414 479 413 484 418 452 420 453 419 1302 422 1303 421 1307 417 473 419 473 419 478 414 457 415 458 414 463 419 462 420 465 417 471 421 1316 418 1322 422 1293 420 452 420 457 415 466 416 469 413 476 416 477 415 482 420 450 422 451 421 456 416 465 417 468 414 475 417 476 416 480 412 459 413 1304 420 457 415 466 416 469 413 476 416 477 415 481 421 1293 420 453 419 458 414 467 415 470 412 477 415 477 415 482 420 450 422 451 421 456 416 465 417 468 414 475 417 476 416 481 421 449 413 1304 420 457 415 1310 414 471 421 1311 413 481 421 475 417 432 419 +# +name: TEMP+ +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3466 1750 420 453 419 1302 422 459 413 472 420 469 413 480 412 485 417 453 419 455 417 460 412 469 413 472 420 469 413 1324 420 476 416 455 417 456 416 461 421 460 422 463 419 470 422 1315 419 1322 412 1302 422 452 420 457 415 1310 414 471 421 468 414 479 413 484 418 452 420 453 419 458 414 468 414 471 421 467 415 478 414 483 419 452 420 453 419 458 414 467 415 470 412 477 415 478 414 483 419 451 421 452 420 457 415 466 416 469 413 476 416 477 415 482 420 450 422 451 421 1300 413 1312 412 473 419 470 412 481 421 475 417 432 419 10572 3473 1743 417 456 416 1305 419 462 420 465 417 472 420 473 419 477 415 456 416 457 415 463 419 461 421 464 418 471 421 1316 418 479 413 457 415 459 413 464 418 463 419 466 416 473 419 1317 417 1324 420 1294 419 454 418 459 413 1312 422 463 419 470 422 471 421 476 416 454 418 455 417 461 421 460 412 473 419 469 413 480 422 475 417 453 419 1298 415 462 420 461 421 1308 415 1317 417 1320 414 483 419 451 421 452 420 1301 412 1312 422 1307 417 473 419 1317 417 480 422 449 412 1304 419 1301 412 1312 422 1307 416 1316 418 1319 415 1326 418 453 419 1298 415 462 420 461 421 464 418 1314 420 1317 417 480 422 449 412 460 422 1299 414 467 415 1313 421 468 414 480 412 484 418 453 419 454 418 459 413 468 414 471 421 468 414 479 413 484 418 452 420 453 419 1302 422 1303 421 1308 415 474 418 474 418 479 413 458 414 459 413 464 418 463 419 466 416 473 419 1318 416 1324 420 1295 418 454 418 459 413 468 414 471 421 468 414 479 413 484 418 452 420 454 418 459 413 468 414 471 421 467 415 478 414 483 419 452 420 1297 416 460 422 459 413 472 420 469 413 480 412 484 418 1297 416 456 416 461 421 460 422 463 419 470 422 471 421 476 416 454 418 455 417 460 422 459 413 472 420 469 413 480 412 485 417 453 419 454 418 1303 421 1304 420 466 416 1316 418 476 416 480 422 1271 412 +# +name: TEMP- +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3474 1742 417 456 416 1305 418 462 420 465 417 472 420 473 419 478 414 456 416 458 414 463 419 462 420 465 417 472 420 1316 418 479 413 458 414 459 413 464 418 463 419 466 416 474 418 1318 416 1325 419 1296 417 455 417 460 422 1303 421 464 418 471 421 472 420 477 415 456 416 457 415 462 420 461 421 464 418 471 421 472 420 477 415 455 417 456 416 462 420 461 421 464 418 471 421 471 421 476 416 455 417 456 416 461 421 460 422 463 419 470 422 471 421 476 416 454 418 456 416 1305 419 1306 417 467 415 474 418 475 417 480 412 437 414 10576 3468 1747 413 461 421 1299 414 467 415 470 422 467 415 478 414 483 419 451 421 452 420 458 414 467 415 470 412 477 415 1321 413 485 417 453 419 454 418 459 413 469 413 472 420 469 413 1323 421 1320 414 1301 412 460 422 456 416 1309 414 470 422 467 415 478 414 483 419 451 421 452 420 457 415 467 415 470 412 477 415 478 414 482 420 451 421 1296 417 459 413 468 414 1315 419 1314 420 1317 417 480 422 449 412 460 422 455 417 1308 415 1313 421 469 413 1323 421 476 416 455 417 1300 413 1308 415 1309 414 1314 420 1313 421 1316 418 1323 421 450 422 1295 418 458 414 468 414 471 421 1311 413 1324 420 477 415 456 415 457 415 1306 417 464 418 1311 412 476 416 477 415 482 420 450 422 451 421 456 416 466 416 469 413 476 416 477 415 481 421 450 422 451 421 1300 413 1311 412 1316 418 472 420 473 419 477 415 456 416 457 415 463 419 462 420 465 417 472 420 1316 418 1323 421 1294 419 453 419 458 414 468 414 471 421 467 415 478 414 483 419 452 420 453 419 458 414 467 415 471 421 467 415 478 414 483 419 452 420 1297 416 460 422 459 413 472 420 469 413 480 412 485 417 1297 416 457 415 462 420 461 421 464 418 471 421 472 420 477 415 455 417 457 415 462 420 461 421 464 418 471 421 471 421 476 416 455 417 456 416 461 421 1304 419 465 417 1316 418 476 416 480 422 1271 422 +# +name: MODE +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3467 1749 420 452 420 1301 412 468 414 472 420 468 414 479 413 484 418 453 419 454 418 459 413 469 413 472 420 469 413 1323 421 476 416 455 417 457 415 462 420 461 421 464 418 471 421 1315 419 1322 422 1293 420 452 420 458 414 1311 412 472 420 469 413 480 412 485 417 453 419 455 417 460 422 459 413 472 420 469 413 480 412 485 417 453 419 455 417 460 412 469 413 472 420 469 413 480 412 485 417 453 419 455 417 460 422 459 413 472 420 469 413 480 412 485 417 453 419 455 417 1304 419 1305 418 466 416 473 419 474 418 479 413 436 415 10538 3465 1751 419 454 418 1303 420 461 421 464 418 471 421 472 420 477 415 455 417 457 415 462 420 461 421 464 418 471 421 1315 418 479 413 457 415 459 412 464 418 463 419 466 416 473 419 1318 415 1325 419 1295 418 455 416 461 421 1303 420 465 417 472 420 473 419 478 414 456 416 458 414 463 419 462 420 465 417 472 420 473 419 478 414 456 416 1301 412 465 417 464 418 1311 412 476 416 477 415 482 420 451 421 452 420 457 415 1310 413 471 421 468 414 1323 421 476 416 455 417 456 416 462 420 461 421 464 418 471 421 471 421 476 416 1299 414 1302 421 1299 414 1311 412 1316 417 471 421 1316 417 479 413 1302 421 1295 418 459 412 1312 421 1307 416 472 420 473 419 478 414 457 415 458 414 464 418 463 419 466 416 473 419 473 419 478 414 457 415 458 414 1307 416 1309 414 1314 419 469 413 480 422 475 417 454 418 455 417 460 422 459 413 473 419 469 413 1324 420 1321 412 1302 421 452 420 457 415 466 416 469 413 476 416 477 415 482 420 451 421 452 420 457 414 467 415 470 412 477 415 478 414 483 419 451 421 1296 417 460 412 469 413 472 420 469 413 480 412 485 417 1297 416 457 415 462 420 461 421 464 418 471 421 472 420 477 415 456 416 457 414 462 420 461 421 464 418 471 421 472 420 477 415 456 416 457 414 1306 417 1307 416 1312 421 1311 412 481 421 1319 414 1279 414 +# +# file: Flipper-IRDB/ACs/Remko/Remko_RKL.ir +# +name: POWER +type: parsed +protocol: NECext +address: 86 6B 00 00 +command: 12 ED 00 00 +# +name: TEMP+ +type: parsed +protocol: NECext +address: 86 6B 00 00 +command: 1A E5 00 00 +# +name: TEMP- +type: parsed +protocol: NECext +address: 86 6B 00 00 +command: 1E E1 00 00 +# +name: MODE +type: parsed +protocol: NECext +address: 86 6B 00 00 +command: 02 FD 00 00 +# +# file: Flipper-IRDB/ACs/Rinnai/Rinnai_AC.ir +# +name: POWER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9026 4519 590 1654 616 1657 623 574 561 582 563 582 563 583 562 1688 592 1656 675 1627 593 1650 620 1654 677 521 563 1660 620 1656 614 1664 616 581 564 578 567 575 560 582 563 581 564 579 566 1684 596 1655 615 1661 619 577 568 574 561 581 564 580 565 579 566 580 565 582 563 581 564 577 568 574 561 582 563 580 565 579 566 1657 613 1664 616 582 563 578 567 575 560 583 562 581 564 580 565 582 563 584 561 582 563 578 567 575 560 583 562 581 564 580 565 1659 621 579 566 578 567 574 561 581 564 579 566 577 568 576 569 577 568 579 566 578 567 574 561 580 565 578 567 576 569 575 560 587 568 579 566 577 568 573 562 580 565 578 567 576 569 575 560 586 569 552 593 577 568 573 562 580 565 577 568 576 559 585 560 586 569 552 593 576 569 1703 567 575 560 1713 567 577 568 576 569 577 568 553 592 578 567 1704 566 1707 563 1656 614 1662 618 1659 621 577 568 553 592 1706 564 +# +# file: Flipper-IRDB/ACs/Samsung/Samsung_AC_AR12K.ir +# +name: POWER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 270 18152 3021 8955 523 499 495 1497 492 504 500 468 526 496 498 498 496 499 495 501 493 502 492 1499 500 496 498 524 470 1521 498 497 497 499 495 1496 492 1499 500 1491 497 1494 494 1497 502 494 500 495 499 497 497 498 496 500 494 528 466 530 464 531 473 522 493 503 491 504 500 495 499 497 497 498 496 500 494 501 493 503 491 504 500 495 499 496 498 498 496 499 495 501 493 529 465 531 473 522 472 523 492 504 490 505 499 496 498 498 496 499 495 1496 492 1499 500 1492 496 1494 525 2947 2999 8953 525 1519 469 499 516 507 497 498 496 500 494 501 493 503 491 504 500 495 499 1492 496 499 495 501 493 1498 501 495 499 1492 496 1522 466 1524 496 1496 492 1499 500 1492 496 499 495 500 494 502 492 504 500 495 499 496 498 498 496 499 495 500 494 502 492 530 474 521 473 523 523 472 522 474 489 506 498 497 497 499 495 500 494 502 492 503 501 494 500 496 498 497 497 499 495 500 494 502 492 503 501 521 473 523 471 524 522 474 520 475 498 497 497 499 495 500 494 2978 2999 8952 525 1492 496 499 495 501 493 503 491 504 500 495 499 497 497 525 469 526 468 1524 516 479 494 501 493 503 491 504 500 495 499 497 497 1494 494 1497 492 1500 499 1492 496 499 495 1497 491 531 473 1518 522 1469 499 496 498 498 496 500 494 1497 491 1500 499 1492 496 499 495 501 493 502 492 504 500 495 499 523 471 525 469 526 520 1472 516 1475 493 502 492 504 500 495 499 1492 496 499 495 501 493 502 492 504 500 495 499 496 498 498 496 1522 466 1525 525 1466 491 1500 499 +# +name: MODE +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 552 17959 3004 8946 521 530 474 1490 519 477 517 505 499 497 497 498 496 500 494 501 493 503 501 1464 524 497 497 499 495 1470 518 504 500 495 499 1493 495 1496 492 1499 520 1472 516 1475 524 498 496 500 494 501 493 503 501 494 500 496 498 497 497 499 495 501 493 502 492 531 473 522 472 524 470 525 490 506 498 497 497 499 495 501 493 502 492 504 500 495 499 497 497 498 496 500 494 501 493 503 501 494 500 496 498 524 470 526 468 527 467 529 496 1469 519 1472 527 1465 523 1468 520 2979 2997 8954 523 1469 519 502 492 504 500 495 499 524 470 525 469 527 467 528 497 499 495 1470 518 503 501 495 499 1466 522 500 494 1471 528 1464 524 497 497 1468 520 1446 604 1440 496 525 469 1496 523 499 495 1470 518 1473 526 497 497 498 496 500 494 1471 528 1464 524 1467 521 501 493 529 465 531 473 522 472 524 491 1474 525 497 497 1469 519 502 492 1474 525 1466 522 500 494 1471 528 494 500 496 498 1494 494 528 466 529 475 521 494 502 492 503 491 1474 525 1467 521 1470 518 1474 525 +# +name: TEMP+ +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 451 18082 2995 8958 519 529 465 1527 472 524 491 504 490 506 498 497 497 499 495 501 493 502 492 1500 499 497 497 498 496 1496 492 503 501 494 500 1492 496 1522 466 1525 494 1497 491 1500 498 498 496 499 495 501 493 503 501 494 500 495 499 497 497 499 495 500 494 502 492 503 501 495 499 523 471 525 469 526 468 528 497 498 496 500 494 502 492 503 501 495 499 496 498 498 496 499 495 501 493 502 492 504 500 495 499 497 497 499 495 527 467 529 465 1500 519 1499 489 1502 497 1495 493 2979 2997 8955 522 1469 519 503 501 494 500 496 498 497 497 499 495 501 493 529 465 531 473 1518 491 505 489 506 498 498 496 1495 493 1498 501 1491 497 499 495 1496 492 1500 499 1492 496 527 467 1525 494 501 493 1498 490 1502 497 499 495 500 494 502 492 1499 500 1492 496 1496 492 503 501 494 500 496 498 524 470 526 468 527 519 1446 522 1497 491 1500 499 1493 495 501 493 502 492 504 500 495 499 497 497 1494 494 501 493 503 501 521 473 523 471 524 522 1470 498 1494 494 1497 502 1490 498 +# +name: TEMP- +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 701 17814 3056 8923 554 441 522 1496 492 504 490 505 499 497 497 498 496 500 494 502 492 503 501 1491 497 498 496 500 494 1497 502 494 500 523 471 1520 468 1523 496 1496 492 1499 500 1492 496 499 495 501 493 503 501 494 500 496 498 497 497 499 495 501 493 502 492 531 473 522 472 524 470 525 490 506 498 498 496 499 495 501 493 503 491 504 500 495 499 497 497 499 495 500 494 502 492 504 500 495 499 497 497 525 469 527 467 528 466 530 495 501 493 1498 501 1491 497 1494 494 1498 500 2972 3004 8948 518 1500 499 470 524 498 496 500 494 529 465 531 473 522 493 530 464 505 489 1502 497 499 495 501 493 1498 501 1491 497 1494 494 1498 501 495 499 1492 496 1522 497 1495 493 502 492 1500 499 497 497 1494 494 1497 502 495 499 496 498 498 496 1496 492 1499 500 1518 470 526 468 527 498 498 496 500 494 501 493 503 491 505 499 1492 496 1495 493 1498 501 495 499 497 497 499 495 500 494 502 492 1527 472 523 523 473 490 506 498 497 497 499 495 1497 491 1500 499 1492 496 1496 492 +# +# file: Flipper-IRDB/ACs/Samsung/Samsung_Wind-Free.ir +# +name: POWER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 606 17832 2994 8936 520 501 496 1491 494 501 496 498 489 532 465 529 488 505 492 502 495 499 488 1499 496 499 498 495 492 1496 499 1463 522 499 498 1463 522 1467 517 1469 526 1491 493 1495 520 500 487 507 490 504 493 500 497 497 490 504 493 501 496 498 489 505 492 501 496 498 499 495 492 502 495 499 498 522 465 529 468 526 492 502 495 499 488 506 491 503 494 499 498 496 491 503 494 500 497 497 490 504 493 500 497 497 490 504 493 500 497 497 490 531 466 528 469 1518 487 1475 520 2947 3018 8939 517 1471 524 496 491 503 494 500 497 497 490 504 493 500 497 497 490 504 493 1521 464 530 467 527 491 1471 514 507 490 1471 524 1465 519 1468 517 1472 523 1465 520 1468 517 477 520 501 496 497 490 531 466 528 469 525 493 501 496 498 489 505 492 502 495 498 499 495 492 502 495 499 498 495 492 502 495 499 498 496 491 502 495 499 498 496 491 529 468 526 471 523 495 499 488 506 491 503 494 499 498 496 491 503 494 500 497 496 491 503 494 500 497 497 490 503 494 2973 2992 8939 517 1469 526 522 465 529 488 506 491 503 494 499 488 506 491 503 494 500 497 1490 495 499 498 496 491 1497 498 1464 520 1468 517 1470 525 524 463 1498 517 1471 524 1465 520 1468 517 1471 524 1465 520 1468 517 1472 523 497 490 504 493 501 496 1491 494 1496 519 1469 516 504 493 501 496 498 489 505 492 501 496 498 499 495 492 502 495 1492 493 1469 526 495 492 529 468 1492 513 1476 519 502 495 498 489 505 492 502 495 499 498 496 491 503 494 499 498 496 491 1497 498 1464 541 +# +# file: Flipper-IRDB/ACs/Toshiba/Toshiba_RAS13SKV2E.ir +# +name: POWER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4387 4349 553 1609 553 1607 555 1605 546 1613 549 531 550 530 551 1610 552 527 554 526 555 526 555 525 556 524 557 1602 549 1611 551 529 552 1608 554 526 555 525 556 525 556 524 546 533 548 532 549 1611 551 1608 554 1606 556 1604 558 1603 548 1611 551 1609 553 1607 555 525 556 525 556 524 557 523 547 533 548 533 548 532 549 531 550 530 551 1607 555 526 555 1605 557 1603 548 531 550 531 550 530 551 529 552 528 553 527 554 526 555 526 555 525 556 524 546 1612 550 1610 552 1608 554 527 554 526 555 526 555 525 556 524 557 523 547 533 548 532 549 531 550 1609 553 1607 555 525 556 525 556 1603 548 1612 550 530 551 7454 4385 4352 549 1611 551 1609 553 1608 554 1606 556 525 556 524 557 1602 549 531 550 531 550 530 551 530 551 529 552 1633 529 1604 558 523 558 1601 550 531 550 530 551 530 551 529 552 528 553 527 554 1604 558 1603 548 1611 551 1609 553 1608 554 1606 556 1604 558 1602 549 532 549 531 550 530 551 530 551 529 552 528 553 527 554 526 555 525 556 1603 548 532 549 1610 552 1608 554 527 554 527 554 526 555 525 556 524 557 523 547 533 548 532 549 531 550 530 551 1608 554 1606 556 1604 558 523 547 533 548 532 549 531 550 530 551 529 552 528 553 527 554 526 555 1605 557 1602 549 531 550 531 550 1609 553 1607 555 526 555 +# +# file: Flipper-IRDB/ACs/Toshiba/Toshiba_RG57H4.ir +# +name: POWER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4413 4375 541 1579 568 504 569 1576 571 501 562 510 563 534 539 507 566 1578 569 1577 570 1575 572 500 563 509 564 508 565 506 567 1578 569 502 571 501 562 1583 564 1581 566 1579 568 504 569 503 570 1574 563 510 563 1607 540 506 567 505 568 503 570 502 571 1573 564 508 565 1580 567 1578 569 503 570 502 571 500 563 535 538 507 566 1579 568 1577 570 502 571 500 563 536 537 1581 566 506 567 504 569 503 570 1574 563 5170 4409 4379 537 509 564 1581 566 506 567 1577 570 1575 572 1573 564 1582 565 507 566 505 568 504 569 1575 572 1573 564 1582 565 1580 567 505 568 1576 571 1574 563 509 564 508 565 532 541 1577 570 1575 572 500 563 1582 565 507 566 1578 569 1576 571 1574 562 1583 564 508 565 1579 568 504 569 502 571 1574 563 1582 565 1580 567 1578 569 1576 571 501 562 510 563 1581 566 1579 568 1577 570 501 572 1573 564 1581 566 1579 568 504 569 +# +# file: Flipper-IRDB/ACs/Whynter/Whynter_AC.ir +# +name: POWER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 781 710 2930 2876 775 716 781 735 752 2175 779 711 776 2177 746 745 773 2232 753 2173 749 768 750 2177 745 745 773 743 754 737 750 741 746 744 754 737 771 746 751 739 748 2178 776 742 755 2172 771 719 778 2227 747 744 754 2200 754 737 750 2203 751 741 746 2181 773 744 753 737 750 741 746 +# # Old SAMSUNG AC # name: POWER diff --git a/assets/resources/infrared/assets/audio.ir b/assets/resources/infrared/assets/audio.ir index 3d4621871..12088838e 100644 --- a/assets/resources/infrared/assets/audio.ir +++ b/assets/resources/infrared/assets/audio.ir @@ -9,7 +9,7 @@ Version: 1 # HUGE thank you to Amec0e for continued maintenance! ##################################################### # -# Updated 15th August 2022 +# Updated 23th August 2022 # # | SPEAKERS | # @@ -562,6 +562,26 @@ type: parsed protocol: NECext address: 0A 1D 00 00 command: 01 FE 00 00 +# +# CREATIVE +# +name: VOL+ +type: parsed +protocol: NEC +address: 20 00 00 00 +command: 06 00 00 00 +# +name: VOL- +type: parsed +protocol: NEC +address: 20 00 00 00 +command: 07 00 00 00 +# +name: MUTE +type: parsed +protocol: NEC +address: 20 00 00 00 +command: 1E 00 00 00 # # KLIPSCH # @@ -952,6 +972,24 @@ protocol: NEC address: 00 00 00 00 command: 40 00 00 00 # +name: VOL+ +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 41 00 00 00 +# +name: VOL- +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 45 00 00 00 +# +name: MUTE +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 48 00 00 00 +# # XIAOMI # name: VOL+ @@ -1114,6 +1152,26 @@ protocol: NECext address: D2 03 00 00 command: 05 FA 00 00 # +# PHILLIPS +# +name: POWER +type: parsed +protocol: RC5 +address: 14 00 00 00 +command: 0C 00 00 00 +# +name: VOL- +type: parsed +protocol: RC5 +address: 14 00 00 00 +command: 11 00 00 00 +# +name: VOL+ +type: parsed +protocol: RC5 +address: 14 00 00 00 +command: 10 00 00 00 +# # REGA # name: MUTE diff --git a/assets/resources/infrared/assets/projectors.ir b/assets/resources/infrared/assets/projectors.ir new file mode 100644 index 000000000..413fdd5e7 --- /dev/null +++ b/assets/resources/infrared/assets/projectors.ir @@ -0,0 +1,326 @@ +Filetype: IR signals file +Version: 1 +# +# file: Flipper-IRDB/Projectors/Anker/NebulaMarsLite.ir +# +name: POWER +type: parsed +protocol: NECext +address: 80 19 00 00 +command: 10 EF 00 00 +# +name: VOL+ +type: parsed +protocol: NECext +address: 80 19 00 00 +command: 1C E3 00 00 +# +name: VOL- +type: parsed +protocol: NECext +address: 80 19 00 00 +command: 46 B9 00 00 +# +# file: Flipper-IRDB/Projectors/Anker/Nebula_Capsule_Mini_Projector.ir +# +name: POWER +type: parsed +protocol: NEC +address: 80 00 00 00 +command: 51 00 00 00 +# +# file: Flipper-IRDB/Projectors/BenQ/BenQ.ir +# +name: POWER +type: parsed +protocol: NECext +address: 40 40 00 00 +command: 0A F5 00 00 +# +# file: Flipper-IRDB/Projectors/BenQ/BenQ_W1050.ir +# +name: POWER +type: parsed +protocol: NECext +address: 00 30 00 00 +command: 4E B1 00 00 +# +name: VOL+ +type: parsed +protocol: NECext +address: 00 30 00 00 +command: 0E F1 00 00 +# +name: VOL- +type: parsed +protocol: NECext +address: 00 30 00 00 +command: 0D F2 00 00 +# +# file: Flipper-IRDB/Projectors/BenQ/BenQ_W1070.ir +# +name: POWER +type: parsed +protocol: NECext +address: 00 30 00 00 +command: 4F B0 00 00 +# +name: MUTE +type: parsed +protocol: NECext +address: 00 30 00 00 +command: 14 EB 00 00 +# +# file: Flipper-IRDB/Projectors/BrandUnknown/LED_Smart_Home_Theater_Projector.ir +# +name: POWER +type: parsed +protocol: NECext +address: 08 16 00 00 +command: 87 78 00 00 +# +name: MUTE +type: parsed +protocol: NECext +address: 08 16 00 00 +command: C8 37 00 00 +# +# file: Flipper-IRDB/Projectors/Byintek/Byintek_P10.ir +# +name: POWER +type: parsed +protocol: NEC +address: 01 00 00 00 +command: 01 00 00 00 +# +name: MUTE +type: parsed +protocol: NEC +address: 01 00 00 00 +command: 02 00 00 00 +# +name: VOL+ +type: parsed +protocol: NEC +address: 01 00 00 00 +command: 28 00 00 00 +# +name: VOL- +type: parsed +protocol: NEC +address: 01 00 00 00 +command: 29 00 00 00 +# +# file: Flipper-IRDB/Projectors/Casio/Casio_YT-130.ir +# +name: POWER +type: parsed +protocol: NECext +address: 84 F4 00 00 +command: 0B F4 00 00 +# +# file: Flipper-IRDB/Projectors/Eiki/Eiki_Projector.ir +# +name: POWER +type: parsed +protocol: NECext +address: 33 00 00 00 +command: 00 FF 00 00 +# +name: VOL- +type: parsed +protocol: NECext +address: 33 00 00 00 +command: 1E E1 00 00 +# +name: VOL+ +type: parsed +protocol: NECext +address: 33 00 00 00 +command: 1D E2 00 00 +# +name: MUTE +type: parsed +protocol: NECext +address: 33 00 00 00 +command: 0B F4 00 00 +# +# file: Flipper-IRDB/Projectors/Epson/Epson.ir +# +name: POWER +type: parsed +protocol: NECext +address: 83 55 00 00 +command: 90 6F 00 00 +# +name: VOL- +type: parsed +protocol: NECext +address: 83 55 00 00 +command: 99 66 00 00 +# +name: VOL+ +type: parsed +protocol: NECext +address: 83 55 00 00 +command: 98 67 00 00 +# +# file: Flipper-IRDB/Projectors/Minolta/Minolta_MN674.ir +# +name: POWER +type: parsed +protocol: NECext +address: 00 DF 00 00 +command: 1C E3 00 00 +# +name: VOL- +type: parsed +protocol: NECext +address: 00 DF 00 00 +command: 4F B0 00 00 +# +name: VOL+ +type: parsed +protocol: NECext +address: 00 DF 00 00 +command: 4B B4 00 00 +# +# file: Flipper-IRDB/Projectors/Optoma/Optoma_Profomo_L-27-5KEY.ir +# +name: POWER +type: parsed +protocol: NEC +address: 32 00 00 00 +command: 02 00 00 00 +# +name: POWER +type: parsed +protocol: NEC +address: 32 00 00 00 +command: 2E 00 00 00 +# +name: MUTE +type: parsed +protocol: NEC +address: 32 00 00 00 +command: 52 00 00 00 +# +# file: Flipper-IRDB/Projectors/Philips/Philips_NeoPix_Prime_Projector.ir +# +name: POWER +type: parsed +protocol: NEC +address: 20 00 00 00 +command: 41 00 00 00 +# +name: VOL+ +type: parsed +protocol: NEC +address: 20 00 00 00 +command: 51 00 00 00 +# +name: VOL- +type: parsed +protocol: NEC +address: 20 00 00 00 +command: 56 00 00 00 +# +name: MUTE +type: parsed +protocol: NEC +address: 20 00 00 00 +command: 5A 00 00 00 +# +# file: Flipper-IRDB/Projectors/Sony/Sony_RM_PJ24.ir +# +name: POWER +type: parsed +protocol: SIRC15 +address: 54 00 00 00 +command: 15 00 00 00 +# +# file: Flipper-IRDB/Projectors/ViewSonic/ViewSonic_Projector_PA503W-2.ir +# +name: VOL+ +type: parsed +protocol: NECext +address: 83 F4 00 00 +command: 82 7D 00 00 +# +name: VOL- +type: parsed +protocol: NECext +address: 83 F4 00 00 +command: 83 7C 00 00 +# +name: MUTE +type: parsed +protocol: NECext +address: 83 F4 00 00 +command: 14 EB 00 00 +# +# file: Flipper-IRDB/Projectors/Vivitek/Vivitek.ir +# +name: POWER +type: parsed +protocol: NEC +address: 31 00 00 00 +command: 91 00 00 00 +# +name: POWER +type: parsed +protocol: NEC +address: 31 00 00 00 +command: 90 00 00 00 +# +name: VOL+ +type: parsed +protocol: NEC +address: 31 00 00 00 +command: D0 00 00 00 +# +name: MUTE +type: parsed +protocol: NEC +address: 31 00 00 00 +command: 89 00 00 00 +# +# 3M +# +name: POWER +type: parsed +protocol: NECext +address: 86 00 00 00 +command: 00 00 00 00 +# +name: VOL+ +type: parsed +protocol: NECext +address: 86 00 00 00 +command: 30 00 00 00 +# +name: VOL- +type: parsed +protocol: NECext +address: 86 00 00 00 +command: 31 00 00 00 +# +name: MUTE +type: parsed +protocol: NECext +address: 86 00 00 00 +command: 32 00 00 00 +# +# Boxlight +# +name: POWER +type: parsed +protocol: NECext +address: 30 00 00 00 +command: 00 00 00 00 +# +name: POWER +type: parsed +protocol: NECext +address: 87 4E 00 00 +command: 0D 00 00 00 diff --git a/assets/resources/infrared/assets/tv.ir b/assets/resources/infrared/assets/tv.ir index e2d819782..8f3d99020 100755 --- a/assets/resources/infrared/assets/tv.ir +++ b/assets/resources/infrared/assets/tv.ir @@ -11,7 +11,7 @@ Version: 1 # Also a HUGE thank you to Amec0e for continued maintenance! ############################################################ # -# Updated on 17th August 2022 +# Updated on 21th August 2022 # name: POWER type: parsed @@ -117,8 +117,6 @@ protocol: Samsung32 address: 0e 00 00 00 command: 14 00 00 00 # -# -# name: VOL- type: parsed protocol: Samsung32 @@ -1765,8 +1763,6 @@ protocol: NECext address: 02 7D 00 00 command: 15 EA 00 00 # -# -# name: POWER type: parsed protocol: NECext @@ -2369,6 +2365,7 @@ type: parsed protocol: NEC address: 01 00 00 00 command: 17 00 00 00 +# # BRANDT # name: POWER @@ -2830,8 +2827,6 @@ type: parsed protocol: NEC address: A0 00 00 00 command: 1F 00 00 00 -# -# ROKU # # SAMSUNG # @@ -3051,7 +3046,6 @@ frequency: 38000 duty_cycle: 0.330000 data: 3994 3986 512 1989 515 1986 518 1982 512 1989 515 985 517 983 519 2008 486 988 514 1987 517 1983 521 979 513 987 515 986 516 984 518 982 520 980 512 1988 516 1985 519 981 521 1980 514 986 516 984 518 2009 485 1990 514 9041 3994 3986 511 1989 515 1985 519 1982 512 1989 515 984 518 982 520 1981 513 987 515 1986 518 1982 512 988 514 986 516 984 518 983 519 981 521 979 513 1987 517 1984 520 980 512 1989 515 984 518 982 520 2007 487 1988 516 9038 4028 3952 514 1986 518 1982 512 1989 515 1985 519 980 512 989 513 2013 491 983 519 1982 512 1989 515 984 519 982 520 980 522 979 513 987 515 985 517 1983 521 1980 514 986 516 1985 519 980 512 989 513 2013 491 1984 520 # -# # WBOX # name: POWER diff --git a/assets/slideshow/update_default/frame_00.png b/assets/slideshow/update_default/frame_00.png index 385fbbe06..bf70b363e 100644 Binary files a/assets/slideshow/update_default/frame_00.png and b/assets/slideshow/update_default/frame_00.png differ diff --git a/assets/unit_tests/subghz/magellen.sub b/assets/unit_tests/subghz/magellen.sub new file mode 100644 index 000000000..3317fd4bc --- /dev/null +++ b/assets/unit_tests/subghz/magellen.sub @@ -0,0 +1,7 @@ +Filetype: Flipper SubGhz Key File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok650Async +Protocol: Magellen +Bit: 32 +Key: 00 00 00 00 37 AE 48 28 diff --git a/assets/unit_tests/subghz/magellen_raw.sub b/assets/unit_tests/subghz/magellen_raw.sub new file mode 100644 index 000000000..0d70576ba --- /dev/null +++ b/assets/unit_tests/subghz/magellen_raw.sub @@ -0,0 +1,8 @@ +Filetype: Flipper SubGhz RAW File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok650Async +Protocol: RAW +RAW_Data: 29262 361 -68 2635 -66 24113 -66 1131 -100 4157 -66 26253 -130 621 -18438 99 -298 231 -66 197 -496 753 -230 7503 -16526 65 -396 65 -296 99 -196 293 -64 429 -132 397 -66 329 -66 37701 -66 13475 -100 54967 -64 18209 -18340 97 -462 197 -98 587 -232 97 -100 259 -98 197 -262 297 -64 557 -100 599 -100 333 -234 42493 -13212 6449 -206 173 -214 217 -176 195 -218 181 -218 181 -182 217 -182 217 -176 187 -214 215 -180 217 -182 217 -182 217 -178 185 -424 1177 -388 387 -240 381 -214 181 -398 211 -380 419 -176 217 -394 203 -394 205 -380 189 -402 421 -168 219 -398 393 -190 191 -398 205 -406 185 -402 381 -212 215 -362 241 -378 421 -176 377 -218 197 -378 427 -210 393 -172 429 -172 397 -212 217 -362 389 -228 197 -372 417 -204 395 -210 181 -398 391 -192 201 -216888 761 -200 299 -166 695 -132 15435 -66 5611 -66 21049 -66 4947 -66 2355 -66 1921 -100 2223 -100 2107 -100 397 -98 3643 -66 5301 -98 14205 -66 37371 -246 175 -216 179 -216 177 -224 149 -246 159 -228 181 -212 201 -204 159 -244 151 -254 169 -214 181 -210 197 -182 181 -454 1141 -444 357 -228 361 -246 177 -396 209 -412 367 -188 187 -434 201 -394 185 -406 193 -402 377 -238 181 -386 381 -234 153 -424 205 -412 157 -412 383 -240 181 -398 203 -392 385 -236 371 -212 179 -400 383 -240 359 -210 375 -220 381 -246 175 -394 383 -240 181 -398 363 -222 379 -246 175 -394 383 -204 217 -182856 99 -66 99 -300 133 -402 65 -198 99 -328 65 -100 491 -164 593 -100 3547 -64 361 -66 789 -68 2521 -66 22883 -66 2659 -98 3309 -130 3789 -100 9689 -17178 99 -1388 65 -266 197 -100 131 -134 99 -232 627 -130 233 -66 1949 -100 14567 -198 165 -256 181 -208 159 -214 183 -220 163 -244 149 -246 159 -236 181 -254 141 -226 151 -246 157 -228 181 -212 201 -400 1163 -428 379 -230 355 -244 177 -396 207 -412 367 -222 157 -418 189 -410 207 -412 171 -430 357 -226 165 -404 413 -204 181 -428 173 -428 169 -426 353 -236 173 -414 173 -408 381 -244 337 -222 201 -408 397 -208 393 -204 395 -208 359 -246 177 -394 387 -200 205 -380 415 -202 395 -208 181 -432 357 -226 169 -195084 65 -300 763 -66 297 -364 593 -68 2883 -66 1357 -68 363 -98 3841 -66 3119 -66 5153 -66 4023 -268 143 -246 133 -290 141 -250 139 -254 141 -226 181 -248 137 -254 143 -252 139 -252 143 -230 181 -250 139 -254 145 -436 1135 -448 349 -240 347 -254 157 -434 167 -426 377 -226 157 -434 167 -426 155 -440 163 -434 375 -206 215 -380 381 -234 153 +RAW_Data: -424 205 -412 159 -412 381 -240 181 -398 203 -392 387 -236 369 -212 179 -400 383 -240 359 -244 339 -222 381 -246 175 -394 383 -240 181 -398 363 -222 381 -244 175 -392 383 -240 181 -184002 99 -360 63 -330 65 -132 129 -232 97 -198 295 -328 6031 -66 831 -132 3417 -66 2187 -64 2183 -100 6535 -66 1127 -66 2569 -66 2031 -66 2271 -66 2183 -66 3815 -66 3803 -66 493 -66 1909 -66 1627 -98 4805 -17512 67 -2164 131 -498 265 -430 163 -98 97 -64 99 -230 99 -100 229 -230 165 -196 63 -132 99 -66 927 -66 14955 -66 19621 -68 2627 -66 14305 -68 23247 -66 2891 -66 3941 -66 3021 -212 173 -242 181 -218 181 -214 181 -208 157 -250 141 -248 181 -218 179 -214 179 -210 159 -250 179 -214 181 -218 181 -404 1153 -404 389 -244 375 -192 181 -436 161 -414 383 -240 181 -398 205 -392 201 -394 205 -394 365 -246 177 -396 383 -204 217 -398 171 -426 167 -428 353 -242 173 -420 173 -408 373 -220 403 -208 175 -422 381 -194 399 -228 357 -246 355 -210 215 -400 387 -208 181 -398 391 -226 353 -246 177 -398 383 -204 217 -185098 163 -166 525 -98 293 -100 63 -66 229 -66 1183 -66 1507 -66 3089 -98 30187 -66 2847 -19112 133 -364 131 -394 97 -166 295 -66 229 -164 227 -66 263 -130 623 -98 2071 -66 493 -66 787 -98 691 -64 10249 -132 3879 -66 1949 -66 3453 -198 23157 -66 2845 -100 1193 -66 1587 -100 3797 -98 3187 -100 3319 -66 22119 -98 5513 -226 155 -244 153 -256 131 -248 151 -246 159 -262 121 -274 133 -272 127 -244 153 -254 167 -248 145 -244 133 -252 177 -398 1169 -418 381 -238 359 -242 141 -430 169 -426 357 -274 139 -422 171 -442 173 -428 167 -426 353 -236 171 -416 379 -226 149 -436 161 -438 173 -406 381 -234 153 -424 205 -380 389 -244 359 -206 215 -384 381 -246 335 -224 383 -246 355 -244 179 -404 385 -206 181 -432 359 -226 355 -246 175 -398 383 -240 181 -179760 97 -168 727 -66 97 -332 1389 -66 2793 -66 4955 -100 12453 -100 2425 -66 21965 -66 3809 -68 1683 -66 3095 -66 2153 -64 999 -208 173 -220 181 -214 191 -196 181 -212 183 -220 191 -212 181 -214 191 -198 181 -212 181 -222 191 -212 181 -214 191 -416 1167 -424 369 -220 373 -210 209 -390 207 -376 403 -190 187 -418 189 -408 209 -412 173 -428 357 -226 169 -404 399 -208 179 -412 209 -396 169 -428 355 -230 201 -378 205 -406 381 -244 339 -222 193 -400 413 -204 393 -208 347 -220 401 -210 175 -422 383 -202 217 -398 365 -222 377 -246 175 -390 385 -204 217 -179890 165 -1552 131 -164 65 +RAW_Data: -1448 361 -17056 131 -134 233 -1462 131 -166 953 -100 261 -164 5077 -272 137 -268 143 -252 141 -248 143 -246 159 -252 141 -244 143 -290 107 -276 145 -244 131 -250 179 -248 143 -252 141 -414 1165 -424 373 -236 359 -242 145 -434 169 -428 355 -230 169 -442 173 -434 157 -406 193 -402 379 -238 181 -422 335 -252 157 -434 167 -428 185 -406 381 -208 211 -390 207 -410 381 -200 373 -236 171 -414 383 -202 393 -210 379 -220 373 -208 211 -390 383 -204 217 -398 365 -220 379 -244 175 -394 381 -240 181 -161030 97 -166 167 -930 593 -2670 1091 -132 229 -98 461 -164 1649 -66 6311 -100 44723 -16832 67 -2656 131 -132 99 -132 263 -100 399 -68 893 -18950 99 -164 165 -198 525 -998 335 -66 565 -66 1057 -17880 97 -360 195 -262 131 -332 625 -98 197 -230 455 -98 9343 -16498 67 -368 131 -598 65 -1066 333 -300 789 -130 757 -66 87207 -16554 97 -3520 97 -786 591 -64 461 -98 21495 -66 24811 -18448 131 -296 491 -134 163 -760 1091 -230 893 -66 927 -68 4581 -68 32965 -64 45217 -17292 131 -1684 231 -132 327 -64 163 -330 263 -230 25751 diff --git a/assets/unit_tests/subghz/test_random_raw.sub b/assets/unit_tests/subghz/test_random_raw.sub index 0a7d529ce..06dfb9b4b 100644 --- a/assets/unit_tests/subghz/test_random_raw.sub +++ b/assets/unit_tests/subghz/test_random_raw.sub @@ -128,3 +128,14 @@ RAW_Data: 161 -302 147 -320 331 -162 171 -314 161 -312 163 -300 175 -296 195 -31 RAW_Data: 163 -300 175 -298 169 -318 177 -290 191 -298 165 -314 161 -310 189 -284 185 -292 189 -298 193 -284 189 -310 163 -312 497 -460 145 -320 325 -170 169 -290 333 -162 171 -314 307 -158 317 -168 169 -290 333 -162 171 -314 281 -182 159 -296 189 -300 165 -312 309 -182 313 -168 143 -318 331 -162 301 -148 319 -196 139 -314 165 -336 137 -338 135 -338 161 -312 159 -294 327 -160 173 -314 163 -312 159 -312 163 -300 175 -302 195 -312 163 -312 159 -312 163 -300 175 -298 169 -318 175 -292 189 -300 165 -312 161 -312 187 -284 185 -292 189 -300 193 -284 187 -312 161 -314 471 -486 167 -314 313 -152 159 -300 335 -162 169 -308 321 -156 329 -170 127 -318 321 -176 155 -300 333 -164 169 -302 165 -314 161 -310 329 -168 299 -166 151 -328 311 -154 331 -168 299 -178 143 -318 153 -322 167 -332 149 -320 151 -318 165 -304 321 -156 181 -290 189 -300 173 -294 177 -294 189 -302 173 -294 177 -294 189 -302 173 -294 177 -318 193 -278 173 -294 179 -292 191 -302 173 -294 177 -294 189 -302 173 -320 177 -294 195 -278 175 -318 491 -460 163 -314 333 -158 159 -294 327 -160 171 -316 311 -180 307 -170 141 -318 331 -162 143 -316 309 -182 157 -294 189 -302 165 -312 309 -182 313 -168 141 -318 331 -164 299 -148 321 -194 141 -314 165 -334 137 -338 135 -338 161 -312 159 -294 329 -158 173 -314 163 -310 161 -306 173 -314 165 -304 193 -284 187 -286 189 -276 197 -294 193 -310 163 -312 161 -310 163 -300 175 -296 193 -310 165 -284 187 -310 163 -300 175 -300 169 -318 177 -316 483 -466 187 -292 341 -134 171 -308 319 -150 179 -300 333 -164 309 -162 171 -316 313 -152 153 -316 327 -160 171 -316 163 -310 161 -312 307 -166 337 -136 171 -322 305 -164 299 -174 301 -194 141 -318 165 -336 137 -338 161 -310 163 -300 149 -320 331 -162 171 -314 161 -312 163 -300 173 -296 195 -310 163 -312 159 -312 163 -312 159 -296 189 -302 165 -312 189 -284 189 -284 185 -294 189 -296 165 -314 161 -310 189 -284 187 -294 189 -298 193 -284 485 -494 167 -298 321 -150 177 -312 313 -168 171 -290 333 -162 299 -176 151 -316 325 -162 171 -316 289 -176 151 -314 189 -296 165 -314 309 -182 305 -170 141 -318 333 -162 299 -150 319 -194 141 -314 165 -334 137 -340 133 -338 161 -302 149 -320 329 -164 171 -286 187 -312 163 -300 173 -298 193 -310 163 -312 159 -312 163 -312 159 -296 189 -302 191 -286 187 -284 189 -312 159 -292 191 -296 165 -314 161 -310 189 -284 187 -292 RAW_Data: 189 -300 193 -284 485 -494 167 -298 321 -150 177 -300 327 -150 167 -328 305 -160 319 -168 171 -290 333 -162 171 -314 281 -184 159 -296 189 -300 165 -314 307 -184 311 -170 141 -318 333 -162 299 -150 319 -194 139 -314 167 -334 135 -340 133 -338 163 -312 159 -294 327 -158 173 -314 163 -312 159 -312 165 -298 177 -302 193 -314 163 -310 161 -312 163 -300 173 -300 167 -318 177 -292 189 -300 165 -312 161 -312 187 -284 185 -292 189 -300 191 -286 187 -284 189 -300 487 -484 149 -320 333 -138 171 -322 321 -154 151 -316 327 -160 311 -164 173 -312 307 -160 161 -298 355 -132 173 -314 163 -310 161 -312 335 -170 311 -164 143 -322 307 -164 323 -150 301 -196 141 -318 165 -334 137 -338 161 -312 161 -302 147 -320 331 -162 173 -312 161 -312 163 -300 173 -298 195 -310 163 -312 159 -312 163 -312 159 -296 189 -300 193 -284 189 -284 189 -284 185 -294 189 -296 165 -314 161 -312 189 -284 185 -294 189 -298 193 -286 485 -468 193 -296 323 -150 147 -340 313 -168 171 -292 331 -164 299 -174 151 -316 325 -160 173 -316 289 -176 149 -316 189 -294 165 -314 309 -182 307 -170 141 -318 331 -164 299 -148 321 -194 139 -314 167 -306 165 -338 133 -338 163 -312 159 -294 327 -160 173 -314 163 -312 161 -310 165 -300 175 -300 169 -318 177 -290 189 -298 165 -314 187 -284 189 -312 159 -294 189 -298 165 -314 161 -310 161 -306 173 -316 193 -280 191 -312 159 -312 163 -300 515 -460 161 -312 305 -190 135 -328 329 -134 201 -286 309 -182 311 -168 143 -318 331 -162 143 -316 309 -182 157 -294 189 -300 165 -312 309 -184 311 -168 143 -318 331 -162 301 -148 319 -196 139 -314 165 -334 137 -340 159 -312 163 -300 149 -318 331 -162 173 -312 161 -312 163 -300 175 -296 195 -310 163 -312 159 -312 163 -300 173 -296 193 -312 163 -312 159 -312 163 -312 161 -298 189 -300 165 -312 161 -312 189 -312 159 -292 191 -300 191 -286 485 -468 193 -298 323 -148 147 -342 323 -160 173 -314 289 -176 297 -196 139 -314 331 -162 143 -314 311 -180 157 -292 191 -300 165 -312 311 -182 309 -170 141 -318 333 -162 299 -150 319 -194 141 -314 165 -334 137 -338 161 -312 161 -312 159 -294 329 -158 173 -314 163 -310 161 -312 165 -298 175 -304 193 -312 163 -310 161 -310 163 -302 173 -300 169 -318 175 -292 189 -300 165 -312 161 -310 189 -278 173 -316 195 -280 191 -312 159 -312 163 -300 515 -460 163 -310 305 -190 137 -328 329 -134 199 -288 309 -182 311 -168 143 -318 RAW_Data: 331 -162 143 -316 309 -182 157 -292 191 -300 165 -312 309 -182 313 -168 141 -318 331 -164 299 -174 295 -194 141 -314 165 -334 137 -340 133 -338 163 -312 159 -292 327 -160 171 -316 163 -310 161 -312 165 -298 175 -304 193 -312 163 -312 159 -312 163 -300 175 -300 169 -316 177 -292 189 -298 167 -312 161 -310 189 -278 173 -316 193 -282 191 -312 159 -312 163 -300 515 -460 163 -310 305 -190 137 -326 331 -132 201 -286 309 -184 309 -170 141 -318 333 -162 143 -314 311 -180 159 -294 189 -300 165 -312 309 -184 311 -168 143 -318 331 -162 301 -148 319 -196 139 -314 165 -336 135 -340 159 -312 163 -300 149 -320 329 -164 171 -314 159 -312 163 -300 175 -296 195 -310 163 -312 159 -312 163 -300 175 -294 195 -310 163 -312 161 -310 163 -300 175 -298 169 -318 153 -314 191 -296 193 -286 187 -284 189 -312 161 -294 509 -468 159 -298 335 -162 169 -304 323 -150 175 -300 335 -162 311 -160 173 -316 313 -152 151 -316 327 -160 171 -316 163 -312 159 -312 307 -168 335 -138 169 -322 305 -164 299 -176 299 -194 141 -316 167 -334 137 -340 159 -312 161 -302 147 -320 331 -162 173 -312 161 -312 163 -300 175 -296 193 -310 165 -310 161 -312 161 -302 173 -296 195 -310 163 -312 161 -310 163 -300 175 -298 169 -318 177 -290 189 -296 193 -286 187 -310 163 -300 175 -296 501 -484 161 -288 347 -154 151 -316 325 -160 173 -314 313 -152 301 -196 141 -314 331 -162 143 -316 309 -182 157 -294 189 -300 165 -314 309 -182 311 -168 143 -318 331 -162 301 -148 319 -196 139 -314 165 -336 137 -338 133 -338 163 -312 159 -294 327 -158 173 -316 161 -312 159 -312 163 -298 177 -302 193 -314 163 -312 159 -312 163 -300 175 -298 169 -318 177 -290 191 -298 165 -314 161 -310 189 -284 193 -292 179 -308 167 -314 161 -312 189 -302 481 -460 173 -320 303 -168 169 -294 331 -164 171 -314 307 -160 319 -168 169 -292 331 -162 143 -342 283 -182 159 -294 189 -302 165 -312 309 -184 311 -168 143 -318 331 -162 299 -150 319 -194 141 -314 165 -336 135 -340 133 -338 163 -312 159 -294 327 -158 173 -316 161 -312 159 -312 165 -298 177 -302 193 -312 163 -312 161 -310 163 -314 161 -296 189 -302 165 -312 187 -284 189 -284 185 -294 189 -296 165 -314 187 -284 189 -302 173 -294 195 -310 491 -458 163 -318 319 -156 153 -314 327 -160 171 -316 311 -152 327 -170 141 -316 331 -164 143 -314 309 -182 157 -294 189 -300 165 -314 309 -182 311 -168 143 -316 333 -162 299 -150 +RAW_Data: 15041 -66 15883 -66 12643 -66 12681 -66 3413 -68 2713 -68 33389 -66 1445 -66 1279 -68 1027 -66 6911 -98 25229 -66 3967 -100 3019 -100 6131 -66 955 -66 3605 -66 12411 -98 1419 -66 3593 -68 2753 -66 2457 -66 6007 -66 627 -100 1597 -66 3071 -98 22749 -66 333 -66 12829 -66 4313 -132 855 -66 44097 -64 20391 -98 29999 -66 3539 -98 557 -66 1489 -100 4081 -100 3857 -64 2895 -132 2261 -166 3089 -66 2429 -68 34467 -66 3585 -66 3087 -66 3329 -132 5287 -66 1063 -98 15259 -100 2535 -66 995 -66 13057 -100 24233 -68 531 -100 26415 -66 1761 -100 2717 -66 4071 -100 12191 -66 23367 -68 2323 -66 19809 -248 245 -1388 255 -242 275 -1358 273 -1370 277 -246 277 -1368 275 -246 275 -1362 275 -244 275 -1364 275 -244 275 -1362 275 -244 275 -1328 273 -278 273 -1358 275 -246 275 -238 263 -1384 275 -246 273 -1358 275 -244 273 -1358 275 -246 275 -1360 275 -1344 277 -246 275 -1358 275 -244 275 -234 263 -1382 277 -1344 277 -246 279 -1362 275 -246 271 -234 261 -1380 275 -246 273 -1360 275 -246 275 -1366 277 -1340 277 -248 279 -238 263 -1382 275 -1344 277 -246 279 -1364 277 -244 275 -234 263 -1382 277 -244 273 -1358 275 -1344 277 -248 279 -1368 275 -244 273 -1360 239 -280 271 -1358 275 -244 275 -1358 275 -174 269 -10298 289 -2660 267 -238 299 -1356 275 -244 275 -1356 275 -1344 277 -248 277 -1360 275 -246 275 -1328 309 -244 273 -1358 277 -244 275 -1356 275 -246 273 -1326 309 -244 275 -1356 275 -246 273 -234 263 -1380 277 -246 273 -1326 309 -244 273 -1356 277 -246 277 -1358 275 -1338 279 -248 279 -1364 275 -246 273 -234 261 -1380 277 -1344 279 -250 277 -1330 309 -244 273 -232 261 -1384 275 -246 273 -1356 275 -248 275 -1360 275 -1340 279 -248 277 -236 263 -1380 277 -1342 279 -248 279 -1366 275 -246 273 -234 263 -1380 275 -246 275 -1358 275 -1340 279 -248 281 -1336 309 -244 273 -1358 275 -246 273 -1360 275 -244 273 -1358 275 -176 267 -10306 257 -2646 299 -234 301 -1354 277 -246 275 -1356 277 -1340 279 -250 279 -1332 309 -244 275 -1358 275 -248 273 -1326 309 -246 273 -1326 309 -244 275 -1356 277 -248 275 -1328 309 -246 273 -234 261 -1382 277 -246 277 -1326 309 -244 275 -1358 277 -246 277 -1356 277 -1346 277 -250 277 -1358 277 -246 275 -234 263 -1382 279 -1346 279 -248 281 -1330 307 -246 273 -236 261 -1380 277 -246 277 -1360 277 -246 277 -1360 275 -1344 279 -248 279 -236 263 -1384 277 -1340 279 -250 281 -1338 307 -246 271 -234 261 -1384 277 -246 275 -1356 277 -1340 279 -250 283 -1336 309 -246 273 -1356 277 -246 273 -1360 277 -246 +RAW_Data: 275 -1328 309 -174 269 -10296 289 -2648 267 -238 299 -1356 277 -246 275 -1324 307 -1342 279 -250 277 -1330 309 -244 275 -1362 277 -244 275 -1356 275 -248 273 -1328 309 -244 273 -1328 309 -244 275 -1360 277 -246 275 -234 259 -1384 277 -246 275 -1360 275 -246 273 -1358 277 -248 277 -1362 275 -1344 277 -248 277 -1328 307 -246 273 -236 261 -1384 277 -1348 279 -248 279 -1360 277 -246 273 -234 263 -1388 275 -246 275 -1360 277 -248 279 -1368 277 -1344 279 -248 279 -240 265 -1386 275 -1342 279 -286 247 -1372 275 -248 275 -238 265 -1386 277 -248 275 -1360 275 -1344 277 -286 247 -1374 275 -246 275 -1362 277 -246 275 -1360 277 -248 275 -1326 307 -174 269 -10290 287 -2654 269 -236 301 -1352 275 -248 273 -1326 311 -1340 277 -248 277 -1328 309 -244 273 -1358 275 -244 275 -1326 309 -244 273 -1356 277 -244 273 -1356 275 -246 275 -1358 275 -244 275 -234 261 -1382 277 -246 273 -1358 275 -246 273 -1360 277 -246 273 -1324 309 -1340 277 -248 277 -1328 307 -246 271 -234 259 -1382 277 -1346 279 -248 277 -1330 309 -244 271 -232 259 -1382 277 -244 275 -1356 277 -248 273 -1354 277 -1342 277 -248 275 -236 261 -1380 277 -1344 277 -248 279 -1330 307 -246 273 -234 261 -1378 277 -246 273 -1356 277 -1342 277 -248 277 -1330 309 -244 273 -1322 307 -246 273 -1326 309 -244 273 -1322 309 -176 267 -10298 257 -2682 265 -236 299 -1324 309 -248 273 -1324 311 -1342 277 -246 279 -1360 277 -244 275 -1362 275 -244 275 -1358 275 -244 275 -1360 275 -246 273 -1360 275 -244 277 -1360 275 -246 273 -234 263 -1384 275 -246 273 -1358 275 -246 275 -1360 277 -246 277 -1356 277 -1342 279 -248 277 -1364 275 -244 275 -234 261 -1384 275 -1344 277 -250 279 -1366 275 -246 273 -236 263 -1384 277 -246 275 -1358 277 -246 277 -1362 277 -1342 279 -248 279 -236 265 -1382 277 -1346 277 -248 281 -1366 275 -246 275 -234 265 -1384 275 -246 273 -1358 277 -1344 279 -248 279 -1364 275 -244 275 -1324 309 -246 273 -1324 307 -246 273 -1326 309 -174 267 -118796 133 -100 131 -892 329 -166 199 -132 131 -166 99 -100 265 -264 4663 -134 4889 -100 365 -98 5921 -100 5903 -68 4877 -98 2953 -98 1645 -64 1687 -66 981 -98 10769 -66 18319 -66 4831 -66 13301 -66 893 -132 5967 -100 15949 -66 3749 -66 497 -100 625 -66 1147 -66 469 -66 1261 -66 3651 -100 265 -100 26741 -68 6873 -66 4485 -100 2667 -68 3159 -68 2857 -132 2655 -66 12903 -66 1277 -66 1711 -66 787 -100 1327 -198 727 -64 1677 -100 1187 -66 1019 -66 891 -66 4303 -100 11297 -66 3923 -254 253 -1380 247 -292 253 -1344 +RAW_Data: 277 -1346 277 -250 279 -1364 275 -244 275 -1362 275 -244 275 -1356 275 -246 273 -1358 241 -278 273 -1356 275 -246 273 -1360 275 -246 273 -234 263 -1382 275 -244 273 -1358 275 -246 273 -1360 275 -246 273 -1358 275 -1340 277 -248 277 -1362 275 -246 273 -234 261 -1380 277 -1344 277 -248 279 -1362 275 -244 273 -236 261 -1380 275 -244 275 -1360 275 -246 275 -1358 275 -1346 277 -246 275 -236 263 -1384 275 -1342 277 -248 277 -1364 277 -244 273 -234 261 -1378 277 -246 273 -1356 277 -1340 277 -248 281 -1334 307 -246 271 -1356 275 -246 273 -1358 275 -244 273 -1326 309 -174 267 -10296 257 -2650 297 -232 263 -1384 277 -244 273 -1358 275 -1340 279 -248 279 -1328 309 -244 275 -1328 307 -244 273 -1356 275 -244 275 -1358 275 -246 273 -1324 309 -244 275 -1328 307 -244 273 -234 261 -1382 275 -246 273 -1326 309 -244 273 -1358 275 -246 273 -1358 275 -1338 279 -248 279 -1330 309 -244 273 -232 261 -1380 277 -1344 279 -248 279 -1330 309 -244 271 -234 261 -1382 275 -246 273 -1358 277 -244 275 -1330 309 -1338 277 -246 277 -236 263 -1380 277 -1342 277 -248 279 -1364 275 -246 273 -232 261 -1380 275 -248 275 -1328 307 -1338 277 -248 279 -1334 309 -244 271 -1358 275 -244 275 -1324 307 -246 271 -1328 309 -174 265 -10270 291 -2640 297 -232 297 -1350 277 -248 275 -1326 309 -1340 277 -248 277 -1328 309 -244 273 -1358 275 -246 273 -1326 309 -244 273 -1354 275 -246 273 -1330 307 -244 273 -1358 275 -246 273 -234 263 -1380 275 -246 273 -1358 275 -246 273 -1360 275 -244 273 -1358 275 -1340 277 -248 279 -1364 275 -244 273 -232 261 -1380 277 -1342 279 -250 279 -1332 307 -244 271 -234 261 -1378 277 -246 273 -1358 275 -248 275 -1360 275 -1340 277 -248 275 -236 263 -1382 277 -1344 277 -246 277 -1364 275 -246 273 -234 259 -1380 275 -246 273 -1362 275 -1342 275 -248 277 -1334 309 -244 271 -1356 275 -244 275 -1326 307 -244 273 -1356 275 -176 267 -10290 289 -2644 267 -238 301 -1320 309 -246 273 -1324 309 -1340 277 -248 277 -1328 307 -246 273 -1326 307 -246 273 -1324 309 -246 273 -1322 309 -246 273 -1322 307 -246 275 -1326 309 -246 273 -234 259 -1382 275 -246 275 -1322 309 -246 273 -1326 309 -246 273 -1326 309 -1340 277 -248 275 -1326 309 -246 273 -232 261 -1380 279 -1346 277 -250 277 -1328 309 -244 271 -232 261 -1380 277 -246 273 -1358 275 -248 273 -1328 307 -1340 277 -248 277 -236 261 -1380 277 -1344 277 -248 279 -1328 309 -244 275 -232 261 -1378 277 -248 273 -1326 309 -1344 277 -248 277 -1358 277 -246 273 -1328 307 -244 271 -1324 309 -244 +RAW_Data: 273 -1324 309 -174 267 -10270 289 -2638 297 -234 297 -1352 275 -248 275 -1328 307 -1340 277 -248 275 -1330 309 -244 273 -1358 275 -244 275 -1326 309 -244 271 -1356 275 -244 275 -1326 307 -246 273 -1326 309 -244 273 -234 261 -1378 275 -248 275 -1326 309 -244 271 -1356 277 -248 273 -1328 309 -1338 277 -248 277 -1328 309 -244 271 -232 261 -1380 277 -1348 279 -248 277 -1328 307 -246 271 -234 259 -1384 275 -244 275 -1356 277 -246 275 -1326 309 -1344 275 -248 275 -236 261 -1378 277 -1342 277 -250 279 -1334 309 -244 271 -232 261 -1380 277 -246 273 -1326 307 -1344 277 -248 277 -1328 309 -246 273 -1326 309 -244 271 -1324 309 -244 273 -1324 307 -176 267 -10288 287 -2618 299 -236 299 -1354 277 -244 273 -1326 307 -1340 279 -248 275 -1328 309 -244 275 -1326 309 -246 273 -1324 307 -246 273 -1322 309 -244 273 -1322 309 -244 275 -1328 309 -246 273 -232 261 -1380 277 -246 275 -1324 309 -244 273 -1356 277 -246 275 -1324 309 -1340 279 -246 277 -1328 309 -244 273 -232 261 -1382 277 -1344 279 -250 277 -1324 309 -246 273 -234 261 -1380 277 -246 273 -1358 277 -246 273 -1328 309 -1340 277 -248 275 -236 261 -1380 275 -1344 279 -248 279 -1360 277 -244 273 -234 261 -1380 277 -246 275 -1354 277 -1344 277 -248 277 -1328 311 -246 273 -1324 307 -244 273 -1324 309 -244 273 -1320 309 -176 269 -118210 761 -168 267 -66 563 -132 99 -132 3543 -66 5345 -100 4355 -66 4617 -68 20503 -166 2379 -132 293 -98 4117 -66 1151 -98 3353 -66 3485 -66 2491 -66 6133 -66 233 -68 16307 -68 16959 -98 357 -66 5419 -134 799 -100 327 -100 791 -66 2481 -66 963 -100 3481 -98 1679 -134 2473 -100 227 -68 3087 -66 11527 -130 4305 -98 435 -66 563 -100 2887 -100 267 -66 1787 -66 9655 -66 4793 -100 2119 -66 359 -98 1313 -132 3393 -234 995 -66 2681 -98 99 -130 1379 -100 3757 -100 21695 -132 5135 -100 693 -98 4631 -100 2325 -68 4937 -66 10409 -98 897 -100 1287 -66 2565 -66 3753 -66 4055 -66 2023 -68 1961 -68 629 -66 431 -66 5039 -66 2155 -100 2673 -66 1163 -98 6539 -100 825 -66 1197 -100 3053 -66 13973 -68 15515 -100 1861 -66 1027 -66 797 -98 959 -98 787 -132 787 -64 3811 -132 1747 -66 6683 -66 1033 -68 24927 -66 1259 -100 1125 -68 663 -66 1687 -66 4357 -132 4567 -66 3969 -98 3317 -132 433 -134 6043 -66 3249 -100 431 -98 2367 -100 11265 -66 5085 -68 2355 -64 1815 -66 1395 -274 241 -1366 275 -244 275 -1362 275 -1338 277 -284 243 -1368 239 -278 275 -1362 275 -244 275 -1360 241 -278 273 -1356 275 -246 275 -1360 239 -280 275 -1360 +RAW_Data: 275 -244 275 -234 263 -1386 239 -280 273 -1356 275 -244 273 -1360 275 -244 277 -1364 275 -1336 277 -248 277 -1366 275 -244 273 -234 263 -1386 275 -1340 277 -248 279 -1364 275 -244 275 -234 263 -1384 273 -244 275 -1358 275 -244 275 -1364 275 -1342 275 -248 277 -236 265 -1384 275 -1340 277 -282 243 -1366 275 -246 273 -236 263 -1382 277 -244 275 -1358 275 -1342 277 -248 277 -1364 275 -246 275 -1360 239 -280 273 -1358 241 -278 275 -1356 275 -210 233 -10302 257 -2652 297 -232 297 -1354 277 -244 275 -1358 275 -1340 279 -248 279 -1360 275 -246 275 -1360 275 -246 273 -1360 275 -244 275 -1328 309 -242 273 -1324 309 -244 275 -1360 275 -246 273 -234 261 -1384 275 -246 273 -1358 275 -244 275 -1358 277 -248 273 -1358 275 -1340 279 -248 277 -1334 307 -242 273 -232 261 -1380 277 -1348 277 -250 277 -1364 275 -244 275 -234 261 -1380 277 -244 275 -1358 277 -246 277 -1360 277 -1342 275 -248 275 -236 263 -1380 277 -1344 277 -248 279 -1368 275 -244 275 -232 261 -1382 277 -244 275 -1356 275 -1344 277 -248 279 -1362 275 -246 275 -1360 275 -246 273 -1356 275 -246 273 -1356 275 -176 267 -10302 257 -2648 299 -234 297 -1352 277 -246 275 -1326 309 -1340 279 -248 277 -1330 309 -244 275 -1328 309 -244 273 -1324 309 -244 275 -1324 309 -246 273 -1324 307 -246 275 -1328 309 -244 273 -234 261 -1378 277 -248 275 -1328 309 -244 273 -1356 277 -248 275 -1326 309 -1344 277 -248 275 -1326 309 -246 273 -234 259 -1380 277 -1348 281 -248 279 -1328 307 -246 273 -234 259 -1382 277 -246 275 -1360 275 -248 275 -1324 309 -1340 279 -248 277 -238 261 -1382 277 -1344 277 -248 279 -1330 311 -244 273 -234 259 -1378 277 -248 275 -1326 309 -1340 279 -248 279 -1336 307 -246 271 -1324 309 -244 275 -1324 307 -246 273 -1326 309 -174 269 -10296 257 -2648 299 -234 297 -1352 277 -248 273 -1326 309 -1342 277 -248 277 -1328 309 -246 275 -1328 309 -244 273 -1326 309 -244 273 -1322 309 -244 273 -1328 307 -244 275 -1328 309 -246 273 -234 261 -1382 277 -246 275 -1326 309 -244 273 -1352 277 -248 275 -1330 309 -1340 277 -248 277 -1328 309 -244 275 -232 261 -1384 277 -1342 279 -250 279 -1328 309 -244 273 -234 263 -1380 277 -246 273 -1360 277 -246 275 -1326 309 -1340 277 -250 277 -236 263 -1382 277 -1342 277 -248 279 -1362 277 -246 273 -234 263 -1382 277 -244 275 -1356 277 -1340 279 -248 279 -1362 275 -246 275 -1328 307 -246 273 -1356 275 -246 273 -1356 275 -174 269 -10292 287 -2650 269 -236 301 -1354 275 -248 273 -1358 275 -1340 279 -248 277 -1332 307 -246 275 -1328 +RAW_Data: 309 -244 273 -1324 309 -244 273 -1356 275 -246 273 -1358 275 -244 277 -1330 309 -244 273 -234 261 -1382 277 -244 275 -1358 275 -246 273 -1356 277 -248 275 -1360 275 -1340 277 -248 277 -1360 275 -246 273 -236 261 -1382 279 -1344 279 -248 279 -1360 277 -244 273 -234 261 -1380 277 -246 275 -1360 277 -246 273 -1360 275 -1342 279 -248 275 -236 263 -1382 275 -1344 279 -248 279 -1362 277 -246 273 -234 263 -1380 277 -246 275 -1356 275 -1342 277 -248 281 -1336 307 -246 271 -1354 277 -246 275 -1328 307 -244 273 -1352 277 -176 269 -10300 257 -2650 299 -232 297 -1354 277 -246 275 -1356 277 -1342 277 -248 279 -1328 309 -244 275 -1360 275 -246 273 -1328 307 -246 273 -1356 277 -246 277 -1326 309 -244 277 -1360 277 -246 273 -234 263 -1384 277 -246 275 -1324 309 -246 275 -1358 277 -246 277 -1360 277 -1344 277 -248 277 -1326 309 -246 273 -236 261 -1382 277 -1348 279 -250 281 -1330 307 -246 273 -234 263 -1386 277 -244 275 -1356 277 -248 277 -1362 277 -1342 277 -250 277 -238 263 -1384 277 -1342 277 -250 281 -1332 309 -246 273 -234 263 -1380 277 -246 275 -1360 277 -1342 279 -248 281 -1334 307 -246 273 -1356 275 -248 275 -1328 309 -244 275 -1324 309 -176 269 -115034 163 -362 67 -894 529 -166 14663 -98 4135 -66 3681 -100 299 -68 9829 -66 3517 -64 21569 -66 3251 -66 2209 -64 23701 -66 3359 -68 1057 -66 723 -66 299 -134 765 -66 589 -98 1687 -134 2153 -66 3081 -68 10447 -66 11643 -66 2451 -66 2277 -66 2897 -66 755 -100 5539 -64 5117 -132 4867 -134 3931 -64 625 -66 1317 -98 11597 -66 2255 -66 1165 -66 1123 -66 6371 -100 699 -68 1811 -66 621 -68 2191 -64 1291 -134 3003 -66 2423 -64 1463 -66 663 -100 1127 -100 6169 -100 489 -100 6087 -100 2027 -66 1195 -66 13195 -66 557 -66 40423 -98 1919 -100 1061 -132 201 -66 2553 -132 12549 -66 1789 -100 921 -134 1067 -66 729 -66 10029 -66 3909 -100 265 -100 16017 -134 21177 -68 2461 -66 2215 -68 1197 -66 5911 -66 2645 -66 3419 -132 16275 -64 5091 -68 2123 -66 2677 -64 10305 -66 12381 -100 427 -166 25331 -66 2457 -66 11859 -248 279 -1368 275 -246 275 -1360 275 -1340 277 -246 279 -1364 239 -278 275 -1358 275 -244 275 -1362 239 -278 273 -1358 239 -280 271 -1360 241 -278 273 -1360 275 -244 275 -234 261 -1384 239 -280 273 -1356 275 -244 273 -1360 275 -244 275 -1358 275 -1344 277 -248 275 -1358 275 -244 273 -236 261 -1384 275 -1342 279 -246 279 -1360 275 -244 275 -234 263 -1384 239 -278 273 -1358 275 -244 275 -1362 275 -1342 275 -248 275 -238 263 -1382 275 -1344 275 -248 +RAW_Data: 277 -1364 275 -244 273 -234 263 -1380 275 -246 273 -1358 275 -1342 277 -246 279 -1366 275 -244 273 -1362 239 -278 239 -1386 275 -246 273 -1360 241 -208 269 -10290 257 -2686 265 -232 265 -1384 275 -246 275 -1358 275 -1344 277 -248 275 -1358 275 -246 275 -1360 277 -244 273 -1326 309 -244 271 -1354 275 -244 275 -1358 275 -246 273 -1358 275 -246 273 -234 263 -1378 275 -246 275 -1360 275 -244 273 -1356 275 -246 275 -1360 275 -1342 277 -246 277 -1360 275 -246 273 -232 261 -1382 277 -1342 279 -248 279 -1360 275 -244 275 -232 261 -1380 277 -244 275 -1356 277 -246 277 -1360 275 -1342 277 -246 275 -236 263 -1384 275 -1342 277 -248 277 -1362 275 -246 273 -234 261 -1378 277 -246 275 -1328 307 -1340 277 -246 279 -1366 275 -244 273 -1326 307 -244 273 -1324 309 -244 273 -1356 275 -174 267 -10304 255 -2648 297 -230 263 -1382 277 -244 275 -1330 307 -1338 277 -248 277 -1330 309 -244 273 -1356 275 -246 273 -1362 275 -244 273 -1356 275 -244 273 -1326 307 -244 273 -1360 273 -246 273 -236 261 -1380 275 -244 275 -1328 307 -244 273 -1358 275 -244 275 -1360 275 -1342 277 -246 277 -1364 275 -244 271 -232 261 -1384 277 -1340 279 -248 279 -1360 275 -246 273 -234 261 -1380 275 -244 275 -1360 277 -244 275 -1356 275 -1342 279 -246 277 -236 263 -1382 275 -1340 277 -248 279 -1366 275 -246 271 -234 261 -1382 277 -244 275 -1354 275 -1342 277 -248 277 -1364 273 -246 273 -1362 275 -244 271 -1360 275 -244 273 -1358 275 -174 267 -10272 289 -2646 265 -262 261 -1382 277 -244 275 -1356 275 -1342 277 -248 277 -1364 275 -244 275 -1360 275 -244 273 -1358 275 -244 273 -1358 275 -244 273 -1326 307 -244 275 -1358 275 -246 273 -234 261 -1382 275 -246 273 -1358 275 -244 273 -1358 275 -246 275 -1360 275 -1338 277 -248 277 -1362 277 -244 271 -234 261 -1380 277 -1344 279 -248 277 -1332 273 -278 271 -234 261 -1382 275 -244 275 -1356 277 -246 275 -1360 277 -1340 277 -246 277 -234 263 -1384 275 -1342 277 -248 277 -1366 275 -244 273 -234 261 -1380 275 -246 273 -1360 275 -1340 277 -246 279 -1334 307 -244 273 -1356 275 -246 273 -1360 275 -244 271 -1354 277 -174 269 -10300 257 -2648 297 -230 263 -1384 277 -244 273 -1356 277 -1342 277 -248 277 -1362 275 -244 275 -1330 307 -244 273 -1324 309 -244 273 -1324 307 -246 273 -1326 307 -244 273 -1358 275 -246 273 -234 261 -1380 277 -246 273 -1358 275 -244 275 -1354 277 -248 275 -1360 275 -1338 279 -246 277 -1360 275 -244 273 -234 261 -1378 279 -1344 279 -248 279 -1330 309 -244 271 -232 261 -1380 277 -246 273 -1360 +RAW_Data: 277 -244 275 -1360 275 -1340 277 -246 277 -236 261 -1380 275 -1346 277 -248 277 -1362 275 -246 273 -234 263 -1380 275 -244 275 -1358 275 -1340 277 -248 279 -1334 309 -244 273 -1324 307 -246 273 -1356 275 -244 273 -1356 275 -174 269 -10302 257 -2644 297 -232 263 -1384 277 -246 275 -1354 275 -1344 277 -248 275 -1360 275 -246 275 -1358 275 -246 273 -1326 307 -246 273 -1324 307 -244 273 -1328 307 -244 273 -1358 275 -244 273 -236 261 -1380 275 -246 273 -1358 275 -244 273 -1358 275 -246 273 -1360 275 -1344 275 -248 275 -1360 275 -244 273 -234 261 -1378 277 -1344 279 -248 277 -1362 275 -246 273 -234 261 -1378 275 -244 275 -1360 275 -246 275 -1358 275 -1344 277 -246 277 -234 263 -1380 275 -1338 279 -246 281 -1368 275 -244 271 -234 261 -1386 275 -244 271 -1358 275 -1342 277 -246 279 -1362 275 -244 275 -1326 273 -278 273 -1358 239 -278 273 -1358 275 -174 267 -127478 195 -964 2317 -66 763 -98 1455 -100 16109 -66 5683 -98 11469 -66 34413 -66 5443 -66 11613 -66 2737 -66 12191 -66 2951 -68 1851 -68 1895 -68 2643 +RAW_Data: 29262 361 -68 2635 -66 24113 -66 1131 -100 4157 -66 26253 -130 621 -18438 99 -298 231 -66 197 -496 753 -230 7503 -16526 65 -396 65 -296 99 -196 293 -64 429 -132 397 -66 329 -66 37701 -66 13475 -100 54967 -64 18209 -18340 97 -462 197 -98 587 -232 97 -100 259 -98 197 -262 297 -64 557 -100 599 -100 333 -234 42493 -13212 6449 -206 173 -214 217 -176 195 -218 181 -218 181 -182 217 -182 217 -176 187 -214 215 -180 217 -182 217 -182 217 -178 185 -424 1177 -388 387 -240 381 -214 181 -398 211 -380 419 -176 217 -394 203 -394 205 -380 189 -402 421 -168 219 -398 393 -190 191 -398 205 -406 185 -402 381 -212 215 -362 241 -378 421 -176 377 -218 197 -378 427 -210 393 -172 429 -172 397 -212 217 -362 389 -228 197 -372 417 -204 395 -210 181 -398 391 -192 201 -216888 761 -200 299 -166 695 -132 15435 -66 5611 -66 21049 -66 4947 -66 2355 -66 1921 -100 2223 -100 2107 -100 397 -98 3643 -66 5301 -98 14205 -66 37371 -246 175 -216 179 -216 177 -224 149 -246 159 -228 181 -212 201 -204 159 -244 151 -254 169 -214 181 -210 197 -182 181 -454 1141 -444 357 -228 361 -246 177 -396 209 -412 367 -188 187 -434 201 -394 185 -406 193 -402 377 -238 181 -386 381 -234 153 -424 205 -412 157 -412 383 -240 181 -398 203 -392 385 -236 371 -212 179 -400 383 -240 359 -210 375 -220 381 -246 175 -394 383 -240 181 -398 363 -222 379 -246 175 -394 383 -204 217 -182856 99 -66 99 -300 133 -402 65 -198 99 -328 65 -100 491 -164 593 -100 3547 -64 361 -66 789 -68 2521 -66 22883 -66 2659 -98 3309 -130 3789 -100 9689 -17178 99 -1388 65 -266 197 -100 131 -134 99 -232 627 -130 233 -66 1949 -100 14567 -198 165 -256 181 -208 159 -214 183 -220 163 -244 149 -246 159 -236 181 -254 141 -226 151 -246 157 -228 181 -212 201 -400 1163 -428 379 -230 355 -244 177 -396 207 -412 367 -222 157 -418 189 -410 207 -412 171 -430 357 -226 165 -404 413 -204 181 -428 173 -428 169 -426 353 -236 173 -414 173 -408 381 -244 337 -222 201 -408 397 -208 393 -204 395 -208 359 -246 177 -394 387 -200 205 -380 415 -202 395 -208 181 -432 357 -226 169 -195084 65 -300 763 -66 297 -364 593 -68 2883 -66 1357 -68 363 -98 3841 -66 3119 -66 5153 -66 4023 -268 143 -246 133 -290 141 -250 139 -254 141 -226 181 -248 137 -254 143 -252 139 -252 143 -230 181 -250 139 -254 145 -436 1135 -448 349 -240 347 -254 157 -434 167 -426 377 -226 157 -434 167 -426 155 -440 163 -434 375 -206 215 -380 381 -234 153 +RAW_Data: -424 205 -412 159 -412 381 -240 181 -398 203 -392 387 -236 369 -212 179 -400 383 -240 359 -244 339 -222 381 -246 175 -394 383 -240 181 -398 363 -222 381 -244 175 -392 383 -240 181 -184002 99 -360 63 -330 65 -132 129 -232 97 -198 295 -328 6031 -66 831 -132 3417 -66 2187 -64 2183 -100 6535 -66 1127 -66 2569 -66 2031 -66 2271 -66 2183 -66 3815 -66 3803 -66 493 -66 1909 -66 1627 -98 4805 -17512 67 -2164 131 -498 265 -430 163 -98 97 -64 99 -230 99 -100 229 -230 165 -196 63 -132 99 -66 927 -66 14955 -66 19621 -68 2627 -66 14305 -68 23247 -66 2891 -66 3941 -66 3021 -212 173 -242 181 -218 181 -214 181 -208 157 -250 141 -248 181 -218 179 -214 179 -210 159 -250 179 -214 181 -218 181 -404 1153 -404 389 -244 375 -192 181 -436 161 -414 383 -240 181 -398 205 -392 201 -394 205 -394 365 -246 177 -396 383 -204 217 -398 171 -426 167 -428 353 -242 173 -420 173 -408 373 -220 403 -208 175 -422 381 -194 399 -228 357 -246 355 -210 215 -400 387 -208 181 -398 391 -226 353 -246 177 -398 383 -204 217 -185098 163 -166 525 -98 293 -100 63 -66 229 -66 1183 -66 1507 -66 3089 -98 30187 -66 2847 -19112 133 -364 131 -394 97 -166 295 -66 229 -164 227 -66 263 -130 623 -98 2071 -66 493 -66 787 -98 691 -64 10249 -132 3879 -66 1949 -66 3453 -198 23157 -66 2845 -100 1193 -66 1587 -100 3797 -98 3187 -100 3319 -66 22119 -98 5513 -226 155 -244 153 -256 131 -248 151 -246 159 -262 121 -274 133 -272 127 -244 153 -254 167 -248 145 -244 133 -252 177 -398 1169 -418 381 -238 359 -242 141 -430 169 -426 357 -274 139 -422 171 -442 173 -428 167 -426 353 -236 171 -416 379 -226 149 -436 161 -438 173 -406 381 -234 153 -424 205 -380 389 -244 359 -206 215 -384 381 -246 335 -224 383 -246 355 -244 179 -404 385 -206 181 -432 359 -226 355 -246 175 -398 383 -240 181 -179760 97 -168 727 -66 97 -332 1389 -66 2793 -66 4955 -100 12453 -100 2425 -66 21965 -66 3809 -68 1683 -66 3095 -66 2153 -64 999 -208 173 -220 181 -214 191 -196 181 -212 183 -220 191 -212 181 -214 191 -198 181 -212 181 -222 191 -212 181 -214 191 -416 1167 -424 369 -220 373 -210 209 -390 207 -376 403 -190 187 -418 189 -408 209 -412 173 -428 357 -226 169 -404 399 -208 179 -412 209 -396 169 -428 355 -230 201 -378 205 -406 381 -244 339 -222 193 -400 413 -204 393 -208 347 -220 401 -210 175 -422 383 -202 217 -398 365 -222 377 -246 175 -390 385 -204 217 -179890 165 -1552 131 -164 65 +RAW_Data: -1448 361 -17056 131 -134 233 -1462 131 -166 953 -100 261 -164 5077 -272 137 -268 143 -252 141 -248 143 -246 159 -252 141 -244 143 -290 107 -276 145 -244 131 -250 179 -248 143 -252 141 -414 1165 -424 373 -236 359 -242 145 -434 169 -428 355 -230 169 -442 173 -434 157 -406 193 -402 379 -238 181 -422 335 -252 157 -434 167 -428 185 -406 381 -208 211 -390 207 -410 381 -200 373 -236 171 -414 383 -202 393 -210 379 -220 373 -208 211 -390 383 -204 217 -398 365 -220 379 -244 175 -394 381 -240 181 -161030 97 -166 167 -930 593 -2670 1091 -132 229 -98 461 -164 1649 -66 6311 -100 44723 -16832 67 -2656 131 -132 99 -132 263 -100 399 -68 893 -18950 99 -164 165 -198 525 -998 335 -66 565 -66 1057 -17880 97 -360 195 -262 131 -332 625 -98 197 -230 455 -98 9343 -16498 67 -368 131 -598 65 -1066 333 -300 789 -130 757 -66 87207 -16554 97 -3520 97 -786 591 -64 461 -98 21495 -66 24811 -18448 131 -296 491 -134 163 -760 1091 -230 893 -66 927 -68 4581 -68 32965 -64 45217 -17292 131 -1684 231 -132 327 -64 163 -330 263 -230 25751 diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index 39aca411a..000000000 --- a/docker-compose.yml +++ /dev/null @@ -1,12 +0,0 @@ -version: '3' -services: - dev: - image: flipperdevices/flipperzero-toolchain - network_mode: host - privileged: true - tty: true - stdin_open: true - volumes: - - .:/project - - /dev/bus/usb:/dev/bus/usb - working_dir: '/project' diff --git a/docker/Dockerfile b/docker/Dockerfile deleted file mode 100644 index 64db408f3..000000000 --- a/docker/Dockerfile +++ /dev/null @@ -1,41 +0,0 @@ -FROM ubuntu:hirsute - -RUN apt-get update \ - && DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -y \ - ca-certificates \ - build-essential \ - python3 \ - git \ - clang-format-12 \ - dfu-util \ - openocd \ - libncurses5 \ - python-setuptools \ - libpython2.7-dev \ - libxml2-dev \ - libxslt1-dev \ - zlib1g-dev \ - wget \ - python3-protobuf \ - protobuf-compiler \ - python3-pip \ - libpython3-dev \ - ccache \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* - -RUN wget --progress=dot:giga "https://developer.arm.com/-/media/Files/downloads/gnu-rm/10.3-2021.07/gcc-arm-none-eabi-10.3-2021.07-$(uname -m)-linux.tar.bz2" && \ - tar xjf gcc-arm-none-eabi-10.3-2021.07-$(uname -m)-linux.tar.bz2 && \ - rm gcc-arm-none-eabi-10.3-2021.07-$(uname -m)-linux.tar.bz2 && \ - cd gcc-arm-none-eabi-10.3-2021.07/bin/ && \ - rm -rf ../share && \ - for file in * ; do ln -s "${PWD}/${file}" "/usr/bin/${file}" ; done && \ - cd / && arm-none-eabi-gcc -v && arm-none-eabi-gdb -v - -RUN pip3 install heatshrink2==0.11.0 Pillow==9.1.1 - -RUN ln -s `which clang-format-12` /usr/local/bin/clang-format - -COPY entrypoint.sh / - -ENTRYPOINT ["/entrypoint.sh"] diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh deleted file mode 100755 index 4d553e0b4..000000000 --- a/docker/entrypoint.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash - -if [ -z "$1" ]; then - bash -else - echo "Running $1" - set -ex - bash -c "$1" -fi diff --git a/documentation/HowToBuild.md b/documentation/HowToBuild.md index 4a23e97cd..be3d87763 100644 --- a/documentation/HowToBuild.md +++ b/documentation/HowToBuild.md @@ -58,31 +58,6 @@ Check `dist/` for build outputs. Use **`flipper-z-{target}-full-{suffix}.dfu`** to flash your device. -## Build with Docker -### Prerequisites - -1. Install [Docker Engine and Docker Compose](https://www.docker.com/get-started) -2. Prepare the container: - - ```sh - docker-compose up -d - ``` - -### Compile everything for development - -```sh -docker-compose exec dev ./fbt -``` - -### Compile everything for release + get updater package to update from microSD card - -```sh -docker-compose exec dev ./fbt COMPACT=1 DEBUG=0 updater_package -``` - -Check `dist/` for build outputs. - -Use **`flipper-z-{target}-full-{suffix}.dfu`** to flash your device. If compilation fails, make sure all submodules are all initialized. Either clone with `--recursive` or use `git submodule update --init --recursive`. diff --git a/documentation/MultiConverter.md b/documentation/MultiConverter.md new file mode 100644 index 000000000..820563622 --- /dev/null +++ b/documentation/MultiConverter.md @@ -0,0 +1,61 @@ +# MultiConverter + +## Author: [theisolinearchip](https://github.com/theisolinearchip/flipperzero_stuff/tree/main/applications/multi_converter) + +An expanded version of my previous __Dec/Hex Converter__, this time allowing more units and a _(probably poorly made from a design-point-of-view)_ selector mode +to swap between different unit groups. + +I wrote it with the idea of _expanding the unit list_ on mind, so adding new ones it's a matter of increasing an array of constants + defining the proper conversion functions. + +(Actually the whole project is more about "making the framework" rather than providing _ALL_ of the possible units : D) + +![Img 1](http://albertgonzalez.coffee/projects/flipperzero/multi_converter/img/1_small.png) ![Img 2](http://albertgonzalez.coffee/projects/flipperzero/multi_converter/img/2_small.png) + +## Current conversions + +- `Decimal / Hexadecimal / Binary` +- `Celsius / Fahernheit / Kelvin` +- `Kilometers / Meters / Centimeters / Miles / Feet / Inches` +- `Degree / Radian` + +## Usage + +Base keyboard allows numbers from `0` to `F`, being disabled (or not) according to the current selected unit. + +Long press on `0` toggles a __negative__ value; long press on `1` sets a __decimal point__ (only if allowed by the current selected unit). + +`<` removes the last character; `#` changes to __Unit Select Mode__. + +### Unit Select Mode + +`Left` and `Right` to swap between __origin unit__ and __destination unit__ (notice the _destination_ will change according to the current selected _origin_). + +`Ok` to save the changes and go back to the __Display Mode__; `Back` to go back without changing any unit. + +## Adding new units + +1. Add the new units in the `MultiConverterUnitType` enum on `multi_converter_definitions.h` (basic definitions header). Notice each enum element will be used as an array index later. + +2. Increase the `MULTI_CONVERTER_AVAILABLE_UNITS` constant on `multi_converter_units.h` (units main header file). + +3. Set a pair of functions for __converting__ units and to __check__ if a target unit is allowed to work with the destination unit (both on `multi_converter_units.h` +and `multi_converter_units.c`; follow the already built-in units for more info). + +4. Add the proper `MultiConverterUnit` structs for each new unit. + +5. Add each new struct to the main `multi_converter_available_units` array. + +And that's it! The system will fetch the new units and display it! + +## Known issues, TODO-list, etc. + +This is an initial release, so expect some bugs and issues (also I don't work with C that much, so there're probably lots of things that can be improved and/or changed!). + +- I've noticed some small decimal variations when "going deep" with some units (like converting __miles__ to __centimeters__ and things like that); probably due to the precision-level required. Need to check that. +- Pending: improve overflow checks. +- The way some long numbers are shown could probably be improved to look fancier. +- Both _origin_ and _destination buffers_ are the same. The destination one could probably be longer in order to avoid certain _overflow scenarios_. +- The GUI needs improvement too: there's a whole __widget/views system__ built in the Flipper that allows things like setting up keys, showing "Save/Back/Cancel" messages with +callbacks and stuff like that. Didn't know anything about them, so I moved on with something more basic (which is probably fince since it's not a "very big project"); but +a more "standard" way with the regular GUI stuff provided by the firmware will be interesting... +- More GUI stuff: the _long click buttons_ for adding a decimal point / negative number aren't very clear on the view itself (I tried to add a small dot / dash symbol, but I think those are small enough to be a little bit confusing) diff --git a/documentation/UniRFRemix.md b/documentation/SubGHzRemotePlugin.md similarity index 100% rename from documentation/UniRFRemix.md rename to documentation/SubGHzRemotePlugin.md diff --git a/firmware.scons b/firmware.scons index 863b35fca..f47e9ff26 100644 --- a/firmware.scons +++ b/firmware.scons @@ -200,6 +200,7 @@ fwelf = fwenv["FW_ELF"] = fwenv.Program( "misc", "mbedtls", "loclass", + "lfrfid", # 2nd round "flipperformat", "toolbox", @@ -244,6 +245,8 @@ if should_gen_cdb_and_link_dir(fwenv, BUILD_TARGETS): # without filtering, both updater & firmware commands would be generated fwenv.Replace(COMPILATIONDB_PATH_FILTER=fwenv.subst("*${FW_FLAVOR}*")) AlwaysBuild(fwcdb) + Precious(fwcdb) + NoClean(fwcdb) Alias(fwenv["FIRMWARE_BUILD_CFG"] + "_cdb", fwcdb) fw_artifacts.append(fwcdb) diff --git a/firmware/SConscript b/firmware/SConscript index 8dade34e1..3530ed827 100644 --- a/firmware/SConscript +++ b/firmware/SConscript @@ -3,6 +3,11 @@ Import("env") env.Append(LINT_SOURCES=["firmware"]) libenv = env.Clone(FW_LIB_NAME="flipper${TARGET_HW}") +libenv.Append( + CPPPATH=[ + "#/lib/STM32CubeWB/Middlewares/ST/STM32_WPAN/interface/patterns/ble_thread/tl", + ] +) libenv.ApplyLibFlags() diff --git a/firmware/targets/f7/Inc/stm32.h b/firmware/targets/f7/Inc/stm32.h index 83dda96e2..8e5cc379b 100644 --- a/firmware/targets/f7/Inc/stm32.h +++ b/firmware/targets/f7/Inc/stm32.h @@ -16,36 +16,6 @@ /* bit value */ #define _BV(bit) (0x01 << (bit)) -#if defined(STM32F0) -#include "STM32F0xx/Include/stm32f0xx.h" -#elif defined(STM32F1) -#include "STM32F1xx/Include/stm32f1xx.h" -#elif defined(STM32F2) -#include "STM32F2xx/Include/stm32f2xx.h" -#elif defined(STM32F3) -#include "STM32F3xx/Include/stm32f3xx.h" -#elif defined(STM32F4) -#include "STM32F4xx/Include/stm32f4xx.h" -#elif defined(STM32F7) -#include "STM32F7xx/Include/stm32f7xx.h" -#elif defined(STM32H7) -#include "STM32H7xx/Include/stm32h7xx.h" -#elif defined(STM32L0) -#include "STM32L0xx/Include/stm32l0xx.h" -#elif defined(STM32L1) -#include "STM32L1xx/Include/stm32l1xx.h" -#elif defined(STM32L4) -#include "STM32L4xx/Include/stm32l4xx.h" -#elif defined(STM32L5) -#include "STM32L5xx/Include/stm32l5xx.h" -#elif defined(STM32G0) -#include "STM32G0xx/Include/stm32g0xx.h" -#elif defined(STM32G4) -#include "STM32G4xx/Include/stm32g4xx.h" -#elif defined(STM32WB) -#include "STM32WBxx/Include/stm32wbxx.h" -#else -#error "STM32 family not defined" -#endif +#include "stm32wbxx.h" #endif // _STM32_H_ diff --git a/firmware/targets/f7/ble_glue/app_conf.h b/firmware/targets/f7/ble_glue/app_conf.h index 1d7474da5..aaa755a36 100644 --- a/firmware/targets/f7/ble_glue/app_conf.h +++ b/firmware/targets/f7/ble_glue/app_conf.h @@ -1,9 +1,10 @@ #pragma once -#include "hw.h" #include "hw_conf.h" #include "hw_if.h" -#include "ble_bufsize.h" + +#include +#include #define CFG_TX_POWER (0x19) /* +0dBm */ diff --git a/firmware/targets/f7/ble_glue/app_debug.c b/firmware/targets/f7/ble_glue/app_debug.c index e480ea364..d84588540 100644 --- a/firmware/targets/f7/ble_glue/app_debug.c +++ b/firmware/targets/f7/ble_glue/app_debug.c @@ -1,10 +1,11 @@ -#include "utilities_common.h" - #include "app_common.h" #include "app_debug.h" -#include "shci.h" -#include "tl.h" -#include "dbg_trace.h" +#include +#include +#include +#include +#include + #include typedef PACKED_STRUCT { diff --git a/firmware/targets/f7/ble_glue/battery_service.c b/firmware/targets/f7/ble_glue/battery_service.c index a95f91872..8c371efad 100644 --- a/firmware/targets/f7/ble_glue/battery_service.c +++ b/firmware/targets/f7/ble_glue/battery_service.c @@ -1,6 +1,6 @@ #include "battery_service.h" #include "app_common.h" -#include "ble.h" +#include #include #include diff --git a/firmware/targets/f7/ble_glue/ble_app.c b/firmware/targets/f7/ble_glue/ble_app.c index 4d3c96e13..3cf02009f 100644 --- a/firmware/targets/f7/ble_glue/ble_app.c +++ b/firmware/targets/f7/ble_glue/ble_app.c @@ -1,8 +1,8 @@ #include "ble_app.h" -#include "hci_tl.h" -#include "ble.h" -#include "shci.h" +#include +#include +#include #include "gap.h" #include diff --git a/firmware/targets/f7/ble_glue/ble_const.h b/firmware/targets/f7/ble_glue/ble_const.h new file mode 100644 index 000000000..0e4c8b398 --- /dev/null +++ b/firmware/targets/f7/ble_glue/ble_const.h @@ -0,0 +1,115 @@ +/***************************************************************************** + * @file ble_const.h + * @author MDG + * @brief This file contains the definitions which are compiler dependent. + ***************************************************************************** + * @attention + * + * Copyright (c) 2018-2022 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ***************************************************************************** + */ + +#ifndef BLE_CONST_H__ +#define BLE_CONST_H__ + +#include +#include +#include +#include +#include "osal.h" + +/* Default BLE variant */ +#ifndef BASIC_FEATURES +#define BASIC_FEATURES 0 +#endif +#ifndef SLAVE_ONLY +#define SLAVE_ONLY 0 +#endif +#ifndef LL_ONLY +#define LL_ONLY 0 +#endif +#ifndef BEACON_ONLY +#define BEACON_ONLY 0 +#endif + +/* Size of command/events buffers: + * + * To change the size of commands and events parameters used in the + * auto-generated files, you need to update 2 defines: + * + * - BLE_CMD_MAX_PARAM_LEN + * - BLE_EVT_MAX_PARAM_LEN + * + * These 2 defines are set below with default values and can be changed. + * + * To compute the value to support a characteristic of 512 bytes for a specific + * command or an event, you need to look in "ble_types.h". + * + * Here are 2 examples, one with a command and one with an event: + * + * - aci_gatt_update_char_value_ext_cp0 + * ---------------------------------- + * + * we have in the structure: + * + * uint8_t Value[(BLE_CMD_MAX_PARAM_LEN- 12)/sizeof(uint8_t)]; + * + * so to support a 512 byte value, we need to have + * + * BLE_CMD_MAX_PARAM_LEN at least equal to: 512 + 12 = 524 + * + * - aci_gatt_read_handle_value_rp0 + * ------------------------------ + * + * we have in the structure: + * + * uint8_t Value[((BLE_EVT_MAX_PARAM_LEN - 3) - 5)/sizeof(uint8_t)]; + * + * so to support a 512 byte value, we need to have + * + * BLE_EVT_MAX_PARAM_LEN at least equal to: 512 + 3 + 5 = 520 + * + * If you need several events or commands with 512-size values, you need to + * take the maximum values for BLE_EVT_MAX_PARAM_LEN and BLE_CMD_MAX_PARAM_LEN. + * + */ + +/* Maximum parameter size of BLE commands. + * Change this value if needed. */ +#define BLE_CMD_MAX_PARAM_LEN HCI_COMMAND_MAX_PARAM_LEN + +/* Maximum parameter size of BLE responses/events. + * Change this value if needed. */ +#define BLE_EVT_MAX_PARAM_LEN HCI_EVENT_MAX_PARAM_LEN + +/* Callback function to send command and receive response */ +struct hci_request { + uint16_t ogf; + uint16_t ocf; + int event; + void* cparam; + int clen; + void* rparam; + int rlen; +}; +extern int hci_send_req(struct hci_request* req, uint8_t async); + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef MIN +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#endif + +#ifndef MAX +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) +#endif + +#endif /* BLE_CONST_H__ */ diff --git a/firmware/targets/f7/ble_glue/ble_glue.c b/firmware/targets/f7/ble_glue/ble_glue.c index be2ae0ee5..87af5f2a8 100644 --- a/firmware/targets/f7/ble_glue/ble_glue.c +++ b/firmware/targets/f7/ble_glue/ble_glue.c @@ -1,14 +1,14 @@ #include "ble_glue.h" #include "app_common.h" #include "ble_app.h" -#include "ble.h" -#include "tl.h" -#include "shci.h" -#include "shci_tl.h" +#include + +#include +#include +#include #include "app_debug.h" #include -#include #define TAG "Core2" @@ -156,7 +156,7 @@ static void ble_glue_update_c2_fw_info() { snprintf( local_info->StackTypeString, BLE_GLUE_MAX_VERSION_STRING_LEN, - "%d.%d.%d.%s", + "%d.%d.%d:%s", local_info->VersionMajor, local_info->VersionMinor, local_info->VersionSub, diff --git a/firmware/targets/f7/ble_glue/compiler.h b/firmware/targets/f7/ble_glue/compiler.h new file mode 100644 index 000000000..1c3962819 --- /dev/null +++ b/firmware/targets/f7/ble_glue/compiler.h @@ -0,0 +1,150 @@ +/***************************************************************************** + * @file compiler.h + * @author MDG + * @brief This file contains the definitions which are compiler dependent. + ***************************************************************************** + * @attention + * + * Copyright (c) 2018-2022 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ***************************************************************************** + */ + +#ifndef COMPILER_H__ +#define COMPILER_H__ + +/** + * @brief This is the section dedicated to IAR toolchain + */ +#if defined(__ICCARM__) || defined(__IAR_SYSTEMS_ASM__) + +#ifndef __WEAK +#define __WEAK __weak +#endif + +#define QUOTE_(a) #a + +/** + * @brief PACKED + * Use the PACKED macro for variables that needs to be packed. + * Usage: PACKED(struct) myStruct_s + * PACKED(union) myStruct_s + */ +#define PACKED(decl) __packed decl + +/** + * @brief SECTION + * Use the SECTION macro to assign data or code in a specific section. + * Usage: SECTION(".my_section") + */ +#define SECTION(name) _Pragma(QUOTE_(location = name)) + +/** + * @brief ALIGN_DEF + * Use the ALIGN_DEF macro to specify the alignment of a variable. + * Usage: ALIGN_DEF(4) + */ +#define ALIGN_DEF(v) _Pragma(QUOTE_(data_alignment = v)) + +/** + * @brief NO_INIT + * Use the NO_INIT macro to declare a not initialized variable. + * Usage: NO_INIT(int my_no_init_var) + * Usage: NO_INIT(uint16_t my_no_init_array[10]) + */ +#define NO_INIT(var) __no_init var + +/** + * @brief This is the section dedicated to GNU toolchain + */ +#else +#ifdef __GNUC__ + +#ifndef __WEAK +#define __WEAK __attribute__((weak)) +#endif + +/** + * @brief PACKED + * Use the PACKED macro for variables that needs to be packed. + * Usage: PACKED(struct) myStruct_s + * PACKED(union) myStruct_s + */ +#define PACKED(decl) decl __attribute__((packed)) + +/** + * @brief SECTION + * Use the SECTION macro to assign data or code in a specific section. + * Usage: SECTION(".my_section") + */ +#define SECTION(name) __attribute__((section(name))) + +/** + * @brief ALIGN_DEF + * Use the ALIGN_DEF macro to specify the alignment of a variable. + * Usage: ALIGN_DEF(4) + */ +#define ALIGN_DEF(N) __attribute__((aligned(N))) + +/** + * @brief NO_INIT + * Use the NO_INIT macro to declare a not initialized variable. + * Usage: NO_INIT(int my_no_init_var) + * Usage: NO_INIT(uint16_t my_no_init_array[10]) + */ +#define NO_INIT(var) var __attribute__((section(".noinit"))) + +/** + * @brief This is the section dedicated to Keil toolchain + */ +#else +#ifdef __CC_ARM + +#ifndef __WEAK +#define __WEAK __weak +#endif + +/** + * @brief PACKED + * Use the PACKED macro for variables that needs to be packed. + * Usage: PACKED(struct) myStruct_s + * PACKED(union) myStruct_s + */ +#define PACKED(decl) decl __attribute__((packed)) + +/** + * @brief SECTION + * Use the SECTION macro to assign data or code in a specific section. + * Usage: SECTION(".my_section") + */ +#define SECTION(name) __attribute__((section(name))) + +/** + * @brief ALIGN_DEF + * Use the ALIGN_DEF macro to specify the alignment of a variable. + * Usage: ALIGN_DEF(4) + */ +#define ALIGN_DEF(N) __attribute__((aligned(N))) + +/** + * @brief NO_INIT + * Use the NO_INIT macro to declare a not initialized variable. + * Usage: NO_INIT(int my_no_init_var) + * Usage: NO_INIT(uint16_t my_no_init_array[10]) + */ +#define NO_INIT(var) var __attribute__((section("NoInit"))) + +#else + +#error Neither ICCARM, CC ARM nor GNUC C detected. Define your macros. + +#endif +#endif +#endif + +#endif /* COMPILER_H__ */ diff --git a/firmware/targets/f7/ble_glue/dev_info_service.c b/firmware/targets/f7/ble_glue/dev_info_service.c index d6d1e479e..ecfa08b17 100755 --- a/firmware/targets/f7/ble_glue/dev_info_service.c +++ b/firmware/targets/f7/ble_glue/dev_info_service.c @@ -1,6 +1,6 @@ #include "dev_info_service.h" #include "app_common.h" -#include "ble.h" +#include #include #include diff --git a/firmware/targets/f7/ble_glue/gap.c b/firmware/targets/f7/ble_glue/gap.c index 7154b9b11..62db30fee 100644 --- a/firmware/targets/f7/ble_glue/gap.c +++ b/firmware/targets/f7/ble_glue/gap.c @@ -1,6 +1,6 @@ #include "gap.h" -#include "ble.h" +#include #include #include diff --git a/firmware/targets/f7/ble_glue/hid_service.c b/firmware/targets/f7/ble_glue/hid_service.c index 0efe1747b..d0ca9685a 100644 --- a/firmware/targets/f7/ble_glue/hid_service.c +++ b/firmware/targets/f7/ble_glue/hid_service.c @@ -1,6 +1,6 @@ #include "hid_service.h" #include "app_common.h" -#include "ble.h" +#include #include diff --git a/firmware/targets/f7/ble_glue/hw_ipcc.c b/firmware/targets/f7/ble_glue/hw_ipcc.c index ccdb0736e..64dd9ef9b 100644 --- a/firmware/targets/f7/ble_glue/hw_ipcc.c +++ b/firmware/targets/f7/ble_glue/hw_ipcc.c @@ -19,7 +19,7 @@ /* Includes ------------------------------------------------------------------*/ #include "app_common.h" -#include "mbox_def.h" +#include /* Global variables ---------------------------------------------------------*/ /* Private defines -----------------------------------------------------------*/ diff --git a/firmware/targets/f7/ble_glue/osal.h b/firmware/targets/f7/ble_glue/osal.h new file mode 100644 index 000000000..e5e0c4f68 --- /dev/null +++ b/firmware/targets/f7/ble_glue/osal.h @@ -0,0 +1,63 @@ +/***************************************************************************** + * @file osal.h + * @author MDG + * @brief This header file defines the OS abstraction layer used by + * the BLE stack. OSAL defines the set of functions which needs to be + * ported to target operating system and target platform. + * Actually, only memset, memcpy and memcmp wrappers are defined. + ***************************************************************************** + * @attention + * + * Copyright (c) 2018-2022 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ***************************************************************************** + */ + +#ifndef OSAL_H__ +#define OSAL_H__ + +/** + * This function copies size number of bytes from a + * memory location pointed by src to a destination + * memory location pointed by dest + * + * @param[in] dest Destination address + * @param[in] src Source address + * @param[in] size size in the bytes + * + * @return Address of the destination + */ + +extern void* Osal_MemCpy(void* dest, const void* src, unsigned int size); + +/** + * This function sets first number of bytes, specified + * by size, to the destination memory pointed by ptr + * to the specified value + * + * @param[in] ptr Destination address + * @param[in] value Value to be set + * @param[in] size Size in the bytes + * + * @return Address of the destination + */ + +extern void* Osal_MemSet(void* ptr, int value, unsigned int size); + +/** + * This function compares n bytes of two regions of memory + * + * @param[in] s1 First buffer to compare. + * @param[in] s2 Second buffer to compare. + * @param[in] size Number of bytes to compare. + * + * @return 0 if the two buffers are equal, 1 otherwise + */ +extern int Osal_MemCmp(const void* s1, const void* s2, unsigned int size); + +#endif /* OSAL_H__ */ diff --git a/firmware/targets/f7/ble_glue/serial_service.c b/firmware/targets/f7/ble_glue/serial_service.c index 91e12dd68..eb58ae0ec 100644 --- a/firmware/targets/f7/ble_glue/serial_service.c +++ b/firmware/targets/f7/ble_glue/serial_service.c @@ -1,6 +1,6 @@ #include "serial_service.h" #include "app_common.h" -#include "ble.h" +#include #include diff --git a/firmware/targets/f7/furi_hal/furi_hal_bt.c b/firmware/targets/f7/furi_hal/furi_hal_bt.c index 47ed5992e..1a2b436d2 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_bt.c +++ b/firmware/targets/f7/furi_hal/furi_hal_bt.c @@ -1,7 +1,8 @@ #include -#include + +#include +#include #include -#include #include #include diff --git a/firmware/targets/f7/furi_hal/furi_hal_crypto.c b/firmware/targets/f7/furi_hal/furi_hal_crypto.c index b3c68e2d0..dbd8c58c2 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_crypto.c +++ b/firmware/targets/f7/furi_hal/furi_hal_crypto.c @@ -4,7 +4,7 @@ #include #include #include -#include +#include #define TAG "FuriHalCrypto" diff --git a/firmware/targets/f7/furi_hal/furi_hal_flash.c b/firmware/targets/f7/furi_hal/furi_hal_flash.c index ac141db04..9e05dc123 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_flash.c +++ b/firmware/targets/f7/furi_hal/furi_hal_flash.c @@ -1,8 +1,8 @@ #include #include #include -#include -#include +#include +#include #include diff --git a/firmware/targets/f7/furi_hal/furi_hal_info.c b/firmware/targets/f7/furi_hal/furi_hal_info.c index 1f75ea331..25ea42bb6 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_info.c +++ b/firmware/targets/f7/furi_hal/furi_hal_info.c @@ -4,7 +4,7 @@ #include #include -#include +#include #include #include diff --git a/firmware/targets/f7/furi_hal/furi_hal_rfid.c b/firmware/targets/f7/furi_hal/furi_hal_rfid.c index 507c53bfe..0ade85e0a 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_rfid.c +++ b/firmware/targets/f7/furi_hal/furi_hal_rfid.c @@ -6,6 +6,7 @@ #include #include +#include #define FURI_HAL_RFID_READ_TIMER TIM1 #define FURI_HAL_RFID_READ_TIMER_CHANNEL LL_TIM_CHANNEL_CH1N @@ -16,8 +17,14 @@ #define FURI_HAL_RFID_EMULATE_TIMER_IRQ FuriHalInterruptIdTIM2 #define FURI_HAL_RFID_EMULATE_TIMER_CHANNEL LL_TIM_CHANNEL_CH3 +#define RFID_CAPTURE_TIM TIM2 +#define RFID_CAPTURE_IND_CH LL_TIM_CHANNEL_CH3 +#define RFID_CAPTURE_DIR_CH LL_TIM_CHANNEL_CH4 + typedef struct { FuriHalRfidEmulateCallback callback; + FuriHalRfidDMACallback dma_callback; + FuriHalRfidReadCaptureCallback read_capture_callback; void* context; } FuriHalRfid; @@ -212,6 +219,185 @@ void furi_hal_rfid_tim_emulate_stop() { furi_hal_interrupt_set_isr(FURI_HAL_RFID_EMULATE_TIMER_IRQ, NULL, NULL); } +static void furi_hal_capture_dma_isr(void* context) { + UNUSED(context); + + // Channel 3, positive level + if(LL_TIM_IsActiveFlag_CC3(RFID_CAPTURE_TIM)) { + LL_TIM_ClearFlag_CC3(RFID_CAPTURE_TIM); + furi_hal_rfid->read_capture_callback( + true, LL_TIM_IC_GetCaptureCH3(RFID_CAPTURE_TIM), furi_hal_rfid->context); + } + + // Channel 4, overall level + if(LL_TIM_IsActiveFlag_CC4(RFID_CAPTURE_TIM)) { + LL_TIM_ClearFlag_CC4(RFID_CAPTURE_TIM); + LL_TIM_SetCounter(RFID_CAPTURE_TIM, 0); + furi_hal_rfid->read_capture_callback( + false, LL_TIM_IC_GetCaptureCH4(RFID_CAPTURE_TIM), furi_hal_rfid->context); + } +} + +void furi_hal_rfid_tim_read_capture_start(FuriHalRfidReadCaptureCallback callback, void* context) { + FURI_CRITICAL_ENTER(); + LL_TIM_DeInit(RFID_CAPTURE_TIM); + FURI_CRITICAL_EXIT(); + + furi_assert(furi_hal_rfid); + + furi_hal_rfid->read_capture_callback = callback; + furi_hal_rfid->context = context; + + // Timer: base + LL_TIM_InitTypeDef TIM_InitStruct = {0}; + TIM_InitStruct.Prescaler = 64 - 1; + TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP; + TIM_InitStruct.Autoreload = UINT32_MAX; + TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1; + LL_TIM_Init(RFID_CAPTURE_TIM, &TIM_InitStruct); + + // Timer: advanced + LL_TIM_SetClockSource(RFID_CAPTURE_TIM, LL_TIM_CLOCKSOURCE_INTERNAL); + LL_TIM_DisableARRPreload(RFID_CAPTURE_TIM); + LL_TIM_SetTriggerInput(RFID_CAPTURE_TIM, LL_TIM_TS_TI2FP2); + LL_TIM_SetSlaveMode(RFID_CAPTURE_TIM, LL_TIM_SLAVEMODE_DISABLED); + LL_TIM_SetTriggerOutput(RFID_CAPTURE_TIM, LL_TIM_TRGO_RESET); + LL_TIM_EnableMasterSlaveMode(RFID_CAPTURE_TIM); + LL_TIM_DisableDMAReq_TRIG(RFID_CAPTURE_TIM); + LL_TIM_DisableIT_TRIG(RFID_CAPTURE_TIM); + LL_TIM_SetRemap(RFID_CAPTURE_TIM, LL_TIM_TIM2_TI4_RMP_COMP1); + + // Timer: channel 3 indirect + LL_TIM_IC_SetActiveInput(RFID_CAPTURE_TIM, RFID_CAPTURE_IND_CH, LL_TIM_ACTIVEINPUT_INDIRECTTI); + LL_TIM_IC_SetPrescaler(RFID_CAPTURE_TIM, RFID_CAPTURE_IND_CH, LL_TIM_ICPSC_DIV1); + LL_TIM_IC_SetPolarity(RFID_CAPTURE_TIM, RFID_CAPTURE_IND_CH, LL_TIM_IC_POLARITY_FALLING); + LL_TIM_IC_SetFilter(RFID_CAPTURE_TIM, RFID_CAPTURE_IND_CH, LL_TIM_IC_FILTER_FDIV1); + + // Timer: channel 4 direct + LL_TIM_IC_SetActiveInput(RFID_CAPTURE_TIM, RFID_CAPTURE_DIR_CH, LL_TIM_ACTIVEINPUT_DIRECTTI); + LL_TIM_IC_SetPrescaler(RFID_CAPTURE_TIM, RFID_CAPTURE_DIR_CH, LL_TIM_ICPSC_DIV1); + LL_TIM_IC_SetPolarity(RFID_CAPTURE_TIM, RFID_CAPTURE_DIR_CH, LL_TIM_IC_POLARITY_RISING); + LL_TIM_IC_SetFilter(RFID_CAPTURE_TIM, RFID_CAPTURE_DIR_CH, LL_TIM_IC_FILTER_FDIV1); + + furi_hal_interrupt_set_isr(FURI_HAL_RFID_EMULATE_TIMER_IRQ, furi_hal_capture_dma_isr, NULL); + + LL_TIM_EnableIT_CC3(RFID_CAPTURE_TIM); + LL_TIM_EnableIT_CC4(RFID_CAPTURE_TIM); + LL_TIM_CC_EnableChannel(RFID_CAPTURE_TIM, RFID_CAPTURE_IND_CH); + LL_TIM_CC_EnableChannel(RFID_CAPTURE_TIM, RFID_CAPTURE_DIR_CH); + LL_TIM_SetCounter(RFID_CAPTURE_TIM, 0); + LL_TIM_EnableCounter(RFID_CAPTURE_TIM); + + furi_hal_rfid_comp_start(); +} + +void furi_hal_rfid_tim_read_capture_stop() { + furi_hal_rfid_comp_stop(); + + furi_hal_interrupt_set_isr(FURI_HAL_RFID_EMULATE_TIMER_IRQ, NULL, NULL); + + FURI_CRITICAL_ENTER(); + LL_TIM_DeInit(RFID_CAPTURE_TIM); + FURI_CRITICAL_EXIT(); +} + +static void furi_hal_rfid_dma_isr() { + if(LL_DMA_IsActiveFlag_HT1(DMA1)) { + LL_DMA_ClearFlag_HT1(DMA1); + furi_hal_rfid->dma_callback(true, furi_hal_rfid->context); + } + + if(LL_DMA_IsActiveFlag_TC1(DMA1)) { + LL_DMA_ClearFlag_TC1(DMA1); + furi_hal_rfid->dma_callback(false, furi_hal_rfid->context); + } +} + +void furi_hal_rfid_tim_emulate_dma_start( + uint32_t* duration, + uint32_t* pulse, + size_t length, + FuriHalRfidDMACallback callback, + void* context) { + furi_assert(furi_hal_rfid); + + // setup interrupts + furi_hal_rfid->dma_callback = callback; + furi_hal_rfid->context = context; + + // setup pins + furi_hal_rfid_pins_emulate(); + + // configure timer + furi_hal_rfid_tim_emulate(125000); + LL_TIM_OC_SetPolarity( + FURI_HAL_RFID_EMULATE_TIMER, FURI_HAL_RFID_EMULATE_TIMER_CHANNEL, LL_TIM_OCPOLARITY_HIGH); + LL_TIM_EnableDMAReq_UPDATE(FURI_HAL_RFID_EMULATE_TIMER); + + // configure DMA "mem -> ARR" channel + LL_DMA_InitTypeDef dma_config = {0}; + dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (FURI_HAL_RFID_EMULATE_TIMER->ARR); + dma_config.MemoryOrM2MDstAddress = (uint32_t)duration; + dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; + dma_config.Mode = LL_DMA_MODE_CIRCULAR; + dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; + dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; + dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; + dma_config.NbData = length; + dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; + dma_config.Priority = LL_DMA_MODE_NORMAL; + LL_DMA_Init(DMA1, LL_DMA_CHANNEL_1, &dma_config); + LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1); + + // configure DMA "mem -> CCR3" channel +#if FURI_HAL_RFID_EMULATE_TIMER_CHANNEL == LL_TIM_CHANNEL_CH3 + dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (FURI_HAL_RFID_EMULATE_TIMER->CCR3); +#else +#error Update this code. Would you kindly? +#endif + dma_config.MemoryOrM2MDstAddress = (uint32_t)pulse; + dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; + dma_config.Mode = LL_DMA_MODE_CIRCULAR; + dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; + dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; + dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; + dma_config.NbData = length; + dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; + dma_config.Priority = LL_DMA_MODE_NORMAL; + LL_DMA_Init(DMA1, LL_DMA_CHANNEL_2, &dma_config); + LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2); + + // attach interrupt to one of DMA channels + furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch1, furi_hal_rfid_dma_isr, NULL); + LL_DMA_EnableIT_TC(DMA1, LL_DMA_CHANNEL_1); + LL_DMA_EnableIT_HT(DMA1, LL_DMA_CHANNEL_1); + + // start + LL_TIM_EnableAllOutputs(FURI_HAL_RFID_EMULATE_TIMER); + + LL_TIM_SetCounter(FURI_HAL_RFID_EMULATE_TIMER, 0); + LL_TIM_EnableCounter(FURI_HAL_RFID_EMULATE_TIMER); +} + +void furi_hal_rfid_tim_emulate_dma_stop() { + LL_TIM_DisableCounter(FURI_HAL_RFID_EMULATE_TIMER); + LL_TIM_DisableAllOutputs(FURI_HAL_RFID_EMULATE_TIMER); + + furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch1, NULL, NULL); + LL_DMA_DisableIT_TC(DMA1, LL_DMA_CHANNEL_1); + LL_DMA_DisableIT_HT(DMA1, LL_DMA_CHANNEL_1); + + FURI_CRITICAL_ENTER(); + + LL_DMA_DeInit(DMA1, LL_DMA_CHANNEL_1); + LL_DMA_DeInit(DMA1, LL_DMA_CHANNEL_2); + LL_TIM_DeInit(FURI_HAL_RFID_EMULATE_TIMER); + + FURI_CRITICAL_EXIT(); +} + void furi_hal_rfid_tim_reset() { FURI_CRITICAL_ENTER(); diff --git a/firmware/targets/f7/furi_hal/furi_hal_version.c b/firmware/targets/f7/furi_hal/furi_hal_version.c index ab635be54..90c09ffe7 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_version.c +++ b/firmware/targets/f7/furi_hal/furi_hal_version.c @@ -6,7 +6,7 @@ #include #include -#include "ble.h" +#include #define TAG "FuriHalVersion" diff --git a/firmware/targets/furi_hal_include/furi_hal_rfid.h b/firmware/targets/furi_hal_include/furi_hal_rfid.h index d26ba53fe..36563c1d1 100644 --- a/firmware/targets/furi_hal_include/furi_hal_rfid.h +++ b/firmware/targets/furi_hal_include/furi_hal_rfid.h @@ -7,6 +7,7 @@ #include #include +#include #ifdef __cplusplus extern "C" { @@ -63,6 +64,23 @@ typedef void (*FuriHalRfidEmulateCallback)(void* context); */ void furi_hal_rfid_tim_emulate_start(FuriHalRfidEmulateCallback callback, void* context); +typedef void (*FuriHalRfidReadCaptureCallback)(bool level, uint32_t duration, void* context); + +void furi_hal_rfid_tim_read_capture_start(FuriHalRfidReadCaptureCallback callback, void* context); + +void furi_hal_rfid_tim_read_capture_stop(); + +typedef void (*FuriHalRfidDMACallback)(bool half, void* context); + +void furi_hal_rfid_tim_emulate_dma_start( + uint32_t* duration, + uint32_t* pulse, + size_t length, + FuriHalRfidDMACallback callback, + void* context); + +void furi_hal_rfid_tim_emulate_dma_stop(); + /** Stop emulation timer */ void furi_hal_rfid_tim_emulate_stop(); diff --git a/furi/core/common_defines.h b/furi/core/common_defines.h index c211ad7ee..31be7fff0 100644 --- a/furi/core/common_defines.h +++ b/furi/core/common_defines.h @@ -1,5 +1,6 @@ #pragma once +#include "core_defines.h" #include #include #include @@ -10,92 +11,6 @@ extern "C" { #include -#ifndef MAX -#define MAX(a, b) \ - ({ \ - __typeof__(a) _a = (a); \ - __typeof__(b) _b = (b); \ - _a > _b ? _a : _b; \ - }) -#endif - -#ifndef MIN -#define MIN(a, b) \ - ({ \ - __typeof__(a) _a = (a); \ - __typeof__(b) _b = (b); \ - _a < _b ? _a : _b; \ - }) -#endif - -#ifndef ROUND_UP_TO -#define ROUND_UP_TO(a, b) \ - ({ \ - __typeof__(a) _a = (a); \ - __typeof__(b) _b = (b); \ - _a / _b + !!(_a % _b); \ - }) -#endif - -#ifndef CLAMP -#define CLAMP(x, upper, lower) (MIN(upper, MAX(x, lower))) -#endif - -#ifndef COUNT_OF -#define COUNT_OF(x) (sizeof(x) / sizeof(x[0])) -#endif - -#ifndef FURI_SWAP -#define FURI_SWAP(x, y) \ - do { \ - typeof(x) SWAP = x; \ - x = y; \ - y = SWAP; \ - } while(0) -#endif - -#ifndef PLACE_IN_SECTION -#define PLACE_IN_SECTION(x) __attribute__((section(x))) -#endif - -#ifndef ALIGN -#define ALIGN(n) __attribute__((aligned(n))) -#endif - -#ifndef __weak -#define __weak __attribute__((weak)) -#endif - -#ifndef UNUSED -#define UNUSED(X) (void)(X) -#endif - -#ifndef STRINGIFY -#define STRINGIFY(x) #x -#endif - -#ifndef TOSTRING -#define TOSTRING(x) STRINGIFY(x) -#endif - -#ifndef REVERSE_BYTES_U32 -#define REVERSE_BYTES_U32(x) \ - ((((x)&0x000000FF) << 24) | (((x)&0x0000FF00) << 8) | (((x)&0x00FF0000) >> 8) | \ - (((x)&0xFF000000) >> 24)) -#endif - -#ifndef FURI_BIT -#define FURI_BIT(x, n) (((x) >> (n)) & 1) -#endif - -#ifndef FURI_BIT_SET -#define FURI_BIT_SET(x, n) ((x) |= (1 << (n))) -#endif - -#ifndef FURI_BIT_CLEAR -#define FURI_BIT_CLEAR(x, n) ((x) &= ~(1 << (n))) -#endif - #ifndef FURI_IS_IRQ_MASKED #define FURI_IS_IRQ_MASKED() (__get_PRIMASK() != 0U) #endif diff --git a/furi/core/core_defines.h b/furi/core/core_defines.h new file mode 100644 index 000000000..801810f6a --- /dev/null +++ b/furi/core/core_defines.h @@ -0,0 +1,99 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#define FURI_RETURNS_NONNULL __attribute__((returns_nonnull)) + +#ifndef MAX +#define MAX(a, b) \ + ({ \ + __typeof__(a) _a = (a); \ + __typeof__(b) _b = (b); \ + _a > _b ? _a : _b; \ + }) +#endif + +#ifndef MIN +#define MIN(a, b) \ + ({ \ + __typeof__(a) _a = (a); \ + __typeof__(b) _b = (b); \ + _a < _b ? _a : _b; \ + }) +#endif + +#ifndef ROUND_UP_TO +#define ROUND_UP_TO(a, b) \ + ({ \ + __typeof__(a) _a = (a); \ + __typeof__(b) _b = (b); \ + _a / _b + !!(_a % _b); \ + }) +#endif + +#ifndef CLAMP +#define CLAMP(x, upper, lower) (MIN(upper, MAX(x, lower))) +#endif + +#ifndef COUNT_OF +#define COUNT_OF(x) (sizeof(x) / sizeof(x[0])) +#endif + +#ifndef FURI_SWAP +#define FURI_SWAP(x, y) \ + do { \ + typeof(x) SWAP = x; \ + x = y; \ + y = SWAP; \ + } while(0) +#endif + +#ifndef PLACE_IN_SECTION +#define PLACE_IN_SECTION(x) __attribute__((section(x))) +#endif + +#ifndef ALIGN +#define ALIGN(n) __attribute__((aligned(n))) +#endif + +#ifndef __weak +#define __weak __attribute__((weak)) +#endif + +#ifndef UNUSED +#define UNUSED(X) (void)(X) +#endif + +#ifndef STRINGIFY +#define STRINGIFY(x) #x +#endif + +#ifndef TOSTRING +#define TOSTRING(x) STRINGIFY(x) +#endif + +#ifndef REVERSE_BYTES_U32 +#define REVERSE_BYTES_U32(x) \ + ((((x)&0x000000FF) << 24) | (((x)&0x0000FF00) << 8) | (((x)&0x00FF0000) >> 8) | \ + (((x)&0xFF000000) >> 24)) +#endif + +#ifndef FURI_BIT +#define FURI_BIT(x, n) (((x) >> (n)) & 1) +#endif + +#ifndef FURI_BIT_SET +#define FURI_BIT_SET(x, n) ((x) |= (1 << (n))) +#endif + +#ifndef FURI_BIT_CLEAR +#define FURI_BIT_CLEAR(x, n) ((x) &= ~(1 << (n))) +#endif + +#define FURI_SW_MEMBARRIER() asm volatile("" : : : "memory") + +#ifdef __cplusplus +} +#endif diff --git a/furi/core/record.h b/furi/core/record.h index cb4bd199f..4819123e2 100644 --- a/furi/core/record.h +++ b/furi/core/record.h @@ -6,6 +6,7 @@ #pragma once #include +#include "core_defines.h" #ifdef __cplusplus extern "C" { @@ -51,7 +52,7 @@ bool furi_record_destroy(const char* name); * @note Thread safe. Open and close must be executed from the same * thread. Suspends caller thread till record is available */ -void* furi_record_open(const char* name); +FURI_RETURNS_NONNULL void* furi_record_open(const char* name); /** Close record * diff --git a/lib/SConscript b/lib/SConscript index 5e5bb2eaa..4498c4c9c 100644 --- a/lib/SConscript +++ b/lib/SConscript @@ -77,6 +77,7 @@ libs = env.BuildModules( "appframe", "misc", "loclass", + "lfrfid", ], ) diff --git a/lib/ST25RFAL002/include/rfal_picopass.h b/lib/ST25RFAL002/include/rfal_picopass.h index 5b8150251..2baf96f37 100644 --- a/lib/ST25RFAL002/include/rfal_picopass.h +++ b/lib/ST25RFAL002/include/rfal_picopass.h @@ -26,6 +26,7 @@ enum { RFAL_PICOPASS_CMD_READCHECK = 0x88, RFAL_PICOPASS_CMD_CHECK = 0x05, RFAL_PICOPASS_CMD_READ = 0x0C, + RFAL_PICOPASS_CMD_WRITE = 0x0C, }; typedef struct { @@ -58,5 +59,6 @@ ReturnCode rfalPicoPassPollerSelect(uint8_t* csn, rfalPicoPassSelectRes* selRes) ReturnCode rfalPicoPassPollerReadCheck(rfalPicoPassReadCheckRes* rcRes); ReturnCode rfalPicoPassPollerCheck(uint8_t* mac, rfalPicoPassCheckRes* chkRes); ReturnCode rfalPicoPassPollerReadBlock(uint8_t blockNum, rfalPicoPassReadBlockRes* readRes); +ReturnCode rfalPicoPassPollerWriteBlock(uint8_t blockNum, uint8_t data[8], uint8_t mac[4]); #endif /* RFAL_PICOPASS_H */ diff --git a/lib/ST25RFAL002/source/rfal_picopass.c b/lib/ST25RFAL002/source/rfal_picopass.c index 55dbe6497..d4422e412 100644 --- a/lib/ST25RFAL002/source/rfal_picopass.c +++ b/lib/ST25RFAL002/source/rfal_picopass.c @@ -158,3 +158,29 @@ ReturnCode rfalPicoPassPollerReadBlock(uint8_t blockNum, rfalPicoPassReadBlockRe fwt); return ret; } + +ReturnCode rfalPicoPassPollerWriteBlock(uint8_t blockNum, uint8_t data[8], uint8_t mac[4]) { + ReturnCode ret; + + uint8_t txBuf[14] = {RFAL_PICOPASS_CMD_WRITE, blockNum, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + memcpy(txBuf + 2, data, RFAL_PICOPASS_MAX_BLOCK_LEN); + memcpy(txBuf + 10, mac, 4); + + uint16_t recvLen = 0; + uint32_t flags = RFAL_PICOPASS_TXRX_FLAGS; + uint32_t fwt = rfalConvMsTo1fc(20); + rfalPicoPassReadBlockRes readRes; + + ret = rfalTransceiveBlockingTxRx( + txBuf, + sizeof(txBuf), + (uint8_t*)&readRes, + sizeof(rfalPicoPassReadBlockRes), + &recvLen, + flags, + fwt); + + // TODO: compare response + + return ret; +} diff --git a/lib/STM32CubeWB.scons b/lib/STM32CubeWB.scons index 02618ae73..80d06b5fc 100644 --- a/lib/STM32CubeWB.scons +++ b/lib/STM32CubeWB.scons @@ -2,19 +2,10 @@ Import("env") env.Append( CPPPATH=[ - "#/lib/STM32CubeWB/Drivers/CMSIS/Device/ST", "#/lib/STM32CubeWB/Drivers/CMSIS/Device/ST/STM32WBxx/Include", "#/lib/STM32CubeWB/Drivers/CMSIS/Include", "#/lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc", - "#/lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/Legacy", "#/lib/STM32CubeWB/Middlewares/ST/STM32_WPAN", - "#/lib/STM32CubeWB/Middlewares/ST/STM32_WPAN/ble", - "#/lib/STM32CubeWB/Middlewares/ST/STM32_WPAN/ble/core", - "#/lib/STM32CubeWB/Middlewares/ST/STM32_WPAN/ble/core/template", - "#/lib/STM32CubeWB/Middlewares/ST/STM32_WPAN/interface/patterns/ble_thread", - "#/lib/STM32CubeWB/Middlewares/ST/STM32_WPAN/interface/patterns/ble_thread/shci", - "#/lib/STM32CubeWB/Middlewares/ST/STM32_WPAN/interface/patterns/ble_thread/tl", - "#/lib/STM32CubeWB/Middlewares/ST/STM32_WPAN/utilities", ], CPPDEFINES=[ "STM32WB", @@ -33,6 +24,16 @@ if env["RAM_EXEC"]: libenv = env.Clone(FW_LIB_NAME="stm32cubewb") +libenv.Append( + CPPPATH=[ + "#/lib/STM32CubeWB/Middlewares/ST/STM32_WPAN/ble", + "#/lib/STM32CubeWB/Middlewares/ST/STM32_WPAN/ble/core", + "#/lib/STM32CubeWB/Middlewares/ST/STM32_WPAN/interface/patterns/ble_thread", + "#/lib/STM32CubeWB/Middlewares/ST/STM32_WPAN/interface/patterns/ble_thread/shci", + "#/lib/STM32CubeWB/Middlewares/ST/STM32_WPAN/interface/patterns/ble_thread/tl", + "#/lib/STM32CubeWB/Middlewares/ST/STM32_WPAN/utilities", + ] +) libenv.ApplyLibFlags() sources = libenv.GlobRecursive( diff --git a/lib/flipper_format/flipper_format.h b/lib/flipper_format/flipper_format.h index 6163dee0e..9e82bb311 100644 --- a/lib/flipper_format/flipper_format.h +++ b/lib/flipper_format/flipper_format.h @@ -41,7 +41,7 @@ * Writing: * * ~~~~~~~~~~~~~~~~~~~~~ - * FlipperFormat format = flipper_format_file_alloc(storage); + * FlipperFormat* format = flipper_format_file_alloc(storage); * * do { * const uint32_t version = 1; @@ -66,7 +66,7 @@ * Reading: * * ~~~~~~~~~~~~~~~~~~~~~ - * FlipperFormat file = flipper_format_file_alloc(storage); + * FlipperFormat* file = flipper_format_file_alloc(storage); * * do { * uint32_t version = 1; diff --git a/lib/lfrfid/SConscript b/lib/lfrfid/SConscript new file mode 100644 index 000000000..8d3542883 --- /dev/null +++ b/lib/lfrfid/SConscript @@ -0,0 +1,19 @@ +Import("env") + +env.Append( + LINT_SOURCES=[ + "#/lib/lfrfid", + ], + CPPPATH=[ + "#/lib/lfrfid", + ], +) + +libenv = env.Clone(FW_LIB_NAME="lfrfid") +libenv.ApplyLibFlags() + +sources = libenv.GlobRecursive("*.c*") + +lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources) +libenv.Install("${LIB_DIST_DIR}", lib) +Return("lib") diff --git a/lib/lfrfid/lfrfid_dict_file.c b/lib/lfrfid/lfrfid_dict_file.c new file mode 100644 index 000000000..bb6af39a4 --- /dev/null +++ b/lib/lfrfid/lfrfid_dict_file.c @@ -0,0 +1,182 @@ +#include "lfrfid_dict_file.h" +#include +#include +#include + +#define LFRFID_DICT_FILETYPE "Flipper RFID key" + +bool lfrfid_dict_file_save(ProtocolDict* dict, ProtocolId protocol, const char* filename) { + furi_check(protocol != PROTOCOL_NO); + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* file = flipper_format_file_alloc(storage); + size_t data_size = protocol_dict_get_data_size(dict, protocol); + uint8_t* data = malloc(data_size); + bool result = false; + + do { + if(!flipper_format_file_open_always(file, filename)) break; + if(!flipper_format_write_header_cstr(file, LFRFID_DICT_FILETYPE, 1)) break; + + // TODO: write comment about protocol types into file + + if(!flipper_format_write_string_cstr( + file, "Key type", protocol_dict_get_name(dict, protocol))) + break; + + // TODO: write comment about protocol sizes into file + + protocol_dict_get_data(dict, protocol, data, data_size); + + if(!flipper_format_write_hex(file, "Data", data, data_size)) break; + result = true; + } while(false); + + flipper_format_free(file); + furi_record_close(RECORD_STORAGE); + free(data); + + return result; +} + +static void lfrfid_dict_protocol_indala_data( + uint8_t* data, + size_t data_size, + uint8_t* protocol_data, + size_t protocol_data_size) { + UNUSED(data_size); + memset(protocol_data, 0, protocol_data_size); + + // fc + bit_lib_set_bit(protocol_data, 24, bit_lib_get_bit(data, 0)); + bit_lib_set_bit(protocol_data, 16, bit_lib_get_bit(data, 1)); + bit_lib_set_bit(protocol_data, 11, bit_lib_get_bit(data, 2)); + bit_lib_set_bit(protocol_data, 14, bit_lib_get_bit(data, 3)); + bit_lib_set_bit(protocol_data, 15, bit_lib_get_bit(data, 4)); + bit_lib_set_bit(protocol_data, 20, bit_lib_get_bit(data, 5)); + bit_lib_set_bit(protocol_data, 6, bit_lib_get_bit(data, 6)); + bit_lib_set_bit(protocol_data, 25, bit_lib_get_bit(data, 7)); + + // cn + bit_lib_set_bit(protocol_data, 9, bit_lib_get_bit(data, 8 + 0)); + bit_lib_set_bit(protocol_data, 12, bit_lib_get_bit(data, 8 + 1)); + bit_lib_set_bit(protocol_data, 10, bit_lib_get_bit(data, 8 + 2)); + bit_lib_set_bit(protocol_data, 7, bit_lib_get_bit(data, 8 + 3)); + bit_lib_set_bit(protocol_data, 19, bit_lib_get_bit(data, 8 + 4)); + bit_lib_set_bit(protocol_data, 3, bit_lib_get_bit(data, 8 + 5)); + bit_lib_set_bit(protocol_data, 2, bit_lib_get_bit(data, 8 + 6)); + bit_lib_set_bit(protocol_data, 18, bit_lib_get_bit(data, 8 + 7)); + bit_lib_set_bit(protocol_data, 13, bit_lib_get_bit(data, 8 + 8)); + bit_lib_set_bit(protocol_data, 0, bit_lib_get_bit(data, 8 + 9)); + bit_lib_set_bit(protocol_data, 4, bit_lib_get_bit(data, 8 + 10)); + bit_lib_set_bit(protocol_data, 21, bit_lib_get_bit(data, 8 + 11)); + bit_lib_set_bit(protocol_data, 23, bit_lib_get_bit(data, 8 + 12)); + bit_lib_set_bit(protocol_data, 26, bit_lib_get_bit(data, 8 + 13)); + bit_lib_set_bit(protocol_data, 17, bit_lib_get_bit(data, 8 + 14)); + bit_lib_set_bit(protocol_data, 8, bit_lib_get_bit(data, 8 + 15)); + + const uint32_t fc_and_card = data[0] << 16 | data[1] << 8 | data[2]; + + // indala checksum + uint8_t checksum_sum = 0; + checksum_sum += ((fc_and_card >> 14) & 1); + checksum_sum += ((fc_and_card >> 12) & 1); + checksum_sum += ((fc_and_card >> 9) & 1); + checksum_sum += ((fc_and_card >> 8) & 1); + checksum_sum += ((fc_and_card >> 6) & 1); + checksum_sum += ((fc_and_card >> 5) & 1); + checksum_sum += ((fc_and_card >> 2) & 1); + checksum_sum += ((fc_and_card >> 0) & 1); + checksum_sum = checksum_sum & 0b1; + + if(checksum_sum) { + bit_lib_set_bit(protocol_data, 27, 0); + bit_lib_set_bit(protocol_data, 28, 1); + } else { + bit_lib_set_bit(protocol_data, 27, 1); + bit_lib_set_bit(protocol_data, 28, 0); + } + + // wiegand parity + uint8_t even_parity_sum = 0; + for(int8_t i = 12; i < 24; i++) { + if(((fc_and_card >> i) & 1) == 1) { + even_parity_sum++; + } + } + bit_lib_set_bit(protocol_data, 1, even_parity_sum % 2); + + uint8_t odd_parity_sum = 1; + for(int8_t i = 0; i < 12; i++) { + if(((fc_and_card >> i) & 1) == 1) { + odd_parity_sum++; + } + } + bit_lib_set_bit(protocol_data, 5, odd_parity_sum % 2); +} + +static ProtocolId lfrfid_dict_protocol_fallback( + ProtocolDict* dict, + const char* protocol_name, + FlipperFormat* file) { + ProtocolId result = PROTOCOL_NO; + if(strcmp(protocol_name, "I40134") == 0) { + ProtocolId protocol = LFRFIDProtocolIndala26; + + size_t data_size = 3; + size_t protocol_data_size = protocol_dict_get_data_size(dict, protocol); + uint8_t* data = malloc(data_size); + uint8_t* protocol_data = malloc(protocol_data_size); + if(flipper_format_read_hex(file, "Data", data, data_size)) { + lfrfid_dict_protocol_indala_data(data, data_size, protocol_data, protocol_data_size); + protocol_dict_set_data(dict, protocol, protocol_data, protocol_data_size); + result = protocol; + } + free(protocol_data); + free(data); + } + + return result; +} + +ProtocolId lfrfid_dict_file_load(ProtocolDict* dict, const char* filename) { + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* file = flipper_format_file_alloc(storage); + ProtocolId result = PROTOCOL_NO; + uint8_t* data = malloc(protocol_dict_get_max_data_size(dict)); + string_t str_result; + string_init(str_result); + + do { + if(!flipper_format_file_open_existing(file, filename)) break; + + // header + uint32_t version; + if(!flipper_format_read_header(file, str_result, &version)) break; + if(string_cmp_str(str_result, LFRFID_DICT_FILETYPE) != 0) break; + if(version != 1) break; + + // type + if(!flipper_format_read_string(file, "Key type", str_result)) break; + ProtocolId protocol; + protocol = protocol_dict_get_protocol_by_name(dict, string_get_cstr(str_result)); + + if(protocol == PROTOCOL_NO) { + protocol = lfrfid_dict_protocol_fallback(dict, string_get_cstr(str_result), file); + if(protocol == PROTOCOL_NO) break; + } else { + // data + size_t data_size = protocol_dict_get_data_size(dict, protocol); + if(!flipper_format_read_hex(file, "Data", data, data_size)) break; + protocol_dict_set_data(dict, protocol, data, data_size); + } + + result = protocol; + } while(false); + + free(data); + string_clear(str_result); + flipper_format_free(file); + furi_record_close(RECORD_STORAGE); + + return result; +} \ No newline at end of file diff --git a/lib/lfrfid/lfrfid_dict_file.h b/lib/lfrfid/lfrfid_dict_file.h new file mode 100644 index 000000000..077bb0ba1 --- /dev/null +++ b/lib/lfrfid/lfrfid_dict_file.h @@ -0,0 +1,31 @@ +#pragma once +#include +#include "protocols/lfrfid_protocols.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Save protocol from dictionary to file + * + * @param dict + * @param protocol + * @param filename + * @return true + * @return false + */ +bool lfrfid_dict_file_save(ProtocolDict* dict, ProtocolId protocol, const char* filename); + +/** + * @brief Load protocol from file to dictionary + * + * @param dict + * @param filename + * @return ProtocolId + */ +ProtocolId lfrfid_dict_file_load(ProtocolDict* dict, const char* filename); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/lib/lfrfid/lfrfid_raw_file.c b/lib/lfrfid/lfrfid_raw_file.c new file mode 100644 index 000000000..27c6f2475 --- /dev/null +++ b/lib/lfrfid/lfrfid_raw_file.c @@ -0,0 +1,145 @@ +#include "lfrfid_raw_file.h" +#include "tools/varint_pair.h" +#include +#include + +#define LFRFID_RAW_FILE_MAGIC 0x4C464952 +#define LFRFID_RAW_FILE_VERSION 1 + +#define TAG "RFID RAW File" + +typedef struct { + uint32_t magic; + uint32_t version; + float frequency; + float duty_cycle; + uint32_t max_buffer_size; +} LFRFIDRawFileHeader; + +struct LFRFIDRawFile { + Stream* stream; + uint32_t max_buffer_size; + + uint8_t* buffer; + uint32_t buffer_size; + size_t buffer_counter; +}; + +LFRFIDRawFile* lfrfid_raw_file_alloc(Storage* storage) { + LFRFIDRawFile* file = malloc(sizeof(LFRFIDRawFile)); + file->stream = file_stream_alloc(storage); + file->buffer = NULL; + return file; +} + +void lfrfid_raw_file_free(LFRFIDRawFile* file) { + if(file->buffer) free(file->buffer); + stream_free(file->stream); + free(file); +} + +bool lfrfid_raw_file_open_write(LFRFIDRawFile* file, const char* file_path) { + return file_stream_open(file->stream, file_path, FSAM_READ_WRITE, FSOM_CREATE_ALWAYS); +} + +bool lfrfid_raw_file_open_read(LFRFIDRawFile* file, const char* file_path) { + return file_stream_open(file->stream, file_path, FSAM_READ, FSOM_OPEN_EXISTING); +} + +bool lfrfid_raw_file_write_header( + LFRFIDRawFile* file, + float frequency, + float duty_cycle, + uint32_t max_buffer_size) { + LFRFIDRawFileHeader header = { + .magic = LFRFID_RAW_FILE_MAGIC, + .version = LFRFID_RAW_FILE_VERSION, + .frequency = frequency, + .duty_cycle = duty_cycle, + .max_buffer_size = max_buffer_size}; + + size_t size = stream_write(file->stream, (uint8_t*)&header, sizeof(LFRFIDRawFileHeader)); + return (size == sizeof(LFRFIDRawFileHeader)); +} + +bool lfrfid_raw_file_write_buffer(LFRFIDRawFile* file, uint8_t* buffer_data, size_t buffer_size) { + size_t size; + size = stream_write(file->stream, (uint8_t*)&buffer_size, sizeof(size_t)); + if(size != sizeof(size_t)) return false; + + size = stream_write(file->stream, buffer_data, buffer_size); + if(size != buffer_size) return false; + + return true; +} + +bool lfrfid_raw_file_read_header(LFRFIDRawFile* file, float* frequency, float* duty_cycle) { + LFRFIDRawFileHeader header; + size_t size = stream_read(file->stream, (uint8_t*)&header, sizeof(LFRFIDRawFileHeader)); + if(size == sizeof(LFRFIDRawFileHeader)) { + if(header.magic == LFRFID_RAW_FILE_MAGIC && header.version == LFRFID_RAW_FILE_VERSION) { + *frequency = header.frequency; + *duty_cycle = header.duty_cycle; + file->max_buffer_size = header.max_buffer_size; + file->buffer = malloc(file->max_buffer_size); + file->buffer_size = 0; + file->buffer_counter = 0; + return true; + } else { + return false; + } + } else { + return false; + } +} + +bool lfrfid_raw_file_read_pair( + LFRFIDRawFile* file, + uint32_t* duration, + uint32_t* pulse, + bool* pass_end) { + size_t length = 0; + if(file->buffer_counter >= file->buffer_size) { + if(stream_eof(file->stream)) { + // rewind stream and pass header + stream_seek(file->stream, sizeof(LFRFIDRawFileHeader), StreamOffsetFromStart); + if(pass_end) *pass_end = true; + } + + length = stream_read(file->stream, (uint8_t*)&file->buffer_size, sizeof(size_t)); + if(length != sizeof(size_t)) { + FURI_LOG_E(TAG, "read pair: failed to read size"); + return false; + } + + if(file->buffer_size > file->max_buffer_size) { + FURI_LOG_E(TAG, "read pair: buffer size is too big"); + return false; + } + + length = stream_read(file->stream, file->buffer, file->buffer_size); + if(length != file->buffer_size) { + FURI_LOG_E(TAG, "read pair: failed to read data"); + return false; + } + + file->buffer_counter = 0; + } + + size_t size = 0; + bool result = varint_pair_unpack( + &file->buffer[file->buffer_counter], + (size_t)(file->buffer_size - file->buffer_counter), + pulse, + duration, + &size); + + if(result) { + file->buffer_counter += size; + } else { + FURI_LOG_E(TAG, "read pair: buffer is too small"); + return false; + } + + return true; +} \ No newline at end of file diff --git a/lib/lfrfid/lfrfid_raw_file.h b/lib/lfrfid/lfrfid_raw_file.h new file mode 100644 index 000000000..3f2f14e09 --- /dev/null +++ b/lib/lfrfid/lfrfid_raw_file.h @@ -0,0 +1,95 @@ +#pragma once +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct LFRFIDRawFile LFRFIDRawFile; + +/** + * @brief Allocate a new LFRFIDRawFile instance + * + * @param storage + * @return LFRFIDRawFile* + */ +LFRFIDRawFile* lfrfid_raw_file_alloc(Storage* storage); + +/** + * @brief Free a LFRFIDRawFile instance + * + * @param file + */ +void lfrfid_raw_file_free(LFRFIDRawFile* file); + +/** + * @brief Open RAW file for writing + * + * @param file + * @param file_path + * @return bool + */ +bool lfrfid_raw_file_open_write(LFRFIDRawFile* file, const char* file_path); + +/** + * @brief Open RAW file for reading + * @param file + * @param file_path + * @return bool + */ +bool lfrfid_raw_file_open_read(LFRFIDRawFile* file, const char* file_path); + +/** + * @brief Write RAW file header + * + * @param file + * @param frequency + * @param duty_cycle + * @param max_buffer_size + * @return bool + */ +bool lfrfid_raw_file_write_header( + LFRFIDRawFile* file, + float frequency, + float duty_cycle, + uint32_t max_buffer_size); + +/** + * @brief Write data to RAW file + * + * @param file + * @param buffer_data + * @param buffer_size + * @return bool + */ +bool lfrfid_raw_file_write_buffer(LFRFIDRawFile* file, uint8_t* buffer_data, size_t buffer_size); + +/** + * @brief Read RAW file header + * + * @param file + * @param frequency + * @param duty_cycle + * @return bool + */ +bool lfrfid_raw_file_read_header(LFRFIDRawFile* file, float* frequency, float* duty_cycle); + +/** + * @brief Read varint-encoded pair from RAW file + * + * @param file + * @param duration + * @param pulse + * @param pass_end file was wrapped around, can be NULL + * @return bool + */ +bool lfrfid_raw_file_read_pair( + LFRFIDRawFile* file, + uint32_t* duration, + uint32_t* pulse, + bool* pass_end); + +#ifdef __cplusplus +} +#endif diff --git a/lib/lfrfid/lfrfid_raw_worker.c b/lib/lfrfid/lfrfid_raw_worker.c new file mode 100644 index 000000000..4050a8ca8 --- /dev/null +++ b/lib/lfrfid/lfrfid_raw_worker.c @@ -0,0 +1,357 @@ +#include +#include +#include +#include +#include +#include "lfrfid_raw_worker.h" +#include "lfrfid_raw_file.h" +#include "tools/varint_pair.h" + +#define EMULATE_BUFFER_SIZE 1024 +#define RFID_DATA_BUFFER_SIZE 2048 +#define READ_DATA_BUFFER_COUNT 4 + +#define TAG_EMULATE "RAW EMULATE" + +// emulate mode +typedef struct { + size_t overrun_count; + StreamBufferHandle_t stream; +} RfidEmulateCtx; + +typedef struct { + uint32_t emulate_buffer_arr[EMULATE_BUFFER_SIZE]; + uint32_t emulate_buffer_ccr[EMULATE_BUFFER_SIZE]; + RfidEmulateCtx ctx; +} LFRFIDRawWorkerEmulateData; + +typedef enum { + HalfTransfer, + TransferComplete, +} LFRFIDRawEmulateDMAEvent; + +// read mode +#define READ_TEMP_DATA_SIZE 10 + +typedef struct { + BufferStream* stream; + VarintPair* pair; +} LFRFIDRawWorkerReadData; + +// main worker +struct LFRFIDRawWorker { + string_t file_path; + FuriThread* thread; + FuriEventFlag* events; + + LFRFIDWorkerEmulateRawCallback emulate_callback; + LFRFIDWorkerReadRawCallback read_callback; + void* context; + + float frequency; + float duty_cycle; +}; + +typedef enum { + LFRFIDRawWorkerEventStop, +} LFRFIDRawWorkerEvent; + +static int32_t lfrfid_raw_read_worker_thread(void* thread_context); +static int32_t lfrfid_raw_emulate_worker_thread(void* thread_context); + +LFRFIDRawWorker* lfrfid_raw_worker_alloc() { + LFRFIDRawWorker* worker = malloc(sizeof(LFRFIDRawWorker)); + + worker->thread = furi_thread_alloc(); + furi_thread_set_name(worker->thread, "lfrfid_raw_worker"); + furi_thread_set_context(worker->thread, worker); + furi_thread_set_stack_size(worker->thread, 2048); + + worker->events = furi_event_flag_alloc(NULL); + + string_init(worker->file_path); + return worker; +} + +void lfrfid_raw_worker_free(LFRFIDRawWorker* worker) { + furi_thread_free(worker->thread); + furi_event_flag_free(worker->events); + string_clear(worker->file_path); + free(worker); +} + +void lfrfid_raw_worker_start_read( + LFRFIDRawWorker* worker, + const char* file_path, + float freq, + float duty_cycle, + LFRFIDWorkerReadRawCallback callback, + void* context) { + furi_check(furi_thread_get_state(worker->thread) == FuriThreadStateStopped); + + string_set(worker->file_path, file_path); + + worker->frequency = freq; + worker->duty_cycle = duty_cycle; + worker->read_callback = callback; + worker->context = context; + + furi_thread_set_callback(worker->thread, lfrfid_raw_read_worker_thread); + + furi_thread_start(worker->thread); +} + +void lfrfid_raw_worker_start_emulate( + LFRFIDRawWorker* worker, + const char* file_path, + LFRFIDWorkerEmulateRawCallback callback, + void* context) { + furi_check(furi_thread_get_state(worker->thread) == FuriThreadStateStopped); + string_set(worker->file_path, file_path); + worker->emulate_callback = callback; + worker->context = context; + furi_thread_set_callback(worker->thread, lfrfid_raw_emulate_worker_thread); + furi_thread_start(worker->thread); +} + +void lfrfid_raw_worker_stop(LFRFIDRawWorker* worker) { + worker->emulate_callback = NULL; + worker->context = NULL; + worker->read_callback = NULL; + worker->context = NULL; + furi_event_flag_set(worker->events, 1 << LFRFIDRawWorkerEventStop); + furi_thread_join(worker->thread); +} + +static void lfrfid_raw_worker_capture(bool level, uint32_t duration, void* context) { + LFRFIDRawWorkerReadData* ctx = context; + + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + + bool need_to_send = varint_pair_pack(ctx->pair, level, duration); + + if(need_to_send) { + buffer_stream_send_from_isr( + ctx->stream, + varint_pair_get_data(ctx->pair), + varint_pair_get_size(ctx->pair), + &xHigherPriorityTaskWoken); + varint_pair_reset(ctx->pair); + } + + portYIELD_FROM_ISR(xHigherPriorityTaskWoken); +} + +static int32_t lfrfid_raw_read_worker_thread(void* thread_context) { + LFRFIDRawWorker* worker = (LFRFIDRawWorker*)thread_context; + + Storage* storage = furi_record_open(RECORD_STORAGE); + LFRFIDRawFile* file = lfrfid_raw_file_alloc(storage); + const char* filename = string_get_cstr(worker->file_path); + bool file_valid = lfrfid_raw_file_open_write(file, filename); + + LFRFIDRawWorkerReadData* data = malloc(sizeof(LFRFIDRawWorkerReadData)); + + data->stream = buffer_stream_alloc(RFID_DATA_BUFFER_SIZE, READ_DATA_BUFFER_COUNT); + data->pair = varint_pair_alloc(); + + if(file_valid) { + // write header + file_valid = lfrfid_raw_file_write_header( + file, worker->frequency, worker->duty_cycle, RFID_DATA_BUFFER_SIZE); + } + + if(file_valid) { + // setup carrier + furi_hal_rfid_pins_read(); + furi_hal_rfid_tim_read(worker->frequency, worker->duty_cycle); + furi_hal_rfid_tim_read_start(); + + // stabilize detector + furi_delay_ms(1500); + + // start capture + furi_hal_rfid_tim_read_capture_start(lfrfid_raw_worker_capture, data); + + while(1) { + Buffer* buffer = buffer_stream_receive(data->stream, 100); + + if(buffer != NULL) { + file_valid = lfrfid_raw_file_write_buffer( + file, buffer_get_data(buffer), buffer_get_size(buffer)); + buffer_reset(buffer); + } + + if(!file_valid) { + if(worker->read_callback != NULL) { + // message file_error to worker + worker->read_callback(LFRFIDWorkerReadRawFileError, worker->context); + } + break; + } + + if(buffer_stream_get_overrun_count(data->stream) > 0 && + worker->read_callback != NULL) { + // message overrun to worker + worker->read_callback(LFRFIDWorkerReadRawOverrun, worker->context); + } + + uint32_t flags = furi_event_flag_get(worker->events); + if(FURI_BIT(flags, LFRFIDRawWorkerEventStop)) { + break; + } + } + + furi_hal_rfid_tim_read_capture_stop(); + furi_hal_rfid_tim_read_stop(); + } else { + if(worker->read_callback != NULL) { + // message file_error to worker + worker->read_callback(LFRFIDWorkerReadRawFileError, worker->context); + } + } + + if(!file_valid) { + const uint32_t available_flags = (1 << LFRFIDRawWorkerEventStop); + while(true) { + uint32_t flags = furi_event_flag_wait( + worker->events, available_flags, FuriFlagWaitAny, FuriWaitForever); + + if(FURI_BIT(flags, LFRFIDRawWorkerEventStop)) { + break; + } + } + } + + varint_pair_free(data->pair); + buffer_stream_free(data->stream); + lfrfid_raw_file_free(file); + furi_record_close(RECORD_STORAGE); + free(data); + + return 0; +} + +static void rfid_emulate_dma_isr(bool half, void* context) { + RfidEmulateCtx* ctx = context; + + uint32_t flag = half ? HalfTransfer : TransferComplete; + size_t len = xStreamBufferSendFromISR(ctx->stream, &flag, sizeof(uint32_t), pdFALSE); + if(len != sizeof(uint32_t)) { + ctx->overrun_count++; + } +} + +static int32_t lfrfid_raw_emulate_worker_thread(void* thread_context) { + LFRFIDRawWorker* worker = thread_context; + + bool file_valid = true; + + LFRFIDRawWorkerEmulateData* data = malloc(sizeof(LFRFIDRawWorkerEmulateData)); + + Storage* storage = furi_record_open(RECORD_STORAGE); + data->ctx.overrun_count = 0; + data->ctx.stream = xStreamBufferCreate(sizeof(uint32_t), sizeof(uint32_t)); + + LFRFIDRawFile* file = lfrfid_raw_file_alloc(storage); + + do { + file_valid = lfrfid_raw_file_open_read(file, string_get_cstr(worker->file_path)); + if(!file_valid) break; + file_valid = lfrfid_raw_file_read_header(file, &worker->frequency, &worker->duty_cycle); + if(!file_valid) break; + + for(size_t i = 0; i < EMULATE_BUFFER_SIZE; i++) { + file_valid = lfrfid_raw_file_read_pair( + file, &data->emulate_buffer_arr[i], &data->emulate_buffer_ccr[i], NULL); + if(!file_valid) break; + data->emulate_buffer_arr[i] /= 8; + data->emulate_buffer_arr[i] -= 1; + data->emulate_buffer_ccr[i] /= 8; + } + } while(false); + + furi_hal_rfid_tim_emulate_dma_start( + data->emulate_buffer_arr, + data->emulate_buffer_ccr, + EMULATE_BUFFER_SIZE, + rfid_emulate_dma_isr, + &data->ctx); + + if(!file_valid && worker->emulate_callback != NULL) { + // message file_error to worker + worker->emulate_callback(LFRFIDWorkerEmulateRawFileError, worker->context); + } + + if(file_valid) { + uint32_t flag = 0; + + while(true) { + size_t size = xStreamBufferReceive(data->ctx.stream, &flag, sizeof(uint32_t), 100); + + if(size == sizeof(uint32_t)) { + size_t start = 0; + if(flag == TransferComplete) { + start = (EMULATE_BUFFER_SIZE / 2); + } + + for(size_t i = 0; i < (EMULATE_BUFFER_SIZE / 2); i++) { + file_valid = lfrfid_raw_file_read_pair( + file, + &data->emulate_buffer_arr[start + i], + &data->emulate_buffer_ccr[start + i], + NULL); + if(!file_valid) break; + data->emulate_buffer_arr[i] /= 8; + data->emulate_buffer_arr[i] -= 1; + data->emulate_buffer_ccr[i] /= 8; + } + } else if(size != 0) { + data->ctx.overrun_count++; + } + + if(!file_valid) { + if(worker->emulate_callback != NULL) { + // message file_error to worker + worker->emulate_callback(LFRFIDWorkerEmulateRawFileError, worker->context); + } + break; + } + + if(data->ctx.overrun_count > 0 && worker->emulate_callback != NULL) { + // message overrun to worker + worker->emulate_callback(LFRFIDWorkerEmulateRawOverrun, worker->context); + } + + uint32_t flags = furi_event_flag_get(worker->events); + if(FURI_BIT(flags, LFRFIDRawWorkerEventStop)) { + break; + }; + } + } + + furi_hal_rfid_tim_emulate_dma_stop(); + + if(!file_valid) { + const uint32_t available_flags = (1 << LFRFIDRawWorkerEventStop); + while(true) { + uint32_t flags = furi_event_flag_wait( + worker->events, available_flags, FuriFlagWaitAny, FuriWaitForever); + + if(FURI_BIT(flags, LFRFIDRawWorkerEventStop)) { + break; + }; + } + } + + if(data->ctx.overrun_count) { + FURI_LOG_E(TAG_EMULATE, "overruns: %lu", data->ctx.overrun_count); + } + + vStreamBufferDelete(data->ctx.stream); + lfrfid_raw_file_free(file); + furi_record_close(RECORD_STORAGE); + free(data); + + return 0; +} \ No newline at end of file diff --git a/lib/lfrfid/lfrfid_raw_worker.h b/lib/lfrfid/lfrfid_raw_worker.h new file mode 100644 index 000000000..1195dd587 --- /dev/null +++ b/lib/lfrfid/lfrfid_raw_worker.h @@ -0,0 +1,68 @@ +#pragma once +#include +#include "lfrfid_worker.h" +#include +#include "protocols/lfrfid_protocols.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct LFRFIDRawWorker LFRFIDRawWorker; + +/** + * @brief Allocate a new LFRFIDRawWorker instance + * + * @return LFRFIDRawWorker* + */ +LFRFIDRawWorker* lfrfid_raw_worker_alloc(); + +/** + * @brief Free a LFRFIDRawWorker instance + * + * @param worker LFRFIDRawWorker instance + */ +void lfrfid_raw_worker_free(LFRFIDRawWorker* worker); + +/** + * @brief Start reading + * + * @param worker LFRFIDRawWorker instance + * @param file_path path where file will be saved + * @param frequency HW frequency + * @param duty_cycle HW duty cycle + * @param callback callback for read event + * @param context context for callback + */ +void lfrfid_raw_worker_start_read( + LFRFIDRawWorker* worker, + const char* file_path, + float frequency, + float duty_cycle, + LFRFIDWorkerReadRawCallback callback, + void* context); + +/** + * @brief Start emulate + * + * @param worker LFRFIDRawWorker instance + * @param file_path path to file that will be emulated + * @param callback callback for emulate event + * @param context context for callback + */ +void lfrfid_raw_worker_start_emulate( + LFRFIDRawWorker* worker, + const char* file_path, + LFRFIDWorkerEmulateRawCallback callback, + void* context); + +/** + * @brief Stop worker + * + * @param worker + */ +void lfrfid_raw_worker_stop(LFRFIDRawWorker* worker); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/lib/lfrfid/lfrfid_worker.c b/lib/lfrfid/lfrfid_worker.c new file mode 100644 index 000000000..8b4f8b6a9 --- /dev/null +++ b/lib/lfrfid/lfrfid_worker.c @@ -0,0 +1,169 @@ +#include +#include +#include +#include "lfrfid_worker_i.h" + +typedef enum { + LFRFIDEventStopThread = (1 << 0), + LFRFIDEventStopMode = (1 << 1), + LFRFIDEventRead = (1 << 2), + LFRFIDEventWrite = (1 << 3), + LFRFIDEventEmulate = (1 << 4), + LFRFIDEventReadRaw = (1 << 5), + LFRFIDEventEmulateRaw = (1 << 6), + LFRFIDEventAll = + (LFRFIDEventStopThread | LFRFIDEventStopMode | LFRFIDEventRead | LFRFIDEventWrite | + LFRFIDEventEmulate | LFRFIDEventReadRaw | LFRFIDEventEmulateRaw), +} LFRFIDEventType; + +static int32_t lfrfid_worker_thread(void* thread_context); + +LFRFIDWorker* lfrfid_worker_alloc(ProtocolDict* dict) { + furi_assert(dict); + + LFRFIDWorker* worker = malloc(sizeof(LFRFIDWorker)); + worker->mode_index = LFRFIDWorkerIdle; + worker->read_cb = NULL; + worker->write_cb = NULL; + worker->cb_ctx = NULL; + worker->raw_filename = NULL; + worker->mode_storage = NULL; + + worker->thread = furi_thread_alloc(); + furi_thread_set_name(worker->thread, "lfrfid_worker"); + furi_thread_set_callback(worker->thread, lfrfid_worker_thread); + furi_thread_set_context(worker->thread, worker); + furi_thread_set_stack_size(worker->thread, 2048); + + worker->protocols = dict; + + return worker; +} + +void lfrfid_worker_free(LFRFIDWorker* worker) { + if(worker->raw_filename) { + free(worker->raw_filename); + } + + furi_thread_free(worker->thread); + free(worker); +} + +void lfrfid_worker_read_start( + LFRFIDWorker* worker, + LFRFIDWorkerReadType type, + LFRFIDWorkerReadCallback callback, + void* context) { + furi_assert(worker->mode_index == LFRFIDWorkerIdle); + worker->read_type = type; + worker->read_cb = callback; + worker->cb_ctx = context; + furi_thread_flags_set(furi_thread_get_id(worker->thread), LFRFIDEventRead); +} + +void lfrfid_worker_write_start( + LFRFIDWorker* worker, + LFRFIDProtocol protocol, + LFRFIDWorkerWriteCallback callback, + void* context) { + furi_assert(worker->mode_index == LFRFIDWorkerIdle); + worker->protocol = protocol; + worker->write_cb = callback; + worker->cb_ctx = context; + furi_thread_flags_set(furi_thread_get_id(worker->thread), LFRFIDEventWrite); +} + +void lfrfid_worker_emulate_start(LFRFIDWorker* worker, LFRFIDProtocol protocol) { + furi_assert(worker->mode_index == LFRFIDWorkerIdle); + worker->protocol = protocol; + furi_thread_flags_set(furi_thread_get_id(worker->thread), LFRFIDEventEmulate); +} + +void lfrfid_worker_set_filename(LFRFIDWorker* worker, const char* filename) { + if(worker->raw_filename) { + free(worker->raw_filename); + } + + worker->raw_filename = strdup(filename); +} + +void lfrfid_worker_read_raw_start( + LFRFIDWorker* worker, + const char* filename, + LFRFIDWorkerReadType type, + LFRFIDWorkerReadRawCallback callback, + void* context) { + furi_assert(worker->mode_index == LFRFIDWorkerIdle); + worker->read_type = type; + worker->read_raw_cb = callback; + worker->cb_ctx = context; + lfrfid_worker_set_filename(worker, filename); + furi_thread_flags_set(furi_thread_get_id(worker->thread), LFRFIDEventReadRaw); +} + +void lfrfid_worker_emulate_raw_start( + LFRFIDWorker* worker, + const char* filename, + LFRFIDWorkerEmulateRawCallback callback, + void* context) { + furi_assert(worker->mode_index == LFRFIDWorkerIdle); + lfrfid_worker_set_filename(worker, filename); + worker->emulate_raw_cb = callback; + worker->cb_ctx = context; + furi_thread_flags_set(furi_thread_get_id(worker->thread), LFRFIDEventEmulateRaw); +} + +void lfrfid_worker_stop(LFRFIDWorker* worker) { + furi_thread_flags_set(furi_thread_get_id(worker->thread), LFRFIDEventStopMode); +} + +void lfrfid_worker_start_thread(LFRFIDWorker* worker) { + furi_thread_start(worker->thread); +} + +void lfrfid_worker_stop_thread(LFRFIDWorker* worker) { + furi_assert(worker->mode_index == LFRFIDWorkerIdle); + furi_thread_flags_set(furi_thread_get_id(worker->thread), LFRFIDEventStopThread); + furi_thread_join(worker->thread); +} + +bool lfrfid_worker_check_for_stop(LFRFIDWorker* worker) { + UNUSED(worker); + uint32_t flags = furi_thread_flags_get(); + return (flags & LFRFIDEventStopMode); +} + +size_t lfrfid_worker_dict_get_data_size(LFRFIDWorker* worker, LFRFIDProtocol protocol) { + furi_assert(worker->mode_index == LFRFIDWorkerIdle); + return protocol_dict_get_data_size(worker->protocols, protocol); +} + +static int32_t lfrfid_worker_thread(void* thread_context) { + LFRFIDWorker* worker = thread_context; + bool running = true; + + while(running) { + uint32_t flags = furi_thread_flags_wait(LFRFIDEventAll, FuriFlagWaitAny, FuriWaitForever); + if(flags != FuriFlagErrorTimeout) { + // stop thread + if(flags & LFRFIDEventStopThread) break; + + // switch mode + if(flags & LFRFIDEventRead) worker->mode_index = LFRFIDWorkerRead; + if(flags & LFRFIDEventWrite) worker->mode_index = LFRFIDWorkerWrite; + if(flags & LFRFIDEventEmulate) worker->mode_index = LFRFIDWorkerEmulate; + if(flags & LFRFIDEventReadRaw) worker->mode_index = LFRFIDWorkerReadRaw; + if(flags & LFRFIDEventEmulateRaw) worker->mode_index = LFRFIDWorkerEmulateRaw; + + // do mode, if it exists + if(lfrfid_worker_modes[worker->mode_index].process) { + lfrfid_worker_modes[worker->mode_index].process(worker); + } + + // reset mode + worker->mode_index = LFRFIDWorkerIdle; + } + } + + return 0; +} \ No newline at end of file diff --git a/lib/lfrfid/lfrfid_worker.h b/lib/lfrfid/lfrfid_worker.h new file mode 100644 index 000000000..def9f89a4 --- /dev/null +++ b/lib/lfrfid/lfrfid_worker.h @@ -0,0 +1,152 @@ +/** + * @file lfrfid_worker.h + * + * LFRFID worker + */ + +#pragma once +#include +#include "protocols/lfrfid_protocols.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + LFRFIDWorkerWriteOK, + LFRFIDWorkerWriteProtocolCannotBeWritten, + LFRFIDWorkerWriteFobCannotBeWritten, + LFRFIDWorkerWriteTooLongToWrite, +} LFRFIDWorkerWriteResult; + +typedef enum { + LFRFIDWorkerReadTypeAuto, + LFRFIDWorkerReadTypeASKOnly, + LFRFIDWorkerReadTypePSKOnly, +} LFRFIDWorkerReadType; + +typedef enum { + LFRFIDWorkerReadSenseStart, // TODO: not implemented + LFRFIDWorkerReadSenseEnd, // TODO: not implemented + LFRFIDWorkerReadSenseCardStart, + LFRFIDWorkerReadSenseCardEnd, + LFRFIDWorkerReadStartASK, + LFRFIDWorkerReadStartPSK, + LFRFIDWorkerReadDone, +} LFRFIDWorkerReadResult; + +typedef enum { + LFRFIDWorkerReadRawFileError, + LFRFIDWorkerReadRawOverrun, +} LFRFIDWorkerReadRawResult; + +typedef enum { + LFRFIDWorkerEmulateRawFileError, + LFRFIDWorkerEmulateRawOverrun, +} LFRFIDWorkerEmulateRawResult; + +typedef void ( + *LFRFIDWorkerReadCallback)(LFRFIDWorkerReadResult result, ProtocolId protocol, void* context); +typedef void (*LFRFIDWorkerWriteCallback)(LFRFIDWorkerWriteResult result, void* context); + +typedef void (*LFRFIDWorkerReadRawCallback)(LFRFIDWorkerReadRawResult result, void* context); +typedef void (*LFRFIDWorkerEmulateRawCallback)(LFRFIDWorkerEmulateRawResult result, void* context); + +typedef struct LFRFIDWorker LFRFIDWorker; + +/** + * Allocate LF-RFID worker + * @return LFRFIDWorker* + */ +LFRFIDWorker* lfrfid_worker_alloc(ProtocolDict* dict); + +/** + * Free LF-RFID worker + * @param worker + */ +void lfrfid_worker_free(LFRFIDWorker* worker); + +/** + * Start LF-RFID worker thread + * @param worker + */ +void lfrfid_worker_start_thread(LFRFIDWorker* worker); + +/** + * Stop LF-RFID worker thread + * @param worker + */ +void lfrfid_worker_stop_thread(LFRFIDWorker* worker); + +/** + * @brief Start read mode + * + * @param worker + * @param type + * @param callback + * @param context + */ +void lfrfid_worker_read_start( + LFRFIDWorker* worker, + LFRFIDWorkerReadType type, + LFRFIDWorkerReadCallback callback, + void* context); + +/** + * @brief Start write mode + * + * @param worker + * @param protocol + * @param callback + * @param context + */ +void lfrfid_worker_write_start( + LFRFIDWorker* worker, + LFRFIDProtocol protocol, + LFRFIDWorkerWriteCallback callback, + void* context); + +/** + * Start emulate mode + * @param worker + */ +void lfrfid_worker_emulate_start(LFRFIDWorker* worker, LFRFIDProtocol protocol); + +/** + * @brief Start raw read mode + * + * @param worker + * @param filename + * @param type + * @param callback + * @param context + */ +void lfrfid_worker_read_raw_start( + LFRFIDWorker* worker, + const char* filename, + LFRFIDWorkerReadType type, + LFRFIDWorkerReadRawCallback callback, + void* context); + +/** + * Emulate raw read mode + * @param worker + * @param filename + * @param callback + * @param context + */ +void lfrfid_worker_emulate_raw_start( + LFRFIDWorker* worker, + const char* filename, + LFRFIDWorkerEmulateRawCallback callback, + void* context); + +/** + * Stop all modes + * @param worker + */ +void lfrfid_worker_stop(LFRFIDWorker* worker); + +#ifdef __cplusplus +} +#endif diff --git a/lib/lfrfid/lfrfid_worker_i.h b/lib/lfrfid/lfrfid_worker_i.h new file mode 100644 index 000000000..33c0bff08 --- /dev/null +++ b/lib/lfrfid/lfrfid_worker_i.h @@ -0,0 +1,64 @@ +/** + * @file lfrfid_worker_i.h + * + * lfrfid worker, internal definitions + */ + +#pragma once +#include +#include "lfrfid_worker.h" +#include "lfrfid_raw_worker.h" +#include "protocols/lfrfid_protocols.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + void (*const process)(LFRFIDWorker* worker); +} LFRFIDWorkerModeType; + +typedef enum { + LFRFIDWorkerIdle, + LFRFIDWorkerRead, + LFRFIDWorkerWrite, + LFRFIDWorkerEmulate, + LFRFIDWorkerReadRaw, + LFRFIDWorkerEmulateRaw, +} LFRFIDWorkerMode; + +struct LFRFIDWorker { + char* raw_filename; + + LFRFIDWorkerMode mode_index; + void* mode_storage; + + FuriEventFlag* events; + FuriThread* thread; + + LFRFIDWorkerReadType read_type; + + LFRFIDWorkerReadCallback read_cb; + LFRFIDWorkerWriteCallback write_cb; + LFRFIDWorkerReadRawCallback read_raw_cb; + LFRFIDWorkerEmulateRawCallback emulate_raw_cb; + + void* cb_ctx; + + ProtocolDict* protocols; + LFRFIDProtocol protocol; +}; + +extern const LFRFIDWorkerModeType lfrfid_worker_modes[]; + +/** + * @brief Check for stop flag + * + * @param worker + * @return bool + */ +bool lfrfid_worker_check_for_stop(LFRFIDWorker* worker); + +#ifdef __cplusplus +} +#endif diff --git a/lib/lfrfid/lfrfid_worker_modes.c b/lib/lfrfid/lfrfid_worker_modes.c new file mode 100644 index 000000000..f41a7194a --- /dev/null +++ b/lib/lfrfid/lfrfid_worker_modes.c @@ -0,0 +1,624 @@ +#include +#include +#include "lfrfid_worker_i.h" +#include "tools/t5577.h" +#include +#include +#include +#include "tools/varint_pair.h" +#include "tools/bit_lib.h" + +#define TAG "LFRFIDWorker" + +/** + * if READ_DEBUG_GPIO is defined: + * gpio_ext_pa7 will repeat signal coming from the comparator + * gpio_ext_pa6 will show load on the decoder + */ +// #define LFRFID_WORKER_READ_DEBUG_GPIO 1 + +#ifdef LFRFID_WORKER_READ_DEBUG_GPIO +#define LFRFID_WORKER_READ_DEBUG_GPIO_VALUE &gpio_ext_pa7 +#define LFRFID_WORKER_READ_DEBUG_GPIO_LOAD &gpio_ext_pa6 +#endif + +#define LFRFID_WORKER_READ_AVERAGE_COUNT 64 +#define LFRFID_WORKER_READ_MIN_TIME_US 16 + +#define LFRFID_WORKER_READ_DROP_TIME_MS 50 +#define LFRFID_WORKER_READ_STABILIZE_TIME_MS 450 +#define LFRFID_WORKER_READ_SWITCH_TIME_MS 1500 + +#define LFRFID_WORKER_WRITE_VERIFY_TIME_MS 1500 +#define LFRFID_WORKER_WRITE_DROP_TIME_MS 50 +#define LFRFID_WORKER_WRITE_TOO_LONG_TIME_MS 10000 + +#define LFRFID_WORKER_WRITE_MAX_UNSUCCESSFUL_READS 5 + +#define LFRFID_WORKER_READ_BUFFER_SIZE 512 +#define LFRFID_WORKER_READ_BUFFER_COUNT 8 + +#define LFRFID_WORKER_EMULATE_BUFFER_SIZE 1024 + +#define LFRFID_WORKER_DELAY_QUANT 50 + +void lfrfid_worker_delay(LFRFIDWorker* worker, uint32_t milliseconds) { + for(uint32_t i = 0; i < (milliseconds / LFRFID_WORKER_DELAY_QUANT); i++) { + if(lfrfid_worker_check_for_stop(worker)) break; + furi_delay_ms(LFRFID_WORKER_DELAY_QUANT); + } +} + +/**************************************************************************************************/ +/********************************************** READ **********************************************/ +/**************************************************************************************************/ + +typedef struct { + BufferStream* stream; + VarintPair* pair; + bool ignore_next_pulse; +} LFRFIDWorkerReadContext; + +static void lfrfid_worker_read_capture(bool level, uint32_t duration, void* context) { + LFRFIDWorkerReadContext* ctx = context; + + // ignore pulse if last pulse was noise + if(ctx->ignore_next_pulse) { + ctx->ignore_next_pulse = false; + return; + } + + // ignore noise spikes + if(duration <= LFRFID_WORKER_READ_MIN_TIME_US) { + if(level) { + ctx->ignore_next_pulse = true; + } + varint_pair_reset(ctx->pair); + return; + } + +#ifdef LFRFID_WORKER_READ_DEBUG_GPIO + furi_hal_gpio_write(LFRFID_WORKER_READ_DEBUG_GPIO_VALUE, level); +#endif + + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + bool need_to_send = varint_pair_pack(ctx->pair, level, duration); + if(need_to_send) { + buffer_stream_send_from_isr( + ctx->stream, + varint_pair_get_data(ctx->pair), + varint_pair_get_size(ctx->pair), + &xHigherPriorityTaskWoken); + varint_pair_reset(ctx->pair); + } + portYIELD_FROM_ISR(xHigherPriorityTaskWoken); +} + +typedef enum { + LFRFIDWorkerReadOK, + LFRFIDWorkerReadExit, + LFRFIDWorkerReadTimeout, +} LFRFIDWorkerReadState; + +static LFRFIDWorkerReadState lfrfid_worker_read_internal( + LFRFIDWorker* worker, + LFRFIDFeature feature, + uint32_t timeout, + ProtocolId* result_protocol) { + LFRFIDWorkerReadState state = LFRFIDWorkerReadTimeout; + furi_hal_rfid_pins_read(); + + if(feature & LFRFIDFeatureASK) { + furi_hal_rfid_tim_read(125000, 0.5); + FURI_LOG_D(TAG, "Start ASK"); + if(worker->read_cb) { + worker->read_cb(LFRFIDWorkerReadStartASK, PROTOCOL_NO, worker->cb_ctx); + } + } else { + furi_hal_rfid_tim_read(62500, 0.25); + FURI_LOG_D(TAG, "Start PSK"); + if(worker->read_cb) { + worker->read_cb(LFRFIDWorkerReadStartPSK, PROTOCOL_NO, worker->cb_ctx); + } + } + + furi_hal_rfid_tim_read_start(); + + // stabilize detector + lfrfid_worker_delay(worker, LFRFID_WORKER_READ_STABILIZE_TIME_MS); + + protocol_dict_decoders_start(worker->protocols); + +#ifdef LFRFID_WORKER_READ_DEBUG_GPIO + furi_hal_gpio_init_simple(LFRFID_WORKER_READ_DEBUG_GPIO_VALUE, GpioModeOutputPushPull); + furi_hal_gpio_init_simple(LFRFID_WORKER_READ_DEBUG_GPIO_LOAD, GpioModeOutputPushPull); +#endif + + LFRFIDWorkerReadContext ctx; + ctx.pair = varint_pair_alloc(); + ctx.stream = + buffer_stream_alloc(LFRFID_WORKER_READ_BUFFER_SIZE, LFRFID_WORKER_READ_BUFFER_COUNT); + + furi_hal_rfid_tim_read_capture_start(lfrfid_worker_read_capture, &ctx); + + *result_protocol = PROTOCOL_NO; + ProtocolId last_protocol = PROTOCOL_NO; + size_t last_size = protocol_dict_get_max_data_size(worker->protocols); + uint8_t* last_data = malloc(last_size); + uint8_t* protocol_data = malloc(last_size); + size_t last_read_count = 0; + + uint32_t switch_os_tick_last = furi_get_tick(); + + uint32_t average_duration = 0; + uint32_t average_pulse = 0; + size_t average_index = 0; + bool card_detected = false; + + FURI_LOG_D(TAG, "Read started"); + while(true) { + if(lfrfid_worker_check_for_stop(worker)) { + state = LFRFIDWorkerReadExit; + break; + } + + Buffer* buffer = buffer_stream_receive(ctx.stream, 100); + +#ifdef LFRFID_WORKER_READ_DEBUG_GPIO + furi_hal_gpio_write(LFRFID_WORKER_READ_DEBUG_GPIO_LOAD, true); +#endif + + if(buffer_stream_get_overrun_count(ctx.stream) > 0) { + FURI_LOG_E(TAG, "Read overrun, recovering"); + buffer_stream_reset(ctx.stream); + continue; + } + + if(buffer == NULL) { + continue; + } + + size_t size = buffer_get_size(buffer); + uint8_t* data = buffer_get_data(buffer); + size_t index = 0; + + while(index < size) { + uint32_t duration; + uint32_t pulse; + size_t tmp_size; + + if(!varint_pair_unpack(&data[index], size - index, &pulse, &duration, &tmp_size)) { + FURI_LOG_E(TAG, "can't unpack varint pair"); + break; + } else { + index += tmp_size; + + average_duration += duration; + average_pulse += pulse; + average_index++; + if(average_index >= LFRFID_WORKER_READ_AVERAGE_COUNT) { + float average = (float)average_pulse / (float)average_duration; + average_pulse = 0; + average_duration = 0; + average_index = 0; + + if(worker->read_cb) { + if(average > 0.2 && average < 0.8) { + if(!card_detected) { + card_detected = true; + worker->read_cb( + LFRFIDWorkerReadSenseStart, PROTOCOL_NO, worker->cb_ctx); + } + } else { + if(card_detected) { + card_detected = false; + worker->read_cb( + LFRFIDWorkerReadSenseEnd, PROTOCOL_NO, worker->cb_ctx); + } + } + } + } + + ProtocolId protocol = PROTOCOL_NO; + + protocol = protocol_dict_decoders_feed_by_feature( + worker->protocols, feature, true, pulse); + if(protocol == PROTOCOL_NO) { + protocol = protocol_dict_decoders_feed_by_feature( + worker->protocols, feature, false, duration - pulse); + } + + if(protocol != PROTOCOL_NO) { + // reset switch timer + switch_os_tick_last = furi_get_tick(); + + size_t protocol_data_size = + protocol_dict_get_data_size(worker->protocols, protocol); + protocol_dict_get_data( + worker->protocols, protocol, protocol_data, protocol_data_size); + + // validate protocol + if(protocol == last_protocol && + memcmp(last_data, protocol_data, protocol_data_size) == 0) { + last_read_count = last_read_count + 1; + + size_t validation_count = + protocol_dict_get_validate_count(worker->protocols, protocol); + + if(last_read_count >= validation_count) { + state = LFRFIDWorkerReadOK; + *result_protocol = protocol; + break; + } + } else { + if(last_protocol == PROTOCOL_NO && worker->read_cb) { + worker->read_cb( + LFRFIDWorkerReadSenseCardStart, protocol, worker->cb_ctx); + } + + last_protocol = protocol; + memcpy(last_data, protocol_data, protocol_data_size); + last_read_count = 0; + } + + string_t string_info; + string_init(string_info); + for(uint8_t i = 0; i < protocol_data_size; i++) { + if(i != 0) { + string_cat_printf(string_info, " "); + } + + string_cat_printf(string_info, "%02X", protocol_data[i]); + } + + FURI_LOG_D( + TAG, + "%s, %d, [%s]", + protocol_dict_get_name(worker->protocols, protocol), + last_read_count, + string_get_cstr(string_info)); + string_clear(string_info); + + protocol_dict_decoders_start(worker->protocols); + } + } + } + + buffer_reset(buffer); + +#ifdef LFRFID_WORKER_READ_DEBUG_GPIO + furi_hal_gpio_write(LFRFID_WORKER_READ_DEBUG_GPIO_LOAD, false); +#endif + + if(*result_protocol != PROTOCOL_NO) { + break; + } + + if((furi_get_tick() - switch_os_tick_last) > timeout) { + state = LFRFIDWorkerReadTimeout; + break; + } + } + + FURI_LOG_D(TAG, "Read stopped"); + + if(last_protocol != PROTOCOL_NO && worker->read_cb) { + worker->read_cb(LFRFIDWorkerReadSenseCardEnd, last_protocol, worker->cb_ctx); + } + + if(card_detected && worker->read_cb) { + worker->read_cb(LFRFIDWorkerReadSenseEnd, last_protocol, worker->cb_ctx); + } + + furi_hal_rfid_tim_read_capture_stop(); + furi_hal_rfid_tim_read_stop(); + furi_hal_rfid_pins_reset(); + + varint_pair_free(ctx.pair); + buffer_stream_free(ctx.stream); + + free(protocol_data); + free(last_data); + +#ifdef LFRFID_WORKER_READ_DEBUG_GPIO + furi_hal_gpio_init_simple(LFRFID_WORKER_READ_DEBUG_GPIO_VALUE, GpioModeAnalog); + furi_hal_gpio_init_simple(LFRFID_WORKER_READ_DEBUG_GPIO_LOAD, GpioModeAnalog); +#endif + + return state; +} + +static void lfrfid_worker_mode_read_process(LFRFIDWorker* worker) { + LFRFIDFeature feature = LFRFIDFeatureASK; + ProtocolId read_result = PROTOCOL_NO; + LFRFIDWorkerReadState state; + + if(worker->read_type == LFRFIDWorkerReadTypePSKOnly) { + feature = LFRFIDFeaturePSK; + } else { + feature = LFRFIDFeatureASK; + } + + if(worker->read_type == LFRFIDWorkerReadTypeAuto) { + while(1) { + // read for a while + state = lfrfid_worker_read_internal( + worker, feature, LFRFID_WORKER_READ_SWITCH_TIME_MS, &read_result); + + if(state == LFRFIDWorkerReadOK || state == LFRFIDWorkerReadExit) { + break; + } + + // switch to next feature + if(feature == LFRFIDFeatureASK) { + feature = LFRFIDFeaturePSK; + } else { + feature = LFRFIDFeatureASK; + } + + lfrfid_worker_delay(worker, LFRFID_WORKER_READ_DROP_TIME_MS); + } + } else { + while(1) { + if(worker->read_type == LFRFIDWorkerReadTypeASKOnly) { + state = lfrfid_worker_read_internal(worker, feature, UINT32_MAX, &read_result); + } else { + state = lfrfid_worker_read_internal( + worker, feature, LFRFID_WORKER_READ_SWITCH_TIME_MS, &read_result); + } + + if(state == LFRFIDWorkerReadOK || state == LFRFIDWorkerReadExit) { + break; + } + + lfrfid_worker_delay(worker, LFRFID_WORKER_READ_DROP_TIME_MS); + } + } + + if(state == LFRFIDWorkerReadOK && worker->read_cb) { + worker->read_cb(LFRFIDWorkerReadDone, read_result, worker->cb_ctx); + } +} + +/**************************************************************************************************/ +/******************************************** EMULATE *********************************************/ +/**************************************************************************************************/ + +typedef struct { + uint32_t duration[LFRFID_WORKER_EMULATE_BUFFER_SIZE]; + uint32_t pulse[LFRFID_WORKER_EMULATE_BUFFER_SIZE]; +} LFRFIDWorkerEmulateBuffer; + +typedef enum { + HalfTransfer, + TransferComplete, +} LFRFIDWorkerEmulateDMAEvent; + +static void lfrfid_worker_emulate_dma_isr(bool half, void* context) { + StreamBufferHandle_t stream = context; + uint32_t flag = half ? HalfTransfer : TransferComplete; + xStreamBufferSendFromISR(stream, &flag, sizeof(uint32_t), pdFALSE); +} + +static void lfrfid_worker_mode_emulate_process(LFRFIDWorker* worker) { + LFRFIDWorkerEmulateBuffer* buffer = malloc(sizeof(LFRFIDWorkerEmulateBuffer)); + StreamBufferHandle_t stream = xStreamBufferCreate(sizeof(uint32_t), sizeof(uint32_t)); + LFRFIDProtocol protocol = worker->protocol; + PulseGlue* pulse_glue = pulse_glue_alloc(); + + protocol_dict_encoder_start(worker->protocols, protocol); + + for(size_t i = 0; i < LFRFID_WORKER_EMULATE_BUFFER_SIZE; i++) { + bool pulse_pop = false; + while(!pulse_pop) { + LevelDuration level_duration = + protocol_dict_encoder_yield(worker->protocols, protocol); + pulse_pop = pulse_glue_push( + pulse_glue, + level_duration_get_level(level_duration), + level_duration_get_duration(level_duration)); + } + uint32_t duration, pulse; + pulse_glue_pop(pulse_glue, &duration, &pulse); + buffer->duration[i] = duration - 1; + buffer->pulse[i] = pulse; + } + +#ifdef LFRFID_WORKER_READ_DEBUG_GPIO + furi_hal_gpio_init_simple(LFRFID_WORKER_READ_DEBUG_GPIO_LOAD, GpioModeOutputPushPull); +#endif + + furi_hal_rfid_tim_emulate_dma_start( + buffer->duration, + buffer->pulse, + LFRFID_WORKER_EMULATE_BUFFER_SIZE, + lfrfid_worker_emulate_dma_isr, + stream); + + while(true) { + uint32_t flag = 0; + size_t size = xStreamBufferReceive(stream, &flag, sizeof(uint32_t), 100); + +#ifdef LFRFID_WORKER_READ_DEBUG_GPIO + furi_hal_gpio_write(LFRFID_WORKER_READ_DEBUG_GPIO_LOAD, true); +#endif + + if(size == sizeof(uint32_t)) { + size_t start = 0; + + if(flag == HalfTransfer) { + start = 0; + } else if(flag == TransferComplete) { + start = (LFRFID_WORKER_EMULATE_BUFFER_SIZE / 2); + } + + for(size_t i = 0; i < (LFRFID_WORKER_EMULATE_BUFFER_SIZE / 2); i++) { + bool pulse_pop = false; + while(!pulse_pop) { + LevelDuration level_duration = + protocol_dict_encoder_yield(worker->protocols, protocol); + pulse_pop = pulse_glue_push( + pulse_glue, + level_duration_get_level(level_duration), + level_duration_get_duration(level_duration)); + } + uint32_t duration, pulse; + pulse_glue_pop(pulse_glue, &duration, &pulse); + buffer->duration[start + i] = duration - 1; + buffer->pulse[start + i] = pulse; + } + } + + if(lfrfid_worker_check_for_stop(worker)) { + break; + } + +#ifdef LFRFID_WORKER_READ_DEBUG_GPIO + furi_hal_gpio_write(LFRFID_WORKER_READ_DEBUG_GPIO_LOAD, false); +#endif + } + + furi_hal_rfid_tim_emulate_dma_stop(); + +#ifdef LFRFID_WORKER_READ_DEBUG_GPIO + furi_hal_gpio_init_simple(LFRFID_WORKER_READ_DEBUG_GPIO_LOAD, GpioModeAnalog); +#endif + + free(buffer); + vStreamBufferDelete(stream); + pulse_glue_free(pulse_glue); +} + +/**************************************************************************************************/ +/********************************************* WRITE **********************************************/ +/**************************************************************************************************/ + +static void lfrfid_worker_mode_write_process(LFRFIDWorker* worker) { + LFRFIDProtocol protocol = worker->protocol; + LFRFIDWriteRequest* request = malloc(sizeof(LFRFIDWriteRequest)); + request->write_type = LFRFIDWriteTypeT5577; + + bool can_be_written = protocol_dict_get_write_data(worker->protocols, protocol, request); + + uint32_t write_start_time = furi_get_tick(); + bool too_long = false; + size_t unsuccessful_reads = 0; + + size_t data_size = protocol_dict_get_data_size(worker->protocols, protocol); + uint8_t* verify_data = malloc(data_size); + uint8_t* read_data = malloc(data_size); + protocol_dict_get_data(worker->protocols, protocol, verify_data, data_size); + + if(can_be_written) { + while(!lfrfid_worker_check_for_stop(worker)) { + FURI_LOG_D(TAG, "Data write"); + t5577_write(&request->t5577); + + ProtocolId read_result = PROTOCOL_NO; + LFRFIDWorkerReadState state = lfrfid_worker_read_internal( + worker, + protocol_dict_get_features(worker->protocols, protocol), + LFRFID_WORKER_WRITE_VERIFY_TIME_MS, + &read_result); + + if(state == LFRFIDWorkerReadOK) { + protocol_dict_get_data(worker->protocols, protocol, read_data, data_size); + + if(memcmp(read_data, verify_data, data_size) == 0) { + if(worker->write_cb) { + worker->write_cb(LFRFIDWorkerWriteOK, worker->cb_ctx); + } + break; + } else { + unsuccessful_reads++; + + if(unsuccessful_reads == LFRFID_WORKER_WRITE_MAX_UNSUCCESSFUL_READS) { + if(worker->write_cb) { + worker->write_cb(LFRFIDWorkerWriteFobCannotBeWritten, worker->cb_ctx); + } + } + } + } else if(state == LFRFIDWorkerReadExit) { + break; + } + + if(!too_long && + (furi_get_tick() - write_start_time) > LFRFID_WORKER_WRITE_TOO_LONG_TIME_MS) { + too_long = true; + if(worker->write_cb) { + worker->write_cb(LFRFIDWorkerWriteTooLongToWrite, worker->cb_ctx); + } + } + + lfrfid_worker_delay(worker, LFRFID_WORKER_WRITE_DROP_TIME_MS); + } + } else { + if(worker->write_cb) { + worker->write_cb(LFRFIDWorkerWriteProtocolCannotBeWritten, worker->cb_ctx); + } + } + + free(request); + free(verify_data); + free(read_data); +} + +/**************************************************************************************************/ +/******************************************* READ RAW *********************************************/ +/**************************************************************************************************/ + +static void lfrfid_worker_mode_read_raw_process(LFRFIDWorker* worker) { + LFRFIDRawWorker* raw_worker = lfrfid_raw_worker_alloc(); + + switch(worker->read_type) { + case LFRFIDWorkerReadTypePSKOnly: + lfrfid_raw_worker_start_read( + raw_worker, worker->raw_filename, 62500, 0.25, worker->read_raw_cb, worker->cb_ctx); + break; + case LFRFIDWorkerReadTypeASKOnly: + lfrfid_raw_worker_start_read( + raw_worker, worker->raw_filename, 125000, 0.5, worker->read_raw_cb, worker->cb_ctx); + break; + default: + furi_crash("RAW can be only PSK or ASK"); + break; + } + + while(!lfrfid_worker_check_for_stop(worker)) { + furi_delay_ms(100); + } + + lfrfid_raw_worker_stop(raw_worker); + lfrfid_raw_worker_free(raw_worker); +} + +/**************************************************************************************************/ +/***************************************** EMULATE RAW ********************************************/ +/**************************************************************************************************/ + +static void lfrfid_worker_mode_emulate_raw_process(LFRFIDWorker* worker) { + LFRFIDRawWorker* raw_worker = lfrfid_raw_worker_alloc(); + + lfrfid_raw_worker_start_emulate( + raw_worker, worker->raw_filename, worker->emulate_raw_cb, worker->cb_ctx); + + while(!lfrfid_worker_check_for_stop(worker)) { + furi_delay_ms(100); + } + + lfrfid_raw_worker_stop(raw_worker); + lfrfid_raw_worker_free(raw_worker); +} + +/**************************************************************************************************/ +/******************************************** MODES ***********************************************/ +/**************************************************************************************************/ + +const LFRFIDWorkerModeType lfrfid_worker_modes[] = { + [LFRFIDWorkerIdle] = {.process = NULL}, + [LFRFIDWorkerRead] = {.process = lfrfid_worker_mode_read_process}, + [LFRFIDWorkerWrite] = {.process = lfrfid_worker_mode_write_process}, + [LFRFIDWorkerEmulate] = {.process = lfrfid_worker_mode_emulate_process}, + [LFRFIDWorkerReadRaw] = {.process = lfrfid_worker_mode_read_raw_process}, + [LFRFIDWorkerEmulateRaw] = {.process = lfrfid_worker_mode_emulate_raw_process}, +}; \ No newline at end of file diff --git a/lib/lfrfid/protocols/lfrfid_protocols.c b/lib/lfrfid/protocols/lfrfid_protocols.c new file mode 100644 index 000000000..5df01b19e --- /dev/null +++ b/lib/lfrfid/protocols/lfrfid_protocols.c @@ -0,0 +1,22 @@ +#include "lfrfid_protocols.h" +#include "protocol_em4100.h" +#include "protocol_h10301.h" +#include "protocol_indala26.h" +#include "protocol_io_prox_xsf.h" +#include "protocol_awid.h" +#include "protocol_fdx_a.h" +#include "protocol_fdx_b.h" +#include "protocol_hid_generic.h" +#include "protocol_hid_ex_generic.h" + +const ProtocolBase* lfrfid_protocols[] = { + [LFRFIDProtocolEM4100] = &protocol_em4100, + [LFRFIDProtocolH10301] = &protocol_h10301, + [LFRFIDProtocolIndala26] = &protocol_indala26, + [LFRFIDProtocolIOProxXSF] = &protocol_io_prox_xsf, + [LFRFIDProtocolAwid] = &protocol_awid, + [LFRFIDProtocolFDXA] = &protocol_fdx_a, + [LFRFIDProtocolFDXB] = &protocol_fdx_b, + [LFRFIDProtocolHidGeneric] = &protocol_hid_generic, + [LFRFIDProtocolHidExGeneric] = &protocol_hid_ex_generic, +}; \ No newline at end of file diff --git a/lib/lfrfid/protocols/lfrfid_protocols.h b/lib/lfrfid/protocols/lfrfid_protocols.h new file mode 100644 index 000000000..4b8f6573d --- /dev/null +++ b/lib/lfrfid/protocols/lfrfid_protocols.h @@ -0,0 +1,35 @@ +#pragma once +#include +#include "../tools/t5577.h" + +typedef enum { + LFRFIDFeatureASK = 1 << 0, /** ASK Demodulation */ + LFRFIDFeaturePSK = 1 << 1, /** PSK Demodulation */ +} LFRFIDFeature; + +typedef enum { + LFRFIDProtocolEM4100, + LFRFIDProtocolH10301, + LFRFIDProtocolIndala26, + LFRFIDProtocolIOProxXSF, + LFRFIDProtocolAwid, + LFRFIDProtocolFDXA, + LFRFIDProtocolFDXB, + LFRFIDProtocolHidGeneric, + LFRFIDProtocolHidExGeneric, + + LFRFIDProtocolMax, +} LFRFIDProtocol; + +extern const ProtocolBase* lfrfid_protocols[]; + +typedef enum { + LFRFIDWriteTypeT5577, +} LFRFIDWriteType; + +typedef struct { + LFRFIDWriteType write_type; + union { + LFRFIDT5577 t5577; + }; +} LFRFIDWriteRequest; \ No newline at end of file diff --git a/lib/lfrfid/protocols/protocol_awid.c b/lib/lfrfid/protocols/protocol_awid.c new file mode 100644 index 000000000..243b5edeb --- /dev/null +++ b/lib/lfrfid/protocols/protocol_awid.c @@ -0,0 +1,239 @@ +#include +#include +#include +#include +#include +#include "lfrfid_protocols.h" + +#define JITTER_TIME (20) +#define MIN_TIME (64 - JITTER_TIME) +#define MAX_TIME (80 + JITTER_TIME) + +#define AWID_DECODED_DATA_SIZE (9) + +#define AWID_ENCODED_BIT_SIZE (96) +#define AWID_ENCODED_DATA_SIZE (((AWID_ENCODED_BIT_SIZE) / 8) + 1) +#define AWID_ENCODED_DATA_LAST (AWID_ENCODED_DATA_SIZE - 1) + +typedef struct { + FSKDemod* fsk_demod; +} ProtocolAwidDecoder; + +typedef struct { + FSKOsc* fsk_osc; + uint8_t encoded_index; +} ProtocolAwidEncoder; + +typedef struct { + ProtocolAwidDecoder decoder; + ProtocolAwidEncoder encoder; + uint8_t encoded_data[AWID_ENCODED_DATA_SIZE]; + uint8_t data[AWID_DECODED_DATA_SIZE]; +} ProtocolAwid; + +ProtocolAwid* protocol_awid_alloc(void) { + ProtocolAwid* protocol = malloc(sizeof(ProtocolAwid)); + protocol->decoder.fsk_demod = fsk_demod_alloc(MIN_TIME, 6, MAX_TIME, 5); + protocol->encoder.fsk_osc = fsk_osc_alloc(8, 10, 50); + + return protocol; +}; + +void protocol_awid_free(ProtocolAwid* protocol) { + fsk_demod_free(protocol->decoder.fsk_demod); + fsk_osc_free(protocol->encoder.fsk_osc); + free(protocol); +}; + +uint8_t* protocol_awid_get_data(ProtocolAwid* protocol) { + return protocol->data; +}; + +void protocol_awid_decoder_start(ProtocolAwid* protocol) { + memset(protocol->encoded_data, 0, AWID_ENCODED_DATA_SIZE); +}; + +static bool protocol_awid_can_be_decoded(const uint8_t* data) { + bool result = false; + + // Index map + // 0 10 20 30 40 50 60 + // | | | | | | | + // 01234567 890 1 234 5 678 9 012 3 456 7 890 1 234 5 678 9 012 3 456 7 890 1 234 5 678 9 012 3 - to 96 + // ----------------------------------------------------------------------------- + // 00000001 000 1 110 1 101 1 011 1 101 1 010 0 000 1 000 1 010 0 001 0 110 1 100 0 000 1 000 1 + // preamble bbb o bbb o bbw o fff o fff o ffc o ccc o ccc o ccc o ccc o ccc o wxx o xxx o xxx o - to 96 + // |---26 bit---| |-----117----||-------------142-------------| + // b = format bit len, o = odd parity of last 3 bits + // f = facility code, c = card number + // w = wiegand parity + // (26 bit format shown) + + do { + // check preamble and spacing + if(data[0] != 0b00000001 || data[AWID_ENCODED_DATA_LAST] != 0b00000001) break; + + // check odd parity for every 4 bits starting from the second byte + bool parity_error = bit_lib_test_parity(data, 8, 88, BitLibParityOdd, 4); + if(parity_error) break; + + result = true; + } while(false); + + return result; +} + +static void protocol_awid_decode(uint8_t* encoded_data, uint8_t* decoded_data) { + bit_lib_remove_bit_every_nth(encoded_data, 8, 88, 4); + bit_lib_copy_bits(decoded_data, 0, 66, encoded_data, 8); +} + +bool protocol_awid_decoder_feed(ProtocolAwid* protocol, bool level, uint32_t duration) { + bool value; + uint32_t count; + bool result = false; + + fsk_demod_feed(protocol->decoder.fsk_demod, level, duration, &value, &count); + if(count > 0) { + for(size_t i = 0; i < count; i++) { + bit_lib_push_bit(protocol->encoded_data, AWID_ENCODED_DATA_SIZE, value); + if(protocol_awid_can_be_decoded(protocol->encoded_data)) { + protocol_awid_decode(protocol->encoded_data, protocol->data); + + result = true; + break; + } + } + } + + return result; +}; + +static void protocol_awid_encode(const uint8_t* decoded_data, uint8_t* encoded_data) { + memset(encoded_data, 0, AWID_ENCODED_DATA_SIZE); + + // preamble + bit_lib_set_bits(encoded_data, 0, 0b00000001, 8); + + for(size_t i = 0; i < 88 / 4; i++) { + uint8_t value = bit_lib_get_bits(decoded_data, i * 3, 3) << 1; + value |= bit_lib_test_parity_32(value, BitLibParityOdd); + bit_lib_set_bits(encoded_data, 8 + i * 4, value, 4); + } +}; + +bool protocol_awid_encoder_start(ProtocolAwid* protocol) { + protocol_awid_encode(protocol->data, (uint8_t*)protocol->encoded_data); + protocol->encoder.encoded_index = 0; + fsk_osc_reset(protocol->encoder.fsk_osc); + return true; +}; + +LevelDuration protocol_awid_encoder_yield(ProtocolAwid* protocol) { + bool level; + uint32_t duration; + + bool bit = bit_lib_get_bit(protocol->encoded_data, protocol->encoder.encoded_index); + bool advance = fsk_osc_next_half(protocol->encoder.fsk_osc, bit, &level, &duration); + + if(advance) { + bit_lib_increment_index(protocol->encoder.encoded_index, AWID_ENCODED_BIT_SIZE); + } + return level_duration_make(level, duration); +}; + +void protocol_awid_render_data(ProtocolAwid* protocol, string_t result) { + // Index map + // 0 10 20 30 40 50 60 + // | | | | | | | + // 01234567 8 90123456 7890123456789012 3 456789012345678901234567890123456 + // ------------------------------------------------------------------------ + // 00011010 1 01110101 0000000010001110 1 000000000000000000000000000000000 + // bbbbbbbb w ffffffff cccccccccccccccc w xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + // |26 bit| |-117--| |-----142------| + // b = format bit len, o = odd parity of last 3 bits + // f = facility code, c = card number + // w = wiegand parity + // (26 bit format shown) + + uint8_t* decoded_data = protocol->data; + uint8_t format_length = decoded_data[0]; + + string_cat_printf(result, "Format: %d\r\n", format_length); + if(format_length == 26) { + uint8_t facility; + bit_lib_copy_bits(&facility, 0, 8, decoded_data, 9); + + uint16_t card_id; + bit_lib_copy_bits((uint8_t*)&card_id, 8, 8, decoded_data, 17); + bit_lib_copy_bits((uint8_t*)&card_id, 0, 8, decoded_data, 25); + string_cat_printf(result, "Facility: %d\r\n", facility); + string_cat_printf(result, "Card: %d", card_id); + } else { + // print 66 bits as hex + string_cat_printf(result, "Data: "); + for(size_t i = 0; i < AWID_DECODED_DATA_SIZE; i++) { + string_cat_printf(result, "%02X", decoded_data[i]); + } + } +}; + +void protocol_awid_render_brief_data(ProtocolAwid* protocol, string_t result) { + uint8_t* decoded_data = protocol->data; + uint8_t format_length = decoded_data[0]; + + string_cat_printf(result, "Format: %d\r\n", format_length); + if(format_length == 26) { + uint8_t facility; + bit_lib_copy_bits(&facility, 0, 8, decoded_data, 9); + + uint16_t card_id; + bit_lib_copy_bits((uint8_t*)&card_id, 8, 8, decoded_data, 17); + bit_lib_copy_bits((uint8_t*)&card_id, 0, 8, decoded_data, 25); + string_cat_printf(result, "ID: %03u,%05u", facility, card_id); + } else { + string_cat_printf(result, "Data: unknown"); + } +}; + +bool protocol_awid_write_data(ProtocolAwid* protocol, void* data) { + LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data; + bool result = false; + + protocol_awid_encode(protocol->data, (uint8_t*)protocol->encoded_data); + + if(request->write_type == LFRFIDWriteTypeT5577) { + request->t5577.block[0] = LFRFID_T5577_MODULATION_FSK2a | LFRFID_T5577_BITRATE_RF_50 | + (3 << LFRFID_T5577_MAXBLOCK_SHIFT); + request->t5577.block[1] = bit_lib_get_bits_32(protocol->encoded_data, 0, 32); + request->t5577.block[2] = bit_lib_get_bits_32(protocol->encoded_data, 32, 32); + request->t5577.block[3] = bit_lib_get_bits_32(protocol->encoded_data, 64, 32); + request->t5577.blocks_to_write = 4; + result = true; + } + return result; +}; + +const ProtocolBase protocol_awid = { + .name = "AWID", + .manufacturer = "AWIG", + .data_size = AWID_DECODED_DATA_SIZE, + .features = LFRFIDFeatureASK, + .validate_count = 3, + .alloc = (ProtocolAlloc)protocol_awid_alloc, + .free = (ProtocolFree)protocol_awid_free, + .get_data = (ProtocolGetData)protocol_awid_get_data, + .decoder = + { + .start = (ProtocolDecoderStart)protocol_awid_decoder_start, + .feed = (ProtocolDecoderFeed)protocol_awid_decoder_feed, + }, + .encoder = + { + .start = (ProtocolEncoderStart)protocol_awid_encoder_start, + .yield = (ProtocolEncoderYield)protocol_awid_encoder_yield, + }, + .render_data = (ProtocolRenderData)protocol_awid_render_data, + .render_brief_data = (ProtocolRenderData)protocol_awid_render_brief_data, + .write_data = (ProtocolWriteData)protocol_awid_write_data, +}; \ No newline at end of file diff --git a/lib/lfrfid/protocols/protocol_awid.h b/lib/lfrfid/protocols/protocol_awid.h new file mode 100644 index 000000000..51a4ea52f --- /dev/null +++ b/lib/lfrfid/protocols/protocol_awid.h @@ -0,0 +1,4 @@ +#pragma once +#include + +extern const ProtocolBase protocol_awid; \ No newline at end of file diff --git a/lib/lfrfid/protocols/protocol_em4100.c b/lib/lfrfid/protocols/protocol_em4100.c new file mode 100644 index 000000000..92721fcdc --- /dev/null +++ b/lib/lfrfid/protocols/protocol_em4100.c @@ -0,0 +1,292 @@ +#include +#include +#include +#include "lfrfid_protocols.h" + +typedef uint64_t EM4100DecodedData; + +#define EM_HEADER_POS (55) +#define EM_HEADER_MASK (0x1FFLLU << EM_HEADER_POS) + +#define EM_FIRST_ROW_POS (50) + +#define EM_ROW_COUNT (10) +#define EM_COLUMN_COUNT (4) +#define EM_BITS_PER_ROW_COUNT (EM_COLUMN_COUNT + 1) + +#define EM_COLUMN_POS (4) +#define EM_STOP_POS (0) +#define EM_STOP_MASK (0x1LLU << EM_STOP_POS) + +#define EM_HEADER_AND_STOP_MASK (EM_HEADER_MASK | EM_STOP_MASK) +#define EM_HEADER_AND_STOP_DATA (EM_HEADER_MASK) + +#define EM4100_DECODED_DATA_SIZE (5) +#define EM4100_ENCODED_DATA_SIZE (sizeof(EM4100DecodedData)) + +#define EM4100_CLOCK_PER_BIT (64) + +#define EM_READ_SHORT_TIME (256) +#define EM_READ_LONG_TIME (512) +#define EM_READ_JITTER_TIME (100) + +#define EM_READ_SHORT_TIME_LOW (EM_READ_SHORT_TIME - EM_READ_JITTER_TIME) +#define EM_READ_SHORT_TIME_HIGH (EM_READ_SHORT_TIME + EM_READ_JITTER_TIME) +#define EM_READ_LONG_TIME_LOW (EM_READ_LONG_TIME - EM_READ_JITTER_TIME) +#define EM_READ_LONG_TIME_HIGH (EM_READ_LONG_TIME + EM_READ_JITTER_TIME) + +typedef struct { + uint8_t data[EM4100_DECODED_DATA_SIZE]; + + EM4100DecodedData encoded_data; + uint8_t encoded_data_index; + bool encoded_polarity; + + ManchesterState decoder_manchester_state; +} ProtocolEM4100; + +ProtocolEM4100* protocol_em4100_alloc(void) { + ProtocolEM4100* proto = malloc(sizeof(ProtocolEM4100)); + return (void*)proto; +}; + +void protocol_em4100_free(ProtocolEM4100* proto) { + free(proto); +}; + +uint8_t* protocol_em4100_get_data(ProtocolEM4100* proto) { + return proto->data; +}; + +static void em4100_decode( + const uint8_t* encoded_data, + const uint8_t encoded_data_size, + uint8_t* decoded_data, + const uint8_t decoded_data_size) { + furi_check(decoded_data_size >= EM4100_DECODED_DATA_SIZE); + furi_check(encoded_data_size >= EM4100_ENCODED_DATA_SIZE); + + uint8_t decoded_data_index = 0; + EM4100DecodedData card_data = *((EM4100DecodedData*)(encoded_data)); + + // clean result + memset(decoded_data, 0, decoded_data_size); + + // header + for(uint8_t i = 0; i < 9; i++) { + card_data = card_data << 1; + } + + // nibbles + uint8_t value = 0; + for(uint8_t r = 0; r < EM_ROW_COUNT; r++) { + uint8_t nibble = 0; + for(uint8_t i = 0; i < 5; i++) { + if(i < 4) nibble = (nibble << 1) | (card_data & (1LLU << 63) ? 1 : 0); + card_data = card_data << 1; + } + value = (value << 4) | nibble; + if(r % 2) { + decoded_data[decoded_data_index] |= value; + decoded_data_index++; + value = 0; + } + } +} + +static bool em4100_can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) { + furi_check(encoded_data_size >= EM4100_ENCODED_DATA_SIZE); + const EM4100DecodedData* card_data = (EM4100DecodedData*)encoded_data; + + // check header and stop bit + if((*card_data & EM_HEADER_AND_STOP_MASK) != EM_HEADER_AND_STOP_DATA) return false; + + // check row parity + for(uint8_t i = 0; i < EM_ROW_COUNT; i++) { + uint8_t parity_sum = 0; + + for(uint8_t j = 0; j < EM_BITS_PER_ROW_COUNT; j++) { + parity_sum += (*card_data >> (EM_FIRST_ROW_POS - i * EM_BITS_PER_ROW_COUNT + j)) & 1; + } + + if((parity_sum % 2)) { + return false; + } + } + + // check columns parity + for(uint8_t i = 0; i < EM_COLUMN_COUNT; i++) { + uint8_t parity_sum = 0; + + for(uint8_t j = 0; j < EM_ROW_COUNT + 1; j++) { + parity_sum += (*card_data >> (EM_COLUMN_POS - i + j * EM_BITS_PER_ROW_COUNT)) & 1; + } + + if((parity_sum % 2)) { + return false; + } + } + + return true; +} + +void protocol_em4100_decoder_start(ProtocolEM4100* proto) { + memset(proto->data, 0, EM4100_DECODED_DATA_SIZE); + proto->encoded_data = 0; + manchester_advance( + proto->decoder_manchester_state, + ManchesterEventReset, + &proto->decoder_manchester_state, + NULL); +}; + +bool protocol_em4100_decoder_feed(ProtocolEM4100* proto, bool level, uint32_t duration) { + bool result = false; + + ManchesterEvent event = ManchesterEventReset; + + if(duration > EM_READ_SHORT_TIME_LOW && duration < EM_READ_SHORT_TIME_HIGH) { + if(!level) { + event = ManchesterEventShortHigh; + } else { + event = ManchesterEventShortLow; + } + } else if(duration > EM_READ_LONG_TIME_LOW && duration < EM_READ_LONG_TIME_HIGH) { + if(!level) { + event = ManchesterEventLongHigh; + } else { + event = ManchesterEventLongLow; + } + } + + if(event != ManchesterEventReset) { + bool data; + bool data_ok = manchester_advance( + proto->decoder_manchester_state, event, &proto->decoder_manchester_state, &data); + + if(data_ok) { + proto->encoded_data = (proto->encoded_data << 1) | data; + + if(em4100_can_be_decoded((uint8_t*)&proto->encoded_data, sizeof(EM4100DecodedData))) { + em4100_decode( + (uint8_t*)&proto->encoded_data, + sizeof(EM4100DecodedData), + proto->data, + EM4100_DECODED_DATA_SIZE); + result = true; + } + } + } + + return result; +}; + +static void em4100_write_nibble(bool low_nibble, uint8_t data, EM4100DecodedData* encoded_data) { + uint8_t parity_sum = 0; + uint8_t start = 0; + if(!low_nibble) start = 4; + + for(int8_t i = (start + 3); i >= start; i--) { + parity_sum += (data >> i) & 1; + *encoded_data = (*encoded_data << 1) | ((data >> i) & 1); + } + + *encoded_data = (*encoded_data << 1) | ((parity_sum % 2) & 1); +} + +bool protocol_em4100_encoder_start(ProtocolEM4100* proto) { + // header + proto->encoded_data = 0b111111111; + + // data + for(uint8_t i = 0; i < EM4100_DECODED_DATA_SIZE; i++) { + em4100_write_nibble(false, proto->data[i], &proto->encoded_data); + em4100_write_nibble(true, proto->data[i], &proto->encoded_data); + } + + // column parity and stop bit + uint8_t parity_sum; + + for(uint8_t c = 0; c < EM_COLUMN_COUNT; c++) { + parity_sum = 0; + for(uint8_t i = 1; i <= EM_ROW_COUNT; i++) { + uint8_t parity_bit = (proto->encoded_data >> (i * EM_BITS_PER_ROW_COUNT - 1)) & 1; + parity_sum += parity_bit; + } + proto->encoded_data = (proto->encoded_data << 1) | ((parity_sum % 2) & 1); + } + + // stop bit + proto->encoded_data = (proto->encoded_data << 1) | 0; + + proto->encoded_data_index = 0; + proto->encoded_polarity = true; + + return true; +}; + +LevelDuration protocol_em4100_encoder_yield(ProtocolEM4100* proto) { + bool level = (proto->encoded_data >> (63 - proto->encoded_data_index)) & 1; + uint32_t duration = EM4100_CLOCK_PER_BIT / 2; + + if(proto->encoded_polarity) { + proto->encoded_polarity = false; + } else { + level = !level; + + proto->encoded_polarity = true; + proto->encoded_data_index++; + if(proto->encoded_data_index >= 64) { + proto->encoded_data_index = 0; + } + } + + return level_duration_make(level, duration); +}; + +bool protocol_em4100_write_data(ProtocolEM4100* protocol, void* data) { + LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data; + bool result = false; + + protocol_em4100_encoder_start(protocol); + + if(request->write_type == LFRFIDWriteTypeT5577) { + request->t5577.block[0] = + (LFRFID_T5577_MODULATION_MANCHESTER | LFRFID_T5577_BITRATE_RF_64 | + (2 << LFRFID_T5577_MAXBLOCK_SHIFT)); + request->t5577.block[1] = protocol->encoded_data; + request->t5577.block[2] = protocol->encoded_data >> 32; + request->t5577.blocks_to_write = 3; + result = true; + } + return result; +}; + +void protocol_em4100_render_data(ProtocolEM4100* protocol, string_t result) { + uint8_t* data = protocol->data; + string_printf(result, "ID: %03u,%05u", data[2], (uint16_t)((data[3] << 8) | (data[4]))); +}; + +const ProtocolBase protocol_em4100 = { + .name = "EM4100", + .manufacturer = "EM-Micro", + .data_size = EM4100_DECODED_DATA_SIZE, + .features = LFRFIDFeatureASK | LFRFIDFeaturePSK, + .validate_count = 3, + .alloc = (ProtocolAlloc)protocol_em4100_alloc, + .free = (ProtocolFree)protocol_em4100_free, + .get_data = (ProtocolGetData)protocol_em4100_get_data, + .decoder = + { + .start = (ProtocolDecoderStart)protocol_em4100_decoder_start, + .feed = (ProtocolDecoderFeed)protocol_em4100_decoder_feed, + }, + .encoder = + { + .start = (ProtocolEncoderStart)protocol_em4100_encoder_start, + .yield = (ProtocolEncoderYield)protocol_em4100_encoder_yield, + }, + .render_data = (ProtocolRenderData)protocol_em4100_render_data, + .render_brief_data = (ProtocolRenderData)protocol_em4100_render_data, + .write_data = (ProtocolWriteData)protocol_em4100_write_data, +}; \ No newline at end of file diff --git a/lib/lfrfid/protocols/protocol_em4100.h b/lib/lfrfid/protocols/protocol_em4100.h new file mode 100644 index 000000000..6e1e25b93 --- /dev/null +++ b/lib/lfrfid/protocols/protocol_em4100.h @@ -0,0 +1,4 @@ +#pragma once +#include + +extern const ProtocolBase protocol_em4100; \ No newline at end of file diff --git a/lib/lfrfid/protocols/protocol_fdx_a.c b/lib/lfrfid/protocols/protocol_fdx_a.c new file mode 100644 index 000000000..23f9e2857 --- /dev/null +++ b/lib/lfrfid/protocols/protocol_fdx_a.c @@ -0,0 +1,239 @@ +#include +#include +#include +#include +#include "lfrfid_protocols.h" +#include + +#define JITTER_TIME (20) +#define MIN_TIME (64 - JITTER_TIME) +#define MAX_TIME (80 + JITTER_TIME) + +#define FDXA_DATA_SIZE 10 +#define FDXA_PREAMBLE_SIZE 2 + +#define FDXA_ENCODED_DATA_SIZE (FDXA_PREAMBLE_SIZE + FDXA_DATA_SIZE + FDXA_PREAMBLE_SIZE) +#define FDXA_ENCODED_BIT_SIZE ((FDXA_PREAMBLE_SIZE + FDXA_DATA_SIZE) * 8) +#define FDXA_DECODED_DATA_SIZE (5) +#define FDXA_DECODED_BIT_SIZE ((FDXA_ENCODED_BIT_SIZE - FDXA_PREAMBLE_SIZE * 8) / 2) + +#define FDXA_PREAMBLE_0 0x55 +#define FDXA_PREAMBLE_1 0x1D + +typedef struct { + FSKDemod* fsk_demod; +} ProtocolFDXADecoder; + +typedef struct { + FSKOsc* fsk_osc; + uint8_t encoded_index; + uint32_t pulse; +} ProtocolFDXAEncoder; + +typedef struct { + ProtocolFDXADecoder decoder; + ProtocolFDXAEncoder encoder; + uint8_t encoded_data[FDXA_ENCODED_DATA_SIZE]; + uint8_t data[FDXA_DECODED_DATA_SIZE]; + size_t protocol_size; +} ProtocolFDXA; + +ProtocolFDXA* protocol_fdx_a_alloc(void) { + ProtocolFDXA* protocol = malloc(sizeof(ProtocolFDXA)); + protocol->decoder.fsk_demod = fsk_demod_alloc(MIN_TIME, 6, MAX_TIME, 5); + protocol->encoder.fsk_osc = fsk_osc_alloc(8, 10, 50); + + return protocol; +}; + +void protocol_fdx_a_free(ProtocolFDXA* protocol) { + fsk_demod_free(protocol->decoder.fsk_demod); + fsk_osc_free(protocol->encoder.fsk_osc); + free(protocol); +}; + +uint8_t* protocol_fdx_a_get_data(ProtocolFDXA* protocol) { + return protocol->data; +}; + +void protocol_fdx_a_decoder_start(ProtocolFDXA* protocol) { + memset(protocol->encoded_data, 0, FDXA_ENCODED_DATA_SIZE); +}; + +static bool protocol_fdx_a_decode(const uint8_t* from, uint8_t* to) { + size_t bit_index = 0; + for(size_t i = FDXA_PREAMBLE_SIZE; i < (FDXA_PREAMBLE_SIZE + FDXA_DATA_SIZE); i++) { + for(size_t n = 0; n < 4; n++) { + uint8_t bit_pair = (from[i] >> (6 - (n * 2))) & 0b11; + if(bit_pair == 0b01) { + bit_lib_set_bit(to, bit_index, 0); + } else if(bit_pair == 0b10) { + bit_lib_set_bit(to, bit_index, 1); + } else { + return false; + } + bit_index++; + } + } + + return true; +} + +static bool protocol_fdx_a_can_be_decoded(const uint8_t* data) { + // check preamble + if(data[0] != FDXA_PREAMBLE_0 || data[1] != FDXA_PREAMBLE_1 || data[12] != FDXA_PREAMBLE_0 || + data[13] != FDXA_PREAMBLE_1) { + return false; + } + + // check for manchester encoding + uint8_t decoded_data[FDXA_DECODED_DATA_SIZE]; + if(!protocol_fdx_a_decode(data, decoded_data)) return false; + + uint8_t parity_sum = 0; + for(size_t i = 0; i < FDXA_DECODED_DATA_SIZE; i++) { + parity_sum += bit_lib_test_parity_32(decoded_data[i], BitLibParityOdd); + decoded_data[i] &= 0x7F; + } + + return (parity_sum == 0); +} + +bool protocol_fdx_a_decoder_feed(ProtocolFDXA* protocol, bool level, uint32_t duration) { + bool value; + uint32_t count; + bool result = false; + + fsk_demod_feed(protocol->decoder.fsk_demod, level, duration, &value, &count); + if(count > 0) { + for(size_t i = 0; i < count; i++) { + bit_lib_push_bit(protocol->encoded_data, FDXA_ENCODED_DATA_SIZE, value); + if(protocol_fdx_a_can_be_decoded(protocol->encoded_data)) { + protocol_fdx_a_decode(protocol->encoded_data, protocol->data); + result = true; + } + } + } + + return result; +}; + +static void protocol_fdx_a_encode(ProtocolFDXA* protocol) { + protocol->encoded_data[0] = FDXA_PREAMBLE_0; + protocol->encoded_data[1] = FDXA_PREAMBLE_1; + + size_t bit_index = 0; + for(size_t i = 0; i < FDXA_DECODED_BIT_SIZE; i++) { + bool bit = bit_lib_get_bit(protocol->data, i); + if(bit) { + bit_lib_set_bit(protocol->encoded_data, 16 + bit_index, 1); + bit_lib_set_bit(protocol->encoded_data, 16 + bit_index + 1, 0); + } else { + bit_lib_set_bit(protocol->encoded_data, 16 + bit_index, 0); + bit_lib_set_bit(protocol->encoded_data, 16 + bit_index + 1, 1); + } + bit_index += 2; + } +} + +bool protocol_fdx_a_encoder_start(ProtocolFDXA* protocol) { + protocol->encoder.encoded_index = 0; + protocol->encoder.pulse = 0; + protocol_fdx_a_encode(protocol); + + return true; +}; + +LevelDuration protocol_fdx_a_encoder_yield(ProtocolFDXA* protocol) { + bool level = 0; + uint32_t duration = 0; + + // if pulse is zero, we need to output high, otherwise we need to output low + if(protocol->encoder.pulse == 0) { + // get bit + uint8_t bit = bit_lib_get_bit(protocol->encoded_data, protocol->encoder.encoded_index); + + // get pulse from oscillator + bool advance = fsk_osc_next(protocol->encoder.fsk_osc, bit, &duration); + + if(advance) { + bit_lib_increment_index(protocol->encoder.encoded_index, FDXA_ENCODED_BIT_SIZE); + } + + // duration diveded by 2 because we need to output high and low + duration = duration / 2; + protocol->encoder.pulse = duration; + level = true; + } else { + // output low half and reset pulse + duration = protocol->encoder.pulse; + protocol->encoder.pulse = 0; + level = false; + } + + return level_duration_make(level, duration); +}; + +bool protocol_fdx_a_write_data(ProtocolFDXA* protocol, void* data) { + LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data; + bool result = false; + + protocol_fdx_a_encoder_start(protocol); + + if(request->write_type == LFRFIDWriteTypeT5577) { + request->t5577.block[0] = LFRFID_T5577_MODULATION_FSK2a | LFRFID_T5577_BITRATE_RF_50 | + (3 << LFRFID_T5577_MAXBLOCK_SHIFT); + request->t5577.block[1] = bit_lib_get_bits_32(protocol->encoded_data, 0, 32); + request->t5577.block[2] = bit_lib_get_bits_32(protocol->encoded_data, 32, 32); + request->t5577.block[3] = bit_lib_get_bits_32(protocol->encoded_data, 64, 32); + request->t5577.blocks_to_write = 4; + result = true; + } + return result; +}; + +void protocol_fdx_a_render_data(ProtocolFDXA* protocol, string_t result) { + uint8_t data[FDXA_DECODED_DATA_SIZE]; + memcpy(data, protocol->data, FDXA_DECODED_DATA_SIZE); + + uint8_t parity_sum = 0; + for(size_t i = 0; i < FDXA_DECODED_DATA_SIZE; i++) { + parity_sum += bit_lib_test_parity_32(data[i], BitLibParityOdd); + data[i] &= 0x7F; + } + + string_printf( + result, + "ID: %02X%02X%02X%02X%02X\r\n" + "Parity: %s", + data[0], + data[1], + data[2], + data[3], + data[4], + parity_sum == 0 ? "+" : "-"); +}; + +const ProtocolBase protocol_fdx_a = { + .name = "FDX-A", + .manufacturer = "FECAVA", + .data_size = FDXA_DECODED_DATA_SIZE, + .features = LFRFIDFeatureASK, + .validate_count = 3, + .alloc = (ProtocolAlloc)protocol_fdx_a_alloc, + .free = (ProtocolFree)protocol_fdx_a_free, + .get_data = (ProtocolGetData)protocol_fdx_a_get_data, + .decoder = + { + .start = (ProtocolDecoderStart)protocol_fdx_a_decoder_start, + .feed = (ProtocolDecoderFeed)protocol_fdx_a_decoder_feed, + }, + .encoder = + { + .start = (ProtocolEncoderStart)protocol_fdx_a_encoder_start, + .yield = (ProtocolEncoderYield)protocol_fdx_a_encoder_yield, + }, + .render_data = (ProtocolRenderData)protocol_fdx_a_render_data, + .render_brief_data = (ProtocolRenderData)protocol_fdx_a_render_data, + .write_data = (ProtocolWriteData)protocol_fdx_a_write_data, +}; \ No newline at end of file diff --git a/lib/lfrfid/protocols/protocol_fdx_a.h b/lib/lfrfid/protocols/protocol_fdx_a.h new file mode 100644 index 000000000..355544881 --- /dev/null +++ b/lib/lfrfid/protocols/protocol_fdx_a.h @@ -0,0 +1,4 @@ +#pragma once +#include + +extern const ProtocolBase protocol_fdx_a; \ No newline at end of file diff --git a/lib/lfrfid/protocols/protocol_fdx_b.c b/lib/lfrfid/protocols/protocol_fdx_b.c new file mode 100644 index 000000000..f68a884e8 --- /dev/null +++ b/lib/lfrfid/protocols/protocol_fdx_b.c @@ -0,0 +1,374 @@ +#include +#include "toolbox/level_duration.h" +#include "protocol_fdx_b.h" +#include +#include +#include "lfrfid_protocols.h" + +#define FDX_B_ENCODED_BIT_SIZE (128) +#define FDX_B_ENCODED_BYTE_SIZE (((FDX_B_ENCODED_BIT_SIZE) / 8)) +#define FDX_B_PREAMBLE_BIT_SIZE (11) +#define FDX_B_PREAMBLE_BYTE_SIZE (2) +#define FDX_B_ENCODED_BYTE_FULL_SIZE (FDX_B_ENCODED_BYTE_SIZE + FDX_B_PREAMBLE_BYTE_SIZE) + +#define FDXB_DECODED_DATA_SIZE (11) + +#define FDX_B_SHORT_TIME (128) +#define FDX_B_LONG_TIME (256) +#define FDX_B_JITTER_TIME (60) + +#define FDX_B_SHORT_TIME_LOW (FDX_B_SHORT_TIME - FDX_B_JITTER_TIME) +#define FDX_B_SHORT_TIME_HIGH (FDX_B_SHORT_TIME + FDX_B_JITTER_TIME) +#define FDX_B_LONG_TIME_LOW (FDX_B_LONG_TIME - FDX_B_JITTER_TIME) +#define FDX_B_LONG_TIME_HIGH (FDX_B_LONG_TIME + FDX_B_JITTER_TIME) + +typedef struct { + bool last_short; + bool last_level; + size_t encoded_index; + uint8_t encoded_data[FDX_B_ENCODED_BYTE_FULL_SIZE]; + uint8_t data[FDXB_DECODED_DATA_SIZE]; +} ProtocolFDXB; + +ProtocolFDXB* protocol_fdx_b_alloc(void) { + ProtocolFDXB* protocol = malloc(sizeof(ProtocolFDXB)); + return protocol; +}; + +void protocol_fdx_b_free(ProtocolFDXB* protocol) { + free(protocol); +}; + +uint8_t* protocol_fdx_b_get_data(ProtocolFDXB* proto) { + return proto->data; +}; + +void protocol_fdx_b_decoder_start(ProtocolFDXB* protocol) { + memset(protocol->encoded_data, 0, FDX_B_ENCODED_BYTE_FULL_SIZE); + protocol->last_short = false; +}; + +static bool protocol_fdx_b_can_be_decoded(ProtocolFDXB* protocol) { + bool result = false; + + /* + msb lsb + 0 10000000000 Header pattern. 11 bits. + 11 1nnnnnnnn + 20 1nnnnnnnn 38 bit (12 digit) National code. + 29 1nnnnnnnn eg. 000000001008 (decimal). + 38 1nnnnnnnn + 47 1nnnnnncc 10 bit (3 digit) Country code. + 56 1cccccccc eg. 999 (decimal). + 65 1s------- 1 bit data block status flag. + 74 1-------a 1 bit animal application indicator. + 83 1xxxxxxxx 16 bit checksum. + 92 1xxxxxxxx + 101 1eeeeeeee 24 bits of extra data if present. + 110 1eeeeeeee eg. $123456. + 119 1eeeeeeee + */ + + do { + // check 11 bits preamble + if(bit_lib_get_bits_16(protocol->encoded_data, 0, 11) != 0b10000000000) break; + // check next 11 bits preamble + if(bit_lib_get_bits_16(protocol->encoded_data, 128, 11) != 0b10000000000) break; + // check control bits + if(!bit_lib_test_parity(protocol->encoded_data, 3, 13 * 9, BitLibParityAlways1, 9)) break; + + // compute checksum + uint8_t crc_data[8]; + for(size_t i = 0; i < 8; i++) { + bit_lib_copy_bits(crc_data, i * 8, 8, protocol->encoded_data, 12 + 9 * i); + } + uint16_t crc_res = bit_lib_crc16(crc_data, 8, 0x1021, 0x0000, false, false, 0x0000); + + // read checksum + uint16_t crc_ex = 0; + bit_lib_copy_bits((uint8_t*)&crc_ex, 8, 8, protocol->encoded_data, 84); + bit_lib_copy_bits((uint8_t*)&crc_ex, 0, 8, protocol->encoded_data, 93); + + // compare checksum + if(crc_res != crc_ex) break; + + result = true; + } while(false); + + return result; +} + +void protocol_fdx_b_decode(ProtocolFDXB* protocol) { + // remove parity + bit_lib_remove_bit_every_nth(protocol->encoded_data, 3, 13 * 9, 9); + + // remove header pattern + for(size_t i = 0; i < 11; i++) + bit_lib_push_bit(protocol->encoded_data, FDX_B_ENCODED_BYTE_FULL_SIZE, 0); + + // 0 nnnnnnnn + // 8 nnnnnnnn 38 bit (12 digit) National code. + // 16 nnnnnnnn eg. 000000001008 (decimal). + // 24 nnnnnnnn + // 32 nnnnnncc 10 bit (3 digit) Country code. + // 40 cccccccc eg. 999 (decimal). + // 48 s------- 1 bit data block status flag. + // 56 -------a 1 bit animal application indicator. + // 64 xxxxxxxx 16 bit checksum. + // 72 xxxxxxxx + // 80 eeeeeeee 24 bits of extra data if present. + // 88 eeeeeeee eg. $123456. + // 92 eeeeeeee + + // copy data without checksum + bit_lib_copy_bits(protocol->data, 0, 64, protocol->encoded_data, 0); + bit_lib_copy_bits(protocol->data, 64, 24, protocol->encoded_data, 80); + + // const BitLibRegion regions_encoded[] = { + // {'n', 0, 38}, + // {'c', 38, 10}, + // {'b', 48, 16}, + // {'x', 64, 16}, + // {'e', 80, 24}, + // }; + + // bit_lib_print_regions(regions_encoded, 5, protocol->encoded_data, FDX_B_ENCODED_BIT_SIZE); + + // const BitLibRegion regions_decoded[] = { + // {'n', 0, 38}, + // {'c', 38, 10}, + // {'b', 48, 16}, + // {'e', 64, 24}, + // }; + + // bit_lib_print_regions(regions_decoded, 4, protocol->data, FDXB_DECODED_DATA_SIZE * 8); +} + +bool protocol_fdx_b_decoder_feed(ProtocolFDXB* protocol, bool level, uint32_t duration) { + bool result = false; + UNUSED(level); + + bool pushed = false; + + // Bi-Phase Manchester decoding + if(duration >= FDX_B_SHORT_TIME_LOW && duration <= FDX_B_SHORT_TIME_HIGH) { + if(protocol->last_short == false) { + protocol->last_short = true; + } else { + pushed = true; + bit_lib_push_bit(protocol->encoded_data, FDX_B_ENCODED_BYTE_FULL_SIZE, false); + protocol->last_short = false; + } + } else if(duration >= FDX_B_LONG_TIME_LOW && duration <= FDX_B_LONG_TIME_HIGH) { + if(protocol->last_short == false) { + pushed = true; + bit_lib_push_bit(protocol->encoded_data, FDX_B_ENCODED_BYTE_FULL_SIZE, true); + } else { + // reset + protocol->last_short = false; + } + } else { + // reset + protocol->last_short = false; + } + + if(pushed && protocol_fdx_b_can_be_decoded(protocol)) { + protocol_fdx_b_decode(protocol); + result = true; + } + + return result; +}; + +bool protocol_fdx_b_encoder_start(ProtocolFDXB* protocol) { + memset(protocol->encoded_data, 0, FDX_B_ENCODED_BYTE_FULL_SIZE); + bit_lib_set_bit(protocol->encoded_data, 0, 1); + for(size_t i = 0; i < 13; i++) { + bit_lib_set_bit(protocol->encoded_data, 11 + 9 * i, 1); + if(i == 8 || i == 9) continue; + + if(i < 8) { + bit_lib_copy_bits(protocol->encoded_data, 12 + 9 * i, 8, protocol->data, i * 8); + } else { + bit_lib_copy_bits(protocol->encoded_data, 12 + 9 * i, 8, protocol->data, (i - 2) * 8); + } + } + + uint16_t crc_res = bit_lib_crc16(protocol->data, 8, 0x1021, 0x0000, false, false, 0x0000); + bit_lib_copy_bits(protocol->encoded_data, 84, 8, (uint8_t*)&crc_res, 8); + bit_lib_copy_bits(protocol->encoded_data, 93, 8, (uint8_t*)&crc_res, 0); + + protocol->encoded_index = 0; + protocol->last_short = false; + protocol->last_level = false; + return true; +}; + +LevelDuration protocol_fdx_b_encoder_yield(ProtocolFDXB* protocol) { + uint32_t duration; + protocol->last_level = !protocol->last_level; + + bool bit = bit_lib_get_bit(protocol->encoded_data, protocol->encoded_index); + + // Bi-Phase Manchester encoder + if(bit) { + // one long pulse for 1 + duration = FDX_B_LONG_TIME / 8; + bit_lib_increment_index(protocol->encoded_index, FDX_B_ENCODED_BIT_SIZE); + } else { + // two short pulses for 0 + duration = FDX_B_SHORT_TIME / 8; + if(protocol->last_short) { + bit_lib_increment_index(protocol->encoded_index, FDX_B_ENCODED_BIT_SIZE); + protocol->last_short = false; + } else { + protocol->last_short = true; + } + } + + return level_duration_make(protocol->last_level, duration); +}; + +// 0 nnnnnnnn +// 8 nnnnnnnn 38 bit (12 digit) National code. +// 16 nnnnnnnn eg. 000000001008 (decimal). +// 24 nnnnnnnn +// 32 nnnnnnnn 10 bit (3 digit) Country code. +// 40 cccccccc eg. 999 (decimal). +// 48 s------- 1 bit data block status flag. +// 56 -------a 1 bit animal application indicator. +// 64 eeeeeeee 24 bits of extra data if present. +// 72 eeeeeeee eg. $123456. +// 80 eeeeeeee + +static uint64_t protocol_fdx_b_get_national_code(const uint8_t* data) { + uint64_t national_code = bit_lib_get_bits_32(data, 0, 32); + national_code = national_code << 32; + national_code |= bit_lib_get_bits_32(data, 32, 6) << (32 - 6); + bit_lib_reverse_bits((uint8_t*)&national_code, 0, 64); + return national_code; +} + +static uint16_t protocol_fdx_b_get_country_code(const uint8_t* data) { + uint16_t country_code = bit_lib_get_bits_16(data, 38, 10) << 6; + bit_lib_reverse_bits((uint8_t*)&country_code, 0, 16); + return country_code; +} + +static bool protocol_fdx_b_get_temp(const uint8_t* data, float* temp) { + uint32_t extended = bit_lib_get_bits_32(data, 64, 24) << 8; + bit_lib_reverse_bits((uint8_t*)&extended, 0, 32); + + uint8_t ex_parity = (extended & 0x100) >> 8; + uint8_t ex_temperature = extended & 0xff; + uint8_t ex_calc_parity = bit_lib_test_parity_32(ex_temperature, BitLibParityOdd); + bool ex_temperature_present = (ex_calc_parity == ex_parity) && !(extended & 0xe00); + + if(ex_temperature_present) { + float temperature_f = 74 + ex_temperature * 0.2; + *temp = temperature_f; + return true; + } else { + return false; + } +} + +void protocol_fdx_b_render_data(ProtocolFDXB* protocol, string_t result) { + // 38 bits of national code + uint64_t national_code = protocol_fdx_b_get_national_code(protocol->data); + + // 10 bit of country code + uint16_t country_code = protocol_fdx_b_get_country_code(protocol->data); + + bool block_status = bit_lib_get_bit(protocol->data, 48); + bool rudi_bit = bit_lib_get_bit(protocol->data, 49); + uint8_t reserved = bit_lib_get_bits(protocol->data, 50, 5); + uint8_t user_info = bit_lib_get_bits(protocol->data, 55, 5); + uint8_t replacement_number = bit_lib_get_bits(protocol->data, 60, 3); + bool animal_flag = bit_lib_get_bit(protocol->data, 63); + + string_printf(result, "ID: %03u-%012llu\r\n", country_code, national_code); + string_cat_printf(result, "Animal: %s, ", animal_flag ? "Yes" : "No"); + + float temperature; + if(protocol_fdx_b_get_temp(protocol->data, &temperature)) { + float temperature_c = (temperature - 32) / 1.8; + string_cat_printf( + result, "T: %.2fF, %.2fC\r\n", (double)temperature, (double)temperature_c); + } else { + string_cat_printf(result, "T: ---\r\n"); + } + + string_cat_printf( + result, + "Bits: %X-%X-%X-%X-%X", + block_status, + rudi_bit, + reserved, + user_info, + replacement_number); +}; + +void protocol_fdx_b_render_brief_data(ProtocolFDXB* protocol, string_t result) { + // 38 bits of national code + uint64_t national_code = protocol_fdx_b_get_national_code(protocol->data); + + // 10 bit of country code + uint16_t country_code = protocol_fdx_b_get_country_code(protocol->data); + + bool animal_flag = bit_lib_get_bit(protocol->data, 63); + + string_printf(result, "ID: %03u-%012llu\r\n", country_code, national_code); + string_cat_printf(result, "Animal: %s, ", animal_flag ? "Yes" : "No"); + + float temperature; + if(protocol_fdx_b_get_temp(protocol->data, &temperature)) { + float temperature_c = (temperature - 32) / 1.8; + string_cat_printf(result, "T: %.2fC", (double)temperature_c); + } else { + string_cat_printf(result, "T: ---"); + } +}; + +bool protocol_fdx_b_write_data(ProtocolFDXB* protocol, void* data) { + LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data; + bool result = false; + + protocol_fdx_b_encoder_start(protocol); + + if(request->write_type == LFRFIDWriteTypeT5577) { + request->t5577.block[0] = LFRFID_T5577_MODULATION_DIPHASE | LFRFID_T5577_BITRATE_RF_32 | + (4 << LFRFID_T5577_MAXBLOCK_SHIFT); + request->t5577.block[1] = bit_lib_get_bits_32(protocol->encoded_data, 0, 32); + request->t5577.block[2] = bit_lib_get_bits_32(protocol->encoded_data, 32, 32); + request->t5577.block[3] = bit_lib_get_bits_32(protocol->encoded_data, 64, 32); + request->t5577.block[4] = bit_lib_get_bits_32(protocol->encoded_data, 96, 32); + request->t5577.blocks_to_write = 5; + result = true; + } + return result; +}; + +const ProtocolBase protocol_fdx_b = { + .name = "FDX-B", + .manufacturer = "ISO", + .data_size = FDXB_DECODED_DATA_SIZE, + .features = LFRFIDFeatureASK, + .validate_count = 3, + .alloc = (ProtocolAlloc)protocol_fdx_b_alloc, + .free = (ProtocolFree)protocol_fdx_b_free, + .get_data = (ProtocolGetData)protocol_fdx_b_get_data, + .decoder = + { + .start = (ProtocolDecoderStart)protocol_fdx_b_decoder_start, + .feed = (ProtocolDecoderFeed)protocol_fdx_b_decoder_feed, + }, + .encoder = + { + .start = (ProtocolEncoderStart)protocol_fdx_b_encoder_start, + .yield = (ProtocolEncoderYield)protocol_fdx_b_encoder_yield, + }, + .render_data = (ProtocolRenderData)protocol_fdx_b_render_data, + .render_brief_data = (ProtocolRenderData)protocol_fdx_b_render_brief_data, + .write_data = (ProtocolWriteData)protocol_fdx_b_write_data, +}; \ No newline at end of file diff --git a/lib/lfrfid/protocols/protocol_fdx_b.h b/lib/lfrfid/protocols/protocol_fdx_b.h new file mode 100644 index 000000000..549c862e3 --- /dev/null +++ b/lib/lfrfid/protocols/protocol_fdx_b.h @@ -0,0 +1,4 @@ +#pragma once +#include + +extern const ProtocolBase protocol_fdx_b; \ No newline at end of file diff --git a/lib/lfrfid/protocols/protocol_h10301.c b/lib/lfrfid/protocols/protocol_h10301.c new file mode 100644 index 000000000..f30f75fac --- /dev/null +++ b/lib/lfrfid/protocols/protocol_h10301.c @@ -0,0 +1,386 @@ +#include +#include +#include +#include +#include "lfrfid_protocols.h" + +#define JITTER_TIME (20) +#define MIN_TIME (64 - JITTER_TIME) +#define MAX_TIME (80 + JITTER_TIME) + +#define H10301_DECODED_DATA_SIZE (3) +#define H10301_ENCODED_DATA_SIZE_U32 (3) +#define H10301_ENCODED_DATA_SIZE (sizeof(uint32_t) * H10301_ENCODED_DATA_SIZE_U32) + +#define H10301_BIT_SIZE (sizeof(uint32_t) * 8) +#define H10301_BIT_MAX_SIZE (H10301_BIT_SIZE * H10301_DECODED_DATA_SIZE) + +typedef struct { + FSKDemod* fsk_demod; +} ProtocolH10301Decoder; + +typedef struct { + FSKOsc* fsk_osc; + uint8_t encoded_index; + uint32_t pulse; +} ProtocolH10301Encoder; + +typedef struct { + ProtocolH10301Decoder decoder; + ProtocolH10301Encoder encoder; + uint32_t encoded_data[H10301_ENCODED_DATA_SIZE_U32]; + uint8_t data[H10301_DECODED_DATA_SIZE]; +} ProtocolH10301; + +ProtocolH10301* protocol_h10301_alloc(void) { + ProtocolH10301* protocol = malloc(sizeof(ProtocolH10301)); + protocol->decoder.fsk_demod = fsk_demod_alloc(MIN_TIME, 6, MAX_TIME, 5); + protocol->encoder.fsk_osc = fsk_osc_alloc(8, 10, 50); + + return protocol; +}; + +void protocol_h10301_free(ProtocolH10301* protocol) { + fsk_demod_free(protocol->decoder.fsk_demod); + fsk_osc_free(protocol->encoder.fsk_osc); + free(protocol); +}; + +uint8_t* protocol_h10301_get_data(ProtocolH10301* protocol) { + return protocol->data; +}; + +void protocol_h10301_decoder_start(ProtocolH10301* protocol) { + memset(protocol->encoded_data, 0, sizeof(uint32_t) * 3); +}; + +static void protocol_h10301_decoder_store_data(ProtocolH10301* protocol, bool data) { + protocol->encoded_data[0] = (protocol->encoded_data[0] << 1) | + ((protocol->encoded_data[1] >> 31) & 1); + protocol->encoded_data[1] = (protocol->encoded_data[1] << 1) | + ((protocol->encoded_data[2] >> 31) & 1); + protocol->encoded_data[2] = (protocol->encoded_data[2] << 1) | data; +} + +static bool protocol_h10301_can_be_decoded(const uint32_t* card_data) { + const uint8_t* encoded_data = (const uint8_t*)card_data; + + // packet preamble + // raw data + if(*(encoded_data + 3) != 0x1D) { + return false; + } + + // encoded company/oem + // coded with 01 = 0, 10 = 1 transitions + // stored in word 0 + if((*card_data >> 10 & 0x3FFF) != 0x1556) { + return false; + } + + // encoded format/length + // coded with 01 = 0, 10 = 1 transitions + // stored in word 0 and word 1 + if((((*card_data & 0x3FF) << 12) | ((*(card_data + 1) >> 20) & 0xFFF)) != 0x155556) { + return false; + } + + // data decoding + uint32_t result = 0; + + // decode from word 1 + // coded with 01 = 0, 10 = 1 transitions + for(int8_t i = 9; i >= 0; i--) { + switch((*(card_data + 1) >> (2 * i)) & 0b11) { + case 0b01: + result = (result << 1) | 0; + break; + case 0b10: + result = (result << 1) | 1; + break; + default: + return false; + break; + } + } + + // decode from word 2 + // coded with 01 = 0, 10 = 1 transitions + for(int8_t i = 15; i >= 0; i--) { + switch((*(card_data + 2) >> (2 * i)) & 0b11) { + case 0b01: + result = (result << 1) | 0; + break; + case 0b10: + result = (result << 1) | 1; + break; + default: + return false; + break; + } + } + + // trailing parity (odd) test + uint8_t parity_sum = 0; + for(int8_t i = 0; i < 13; i++) { + if(((result >> i) & 1) == 1) { + parity_sum++; + } + } + + if((parity_sum % 2) != 1) { + return false; + } + + // leading parity (even) test + parity_sum = 0; + for(int8_t i = 13; i < 26; i++) { + if(((result >> i) & 1) == 1) { + parity_sum++; + } + } + + if((parity_sum % 2) == 1) { + return false; + } + + return true; +} + +static void protocol_h10301_decode(const uint32_t* card_data, uint8_t* decoded_data) { + // data decoding + uint32_t result = 0; + + // decode from word 1 + // coded with 01 = 0, 10 = 1 transitions + for(int8_t i = 9; i >= 0; i--) { + switch((*(card_data + 1) >> (2 * i)) & 0b11) { + case 0b01: + result = (result << 1) | 0; + break; + case 0b10: + result = (result << 1) | 1; + break; + default: + break; + } + } + + // decode from word 2 + // coded with 01 = 0, 10 = 1 transitions + for(int8_t i = 15; i >= 0; i--) { + switch((*(card_data + 2) >> (2 * i)) & 0b11) { + case 0b01: + result = (result << 1) | 0; + break; + case 0b10: + result = (result << 1) | 1; + break; + default: + break; + } + } + + uint8_t data[H10301_DECODED_DATA_SIZE] = { + (uint8_t)(result >> 17), (uint8_t)(result >> 9), (uint8_t)(result >> 1)}; + + memcpy(decoded_data, &data, H10301_DECODED_DATA_SIZE); +} + +bool protocol_h10301_decoder_feed(ProtocolH10301* protocol, bool level, uint32_t duration) { + bool value; + uint32_t count; + bool result = false; + + fsk_demod_feed(protocol->decoder.fsk_demod, level, duration, &value, &count); + if(count > 0) { + for(size_t i = 0; i < count; i++) { + protocol_h10301_decoder_store_data(protocol, value); + if(protocol_h10301_can_be_decoded(protocol->encoded_data)) { + protocol_h10301_decode(protocol->encoded_data, protocol->data); + result = true; + break; + } + } + } + + return result; +}; + +static void protocol_h10301_write_raw_bit(bool bit, uint8_t position, uint32_t* card_data) { + if(bit) { + card_data[position / H10301_BIT_SIZE] |= + 1UL << (H10301_BIT_SIZE - (position % H10301_BIT_SIZE) - 1); + } else { + card_data[position / H10301_BIT_SIZE] &= + ~(1UL << (H10301_BIT_SIZE - (position % H10301_BIT_SIZE) - 1)); + } +} + +static void protocol_h10301_write_bit(bool bit, uint8_t position, uint32_t* card_data) { + protocol_h10301_write_raw_bit(bit, position + 0, card_data); + protocol_h10301_write_raw_bit(!bit, position + 1, card_data); +} + +void protocol_h10301_encode(const uint8_t* decoded_data, uint8_t* encoded_data) { + uint32_t card_data[H10301_DECODED_DATA_SIZE] = {0, 0, 0}; + + uint32_t fc_cn = (decoded_data[0] << 16) | (decoded_data[1] << 8) | decoded_data[2]; + + // even parity sum calculation (high 12 bits of data) + uint8_t even_parity_sum = 0; + for(int8_t i = 12; i < 24; i++) { + if(((fc_cn >> i) & 1) == 1) { + even_parity_sum++; + } + } + + // odd parity sum calculation (low 12 bits of data) + uint8_t odd_parity_sum = 1; + for(int8_t i = 0; i < 12; i++) { + if(((fc_cn >> i) & 1) == 1) { + odd_parity_sum++; + } + } + + // 0x1D preamble + protocol_h10301_write_raw_bit(0, 0, card_data); + protocol_h10301_write_raw_bit(0, 1, card_data); + protocol_h10301_write_raw_bit(0, 2, card_data); + protocol_h10301_write_raw_bit(1, 3, card_data); + protocol_h10301_write_raw_bit(1, 4, card_data); + protocol_h10301_write_raw_bit(1, 5, card_data); + protocol_h10301_write_raw_bit(0, 6, card_data); + protocol_h10301_write_raw_bit(1, 7, card_data); + + // company / OEM code 1 + protocol_h10301_write_bit(0, 8, card_data); + protocol_h10301_write_bit(0, 10, card_data); + protocol_h10301_write_bit(0, 12, card_data); + protocol_h10301_write_bit(0, 14, card_data); + protocol_h10301_write_bit(0, 16, card_data); + protocol_h10301_write_bit(0, 18, card_data); + protocol_h10301_write_bit(1, 20, card_data); + + // card format / length 1 + protocol_h10301_write_bit(0, 22, card_data); + protocol_h10301_write_bit(0, 24, card_data); + protocol_h10301_write_bit(0, 26, card_data); + protocol_h10301_write_bit(0, 28, card_data); + protocol_h10301_write_bit(0, 30, card_data); + protocol_h10301_write_bit(0, 32, card_data); + protocol_h10301_write_bit(0, 34, card_data); + protocol_h10301_write_bit(0, 36, card_data); + protocol_h10301_write_bit(0, 38, card_data); + protocol_h10301_write_bit(0, 40, card_data); + protocol_h10301_write_bit(1, 42, card_data); + + // even parity bit + protocol_h10301_write_bit((even_parity_sum % 2), 44, card_data); + + // data + for(uint8_t i = 0; i < 24; i++) { + protocol_h10301_write_bit((fc_cn >> (23 - i)) & 1, 46 + (i * 2), card_data); + } + + // odd parity bit + protocol_h10301_write_bit((odd_parity_sum % 2), 94, card_data); + + memcpy(encoded_data, &card_data, H10301_ENCODED_DATA_SIZE); +} + +bool protocol_h10301_encoder_start(ProtocolH10301* protocol) { + protocol_h10301_encode(protocol->data, (uint8_t*)protocol->encoded_data); + protocol->encoder.encoded_index = 0; + protocol->encoder.pulse = 0; + + return true; +}; + +LevelDuration protocol_h10301_encoder_yield(ProtocolH10301* protocol) { + bool level = 0; + uint32_t duration = 0; + + // if pulse is zero, we need to output high, otherwise we need to output low + if(protocol->encoder.pulse == 0) { + // get bit + uint8_t bit = + (protocol->encoded_data[protocol->encoder.encoded_index / H10301_BIT_SIZE] >> + ((H10301_BIT_SIZE - 1) - (protocol->encoder.encoded_index % H10301_BIT_SIZE))) & + 1; + + // get pulse from oscillator + bool advance = fsk_osc_next(protocol->encoder.fsk_osc, bit, &duration); + + if(advance) { + protocol->encoder.encoded_index++; + if(protocol->encoder.encoded_index >= (H10301_BIT_MAX_SIZE)) { + protocol->encoder.encoded_index = 0; + } + } + + // duration diveded by 2 because we need to output high and low + duration = duration / 2; + protocol->encoder.pulse = duration; + level = true; + } else { + // output low half and reset pulse + duration = protocol->encoder.pulse; + protocol->encoder.pulse = 0; + level = false; + } + + return level_duration_make(level, duration); +}; + +bool protocol_h10301_write_data(ProtocolH10301* protocol, void* data) { + LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data; + bool result = false; + + protocol_h10301_encoder_start(protocol); + + if(request->write_type == LFRFIDWriteTypeT5577) { + request->t5577.block[0] = LFRFID_T5577_MODULATION_FSK2a | LFRFID_T5577_BITRATE_RF_50 | + (3 << LFRFID_T5577_MAXBLOCK_SHIFT); + request->t5577.block[1] = protocol->encoded_data[0]; + request->t5577.block[2] = protocol->encoded_data[1]; + request->t5577.block[3] = protocol->encoded_data[2]; + request->t5577.blocks_to_write = 4; + result = true; + } + return result; +}; + +void protocol_h10301_render_data(ProtocolH10301* protocol, string_t result) { + uint8_t* data = protocol->data; + string_printf( + result, + "FC: %u\r\n" + "Card: %u", + data[0], + (uint16_t)((data[1] << 8) | (data[2]))); +}; + +const ProtocolBase protocol_h10301 = { + .name = "H10301", + .manufacturer = "HID", + .data_size = H10301_DECODED_DATA_SIZE, + .features = LFRFIDFeatureASK, + .validate_count = 3, + .alloc = (ProtocolAlloc)protocol_h10301_alloc, + .free = (ProtocolFree)protocol_h10301_free, + .get_data = (ProtocolGetData)protocol_h10301_get_data, + .decoder = + { + .start = (ProtocolDecoderStart)protocol_h10301_decoder_start, + .feed = (ProtocolDecoderFeed)protocol_h10301_decoder_feed, + }, + .encoder = + { + .start = (ProtocolEncoderStart)protocol_h10301_encoder_start, + .yield = (ProtocolEncoderYield)protocol_h10301_encoder_yield, + }, + .render_data = (ProtocolRenderData)protocol_h10301_render_data, + .render_brief_data = (ProtocolRenderData)protocol_h10301_render_data, + .write_data = (ProtocolWriteData)protocol_h10301_write_data, +}; \ No newline at end of file diff --git a/lib/lfrfid/protocols/protocol_h10301.h b/lib/lfrfid/protocols/protocol_h10301.h new file mode 100644 index 000000000..b7ee5ad57 --- /dev/null +++ b/lib/lfrfid/protocols/protocol_h10301.h @@ -0,0 +1,4 @@ +#pragma once +#include + +extern const ProtocolBase protocol_h10301; \ No newline at end of file diff --git a/lib/lfrfid/protocols/protocol_hid_ex_generic.c b/lib/lfrfid/protocols/protocol_hid_ex_generic.c new file mode 100644 index 000000000..e0a852661 --- /dev/null +++ b/lib/lfrfid/protocols/protocol_hid_ex_generic.c @@ -0,0 +1,219 @@ +#include +#include +#include +#include +#include "lfrfid_protocols.h" +#include + +#define JITTER_TIME (20) +#define MIN_TIME (64 - JITTER_TIME) +#define MAX_TIME (80 + JITTER_TIME) + +#define HID_DATA_SIZE 23 +#define HID_PREAMBLE_SIZE 1 + +#define HID_ENCODED_DATA_SIZE (HID_PREAMBLE_SIZE + HID_DATA_SIZE + HID_PREAMBLE_SIZE) +#define HID_ENCODED_BIT_SIZE ((HID_PREAMBLE_SIZE + HID_DATA_SIZE) * 8) +#define HID_DECODED_DATA_SIZE (12) +#define HID_DECODED_BIT_SIZE ((HID_ENCODED_BIT_SIZE - HID_PREAMBLE_SIZE * 8) / 2) + +#define HID_PREAMBLE 0x1D + +typedef struct { + FSKDemod* fsk_demod; +} ProtocolHIDExDecoder; + +typedef struct { + FSKOsc* fsk_osc; + uint8_t encoded_index; + uint32_t pulse; +} ProtocolHIDExEncoder; + +typedef struct { + ProtocolHIDExDecoder decoder; + ProtocolHIDExEncoder encoder; + uint8_t encoded_data[HID_ENCODED_DATA_SIZE]; + uint8_t data[HID_DECODED_DATA_SIZE]; + size_t protocol_size; +} ProtocolHIDEx; + +ProtocolHIDEx* protocol_hid_ex_generic_alloc(void) { + ProtocolHIDEx* protocol = malloc(sizeof(ProtocolHIDEx)); + protocol->decoder.fsk_demod = fsk_demod_alloc(MIN_TIME, 6, MAX_TIME, 5); + protocol->encoder.fsk_osc = fsk_osc_alloc(8, 10, 50); + + return protocol; +}; + +void protocol_hid_ex_generic_free(ProtocolHIDEx* protocol) { + fsk_demod_free(protocol->decoder.fsk_demod); + fsk_osc_free(protocol->encoder.fsk_osc); + free(protocol); +}; + +uint8_t* protocol_hid_ex_generic_get_data(ProtocolHIDEx* protocol) { + return protocol->data; +}; + +void protocol_hid_ex_generic_decoder_start(ProtocolHIDEx* protocol) { + memset(protocol->encoded_data, 0, HID_ENCODED_DATA_SIZE); +}; + +static bool protocol_hid_ex_generic_can_be_decoded(const uint8_t* data) { + // check preamble + if(data[0] != HID_PREAMBLE || data[HID_PREAMBLE_SIZE + HID_DATA_SIZE] != HID_PREAMBLE) { + return false; + } + + // check for manchester encoding + for(size_t i = HID_PREAMBLE_SIZE; i < (HID_PREAMBLE_SIZE + HID_DATA_SIZE); i++) { + for(size_t n = 0; n < 4; n++) { + uint8_t bit_pair = (data[i] >> (n * 2)) & 0b11; + if(bit_pair == 0b11 || bit_pair == 0b00) { + return false; + } + } + } + + return true; +} + +static void protocol_hid_ex_generic_decode(const uint8_t* from, uint8_t* to) { + size_t bit_index = 0; + for(size_t i = HID_PREAMBLE_SIZE; i < (HID_PREAMBLE_SIZE + HID_DATA_SIZE); i++) { + for(size_t n = 0; n < 4; n++) { + uint8_t bit_pair = (from[i] >> (6 - (n * 2))) & 0b11; + if(bit_pair == 0b01) { + bit_lib_set_bit(to, bit_index, 0); + } else if(bit_pair == 0b10) { + bit_lib_set_bit(to, bit_index, 1); + } + bit_index++; + } + } +} + +bool protocol_hid_ex_generic_decoder_feed(ProtocolHIDEx* protocol, bool level, uint32_t duration) { + bool value; + uint32_t count; + bool result = false; + + fsk_demod_feed(protocol->decoder.fsk_demod, level, duration, &value, &count); + if(count > 0) { + for(size_t i = 0; i < count; i++) { + bit_lib_push_bit(protocol->encoded_data, HID_ENCODED_DATA_SIZE, value); + if(protocol_hid_ex_generic_can_be_decoded(protocol->encoded_data)) { + protocol_hid_ex_generic_decode(protocol->encoded_data, protocol->data); + result = true; + } + } + } + + return result; +}; + +static void protocol_hid_ex_generic_encode(ProtocolHIDEx* protocol) { + protocol->encoded_data[0] = HID_PREAMBLE; + + size_t bit_index = 0; + for(size_t i = 0; i < HID_DECODED_BIT_SIZE; i++) { + bool bit = bit_lib_get_bit(protocol->data, i); + if(bit) { + bit_lib_set_bit(protocol->encoded_data, 8 + bit_index, 1); + bit_lib_set_bit(protocol->encoded_data, 8 + bit_index + 1, 0); + } else { + bit_lib_set_bit(protocol->encoded_data, 8 + bit_index, 0); + bit_lib_set_bit(protocol->encoded_data, 8 + bit_index + 1, 1); + } + bit_index += 2; + } +} + +bool protocol_hid_ex_generic_encoder_start(ProtocolHIDEx* protocol) { + protocol->encoder.encoded_index = 0; + protocol->encoder.pulse = 0; + protocol_hid_ex_generic_encode(protocol); + + return true; +}; + +LevelDuration protocol_hid_ex_generic_encoder_yield(ProtocolHIDEx* protocol) { + bool level = 0; + uint32_t duration = 0; + + // if pulse is zero, we need to output high, otherwise we need to output low + if(protocol->encoder.pulse == 0) { + // get bit + uint8_t bit = bit_lib_get_bit(protocol->encoded_data, protocol->encoder.encoded_index); + + // get pulse from oscillator + bool advance = fsk_osc_next(protocol->encoder.fsk_osc, bit, &duration); + + if(advance) { + bit_lib_increment_index(protocol->encoder.encoded_index, HID_ENCODED_BIT_SIZE); + } + + // duration diveded by 2 because we need to output high and low + duration = duration / 2; + protocol->encoder.pulse = duration; + level = true; + } else { + // output low half and reset pulse + duration = protocol->encoder.pulse; + protocol->encoder.pulse = 0; + level = false; + } + + return level_duration_make(level, duration); +}; + +bool protocol_hid_ex_generic_write_data(ProtocolHIDEx* protocol, void* data) { + LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data; + bool result = false; + + protocol_hid_ex_generic_encoder_start(protocol); + + if(request->write_type == LFRFIDWriteTypeT5577) { + request->t5577.block[0] = LFRFID_T5577_MODULATION_FSK2a | LFRFID_T5577_BITRATE_RF_50 | + (6 << LFRFID_T5577_MAXBLOCK_SHIFT); + request->t5577.block[1] = bit_lib_get_bits_32(protocol->encoded_data, 0, 32); + request->t5577.block[2] = bit_lib_get_bits_32(protocol->encoded_data, 32, 32); + request->t5577.block[3] = bit_lib_get_bits_32(protocol->encoded_data, 64, 32); + request->t5577.block[4] = bit_lib_get_bits_32(protocol->encoded_data, 96, 32); + request->t5577.block[5] = bit_lib_get_bits_32(protocol->encoded_data, 128, 32); + request->t5577.block[6] = bit_lib_get_bits_32(protocol->encoded_data, 160, 32); + request->t5577.blocks_to_write = 7; + result = true; + } + return result; +}; + +void protocol_hid_ex_generic_render_data(ProtocolHIDEx* protocol, string_t result) { + // TODO: parser and render functions + UNUSED(protocol); + string_printf(result, "Generic HID Extended\r\nData: Unknown"); +}; + +const ProtocolBase protocol_hid_ex_generic = { + .name = "HIDExt", + .manufacturer = "Generic", + .data_size = HID_DECODED_DATA_SIZE, + .features = LFRFIDFeatureASK, + .validate_count = 3, + .alloc = (ProtocolAlloc)protocol_hid_ex_generic_alloc, + .free = (ProtocolFree)protocol_hid_ex_generic_free, + .get_data = (ProtocolGetData)protocol_hid_ex_generic_get_data, + .decoder = + { + .start = (ProtocolDecoderStart)protocol_hid_ex_generic_decoder_start, + .feed = (ProtocolDecoderFeed)protocol_hid_ex_generic_decoder_feed, + }, + .encoder = + { + .start = (ProtocolEncoderStart)protocol_hid_ex_generic_encoder_start, + .yield = (ProtocolEncoderYield)protocol_hid_ex_generic_encoder_yield, + }, + .render_data = (ProtocolRenderData)protocol_hid_ex_generic_render_data, + .render_brief_data = (ProtocolRenderData)protocol_hid_ex_generic_render_data, + .write_data = (ProtocolWriteData)protocol_hid_ex_generic_write_data, +}; \ No newline at end of file diff --git a/lib/lfrfid/protocols/protocol_hid_ex_generic.h b/lib/lfrfid/protocols/protocol_hid_ex_generic.h new file mode 100644 index 000000000..9c4ddffff --- /dev/null +++ b/lib/lfrfid/protocols/protocol_hid_ex_generic.h @@ -0,0 +1,4 @@ +#pragma once +#include + +extern const ProtocolBase protocol_hid_ex_generic; \ No newline at end of file diff --git a/lib/lfrfid/protocols/protocol_hid_generic.c b/lib/lfrfid/protocols/protocol_hid_generic.c new file mode 100644 index 000000000..2516d6810 --- /dev/null +++ b/lib/lfrfid/protocols/protocol_hid_generic.c @@ -0,0 +1,280 @@ +#include +#include +#include +#include +#include "lfrfid_protocols.h" +#include + +#define JITTER_TIME (20) +#define MIN_TIME (64 - JITTER_TIME) +#define MAX_TIME (80 + JITTER_TIME) + +#define HID_DATA_SIZE 11 +#define HID_PREAMBLE_SIZE 1 +#define HID_PROTOCOL_SIZE_UNKNOWN 0 + +#define HID_ENCODED_DATA_SIZE (HID_PREAMBLE_SIZE + HID_DATA_SIZE + HID_PREAMBLE_SIZE) +#define HID_ENCODED_BIT_SIZE ((HID_PREAMBLE_SIZE + HID_DATA_SIZE) * 8) +#define HID_DECODED_DATA_SIZE (6) +#define HID_DECODED_BIT_SIZE ((HID_ENCODED_BIT_SIZE - HID_PREAMBLE_SIZE * 8) / 2) + +#define HID_PREAMBLE 0x1D + +typedef struct { + FSKDemod* fsk_demod; +} ProtocolHIDDecoder; + +typedef struct { + FSKOsc* fsk_osc; + uint8_t encoded_index; + uint32_t pulse; +} ProtocolHIDEncoder; + +typedef struct { + ProtocolHIDDecoder decoder; + ProtocolHIDEncoder encoder; + uint8_t encoded_data[HID_ENCODED_DATA_SIZE]; + uint8_t data[HID_DECODED_DATA_SIZE]; +} ProtocolHID; + +ProtocolHID* protocol_hid_generic_alloc(void) { + ProtocolHID* protocol = malloc(sizeof(ProtocolHID)); + protocol->decoder.fsk_demod = fsk_demod_alloc(MIN_TIME, 6, MAX_TIME, 5); + protocol->encoder.fsk_osc = fsk_osc_alloc(8, 10, 50); + + return protocol; +}; + +void protocol_hid_generic_free(ProtocolHID* protocol) { + fsk_demod_free(protocol->decoder.fsk_demod); + fsk_osc_free(protocol->encoder.fsk_osc); + free(protocol); +}; + +uint8_t* protocol_hid_generic_get_data(ProtocolHID* protocol) { + return protocol->data; +}; + +void protocol_hid_generic_decoder_start(ProtocolHID* protocol) { + memset(protocol->encoded_data, 0, HID_ENCODED_DATA_SIZE); +}; + +static bool protocol_hid_generic_can_be_decoded(const uint8_t* data) { + // check preamble + if(data[0] != HID_PREAMBLE || data[HID_PREAMBLE_SIZE + HID_DATA_SIZE] != HID_PREAMBLE) { + return false; + } + + // check for manchester encoding + for(size_t i = HID_PREAMBLE_SIZE; i < (HID_PREAMBLE_SIZE + HID_DATA_SIZE); i++) { + for(size_t n = 0; n < 4; n++) { + uint8_t bit_pair = (data[i] >> (n * 2)) & 0b11; + if(bit_pair == 0b11 || bit_pair == 0b00) { + return false; + } + } + } + + return true; +} + +static void protocol_hid_generic_decode(const uint8_t* from, uint8_t* to) { + size_t bit_index = 0; + for(size_t i = HID_PREAMBLE_SIZE; i < (HID_PREAMBLE_SIZE + HID_DATA_SIZE); i++) { + for(size_t n = 0; n < 4; n++) { + uint8_t bit_pair = (from[i] >> (6 - (n * 2))) & 0b11; + if(bit_pair == 0b01) { + bit_lib_set_bit(to, bit_index, 0); + } else if(bit_pair == 0b10) { + bit_lib_set_bit(to, bit_index, 1); + } + bit_index++; + } + } +} + +/** + * Decodes size from the HID Proximity header: + * - If any of the first six bits is 1, the key is composed of the bits + * following the first 1 + * - Otherwise, if the first six bits are 0: + * - If the seventh bit is 0, the key is composed of the remaining 37 bits. + * - If the seventh bit is 1, the size header continues until the next 1 bit, + * and the key is composed of however many bits remain. + * + * HID Proximity keys are 26 bits at minimum. If the header implies a key size + * under 26 bits, this function returns HID_PROTOCOL_SIZE_UNKNOWN. + */ +static uint8_t protocol_hid_generic_decode_protocol_size(ProtocolHID* protocol) { + for(size_t bit_index = 0; bit_index < 6; bit_index++) { + if(bit_lib_get_bit(protocol->data, bit_index)) { + return HID_DECODED_BIT_SIZE - bit_index - 1; + } + } + + if(!bit_lib_get_bit(protocol->data, 6)) { + return 37; + } + + size_t bit_index = 7; + uint8_t size = 36; + while(!bit_lib_get_bit(protocol->data, bit_index) && size >= 26) { + size--; + bit_index++; + } + return size < 26 ? HID_PROTOCOL_SIZE_UNKNOWN : size; +} + +bool protocol_hid_generic_decoder_feed(ProtocolHID* protocol, bool level, uint32_t duration) { + bool value; + uint32_t count; + bool result = false; + + fsk_demod_feed(protocol->decoder.fsk_demod, level, duration, &value, &count); + if(count > 0) { + for(size_t i = 0; i < count; i++) { + bit_lib_push_bit(protocol->encoded_data, HID_ENCODED_DATA_SIZE, value); + if(protocol_hid_generic_can_be_decoded(protocol->encoded_data)) { + protocol_hid_generic_decode(protocol->encoded_data, protocol->data); + result = true; + } + } + } + + return result; +}; + +static void protocol_hid_generic_encode(ProtocolHID* protocol) { + protocol->encoded_data[0] = HID_PREAMBLE; + + size_t bit_index = 0; + for(size_t i = 0; i < HID_DECODED_BIT_SIZE; i++) { + bool bit = bit_lib_get_bit(protocol->data, i); + if(bit) { + bit_lib_set_bit(protocol->encoded_data, 8 + bit_index, 1); + bit_lib_set_bit(protocol->encoded_data, 8 + bit_index + 1, 0); + } else { + bit_lib_set_bit(protocol->encoded_data, 8 + bit_index, 0); + bit_lib_set_bit(protocol->encoded_data, 8 + bit_index + 1, 1); + } + bit_index += 2; + } +} + +bool protocol_hid_generic_encoder_start(ProtocolHID* protocol) { + protocol->encoder.encoded_index = 0; + protocol->encoder.pulse = 0; + protocol_hid_generic_encode(protocol); + + return true; +}; + +LevelDuration protocol_hid_generic_encoder_yield(ProtocolHID* protocol) { + bool level = 0; + uint32_t duration = 0; + + // if pulse is zero, we need to output high, otherwise we need to output low + if(protocol->encoder.pulse == 0) { + // get bit + uint8_t bit = bit_lib_get_bit(protocol->encoded_data, protocol->encoder.encoded_index); + + // get pulse from oscillator + bool advance = fsk_osc_next(protocol->encoder.fsk_osc, bit, &duration); + + if(advance) { + bit_lib_increment_index(protocol->encoder.encoded_index, HID_ENCODED_BIT_SIZE); + } + + // duration diveded by 2 because we need to output high and low + duration = duration / 2; + protocol->encoder.pulse = duration; + level = true; + } else { + // output low half and reset pulse + duration = protocol->encoder.pulse; + protocol->encoder.pulse = 0; + level = false; + } + + return level_duration_make(level, duration); +}; + +bool protocol_hid_generic_write_data(ProtocolHID* protocol, void* data) { + LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data; + bool result = false; + + protocol_hid_generic_encoder_start(protocol); + + if(request->write_type == LFRFIDWriteTypeT5577) { + request->t5577.block[0] = LFRFID_T5577_MODULATION_FSK2a | LFRFID_T5577_BITRATE_RF_50 | + (3 << LFRFID_T5577_MAXBLOCK_SHIFT); + request->t5577.block[1] = bit_lib_get_bits_32(protocol->encoded_data, 0, 32); + request->t5577.block[2] = bit_lib_get_bits_32(protocol->encoded_data, 32, 32); + request->t5577.block[3] = bit_lib_get_bits_32(protocol->encoded_data, 64, 32); + request->t5577.blocks_to_write = 4; + result = true; + } + return result; +}; + +static void protocol_hid_generic_string_cat_protocol_bits(ProtocolHID* protocol, uint8_t protocol_size, string_t result) { + // round up to the nearest nibble + const uint8_t hex_character_count = (protocol_size + 3) / 4; + const uint8_t protocol_bit_index = HID_DECODED_BIT_SIZE - protocol_size; + + for(size_t i = 0; i < hex_character_count; i++) { + uint8_t nibble = + i == 0 ? bit_lib_get_bits( + protocol->data, protocol_bit_index, protocol_size % 4 == 0 ? 4 : protocol_size % 4) : + bit_lib_get_bits(protocol->data, protocol_bit_index + i * 4, 4); + string_cat_printf(result, "%X", nibble & 0xF); + } +} + +void protocol_hid_generic_render_data(ProtocolHID* protocol, string_t result) { + const uint8_t protocol_size = protocol_hid_generic_decode_protocol_size(protocol); + + if(protocol_size == HID_PROTOCOL_SIZE_UNKNOWN) { + string_printf( + result, + "Generic HID Proximity\r\n" + "Data: %02X%02X%02X%02X%02X%X", + protocol->data[0], + protocol->data[1], + protocol->data[2], + protocol->data[3], + protocol->data[4], + protocol->data[5] >> 4); + } else { + string_printf( + result, + "%hhu-bit HID Proximity\r\n" + "Data: ", + protocol_size); + protocol_hid_generic_string_cat_protocol_bits(protocol, protocol_size, result); + } +}; + +const ProtocolBase protocol_hid_generic = { + .name = "HIDProx", + .manufacturer = "Generic", + .data_size = HID_DECODED_DATA_SIZE, + .features = LFRFIDFeatureASK, + .validate_count = 6, + .alloc = (ProtocolAlloc)protocol_hid_generic_alloc, + .free = (ProtocolFree)protocol_hid_generic_free, + .get_data = (ProtocolGetData)protocol_hid_generic_get_data, + .decoder = + { + .start = (ProtocolDecoderStart)protocol_hid_generic_decoder_start, + .feed = (ProtocolDecoderFeed)protocol_hid_generic_decoder_feed, + }, + .encoder = + { + .start = (ProtocolEncoderStart)protocol_hid_generic_encoder_start, + .yield = (ProtocolEncoderYield)protocol_hid_generic_encoder_yield, + }, + .render_data = (ProtocolRenderData)protocol_hid_generic_render_data, + .render_brief_data = (ProtocolRenderData)protocol_hid_generic_render_data, + .write_data = (ProtocolWriteData)protocol_hid_generic_write_data, +}; diff --git a/lib/lfrfid/protocols/protocol_hid_generic.h b/lib/lfrfid/protocols/protocol_hid_generic.h new file mode 100644 index 000000000..22e78a4d3 --- /dev/null +++ b/lib/lfrfid/protocols/protocol_hid_generic.h @@ -0,0 +1,4 @@ +#pragma once +#include + +extern const ProtocolBase protocol_hid_generic; \ No newline at end of file diff --git a/lib/lfrfid/protocols/protocol_indala26.c b/lib/lfrfid/protocols/protocol_indala26.c new file mode 100644 index 000000000..136ececf8 --- /dev/null +++ b/lib/lfrfid/protocols/protocol_indala26.c @@ -0,0 +1,353 @@ +#include +#include +#include +#include "lfrfid_protocols.h" + +#define INDALA26_PREAMBLE_BIT_SIZE (33) +#define INDALA26_PREAMBLE_DATA_SIZE (5) + +#define INDALA26_ENCODED_BIT_SIZE (64) +#define INDALA26_ENCODED_DATA_SIZE \ + (((INDALA26_ENCODED_BIT_SIZE) / 8) + INDALA26_PREAMBLE_DATA_SIZE) +#define INDALA26_ENCODED_DATA_LAST ((INDALA26_ENCODED_BIT_SIZE) / 8) + +#define INDALA26_DECODED_BIT_SIZE (28) +#define INDALA26_DECODED_DATA_SIZE (4) + +#define INDALA26_US_PER_BIT (255) +#define INDALA26_ENCODER_PULSES_PER_BIT (16) + +typedef struct { + uint8_t data_index; + uint8_t bit_clock_index; + bool last_bit; + bool current_polarity; + bool pulse_phase; +} ProtocolIndalaEncoder; + +typedef struct { + uint8_t encoded_data[INDALA26_ENCODED_DATA_SIZE]; + uint8_t negative_encoded_data[INDALA26_ENCODED_DATA_SIZE]; + uint8_t corrupted_encoded_data[INDALA26_ENCODED_DATA_SIZE]; + uint8_t corrupted_negative_encoded_data[INDALA26_ENCODED_DATA_SIZE]; + + uint8_t data[INDALA26_DECODED_DATA_SIZE]; + ProtocolIndalaEncoder encoder; +} ProtocolIndala; + +ProtocolIndala* protocol_indala26_alloc(void) { + ProtocolIndala* protocol = malloc(sizeof(ProtocolIndala)); + return protocol; +}; + +void protocol_indala26_free(ProtocolIndala* protocol) { + free(protocol); +}; + +uint8_t* protocol_indala26_get_data(ProtocolIndala* protocol) { + return protocol->data; +}; + +void protocol_indala26_decoder_start(ProtocolIndala* protocol) { + memset(protocol->encoded_data, 0, INDALA26_ENCODED_DATA_SIZE); + memset(protocol->negative_encoded_data, 0, INDALA26_ENCODED_DATA_SIZE); + memset(protocol->corrupted_encoded_data, 0, INDALA26_ENCODED_DATA_SIZE); + memset(protocol->corrupted_negative_encoded_data, 0, INDALA26_ENCODED_DATA_SIZE); +}; + +static bool protocol_indala26_check_preamble(uint8_t* data, size_t bit_index) { + // Preamble 10100000 00000000 00000000 00000000 1 + if(*(uint32_t*)&data[bit_index / 8] != 0b00000000000000000000000010100000) return false; + if(bit_lib_get_bit(data, bit_index + 32) != 1) return false; + return true; +} + +static bool protocol_indala26_can_be_decoded(uint8_t* data) { + if(!protocol_indala26_check_preamble(data, 0)) return false; + if(!protocol_indala26_check_preamble(data, 64)) return false; + if(bit_lib_get_bit(data, 61) != 0) return false; + if(bit_lib_get_bit(data, 60) != 0) return false; + return true; +} + +static bool protocol_indala26_decoder_feed_internal(bool polarity, uint32_t time, uint8_t* data) { + time += (INDALA26_US_PER_BIT / 2); + + size_t bit_count = (time / INDALA26_US_PER_BIT); + bool result = false; + + if(bit_count < INDALA26_ENCODED_BIT_SIZE) { + for(size_t i = 0; i < bit_count; i++) { + bit_lib_push_bit(data, INDALA26_ENCODED_DATA_SIZE, polarity); + if(protocol_indala26_can_be_decoded(data)) { + result = true; + break; + } + } + } + + return result; +} + +static void protocol_indala26_decoder_save(uint8_t* data_to, const uint8_t* data_from) { + bit_lib_copy_bits(data_to, 0, 22, data_from, 33); + bit_lib_copy_bits(data_to, 22, 5, data_from, 55); + bit_lib_copy_bits(data_to, 27, 2, data_from, 62); +} + +bool protocol_indala26_decoder_feed(ProtocolIndala* protocol, bool level, uint32_t duration) { + bool result = false; + + if(duration > (INDALA26_US_PER_BIT / 2)) { + if(protocol_indala26_decoder_feed_internal(level, duration, protocol->encoded_data)) { + protocol_indala26_decoder_save(protocol->data, protocol->encoded_data); + FURI_LOG_D("Indala26", "Positive"); + result = true; + return result; + } + + if(protocol_indala26_decoder_feed_internal( + !level, duration, protocol->negative_encoded_data)) { + protocol_indala26_decoder_save(protocol->data, protocol->negative_encoded_data); + FURI_LOG_D("Indala26", "Negative"); + result = true; + return result; + } + } + + if(duration > (INDALA26_US_PER_BIT / 4)) { + // Try to decode wrong phase synced data + if(level) { + duration += 120; + } else { + if(duration > 120) { + duration -= 120; + } + } + + if(protocol_indala26_decoder_feed_internal( + level, duration, protocol->corrupted_encoded_data)) { + protocol_indala26_decoder_save(protocol->data, protocol->corrupted_encoded_data); + FURI_LOG_D("Indala26", "Positive Corrupted"); + + result = true; + return result; + } + + if(protocol_indala26_decoder_feed_internal( + !level, duration, protocol->corrupted_negative_encoded_data)) { + protocol_indala26_decoder_save( + protocol->data, protocol->corrupted_negative_encoded_data); + FURI_LOG_D("Indala26", "Negative Corrupted"); + + result = true; + return result; + } + } + + return result; +}; + +bool protocol_indala26_encoder_start(ProtocolIndala* protocol) { + memset(protocol->encoded_data, 0, INDALA26_ENCODED_DATA_SIZE); + *(uint32_t*)&protocol->encoded_data[0] = 0b00000000000000000000000010100000; + bit_lib_set_bit(protocol->encoded_data, 32, 1); + bit_lib_copy_bits(protocol->encoded_data, 33, 22, protocol->data, 0); + bit_lib_copy_bits(protocol->encoded_data, 55, 5, protocol->data, 22); + bit_lib_copy_bits(protocol->encoded_data, 62, 2, protocol->data, 27); + + protocol->encoder.last_bit = + bit_lib_get_bit(protocol->encoded_data, INDALA26_ENCODED_BIT_SIZE - 1); + protocol->encoder.data_index = 0; + protocol->encoder.current_polarity = true; + protocol->encoder.pulse_phase = true; + protocol->encoder.bit_clock_index = 0; + + return true; +}; + +LevelDuration protocol_indala26_encoder_yield(ProtocolIndala* protocol) { + LevelDuration level_duration; + ProtocolIndalaEncoder* encoder = &protocol->encoder; + + if(encoder->pulse_phase) { + level_duration = level_duration_make(encoder->current_polarity, 1); + encoder->pulse_phase = false; + } else { + level_duration = level_duration_make(!encoder->current_polarity, 1); + encoder->pulse_phase = true; + + encoder->bit_clock_index++; + if(encoder->bit_clock_index >= INDALA26_ENCODER_PULSES_PER_BIT) { + encoder->bit_clock_index = 0; + + bool current_bit = bit_lib_get_bit(protocol->encoded_data, encoder->data_index); + + if(current_bit != encoder->last_bit) { + encoder->current_polarity = !encoder->current_polarity; + } + + encoder->last_bit = current_bit; + + bit_lib_increment_index(encoder->data_index, INDALA26_ENCODED_BIT_SIZE); + } + } + + return level_duration; +}; + +// factory code +static uint8_t get_fc(const uint8_t* data) { + uint8_t fc = 0; + + fc = fc << 1 | bit_lib_get_bit(data, 24); + fc = fc << 1 | bit_lib_get_bit(data, 16); + fc = fc << 1 | bit_lib_get_bit(data, 11); + fc = fc << 1 | bit_lib_get_bit(data, 14); + fc = fc << 1 | bit_lib_get_bit(data, 15); + fc = fc << 1 | bit_lib_get_bit(data, 20); + fc = fc << 1 | bit_lib_get_bit(data, 6); + fc = fc << 1 | bit_lib_get_bit(data, 25); + + return fc; +} + +// card number +static uint16_t get_cn(const uint8_t* data) { + uint16_t cn = 0; + + cn = cn << 1 | bit_lib_get_bit(data, 9); + cn = cn << 1 | bit_lib_get_bit(data, 12); + cn = cn << 1 | bit_lib_get_bit(data, 10); + cn = cn << 1 | bit_lib_get_bit(data, 7); + cn = cn << 1 | bit_lib_get_bit(data, 19); + cn = cn << 1 | bit_lib_get_bit(data, 3); + cn = cn << 1 | bit_lib_get_bit(data, 2); + cn = cn << 1 | bit_lib_get_bit(data, 18); + cn = cn << 1 | bit_lib_get_bit(data, 13); + cn = cn << 1 | bit_lib_get_bit(data, 0); + cn = cn << 1 | bit_lib_get_bit(data, 4); + cn = cn << 1 | bit_lib_get_bit(data, 21); + cn = cn << 1 | bit_lib_get_bit(data, 23); + cn = cn << 1 | bit_lib_get_bit(data, 26); + cn = cn << 1 | bit_lib_get_bit(data, 17); + cn = cn << 1 | bit_lib_get_bit(data, 8); + + return cn; +} + +void protocol_indala26_render_data_internal(ProtocolIndala* protocol, string_t result, bool brief) { + bool wiegand_correct = true; + bool checksum_correct = true; + + const uint8_t fc = get_fc(protocol->data); + const uint16_t card = get_cn(protocol->data); + const uint32_t fc_and_card = fc << 16 | card; + const uint8_t checksum = bit_lib_get_bit(protocol->data, 27) << 1 | + bit_lib_get_bit(protocol->data, 28); + const bool even_parity = bit_lib_get_bit(protocol->data, 1); + const bool odd_parity = bit_lib_get_bit(protocol->data, 5); + + // indala checksum + uint8_t checksum_sum = 0; + checksum_sum += ((fc_and_card >> 14) & 1); + checksum_sum += ((fc_and_card >> 12) & 1); + checksum_sum += ((fc_and_card >> 9) & 1); + checksum_sum += ((fc_and_card >> 8) & 1); + checksum_sum += ((fc_and_card >> 6) & 1); + checksum_sum += ((fc_and_card >> 5) & 1); + checksum_sum += ((fc_and_card >> 2) & 1); + checksum_sum += ((fc_and_card >> 0) & 1); + checksum_sum = checksum_sum & 0b1; + + if(checksum_sum == 1 && checksum == 0b01) { + } else if(checksum_sum == 0 && checksum == 0b10) { + } else { + checksum_correct = false; + } + + // wiegand parity + uint8_t even_parity_sum = 0; + for(int8_t i = 12; i < 24; i++) { + if(((fc_and_card >> i) & 1) == 1) { + even_parity_sum++; + } + } + if(even_parity_sum % 2 != even_parity) wiegand_correct = false; + + uint8_t odd_parity_sum = 1; + for(int8_t i = 0; i < 12; i++) { + if(((fc_and_card >> i) & 1) == 1) { + odd_parity_sum++; + } + } + if(odd_parity_sum % 2 != odd_parity) wiegand_correct = false; + + if(brief) { + string_printf( + result, + "FC: %u\r\nCard: %u, Parity:%s%s", + fc, + card, + (checksum_correct ? "+" : "-"), + (wiegand_correct ? "+" : "-")); + } else { + string_printf( + result, + "FC: %u\r\n" + "Card: %u\r\n" + "Checksum: %s\r\n" + "W26 Parity: %s", + fc, + card, + (checksum_correct ? "+" : "-"), + (wiegand_correct ? "+" : "-")); + } +} +void protocol_indala26_render_data(ProtocolIndala* protocol, string_t result) { + protocol_indala26_render_data_internal(protocol, result, false); +} +void protocol_indala26_render_brief_data(ProtocolIndala* protocol, string_t result) { + protocol_indala26_render_data_internal(protocol, result, true); +} + +bool protocol_indala26_write_data(ProtocolIndala* protocol, void* data) { + LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data; + bool result = false; + + protocol_indala26_encoder_start(protocol); + + if(request->write_type == LFRFIDWriteTypeT5577) { + request->t5577.block[0] = LFRFID_T5577_BITRATE_RF_32 | LFRFID_T5577_MODULATION_PSK1 | + (2 << LFRFID_T5577_MAXBLOCK_SHIFT); + request->t5577.block[1] = bit_lib_get_bits_32(protocol->encoded_data, 0, 32); + request->t5577.block[2] = bit_lib_get_bits_32(protocol->encoded_data, 32, 32); + request->t5577.blocks_to_write = 3; + result = true; + } + return result; +}; + +const ProtocolBase protocol_indala26 = { + .name = "Indala26", + .manufacturer = "Motorola", + .data_size = INDALA26_DECODED_DATA_SIZE, + .features = LFRFIDFeaturePSK, + .validate_count = 6, + .alloc = (ProtocolAlloc)protocol_indala26_alloc, + .free = (ProtocolFree)protocol_indala26_free, + .get_data = (ProtocolGetData)protocol_indala26_get_data, + .decoder = + { + .start = (ProtocolDecoderStart)protocol_indala26_decoder_start, + .feed = (ProtocolDecoderFeed)protocol_indala26_decoder_feed, + }, + .encoder = + { + .start = (ProtocolEncoderStart)protocol_indala26_encoder_start, + .yield = (ProtocolEncoderYield)protocol_indala26_encoder_yield, + }, + .render_data = (ProtocolRenderData)protocol_indala26_render_data, + .render_brief_data = (ProtocolRenderData)protocol_indala26_render_brief_data, + .write_data = (ProtocolWriteData)protocol_indala26_write_data, +}; \ No newline at end of file diff --git a/lib/lfrfid/protocols/protocol_indala26.h b/lib/lfrfid/protocols/protocol_indala26.h new file mode 100644 index 000000000..c0c61784b --- /dev/null +++ b/lib/lfrfid/protocols/protocol_indala26.h @@ -0,0 +1,4 @@ +#pragma once +#include + +extern const ProtocolBase protocol_indala26; diff --git a/lib/lfrfid/protocols/protocol_io_prox_xsf.c b/lib/lfrfid/protocols/protocol_io_prox_xsf.c new file mode 100644 index 000000000..66b1610bf --- /dev/null +++ b/lib/lfrfid/protocols/protocol_io_prox_xsf.c @@ -0,0 +1,297 @@ +#include +#include +#include +#include +#include +#include "lfrfid_protocols.h" + +#define JITTER_TIME (20) +#define MIN_TIME (64 - JITTER_TIME) +#define MAX_TIME (80 + JITTER_TIME) + +#define IOPROXXSF_DECODED_DATA_SIZE (4) +#define IOPROXXSF_ENCODED_DATA_SIZE (8) + +#define IOPROXXSF_BIT_SIZE (8) +#define IOPROXXSF_BIT_MAX_SIZE (IOPROXXSF_BIT_SIZE * IOPROXXSF_ENCODED_DATA_SIZE) + +typedef struct { + FSKDemod* fsk_demod; +} ProtocolIOProxXSFDecoder; + +typedef struct { + FSKOsc* fsk_osc; + uint8_t encoded_index; +} ProtocolIOProxXSFEncoder; + +typedef struct { + ProtocolIOProxXSFEncoder encoder; + ProtocolIOProxXSFDecoder decoder; + uint8_t encoded_data[IOPROXXSF_ENCODED_DATA_SIZE]; + uint8_t data[IOPROXXSF_DECODED_DATA_SIZE]; +} ProtocolIOProxXSF; + +ProtocolIOProxXSF* protocol_io_prox_xsf_alloc(void) { + ProtocolIOProxXSF* protocol = malloc(sizeof(ProtocolIOProxXSF)); + protocol->decoder.fsk_demod = fsk_demod_alloc(MIN_TIME, 8, MAX_TIME, 6); + protocol->encoder.fsk_osc = fsk_osc_alloc(8, 10, 64); + return protocol; +}; + +void protocol_io_prox_xsf_free(ProtocolIOProxXSF* protocol) { + fsk_demod_free(protocol->decoder.fsk_demod); + fsk_osc_free(protocol->encoder.fsk_osc); + free(protocol); +}; + +uint8_t* protocol_io_prox_xsf_get_data(ProtocolIOProxXSF* protocol) { + return protocol->data; +}; + +void protocol_io_prox_xsf_decoder_start(ProtocolIOProxXSF* protocol) { + memset(protocol->encoded_data, 0, IOPROXXSF_ENCODED_DATA_SIZE); +}; + +static uint8_t protocol_io_prox_xsf_compute_checksum(const uint8_t* data) { + // Packet structure: + // + //0 1 2 3 4 5 6 7 + //v v v v v v v v + //01234567 8 9ABCDEF0 1 23456789 A BCDEF012 3 456789AB C DEF01234 5 6789ABCD EF + //00000000 0 VVVVVVVV 1 WWWWWWWW 1 XXXXXXXX 1 YYYYYYYY 1 ZZZZZZZZ 1 CHECKSUM 11 + // + // algorithm as observed by the proxmark3 folks + // CHECKSUM == 0xFF - (V + W + X + Y + Z) + + uint8_t checksum = 0; + + for(size_t i = 1; i <= 5; i++) { + checksum += bit_lib_get_bits(data, 9 * i, 8); + } + + return 0xFF - checksum; +} + +static bool protocol_io_prox_xsf_can_be_decoded(const uint8_t* encoded_data) { + // Packet framing + // + //0 1 2 3 4 5 6 7 + //v v v v v v v v + //01234567 89ABCDEF 01234567 89ABCDEF 01234567 89ABCDEF 01234567 89ABCDEF + //----------------------------------------------------------------------- + //00000000 01______ _1______ __1_____ ___1____ ____1___ _____1XX XXXXXX11 + // + // _ = variable data + // 0 = preamble 0 + // 1 = framing 1 + // X = checksum + + // Validate the packet preamble is there... + if(encoded_data[0] != 0b00000000) { + return false; + } + if((encoded_data[1] >> 6) != 0b01) { + return false; + } + + // ... check for known ones... + if(bit_lib_bit_is_not_set(encoded_data[2], 6)) { + return false; + } + if(bit_lib_bit_is_not_set(encoded_data[3], 5)) { + return false; + } + if(bit_lib_bit_is_not_set(encoded_data[4], 4)) { + return false; + } + if(bit_lib_bit_is_not_set(encoded_data[5], 3)) { + return false; + } + if(bit_lib_bit_is_not_set(encoded_data[6], 2)) { + return false; + } + if(bit_lib_bit_is_not_set(encoded_data[7], 1)) { + return false; + } + if(bit_lib_bit_is_not_set(encoded_data[7], 0)) { + return false; + } + + // ... and validate our checksums. + uint8_t checksum = protocol_io_prox_xsf_compute_checksum(encoded_data); + uint8_t checkval = bit_lib_get_bits(encoded_data, 54, 8); + + if(checksum != checkval) { + return false; + } + + return true; +} + +void protocol_io_prox_xsf_decode(const uint8_t* encoded_data, uint8_t* decoded_data) { + // Packet structure: + // (Note: the second word seems fixed; but this may not be a guarantee; + // it currently has no meaning.) + // + //0 1 2 3 4 5 6 7 + //v v v v v v v v + //01234567 89ABCDEF 01234567 89ABCDEF 01234567 89ABCDEF 01234567 89ABCDEF + //----------------------------------------------------------------------- + //00000000 01111000 01FFFFFF FF1VVVVV VVV1CCCC CCCC1CCC CCCCC1XX XXXXXX11 + // + // F = facility code + // V = version + // C = code + // X = checksum + + // Facility code + decoded_data[0] = bit_lib_get_bits(encoded_data, 18, 8); + + // Version code. + decoded_data[1] = bit_lib_get_bits(encoded_data, 27, 8); + + // Code bytes. + decoded_data[2] = bit_lib_get_bits(encoded_data, 36, 8); + decoded_data[3] = bit_lib_get_bits(encoded_data, 45, 8); +} + +bool protocol_io_prox_xsf_decoder_feed(ProtocolIOProxXSF* protocol, bool level, uint32_t duration) { + bool result = false; + + uint32_t count; + bool value; + + fsk_demod_feed(protocol->decoder.fsk_demod, level, duration, &value, &count); + for(size_t i = 0; i < count; i++) { + bit_lib_push_bit(protocol->encoded_data, IOPROXXSF_ENCODED_DATA_SIZE, value); + if(protocol_io_prox_xsf_can_be_decoded(protocol->encoded_data)) { + protocol_io_prox_xsf_decode(protocol->encoded_data, protocol->data); + result = true; + break; + } + } + + return result; +}; + +static void protocol_io_prox_xsf_encode(const uint8_t* decoded_data, uint8_t* encoded_data) { + // Packet to transmit: + // + // 0 10 20 30 40 50 60 + // v v v v v v v + // 01234567 8 90123456 7 89012345 6 78901234 5 67890123 4 56789012 3 45678901 23 + // ----------------------------------------------------------------------------- + // 00000000 0 11110000 1 facility 1 version_ 1 code-one 1 code-two 1 checksum 11 + + // Preamble. + bit_lib_set_bits(encoded_data, 0, 0b00000000, 8); + bit_lib_set_bit(encoded_data, 8, 0); + + bit_lib_set_bits(encoded_data, 9, 0b11110000, 8); + bit_lib_set_bit(encoded_data, 17, 1); + + // Facility code. + bit_lib_set_bits(encoded_data, 18, decoded_data[0], 8); + bit_lib_set_bit(encoded_data, 26, 1); + + // Version + bit_lib_set_bits(encoded_data, 27, decoded_data[1], 8); + bit_lib_set_bit(encoded_data, 35, 1); + + // Code one + bit_lib_set_bits(encoded_data, 36, decoded_data[2], 8); + bit_lib_set_bit(encoded_data, 44, 1); + + // Code two + bit_lib_set_bits(encoded_data, 45, decoded_data[3], 8); + bit_lib_set_bit(encoded_data, 53, 1); + + // Checksum + bit_lib_set_bits(encoded_data, 54, protocol_io_prox_xsf_compute_checksum(encoded_data), 8); + bit_lib_set_bit(encoded_data, 62, 1); + bit_lib_set_bit(encoded_data, 63, 1); +} + +bool protocol_io_prox_xsf_encoder_start(ProtocolIOProxXSF* protocol) { + protocol_io_prox_xsf_encode(protocol->data, protocol->encoded_data); + protocol->encoder.encoded_index = 0; + fsk_osc_reset(protocol->encoder.fsk_osc); + return true; +}; + +LevelDuration protocol_io_prox_xsf_encoder_yield(ProtocolIOProxXSF* protocol) { + bool level; + uint32_t duration; + + bool bit = bit_lib_get_bit(protocol->encoded_data, protocol->encoder.encoded_index); + bool advance = fsk_osc_next_half(protocol->encoder.fsk_osc, bit, &level, &duration); + + if(advance) { + bit_lib_increment_index(protocol->encoder.encoded_index, IOPROXXSF_BIT_MAX_SIZE); + } + return level_duration_make(level, duration); +}; + +void protocol_io_prox_xsf_render_data(ProtocolIOProxXSF* protocol, string_t result) { + uint8_t* data = protocol->data; + string_printf( + result, + "FC: %u\r\n" + "VС: %u\r\n" + "Card: %u", + data[0], + data[1], + (uint16_t)((data[2] << 8) | (data[3]))); +} + +void protocol_io_prox_xsf_render_brief_data(ProtocolIOProxXSF* protocol, string_t result) { + uint8_t* data = protocol->data; + string_printf( + result, + "FC: %u, VС: %u\r\n" + "Card: %u", + data[0], + data[1], + (uint16_t)((data[2] << 8) | (data[3]))); +} + +bool protocol_io_prox_xsf_write_data(ProtocolIOProxXSF* protocol, void* data) { + LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data; + bool result = false; + + protocol_io_prox_xsf_encode(protocol->data, protocol->encoded_data); + + if(request->write_type == LFRFIDWriteTypeT5577) { + request->t5577.block[0] = LFRFID_T5577_MODULATION_FSK2a | LFRFID_T5577_BITRATE_RF_64 | + (2 << LFRFID_T5577_MAXBLOCK_SHIFT); + request->t5577.block[1] = bit_lib_get_bits_32(protocol->encoded_data, 0, 32); + request->t5577.block[2] = bit_lib_get_bits_32(protocol->encoded_data, 32, 32); + request->t5577.blocks_to_write = 3; + result = true; + } + return result; +}; + +const ProtocolBase protocol_io_prox_xsf = { + .name = "IoProxXSF", + .manufacturer = "Kantech", + .data_size = IOPROXXSF_DECODED_DATA_SIZE, + .features = LFRFIDFeatureASK, + .validate_count = 3, + .alloc = (ProtocolAlloc)protocol_io_prox_xsf_alloc, + .free = (ProtocolFree)protocol_io_prox_xsf_free, + .get_data = (ProtocolGetData)protocol_io_prox_xsf_get_data, + .decoder = + { + .start = (ProtocolDecoderStart)protocol_io_prox_xsf_decoder_start, + .feed = (ProtocolDecoderFeed)protocol_io_prox_xsf_decoder_feed, + }, + .encoder = + { + .start = (ProtocolEncoderStart)protocol_io_prox_xsf_encoder_start, + .yield = (ProtocolEncoderYield)protocol_io_prox_xsf_encoder_yield, + }, + .render_data = (ProtocolRenderData)protocol_io_prox_xsf_render_data, + .render_brief_data = (ProtocolRenderData)protocol_io_prox_xsf_render_brief_data, + .write_data = (ProtocolWriteData)protocol_io_prox_xsf_write_data, +}; \ No newline at end of file diff --git a/lib/lfrfid/protocols/protocol_io_prox_xsf.h b/lib/lfrfid/protocols/protocol_io_prox_xsf.h new file mode 100644 index 000000000..af94fb463 --- /dev/null +++ b/lib/lfrfid/protocols/protocol_io_prox_xsf.h @@ -0,0 +1,4 @@ +#pragma once +#include + +extern const ProtocolBase protocol_io_prox_xsf; diff --git a/lib/lfrfid/tools/bit_lib.c b/lib/lfrfid/tools/bit_lib.c new file mode 100644 index 000000000..b57bda8a8 --- /dev/null +++ b/lib/lfrfid/tools/bit_lib.c @@ -0,0 +1,291 @@ +#include "bit_lib.h" +#include +#include + +void bit_lib_push_bit(uint8_t* data, size_t data_size, bool bit) { + size_t last_index = data_size - 1; + + for(size_t i = 0; i < last_index; ++i) { + data[i] = (data[i] << 1) | ((data[i + 1] >> 7) & 1); + } + data[last_index] = (data[last_index] << 1) | bit; +} + +void bit_lib_set_bit(uint8_t* data, size_t position, bool bit) { + if(bit) { + data[position / 8] |= 1UL << (7 - (position % 8)); + } else { + data[position / 8] &= ~(1UL << (7 - (position % 8))); + } +} + +void bit_lib_set_bits(uint8_t* data, size_t position, uint8_t byte, uint8_t length) { + furi_check(length <= 8); + furi_check(length > 0); + + for(uint8_t i = 0; i < length; ++i) { + uint8_t shift = (length - 1) - i; + bit_lib_set_bit(data, position + i, (byte >> shift) & 1); + } +} + +bool bit_lib_get_bit(const uint8_t* data, size_t position) { + return (data[position / 8] >> (7 - (position % 8))) & 1; +} + +uint8_t bit_lib_get_bits(const uint8_t* data, size_t position, uint8_t length) { + uint8_t shift = position % 8; + if(shift == 0) { + return data[position / 8] >> (8 - length); + } else { + // TODO fix read out of bounds + uint8_t value = (data[position / 8] << (shift)); + value |= data[position / 8 + 1] >> (8 - shift); + value = value >> (8 - length); + return value; + } +} + +uint16_t bit_lib_get_bits_16(const uint8_t* data, size_t position, uint8_t length) { + uint16_t value = 0; + if(length <= 8) { + value = bit_lib_get_bits(data, position, length); + } else { + value = bit_lib_get_bits(data, position, 8) << (length - 8); + value |= bit_lib_get_bits(data, position + 8, length - 8); + } + return value; +} + +uint32_t bit_lib_get_bits_32(const uint8_t* data, size_t position, uint8_t length) { + uint32_t value = 0; + if(length <= 8) { + value = bit_lib_get_bits(data, position, length); + } else if(length <= 16) { + value = bit_lib_get_bits(data, position, 8) << (length - 8); + value |= bit_lib_get_bits(data, position + 8, length - 8); + } else if(length <= 24) { + value = bit_lib_get_bits(data, position, 8) << (length - 8); + value |= bit_lib_get_bits(data, position + 8, 8) << (length - 16); + value |= bit_lib_get_bits(data, position + 16, length - 16); + } else { + value = bit_lib_get_bits(data, position, 8) << (length - 8); + value |= bit_lib_get_bits(data, position + 8, 8) << (length - 16); + value |= bit_lib_get_bits(data, position + 16, 8) << (length - 24); + value |= bit_lib_get_bits(data, position + 24, length - 24); + } + + return value; +} + +bool bit_lib_test_parity_32(uint32_t bits, BitLibParity parity) { +#if !defined __GNUC__ +#error Please, implement parity test for non-GCC compilers +#else + switch(parity) { + case BitLibParityEven: + return __builtin_parity(bits); + case BitLibParityOdd: + return !__builtin_parity(bits); + default: + furi_crash("Unknown parity"); + } +#endif +} + +bool bit_lib_test_parity( + const uint8_t* bits, + size_t position, + uint8_t length, + BitLibParity parity, + uint8_t parity_length) { + uint32_t parity_block; + bool result = true; + const size_t parity_blocks_count = length / parity_length; + + for(size_t i = 0; i < parity_blocks_count; ++i) { + switch(parity) { + case BitLibParityEven: + case BitLibParityOdd: + parity_block = bit_lib_get_bits_32(bits, position + i * parity_length, parity_length); + if(!bit_lib_test_parity_32(parity_block, parity)) { + result = false; + } + break; + case BitLibParityAlways0: + if(bit_lib_get_bit(bits, position + i * parity_length + parity_length - 1)) { + result = false; + } + break; + case BitLibParityAlways1: + if(!bit_lib_get_bit(bits, position + i * parity_length + parity_length - 1)) { + result = false; + } + break; + } + + if(!result) break; + } + return result; +} + +size_t bit_lib_remove_bit_every_nth(uint8_t* data, size_t position, uint8_t length, uint8_t n) { + size_t counter = 0; + size_t result_counter = 0; + uint8_t bit_buffer = 0; + uint8_t bit_counter = 0; + + while(counter < length) { + if((counter + 1) % n != 0) { + bit_buffer = (bit_buffer << 1) | bit_lib_get_bit(data, position + counter); + bit_counter++; + } + + if(bit_counter == 8) { + bit_lib_set_bits(data, position + result_counter, bit_buffer, 8); + bit_counter = 0; + bit_buffer = 0; + result_counter += 8; + } + counter++; + } + + if(bit_counter != 0) { + bit_lib_set_bits(data, position + result_counter, bit_buffer, bit_counter); + result_counter += bit_counter; + } + return result_counter; +} + +void bit_lib_copy_bits( + uint8_t* data, + size_t position, + size_t length, + const uint8_t* source, + size_t source_position) { + for(size_t i = 0; i < length; ++i) { + bit_lib_set_bit(data, position + i, bit_lib_get_bit(source, source_position + i)); + } +} + +void bit_lib_reverse_bits(uint8_t* data, size_t position, uint8_t length) { + size_t i = 0; + size_t j = length - 1; + + while(i < j) { + bool tmp = bit_lib_get_bit(data, position + i); + bit_lib_set_bit(data, position + i, bit_lib_get_bit(data, position + j)); + bit_lib_set_bit(data, position + j, tmp); + i++; + j--; + } +} + +uint8_t bit_lib_get_bit_count(uint32_t data) { +#if defined __GNUC__ + return __builtin_popcountl(data); +#else +#error Please, implement popcount for non-GCC compilers +#endif +} + +void bit_lib_print_bits(const uint8_t* data, size_t length) { + for(size_t i = 0; i < length; ++i) { + printf("%u", bit_lib_get_bit(data, i)); + } +} + +void bit_lib_print_regions( + const BitLibRegion* regions, + size_t region_count, + const uint8_t* data, + size_t length) { + // print data + bit_lib_print_bits(data, length); + printf("\r\n"); + + // print regions + for(size_t c = 0; c < length; ++c) { + bool print = false; + + for(size_t i = 0; i < region_count; i++) { + if(regions[i].start <= c && c < regions[i].start + regions[i].length) { + print = true; + printf("%c", regions[i].mark); + break; + } + } + + if(!print) { + printf(" "); + } + } + printf("\r\n"); + + // print regions data + for(size_t c = 0; c < length; ++c) { + bool print = false; + + for(size_t i = 0; i < region_count; i++) { + if(regions[i].start <= c && c < regions[i].start + regions[i].length) { + print = true; + printf("%u", bit_lib_get_bit(data, c)); + break; + } + } + + if(!print) { + printf(" "); + } + } + printf("\r\n"); +} + +uint16_t bit_lib_reverse_16_fast(uint16_t data) { + uint16_t result = 0; + result |= (data & 0x8000) >> 15; + result |= (data & 0x4000) >> 13; + result |= (data & 0x2000) >> 11; + result |= (data & 0x1000) >> 9; + result |= (data & 0x0800) >> 7; + result |= (data & 0x0400) >> 5; + result |= (data & 0x0200) >> 3; + result |= (data & 0x0100) >> 1; + result |= (data & 0x0080) << 1; + result |= (data & 0x0040) << 3; + result |= (data & 0x0020) << 5; + result |= (data & 0x0010) << 7; + result |= (data & 0x0008) << 9; + result |= (data & 0x0004) << 11; + result |= (data & 0x0002) << 13; + result |= (data & 0x0001) << 15; + return result; +} + +uint16_t bit_lib_crc16( + uint8_t const* data, + size_t data_size, + uint16_t polynom, + uint16_t init, + bool ref_in, + bool ref_out, + uint16_t xor_out) { + uint16_t crc = init; + + for(size_t i = 0; i < data_size; ++i) { + uint8_t byte = data[i]; + if(ref_in) byte = bit_lib_reverse_16_fast(byte) >> 8; + + for(size_t j = 0; j < 8; ++j) { + bool c15 = (crc >> 15 & 1); + bool bit = (byte >> (7 - j) & 1); + crc <<= 1; + if(c15 ^ bit) crc ^= polynom; + } + } + + if(ref_out) crc = bit_lib_reverse_16_fast(crc); + crc ^= xor_out; + + return crc; +} \ No newline at end of file diff --git a/lib/lfrfid/tools/bit_lib.h b/lib/lfrfid/tools/bit_lib.h new file mode 100644 index 000000000..24aae0e9c --- /dev/null +++ b/lib/lfrfid/tools/bit_lib.h @@ -0,0 +1,220 @@ +#pragma once +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + BitLibParityEven, + BitLibParityOdd, + BitLibParityAlways0, + BitLibParityAlways1, +} BitLibParity; + +/** @brief Increment and wrap around a value. + * @param index value to increment + * @param length wrap-around range + */ +#define bit_lib_increment_index(index, length) (index = (((index) + 1) % (length))) + +/** @brief Test if a bit is set. + * @param data value to test + * @param index bit index to test + */ +#define bit_lib_bit_is_set(data, index) ((data & (1 << (index))) != 0) + +/** @brief Test if a bit is not set. + * @param data value to test + * @param index bit index to test + */ +#define bit_lib_bit_is_not_set(data, index) ((data & (1 << (index))) == 0) + +/** @brief Push a bit into a byte array. + * @param data array to push bit into + * @param data_size array size + * @param bit bit to push + */ +void bit_lib_push_bit(uint8_t* data, size_t data_size, bool bit); + +/** @brief Set a bit in a byte array. + * @param data array to set bit in + * @param position The position of the bit to set. + * @param bit bit value to set + */ +void bit_lib_set_bit(uint8_t* data, size_t position, bool bit); + +/** @brief Set the bit at the given position to the given value. + * @param data The data to set the bit in. + * @param position The position of the bit to set. + * @param byte The data to set the bit to. + * @param length The length of the data. + */ +void bit_lib_set_bits(uint8_t* data, size_t position, uint8_t byte, uint8_t length); + +/** @brief Get the bit of a byte. + * @param data The byte to get the bits from. + * @param position The position of the bit. + * @return The bit. + */ +bool bit_lib_get_bit(const uint8_t* data, size_t position); + +/** + * @brief Get the bits of a data, as uint8_t. + * @param data The data to get the bits from. + * @param position The position of the first bit. + * @param length The length of the bits. + * @return The bits. + */ +uint8_t bit_lib_get_bits(const uint8_t* data, size_t position, uint8_t length); + +/** + * @brief Get the bits of a data, as uint16_t. + * @param data The data to get the bits from. + * @param position The position of the first bit. + * @param length The length of the bits. + * @return The bits. + */ +uint16_t bit_lib_get_bits_16(const uint8_t* data, size_t position, uint8_t length); + +/** + * @brief Get the bits of a data, as uint32_t. + * @param data The data to get the bits from. + * @param position The position of the first bit. + * @param length The length of the bits. + * @return The bits. + */ +uint32_t bit_lib_get_bits_32(const uint8_t* data, size_t position, uint8_t length); + +/** + * @brief Test parity of given bits + * @param bits Bits to test parity of + * @param parity Parity to test against + * @return true if parity is correct, false otherwise + */ +bool bit_lib_test_parity_32(uint32_t bits, BitLibParity parity); + +/** + * @brief Test parity of bit array, check parity for every parity_length block from start + * + * @param data Bit array + * @param position Start position + * @param length Bit count + * @param parity Parity to test against + * @param parity_length Parity block length + * @return true + * @return false + */ +bool bit_lib_test_parity( + const uint8_t* data, + size_t position, + uint8_t length, + BitLibParity parity, + uint8_t parity_length); + +/** + * @brief Remove bit every n in array and shift array left. Useful to remove parity. + * + * @param data Bit array + * @param position Start position + * @param length Bit count + * @param n every n bit will be removed + * @return size_t + */ +size_t bit_lib_remove_bit_every_nth(uint8_t* data, size_t position, uint8_t length, uint8_t n); + +/** + * @brief Copy bits from source to destination. + * + * @param data destination array + * @param position position in destination array + * @param length length of bits to copy + * @param source source array + * @param source_position position in source array + */ +void bit_lib_copy_bits( + uint8_t* data, + size_t position, + size_t length, + const uint8_t* source, + size_t source_position); + +/** + * @brief Reverse bits in bit array + * + * @param data Bit array + * @param position start position + * @param length length of bits to reverse + */ +void bit_lib_reverse_bits(uint8_t* data, size_t position, uint8_t length); + +/** + * @brief Count 1 bits in data + * + * @param data + * @return uint8_t set bit count + */ +uint8_t bit_lib_get_bit_count(uint32_t data); + +/** + * @brief Print data as bit array + * + * @param data + * @param length + */ +void bit_lib_print_bits(const uint8_t* data, size_t length); + +typedef struct { + const char mark; + const size_t start; + const size_t length; +} BitLibRegion; + +/** + * @brief Print data as bit array and mark regions. Regions needs to be sorted by start position. + * + * @param regions + * @param region_count + * @param data + * @param length + */ +void bit_lib_print_regions( + const BitLibRegion* regions, + size_t region_count, + const uint8_t* data, + size_t length); + +/** + * @brief Reverse bits in uint16_t, faster than generic bit_lib_reverse_bits. + * + * @param data + * @return uint16_t + */ +uint16_t bit_lib_reverse_16_fast(uint16_t data); + +/** + * @brief Slow, but generic CRC16 implementation + * + * @param data + * @param data_size + * @param polynom CRC polynom + * @param init init value + * @param ref_in true if the right bit is older + * @param ref_out true to reverse output + * @param xor_out xor output with this value + * @return uint16_t + */ +uint16_t bit_lib_crc16( + uint8_t const* data, + size_t data_size, + uint16_t polynom, + uint16_t init, + bool ref_in, + bool ref_out, + uint16_t xor_out); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/lib/lfrfid/tools/fsk_demod.c b/lib/lfrfid/tools/fsk_demod.c new file mode 100644 index 000000000..a5155d88e --- /dev/null +++ b/lib/lfrfid/tools/fsk_demod.c @@ -0,0 +1,93 @@ +#include +#include "fsk_demod.h" + +struct FSKDemod { + uint32_t low_time; + uint32_t low_pulses; + uint32_t hi_time; + uint32_t hi_pulses; + + bool invert; + uint32_t mid_time; + uint32_t time; + uint32_t count; + bool last_pulse; +}; + +FSKDemod* + fsk_demod_alloc(uint32_t low_time, uint32_t low_pulses, uint32_t hi_time, uint32_t hi_pulses) { + FSKDemod* demod = malloc(sizeof(FSKDemod)); + demod->invert = false; + + if(low_time > hi_time) { + uint32_t tmp; + tmp = hi_time; + hi_time = low_time; + low_time = tmp; + + tmp = hi_pulses; + hi_pulses = low_pulses; + low_pulses = tmp; + + demod->invert = true; + } + + demod->low_time = low_time; + demod->low_pulses = low_pulses; + demod->hi_time = hi_time; + demod->hi_pulses = hi_pulses; + + demod->mid_time = (hi_time - low_time) / 2 + low_time; + demod->time = 0; + demod->count = 0; + demod->last_pulse = false; + + return demod; +} + +void fsk_demod_free(FSKDemod* demod) { + free(demod); +} + +void fsk_demod_feed(FSKDemod* demod, bool polarity, uint32_t time, bool* value, uint32_t* count) { + *count = 0; + + if(polarity) { + // accumulate time + demod->time = time; + } else { + demod->time += time; + + // check for valid pulse + if(demod->time >= demod->low_time && demod->time < demod->hi_time) { + bool pulse; + + if(demod->time < demod->mid_time) { + pulse = false; + } else { + pulse = true; + } + + demod->count++; + + // check for edge transition + if(demod->last_pulse != pulse) { + uint32_t data_count = demod->count + 1; + + if(demod->last_pulse) { + data_count /= demod->hi_pulses; + *value = !demod->invert; + } else { + data_count /= demod->low_pulses; + *value = demod->invert; + } + + *count = data_count; + demod->count = 0; + demod->last_pulse = pulse; + } + } else { + demod->count = 0; + } + } +} diff --git a/lib/lfrfid/tools/fsk_demod.h b/lib/lfrfid/tools/fsk_demod.h new file mode 100644 index 000000000..d816b0dac --- /dev/null +++ b/lib/lfrfid/tools/fsk_demod.h @@ -0,0 +1,44 @@ +#pragma once +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct FSKDemod FSKDemod; + +/** + * @brief Allocate a new FSKDemod instance + * FSKDemod is a demodulator that can decode FSK encoded data + * + * @param low_time time between rising edges for the 0 bit + * @param low_pulses rising edges count for the 0 bit + * @param hi_time time between rising edges for the 1 bit + * @param hi_pulses rising edges count for the 1 bit + * @return FSKDemod* + */ +FSKDemod* + fsk_demod_alloc(uint32_t low_time, uint32_t low_pulses, uint32_t hi_time, uint32_t hi_pulses); + +/** + * @brief Free a FSKDemod instance + * + * @param fsk_demod + */ +void fsk_demod_free(FSKDemod* fsk_demod); + +/** + * @brief Feed sample to demodulator + * + * @param demod FSKDemod instance + * @param polarity sample polarity + * @param time sample time + * @param value demodulated bit value + * @param count demodulated bit count + */ +void fsk_demod_feed(FSKDemod* demod, bool polarity, uint32_t time, bool* value, uint32_t* count); + +#ifdef __cplusplus +} +#endif diff --git a/lib/lfrfid/tools/fsk_ocs.c b/lib/lfrfid/tools/fsk_ocs.c new file mode 100644 index 000000000..1fd46cf17 --- /dev/null +++ b/lib/lfrfid/tools/fsk_ocs.c @@ -0,0 +1,62 @@ +#include "fsk_osc.h" +#include + +struct FSKOsc { + uint16_t freq[2]; + uint16_t osc_phase_max; + int32_t osc_phase_current; + + uint32_t pulse; +}; + +FSKOsc* fsk_osc_alloc(uint32_t freq_low, uint32_t freq_hi, uint32_t osc_phase_max) { + FSKOsc* osc = malloc(sizeof(FSKOsc)); + osc->freq[0] = freq_low; + osc->freq[1] = freq_hi; + osc->osc_phase_max = osc_phase_max; + osc->osc_phase_current = 0; + osc->pulse = 0; + return osc; +} + +void fsk_osc_free(FSKOsc* osc) { + free(osc); +} + +void fsk_osc_reset(FSKOsc* osc) { + osc->osc_phase_current = 0; + osc->pulse = 0; +} + +bool fsk_osc_next(FSKOsc* osc, bool bit, uint32_t* period) { + bool advance = false; + *period = osc->freq[bit]; + osc->osc_phase_current += *period; + + if(osc->osc_phase_current > osc->osc_phase_max) { + advance = true; + osc->osc_phase_current -= osc->osc_phase_max; + } + + return advance; +} + +bool fsk_osc_next_half(FSKOsc* osc, bool bit, bool* level, uint32_t* duration) { + bool advance = false; + + // if pulse is zero, we need to output high, otherwise we need to output low + if(osc->pulse == 0) { + uint32_t length; + advance = fsk_osc_next(osc, bit, &length); + *duration = length / 2; + osc->pulse = *duration; + *level = true; + } else { + // output low half and reset pulse + *duration = osc->pulse; + osc->pulse = 0; + *level = false; + } + + return advance; +} \ No newline at end of file diff --git a/lib/lfrfid/tools/fsk_osc.h b/lib/lfrfid/tools/fsk_osc.h new file mode 100644 index 000000000..ed7d436c8 --- /dev/null +++ b/lib/lfrfid/tools/fsk_osc.h @@ -0,0 +1,60 @@ +#pragma once +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct FSKOsc FSKOsc; + +/** + * @brief Allocate a new FSKOsc instance + * FSKOsc is a oscillator that can provide FSK encoding + * + * @param freq_low + * @param freq_hi + * @param osc_phase_max + * @return FSKOsc* + */ +FSKOsc* fsk_osc_alloc(uint32_t freq_low, uint32_t freq_hi, uint32_t osc_phase_max); + +/** + * @brief Free a FSKOsc instance + * + * @param osc + */ +void fsk_osc_free(FSKOsc* osc); + +/** + * @brief Reset ocillator + * + * @param osc + */ +void fsk_osc_reset(FSKOsc* osc); + +/** + * @brief Get next duration sample from oscillator + * + * @param osc + * @param bit + * @param period + * @return bool + */ +bool fsk_osc_next(FSKOsc* osc, bool bit, uint32_t* period); + +/** + * @brief Get next half of sample from oscillator + * Useful when encoding high and low levels separately. + * + * @param osc + * @param bit + * @param level + * @param duration + * @return bool + */ +bool fsk_osc_next_half(FSKOsc* osc, bool bit, bool* level, uint32_t* duration); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/lib/lfrfid/tools/t5577.c b/lib/lfrfid/tools/t5577.c new file mode 100644 index 000000000..3444afea3 --- /dev/null +++ b/lib/lfrfid/tools/t5577.c @@ -0,0 +1,94 @@ +#include "t5577.h" +#include +#include + +#define T5577_TIMING_WAIT_TIME 400 +#define T5577_TIMING_START_GAP 30 +#define T5577_TIMING_WRITE_GAP 18 +#define T5577_TIMING_DATA_0 24 +#define T5577_TIMING_DATA_1 56 +#define T5577_TIMING_PROGRAM 700 + +#define T5577_OPCODE_PAGE_0 0b10 +#define T5577_OPCODE_PAGE_1 0b11 +#define T5577_OPCODE_RESET 0b00 + +static void t5577_start() { + furi_hal_rfid_tim_read(125000, 0.5); + furi_hal_rfid_pins_read(); + furi_hal_rfid_tim_read_start(); + + // do not ground the antenna + furi_hal_rfid_pin_pull_release(); +} + +static void t5577_stop() { + furi_hal_rfid_tim_read_stop(); + furi_hal_rfid_tim_reset(); + furi_hal_rfid_pins_reset(); +} + +static void t5577_write_gap(uint32_t gap_time) { + furi_hal_rfid_tim_read_stop(); + furi_delay_us(gap_time * 8); + furi_hal_rfid_tim_read_start(); +} + +static void t5577_write_bit(bool value) { + if(value) { + furi_delay_us(T5577_TIMING_DATA_1 * 8); + } else { + furi_delay_us(T5577_TIMING_DATA_0 * 8); + } + t5577_write_gap(T5577_TIMING_WRITE_GAP); +} + +static void t5577_write_opcode(uint8_t value) { + t5577_write_bit((value >> 1) & 1); + t5577_write_bit((value >> 0) & 1); +} + +static void t5577_write_reset() { + t5577_write_gap(T5577_TIMING_START_GAP); + t5577_write_bit(1); + t5577_write_bit(0); +} + +static void t5577_write_block(uint8_t block, bool lock_bit, uint32_t data) { + furi_delay_us(T5577_TIMING_WAIT_TIME * 8); + + // start gap + t5577_write_gap(T5577_TIMING_START_GAP); + + // opcode for page 0 + t5577_write_opcode(T5577_OPCODE_PAGE_0); + + // lock bit + t5577_write_bit(lock_bit); + + // data + for(uint8_t i = 0; i < 32; i++) { + t5577_write_bit((data >> (31 - i)) & 1); + } + + // block address + t5577_write_bit((block >> 2) & 1); + t5577_write_bit((block >> 1) & 1); + t5577_write_bit((block >> 0) & 1); + + furi_delay_us(T5577_TIMING_PROGRAM * 8); + + furi_delay_us(T5577_TIMING_WAIT_TIME * 8); + t5577_write_reset(); +} + +void t5577_write(LFRFIDT5577* data) { + t5577_start(); + FURI_CRITICAL_ENTER(); + for(size_t i = 0; i < data->blocks_to_write; i++) { + t5577_write_block(i, false, data->block[i]); + } + t5577_write_reset(); + FURI_CRITICAL_EXIT(); + t5577_stop(); +} \ No newline at end of file diff --git a/lib/lfrfid/tools/t5577.h b/lib/lfrfid/tools/t5577.h new file mode 100644 index 000000000..6d53b5dc7 --- /dev/null +++ b/lib/lfrfid/tools/t5577.h @@ -0,0 +1,56 @@ +#pragma once +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define LFRFID_T5577_BLOCK_COUNT 8 + +// T5577 block 0 definitions, thanks proxmark3! +#define LFRFID_T5577_POR_DELAY 0x00000001 +#define LFRFID_T5577_ST_TERMINATOR 0x00000008 +#define LFRFID_T5577_PWD 0x00000010 +#define LFRFID_T5577_MAXBLOCK_SHIFT 5 +#define LFRFID_T5577_AOR 0x00000200 +#define LFRFID_T5577_PSKCF_RF_2 0 +#define LFRFID_T5577_PSKCF_RF_4 0x00000400 +#define LFRFID_T5577_PSKCF_RF_8 0x00000800 +#define LFRFID_T5577_MODULATION_DIRECT 0 +#define LFRFID_T5577_MODULATION_PSK1 0x00001000 +#define LFRFID_T5577_MODULATION_PSK2 0x00002000 +#define LFRFID_T5577_MODULATION_PSK3 0x00003000 +#define LFRFID_T5577_MODULATION_FSK1 0x00004000 +#define LFRFID_T5577_MODULATION_FSK2 0x00005000 +#define LFRFID_T5577_MODULATION_FSK1a 0x00006000 +#define LFRFID_T5577_MODULATION_FSK2a 0x00007000 +#define LFRFID_T5577_MODULATION_MANCHESTER 0x00008000 +#define LFRFID_T5577_MODULATION_BIPHASE 0x00010000 +#define LFRFID_T5577_MODULATION_DIPHASE 0x00018000 +#define LFRFID_T5577_X_MODE 0x00020000 +#define LFRFID_T5577_BITRATE_RF_8 0 +#define LFRFID_T5577_BITRATE_RF_16 0x00040000 +#define LFRFID_T5577_BITRATE_RF_32 0x00080000 +#define LFRFID_T5577_BITRATE_RF_40 0x000C0000 +#define LFRFID_T5577_BITRATE_RF_50 0x00100000 +#define LFRFID_T5577_BITRATE_RF_64 0x00140000 +#define LFRFID_T5577_BITRATE_RF_100 0x00180000 +#define LFRFID_T5577_BITRATE_RF_128 0x001C0000 +#define LFRFID_T5577_TESTMODE_DISABLED 0x60000000 + +typedef struct { + uint32_t block[LFRFID_T5577_BLOCK_COUNT]; + uint32_t blocks_to_write; +} LFRFIDT5577; + +/** + * @brief Write T5577 tag data to tag + * + * @param data + */ +void t5577_write(LFRFIDT5577* data); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/lib/lfrfid/tools/varint_pair.c b/lib/lfrfid/tools/varint_pair.c new file mode 100644 index 000000000..c59ba55b4 --- /dev/null +++ b/lib/lfrfid/tools/varint_pair.c @@ -0,0 +1,77 @@ +#include "varint_pair.h" +#include + +#define VARINT_PAIR_SIZE 10 + +struct VarintPair { + size_t data_length; + uint8_t data[VARINT_PAIR_SIZE]; +}; + +VarintPair* varint_pair_alloc() { + VarintPair* pair = malloc(sizeof(VarintPair)); + pair->data_length = 0; + return pair; +} + +void varint_pair_free(VarintPair* pair) { + free(pair); +} + +bool varint_pair_pack(VarintPair* pair, bool first, uint32_t value) { + bool result = false; + + if(first) { + if(pair->data_length == 0) { + pair->data_length = varint_uint32_pack(value, pair->data); + } else { + pair->data_length = 0; + } + } else { + if(pair->data_length > 0) { + pair->data_length += varint_uint32_pack(value, pair->data + pair->data_length); + result = true; + } else { + pair->data_length = 0; + } + } + + return result; +} + +bool varint_pair_unpack( + uint8_t* data, + size_t data_length, + uint32_t* value_1, + uint32_t* value_2, + size_t* length) { + size_t size = 0; + uint32_t tmp_value_1; + uint32_t tmp_value_2; + + size += varint_uint32_unpack(&tmp_value_1, &data[size], data_length); + + if(size >= data_length) { + return false; + } + + size += varint_uint32_unpack(&tmp_value_2, &data[size], (size_t)(data_length - size)); + + *value_1 = tmp_value_1; + *value_2 = tmp_value_2; + *length = size; + + return true; +} + +uint8_t* varint_pair_get_data(VarintPair* pair) { + return pair->data; +} + +size_t varint_pair_get_size(VarintPair* pair) { + return pair->data_length; +} + +void varint_pair_reset(VarintPair* pair) { + pair->data_length = 0; +} diff --git a/lib/lfrfid/tools/varint_pair.h b/lib/lfrfid/tools/varint_pair.h new file mode 100644 index 000000000..3c8386423 --- /dev/null +++ b/lib/lfrfid/tools/varint_pair.h @@ -0,0 +1,79 @@ +#pragma once +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct VarintPair VarintPair; + +/** + * @brief Allocate a new VarintPair instance + * + * VarintPair is a buffer that holds pair of varint values + * @return VarintPair* + */ +VarintPair* varint_pair_alloc(); + +/** + * @brief Free a VarintPair instance + * + * @param pair + */ +void varint_pair_free(VarintPair* pair); + +/** + * @brief Write varint pair to buffer + * + * @param pair + * @param first + * @param value + * @return bool pair complete and needs to be written + */ +bool varint_pair_pack(VarintPair* pair, bool first, uint32_t value); + +/** + * @brief Get pointer to varint pair buffer + * + * @param pair + * @return uint8_t* + */ +uint8_t* varint_pair_get_data(VarintPair* pair); + +/** + * @brief Get size of varint pair buffer + * + * @param pair + * @return size_t + */ +size_t varint_pair_get_size(VarintPair* pair); + +/** + * @brief Reset varint pair buffer + * + * @param pair + */ +void varint_pair_reset(VarintPair* pair); + +/** + * @brief Unpack varint pair to uint32_t pair from buffer + * + * @param data + * @param data_length + * @param value_1 + * @param value_2 + * @param length + * @return bool + */ +bool varint_pair_unpack( + uint8_t* data, + size_t data_length, + uint32_t* value_1, + uint32_t* value_2, + size_t* length); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index 45bbc5f41..f07ea616c 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -545,6 +545,16 @@ void nfc_worker_mf_ultralight_read_auth(NfcWorker* nfc_worker) { } data->auth_success = mf_ultralight_authenticate(&tx_rx, key, &pack); + + if(!data->auth_success) { + // Reset card + furi_hal_nfc_sleep(); + if(!furi_hal_nfc_activate_nfca(300, NULL)) { + nfc_worker->callback(NfcWorkerEventFail, nfc_worker->context); + break; + } + } + mf_ul_read_card(&tx_rx, &reader, data); if(data->auth_success) { MfUltralightConfigPages* config_pages = mf_ultralight_get_config_pages(data); diff --git a/lib/one_wire/ibutton/encoder/encoder_cyfral.c b/lib/one_wire/ibutton/encoder/encoder_cyfral.c deleted file mode 100644 index 0b506b2b0..000000000 --- a/lib/one_wire/ibutton/encoder/encoder_cyfral.c +++ /dev/null @@ -1,126 +0,0 @@ -#include "encoder_cyfral.h" -#include - -#define CYFRAL_DATA_SIZE sizeof(uint16_t) -#define CYFRAL_PERIOD (125 * furi_hal_cortex_instructions_per_microsecond()) -#define CYFRAL_0_LOW (CYFRAL_PERIOD * 0.66f) -#define CYFRAL_0_HI (CYFRAL_PERIOD * 0.33f) -#define CYFRAL_1_LOW (CYFRAL_PERIOD * 0.33f) -#define CYFRAL_1_HI (CYFRAL_PERIOD * 0.66f) - -#define CYFRAL_SET_DATA(level, len) \ - *polarity = level; \ - *length = len; - -struct EncoderCyfral { - uint32_t data; - uint32_t index; -}; - -EncoderCyfral* encoder_cyfral_alloc() { - EncoderCyfral* cyfral = malloc(sizeof(EncoderCyfral)); - encoder_cyfral_reset(cyfral); - return cyfral; -} - -void encoder_cyfral_free(EncoderCyfral* cyfral) { - free(cyfral); -} - -void encoder_cyfral_reset(EncoderCyfral* cyfral) { - cyfral->data = 0; - cyfral->index = 0; -} - -uint32_t cyfral_encoder_encode(const uint16_t data) { - uint32_t value = 0; - for(int8_t i = 0; i <= 7; i++) { - switch((data >> (i * 2)) & 0b00000011) { - case 0b11: - value = value << 4; - value += 0b00000111; - break; - case 0b10: - value = value << 4; - value += 0b00001011; - break; - case 0b01: - value = value << 4; - value += 0b00001101; - break; - case 0b00: - value = value << 4; - value += 0b00001110; - break; - default: - break; - } - } - - return value; -} - -void encoder_cyfral_set_data(EncoderCyfral* cyfral, const uint8_t* data, size_t data_size) { - furi_assert(cyfral); - furi_check(data_size >= CYFRAL_DATA_SIZE); - uint16_t intermediate; - memcpy(&intermediate, data, CYFRAL_DATA_SIZE); - cyfral->data = cyfral_encoder_encode(intermediate); -} - -void encoder_cyfral_get_pulse(EncoderCyfral* cyfral, bool* polarity, uint32_t* length) { - if(cyfral->index < 8) { - // start word (0b0001) - switch(cyfral->index) { - case 0: - CYFRAL_SET_DATA(false, CYFRAL_0_LOW); - break; - case 1: - CYFRAL_SET_DATA(true, CYFRAL_0_HI); - break; - case 2: - CYFRAL_SET_DATA(false, CYFRAL_0_LOW); - break; - case 3: - CYFRAL_SET_DATA(true, CYFRAL_0_HI); - break; - case 4: - CYFRAL_SET_DATA(false, CYFRAL_0_LOW); - break; - case 5: - CYFRAL_SET_DATA(true, CYFRAL_0_HI); - break; - case 6: - CYFRAL_SET_DATA(false, CYFRAL_1_LOW); - break; - case 7: - CYFRAL_SET_DATA(true, CYFRAL_1_HI); - break; - } - } else { - // data - uint8_t data_start_index = cyfral->index - 8; - bool clock_polarity = (data_start_index) % 2; - uint8_t bit_index = (data_start_index) / 2; - bool bit_value = ((cyfral->data >> bit_index) & 1); - - if(!clock_polarity) { - if(bit_value) { - CYFRAL_SET_DATA(false, CYFRAL_1_LOW); - } else { - CYFRAL_SET_DATA(false, CYFRAL_0_LOW); - } - } else { - if(bit_value) { - CYFRAL_SET_DATA(true, CYFRAL_1_HI); - } else { - CYFRAL_SET_DATA(true, CYFRAL_0_HI); - } - } - } - - cyfral->index++; - if(cyfral->index >= (9 * 4 * 2)) { - cyfral->index = 0; - } -} diff --git a/lib/one_wire/ibutton/encoder/encoder_cyfral.h b/lib/one_wire/ibutton/encoder/encoder_cyfral.h deleted file mode 100644 index 4fc7be5ed..000000000 --- a/lib/one_wire/ibutton/encoder/encoder_cyfral.h +++ /dev/null @@ -1,54 +0,0 @@ -/** - * @file encoder_cyfral.h - * - * Cyfral pulse format encoder - */ - -#pragma once -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct EncoderCyfral EncoderCyfral; - -/** - * Allocate Cyfral encoder - * @return EncoderCyfral* - */ -EncoderCyfral* encoder_cyfral_alloc(); - -/** - * Deallocate Cyfral encoder - * @param cyfral - */ -void encoder_cyfral_free(EncoderCyfral* cyfral); - -/** - * Reset Cyfral encoder - * @param cyfral - */ -void encoder_cyfral_reset(EncoderCyfral* cyfral); - -/** - * Set data to be encoded to Cyfral pulse format, 2 bytes - * @param cyfral - * @param data - * @param data_size - */ -void encoder_cyfral_set_data(EncoderCyfral* cyfral, const uint8_t* data, size_t data_size); - -/** - * Pop pulse from Cyfral encoder - * @param cyfral - * @param polarity - * @param length - */ -void encoder_cyfral_get_pulse(EncoderCyfral* cyfral, bool* polarity, uint32_t* length); - -#ifdef __cplusplus -} -#endif diff --git a/lib/one_wire/ibutton/encoder/encoder_metakom.c b/lib/one_wire/ibutton/encoder/encoder_metakom.c deleted file mode 100644 index 5b978ebe2..000000000 --- a/lib/one_wire/ibutton/encoder/encoder_metakom.c +++ /dev/null @@ -1,93 +0,0 @@ -#include "encoder_metakom.h" -#include - -#define METAKOM_DATA_SIZE sizeof(uint32_t) -#define METAKOM_PERIOD (125 * furi_hal_cortex_instructions_per_microsecond()) -#define METAKOM_0_LOW (METAKOM_PERIOD * 0.33f) -#define METAKOM_0_HI (METAKOM_PERIOD * 0.66f) -#define METAKOM_1_LOW (METAKOM_PERIOD * 0.66f) -#define METAKOM_1_HI (METAKOM_PERIOD * 0.33f) - -#define METAKOM_SET_DATA(level, len) \ - *polarity = !level; \ - *length = len; - -struct EncoderMetakom { - uint32_t data; - uint32_t index; -}; - -EncoderMetakom* encoder_metakom_alloc() { - EncoderMetakom* metakom = malloc(sizeof(EncoderMetakom)); - encoder_metakom_reset(metakom); - return metakom; -} - -void encoder_metakom_free(EncoderMetakom* metakom) { - free(metakom); -} - -void encoder_metakom_reset(EncoderMetakom* metakom) { - metakom->data = 0; - metakom->index = 0; -} - -void encoder_metakom_set_data(EncoderMetakom* metakom, const uint8_t* data, size_t data_size) { - furi_assert(metakom); - furi_check(data_size >= METAKOM_DATA_SIZE); - memcpy(&metakom->data, data, METAKOM_DATA_SIZE); -} - -void encoder_metakom_get_pulse(EncoderMetakom* metakom, bool* polarity, uint32_t* length) { - if(metakom->index == 0) { - // sync bit - METAKOM_SET_DATA(true, METAKOM_PERIOD); - } else if(metakom->index >= 1 && metakom->index <= 6) { - // start word (0b010) - switch(metakom->index) { - case 1: - METAKOM_SET_DATA(false, METAKOM_0_LOW); - break; - case 2: - METAKOM_SET_DATA(true, METAKOM_0_HI); - break; - case 3: - METAKOM_SET_DATA(false, METAKOM_1_LOW); - break; - case 4: - METAKOM_SET_DATA(true, METAKOM_1_HI); - break; - case 5: - METAKOM_SET_DATA(false, METAKOM_0_LOW); - break; - case 6: - METAKOM_SET_DATA(true, METAKOM_0_HI); - break; - } - } else { - // data - uint8_t data_start_index = metakom->index - 7; - bool clock_polarity = (data_start_index) % 2; - uint8_t bit_index = (data_start_index) / 2; - bool bit_value = (metakom->data >> (32 - 1 - bit_index)) & 1; - - if(!clock_polarity) { - if(bit_value) { - METAKOM_SET_DATA(false, METAKOM_1_LOW); - } else { - METAKOM_SET_DATA(false, METAKOM_0_LOW); - } - } else { - if(bit_value) { - METAKOM_SET_DATA(true, METAKOM_1_HI); - } else { - METAKOM_SET_DATA(true, METAKOM_0_HI); - } - } - } - - metakom->index++; - if(metakom->index >= (1 + 3 * 2 + 32 * 2)) { - metakom->index = 0; - } -} diff --git a/lib/one_wire/ibutton/encoder/encoder_metakom.h b/lib/one_wire/ibutton/encoder/encoder_metakom.h deleted file mode 100644 index 50ff11a24..000000000 --- a/lib/one_wire/ibutton/encoder/encoder_metakom.h +++ /dev/null @@ -1,54 +0,0 @@ -/** - * @file encoder_metakom.h - * - * Metakom pulse format encoder - */ - -#pragma once -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct EncoderMetakom EncoderMetakom; - -/** - * Allocate Metakom encoder - * @return EncoderMetakom* - */ -EncoderMetakom* encoder_metakom_alloc(); - -/** - * Deallocate Metakom encoder - * @param metakom - */ -void encoder_metakom_free(EncoderMetakom* metakom); - -/** - * Reset Metakom encoder - * @param metakom - */ -void encoder_metakom_reset(EncoderMetakom* metakom); - -/** - * Set data to be encoded to Metakom pulse format, 4 bytes - * @param metakom - * @param data - * @param data_size - */ -void encoder_metakom_set_data(EncoderMetakom* metakom, const uint8_t* data, size_t data_size); - -/** - * Pop pulse from Metakom encoder - * @param cyfral - * @param polarity - * @param length - */ -void encoder_metakom_get_pulse(EncoderMetakom* metakom, bool* polarity, uint32_t* length); - -#ifdef __cplusplus -} -#endif diff --git a/lib/one_wire/ibutton/ibutton_key.h b/lib/one_wire/ibutton/ibutton_key.h index f66537d7e..d682555a8 100644 --- a/lib/one_wire/ibutton/ibutton_key.h +++ b/lib/one_wire/ibutton/ibutton_key.h @@ -5,6 +5,7 @@ */ #pragma once +#include #ifdef __cplusplus extern "C" { diff --git a/lib/one_wire/ibutton/ibutton_worker.c b/lib/one_wire/ibutton/ibutton_worker.c index 755ed32f3..26982bcb6 100644 --- a/lib/one_wire/ibutton/ibutton_worker.c +++ b/lib/one_wire/ibutton/ibutton_worker.c @@ -29,34 +29,21 @@ iButtonWorker* ibutton_worker_alloc() { worker->slave = onewire_slave_alloc(); worker->writer = ibutton_writer_alloc(worker->host); worker->device = onewire_device_alloc(0, 0, 0, 0, 0, 0, 0, 0); - worker->pulse_decoder = pulse_decoder_alloc(); - worker->protocol_cyfral = protocol_cyfral_alloc(); - worker->protocol_metakom = protocol_metakom_alloc(); worker->messages = furi_message_queue_alloc(1, sizeof(iButtonMessage)); + worker->mode_index = iButtonWorkerIdle; - worker->last_dwt_value = 0; worker->read_cb = NULL; worker->write_cb = NULL; worker->emulate_cb = NULL; worker->cb_ctx = NULL; - worker->encoder_cyfral = encoder_cyfral_alloc(); - worker->encoder_metakom = encoder_metakom_alloc(); - worker->thread = furi_thread_alloc(); furi_thread_set_name(worker->thread, "ibutton_worker"); furi_thread_set_callback(worker->thread, ibutton_worker_thread); furi_thread_set_context(worker->thread, worker); furi_thread_set_stack_size(worker->thread, 2048); - pulse_decoder_add_protocol( - worker->pulse_decoder, - protocol_cyfral_get_protocol(worker->protocol_cyfral), - PulseProtocolCyfral); - pulse_decoder_add_protocol( - worker->pulse_decoder, - protocol_metakom_get_protocol(worker->protocol_metakom), - PulseProtocolMetakom); + worker->protocols = protocol_dict_alloc(ibutton_protocols, iButtonProtocolMax); return worker; } @@ -113,10 +100,6 @@ void ibutton_worker_stop(iButtonWorker* worker) { } void ibutton_worker_free(iButtonWorker* worker) { - pulse_decoder_free(worker->pulse_decoder); - protocol_metakom_free(worker->protocol_metakom); - protocol_cyfral_free(worker->protocol_cyfral); - ibutton_writer_free(worker->writer); onewire_slave_free(worker->slave); @@ -124,8 +107,7 @@ void ibutton_worker_free(iButtonWorker* worker) { onewire_host_free(worker->host); onewire_device_free(worker->device); - encoder_cyfral_free(worker->encoder_cyfral); - encoder_metakom_free(worker->encoder_metakom); + protocol_dict_free(worker->protocols); furi_message_queue_free(worker->messages); diff --git a/lib/one_wire/ibutton/ibutton_worker_i.h b/lib/one_wire/ibutton/ibutton_worker_i.h index 285884439..2396fbd61 100644 --- a/lib/one_wire/ibutton/ibutton_worker_i.h +++ b/lib/one_wire/ibutton/ibutton_worker_i.h @@ -10,21 +10,13 @@ #include "../one_wire_host.h" #include "../one_wire_slave.h" #include "../one_wire_device.h" -#include "../pulse_protocols/pulse_decoder.h" -#include "pulse_protocols/protocol_cyfral.h" -#include "pulse_protocols/protocol_metakom.h" -#include "encoder/encoder_cyfral.h" -#include "encoder/encoder_metakom.h" +#include +#include "protocols/ibutton_protocols.h" #ifdef __cplusplus extern "C" { #endif -typedef enum { - PulseProtocolCyfral, - PulseProtocolMetakom, -} PulseProtocols; - typedef struct { const uint32_t quant; void (*const start)(iButtonWorker* worker); @@ -39,11 +31,6 @@ typedef enum { iButtonWorkerEmulate = 3, } iButtonWorkerMode; -typedef enum { - iButtonEmulateModeCyfral, - iButtonEmulateModeMetakom, -} iButtonEmulateMode; - struct iButtonWorker { iButtonKey* key_p; uint8_t* key_data; @@ -55,19 +42,13 @@ struct iButtonWorker { FuriMessageQueue* messages; FuriThread* thread; - PulseDecoder* pulse_decoder; - ProtocolCyfral* protocol_cyfral; - ProtocolMetakom* protocol_metakom; - uint32_t last_dwt_value; - iButtonWorkerReadCallback read_cb; iButtonWorkerWriteCallback write_cb; iButtonWorkerEmulateCallback emulate_cb; void* cb_ctx; - EncoderCyfral* encoder_cyfral; - EncoderMetakom* encoder_metakom; - iButtonEmulateMode emulate_mode; + ProtocolDict* protocols; + iButtonProtocol protocol_to_encode; }; extern const iButtonWorkerModeType ibutton_worker_modes[]; diff --git a/lib/one_wire/ibutton/ibutton_worker_modes.c b/lib/one_wire/ibutton/ibutton_worker_modes.c index 78e05d0ee..d585e27f4 100644 --- a/lib/one_wire/ibutton/ibutton_worker_modes.c +++ b/lib/one_wire/ibutton/ibutton_worker_modes.c @@ -2,6 +2,7 @@ #include #include "ibutton_worker_i.h" #include "ibutton_key_command.h" +#include void ibutton_worker_mode_idle_start(iButtonWorker* worker); void ibutton_worker_mode_idle_tick(iButtonWorker* worker); @@ -62,59 +63,86 @@ void ibutton_worker_mode_idle_stop(iButtonWorker* worker) { /*********************** READ ***********************/ +typedef struct { + uint32_t last_dwt_value; + StreamBufferHandle_t stream; +} iButtonReadContext; + void ibutton_worker_comparator_callback(bool level, void* context) { - iButtonWorker* worker = context; + iButtonReadContext* read_context = context; uint32_t current_dwt_value = DWT->CYCCNT; - pulse_decoder_process_pulse( - worker->pulse_decoder, level, current_dwt_value - worker->last_dwt_value); + LevelDuration data = + level_duration_make(level, current_dwt_value - read_context->last_dwt_value); + xStreamBufferSend(read_context->stream, &data, sizeof(LevelDuration), 0); - worker->last_dwt_value = current_dwt_value; + read_context->last_dwt_value = current_dwt_value; } bool ibutton_worker_read_comparator(iButtonWorker* worker) { bool result = false; - pulse_decoder_reset(worker->pulse_decoder); + protocol_dict_decoders_start(worker->protocols); furi_hal_rfid_pins_reset(); // pulldown pull pin, we sense the signal through the analog part of the RFID schematic furi_hal_rfid_pin_pull_pulldown(); - furi_hal_rfid_comp_set_callback(ibutton_worker_comparator_callback, worker); - worker->last_dwt_value = DWT->CYCCNT; + + iButtonReadContext read_context = { + .last_dwt_value = DWT->CYCCNT, + .stream = xStreamBufferCreate(sizeof(LevelDuration) * 512, 1), + }; + + furi_hal_rfid_comp_set_callback(ibutton_worker_comparator_callback, &read_context); furi_hal_rfid_comp_start(); - // TODO: rework with thread events, "pulse_decoder_get_decoded_index_with_timeout" - furi_delay_ms(100); - int32_t decoded_index = pulse_decoder_get_decoded_index(worker->pulse_decoder); - if(decoded_index >= 0) { - pulse_decoder_get_data( - worker->pulse_decoder, decoded_index, worker->key_data, ibutton_key_get_max_size()); - } + uint32_t tick_start = furi_get_tick(); + while(true) { + LevelDuration level; + size_t ret = xStreamBufferReceive(read_context.stream, &level, sizeof(LevelDuration), 100); - switch(decoded_index) { - case PulseProtocolCyfral: - furi_check(worker->key_p != NULL); - ibutton_key_set_type(worker->key_p, iButtonKeyCyfral); - ibutton_key_set_data(worker->key_p, worker->key_data, ibutton_key_get_max_size()); - result = true; - break; - case PulseProtocolMetakom: - furi_check(worker->key_p != NULL); - ibutton_key_set_type(worker->key_p, iButtonKeyMetakom); - ibutton_key_set_data(worker->key_p, worker->key_data, ibutton_key_get_max_size()); - result = true; - break; - break; - default: - break; + if((furi_get_tick() - tick_start) > 100) { + break; + } + + if(ret > 0) { + ProtocolId decoded_index = protocol_dict_decoders_feed( + worker->protocols, + level_duration_get_level(level), + level_duration_get_duration(level)); + + if(decoded_index == PROTOCOL_NO) continue; + + protocol_dict_get_data( + worker->protocols, decoded_index, worker->key_data, ibutton_key_get_max_size()); + + switch(decoded_index) { + case iButtonProtocolCyfral: + furi_check(worker->key_p != NULL); + ibutton_key_set_type(worker->key_p, iButtonKeyCyfral); + ibutton_key_set_data(worker->key_p, worker->key_data, ibutton_key_get_max_size()); + result = true; + break; + case iButtonProtocolMetakom: + furi_check(worker->key_p != NULL); + ibutton_key_set_type(worker->key_p, iButtonKeyMetakom); + ibutton_key_set_data(worker->key_p, worker->key_data, ibutton_key_get_max_size()); + result = true; + break; + break; + default: + break; + } + } } furi_hal_rfid_comp_stop(); furi_hal_rfid_comp_set_callback(NULL, NULL); furi_hal_rfid_pins_reset(); + vStreamBufferDelete(read_context.stream); + return result; } @@ -207,21 +235,12 @@ void ibutton_worker_emulate_timer_cb(void* context) { furi_assert(context); iButtonWorker* worker = context; - bool polarity; - uint32_t length; + LevelDuration level = + protocol_dict_encoder_yield(worker->protocols, worker->protocol_to_encode); - switch(worker->emulate_mode) { - case iButtonEmulateModeCyfral: - encoder_cyfral_get_pulse(worker->encoder_cyfral, &polarity, &length); - break; - case iButtonEmulateModeMetakom: - encoder_metakom_get_pulse(worker->encoder_metakom, &polarity, &length); - break; - } + furi_hal_ibutton_emulate_set_next(level_duration_get_duration(level)); - furi_hal_ibutton_emulate_set_next(length); - - if(polarity) { + if(level_duration_get_level(level)) { furi_hal_ibutton_pin_high(); } else { furi_hal_ibutton_pin_low(); @@ -238,17 +257,16 @@ void ibutton_worker_emulate_timer_start(iButtonWorker* worker) { return; break; case iButtonKeyCyfral: - worker->emulate_mode = iButtonEmulateModeCyfral; - encoder_cyfral_reset(worker->encoder_cyfral); - encoder_cyfral_set_data(worker->encoder_cyfral, key_id, key_size); + worker->protocol_to_encode = iButtonProtocolCyfral; break; case iButtonKeyMetakom: - worker->emulate_mode = iButtonEmulateModeMetakom; - encoder_metakom_reset(worker->encoder_metakom); - encoder_metakom_set_data(worker->encoder_metakom, key_id, key_size); + worker->protocol_to_encode = iButtonProtocolMetakom; break; } + protocol_dict_set_data(worker->protocols, worker->protocol_to_encode, key_id, key_size); + protocol_dict_encoder_start(worker->protocols, worker->protocol_to_encode); + furi_hal_ibutton_start_drive(); furi_hal_ibutton_emulate_start(0, ibutton_worker_emulate_timer_cb, worker); } diff --git a/lib/one_wire/ibutton/protocols/ibutton_protocols.c b/lib/one_wire/ibutton/protocols/ibutton_protocols.c new file mode 100644 index 000000000..b07d68b33 --- /dev/null +++ b/lib/one_wire/ibutton/protocols/ibutton_protocols.c @@ -0,0 +1,8 @@ +#include "ibutton_protocols.h" +#include "protocol_cyfral.h" +#include "protocol_metakom.h" + +const ProtocolBase* ibutton_protocols[] = { + [iButtonProtocolCyfral] = &protocol_cyfral, + [iButtonProtocolMetakom] = &protocol_metakom, +}; \ No newline at end of file diff --git a/lib/one_wire/ibutton/protocols/ibutton_protocols.h b/lib/one_wire/ibutton/protocols/ibutton_protocols.h new file mode 100644 index 000000000..cdaa3ab6f --- /dev/null +++ b/lib/one_wire/ibutton/protocols/ibutton_protocols.h @@ -0,0 +1,11 @@ +#pragma once +#include "toolbox/protocols/protocol.h" + +typedef enum { + iButtonProtocolCyfral, + iButtonProtocolMetakom, + + iButtonProtocolMax, +} iButtonProtocol; + +extern const ProtocolBase* ibutton_protocols[]; \ No newline at end of file diff --git a/lib/one_wire/ibutton/protocols/protocol_cyfral.c b/lib/one_wire/ibutton/protocols/protocol_cyfral.c new file mode 100644 index 000000000..51c42824f --- /dev/null +++ b/lib/one_wire/ibutton/protocols/protocol_cyfral.c @@ -0,0 +1,344 @@ +#include +#include +#include "protocol_cyfral.h" + +#define CYFRAL_DATA_SIZE sizeof(uint16_t) +#define CYFRAL_PERIOD (125 * furi_hal_cortex_instructions_per_microsecond()) +#define CYFRAL_0_LOW (CYFRAL_PERIOD * 0.66f) +#define CYFRAL_0_HI (CYFRAL_PERIOD * 0.33f) +#define CYFRAL_1_LOW (CYFRAL_PERIOD * 0.33f) +#define CYFRAL_1_HI (CYFRAL_PERIOD * 0.66f) + +#define CYFRAL_MAX_PERIOD_US 230 + +typedef enum { + CYFRAL_BIT_WAIT_FRONT_HIGH, + CYFRAL_BIT_WAIT_FRONT_LOW, +} CyfralBitState; + +typedef enum { + CYFRAL_WAIT_START_NIBBLE, + CYFRAL_READ_NIBBLE, + CYFRAL_READ_STOP_NIBBLE, +} CyfralState; + +typedef struct { + CyfralState state; + CyfralBitState bit_state; + + // high + low period time + uint32_t period_time; + // temporary nibble storage + uint8_t nibble; + // data valid flag + // MUST be checked only in READ_STOP_NIBBLE state + bool data_valid; + // nibble index, we expect 8 nibbles + uint8_t index; + // bit index in nibble, 4 bit per nibble + uint8_t bit_index; + // max period, 230us x clock per us + uint32_t max_period; +} ProtocolCyfralDecoder; + +typedef struct { + uint32_t data; + uint32_t index; +} ProtocolCyfralEncoder; + +typedef struct { + uint16_t data; + + ProtocolCyfralDecoder decoder; + ProtocolCyfralEncoder encoder; +} ProtocolCyfral; + +static void* protocol_cyfral_alloc(void) { + ProtocolCyfral* proto = malloc(sizeof(ProtocolCyfral)); + return (void*)proto; +} + +static void protocol_cyfral_free(ProtocolCyfral* proto) { + free(proto); +} + +static uint8_t* protocol_cyfral_get_data(ProtocolCyfral* proto) { + return (uint8_t*)&proto->data; +} + +static void protocol_cyfral_decoder_start(ProtocolCyfral* proto) { + ProtocolCyfralDecoder* cyfral = &proto->decoder; + + cyfral->state = CYFRAL_WAIT_START_NIBBLE; + cyfral->bit_state = CYFRAL_BIT_WAIT_FRONT_LOW; + cyfral->period_time = 0; + cyfral->bit_index = 0; + cyfral->index = 0; + cyfral->nibble = 0; + cyfral->data_valid = true; + cyfral->max_period = CYFRAL_MAX_PERIOD_US * furi_hal_cortex_instructions_per_microsecond(); + + proto->data = 0; +} + +static bool protocol_cyfral_decoder_process_bit( + ProtocolCyfralDecoder* cyfral, + bool polarity, + uint32_t length, + bool* bit_ready, + bool* bit_value) { + bool result = true; + *bit_ready = false; + + // bit start from low + switch(cyfral->bit_state) { + case CYFRAL_BIT_WAIT_FRONT_LOW: + if(polarity == true) { + cyfral->period_time += length; + + *bit_ready = true; + if(cyfral->period_time <= cyfral->max_period) { + if((cyfral->period_time / 2) > length) { + *bit_value = false; + } else { + *bit_value = true; + } + } else { + result = false; + } + + cyfral->bit_state = CYFRAL_BIT_WAIT_FRONT_HIGH; + } else { + result = false; + } + break; + case CYFRAL_BIT_WAIT_FRONT_HIGH: + if(polarity == false) { + cyfral->period_time = length; + cyfral->bit_state = CYFRAL_BIT_WAIT_FRONT_LOW; + } else { + result = false; + } + break; + } + + return result; +} + +static bool protocol_cyfral_decoder_feed(ProtocolCyfral* proto, bool level, uint32_t duration) { + ProtocolCyfralDecoder* cyfral = &proto->decoder; + + bool bit_ready; + bool bit_value; + bool decoded = false; + + switch(cyfral->state) { + case CYFRAL_WAIT_START_NIBBLE: + // wait for start word + if(protocol_cyfral_decoder_process_bit(cyfral, level, duration, &bit_ready, &bit_value)) { + if(bit_ready) { + cyfral->nibble = ((cyfral->nibble << 1) | bit_value) & 0x0F; + if(cyfral->nibble == 0b0001) { + cyfral->nibble = 0; + cyfral->state = CYFRAL_READ_NIBBLE; + } + } + } else { + protocol_cyfral_decoder_start(proto); + } + + break; + case CYFRAL_READ_NIBBLE: + // read nibbles + if(protocol_cyfral_decoder_process_bit(cyfral, level, duration, &bit_ready, &bit_value)) { + if(bit_ready) { + cyfral->nibble = (cyfral->nibble << 1) | bit_value; + + cyfral->bit_index++; + + //convert every nibble to 2-bit index + if(cyfral->bit_index == 4) { + switch(cyfral->nibble) { + case 0b1110: + proto->data = (proto->data << 2) | 0b11; + break; + case 0b1101: + proto->data = (proto->data << 2) | 0b10; + break; + case 0b1011: + proto->data = (proto->data << 2) | 0b01; + break; + case 0b0111: + proto->data = (proto->data << 2) | 0b00; + break; + default: + cyfral->data_valid = false; + break; + } + + cyfral->nibble = 0; + cyfral->bit_index = 0; + cyfral->index++; + } + + // succefully read 8 nibbles + if(cyfral->index == 8) { + cyfral->state = CYFRAL_READ_STOP_NIBBLE; + } + } + } else { + protocol_cyfral_decoder_start(proto); + } + break; + case CYFRAL_READ_STOP_NIBBLE: + // read stop nibble + if(protocol_cyfral_decoder_process_bit(cyfral, level, duration, &bit_ready, &bit_value)) { + if(bit_ready) { + cyfral->nibble = ((cyfral->nibble << 1) | bit_value) & 0x0F; + cyfral->bit_index++; + + switch(cyfral->bit_index) { + case 0: + case 1: + case 2: + case 3: + break; + case 4: + if(cyfral->nibble == 0b0001) { + // validate data + if(cyfral->data_valid) { + decoded = true; + } else { + protocol_cyfral_decoder_start(proto); + } + } else { + protocol_cyfral_decoder_start(proto); + } + break; + default: + protocol_cyfral_decoder_start(proto); + break; + } + } + } else { + protocol_cyfral_decoder_start(proto); + } + break; + } + + return decoded; +} + +static uint32_t protocol_cyfral_encoder_encode(const uint16_t data) { + uint32_t value = 0; + for(int8_t i = 0; i <= 7; i++) { + switch((data >> (i * 2)) & 0b00000011) { + case 0b11: + value = value << 4; + value += 0b00000111; + break; + case 0b10: + value = value << 4; + value += 0b00001011; + break; + case 0b01: + value = value << 4; + value += 0b00001101; + break; + case 0b00: + value = value << 4; + value += 0b00001110; + break; + default: + break; + } + } + + return value; +} + +static bool protocol_cyfral_encoder_start(ProtocolCyfral* proto) { + proto->encoder.index = 0; + proto->encoder.data = protocol_cyfral_encoder_encode(proto->data); + return true; +} + +static LevelDuration protocol_cyfral_encoder_yield(ProtocolCyfral* proto) { + LevelDuration result; + + if(proto->encoder.index < 8) { + // start word (0b0001) + switch(proto->encoder.index) { + case 0: + result = level_duration_make(false, CYFRAL_0_LOW); + break; + case 1: + result = level_duration_make(true, CYFRAL_0_HI); + break; + case 2: + result = level_duration_make(false, CYFRAL_0_LOW); + break; + case 3: + result = level_duration_make(true, CYFRAL_0_HI); + break; + case 4: + result = level_duration_make(false, CYFRAL_0_LOW); + break; + case 5: + result = level_duration_make(true, CYFRAL_0_HI); + break; + case 6: + result = level_duration_make(false, CYFRAL_1_LOW); + break; + case 7: + result = level_duration_make(true, CYFRAL_1_HI); + break; + } + } else { + // data + uint8_t data_start_index = proto->encoder.index - 8; + bool clock_polarity = (data_start_index) % 2; + uint8_t bit_index = (data_start_index) / 2; + bool bit_value = ((proto->encoder.data >> bit_index) & 1); + + if(!clock_polarity) { + if(bit_value) { + result = level_duration_make(false, CYFRAL_1_LOW); + } else { + result = level_duration_make(false, CYFRAL_0_LOW); + } + } else { + if(bit_value) { + result = level_duration_make(true, CYFRAL_1_HI); + } else { + result = level_duration_make(true, CYFRAL_0_HI); + } + } + } + + proto->encoder.index++; + if(proto->encoder.index >= (9 * 4 * 2)) { + proto->encoder.index = 0; + } + + return result; +} + +const ProtocolBase protocol_cyfral = { + .name = "Cyfral", + .manufacturer = "Cyfral", + .data_size = CYFRAL_DATA_SIZE, + .alloc = (ProtocolAlloc)protocol_cyfral_alloc, + .free = (ProtocolFree)protocol_cyfral_free, + .get_data = (ProtocolGetData)protocol_cyfral_get_data, + .decoder = + { + .start = (ProtocolDecoderStart)protocol_cyfral_decoder_start, + .feed = (ProtocolDecoderFeed)protocol_cyfral_decoder_feed, + }, + .encoder = + { + .start = (ProtocolEncoderStart)protocol_cyfral_encoder_start, + .yield = (ProtocolEncoderYield)protocol_cyfral_encoder_yield, + }, +}; \ No newline at end of file diff --git a/lib/one_wire/ibutton/protocols/protocol_cyfral.h b/lib/one_wire/ibutton/protocols/protocol_cyfral.h new file mode 100644 index 000000000..97a98e485 --- /dev/null +++ b/lib/one_wire/ibutton/protocols/protocol_cyfral.h @@ -0,0 +1,4 @@ +#pragma once +#include "toolbox/protocols/protocol.h" + +extern const ProtocolBase protocol_cyfral; \ No newline at end of file diff --git a/lib/one_wire/ibutton/pulse_protocols/protocol_metakom.c b/lib/one_wire/ibutton/protocols/protocol_metakom.c similarity index 51% rename from lib/one_wire/ibutton/pulse_protocols/protocol_metakom.c rename to lib/one_wire/ibutton/protocols/protocol_metakom.c index 9df9e20be..00f16e455 100644 --- a/lib/one_wire/ibutton/pulse_protocols/protocol_metakom.c +++ b/lib/one_wire/ibutton/protocols/protocol_metakom.c @@ -1,9 +1,14 @@ +#include +#include #include "protocol_metakom.h" -#include -#include -#include -#define METAKOM_DATA_SIZE 4 +#define METAKOM_DATA_SIZE sizeof(uint32_t) +#define METAKOM_PERIOD (125 * furi_hal_cortex_instructions_per_microsecond()) +#define METAKOM_0_LOW (METAKOM_PERIOD * 0.33f) +#define METAKOM_0_HI (METAKOM_PERIOD * 0.66f) +#define METAKOM_1_LOW (METAKOM_PERIOD * 0.66f) +#define METAKOM_1_HI (METAKOM_PERIOD * 0.33f) + #define METAKOM_PERIOD_SAMPLE_COUNT 10 typedef enum { @@ -19,79 +24,49 @@ typedef enum { METAKOM_BIT_WAIT_FRONT_LOW, } MetakomBitState; -struct ProtocolMetakom { - PulseProtocol* protocol; - +typedef struct { // high + low period time uint32_t period_time; uint32_t low_time_storage; uint8_t period_sample_index; uint32_t period_sample_data[METAKOM_PERIOD_SAMPLE_COUNT]; - // ready flag - // TODO: atomic access - bool ready; - uint8_t tmp_data; uint8_t tmp_counter; - uint32_t key_data; uint8_t key_data_index; MetakomBitState bit_state; MetakomState state; -}; +} ProtocolMetakomDecoder; -static void metakom_pulse(void* context, bool polarity, uint32_t length); -static void metakom_reset(void* context); -static void metakom_get_data(void* context, uint8_t* data, size_t length); -static bool metakom_decoded(void* context); +typedef struct { + uint32_t index; +} ProtocolMetakomEncoder; -ProtocolMetakom* protocol_metakom_alloc() { - ProtocolMetakom* metakom = malloc(sizeof(ProtocolMetakom)); - metakom_reset(metakom); +typedef struct { + uint32_t data; - metakom->protocol = pulse_protocol_alloc(); + ProtocolMetakomDecoder decoder; + ProtocolMetakomEncoder encoder; +} ProtocolMetakom; - pulse_protocol_set_context(metakom->protocol, metakom); - pulse_protocol_set_pulse_cb(metakom->protocol, metakom_pulse); - pulse_protocol_set_reset_cb(metakom->protocol, metakom_reset); - pulse_protocol_set_get_data_cb(metakom->protocol, metakom_get_data); - pulse_protocol_set_decoded_cb(metakom->protocol, metakom_decoded); - - return metakom; +static ProtocolMetakom* protocol_metakom_alloc(void) { + ProtocolMetakom* proto = malloc(sizeof(ProtocolMetakom)); + return (void*)proto; } -void protocol_metakom_free(ProtocolMetakom* metakom) { - furi_assert(metakom); - pulse_protocol_free(metakom->protocol); - free(metakom); +static void protocol_metakom_free(ProtocolMetakom* proto) { + free(proto); } -PulseProtocol* protocol_metakom_get_protocol(ProtocolMetakom* metakom) { - furi_assert(metakom); - return metakom->protocol; +static uint8_t* protocol_metakom_get_data(ProtocolMetakom* proto) { + return (uint8_t*)&proto->data; } -static void metakom_get_data(void* context, uint8_t* data, size_t length) { - furi_assert(context); - furi_check(length >= METAKOM_DATA_SIZE); - ProtocolMetakom* metakom = context; - memcpy(data, &metakom->key_data, METAKOM_DATA_SIZE); -} +static void protocol_metakom_decoder_start(ProtocolMetakom* proto) { + ProtocolMetakomDecoder* metakom = &proto->decoder; -static bool metakom_decoded(void* context) { - furi_assert(context); - ProtocolMetakom* metakom = context; - bool decoded = metakom->ready; - return decoded; -} - -static void metakom_reset(void* context) { - furi_assert(context); - ProtocolMetakom* metakom = context; - - metakom->ready = false; metakom->period_sample_index = 0; metakom->period_time = 0; metakom->tmp_counter = 0; @@ -101,9 +76,10 @@ static void metakom_reset(void* context) { }; metakom->state = METAKOM_WAIT_PERIOD_SYNC; metakom->bit_state = METAKOM_BIT_WAIT_FRONT_LOW; - metakom->key_data = 0; metakom->key_data_index = 0; metakom->low_time_storage = 0; + + proto->data = 0; } static bool metakom_parity_check(uint8_t data) { @@ -122,7 +98,7 @@ static bool metakom_parity_check(uint8_t data) { } static bool metakom_process_bit( - ProtocolMetakom* metakom, + ProtocolMetakomDecoder* metakom, bool polarity, uint32_t time, uint32_t* high_time, @@ -149,18 +125,17 @@ static bool metakom_process_bit( return result; } -static void metakom_pulse(void* context, bool polarity, uint32_t time) { - furi_assert(context); - ProtocolMetakom* metakom = context; +static bool protocol_metakom_decoder_feed(ProtocolMetakom* proto, bool level, uint32_t duration) { + ProtocolMetakomDecoder* metakom = &proto->decoder; - if(metakom->ready) return; + bool ready = false; uint32_t high_time = 0; uint32_t low_time = 0; switch(metakom->state) { case METAKOM_WAIT_PERIOD_SYNC: - if(metakom_process_bit(metakom, polarity, time, &high_time, &low_time)) { + if(metakom_process_bit(metakom, level, duration, &high_time, &low_time)) { metakom->period_sample_data[metakom->period_sample_index] = high_time + low_time; metakom->period_sample_index++; @@ -176,7 +151,7 @@ static void metakom_pulse(void* context, bool polarity, uint32_t time) { break; case METAKOM_WAIT_START_BIT: - if(metakom_process_bit(metakom, polarity, time, &high_time, &low_time)) { + if(metakom_process_bit(metakom, level, duration, &high_time, &low_time)) { metakom->tmp_counter++; if(high_time > metakom->period_time) { metakom->tmp_counter = 0; @@ -184,13 +159,13 @@ static void metakom_pulse(void* context, bool polarity, uint32_t time) { } if(metakom->tmp_counter > 40) { - metakom_reset(metakom); + protocol_metakom_decoder_start(proto); } } break; case METAKOM_WAIT_START_WORD: - if(metakom_process_bit(metakom, polarity, time, &high_time, &low_time)) { + if(metakom_process_bit(metakom, level, duration, &high_time, &low_time)) { if(low_time < (metakom->period_time / 2)) { metakom->tmp_data = (metakom->tmp_data << 1) | 0b0; } else { @@ -204,13 +179,13 @@ static void metakom_pulse(void* context, bool polarity, uint32_t time) { metakom->tmp_data = 0; metakom->state = METAKOM_READ_WORD; } else { - metakom_reset(metakom); + protocol_metakom_decoder_start(proto); } } } break; case METAKOM_READ_WORD: - if(metakom_process_bit(metakom, polarity, time, &high_time, &low_time)) { + if(metakom_process_bit(metakom, level, duration, &high_time, &low_time)) { if(low_time < (metakom->period_time / 2)) { metakom->tmp_data = (metakom->tmp_data << 1) | 0b0; } else { @@ -220,7 +195,7 @@ static void metakom_pulse(void* context, bool polarity, uint32_t time) { if(metakom->tmp_counter == 8) { if(metakom_parity_check(metakom->tmp_data)) { - metakom->key_data = (metakom->key_data << 8) | metakom->tmp_data; + proto->data = (proto->data << 8) | metakom->tmp_data; metakom->key_data_index++; metakom->tmp_data = 0; metakom->tmp_counter = 0; @@ -230,17 +205,17 @@ static void metakom_pulse(void* context, bool polarity, uint32_t time) { if(high_time > metakom->period_time) { metakom->state = METAKOM_READ_STOP_WORD; } else { - metakom_reset(metakom); + protocol_metakom_decoder_start(proto); } } } else { - metakom_reset(metakom); + protocol_metakom_decoder_start(proto); } } } break; case METAKOM_READ_STOP_WORD: - if(metakom_process_bit(metakom, polarity, time, &high_time, &low_time)) { + if(metakom_process_bit(metakom, level, duration, &high_time, &low_time)) { if(low_time < (metakom->period_time / 2)) { metakom->tmp_data = (metakom->tmp_data << 1) | 0b0; } else { @@ -250,12 +225,96 @@ static void metakom_pulse(void* context, bool polarity, uint32_t time) { if(metakom->tmp_counter == 3) { if(metakom->tmp_data == 0b010) { - metakom->ready = true; + ready = true; } else { - metakom_reset(metakom); + protocol_metakom_decoder_start(proto); } } } break; } + + return ready; } + +static bool protocol_metakom_encoder_start(ProtocolMetakom* proto) { + proto->encoder.index = 0; + return true; +} + +static LevelDuration protocol_metakom_encoder_yield(ProtocolMetakom* proto) { + LevelDuration result; + + if(proto->encoder.index == 0) { + // sync bit + result = level_duration_make(false, METAKOM_PERIOD); + } else if(proto->encoder.index >= 1 && proto->encoder.index <= 6) { + // start word (0b010) + switch(proto->encoder.index) { + case 1: + result = level_duration_make(true, METAKOM_0_LOW); + break; + case 2: + result = level_duration_make(false, METAKOM_0_HI); + break; + case 3: + result = level_duration_make(true, METAKOM_1_LOW); + break; + case 4: + result = level_duration_make(false, METAKOM_1_HI); + break; + case 5: + result = level_duration_make(true, METAKOM_0_LOW); + break; + case 6: + result = level_duration_make(false, METAKOM_0_HI); + break; + } + } else { + // data + uint8_t data_start_index = proto->encoder.index - 7; + bool clock_polarity = (data_start_index) % 2; + uint8_t bit_index = (data_start_index) / 2; + bool bit_value = (proto->data >> (32 - 1 - bit_index)) & 1; + + if(!clock_polarity) { + if(bit_value) { + result = level_duration_make(true, METAKOM_1_LOW); + } else { + result = level_duration_make(true, METAKOM_0_LOW); + } + } else { + if(bit_value) { + result = level_duration_make(false, METAKOM_1_HI); + } else { + result = level_duration_make(false, METAKOM_0_HI); + } + } + } + + proto->encoder.index++; + if(proto->encoder.index >= (1 + 3 * 2 + 32 * 2)) { + proto->encoder.index = 0; + } + + return result; +} + +const ProtocolBase protocol_metakom = { + .name = "Metakom", + .manufacturer = "Metakom", + .data_size = METAKOM_DATA_SIZE, + .alloc = (ProtocolAlloc)protocol_metakom_alloc, + .free = (ProtocolFree)protocol_metakom_free, + .get_data = (ProtocolGetData)protocol_metakom_get_data, + .decoder = + { + .start = (ProtocolDecoderStart)protocol_metakom_decoder_start, + .feed = (ProtocolDecoderFeed)protocol_metakom_decoder_feed, + }, + .encoder = + { + .start = (ProtocolEncoderStart)protocol_metakom_encoder_start, + .yield = (ProtocolEncoderYield)protocol_metakom_encoder_yield, + }, +}; \ No newline at end of file diff --git a/lib/one_wire/ibutton/protocols/protocol_metakom.h b/lib/one_wire/ibutton/protocols/protocol_metakom.h new file mode 100644 index 000000000..5e44a2a8c --- /dev/null +++ b/lib/one_wire/ibutton/protocols/protocol_metakom.h @@ -0,0 +1,4 @@ +#pragma once +#include "toolbox/protocols/protocol.h" + +extern const ProtocolBase protocol_metakom; \ No newline at end of file diff --git a/lib/one_wire/ibutton/pulse_protocols/protocol_cyfral.c b/lib/one_wire/ibutton/pulse_protocols/protocol_cyfral.c deleted file mode 100644 index 7c2897907..000000000 --- a/lib/one_wire/ibutton/pulse_protocols/protocol_cyfral.c +++ /dev/null @@ -1,256 +0,0 @@ -#include "protocol_cyfral.h" -#include -#include -#include -#include - -#define CYFRAL_DATA_SIZE 2 -#define CYFRAL_MAX_PERIOD_US 230 - -typedef enum { - CYFRAL_BIT_WAIT_FRONT_HIGH, - CYFRAL_BIT_WAIT_FRONT_LOW, -} CyfralBitState; - -typedef enum { - CYFRAL_WAIT_START_NIBBLE, - CYFRAL_READ_NIBBLE, - CYFRAL_READ_STOP_NIBBLE, -} CyfralState; - -struct ProtocolCyfral { - PulseProtocol* protocol; - - CyfralState state; - CyfralBitState bit_state; - - // ready flag, key is read and valid - // TODO: atomic access - bool ready; - // key data storage - uint16_t key_data; - // high + low period time - uint32_t period_time; - // temporary nibble storage - uint8_t nibble; - // data valid flag - // MUST be checked only in READ_STOP_NIBBLE state - bool data_valid; - // nibble index, we expect 8 nibbles - uint8_t index; - // bit index in nibble, 4 bit per nibble - uint8_t bit_index; - // max period, 230us x clock per us - uint32_t max_period; -}; - -static void cyfral_pulse(void* context, bool polarity, uint32_t length); -static void cyfral_reset(void* context); -static void cyfral_get_data(void* context, uint8_t* data, size_t length); -static bool cyfral_decoded(void* context); - -ProtocolCyfral* protocol_cyfral_alloc() { - ProtocolCyfral* cyfral = malloc(sizeof(ProtocolCyfral)); - cyfral_reset(cyfral); - - cyfral->protocol = pulse_protocol_alloc(); - - pulse_protocol_set_context(cyfral->protocol, cyfral); - pulse_protocol_set_pulse_cb(cyfral->protocol, cyfral_pulse); - pulse_protocol_set_reset_cb(cyfral->protocol, cyfral_reset); - pulse_protocol_set_get_data_cb(cyfral->protocol, cyfral_get_data); - pulse_protocol_set_decoded_cb(cyfral->protocol, cyfral_decoded); - - return cyfral; -} - -void protocol_cyfral_free(ProtocolCyfral* cyfral) { - furi_assert(cyfral); - pulse_protocol_free(cyfral->protocol); - free(cyfral); -} - -PulseProtocol* protocol_cyfral_get_protocol(ProtocolCyfral* cyfral) { - furi_assert(cyfral); - return cyfral->protocol; -} - -static void cyfral_get_data(void* context, uint8_t* data, size_t length) { - furi_assert(context); - furi_check(length >= CYFRAL_DATA_SIZE); - ProtocolCyfral* cyfral = context; - memcpy(data, &cyfral->key_data, CYFRAL_DATA_SIZE); -} - -static bool cyfral_decoded(void* context) { - furi_assert(context); - ProtocolCyfral* cyfral = context; - bool decoded = cyfral->ready; - return decoded; -} - -static void cyfral_reset(void* context) { - furi_assert(context); - ProtocolCyfral* cyfral = context; - cyfral->state = CYFRAL_WAIT_START_NIBBLE; - cyfral->bit_state = CYFRAL_BIT_WAIT_FRONT_LOW; - - cyfral->period_time = 0; - cyfral->bit_index = 0; - cyfral->ready = false; - cyfral->index = 0; - - cyfral->key_data = 0; - cyfral->nibble = 0; - cyfral->data_valid = true; - - cyfral->max_period = CYFRAL_MAX_PERIOD_US * furi_hal_cortex_instructions_per_microsecond(); -} - -static bool cyfral_process_bit( - ProtocolCyfral* cyfral, - bool polarity, - uint32_t length, - bool* bit_ready, - bool* bit_value) { - bool result = true; - *bit_ready = false; - - // bit start from low - switch(cyfral->bit_state) { - case CYFRAL_BIT_WAIT_FRONT_LOW: - if(polarity == true) { - cyfral->period_time += length; - - *bit_ready = true; - if(cyfral->period_time <= cyfral->max_period) { - if((cyfral->period_time / 2) > length) { - *bit_value = false; - } else { - *bit_value = true; - } - } else { - result = false; - } - - cyfral->bit_state = CYFRAL_BIT_WAIT_FRONT_HIGH; - } else { - result = false; - } - break; - case CYFRAL_BIT_WAIT_FRONT_HIGH: - if(polarity == false) { - cyfral->period_time = length; - cyfral->bit_state = CYFRAL_BIT_WAIT_FRONT_LOW; - } else { - result = false; - } - break; - } - - return result; -} - -static void cyfral_pulse(void* context, bool polarity, uint32_t length) { - furi_assert(context); - ProtocolCyfral* cyfral = context; - - bool bit_ready; - bool bit_value; - - if(cyfral->ready) return; - - switch(cyfral->state) { - case CYFRAL_WAIT_START_NIBBLE: - // wait for start word - if(cyfral_process_bit(cyfral, polarity, length, &bit_ready, &bit_value)) { - if(bit_ready) { - cyfral->nibble = ((cyfral->nibble << 1) | bit_value) & 0x0F; - if(cyfral->nibble == 0b0001) { - cyfral->nibble = 0; - cyfral->state = CYFRAL_READ_NIBBLE; - } - } - } else { - cyfral_reset(cyfral); - } - - break; - case CYFRAL_READ_NIBBLE: - // read nibbles - if(cyfral_process_bit(cyfral, polarity, length, &bit_ready, &bit_value)) { - if(bit_ready) { - cyfral->nibble = (cyfral->nibble << 1) | bit_value; - - cyfral->bit_index++; - - //convert every nibble to 2-bit index - if(cyfral->bit_index == 4) { - switch(cyfral->nibble) { - case 0b1110: - cyfral->key_data = (cyfral->key_data << 2) | 0b11; - break; - case 0b1101: - cyfral->key_data = (cyfral->key_data << 2) | 0b10; - break; - case 0b1011: - cyfral->key_data = (cyfral->key_data << 2) | 0b01; - break; - case 0b0111: - cyfral->key_data = (cyfral->key_data << 2) | 0b00; - break; - default: - cyfral->data_valid = false; - break; - } - - cyfral->nibble = 0; - cyfral->bit_index = 0; - cyfral->index++; - } - - // succefully read 8 nibbles - if(cyfral->index == 8) { - cyfral->state = CYFRAL_READ_STOP_NIBBLE; - } - } - } else { - cyfral_reset(cyfral); - } - break; - case CYFRAL_READ_STOP_NIBBLE: - // read stop nibble - if(cyfral_process_bit(cyfral, polarity, length, &bit_ready, &bit_value)) { - if(bit_ready) { - cyfral->nibble = ((cyfral->nibble << 1) | bit_value) & 0x0F; - cyfral->bit_index++; - - switch(cyfral->bit_index) { - case 0: - case 1: - case 2: - case 3: - break; - case 4: - if(cyfral->nibble == 0b0001) { - // validate data - if(cyfral->data_valid) { - cyfral->ready = true; - } else { - cyfral_reset(cyfral); - } - } else { - cyfral_reset(cyfral); - } - break; - default: - cyfral_reset(cyfral); - break; - } - } - } else { - cyfral_reset(cyfral); - } - break; - } -} diff --git a/lib/one_wire/ibutton/pulse_protocols/protocol_cyfral.h b/lib/one_wire/ibutton/pulse_protocols/protocol_cyfral.h deleted file mode 100644 index 10305da12..000000000 --- a/lib/one_wire/ibutton/pulse_protocols/protocol_cyfral.h +++ /dev/null @@ -1,38 +0,0 @@ -/** - * @file protocol_cyfral.h - * - * Cyfral pulse format decoder - */ - -#pragma once -#include -#include "../../pulse_protocols/pulse_protocol.h" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct ProtocolCyfral ProtocolCyfral; - -/** - * Allocate decoder - * @return ProtocolCyfral* - */ -ProtocolCyfral* protocol_cyfral_alloc(); - -/** - * Deallocate decoder - * @param cyfral - */ -void protocol_cyfral_free(ProtocolCyfral* cyfral); - -/** - * Get protocol interface - * @param cyfral - * @return PulseProtocol* - */ -PulseProtocol* protocol_cyfral_get_protocol(ProtocolCyfral* cyfral); - -#ifdef __cplusplus -} -#endif diff --git a/lib/one_wire/ibutton/pulse_protocols/protocol_metakom.h b/lib/one_wire/ibutton/pulse_protocols/protocol_metakom.h deleted file mode 100644 index fdc457695..000000000 --- a/lib/one_wire/ibutton/pulse_protocols/protocol_metakom.h +++ /dev/null @@ -1,38 +0,0 @@ -/** - * @file protocol_metakom.h - * - * Metakom pulse format decoder - */ - -#pragma once -#include -#include "../../pulse_protocols/pulse_protocol.h" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct ProtocolMetakom ProtocolMetakom; - -/** - * Allocate decoder - * @return ProtocolMetakom* - */ -ProtocolMetakom* protocol_metakom_alloc(); - -/** - * Free decoder - * @param metakom - */ -void protocol_metakom_free(ProtocolMetakom* metakom); - -/** - * Get protocol interface - * @param metakom - * @return PulseProtocol* - */ -PulseProtocol* protocol_metakom_get_protocol(ProtocolMetakom* metakom); - -#ifdef __cplusplus -} -#endif diff --git a/lib/one_wire/pulse_protocols/pulse_decoder.c b/lib/one_wire/pulse_protocols/pulse_decoder.c deleted file mode 100644 index c7d3b09ec..000000000 --- a/lib/one_wire/pulse_protocols/pulse_decoder.c +++ /dev/null @@ -1,66 +0,0 @@ -#include -#include "pulse_decoder.h" -#include -#include - -#define MAX_PROTOCOL 5 - -struct PulseDecoder { - PulseProtocol* protocols[MAX_PROTOCOL]; -}; - -PulseDecoder* pulse_decoder_alloc() { - PulseDecoder* decoder = malloc(sizeof(PulseDecoder)); - memset(decoder, 0, sizeof(PulseDecoder)); - return decoder; -} - -void pulse_decoder_free(PulseDecoder* reader) { - furi_assert(reader); - free(reader); -} - -void pulse_decoder_add_protocol(PulseDecoder* reader, PulseProtocol* protocol, int32_t index) { - furi_check(index < MAX_PROTOCOL); - furi_check(reader->protocols[index] == NULL); - reader->protocols[index] = protocol; -} - -void pulse_decoder_process_pulse(PulseDecoder* reader, bool polarity, uint32_t length) { - furi_assert(reader); - for(size_t index = 0; index < MAX_PROTOCOL; index++) { - if(reader->protocols[index] != NULL) { - pulse_protocol_process_pulse(reader->protocols[index], polarity, length); - } - } -} - -int32_t pulse_decoder_get_decoded_index(PulseDecoder* reader) { - furi_assert(reader); - int32_t decoded = -1; - for(size_t index = 0; index < MAX_PROTOCOL; index++) { - if(reader->protocols[index] != NULL) { - if(pulse_protocol_decoded(reader->protocols[index])) { - decoded = index; - break; - } - } - } - - return decoded; -} - -void pulse_decoder_reset(PulseDecoder* reader) { - furi_assert(reader); - for(size_t index = 0; index < MAX_PROTOCOL; index++) { - if(reader->protocols[index] != NULL) { - pulse_protocol_reset(reader->protocols[index]); - } - } -} - -void pulse_decoder_get_data(PulseDecoder* reader, int32_t index, uint8_t* data, size_t length) { - furi_assert(reader); - furi_check(reader->protocols[index] != NULL); - pulse_protocol_get_data(reader->protocols[index], data, length); -} diff --git a/lib/one_wire/pulse_protocols/pulse_decoder.h b/lib/one_wire/pulse_protocols/pulse_decoder.h deleted file mode 100644 index dbaef52b5..000000000 --- a/lib/one_wire/pulse_protocols/pulse_decoder.h +++ /dev/null @@ -1,70 +0,0 @@ -/** - * @file pulse_decoder.h - * - * Generic pulse protocol decoder library - */ - -#pragma once -#include -#include -#include "pulse_protocol.h" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct PulseDecoder PulseDecoder; - -/** - * Allocate decoder - * @return PulseDecoder* - */ -PulseDecoder* pulse_decoder_alloc(); - -/** - * Deallocate decoder - * @param decoder - */ -void pulse_decoder_free(PulseDecoder* decoder); - -/** - * Add protocol to decoder - * @param decoder - * @param protocol protocol implementation - * @param index protocol index, should not be repeated - */ -void pulse_decoder_add_protocol(PulseDecoder* decoder, PulseProtocol* protocol, int32_t index); - -/** - * Push and process pulse with decoder - * @param decoder - * @param polarity - * @param length - */ -void pulse_decoder_process_pulse(PulseDecoder* decoder, bool polarity, uint32_t length); - -/** - * Get indec of decoded protocol - * @param decoder - * @return int32_t, -1 if nothing decoded, or index of decoded protocol - */ -int32_t pulse_decoder_get_decoded_index(PulseDecoder* decoder); - -/** - * Reset all protocols in decoder - * @param decoder - */ -void pulse_decoder_reset(PulseDecoder* decoder); - -/** - * Get decoded data from protocol - * @param decoder - * @param index - * @param data - * @param length - */ -void pulse_decoder_get_data(PulseDecoder* decoder, int32_t index, uint8_t* data, size_t length); - -#ifdef __cplusplus -} -#endif diff --git a/lib/one_wire/pulse_protocols/pulse_protocol.c b/lib/one_wire/pulse_protocols/pulse_protocol.c deleted file mode 100644 index 76feba113..000000000 --- a/lib/one_wire/pulse_protocols/pulse_protocol.c +++ /dev/null @@ -1,67 +0,0 @@ -#include "pulse_protocol.h" -#include -#include - -struct PulseProtocol { - void* context; - PulseProtocolPulseCallback pulse_cb; - PulseProtocolResetCallback reset_cb; - PulseProtocolGetDataCallback get_data_cb; - PulseProtocolDecodedCallback decoded_cb; -}; - -PulseProtocol* pulse_protocol_alloc() { - PulseProtocol* protocol = malloc(sizeof(PulseProtocol)); - memset(protocol, 0, sizeof(PulseProtocol)); - return protocol; -} - -void pulse_protocol_set_context(PulseProtocol* protocol, void* context) { - protocol->context = context; -} - -void pulse_protocol_set_pulse_cb(PulseProtocol* protocol, PulseProtocolPulseCallback callback) { - protocol->pulse_cb = callback; -} - -void pulse_protocol_set_reset_cb(PulseProtocol* protocol, PulseProtocolResetCallback callback) { - protocol->reset_cb = callback; -} - -void pulse_protocol_set_get_data_cb(PulseProtocol* protocol, PulseProtocolGetDataCallback callback) { - protocol->get_data_cb = callback; -} - -void pulse_protocol_set_decoded_cb(PulseProtocol* protocol, PulseProtocolDecodedCallback callback) { - protocol->decoded_cb = callback; -} - -void pulse_protocol_free(PulseProtocol* protocol) { - free(protocol); -} - -void pulse_protocol_process_pulse(PulseProtocol* protocol, bool polarity, uint32_t length) { - if(protocol->pulse_cb != NULL) { - protocol->pulse_cb(protocol->context, polarity, length); - } -} - -void pulse_protocol_reset(PulseProtocol* protocol) { - if(protocol->reset_cb != NULL) { - protocol->reset_cb(protocol->context); - } -} - -bool pulse_protocol_decoded(PulseProtocol* protocol) { - bool result = false; - if(protocol->decoded_cb != NULL) { - result = protocol->decoded_cb(protocol->context); - } - return result; -} - -void pulse_protocol_get_data(PulseProtocol* protocol, uint8_t* data, size_t length) { - if(protocol->get_data_cb != NULL) { - protocol->get_data_cb(protocol->context, data, length); - } -} diff --git a/lib/one_wire/pulse_protocols/pulse_protocol.h b/lib/one_wire/pulse_protocols/pulse_protocol.h deleted file mode 100644 index bfce0e76d..000000000 --- a/lib/one_wire/pulse_protocols/pulse_protocol.h +++ /dev/null @@ -1,122 +0,0 @@ -/** - * @file pulse_protocol.h - * - * Generic pulse protocol decoder library, protocol interface - */ - -#pragma once -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * Anonymous PulseProtocol struct - */ -typedef struct PulseProtocol PulseProtocol; - -/** - * Process pulse callback - */ -typedef void (*PulseProtocolPulseCallback)(void* context, bool polarity, uint32_t length); - -/** - * Reset protocol callback - */ -typedef void (*PulseProtocolResetCallback)(void* context); - -/** - * Get decoded data callback - */ -typedef void (*PulseProtocolGetDataCallback)(void* context, uint8_t* data, size_t length); - -/** - * Is protocol decoded callback - */ -typedef bool (*PulseProtocolDecodedCallback)(void* context); - -/** - * Allocate protocol - * @return PulseProtocol* - */ -PulseProtocol* pulse_protocol_alloc(); - -/** - * Deallocate protocol - * @param protocol - */ -void pulse_protocol_free(PulseProtocol* protocol); - -/** - * Set context for callbacks - * @param protocol - * @param context - */ -void pulse_protocol_set_context(PulseProtocol* protocol, void* context); - -/** - * Set "Process pulse" callback. Called from the decoder when a new pulse is received. - * @param protocol - * @param callback - */ -void pulse_protocol_set_pulse_cb(PulseProtocol* protocol, PulseProtocolPulseCallback callback); - -/** - * Set "Reset protocol" callback. Called from the decoder when the decoder is reset. - * @param protocol - * @param callback - */ -void pulse_protocol_set_reset_cb(PulseProtocol* protocol, PulseProtocolResetCallback callback); - -/** - * Set "Get decoded data" callback. Called from the decoder when the decoder wants to get decoded data. - * @param protocol - * @param callback - */ -void pulse_protocol_set_get_data_cb(PulseProtocol* protocol, PulseProtocolGetDataCallback callback); - -/** - * Set "Is protocol decoded" callback. Called from the decoder when the decoder wants to know if a protocol has been decoded. - * @param protocol - * @param callback - */ -void pulse_protocol_set_decoded_cb(PulseProtocol* protocol, PulseProtocolDecodedCallback callback); - -/** - * Part of decoder interface. - * @param protocol - * @param polarity - * @param length - */ -void pulse_protocol_process_pulse(PulseProtocol* protocol, bool polarity, uint32_t length); - -/** - * Part of decoder interface. - * @param protocol - * @return true - * @return false - */ -bool pulse_protocol_decoded(PulseProtocol* protocol); - -/** - * Part of decoder interface. - * @param protocol - * @return true - * @return false - */ -void pulse_protocol_get_data(PulseProtocol* protocol, uint8_t* data, size_t length); - -/** - * Part of decoder interface. - * @param protocol - * @return true - * @return false - */ -void pulse_protocol_reset(PulseProtocol* protocol); - -#ifdef __cplusplus -} -#endif diff --git a/lib/subghz/protocols/keeloq_common.c b/lib/subghz/protocols/keeloq_common.c index 22217c025..5306002ba 100644 --- a/lib/subghz/protocols/keeloq_common.c +++ b/lib/subghz/protocols/keeloq_common.c @@ -5,6 +5,10 @@ #include #include +#define bit(x, n) (((x) >> (n)) & 1) +#define g5(x, a, b, c, d, e) \ + (bit(x, a) + bit(x, b) * 2 + bit(x, c) * 4 + bit(x, d) * 8 + bit(x, e) * 16) + /** Simple Learning Encrypt * @param data - 0xBSSSCCCC, B(4bit) key, S(10bit) serial&0x3FF, C(16bit) counter * @param key - manufacture (64bit) diff --git a/lib/subghz/protocols/keeloq_common.h b/lib/subghz/protocols/keeloq_common.h index 99d09d41c..554afaa61 100644 --- a/lib/subghz/protocols/keeloq_common.h +++ b/lib/subghz/protocols/keeloq_common.h @@ -11,9 +11,6 @@ * */ #define KEELOQ_NLF 0x3A5C742E -#define bit(x, n) (((x) >> (n)) & 1) -#define g5(x, a, b, c, d, e) \ - (bit(x, a) + bit(x, b) * 2 + bit(x, c) * 4 + bit(x, d) * 8 + bit(x, e) * 16) /* * KeeLoq learning types diff --git a/lib/subghz/protocols/magellen.c b/lib/subghz/protocols/magellen.c new file mode 100644 index 000000000..859f80351 --- /dev/null +++ b/lib/subghz/protocols/magellen.c @@ -0,0 +1,444 @@ +#include "magellen.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocolMagellen" + +static const SubGhzBlockConst subghz_protocol_magellen_const = { + .te_short = 200, + .te_long = 400, + .te_delta = 100, + .min_count_bit_for_found = 32, +}; + +struct SubGhzProtocolDecoderMagellen { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + uint16_t header_count; +}; + +struct SubGhzProtocolEncoderMagellen { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + MagellenDecoderStepReset = 0, + MagellenDecoderStepCheckPreambula, + MagellenDecoderStepFoundPreambula, + MagellenDecoderStepSaveDuration, + MagellenDecoderStepCheckDuration, +} MagellenDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_magellen_decoder = { + .alloc = subghz_protocol_decoder_magellen_alloc, + .free = subghz_protocol_decoder_magellen_free, + + .feed = subghz_protocol_decoder_magellen_feed, + .reset = subghz_protocol_decoder_magellen_reset, + + .get_hash_data = subghz_protocol_decoder_magellen_get_hash_data, + .serialize = subghz_protocol_decoder_magellen_serialize, + .deserialize = subghz_protocol_decoder_magellen_deserialize, + .get_string = subghz_protocol_decoder_magellen_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_magellen_encoder = { + .alloc = subghz_protocol_encoder_magellen_alloc, + .free = subghz_protocol_encoder_magellen_free, + + .deserialize = subghz_protocol_encoder_magellen_deserialize, + .stop = subghz_protocol_encoder_magellen_stop, + .yield = subghz_protocol_encoder_magellen_yield, +}; + +const SubGhzProtocol subghz_protocol_magellen = { + .name = SUBGHZ_PROTOCOL_MAGELLEN_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_magellen_decoder, + .encoder = &subghz_protocol_magellen_encoder, +}; + +void* subghz_protocol_encoder_magellen_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderMagellen* instance = malloc(sizeof(SubGhzProtocolEncoderMagellen)); + + instance->base.protocol = &subghz_protocol_magellen; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 256; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_magellen_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderMagellen* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderMagellen instance + * @return true On success + */ +static bool subghz_protocol_encoder_magellen_get_upload(SubGhzProtocolEncoderMagellen* instance) { + furi_assert(instance); + + size_t index = 0; + + //Send header + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_magellen_const.te_short * 4); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_magellen_const.te_short); + for(uint8_t i = 0; i < 12; i++) { + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_magellen_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_magellen_const.te_short); + } + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_magellen_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_magellen_const.te_long); + + //Send start bit + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_magellen_const.te_long * 3); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_magellen_const.te_long); + + //Send key data + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_magellen_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_magellen_const.te_long); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_magellen_const.te_long); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_magellen_const.te_short); + } + } + + //Send stop bit + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_magellen_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_magellen_const.te_long * 100); + + instance->encoder.size_upload = index; + return true; +} + +bool subghz_protocol_encoder_magellen_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderMagellen* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_magellen_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + subghz_protocol_encoder_magellen_get_upload(instance); + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_magellen_stop(void* context) { + SubGhzProtocolEncoderMagellen* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_magellen_yield(void* context) { + SubGhzProtocolEncoderMagellen* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_running) { + instance->encoder.is_running = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + + return ret; +} + +void* subghz_protocol_decoder_magellen_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderMagellen* instance = malloc(sizeof(SubGhzProtocolDecoderMagellen)); + instance->base.protocol = &subghz_protocol_magellen; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_magellen_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderMagellen* instance = context; + free(instance); +} + +void subghz_protocol_decoder_magellen_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderMagellen* instance = context; + instance->decoder.parser_step = MagellenDecoderStepReset; +} + +uint8_t subghz_protocol_magellen_crc8(uint8_t* data, size_t len) { + uint8_t crc = 0x00; + size_t i, j; + for(i = 0; i < len; i++) { + crc ^= data[i]; + for(j = 0; j < 8; j++) { + if((crc & 0x80) != 0) + crc = (uint8_t)((crc << 1) ^ 0x31); + else + crc <<= 1; + } + } + return crc; +} + +static bool subghz_protocol_magellen_check_crc(SubGhzProtocolDecoderMagellen* instance) { + uint8_t data[3] = { + instance->decoder.decode_data >> 24, + instance->decoder.decode_data >> 16, + instance->decoder.decode_data >> 8}; + return (instance->decoder.decode_data & 0xFF) == + subghz_protocol_magellen_crc8(data, sizeof(data)); +} + +void subghz_protocol_decoder_magellen_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderMagellen* instance = context; + + switch(instance->decoder.parser_step) { + case MagellenDecoderStepReset: + if((level) && (DURATION_DIFF(duration, subghz_protocol_magellen_const.te_short) < + subghz_protocol_magellen_const.te_delta)) { + instance->decoder.parser_step = MagellenDecoderStepCheckPreambula; + instance->decoder.te_last = duration; + instance->header_count = 0; + } + break; + + case MagellenDecoderStepCheckPreambula: + if(level) { + instance->decoder.te_last = duration; + } else { + if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_magellen_const.te_short) < + subghz_protocol_magellen_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_magellen_const.te_short) < + subghz_protocol_magellen_const.te_delta)) { + // Found header + instance->header_count++; + } else if( + (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_magellen_const.te_short) < + subghz_protocol_magellen_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_magellen_const.te_long) < + subghz_protocol_magellen_const.te_delta * 2) && + (instance->header_count > 10)) { + instance->decoder.parser_step = MagellenDecoderStepFoundPreambula; + } else { + instance->decoder.parser_step = MagellenDecoderStepReset; + } + } + break; + + case MagellenDecoderStepFoundPreambula: + if(level) { + instance->decoder.te_last = duration; + } else { + if((DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_magellen_const.te_short * 6) < + subghz_protocol_magellen_const.te_delta * 3) && + (DURATION_DIFF(duration, subghz_protocol_magellen_const.te_long) < + subghz_protocol_magellen_const.te_delta * 2)) { + instance->decoder.parser_step = MagellenDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } else { + instance->decoder.parser_step = MagellenDecoderStepReset; + } + } + break; + + case MagellenDecoderStepSaveDuration: + if(level) { + instance->decoder.te_last = duration; + instance->decoder.parser_step = MagellenDecoderStepCheckDuration; + } else { + instance->decoder.parser_step = MagellenDecoderStepReset; + } + break; + + case MagellenDecoderStepCheckDuration: + if(!level) { + if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_magellen_const.te_short) < + subghz_protocol_magellen_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_magellen_const.te_long) < + subghz_protocol_magellen_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = MagellenDecoderStepSaveDuration; + } else if( + (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_magellen_const.te_long) < + subghz_protocol_magellen_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_magellen_const.te_short) < + subghz_protocol_magellen_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = MagellenDecoderStepSaveDuration; + } else if(duration >= (subghz_protocol_magellen_const.te_long * 3)) { + //Found stop bit + if((instance->decoder.decode_count_bit == + subghz_protocol_magellen_const.min_count_bit_for_found) && + subghz_protocol_magellen_check_crc(instance)) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + instance->decoder.parser_step = MagellenDecoderStepReset; + } else { + instance->decoder.parser_step = MagellenDecoderStepReset; + } + } else { + instance->decoder.parser_step = MagellenDecoderStepReset; + } + break; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + */ +static void subghz_protocol_magellen_check_remote_controller(SubGhzBlockGeneric* instance) { + /* +* package 32b data 24b CRC8 +* 0x037AE4828 => 001101111010111001001000 00101000 +* +* 0x037AE48 (flipped in reverse bit sequence) => 0x1275EC +* +* 0x1275EC => 0x12-event codes, 0x75EC-serial (dec 117236) +* +* event codes +* bit_0: 1-alarm, 0-close +* bit_1: 1-Tamper On (alarm), 0-Tamper Off (ok) +* bit_2: ? +* bit_3: 1-power on +* bit_4: model type - door alarm +* bit_5: model type - motion sensor +* bit_6: ? +* bit_7: ? +* +*/ + uint64_t data_rev = subghz_protocol_blocks_reverse_key(instance->data >> 8, 24); + instance->serial = data_rev & 0xFFFF; + instance->btn = (data_rev >> 16) & 0xFF; +} + +static void subghz_protocol_magellen_get_event_serialize(uint8_t event, string_t output) { + string_cat_printf( + output, + "%s%s%s%s%s%s%s%s", + (event & 0x1 ? " Alarm" : "Ok"), + ((event >> 1) & 0x1 ? ", Tamper On (Alarm)" : ""), + ((event >> 2) & 0x1 ? ", ?" : ""), + ((event >> 3) & 0x1 ? ", Power On" : ""), + ((event >> 4) & 0x1 ? ", MT:Door_Alarm" : ""), + ((event >> 5) & 0x1 ? ", MT:Motion_Sensor" : ""), + ((event >> 6) & 0x1 ? ", ?" : ""), + ((event >> 7) & 0x1 ? ", ?" : "")); +} + +uint8_t subghz_protocol_decoder_magellen_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderMagellen* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_magellen_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzPresetDefinition* preset) { + furi_assert(context); + SubGhzProtocolDecoderMagellen* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_magellen_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderMagellen* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_magellen_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; +} + +void subghz_protocol_decoder_magellen_get_string(void* context, string_t output) { + furi_assert(context); + SubGhzProtocolDecoderMagellen* instance = context; + subghz_protocol_magellen_check_remote_controller(&instance->generic); + string_cat_printf( + output, + "%s %dbit\r\n" + "Key:0x%08lX\r\n" + "Sn:%03d%03d, Event:0x%02X\r\n" + "Stat:", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data & 0xFFFFFFFF), + (instance->generic.serial >> 8) & 0xFF, + instance->generic.serial & 0xFF, + instance->generic.btn); + + subghz_protocol_magellen_get_event_serialize(instance->generic.btn, output); +} diff --git a/lib/subghz/protocols/magellen.h b/lib/subghz/protocols/magellen.h new file mode 100644 index 000000000..224f79011 --- /dev/null +++ b/lib/subghz/protocols/magellen.h @@ -0,0 +1,107 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_MAGELLEN_NAME "Magellen" + +typedef struct SubGhzProtocolDecoderMagellen SubGhzProtocolDecoderMagellen; +typedef struct SubGhzProtocolEncoderMagellen SubGhzProtocolEncoderMagellen; + +extern const SubGhzProtocolDecoder subghz_protocol_magellen_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_magellen_encoder; +extern const SubGhzProtocol subghz_protocol_magellen; + +/** + * Allocate SubGhzProtocolEncoderMagellen. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderMagellen* pointer to a SubGhzProtocolEncoderMagellen instance + */ +void* subghz_protocol_encoder_magellen_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderMagellen. + * @param context Pointer to a SubGhzProtocolEncoderMagellen instance + */ +void subghz_protocol_encoder_magellen_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderMagellen instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_magellen_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderMagellen instance + */ +void subghz_protocol_encoder_magellen_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderMagellen instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_magellen_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderMagellen. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderMagellen* pointer to a SubGhzProtocolDecoderMagellen instance + */ +void* subghz_protocol_decoder_magellen_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderMagellen. + * @param context Pointer to a SubGhzProtocolDecoderMagellen instance + */ +void subghz_protocol_decoder_magellen_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderMagellen. + * @param context Pointer to a SubGhzProtocolDecoderMagellen instance + */ +void subghz_protocol_decoder_magellen_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderMagellen instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_magellen_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderMagellen instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_magellen_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderMagellen. + * @param context Pointer to a SubGhzProtocolDecoderMagellen instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @return true On success + */ +bool subghz_protocol_decoder_magellen_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzPresetDefinition* preset); + +/** + * Deserialize data SubGhzProtocolDecoderMagellen. + * @param context Pointer to a SubGhzProtocolDecoderMagellen instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_magellen_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderMagellen instance + * @param output Resulting text + */ +void subghz_protocol_decoder_magellen_get_string(void* context, string_t output); diff --git a/lib/subghz/protocols/registry.c b/lib/subghz/protocols/registry.c index 19b03cebc..b72278788 100644 --- a/lib/subghz/protocols/registry.c +++ b/lib/subghz/protocols/registry.c @@ -11,7 +11,7 @@ const SubGhzProtocol* subghz_protocol_registry[] = { &subghz_protocol_secplus_v1, &subghz_protocol_megacode, &subghz_protocol_holtek, &subghz_protocol_chamb_code, &subghz_protocol_power_smart, &subghz_protocol_marantec, &subghz_protocol_bett, &subghz_protocol_doitrand, &subghz_protocol_phoenix_v2, - &subghz_protocol_honeywell_wdb, + &subghz_protocol_honeywell_wdb, &subghz_protocol_magellen, }; diff --git a/lib/subghz/protocols/registry.h b/lib/subghz/protocols/registry.h index d75695132..36a560765 100644 --- a/lib/subghz/protocols/registry.h +++ b/lib/subghz/protocols/registry.h @@ -33,6 +33,7 @@ #include "doitrand.h" #include "phoenix_v2.h" #include "honeywell_wdb.h" +#include "magellen.h" /** * Registration by name SubGhzProtocol. diff --git a/lib/toolbox/SConscript b/lib/toolbox/SConscript index 26c233d98..e4f2cbd02 100644 --- a/lib/toolbox/SConscript +++ b/lib/toolbox/SConscript @@ -29,6 +29,7 @@ build_version = libenv.VersionBuilder( fw_version_json = libenv.InstallAs( "${BUILD_DIR}/${FIRMWARE_BUILD_CFG}.json", "version.json" ) +Alias("version_json", fw_version_json) env.Append(FW_VERSION_JSON=fw_version_json) # Default(fw_version_json) @@ -40,7 +41,7 @@ if not version_depends: sources = libenv.GlobRecursive("*.c") -libenv.Append(CPPPATH=["."]) +libenv.Append(CPPPATH=[libenv.Dir(".")]) lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources) libenv.Install("${LIB_DIST_DIR}", lib) diff --git a/lib/toolbox/buffer_stream.c b/lib/toolbox/buffer_stream.c new file mode 100644 index 000000000..66d210963 --- /dev/null +++ b/lib/toolbox/buffer_stream.c @@ -0,0 +1,145 @@ +#include "buffer_stream.h" +#include + +struct Buffer { + volatile bool occupied; + volatile size_t size; + uint8_t* data; + size_t max_data_size; +}; + +struct BufferStream { + size_t stream_overrun_count; + StreamBufferHandle_t stream; + + size_t index; + Buffer* buffers; + size_t max_buffers_count; +}; + +bool buffer_write(Buffer* buffer, const uint8_t* data, size_t size) { + if(buffer->occupied) { + return false; + } + if((buffer->size + size) > buffer->max_data_size) { + return false; + } + memcpy(buffer->data + buffer->size, data, size); + buffer->size += size; + return true; +} + +uint8_t* buffer_get_data(Buffer* buffer) { + return buffer->data; +} + +size_t buffer_get_size(Buffer* buffer) { + return buffer->size; +} + +void buffer_reset(Buffer* buffer) { + buffer->occupied = false; + buffer->size = 0; +} + +BufferStream* buffer_stream_alloc(size_t buffer_size, size_t buffers_count) { + furi_assert(buffer_size > 0); + furi_assert(buffers_count > 0); + BufferStream* buffer_stream = malloc(sizeof(BufferStream)); + buffer_stream->max_buffers_count = buffers_count; + buffer_stream->buffers = malloc(sizeof(Buffer) * buffer_stream->max_buffers_count); + for(size_t i = 0; i < buffer_stream->max_buffers_count; i++) { + buffer_stream->buffers[i].occupied = false; + buffer_stream->buffers[i].size = 0; + buffer_stream->buffers[i].data = malloc(buffer_size); + buffer_stream->buffers[i].max_data_size = buffer_size; + } + buffer_stream->stream = xStreamBufferCreate( + sizeof(BufferStream*) * buffer_stream->max_buffers_count, sizeof(BufferStream*)); + buffer_stream->stream_overrun_count = 0; + buffer_stream->index = 0; + + return buffer_stream; +} + +void buffer_stream_free(BufferStream* buffer_stream) { + for(size_t i = 0; i < buffer_stream->max_buffers_count; i++) { + free(buffer_stream->buffers[i].data); + } + vStreamBufferDelete(buffer_stream->stream); + free(buffer_stream->buffers); + free(buffer_stream); +} + +static inline int8_t buffer_stream_get_free_buffer(BufferStream* buffer_stream) { + int8_t id = -1; + for(size_t i = 0; i < buffer_stream->max_buffers_count; i++) { + if(buffer_stream->buffers[i].occupied == false) { + id = i; + break; + } + } + + return id; +} + +bool buffer_stream_send_from_isr( + BufferStream* buffer_stream, + const uint8_t* data, + size_t size, + BaseType_t* const task_woken) { + Buffer* buffer = &buffer_stream->buffers[buffer_stream->index]; + bool result = true; + + // write to buffer + if(!buffer_write(buffer, data, size)) { + // if buffer is full - send it + buffer->occupied = true; + // we always have space for buffer in stream + xStreamBufferSendFromISR(buffer_stream->stream, &buffer, sizeof(Buffer*), task_woken); + + // get new buffer from the pool + int8_t index = buffer_stream_get_free_buffer(buffer_stream); + + // check that we have valid buffer + if(index == -1) { + // no free buffer + buffer_stream->stream_overrun_count++; + result = false; + } else { + // write to new buffer + buffer_stream->index = index; + buffer = &buffer_stream->buffers[buffer_stream->index]; + buffer_write(buffer, data, size); + } + } + + return result; +} + +Buffer* buffer_stream_receive(BufferStream* buffer_stream, TickType_t timeout) { + Buffer* buffer; + size_t size = xStreamBufferReceive(buffer_stream->stream, &buffer, sizeof(Buffer*), timeout); + + if(size == sizeof(Buffer*)) { + return buffer; + } else { + return NULL; + } +} + +size_t buffer_stream_get_overrun_count(BufferStream* buffer_stream) { + return buffer_stream->stream_overrun_count; +} + +void buffer_stream_reset(BufferStream* buffer_stream) { + FURI_CRITICAL_ENTER(); + BaseType_t xReturn = xStreamBufferReset(buffer_stream->stream); + furi_assert(xReturn == pdPASS); + UNUSED(xReturn); + buffer_stream->stream_overrun_count = 0; + for(size_t i = 0; i < buffer_stream->max_buffers_count; i++) { + buffer_reset(&buffer_stream->buffers[i]); + } + FURI_CRITICAL_EXIT(); +} \ No newline at end of file diff --git a/lib/toolbox/buffer_stream.h b/lib/toolbox/buffer_stream.h new file mode 100644 index 000000000..d4c3cddf7 --- /dev/null +++ b/lib/toolbox/buffer_stream.h @@ -0,0 +1,94 @@ +/** + * @file buffer_stream.h + * + * This file implements the concept of a buffer stream. + * Data is written to the buffer until the buffer is full. + * Then the buffer pointer is written to the stream, and the new write buffer is taken from the buffer pool. + * After the buffer has been read by the receiving thread, it is sent to the free buffer pool. + * + * This will speed up sending large chunks of data between threads, compared to using a stream directly. + */ +#pragma once +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct Buffer Buffer; + +/** + * @brief Get buffer data pointer + * @param buffer + * @return uint8_t* + */ +uint8_t* buffer_get_data(Buffer* buffer); + +/** + * @brief Get buffer size + * @param buffer + * @return size_t + */ +size_t buffer_get_size(Buffer* buffer); + +/** + * @brief Reset buffer and send to free buffer pool + * @param buffer + */ +void buffer_reset(Buffer* buffer); + +typedef struct BufferStream BufferStream; + +/** + * @brief Allocate a new BufferStream instance + * @param buffer_size + * @param buffers_count + * @return BufferStream* + */ +BufferStream* buffer_stream_alloc(size_t buffer_size, size_t buffers_count); + +/** + * @brief Free a BufferStream instance + * @param buffer_stream + */ +void buffer_stream_free(BufferStream* buffer_stream); + +/** + * @brief Write data to buffer stream, from ISR context + * Data will be written to the buffer until the buffer is full, and only then will the buffer be sent. + * @param buffer_stream + * @param data + * @param size + * @param task_woken + * @return bool + */ +bool buffer_stream_send_from_isr( + BufferStream* buffer_stream, + const uint8_t* data, + size_t size, + BaseType_t* const task_woken); + +/** + * @brief Receive buffer from stream + * @param buffer_stream + * @param timeout + * @return Buffer* + */ +Buffer* buffer_stream_receive(BufferStream* buffer_stream, TickType_t timeout); + +/** + * @brief Get stream overrun count + * @param buffer_stream + * @return size_t + */ +size_t buffer_stream_get_overrun_count(BufferStream* buffer_stream); + +/** + * @brief Reset stream and buffer pool + * @param buffer_stream + */ +void buffer_stream_reset(BufferStream* buffer_stream); + +#ifdef __cplusplus +} +#endif diff --git a/lib/toolbox/profiler.c b/lib/toolbox/profiler.c new file mode 100644 index 000000000..96f38dce2 --- /dev/null +++ b/lib/toolbox/profiler.c @@ -0,0 +1,87 @@ +#include "profiler.h" +#include +#include +#include +#include + +typedef struct { + uint32_t start; + uint32_t length; + uint32_t count; +} ProfilerRecord; + +DICT_DEF2(ProfilerRecordDict, const char*, M_CSTR_OPLIST, ProfilerRecord, M_POD_OPLIST) +#define M_OPL_ProfilerRecord_t() DICT_OPLIST(ProfilerRecord, M_CSTR_OPLIST, M_POD_OPLIST) + +struct Profiler { + ProfilerRecordDict_t records; +}; + +Profiler* profiler_alloc() { + Profiler* profiler = malloc(sizeof(Profiler)); + ProfilerRecordDict_init(profiler->records); + return profiler; +} + +void profiler_free(Profiler* profiler) { + ProfilerRecordDict_clear(profiler->records); + free(profiler); +} + +void profiler_prealloc(Profiler* profiler, const char* key) { + ProfilerRecord record = { + .start = 0, + .length = 0, + .count = 0, + }; + + ProfilerRecordDict_set_at(profiler->records, key, record); +} + +void profiler_start(Profiler* profiler, const char* key) { + ProfilerRecord* record = ProfilerRecordDict_get(profiler->records, key); + if(record == NULL) { + profiler_prealloc(profiler, key); + record = ProfilerRecordDict_get(profiler->records, key); + } + + furi_check(record->start == 0); + record->start = DWT->CYCCNT; +} + +void profiler_stop(Profiler* profiler, const char* key) { + ProfilerRecord* record = ProfilerRecordDict_get(profiler->records, key); + furi_check(record != NULL); + + record->length += DWT->CYCCNT - record->start; + record->start = 0; + record->count++; +} + +void profiler_dump(Profiler* profiler) { + printf("Profiler:\r\n"); + + ProfilerRecordDict_it_t it; + for(ProfilerRecordDict_it(it, profiler->records); !ProfilerRecordDict_end_p(it); + ProfilerRecordDict_next(it)) { + const ProfilerRecordDict_itref_t* itref = ProfilerRecordDict_cref(it); + + uint32_t count = itref->value.count; + + uint32_t clocks = itref->value.length; + double us = (double)clocks / (double)64.0; + double ms = (double)clocks / (double)64000.0; + double s = (double)clocks / (double)64000000.0; + + printf("\t%s[%lu]: %f s, %f ms, %f us, %lu clk\r\n", itref->key, count, s, ms, us, clocks); + + if(count > 1) { + us /= (double)count; + ms /= (double)count; + s /= (double)count; + clocks /= count; + + printf("\t%s[1]: %f s, %f ms, %f us, %lu clk\r\n", itref->key, s, ms, us, clocks); + } + } +} diff --git a/lib/toolbox/profiler.h b/lib/toolbox/profiler.h new file mode 100644 index 000000000..840146332 --- /dev/null +++ b/lib/toolbox/profiler.h @@ -0,0 +1,23 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct Profiler Profiler; + +Profiler* profiler_alloc(); + +void profiler_free(Profiler* profiler); + +void profiler_prealloc(Profiler* profiler, const char* key); + +void profiler_start(Profiler* profiler, const char* key); + +void profiler_stop(Profiler* profiler, const char* key); + +void profiler_dump(Profiler* profiler); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/lib/toolbox/protocols/protocol.h b/lib/toolbox/protocols/protocol.h new file mode 100644 index 000000000..56bb1b74c --- /dev/null +++ b/lib/toolbox/protocols/protocol.h @@ -0,0 +1,46 @@ +#pragma once +#include +#include +#include +#include +#include + +typedef void* (*ProtocolAlloc)(void); +typedef void (*ProtocolFree)(void* protocol); +typedef uint8_t* (*ProtocolGetData)(void* protocol); + +typedef void (*ProtocolDecoderStart)(void* protocol); +typedef bool (*ProtocolDecoderFeed)(void* protocol, bool level, uint32_t duration); + +typedef bool (*ProtocolEncoderStart)(void* protocol); +typedef LevelDuration (*ProtocolEncoderYield)(void* protocol); + +typedef void (*ProtocolRenderData)(void* protocol, string_t result); +typedef bool (*ProtocolWriteData)(void* protocol, void* data); + +typedef struct { + ProtocolDecoderStart start; + ProtocolDecoderFeed feed; +} ProtocolDecoder; + +typedef struct { + ProtocolEncoderStart start; + ProtocolEncoderYield yield; +} ProtocolEncoder; + +typedef struct { + const size_t data_size; + const char* name; + const char* manufacturer; + const uint32_t features; + const uint8_t validate_count; + + ProtocolAlloc alloc; + ProtocolFree free; + ProtocolGetData get_data; + ProtocolDecoder decoder; + ProtocolEncoder encoder; + ProtocolRenderData render_data; + ProtocolRenderData render_brief_data; + ProtocolWriteData write_data; +} ProtocolBase; \ No newline at end of file diff --git a/lib/toolbox/protocols/protocol_dict.c b/lib/toolbox/protocols/protocol_dict.c new file mode 100644 index 000000000..2a022cc40 --- /dev/null +++ b/lib/toolbox/protocols/protocol_dict.c @@ -0,0 +1,226 @@ +#include +#include "protocol_dict.h" + +struct ProtocolDict { + const ProtocolBase** base; + size_t count; + void** data; +}; + +ProtocolDict* protocol_dict_alloc(const ProtocolBase** protocols, size_t count) { + ProtocolDict* dict = malloc(sizeof(ProtocolDict)); + dict->base = protocols; + dict->count = count; + dict->data = malloc(sizeof(void*) * dict->count); + + for(size_t i = 0; i < dict->count; i++) { + dict->data[i] = dict->base[i]->alloc(); + } + + return dict; +} + +void protocol_dict_free(ProtocolDict* dict) { + for(size_t i = 0; i < dict->count; i++) { + dict->base[i]->free(dict->data[i]); + } + + free(dict->data); + free(dict); +} + +void protocol_dict_set_data( + ProtocolDict* dict, + size_t protocol_index, + const uint8_t* data, + size_t data_size) { + furi_assert(protocol_index < dict->count); + furi_assert(dict->base[protocol_index]->get_data != NULL); + uint8_t* protocol_data = dict->base[protocol_index]->get_data(dict->data[protocol_index]); + size_t protocol_data_size = dict->base[protocol_index]->data_size; + furi_check(data_size >= protocol_data_size); + memcpy(protocol_data, data, protocol_data_size); +} + +void protocol_dict_get_data( + ProtocolDict* dict, + size_t protocol_index, + uint8_t* data, + size_t data_size) { + furi_assert(protocol_index < dict->count); + furi_assert(dict->base[protocol_index]->get_data != NULL); + uint8_t* protocol_data = dict->base[protocol_index]->get_data(dict->data[protocol_index]); + size_t protocol_data_size = dict->base[protocol_index]->data_size; + furi_check(data_size >= protocol_data_size); + memcpy(data, protocol_data, protocol_data_size); +} + +size_t protocol_dict_get_data_size(ProtocolDict* dict, size_t protocol_index) { + furi_assert(protocol_index < dict->count); + return dict->base[protocol_index]->data_size; +} + +size_t protocol_dict_get_max_data_size(ProtocolDict* dict) { + size_t max_data_size = 0; + for(size_t i = 0; i < dict->count; i++) { + size_t data_size = dict->base[i]->data_size; + if(data_size > max_data_size) { + max_data_size = data_size; + } + } + + return max_data_size; +} + +const char* protocol_dict_get_name(ProtocolDict* dict, size_t protocol_index) { + furi_assert(protocol_index < dict->count); + return dict->base[protocol_index]->name; +} + +const char* protocol_dict_get_manufacturer(ProtocolDict* dict, size_t protocol_index) { + furi_assert(protocol_index < dict->count); + return dict->base[protocol_index]->manufacturer; +} + +void protocol_dict_decoders_start(ProtocolDict* dict) { + for(size_t i = 0; i < dict->count; i++) { + ProtocolDecoderStart fn = dict->base[i]->decoder.start; + + if(fn) { + fn(dict->data[i]); + } + } +} + +uint32_t protocol_dict_get_features(ProtocolDict* dict, size_t protocol_index) { + furi_assert(protocol_index < dict->count); + return dict->base[protocol_index]->features; +} + +ProtocolId protocol_dict_decoders_feed(ProtocolDict* dict, bool level, uint32_t duration) { + bool done = false; + ProtocolId ready_protocol_id = PROTOCOL_NO; + + for(size_t i = 0; i < dict->count; i++) { + ProtocolDecoderFeed fn = dict->base[i]->decoder.feed; + + if(fn) { + if(fn(dict->data[i], level, duration)) { + if(!done) { + ready_protocol_id = i; + done = true; + } + } + } + } + + return ready_protocol_id; +} + +ProtocolId protocol_dict_decoders_feed_by_feature( + ProtocolDict* dict, + uint32_t feature, + bool level, + uint32_t duration) { + bool done = false; + ProtocolId ready_protocol_id = PROTOCOL_NO; + + for(size_t i = 0; i < dict->count; i++) { + uint32_t features = dict->base[i]->features; + if(features & feature) { + ProtocolDecoderFeed fn = dict->base[i]->decoder.feed; + + if(fn) { + if(fn(dict->data[i], level, duration)) { + if(!done) { + ready_protocol_id = i; + done = true; + } + } + } + } + } + + return ready_protocol_id; +} + +ProtocolId protocol_dict_decoders_feed_by_id( + ProtocolDict* dict, + size_t protocol_index, + bool level, + uint32_t duration) { + furi_assert(protocol_index < dict->count); + + ProtocolId ready_protocol_id = PROTOCOL_NO; + ProtocolDecoderFeed fn = dict->base[protocol_index]->decoder.feed; + + if(fn) { + if(fn(dict->data[protocol_index], level, duration)) { + ready_protocol_id = protocol_index; + } + } + + return ready_protocol_id; +} + +bool protocol_dict_encoder_start(ProtocolDict* dict, size_t protocol_index) { + furi_assert(protocol_index < dict->count); + ProtocolEncoderStart fn = dict->base[protocol_index]->encoder.start; + + if(fn) { + return fn(dict->data[protocol_index]); + } else { + return false; + } +} + +LevelDuration protocol_dict_encoder_yield(ProtocolDict* dict, size_t protocol_index) { + furi_assert(protocol_index < dict->count); + ProtocolEncoderYield fn = dict->base[protocol_index]->encoder.yield; + + if(fn) { + return fn(dict->data[protocol_index]); + } else { + return level_duration_reset(); + } +} + +void protocol_dict_render_data(ProtocolDict* dict, string_t result, size_t protocol_index) { + furi_assert(protocol_index < dict->count); + ProtocolRenderData fn = dict->base[protocol_index]->render_data; + + if(fn) { + return fn(dict->data[protocol_index], result); + } +} + +void protocol_dict_render_brief_data(ProtocolDict* dict, string_t result, size_t protocol_index) { + furi_assert(protocol_index < dict->count); + ProtocolRenderData fn = dict->base[protocol_index]->render_brief_data; + + if(fn) { + return fn(dict->data[protocol_index], result); + } +} + +uint32_t protocol_dict_get_validate_count(ProtocolDict* dict, size_t protocol_index) { + furi_assert(protocol_index < dict->count); + return dict->base[protocol_index]->validate_count; +} + +ProtocolId protocol_dict_get_protocol_by_name(ProtocolDict* dict, const char* name) { + for(size_t i = 0; i < dict->count; i++) { + if(strcmp(name, protocol_dict_get_name(dict, i)) == 0) { + return i; + } + } + return PROTOCOL_NO; +} + +bool protocol_dict_get_write_data(ProtocolDict* dict, size_t protocol_index, void* data) { + furi_assert(protocol_index < dict->count); + ProtocolWriteData fn = dict->base[protocol_index]->write_data; + + furi_assert(fn); + return fn(dict->data[protocol_index], data); +} \ No newline at end of file diff --git a/lib/toolbox/protocols/protocol_dict.h b/lib/toolbox/protocols/protocol_dict.h new file mode 100644 index 000000000..3037ddd5e --- /dev/null +++ b/lib/toolbox/protocols/protocol_dict.h @@ -0,0 +1,73 @@ +#pragma once +#include "protocol.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct ProtocolDict ProtocolDict; + +typedef int32_t ProtocolId; + +#define PROTOCOL_NO (-1) +#define PROTOCOL_ALL_FEATURES (0xFFFFFFFF) + +ProtocolDict* protocol_dict_alloc(const ProtocolBase** protocols, size_t protocol_count); + +void protocol_dict_free(ProtocolDict* dict); + +void protocol_dict_set_data( + ProtocolDict* dict, + size_t protocol_index, + const uint8_t* data, + size_t data_size); + +void protocol_dict_get_data( + ProtocolDict* dict, + size_t protocol_index, + uint8_t* data, + size_t data_size); + +size_t protocol_dict_get_data_size(ProtocolDict* dict, size_t protocol_index); + +size_t protocol_dict_get_max_data_size(ProtocolDict* dict); + +const char* protocol_dict_get_name(ProtocolDict* dict, size_t protocol_index); + +const char* protocol_dict_get_manufacturer(ProtocolDict* dict, size_t protocol_index); + +void protocol_dict_decoders_start(ProtocolDict* dict); + +uint32_t protocol_dict_get_features(ProtocolDict* dict, size_t protocol_index); + +ProtocolId protocol_dict_decoders_feed(ProtocolDict* dict, bool level, uint32_t duration); + +ProtocolId protocol_dict_decoders_feed_by_feature( + ProtocolDict* dict, + uint32_t feature, + bool level, + uint32_t duration); + +ProtocolId protocol_dict_decoders_feed_by_id( + ProtocolDict* dict, + size_t protocol_index, + bool level, + uint32_t duration); + +bool protocol_dict_encoder_start(ProtocolDict* dict, size_t protocol_index); + +LevelDuration protocol_dict_encoder_yield(ProtocolDict* dict, size_t protocol_index); + +void protocol_dict_render_data(ProtocolDict* dict, string_t result, size_t protocol_index); + +void protocol_dict_render_brief_data(ProtocolDict* dict, string_t result, size_t protocol_index); + +uint32_t protocol_dict_get_validate_count(ProtocolDict* dict, size_t protocol_index); + +ProtocolId protocol_dict_get_protocol_by_name(ProtocolDict* dict, const char* name); + +bool protocol_dict_get_write_data(ProtocolDict* dict, size_t protocol_index, void* data); + +#ifdef __cplusplus +} +#endif diff --git a/lib/toolbox/pulse_joiner.c b/lib/toolbox/pulse_joiner.c new file mode 100644 index 000000000..b6206486c --- /dev/null +++ b/lib/toolbox/pulse_joiner.c @@ -0,0 +1,117 @@ +#include "pulse_joiner.h" +#include + +#define PULSE_MAX_COUNT 6 + +typedef struct { + bool polarity; + uint16_t time; +} Pulse; + +struct PulseJoiner { + size_t pulse_index; + Pulse pulses[PULSE_MAX_COUNT]; +}; + +PulseJoiner* pulse_joiner_alloc() { + PulseJoiner* pulse_joiner = malloc(sizeof(PulseJoiner)); + + pulse_joiner->pulse_index = 0; + for(uint8_t i = 0; i < PULSE_MAX_COUNT; i++) { + pulse_joiner->pulses[i].polarity = false; + pulse_joiner->pulses[i].time = 0; + } + + return pulse_joiner; +} + +void pulse_joiner_free(PulseJoiner* pulse_joiner) { + free(pulse_joiner); +} + +bool pulse_joiner_push_pulse(PulseJoiner* pulse_joiner, bool polarity, size_t period, size_t pulse) { + bool result = false; + furi_check((pulse_joiner->pulse_index + 1) < PULSE_MAX_COUNT); + + if(polarity == false && pulse_joiner->pulse_index == 0) { + // first negative pulse is omitted + + } else { + pulse_joiner->pulses[pulse_joiner->pulse_index].polarity = polarity; + pulse_joiner->pulses[pulse_joiner->pulse_index].time = pulse; + pulse_joiner->pulse_index++; + } + + if(period > pulse) { + pulse_joiner->pulses[pulse_joiner->pulse_index].polarity = !polarity; + pulse_joiner->pulses[pulse_joiner->pulse_index].time = period - pulse; + pulse_joiner->pulse_index++; + } + + if(pulse_joiner->pulse_index >= 4) { + // we know that first pulse is always high + // so we wait 2 edges, hi2low and next low2hi + + uint8_t edges_count = 0; + bool last_polarity = pulse_joiner->pulses[0].polarity; + + for(uint8_t i = 1; i < pulse_joiner->pulse_index; i++) { + if(pulse_joiner->pulses[i].polarity != last_polarity) { + edges_count++; + last_polarity = pulse_joiner->pulses[i].polarity; + } + } + + if(edges_count >= 2) { + result = true; + } + } + + return result; +} + +void pulse_joiner_pop_pulse(PulseJoiner* pulse_joiner, size_t* period, size_t* pulse) { + furi_check(pulse_joiner->pulse_index <= (PULSE_MAX_COUNT + 1)); + + uint16_t tmp_period = 0; + uint16_t tmp_pulse = 0; + uint8_t edges_count = 0; + bool last_polarity = pulse_joiner->pulses[0].polarity; + uint8_t next_fist_pulse = 0; + + for(uint8_t i = 0; i < PULSE_MAX_COUNT; i++) { + // count edges + if(pulse_joiner->pulses[i].polarity != last_polarity) { + edges_count++; + last_polarity = pulse_joiner->pulses[i].polarity; + } + + // wait for 2 edges + if(edges_count == 2) { + next_fist_pulse = i; + break; + } + + // sum pulse time + if(pulse_joiner->pulses[i].polarity) { + tmp_period += pulse_joiner->pulses[i].time; + tmp_pulse += pulse_joiner->pulses[i].time; + } else { + tmp_period += pulse_joiner->pulses[i].time; + } + pulse_joiner->pulse_index--; + } + + *period = tmp_period; + *pulse = tmp_pulse; + + // remove counted periods and shift data + for(uint8_t i = 0; i < PULSE_MAX_COUNT; i++) { + if((next_fist_pulse + i) < PULSE_MAX_COUNT) { + pulse_joiner->pulses[i].polarity = pulse_joiner->pulses[next_fist_pulse + i].polarity; + pulse_joiner->pulses[i].time = pulse_joiner->pulses[next_fist_pulse + i].time; + } else { + break; + } + } +} \ No newline at end of file diff --git a/lib/toolbox/pulse_joiner.h b/lib/toolbox/pulse_joiner.h new file mode 100644 index 000000000..25f702e72 --- /dev/null +++ b/lib/toolbox/pulse_joiner.h @@ -0,0 +1,46 @@ +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct PulseJoiner PulseJoiner; + +/** + * @brief Alloc PulseJoiner + * + * @return PulseJoiner* + */ +PulseJoiner* pulse_joiner_alloc(); + +/** + * @brief Free PulseJoiner + * + * @param pulse_joiner + */ +void pulse_joiner_free(PulseJoiner* pulse_joiner); + +/** + * @brief Push timer pulse. First negative pulse is ommited. + * + * @param polarity pulse polarity: true = high2low, false = low2high + * @param period overall period time in timer clicks + * @param pulse pulse time in timer clicks + * + * @return true - next pulse can and must be popped immediatly + */ +bool pulse_joiner_push_pulse(PulseJoiner* pulse_joiner, bool polarity, size_t period, size_t pulse); + +/** + * @brief Get the next timer pulse. Call only if push_pulse returns true. + * + * @param period overall period time in timer clicks + * @param pulse pulse time in timer clicks + */ +void pulse_joiner_pop_pulse(PulseJoiner* pulse_joiner, size_t* period, size_t* pulse); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/lib/one_wire/pulse_protocols/pulse_glue.c b/lib/toolbox/pulse_protocols/pulse_glue.c similarity index 100% rename from lib/one_wire/pulse_protocols/pulse_glue.c rename to lib/toolbox/pulse_protocols/pulse_glue.c diff --git a/lib/one_wire/pulse_protocols/pulse_glue.h b/lib/toolbox/pulse_protocols/pulse_glue.h similarity index 100% rename from lib/one_wire/pulse_protocols/pulse_glue.h rename to lib/toolbox/pulse_protocols/pulse_glue.h diff --git a/lib/toolbox/varint.c b/lib/toolbox/varint.c new file mode 100644 index 000000000..ee2f5c3af --- /dev/null +++ b/lib/toolbox/varint.c @@ -0,0 +1,76 @@ +#include "varint.h" + +size_t varint_uint32_pack(uint32_t value, uint8_t* output) { + uint8_t* start = output; + while(value >= 0x80) { + *output++ = (value | 0x80); + value >>= 7; + } + *output++ = value; + return output - start; +} + +size_t varint_uint32_unpack(uint32_t* value, const uint8_t* input, size_t input_size) { + size_t i; + uint32_t parsed = 0; + + for(i = 0; i < input_size; i++) { + parsed |= (input[i] & 0x7F) << (7 * i); + + if(!(input[i] & 0x80)) { + break; + } + } + + *value = parsed; + + return i + 1; +} + +size_t varint_uint32_length(uint32_t value) { + size_t size = 0; + while(value >= 0x80) { + value >>= 7; + size++; + } + size++; + + return size; +} + +size_t varint_int32_pack(int32_t value, uint8_t* output) { + uint32_t v; + + if(value >= 0) { + v = value * 2; + } else { + v = (value * -2) - 1; + } + + return varint_uint32_pack(v, output); +} + +size_t varint_int32_unpack(int32_t* value, const uint8_t* input, size_t input_size) { + uint32_t v; + size_t size = varint_uint32_unpack(&v, input, input_size); + + if(v & 1) { + *value = (int32_t)(v + 1) / (-2); + } else { + *value = v / 2; + } + + return size; +} + +size_t varint_int32_length(int32_t value) { + uint32_t v; + + if(value >= 0) { + v = value * 2; + } else { + v = (value * -2) - 1; + } + + return varint_uint32_length(v); +} \ No newline at end of file diff --git a/lib/toolbox/varint.h b/lib/toolbox/varint.h new file mode 100644 index 000000000..bf4681d4d --- /dev/null +++ b/lib/toolbox/varint.h @@ -0,0 +1,35 @@ +#pragma once +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Pack uint32 to varint + * @param value value from UINT32_MIN to UINT32_MAX + * @param output output array, need to be at least 5 bytes long + * @return size_t + */ +size_t varint_uint32_pack(uint32_t value, uint8_t* output); + +size_t varint_uint32_unpack(uint32_t* value, const uint8_t* input, size_t input_size); + +size_t varint_uint32_length(uint32_t value); + +/** + * Pack int32 to varint + * @param value value from (INT32_MIN / 2 + 1) to INT32_MAX + * @param output output array, need to be at least 5 bytes long + * @return size_t + */ +size_t varint_int32_pack(int32_t value, uint8_t* output); + +size_t varint_int32_unpack(int32_t* value, const uint8_t* input, size_t input_size); + +size_t varint_int32_length(int32_t value); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/scripts/amap_mariadb_insert.py b/scripts/amap_mariadb_insert.py new file mode 100755 index 000000000..6ff1b3bf0 --- /dev/null +++ b/scripts/amap_mariadb_insert.py @@ -0,0 +1,136 @@ +#!/usr/bin/env python3 + +from datetime import datetime +import argparse +import mariadb +import sys +import os + + +def parseArgs(): + parser = argparse.ArgumentParser() + parser.add_argument("db_user", help="MariaDB user") + parser.add_argument("db_pass", help="MariaDB password") + parser.add_argument("db_host", help="MariaDB hostname") + parser.add_argument("db_port", type=int, help="MariaDB port") + parser.add_argument("db_name", help="MariaDB database") + parser.add_argument("report_file", help="Report file(.map.all)") + args = parser.parse_args() + return args + + +def mariadbConnect(args): + try: + conn = mariadb.connect( + user=args.db_user, + password=args.db_pass, + host=args.db_host, + port=args.db_port, + database=args.db_name, + ) + except mariadb.Error as e: + print(f"Error connecting to MariaDB: {e}") + sys.exit(1) + return conn + + +def parseEnv(): + outArr = [] + outArr.append(datetime.now().strftime("%Y-%m-%d %H:%M:%S")) + outArr.append(os.getenv("COMMIT_HASH", default=None)) + outArr.append(os.getenv("COMMIT_MSG", default=None)) + outArr.append(os.getenv("BRANCH_NAME", default=None)) + outArr.append(os.getenv("BSS_SIZE", default=None)) + outArr.append(os.getenv("TEXT_SIZE", default=None)) + outArr.append(os.getenv("RODATA_SIZE", default=None)) + outArr.append(os.getenv("DATA_SIZE", default=None)) + outArr.append(os.getenv("FREE_FLASH_SIZE", default=None)) + outArr.append(os.getenv("PULL_ID", default=None)) + outArr.append(os.getenv("PULL_NAME", default=None)) + return outArr + + +def createTables(cur, conn): + headerTable = "CREATE TABLE IF NOT EXISTS `header` ( \ + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, \ + `datetime` datetime NOT NULL, \ + `commit` varchar(40) NOT NULL, \ + `commit_msg` text NOT NULL, \ + `branch_name` text NOT NULL, \ + `bss_size` int(10) unsigned NOT NULL, \ + `text_size` int(10) unsigned NOT NULL, \ + `rodata_size` int(10) unsigned NOT NULL, \ + `data_size` int(10) unsigned NOT NULL, \ + `free_flash_size` int(10) unsigned NOT NULL, \ + `pullrequest_id` int(10) unsigned DEFAULT NULL, \ + `pullrequest_name` text DEFAULT NULL, \ + PRIMARY KEY (`id`), \ + KEY `header_id_index` (`id`) )" + dataTable = "CREATE TABLE IF NOT EXISTS `data` ( \ + `header_id` int(10) unsigned NOT NULL, \ + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, \ + `section` text NOT NULL, \ + `address` text NOT NULL, \ + `size` int(10) unsigned NOT NULL, \ + `name` text NOT NULL, \ + `lib` text NOT NULL, \ + `obj_name` text NOT NULL, \ + PRIMARY KEY (`id`), \ + KEY `data_id_index` (`id`), \ + KEY `data_header_id_index` (`header_id`), \ + CONSTRAINT `data_header_id_foreign` FOREIGN KEY (`header_id`) REFERENCES `header` (`id`) )" + cur.execute(headerTable) + cur.execute(dataTable) + conn.commit() + + +def insertHeader(data, cur, conn): + query = "INSERT INTO `header` ( \ + datetime, commit, commit_msg, branch_name, bss_size, text_size, \ + rodata_size, data_size, free_flash_size, pullrequest_id, pullrequest_name) \ + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" + cur.execute(query, data) + conn.commit() + return cur.lastrowid + + +def parseFile(fileObj, headerID): + arr = [] + fileLines = fileObj.readlines() + for line in fileLines: + lineArr = [] + tempLineArr = line.split("\t") + lineArr.append(headerID) + lineArr.append(tempLineArr[0]) # section + lineArr.append(int(tempLineArr[2], 16)) # address hex + lineArr.append(int(tempLineArr[3])) # size + lineArr.append(tempLineArr[4]) # name + lineArr.append(tempLineArr[5]) # lib + lineArr.append(tempLineArr[6]) # obj_name + arr.append(tuple(lineArr)) + return arr + + +def insertData(data, cur, conn): + query = "INSERT INTO `data` ( \ + header_id, section, address, size, \ + name, lib, obj_name) \ + VALUES (?, ?, ?, ?, ? ,?, ?)" + cur.executemany(query, data) + conn.commit() + + +def main(): + args = parseArgs() + dbConn = mariadbConnect(args) + reportFile = open(args.report_file) + dbCurs = dbConn.cursor() + createTables(dbCurs, dbConn) + headerID = insertHeader(parseEnv(), dbCurs, dbConn) + insertData(parseFile(reportFile, headerID), dbCurs, dbConn) + reportFile.close() + dbCurs.close() + + +if __name__ == "__main__": + main() diff --git a/scripts/slideshow.py b/scripts/slideshow.py index 7626a6aca..8a9541a7c 100644 --- a/scripts/slideshow.py +++ b/scripts/slideshow.py @@ -36,7 +36,7 @@ class Main(App): file_idx += 1 except Exception as e: self.logger.error(e) - break + return 3 widths = set(img.width for img in images) heights = set(img.height for img in images) diff --git a/scripts/toolchain/fbtenv.cmd b/scripts/toolchain/fbtenv.cmd index aac2a3309..f955a4db3 100644 --- a/scripts/toolchain/fbtenv.cmd +++ b/scripts/toolchain/fbtenv.cmd @@ -13,7 +13,7 @@ if not [%FBT_NOENV%] == [] ( exit /b 0 ) -set "FLIPPER_TOOLCHAIN_VERSION=8" +set "FLIPPER_TOOLCHAIN_VERSION=9" set "FBT_TOOLCHAIN_ROOT=%FBT_ROOT%\toolchain\i686-windows" diff --git a/scripts/toolchain/fbtenv.sh b/scripts/toolchain/fbtenv.sh index c12bc0d48..0ef81114a 100755 --- a/scripts/toolchain/fbtenv.sh +++ b/scripts/toolchain/fbtenv.sh @@ -8,23 +8,61 @@ SCRIPT_PATH="${SCRIPT_PATH:-$DEFAULT_SCRIPT_PATH}"; FBT_TOOLCHAIN_VERSION="${FBT_TOOLCHAIN_VERSION:-"8"}"; FBT_TOOLCHAIN_PATH="${FBT_TOOLCHAIN_PATH:-$SCRIPT_PATH}"; +fbtenv_show_usage() +{ + echo "Running this script manually is wrong, please source it"; + echo "Example:"; + printf "\tsource scripts/toolchain/fbtenv.sh\n"; +} + +fbtenv_curl() +{ + curl --progress-bar -SLo "$1" "$2"; +} + +fbtenv_wget() +{ + wget --show-progress --progress=bar:force -qO "$1" "$2"; +} + fbtenv_check_sourced() { case "${ZSH_EVAL_CONTEXT:-""}" in *:file:*) return 0;; esac - case ${0##*/} in dash|-dash|bash|-bash|ksh|-ksh|sh|-sh) + if [ ${0##*/} = "fbtenv.sh" ]; then # exluding script itself + fbtenv_show_usage; + return 1; + fi + case ${0##*/} in dash|-dash|bash|-bash|ksh|-ksh|sh|-sh|*.sh|fbt) return 0;; esac - if [ "$(basename $0)" = "fbt" ]; then - return 0; - fi - echo "Running this script manually is wrong, please source it"; - echo "Example:"; - printf "\tsource scripts/toolchain/fbtenv.sh\n"; + fbtenv_show_usage; return 1; } +fbtenv_chck_many_source() +{ + if ! echo "${PS1:-""}" | grep -q "[fbt]"; then + if ! echo "${PROMPT:-""}" | grep -q "[fbt]"; then + return 0; + fi + fi + echo "Warning! It script seen to be sourced more then once!"; + echo "It may signalise what you are making some mistakes, please open a new shell!"; + return 1; +} + +fbtenv_set_shell_prompt() +{ + if [ -n "${PS1:-""}" ]; then + PS1="[fbt]$PS1"; + elif [ -n "${PROMPT:-""}" ]; then + PROMPT="[fbt]$PROMPT"; + fi + return 0; # all other shells +} + fbtenv_check_script_path() { if [ ! -x "$SCRIPT_PATH/fbt" ]; then @@ -110,7 +148,7 @@ fbtenv_download_toolchain_tar() { echo "Downloading toolchain:"; mkdir -p "$FBT_TOOLCHAIN_PATH/toolchain" || return 1; - "$DOWNLOADER" $DOWNLOADER_ARGS "$FBT_TOOLCHAIN_PATH/toolchain/$TOOLCHAIN_TAR" "$TOOLCHAIN_URL" || return 1; + "$FBT_DOWNLOADER" "$FBT_TOOLCHAIN_PATH/toolchain/$TOOLCHAIN_TAR" "$TOOLCHAIN_URL" || return 1; echo "done"; return 0; } @@ -169,13 +207,11 @@ fbtenv_curl_wget_check() return 1; fi echo "yes" - DOWNLOADER="wget"; - DOWNLOADER_ARGS="--show-progress --progress=bar:force -qO"; + FBT_DOWNLOADER="fbtenv_wget"; return 0; fi echo "yes" - DOWNLOADER="curl"; - DOWNLOADER_ARGS="--progress-bar -SLo"; + FBT_DOWNLOADER="fbtenv_curl"; return 0; } @@ -209,6 +245,8 @@ fbtenv_download_toolchain() fbtenv_main() { fbtenv_check_sourced || return 1; + fbtenv_chck_many_source; # many source it's just a warning + fbtenv_set_shell_prompt; fbtenv_check_script_path || return 1; fbtenv_get_kernel_type || return 1; fbtenv_check_download_toolchain || return 1;