From 0bc400a3ae80c4369f19f3f08a1a71f7746265ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Mon, 10 Jun 2024 18:04:29 +0100 Subject: [PATCH 1/3] Code Cleanup: unused includes, useless checks, unused variables, etc... (#3696) * Remove unnecessary checks * Sublime: never insert missing headers * Cleanup furi defines use * Cleanup startup. Cleanup linker scripts. Explicitly define all interrupts hadlers, including uninmplemented one. * Startup routine in C * Drop assembler startup * Move linker defines to stm32wb55_linker.h, cleanup naming, unify usage. Mpu: protect last 32b of main stack. Document various obscure things. * Move furi_hal_switch documentation to appropriate place, use 0x0 for updater jump. * UnitTests: move all temporary test files into tmp folder --------- Co-authored-by: SG --- .sublime-project | 2 +- applications/debug/blink_test/blink_test.c | 1 - applications/debug/ccid_test/ccid_test_app.c | 1 - applications/debug/keypad_test/keypad_test.c | 7 - .../text_box_element_test.c | 8 - applications/debug/unit_tests/test_runner.c | 13 + .../flipper_format/flipper_format_test.c | 2 +- .../flipper_format_string_test.c | 3 +- .../debug/unit_tests/tests/rpc/rpc_test.c | 2 +- .../unit_tests/tests/stream/stream_test.c | 29 +- applications/debug/usb_mouse/usb_mouse.c | 1 - applications/main/nfc/helpers/mf_user_dict.c | 2 - .../nfc/scenes/nfc_scene_mf_classic_keys.c | 12 +- .../scenes/nfc_scene_mf_classic_keys_add.c | 1 - applications/main/subghz/subghz_cli.c | 2 - applications/services/cli/cli.c | 1 - applications/services/dolphin/dolphin.c | 2 +- applications/services/gui/modules/popup.c | 1 - applications/services/gui/view.c | 1 - .../settings/dolphin_passport/passport.c | 2 - applications/system/snake_game/snake_game.c | 6 - furi/core/common_defines.h | 8 + furi/core/memmgr_heap.c | 3 +- furi/core/message_queue.h | 2 +- furi/core/pubsub.c | 1 - furi/core/record.c | 1 - furi/core/timer.h | 2 +- scripts/fbt_tools/fbt_hwtarget.py | 4 +- targets/f7/application_ext.ld | 81 ++-- targets/f7/furi_hal/furi_hal.c | 9 +- targets/f7/furi_hal/furi_hal_flash.c | 4 +- targets/f7/furi_hal/furi_hal_interrupt.c | 26 +- targets/f7/furi_hal/furi_hal_mpu.c | 8 +- targets/f7/inc/stm32wb55_linker.h | 34 ++ targets/f7/inc/stm32wb55_startup.h | 94 ++++ targets/f7/src/stm32wb55_startup.c | 199 ++++++++ targets/f7/src/system_stm32wbxx.c | 97 ---- targets/f7/src/update.c | 2 +- targets/f7/startup_stm32wb55xx_cm4.s | 444 ------------------ targets/f7/stm32wb55xx_flash.ld | 304 +++++------- targets/f7/stm32wb55xx_ram_fw.ld | 300 +++++------- targets/f7/target.json | 1 - targets/furi_hal_include/furi_hal.h | 10 +- targets/furi_hal_include/furi_hal_mpu.h | 5 +- 44 files changed, 687 insertions(+), 1051 deletions(-) create mode 100644 targets/f7/inc/stm32wb55_linker.h create mode 100644 targets/f7/inc/stm32wb55_startup.h create mode 100644 targets/f7/src/stm32wb55_startup.c delete mode 100644 targets/f7/src/system_stm32wbxx.c delete mode 100644 targets/f7/startup_stm32wb55xx_cm4.s diff --git a/.sublime-project b/.sublime-project index 4912c9974..5d751aaf6 100644 --- a/.sublime-project +++ b/.sublime-project @@ -10,7 +10,7 @@ "clangd": { "initializationOptions": { "clangd.compile-commands-dir": "build/latest", - "clangd.header-insertion": null, + "clangd.header-insertion": "never", "clangd.query-driver": "**", "clangd.clang-tidy": true, }, diff --git a/applications/debug/blink_test/blink_test.c b/applications/debug/blink_test/blink_test.c index 7dd2c9e96..638878da2 100644 --- a/applications/debug/blink_test/blink_test.c +++ b/applications/debug/blink_test/blink_test.c @@ -1,4 +1,3 @@ -#include #include #include diff --git a/applications/debug/ccid_test/ccid_test_app.c b/applications/debug/ccid_test/ccid_test_app.c index be7f7f9e6..6993901d2 100644 --- a/applications/debug/ccid_test/ccid_test_app.c +++ b/applications/debug/ccid_test/ccid_test_app.c @@ -72,7 +72,6 @@ CcidTestApp* ccid_test_app_alloc(void) { //message queue app->event_queue = furi_message_queue_alloc(8, sizeof(CcidTestAppEvent)); - furi_check(app->event_queue); view_port_input_callback_set(app->view_port, ccid_test_app_input_callback, app->event_queue); return app; diff --git a/applications/debug/keypad_test/keypad_test.c b/applications/debug/keypad_test/keypad_test.c index 9e8881def..bc4036120 100644 --- a/applications/debug/keypad_test/keypad_test.c +++ b/applications/debug/keypad_test/keypad_test.c @@ -64,16 +64,9 @@ static void keypad_test_input_callback(InputEvent* input_event, void* ctx) { int32_t keypad_test_app(void* p) { UNUSED(p); FuriMessageQueue* event_queue = furi_message_queue_alloc(32, sizeof(InputEvent)); - furi_check(event_queue); - KeypadTestState state = {{false, false, false, false, false}, 0, 0, 0, 0, 0, NULL}; state.mutex = furi_mutex_alloc(FuriMutexTypeNormal); - if(!state.mutex) { - FURI_LOG_E(TAG, "cannot create mutex"); - return 0; - } - ViewPort* view_port = view_port_alloc(); view_port_draw_callback_set(view_port, keypad_test_render_callback, &state); diff --git a/applications/debug/text_box_element_test/text_box_element_test.c b/applications/debug/text_box_element_test/text_box_element_test.c index 2b9475d2b..8002f14b6 100644 --- a/applications/debug/text_box_element_test/text_box_element_test.c +++ b/applications/debug/text_box_element_test/text_box_element_test.c @@ -74,16 +74,8 @@ static void text_box_test_input_callback(InputEvent* input_event, void* ctx) { int32_t text_box_element_test_app(void* p) { UNUSED(p); FuriMessageQueue* event_queue = furi_message_queue_alloc(32, sizeof(InputEvent)); - furi_check(event_queue); - TextBoxTestState state = {.idx = 0, .mutex = NULL}; state.mutex = furi_mutex_alloc(FuriMutexTypeNormal); - - if(!state.mutex) { - FURI_LOG_E(TAG, "Cannot create mutex"); - return 0; - } - ViewPort* view_port = view_port_alloc(); view_port_draw_callback_set(view_port, text_box_test_render_callback, &state); diff --git a/applications/debug/unit_tests/test_runner.c b/applications/debug/unit_tests/test_runner.c index 6af807086..517a4fd24 100644 --- a/applications/debug/unit_tests/test_runner.c +++ b/applications/debug/unit_tests/test_runner.c @@ -72,6 +72,9 @@ void test_runner_free(TestRunner* instance) { free(instance); } +#define TEST_RUNNER_TMP_DIR EXT_PATH(".tmp") +#define TEST_RUNNER_TMP_UNIT_TESTS_DIR TEST_RUNNER_TMP_DIR "/unit_tests" + static bool test_runner_run_plugin(TestRunner* instance, const char* path) { furi_assert(instance); @@ -128,6 +131,16 @@ static void test_runner_run_internal(TestRunner* instance) { File* directory = storage_file_alloc(instance->storage); do { + if(!storage_simply_mkdir(instance->storage, TEST_RUNNER_TMP_DIR)) { + FURI_LOG_E(TAG, "Cannot create dir %s", TEST_RUNNER_TMP_DIR); + break; + } + + if(!storage_simply_mkdir(instance->storage, TEST_RUNNER_TMP_UNIT_TESTS_DIR)) { + FURI_LOG_E(TAG, "Cannot create dir %s", TEST_RUNNER_TMP_UNIT_TESTS_DIR); + break; + } + if(!storage_dir_open(directory, PLUGINS_PATH)) { FURI_LOG_E(TAG, "Failed to open directory %s", PLUGINS_PATH); break; diff --git a/applications/debug/unit_tests/tests/flipper_format/flipper_format_test.c b/applications/debug/unit_tests/tests/flipper_format/flipper_format_test.c index 03a9e5ab0..d26acf577 100644 --- a/applications/debug/unit_tests/tests/flipper_format/flipper_format_test.c +++ b/applications/debug/unit_tests/tests/flipper_format/flipper_format_test.c @@ -4,8 +4,8 @@ #include #include "../test.h" // IWYU pragma: keep +#define TEST_DIR_NAME EXT_PATH(".tmp/unit_tests/ff") #define TEST_DIR TEST_DIR_NAME "/" -#define TEST_DIR_NAME EXT_PATH("unit_tests_tmp") static const char* test_filetype = "Flipper File test"; static const uint32_t test_version = 666; diff --git a/applications/debug/unit_tests/tests/flipper_format_string/flipper_format_string_test.c b/applications/debug/unit_tests/tests/flipper_format_string/flipper_format_string_test.c index a6e1b7700..de6964cbe 100644 --- a/applications/debug/unit_tests/tests/flipper_format_string/flipper_format_string_test.c +++ b/applications/debug/unit_tests/tests/flipper_format_string/flipper_format_string_test.c @@ -298,7 +298,8 @@ MU_TEST(flipper_format_string_test) { MU_TEST(flipper_format_file_test) { Storage* storage = furi_record_open(RECORD_STORAGE); FlipperFormat* flipper_format = flipper_format_file_alloc(storage); - mu_check(flipper_format_file_open_always(flipper_format, EXT_PATH("flipper.fff"))); + mu_check( + flipper_format_file_open_always(flipper_format, EXT_PATH(".tmp/unit_tests/flipper.fff"))); Stream* stream = flipper_format_get_raw_stream(flipper_format); mu_check(flipper_format_write_header_cstr(flipper_format, test_filetype, test_version)); diff --git a/applications/debug/unit_tests/tests/rpc/rpc_test.c b/applications/debug/unit_tests/tests/rpc/rpc_test.c index 5bc2cc1f8..f5b9e762d 100644 --- a/applications/debug/unit_tests/tests/rpc/rpc_test.c +++ b/applications/debug/unit_tests/tests/rpc/rpc_test.c @@ -46,8 +46,8 @@ static RpcSessionContext rpc_session[TEST_RPC_SESSIONS]; #define MAX_RECEIVE_OUTPUT_TIMEOUT 3000 #define MAX_NAME_LENGTH 255 #define MAX_DATA_SIZE 512u // have to be exact as in rpc_storage.c +#define TEST_DIR_NAME EXT_PATH(".tmp/unit_tests/rpc") #define TEST_DIR TEST_DIR_NAME "/" -#define TEST_DIR_NAME EXT_PATH("unit_tests_tmp") #define MD5SUM_SIZE 16 #define PING_REQUEST 0 diff --git a/applications/debug/unit_tests/tests/stream/stream_test.c b/applications/debug/unit_tests/tests/stream/stream_test.c index ad41c08ec..4f1ba2fd7 100644 --- a/applications/debug/unit_tests/tests/stream/stream_test.c +++ b/applications/debug/unit_tests/tests/stream/stream_test.c @@ -15,6 +15,8 @@ static const char* stream_test_left_data = "There are two cardinal human sins "; static const char* stream_test_right_data = "from which all others derive: impatience and indolence."; +#define FILESTREAM_PATH EXT_PATH(".tmp/unit_tests/filestream.str") + MU_TEST_1(stream_composite_subtest, Stream* stream) { const size_t data_size = 128; uint8_t data[data_size]; @@ -304,15 +306,14 @@ MU_TEST(stream_composite_test) { // test file stream Storage* storage = furi_record_open(RECORD_STORAGE); stream = file_stream_alloc(storage); - mu_check( - file_stream_open(stream, EXT_PATH("filestream.str"), FSAM_READ_WRITE, FSOM_CREATE_ALWAYS)); + mu_check(file_stream_open(stream, FILESTREAM_PATH, FSAM_READ_WRITE, FSOM_CREATE_ALWAYS)); MU_RUN_TEST_1(stream_composite_subtest, stream); stream_free(stream); // test buffered file stream stream = buffered_file_stream_alloc(storage); - mu_check(buffered_file_stream_open( - stream, EXT_PATH("filestream.str"), FSAM_READ_WRITE, FSOM_CREATE_ALWAYS)); + mu_check( + buffered_file_stream_open(stream, FILESTREAM_PATH, FSAM_READ_WRITE, FSOM_CREATE_ALWAYS)); MU_RUN_TEST_1(stream_composite_subtest, stream); stream_free(stream); furi_record_close(RECORD_STORAGE); @@ -346,7 +347,7 @@ MU_TEST(stream_write_read_save_load_test) { mu_check(stream_seek(stream_orig, 0, StreamOffsetFromStart)); mu_assert_int_eq( strlen(stream_test_data), - stream_save_to_file(stream_orig, storage, EXT_PATH("filestream.str"), FSOM_CREATE_ALWAYS)); + stream_save_to_file(stream_orig, storage, FILESTREAM_PATH, FSOM_CREATE_ALWAYS)); stream_free(stream_copy); stream_free(stream_orig); @@ -354,8 +355,7 @@ MU_TEST(stream_write_read_save_load_test) { // load from file, read Stream* stream_new = string_stream_alloc(); mu_assert_int_eq( - strlen(stream_test_data), - stream_load_from_file(stream_new, storage, EXT_PATH("filestream.str"))); + strlen(stream_test_data), stream_load_from_file(stream_new, storage, FILESTREAM_PATH)); MU_RUN_TEST_1(stream_read_subtest, stream_new); stream_free(stream_new); @@ -396,15 +396,14 @@ MU_TEST(stream_split_test) { // test file stream Storage* storage = furi_record_open(RECORD_STORAGE); stream = file_stream_alloc(storage); - mu_check( - file_stream_open(stream, EXT_PATH("filestream.str"), FSAM_READ_WRITE, FSOM_CREATE_ALWAYS)); + mu_check(file_stream_open(stream, FILESTREAM_PATH, FSAM_READ_WRITE, FSOM_CREATE_ALWAYS)); MU_RUN_TEST_1(stream_split_subtest, stream); stream_free(stream); // test buffered stream stream = buffered_file_stream_alloc(storage); - mu_check(buffered_file_stream_open( - stream, EXT_PATH("filestream.str"), FSAM_READ_WRITE, FSOM_CREATE_ALWAYS)); + mu_check( + buffered_file_stream_open(stream, FILESTREAM_PATH, FSAM_READ_WRITE, FSOM_CREATE_ALWAYS)); MU_RUN_TEST_1(stream_split_subtest, stream); stream_free(stream); @@ -424,8 +423,8 @@ MU_TEST(stream_buffered_write_after_read_test) { Storage* storage = furi_record_open(RECORD_STORAGE); Stream* stream = buffered_file_stream_alloc(storage); - mu_check(buffered_file_stream_open( - stream, EXT_PATH("filestream.str"), FSAM_READ_WRITE, FSOM_CREATE_ALWAYS)); + mu_check( + buffered_file_stream_open(stream, FILESTREAM_PATH, FSAM_READ_WRITE, FSOM_CREATE_ALWAYS)); mu_assert_int_eq(strlen(stream_test_data), stream_write_cstring(stream, stream_test_data)); mu_check(stream_rewind(stream)); mu_assert_int_eq(prefix_len, stream_read(stream, (uint8_t*)buf, prefix_len)); @@ -458,8 +457,8 @@ MU_TEST(stream_buffered_large_file_test) { // write test data to file Stream* stream = buffered_file_stream_alloc(storage); - mu_check(buffered_file_stream_open( - stream, EXT_PATH("filestream.str"), FSAM_READ_WRITE, FSOM_CREATE_ALWAYS)); + mu_check( + buffered_file_stream_open(stream, FILESTREAM_PATH, FSAM_READ_WRITE, FSOM_CREATE_ALWAYS)); mu_assert_int_eq(0, stream_size(stream)); mu_assert_int_eq(furi_string_size(input_data), stream_write_string(stream, input_data)); mu_assert_int_eq(furi_string_size(input_data), stream_size(stream)); diff --git a/applications/debug/usb_mouse/usb_mouse.c b/applications/debug/usb_mouse/usb_mouse.c index 6e716e69a..2b7710451 100644 --- a/applications/debug/usb_mouse/usb_mouse.c +++ b/applications/debug/usb_mouse/usb_mouse.c @@ -40,7 +40,6 @@ static void usb_mouse_input_callback(InputEvent* input_event, void* ctx) { int32_t usb_mouse_app(void* p) { UNUSED(p); FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(UsbMouseEvent)); - furi_check(event_queue); ViewPort* view_port = view_port_alloc(); FuriHalUsbInterface* usb_mode_prev = furi_hal_usb_get_config(); diff --git a/applications/main/nfc/helpers/mf_user_dict.c b/applications/main/nfc/helpers/mf_user_dict.c index 1a019cf59..9410c8537 100644 --- a/applications/main/nfc/helpers/mf_user_dict.c +++ b/applications/main/nfc/helpers/mf_user_dict.c @@ -17,7 +17,6 @@ MfUserDict* mf_user_dict_alloc(size_t max_keys_to_load) { KeysDict* dict = keys_dict_alloc( NFC_APP_MF_CLASSIC_DICT_USER_PATH, KeysDictModeOpenAlways, sizeof(MfClassicKey)); - furi_assert(dict); size_t dict_keys_num = keys_dict_get_total_keys(dict); instance->keys_num = MIN(max_keys_to_load, dict_keys_num); @@ -69,7 +68,6 @@ bool mf_user_dict_delete_key(MfUserDict* instance, uint32_t index) { KeysDict* dict = keys_dict_alloc( NFC_APP_MF_CLASSIC_DICT_USER_PATH, KeysDictModeOpenAlways, sizeof(MfClassicKey)); - furi_assert(dict); bool key_delete_success = keys_dict_delete_key(dict, instance->keys_arr[index].data, sizeof(MfClassicKey)); diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys.c index 44f9963af..eaa054149 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys.c @@ -16,19 +16,15 @@ void nfc_scene_mf_classic_keys_on_enter(void* context) { uint32_t flipper_dict_keys_total = 0; KeysDict* dict = keys_dict_alloc( NFC_APP_MF_CLASSIC_DICT_SYSTEM_PATH, KeysDictModeOpenExisting, sizeof(MfClassicKey)); - if(dict) { - flipper_dict_keys_total = keys_dict_get_total_keys(dict); - keys_dict_free(dict); - } + flipper_dict_keys_total = keys_dict_get_total_keys(dict); + keys_dict_free(dict); // Load user dict keys total uint32_t user_dict_keys_total = 0; dict = keys_dict_alloc( NFC_APP_MF_CLASSIC_DICT_USER_PATH, KeysDictModeOpenAlways, sizeof(MfClassicKey)); - if(dict) { - user_dict_keys_total = keys_dict_get_total_keys(dict); - keys_dict_free(dict); - } + user_dict_keys_total = keys_dict_get_total_keys(dict); + keys_dict_free(dict); FuriString* temp_str = furi_string_alloc(); widget_add_string_element( diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_add.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_add.c index 4111cca81..a963f44ac 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_add.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_add.c @@ -31,7 +31,6 @@ bool nfc_scene_mf_classic_keys_add_on_event(void* context, SceneManagerEvent eve // Add key to dict KeysDict* dict = keys_dict_alloc( NFC_APP_MF_CLASSIC_DICT_USER_PATH, KeysDictModeOpenAlways, sizeof(MfClassicKey)); - furi_assert(dict); MfClassicKey key = {}; memcpy(key.data, instance->byte_input_store, sizeof(MfClassicKey)); diff --git a/applications/main/subghz/subghz_cli.c b/applications/main/subghz/subghz_cli.c index 679dafaf1..b96a22db6 100644 --- a/applications/main/subghz/subghz_cli.c +++ b/applications/main/subghz/subghz_cli.c @@ -344,7 +344,6 @@ void subghz_cli_command_rx(Cli* cli, FuriString* args, void* context) { SubGhzCliCommandRx* instance = malloc(sizeof(SubGhzCliCommandRx)); instance->stream = furi_stream_buffer_alloc(sizeof(LevelDuration) * 1024, sizeof(LevelDuration)); - furi_check(instance->stream); SubGhzEnvironment* environment = subghz_cli_environment_init(); @@ -425,7 +424,6 @@ void subghz_cli_command_rx_raw(Cli* cli, FuriString* args, void* context) { SubGhzCliCommandRx* instance = malloc(sizeof(SubGhzCliCommandRx)); instance->stream = furi_stream_buffer_alloc(sizeof(LevelDuration) * 1024, sizeof(LevelDuration)); - furi_check(instance->stream); // Configure radio furi_hal_subghz_reset(); diff --git a/applications/services/cli/cli.c b/applications/services/cli/cli.c index 7ba704219..709d69768 100644 --- a/applications/services/cli/cli.c +++ b/applications/services/cli/cli.c @@ -19,7 +19,6 @@ Cli* cli_alloc(void) { cli->session = NULL; cli->mutex = furi_mutex_alloc(FuriMutexTypeNormal); - furi_check(cli->mutex); cli->idle_sem = furi_semaphore_alloc(1, 0); diff --git a/applications/services/dolphin/dolphin.c b/applications/services/dolphin/dolphin.c index 28c17ec16..4a75241e6 100644 --- a/applications/services/dolphin/dolphin.c +++ b/applications/services/dolphin/dolphin.c @@ -99,8 +99,8 @@ void dolphin_event_send_async(Dolphin* dolphin, DolphinEvent* event) { void dolphin_event_send_wait(Dolphin* dolphin, DolphinEvent* event) { furi_assert(dolphin); furi_assert(event); + event->flag = furi_event_flag_alloc(); - furi_check(event->flag); furi_check( furi_message_queue_put(dolphin->event_queue, event, FuriWaitForever) == FuriStatusOk); furi_check( diff --git a/applications/services/gui/modules/popup.c b/applications/services/gui/modules/popup.c index 45014f6f4..5c9c75e4a 100644 --- a/applications/services/gui/modules/popup.c +++ b/applications/services/gui/modules/popup.c @@ -112,7 +112,6 @@ Popup* popup_alloc(void) { Popup* popup = malloc(sizeof(Popup)); popup->view = view_alloc(); popup->timer = furi_timer_alloc(popup_timer_callback, FuriTimerTypeOnce, popup); - furi_assert(popup->timer); popup->timer_period_in_ms = 1000; popup->timer_enabled = false; diff --git a/applications/services/gui/view.c b/applications/services/gui/view.c index a35c2fa38..51d023543 100644 --- a/applications/services/gui/view.c +++ b/applications/services/gui/view.c @@ -78,7 +78,6 @@ void view_allocate_model(View* view, ViewModelType type, size_t size) { } else if(view->model_type == ViewModelTypeLocking) { ViewModelLocking* model = malloc(sizeof(ViewModelLocking)); model->mutex = furi_mutex_alloc(FuriMutexTypeRecursive); - furi_check(model->mutex); model->data = malloc(size); view->model = model; } else { diff --git a/applications/settings/dolphin_passport/passport.c b/applications/settings/dolphin_passport/passport.c index 0f3008a36..aae53cd2a 100644 --- a/applications/settings/dolphin_passport/passport.c +++ b/applications/settings/dolphin_passport/passport.c @@ -90,8 +90,6 @@ static void render_callback(Canvas* canvas, void* ctx) { int32_t passport_app(void* p) { UNUSED(p); FuriSemaphore* semaphore = furi_semaphore_alloc(1, 0); - furi_assert(semaphore); - ViewPort* view_port = view_port_alloc(); Dolphin* dolphin = furi_record_open(RECORD_DOLPHIN); diff --git a/applications/system/snake_game/snake_game.c b/applications/system/snake_game/snake_game.c index a7765f945..aee9fd16d 100644 --- a/applications/system/snake_game/snake_game.c +++ b/applications/system/snake_game/snake_game.c @@ -327,12 +327,6 @@ int32_t snake_game_app(void* p) { snake_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); - if(!snake_state->mutex) { - FURI_LOG_E("SnakeGame", "cannot create mutex\r\n"); - free(snake_state); - return 255; - } - ViewPort* view_port = view_port_alloc(); view_port_draw_callback_set(view_port, snake_game_render_callback, snake_state); view_port_input_callback_set(view_port, snake_game_input_callback, event_queue); diff --git a/furi/core/common_defines.h b/furi/core/common_defines.h index beb9f6519..0f6230c19 100644 --- a/furi/core/common_defines.h +++ b/furi/core/common_defines.h @@ -67,6 +67,14 @@ void __furi_critical_exit(__FuriCriticalInfo info); #define FURI_CHECK_RETURN __attribute__((__warn_unused_result__)) #endif +#ifndef FURI_NAKED +#define FURI_NAKED __attribute__((naked)) +#endif + +#ifndef FURI_DEFAULT +#define FURI_DEFAULT(x) __attribute__((weak, alias(x))) +#endif + #ifdef __cplusplus } #endif diff --git a/furi/core/memmgr_heap.c b/furi/core/memmgr_heap.c index 3a0e2c378..3827ddde3 100644 --- a/furi/core/memmgr_heap.c +++ b/furi/core/memmgr_heap.c @@ -39,6 +39,7 @@ #include #include #include +#include #include #include @@ -63,8 +64,6 @@ task.h is included from an application file. */ #define heapBITS_PER_BYTE ((size_t)8) /* Heap start end symbols provided by linker */ -extern const void __heap_start__; -extern const void __heap_end__; uint8_t* ucHeap = (uint8_t*)&__heap_start__; /* Define the linked list structure. This is used to link free blocks in order diff --git a/furi/core/message_queue.h b/furi/core/message_queue.h index a055a05f1..4c5b5af3c 100644 --- a/furi/core/message_queue.h +++ b/furi/core/message_queue.h @@ -4,7 +4,7 @@ */ #pragma once -#include "core/base.h" +#include "base.h" #ifdef __cplusplus extern "C" { diff --git a/furi/core/pubsub.c b/furi/core/pubsub.c index 0fdd5d02d..7c1730632 100644 --- a/furi/core/pubsub.c +++ b/furi/core/pubsub.c @@ -20,7 +20,6 @@ FuriPubSub* furi_pubsub_alloc(void) { FuriPubSub* pubsub = malloc(sizeof(FuriPubSub)); pubsub->mutex = furi_mutex_alloc(FuriMutexTypeNormal); - furi_assert(pubsub->mutex); FuriPubSubSubscriptionList_init(pubsub->items); diff --git a/furi/core/record.c b/furi/core/record.c index 6c44df846..fa384369a 100644 --- a/furi/core/record.c +++ b/furi/core/record.c @@ -39,7 +39,6 @@ static void furi_record_erase(const char* name, FuriRecordData* record_data) { void furi_record_init(void) { furi_record = malloc(sizeof(FuriRecord)); furi_record->mutex = furi_mutex_alloc(FuriMutexTypeNormal); - furi_check(furi_record->mutex); FuriRecordDataDict_init(furi_record->records); } diff --git a/furi/core/timer.h b/furi/core/timer.h index 2f55001f7..00056db18 100644 --- a/furi/core/timer.h +++ b/furi/core/timer.h @@ -4,7 +4,7 @@ */ #pragma once -#include "core/base.h" +#include "base.h" #ifdef __cplusplus extern "C" { diff --git a/scripts/fbt_tools/fbt_hwtarget.py b/scripts/fbt_tools/fbt_hwtarget.py index a3b0d4a78..cce247098 100644 --- a/scripts/fbt_tools/fbt_hwtarget.py +++ b/scripts/fbt_tools/fbt_hwtarget.py @@ -76,7 +76,9 @@ class HardwareTargetLoader: self._processTargetDefinitions(inherited_target) def gatherSources(self): - sources = [self.startup_script] + sources = [] + if self.startup_script: + sources.append(self.startup_script) seen_filenames = set(self.excluded_sources) # print("Layers: ", self.layered_target_dirs) for target_dir in self.layered_target_dirs: diff --git a/targets/f7/application_ext.ld b/targets/f7/application_ext.ld index 01bb021b6..b6496290a 100644 --- a/targets/f7/application_ext.ld +++ b/targets/f7/application_ext.ld @@ -1,54 +1,47 @@ FORCE_COMMON_ALLOCATION -SECTIONS -{ - .text 0x00000000 : ALIGN(4) - { - *(.text) - *(.stub) - *(.text*) - *(.text.*) - *(.text._*) +SECTIONS { + .text 0x00000000 : ALIGN(4) { + *(.text) + *(.stub) + *(.text*) + *(.text.*) + *(.text._*) - KEEP (*(.init)) - KEEP (*(.fini)) - } + KEEP (*(.init)) + KEEP (*(.fini)) + } - .rodata : - { - *(.rodata) - *(.rodata1) - *(.rodata.*) - } + .rodata : { + *(.rodata) + *(.rodata1) + *(.rodata.*) + } - .data : - { - *(.data) - *(.data1) - *(.data.*) - } + .data : { + *(.data) + *(.data1) + *(.data.*) + } - .bss : - { - *(.bss) - *(.bss*) - *(.sbss) - *(.sbss*) - *(COMMON) - } + .bss : { + *(.bss) + *(.bss*) + *(.sbss) + *(.sbss*) + *(COMMON) + } - .ARM.attributes : - { - *(.ARM.attributes) - *(.ARM.attributes.*) - } + .ARM.attributes : { + *(.ARM.attributes) + *(.ARM.attributes.*) + } - /DISCARD/ : - { - *(.comment) - *(.comment.*) - *(.llvmbc) - *(.llvmcmd) - } + /DISCARD/ : { + *(.comment) + *(.comment.*) + *(.llvmbc) + *(.llvmcmd) + } } diff --git a/targets/f7/furi_hal/furi_hal.c b/targets/f7/furi_hal/furi_hal.c index 3a58fe196..7424b5e82 100644 --- a/targets/f7/furi_hal/furi_hal.c +++ b/targets/f7/furi_hal/furi_hal.c @@ -63,10 +63,11 @@ void furi_hal_init(void) { void furi_hal_switch(void* address) { __set_BASEPRI(0); - asm volatile("ldr r3, [%0] \n" - "msr msp, r3 \n" - "ldr r3, [%1] \n" - "mov pc, r3 \n" + // This code emulates system reset: sets MSP and calls Reset ISR + asm volatile("ldr r3, [%0] \n" // Load SP from new vector to r3 + "msr msp, r3 \n" // Set MSP from r3 + "ldr r3, [%1] \n" // Load Reset Handler address to r3 + "mov pc, r3 \n" // Set PC from r3 (jump to Reset ISR) : : "r"(address), "r"(address + 0x4) : "r3"); diff --git a/targets/f7/furi_hal/furi_hal_flash.c b/targets/f7/furi_hal/furi_hal_flash.c index 9cf64acc5..8999edaed 100644 --- a/targets/f7/furi_hal/furi_hal_flash.c +++ b/targets/f7/furi_hal/furi_hal_flash.c @@ -8,6 +8,7 @@ #include #include +#include #include @@ -53,9 +54,6 @@ (((__VALUE__) >= FLASH_BASE) && ((__VALUE__) <= (FLASH_BASE + FLASH_SIZE - 8UL)) && \ (((__VALUE__) % 8UL) == 0UL)) -/* Free flash space borders, exported by linker */ -extern const void __free_flash_start__; - size_t furi_hal_flash_get_base(void) { return FLASH_BASE; } diff --git a/targets/f7/furi_hal/furi_hal_interrupt.c b/targets/f7/furi_hal/furi_hal_interrupt.c index 5c2c315ef..5d6680ac2 100644 --- a/targets/f7/furi_hal/furi_hal_interrupt.c +++ b/targets/f7/furi_hal/furi_hal_interrupt.c @@ -67,13 +67,12 @@ const IRQn_Type furi_hal_interrupt_irqn[FuriHalInterruptIdMax] = { [FuriHalInterruptIdLpUart1] = LPUART1_IRQn, }; -__attribute__((always_inline)) static inline void - furi_hal_interrupt_call(FuriHalInterruptId index) { +FURI_ALWAYS_STATIC_INLINE void furi_hal_interrupt_call(FuriHalInterruptId index) { furi_check(furi_hal_interrupt_isr[index].isr); furi_hal_interrupt_isr[index].isr(furi_hal_interrupt_isr[index].context); } -__attribute__((always_inline)) static inline void +FURI_ALWAYS_STATIC_INLINE void furi_hal_interrupt_enable(FuriHalInterruptId index, uint16_t priority) { NVIC_SetPriority( furi_hal_interrupt_irqn[index], @@ -81,23 +80,19 @@ __attribute__((always_inline)) static inline void NVIC_EnableIRQ(furi_hal_interrupt_irqn[index]); } -__attribute__((always_inline)) static inline void - furi_hal_interrupt_clear_pending(FuriHalInterruptId index) { +FURI_ALWAYS_STATIC_INLINE void furi_hal_interrupt_clear_pending(FuriHalInterruptId index) { NVIC_ClearPendingIRQ(furi_hal_interrupt_irqn[index]); } -__attribute__((always_inline)) static inline void - furi_hal_interrupt_get_pending(FuriHalInterruptId index) { +FURI_ALWAYS_STATIC_INLINE void furi_hal_interrupt_get_pending(FuriHalInterruptId index) { NVIC_GetPendingIRQ(furi_hal_interrupt_irqn[index]); } -__attribute__((always_inline)) static inline void - furi_hal_interrupt_set_pending(FuriHalInterruptId index) { +FURI_ALWAYS_STATIC_INLINE void furi_hal_interrupt_set_pending(FuriHalInterruptId index) { NVIC_SetPendingIRQ(furi_hal_interrupt_irqn[index]); } -__attribute__((always_inline)) static inline void - furi_hal_interrupt_disable(FuriHalInterruptId index) { +FURI_ALWAYS_STATIC_INLINE void furi_hal_interrupt_disable(FuriHalInterruptId index) { NVIC_DisableIRQ(furi_hal_interrupt_irqn[index]); } @@ -279,11 +274,11 @@ void MemManage_Handler(void) { // from 0x00 to 1MB, see FuriHalMpuRegionNULL furi_crash("NULL pointer dereference"); } else { - // write or read of MPU region 1 (FuriHalMpuRegionStack) + // write or read of MPU region 1 (FuriHalMpuRegionThreadStack) furi_crash("MPU fault, possibly stack overflow"); } } else if(FURI_BIT(SCB->CFSR, SCB_CFSR_MSTKERR_Pos)) { - // push to stack on MPU region 1 (FuriHalMpuRegionStack) + // push to stack on MPU region 1 (FuriHalMpuRegionThreadStack) furi_crash("MemManage fault, possibly stack overflow"); } @@ -318,9 +313,6 @@ void USB_LP_IRQHandler(void) { #endif } -void USB_HP_IRQHandler(void) { -} - void IPCC_C1_TX_IRQHandler(void) { HW_IPCC_Tx_Handler(); } @@ -347,4 +339,4 @@ void USART1_IRQHandler(void) { void LPUART1_IRQHandler(void) { furi_hal_interrupt_call(FuriHalInterruptIdLpUart1); -} \ No newline at end of file +} diff --git a/targets/f7/furi_hal/furi_hal_mpu.c b/targets/f7/furi_hal/furi_hal_mpu.c index 16724c975..5fe3ab66b 100644 --- a/targets/f7/furi_hal/furi_hal_mpu.c +++ b/targets/f7/furi_hal/furi_hal_mpu.c @@ -1,6 +1,8 @@ #include #include +#include + #define FURI_HAL_MPU_ATTRIBUTES \ (LL_MPU_ACCESS_BUFFERABLE | LL_MPU_ACCESS_CACHEABLE | LL_MPU_ACCESS_SHAREABLE | \ LL_MPU_TEX_LEVEL1 | LL_MPU_INSTRUCTION_ACCESS_ENABLE) @@ -12,6 +14,10 @@ void furi_hal_mpu_init(void) { // NULL pointer dereference protection furi_hal_mpu_protect_no_access(FuriHalMpuRegionNULL, 0x00, FuriHalMPURegionSize1MB); + furi_hal_mpu_protect_no_access( + FuriHalMpuRegionMainStack, + (uint32_t)(&_stack_end - &_stack_size), + FuriHalMPURegionSize32B); } void furi_hal_mpu_enable(void) { @@ -62,5 +68,5 @@ void furi_hal_mpu_set_stack_protection(uint32_t* stack) { if(stack_ptr < (uint32_t)stack) stack_ptr += (mask + 1); furi_hal_mpu_protect_read_only( - FuriHalMpuRegionStack, stack_ptr, FURI_HAL_MPU_STACK_PROTECT_REGION); + FuriHalMpuRegionThreadStack, stack_ptr, FURI_HAL_MPU_STACK_PROTECT_REGION); } \ No newline at end of file diff --git a/targets/f7/inc/stm32wb55_linker.h b/targets/f7/inc/stm32wb55_linker.h new file mode 100644 index 000000000..4b56a11be --- /dev/null +++ b/targets/f7/inc/stm32wb55_linker.h @@ -0,0 +1,34 @@ +/** + * @file stm32wb55_linker.h + * + * Linker defined symbols. Used in various part of firmware to understand + * hardware boundaries. + * + */ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +extern const void _stack_end; /**< end of stack */ +extern const void _stack_size; /**< stack size */ + +extern const void _sidata; /**< data initial value start */ +extern const void _sdata; /**< data start */ +extern const void _edata; /**< data end */ + +extern const void _sbss; /**< bss start */ +extern const void _ebss; /**< bss end */ + +extern const void _sMB_MEM2; /**< RAM2a start */ +extern const void _eMB_MEM2; /**< RAM2a end */ + +extern const void __heap_start__; /**< RAM1 Heap start */ +extern const void __heap_end__; /**< RAM1 Heap end */ + +extern const void __free_flash_start__; /**< Free Flash space start */ + +#ifdef __cplusplus +} +#endif diff --git a/targets/f7/inc/stm32wb55_startup.h b/targets/f7/inc/stm32wb55_startup.h new file mode 100644 index 000000000..712388576 --- /dev/null +++ b/targets/f7/inc/stm32wb55_startup.h @@ -0,0 +1,94 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern int main(void); +extern void __libc_init_array(void); + +void Default_Handler(void); + +#define DEFAULT FURI_DEFAULT("Default_Handler") + +/* 15 Unmask-able ISR */ +DEFAULT void NMI_Handler(void); +DEFAULT void HardFault_Handler(void); +DEFAULT void MemManage_Handler(void); +DEFAULT void BusFault_Handler(void); +DEFAULT void UsageFault_Handler(void); +DEFAULT void SVC_Handler(void); +DEFAULT void DebugMon_Handler(void); +DEFAULT void PendSV_Handler(void); +DEFAULT void SysTick_Handler(void); + +/* 63 Mask-able ISR */ +DEFAULT void WWDG_IRQHandler(void); +DEFAULT void PVD_PVM_IRQHandler(void); +DEFAULT void TAMP_STAMP_LSECSS_IRQHandler(void); +DEFAULT void RTC_WKUP_IRQHandler(void); +DEFAULT void FLASH_IRQHandler(void); +DEFAULT void RCC_IRQHandler(void); +DEFAULT void EXTI0_IRQHandler(void); +DEFAULT void EXTI1_IRQHandler(void); +DEFAULT void EXTI2_IRQHandler(void); +DEFAULT void EXTI3_IRQHandler(void); +DEFAULT void EXTI4_IRQHandler(void); +DEFAULT void DMA1_Channel1_IRQHandler(void); +DEFAULT void DMA1_Channel2_IRQHandler(void); +DEFAULT void DMA1_Channel3_IRQHandler(void); +DEFAULT void DMA1_Channel4_IRQHandler(void); +DEFAULT void DMA1_Channel5_IRQHandler(void); +DEFAULT void DMA1_Channel6_IRQHandler(void); +DEFAULT void DMA1_Channel7_IRQHandler(void); +DEFAULT void ADC1_IRQHandler(void); +DEFAULT void USB_HP_IRQHandler(void); +DEFAULT void USB_LP_IRQHandler(void); +DEFAULT void C2SEV_PWR_C2H_IRQHandler(void); +DEFAULT void COMP_IRQHandler(void); +DEFAULT void EXTI9_5_IRQHandler(void); +DEFAULT void TIM1_BRK_IRQHandler(void); +DEFAULT void TIM1_UP_TIM16_IRQHandler(void); +DEFAULT void TIM1_TRG_COM_TIM17_IRQHandler(void); +DEFAULT void TIM1_CC_IRQHandler(void); +DEFAULT void TIM2_IRQHandler(void); +DEFAULT void PKA_IRQHandler(void); +DEFAULT void I2C1_EV_IRQHandler(void); +DEFAULT void I2C1_ER_IRQHandler(void); +DEFAULT void I2C3_EV_IRQHandler(void); +DEFAULT void I2C3_ER_IRQHandler(void); +DEFAULT void SPI1_IRQHandler(void); +DEFAULT void SPI2_IRQHandler(void); +DEFAULT void USART1_IRQHandler(void); +DEFAULT void LPUART1_IRQHandler(void); +DEFAULT void SAI1_IRQHandler(void); +DEFAULT void TSC_IRQHandler(void); +DEFAULT void EXTI15_10_IRQHandler(void); +DEFAULT void RTC_Alarm_IRQHandler(void); +DEFAULT void CRS_IRQHandler(void); +DEFAULT void PWR_SOTF_BLEACT_802ACT_RFPHASE_IRQHandler(void); +DEFAULT void IPCC_C1_RX_IRQHandler(void); +DEFAULT void IPCC_C1_TX_IRQHandler(void); +DEFAULT void HSEM_IRQHandler(void); +DEFAULT void LPTIM1_IRQHandler(void); +DEFAULT void LPTIM2_IRQHandler(void); +DEFAULT void LCD_IRQHandler(void); +DEFAULT void QUADSPI_IRQHandler(void); +DEFAULT void AES1_IRQHandler(void); +DEFAULT void AES2_IRQHandler(void); +DEFAULT void RNG_IRQHandler(void); +DEFAULT void FPU_IRQHandler(void); +DEFAULT void DMA2_Channel1_IRQHandler(void); +DEFAULT void DMA2_Channel2_IRQHandler(void); +DEFAULT void DMA2_Channel3_IRQHandler(void); +DEFAULT void DMA2_Channel4_IRQHandler(void); +DEFAULT void DMA2_Channel5_IRQHandler(void); +DEFAULT void DMA2_Channel6_IRQHandler(void); +DEFAULT void DMA2_Channel7_IRQHandler(void); +DEFAULT void DMAMUX1_OVR_IRQHandler(void); + +#ifdef __cplusplus +} +#endif diff --git a/targets/f7/src/stm32wb55_startup.c b/targets/f7/src/stm32wb55_startup.c new file mode 100644 index 000000000..1c1cfdcc3 --- /dev/null +++ b/targets/f7/src/stm32wb55_startup.c @@ -0,0 +1,199 @@ +#include +#include +#include + +/** System Core Clock speed + * + * CPU1: M4 on MSI clock after startup (4MHz). + * Modified by RCC LL HAL. + */ +uint32_t SystemCoreClock = 4000000UL; + +/** AHB Prescaler Table. Used by RCC LL HAL */ +const uint32_t AHBPrescTable[16UL] = + {1UL, 3UL, 5UL, 1UL, 1UL, 6UL, 10UL, 32UL, 2UL, 4UL, 8UL, 16UL, 64UL, 128UL, 256UL, 512UL}; +/** APB Prescaler Table. Used by RCC LL HAL */ +const uint32_t APBPrescTable[8UL] = {0UL, 0UL, 0UL, 0UL, 1UL, 2UL, 3UL, 4UL}; +/** MSI Range Table. Used by RCC LL HAL */ +const uint32_t MSIRangeTable[16UL] = { + 100000UL, + 200000UL, + 400000UL, + 800000UL, + 1000000UL, + 2000000UL, + 4000000UL, + 8000000UL, + 16000000UL, + 24000000UL, + 32000000UL, + 48000000UL, + 0UL, + 0UL, + 0UL, + 0UL}; /* 0UL values are incorrect cases */ + +/** MCU Initialization Routine. Part of ST HAL convention, so we keep it.*/ +void SystemInit(void) { + // Set ISR Vector location +#if defined(VECT_TAB_SRAM) + // Point ISR Vector to SRAM + SCB->VTOR = SRAM1_BASE; +#else + // Point ISR Vector to 0x0, which is mapped to 0x08000000(Flash) + SCB->VTOR = 0x0; +#endif + +#if(__FPU_PRESENT == 1) && (__FPU_USED == 1) + // Enable access to FPU + SCB->CPACR |= + ((3UL << (10UL * 2UL)) | (3UL << (11UL * 2UL))); /* set CP10 and CP11 Full Access */ +#endif + + // Reset the RCC clock configuration to the default reset state + // Set MSION bit + RCC->CR |= RCC_CR_MSION; + // Reset CFGR register + RCC->CFGR = 0x00070000U; + // Reset PLLSAI1ON, PLLON, HSECSSON, HSEON, HSION, and MSIPLLON bits + RCC->CR &= (uint32_t)0xFAF6FEFBU; + // Reset LSI1 and LSI2 bits + RCC->CSR &= (uint32_t)0xFFFFFFFAU; + // Reset HSI48ON bit + RCC->CRRCR &= (uint32_t)0xFFFFFFFEU; + // Reset PLLCFGR register + RCC->PLLCFGR = 0x22041000U; +#if defined(STM32WB55xx) || defined(STM32WB5Mxx) + // Reset PLLSAI1CFGR register + RCC->PLLSAI1CFGR = 0x22041000U; +#endif + // Reset HSEBYP bit + RCC->CR &= 0xFFFBFFFFU; + // Disable all RCC related interrupts + RCC->CIER = 0x00000000; +} + +void Default_Handler(void) { + furi_crash("NotImplemented"); +} + +/** Start your journey here */ +FURI_NAKED void Reset_Handler(void) { + // Funny thing: SP and MSP are set to _stack_end if we came here after MCU reset + // Now, what if we came from boot loader? Lets set SP to _stack_end again. + // By the way Furi stage loader doing it too, but we don't know who called us. + asm volatile("ldr r0, =_stack_end"); + asm volatile("mov sp, r0"); + + // ST chip initialization routine + SystemInit(); + + // Copy data section from flash + memcpy((void*)&_sdata, &_sidata, &_edata - &_sdata); + + // Wipe BSS + memset((void*)&_sbss, 0x00, &_ebss - &_sbss); + + // Core2 related quirks: wipe MB_MEM2 section + memset((void*)&_sMB_MEM2, 0x00, &_eMB_MEM2 - &_sMB_MEM2); + + // libc init array + __libc_init_array(); + + // Our main + main(); + + // You should never exit from main, but we'll catch you if you do + furi_crash("WhyExit?"); +} + +/** ISR type */ +typedef void (*element_t)(void); + +/** System initialization vector. Contains: main stack end address, 15 pointers + * to unmask-able ISR and 63 to mask-able ISR. */ +PLACE_IN_SECTION(".isr_vector") +const element_t reset_vector[] = { + /* Main stack top */ + (element_t)&_stack_end, + /* 15 Unmaskable ISR */ + Reset_Handler, + NMI_Handler, + HardFault_Handler, + MemManage_Handler, + BusFault_Handler, + UsageFault_Handler, + NULL, + NULL, + NULL, + NULL, + SVC_Handler, + DebugMon_Handler, + NULL, + PendSV_Handler, + SysTick_Handler, + /* 63 Maskable ISR */ + WWDG_IRQHandler, + PVD_PVM_IRQHandler, + TAMP_STAMP_LSECSS_IRQHandler, + RTC_WKUP_IRQHandler, + FLASH_IRQHandler, + RCC_IRQHandler, + EXTI0_IRQHandler, + EXTI1_IRQHandler, + EXTI2_IRQHandler, + EXTI3_IRQHandler, + EXTI4_IRQHandler, + DMA1_Channel1_IRQHandler, + DMA1_Channel2_IRQHandler, + DMA1_Channel3_IRQHandler, + DMA1_Channel4_IRQHandler, + DMA1_Channel5_IRQHandler, + DMA1_Channel6_IRQHandler, + DMA1_Channel7_IRQHandler, + ADC1_IRQHandler, + USB_HP_IRQHandler, + USB_LP_IRQHandler, + C2SEV_PWR_C2H_IRQHandler, + COMP_IRQHandler, + EXTI9_5_IRQHandler, + TIM1_BRK_IRQHandler, + TIM1_UP_TIM16_IRQHandler, + TIM1_TRG_COM_TIM17_IRQHandler, + TIM1_CC_IRQHandler, + TIM2_IRQHandler, + PKA_IRQHandler, + I2C1_EV_IRQHandler, + I2C1_ER_IRQHandler, + I2C3_EV_IRQHandler, + I2C3_ER_IRQHandler, + SPI1_IRQHandler, + SPI2_IRQHandler, + USART1_IRQHandler, + LPUART1_IRQHandler, + SAI1_IRQHandler, + TSC_IRQHandler, + EXTI15_10_IRQHandler, + RTC_Alarm_IRQHandler, + CRS_IRQHandler, + PWR_SOTF_BLEACT_802ACT_RFPHASE_IRQHandler, + IPCC_C1_RX_IRQHandler, + IPCC_C1_TX_IRQHandler, + HSEM_IRQHandler, + LPTIM1_IRQHandler, + LPTIM2_IRQHandler, + LCD_IRQHandler, + QUADSPI_IRQHandler, + AES1_IRQHandler, + AES2_IRQHandler, + RNG_IRQHandler, + FPU_IRQHandler, + DMA2_Channel1_IRQHandler, + DMA2_Channel2_IRQHandler, + DMA2_Channel3_IRQHandler, + DMA2_Channel4_IRQHandler, + DMA2_Channel5_IRQHandler, + DMA2_Channel6_IRQHandler, + DMA2_Channel7_IRQHandler, + DMAMUX1_OVR_IRQHandler, +}; diff --git a/targets/f7/src/system_stm32wbxx.c b/targets/f7/src/system_stm32wbxx.c deleted file mode 100644 index 77430fda8..000000000 --- a/targets/f7/src/system_stm32wbxx.c +++ /dev/null @@ -1,97 +0,0 @@ -#include "stm32wbxx.h" - -/*!< Uncomment the following line if you need to relocate your vector Table in Internal SRAM. */ -/* #define VECT_TAB_SRAM */ - -#ifndef VECT_TAB_OFFSET -#define VECT_TAB_OFFSET \ - 0x0 /*!< Vector Table base offset field. This value must be a multiple of 0x200. */ -#endif - -#define VECT_TAB_BASE_ADDRESS \ - SRAM1_BASE /*!< Vector Table base offset field. This value must be a multiple of 0x200. */ - -/* The SystemCoreClock variable is updated in three ways: - 1) by calling CMSIS function SystemCoreClockUpdate() - 2) by calling HAL API function HAL_RCC_GetHCLKFreq() - 3) each time HAL_RCC_ClockConfig() is called to configure the system clock frequency - Note: If you use this function to configure the system clock; then there - is no need to call the 2 first functions listed above, since SystemCoreClock - variable is updated automatically. - */ -uint32_t SystemCoreClock = 4000000UL; /*CPU1: M4 on MSI clock after startup (4MHz)*/ - -const uint32_t AHBPrescTable[16UL] = - {1UL, 3UL, 5UL, 1UL, 1UL, 6UL, 10UL, 32UL, 2UL, 4UL, 8UL, 16UL, 64UL, 128UL, 256UL, 512UL}; - -const uint32_t APBPrescTable[8UL] = {0UL, 0UL, 0UL, 0UL, 1UL, 2UL, 3UL, 4UL}; - -const uint32_t MSIRangeTable[16UL] = { - 100000UL, - 200000UL, - 400000UL, - 800000UL, - 1000000UL, - 2000000UL, - 4000000UL, - 8000000UL, - 16000000UL, - 24000000UL, - 32000000UL, - 48000000UL, - 0UL, - 0UL, - 0UL, - 0UL}; /* 0UL values are incorrect cases */ - -/** - * @brief Setup the microcontroller system. - * @param None - * @retval None - */ -void SystemInit(void) { - /* Configure the Vector Table location add offset address ------------------*/ -#if defined(VECT_TAB_SRAM) && defined(VECT_TAB_BASE_ADDRESS) - /* program in SRAMx */ - SCB->VTOR = VECT_TAB_BASE_ADDRESS | - VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAMx for CPU1 */ -#else /* program in FLASH */ - SCB->VTOR = VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */ -#endif - -/* FPU settings ------------------------------------------------------------*/ -#if(__FPU_PRESENT == 1) && (__FPU_USED == 1) - SCB->CPACR |= - ((3UL << (10UL * 2UL)) | (3UL << (11UL * 2UL))); /* set CP10 and CP11 Full Access */ -#endif - - /* Reset the RCC clock configuration to the default reset state ------------*/ - /* Set MSION bit */ - RCC->CR |= RCC_CR_MSION; - - /* Reset CFGR register */ - RCC->CFGR = 0x00070000U; - - /* Reset PLLSAI1ON, PLLON, HSECSSON, HSEON, HSION, and MSIPLLON bits */ - RCC->CR &= (uint32_t)0xFAF6FEFBU; - - /*!< Reset LSI1 and LSI2 bits */ - RCC->CSR &= (uint32_t)0xFFFFFFFAU; - - /*!< Reset HSI48ON bit */ - RCC->CRRCR &= (uint32_t)0xFFFFFFFEU; - - /* Reset PLLCFGR register */ - RCC->PLLCFGR = 0x22041000U; - -#if defined(STM32WB55xx) || defined(STM32WB5Mxx) - /* Reset PLLSAI1CFGR register */ - RCC->PLLSAI1CFGR = 0x22041000U; -#endif - - /* Reset HSEBYP bit */ - RCC->CR &= 0xFFFBFFFFU; - - /* Disable all interrupts */ - RCC->CIER = 0x00000000; -} diff --git a/targets/f7/src/update.c b/targets/f7/src/update.c index 261adb5ca..d15474e52 100644 --- a/targets/f7/src/update.c +++ b/targets/f7/src/update.c @@ -109,7 +109,7 @@ static bool flipper_update_load_stage(const FuriString* work_dir, UpdateManifest memmove((void*)(SRAM1_BASE), img, stat.fsize); LL_SYSCFG_SetRemapMemory(LL_SYSCFG_REMAP_SRAM); - furi_hal_switch((void*)SRAM1_BASE); + furi_hal_switch(0x0); return true; } while(false); diff --git a/targets/f7/startup_stm32wb55xx_cm4.s b/targets/f7/startup_stm32wb55xx_cm4.s deleted file mode 100644 index c5c2b3fc3..000000000 --- a/targets/f7/startup_stm32wb55xx_cm4.s +++ /dev/null @@ -1,444 +0,0 @@ -/** - ****************************************************************************** - * @file startup_stm32wb55xx_cm4.s - * @author MCD Application Team - * @brief STM32WB55xx devices vector table GCC toolchain. - * This module performs: - * - Set the initial SP - * - Set the initial PC == Reset_Handler, - * - Set the vector table entries with the exceptions ISR address - * - Branches to main in the C library (which eventually - * calls main()). - * After Reset the Cortex-M4 processor is in Thread mode, - * priority is Privileged, and the Stack is set to Main. - ****************************************************************************** - * @attention - * - * Copyright (c) 2019-2021 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. - * - ****************************************************************************** - */ - - .syntax unified - .cpu cortex-m4 - .fpu softvfp - .thumb - -.global g_pfnVectors -.global Default_Handler - -/* start address for the initialization values of the .data section. -defined in linker script */ -.word _sidata -/* start address for the .data section. defined in linker script */ -.word _sdata -/* end address for the .data section. defined in linker script */ -.word _edata -/* start address for the .bss section. defined in linker script */ -.word _sbss -/* end address for the .bss section. defined in linker script */ -.word _ebss -/* start address for the .MB_MEM2 section. defined in linker script */ -.word _sMB_MEM2 -/* end address for the .MB_MEM2 section. defined in linker script */ -.word _eMB_MEM2 - -/* INIT_BSS macro is used to fill the specified region [start : end] with zeros */ -.macro INIT_BSS start, end - ldr r0, =\start - ldr r1, =\end - movs r3, #0 - bl LoopFillZerobss -.endm - -/* INIT_DATA macro is used to copy data in the region [start : end] starting from 'src' */ -.macro INIT_DATA start, end, src - ldr r0, =\start - ldr r1, =\end - ldr r2, =\src - movs r3, #0 - bl LoopCopyDataInit -.endm - -.section .text.data_initializers -CopyDataInit: - ldr r4, [r2, r3] - str r4, [r0, r3] - adds r3, r3, #4 - -LoopCopyDataInit: - adds r4, r0, r3 - cmp r4, r1 - bcc CopyDataInit - bx lr - -FillZerobss: - str r3, [r0] - adds r0, r0, #4 - -LoopFillZerobss: - cmp r0, r1 - bcc FillZerobss - bx lr - - .section .text.Reset_Handler - .weak Reset_Handler - .type Reset_Handler, %function -Reset_Handler: - ldr r0, =_estack - mov sp, r0 /* set stack pointer */ -/* Call the clock system intitialization function.*/ - bl SystemInit - -/* Copy the data segment initializers from flash to SRAM */ - INIT_DATA _sdata, _edata, _sidata - -/* Zero fill the bss segments. */ - INIT_BSS _sbss, _ebss - INIT_BSS _sMB_MEM2, _eMB_MEM2 - -/* Call static constructors */ - bl __libc_init_array -/* Call the application s entry point.*/ - bl main - -LoopForever: - b LoopForever - -.size Reset_Handler, .-Reset_Handler - -/** - * @brief This is the code that gets called when the processor receives an - * unexpected interrupt. This simply enters an infinite loop, preserving - * the system state for examination by a debugger. - * - * @param None - * @retval None -*/ - .section .text.Default_Handler,"ax",%progbits -Default_Handler: -Infinite_Loop: - b Infinite_Loop - .size Default_Handler, .-Default_Handler -/****************************************************************************** -* -* The minimal vector table for a Cortex-M4. Note that the proper constructs -* must be placed on this to ensure that it ends up at physical address -* 0x0000.0000. -* -******************************************************************************/ - .section .isr_vector,"a",%progbits - .type g_pfnVectors, %object - .size g_pfnVectors, .-g_pfnVectors - - -g_pfnVectors: - .word _estack - .word Reset_Handler - .word NMI_Handler - .word HardFault_Handler - .word MemManage_Handler - .word BusFault_Handler - .word UsageFault_Handler - .word 0 - .word 0 - .word 0 - .word 0 - .word SVC_Handler - .word DebugMon_Handler - .word 0 - .word PendSV_Handler - .word SysTick_Handler - .word WWDG_IRQHandler - .word PVD_PVM_IRQHandler - .word TAMP_STAMP_LSECSS_IRQHandler - .word RTC_WKUP_IRQHandler - .word FLASH_IRQHandler - .word RCC_IRQHandler - .word EXTI0_IRQHandler - .word EXTI1_IRQHandler - .word EXTI2_IRQHandler - .word EXTI3_IRQHandler - .word EXTI4_IRQHandler - .word DMA1_Channel1_IRQHandler - .word DMA1_Channel2_IRQHandler - .word DMA1_Channel3_IRQHandler - .word DMA1_Channel4_IRQHandler - .word DMA1_Channel5_IRQHandler - .word DMA1_Channel6_IRQHandler - .word DMA1_Channel7_IRQHandler - .word ADC1_IRQHandler - .word USB_HP_IRQHandler - .word USB_LP_IRQHandler - .word C2SEV_PWR_C2H_IRQHandler - .word COMP_IRQHandler - .word EXTI9_5_IRQHandler - .word TIM1_BRK_IRQHandler - .word TIM1_UP_TIM16_IRQHandler - .word TIM1_TRG_COM_TIM17_IRQHandler - .word TIM1_CC_IRQHandler - .word TIM2_IRQHandler - .word PKA_IRQHandler - .word I2C1_EV_IRQHandler - .word I2C1_ER_IRQHandler - .word I2C3_EV_IRQHandler - .word I2C3_ER_IRQHandler - .word SPI1_IRQHandler - .word SPI2_IRQHandler - .word USART1_IRQHandler - .word LPUART1_IRQHandler - .word SAI1_IRQHandler - .word TSC_IRQHandler - .word EXTI15_10_IRQHandler - .word RTC_Alarm_IRQHandler - .word CRS_IRQHandler - .word PWR_SOTF_BLEACT_802ACT_RFPHASE_IRQHandler - .word IPCC_C1_RX_IRQHandler - .word IPCC_C1_TX_IRQHandler - .word HSEM_IRQHandler - .word LPTIM1_IRQHandler - .word LPTIM2_IRQHandler - .word LCD_IRQHandler - .word QUADSPI_IRQHandler - .word AES1_IRQHandler - .word AES2_IRQHandler - .word RNG_IRQHandler - .word FPU_IRQHandler - .word DMA2_Channel1_IRQHandler - .word DMA2_Channel2_IRQHandler - .word DMA2_Channel3_IRQHandler - .word DMA2_Channel4_IRQHandler - .word DMA2_Channel5_IRQHandler - .word DMA2_Channel6_IRQHandler - .word DMA2_Channel7_IRQHandler - .word DMAMUX1_OVR_IRQHandler - -/******************************************************************************* -* -* Provide weak aliases for each Exception handler to the Default_Handler. -* As they are weak aliases, any function with the same name will override -* this definition. -* -*******************************************************************************/ - .weak NMI_Handler - .thumb_set NMI_Handler,Default_Handler - - .weak HardFault_Handler - .thumb_set HardFault_Handler,Default_Handler - - .weak MemManage_Handler - .thumb_set MemManage_Handler,Default_Handler - - .weak BusFault_Handler - .thumb_set BusFault_Handler,Default_Handler - - .weak UsageFault_Handler - .thumb_set UsageFault_Handler,Default_Handler - - .weak SVC_Handler - .thumb_set SVC_Handler,Default_Handler - - .weak DebugMon_Handler - .thumb_set DebugMon_Handler,Default_Handler - - .weak PendSV_Handler - .thumb_set PendSV_Handler,Default_Handler - - .weak SysTick_Handler - .thumb_set SysTick_Handler,Default_Handler - - .weak WWDG_IRQHandler - .thumb_set WWDG_IRQHandler,Default_Handler - - .weak PVD_PVM_IRQHandler - .thumb_set PVD_PVM_IRQHandler,Default_Handler - - .weak TAMP_STAMP_LSECSS_IRQHandler - .thumb_set TAMP_STAMP_LSECSS_IRQHandler,Default_Handler - - .weak RTC_WKUP_IRQHandler - .thumb_set RTC_WKUP_IRQHandler,Default_Handler - - .weak FLASH_IRQHandler - .thumb_set FLASH_IRQHandler,Default_Handler - - .weak RCC_IRQHandler - .thumb_set RCC_IRQHandler,Default_Handler - - .weak EXTI0_IRQHandler - .thumb_set EXTI0_IRQHandler,Default_Handler - - .weak EXTI1_IRQHandler - .thumb_set EXTI1_IRQHandler,Default_Handler - - .weak EXTI2_IRQHandler - .thumb_set EXTI2_IRQHandler,Default_Handler - - .weak EXTI3_IRQHandler - .thumb_set EXTI3_IRQHandler,Default_Handler - - .weak EXTI4_IRQHandler - .thumb_set EXTI4_IRQHandler,Default_Handler - - .weak DMA1_Channel1_IRQHandler - .thumb_set DMA1_Channel1_IRQHandler,Default_Handler - - .weak DMA1_Channel2_IRQHandler - .thumb_set DMA1_Channel2_IRQHandler,Default_Handler - - .weak DMA1_Channel3_IRQHandler - .thumb_set DMA1_Channel3_IRQHandler,Default_Handler - - .weak DMA1_Channel4_IRQHandler - .thumb_set DMA1_Channel4_IRQHandler,Default_Handler - - .weak DMA1_Channel5_IRQHandler - .thumb_set DMA1_Channel5_IRQHandler,Default_Handler - - .weak DMA1_Channel6_IRQHandler - .thumb_set DMA1_Channel6_IRQHandler,Default_Handler - - .weak DMA1_Channel7_IRQHandler - .thumb_set DMA1_Channel7_IRQHandler,Default_Handler - - .weak ADC1_IRQHandler - .thumb_set ADC1_IRQHandler,Default_Handler - - .weak USB_HP_IRQHandler - .thumb_set USB_HP_IRQHandler,Default_Handler - - .weak USB_LP_IRQHandler - .thumb_set USB_LP_IRQHandler,Default_Handler - - .weak C2SEV_PWR_C2H_IRQHandler - .thumb_set C2SEV_PWR_C2H_IRQHandler,Default_Handler - - .weak COMP_IRQHandler - .thumb_set COMP_IRQHandler,Default_Handler - - .weak EXTI9_5_IRQHandler - .thumb_set EXTI9_5_IRQHandler,Default_Handler - - .weak TIM1_BRK_IRQHandler - .thumb_set TIM1_BRK_IRQHandler,Default_Handler - - .weak TIM1_UP_TIM16_IRQHandler - .thumb_set TIM1_UP_TIM16_IRQHandler,Default_Handler - - .weak TIM1_TRG_COM_TIM17_IRQHandler - .thumb_set TIM1_TRG_COM_TIM17_IRQHandler,Default_Handler - - .weak TIM1_CC_IRQHandler - .thumb_set TIM1_CC_IRQHandler,Default_Handler - - .weak TIM2_IRQHandler - .thumb_set TIM2_IRQHandler,Default_Handler - - .weak PKA_IRQHandler - .thumb_set PKA_IRQHandler,Default_Handler - - .weak I2C1_EV_IRQHandler - .thumb_set I2C1_EV_IRQHandler,Default_Handler - - .weak I2C1_ER_IRQHandler - .thumb_set I2C1_ER_IRQHandler,Default_Handler - - .weak I2C3_EV_IRQHandler - .thumb_set I2C3_EV_IRQHandler,Default_Handler - - .weak I2C3_ER_IRQHandler - .thumb_set I2C3_ER_IRQHandler,Default_Handler - - .weak SPI1_IRQHandler - .thumb_set SPI1_IRQHandler,Default_Handler - - .weak SPI2_IRQHandler - .thumb_set SPI2_IRQHandler,Default_Handler - - .weak USART1_IRQHandler - .thumb_set USART1_IRQHandler,Default_Handler - - .weak LPUART1_IRQHandler - .thumb_set LPUART1_IRQHandler,Default_Handler - - .weak SAI1_IRQHandler - .thumb_set SAI1_IRQHandler,Default_Handler - - .weak TSC_IRQHandler - .thumb_set TSC_IRQHandler,Default_Handler - - .weak EXTI15_10_IRQHandler - .thumb_set EXTI15_10_IRQHandler,Default_Handler - - .weak RTC_Alarm_IRQHandler - .thumb_set RTC_Alarm_IRQHandler,Default_Handler - - .weak CRS_IRQHandler - .thumb_set CRS_IRQHandler,Default_Handler - - .weak PWR_SOTF_BLEACT_802ACT_RFPHASE_IRQHandler - .thumb_set PWR_SOTF_BLEACT_802ACT_RFPHASE_IRQHandler,Default_Handler - - .weak IPCC_C1_RX_IRQHandler - .thumb_set IPCC_C1_RX_IRQHandler,Default_Handler - - .weak IPCC_C1_TX_IRQHandler - .thumb_set IPCC_C1_TX_IRQHandler,Default_Handler - - .weak HSEM_IRQHandler - .thumb_set HSEM_IRQHandler,Default_Handler - - .weak LPTIM1_IRQHandler - .thumb_set LPTIM1_IRQHandler,Default_Handler - - .weak LPTIM2_IRQHandler - .thumb_set LPTIM2_IRQHandler,Default_Handler - - .weak LCD_IRQHandler - .thumb_set LCD_IRQHandler,Default_Handler - - .weak QUADSPI_IRQHandler - .thumb_set QUADSPI_IRQHandler,Default_Handler - - .weak AES1_IRQHandler - .thumb_set AES1_IRQHandler,Default_Handler - - .weak AES2_IRQHandler - .thumb_set AES2_IRQHandler,Default_Handler - - .weak RNG_IRQHandler - .thumb_set RNG_IRQHandler,Default_Handler - - .weak FPU_IRQHandler - .thumb_set FPU_IRQHandler,Default_Handler - - .weak DMA2_Channel1_IRQHandler - .thumb_set DMA2_Channel1_IRQHandler,Default_Handler - - .weak DMA2_Channel2_IRQHandler - .thumb_set DMA2_Channel2_IRQHandler,Default_Handler - - .weak DMA2_Channel3_IRQHandler - .thumb_set DMA2_Channel3_IRQHandler,Default_Handler - - .weak DMA2_Channel4_IRQHandler - .thumb_set DMA2_Channel4_IRQHandler,Default_Handler - - .weak DMA2_Channel5_IRQHandler - .thumb_set DMA2_Channel5_IRQHandler,Default_Handler - - .weak DMA2_Channel6_IRQHandler - .thumb_set DMA2_Channel6_IRQHandler,Default_Handler - - .weak DMA2_Channel7_IRQHandler - .thumb_set DMA2_Channel7_IRQHandler,Default_Handler - - .weak DMAMUX1_OVR_IRQHandler - .thumb_set DMAMUX1_OVR_IRQHandler,Default_Handler - -/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/targets/f7/stm32wb55xx_flash.ld b/targets/f7/stm32wb55xx_flash.ld index df4c5b726..3fb789645 100644 --- a/targets/f7/stm32wb55xx_flash.ld +++ b/targets/f7/stm32wb55xx_flash.ld @@ -1,198 +1,134 @@ -/** -***************************************************************************** -** -** File : stm32wb55xx_flash.ld -** -** Abstract : System Workbench Minimal System calls file -** -** For more information about which c-functions -** need which of these lowlevel functions -** please consult the Newlib libc-manual -** -** Environment : System Workbench for MCU -** -** Distribution: The file is distributed “as is,” without any warranty -** of any kind. -** -***************************************************************************** -** -**

© COPYRIGHT(c) 2019 Ac6

-** -** Redistribution and use in source and binary forms, with or without modification, -** are permitted provided that the following conditions are met: -** 1. Redistributions of source code must retain the above copyright notice, -** this list of conditions and the following disclaimer. -** 2. Redistributions in binary form must reproduce the above copyright notice, -** this list of conditions and the following disclaimer in the documentation -** and/or other materials provided with the distribution. -** 3. Neither the name of Ac6 nor the names of its contributors -** may be used to endorse or promote products derived from this software -** without specific prior written permission. -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -** AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -** DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -** OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -** -***************************************************************************** -*/ - -/* Entry Point */ ENTRY(Reset_Handler) /* Highest address of the user mode stack */ -_estack = 0x20030000; /* end of RAM */ +_stack_end = 0x20030000; /* end of RAM */ /* Generate a link error if heap and stack don't fit into RAM */ -_Min_Heap_Size = 0x400; /* required amount of heap */ -_Min_Stack_Size = 0x1000; /* required amount of stack */ +_stack_size = 0x1000; /* required amount of stack */ -/* Specify the memory areas */ -MEMORY -{ -FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024K -RAM1 (xrw) : ORIGIN = 0x20000008, LENGTH = 0x2FFF8 -RAM2A (xrw) : ORIGIN = 0x20030000, LENGTH = 10K -RAM2B (xrw) : ORIGIN = 0x20038000, LENGTH = 10K +MEMORY { + FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024K + RAM1 (xrw) : ORIGIN = 0x20000008, LENGTH = 0x2FFF8 + RAM2A (xrw) : ORIGIN = 0x20030000, LENGTH = 10K + RAM2B (xrw) : ORIGIN = 0x20038000, LENGTH = 10K } -/* Define output sections */ -SECTIONS -{ - /* The startup code goes first into FLASH */ - .isr_vector : - { +SECTIONS { + /* The startup code goes first into FLASH */ + .isr_vector : { + . = ALIGN(4); + KEEP(*(.isr_vector)) /* Startup code */ + . = ALIGN(4); + } >FLASH + + /* The program code and other data goes into FLASH */ + .text : { + . = ALIGN(4); + *lib*.a:*(.text .text.*) /* code from libraries before apps */ + *(.text) /* .text sections (code) */ + *(.text*) /* .text* sections (code) */ + *(.glue_7) /* glue arm to thumb code */ + *(.glue_7t) /* glue thumb to arm code */ + *(.eh_frame) + + KEEP (*(.init)) + KEEP (*(.fini)) + + . = ALIGN(4); + _etext = .; /* define a global symbols at end of code */ + } >FLASH + + /* Constant data goes into FLASH */ + .rodata : { + . = ALIGN(4); + *(.rodata) /* .rodata sections (constants, strings, etc.) */ + *(.rodata*) /* .rodata* sections (constants, strings, etc.) */ + . = ALIGN(4); + } >FLASH + + .ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH + .ARM : { + __exidx_start = .; + *(.ARM.exidx*) + __exidx_end = .; + } >FLASH + + .preinit_array : { + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array*)) + PROVIDE_HIDDEN (__preinit_array_end = .); + } >FLASH + .init_array : { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array*)) + PROVIDE_HIDDEN (__init_array_end = .); + } >FLASH + .fini_array : { + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(SORT(.fini_array.*))) + KEEP (*(.fini_array*)) + PROVIDE_HIDDEN (__fini_array_end = .); + } >FLASH + + /* used by the startup to initialize data */ + _sidata = LOADADDR(.data); + + /* Initialized data sections goes into RAM, load LMA copy after code */ + .data : { + . = ALIGN(4); + _sdata = .; /* create a global symbol at data start */ + *(.data) /* .data sections */ + *(.data*) /* .data* sections */ + *(*_DRIVER_CONTEXT) + + . = ALIGN(4); + _edata = .; /* define a global symbol at data end */ + } >RAM1 AT> FLASH + + /* Uninitialized data section */ . = ALIGN(4); - KEEP(*(.isr_vector)) /* Startup code */ - . = ALIGN(4); - } >FLASH + .bss : { + /* This is used by the startup in order to initialize the .bss secion */ + _sbss = .; /* define a global symbol at bss start */ + __bss_start__ = _sbss; + *(.bss) + *(.bss*) + *(COMMON) - /* The program code and other data goes into FLASH */ - .text : - { - . = ALIGN(4); - *lib*.a:*(.text .text.*) /* code from libraries before apps */ - *(.text) /* .text sections (code) */ - *(.text*) /* .text* sections (code) */ - *(.glue_7) /* glue arm to thumb code */ - *(.glue_7t) /* glue thumb to arm code */ - *(.eh_frame) + . = ALIGN(4); + _ebss = .; /* define a global symbol at bss end */ + __bss_end__ = _ebss; + } >RAM1 - KEEP (*(.init)) - KEEP (*(.fini)) + /* User_heap_stack section, used to check that there is enough RAM left */ + /* DSECT: https://software-dl.ti.com/ccs/esd/documents/sdto_cgt_linker_special_section_types.html */ + ._user_heap_stack(DSECT) : { + . = ALIGN(8); + __heap_start__ = .; + . = ORIGIN(RAM1) + LENGTH(RAM1) - _stack_size; + __heap_end__ = .; + . = . + _stack_size; + . = ALIGN(8); + } >RAM1 - . = ALIGN(4); - _etext = .; /* define a global symbols at end of code */ - } >FLASH + /* Free Flash space, that can be used for internal storage */ + .free_flash(DSECT) : { + __free_flash_start__ = .; + . = ORIGIN(FLASH) + LENGTH(FLASH); + } >FLASH - /* Constant data goes into FLASH */ - .rodata : - { - . = ALIGN(4); - *(.rodata) /* .rodata sections (constants, strings, etc.) */ - *(.rodata*) /* .rodata* sections (constants, strings, etc.) */ - . = ALIGN(4); - } >FLASH + /* Remove information from the standard libraries */ + /DISCARD/ : { + libc.a ( * ) + libm.a ( * ) + libgcc.a ( * ) + } - .ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH - .ARM : { - __exidx_start = .; - *(.ARM.exidx*) - __exidx_end = .; - } >FLASH - - .preinit_array : - { - PROVIDE_HIDDEN (__preinit_array_start = .); - KEEP (*(.preinit_array*)) - PROVIDE_HIDDEN (__preinit_array_end = .); - } >FLASH - .init_array : - { - PROVIDE_HIDDEN (__init_array_start = .); - KEEP (*(SORT(.init_array.*))) - KEEP (*(.init_array*)) - PROVIDE_HIDDEN (__init_array_end = .); - } >FLASH - .fini_array : - { - PROVIDE_HIDDEN (__fini_array_start = .); - KEEP (*(SORT(.fini_array.*))) - KEEP (*(.fini_array*)) - PROVIDE_HIDDEN (__fini_array_end = .); - } >FLASH - - /* used by the startup to initialize data */ - _sidata = LOADADDR(.data); - - /* Initialized data sections goes into RAM, load LMA copy after code */ - .data : - { - . = ALIGN(4); - _sdata = .; /* create a global symbol at data start */ - *(.data) /* .data sections */ - *(.data*) /* .data* sections */ - *(*_DRIVER_CONTEXT) - - . = ALIGN(4); - _edata = .; /* define a global symbol at data end */ - } >RAM1 AT> FLASH - - /* Uninitialized data section */ - . = ALIGN(4); - .bss : - { - /* This is used by the startup in order to initialize the .bss secion */ - _sbss = .; /* define a global symbol at bss start */ - __bss_start__ = _sbss; - *(.bss) - *(.bss*) - *(COMMON) - - . = ALIGN(4); - _ebss = .; /* define a global symbol at bss end */ - __bss_end__ = _ebss; - } >RAM1 - - /* User_heap_stack section, used to check that there is enough RAM left */ - /* DSECT: https://software-dl.ti.com/ccs/esd/documents/sdto_cgt_linker_special_section_types.html */ - ._user_heap_stack(DSECT): - { - . = ALIGN(8); - __heap_start__ = .; - . = ORIGIN(RAM1) + LENGTH(RAM1) - _Min_Stack_Size; - __heap_end__ = .; - . = . + _Min_Stack_Size; - . = ALIGN(8); - } >RAM1 - - /* Free Flash space, that can be used for internal storage */ - .free_flash(DSECT): - { - __free_flash_start__ = .; - . = ORIGIN(FLASH) + LENGTH(FLASH); - } >FLASH - - /* Remove information from the standard libraries */ - /DISCARD/ : - { - libc.a ( * ) - libm.a ( * ) - libgcc.a ( * ) - } - - .ARM.attributes 0 : { *(.ARM.attributes) } - ._sram2a_start : { . = ALIGN(4); __sram2a_start__ = .; } >RAM2A - MAPPING_TABLE (NOLOAD) : { *(MAPPING_TABLE) } >RAM2A - MB_MEM1 (NOLOAD) : { *(MB_MEM1) } >RAM2A - MB_MEM2 (NOLOAD) : { _sMB_MEM2 = . ; *(MB_MEM2) ; _eMB_MEM2 = . ; } >RAM2A - ._sram2a_free : { . = ALIGN(4); __sram2a_free__ = .; } >RAM2A - ._sram2b_start : { . = ALIGN(4); __sram2b_start__ = .; } >RAM2B + .ARM.attributes 0 : { *(.ARM.attributes) } + ._sram2a_start : { . = ALIGN(4); __sram2a_start__ = .; } >RAM2A + MAPPING_TABLE (NOLOAD) : { *(MAPPING_TABLE) } >RAM2A + MB_MEM1 (NOLOAD) : { *(MB_MEM1) } >RAM2A + MB_MEM2 (NOLOAD) : { _sMB_MEM2 = . ; *(MB_MEM2) ; _eMB_MEM2 = . ; } >RAM2A + ._sram2a_free : { . = ALIGN(4); __sram2a_free__ = .; } >RAM2A + ._sram2b_start : { . = ALIGN(4); __sram2b_start__ = .; } >RAM2B } - - diff --git a/targets/f7/stm32wb55xx_ram_fw.ld b/targets/f7/stm32wb55xx_ram_fw.ld index 0ac9be4df..cae30b6e9 100644 --- a/targets/f7/stm32wb55xx_ram_fw.ld +++ b/targets/f7/stm32wb55xx_ram_fw.ld @@ -1,196 +1,132 @@ -/** -***************************************************************************** -** -** File : stm32wb55xx_ram_fw.ld -** -** Abstract : System Workbench Minimal System calls file -** -** For more information about which c-functions -** need which of these lowlevel functions -** please consult the Newlib libc-manual -** -** Environment : System Workbench for MCU -** -** Distribution: The file is distributed “as is,” without any warranty -** of any kind. -** -***************************************************************************** -** -**

© COPYRIGHT(c) 2019 Ac6

-** -** Redistribution and use in source and binary forms, with or without modification, -** are permitted provided that the following conditions are met: -** 1. Redistributions of source code must retain the above copyright notice, -** this list of conditions and the following disclaimer. -** 2. Redistributions in binary form must reproduce the above copyright notice, -** this list of conditions and the following disclaimer in the documentation -** and/or other materials provided with the distribution. -** 3. Neither the name of Ac6 nor the names of its contributors -** may be used to endorse or promote products derived from this software -** without specific prior written permission. -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -** AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -** DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -** OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -** -***************************************************************************** -*/ - -/* Entry Point */ ENTRY(Reset_Handler) /* Highest address of the user mode stack */ -_estack = 0x20030000; /* end of RAM */ +_stack_end = 0x20030000; /* end of RAM */ /* Generate a link error if heap and stack don't fit into RAM */ -_Min_Heap_Size = 0x400; /* required amount of heap */ -_Min_Stack_Size = 0x1000; /* required amount of stack */ +_stack_size = 0x1000; /* required amount of stack */ -/* Specify the memory areas */ -MEMORY -{ -FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024K -RAM1 (xrw) : ORIGIN = 0x20000000, LENGTH = 0x30000 -RAM2A (xrw) : ORIGIN = 0x20030000, LENGTH = 10K -RAM2B (xrw) : ORIGIN = 0x20038000, LENGTH = 10K +MEMORY { + FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024K + RAM1 (xrw) : ORIGIN = 0x20000000, LENGTH = 0x30000 + RAM2A (xrw) : ORIGIN = 0x20030000, LENGTH = 10K + RAM2B (xrw) : ORIGIN = 0x20038000, LENGTH = 10K } -/* Define output sections */ -SECTIONS -{ - /* The startup code goes first into FLASH */ - .isr_vector : - { +SECTIONS { + /* The startup code goes first into FLASH */ + .isr_vector : { + . = ALIGN(4); + KEEP(*(.isr_vector)) /* Startup code */ + . = ALIGN(4); + } >RAM1 + + /* The program code and other data goes into FLASH */ + .text : { + . = ALIGN(4); + *(.text) /* .text sections (code) */ + *(.text*) /* .text* sections (code) */ + *(.glue_7) /* glue arm to thumb code */ + *(.glue_7t) /* glue thumb to arm code */ + *(.eh_frame) + + KEEP (*(.init)) + KEEP (*(.fini)) + + . = ALIGN(4); + _etext = .; /* define a global symbols at end of code */ + } >RAM1 + + /* Constant data goes into FLASH */ + .rodata : { + . = ALIGN(4); + *(.rodata) /* .rodata sections (constants, strings, etc.) */ + *(.rodata*) /* .rodata* sections (constants, strings, etc.) */ + . = ALIGN(4); + } >RAM1 + + .ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH + .ARM : { + __exidx_start = .; + *(.ARM.exidx*) + __exidx_end = .; + } >RAM1 + + .preinit_array : { + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array*)) + PROVIDE_HIDDEN (__preinit_array_end = .); + } >RAM1 + .init_array : { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array*)) + PROVIDE_HIDDEN (__init_array_end = .); + } >RAM1 + .fini_array : { + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(SORT(.fini_array.*))) + KEEP (*(.fini_array*)) + PROVIDE_HIDDEN (__fini_array_end = .); + } >RAM1 + + /* used by the startup to initialize data */ + _sidata = LOADADDR(.data); + + /* Initialized data sections goes into RAM, load LMA copy after code */ + .data : { + . = ALIGN(4); + _sdata = .; /* create a global symbol at data start */ + *(.data) /* .data sections */ + *(.data*) /* .data* sections */ + + . = ALIGN(4); + _edata = .; /* define a global symbol at data end */ + } >RAM1 AT> RAM1 + + /* Uninitialized data section */ . = ALIGN(4); - KEEP(*(.isr_vector)) /* Startup code */ - . = ALIGN(4); - } >RAM1 + .bss : { + /* This is used by the startup in order to initialize the .bss secion */ + _sbss = .; /* define a global symbol at bss start */ + __bss_start__ = _sbss; + *(.bss) + *(.bss*) + *(COMMON) - /* The program code and other data goes into FLASH */ - .text : - { - . = ALIGN(4); - *(.text) /* .text sections (code) */ - *(.text*) /* .text* sections (code) */ - *(.glue_7) /* glue arm to thumb code */ - *(.glue_7t) /* glue thumb to arm code */ - *(.eh_frame) + . = ALIGN(4); + _ebss = .; /* define a global symbol at bss end */ + __bss_end__ = _ebss; + } >RAM1 - KEEP (*(.init)) - KEEP (*(.fini)) + /* User_heap_stack section, used to check that there is enough RAM left */ + /* DSECT: https://software-dl.ti.com/ccs/esd/documents/sdto_cgt_linker_special_section_types.html */ + ._user_heap_stack(DSECT) : { + . = ALIGN(8); + __heap_start__ = .; + . = ORIGIN(RAM1) + LENGTH(RAM1) - _stack_size; + __heap_end__ = .; + . = . + _stack_size; + . = ALIGN(8); + } >RAM1 - . = ALIGN(4); - _etext = .; /* define a global symbols at end of code */ - } >RAM1 + /* Free Flash space, that can be used for internal storage */ + /*.free_flash(DSECT) : { + __free_flash_start__ = .; + . = ORIGIN(FLASH) + LENGTH(FLASH); + } >FLASH*/ - /* Constant data goes into FLASH */ - .rodata : - { - . = ALIGN(4); - *(.rodata) /* .rodata sections (constants, strings, etc.) */ - *(.rodata*) /* .rodata* sections (constants, strings, etc.) */ - . = ALIGN(4); - } >RAM1 + /* Remove information from the standard libraries */ + /DISCARD/ : { + libc.a ( * ) + libm.a ( * ) + libgcc.a ( * ) + } - .ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH - .ARM : { - __exidx_start = .; - *(.ARM.exidx*) - __exidx_end = .; - } >RAM1 - - .preinit_array : - { - PROVIDE_HIDDEN (__preinit_array_start = .); - KEEP (*(.preinit_array*)) - PROVIDE_HIDDEN (__preinit_array_end = .); - } >RAM1 - .init_array : - { - PROVIDE_HIDDEN (__init_array_start = .); - KEEP (*(SORT(.init_array.*))) - KEEP (*(.init_array*)) - PROVIDE_HIDDEN (__init_array_end = .); - } >RAM1 - .fini_array : - { - PROVIDE_HIDDEN (__fini_array_start = .); - KEEP (*(SORT(.fini_array.*))) - KEEP (*(.fini_array*)) - PROVIDE_HIDDEN (__fini_array_end = .); - } >RAM1 - - /* used by the startup to initialize data */ - _sidata = LOADADDR(.data); - - /* Initialized data sections goes into RAM, load LMA copy after code */ - .data : - { - . = ALIGN(4); - _sdata = .; /* create a global symbol at data start */ - *(.data) /* .data sections */ - *(.data*) /* .data* sections */ - - . = ALIGN(4); - _edata = .; /* define a global symbol at data end */ - } >RAM1 AT> RAM1 - - - /* Uninitialized data section */ - . = ALIGN(4); - .bss : - { - /* This is used by the startup in order to initialize the .bss secion */ - _sbss = .; /* define a global symbol at bss start */ - __bss_start__ = _sbss; - *(.bss) - *(.bss*) - *(COMMON) - - . = ALIGN(4); - _ebss = .; /* define a global symbol at bss end */ - __bss_end__ = _ebss; - } >RAM1 - - /* User_heap_stack section, used to check that there is enough RAM left */ - ._user_heap_stack(DSECT) : - { - . = ALIGN(8); - __heap_start__ = .; - . = ORIGIN(RAM1) + LENGTH(RAM1) - _Min_Stack_Size; - __heap_end__ = .; - . = . + _Min_Stack_Size; - . = ALIGN(8); - } >RAM1 - - /* Free Flash space, that can be used for internal storage */ - /*.free_flash(DSECT): - { - __free_flash_start__ = .; - . = ORIGIN(FLASH) + LENGTH(FLASH); - } >FLASH*/ - - /* Remove information from the standard libraries */ - /DISCARD/ : - { - libc.a ( * ) - libm.a ( * ) - libgcc.a ( * ) - } - - .ARM.attributes 0 : { *(.ARM.attributes) } - ._sram2a_start : { . = ALIGN(4); __sram2a_start__ = .; } >RAM2A - MAPPING_TABLE (NOLOAD) : { *(MAPPING_TABLE) } >RAM2A - MB_MEM1 (NOLOAD) : { *(MB_MEM1) } >RAM2A - MB_MEM2 (NOLOAD) : { _sMB_MEM2 = . ; *(MB_MEM2) ; _eMB_MEM2 = . ; } >RAM2A - ._sram2a_free : { . = ALIGN(4); __sram2a_free__ = .; } >RAM2A - ._sram2b_start : { . = ALIGN(4); __sram2b_start__ = .; } >RAM2B + .ARM.attributes 0 : { *(.ARM.attributes) } + ._sram2a_start : { . = ALIGN(4); __sram2a_start__ = .; } >RAM2A + MAPPING_TABLE (NOLOAD) : { *(MAPPING_TABLE) } >RAM2A + MB_MEM1 (NOLOAD) : { *(MB_MEM1) } >RAM2A + MB_MEM2 (NOLOAD) : { _sMB_MEM2 = . ; *(MB_MEM2) ; _eMB_MEM2 = . ; } >RAM2A + ._sram2a_free : { . = ALIGN(4); __sram2a_free__ = .; } >RAM2A + ._sram2b_start : { . = ALIGN(4); __sram2b_start__ = .; } >RAM2B } - - diff --git a/targets/f7/target.json b/targets/f7/target.json index eae92a5cd..665864d7d 100644 --- a/targets/f7/target.json +++ b/targets/f7/target.json @@ -13,7 +13,6 @@ "ble_glue/services", "ble_glue/profiles" ], - "startup_script": "startup_stm32wb55xx_cm4.s", "linker_script_flash": "stm32wb55xx_flash.ld", "linker_script_ram": "stm32wb55xx_ram_fw.ld", "linker_script_app": "application_ext.ld", diff --git a/targets/furi_hal_include/furi_hal.h b/targets/furi_hal_include/furi_hal.h index 719df0275..cf483553f 100644 --- a/targets/furi_hal_include/furi_hal.h +++ b/targets/furi_hal_include/furi_hal.h @@ -55,9 +55,15 @@ void furi_hal_deinit_early(void); /** Init FuriHal */ void furi_hal_init(void); -/** Transfer execution to address +/** Jump to the void* * - * @param[in] address pointer to new executable + * Allow your code to transfer control to another firmware. + * + * @warning This code doesn't reset system before jump. Call it only from + * main thread, no kernel should be running. Ensure that no + * peripheral blocks active and no interrupts are pending. + * + * @param address The System Vector address(start of your new firmware) */ void furi_hal_switch(void* address); diff --git a/targets/furi_hal_include/furi_hal_mpu.h b/targets/furi_hal_include/furi_hal_mpu.h index 7a5759c17..1d91b123d 100644 --- a/targets/furi_hal_include/furi_hal_mpu.h +++ b/targets/furi_hal_include/furi_hal_mpu.h @@ -14,8 +14,9 @@ extern "C" { typedef enum { FuriHalMpuRegionNULL = 0x00, // region 0 used to protect null pointer dereference - FuriHalMpuRegionStack = 0x01, // region 1 used to protect stack - FuriHalMpuRegion2 = 0x02, + FuriHalMpuRegionMainStack = 0x01, // region 1 used to protect Main Stack + FuriHalMpuRegionThreadStack = + 0x02, // region 2 used to protect currently executed RTOS Thread Stack FuriHalMpuRegion3 = 0x03, FuriHalMpuRegion4 = 0x04, FuriHalMpuRegion5 = 0x05, From 6d8b050eda8d1e782f553880da66f4135f01a685 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Mon, 10 Jun 2024 18:53:08 +0100 Subject: [PATCH 2/3] [FL-3833] Furi: event loop (#3675) * Furi: epoll prototype * Gui: simplify view_dispatcher custom event processing * Furi: add missing critical sections to epoll * Furi: add epoll unit tests, fully implement level processing for in and out events * Furi: properly trigger epoll item event on adding mq, update tests. * Unit tests: cleanup defines * Furi: protect epoll from modification in callback * Furi: rename epoll into event_loop, cleanup api naming * Sync API Symbols * Furi: add event loop contract and link api, port mq to new api, cleanup code * Format Sources * Furi: cleanup mq and event loop code * Furi: remove unused staff from message queue * ApiSymbols: remove event loop from public APIs. * Fix furi unit tests --------- Co-authored-by: Georgii Surkov Co-authored-by: Georgii Surkov <37121527+gsurkov@users.noreply.github.com> --- .../unit_tests/tests/furi/furi_event_loop.c | 164 ++++++++ .../debug/unit_tests/tests/furi/furi_test.c | 8 +- .../debug/unit_tests/unit_test_api_table_i.h | 16 +- applications/services/gui/view_dispatcher.c | 133 ++++--- applications/services/gui/view_dispatcher.h | 18 +- applications/services/gui/view_dispatcher_i.h | 28 +- furi/core/base.h | 1 + furi/core/event_loop.c | 352 ++++++++++++++++++ furi/core/event_loop.h | 134 +++++++ furi/core/event_loop_i.h | 33 ++ furi/core/message_queue.c | 53 ++- furi/core/message_queue_i.h | 12 + furi/core/stream_buffer.c | 4 +- furi/core/stream_buffer.h | 4 +- furi/furi.h | 1 + targets/f18/api_symbols.csv | 8 + targets/f7/api_symbols.csv | 8 + targets/f7/inc/FreeRTOSConfig.h | 8 +- 18 files changed, 906 insertions(+), 79 deletions(-) create mode 100644 applications/debug/unit_tests/tests/furi/furi_event_loop.c create mode 100644 furi/core/event_loop.c create mode 100644 furi/core/event_loop.h create mode 100644 furi/core/event_loop_i.h create mode 100644 furi/core/message_queue_i.h diff --git a/applications/debug/unit_tests/tests/furi/furi_event_loop.c b/applications/debug/unit_tests/tests/furi/furi_event_loop.c new file mode 100644 index 000000000..06afc4fd9 --- /dev/null +++ b/applications/debug/unit_tests/tests/furi/furi_event_loop.c @@ -0,0 +1,164 @@ +#include "../test.h" +#include +#include + +#define TAG "TestFuriEventLoop" + +#define EVENT_LOOP_EVENT_COUNT (256u) + +typedef struct { + FuriMessageQueue* mq; + + FuriEventLoop* producer_event_loop; + uint32_t producer_counter; + + FuriEventLoop* consumer_event_loop; + uint32_t consumer_counter; +} TestFuriData; + +bool test_furi_event_loop_producer_mq_callback(FuriMessageQueue* queue, void* context) { + furi_check(context); + + TestFuriData* data = context; + furi_check(data->mq == queue, "Invalid queue"); + + FURI_LOG_I( + TAG, "producer_mq_callback: %lu %lu", data->producer_counter, data->consumer_counter); + + // Remove and add should not cause crash + // if(data->producer_counter == EVENT_LOOP_EVENT_COUNT/2) { + // furi_event_loop_message_queue_remove(data->producer_event_loop, data->mq); + // furi_event_loop_message_queue_add( + // data->producer_event_loop, + // data->mq, + // FuriEventLoopEventOut, + // test_furi_event_loop_producer_mq_callback, + // data); + // } + + if(data->producer_counter == EVENT_LOOP_EVENT_COUNT) { + furi_event_loop_stop(data->producer_event_loop); + return false; + } + + data->producer_counter++; + furi_check( + furi_message_queue_put(data->mq, &data->producer_counter, 0) == FuriStatusOk, + "furi_message_queue_put failed"); + furi_delay_us(furi_hal_random_get() % 1000); + + return true; +} + +int32_t test_furi_event_loop_producer(void* p) { + furi_check(p); + + FURI_LOG_I(TAG, "producer start"); + + TestFuriData* data = p; + + data->producer_event_loop = furi_event_loop_alloc(); + furi_event_loop_message_queue_subscribe( + data->producer_event_loop, + data->mq, + FuriEventLoopEventOut, + test_furi_event_loop_producer_mq_callback, + data); + + furi_event_loop_run(data->producer_event_loop); + + furi_event_loop_message_queue_unsubscribe(data->producer_event_loop, data->mq); + furi_event_loop_free(data->producer_event_loop); + + FURI_LOG_I(TAG, "producer end"); + + return 0; +} + +bool test_furi_event_loop_consumer_mq_callback(FuriMessageQueue* queue, void* context) { + furi_check(context); + + TestFuriData* data = context; + furi_check(data->mq == queue); + + furi_delay_us(furi_hal_random_get() % 1000); + furi_check(furi_message_queue_get(data->mq, &data->consumer_counter, 0) == FuriStatusOk); + + FURI_LOG_I( + TAG, "consumer_mq_callback: %lu %lu", data->producer_counter, data->consumer_counter); + + // Remove and add should not cause crash + // if(data->producer_counter == EVENT_LOOP_EVENT_COUNT/2) { + // furi_event_loop_message_queue_remove(data->consumer_event_loop, data->mq); + // furi_event_loop_message_queue_add( + // data->consumer_event_loop, + // data->mq, + // FuriEventLoopEventIn, + // test_furi_event_loop_producer_mq_callback, + // data); + // } + + if(data->consumer_counter == EVENT_LOOP_EVENT_COUNT) { + furi_event_loop_stop(data->consumer_event_loop); + return false; + } + + return true; +} + +int32_t test_furi_event_loop_consumer(void* p) { + furi_check(p); + + FURI_LOG_I(TAG, "consumer start"); + + TestFuriData* data = p; + + data->consumer_event_loop = furi_event_loop_alloc(); + furi_event_loop_message_queue_subscribe( + data->consumer_event_loop, + data->mq, + FuriEventLoopEventIn, + test_furi_event_loop_consumer_mq_callback, + data); + + furi_event_loop_run(data->consumer_event_loop); + + furi_event_loop_message_queue_unsubscribe(data->consumer_event_loop, data->mq); + furi_event_loop_free(data->consumer_event_loop); + + FURI_LOG_I(TAG, "consumer end"); + + return 0; +} + +void test_furi_event_loop(void) { + TestFuriData data = {}; + + data.mq = furi_message_queue_alloc(16, sizeof(uint32_t)); + + FuriThread* producer_thread = furi_thread_alloc(); + furi_thread_set_name(producer_thread, "producer_thread"); + furi_thread_set_stack_size(producer_thread, 1 * 1024); + furi_thread_set_callback(producer_thread, test_furi_event_loop_producer); + furi_thread_set_context(producer_thread, &data); + furi_thread_start(producer_thread); + + FuriThread* consumer_thread = furi_thread_alloc(); + furi_thread_set_name(consumer_thread, "consumer_thread"); + furi_thread_set_stack_size(consumer_thread, 1 * 1024); + furi_thread_set_callback(consumer_thread, test_furi_event_loop_consumer); + furi_thread_set_context(consumer_thread, &data); + furi_thread_start(consumer_thread); + + // Wait for thread to complete their tasks + furi_thread_join(producer_thread); + furi_thread_join(consumer_thread); + + // The test itself + mu_assert_int_eq(data.producer_counter, data.consumer_counter); + + // Release memory + furi_thread_free(consumer_thread); + furi_thread_free(producer_thread); + furi_message_queue_free(data.mq); +} diff --git a/applications/debug/unit_tests/tests/furi/furi_test.c b/applications/debug/unit_tests/tests/furi/furi_test.c index f08e4aa6b..be579d2b8 100644 --- a/applications/debug/unit_tests/tests/furi/furi_test.c +++ b/applications/debug/unit_tests/tests/furi/furi_test.c @@ -6,8 +6,8 @@ void test_furi_create_open(void); void test_furi_concurrent_access(void); void test_furi_pubsub(void); - void test_furi_memmgr(void); +void test_furi_event_loop(void); static int foo = 0; @@ -38,15 +38,19 @@ MU_TEST(mu_test_furi_memmgr) { test_furi_memmgr(); } +MU_TEST(mu_test_furi_event_loop) { + test_furi_event_loop(); +} + MU_TEST_SUITE(test_suite) { MU_SUITE_CONFIGURE(&test_setup, &test_teardown); - MU_RUN_TEST(test_check); // v2 tests MU_RUN_TEST(mu_test_furi_create_open); MU_RUN_TEST(mu_test_furi_pubsub); MU_RUN_TEST(mu_test_furi_memmgr); + MU_RUN_TEST(mu_test_furi_event_loop); } int run_minunit_test_furi(void) { diff --git a/applications/debug/unit_tests/unit_test_api_table_i.h b/applications/debug/unit_tests/unit_test_api_table_i.h index e6409f3ac..fc659aea9 100644 --- a/applications/debug/unit_tests/unit_test_api_table_i.h +++ b/applications/debug/unit_tests/unit_test_api_table_i.h @@ -6,11 +6,12 @@ #include #include +#include static constexpr auto unit_tests_api_table = sort(create_array_t( API_METHOD(resource_manifest_reader_alloc, ResourceManifestReader*, (Storage*)), API_METHOD(resource_manifest_reader_free, void, (ResourceManifestReader*)), - API_METHOD(resource_manifest_reader_open, bool, (ResourceManifestReader*, const char* filename)), + API_METHOD(resource_manifest_reader_open, bool, (ResourceManifestReader*, const char*)), API_METHOD(resource_manifest_reader_next, ResourceManifestEntry*, (ResourceManifestReader*)), API_METHOD(resource_manifest_reader_previous, ResourceManifestEntry*, (ResourceManifestReader*)), API_METHOD(slix_process_iso15693_3_error, SlixError, (Iso15693_3Error)), @@ -26,4 +27,17 @@ static constexpr auto unit_tests_api_table = sort(create_array_t( xQueueGenericSend, BaseType_t, (QueueHandle_t, const void* const, TickType_t, const BaseType_t)), + API_METHOD(furi_event_loop_alloc, FuriEventLoop*, (void)), + API_METHOD(furi_event_loop_free, void, (FuriEventLoop*)), + API_METHOD( + furi_event_loop_message_queue_subscribe, + void, + (FuriEventLoop*, + FuriMessageQueue*, + FuriEventLoopEvent, + FuriEventLoopMessageQueueCallback, + void*)), + API_METHOD(furi_event_loop_message_queue_unsubscribe, void, (FuriEventLoop*, FuriMessageQueue*)), + API_METHOD(furi_event_loop_run, void, (FuriEventLoop*)), + API_METHOD(furi_event_loop_stop, void, (FuriEventLoop*)), API_VARIABLE(PB_Main_msg, PB_Main_msg_t))); diff --git a/applications/services/gui/view_dispatcher.c b/applications/services/gui/view_dispatcher.c index d4c2f61e7..0d0437736 100644 --- a/applications/services/gui/view_dispatcher.c +++ b/applications/services/gui/view_dispatcher.c @@ -29,8 +29,18 @@ void view_dispatcher_free(ViewDispatcher* view_dispatcher) { // Free ViewPort view_port_free(view_dispatcher->view_port); // Free internal queue - if(view_dispatcher->queue) { - furi_message_queue_free(view_dispatcher->queue); + if(view_dispatcher->input_queue) { + furi_event_loop_message_queue_unsubscribe( + view_dispatcher->event_loop, view_dispatcher->input_queue); + furi_message_queue_free(view_dispatcher->input_queue); + } + if(view_dispatcher->event_queue) { + furi_event_loop_message_queue_unsubscribe( + view_dispatcher->event_loop, view_dispatcher->event_queue); + furi_message_queue_free(view_dispatcher->event_queue); + } + if(view_dispatcher->event_loop) { + furi_event_loop_free(view_dispatcher->event_loop); } // Free dispatcher free(view_dispatcher); @@ -38,8 +48,25 @@ void view_dispatcher_free(ViewDispatcher* view_dispatcher) { void view_dispatcher_enable_queue(ViewDispatcher* view_dispatcher) { furi_check(view_dispatcher); - furi_check(view_dispatcher->queue == NULL); - view_dispatcher->queue = furi_message_queue_alloc(16, sizeof(ViewDispatcherMessage)); + furi_check(view_dispatcher->event_loop == NULL); + + view_dispatcher->event_loop = furi_event_loop_alloc(); + + view_dispatcher->input_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); + furi_event_loop_message_queue_subscribe( + view_dispatcher->event_loop, + view_dispatcher->input_queue, + FuriEventLoopEventIn, + view_dispatcher_run_input_callback, + view_dispatcher); + + view_dispatcher->event_queue = furi_message_queue_alloc(8, sizeof(uint32_t)); + furi_event_loop_message_queue_subscribe( + view_dispatcher->event_loop, + view_dispatcher->event_queue, + FuriEventLoopEventIn, + view_dispatcher_run_event_callback, + view_dispatcher); } void view_dispatcher_set_event_callback_context(ViewDispatcher* view_dispatcher, void* context) { @@ -70,48 +97,45 @@ void view_dispatcher_set_tick_event_callback( view_dispatcher->tick_period = tick_period; } +FuriEventLoop* view_dispatcher_get_event_loop(ViewDispatcher* view_dispatcher) { + furi_check(view_dispatcher); + furi_check(view_dispatcher->event_loop); + + return view_dispatcher->event_loop; +} + void view_dispatcher_run(ViewDispatcher* view_dispatcher) { furi_check(view_dispatcher); - furi_check(view_dispatcher->queue); + furi_check(view_dispatcher->event_loop); uint32_t tick_period = view_dispatcher->tick_period == 0 ? FuriWaitForever : view_dispatcher->tick_period; - ViewDispatcherMessage message; - while(1) { - if(furi_message_queue_get(view_dispatcher->queue, &message, tick_period) != FuriStatusOk) { - view_dispatcher_handle_tick_event(view_dispatcher); - continue; - } - if(message.type == ViewDispatcherMessageTypeStop) { - break; - } else if(message.type == ViewDispatcherMessageTypeInput) { - view_dispatcher_handle_input(view_dispatcher, &message.input); - } else if(message.type == ViewDispatcherMessageTypeCustomEvent) { - view_dispatcher_handle_custom_event(view_dispatcher, message.custom_event); - } - } + + furi_event_loop_tick_set( + view_dispatcher->event_loop, + tick_period, + view_dispatcher_handle_tick_event, + view_dispatcher); + + furi_event_loop_run(view_dispatcher->event_loop); // Wait till all input events delivered + InputEvent input; while(view_dispatcher->ongoing_input) { - furi_message_queue_get(view_dispatcher->queue, &message, FuriWaitForever); - if(message.type == ViewDispatcherMessageTypeInput) { - uint8_t key_bit = (1 << message.input.key); - if(message.input.type == InputTypePress) { - view_dispatcher->ongoing_input |= key_bit; - } else if(message.input.type == InputTypeRelease) { - view_dispatcher->ongoing_input &= ~key_bit; - } + furi_message_queue_get(view_dispatcher->input_queue, &input, FuriWaitForever); + uint8_t key_bit = (1 << input.key); + if(input.type == InputTypePress) { + view_dispatcher->ongoing_input |= key_bit; + } else if(input.type == InputTypeRelease) { + view_dispatcher->ongoing_input &= ~key_bit; } } } void view_dispatcher_stop(ViewDispatcher* view_dispatcher) { furi_check(view_dispatcher); - furi_check(view_dispatcher->queue); - ViewDispatcherMessage message; - message.type = ViewDispatcherMessageTypeStop; - furi_check( - furi_message_queue_put(view_dispatcher->queue, &message, FuriWaitForever) == FuriStatusOk); + furi_check(view_dispatcher->event_loop); + furi_event_loop_stop(view_dispatcher->event_loop); } void view_dispatcher_add_view(ViewDispatcher* view_dispatcher, uint32_t view_id, View* view) { @@ -218,12 +242,9 @@ void view_dispatcher_draw_callback(Canvas* canvas, void* context) { void view_dispatcher_input_callback(InputEvent* event, void* context) { ViewDispatcher* view_dispatcher = context; - if(view_dispatcher->queue) { - ViewDispatcherMessage message; - message.type = ViewDispatcherMessageTypeInput; - message.input = *event; + if(view_dispatcher->input_queue) { furi_check( - furi_message_queue_put(view_dispatcher->queue, &message, FuriWaitForever) == + furi_message_queue_put(view_dispatcher->input_queue, event, FuriWaitForever) == FuriStatusOk); } else { view_dispatcher_handle_input(view_dispatcher, event); @@ -287,7 +308,8 @@ void view_dispatcher_handle_input(ViewDispatcher* view_dispatcher, InputEvent* e } } -void view_dispatcher_handle_tick_event(ViewDispatcher* view_dispatcher) { +void view_dispatcher_handle_tick_event(void* context) { + ViewDispatcher* view_dispatcher = context; if(view_dispatcher->tick_event_callback) { view_dispatcher->tick_event_callback(view_dispatcher->event_context); } @@ -306,14 +328,11 @@ void view_dispatcher_handle_custom_event(ViewDispatcher* view_dispatcher, uint32 void view_dispatcher_send_custom_event(ViewDispatcher* view_dispatcher, uint32_t event) { furi_check(view_dispatcher); - furi_check(view_dispatcher->queue); - - ViewDispatcherMessage message; - message.type = ViewDispatcherMessageTypeCustomEvent; - message.custom_event = event; + furi_check(view_dispatcher->event_loop); furi_check( - furi_message_queue_put(view_dispatcher->queue, &message, FuriWaitForever) == FuriStatusOk); + furi_message_queue_put(view_dispatcher->event_queue, &event, FuriWaitForever) == + FuriStatusOk); } static const ViewPortOrientation view_dispatcher_view_port_orientation_table[] = { @@ -345,7 +364,7 @@ void view_dispatcher_set_current_view(ViewDispatcher* view_dispatcher, View* vie view_port_update(view_dispatcher->view_port); } else { view_port_enabled_set(view_dispatcher->view_port, false); - if(view_dispatcher->queue) { + if(view_dispatcher->event_loop) { view_dispatcher_stop(view_dispatcher); } } @@ -361,3 +380,27 @@ void view_dispatcher_update(View* view, void* context) { view_port_update(view_dispatcher->view_port); } } + +bool view_dispatcher_run_event_callback(FuriMessageQueue* queue, void* context) { + furi_assert(context); + ViewDispatcher* instance = context; + furi_assert(instance->event_queue == queue); + + uint32_t event; + furi_check(furi_message_queue_get(instance->event_queue, &event, 0) == FuriStatusOk); + view_dispatcher_handle_custom_event(instance, event); + + return true; +} + +bool view_dispatcher_run_input_callback(FuriMessageQueue* queue, void* context) { + furi_assert(context); + ViewDispatcher* instance = context; + furi_assert(instance->input_queue == queue); + + InputEvent input; + furi_check(furi_message_queue_get(instance->input_queue, &input, 0) == FuriStatusOk); + view_dispatcher_handle_input(instance, &input); + + return true; +} diff --git a/applications/services/gui/view_dispatcher.h b/applications/services/gui/view_dispatcher.h index f8567ea1a..7627e5a0b 100644 --- a/applications/services/gui/view_dispatcher.h +++ b/applications/services/gui/view_dispatcher.h @@ -47,8 +47,8 @@ void view_dispatcher_free(ViewDispatcher* view_dispatcher); /** Enable queue support * - * If queue enabled all input and custom events will be dispatched throw - * internal queue + * Allocates event_loop, input and event message queues. Must be used with + * `view_dispatcher_run` * * @param view_dispatcher ViewDispatcher instance */ @@ -101,6 +101,20 @@ void view_dispatcher_set_tick_event_callback( */ void view_dispatcher_set_event_callback_context(ViewDispatcher* view_dispatcher, void* context); +/** Get event_loop instance + * + * event_loop instance is allocated on `view_dispatcher_enable_queue` and used + * in view_dispatcher_run. + * + * You can add your objects into event_loop instance, but don't run the loop on + * your side it will cause issues with input processing on dispatcher stop. + * + * @param view_dispatcher ViewDispatcher instance + * + * @return The event_loop instance. + */ +FuriEventLoop* view_dispatcher_get_event_loop(ViewDispatcher* view_dispatcher); + /** Run ViewDispatcher * * Use only after queue enabled diff --git a/applications/services/gui/view_dispatcher_i.h b/applications/services/gui/view_dispatcher_i.h index f30a84e6b..fcf426c31 100644 --- a/applications/services/gui/view_dispatcher_i.h +++ b/applications/services/gui/view_dispatcher_i.h @@ -5,7 +5,6 @@ #pragma once -#include #include #include "view_dispatcher.h" @@ -15,7 +14,10 @@ DICT_DEF2(ViewDict, uint32_t, M_DEFAULT_OPLIST, View*, M_PTR_OPLIST) struct ViewDispatcher { - FuriMessageQueue* queue; + FuriEventLoop* event_loop; + FuriMessageQueue* input_queue; + FuriMessageQueue* event_queue; + Gui* gui; ViewPort* view_port; ViewDict_t views; @@ -32,20 +34,6 @@ struct ViewDispatcher { void* event_context; }; -typedef enum { - ViewDispatcherMessageTypeInput, - ViewDispatcherMessageTypeCustomEvent, - ViewDispatcherMessageTypeStop, -} ViewDispatcherMessageType; - -typedef struct { - ViewDispatcherMessageType type; - union { - InputEvent input; - uint32_t custom_event; - }; -} ViewDispatcherMessage; - /** ViewPort Draw Callback */ void view_dispatcher_draw_callback(Canvas* canvas, void* context); @@ -56,7 +44,7 @@ void view_dispatcher_input_callback(InputEvent* event, void* context); void view_dispatcher_handle_input(ViewDispatcher* view_dispatcher, InputEvent* event); /** Tick handler */ -void view_dispatcher_handle_tick_event(ViewDispatcher* view_dispatcher); +void view_dispatcher_handle_tick_event(void* context); /** Custom event handler */ void view_dispatcher_handle_custom_event(ViewDispatcher* view_dispatcher, uint32_t event); @@ -66,3 +54,9 @@ void view_dispatcher_set_current_view(ViewDispatcher* view_dispatcher, View* vie /** ViewDispatcher update event */ void view_dispatcher_update(View* view, void* context); + +/** ViewDispatcher run event loop event callback */ +bool view_dispatcher_run_event_callback(FuriMessageQueue* queue, void* context); + +/** ViewDispatcher run event loop input callback */ +bool view_dispatcher_run_input_callback(FuriMessageQueue* queue, void* context); diff --git a/furi/core/base.h b/furi/core/base.h index 642ff2b6c..92a52a797 100644 --- a/furi/core/base.h +++ b/furi/core/base.h @@ -2,6 +2,7 @@ #include #include +#include #include #ifdef __cplusplus diff --git a/furi/core/event_loop.c b/furi/core/event_loop.c new file mode 100644 index 000000000..f22834692 --- /dev/null +++ b/furi/core/event_loop.c @@ -0,0 +1,352 @@ +#include "event_loop_i.h" +#include "message_queue_i.h" + +#include "check.h" +#include "thread.h" + +#include +#include + +#include +#include + +struct FuriEventLoopItem { + // Source + FuriEventLoop* owner; + + // Tracking item + const FuriEventLoopContract* contract; + void* object; + FuriEventLoopEvent event; + + // Callback and context + FuriEventLoopMessageQueueCallback callback; + void* callback_context; + + // Waiting list + ILIST_INTERFACE(WaitingList, struct FuriEventLoopItem); +}; + +ILIST_DEF(WaitingList, FuriEventLoopItem, M_POD_OPLIST) + +static FuriEventLoopItem* furi_event_loop_item_alloc( + FuriEventLoop* owner, + const FuriEventLoopContract* contract, + void* object, + FuriEventLoopEvent event); + +static void furi_event_loop_item_free(FuriEventLoopItem* instance); + +static void furi_event_loop_item_set_callback( + FuriEventLoopItem* instance, + FuriEventLoopMessageQueueCallback callback, + void* callback_context); + +static void furi_event_loop_item_notify(FuriEventLoopItem* instance); + +/* Event Loop RB tree */ +#define FURI_EVENT_LOOP_TREE_RANK (4) + +BPTREE_DEF2( // NOLINT + FuriEventLoopTree, + FURI_EVENT_LOOP_TREE_RANK, + void*, /* pointer to object we track */ + M_PTR_OPLIST, + FuriEventLoopItem*, /* pointer to the FuriEventLoopItem */ + M_PTR_OPLIST) + +#define M_OPL_FuriEventLoopTree_t() BPTREE_OPLIST(FuriEventLoopTree, M_POD_OPLIST) + +#define FURI_EVENT_LOOP_FLAG_NOTIFY_INDEX (2) + +typedef enum { + FuriEventLoopFlagEvent = (1 << 0), + FuriEventLoopFlagStop = (1 << 1), +} FuriEventLoopFlag; + +#define FuriEventLoopFlagAll (FuriEventLoopFlagEvent | FuriEventLoopFlagStop) + +typedef enum { + FuriEventLoopProcessStatusComplete, + FuriEventLoopProcessStatusIncomplete, + FuriEventLoopProcessStatusAgain, +} FuriEventLoopProcessStatus; + +typedef enum { + FuriEventLoopStateIdle, + FuriEventLoopStateProcessing, +} FuriEventLoopState; + +struct FuriEventLoop { + // Only works if all operations are done from the same thread + FuriThreadId thread_id; + + // Poller state + volatile FuriEventLoopState state; + + // Tree + FuriEventLoopTree_t tree; + // Tree waiting list + WaitingList_t waiting_list; + + // Tick event + uint32_t tick_interval; + FuriEventLoopTickCallback tick_callback; + void* tick_callback_context; +}; + +FuriEventLoop* furi_event_loop_alloc(void) { + FuriEventLoop* instance = malloc(sizeof(FuriEventLoop)); + + instance->thread_id = furi_thread_get_current_id(); + FuriEventLoopTree_init(instance->tree); + WaitingList_init(instance->waiting_list); + + return instance; +} + +void furi_event_loop_free(FuriEventLoop* instance) { + furi_check(instance); + furi_check(instance->thread_id == furi_thread_get_current_id()); + + FuriEventLoopTree_clear(instance->tree); + free(instance); +} + +static FuriEventLoopProcessStatus + furi_event_loop_poll_process_event(FuriEventLoop* instance, FuriEventLoopItem* item) { + UNUSED(instance); + + if(!item->contract->get_level(item->object, item->event)) { + return FuriEventLoopProcessStatusComplete; + } + + if(item->callback(item->object, item->callback_context)) { + return FuriEventLoopProcessStatusIncomplete; + } else { + return FuriEventLoopProcessStatusAgain; + } +} + +void furi_event_loop_run(FuriEventLoop* instance) { + furi_check(instance); + furi_check(instance->thread_id == furi_thread_get_current_id()); + + uint32_t timeout = instance->tick_callback ? instance->tick_interval : FuriWaitForever; + + while(true) { + uint32_t flags = 0; + BaseType_t ret = xTaskNotifyWaitIndexed( + FURI_EVENT_LOOP_FLAG_NOTIFY_INDEX, 0, FuriEventLoopFlagAll, &flags, timeout); + + instance->state = FuriEventLoopStateProcessing; + if(ret == pdTRUE) { + if(flags & FuriEventLoopFlagStop) { + instance->state = FuriEventLoopStateIdle; + break; + } else if(flags & FuriEventLoopFlagEvent) { + FuriEventLoopItem* item = NULL; + FURI_CRITICAL_ENTER(); + if(!WaitingList_empty_p(instance->waiting_list)) { + item = WaitingList_pop_front(instance->waiting_list); + WaitingList_init_field(item); + } + FURI_CRITICAL_EXIT(); + if(item) { + while(true) { + FuriEventLoopProcessStatus ret = + furi_event_loop_poll_process_event(instance, item); + if(ret == FuriEventLoopProcessStatusComplete) { + // Event processing complete, break from loop + break; + } else if(ret == FuriEventLoopProcessStatusIncomplete) { + // Event processing incomplete more processing needed + } else if(ret == FuriEventLoopProcessStatusAgain) { //-V547 + furi_event_loop_item_notify(item); + break; + } else { + furi_crash(); + } + } + } + } + } else { + if(instance->tick_callback) { + instance->tick_callback(instance->tick_callback_context); + } + } + instance->state = FuriEventLoopStateIdle; + } +} + +void furi_event_loop_stop(FuriEventLoop* instance) { + furi_check(instance); + furi_check(instance->thread_id == furi_thread_get_current_id()); + + xTaskNotifyIndexed( + instance->thread_id, FURI_EVENT_LOOP_FLAG_NOTIFY_INDEX, FuriEventLoopFlagStop, eSetBits); +} + +void furi_event_loop_tick_set( + FuriEventLoop* instance, + uint32_t interval, + FuriEventLoopTickCallback callback, + void* context) { + furi_check(instance); + furi_check(instance->thread_id == furi_thread_get_current_id()); + furi_check(callback ? interval > 0 : true); + + instance->tick_interval = interval; + instance->tick_callback = callback; + instance->tick_callback_context = context; +} + +void furi_event_loop_message_queue_subscribe( + FuriEventLoop* instance, + FuriMessageQueue* message_queue, + FuriEventLoopEvent event, + FuriEventLoopMessageQueueCallback callback, + void* context) { + furi_check(instance); + furi_check(instance->thread_id == furi_thread_get_current_id()); + furi_check(instance->state == FuriEventLoopStateIdle); + furi_check(message_queue); + + FURI_CRITICAL_ENTER(); + + furi_check(FuriEventLoopTree_get(instance->tree, message_queue) == NULL); + + // Allocate and setup item + FuriEventLoopItem* item = furi_event_loop_item_alloc( + instance, &furi_message_queue_event_loop_contract, message_queue, event); + furi_event_loop_item_set_callback(item, callback, context); + + FuriEventLoopTree_set_at(instance->tree, message_queue, item); + + FuriEventLoopLink* link = item->contract->get_link(message_queue); + + if(item->event == FuriEventLoopEventIn) { + furi_check(link->item_in == NULL); + link->item_in = item; + } else if(item->event == FuriEventLoopEventOut) { + furi_check(link->item_out == NULL); + link->item_out = item; + } else { + furi_crash(); + } + + if(item->contract->get_level(item->object, item->event)) { + furi_event_loop_item_notify(item); + } + + FURI_CRITICAL_EXIT(); +} + +void furi_event_loop_message_queue_unsubscribe( + FuriEventLoop* instance, + FuriMessageQueue* message_queue) { + furi_check(instance); + furi_check(instance->state == FuriEventLoopStateIdle); + furi_check(instance->thread_id == furi_thread_get_current_id()); + + FURI_CRITICAL_ENTER(); + + FuriEventLoopItem** item_ptr = FuriEventLoopTree_get(instance->tree, message_queue); + furi_check(item_ptr); + + FuriEventLoopItem* item = *item_ptr; + furi_check(item); + furi_check(item->owner == instance); + + FuriEventLoopLink* link = item->contract->get_link(message_queue); + + if(item->event == FuriEventLoopEventIn) { + furi_check(link->item_in == item); + link->item_in = NULL; + } else if(item->event == FuriEventLoopEventOut) { + furi_check(link->item_out == item); + link->item_out = NULL; + } else { + furi_crash(); + } + + furi_event_loop_item_free(item); + + FuriEventLoopTree_erase(instance->tree, message_queue); + + FURI_CRITICAL_EXIT(); +} + +/* + * Event Loop Item API, used internally + */ + +static FuriEventLoopItem* furi_event_loop_item_alloc( + FuriEventLoop* owner, + const FuriEventLoopContract* contract, + void* object, + FuriEventLoopEvent event) { + furi_assert(owner); + furi_assert(object); + + FuriEventLoopItem* instance = malloc(sizeof(FuriEventLoopItem)); + + instance->owner = owner; + instance->contract = contract; + instance->object = object; + instance->event = event; + + WaitingList_init_field(instance); + + return instance; +} + +static void furi_event_loop_item_free(FuriEventLoopItem* instance) { + furi_assert(instance); + free(instance); +} + +static void furi_event_loop_item_set_callback( + FuriEventLoopItem* instance, + FuriEventLoopMessageQueueCallback callback, + void* callback_context) { + furi_assert(instance); + furi_assert(!instance->callback); + + instance->callback = callback; + instance->callback_context = callback_context; +} + +static void furi_event_loop_item_notify(FuriEventLoopItem* instance) { + furi_assert(instance); + + FURI_CRITICAL_ENTER(); + + if(!instance->WaitingList.prev && !instance->WaitingList.next) { + WaitingList_push_back(instance->owner->waiting_list, instance); + } + + FURI_CRITICAL_EXIT(); + + xTaskNotifyIndexed( + instance->owner->thread_id, + FURI_EVENT_LOOP_FLAG_NOTIFY_INDEX, + FuriEventLoopFlagEvent, + eSetBits); +} + +void furi_event_loop_link_notify(FuriEventLoopLink* instance, FuriEventLoopEvent event) { + furi_assert(instance); + + FURI_CRITICAL_ENTER(); + + if(event == FuriEventLoopEventIn) { + if(instance->item_in) furi_event_loop_item_notify(instance->item_in); + } else if(event == FuriEventLoopEventOut) { + if(instance->item_out) furi_event_loop_item_notify(instance->item_out); + } else { + furi_crash(); + } + + FURI_CRITICAL_EXIT(); +} \ No newline at end of file diff --git a/furi/core/event_loop.h b/furi/core/event_loop.h new file mode 100644 index 000000000..7221a90bc --- /dev/null +++ b/furi/core/event_loop.h @@ -0,0 +1,134 @@ +/** + * @file event_loop.h + * @brief Furi Event Loop + * + * This module is designed to handle application event loop in fully + * asynchronous, reactive nature. On the low level this modules is + * inspired by epoll/kqueue concept, on the high level by asyncio + * event loop. + * + * This module is trying to best fit into Furi OS, so we don't + * provide any compatibility with other event driven APIs. But + * programming concepts are the same, except some runtime + * limitations from our side. + */ +#pragma once + +#include "base.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Event Loop events */ +typedef enum { + FuriEventLoopEventOut, /**< On departure: item was retrieved from container, flag reset, etc... */ + FuriEventLoopEventIn, /**< On arrival: item was inserted into container, flag set, etc... */ +} FuriEventLoopEvent; + +/** Anonymous message queue type */ +typedef struct FuriEventLoop FuriEventLoop; + +/** Allocate Event Loop instance + * + * Couple things to keep in mind: + * - You can have 1 event_loop per 1 thread + * - You can not use event_loop instance in the other thread + * - Do not use blocking api to query object delegated to Event Loop + * + * @return The Event Loop instance + */ +FuriEventLoop* furi_event_loop_alloc(void); + +/** Free Event Loop instance + * + * @param instance The Event Loop instance + */ +void furi_event_loop_free(FuriEventLoop* instance); + +/** Continuously poll for events + * + * Can be stopped with `furi_event_loop_stop` + * + * @param instance The Event Loop instance + */ +void furi_event_loop_run(FuriEventLoop* instance); + +/** Stop Event Loop instance + * + * @param instance The Event Loop instance + */ +void furi_event_loop_stop(FuriEventLoop* instance); + +/* + * Tick related API + */ + +/** Tick callback type + * + * @param context The context for callback + */ +typedef void (*FuriEventLoopTickCallback)(void* context); + +/** Set Event Loop tick callback + * + * Tick callback called after specified inactivity time. It's not periodic. If + * Event Loop is busy then ticks will be skipped. + * + * @param instance The Event Loop instance + * @param[in] interval The tick interval + * @param[in] callback The callback to call + * @param context The context for callback + */ +void furi_event_loop_tick_set( + FuriEventLoop* instance, + uint32_t interval, + FuriEventLoopTickCallback callback, + void* context); + +/* + * Message queue related APIs + */ + +/** Anonymous message queue type */ +typedef struct FuriMessageQueue FuriMessageQueue; + +/** Callback type for message queue + * + * @param queue The queue that triggered event + * @param context The context that was provided on + * furi_event_loop_message_queue_subscribe call + * + * @return true if event was processed, false if we need to delay processing + */ +typedef bool (*FuriEventLoopMessageQueueCallback)(FuriMessageQueue* queue, void* context); + +/** Subscribe to message queue events + * + * @warning you can only have one subscription for one event type. + * + * @param instance The Event Loop instance + * @param message_queue The message queue to add + * @param[in] event The Event Loop event to trigger on + * @param[in] callback The callback to call on event + * @param context The context for callback + */ +void furi_event_loop_message_queue_subscribe( + FuriEventLoop* instance, + FuriMessageQueue* message_queue, + FuriEventLoopEvent event, + FuriEventLoopMessageQueueCallback callback, + void* context); + +/** Unsubscribe from message queue + * + * @param instance The Event Loop instance + * @param message_queue The message queue + */ +void furi_event_loop_message_queue_unsubscribe( + FuriEventLoop* instance, + FuriMessageQueue* message_queue); + +#ifdef __cplusplus +} +#endif diff --git a/furi/core/event_loop_i.h b/furi/core/event_loop_i.h new file mode 100644 index 000000000..8ddd10966 --- /dev/null +++ b/furi/core/event_loop_i.h @@ -0,0 +1,33 @@ +#pragma once + +#include "event_loop.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct FuriEventLoopItem FuriEventLoopItem; + +/* Link between Event Loop */ + +typedef struct { + FuriEventLoopItem* item_in; + FuriEventLoopItem* item_out; +} FuriEventLoopLink; + +void furi_event_loop_link_notify(FuriEventLoopLink* instance, FuriEventLoopEvent event); + +/* Contract between event loop and an object */ + +typedef FuriEventLoopLink* (*FuriEventLoopContractGetLink)(void* object); + +typedef uint32_t (*FuriEventLoopContractGetLevel)(void* object, FuriEventLoopEvent event); + +typedef struct { + const FuriEventLoopContractGetLink get_link; + const FuriEventLoopContractGetLevel get_level; +} FuriEventLoopContract; + +#ifdef __cplusplus +} +#endif diff --git a/furi/core/message_queue.c b/furi/core/message_queue.c index 0454e289b..cda775abe 100644 --- a/furi/core/message_queue.c +++ b/furi/core/message_queue.c @@ -1,9 +1,4 @@ -#include "kernel.h" -#include "message_queue.h" -#include "check.h" - -#include -#include +#include "message_queue_i.h" // Internal FreeRTOS member names #define uxMessagesWaiting uxDummy4[0] @@ -12,6 +7,10 @@ struct FuriMessageQueue { StaticQueue_t container; + + // Event Loop Link + FuriEventLoopLink event_loop_link; + uint8_t buffer[]; }; @@ -42,6 +41,10 @@ void furi_message_queue_free(FuriMessageQueue* instance) { furi_check(furi_kernel_is_irq_or_masked() == 0U); furi_check(instance); + // Event Loop must be disconnected + furi_check(!instance->event_loop_link.item_in); + furi_check(!instance->event_loop_link.item_out); + vQueueDelete((QueueHandle_t)instance); free(instance); } @@ -82,6 +85,11 @@ FuriStatus } } + if(stat == FuriStatusOk) { + furi_event_loop_link_notify(&instance->event_loop_link, FuriEventLoopEventIn); + } + + /* Return execution status */ return stat; } @@ -120,6 +128,10 @@ FuriStatus furi_message_queue_get(FuriMessageQueue* instance, void* msg_ptr, uin } } + if(stat == FuriStatusOk) { + furi_event_loop_link_notify(&instance->event_loop_link, FuriEventLoopEventOut); + } + return stat; } @@ -182,5 +194,34 @@ FuriStatus furi_message_queue_reset(FuriMessageQueue* instance) { (void)xQueueReset(hQueue); } + if(stat == FuriStatusOk) { + furi_event_loop_link_notify(&instance->event_loop_link, FuriEventLoopEventOut); + } + + /* Return execution status */ return stat; } + +static FuriEventLoopLink* furi_message_queue_event_loop_get_link(void* object) { + FuriMessageQueue* instance = object; + furi_assert(instance); + return &instance->event_loop_link; +} + +static uint32_t furi_message_queue_event_loop_get_level(void* object, FuriEventLoopEvent event) { + FuriMessageQueue* instance = object; + furi_assert(instance); + + if(event == FuriEventLoopEventIn) { + return furi_message_queue_get_count(instance); + } else if(event == FuriEventLoopEventOut) { + return furi_message_queue_get_space(instance); + } else { + furi_crash(); + } +} + +const FuriEventLoopContract furi_message_queue_event_loop_contract = { + .get_link = furi_message_queue_event_loop_get_link, + .get_level = furi_message_queue_event_loop_get_level, +}; diff --git a/furi/core/message_queue_i.h b/furi/core/message_queue_i.h new file mode 100644 index 000000000..aa24cfe54 --- /dev/null +++ b/furi/core/message_queue_i.h @@ -0,0 +1,12 @@ +#pragma once + +#include "message_queue.h" + +#include "kernel.h" +#include "event_loop_i.h" +#include "check.h" + +#include +#include + +extern const FuriEventLoopContract furi_message_queue_event_loop_contract; \ No newline at end of file diff --git a/furi/core/stream_buffer.c b/furi/core/stream_buffer.c index eefda0e79..879520010 100644 --- a/furi/core/stream_buffer.c +++ b/furi/core/stream_buffer.c @@ -1,6 +1,6 @@ -#include "base.h" -#include "check.h" #include "stream_buffer.h" + +#include "check.h" #include "common_defines.h" #include diff --git a/furi/core/stream_buffer.h b/furi/core/stream_buffer.h index 3cc9c1b67..eef8ee510 100644 --- a/furi/core/stream_buffer.h +++ b/furi/core/stream_buffer.h @@ -12,8 +12,8 @@ * interrupt that will read from the buffer (the reader). */ #pragma once -#include -#include + +#include "base.h" #ifdef __cplusplus extern "C" { diff --git a/furi/furi.h b/furi/furi.h index d8aec91c0..24e597acf 100644 --- a/furi/furi.h +++ b/furi/furi.h @@ -4,6 +4,7 @@ #include "core/check.h" #include "core/common_defines.h" +#include "core/event_loop.h" #include "core/event_flag.h" #include "core/kernel.h" #include "core/log.h" diff --git a/targets/f18/api_symbols.csv b/targets/f18/api_symbols.csv index 91999f7a0..d732e991d 100644 --- a/targets/f18/api_symbols.csv +++ b/targets/f18/api_symbols.csv @@ -1098,6 +1098,13 @@ Function,+,furi_event_flag_free,void,FuriEventFlag* Function,+,furi_event_flag_get,uint32_t,FuriEventFlag* Function,+,furi_event_flag_set,uint32_t,"FuriEventFlag*, uint32_t" Function,+,furi_event_flag_wait,uint32_t,"FuriEventFlag*, uint32_t, uint32_t, uint32_t" +Function,-,furi_event_loop_alloc,FuriEventLoop*, +Function,-,furi_event_loop_free,void,FuriEventLoop* +Function,-,furi_event_loop_message_queue_subscribe,void,"FuriEventLoop*, FuriMessageQueue*, FuriEventLoopEvent, FuriEventLoopMessageQueueCallback, void*" +Function,-,furi_event_loop_message_queue_unsubscribe,void,"FuriEventLoop*, FuriMessageQueue*" +Function,-,furi_event_loop_run,void,FuriEventLoop* +Function,-,furi_event_loop_stop,void,FuriEventLoop* +Function,-,furi_event_loop_tick_set,void,"FuriEventLoop*, uint32_t, FuriEventLoopTickCallback, void*" Function,+,furi_get_tick,uint32_t, Function,+,furi_hal_adc_acquire,FuriHalAdcHandle*, Function,+,furi_hal_adc_configure,void,FuriHalAdcHandle* @@ -2696,6 +2703,7 @@ Function,+,view_dispatcher_alloc,ViewDispatcher*, Function,+,view_dispatcher_attach_to_gui,void,"ViewDispatcher*, Gui*, ViewDispatcherType" Function,+,view_dispatcher_enable_queue,void,ViewDispatcher* Function,+,view_dispatcher_free,void,ViewDispatcher* +Function,-,view_dispatcher_get_event_loop,FuriEventLoop*,ViewDispatcher* Function,+,view_dispatcher_remove_view,void,"ViewDispatcher*, uint32_t" Function,+,view_dispatcher_run,void,ViewDispatcher* Function,+,view_dispatcher_send_custom_event,void,"ViewDispatcher*, uint32_t" diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 32160b56e..2686cf1bf 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -1201,6 +1201,13 @@ Function,+,furi_event_flag_free,void,FuriEventFlag* Function,+,furi_event_flag_get,uint32_t,FuriEventFlag* Function,+,furi_event_flag_set,uint32_t,"FuriEventFlag*, uint32_t" Function,+,furi_event_flag_wait,uint32_t,"FuriEventFlag*, uint32_t, uint32_t, uint32_t" +Function,-,furi_event_loop_alloc,FuriEventLoop*, +Function,-,furi_event_loop_free,void,FuriEventLoop* +Function,-,furi_event_loop_message_queue_subscribe,void,"FuriEventLoop*, FuriMessageQueue*, FuriEventLoopEvent, FuriEventLoopMessageQueueCallback, void*" +Function,-,furi_event_loop_message_queue_unsubscribe,void,"FuriEventLoop*, FuriMessageQueue*" +Function,-,furi_event_loop_run,void,FuriEventLoop* +Function,-,furi_event_loop_stop,void,FuriEventLoop* +Function,-,furi_event_loop_tick_set,void,"FuriEventLoop*, uint32_t, FuriEventLoopTickCallback, void*" Function,+,furi_get_tick,uint32_t, Function,+,furi_hal_adc_acquire,FuriHalAdcHandle*, Function,+,furi_hal_adc_configure,void,FuriHalAdcHandle* @@ -3515,6 +3522,7 @@ Function,+,view_dispatcher_alloc,ViewDispatcher*, Function,+,view_dispatcher_attach_to_gui,void,"ViewDispatcher*, Gui*, ViewDispatcherType" Function,+,view_dispatcher_enable_queue,void,ViewDispatcher* Function,+,view_dispatcher_free,void,ViewDispatcher* +Function,-,view_dispatcher_get_event_loop,FuriEventLoop*,ViewDispatcher* Function,+,view_dispatcher_remove_view,void,"ViewDispatcher*, uint32_t" Function,+,view_dispatcher_run,void,ViewDispatcher* Function,+,view_dispatcher_send_custom_event,void,"ViewDispatcher*, uint32_t" diff --git a/targets/f7/inc/FreeRTOSConfig.h b/targets/f7/inc/FreeRTOSConfig.h index 0abce558f..e6233f624 100644 --- a/targets/f7/inc/FreeRTOSConfig.h +++ b/targets/f7/inc/FreeRTOSConfig.h @@ -86,8 +86,12 @@ to exclude the API function. */ #define INCLUDE_xTaskGetSchedulerState 1 #define INCLUDE_xTimerPendFunctionCall 1 -/* Furi-specific */ -#define configTASK_NOTIFICATION_ARRAY_ENTRIES 2 +/* Workaround for various notification issues: + * - First one used by system primitives + * - Second one by thread event notification + * - Third one by FuriEventLoop + */ +#define configTASK_NOTIFICATION_ARRAY_ENTRIES 3 extern __attribute__((__noreturn__)) void furi_thread_catch(void); #define configTASK_RETURN_ADDRESS (furi_thread_catch + 2) From cf8c82c45151a1e2f37d82bb6e18519ab887be30 Mon Sep 17 00:00:00 2001 From: Astra <93453568+Astrrra@users.noreply.github.com> Date: Tue, 11 Jun 2024 04:36:46 +0900 Subject: [PATCH 3/3] [FL-3746] Mifare Plus detection support (#3607) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Initial MFPlus draft * Proper detection (WIP) * Mifare Plus detection done * Bump F18 API * Alloc takes no arguments * Fixes from code review * Remove leftover logging * Remove stray reminder comment * Review changes and extra logging * Fix atqa detection * Fix incorrect comparison * ATQA byte swap fix * mf plus: code clean up * mf plus: remove unused code * mf plus: fix read fail event handling * mf plus: fix return error codes * mf plus: handle load and save errors * mf plus: assert -> check in public API funxtion * Bump API Symbols version * Fix wrong feature mask * Skylanders plugin separation * Fix navigation * Fix info box size Co-authored-by: gornekich Co-authored-by: あく --- .../protocol_support/mf_plus/mf_plus.c | 124 +++ .../protocol_support/mf_plus/mf_plus.h | 5 + .../protocol_support/mf_plus/mf_plus_render.c | 67 ++ .../protocol_support/mf_plus/mf_plus_render.h | 14 + .../nfc_protocol_support_defs.c | 2 + .../nfc/plugins/supported_cards/skylanders.c | 785 ++---------------- .../nfc/resources/nfc/assets/skylanders.nfc | 247 ++++++ .../main/nfc/scenes/nfc_scene_exit_confirm.c | 10 +- lib/nfc/SConscript | 2 + lib/nfc/protocols/mf_plus/mf_plus.c | 180 ++++ lib/nfc/protocols/mf_plus/mf_plus.h | 115 +++ lib/nfc/protocols/mf_plus/mf_plus_i.c | 406 +++++++++ lib/nfc/protocols/mf_plus/mf_plus_i.h | 29 + lib/nfc/protocols/mf_plus/mf_plus_poller.c | 210 +++++ lib/nfc/protocols/mf_plus/mf_plus_poller.h | 55 ++ .../protocols/mf_plus/mf_plus_poller_defs.h | 5 + lib/nfc/protocols/mf_plus/mf_plus_poller_i.c | 59 ++ lib/nfc/protocols/mf_plus/mf_plus_poller_i.h | 56 ++ lib/nfc/protocols/nfc_device_defs.c | 2 + lib/nfc/protocols/nfc_poller_defs.c | 2 + lib/nfc/protocols/nfc_protocol.c | 7 + lib/nfc/protocols/nfc_protocol.h | 1 + targets/f7/api_symbols.csv | 16 + 23 files changed, 1655 insertions(+), 744 deletions(-) create mode 100644 applications/main/nfc/helpers/protocol_support/mf_plus/mf_plus.c create mode 100644 applications/main/nfc/helpers/protocol_support/mf_plus/mf_plus.h create mode 100644 applications/main/nfc/helpers/protocol_support/mf_plus/mf_plus_render.c create mode 100644 applications/main/nfc/helpers/protocol_support/mf_plus/mf_plus_render.h create mode 100644 applications/main/nfc/resources/nfc/assets/skylanders.nfc create mode 100644 lib/nfc/protocols/mf_plus/mf_plus.c create mode 100644 lib/nfc/protocols/mf_plus/mf_plus.h create mode 100644 lib/nfc/protocols/mf_plus/mf_plus_i.c create mode 100644 lib/nfc/protocols/mf_plus/mf_plus_i.h create mode 100644 lib/nfc/protocols/mf_plus/mf_plus_poller.c create mode 100644 lib/nfc/protocols/mf_plus/mf_plus_poller.h create mode 100644 lib/nfc/protocols/mf_plus/mf_plus_poller_defs.h create mode 100644 lib/nfc/protocols/mf_plus/mf_plus_poller_i.c create mode 100644 lib/nfc/protocols/mf_plus/mf_plus_poller_i.h diff --git a/applications/main/nfc/helpers/protocol_support/mf_plus/mf_plus.c b/applications/main/nfc/helpers/protocol_support/mf_plus/mf_plus.c new file mode 100644 index 000000000..eebed2a8d --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/mf_plus/mf_plus.c @@ -0,0 +1,124 @@ +#include "mf_plus.h" +#include "mf_plus_render.h" + +#include + +#include "nfc/nfc_app_i.h" + +#include "../nfc_protocol_support_common.h" +#include "../nfc_protocol_support_gui_common.h" +#include "../iso14443_4a/iso14443_4a_i.h" + +static void nfc_scene_info_on_enter_mf_plus(NfcApp* instance) { + const NfcDevice* device = instance->nfc_device; + const MfPlusData* data = nfc_device_get_data(device, NfcProtocolMfPlus); + + FuriString* temp_str = furi_string_alloc(); + nfc_append_filename_string_when_present(instance, temp_str); + furi_string_cat_printf( + temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); + furi_string_replace(temp_str, "Mifare", "MIFARE"); + nfc_render_mf_plus_info(data, NfcProtocolFormatTypeFull, temp_str); + + widget_add_text_scroll_element( + instance->widget, 0, 0, 128, 64, furi_string_get_cstr(temp_str)); + + furi_string_free(temp_str); +} +static NfcCommand nfc_scene_read_poller_callback_mf_plus(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.protocol == NfcProtocolMfPlus); + furi_assert(event.event_data); + + NfcApp* instance = context; + const MfPlusPollerEvent* mf_plus_event = event.event_data; + + NfcCommand command = NfcCommandContinue; + + if(mf_plus_event->type == MfPlusPollerEventTypeReadSuccess) { + nfc_device_set_data( + instance->nfc_device, NfcProtocolMfPlus, nfc_poller_get_data(instance->poller)); + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess); + command = NfcCommandStop; + } else if(mf_plus_event->type == MfPlusPollerEventTypeReadFailed) { + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerFailure); + command = NfcCommandStop; + } + + return command; +} + +static void nfc_scene_read_on_enter_mf_plus(NfcApp* instance) { + nfc_poller_start(instance->poller, nfc_scene_read_poller_callback_mf_plus, instance); +} + +static void nfc_scene_read_success_on_enter_mf_plus(NfcApp* instance) { + const NfcDevice* device = instance->nfc_device; + const MfPlusData* data = nfc_device_get_data(device, NfcProtocolMfPlus); + + FuriString* temp_str = furi_string_alloc(); + furi_string_cat_printf( + temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); + furi_string_replace(temp_str, "Mifare", "MIFARE"); + nfc_render_mf_plus_info(data, NfcProtocolFormatTypeShort, temp_str); + + widget_add_text_scroll_element( + instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str)); + + furi_string_free(temp_str); +} + +static void nfc_scene_emulate_on_enter_mf_plus(NfcApp* instance) { + const Iso14443_4aData* iso14443_4a_data = + nfc_device_get_data(instance->nfc_device, NfcProtocolIso14443_4a); + + instance->listener = + nfc_listener_alloc(instance->nfc, NfcProtocolIso14443_4a, iso14443_4a_data); + nfc_listener_start( + instance->listener, nfc_scene_emulate_listener_callback_iso14443_4a, instance); +} + +const NfcProtocolSupportBase nfc_protocol_support_mf_plus = { + .features = NfcProtocolFeatureEmulateUid, + + .scene_info = + { + .on_enter = nfc_scene_info_on_enter_mf_plus, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_more_info = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_read = + { + .on_enter = nfc_scene_read_on_enter_mf_plus, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_read_menu = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_read_success = + { + .on_enter = nfc_scene_read_success_on_enter_mf_plus, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_saved_menu = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_save_name = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_emulate = + { + .on_enter = nfc_scene_emulate_on_enter_mf_plus, + .on_event = nfc_protocol_support_common_on_event_empty, + }, +}; \ No newline at end of file diff --git a/applications/main/nfc/helpers/protocol_support/mf_plus/mf_plus.h b/applications/main/nfc/helpers/protocol_support/mf_plus/mf_plus.h new file mode 100644 index 000000000..7f2e63dd1 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/mf_plus/mf_plus.h @@ -0,0 +1,5 @@ +#pragma once + +#include "../nfc_protocol_support_base.h" + +extern const NfcProtocolSupportBase nfc_protocol_support_mf_plus; \ No newline at end of file diff --git a/applications/main/nfc/helpers/protocol_support/mf_plus/mf_plus_render.c b/applications/main/nfc/helpers/protocol_support/mf_plus/mf_plus_render.c new file mode 100644 index 000000000..8640fa16d --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/mf_plus/mf_plus_render.c @@ -0,0 +1,67 @@ +#include "mf_plus_render.h" + +#include "../iso14443_4a/iso14443_4a_render.h" + +void nfc_render_mf_plus_info( + const MfPlusData* data, + NfcProtocolFormatType format_type, + FuriString* str) { + nfc_render_iso14443_4a_brief(mf_plus_get_base_data(data), str); + + if(format_type != NfcProtocolFormatTypeFull) return; + + furi_string_cat(str, "\n\e#ISO14443-4 data"); + nfc_render_iso14443_4a_extra(mf_plus_get_base_data(data), str); +} + +void nfc_render_mf_plus_data(const MfPlusData* data, FuriString* str) { + nfc_render_mf_plus_version(&data->version, str); +} + +void nfc_render_mf_plus_version(const MfPlusVersion* data, FuriString* str) { + furi_string_cat_printf( + str, + "%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", + data->uid[0], + data->uid[1], + data->uid[2], + data->uid[3], + data->uid[4], + data->uid[5], + data->uid[6]); + furi_string_cat_printf( + str, + "hw %02x type %02x sub %02x\n" + " maj %02x min %02x\n" + " size %02x proto %02x\n", + data->hw_vendor, + data->hw_type, + data->hw_subtype, + data->hw_major, + data->hw_minor, + data->hw_storage, + data->hw_proto); + furi_string_cat_printf( + str, + "sw %02x type %02x sub %02x\n" + " maj %02x min %02x\n" + " size %02x proto %02x\n", + data->sw_vendor, + data->sw_type, + data->sw_subtype, + data->sw_major, + data->sw_minor, + data->sw_storage, + data->sw_proto); + furi_string_cat_printf( + str, + "batch %02x:%02x:%02x:%02x:%02x\n" + "week %d year %d\n", + data->batch[0], + data->batch[1], + data->batch[2], + data->batch[3], + data->batch[4], + data->prod_week, + data->prod_year); +} diff --git a/applications/main/nfc/helpers/protocol_support/mf_plus/mf_plus_render.h b/applications/main/nfc/helpers/protocol_support/mf_plus/mf_plus_render.h new file mode 100644 index 000000000..5aa8436a9 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/mf_plus/mf_plus_render.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +#include "../nfc_protocol_support_render_common.h" + +void nfc_render_mf_plus_info( + const MfPlusData* data, + NfcProtocolFormatType format_type, + FuriString* str); + +void nfc_render_mf_plus_data(const MfPlusData* data, FuriString* str); + +void nfc_render_mf_plus_version(const MfPlusVersion* data, FuriString* str); diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_defs.c b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_defs.c index 215ffc455..66839aacc 100644 --- a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_defs.c +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_defs.c @@ -17,6 +17,7 @@ #include "felica/felica.h" #include "mf_ultralight/mf_ultralight.h" #include "mf_classic/mf_classic.h" +#include "mf_plus/mf_plus.h" #include "mf_desfire/mf_desfire.h" #include "slix/slix.h" #include "st25tb/st25tb.h" @@ -38,6 +39,7 @@ const NfcProtocolSupportBase* nfc_protocol_support[NfcProtocolNum] = { [NfcProtocolFelica] = &nfc_protocol_support_felica, [NfcProtocolMfUltralight] = &nfc_protocol_support_mf_ultralight, [NfcProtocolMfClassic] = &nfc_protocol_support_mf_classic, + [NfcProtocolMfPlus] = &nfc_protocol_support_mf_plus, [NfcProtocolMfDesfire] = &nfc_protocol_support_mf_desfire, [NfcProtocolSlix] = &nfc_protocol_support_slix, [NfcProtocolSt25tb] = &nfc_protocol_support_st25tb, diff --git a/applications/main/nfc/plugins/supported_cards/skylanders.c b/applications/main/nfc/plugins/supported_cards/skylanders.c index fca1c3185..6c199f114 100644 --- a/applications/main/nfc/plugins/supported_cards/skylanders.c +++ b/applications/main/nfc/plugins/supported_cards/skylanders.c @@ -5,11 +5,54 @@ #include #include #include +#include #define TAG "Skylanders" static const uint64_t skylanders_key = 0x4b0b20107ccb; +static const char* nfc_resources_header = "Flipper NFC resources"; +static const uint32_t nfc_resources_file_version = 1; + +static bool skylanders_search_data( + Storage* storage, + const char* file_name, + FuriString* key, + FuriString* data) { + bool parsed = false; + FlipperFormat* file = flipper_format_file_alloc(storage); + FuriString* temp_str; + temp_str = furi_string_alloc(); + + do { + // Open file + if(!flipper_format_file_open_existing(file, file_name)) break; + // Read file header and version + uint32_t version = 0; + if(!flipper_format_read_header(file, temp_str, &version)) break; + if(furi_string_cmp_str(temp_str, nfc_resources_header) || + (version != nfc_resources_file_version)) + break; + if(!flipper_format_read_string(file, furi_string_get_cstr(key), data)) break; + parsed = true; + } while(false); + + furi_string_free(temp_str); + flipper_format_free(file); + return parsed; +} + +bool skylanders_get_name(Storage* storage, uint16_t id, FuriString* name) { + bool parsed = false; + FuriString* key; + key = furi_string_alloc_printf("%04X", id); + if(skylanders_search_data(storage, EXT_PATH("nfc/assets/skylanders.nfc"), key, name)) { + parsed = true; + } + furi_string_free(key); + return parsed; +} + bool skylanders_verify(Nfc* nfc) { bool verified = false; @@ -75,742 +118,6 @@ static bool skylanders_read(Nfc* nfc, NfcDevice* device) { return is_read; } -static uint8_t fill_name(const uint16_t id, FuriString* name) { - // USED RESEARCH FROM https://github.com/silicontrip/SkyReader/blob/master/toynames.cpp#L15C1-L163C1 - // AND https://github.com/bettse/Solarbreeze/blob/master/Solarbreeze/ThePoster.swift#L438C1-L681C1 - switch(id) { - case 0x0000: - furi_string_cat_printf(name, "Whirlwind"); - break; - case 0x0001: - furi_string_cat_printf(name, "Sonic Boom"); - break; - case 0x0002: - furi_string_cat_printf(name, "Warnado"); - break; - case 0x0003: - furi_string_cat_printf(name, "Lightning Rod"); - break; - case 0x0004: - case 0x0194: - furi_string_cat_printf(name, "Bash"); - break; - case 0x0005: - furi_string_cat_printf(name, "Terrafin"); - break; - case 0x0006: - furi_string_cat_printf(name, "Dino-Rang"); - break; - case 0x0007: - furi_string_cat_printf(name, "Prism Break"); - break; - case 0x0008: - furi_string_cat_printf(name, "Sunburn"); - break; - case 0x0009: - furi_string_cat_printf(name, "Eruptor"); - break; - case 0x000A: - furi_string_cat_printf(name, "Ignitor"); - break; - case 0x000B: - furi_string_cat_printf(name, "Flameslinger"); - break; - case 0x000C: - furi_string_cat_printf(name, "Zap"); - break; - case 0x000D: - furi_string_cat_printf(name, "Wham-Shell"); - break; - case 0x000E: - furi_string_cat_printf(name, "Gill Grunt"); - break; - case 0x000F: - furi_string_cat_printf(name, "Slam Bam"); - break; - case 0x0010: - case 0x01A0: - furi_string_cat_printf(name, "Spyro"); - break; - case 0x0011: - furi_string_cat_printf(name, "Voodood"); - break; - case 0x0012: - furi_string_cat_printf(name, "Double Trouble"); - break; - case 0x0013: - case 0x01A3: - furi_string_cat_printf(name, "Trigger Happy"); - break; - case 0x0014: - furi_string_cat_printf(name, "Drobot"); - break; - case 0x0015: - furi_string_cat_printf(name, "Drill Sergeant"); - break; - case 0x0016: - furi_string_cat_printf(name, "Boomer"); - break; - case 0x0017: - furi_string_cat_printf(name, "Wrecking Ball"); - break; - case 0x0018: - furi_string_cat_printf(name, "Camo"); - break; - case 0x0019: - furi_string_cat_printf(name, "Zook"); - break; - case 0x001A: - furi_string_cat_printf(name, "Stealth Elf"); - break; - case 0x001B: - furi_string_cat_printf(name, "Stump Smash"); - break; - case 0x001C: - furi_string_cat_printf(name, "Dark Spyro"); - break; - case 0x001D: - furi_string_cat_printf(name, "Hex"); - break; - case 0x001E: - case 0x01AE: - furi_string_cat_printf(name, "Chop Chop"); - break; - case 0x001F: - furi_string_cat_printf(name, "Ghost Roaster"); - break; - case 0x0020: - furi_string_cat_printf(name, "Cynder"); - break; - case 0x0064: - furi_string_cat_printf(name, "Jet Vac"); - break; - case 0x0065: - furi_string_cat_printf(name, "Swarm"); - break; - case 0x0066: - furi_string_cat_printf(name, "Crusher"); - break; - case 0x0067: - furi_string_cat_printf(name, "Flashwing"); - break; - case 0x0068: - furi_string_cat_printf(name, "Hot Head"); - break; - case 0x0069: - furi_string_cat_printf(name, "Hot Dog"); - break; - case 0x006A: - furi_string_cat_printf(name, "Chill"); - break; - case 0x006B: - furi_string_cat_printf(name, "Thumpback"); - break; - case 0x006C: - furi_string_cat_printf(name, "Pop Fizz"); - break; - case 0x006D: - furi_string_cat_printf(name, "Ninjini"); - break; - case 0x006E: - furi_string_cat_printf(name, "Bouncer"); - break; - case 0x006F: - furi_string_cat_printf(name, "Sprocket"); - break; - case 0x0070: - furi_string_cat_printf(name, "Tree Rex"); - break; - case 0x0071: - furi_string_cat_printf(name, "Shroomboom"); - break; - case 0x0072: - furi_string_cat_printf(name, "Eye-Brawl"); - break; - case 0x0073: - furi_string_cat_printf(name, "Fright Rider"); - break; - case 0x00C8: - furi_string_cat_printf(name, "Anvil Rain"); - break; - case 0x00C9: - furi_string_cat_printf(name, "Treasure Chest"); - break; - case 0x00CA: - furi_string_cat_printf(name, "Healing Elixer"); - break; - case 0x00CB: - furi_string_cat_printf(name, "Ghost Swords"); - break; - case 0x00CC: - furi_string_cat_printf(name, "Time Twister"); - break; - case 0x00CD: - furi_string_cat_printf(name, "Sky-Iron Shield"); - break; - case 0x00CE: - furi_string_cat_printf(name, "Winged Boots"); - break; - case 0x00CF: - furi_string_cat_printf(name, "Sparx Dragonfly"); - break; - case 0x00D0: - furi_string_cat_printf(name, "Dragonfire Cannon"); - break; - case 0x00D1: - furi_string_cat_printf(name, "Scorpion Striker Catapult"); - break; - case 0x00D2: - furi_string_cat_printf(name, "Trap - Magic"); - break; - case 0x00D3: - furi_string_cat_printf(name, "Trap - Water"); - break; - case 0x00D4: - furi_string_cat_printf(name, "Trap - Air"); - break; - case 0x00D5: - furi_string_cat_printf(name, "Trap - Undead"); - break; - case 0x00D6: - furi_string_cat_printf(name, "Trap - Tech"); - break; - case 0x00D7: - furi_string_cat_printf(name, "Trap - Fire"); - break; - case 0x00D8: - furi_string_cat_printf(name, "Trap - Earth"); - break; - case 0x00D9: - furi_string_cat_printf(name, "Trap - Life"); - break; - case 0x00DA: - furi_string_cat_printf(name, "Trap - Light"); - break; - case 0x00DB: - furi_string_cat_printf(name, "Trap - Dark"); - break; - case 0x00DC: - furi_string_cat_printf(name, "Trap - Kaos"); - break; - case 0x00E6: - furi_string_cat_printf(name, "Hand Of Fate"); - break; - case 0x00E7: - furi_string_cat_printf(name, "Piggy Bank"); - break; - case 0x00E8: - furi_string_cat_printf(name, "Rocket Ram"); - break; - case 0x00E9: - furi_string_cat_printf(name, "Tiki Speaky"); - break; - case 0x00EB: - furi_string_cat_printf(name, "Imaginite Mystery Chest"); - break; - case 0x012C: - furi_string_cat_printf(name, "Dragons Peak"); - break; - case 0x012D: - furi_string_cat_printf(name, "Empire of Ice"); - break; - case 0x012E: - furi_string_cat_printf(name, "Pirate Seas"); - break; - case 0x012F: - furi_string_cat_printf(name, "Darklight Crypt"); - break; - case 0x0130: - furi_string_cat_printf(name, "Volcanic Vault"); - break; - case 0x0131: - furi_string_cat_printf(name, "Mirror Of Mystery"); - break; - case 0x0132: - furi_string_cat_printf(name, "Nightmare Express"); - break; - case 0x0133: - furi_string_cat_printf(name, "Sunscraper Spire"); - break; - case 0x0134: - furi_string_cat_printf(name, "Midnight Museum"); - break; - case 0x01C2: - furi_string_cat_printf(name, "Gusto"); - break; - case 0x01C3: - furi_string_cat_printf(name, "Thunderbolt"); - break; - case 0x01C4: - furi_string_cat_printf(name, "Fling Kong"); - break; - case 0x01C5: - furi_string_cat_printf(name, "Blades"); - break; - case 0x01C6: - furi_string_cat_printf(name, "Wallop"); - break; - case 0x01C7: - furi_string_cat_printf(name, "Head Rush"); - break; - case 0x01C8: - furi_string_cat_printf(name, "Fist Bump"); - break; - case 0x01C9: - furi_string_cat_printf(name, "Rocky Roll"); - break; - case 0x01CA: - furi_string_cat_printf(name, "Wildfire"); - break; - case 0x01CB: - furi_string_cat_printf(name, "Ka Boom"); - break; - case 0x01CC: - furi_string_cat_printf(name, "Trail Blazer"); - break; - case 0x01CD: - furi_string_cat_printf(name, "Torch"); - break; - case 0x01CE: - furi_string_cat_printf(name, "Snap Shot"); - break; - case 0x01CF: - furi_string_cat_printf(name, "Lob Star"); - break; - case 0x01D0: - furi_string_cat_printf(name, "Flip Wreck"); - break; - case 0x01D1: - furi_string_cat_printf(name, "Echo"); - break; - case 0x01D2: - furi_string_cat_printf(name, "Blastermind"); - break; - case 0x01D3: - furi_string_cat_printf(name, "Enigma"); - break; - case 0x01D4: - furi_string_cat_printf(name, "Deja Vu"); - break; - case 0x01D5: - furi_string_cat_printf(name, "Cobra Cadabra"); - break; - case 0x01D6: - furi_string_cat_printf(name, "Jawbreaker"); - break; - case 0x01D7: - furi_string_cat_printf(name, "Gearshift"); - break; - case 0x01D8: - furi_string_cat_printf(name, "Chopper"); - break; - case 0x01D9: - furi_string_cat_printf(name, "Tread Head"); - break; - case 0x01DA: - furi_string_cat_printf(name, "Bushwhack"); - break; - case 0x01DB: - furi_string_cat_printf(name, "Tuff Luck"); - break; - case 0x01DC: - furi_string_cat_printf(name, "Food Fight"); - break; - case 0x01DD: - furi_string_cat_printf(name, "High Five"); - break; - case 0x01DE: - furi_string_cat_printf(name, "Krypt King"); - break; - case 0x01DF: - furi_string_cat_printf(name, "Short Cut"); - break; - case 0x01E0: - furi_string_cat_printf(name, "Bat Spin"); - break; - case 0x01E1: - furi_string_cat_printf(name, "Funny Bone"); - break; - case 0x01E2: - furi_string_cat_printf(name, "Knight light"); - break; - case 0x01E3: - furi_string_cat_printf(name, "Spotlight"); - break; - case 0x01E4: - furi_string_cat_printf(name, "Knight Mare"); - break; - case 0x01E5: - furi_string_cat_printf(name, "Blackout"); - break; - case 0x01F6: - furi_string_cat_printf(name, "Bop"); - break; - case 0x01F7: - furi_string_cat_printf(name, "Spry"); - break; - case 0x01F8: - furi_string_cat_printf(name, "Hijinx"); - break; - case 0x01F9: - furi_string_cat_printf(name, "Terrabite"); - break; - case 0x01FA: - furi_string_cat_printf(name, "Breeze"); - break; - case 0x01FB: - furi_string_cat_printf(name, "Weeruptor"); - break; - case 0x01FC: - furi_string_cat_printf(name, "Pet Vac"); - break; - case 0x01FD: - furi_string_cat_printf(name, "Small Fry"); - break; - case 0x01FE: - furi_string_cat_printf(name, "Drobit"); - break; - case 0x0202: - furi_string_cat_printf(name, "Gill Runt"); - break; - case 0x0207: - furi_string_cat_printf(name, "Trigger Snappy"); - break; - case 0x020E: - furi_string_cat_printf(name, "Whisper Elf"); - break; - case 0x021C: - furi_string_cat_printf(name, "Barkley"); - break; - case 0x021D: - furi_string_cat_printf(name, "Thumpling"); - break; - case 0x021E: - furi_string_cat_printf(name, "Mini Jini"); - break; - case 0x021F: - furi_string_cat_printf(name, "Eye Small"); - break; - case 0x0259: - furi_string_cat_printf(name, "King Pen"); - break; - case 0x0265: - furi_string_cat_printf(name, "Golden Queen"); - break; - case 0x02AD: - furi_string_cat_printf(name, "Fire Acorn"); - break; - case 0x03E8: - furi_string_cat_printf(name, "(Boom) Jet"); - break; - case 0x03E9: - furi_string_cat_printf(name, "(Free) Ranger"); - break; - case 0x03EA: - furi_string_cat_printf(name, "(Rubble) Rouser"); - break; - case 0x03EB: - furi_string_cat_printf(name, "(Doom) Stone"); - break; - case 0x03EC: - furi_string_cat_printf(name, "Blast Zone"); - break; - case 0x03ED: - furi_string_cat_printf(name, "(Fire) Kraken"); - break; - case 0x03EE: - furi_string_cat_printf(name, "(Stink) Bomb"); - break; - case 0x03EF: - furi_string_cat_printf(name, "(Grilla) Drilla"); - break; - case 0x03F0: - furi_string_cat_printf(name, "(Hoot) Loop"); - break; - case 0x03F1: - furi_string_cat_printf(name, "(Trap) Shadow"); - break; - case 0x03F2: - furi_string_cat_printf(name, "(Magna) Charge"); - break; - case 0x03F3: - furi_string_cat_printf(name, "(Spy) Rise"); - break; - case 0x03F4: - furi_string_cat_printf(name, "(Night) Shift"); - break; - case 0x03F5: - furi_string_cat_printf(name, "(Rattle) Shake"); - break; - case 0x03F6: - furi_string_cat_printf(name, "(Freeze) Blade"); - break; - case 0x03F7: - furi_string_cat_printf(name, "Wash Buckler"); - break; - case 0x07D0: - furi_string_cat_printf(name, "Boom (Jet)"); - break; - case 0x07D1: - furi_string_cat_printf(name, "Free (Ranger)"); - break; - case 0x07D2: - furi_string_cat_printf(name, "Rubble (Rouser)"); - break; - case 0x07D3: - furi_string_cat_printf(name, "Doom (Stone)"); - break; - case 0x07D4: - furi_string_cat_printf(name, "Blast Zone (Head)"); - break; - case 0x07D5: - furi_string_cat_printf(name, "Fire (Kraken)"); - break; - case 0x07D6: - furi_string_cat_printf(name, "Stink (Bomb)"); - break; - case 0x07D7: - furi_string_cat_printf(name, "Grilla (Drilla)"); - break; - case 0x07D8: - furi_string_cat_printf(name, "Hoot (Loop)"); - break; - case 0x07D9: - furi_string_cat_printf(name, "Trap (Shadow)"); - break; - case 0x07DA: - furi_string_cat_printf(name, "Magna (Charge)"); - break; - case 0x07DB: - furi_string_cat_printf(name, "Spy (Rise)"); - break; - case 0x07DC: - furi_string_cat_printf(name, "Night (Shift)"); - break; - case 0x07DD: - furi_string_cat_printf(name, "Rattle (Shake)"); - break; - case 0x07DE: - furi_string_cat_printf(name, "Freeze (Blade)"); - break; - case 0x07DF: - furi_string_cat_printf(name, "Wash Buckler (Head)"); - break; - case 0x0BB8: - furi_string_cat_printf(name, "Scratch"); - break; - case 0x0BB9: - furi_string_cat_printf(name, "Pop Thorn"); - break; - case 0x0BBA: - furi_string_cat_printf(name, "Slobber Tooth"); - break; - case 0x0BBB: - furi_string_cat_printf(name, "Scorp"); - break; - case 0x0BBC: - furi_string_cat_printf(name, "Fryno"); - break; - case 0x0BBD: - furi_string_cat_printf(name, "Smolderdash"); - break; - case 0x0BBE: - furi_string_cat_printf(name, "Bumble Blast"); - break; - case 0x0BBF: - furi_string_cat_printf(name, "Zoo Lou"); - break; - case 0x0BC0: - furi_string_cat_printf(name, "Dune Bug"); - break; - case 0x0BC1: - furi_string_cat_printf(name, "Star Strike"); - break; - case 0x0BC2: - furi_string_cat_printf(name, "Countdown"); - break; - case 0x0BC3: - furi_string_cat_printf(name, "Wind Up"); - break; - case 0x0BC4: - furi_string_cat_printf(name, "Roller Brawl"); - break; - case 0x0BC5: - furi_string_cat_printf(name, "Grim Creeper"); - break; - case 0x0BC6: - furi_string_cat_printf(name, "Rip Tide"); - break; - case 0x0BC7: - furi_string_cat_printf(name, "Punk Shock"); - break; - case 0x0C80: - furi_string_cat_printf(name, "Battle Hammer"); - break; - case 0x0C81: - furi_string_cat_printf(name, "Sky Diamond"); - break; - case 0x0C82: - furi_string_cat_printf(name, "Platinum Sheep"); - break; - case 0x0C83: - furi_string_cat_printf(name, "Groove Machine"); - break; - case 0x0C84: - furi_string_cat_printf(name, "UFO Hat"); - break; - case 0x0C94: - furi_string_cat_printf(name, "Jet Stream"); - break; - case 0x0C95: - furi_string_cat_printf(name, "Tomb Buggy"); - break; - case 0x0C96: - furi_string_cat_printf(name, "Reef Ripper"); - break; - case 0x0C97: - furi_string_cat_printf(name, "Burn Cycle"); - break; - case 0x0C98: - furi_string_cat_printf(name, "Hot Streak"); - break; - case 0x0C99: - furi_string_cat_printf(name, "Shark Tank"); - break; - case 0x0C9A: - furi_string_cat_printf(name, "Thump Truck"); - break; - case 0x0C9B: - furi_string_cat_printf(name, "Crypt Crusher"); - break; - case 0x0C9C: - furi_string_cat_printf(name, "Stealth Stinger"); - break; - case 0x0C9F: - furi_string_cat_printf(name, "Dive Bomber"); - break; - case 0x0CA0: - furi_string_cat_printf(name, "Sky Slicer"); - break; - case 0x0CA1: - furi_string_cat_printf(name, "Clown Cruiser"); - break; - case 0x0CA2: - furi_string_cat_printf(name, "Gold Rusher"); - break; - case 0x0CA3: - furi_string_cat_printf(name, "Shield Striker"); - break; - case 0x0CA4: - furi_string_cat_printf(name, "Sun Runner"); - break; - case 0x0CA5: - furi_string_cat_printf(name, "Sea Shadow"); - break; - case 0x0CA6: - furi_string_cat_printf(name, "Splatter Splasher"); - break; - case 0x0CA7: - furi_string_cat_printf(name, "Soda Skimmer"); - break; - case 0x0CA8: - furi_string_cat_printf(name, "Barrel Blaster"); - break; - case 0x0CA9: - furi_string_cat_printf(name, "Buzz Wing"); - break; - case 0x0CE4: - furi_string_cat_printf(name, "Sheep Wreck Island"); - break; - case 0x0CE5: - furi_string_cat_printf(name, "Tower of Time"); - break; - case 0x0CE6: - furi_string_cat_printf(name, "Fiery Forge"); - break; - case 0x0CE7: - furi_string_cat_printf(name, "Arkeyan Crossbow"); - break; - case 0x0D48: - furi_string_cat_printf(name, "Fiesta"); - break; - case 0x0D49: - furi_string_cat_printf(name, "High Volt"); - break; - case 0x0D4A: - furi_string_cat_printf(name, "Splat"); - break; - case 0x0D4E: - furi_string_cat_printf(name, "Stormblade"); - break; - case 0x0D53: - furi_string_cat_printf(name, "Smash It"); - break; - case 0x0D54: - furi_string_cat_printf(name, "Spitfire"); - break; - case 0x0D55: - furi_string_cat_printf(name, "Hurricane Jet-Vac"); - break; - case 0x0D56: - furi_string_cat_printf(name, "Double Dare Trigger Happy"); - break; - case 0x0D57: - furi_string_cat_printf(name, "Super Shot Stealth Elf"); - break; - case 0x0D58: - furi_string_cat_printf(name, "Shark Shooter Terrafin"); - break; - case 0x0D59: - furi_string_cat_printf(name, "Bone Bash Roller Brawl"); - break; - case 0x0D5C: - furi_string_cat_printf(name, "Big Bubble Pop Fizz"); - break; - case 0x0D5D: - furi_string_cat_printf(name, "Lava Lance Eruptor"); - break; - case 0x0D5E: - furi_string_cat_printf(name, "Deep Dive Gill Grunt"); - break; - case 0x0D5F: - furi_string_cat_printf(name, "Turbo Charge Donkey Kong"); - break; - case 0x0D60: - furi_string_cat_printf(name, "Hammer Slam Bowser"); - break; - case 0x0D61: - furi_string_cat_printf(name, "Dive-Clops"); - break; - case 0x0D62: - furi_string_cat_printf(name, "Astroblast"); - break; - case 0x0D63: - furi_string_cat_printf(name, "Nightfall"); - break; - case 0x0D64: - furi_string_cat_printf(name, "Thrillipede"); - break; - case 0x0DAC: - furi_string_cat_printf(name, "Sky Trophy"); - break; - case 0x0DAD: - furi_string_cat_printf(name, "Land Trophy"); - break; - case 0x0DAE: - furi_string_cat_printf(name, "Sea Trophy"); - break; - case 0x0DAF: - furi_string_cat_printf(name, "Kaos Trophy"); - break; - default: - furi_string_cat_printf(name, "Unknown"); - break; - } - - return true; -} - static bool skylanders_parse(const NfcDevice* device, FuriString* parsed_data) { furi_assert(device); @@ -830,7 +137,11 @@ static bool skylanders_parse(const NfcDevice* device, FuriString* parsed_data) { const uint16_t id = (uint16_t)*data->block[1].data; if(id == 0) break; - bool success = fill_name(id, name); + Storage* storage = furi_record_open(RECORD_STORAGE); + + bool success = skylanders_get_name(storage, id, name); + + furi_record_close(RECORD_STORAGE); if(!success) break; furi_string_printf(parsed_data, "\e#Skylanders\n%s", furi_string_get_cstr(name)); diff --git a/applications/main/nfc/resources/nfc/assets/skylanders.nfc b/applications/main/nfc/resources/nfc/assets/skylanders.nfc new file mode 100644 index 000000000..58e6d5276 --- /dev/null +++ b/applications/main/nfc/resources/nfc/assets/skylanders.nfc @@ -0,0 +1,247 @@ +Filetype: Flipper NFC resources +Version: 1 +# ID: Name +0000: Whirlwind +0001: Sonic Boom +0002: Warnado +0003: Lightning Rod +0004: Bash +0194: Bash +0005: Terrafin +0006: Dino-Rang +0007: Prism Break +0008: Sunburn +0009: Eruptor +000A: Ignitor +000B: Flameslinger +000C: Zap +000D: Wham-Shell +000E: Gill Grunt +000F: Slam Bam +0010: Spyro +01A0: Spyro +0011: Voodood +0012: Double Trouble +0013: Trigger Happy +01A3: Trigger Happy +0014: Drobot +0015: Drill Sergeant +0016: Boomer +0017: Wrecking Ball +0018: Camo +0019: Zook +001A: Stealth Elf +001B: Stump Smash +001C: Dark Spyro +001D: Hex +001E: Chop Chop +01AE: Chop Chop +001F: Ghost Roaster +0020: Cynder +0064: Jet Vac +0065: Swarm +0066: Crusher +0067: Flashwing +0068: Hot Head +0069: Hot Dog +006A: Chill +006B: Thumpback +006C: Pop Fizz +006D: Ninjini +006E: Bouncer +006F: Sprocket +0070: Tree Rex +0071: Shroomboom +0072: Eye-Brawl +0073: Fright Rider +00C8: Anvil Rain +00C9: Treasure Chest +00CA: Healing Elixer +00CB: Ghost Swords +00CC: Time Twister +00CD: Sky-Iron Shield +00CE: Winged Boots +00CF: Sparx Dragonfly +00D0: Dragonfire Cannon +00D1: Scorpion Striker Catapult +00D2: Trap - Magic +00D3: Trap - Water +00D4: Trap - Air +00D5: Trap - Undead +00D6: Trap - Tech +00D7: Trap - Fire +00D8: Trap - Earth +00D9: Trap - Life +00DA: Trap - Light +00DB: Trap - Dark +00DC: Trap - Kaos +00E6: Hand Of Fate +00E7: Piggy Bank +00E8: Rocket Ram +00E9: Tiki Speaky +00EB: Imaginite Mystery Chest +012C: Dragons Peak +012D: Empire of Ice +012E: Pirate Seas +012F: Darklight Crypt +0130: Volcanic Vault +0131: Mirror Of Mystery +0132: Nightmare Express +0133: Sunscraper Spire +0134: Midnight Museum +01C2: Gusto +01C3: Thunderbolt +01C4: Fling Kong +01C5: Blades +01C6: Wallop +01C7: Head Rush +01C8: Fist Bump +01C9: Rocky Roll +01CA: Wildfire +01CB: Ka Boom +01CC: Trail Blazer +01CD: Torch +01CE: Snap Shot +01CF: Lob Star +01D0: Flip Wreck +01D1: Echo +01D2: Blastermind +01D3: Enigma +01D4: Deja Vu +01D5: Cobra Cadabra +01D6: Jawbreaker +01D7: Gearshift +01D8: Chopper +01D9: Tread Head +01DA: Bushwhack +01DB: Tuff Luck +01DC: Food Fight +01DD: High Five +01DE: Krypt King +01DF: Short Cut +01E0: Bat Spin +01E1: Funny Bone +01E2: Knight light +01E3: Spotlight +01E4: Knight Mare +01E5: Blackout +01F6: Bop +01F7: Spry +01F8: Hijinx +01F9: Terrabite +01FA: Breeze +01FB: Weeruptor +01FC: Pet Vac +01FD: Small Fry +01FE: Drobit +0202: Gill Runt +0207: Trigger Snappy +020E: Whisper Elf +021C: Barkley +021D: Thumpling +021E: Mini Jini +021F: Eye Small +0259: King Pen +0265: Golden Queen +02AD: Fire Acorn +03E8: (Boom) Jet +03E9: (Free) Ranger +03EA: (Rubble) Rouser +03EB: (Doom) Stone +03EC: Blast Zone +03ED: (Fire) Kraken +03EE: (Stink) Bomb +03EF: (Grilla) Drilla +03F0: (Hoot) Loop +03F1: (Trap) Shadow +03F2: (Magna) Charge +03F3: (Spy) Rise +03F4: (Night) Shift +03F5: (Rattle) Shake +03F6: (Freeze) Blade +03F7: Wash Buckler +07D0: Boom (Jet) +07D1: Free (Ranger) +07D2: Rubble (Rouser) +07D3: Doom (Stone) +07D4: Blast Zone (Head) +07D5: Fire (Kraken) +07D6: Stink (Bomb) +07D7: Grilla (Drilla) +07D8: Hoot (Loop) +07D9: Trap (Shadow) +07DA: Magna (Charge) +07DB: Spy (Rise) +07DC: Night (Shift) +07DD: Rattle (Shake) +07DE: Freeze (Blade) +07DF: Wash Buckler (Head) +0BB8: Scratch +0BB9: Pop Thorn +0BBA: Slobber Tooth +0BBB: Scorp +0BBC: Fryno +0BBD: Smolderdash +0BBE: Bumble Blast +0BBF: Zoo Lou +0BC0: Dune Bug +0BC1: Star Strike +0BC2: Countdown +0BC3: Wind Up +0BC4: Roller Brawl +0BC5: Grim Creeper +0BC6: Rip Tide +0BC7: Punk Shock +0C80: Battle Hammer +0C81: Sky Diamond +0C82: Platinum Sheep +0C83: Groove Machine +0C84: UFO Hat +0C94: Jet Stream +0C95: Tomb Buggy +0C96: Reef Ripper +0C97: Burn Cycle +0C98: Hot Streak +0C99: Shark Tank +0C9A: Thump Truck +0C9B: Crypt Crusher +0C9C: Stealth Stinger +0C9F: Dive Bomber +0CA0: Sky Slicer +0CA1: Clown Cruiser +0CA2: Gold Rusher +0CA3: Shield Striker +0CA4: Sun Runner +0CA5: Sea Shadow +0CA6: Splatter Splasher +0CA7: Soda Skimmer +0CA8: Barrel Blaster +0CA9: Buzz Wing +0CE4: Sheep Wreck Island +0CE5: Tower of Time +0CE6: Fiery Forge +0CE7: Arkeyan Crossbow +0D48: Fiesta +0D49: High Volt +0D4A: Splat +0D4E: Stormblade +0D53: Smash It +0D54: Spitfire +0D55: Hurricane Jet-Vac +0D56: Double Dare Trigger Happy +0D57: Super Shot Stealth Elf +0D58: Shark Shooter Terrafin +0D59: Bone Bash Roller Brawl +0D5C: Big Bubble Pop Fizz +0D5D: Lava Lance Eruptor +0D5E: Deep Dive Gill Grunt +0D5F: Turbo Charge Donkey Kong +0D60: Hammer Slam Bowser +0D61: Dive-Clops +0D62: Astroblast +0D63: Nightfall +0D64: Thrillipede +0DAC: Sky Trophy +0DAD: Land Trophy +0DAE: Sea Trophy +0DAF: Kaos Trophy diff --git a/applications/main/nfc/scenes/nfc_scene_exit_confirm.c b/applications/main/nfc/scenes/nfc_scene_exit_confirm.c index 7c4a3d19d..d62ee2d8e 100644 --- a/applications/main/nfc/scenes/nfc_scene_exit_confirm.c +++ b/applications/main/nfc/scenes/nfc_scene_exit_confirm.c @@ -28,13 +28,9 @@ bool nfc_scene_exit_confirm_on_event(void* context, SceneManagerEvent event) { if(event.event == DialogExResultRight) { consumed = scene_manager_previous_scene(nfc->scene_manager); } else if(event.event == DialogExResultLeft) { - if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSelectProtocol)) { - consumed = scene_manager_search_and_switch_to_previous_scene( - nfc->scene_manager, NfcSceneSelectProtocol); - } else if( - scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneMfClassicDictAttack) && - (scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneReadMenu) || - scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSavedMenu))) { + if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneMfClassicDictAttack) && + (scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneReadMenu) || + scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSavedMenu))) { const uint32_t possible_scenes[] = {NfcSceneReadMenu, NfcSceneSavedMenu}; consumed = scene_manager_search_and_switch_to_previous_scene_one_of( nfc->scene_manager, possible_scenes, COUNT_OF(possible_scenes)); diff --git a/lib/nfc/SConscript b/lib/nfc/SConscript index 06fd1bb31..82b87ea2a 100644 --- a/lib/nfc/SConscript +++ b/lib/nfc/SConscript @@ -21,6 +21,7 @@ env.Append( File("protocols/iso14443_4b/iso14443_4b.h"), File("protocols/mf_ultralight/mf_ultralight.h"), File("protocols/mf_classic/mf_classic.h"), + File("protocols/mf_plus/mf_plus.h"), File("protocols/mf_desfire/mf_desfire.h"), File("protocols/slix/slix.h"), File("protocols/st25tb/st25tb.h"), @@ -32,6 +33,7 @@ env.Append( File("protocols/iso14443_4b/iso14443_4b_poller.h"), File("protocols/mf_ultralight/mf_ultralight_poller.h"), File("protocols/mf_classic/mf_classic_poller.h"), + File("protocols/mf_plus/mf_plus_poller.h"), File("protocols/mf_desfire/mf_desfire_poller.h"), File("protocols/slix/slix_poller.h"), File("protocols/st25tb/st25tb_poller.h"), diff --git a/lib/nfc/protocols/mf_plus/mf_plus.c b/lib/nfc/protocols/mf_plus/mf_plus.c new file mode 100644 index 000000000..32d8d6c59 --- /dev/null +++ b/lib/nfc/protocols/mf_plus/mf_plus.c @@ -0,0 +1,180 @@ +#include "mf_plus_i.h" + +#include +#include + +#define MF_PLUS_PROTOCOL_NAME "Mifare Plus" + +static const char* mf_plus_type_strings[] = { + [MfPlusTypeS] = "Plus S", + [MfPlusTypeX] = "Plus X", + [MfPlusTypeSE] = "Plus SE", + [MfPlusTypeEV1] = "Plus EV1", + [MfPlusTypeEV2] = "Plus EV2", + [MfPlusTypePlus] = "Plus", + [MfPlusTypeUnknown] = "Unknown", +}; + +static const char* mf_plus_size_strings[] = { + [MfPlusSize1K] = "1K", + [MfPlusSize2K] = "2K", + [MfPlusSize4K] = "4K", + [MfPlusSizeUnknown] = "Unknown", +}; + +static const char* mf_plus_security_level_strings[] = { + [MfPlusSecurityLevel0] = "SL0", + [MfPlusSecurityLevel1] = "SL1", + [MfPlusSecurityLevel2] = "SL2", + [MfPlusSecurityLevel3] = "SL3", + [MfPlusSecurityLevelUnknown] = "Unknown", +}; + +const NfcDeviceBase nfc_device_mf_plus = { + .protocol_name = MF_PLUS_PROTOCOL_NAME, + .alloc = (NfcDeviceAlloc)mf_plus_alloc, + .free = (NfcDeviceFree)mf_plus_free, + .reset = (NfcDeviceReset)mf_plus_reset, + .copy = (NfcDeviceCopy)mf_plus_copy, + .verify = (NfcDeviceVerify)mf_plus_verify, + .load = (NfcDeviceLoad)mf_plus_load, + .save = (NfcDeviceSave)mf_plus_save, + .is_equal = (NfcDeviceEqual)mf_plus_is_equal, + .get_name = (NfcDeviceGetName)mf_plus_get_device_name, + .get_uid = (NfcDeviceGetUid)mf_plus_get_uid, + .set_uid = (NfcDeviceSetUid)mf_plus_set_uid, + .get_base_data = (NfcDeviceGetBaseData)mf_plus_get_base_data, +}; + +MfPlusData* mf_plus_alloc(void) { + MfPlusData* data = malloc(sizeof(MfPlusData)); + data->device_name = furi_string_alloc(); + data->iso14443_4a_data = iso14443_4a_alloc(); + + data->type = MfPlusTypeUnknown; + data->security_level = MfPlusSecurityLevelUnknown; + data->size = MfPlusSizeUnknown; + + return data; +} + +void mf_plus_free(MfPlusData* data) { + furi_check(data); + + furi_string_free(data->device_name); + iso14443_4a_free(data->iso14443_4a_data); + free(data); +} + +void mf_plus_reset(MfPlusData* data) { + furi_check(data); + + iso14443_4a_reset(data->iso14443_4a_data); + memset(&data->version, 0, sizeof(data->version)); + furi_string_reset(data->device_name); + data->type = MfPlusTypeUnknown; + data->security_level = MfPlusSecurityLevelUnknown; + data->size = MfPlusSizeUnknown; +} + +void mf_plus_copy(MfPlusData* data, const MfPlusData* other) { + furi_check(data); + furi_check(other); + + iso14443_4a_copy(data->iso14443_4a_data, other->iso14443_4a_data); + data->version = other->version; + data->type = other->type; + data->security_level = other->security_level; + data->size = other->size; +} + +bool mf_plus_verify(MfPlusData* data, const FuriString* device_type) { + UNUSED(data); + return furi_string_equal_str(device_type, MF_PLUS_PROTOCOL_NAME); +} + +bool mf_plus_load(MfPlusData* data, FlipperFormat* ff, uint32_t version) { + furi_check(data); + + bool success = false; + do { + if(!iso14443_4a_load(data->iso14443_4a_data, ff, version)) break; + if(!mf_plus_version_load(&data->version, ff)) break; + if(!mf_plus_type_load(&data->type, ff)) break; + if(!mf_plus_security_level_load(&data->security_level, ff)) break; + if(!mf_plus_size_load(&data->size, ff)) break; + success = true; + } while(false); + + return success; +} + +bool mf_plus_save(const MfPlusData* data, FlipperFormat* ff) { + furi_check(data); + + bool success = false; + do { + if(!iso14443_4a_save(data->iso14443_4a_data, ff)) break; + if(!flipper_format_write_comment_cstr(ff, MF_PLUS_PROTOCOL_NAME " specific data")) break; + if(!mf_plus_version_save(&data->version, ff)) break; + if(!mf_plus_type_save(&data->type, ff)) break; + if(!mf_plus_security_level_save(&data->security_level, ff)) break; + if(!mf_plus_size_save(&data->size, ff)) break; + success = true; + } while(false); + + return success; +} + +bool mf_plus_is_equal(const MfPlusData* data, const MfPlusData* other) { + furi_check(data); + furi_check(other); + + bool equal = false; + do { + if(!iso14443_4a_is_equal(data->iso14443_4a_data, other->iso14443_4a_data)) break; + if(memcmp(&data->version, &other->version, sizeof(data->version)) != 0) break; + if(data->security_level != other->security_level) break; + if(data->type != other->type) break; + if(data->size != other->size) break; + equal = true; + } while(false); + + return equal; +} + +const char* mf_plus_get_device_name(const MfPlusData* data, NfcDeviceNameType name_type) { + furi_check(data); + + if(name_type == NfcDeviceNameTypeFull) { + furi_string_printf( + data->device_name, + "Mifare %s %s %s", + mf_plus_type_strings[data->type], // Includes "Plus" for regular Mifare Plus cards + mf_plus_size_strings[data->size], + mf_plus_security_level_strings[data->security_level]); + } else if(name_type == NfcDeviceNameTypeShort) { + furi_string_set_str(data->device_name, MF_PLUS_PROTOCOL_NAME); + } else { + furi_crash("Unexpected name type"); + } + + return furi_string_get_cstr(data->device_name); +} + +const uint8_t* mf_plus_get_uid(const MfPlusData* data, size_t* uid_len) { + furi_check(data); + + return iso14443_4a_get_uid(data->iso14443_4a_data, uid_len); +} + +bool mf_plus_set_uid(MfPlusData* data, const uint8_t* uid, size_t uid_len) { + furi_check(data); + + return iso14443_4a_set_uid(data->iso14443_4a_data, uid, uid_len); +} +Iso14443_4aData* mf_plus_get_base_data(const MfPlusData* data) { + furi_check(data); + + return data->iso14443_4a_data; +} \ No newline at end of file diff --git a/lib/nfc/protocols/mf_plus/mf_plus.h b/lib/nfc/protocols/mf_plus/mf_plus.h new file mode 100644 index 000000000..31559ffdc --- /dev/null +++ b/lib/nfc/protocols/mf_plus/mf_plus.h @@ -0,0 +1,115 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define MF_PLUS_UID_SIZE_MAX (7) +#define MF_PLUS_BATCH_SIZE (5) + +#define MF_PLUS_CMD_GET_VERSION (0x60) + +typedef enum { + MfPlusErrorNone, + MfPlusErrorUnknown, + MfPlusErrorNotPresent, + MfPlusErrorProtocol, + MfPlusErrorAuth, + MfPlusErrorPartialRead, + MfPlusErrorTimeout, +} MfPlusError; + +typedef enum { + MfPlusTypePlus, + MfPlusTypeEV1, + MfPlusTypeEV2, + MfPlusTypeS, + MfPlusTypeSE, + MfPlusTypeX, + + MfPlusTypeUnknown, + MfPlusTypeNum, +} MfPlusType; + +typedef enum { + MfPlusSize1K, + MfPlusSize2K, + MfPlusSize4K, + + MfPlusSizeUnknown, + MfPlusSizeNum, +} MfPlusSize; + +typedef enum { + MfPlusSecurityLevel0, + MfPlusSecurityLevel1, + MfPlusSecurityLevel2, + MfPlusSecurityLevel3, + + MfPlusSecurityLevelUnknown, + MfPlusSecurityLevelNum, +} MfPlusSecurityLevel; + +typedef struct { + uint8_t hw_vendor; + uint8_t hw_type; + uint8_t hw_subtype; + uint8_t hw_major; + uint8_t hw_minor; + uint8_t hw_storage; + uint8_t hw_proto; + + uint8_t sw_vendor; + uint8_t sw_type; + uint8_t sw_subtype; + uint8_t sw_major; + uint8_t sw_minor; + uint8_t sw_storage; + uint8_t sw_proto; + + uint8_t uid[MF_PLUS_UID_SIZE_MAX]; + uint8_t batch[MF_PLUS_BATCH_SIZE]; + uint8_t prod_week; + uint8_t prod_year; +} MfPlusVersion; + +typedef struct { + Iso14443_4aData* iso14443_4a_data; + MfPlusVersion version; + MfPlusType type; + MfPlusSize size; + MfPlusSecurityLevel security_level; + FuriString* device_name; +} MfPlusData; + +extern const NfcDeviceBase nfc_device_mf_plus; + +MfPlusData* mf_plus_alloc(void); + +void mf_plus_free(MfPlusData* data); + +void mf_plus_reset(MfPlusData* data); + +void mf_plus_copy(MfPlusData* data, const MfPlusData* other); + +bool mf_plus_verify(MfPlusData* data, const FuriString* device_type); + +bool mf_plus_load(MfPlusData* data, FlipperFormat* ff, uint32_t version); + +bool mf_plus_save(const MfPlusData* data, FlipperFormat* ff); + +bool mf_plus_is_equal(const MfPlusData* data, const MfPlusData* other); + +const char* mf_plus_get_device_name(const MfPlusData* data, NfcDeviceNameType name_type); + +const uint8_t* mf_plus_get_uid(const MfPlusData* data, size_t* uid_len); + +bool mf_plus_set_uid(MfPlusData* data, const uint8_t* uid, size_t uid_len); + +Iso14443_4aData* mf_plus_get_base_data(const MfPlusData* data); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/lib/nfc/protocols/mf_plus/mf_plus_i.c b/lib/nfc/protocols/mf_plus/mf_plus_i.c new file mode 100644 index 000000000..d5fe5be82 --- /dev/null +++ b/lib/nfc/protocols/mf_plus/mf_plus_i.c @@ -0,0 +1,406 @@ +#include "mf_plus_i.h" + +#define MF_PLUS_FFF_VERSION_KEY \ + MF_PLUS_FFF_PICC_PREFIX " " \ + "Version" + +#define MF_PLUS_FFF_SECURITY_LEVEL_KEY "Security Level" +#define MF_PLUS_FFF_CARD_TYPE_KEY "Card Type" +#define MF_PLUS_FFF_MEMORY_SIZE_KEY "Memory Size" + +#define TAG "MfPlus" + +const uint8_t mf_plus_ats_t1_tk_values[][7] = { + {0xC1, 0x05, 0x2F, 0x2F, 0x00, 0x35, 0xC7}, // Mifare Plus S + {0xC1, 0x05, 0x2F, 0x2F, 0x01, 0xBC, 0xD6}, // Mifare Plus X + {0xC1, 0x05, 0x2F, 0x2F, 0x00, 0xF6, 0xD1}, // Mifare Plus SE + {0xC1, 0x05, 0x2F, 0x2F, 0x01, 0xF6, 0xD1}, // Mifare Plus SE +}; + +MfPlusError mf_plus_get_type_from_version( + const Iso14443_4aData* iso14443_4a_data, + MfPlusData* mf_plus_data) { + furi_assert(iso14443_4a_data); + furi_assert(mf_plus_data); + + MfPlusError error = MfPlusErrorProtocol; + + if(mf_plus_data->version.hw_major == 0x02 || mf_plus_data->version.hw_major == 0x82) { + error = MfPlusErrorNone; + if(iso14443_4a_data->iso14443_3a_data->sak == 0x10) { + // Mifare Plus 2K SL2 + mf_plus_data->type = MfPlusTypePlus; + mf_plus_data->size = MfPlusSize2K; + mf_plus_data->security_level = MfPlusSecurityLevel2; + FURI_LOG_D(TAG, "Mifare Plus 2K SL2"); + } else if(iso14443_4a_data->iso14443_3a_data->sak == 0x11) { + // Mifare Plus 4K SL3 + mf_plus_data->type = MfPlusTypePlus; + mf_plus_data->size = MfPlusSize4K; + mf_plus_data->security_level = MfPlusSecurityLevel3; + FURI_LOG_D(TAG, "Mifare Plus 4K SL3"); + } else { + // Mifare Plus EV1/EV2 + + // Revision + switch(mf_plus_data->version.hw_major) { + case 0x11: + mf_plus_data->type = MfPlusTypeEV1; + FURI_LOG_D(TAG, "Mifare Plus EV1"); + break; + case 0x22: + mf_plus_data->type = MfPlusTypeEV2; + FURI_LOG_D(TAG, "Mifare Plus EV2"); + break; + default: + mf_plus_data->type = MfPlusTypeUnknown; + FURI_LOG_D(TAG, "Unknown Mifare Plus EV type"); + break; + } + + // Storage size + switch(mf_plus_data->version.hw_storage) { + case 0x16: + mf_plus_data->size = MfPlusSize2K; + FURI_LOG_D(TAG, "2K"); + break; + case 0x18: + mf_plus_data->size = MfPlusSize4K; + FURI_LOG_D(TAG, "4K"); + break; + default: + mf_plus_data->size = MfPlusSizeUnknown; + FURI_LOG_D(TAG, "Unknown storage size"); + break; + } + + // Security level + if(iso14443_4a_data->iso14443_3a_data->sak == 0x20) { + // Mifare Plus EV1/2 SL3 + mf_plus_data->security_level = MfPlusSecurityLevel3; + FURI_LOG_D(TAG, "Miare Plus EV1/2 SL3"); + } else { + // Mifare Plus EV1/2 SL1 + mf_plus_data->security_level = MfPlusSecurityLevel1; + FURI_LOG_D(TAG, "Miare Plus EV1/2 SL1"); + } + } + } + + return error; +} + +MfPlusError + mf_plus_get_type_from_iso4(const Iso14443_4aData* iso4_data, MfPlusData* mf_plus_data) { + furi_assert(iso4_data); + furi_assert(mf_plus_data); + + MfPlusError error = MfPlusErrorProtocol; + + switch(iso4_data->iso14443_3a_data->sak) { + case 0x08: + if(memcmp( + simple_array_get_data(iso4_data->ats_data.t1_tk), + mf_plus_ats_t1_tk_values[0], + simple_array_get_count(iso4_data->ats_data.t1_tk)) == 0) { + // Mifare Plus S 2K SL1 + mf_plus_data->type = MfPlusTypeS; + mf_plus_data->size = MfPlusSize2K; + mf_plus_data->security_level = MfPlusSecurityLevel1; + + FURI_LOG_D(TAG, "Mifare Plus S 2K SL1"); + error = MfPlusErrorNone; + } else if( + memcmp( + simple_array_get_data(iso4_data->ats_data.t1_tk), + mf_plus_ats_t1_tk_values[1], + simple_array_get_count(iso4_data->ats_data.t1_tk)) == 0) { + // Mifare Plus X 2K SL1 + mf_plus_data->type = MfPlusTypeX; + mf_plus_data->size = MfPlusSize2K; + mf_plus_data->security_level = MfPlusSecurityLevel1; + + FURI_LOG_D(TAG, "Mifare Plus X 2K SL1"); + error = MfPlusErrorNone; + } else if( + memcmp( + simple_array_get_data(iso4_data->ats_data.t1_tk), + mf_plus_ats_t1_tk_values[2], + simple_array_get_count(iso4_data->ats_data.t1_tk)) == 0 || + memcmp( + simple_array_get_data(iso4_data->ats_data.t1_tk), + mf_plus_ats_t1_tk_values[3], + simple_array_get_count(iso4_data->ats_data.t1_tk)) == 0) { + // Mifare Plus SE 1K SL1 + mf_plus_data->type = MfPlusTypeSE; + mf_plus_data->size = MfPlusSize1K; + mf_plus_data->security_level = MfPlusSecurityLevel1; + + FURI_LOG_D(TAG, "Mifare Plus SE 1K SL1"); + error = MfPlusErrorNone; + } else { + FURI_LOG_D(TAG, "Sak 08 but no known Mifare Plus type"); + } + + break; + case 0x18: + if(memcmp( + simple_array_get_data(iso4_data->ats_data.t1_tk), + mf_plus_ats_t1_tk_values[0], + simple_array_get_count(iso4_data->ats_data.t1_tk)) == 0) { + // Mifare Plus S 4K SL1 + mf_plus_data->type = MfPlusTypeS; + mf_plus_data->size = MfPlusSize4K; + mf_plus_data->security_level = MfPlusSecurityLevel1; + + FURI_LOG_D(TAG, "Mifare Plus S 4K SL1"); + error = MfPlusErrorNone; + } else if( + memcmp( + simple_array_get_data(iso4_data->ats_data.t1_tk), + mf_plus_ats_t1_tk_values[1], + simple_array_get_count(iso4_data->ats_data.t1_tk)) == 0) { + // Mifare Plus X 4K SL1 + mf_plus_data->type = MfPlusTypeX; + mf_plus_data->size = MfPlusSize4K; + mf_plus_data->security_level = MfPlusSecurityLevel1; + + FURI_LOG_D(TAG, "Mifare Plus X 4K SL1"); + error = MfPlusErrorNone; + } else { + FURI_LOG_D(TAG, "Sak 18 but no known Mifare Plus type"); + } + + break; + case 0x20: + if(memcmp( + simple_array_get_data(iso4_data->ats_data.t1_tk), + mf_plus_ats_t1_tk_values[0], + simple_array_get_count(iso4_data->ats_data.t1_tk)) == 0) { + // Mifare Plus S 2/4K SL3 + FURI_LOG_D(TAG, "Mifare Plus S SL3"); + mf_plus_data->type = MfPlusTypeS; + mf_plus_data->security_level = MfPlusSecurityLevel3; + + if((iso4_data->iso14443_3a_data->atqa[0] & 0x0F) == 0x04) { + // Mifare Plus S 2K SL3 + mf_plus_data->size = MfPlusSize2K; + + FURI_LOG_D(TAG, "Mifare Plus S 2K SL3"); + error = MfPlusErrorNone; + } else if((iso4_data->iso14443_3a_data->atqa[0] & 0x0F) == 0x02) { + // Mifare Plus S 4K SL3 + mf_plus_data->size = MfPlusSize4K; + + FURI_LOG_D(TAG, "Mifare Plus S 4K SL3"); + error = MfPlusErrorNone; + } else { + FURI_LOG_D(TAG, "Sak 20 but no known Mifare Plus type (S)"); + } + } else if( + memcmp( + simple_array_get_data(iso4_data->ats_data.t1_tk), + mf_plus_ats_t1_tk_values[1], + simple_array_get_count(iso4_data->ats_data.t1_tk)) == 0) { + mf_plus_data->type = MfPlusTypeX; + mf_plus_data->security_level = MfPlusSecurityLevel3; + FURI_LOG_D(TAG, "Mifare Plus X SL3"); + + if((iso4_data->iso14443_3a_data->atqa[0] & 0x0F) == 0x04) { + mf_plus_data->size = MfPlusSize2K; + + FURI_LOG_D(TAG, "Mifare Plus X 2K SL3"); + error = MfPlusErrorNone; + } else if((iso4_data->iso14443_3a_data->atqa[0] & 0x0F) == 0x02) { + mf_plus_data->size = MfPlusSize4K; + + FURI_LOG_D(TAG, "Mifare Plus X 4K SL3"); + error = MfPlusErrorNone; + } else { + FURI_LOG_D(TAG, "Sak 20 but no known Mifare Plus type (X)"); + } + } else { + FURI_LOG_D(TAG, "Sak 20 but no known Mifare Plus type"); + } + } + + return error; +} + +MfPlusError mf_plus_version_parse(MfPlusVersion* data, const BitBuffer* buf) { + const bool can_parse = bit_buffer_get_size_bytes(buf) == sizeof(MfPlusVersion); + + if(can_parse) { + bit_buffer_write_bytes(buf, data, sizeof(MfPlusVersion)); + } + + return can_parse ? MfPlusErrorNone : MfPlusErrorProtocol; +} + +bool mf_plus_version_load(MfPlusVersion* data, FlipperFormat* ff) { + return flipper_format_read_hex( + ff, MF_PLUS_FFF_VERSION_KEY, (uint8_t*)data, sizeof(MfPlusVersion)); +} + +bool mf_plus_security_level_load(MfPlusSecurityLevel* data, FlipperFormat* ff) { + FuriString* security_level_string = furi_string_alloc(); + flipper_format_read_string(ff, MF_PLUS_FFF_SECURITY_LEVEL_KEY, security_level_string); + + // Take the last character of the string + char security_level_char = furi_string_get_char( + security_level_string, furi_string_utf8_length(security_level_string) - 1); + + switch(security_level_char) { + case '0': + *data = MfPlusSecurityLevel0; + break; + case '1': + *data = MfPlusSecurityLevel1; + break; + case '2': + *data = MfPlusSecurityLevel2; + break; + case '3': + *data = MfPlusSecurityLevel3; + break; + default: + *data = MfPlusSecurityLevelUnknown; + break; + } + + furi_string_free(security_level_string); + + return true; +} + +bool mf_plus_type_load(MfPlusType* data, FlipperFormat* ff) { + FuriString* type_string = furi_string_alloc(); + flipper_format_read_string(ff, MF_PLUS_FFF_CARD_TYPE_KEY, type_string); + + if(furi_string_equal_str(type_string, "Mifare Plus")) { + *data = MfPlusTypePlus; + } else if(furi_string_equal_str(type_string, "Mifare Plus X")) { + *data = MfPlusTypeX; + } else if(furi_string_equal_str(type_string, "Mifare Plus S")) { + *data = MfPlusTypeS; + } else if(furi_string_equal_str(type_string, "Mifare Plus SE")) { + *data = MfPlusTypeSE; + } else if(furi_string_equal_str(type_string, "Mifare Plus EV1")) { + *data = MfPlusTypeEV1; + } else if(furi_string_equal_str(type_string, "Mifare Plus EV2")) { + *data = MfPlusTypeEV2; + } else { + *data = MfPlusTypeUnknown; + } + + furi_string_free(type_string); + return true; +} + +bool mf_plus_size_load(MfPlusSize* data, FlipperFormat* ff) { + FuriString* size_string = furi_string_alloc(); + flipper_format_read_string(ff, MF_PLUS_FFF_MEMORY_SIZE_KEY, size_string); + + if(furi_string_equal_str(size_string, "1K")) { + *data = MfPlusSize1K; + } else if(furi_string_equal_str(size_string, "2K")) { + *data = MfPlusSize2K; + } else if(furi_string_equal_str(size_string, "4K")) { + *data = MfPlusSize4K; + } else { + *data = MfPlusSizeUnknown; + } + + furi_string_free(size_string); + return true; +} + +bool mf_plus_version_save(const MfPlusVersion* data, FlipperFormat* ff) { + return flipper_format_write_hex( + ff, MF_PLUS_FFF_VERSION_KEY, (const uint8_t*)data, sizeof(MfPlusVersion)); +} + +bool mf_plus_security_level_save(const MfPlusSecurityLevel* data, FlipperFormat* ff) { + FuriString* security_level_string = furi_string_alloc(); + + switch(*data) { + case MfPlusSecurityLevel0: + furi_string_cat(security_level_string, "SL0"); + break; + case MfPlusSecurityLevel1: + furi_string_cat(security_level_string, "SL1"); + break; + case MfPlusSecurityLevel2: + furi_string_cat(security_level_string, "SL2"); + break; + case MfPlusSecurityLevel3: + furi_string_cat(security_level_string, "SL3"); + break; + default: + furi_string_cat(security_level_string, "Unknown"); + break; + } + + bool success = + flipper_format_write_string(ff, MF_PLUS_FFF_SECURITY_LEVEL_KEY, security_level_string); + furi_string_free(security_level_string); + + return success; +} + +bool mf_plus_type_save(const MfPlusType* data, FlipperFormat* ff) { + FuriString* type_string = furi_string_alloc(); + + switch(*data) { + case MfPlusTypePlus: + furi_string_cat(type_string, "Mifare Plus"); + break; + case MfPlusTypeX: + furi_string_cat(type_string, "Mifare Plus X"); + break; + case MfPlusTypeS: + furi_string_cat(type_string, "Mifare Plus S"); + break; + case MfPlusTypeSE: + furi_string_cat(type_string, "Mifare Plus SE"); + break; + case MfPlusTypeEV1: + furi_string_cat(type_string, "Mifare Plus EV1"); + break; + case MfPlusTypeEV2: + furi_string_cat(type_string, "Mifare Plus EV2"); + break; + default: + furi_string_cat(type_string, "Unknown"); + break; + } + + bool success = flipper_format_write_string(ff, MF_PLUS_FFF_CARD_TYPE_KEY, type_string); + furi_string_free(type_string); + + return success; +} + +bool mf_plus_size_save(const MfPlusSize* data, FlipperFormat* ff) { + FuriString* size_string = furi_string_alloc(); + + switch(*data) { + case MfPlusSize1K: + furi_string_cat(size_string, "1K"); + break; + case MfPlusSize2K: + furi_string_cat(size_string, "2K"); + break; + case MfPlusSize4K: + furi_string_cat(size_string, "4K"); + break; + default: + furi_string_cat(size_string, "Unknown"); + break; + } + + bool success = flipper_format_write_string(ff, MF_PLUS_FFF_MEMORY_SIZE_KEY, size_string); + furi_string_free(size_string); + + return success; +} diff --git a/lib/nfc/protocols/mf_plus/mf_plus_i.h b/lib/nfc/protocols/mf_plus/mf_plus_i.h new file mode 100644 index 000000000..1b80030f9 --- /dev/null +++ b/lib/nfc/protocols/mf_plus/mf_plus_i.h @@ -0,0 +1,29 @@ +#pragma once + +#include "mf_plus.h" + +#define MF_PLUS_FFF_PICC_PREFIX "PICC" + +MfPlusError mf_plus_get_type_from_version( + const Iso14443_4aData* iso14443_4a_data, + MfPlusData* mf_plus_data); + +MfPlusError mf_plus_get_type_from_iso4(const Iso14443_4aData* iso4_data, MfPlusData* mf_plus_data); + +MfPlusError mf_plus_version_parse(MfPlusVersion* data, const BitBuffer* buf); + +bool mf_plus_version_load(MfPlusVersion* data, FlipperFormat* ff); + +bool mf_plus_security_level_load(MfPlusSecurityLevel* data, FlipperFormat* ff); + +bool mf_plus_type_load(MfPlusType* data, FlipperFormat* ff); + +bool mf_plus_size_load(MfPlusSize* data, FlipperFormat* ff); + +bool mf_plus_version_save(const MfPlusVersion* data, FlipperFormat* ff); + +bool mf_plus_security_level_save(const MfPlusSecurityLevel* data, FlipperFormat* ff); + +bool mf_plus_type_save(const MfPlusType* data, FlipperFormat* ff); + +bool mf_plus_size_save(const MfPlusSize* data, FlipperFormat* ff); diff --git a/lib/nfc/protocols/mf_plus/mf_plus_poller.c b/lib/nfc/protocols/mf_plus/mf_plus_poller.c new file mode 100644 index 000000000..c93ec9e67 --- /dev/null +++ b/lib/nfc/protocols/mf_plus/mf_plus_poller.c @@ -0,0 +1,210 @@ +#include "mf_plus_poller_i.h" +#include "mf_plus_i.h" + +#include + +#include + +#define TAG "MfPlusPoller" + +#define MF_PLUS_BUF_SIZE (64U) +#define MF_PLUS_RESULT_BUF_SIZE (512U) + +typedef NfcCommand (*MfPlusPollerReadHandler)(MfPlusPoller* instance); + +const MfPlusData* mf_plus_poller_get_data(MfPlusPoller* instance) { + furi_assert(instance); + + return instance->data; +} + +MfPlusPoller* mf_plus_poller_alloc(Iso14443_4aPoller* iso14443_4a_poller) { + furi_assert(iso14443_4a_poller); + + MfPlusPoller* instance = malloc(sizeof(MfPlusPoller)); + + instance->iso14443_4a_poller = iso14443_4a_poller; + + instance->data = mf_plus_alloc(); + + instance->tx_buffer = bit_buffer_alloc(MF_PLUS_BUF_SIZE); + instance->rx_buffer = bit_buffer_alloc(MF_PLUS_BUF_SIZE); + instance->input_buffer = bit_buffer_alloc(MF_PLUS_BUF_SIZE); + instance->result_buffer = bit_buffer_alloc(MF_PLUS_RESULT_BUF_SIZE); + + instance->general_event.protocol = NfcProtocolMfPlus; + instance->general_event.event_data = &instance->mfp_event; + instance->general_event.instance = instance; + + instance->mfp_event.data = &instance->mfp_event_data; + + return instance; +} + +static NfcCommand mf_plus_poller_handler_idle(MfPlusPoller* instance) { + furi_assert(instance); + + bit_buffer_reset(instance->input_buffer); + bit_buffer_reset(instance->result_buffer); + bit_buffer_reset(instance->tx_buffer); + bit_buffer_reset(instance->rx_buffer); + + iso14443_4a_copy( + instance->data->iso14443_4a_data, + iso14443_4a_poller_get_data(instance->iso14443_4a_poller)); + + instance->state = MfPlusPollerStateReadVersion; + return NfcCommandContinue; +} + +static NfcCommand mf_plus_poller_handler_read_version(MfPlusPoller* instance) { + MfPlusError error = mf_plus_poller_read_version(instance, &instance->data->version); + if(error == MfPlusErrorNone) { + instance->state = MfPlusPollerStateParseVersion; + } else { + instance->state = MfPlusPollerStateParseIso4; + } + + return NfcCommandContinue; +} + +static NfcCommand mf_plus_poller_handler_parse_version(MfPlusPoller* instance) { + furi_assert(instance); + + MfPlusError error = mf_plus_get_type_from_version( + iso14443_4a_poller_get_data(instance->iso14443_4a_poller), instance->data); + if(error == MfPlusErrorNone) { + instance->state = MfPlusPollerStateReadSuccess; + } else { + instance->error = error; + instance->state = MfPlusPollerStateReadFailed; + } + + return NfcCommandContinue; +} + +static NfcCommand mf_plus_poller_handler_parse_iso4(MfPlusPoller* instance) { + furi_assert(instance); + + MfPlusError error = mf_plus_get_type_from_iso4( + iso14443_4a_poller_get_data(instance->iso14443_4a_poller), instance->data); + if(error == MfPlusErrorNone) { + instance->state = MfPlusPollerStateReadSuccess; + } else { + instance->error = error; + instance->state = MfPlusPollerStateReadFailed; + } + + return NfcCommandContinue; +} + +static NfcCommand mf_plus_poller_handler_read_failed(MfPlusPoller* instance) { + furi_assert(instance); + + FURI_LOG_D(TAG, "Read failed"); + iso14443_4a_poller_halt(instance->iso14443_4a_poller); + + instance->mfp_event.type = MfPlusPollerEventTypeReadFailed; + instance->mfp_event.data->error = instance->error; + NfcCommand command = instance->callback(instance->general_event, instance->context); + instance->state = MfPlusPollerStateIdle; + + return command; +} + +static NfcCommand mf_plus_poller_handler_read_success(MfPlusPoller* instance) { + furi_assert(instance); + + FURI_LOG_D(TAG, "Read success"); + iso14443_4a_poller_halt(instance->iso14443_4a_poller); + + instance->mfp_event.type = MfPlusPollerEventTypeReadSuccess; + NfcCommand command = instance->callback(instance->general_event, instance->context); + + return command; +} + +static const MfPlusPollerReadHandler mf_plus_poller_read_handler[MfPlusPollerStateNum] = { + [MfPlusPollerStateIdle] = mf_plus_poller_handler_idle, + [MfPlusPollerStateReadVersion] = mf_plus_poller_handler_read_version, + [MfPlusPollerStateParseVersion] = mf_plus_poller_handler_parse_version, + [MfPlusPollerStateParseIso4] = mf_plus_poller_handler_parse_iso4, + [MfPlusPollerStateReadFailed] = mf_plus_poller_handler_read_failed, + [MfPlusPollerStateReadSuccess] = mf_plus_poller_handler_read_success, +}; + +static void mf_plus_poller_set_callback( + MfPlusPoller* instance, + NfcGenericCallback callback, + void* context) { + furi_assert(instance); + furi_assert(callback); + + instance->callback = callback; + instance->context = context; +} + +static NfcCommand mf_plus_poller_run(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.protocol = NfcProtocolIso14443_4a); + furi_assert(event.event_data); + + MfPlusPoller* instance = context; + const Iso14443_4aPollerEvent* iso14443_4a_event = event.event_data; + + NfcCommand command = NfcCommandContinue; + + if(iso14443_4a_event->type == Iso14443_4aPollerEventTypeReady) { + command = mf_plus_poller_read_handler[instance->state](instance); + } else if(iso14443_4a_event->type == Iso14443_4aPollerEventTypeError) { + instance->mfp_event.type = MfPlusPollerEventTypeReadFailed; + command = instance->callback(instance->general_event, instance->context); + } + + return command; +} + +void mf_plus_poller_free(MfPlusPoller* instance) { + furi_assert(instance); + furi_assert(instance->data); + + bit_buffer_free(instance->tx_buffer); + bit_buffer_free(instance->rx_buffer); + bit_buffer_free(instance->input_buffer); + bit_buffer_free(instance->result_buffer); + mf_plus_free(instance->data); + free(instance); +} + +static bool mf_plus_poller_detect(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.protocol = NfcProtocolIso14443_4a); + furi_assert(event.event_data); + + MfPlusPoller* instance = context; + Iso14443_4aPollerEvent* iso14443_4a_event = event.event_data; + + MfPlusError error = MfPlusErrorUnknown; + + if(iso14443_4a_event->type == Iso14443_4aPollerEventTypeReady) { + error = mf_plus_poller_read_version(instance, &instance->data->version); + if(error == MfPlusErrorNone) { + error = mf_plus_get_type_from_version( + iso14443_4a_poller_get_data(instance->iso14443_4a_poller), instance->data); + } else { + error = mf_plus_get_type_from_iso4( + iso14443_4a_poller_get_data(instance->iso14443_4a_poller), instance->data); + } + } + + return (error == MfPlusErrorNone); +} + +const NfcPollerBase mf_plus_poller = { + .alloc = (NfcPollerAlloc)mf_plus_poller_alloc, + .free = (NfcPollerFree)mf_plus_poller_free, + .set_callback = (NfcPollerSetCallback)mf_plus_poller_set_callback, + .run = (NfcPollerRun)mf_plus_poller_run, + .detect = (NfcPollerDetect)mf_plus_poller_detect, + .get_data = (NfcPollerGetData)mf_plus_poller_get_data, +}; diff --git a/lib/nfc/protocols/mf_plus/mf_plus_poller.h b/lib/nfc/protocols/mf_plus/mf_plus_poller.h new file mode 100644 index 000000000..7e892366f --- /dev/null +++ b/lib/nfc/protocols/mf_plus/mf_plus_poller.h @@ -0,0 +1,55 @@ +#pragma once + +#include "mf_plus.h" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief MIFARE Plus poller opaque type definition. + */ +typedef struct MfPlusPoller MfPlusPoller; + +/** + * @brief Enumeration of possible MfPlus poller event types. + */ + +typedef enum { + MfPlusPollerEventTypeReadSuccess, /**< Card was read successfully. */ + MfPlusPollerEventTypeReadFailed, /**< Poller failed to read the card. */ +} MfPlusPollerEventType; + +/** + * @brief MIFARE Plus poller event data. + */ +typedef union { + MfPlusError error; /**< Error code indicating card reading fail reason. */ +} MfPlusPollerEventData; + +/** + * @brief MIFARE Plus poller event structure. + * + * Upon emission of an event, an instance of this struct will be passed to the callback. + */ +typedef struct { + MfPlusPollerEventType type; /**< Type of emitted event. */ + MfPlusPollerEventData* data; /**< Pointer to event specific data. */ +} MfPlusPollerEvent; + +/** + * @brief Read MfPlus card version. + * + * Must ONLY be used inside the callback function. + * + * @param[in, out] instance pointer to the instance to be used in the transaction. + * @param[out] data pointer to the MfPlusVersion structure to be filled with version data. + * @return MfPlusErrorNone on success, an error code on failure. + */ +MfPlusError mf_plus_poller_read_version(MfPlusPoller* instance, MfPlusVersion* data); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/lib/nfc/protocols/mf_plus/mf_plus_poller_defs.h b/lib/nfc/protocols/mf_plus/mf_plus_poller_defs.h new file mode 100644 index 000000000..366eb5116 --- /dev/null +++ b/lib/nfc/protocols/mf_plus/mf_plus_poller_defs.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +extern const NfcPollerBase mf_plus_poller; diff --git a/lib/nfc/protocols/mf_plus/mf_plus_poller_i.c b/lib/nfc/protocols/mf_plus/mf_plus_poller_i.c new file mode 100644 index 000000000..cab906f1d --- /dev/null +++ b/lib/nfc/protocols/mf_plus/mf_plus_poller_i.c @@ -0,0 +1,59 @@ +#include "mf_plus_poller_i.h" + +#include + +#include "mf_plus_i.h" + +#define TAG "MfPlusPoller" + +MfPlusError mf_plus_process_error(Iso14443_4aError error) { + switch(error) { + case Iso14443_4aErrorNone: + return MfPlusErrorNone; + case Iso14443_4aErrorNotPresent: + return MfPlusErrorNotPresent; + case Iso14443_4aErrorTimeout: + return MfPlusErrorTimeout; + default: + return MfPlusErrorProtocol; + } +} + +MfPlusError mf_plus_poller_send_chunk( + MfPlusPoller* instance, + const BitBuffer* tx_buffer, + BitBuffer* rx_buffer) { + furi_assert(instance); + furi_assert(instance->iso14443_4a_poller); + furi_assert(instance->tx_buffer); + furi_assert(instance->rx_buffer); + furi_assert(tx_buffer); + furi_assert(rx_buffer); + + Iso14443_4aError iso14443_4a_error = iso14443_4a_poller_send_block( + instance->iso14443_4a_poller, tx_buffer, instance->rx_buffer); + MfPlusError error = mf_plus_process_error(iso14443_4a_error); + + if(error == MfPlusErrorNone) { + bit_buffer_copy(rx_buffer, instance->rx_buffer); + } + + bit_buffer_reset(instance->tx_buffer); + + return error; +} + +MfPlusError mf_plus_poller_read_version(MfPlusPoller* instance, MfPlusVersion* data) { + furi_check(instance); + + bit_buffer_reset(instance->input_buffer); + bit_buffer_append_byte(instance->input_buffer, MF_PLUS_CMD_GET_VERSION); + + MfPlusError error = + mf_plus_poller_send_chunk(instance, instance->input_buffer, instance->result_buffer); + if(error == MfPlusErrorNone) { + error = mf_plus_version_parse(data, instance->result_buffer); + } + + return error; +} diff --git a/lib/nfc/protocols/mf_plus/mf_plus_poller_i.h b/lib/nfc/protocols/mf_plus/mf_plus_poller_i.h new file mode 100644 index 000000000..c7b547a84 --- /dev/null +++ b/lib/nfc/protocols/mf_plus/mf_plus_poller_i.h @@ -0,0 +1,56 @@ +#pragma once + +#include "mf_plus_poller.h" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define MF_PLUS_FWT_FC (60000) + +typedef enum { + MfPlusCardStateDetected, + MfPlusCardStateLost, +} MfPlusCardState; + +typedef enum { + MfPlusPollerStateIdle, + MfPlusPollerStateReadVersion, + MfPlusPollerStateParseVersion, + MfPlusPollerStateParseIso4, + MfPlusPollerStateReadFailed, + MfPlusPollerStateReadSuccess, + + MfPlusPollerStateNum, +} MfPlusPollerState; + +struct MfPlusPoller { + Iso14443_4aPoller* iso14443_4a_poller; + + MfPlusData* data; + MfPlusPollerState state; + + BitBuffer* tx_buffer; + BitBuffer* rx_buffer; + BitBuffer* input_buffer; + BitBuffer* result_buffer; + + MfPlusError error; + NfcGenericEvent general_event; + MfPlusPollerEvent mfp_event; + MfPlusPollerEventData mfp_event_data; + NfcGenericCallback callback; + void* context; +}; + +MfPlusError mf_plus_process_error(Iso14443_4aError error); + +MfPlusPoller* mf_plus_poller_alloc(Iso14443_4aPoller* iso14443_4a_poller); + +void mf_plus_poller_free(MfPlusPoller* instance); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/nfc_device_defs.c b/lib/nfc/protocols/nfc_device_defs.c index 870bcafd9..6a145445c 100644 --- a/lib/nfc/protocols/nfc_device_defs.c +++ b/lib/nfc/protocols/nfc_device_defs.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -39,6 +40,7 @@ const NfcDeviceBase* nfc_devices[NfcProtocolNum] = { [NfcProtocolFelica] = &nfc_device_felica, [NfcProtocolMfUltralight] = &nfc_device_mf_ultralight, [NfcProtocolMfClassic] = &nfc_device_mf_classic, + [NfcProtocolMfPlus] = &nfc_device_mf_plus, [NfcProtocolMfDesfire] = &nfc_device_mf_desfire, [NfcProtocolSlix] = &nfc_device_slix, [NfcProtocolSt25tb] = &nfc_device_st25tb, diff --git a/lib/nfc/protocols/nfc_poller_defs.c b/lib/nfc/protocols/nfc_poller_defs.c index 7553c74de..c007740b7 100644 --- a/lib/nfc/protocols/nfc_poller_defs.c +++ b/lib/nfc/protocols/nfc_poller_defs.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -21,6 +22,7 @@ const NfcPollerBase* nfc_pollers_api[NfcProtocolNum] = { [NfcProtocolFelica] = &nfc_poller_felica, [NfcProtocolMfUltralight] = &mf_ultralight_poller, [NfcProtocolMfClassic] = &mf_classic_poller, + [NfcProtocolMfPlus] = &mf_plus_poller, [NfcProtocolMfDesfire] = &mf_desfire_poller, [NfcProtocolSlix] = &nfc_poller_slix, /* Add new pollers here */ diff --git a/lib/nfc/protocols/nfc_protocol.c b/lib/nfc/protocols/nfc_protocol.c index 63f05c8d9..40201843e 100644 --- a/lib/nfc/protocols/nfc_protocol.c +++ b/lib/nfc/protocols/nfc_protocol.c @@ -61,6 +61,7 @@ static const NfcProtocol nfc_protocol_iso14443_3b_children_protocol[] = { /** List of ISO14443-4A child protocols. */ static const NfcProtocol nfc_protocol_iso14443_4a_children_protocol[] = { NfcProtocolMfDesfire, + NfcProtocolMfPlus, }; /** List of ISO115693-3 child protocols. */ @@ -128,6 +129,12 @@ static const NfcProtocolTreeNode nfc_protocol_nodes[NfcProtocolNum] = { .children_num = 0, .children_protocol = NULL, }, + [NfcProtocolMfPlus] = + { + .parent_protocol = NfcProtocolIso14443_4a, + .children_num = 0, + .children_protocol = NULL, + }, [NfcProtocolMfDesfire] = { .parent_protocol = NfcProtocolIso14443_4a, diff --git a/lib/nfc/protocols/nfc_protocol.h b/lib/nfc/protocols/nfc_protocol.h index ee6345333..cf74972f7 100644 --- a/lib/nfc/protocols/nfc_protocol.h +++ b/lib/nfc/protocols/nfc_protocol.h @@ -184,6 +184,7 @@ typedef enum { NfcProtocolFelica, NfcProtocolMfUltralight, NfcProtocolMfClassic, + NfcProtocolMfPlus, NfcProtocolMfDesfire, NfcProtocolSlix, NfcProtocolSt25tb, diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 2686cf1bf..85150a58b 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -156,6 +156,8 @@ Header,+,lib/nfc/protocols/mf_classic/mf_classic_poller.h,, Header,+,lib/nfc/protocols/mf_classic/mf_classic_poller_sync.h,, Header,+,lib/nfc/protocols/mf_desfire/mf_desfire.h,, Header,+,lib/nfc/protocols/mf_desfire/mf_desfire_poller.h,, +Header,+,lib/nfc/protocols/mf_plus/mf_plus.h,, +Header,+,lib/nfc/protocols/mf_plus/mf_plus_poller.h,, Header,+,lib/nfc/protocols/mf_ultralight/mf_ultralight.h,, Header,+,lib/nfc/protocols/mf_ultralight/mf_ultralight_listener.h,, Header,+,lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.h,, @@ -2531,6 +2533,19 @@ Function,+,mf_desfire_save,_Bool,"const MfDesfireData*, FlipperFormat*" Function,+,mf_desfire_send_chunks,MfDesfireError,"MfDesfirePoller*, const BitBuffer*, BitBuffer*" Function,+,mf_desfire_set_uid,_Bool,"MfDesfireData*, const uint8_t*, size_t" Function,+,mf_desfire_verify,_Bool,"MfDesfireData*, const FuriString*" +Function,+,mf_plus_alloc,MfPlusData*, +Function,+,mf_plus_copy,void,"MfPlusData*, const MfPlusData*" +Function,+,mf_plus_free,void,MfPlusData* +Function,+,mf_plus_get_base_data,Iso14443_4aData*,const MfPlusData* +Function,+,mf_plus_get_device_name,const char*,"const MfPlusData*, NfcDeviceNameType" +Function,+,mf_plus_get_uid,const uint8_t*,"const MfPlusData*, size_t*" +Function,+,mf_plus_is_equal,_Bool,"const MfPlusData*, const MfPlusData*" +Function,+,mf_plus_load,_Bool,"MfPlusData*, FlipperFormat*, uint32_t" +Function,+,mf_plus_poller_read_version,MfPlusError,"MfPlusPoller*, MfPlusVersion*" +Function,+,mf_plus_reset,void,MfPlusData* +Function,+,mf_plus_save,_Bool,"const MfPlusData*, FlipperFormat*" +Function,+,mf_plus_set_uid,_Bool,"MfPlusData*, const uint8_t*, size_t" +Function,+,mf_plus_verify,_Bool,"MfPlusData*, const FuriString*" Function,+,mf_ultralight_alloc,MfUltralightData*, Function,+,mf_ultralight_copy,void,"MfUltralightData*, const MfUltralightData*" Function,+,mf_ultralight_detect_protocol,_Bool,const Iso14443_3aData* @@ -3843,6 +3858,7 @@ Variable,+,message_vibro_on,const NotificationMessage, Variable,-,nfc_device_felica,const NfcDeviceBase, Variable,-,nfc_device_mf_classic,const NfcDeviceBase, Variable,-,nfc_device_mf_desfire,const NfcDeviceBase, +Variable,-,nfc_device_mf_plus,const NfcDeviceBase, Variable,-,nfc_device_mf_ultralight,const NfcDeviceBase, Variable,-,nfc_device_st25tb,const NfcDeviceBase, Variable,+,sequence_audiovisual_alert,const NotificationSequence,