diff --git a/.clangd b/.clangd index 3e0024e8c..12e13751d 100644 --- a/.clangd +++ b/.clangd @@ -5,6 +5,10 @@ CompileFlags: Remove: - -mword-relocations +Diagnostics: + ClangTidy: + FastCheckFilter: None + --- If: diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml index 40f72bd0b..d35ca0c17 100644 --- a/.github/workflows/unit_tests.yml +++ b/.github/workflows/unit_tests.yml @@ -64,6 +64,6 @@ jobs: python3 scripts/testing/units.py ${{steps.device.outputs.flipper}} - name: 'Check GDB output' - if: failure() + if: failure() && steps.flashing.outcome == 'success' run: | ./fbt gdb_trace_all SWD_TRANSPORT_SERIAL=2A0906016415303030303032 LIB_DEBUG=1 FIRMWARE_APP_SET=unit_tests FORCE=1 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/accessor/scene/accessor_scene_start.cpp b/applications/debug/accessor/scene/accessor_scene_start.cpp index d31001d2d..79f51f061 100644 --- a/applications/debug/accessor/scene/accessor_scene_start.cpp +++ b/applications/debug/accessor/scene/accessor_scene_start.cpp @@ -1,7 +1,6 @@ #include "../accessor_app.h" #include "../accessor_view_manager.h" #include "../accessor_event.h" -#include "callback_connector.h" #include "accessor_scene_start.h" void AccessorSceneStart::on_enter(AccessorApp* app) { diff --git a/applications/debug/battery_test_app/views/battery_info.c b/applications/debug/battery_test_app/views/battery_info.c index 4b5dcd627..5c5a3bd45 100644 --- a/applications/debug/battery_test_app/views/battery_info.c +++ b/applications/debug/battery_test_app/views/battery_info.c @@ -68,7 +68,7 @@ static void draw_battery(Canvas* canvas, BatteryInfoModel* data, int x, int y) { drain_current > HIGH_DRAIN_CURRENT_THRESHOLD ? "mA!" : "mA"); } else if(drain_current != 0) { snprintf(header, 20, "..."); - } else if(data->charging_voltage < 4.2) { + } else if(data->charging_voltage < 4.2f) { // Non-default battery charging limit, mention it snprintf(emote, sizeof(emote), "Charged!"); snprintf(header, sizeof(header), "Limited to"); 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/display_test/display_test.c b/applications/debug/display_test/display_test.c index ac1b6c65d..3028a13b9 100644 --- a/applications/debug/display_test/display_test.c +++ b/applications/debug/display_test/display_test.c @@ -1,5 +1,3 @@ -#include "display_test.h" - #include #include diff --git a/applications/debug/display_test/display_test.h b/applications/debug/display_test/display_test.h deleted file mode 100644 index 6f70f09be..000000000 --- a/applications/debug/display_test/display_test.h +++ /dev/null @@ -1 +0,0 @@ -#pragma once 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/lfrfid_debug/views/lfrfid_debug_view_tune.c b/applications/debug/lfrfid_debug/views/lfrfid_debug_view_tune.c index 6b9501e5b..ddca372e3 100644 --- a/applications/debug/lfrfid_debug/views/lfrfid_debug_view_tune.c +++ b/applications/debug/lfrfid_debug/views/lfrfid_debug_view_tune.c @@ -34,8 +34,8 @@ static void lfrfid_debug_view_tune_draw_callback(Canvas* canvas, void* _model) { canvas_set_color(canvas, ColorBlack); char buffer[TEMP_STR_LEN + 1]; - double freq = ((float)SystemCoreClock / ((float)model->ARR + 1)); - double duty = ((float)model->CCR + 1) / ((float)model->ARR + 1) * 100.0f; + double freq = ((double)SystemCoreClock / (model->ARR + 1)); + double duty = (double)((model->CCR + 1) * 100) / (model->ARR + 1); snprintf( buffer, TEMP_STR_LEN, diff --git a/applications/debug/subghz_test/scenes/subghz_test_scene.c b/applications/debug/subghz_test/scenes/subghz_test_scene.c index ff439ef0f..fad16ddb4 100644 --- a/applications/debug/subghz_test/scenes/subghz_test_scene.c +++ b/applications/debug/subghz_test/scenes/subghz_test_scene.c @@ -1,4 +1,4 @@ -#include "../subghz_test_app_i.h" +#include "../subghz_test_app_i.h" // IWYU pragma: keep // Generate scene on_enter handlers array #define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, diff --git a/applications/debug/subghz_test/subghz_test_app_i.c b/applications/debug/subghz_test/subghz_test_app_i.c index 0ec6635a0..e0daee6a6 100644 --- a/applications/debug/subghz_test/subghz_test_app_i.c +++ b/applications/debug/subghz_test/subghz_test_app_i.c @@ -1,5 +1,3 @@ -#include "subghz_test_app_i.h" - #include #define TAG "SubGhzTest" diff --git a/applications/debug/subghz_test/views/subghz_test_carrier.c b/applications/debug/subghz_test/views/subghz_test_carrier.c index a941cd4bb..2c82380f7 100644 --- a/applications/debug/subghz_test/views/subghz_test_carrier.c +++ b/applications/debug/subghz_test/views/subghz_test_carrier.c @@ -1,5 +1,4 @@ #include "subghz_test_carrier.h" -#include "../subghz_test_app_i.h" #include "../helpers/subghz_test_frequency.h" #include @@ -74,7 +73,7 @@ void subghz_test_carrier_draw(Canvas* canvas, SubGhzTestCarrierModel* model) { sizeof(buffer), "RSSI: %ld.%ld dBm", (int32_t)(model->rssi), - (int32_t)fabs(model->rssi * 10) % 10); + (int32_t)fabsf(model->rssi * 10.f) % 10); canvas_draw_str(canvas, 0, 42, buffer); } else { canvas_draw_str(canvas, 0, 42, "TX"); diff --git a/applications/debug/subghz_test/views/subghz_test_packet.c b/applications/debug/subghz_test/views/subghz_test_packet.c index d50f10ad4..674e7c737 100644 --- a/applications/debug/subghz_test/views/subghz_test_packet.c +++ b/applications/debug/subghz_test/views/subghz_test_packet.c @@ -1,5 +1,4 @@ #include "subghz_test_packet.h" -#include "../subghz_test_app_i.h" #include "../helpers/subghz_test_frequency.h" #include @@ -123,7 +122,7 @@ static void subghz_test_packet_draw(Canvas* canvas, SubGhzTestPacketModel* model sizeof(buffer), "RSSI: %ld.%ld dBm", (int32_t)(model->rssi), - (int32_t)fabs(model->rssi * 10) % 10); + (int32_t)fabsf(model->rssi * 10.0f) % 10); canvas_draw_str(canvas, 0, 53, buffer); } else { canvas_draw_str(canvas, 0, 53, "TX"); diff --git a/applications/debug/subghz_test/views/subghz_test_static.c b/applications/debug/subghz_test/views/subghz_test_static.c index 22ad662c5..b7b514bd3 100644 --- a/applications/debug/subghz_test/views/subghz_test_static.c +++ b/applications/debug/subghz_test/views/subghz_test_static.c @@ -1,9 +1,7 @@ #include "subghz_test_static.h" -#include "../subghz_test_app_i.h" #include "../helpers/subghz_test_frequency.h" #include -#include #include #include #include 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/resources/unit_tests/nfc/Felica.nfc b/applications/debug/unit_tests/resources/unit_tests/nfc/Felica.nfc new file mode 100644 index 000000000..93ba4ba6c --- /dev/null +++ b/applications/debug/unit_tests/resources/unit_tests/nfc/Felica.nfc @@ -0,0 +1,40 @@ +Filetype: Flipper NFC device +Version: 4 +# Device type can be ISO14443-3A, ISO14443-3B, ISO14443-4A, ISO14443-4B, ISO15693-3, FeliCa, NTAG/Ultralight, Mifare Classic, Mifare DESFire, SLIX, ST25TB +Device type: FeliCa +# UID is common for all formats +UID: 29 9F FA 53 AB 75 87 6E +# FeliCa specific data +Data format version: 1 +Manufacture id: 29 9F FA 53 AB 75 87 6E +Manufacture parameter: 57 4E 10 2A 94 16 BC 8E +Blocks total: 28 +Blocks read: 28 +Block 0: 00 00 DE AD BE AF 00 00 00 00 00 00 00 00 DE AD BE AF +Block 1: 00 00 00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF +Block 2: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +Block 3: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +Block 4: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +Block 5: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +Block 6: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +Block 7: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +Block 8: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +Block 9: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +Block 10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +Block 11: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +Block 12: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +Block 13: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +Block 14: 00 00 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF +Block 15: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +Block 16: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +Block 17: 00 00 29 9F FA 53 AB 75 87 6E 57 4E 10 2A 94 16 BC 8E +Block 18: 00 00 29 9F FA 53 AB 75 87 6E 00 F1 00 00 00 01 43 00 +Block 19: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +Block 20: 00 00 88 B4 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +Block 21: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +Block 22: 00 00 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF +Block 23: 00 00 FF FF FF 00 FF 00 10 00 00 00 00 00 00 00 00 00 +Block 24: 00 00 24 FE FF 00 00 00 00 00 00 00 00 00 00 00 00 00 +Block 25: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +Block 26: 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +Block 27: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 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/bit_lib/bit_lib_test.c b/applications/debug/unit_tests/tests/bit_lib/bit_lib_test.c index d12aeb2aa..1487cdb48 100644 --- a/applications/debug/unit_tests/tests/bit_lib/bit_lib_test.c +++ b/applications/debug/unit_tests/tests/bit_lib/bit_lib_test.c @@ -1,5 +1,5 @@ #include -#include "../test.h" +#include "../test.h" // IWYU pragma: keep #include MU_TEST(test_bit_lib_increment_index) { diff --git a/applications/debug/unit_tests/tests/bt/bt_test.c b/applications/debug/unit_tests/tests/bt/bt_test.c index e7704381d..b65a35bf5 100644 --- a/applications/debug/unit_tests/tests/bt/bt_test.c +++ b/applications/debug/unit_tests/tests/bt/bt_test.c @@ -1,6 +1,6 @@ #include #include -#include "../test.h" +#include "../test.h" // IWYU pragma: keep #include #include diff --git a/applications/debug/unit_tests/tests/compress/compress_test.c b/applications/debug/unit_tests/tests/compress/compress_test.c index 8f6153297..15984083d 100644 --- a/applications/debug/unit_tests/tests/compress/compress_test.c +++ b/applications/debug/unit_tests/tests/compress/compress_test.c @@ -1,4 +1,4 @@ -#include "../test.h" +#include "../test.h" // IWYU pragma: keep #include diff --git a/applications/debug/unit_tests/tests/datetime/datetimelib_test.c b/applications/debug/unit_tests/tests/datetime/datetimelib_test.c index 7d90f18de..9ba3e59d1 100644 --- a/applications/debug/unit_tests/tests/datetime/datetimelib_test.c +++ b/applications/debug/unit_tests/tests/datetime/datetimelib_test.c @@ -1,5 +1,5 @@ #include -#include "../test.h" +#include "../test.h" // IWYU pragma: keep #include diff --git a/applications/debug/unit_tests/tests/dialogs_file_browser_options/dialogs_file_browser_options.c b/applications/debug/unit_tests/tests/dialogs_file_browser_options/dialogs_file_browser_options.c index e42e00f9a..14cd078d5 100644 --- a/applications/debug/unit_tests/tests/dialogs_file_browser_options/dialogs_file_browser_options.c +++ b/applications/debug/unit_tests/tests/dialogs_file_browser_options/dialogs_file_browser_options.c @@ -1,6 +1,6 @@ #include -#include "../test.h" +#include "../test.h" // IWYU pragma: keep MU_TEST(test_dialog_file_browser_set_basic_options_should_init_all_fields) { mu_assert( diff --git a/applications/debug/unit_tests/tests/dirwalk/dirwalk_test.c b/applications/debug/unit_tests/tests/dirwalk/dirwalk_test.c index c2474e755..e0102a575 100644 --- a/applications/debug/unit_tests/tests/dirwalk/dirwalk_test.c +++ b/applications/debug/unit_tests/tests/dirwalk/dirwalk_test.c @@ -1,4 +1,4 @@ -#include "../test.h" +#include "../test.h" // IWYU pragma: keep #include #include #include diff --git a/applications/debug/unit_tests/tests/expansion/expansion_test.c b/applications/debug/unit_tests/tests/expansion/expansion_test.c index 79d9b0a57..ea4fea28c 100644 --- a/applications/debug/unit_tests/tests/expansion/expansion_test.c +++ b/applications/debug/unit_tests/tests/expansion/expansion_test.c @@ -1,4 +1,4 @@ -#include "../test.h" +#include "../test.h" // IWYU pragma: keep #include #include 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 1a2eaf222..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 @@ -2,10 +2,10 @@ #include #include #include -#include "../test.h" +#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 821b1ba22..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 @@ -3,7 +3,7 @@ #include #include #include -#include "../test.h" +#include "../test.h" // IWYU pragma: keep static const char* test_filetype = "Flipper Format test"; static const uint32_t test_version = 666; @@ -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/float_tools/float_tools_test.c b/applications/debug/unit_tests/tests/float_tools/float_tools_test.c index d016ca4a2..41d78da05 100644 --- a/applications/debug/unit_tests/tests/float_tools/float_tools_test.c +++ b/applications/debug/unit_tests/tests/float_tools/float_tools_test.c @@ -1,7 +1,7 @@ #include #include -#include "../test.h" +#include "../test.h" // IWYU pragma: keep MU_TEST(float_tools_equal_test) { mu_check(float_is_equal(FLT_MAX, FLT_MAX)); 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_memmgr_test.c b/applications/debug/unit_tests/tests/furi/furi_memmgr_test.c index b8d9e0712..008f9846d 100644 --- a/applications/debug/unit_tests/tests/furi/furi_memmgr_test.c +++ b/applications/debug/unit_tests/tests/furi/furi_memmgr_test.c @@ -1,4 +1,4 @@ -#include "../test.h" +#include "../test.h" // IWYU pragma: keep #include #include #include diff --git a/applications/debug/unit_tests/tests/furi/furi_pubsub_test.c b/applications/debug/unit_tests/tests/furi/furi_pubsub_test.c index fdf15e6f5..d9956b0ae 100644 --- a/applications/debug/unit_tests/tests/furi/furi_pubsub_test.c +++ b/applications/debug/unit_tests/tests/furi/furi_pubsub_test.c @@ -1,7 +1,7 @@ #include #include #include -#include "../test.h" +#include "../test.h" // IWYU pragma: keep const uint32_t context_value = 0xdeadbeef; const uint32_t notify_value_0 = 0x12345678; diff --git a/applications/debug/unit_tests/tests/furi/furi_record_test.c b/applications/debug/unit_tests/tests/furi/furi_record_test.c index 6a2b5a3d2..b192f1527 100644 --- a/applications/debug/unit_tests/tests/furi/furi_record_test.c +++ b/applications/debug/unit_tests/tests/furi/furi_record_test.c @@ -1,7 +1,5 @@ -#include -#include #include -#include "../test.h" +#include "../test.h" // IWYU pragma: keep #define TEST_RECORD_NAME "test/holding" diff --git a/applications/debug/unit_tests/tests/furi/furi_test.c b/applications/debug/unit_tests/tests/furi/furi_test.c index 62a896fb1..29f7c3bec 100644 --- a/applications/debug/unit_tests/tests/furi/furi_test.c +++ b/applications/debug/unit_tests/tests/furi/furi_test.c @@ -1,16 +1,15 @@ -#include #include #include -#include "../test.h" +#include "../test.h" // IWYU pragma: keep // v2 tests 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_memmgr_advanced(void); void test_furi_memmgr_aligned8(void); +void test_furi_event_loop(void); static int foo = 0; @@ -43,15 +42,19 @@ MU_TEST(mu_test_furi_memmgr) { test_furi_memmgr_aligned8(); } +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/tests/furi_hal/furi_hal_tests.c b/applications/debug/unit_tests/tests/furi_hal/furi_hal_tests.c index 985daa03d..4b515206c 100644 --- a/applications/debug/unit_tests/tests/furi_hal/furi_hal_tests.c +++ b/applications/debug/unit_tests/tests/furi_hal/furi_hal_tests.c @@ -1,11 +1,9 @@ -#include "furi_hal_rtc.h" #include #include #include #include #include -#include "../test.h" -#include +#include "../test.h" // IWYU pragma: keep #define DATA_SIZE 4 #define EEPROM_ADDRESS 0b10101000 diff --git a/applications/debug/unit_tests/tests/furi_hal_crypto/furi_hal_crypto_tests.c b/applications/debug/unit_tests/tests/furi_hal_crypto/furi_hal_crypto_tests.c index 0a264a18a..26527c669 100644 --- a/applications/debug/unit_tests/tests/furi_hal_crypto/furi_hal_crypto_tests.c +++ b/applications/debug/unit_tests/tests/furi_hal_crypto/furi_hal_crypto_tests.c @@ -1,7 +1,6 @@ -#include #include #include -#include "../test.h" +#include "../test.h" // IWYU pragma: keep static const uint8_t key_ctr_1[32] = { 0x77, 0x6B, 0xEF, 0xF2, 0x85, 0x1D, 0xB0, 0x6F, 0x4C, 0x8A, 0x05, 0x42, 0xC8, 0x69, 0x6F, 0x6C, diff --git a/applications/debug/unit_tests/tests/furi_string/furi_string_test.c b/applications/debug/unit_tests/tests/furi_string/furi_string_test.c index 949d8c048..df835006e 100644 --- a/applications/debug/unit_tests/tests/furi_string/furi_string_test.c +++ b/applications/debug/unit_tests/tests/furi_string/furi_string_test.c @@ -1,5 +1,5 @@ #include -#include "../test.h" +#include "../test.h" // IWYU pragma: keep static void test_setup(void) { } diff --git a/applications/debug/unit_tests/tests/infrared/infrared_test.c b/applications/debug/unit_tests/tests/infrared/infrared_test.c index 922577aa8..cf6a13220 100644 --- a/applications/debug/unit_tests/tests/infrared/infrared_test.c +++ b/applications/debug/unit_tests/tests/infrared/infrared_test.c @@ -2,7 +2,7 @@ #include #include #include -#include "../test.h" +#include "../test.h" // IWYU pragma: keep #define IR_TEST_FILES_DIR EXT_PATH("unit_tests/infrared/") #define IR_TEST_FILE_PREFIX "test_" diff --git a/applications/debug/unit_tests/tests/lfrfid/lfrfid_protocols.c b/applications/debug/unit_tests/tests/lfrfid/lfrfid_protocols.c index fc380ecc4..8bf7753c0 100644 --- a/applications/debug/unit_tests/tests/lfrfid/lfrfid_protocols.c +++ b/applications/debug/unit_tests/tests/lfrfid/lfrfid_protocols.c @@ -1,5 +1,5 @@ #include -#include "../test.h" +#include "../test.h" // IWYU pragma: keep #include #include #include diff --git a/applications/debug/unit_tests/tests/manifest/manifest.c b/applications/debug/unit_tests/tests/manifest/manifest.c index fcbab0ae8..b2c888f1a 100644 --- a/applications/debug/unit_tests/tests/manifest/manifest.c +++ b/applications/debug/unit_tests/tests/manifest/manifest.c @@ -1,5 +1,5 @@ -#include -#include "../test.h" +#include +#include "../test.h" // IWYU pragma: keep #include #define TAG "Manifest" diff --git a/applications/debug/unit_tests/tests/nfc/nfc_test.c b/applications/debug/unit_tests/tests/nfc/nfc_test.c index 4de364a09..1e4407076 100644 --- a/applications/debug/unit_tests/tests/nfc/nfc_test.c +++ b/applications/debug/unit_tests/tests/nfc/nfc_test.c @@ -12,6 +12,8 @@ #include #include #include +#include +#include #include #include #include @@ -24,7 +26,7 @@ #include #include -#include "../test.h" +#include "../test.h" // IWYU pragma: keep #define TAG "NfcTest" @@ -646,6 +648,56 @@ MU_TEST(mf_classic_dict_test) { "Remove test dict failed"); } +static FelicaError + felica_do_request_response(FelicaData* felica_data, const FelicaCardKey* card_key) { + NfcDeviceData* nfc_device = nfc_device_alloc(); + + FelicaError error = FelicaErrorNone; + if(!nfc_device_load(nfc_device, EXT_PATH("unit_tests/nfc/Felica.nfc"))) { + error = FelicaErrorNotPresent; + } else { + Nfc* poller = nfc_alloc(); + Nfc* listener = nfc_alloc(); + NfcListener* felica_listener = nfc_listener_alloc( + listener, NfcProtocolFelica, nfc_device_get_data(nfc_device, NfcProtocolFelica)); + nfc_listener_start(felica_listener, NULL, NULL); + + error = felica_poller_sync_read(poller, felica_data, card_key); + + nfc_listener_stop(felica_listener); + nfc_listener_free(felica_listener); + + nfc_free(listener); + nfc_free(poller); + } + + nfc_device_free(nfc_device); + return error; +} + +MU_TEST(felica_read) { + FelicaData* felica_data = felica_alloc(); + FelicaError error = felica_do_request_response(felica_data, NULL); + mu_assert(error == FelicaErrorNone, "felica_poller() failed"); + mu_assert(felica_data->data.fs.spad[4].SF1 == 0x01, "block[4].SF1 != 0x01"); + mu_assert(felica_data->data.fs.spad[4].SF2 == 0xB1, "block[4].SF2 != 0xB1"); + + felica_free(felica_data); +} + +MU_TEST(felica_read_auth) { + FelicaData* felica_data = felica_alloc(); + FelicaCardKey card_key; + memset(card_key.data, 0xFF, FELICA_DATA_BLOCK_SIZE); + + FelicaError error = felica_do_request_response(felica_data, &card_key); + mu_assert(error == FelicaErrorNone, "felica_poller() failed"); + mu_assert(felica_data->data.fs.spad[4].SF1 == 0x00, "block[4].SF1 != 0x00"); + mu_assert(felica_data->data.fs.spad[4].SF2 == 0x00, "block[4].SF2 != 0x00"); + + felica_free(felica_data); +} + MU_TEST(slix_file_with_capabilities_test) { NfcDevice* nfc_device_missed_cap = nfc_device_alloc(); mu_assert( @@ -807,6 +859,8 @@ MU_TEST_SUITE(nfc) { MU_RUN_TEST(mf_classic_value_block); MU_RUN_TEST(mf_classic_send_frame_test); MU_RUN_TEST(mf_classic_dict_test); + MU_RUN_TEST(felica_read); + MU_RUN_TEST(felica_read_auth); MU_RUN_TEST(slix_file_with_capabilities_test); MU_RUN_TEST(slix_set_password_default_cap_correct_pass); diff --git a/applications/debug/unit_tests/tests/power/power_test.c b/applications/debug/unit_tests/tests/power/power_test.c index d121acba6..ae332c96d 100644 --- a/applications/debug/unit_tests/tests/power/power_test.c +++ b/applications/debug/unit_tests/tests/power/power_test.c @@ -1,6 +1,6 @@ #include #include -#include "../test.h" +#include "../test.h" // IWYU pragma: keep static void power_test_deinit(void) { // Try to reset to default charge voltage limit @@ -13,46 +13,47 @@ MU_TEST(test_power_charge_voltage_limit_exact) { // // This test may need adapted if other charge controllers are used in the future. for(uint16_t charge_mv = 3840; charge_mv <= 4208; charge_mv += 16) { - float charge_volt = (float)charge_mv / 1000.0f; + float charge_volt = (float)charge_mv / 1000; furi_hal_power_set_battery_charge_voltage_limit(charge_volt); - mu_assert_double_eq(charge_volt, furi_hal_power_get_battery_charge_voltage_limit()); + mu_assert_double_eq( + (double)charge_volt, (double)furi_hal_power_get_battery_charge_voltage_limit()); } } MU_TEST(test_power_charge_voltage_limit_floating_imprecision) { // 4.016f should act as 4.016 V, even with floating point imprecision furi_hal_power_set_battery_charge_voltage_limit(4.016f); - mu_assert_double_eq(4.016f, furi_hal_power_get_battery_charge_voltage_limit()); + mu_assert_double_eq(4.016, (double)furi_hal_power_get_battery_charge_voltage_limit()); } MU_TEST(test_power_charge_voltage_limit_inexact) { // Charge voltage limits that are not power of 16mV get truncated down furi_hal_power_set_battery_charge_voltage_limit(3.841f); - mu_assert_double_eq(3.840, furi_hal_power_get_battery_charge_voltage_limit()); + mu_assert_double_eq(3.840, (double)furi_hal_power_get_battery_charge_voltage_limit()); furi_hal_power_set_battery_charge_voltage_limit(3.900f); - mu_assert_double_eq(3.888, furi_hal_power_get_battery_charge_voltage_limit()); + mu_assert_double_eq(3.888, (double)furi_hal_power_get_battery_charge_voltage_limit()); furi_hal_power_set_battery_charge_voltage_limit(4.200f); - mu_assert_double_eq(4.192, furi_hal_power_get_battery_charge_voltage_limit()); + mu_assert_double_eq(4.192, (double)furi_hal_power_get_battery_charge_voltage_limit()); } MU_TEST(test_power_charge_voltage_limit_invalid_clamped) { // Out-of-range charge voltage limits get clamped to 3.840 V and 4.208 V furi_hal_power_set_battery_charge_voltage_limit(3.808f); - mu_assert_double_eq(3.840, furi_hal_power_get_battery_charge_voltage_limit()); + mu_assert_double_eq(3.840, (double)furi_hal_power_get_battery_charge_voltage_limit()); furi_hal_power_set_battery_charge_voltage_limit(1.0f); - mu_assert_double_eq(3.840, furi_hal_power_get_battery_charge_voltage_limit()); + mu_assert_double_eq(3.840, (double)furi_hal_power_get_battery_charge_voltage_limit()); // NOTE: Intentionally picking a small increment above 4.208 V to reduce the risk of an // unhappy battery if this fails. furi_hal_power_set_battery_charge_voltage_limit(4.240f); - mu_assert_double_eq(4.208, furi_hal_power_get_battery_charge_voltage_limit()); + mu_assert_double_eq(4.208, (double)furi_hal_power_get_battery_charge_voltage_limit()); // Likewise, picking a number that the uint8_t wraparound in the driver would result in a // VREG value under 23 if this test fails. // E.g. (uint8_t)((8105-3840)/16) -> 10 furi_hal_power_set_battery_charge_voltage_limit(8.105f); - mu_assert_double_eq(4.208, furi_hal_power_get_battery_charge_voltage_limit()); + mu_assert_double_eq(4.208, (double)furi_hal_power_get_battery_charge_voltage_limit()); } MU_TEST_SUITE(test_power_suite) { diff --git a/applications/debug/unit_tests/tests/protocol_dict/protocol_dict_test.c b/applications/debug/unit_tests/tests/protocol_dict/protocol_dict_test.c index 9ced106ab..4e2975218 100644 --- a/applications/debug/unit_tests/tests/protocol_dict/protocol_dict_test.c +++ b/applications/debug/unit_tests/tests/protocol_dict/protocol_dict_test.c @@ -1,5 +1,5 @@ #include -#include "../test.h" +#include "../test.h" // IWYU pragma: keep #include typedef enum { diff --git a/applications/debug/unit_tests/tests/rpc/rpc_test.c b/applications/debug/unit_tests/tests/rpc/rpc_test.c index be7b331f9..f5b9e762d 100644 --- a/applications/debug/unit_tests/tests/rpc/rpc_test.c +++ b/applications/debug/unit_tests/tests/rpc/rpc_test.c @@ -1,11 +1,6 @@ -#include -#include #include #include -#include -#include - #include #include #include @@ -17,7 +12,7 @@ #include #include -#include "../test.h" +#include "../test.h" // IWYU pragma: keep #include #include @@ -40,8 +35,8 @@ static uint32_t command_id = 0; typedef struct { RpcSession* session; FuriStreamBuffer* output_stream; - SemaphoreHandle_t close_session_semaphore; - SemaphoreHandle_t terminate_semaphore; + FuriSemaphore* close_session_semaphore; + FuriSemaphore* terminate_semaphore; uint32_t timeout; } RpcSessionContext; @@ -51,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 @@ -96,8 +91,8 @@ static void test_rpc_setup(void) { rpc_session[0].output_stream = furi_stream_buffer_alloc(4096, 1); rpc_session_set_send_bytes_callback(rpc_session[0].session, output_bytes_callback); - rpc_session[0].close_session_semaphore = xSemaphoreCreateBinary(); - rpc_session[0].terminate_semaphore = xSemaphoreCreateBinary(); + rpc_session[0].close_session_semaphore = furi_semaphore_alloc(1, 0); + rpc_session[0].terminate_semaphore = furi_semaphore_alloc(1, 0); rpc_session_set_close_callback(rpc_session[0].session, test_rpc_session_close_callback); rpc_session_set_terminated_callback( rpc_session[0].session, test_rpc_session_terminated_callback); @@ -116,8 +111,8 @@ static void test_rpc_setup_second_session(void) { rpc_session[1].output_stream = furi_stream_buffer_alloc(1000, 1); rpc_session_set_send_bytes_callback(rpc_session[1].session, output_bytes_callback); - rpc_session[1].close_session_semaphore = xSemaphoreCreateBinary(); - rpc_session[1].terminate_semaphore = xSemaphoreCreateBinary(); + rpc_session[1].close_session_semaphore = furi_semaphore_alloc(1, 0); + rpc_session[1].terminate_semaphore = furi_semaphore_alloc(1, 0); rpc_session_set_close_callback(rpc_session[1].session, test_rpc_session_close_callback); rpc_session_set_terminated_callback( rpc_session[1].session, test_rpc_session_terminated_callback); @@ -126,13 +121,15 @@ static void test_rpc_setup_second_session(void) { static void test_rpc_teardown(void) { furi_check(rpc_session[0].close_session_semaphore); - xSemaphoreTake(rpc_session[0].terminate_semaphore, 0); + furi_semaphore_acquire(rpc_session[0].terminate_semaphore, 0); rpc_session_close(rpc_session[0].session); - furi_check(xSemaphoreTake(rpc_session[0].terminate_semaphore, portMAX_DELAY)); + furi_check( + furi_semaphore_acquire(rpc_session[0].terminate_semaphore, FuriWaitForever) == + FuriStatusOk); furi_record_close(RECORD_RPC); furi_stream_buffer_free(rpc_session[0].output_stream); - vSemaphoreDelete(rpc_session[0].close_session_semaphore); - vSemaphoreDelete(rpc_session[0].terminate_semaphore); + furi_semaphore_free(rpc_session[0].close_session_semaphore); + furi_semaphore_free(rpc_session[0].terminate_semaphore); ++command_id; rpc_session[0].output_stream = NULL; rpc_session[0].close_session_semaphore = NULL; @@ -142,12 +139,14 @@ static void test_rpc_teardown(void) { static void test_rpc_teardown_second_session(void) { furi_check(rpc_session[1].close_session_semaphore); - xSemaphoreTake(rpc_session[1].terminate_semaphore, 0); + furi_semaphore_acquire(rpc_session[1].terminate_semaphore, 0); rpc_session_close(rpc_session[1].session); - furi_check(xSemaphoreTake(rpc_session[1].terminate_semaphore, portMAX_DELAY)); + furi_check( + furi_semaphore_acquire(rpc_session[1].terminate_semaphore, FuriWaitForever) == + FuriStatusOk); furi_stream_buffer_free(rpc_session[1].output_stream); - vSemaphoreDelete(rpc_session[1].close_session_semaphore); - vSemaphoreDelete(rpc_session[1].terminate_semaphore); + furi_semaphore_free(rpc_session[1].close_session_semaphore); + furi_semaphore_free(rpc_session[1].terminate_semaphore); ++command_id; rpc_session[1].output_stream = NULL; rpc_session[1].close_session_semaphore = NULL; @@ -204,14 +203,14 @@ static void test_rpc_session_close_callback(void* context) { furi_check(context); RpcSessionContext* callbacks_context = context; - xSemaphoreGive(callbacks_context->close_session_semaphore); + furi_check(furi_semaphore_release(callbacks_context->close_session_semaphore) == FuriStatusOk); } static void test_rpc_session_terminated_callback(void* context) { furi_check(context); RpcSessionContext* callbacks_context = context; - xSemaphoreGive(callbacks_context->terminate_semaphore); + furi_check(furi_semaphore_release(callbacks_context->terminate_semaphore) == FuriStatusOk); } static void test_rpc_print_message_list(MsgList_t msg_list) { @@ -1645,7 +1644,7 @@ static void test_rpc_feed_rubbish_run( test_rpc_add_empty_to_list(expected, PB_CommandStatus_ERROR_DECODE, 0); - furi_check(!xSemaphoreTake(rpc_session[0].close_session_semaphore, 0)); + furi_check(furi_semaphore_acquire(rpc_session[0].close_session_semaphore, 0) != FuriStatusOk); test_rpc_encode_and_feed(input_before, 0); test_send_rubbish(rpc_session[0].session, pattern, pattern_size, size); test_rpc_encode_and_feed(input_after, 0); diff --git a/applications/debug/unit_tests/tests/storage/storage_test.c b/applications/debug/unit_tests/tests/storage/storage_test.c index 1d887ced0..65a25cf49 100644 --- a/applications/debug/unit_tests/tests/storage/storage_test.c +++ b/applications/debug/unit_tests/tests/storage/storage_test.c @@ -1,4 +1,4 @@ -#include "../test.h" +#include "../test.h" // IWYU pragma: keep #include #include diff --git a/applications/debug/unit_tests/tests/stream/stream_test.c b/applications/debug/unit_tests/tests/stream/stream_test.c index a9c22c723..4f1ba2fd7 100644 --- a/applications/debug/unit_tests/tests/stream/stream_test.c +++ b/applications/debug/unit_tests/tests/stream/stream_test.c @@ -4,7 +4,7 @@ #include #include #include -#include "../test.h" +#include "../test.h" // IWYU pragma: keep static const char* stream_test_data = "I write differently from what I speak, " "I speak differently from what I think, " @@ -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/unit_tests/tests/subghz/subghz_test.c b/applications/debug/unit_tests/tests/subghz/subghz_test.c index 030a4223f..6a4129359 100644 --- a/applications/debug/unit_tests/tests/subghz/subghz_test.c +++ b/applications/debug/unit_tests/tests/subghz/subghz_test.c @@ -1,6 +1,6 @@ #include #include -#include "../test.h" +#include "../test.h" // IWYU pragma: keep #include #include #include diff --git a/applications/debug/unit_tests/tests/varint/varint_test.c b/applications/debug/unit_tests/tests/varint/varint_test.c index 3fdf5115a..6a9d94c16 100644 --- a/applications/debug/unit_tests/tests/varint/varint_test.c +++ b/applications/debug/unit_tests/tests/varint/varint_test.c @@ -1,7 +1,7 @@ #include #include -#include "../test.h" +#include "../test.h" // IWYU pragma: keep #include #include 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 8c2fa4687..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)), @@ -19,11 +20,24 @@ static constexpr auto unit_tests_api_table = sort(create_array_t( API_METHOD(xQueueSemaphoreTake, BaseType_t, (QueueHandle_t, TickType_t)), API_METHOD(vQueueDelete, void, (QueueHandle_t)), API_METHOD( - xQueueGenericCreate, + xQueueGenericCreateStatic, QueueHandle_t, - (const UBaseType_t, const UBaseType_t, const uint8_t)), + (const UBaseType_t, const UBaseType_t, uint8_t*, StaticQueue_t*, const uint8_t)), API_METHOD( 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/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/archive/helpers/archive_apps.c b/applications/main/archive/helpers/archive_apps.c index c8ad67625..43befc055 100644 --- a/applications/main/archive/helpers/archive_apps.c +++ b/applications/main/archive/helpers/archive_apps.c @@ -1,4 +1,3 @@ -#include "archive_files.h" #include "archive_apps.h" #include "archive_browser.h" diff --git a/applications/main/archive/helpers/archive_browser.c b/applications/main/archive/helpers/archive_browser.c index 51457fe81..ea9cdbdc3 100644 --- a/applications/main/archive/helpers/archive_browser.c +++ b/applications/main/archive/helpers/archive_browser.c @@ -7,7 +7,6 @@ #include #include #include -#include static void archive_folder_open_cb(void* context, uint32_t item_cnt, int32_t file_idx, bool is_root) { diff --git a/applications/main/archive/scenes/archive_scene_browser.c b/applications/main/archive/scenes/archive_scene_browser.c index ba928c499..c08deb60a 100644 --- a/applications/main/archive/scenes/archive_scene_browser.c +++ b/applications/main/archive/scenes/archive_scene_browser.c @@ -1,6 +1,5 @@ #include "../archive_i.h" #include "../helpers/archive_files.h" -#include "../helpers/archive_apps.h" #include "../helpers/archive_favorites.h" #include "../helpers/archive_browser.h" #include "../views/archive_browser_view.h" diff --git a/applications/main/archive/scenes/archive_scene_delete.c b/applications/main/archive/scenes/archive_scene_delete.c index 45d5b47cc..cbeb4824d 100644 --- a/applications/main/archive/scenes/archive_scene_delete.c +++ b/applications/main/archive/scenes/archive_scene_delete.c @@ -1,5 +1,4 @@ #include "../archive_i.h" -#include "../helpers/archive_favorites.h" #include "../helpers/archive_files.h" #include "../helpers/archive_apps.h" #include "../helpers/archive_browser.h" diff --git a/applications/main/archive/scenes/archive_scene_rename.c b/applications/main/archive/scenes/archive_scene_rename.c index 37f860a9a..7b6552dfb 100644 --- a/applications/main/archive/scenes/archive_scene_rename.c +++ b/applications/main/archive/scenes/archive_scene_rename.c @@ -18,13 +18,18 @@ void archive_scene_rename_on_enter(void* context) { TextInput* text_input = archive->text_input; ArchiveFile_t* current = archive_get_current_file(archive->browser); + const bool is_file = current->type != ArchiveFileTypeFolder; FuriString* filename; filename = furi_string_alloc(); - path_extract_filename(current->path, filename, true); + path_extract_filename(current->path, filename, is_file); strlcpy(archive->text_store, furi_string_get_cstr(filename), MAX_NAME_LEN); - path_extract_extension(current->path, archive->file_extension, MAX_EXT_LEN); + if(is_file) { + path_extract_extension(current->path, archive->file_extension, MAX_EXT_LEN); + } else { + memset(archive->file_extension, 0, sizeof(archive->file_extension)); + } text_input_set_header_text(text_input, "Rename:"); diff --git a/applications/main/archive/views/archive_browser_view.c b/applications/main/archive/views/archive_browser_view.c index 6cdf12c62..de06b9684 100644 --- a/applications/main/archive/views/archive_browser_view.c +++ b/applications/main/archive/views/archive_browser_view.c @@ -1,7 +1,6 @@ #include "assets_icons.h" #include "toolbox/path.h" #include -#include "../archive_i.h" #include "archive_browser_view.h" #include "../helpers/archive_browser.h" diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_config.c b/applications/main/bad_usb/scenes/bad_usb_scene_config.c index d5f6fce23..1acf3acb1 100644 --- a/applications/main/bad_usb/scenes/bad_usb_scene_config.c +++ b/applications/main/bad_usb/scenes/bad_usb_scene_config.c @@ -1,5 +1,4 @@ #include "../bad_usb_app_i.h" -#include "furi_hal_power.h" enum SubmenuIndex { ConfigIndexKeyboardLayout, diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_config_layout.c b/applications/main/bad_usb/scenes/bad_usb_scene_config_layout.c index a5d0df94c..3f01d7090 100644 --- a/applications/main/bad_usb/scenes/bad_usb_scene_config_layout.c +++ b/applications/main/bad_usb/scenes/bad_usb_scene_config_layout.c @@ -1,5 +1,4 @@ #include "../bad_usb_app_i.h" -#include "furi_hal_power.h" #include static bool bad_usb_layout_select(BadUsbApp* bad_usb) { diff --git a/applications/main/gpio/views/gpio_usb_uart.c b/applications/main/gpio/views/gpio_usb_uart.c index f3d047d67..5e714576d 100644 --- a/applications/main/gpio/views/gpio_usb_uart.c +++ b/applications/main/gpio/views/gpio_usb_uart.c @@ -1,7 +1,8 @@ +#include "gpio_usb_uart.h" #include "../usb_uart_bridge.h" -#include "../gpio_app_i.h" #include #include +#include struct GpioUsbUart { View* view; diff --git a/applications/main/infrared/scenes/infrared_scene_universal_ac.c b/applications/main/infrared/scenes/infrared_scene_universal_ac.c index b82bcc1f9..9288a4a4d 100644 --- a/applications/main/infrared/scenes/infrared_scene_universal_ac.c +++ b/applications/main/infrared/scenes/infrared_scene_universal_ac.c @@ -1,4 +1,4 @@ -#include "../infrared_app_i.h" +#include "../infrared_app_i.h" // IWYU pragma: keep #include "common/infrared_scene_universal_common.h" 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/helpers/protocol_support/felica/felica.c b/applications/main/nfc/helpers/protocol_support/felica/felica.c index b0660f3e6..ceb180653 100644 --- a/applications/main/nfc/helpers/protocol_support/felica/felica.c +++ b/applications/main/nfc/helpers/protocol_support/felica/felica.c @@ -26,12 +26,6 @@ static void nfc_scene_info_on_enter_felica(NfcApp* instance) { widget_add_text_scroll_element( instance->widget, 0, 0, 128, 48, furi_string_get_cstr(temp_str)); - widget_add_button_element( - instance->widget, - GuiButtonTypeRight, - "More", - nfc_protocol_support_common_widget_callback, - instance); furi_string_free(temp_str); } @@ -177,7 +171,7 @@ static bool nfc_scene_read_menu_on_event_felica(NfcApp* instance, SceneManagerEv } const NfcProtocolSupportBase nfc_protocol_support_felica = { - .features = NfcProtocolFeatureEmulateUid, + .features = NfcProtocolFeatureEmulateFull | NfcProtocolFeatureMoreInfo, .scene_info = { diff --git a/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c index 6abaa48cd..7a51e3d86 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c +++ b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c @@ -197,7 +197,10 @@ static bool nfc_scene_read_menu_on_event_mf_classic(NfcApp* instance, SceneManag dolphin_deed(DolphinDeedNfcDetectReader); consumed = true; } else if(event.event == SubmenuIndexDictAttack) { - scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicDictAttack); + if(!scene_manager_search_and_switch_to_previous_scene( + instance->scene_manager, NfcSceneMfClassicDictAttack)) { + scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicDictAttack); + } consumed = true; } else if(event.event == SubmenuIndexCommonEdit) { scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid); @@ -222,7 +225,10 @@ static bool nfc_scene_saved_menu_on_event_mf_classic(NfcApp* instance, SceneMana scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicUpdateInitial); consumed = true; } else if(event.event == SubmenuIndexDictAttack) { - scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicDictAttack); + if(!scene_manager_search_and_switch_to_previous_scene( + instance->scene_manager, NfcSceneMfClassicDictAttack)) { + scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicDictAttack); + } consumed = true; } } 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/mf_ultralight/mf_ultralight.c b/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c index cd16374bc..a3028905f 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c +++ b/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c @@ -167,7 +167,7 @@ bool nfc_scene_read_on_event_mf_ultralight(NfcApp* instance, SceneManagerEvent e if(event.type == SceneManagerEventTypeCustom) { if(event.event == NfcCustomEventCardDetected) { nfc_unlock_helper_card_detected_handler(instance); - } else if((event.event == NfcCustomEventPollerIncomplete)) { + } else if(event.event == NfcCustomEventPollerIncomplete) { notification_message(instance->notifications, &sequence_semi_success); scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess); dolphin_deed(DolphinDeedNfcReadSuccess); @@ -191,7 +191,8 @@ static void nfc_scene_read_and_saved_menu_on_enter_mf_ultralight(NfcApp* instanc instance); } else if( data->type == MfUltralightTypeNTAG213 || data->type == MfUltralightTypeNTAG215 || - data->type == MfUltralightTypeNTAG216) { + data->type == MfUltralightTypeNTAG216 || data->type == MfUltralightTypeUL11 || + data->type == MfUltralightTypeUL21) { submenu_add_item( submenu, "Write", 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/plugins/supported_cards/troika.c b/applications/main/nfc/plugins/supported_cards/troika.c index 64a9ac5dc..de9a47fac 100644 --- a/applications/main/nfc/plugins/supported_cards/troika.c +++ b/applications/main/nfc/plugins/supported_cards/troika.c @@ -7,7 +7,6 @@ #include #include #include "../../api/mosgortrans/mosgortrans_util.h" -#include "furi_hal_rtc.h" #define TAG "Troika" 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/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/helpers/subghz_frequency_analyzer_worker.c b/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.c index 995434631..624caf30e 100644 --- a/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.c +++ b/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.c @@ -49,7 +49,7 @@ static uint32_t subghz_frequency_analyzer_worker_expRunningAverageAdaptive( float k; float newValFloat = newVal; // the sharpness of the filter depends on the absolute value of the difference - if(fabs(newValFloat - instance->filVal) > 500000) + if(fabsf(newValFloat - instance->filVal) > 500000.f) k = 0.9; else k = 0.03; diff --git a/applications/main/subghz/helpers/subghz_threshold_rssi.c b/applications/main/subghz/helpers/subghz_threshold_rssi.c index 07d7bccf9..f9906b513 100644 --- a/applications/main/subghz/helpers/subghz_threshold_rssi.c +++ b/applications/main/subghz/helpers/subghz_threshold_rssi.c @@ -1,6 +1,6 @@ #include "subghz_threshold_rssi.h" +#include "../views/subghz_read_raw.h" #include -#include "../subghz_i.h" #define TAG "SubGhzThresholdRssi" #define THRESHOLD_RSSI_LOW_COUNT 10 diff --git a/applications/main/subghz/helpers/subghz_txrx.c b/applications/main/subghz/helpers/subghz_txrx.c index bcc0cc433..d14be4768 100644 --- a/applications/main/subghz/helpers/subghz_txrx.c +++ b/applications/main/subghz/helpers/subghz_txrx.c @@ -1,4 +1,4 @@ -#include "subghz_txrx_i.h" +#include "subghz_txrx_i.h" // IWYU pragma: keep #include #include diff --git a/applications/main/subghz/helpers/subghz_txrx_create_protocol_key.c b/applications/main/subghz/helpers/subghz_txrx_create_protocol_key.c index bafaef5ee..3e0656550 100644 --- a/applications/main/subghz/helpers/subghz_txrx_create_protocol_key.c +++ b/applications/main/subghz/helpers/subghz_txrx_create_protocol_key.c @@ -1,4 +1,4 @@ -#include "subghz_txrx_i.h" +#include "subghz_txrx_i.h" // IWYU pragma: keep #include "subghz_txrx_create_protocol_key.h" #include #include diff --git a/applications/main/subghz/helpers/subghz_types.h b/applications/main/subghz/helpers/subghz_types.h index e71c22dd5..5c17dc3d4 100644 --- a/applications/main/subghz/helpers/subghz_types.h +++ b/applications/main/subghz/helpers/subghz_types.h @@ -52,6 +52,7 @@ typedef enum { SubGhzRxKeyStateAddKey, SubGhzRxKeyStateExit, SubGhzRxKeyStateRAWLoad, + SubGhzRxKeyStateRAWMore, SubGhzRxKeyStateRAWSave, } SubGhzRxKeyState; diff --git a/applications/main/subghz/scenes/subghz_scene_delete_raw.c b/applications/main/subghz/scenes/subghz_scene_delete_raw.c index 53f13b68e..5862ad7c7 100644 --- a/applications/main/subghz/scenes/subghz_scene_delete_raw.c +++ b/applications/main/subghz/scenes/subghz_scene_delete_raw.c @@ -58,7 +58,13 @@ bool subghz_scene_delete_raw_on_event(void* context, SceneManagerEvent event) { if(event.event == SubGhzCustomEventSceneDeleteRAW) { furi_string_set(subghz->file_path_tmp, subghz->file_path); if(subghz_delete_file(subghz)) { - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneDeleteSuccess); + if(subghz_rx_key_state_get(subghz) != SubGhzRxKeyStateRAWLoad) { + subghz_rx_key_state_set(subghz, SubGhzRxKeyStateIDLE); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneDeleteSuccess); + } else { + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaved); + } + } else { scene_manager_search_and_switch_to_previous_scene( subghz->scene_manager, SubGhzSceneStart); diff --git a/applications/main/subghz/scenes/subghz_scene_delete_success.c b/applications/main/subghz/scenes/subghz_scene_delete_success.c index d5c2b391c..0150f2996 100644 --- a/applications/main/subghz/scenes/subghz_scene_delete_success.c +++ b/applications/main/subghz/scenes/subghz_scene_delete_success.c @@ -1,4 +1,4 @@ -#include "../subghz_i.h" +#include "../subghz_i.h" // IWYU pragma: keep #include "../helpers/subghz_custom_event.h" void subghz_scene_delete_success_popup_callback(void* context) { diff --git a/applications/main/subghz/scenes/subghz_scene_frequency_analyzer.c b/applications/main/subghz/scenes/subghz_scene_frequency_analyzer.c index b48a821da..da861d718 100644 --- a/applications/main/subghz/scenes/subghz_scene_frequency_analyzer.c +++ b/applications/main/subghz/scenes/subghz_scene_frequency_analyzer.c @@ -1,4 +1,4 @@ -#include "../subghz_i.h" +#include "../subghz_i.h" // IWYU pragma: keep #include "../views/subghz_frequency_analyzer.h" void subghz_scene_frequency_analyzer_callback(SubGhzCustomEvent event, void* context) { diff --git a/applications/main/subghz/scenes/subghz_scene_radio_setting.c b/applications/main/subghz/scenes/subghz_scene_radio_setting.c index 1f8e4d83d..c324d0d2e 100644 --- a/applications/main/subghz/scenes/subghz_scene_radio_setting.c +++ b/applications/main/subghz/scenes/subghz_scene_radio_setting.c @@ -1,4 +1,4 @@ -#include "../subghz_i.h" +#include "../subghz_i.h" // IWYU pragma: keep #include #include #include diff --git a/applications/main/subghz/scenes/subghz_scene_read_raw.c b/applications/main/subghz/scenes/subghz_scene_read_raw.c index f2ab65770..b12c94681 100644 --- a/applications/main/subghz/scenes/subghz_scene_read_raw.c +++ b/applications/main/subghz/scenes/subghz_scene_read_raw.c @@ -2,7 +2,7 @@ #include "../views/subghz_read_raw.h" #include #include -#include +#include #define RAW_FILE_NAME "Raw_signal_" #define TAG "SubGhzSceneReadRaw" @@ -77,6 +77,7 @@ void subghz_scene_read_raw_on_enter(void* context) { subghz->subghz_read_raw, SubGhzReadRAWStatusIDLE, "", threshold_rssi); break; case SubGhzRxKeyStateRAWLoad: + case SubGhzRxKeyStateRAWMore: path_extract_filename(subghz->file_path, file_name, true); subghz_read_raw_set_status( subghz->subghz_read_raw, @@ -98,7 +99,8 @@ void subghz_scene_read_raw_on_enter(void* context) { break; } - if(subghz_rx_key_state_get(subghz) != SubGhzRxKeyStateBack) { + if((subghz_rx_key_state_get(subghz) != SubGhzRxKeyStateBack) && + (subghz_rx_key_state_get(subghz) != SubGhzRxKeyStateRAWLoad)) { subghz_rx_key_state_set(subghz, SubGhzRxKeyStateIDLE); } furi_string_free(file_name); @@ -177,7 +179,9 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { if(subghz_scene_read_raw_update_filename(subghz)) { scene_manager_set_scene_state( subghz->scene_manager, SubGhzSceneReadRAW, SubGhzCustomEventManagerSet); - subghz_rx_key_state_set(subghz, SubGhzRxKeyStateRAWLoad); + if(subghz_rx_key_state_get(subghz) != SubGhzRxKeyStateRAWLoad) { + subghz_rx_key_state_set(subghz, SubGhzRxKeyStateRAWMore); + } scene_manager_next_scene(subghz->scene_manager, SubGhzSceneMoreRAW); consumed = true; } else { diff --git a/applications/main/subghz/scenes/subghz_scene_region_info.c b/applications/main/subghz/scenes/subghz_scene_region_info.c index 61bd1acc8..ce123dab3 100644 --- a/applications/main/subghz/scenes/subghz_scene_region_info.c +++ b/applications/main/subghz/scenes/subghz_scene_region_info.c @@ -1,4 +1,4 @@ -#include "../subghz_i.h" +#include "../subghz_i.h" // IWYU pragma: keep #include diff --git a/applications/main/subghz/scenes/subghz_scene_saved_menu.c b/applications/main/subghz/scenes/subghz_scene_saved_menu.c index a65830f4b..61e362d5b 100644 --- a/applications/main/subghz/scenes/subghz_scene_saved_menu.c +++ b/applications/main/subghz/scenes/subghz_scene_saved_menu.c @@ -1,4 +1,4 @@ -#include "../subghz_i.h" +#include "../subghz_i.h" // IWYU pragma: keep enum SubmenuIndex { SubmenuIndexEmulate, diff --git a/applications/main/subghz/scenes/subghz_scene_show_error.c b/applications/main/subghz/scenes/subghz_scene_show_error.c index d52eca9b6..904663e0b 100644 --- a/applications/main/subghz/scenes/subghz_scene_show_error.c +++ b/applications/main/subghz/scenes/subghz_scene_show_error.c @@ -1,4 +1,4 @@ -#include "../subghz_i.h" +#include "../subghz_i.h" // IWYU pragma: keep #include "../helpers/subghz_custom_event.h" static const NotificationSequence subghs_sequence_sd_error = { diff --git a/applications/main/subghz/scenes/subghz_scene_show_error_sub.c b/applications/main/subghz/scenes/subghz_scene_show_error_sub.c index 0de48c442..e7eda9c71 100644 --- a/applications/main/subghz/scenes/subghz_scene_show_error_sub.c +++ b/applications/main/subghz/scenes/subghz_scene_show_error_sub.c @@ -1,4 +1,4 @@ -#include "../subghz_i.h" +#include "../subghz_i.h" // IWYU pragma: keep #include "../helpers/subghz_custom_event.h" void subghz_scene_show_error_sub_popup_callback(void* context) { 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/main/subghz/subghz_i.c b/applications/main/subghz/subghz_i.c index b553d00de..243ad101f 100644 --- a/applications/main/subghz/subghz_i.c +++ b/applications/main/subghz/subghz_i.c @@ -2,7 +2,6 @@ #include "assets_icons.h" #include "subghz/types.h" -#include #include #include #include @@ -10,7 +9,6 @@ #include #include #include -#include "views/receiver.h" #include #include diff --git a/applications/main/subghz/views/receiver.c b/applications/main/subghz/views/receiver.c index ea332f544..39b5c78b9 100644 --- a/applications/main/subghz/views/receiver.c +++ b/applications/main/subghz/views/receiver.c @@ -1,7 +1,6 @@ #include "receiver.h" -#include "../subghz_i.h" -#include +#include "types.h" #include #include #include diff --git a/applications/main/subghz/views/subghz_frequency_analyzer.c b/applications/main/subghz/views/subghz_frequency_analyzer.c index 133868dde..b59426f02 100644 --- a/applications/main/subghz/views/subghz_frequency_analyzer.c +++ b/applications/main/subghz/views/subghz_frequency_analyzer.c @@ -1,7 +1,5 @@ #include "subghz_frequency_analyzer.h" -#include "../subghz_i.h" -#include #include #include #include diff --git a/applications/main/subghz/views/subghz_read_raw.c b/applications/main/subghz/views/subghz_read_raw.c index 485080f4c..c05d0b7e3 100644 --- a/applications/main/subghz/views/subghz_read_raw.c +++ b/applications/main/subghz/views/subghz_read_raw.c @@ -1,7 +1,5 @@ #include "subghz_read_raw.h" -#include "../subghz_i.h" -#include #include #include #include @@ -72,7 +70,7 @@ void subghz_read_raw_add_data_rssi(SubGhzReadRAW* instance, float rssi, bool tra if(rssi < SUBGHZ_RAW_THRESHOLD_MIN) { u_rssi = 0; } else { - u_rssi = (uint8_t)((rssi - SUBGHZ_RAW_THRESHOLD_MIN) / 2.7); + u_rssi = (uint8_t)((rssi - SUBGHZ_RAW_THRESHOLD_MIN) / 2.7f); } with_view_model( @@ -272,7 +270,7 @@ void subghz_read_raw_draw_threshold_rssi(Canvas* canvas, SubGhzReadRAWModel* mod if(model->raw_threshold_rssi > SUBGHZ_RAW_THRESHOLD_MIN) { uint8_t x = 118; - y -= (uint8_t)((model->raw_threshold_rssi - SUBGHZ_RAW_THRESHOLD_MIN) / 2.7); + y -= (uint8_t)((model->raw_threshold_rssi - SUBGHZ_RAW_THRESHOLD_MIN) / 2.7f); uint8_t width = 3; for(uint8_t i = 0; i < x; i += width * 2) { diff --git a/applications/main/subghz/views/transmitter.c b/applications/main/subghz/views/transmitter.c index f9b3f44ba..ab41ea956 100644 --- a/applications/main/subghz/views/transmitter.c +++ b/applications/main/subghz/views/transmitter.c @@ -1,6 +1,6 @@ #include "transmitter.h" -#include "../subghz_i.h" +#include #include #include diff --git a/applications/main/u2f/u2f.c b/applications/main/u2f/u2f.c index 2a2e91f21..b61cfa15d 100644 --- a/applications/main/u2f/u2f.c +++ b/applications/main/u2f/u2f.c @@ -1,5 +1,4 @@ #include "u2f.h" -#include "u2f_hid.h" #include "u2f_data.h" #include diff --git a/applications/services/bt/bt_service/bt.c b/applications/services/bt/bt_service/bt.c index 389275b86..59eb86388 100644 --- a/applications/services/bt/bt_service/bt.c +++ b/applications/services/bt/bt_service/bt.c @@ -435,6 +435,8 @@ int32_t bt_srv(void* p) { FURI_LOG_W(TAG, "Skipping start in special boot mode"); ble_glue_wait_for_c2_start(FURI_HAL_BT_C2_START_TIMEOUT); furi_record_create(RECORD_BT, bt); + + furi_thread_suspend(furi_thread_get_current_id()); return 0; } 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/cli/cli_vcp.c b/applications/services/cli/cli_vcp.c index 58c5c887f..cdabaaa05 100644 --- a/applications/services/cli/cli_vcp.c +++ b/applications/services/cli/cli_vcp.c @@ -1,7 +1,7 @@ +#include "cli_i.h" // IWYU pragma: keep #include #include #include -#include "cli_i.h" #define TAG "CliVcp" diff --git a/applications/services/desktop/animations/animation_storage.c b/applications/services/desktop/animations/animation_storage.c index c99cc7b5d..1db61f1ab 100644 --- a/applications/services/desktop/animations/animation_storage.c +++ b/applications/services/desktop/animations/animation_storage.c @@ -8,7 +8,6 @@ #include "animation_manager.h" #include "animation_storage.h" -#include "animation_storage_i.h" #include #include diff --git a/applications/services/desktop/animations/views/bubble_animation_view.c b/applications/services/desktop/animations/views/bubble_animation_view.c index 9585b2771..cea039671 100644 --- a/applications/services/desktop/animations/views/bubble_animation_view.c +++ b/applications/services/desktop/animations/views/bubble_animation_view.c @@ -1,6 +1,5 @@ #include "../animation_manager.h" -#include "../animation_storage.h" #include "bubble_animation_view.h" #include diff --git a/applications/services/desktop/desktop.c b/applications/services/desktop/desktop.c index 748f9a555..8b0c6d753 100644 --- a/applications/services/desktop/desktop.c +++ b/applications/services/desktop/desktop.c @@ -441,6 +441,8 @@ int32_t desktop_srv(void* p) { if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) { FURI_LOG_W(TAG, "Skipping start in special boot mode"); + + furi_thread_suspend(furi_thread_get_current_id()); return 0; } diff --git a/applications/services/desktop/helpers/pin.c b/applications/services/desktop/helpers/pin.c index 374b569f0..0b1149d6c 100644 --- a/applications/services/desktop/helpers/pin.c +++ b/applications/services/desktop/helpers/pin.c @@ -7,8 +7,6 @@ #include #include -#include "../desktop_i.h" - static const NotificationSequence sequence_pin_fail = { &message_display_backlight_on, diff --git a/applications/services/desktop/scenes/desktop_scene_lock_menu.c b/applications/services/desktop/scenes/desktop_scene_lock_menu.c index 105b2b37a..5951a8e4e 100644 --- a/applications/services/desktop/scenes/desktop_scene_lock_menu.c +++ b/applications/services/desktop/scenes/desktop_scene_lock_menu.c @@ -8,9 +8,7 @@ #include "../desktop_i.h" #include #include "../views/desktop_view_lock_menu.h" -#include "desktop_scene_i.h" #include "desktop_scene.h" -#include "../helpers/pin.h" #define TAG "DesktopSceneLock" diff --git a/applications/services/desktop/scenes/desktop_scene_locked.c b/applications/services/desktop/scenes/desktop_scene_locked.c index c4cd5748f..6d432858a 100644 --- a/applications/services/desktop/scenes/desktop_scene_locked.c +++ b/applications/services/desktop/scenes/desktop_scene_locked.c @@ -9,7 +9,6 @@ #include "../helpers/pin.h" #include "../animations/animation_manager.h" #include "../views/desktop_events.h" -#include "../views/desktop_view_pin_input.h" #include "../views/desktop_view_locked.h" #include "desktop_scene.h" #include "desktop_scene_i.h" diff --git a/applications/services/desktop/scenes/desktop_scene_main.c b/applications/services/desktop/scenes/desktop_scene_main.c index 41b1c3e75..5f3778d13 100644 --- a/applications/services/desktop/scenes/desktop_scene_main.c +++ b/applications/services/desktop/scenes/desktop_scene_main.c @@ -8,7 +8,6 @@ #include "../views/desktop_events.h" #include "../views/desktop_view_main.h" #include "desktop_scene.h" -#include "desktop_scene_i.h" #define TAG "DesktopSrv" diff --git a/applications/services/desktop/scenes/desktop_scene_pin_input.c b/applications/services/desktop/scenes/desktop_scene_pin_input.c index f6de987bf..b1c0d4c85 100644 --- a/applications/services/desktop/scenes/desktop_scene_pin_input.c +++ b/applications/services/desktop/scenes/desktop_scene_pin_input.c @@ -8,12 +8,10 @@ #include "../desktop.h" #include "../desktop_i.h" -#include "../animations/animation_manager.h" #include "../views/desktop_events.h" #include "../views/desktop_view_pin_input.h" #include "../helpers/pin.h" #include "desktop_scene.h" -#include "desktop_scene_i.h" #define WRONG_PIN_HEADER_TIMEOUT 3000 #define INPUT_PIN_VIEW_TIMEOUT 15000 diff --git a/applications/services/desktop/scenes/desktop_scene_pin_timeout.c b/applications/services/desktop/scenes/desktop_scene_pin_timeout.c index e3336ad76..ed6f9f12e 100644 --- a/applications/services/desktop/scenes/desktop_scene_pin_timeout.c +++ b/applications/services/desktop/scenes/desktop_scene_pin_timeout.c @@ -1,11 +1,9 @@ #include -#include #include #include "../desktop_i.h" #include "../views/desktop_view_pin_timeout.h" #include "desktop_scene.h" -#include "desktop_scene_i.h" static void desktop_scene_pin_timeout_callback(void* context) { Desktop* desktop = (Desktop*)context; diff --git a/applications/services/desktop/views/desktop_view_lock_menu.c b/applications/services/desktop/views/desktop_view_lock_menu.c index 9f8b5f51a..1b6bdab9c 100644 --- a/applications/services/desktop/views/desktop_view_lock_menu.c +++ b/applications/services/desktop/views/desktop_view_lock_menu.c @@ -126,8 +126,8 @@ bool desktop_lock_menu_input_callback(InputEvent* event, void* context) { update); if(event->key == InputKeyOk) { - if((idx == DesktopLockMenuIndexLock)) { - if((event->type == InputTypeShort)) { + if(idx == DesktopLockMenuIndexLock) { + if(event->type == InputTypeShort) { lock_menu->callback(DesktopLockMenuEventLock, lock_menu->context); } } else if(idx == DesktopLockMenuIndexStealth) { diff --git a/applications/services/desktop/views/desktop_view_main.c b/applications/services/desktop/views/desktop_view_main.c index 0c1160d40..87f12d84a 100644 --- a/applications/services/desktop/views/desktop_view_main.c +++ b/applications/services/desktop/views/desktop_view_main.c @@ -6,7 +6,6 @@ #include #include -#include "../desktop_i.h" #include "desktop_view_main.h" struct DesktopMainView { diff --git a/applications/services/desktop/views/desktop_view_pin_timeout.c b/applications/services/desktop/views/desktop_view_pin_timeout.c index f24ecc8ea..2811ba7d2 100644 --- a/applications/services/desktop/views/desktop_view_pin_timeout.c +++ b/applications/services/desktop/views/desktop_view_pin_timeout.c @@ -2,7 +2,6 @@ #include #include #include -#include #include #include #include diff --git a/applications/services/desktop/views/desktop_view_slideshow.c b/applications/services/desktop/views/desktop_view_slideshow.c index 84e4d5706..de8c1cb86 100644 --- a/applications/services/desktop/views/desktop_view_slideshow.c +++ b/applications/services/desktop/views/desktop_view_slideshow.c @@ -3,9 +3,7 @@ #include #include "desktop_view_slideshow.h" -#include "../desktop_i.h" #include "../helpers/slideshow.h" -#include "../helpers/slideshow_filename.h" #define DESKTOP_SLIDESHOW_POWEROFF_SHORT 5000 #define DESKTOP_SLIDESHOW_POWEROFF_LONG (60 * 60 * 1000) diff --git a/applications/services/desktop/views/desktop_view_slideshow.h b/applications/services/desktop/views/desktop_view_slideshow.h index b9e610c62..942a0d25a 100644 --- a/applications/services/desktop/views/desktop_view_slideshow.h +++ b/applications/services/desktop/views/desktop_view_slideshow.h @@ -4,6 +4,7 @@ #include "desktop_events.h" #include "../helpers/slideshow_filename.h" +#include #define SLIDESHOW_FS_PATH INT_PATH(SLIDESHOW_FILE_NAME) diff --git a/applications/services/dialogs/dialogs.c b/applications/services/dialogs/dialogs.c index ec1289d5b..31856aa29 100644 --- a/applications/services/dialogs/dialogs.c +++ b/applications/services/dialogs/dialogs.c @@ -1,5 +1,4 @@ #include "dialogs/dialogs_message.h" -#include "dialogs_i.h" #include #include "dialogs_module_file_browser.h" #include "dialogs_module_message.h" diff --git a/applications/services/dialogs/dialogs_api.c b/applications/services/dialogs/dialogs_api.c index 73d1d876a..9df3eedb9 100644 --- a/applications/services/dialogs/dialogs_api.c +++ b/applications/services/dialogs/dialogs_api.c @@ -1,5 +1,4 @@ #include "dialogs_message.h" -#include "dialogs_i.h" #include #include #include diff --git a/applications/services/dialogs/dialogs_module_file_browser.c b/applications/services/dialogs/dialogs_module_file_browser.c index 79d151c59..b1558f1e9 100644 --- a/applications/services/dialogs/dialogs_module_file_browser.c +++ b/applications/services/dialogs/dialogs_module_file_browser.c @@ -1,5 +1,4 @@ -#include "dialogs_i.h" - +#include "dialogs_message.h" #include #include diff --git a/applications/services/dialogs/dialogs_module_message.c b/applications/services/dialogs/dialogs_module_message.c index 69d28c405..a71f403c5 100644 --- a/applications/services/dialogs/dialogs_module_message.c +++ b/applications/services/dialogs/dialogs_module_message.c @@ -1,4 +1,6 @@ -#include "dialogs_i.h" +#include "dialogs.h" +#include "dialogs_message.h" +#include #include #include diff --git a/applications/services/dolphin/dolphin.c b/applications/services/dolphin/dolphin.c index 5f3d5cb28..4a75241e6 100644 --- a/applications/services/dolphin/dolphin.c +++ b/applications/services/dolphin/dolphin.c @@ -1,7 +1,6 @@ -#include "dolphin/dolphin.h" -#include "dolphin/helpers/dolphin_state.h" +#include "dolphin.h" +#include "helpers/dolphin_state.h" #include "dolphin_i.h" -#include "projdefs.h" #include #include #include @@ -100,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( @@ -149,6 +148,8 @@ int32_t dolphin_srv(void* p) { if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) { FURI_LOG_W(TAG, "Skipping start in special boot mode"); + + furi_thread_suspend(furi_thread_get_current_id()); return 0; } diff --git a/applications/services/dolphin/helpers/dolphin_state.c b/applications/services/dolphin/helpers/dolphin_state.c index 55c2286d9..724060e53 100644 --- a/applications/services/dolphin/helpers/dolphin_state.c +++ b/applications/services/dolphin/helpers/dolphin_state.c @@ -6,7 +6,6 @@ #include #include #include -#include #include #define TAG "DolphinState" diff --git a/applications/services/gui/elements.c b/applications/services/gui/elements.c index 06b63fd90..5b38c5c79 100644 --- a/applications/services/gui/elements.c +++ b/applications/services/gui/elements.c @@ -9,7 +9,6 @@ #include #include -#include "canvas_i.h" #include #include diff --git a/applications/services/gui/icon.c b/applications/services/gui/icon.c index 6e015ed75..85d640f30 100644 --- a/applications/services/gui/icon.c +++ b/applications/services/gui/icon.c @@ -1,5 +1,5 @@ #include "icon.h" -#include "icon_i.h" +#include "icon_i.h" // IWYU pragma: keep #include #include diff --git a/applications/services/gui/icon_animation.c b/applications/services/gui/icon_animation.c index f887b90ac..f456ba368 100644 --- a/applications/services/gui/icon_animation.c +++ b/applications/services/gui/icon_animation.c @@ -1,5 +1,5 @@ #include "icon_animation_i.h" -#include "icon_i.h" +#include "icon_i.h" // IWYU pragma: keep #include diff --git a/applications/services/gui/modules/button_panel.c b/applications/services/gui/modules/button_panel.c index d768b324a..0aa9dd005 100644 --- a/applications/services/gui/modules/button_panel.c +++ b/applications/services/gui/modules/button_panel.c @@ -39,7 +39,7 @@ typedef struct ButtonItem { void* callback_context; } ButtonItem; -ARRAY_DEF(ButtonArray, ButtonItem*, M_PTR_OPLIST); +ARRAY_DEF(ButtonArray, ButtonItem*, M_PTR_OPLIST); // NOLINT #define M_OPL_ButtonArray_t() ARRAY_OPLIST(ButtonArray, M_PTR_OPLIST) ARRAY_DEF(ButtonMatrix, ButtonArray_t); #define M_OPL_ButtonMatrix_t() ARRAY_OPLIST(ButtonMatrix, M_OPL_ButtonArray_t()) 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/modules/widget.c b/applications/services/gui/modules/widget.c index 50171e0cc..ea3315d8d 100644 --- a/applications/services/gui/modules/widget.c +++ b/applications/services/gui/modules/widget.c @@ -3,7 +3,7 @@ #include #include -ARRAY_DEF(ElementArray, WidgetElement*, M_PTR_OPLIST); +ARRAY_DEF(ElementArray, WidgetElement*, M_PTR_OPLIST); // NOLINT struct Widget { View* view; 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/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/applications/services/notification/notification_app_api.c b/applications/services/notification/notification_app_api.c index 9c9a2680e..90e3f236e 100644 --- a/applications/services/notification/notification_app_api.c +++ b/applications/services/notification/notification_app_api.c @@ -1,7 +1,6 @@ #include #include #include "notification.h" -#include "notification_messages.h" #include "notification_app.h" void notification_message(NotificationApp* app, const NotificationSequence* sequence) { diff --git a/applications/services/power/power_service/power.c b/applications/services/power/power_service/power.c index 2db9bb1c3..278854e13 100644 --- a/applications/services/power/power_service/power.c +++ b/applications/services/power/power_service/power.c @@ -13,7 +13,7 @@ void power_draw_battery_callback(Canvas* canvas, void* context) { if(power->info.gauge_is_ok) { canvas_draw_box(canvas, 2, 2, (power->info.charge + 4) / 5, 4); - if(power->info.voltage_battery_charge_limit < 4.2) { + if(power->info.voltage_battery_charge_limit < 4.2f) { // Battery charge voltage limit is modified, indicate with cross pattern canvas_invert_color(canvas); uint8_t battery_bar_width = (power->info.charge + 4) / 5; @@ -198,6 +198,8 @@ int32_t power_srv(void* p) { if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) { FURI_LOG_W(TAG, "Skipping start in special boot mode"); + + furi_thread_suspend(furi_thread_get_current_id()); return 0; } diff --git a/applications/services/rpc/rpc.c b/applications/services/rpc/rpc.c index 054a0286f..32371fa82 100644 --- a/applications/services/rpc/rpc.c +++ b/applications/services/rpc/rpc.c @@ -1,4 +1,3 @@ -#include "profiles/serial_profile.h" #include "rpc_i.h" #include diff --git a/applications/services/rpc/rpc_desktop.c b/applications/services/rpc/rpc_desktop.c index 0d72b43d5..a70e84363 100644 --- a/applications/services/rpc/rpc_desktop.c +++ b/applications/services/rpc/rpc_desktop.c @@ -1,7 +1,6 @@ #include "flipper.pb.h" #include "rpc_i.h" #include -#include "desktop.pb.h" #define TAG "RpcDesktop" diff --git a/applications/services/rpc/rpc_storage.c b/applications/services/rpc/rpc_storage.c index 151d73d61..306b25777 100644 --- a/applications/services/rpc/rpc_storage.c +++ b/applications/services/rpc/rpc_storage.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -50,7 +51,6 @@ static void rpc_system_storage_reset_state( if(rpc_storage->state == RpcStorageStateWriting) { storage_file_close(rpc_storage->file); storage_file_free(rpc_storage->file); - furi_record_close(RECORD_STORAGE); } rpc_storage->state = RpcStorageStateIdle; @@ -118,10 +118,8 @@ static void rpc_system_storage_info_process(const PB_Main* request, void* contex PB_Main* response = malloc(sizeof(PB_Main)); response->command_id = request->command_id; - Storage* fs_api = furi_record_open(RECORD_STORAGE); - FS_Error error = storage_common_fs_info( - fs_api, + rpc_storage->api, request->content.storage_info_request.path, &response->content.storage_info_response.total_space, &response->content.storage_info_response.free_space); @@ -135,7 +133,6 @@ static void rpc_system_storage_info_process(const PB_Main* request, void* contex rpc_send_and_release(session, response); free(response); - furi_record_close(RECORD_STORAGE); } static void rpc_system_storage_timestamp_process(const PB_Main* request, void* context) { @@ -154,11 +151,9 @@ static void rpc_system_storage_timestamp_process(const PB_Main* request, void* c PB_Main* response = malloc(sizeof(PB_Main)); response->command_id = request->command_id; - Storage* fs_api = furi_record_open(RECORD_STORAGE); - const char* path = request->content.storage_timestamp_request.path; uint32_t timestamp = 0; - FS_Error error = storage_common_timestamp(fs_api, path, ×tamp); + FS_Error error = storage_common_timestamp(rpc_storage->api, path, ×tamp); response->command_status = rpc_system_storage_get_error(error); response->which_content = PB_Main_empty_tag; @@ -170,7 +165,6 @@ static void rpc_system_storage_timestamp_process(const PB_Main* request, void* c rpc_send_and_release(session, response); free(response); - furi_record_close(RECORD_STORAGE); } static void rpc_system_storage_stat_process(const PB_Main* request, void* context) { @@ -189,11 +183,9 @@ static void rpc_system_storage_stat_process(const PB_Main* request, void* contex PB_Main* response = malloc(sizeof(PB_Main)); response->command_id = request->command_id; - Storage* fs_api = furi_record_open(RECORD_STORAGE); - const char* path = request->content.storage_stat_request.path; FileInfo fileinfo; - FS_Error error = storage_common_stat(fs_api, path, &fileinfo); + FS_Error error = storage_common_stat(rpc_storage->api, path, &fileinfo); response->command_status = rpc_system_storage_get_error(error); response->which_content = PB_Main_empty_tag; @@ -209,12 +201,12 @@ static void rpc_system_storage_stat_process(const PB_Main* request, void* contex rpc_send_and_release(session, response); free(response); - furi_record_close(RECORD_STORAGE); } static void rpc_system_storage_list_root(const PB_Main* request, void* context) { furi_assert(request); furi_assert(context); + RpcStorageSystem* rpc_storage = context; RpcSession* session = rpc_storage->session; furi_assert(session); @@ -279,8 +271,7 @@ static void rpc_system_storage_list_process(const PB_Main* request, void* contex return; } - Storage* fs_api = furi_record_open(RECORD_STORAGE); - File* dir = storage_file_alloc(fs_api); + File* dir = storage_file_alloc(rpc_storage->api); PB_Main response = { .command_id = request->command_id, @@ -293,7 +284,7 @@ static void rpc_system_storage_list_process(const PB_Main* request, void* contex bool include_md5 = list_request->include_md5; FuriString* md5 = furi_string_alloc(); FuriString* md5_path = furi_string_alloc(); - File* file = storage_file_alloc(fs_api); + File* file = storage_file_alloc(rpc_storage->api); bool finish = false; int i = 0; @@ -350,8 +341,6 @@ static void rpc_system_storage_list_process(const PB_Main* request, void* contex storage_dir_close(dir); storage_file_free(dir); storage_file_free(file); - - furi_record_close(RECORD_STORAGE); } static void rpc_system_storage_read_process(const PB_Main* request, void* context) { @@ -370,8 +359,7 @@ static void rpc_system_storage_read_process(const PB_Main* request, void* contex /* use same message memory to send response */ PB_Main* response = malloc(sizeof(PB_Main)); const char* path = request->content.storage_read_request.path; - Storage* fs_api = furi_record_open(RECORD_STORAGE); - File* file = storage_file_alloc(fs_api); + File* file = storage_file_alloc(rpc_storage->api); bool fs_operation_success = storage_file_open(file, path, FSAM_READ, FSOM_OPEN_EXISTING); if(fs_operation_success) { @@ -420,8 +408,6 @@ static void rpc_system_storage_read_process(const PB_Main* request, void* contex free(response); storage_file_close(file); storage_file_free(file); - - furi_record_close(RECORD_STORAGE); } static void rpc_system_storage_write_process(const PB_Main* request, void* context) { @@ -451,7 +437,6 @@ static void rpc_system_storage_write_process(const PB_Main* request, void* conte } if(rpc_storage->state != RpcStorageStateWriting) { - rpc_storage->api = furi_record_open(RECORD_STORAGE); rpc_storage->file = storage_file_alloc(rpc_storage->api); rpc_storage->current_command_id = request->command_id; rpc_storage->state = RpcStorageStateWriting; @@ -492,14 +477,15 @@ static void rpc_system_storage_write_process(const PB_Main* request, void* conte } } -static bool rpc_system_storage_is_dir_is_empty(Storage* fs_api, const char* path) { - furi_assert(fs_api); +static bool rpc_system_storage_is_dir_is_empty(Storage* storage, const char* path) { + furi_assert(storage); furi_assert(path); + FileInfo fileinfo; bool is_dir_is_empty = true; - FS_Error error = storage_common_stat(fs_api, path, &fileinfo); + FS_Error error = storage_common_stat(storage, path, &fileinfo); if((error == FSE_OK) && file_info_is_dir(&fileinfo)) { - File* dir = storage_file_alloc(fs_api); + File* dir = storage_file_alloc(storage); if(storage_dir_open(dir, path)) { char* name = malloc(MAX_NAME_LENGTH); while(storage_dir_read(dir, &fileinfo, name, MAX_NAME_LENGTH)) { @@ -531,18 +517,17 @@ static void rpc_system_storage_delete_process(const PB_Main* request, void* cont PB_CommandStatus status = PB_CommandStatus_ERROR; rpc_system_storage_reset_state(rpc_storage, session, true); - Storage* fs_api = furi_record_open(RECORD_STORAGE); - char* path = request->content.storage_delete_request.path; if(!path) { status = PB_CommandStatus_ERROR_INVALID_PARAMETERS; } else { - FS_Error error_remove = storage_common_remove(fs_api, path); + FS_Error error_remove = storage_common_remove(rpc_storage->api, path); // FSE_DENIED is for empty directory, but not only for this // that's why we have to check it - if((error_remove == FSE_DENIED) && !rpc_system_storage_is_dir_is_empty(fs_api, path)) { + if((error_remove == FSE_DENIED) && + !rpc_system_storage_is_dir_is_empty(rpc_storage->api, path)) { if(request->content.storage_delete_request.recursive) { - bool deleted = storage_simply_remove_recursive(fs_api, path); + bool deleted = storage_simply_remove_recursive(rpc_storage->api, path); status = deleted ? PB_CommandStatus_OK : PB_CommandStatus_ERROR; } else { status = PB_CommandStatus_ERROR_STORAGE_DIR_NOT_EMPTY; @@ -554,7 +539,6 @@ static void rpc_system_storage_delete_process(const PB_Main* request, void* cont } } - furi_record_close(RECORD_STORAGE); rpc_send_and_release_empty(session, request->command_id, status); } @@ -572,11 +556,10 @@ static void rpc_system_storage_mkdir_process(const PB_Main* request, void* conte PB_CommandStatus status; rpc_system_storage_reset_state(rpc_storage, session, true); - Storage* fs_api = furi_record_open(RECORD_STORAGE); char* path = request->content.storage_mkdir_request.path; if(path) { if(path_contains_only_ascii(path)) { - FS_Error error = storage_common_mkdir(fs_api, path); + FS_Error error = storage_common_mkdir(rpc_storage->api, path); status = rpc_system_storage_get_error(error); } else { status = PB_CommandStatus_ERROR_STORAGE_INVALID_NAME; @@ -584,7 +567,6 @@ static void rpc_system_storage_mkdir_process(const PB_Main* request, void* conte } else { status = PB_CommandStatus_ERROR_INVALID_PARAMETERS; } - furi_record_close(RECORD_STORAGE); rpc_send_and_release_empty(session, request->command_id, status); } @@ -608,8 +590,7 @@ static void rpc_system_storage_md5sum_process(const PB_Main* request, void* cont return; } - Storage* fs_api = furi_record_open(RECORD_STORAGE); - File* file = storage_file_alloc(fs_api); + File* file = storage_file_alloc(rpc_storage->api); FuriString* md5 = furi_string_alloc(); FS_Error file_error; @@ -633,8 +614,6 @@ static void rpc_system_storage_md5sum_process(const PB_Main* request, void* cont furi_string_free(md5); storage_file_free(file); - - furi_record_close(RECORD_STORAGE); } static void rpc_system_storage_rename_process(const PB_Main* request, void* context) { @@ -651,11 +630,9 @@ static void rpc_system_storage_rename_process(const PB_Main* request, void* cont PB_CommandStatus status; rpc_system_storage_reset_state(rpc_storage, session, true); - Storage* fs_api = furi_record_open(RECORD_STORAGE); - if(path_contains_only_ascii(request->content.storage_rename_request.new_path)) { FS_Error error = storage_common_rename( - fs_api, + rpc_storage->api, request->content.storage_rename_request.old_path, request->content.storage_rename_request.new_path); status = rpc_system_storage_get_error(error); @@ -663,7 +640,6 @@ static void rpc_system_storage_rename_process(const PB_Main* request, void* cont status = PB_CommandStatus_ERROR_STORAGE_INVALID_NAME; } - furi_record_close(RECORD_STORAGE); rpc_send_and_release_empty(session, request->command_id, status); } @@ -678,12 +654,10 @@ static void rpc_system_storage_backup_create_process(const PB_Main* request, voi RpcSession* session = rpc_storage->session; furi_assert(session); - Storage* fs_api = furi_record_open(RECORD_STORAGE); + rpc_system_storage_reset_state(rpc_storage, session, true); - bool backup_ok = - lfs_backup_create(fs_api, request->content.storage_backup_create_request.archive_path); - - furi_record_close(RECORD_STORAGE); + bool backup_ok = lfs_backup_create( + rpc_storage->api, request->content.storage_backup_create_request.archive_path); rpc_send_and_release_empty( session, request->command_id, backup_ok ? PB_CommandStatus_OK : PB_CommandStatus_ERROR); @@ -700,17 +674,58 @@ static void rpc_system_storage_backup_restore_process(const PB_Main* request, vo RpcSession* session = rpc_storage->session; furi_assert(session); - Storage* fs_api = furi_record_open(RECORD_STORAGE); + rpc_system_storage_reset_state(rpc_storage, session, true); - bool backup_ok = - lfs_backup_unpack(fs_api, request->content.storage_backup_restore_request.archive_path); - - furi_record_close(RECORD_STORAGE); + bool backup_ok = lfs_backup_unpack( + rpc_storage->api, request->content.storage_backup_restore_request.archive_path); rpc_send_and_release_empty( session, request->command_id, backup_ok ? PB_CommandStatus_OK : PB_CommandStatus_ERROR); } +static void rpc_system_storage_tar_extract_process(const PB_Main* request, void* context) { + furi_assert(request); + furi_assert(request->which_content == PB_Main_storage_tar_extract_request_tag); + furi_assert(context); + + FURI_LOG_D(TAG, "TarExtract"); + + RpcStorageSystem* rpc_storage = context; + RpcSession* session = rpc_storage->session; + furi_assert(session); + + PB_CommandStatus status; + rpc_system_storage_reset_state(rpc_storage, session, true); + + TarArchive* archive = tar_archive_alloc(rpc_storage->api); + + do { + if(!path_contains_only_ascii(request->content.storage_tar_extract_request.out_path)) { + status = PB_CommandStatus_ERROR_STORAGE_INVALID_NAME; + break; + } + + if(!tar_archive_open( + archive, + request->content.storage_tar_extract_request.tar_path, + TAR_OPEN_MODE_READ)) { + status = PB_CommandStatus_ERROR_STORAGE_INVALID_PARAMETER; + break; + } + + if(!tar_archive_unpack_to( + archive, request->content.storage_tar_extract_request.out_path, NULL)) { + status = PB_CommandStatus_ERROR_STORAGE_INTERNAL; + break; + } + + status = PB_CommandStatus_OK; + } while(0); + + tar_archive_free(archive); + rpc_send_and_release_empty(session, request->command_id, status); +} + void* rpc_system_storage_alloc(RpcSession* session) { furi_assert(session); @@ -761,6 +776,9 @@ void* rpc_system_storage_alloc(RpcSession* session) { rpc_handler.message_handler = rpc_system_storage_backup_restore_process; rpc_add_handler(session, PB_Main_storage_backup_restore_request_tag, &rpc_handler); + rpc_handler.message_handler = rpc_system_storage_tar_extract_process; + rpc_add_handler(session, PB_Main_storage_tar_extract_request_tag, &rpc_handler); + return rpc_storage; } @@ -771,5 +789,8 @@ void rpc_system_storage_free(void* context) { furi_assert(session); rpc_system_storage_reset_state(rpc_storage, session, false); + + furi_record_close(RECORD_STORAGE); + rpc_storage->api = NULL; free(rpc_storage); } diff --git a/applications/services/storage/storage_external_api.c b/applications/services/storage/storage_external_api.c index a377751ea..adc0e2465 100644 --- a/applications/services/storage/storage_external_api.c +++ b/applications/services/storage/storage_external_api.c @@ -1,7 +1,7 @@ #include #include #include "storage.h" -#include "storage_i.h" +#include "storage_i.h" // IWYU pragma: keep #include "storage_message.h" #include #include diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_disable.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_disable.c index 2f21fd999..f31a96894 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_disable.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_disable.c @@ -1,4 +1,3 @@ -#include #include #include #include diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup_done.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup_done.c index 4cef4ba98..170e6bca5 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup_done.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup_done.c @@ -1,7 +1,6 @@ #include #include #include -#include #include #include diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup_howto2.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup_howto2.c index 0ebf85c64..efa39f1f0 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup_howto2.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup_howto2.c @@ -1,6 +1,5 @@ #include #include -#include #include "desktop_settings_scene.h" #include "../desktop_settings_app.h" diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_start.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_start.c index 8dec26016..7b3e5b96b 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_start.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_start.c @@ -3,7 +3,6 @@ #include "../desktop_settings_app.h" #include "desktop_settings_scene.h" -#include "desktop_settings_scene_i.h" typedef enum { DesktopSettingsPinSetup = 0, diff --git a/applications/settings/dolphin_passport/passport.c b/applications/settings/dolphin_passport/passport.c index d43f150c6..aae53cd2a 100644 --- a/applications/settings/dolphin_passport/passport.c +++ b/applications/settings/dolphin_passport/passport.c @@ -1,12 +1,11 @@ -#include "assets_icons.h" -#include "dolphin/helpers/dolphin_state.h" -#include -#include #include -#include #include -#include "dolphin/dolphin.h" -#include "math.h" + +#include +#include +#include + +#include #define MOODS_TOTAL 3 #define BUTTHURT_MAX 3 @@ -91,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/settings/power_settings_app/views/battery_info.c b/applications/settings/power_settings_app/views/battery_info.c index dd2ec2dbc..c626d1844 100644 --- a/applications/settings/power_settings_app/views/battery_info.c +++ b/applications/settings/power_settings_app/views/battery_info.c @@ -68,7 +68,7 @@ static void draw_battery(Canvas* canvas, BatteryInfoModel* data, int x, int y) { ABS(current), current < HIGH_DRAIN_CURRENT_THRESHOLD ? "mA!" : "mA"); } else if(data->vbus_voltage > 0) { - if(data->charge_voltage_limit < 4.2) { + if(data->charge_voltage_limit < 4.2f) { // Non-default battery charging limit, mention it snprintf(emote, sizeof(emote), "Charged!"); snprintf(header, sizeof(header), "Limited to"); diff --git a/applications/system/js_app/modules/js_flipper.c b/applications/system/js_app/modules/js_flipper.c index 17c0ad36b..4619a1593 100644 --- a/applications/system/js_app/modules/js_flipper.c +++ b/applications/system/js_app/modules/js_flipper.c @@ -1,5 +1,5 @@ +#include "../js_modules.h" // IWYU pragma: keep #include -#include "../js_modules.h" #include #include diff --git a/applications/system/js_app/plugin_api/app_api_table_i.h b/applications/system/js_app/plugin_api/app_api_table_i.h index d84ae8110..b48221343 100644 --- a/applications/system/js_app/plugin_api/app_api_table_i.h +++ b/applications/system/js_app/plugin_api/app_api_table_i.h @@ -1,4 +1,3 @@ -#include #include "js_plugin_api.h" /* * A list of app's private functions and objects to expose for plugins. 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/assets/protobuf b/assets/protobuf index 1956b83bb..816de200a 160000 --- a/assets/protobuf +++ b/assets/protobuf @@ -1 +1 @@ -Subproject commit 1956b83bba99313ee8d8386e5d35d0549341ca26 +Subproject commit 816de200a4a43efc25c5b92d6a57fc982d7e988a 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/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/event_flag.c b/furi/core/event_flag.c index ccbee93d6..c2e04e2fd 100644 --- a/furi/core/event_flag.c +++ b/furi/core/event_flag.c @@ -8,18 +8,27 @@ #define FURI_EVENT_FLAG_MAX_BITS_EVENT_GROUPS 24U #define FURI_EVENT_FLAG_INVALID_BITS (~((1UL << FURI_EVENT_FLAG_MAX_BITS_EVENT_GROUPS) - 1U)) +struct FuriEventFlag { + StaticEventGroup_t container; +}; + +// IMPORTANT: container MUST be the FIRST struct member +static_assert(offsetof(FuriEventFlag, container) == 0); + FuriEventFlag* furi_event_flag_alloc(void) { furi_check(!FURI_IS_IRQ_MODE()); - EventGroupHandle_t handle = xEventGroupCreate(); - furi_check(handle); + FuriEventFlag* instance = malloc(sizeof(FuriEventFlag)); - return ((FuriEventFlag*)handle); + furi_check(xEventGroupCreateStatic(&instance->container) == (EventGroupHandle_t)instance); + + return instance; } void furi_event_flag_free(FuriEventFlag* instance) { furi_check(!FURI_IS_IRQ_MODE()); vEventGroupDelete((EventGroupHandle_t)instance); + free(instance); } uint32_t furi_event_flag_set(FuriEventFlag* instance, uint32_t flags) { @@ -43,7 +52,7 @@ uint32_t furi_event_flag_set(FuriEventFlag* instance, uint32_t flags) { } /* Return event flags after setting */ - return (rflags); + return rflags; } uint32_t furi_event_flag_clear(FuriEventFlag* instance, uint32_t flags) { @@ -69,7 +78,7 @@ uint32_t furi_event_flag_clear(FuriEventFlag* instance, uint32_t flags) { } /* Return event flags before clearing */ - return (rflags); + return rflags; } uint32_t furi_event_flag_get(FuriEventFlag* instance) { @@ -85,7 +94,7 @@ uint32_t furi_event_flag_get(FuriEventFlag* instance) { } /* Return current event flags */ - return (rflags); + return rflags; } uint32_t furi_event_flag_wait( @@ -136,5 +145,5 @@ uint32_t furi_event_flag_wait( } /* Return event flags before clearing */ - return (rflags); + return rflags; } diff --git a/furi/core/event_flag.h b/furi/core/event_flag.h index 7200144cd..fbc3bb004 100644 --- a/furi/core/event_flag.h +++ b/furi/core/event_flag.h @@ -10,7 +10,7 @@ extern "C" { #endif -typedef void FuriEventFlag; +typedef struct FuriEventFlag FuriEventFlag; /** Allocate FuriEventFlag * 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/kernel.c b/furi/core/kernel.c index db3d6a716..52c0e285e 100644 --- a/furi/core/kernel.c +++ b/furi/core/kernel.c @@ -8,8 +8,6 @@ #include #include -#include CMSIS_device_header - bool furi_kernel_is_irq_or_masked(void) { bool irq = false; BaseType_t state; diff --git a/furi/core/memmgr.c b/furi/core/memmgr.c index 768d44890..903c6c141 100644 --- a/furi/core/memmgr.c +++ b/furi/core/memmgr.c @@ -1,5 +1,4 @@ #include "memmgr.h" -#include "common_defines.h" #include #include diff --git a/furi/core/message_queue.c b/furi/core/message_queue.c index 6a4e8c324..cda775abe 100644 --- a/furi/core/message_queue.c +++ b/furi/core/message_queue.c @@ -1,18 +1,24 @@ -#include "kernel.h" -#include "message_queue.h" -#include "check.h" +#include "message_queue_i.h" -#include -#include +// Internal FreeRTOS member names +#define uxMessagesWaiting uxDummy4[0] +#define uxLength uxDummy4[1] +#define uxItemSize uxDummy4[2] struct FuriMessageQueue { - // !!! Semi-Opaque type inheritance, Very Fragile, DO NOT MOVE !!! StaticQueue_t container; - // !!! Data buffer, must be last in the structure, DO NOT MOVE !!! + // Event Loop Link + FuriEventLoopLink event_loop_link; + uint8_t buffer[]; }; +// IMPORTANT: container MUST be the FIRST struct member +static_assert(offsetof(FuriMessageQueue, container) == 0); +// IMPORTANT: buffer MUST be the LAST struct member +static_assert(offsetof(FuriMessageQueue, buffer) == sizeof(FuriMessageQueue)); + FuriMessageQueue* furi_message_queue_alloc(uint32_t msg_count, uint32_t msg_size) { furi_check((furi_kernel_is_irq_or_masked() == 0U) && (msg_count > 0U) && (msg_size > 0U)); @@ -35,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); } @@ -75,8 +85,12 @@ FuriStatus } } + if(stat == FuriStatusOk) { + furi_event_loop_link_notify(&instance->event_loop_link, FuriEventLoopEventIn); + } + /* Return execution status */ - return (stat); + return stat; } FuriStatus furi_message_queue_get(FuriMessageQueue* instance, void* msg_ptr, uint32_t timeout) { @@ -114,31 +128,23 @@ FuriStatus furi_message_queue_get(FuriMessageQueue* instance, void* msg_ptr, uin } } - return (stat); + if(stat == FuriStatusOk) { + furi_event_loop_link_notify(&instance->event_loop_link, FuriEventLoopEventOut); + } + + return stat; } uint32_t furi_message_queue_get_capacity(FuriMessageQueue* instance) { furi_check(instance); - uint32_t capacity; - - /* capacity = pxQueue->uxLength */ - capacity = instance->container.uxDummy4[1]; - - /* Return maximum number of messages */ - return (capacity); + return instance->container.uxLength; } uint32_t furi_message_queue_get_message_size(FuriMessageQueue* instance) { furi_check(instance); - uint32_t size; - - /* size = pxQueue->uxItemSize */ - size = instance->container.uxDummy4[2]; - - /* Return maximum message size */ - return (size); + return instance->container.uxItemSize; } uint32_t furi_message_queue_get_count(FuriMessageQueue* instance) { @@ -153,8 +159,7 @@ uint32_t furi_message_queue_get_count(FuriMessageQueue* instance) { count = uxQueueMessagesWaiting(hQueue); } - /* Return number of queued messages */ - return ((uint32_t)count); + return (uint32_t)count; } uint32_t furi_message_queue_get_space(FuriMessageQueue* instance) { @@ -166,16 +171,14 @@ uint32_t furi_message_queue_get_space(FuriMessageQueue* instance) { if(furi_kernel_is_irq_or_masked() != 0U) { isrm = taskENTER_CRITICAL_FROM_ISR(); - /* space = pxQueue->uxLength - pxQueue->uxMessagesWaiting; */ - space = instance->container.uxDummy4[1] - instance->container.uxDummy4[0]; + space = instance->container.uxLength - instance->container.uxMessagesWaiting; taskEXIT_CRITICAL_FROM_ISR(isrm); } else { space = (uint32_t)uxQueueSpacesAvailable((QueueHandle_t)instance); } - /* Return number of available slots */ - return (space); + return space; } FuriStatus furi_message_queue_reset(FuriMessageQueue* instance) { @@ -191,6 +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); + 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.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/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/mutex.c b/furi/core/mutex.c index e32be1a39..f59ae83ad 100644 --- a/furi/core/mutex.c +++ b/furi/core/mutex.c @@ -5,129 +5,120 @@ #include #include +// Internal FreeRTOS member names +#define ucQueueType ucDummy9 + +struct FuriMutex { + StaticSemaphore_t container; +}; + +// IMPORTANT: container MUST be the FIRST struct member +static_assert(offsetof(FuriMutex, container) == 0); + FuriMutex* furi_mutex_alloc(FuriMutexType type) { furi_check(!FURI_IS_IRQ_MODE()); - SemaphoreHandle_t hMutex = NULL; + FuriMutex* instance = malloc(sizeof(FuriMutex)); + + SemaphoreHandle_t hMutex; if(type == FuriMutexTypeNormal) { - hMutex = xSemaphoreCreateMutex(); + hMutex = xSemaphoreCreateMutexStatic(&instance->container); } else if(type == FuriMutexTypeRecursive) { - hMutex = xSemaphoreCreateRecursiveMutex(); + hMutex = xSemaphoreCreateRecursiveMutexStatic(&instance->container); } else { furi_crash(); } - furi_check(hMutex != NULL); + furi_check(hMutex == (SemaphoreHandle_t)instance); - if(type == FuriMutexTypeRecursive) { - /* Set LSB as 'recursive mutex flag' */ - hMutex = (SemaphoreHandle_t)((uint32_t)hMutex | 1U); - } - - /* Return mutex ID */ - return ((FuriMutex*)hMutex); + return instance; } void furi_mutex_free(FuriMutex* instance) { furi_check(!FURI_IS_IRQ_MODE()); furi_check(instance); - vSemaphoreDelete((SemaphoreHandle_t)((uint32_t)instance & ~1U)); + vSemaphoreDelete((SemaphoreHandle_t)instance); + free(instance); } FuriStatus furi_mutex_acquire(FuriMutex* instance, uint32_t timeout) { furi_check(instance); - SemaphoreHandle_t hMutex; - FuriStatus stat; - uint32_t rmtx; + SemaphoreHandle_t hMutex = (SemaphoreHandle_t)(instance); + const uint8_t mutex_type = instance->container.ucQueueType; - hMutex = (SemaphoreHandle_t)((uint32_t)instance & ~1U); - - /* Extract recursive mutex flag */ - rmtx = (uint32_t)instance & 1U; - - stat = FuriStatusOk; + FuriStatus stat = FuriStatusOk; if(FURI_IS_IRQ_MODE()) { stat = FuriStatusErrorISR; - } else if(hMutex == NULL) { - stat = FuriStatusErrorParameter; - } else { - if(rmtx != 0U) { - if(xSemaphoreTakeRecursive(hMutex, timeout) != pdPASS) { - if(timeout != 0U) { - stat = FuriStatusErrorTimeout; - } else { - stat = FuriStatusErrorResource; - } - } - } else { - if(xSemaphoreTake(hMutex, timeout) != pdPASS) { - if(timeout != 0U) { - stat = FuriStatusErrorTimeout; - } else { - stat = FuriStatusErrorResource; - } + + } else if(mutex_type == queueQUEUE_TYPE_RECURSIVE_MUTEX) { + if(xSemaphoreTakeRecursive(hMutex, timeout) != pdPASS) { + if(timeout != 0U) { + stat = FuriStatusErrorTimeout; + } else { + stat = FuriStatusErrorResource; } } + + } else if(mutex_type == queueQUEUE_TYPE_MUTEX) { + if(xSemaphoreTake(hMutex, timeout) != pdPASS) { + if(timeout != 0U) { + stat = FuriStatusErrorTimeout; + } else { + stat = FuriStatusErrorResource; + } + } + + } else { + furi_crash(); } - /* Return execution status */ - return (stat); + return stat; } FuriStatus furi_mutex_release(FuriMutex* instance) { furi_check(instance); - SemaphoreHandle_t hMutex; - FuriStatus stat; - uint32_t rmtx; + SemaphoreHandle_t hMutex = (SemaphoreHandle_t)(instance); + const uint8_t mutex_type = instance->container.ucQueueType; - hMutex = (SemaphoreHandle_t)((uint32_t)instance & ~1U); - - /* Extract recursive mutex flag */ - rmtx = (uint32_t)instance & 1U; - - stat = FuriStatusOk; + FuriStatus stat = FuriStatusOk; if(FURI_IS_IRQ_MODE()) { stat = FuriStatusErrorISR; - } else if(hMutex == NULL) { - stat = FuriStatusErrorParameter; - } else { - if(rmtx != 0U) { - if(xSemaphoreGiveRecursive(hMutex) != pdPASS) { - stat = FuriStatusErrorResource; - } - } else { - if(xSemaphoreGive(hMutex) != pdPASS) { - stat = FuriStatusErrorResource; - } + + } else if(mutex_type == queueQUEUE_TYPE_RECURSIVE_MUTEX) { + if(xSemaphoreGiveRecursive(hMutex) != pdPASS) { + stat = FuriStatusErrorResource; } + + } else if(mutex_type == queueQUEUE_TYPE_MUTEX) { + if(xSemaphoreGive(hMutex) != pdPASS) { + stat = FuriStatusErrorResource; + } + + } else { + furi_crash(); } - /* Return execution status */ - return (stat); + return stat; } FuriThreadId furi_mutex_get_owner(FuriMutex* instance) { furi_check(instance); - SemaphoreHandle_t hMutex; + SemaphoreHandle_t hMutex = (SemaphoreHandle_t)instance; + FuriThreadId owner; - hMutex = (SemaphoreHandle_t)((uint32_t)instance & ~1U); - - if((hMutex == NULL)) { - owner = 0; - } else if(FURI_IS_IRQ_MODE()) { + if(FURI_IS_IRQ_MODE()) { owner = (FuriThreadId)xSemaphoreGetMutexHolderFromISR(hMutex); } else { owner = (FuriThreadId)xSemaphoreGetMutexHolder(hMutex); } - /* Return owner thread ID */ - return (owner); + return owner; } diff --git a/furi/core/mutex.h b/furi/core/mutex.h index aa55fa7bc..60372a292 100644 --- a/furi/core/mutex.h +++ b/furi/core/mutex.h @@ -16,7 +16,7 @@ typedef enum { FuriMutexTypeRecursive, } FuriMutexType; -typedef void FuriMutex; +typedef struct FuriMutex FuriMutex; /** Allocate FuriMutex * diff --git a/furi/core/pubsub.c b/furi/core/pubsub.c index bcabc74a7..7c1730632 100644 --- a/furi/core/pubsub.c +++ b/furi/core/pubsub.c @@ -1,5 +1,4 @@ #include "pubsub.h" -#include "memmgr.h" #include "check.h" #include "mutex.h" @@ -21,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 e9bd8edcd..fa384369a 100644 --- a/furi/core/record.c +++ b/furi/core/record.c @@ -1,6 +1,5 @@ #include "record.h" #include "check.h" -#include "memmgr.h" #include "mutex.h" #include "event_flag.h" @@ -40,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/semaphore.c b/furi/core/semaphore.c index 503eec472..6413eb65f 100644 --- a/furi/core/semaphore.c +++ b/furi/core/semaphore.c @@ -5,36 +5,43 @@ #include #include +struct FuriSemaphore { + StaticSemaphore_t container; +}; + +// IMPORTANT: container MUST be the FIRST struct member +static_assert(offsetof(FuriSemaphore, container) == 0); + FuriSemaphore* furi_semaphore_alloc(uint32_t max_count, uint32_t initial_count) { furi_check(!FURI_IS_IRQ_MODE()); furi_check((max_count > 0U) && (initial_count <= max_count)); - SemaphoreHandle_t hSemaphore = NULL; + FuriSemaphore* instance = malloc(sizeof(FuriSemaphore)); + + SemaphoreHandle_t hSemaphore; + if(max_count == 1U) { - hSemaphore = xSemaphoreCreateBinary(); - if((hSemaphore != NULL) && (initial_count != 0U)) { - if(xSemaphoreGive(hSemaphore) != pdPASS) { - vSemaphoreDelete(hSemaphore); - hSemaphore = NULL; - } - } + hSemaphore = xSemaphoreCreateBinaryStatic(&instance->container); } else { - hSemaphore = xSemaphoreCreateCounting(max_count, initial_count); + hSemaphore = + xSemaphoreCreateCountingStatic(max_count, initial_count, &instance->container); } - furi_check(hSemaphore); + furi_check(hSemaphore == (SemaphoreHandle_t)instance); - /* Return semaphore ID */ - return ((FuriSemaphore*)hSemaphore); + if(max_count == 1U && initial_count != 0U) { + furi_check(xSemaphoreGive(hSemaphore) == pdPASS); + } + + return instance; } void furi_semaphore_free(FuriSemaphore* instance) { furi_check(instance); furi_check(!FURI_IS_IRQ_MODE()); - SemaphoreHandle_t hSemaphore = (SemaphoreHandle_t)instance; - - vSemaphoreDelete(hSemaphore); + vSemaphoreDelete((SemaphoreHandle_t)instance); + free(instance); } FuriStatus furi_semaphore_acquire(FuriSemaphore* instance, uint32_t timeout) { @@ -58,6 +65,7 @@ FuriStatus furi_semaphore_acquire(FuriSemaphore* instance, uint32_t timeout) { portYIELD_FROM_ISR(yield); } } + } else { if(xSemaphoreTake(hSemaphore, (TickType_t)timeout) != pdPASS) { if(timeout != 0U) { @@ -68,8 +76,7 @@ FuriStatus furi_semaphore_acquire(FuriSemaphore* instance, uint32_t timeout) { } } - /* Return execution status */ - return (stat); + return stat; } FuriStatus furi_semaphore_release(FuriSemaphore* instance) { @@ -89,14 +96,14 @@ FuriStatus furi_semaphore_release(FuriSemaphore* instance) { } else { portYIELD_FROM_ISR(yield); } + } else { if(xSemaphoreGive(hSemaphore) != pdPASS) { stat = FuriStatusErrorResource; } } - /* Return execution status */ - return (stat); + return stat; } uint32_t furi_semaphore_get_count(FuriSemaphore* instance) { @@ -111,6 +118,5 @@ uint32_t furi_semaphore_get_count(FuriSemaphore* instance) { count = (uint32_t)uxSemaphoreGetCount(hSemaphore); } - /* Return number of tokens */ - return (count); + return count; } diff --git a/furi/core/semaphore.h b/furi/core/semaphore.h index 19d056bfe..c6b9a1176 100644 --- a/furi/core/semaphore.h +++ b/furi/core/semaphore.h @@ -11,7 +11,7 @@ extern "C" { #endif -typedef void FuriSemaphore; +typedef struct FuriSemaphore FuriSemaphore; /** Allocate semaphore * diff --git a/furi/core/stream_buffer.c b/furi/core/stream_buffer.c index 8bd00d91c..879520010 100644 --- a/furi/core/stream_buffer.c +++ b/furi/core/stream_buffer.c @@ -1,29 +1,47 @@ -#include "base.h" -#include "check.h" #include "stream_buffer.h" + +#include "check.h" #include "common_defines.h" #include #include +struct FuriStreamBuffer { + StaticStreamBuffer_t container; + uint8_t buffer[]; +}; + +// IMPORTANT: container MUST be the FIRST struct member +static_assert(offsetof(FuriStreamBuffer, container) == 0); +// IMPORTANT: buffer MUST be the LAST struct member +static_assert(offsetof(FuriStreamBuffer, buffer) == sizeof(FuriStreamBuffer)); + FuriStreamBuffer* furi_stream_buffer_alloc(size_t size, size_t trigger_level) { furi_check(size != 0); - StreamBufferHandle_t handle = xStreamBufferCreate(size, trigger_level); - furi_check(handle); + // Actual FreeRTOS usable buffer size seems to be one less + const size_t buffer_size = size + 1; - return handle; + FuriStreamBuffer* stream_buffer = malloc(sizeof(FuriStreamBuffer) + buffer_size); + StreamBufferHandle_t hStreamBuffer = xStreamBufferCreateStatic( + buffer_size, trigger_level, stream_buffer->buffer, &stream_buffer->container); + + furi_check(hStreamBuffer == (StreamBufferHandle_t)stream_buffer); + + return stream_buffer; }; void furi_stream_buffer_free(FuriStreamBuffer* stream_buffer) { furi_check(stream_buffer); - vStreamBufferDelete(stream_buffer); + vStreamBufferDelete((StreamBufferHandle_t)stream_buffer); + free(stream_buffer); }; bool furi_stream_set_trigger_level(FuriStreamBuffer* stream_buffer, size_t trigger_level) { furi_check(stream_buffer); - return xStreamBufferSetTriggerLevel(stream_buffer, trigger_level) == pdTRUE; + return xStreamBufferSetTriggerLevel((StreamBufferHandle_t)stream_buffer, trigger_level) == + pdTRUE; }; size_t furi_stream_buffer_send( @@ -37,10 +55,10 @@ size_t furi_stream_buffer_send( if(FURI_IS_IRQ_MODE()) { BaseType_t yield; - ret = xStreamBufferSendFromISR(stream_buffer, data, length, &yield); + ret = xStreamBufferSendFromISR((StreamBufferHandle_t)stream_buffer, data, length, &yield); portYIELD_FROM_ISR(yield); } else { - ret = xStreamBufferSend(stream_buffer, data, length, timeout); + ret = xStreamBufferSend((StreamBufferHandle_t)stream_buffer, data, length, timeout); } return ret; @@ -57,10 +75,11 @@ size_t furi_stream_buffer_receive( if(FURI_IS_IRQ_MODE()) { BaseType_t yield; - ret = xStreamBufferReceiveFromISR(stream_buffer, data, length, &yield); + ret = + xStreamBufferReceiveFromISR((StreamBufferHandle_t)stream_buffer, data, length, &yield); portYIELD_FROM_ISR(yield); } else { - ret = xStreamBufferReceive(stream_buffer, data, length, timeout); + ret = xStreamBufferReceive((StreamBufferHandle_t)stream_buffer, data, length, timeout); } return ret; @@ -69,33 +88,33 @@ size_t furi_stream_buffer_receive( size_t furi_stream_buffer_bytes_available(FuriStreamBuffer* stream_buffer) { furi_check(stream_buffer); - return xStreamBufferBytesAvailable(stream_buffer); + return xStreamBufferBytesAvailable((StreamBufferHandle_t)stream_buffer); }; size_t furi_stream_buffer_spaces_available(FuriStreamBuffer* stream_buffer) { furi_check(stream_buffer); - return xStreamBufferSpacesAvailable(stream_buffer); + return xStreamBufferSpacesAvailable((StreamBufferHandle_t)stream_buffer); }; bool furi_stream_buffer_is_full(FuriStreamBuffer* stream_buffer) { furi_check(stream_buffer); - return xStreamBufferIsFull(stream_buffer) == pdTRUE; + return xStreamBufferIsFull((StreamBufferHandle_t)stream_buffer) == pdTRUE; }; bool furi_stream_buffer_is_empty(FuriStreamBuffer* stream_buffer) { furi_check(stream_buffer); - return (xStreamBufferIsEmpty(stream_buffer) == pdTRUE); + return (xStreamBufferIsEmpty((StreamBufferHandle_t)stream_buffer) == pdTRUE); }; FuriStatus furi_stream_buffer_reset(FuriStreamBuffer* stream_buffer) { furi_check(stream_buffer); - if(xStreamBufferReset(stream_buffer) == pdPASS) { + if(xStreamBufferReset((StreamBufferHandle_t)stream_buffer) == pdPASS) { return FuriStatusOk; } else { return FuriStatusError; } -} \ No newline at end of file +} diff --git a/furi/core/stream_buffer.h b/furi/core/stream_buffer.h index 5ddc49416..eef8ee510 100644 --- a/furi/core/stream_buffer.h +++ b/furi/core/stream_buffer.h @@ -12,14 +12,14 @@ * interrupt that will read from the buffer (the reader). */ #pragma once -#include -#include + +#include "base.h" #ifdef __cplusplus extern "C" { #endif -typedef void FuriStreamBuffer; +typedef struct FuriStreamBuffer FuriStreamBuffer; /** * @brief Allocate stream buffer instance. diff --git a/furi/core/thread.c b/furi/core/thread.c index c9bf79d32..db4b7bc80 100644 --- a/furi/core/thread.c +++ b/furi/core/thread.c @@ -15,7 +15,9 @@ #define TAG "FuriThread" -#define THREAD_NOTIFY_INDEX 1 // Index 0 is used for stream buffers +#define THREAD_NOTIFY_INDEX (1) // Index 0 is used for stream buffers + +#define THREAD_MAX_STACK_SIZE (UINT16_MAX * sizeof(StackType_t)) typedef struct FuriThreadStdout FuriThreadStdout; @@ -25,6 +27,10 @@ struct FuriThreadStdout { }; struct FuriThread { + StaticTask_t container; + TaskHandle_t task_handle; + StackType_t* stack_buffer; + FuriThreadState state; int32_t ret; @@ -39,7 +45,7 @@ struct FuriThread { FuriThreadPriority priority; - TaskHandle_t task_handle; + size_t stack_size; size_t heap_size; FuriThreadStdout output; @@ -48,10 +54,11 @@ struct FuriThread { // this ensures that the size of this structure is minimal bool is_service; bool heap_trace_enabled; - - configSTACK_DEPTH_TYPE stack_size; }; +// IMPORTANT: container MUST be the FIRST struct member +static_assert(offsetof(FuriThread, container) == 0); + static size_t __furi_thread_stdout_write(FuriThread* thread, const char* data, size_t size); static int32_t __furi_thread_stdout_flush(FuriThread* thread); @@ -90,6 +97,8 @@ static void furi_thread_body(void* context) { thread->ret = thread->callback(thread->context); + furi_check(!thread->is_service, "Service threads MUST NOT return"); + if(thread->heap_trace_enabled == true) { furi_delay_ms(33); thread->heap_size = memmgr_heap_get_thread_memory((FuriThreadId)task_handle); @@ -104,13 +113,6 @@ static void furi_thread_body(void* context) { furi_check(thread->state == FuriThreadStateRunning); - if(thread->is_service) { - FURI_LOG_W( - TAG, - "%s service thread TCB memory will not be reclaimed", - thread->name ? thread->name : ""); - } - // flush stdout __furi_thread_stdout_flush(thread); @@ -120,10 +122,8 @@ static void furi_thread_body(void* context) { furi_thread_catch(); } -FuriThread* furi_thread_alloc(void) { - FuriThread* thread = malloc(sizeof(FuriThread)); +static void furi_thread_init_common(FuriThread* thread) { thread->output.buffer = furi_string_alloc(); - thread->is_service = false; FuriThread* parent = NULL; if(xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) { @@ -148,6 +148,32 @@ FuriThread* furi_thread_alloc(void) { } else { thread->heap_trace_enabled = false; } +} + +FuriThread* furi_thread_alloc(void) { + FuriThread* thread = malloc(sizeof(FuriThread)); + + furi_thread_init_common(thread); + + return thread; +} + +FuriThread* furi_thread_alloc_service( + const char* name, + uint32_t stack_size, + FuriThreadCallback callback, + void* context) { + FuriThread* thread = memmgr_alloc_from_pool(sizeof(FuriThread)); + + furi_thread_init_common(thread); + + thread->stack_buffer = memmgr_alloc_from_pool(stack_size); + thread->stack_size = stack_size; + thread->is_service = true; + + furi_thread_set_name(thread, name); + furi_thread_set_callback(thread, callback); + furi_thread_set_context(thread, context); return thread; } @@ -167,15 +193,20 @@ FuriThread* furi_thread_alloc_ex( void furi_thread_free(FuriThread* thread) { furi_check(thread); - - // Ensure that use join before free + // Cannot free a service thread + furi_check(thread->is_service == false); + // Cannot free a non-joined thread furi_check(thread->state == FuriThreadStateStopped); furi_check(thread->task_handle == NULL); - if(thread->name) free(thread->name); - if(thread->appid) free(thread->appid); - furi_string_free(thread->output.buffer); + furi_thread_set_name(thread, NULL); + furi_thread_set_appid(thread, NULL); + if(thread->stack_buffer) { + free(thread->stack_buffer); + } + + furi_string_free(thread->output.buffer); free(thread); } @@ -183,7 +214,9 @@ void furi_thread_set_name(FuriThread* thread, const char* name) { furi_check(thread); furi_check(thread->state == FuriThreadStateStopped); - if(thread->name) free(thread->name); + if(thread->name) { + free(thread->name); + } thread->name = name ? strdup(name) : NULL; } @@ -191,18 +224,28 @@ void furi_thread_set_name(FuriThread* thread, const char* name) { void furi_thread_set_appid(FuriThread* thread, const char* appid) { furi_check(thread); furi_check(thread->state == FuriThreadStateStopped); - if(thread->appid) free(thread->appid); - thread->appid = appid ? strdup(appid) : NULL; -} -void furi_thread_mark_as_service(FuriThread* thread) { - thread->is_service = true; + if(thread->appid) { + free(thread->appid); + } + + thread->appid = appid ? strdup(appid) : NULL; } void furi_thread_set_stack_size(FuriThread* thread, size_t stack_size) { furi_check(thread); furi_check(thread->state == FuriThreadStateStopped); - furi_check(stack_size % 4 == 0); + furi_check(stack_size); + furi_check(stack_size <= THREAD_MAX_STACK_SIZE); + furi_check(stack_size % sizeof(StackType_t) == 0); + // Stack size cannot be configured for a thread that has been marked as a service + furi_check(thread->is_service == false); + + if(thread->stack_buffer) { + free(thread->stack_buffer); + } + + thread->stack_buffer = malloc(stack_size); thread->stack_size = stack_size; } @@ -263,28 +306,23 @@ void furi_thread_start(FuriThread* thread) { furi_check(thread); furi_check(thread->callback); furi_check(thread->state == FuriThreadStateStopped); - furi_check(thread->stack_size > 0 && thread->stack_size < (UINT16_MAX * sizeof(StackType_t))); + furi_check(thread->stack_size > 0); furi_thread_set_state(thread, FuriThreadStateStarting); - uint32_t stack = thread->stack_size / sizeof(StackType_t); + uint32_t stack_depth = thread->stack_size / sizeof(StackType_t); UBaseType_t priority = thread->priority ? thread->priority : FuriThreadPriorityNormal; - if(thread->is_service) { - thread->task_handle = xTaskCreateStatic( - furi_thread_body, - thread->name, - stack, - thread, - priority, - memmgr_aux_pool_alloc(sizeof(StackType_t) * stack), - memmgr_aux_pool_alloc(sizeof(StaticTask_t))); - } else { - BaseType_t ret = xTaskCreate( - furi_thread_body, thread->name, stack, thread, priority, &thread->task_handle); - furi_check(ret == pdPASS); - } - furi_check(thread->task_handle); + thread->task_handle = xTaskCreateStatic( + furi_thread_body, + thread->name, + stack_depth, + thread, + priority, + thread->stack_buffer, + &thread->container); + + furi_check(thread->task_handle == (TaskHandle_t)&thread->container); } void furi_thread_cleanup_tcb_event(TaskHandle_t task) { @@ -299,7 +337,9 @@ void furi_thread_cleanup_tcb_event(TaskHandle_t task) { bool furi_thread_join(FuriThread* thread) { furi_check(thread); - + // Cannot join a service thread + furi_check(!thread->is_service); + // Cannot join a thread to itself furi_check(furi_thread_get_current() != thread); // !!! IMPORTANT NOTICE !!! @@ -387,7 +427,7 @@ uint32_t furi_thread_flags_set(FuriThreadId thread_id, uint32_t flags) { } } /* Return flags after setting */ - return (rflags); + return rflags; } uint32_t furi_thread_flags_clear(uint32_t flags) { @@ -416,7 +456,7 @@ uint32_t furi_thread_flags_clear(uint32_t flags) { } /* Return flags before clearing */ - return (rflags); + return rflags; } uint32_t furi_thread_flags_get(void) { @@ -434,7 +474,7 @@ uint32_t furi_thread_flags_get(void) { } } - return (rflags); + return rflags; } uint32_t furi_thread_flags_wait(uint32_t flags, uint32_t options, uint32_t timeout) { @@ -504,7 +544,7 @@ uint32_t furi_thread_flags_wait(uint32_t flags, uint32_t options, uint32_t timeo } /* Return flags before clearing */ - return (rflags); + return rflags; } uint32_t furi_thread_enumerate(FuriThreadId* thread_array, uint32_t array_item_count) { @@ -533,7 +573,7 @@ uint32_t furi_thread_enumerate(FuriThreadId* thread_array, uint32_t array_item_c vPortFree(task); } - return (count); + return count; } const char* furi_thread_get_name(FuriThreadId thread_id) { @@ -546,7 +586,7 @@ const char* furi_thread_get_name(FuriThreadId thread_id) { name = pcTaskGetName(hTask); } - return (name); + return name; } const char* furi_thread_get_appid(FuriThreadId thread_id) { @@ -560,7 +600,7 @@ const char* furi_thread_get_appid(FuriThreadId thread_id) { } } - return (appid); + return appid; } uint32_t furi_thread_get_stack_space(FuriThreadId thread_id) { @@ -573,7 +613,7 @@ uint32_t furi_thread_get_stack_space(FuriThreadId thread_id) { sz = (uint32_t)(uxTaskGetStackHighWaterMark(hTask) * sizeof(StackType_t)); } - return (sz); + return sz; } static size_t __furi_thread_stdout_write(FuriThread* thread, const char* data, size_t size) { diff --git a/furi/core/thread.h b/furi/core/thread.h index f21ee9df3..d78272a4d 100644 --- a/furi/core/thread.h +++ b/furi/core/thread.h @@ -1,6 +1,6 @@ /** * @file thread.h - * Furi: Furi Thread API + * @brief Furi: Furi Thread API */ #pragma once @@ -15,14 +15,20 @@ extern "C" { #endif -/** FuriThreadState */ +/** + * @brief Enumeration of possible FuriThread states. + * + * Many of the FuriThread functions MUST ONLY be called when the thread is STOPPED. + */ typedef enum { - FuriThreadStateStopped, - FuriThreadStateStarting, - FuriThreadStateRunning, + FuriThreadStateStopped, /**< Thread is stopped */ + FuriThreadStateStarting, /**< Thread is starting */ + FuriThreadStateRunning, /**< Thread is running */ } FuriThreadState; -/** FuriThreadPriority */ +/** + * @brief Enumeration of possible FuriThread priorities. + */ typedef enum { FuriThreadPriorityNone = 0, /**< Uninitialized, choose system default */ FuriThreadPriorityIdle = 1, /**< Idle priority */ @@ -35,42 +41,85 @@ typedef enum { (FURI_CONFIG_THREAD_MAX_PRIORITIES - 1), /**< Deferred ISR (highest possible) */ } FuriThreadPriority; -/** FuriThread anonymous structure */ +/** + * @brief FuriThread opaque type. + */ typedef struct FuriThread FuriThread; -/** FuriThreadId proxy type to OS low level functions */ +/** + * @brief Unique thread identifier type (used by the OS kernel). + */ typedef void* FuriThreadId; -/** FuriThreadCallback Your callback to run in new thread - * @warning never use osThreadExit in FuriThread +/** + * @brief Thread callback function pointer type. + * + * The function to be used as a thread callback MUST follow this signature. + * + * @param[in,out] context pointer to a user-specified object + * @return value to be used as the thread return code */ typedef int32_t (*FuriThreadCallback)(void* context); -/** Write to stdout callback - * @param data pointer to data - * @param size data size @warning your handler must consume everything +/** + * @brief Standard output callback function pointer type. + * + * The function to be used as a standard output callback MUST follow this signature. + * + * @warning The handler MUST process ALL of the provided data before returning. + * + * @param[in] data pointer to the data to be written to the standard out + * @param[in] size size of the data in bytes */ typedef void (*FuriThreadStdoutWriteCallback)(const char* data, size_t size); -/** FuriThread state change callback called upon thread state change - * @param state new thread state - * @param context callback context +/** + * @brief State change callback function pointer type. + * + * The function to be used as a state callback MUST follow this signature. + * + * @param[in] state identifier of the state the thread has transitioned to + * @param[in,out] context pointer to a user-specified object */ typedef void (*FuriThreadStateCallback)(FuriThreadState state, void* context); -/** Allocate FuriThread +/** + * @brief Create a FuriThread instance. * - * @return FuriThread instance + * @return pointer to the created FuriThread instance */ FuriThread* furi_thread_alloc(void); -/** Allocate FuriThread, shortcut version +/** + * @brief Create a FuriThread instance (service mode). + * + * Service threads are more memory efficient, but have + * the following limitations: + * + * - Cannot return from the callback + * - Cannot be joined or freed + * - Stack size cannot be altered + * + * @param[in] name human-readable thread name (can be NULL) + * @param[in] stack_size stack size in bytes (cannot be changed later) + * @param[in] callback pointer to a function to be executed in this thread + * @param[in] context pointer to a user-specified object (will be passed to the callback) + * @return pointer to the created FuriThread instance + */ +FuriThread* furi_thread_alloc_service( + const char* name, + uint32_t stack_size, + FuriThreadCallback callback, + void* context); + +/** + * @brief Create a FuriThread instance w/ extra parameters. * - * @param name - * @param stack_size - * @param callback - * @param context - * @return FuriThread* + * @param[in] name human-readable thread name (can be NULL) + * @param[in] stack_size stack size in bytes (can be changed later) + * @param[in] callback pointer to a function to be executed in this thread + * @param[in] context pointer to a user-specified object (will be passed to the callback) + * @return pointer to the created FuriThread instance */ FuriThread* furi_thread_alloc_ex( const char* name, @@ -78,261 +127,339 @@ FuriThread* furi_thread_alloc_ex( FuriThreadCallback callback, void* context); -/** Release FuriThread +/** + * @brief Delete a FuriThread instance. * - * @warning see furi_thread_join + * The thread MUST be stopped when calling this function. * - * @param thread FuriThread instance + * @warning see furi_thread_join for caveats on stopping a thread. + * + * @param[in,out] thread pointer to the FuriThread instance to be deleted */ void furi_thread_free(FuriThread* thread); -/** Set FuriThread name +/** + * @brief Set the name of a FuriThread instance. * - * @param thread FuriThread instance - * @param name string + * The thread MUST be stopped when calling this function. + * + * @param[in,out] thread pointer to the FuriThread instance to be modified + * @param[in] name human-readable thread name (can be NULL) */ void furi_thread_set_name(FuriThread* thread, const char* name); /** - * @brief Set FuriThread appid + * @brief Set the application ID of a FuriThread instance. + * + * The thread MUST be stopped when calling this function. + * * Technically, it is like a "process id", but it is not a system-wide unique identifier. * All threads spawned by the same app will have the same appid. * - * @param thread - * @param appid + * @param[in,out] thread pointer to the FuriThread instance to be modified + * @param[in] appid thread application ID (can be NULL) */ void furi_thread_set_appid(FuriThread* thread, const char* appid); -/** Mark thread as service - * The service cannot be stopped or removed, and cannot exit from the thread body - * - * @param thread - */ -void furi_thread_mark_as_service(FuriThread* thread); - -/** Set FuriThread stack size +/** + * @brief Set the stack size of a FuriThread instance. * - * @param thread FuriThread instance - * @param stack_size stack size in bytes + * The thread MUST be stopped when calling this function. Additionally, it is NOT possible + * to change the stack size of a service thread under any circumstances. + * + * @param[in,out] thread pointer to the FuriThread instance to be modified + * @param[in] stack_size stack size in bytes */ void furi_thread_set_stack_size(FuriThread* thread, size_t stack_size); -/** Set FuriThread callback +/** + * @brief Set the user callback function to be executed in a FuriThread. * - * @param thread FuriThread instance - * @param callback FuriThreadCallback, called upon thread run + * The thread MUST be stopped when calling this function. + * + * @param[in,out] thread pointer to the FuriThread instance to be modified + * @param[in] callback pointer to a user-specified function to be executed in this thread */ void furi_thread_set_callback(FuriThread* thread, FuriThreadCallback callback); -/** Set FuriThread context +/** + * @brief Set the callback function context. * - * @param thread FuriThread instance - * @param context pointer to context for thread callback + * The thread MUST be stopped when calling this function. + * + * @param[in,out] thread pointer to the FuriThread instance to be modified + * @param[in] context pointer to a user-specified object (will be passed to the callback, can be NULL) */ void furi_thread_set_context(FuriThread* thread, void* context); -/** Set FuriThread priority +/** + * @brief Set the priority of a FuriThread. * - * @param thread FuriThread instance - * @param priority FuriThreadPriority value + * The thread MUST be stopped when calling this function. + * + * @param[in,out] thread pointer to the FuriThread instance to be modified + * @param[in] priority priority level value */ void furi_thread_set_priority(FuriThread* thread, FuriThreadPriority priority); -/** Get FuriThread priority +/** + * @brief Get the priority of a FuriThread. * - * @param thread FuriThread instance - * @return FuriThreadPriority value + * @param[in] thread pointer to the FuriThread instance to be queried + * @return priority level value */ FuriThreadPriority furi_thread_get_priority(FuriThread* thread); -/** Set current thread priority +/** + * @brief Set the priority of the current FuriThread. * - * @param priority FuriThreadPriority value + * @param priority priority level value */ void furi_thread_set_current_priority(FuriThreadPriority priority); -/** Get current thread priority +/** + * @brief Get the priority of the current FuriThread. * - * @return FuriThreadPriority value + * @return priority level value */ FuriThreadPriority furi_thread_get_current_priority(void); -/** Set FuriThread state change callback +/** + * Set the callback function to be executed upon a state thransition of a FuriThread. * - * @param thread FuriThread instance - * @param callback state change callback + * The thread MUST be stopped when calling this function. + * + * @param[in,out] thread pointer to the FuriThread instance to be modified + * @param[in] callback pointer to a user-specified callback function */ void furi_thread_set_state_callback(FuriThread* thread, FuriThreadStateCallback callback); -/** Set FuriThread state change context +/** + * @brief Set the state change callback context. * - * @param thread FuriThread instance - * @param context pointer to context + * The thread MUST be stopped when calling this function. + * + * @param[in,out] thread pointer to the FuriThread instance to be modified + * @param[in] context pointer to a user-specified object (will be passed to the callback, can be NULL) */ void furi_thread_set_state_context(FuriThread* thread, void* context); -/** Get FuriThread state +/** + * @brief Get the state of a FuriThread isntance. * - * @param thread FuriThread instance - * - * @return thread state from FuriThreadState + * @param[in] thread pointer to the FuriThread instance to be queried + * @return thread state value */ FuriThreadState furi_thread_get_state(FuriThread* thread); -/** Start FuriThread +/** + * @brief Start a FuriThread instance. * - * @param thread FuriThread instance + * The thread MUST be stopped when calling this function. + * + * @param[in,out] thread pointer to the FuriThread instance to be started */ void furi_thread_start(FuriThread* thread); -/** Join FuriThread +/** + * @brief Wait for a FuriThread to exit. * - * @warning Use this method only when CPU is not busy(Idle task receives - * control), otherwise it will wait forever. + * The thread callback function must return in order for the FuriThread instance to become joinable. * - * @param thread FuriThread instance + * @warning Use this method only when the CPU is not busy (i.e. when the + * Idle task receives control), otherwise it will wait forever. * - * @return bool + * @param[in] thread pointer to the FuriThread instance to be joined + * @return always true */ bool furi_thread_join(FuriThread* thread); -/** Get FreeRTOS FuriThreadId for FuriThread instance +/** + * @brief Get the unique identifier of a FuriThread instance. * - * @param thread FuriThread instance - * - * @return FuriThreadId or NULL + * @param[in] thread pointer to the FuriThread instance to be queried + * @return unique identifier value or NULL if thread is not running */ FuriThreadId furi_thread_get_id(FuriThread* thread); -/** Enable heap tracing +/** + * @brief Enable heap usage tracing for a FuriThread. * - * @param thread FuriThread instance + * The thread MUST be stopped when calling this function. + * + * @param[in,out] thread pointer to the FuriThread instance to be modified */ void furi_thread_enable_heap_trace(FuriThread* thread); -/** Disable heap tracing +/** + * @brief Disable heap usage tracing for a FuriThread. * - * @param thread FuriThread instance + * The thread MUST be stopped when calling this function. + * + * @param[in,out] thread pointer to the FuriThread instance to be modified */ void furi_thread_disable_heap_trace(FuriThread* thread); -/** Get thread heap size +/** + * @brief Get heap usage by a FuriThread instance. * - * @param thread FuriThread instance + * The heap trace MUST be enabled before callgin this function. * - * @return size in bytes + * @param[in] thread pointer to the FuriThread instance to be queried + * @return heap usage in bytes */ size_t furi_thread_get_heap_size(FuriThread* thread); -/** Get thread return code +/** + * @brief Get the return code of a FuriThread instance. * - * @param thread FuriThread instance + * This value is equal to the return value of the thread callback function. * - * @return return code + * The thread MUST be stopped when calling this function. + * + * @param[in] thread pointer to the FuriThread instance to be queried + * @return return code value */ int32_t furi_thread_get_return_code(FuriThread* thread); -/** Thread related methods that doesn't involve FuriThread directly */ - -/** Get FreeRTOS FuriThreadId for current thread +/** + * @brief Get the unique identifier of the current FuriThread. * - * @return FuriThreadId or NULL + * @return unique identifier value */ FuriThreadId furi_thread_get_current_id(void); -/** Get FuriThread instance for current thread +/** + * @brief Get the FuriThread instance associated with the current thread. * - * @return pointer to FuriThread or NULL if this thread doesn't belongs to Furi + * @return pointer to a FuriThread instance or NULL if this thread does not belong to Furi */ FuriThread* furi_thread_get_current(void); -/** Return control to scheduler */ +/** + * @brief Return control to the scheduler. + */ void furi_thread_yield(void); +/** + * @brief Set the thread flags of a FuriThread. + * + * Can be used as a simple inter-thread communication mechanism. + * + * @param[in] thread_id unique identifier of the thread to be notified + * @param[in] flags bitmask of thread flags to set + * @return bitmask combination of previous and newly set flags + */ uint32_t furi_thread_flags_set(FuriThreadId thread_id, uint32_t flags); +/** + * @brief Clear the thread flags of the current FuriThread. + * + * @param[in] flags bitmask of thread flags to clear + * @return bitmask of thread flags before clearing + */ uint32_t furi_thread_flags_clear(uint32_t flags); +/** + * @brief Get the thread flags of the current FuriThread. + * @return current bitmask of thread flags + */ uint32_t furi_thread_flags_get(void); +/** + * @brief Wait for some thread flags to be set. + * + * @see FuriFlag for option and error flags. + * + * @param[in] flags bitmask of thread flags to wait for + * @param[in] options combination of option flags determining the behavior of the function + * @param[in] timeout maximum time to wait in milliseconds (use FuriWaitForever to wait forever) + * @return bitmask combination of received thread and error flags + */ uint32_t furi_thread_flags_wait(uint32_t flags, uint32_t options, uint32_t timeout); /** - * @brief Enumerate threads + * @brief Enumerate all threads. * - * @param thread_array array of FuriThreadId, where thread ids will be stored - * @param array_item_count array size - * @return uint32_t threads count + * @param[out] thread_array pointer to the output array (must be properly allocated) + * @param[in] array_item_count output array capacity in elements (NOT bytes) + * @return total thread count (array_item_count or less) */ uint32_t furi_thread_enumerate(FuriThreadId* thread_array, uint32_t array_item_count); /** - * @brief Get thread name + * @brief Get the name of a thread based on its unique identifier. * - * @param thread_id - * @return const char* name or NULL + * @param[in] thread_id unique identifier of the thread to be queried + * @return pointer to a zero-terminated string or NULL */ const char* furi_thread_get_name(FuriThreadId thread_id); /** - * @brief Get thread appid + * @brief Get the application id of a thread based on its unique identifier. * - * @param thread_id - * @return const char* appid + * @param[in] thread_id unique identifier of the thread to be queried + * @return pointer to a zero-terminated string */ const char* furi_thread_get_appid(FuriThreadId thread_id); /** - * @brief Get thread stack watermark + * @brief Get thread stack watermark. * - * @param thread_id - * @return uint32_t + * @param[in] thread_id unique identifier of the thread to be queried + * @return stack watermark value */ uint32_t furi_thread_get_stack_space(FuriThreadId thread_id); -/** Get STDOUT callback for thead +/** + * @brief Get the standard output callback for the current thead. * - * @return STDOUT callback + * @return pointer to the standard out callback function */ FuriThreadStdoutWriteCallback furi_thread_get_stdout_callback(void); -/** Set STDOUT callback for thread +/** Set standard output callback for the current thread. * - * @param callback callback or NULL to clear + * @param[in] callback pointer to the callback function or NULL to clear */ void furi_thread_set_stdout_callback(FuriThreadStdoutWriteCallback callback); -/** Write data to buffered STDOUT +/** Write data to buffered standard output. * - * @param data input data - * @param size input data size - * - * @return size_t written data size + * @param[in] data pointer to the data to be written + * @param[in] size data size in bytes + * @return number of bytes that was actually written */ size_t furi_thread_stdout_write(const char* data, size_t size); -/** Flush data to STDOUT +/** + * @brief Flush buffered data to standard output. * - * @return int32_t error code + * @return error code value */ int32_t furi_thread_stdout_flush(void); -/** Suspend thread +/** + * @brief Suspend a thread. + * + * Suspended threads are no more receiving any of the processor time. * - * @param thread_id thread id + * @param[in] thread_id unique identifier of the thread to be suspended */ void furi_thread_suspend(FuriThreadId thread_id); -/** Resume thread +/** + * @brief Resume a thread. * - * @param thread_id thread id + * @param[in] thread_id unique identifier of the thread to be resumed */ void furi_thread_resume(FuriThreadId thread_id); -/** Get thread suspended state +/** + * @brief Test if a thread is suspended. * - * @param thread_id thread id - * @return true if thread is suspended + * @param[in] thread_id unique identifier of the thread to be queried + * @return true if thread is suspended, false otherwise */ bool furi_thread_is_suspended(FuriThreadId thread_id); diff --git a/furi/core/timer.c b/furi/core/timer.c index 2e688dcc3..1ca56f0fa 100644 --- a/furi/core/timer.c +++ b/furi/core/timer.c @@ -1,61 +1,50 @@ #include "timer.h" #include "check.h" -#include "memmgr.h" #include "kernel.h" #include #include -typedef struct { - FuriTimerCallback func; - void* context; -} TimerCallback_t; +struct FuriTimer { + StaticTimer_t container; + FuriTimerCallback cb_func; + void* cb_context; + volatile bool can_be_removed; +}; + +// IMPORTANT: container MUST be the FIRST struct member +static_assert(offsetof(FuriTimer, container) == 0); static void TimerCallback(TimerHandle_t hTimer) { - TimerCallback_t* callb; - - /* Retrieve pointer to callback function and context */ - callb = (TimerCallback_t*)pvTimerGetTimerID(hTimer); - - /* Remove dynamic allocation flag */ - callb = (TimerCallback_t*)((uint32_t)callb & ~1U); - - if(callb != NULL) { - callb->func(callb->context); - } + FuriTimer* instance = pvTimerGetTimerID(hTimer); + furi_check(instance); + instance->cb_func(instance->cb_context); } FuriTimer* furi_timer_alloc(FuriTimerCallback func, FuriTimerType type, void* context) { furi_check((furi_kernel_is_irq_or_masked() == 0U) && (func != NULL)); - TimerHandle_t hTimer; - TimerCallback_t* callb; - UBaseType_t reload; + FuriTimer* instance = malloc(sizeof(FuriTimer)); - hTimer = NULL; + instance->cb_func = func; + instance->cb_context = context; - /* Dynamic memory allocation is available: if memory for callback and */ - /* its context is not provided, allocate it from dynamic memory pool */ - callb = (TimerCallback_t*)malloc(sizeof(TimerCallback_t)); + const UBaseType_t reload = (type == FuriTimerTypeOnce ? pdFALSE : pdTRUE); + const TimerHandle_t hTimer = xTimerCreateStatic( + NULL, portMAX_DELAY, reload, instance, TimerCallback, &instance->container); - callb->func = func; - callb->context = context; + furi_check(hTimer == (TimerHandle_t)instance); - if(type == FuriTimerTypeOnce) { - reload = pdFALSE; - } else { - reload = pdTRUE; - } + return instance; +} - /* Store callback memory dynamic allocation flag */ - callb = (TimerCallback_t*)((uint32_t)callb | 1U); - // TimerCallback function is always provided as a callback and is used to call application - // specified function with its context both stored in structure callb. - hTimer = xTimerCreate(NULL, portMAX_DELAY, reload, callb, TimerCallback); - furi_check(hTimer); +static void furi_timer_epilogue(void* context, uint32_t arg) { + furi_assert(context); + UNUSED(arg); - /* Return timer ID */ - return ((FuriTimer*)hTimer); + FuriTimer* instance = context; + + instance->can_be_removed = true; } void furi_timer_free(FuriTimer* instance) { @@ -63,26 +52,14 @@ void furi_timer_free(FuriTimer* instance) { furi_check(instance); TimerHandle_t hTimer = (TimerHandle_t)instance; - TimerCallback_t* callb; + furi_check(xTimerDelete(hTimer, portMAX_DELAY) == pdPASS); + furi_check(xTimerPendFunctionCall(furi_timer_epilogue, instance, 0, portMAX_DELAY) == pdPASS); - callb = (TimerCallback_t*)pvTimerGetTimerID(hTimer); - - if((uint32_t)callb & 1U) { - /* If callback memory was allocated, it is only safe to free it with - * the timer inactive. Send a stop command and wait for the timer to - * be in an inactive state. - */ - furi_check(xTimerStop(hTimer, portMAX_DELAY) == pdPASS); - while(furi_timer_is_running(instance)) furi_delay_tick(2); - - /* Callback memory was allocated from dynamic pool, clear flag */ - callb = (TimerCallback_t*)((uint32_t)callb & ~1U); - - /* Return allocated memory to dynamic pool */ - free(callb); + while(!instance->can_be_removed) { + furi_delay_tick(2); } - furi_check(xTimerDelete(hTimer, portMAX_DELAY) == pdPASS); + free(instance); } FuriStatus furi_timer_start(FuriTimer* instance, uint32_t ticks) { @@ -99,8 +76,7 @@ FuriStatus furi_timer_start(FuriTimer* instance, uint32_t ticks) { stat = FuriStatusErrorResource; } - /* Return execution status */ - return (stat); + return stat; } FuriStatus furi_timer_restart(FuriTimer* instance, uint32_t ticks) { @@ -118,8 +94,7 @@ FuriStatus furi_timer_restart(FuriTimer* instance, uint32_t ticks) { stat = FuriStatusErrorResource; } - /* Return execution status */ - return (stat); + return stat; } FuriStatus furi_timer_stop(FuriTimer* instance) { diff --git a/furi/core/timer.h b/furi/core/timer.h index f8f40c562..00056db18 100644 --- a/furi/core/timer.h +++ b/furi/core/timer.h @@ -1,6 +1,10 @@ +/** + * @file timer.h + * @brief Furi software Timer API. + */ #pragma once -#include "core/base.h" +#include "base.h" #ifdef __cplusplus extern "C" { @@ -13,7 +17,7 @@ typedef enum { FuriTimerTypePeriodic = 1 ///< Repeating timer. } FuriTimerType; -typedef void FuriTimer; +typedef struct FuriTimer FuriTimer; /** Allocate timer * diff --git a/furi/flipper.c b/furi/flipper.c index 6c7b9831a..581d86f0c 100644 --- a/furi/flipper.c +++ b/furi/flipper.c @@ -37,12 +37,11 @@ void flipper_init(void) { for(size_t i = 0; i < FLIPPER_SERVICES_COUNT; i++) { FURI_LOG_D(TAG, "Starting service %s", FLIPPER_SERVICES[i].name); - FuriThread* thread = furi_thread_alloc_ex( + FuriThread* thread = furi_thread_alloc_service( FLIPPER_SERVICES[i].name, FLIPPER_SERVICES[i].stack_size, FLIPPER_SERVICES[i].app, NULL); - furi_thread_mark_as_service(thread); furi_thread_set_appid(thread, FLIPPER_SERVICES[i].appid); furi_thread_start(thread); @@ -72,4 +71,4 @@ void vApplicationGetTimerTaskMemory( *tcb_ptr = &timer_task_tcb; *stack_ptr = timer_task_stack; *stack_size = configTIMER_TASK_STACK_DEPTH; -} \ No newline at end of file +} diff --git a/furi/furi.c b/furi/furi.c index 628c47ea2..dca674da5 100644 --- a/furi/furi.c +++ b/furi/furi.c @@ -1,5 +1,4 @@ #include "furi.h" -#include #include #include 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/lib/ble_profile/extra_services/hid_service.c b/lib/ble_profile/extra_services/hid_service.c index 92422d5d5..e46d2010c 100644 --- a/lib/ble_profile/extra_services/hid_service.c +++ b/lib/ble_profile/extra_services/hid_service.c @@ -1,5 +1,5 @@ #include "hid_service.h" -#include "app_common.h" +#include "app_common.h" // IWYU pragma: keep #include #include #include diff --git a/lib/drivers/lp5562.c b/lib/drivers/lp5562.c index e755d2d60..30a5b559a 100644 --- a/lib/drivers/lp5562.c +++ b/lib/drivers/lp5562.c @@ -3,8 +3,6 @@ #include "lp5562_reg.h" #include -#include - void lp5562_reset(FuriHalI2cBusHandle* handle) { Reg0D_Reset reg = {.value = 0xFF}; furi_hal_i2c_write_reg_8(handle, LP5562_ADDRESS, 0x0D, *(uint8_t*)®, LP5562_I2C_TIMEOUT); diff --git a/lib/flipper_application/application_assets.c b/lib/flipper_application/application_assets.c index ec3fc22ee..5fc250257 100644 --- a/lib/flipper_application/application_assets.c +++ b/lib/flipper_application/application_assets.c @@ -19,17 +19,13 @@ #define TAG "FapAssets" -#pragma pack(push, 1) - -typedef struct { +typedef struct FURI_PACKED { uint32_t magic; uint32_t version; uint32_t dirs_count; uint32_t files_count; } FlipperApplicationAssetsHeader; -#pragma pack(pop) - typedef enum { AssetsSignatureResultEqual, AssetsSignatureResultNotEqual, diff --git a/lib/flipper_application/flipper_application.c b/lib/flipper_application/flipper_application.c index 3c4a07f2f..a3509896f 100644 --- a/lib/flipper_application/flipper_application.c +++ b/lib/flipper_application/flipper_application.c @@ -18,7 +18,7 @@ struct FlipperApplication { /********************** Debugger access to loader state **********************/ -LIST_DEF(FlipperApplicationList, const FlipperApplication*, M_POD_OPLIST); +LIST_DEF(FlipperApplicationList, const FlipperApplication*, M_POD_OPLIST); // NOLINT FlipperApplicationList_t flipper_application_loaded_app_list = {0}; static bool flipper_application_loaded_app_list_initialized = false; @@ -277,8 +277,10 @@ static const char* preload_status_strings[] = { [FlipperApplicationPreloadStatusUnspecifiedError] = "Unknown error", [FlipperApplicationPreloadStatusInvalidFile] = "Invalid file", [FlipperApplicationPreloadStatusInvalidManifest] = "Invalid file manifest", - [FlipperApplicationPreloadStatusApiTooOld] = "Update Application to use with this Firmware (ApiTooOld)", - [FlipperApplicationPreloadStatusApiTooNew] = "Update Firmware to use with this Application (ApiTooNew)", + [FlipperApplicationPreloadStatusApiTooOld] = + "Update Application to use with this Firmware (ApiTooOld)", + [FlipperApplicationPreloadStatusApiTooNew] = + "Update Firmware to use with this Application (ApiTooNew)", [FlipperApplicationPreloadStatusTargetMismatch] = "Hardware target mismatch", }; @@ -286,7 +288,8 @@ static const char* load_status_strings[] = { [FlipperApplicationLoadStatusSuccess] = "Success", [FlipperApplicationLoadStatusUnspecifiedError] = "Unknown error", [FlipperApplicationLoadStatusNoFreeMemory] = "Out of memory", - [FlipperApplicationLoadStatusMissingImports] = "Update Firmware to use with this Application (MissingImports)", + [FlipperApplicationLoadStatusMissingImports] = + "Update Firmware to use with this Application (MissingImports)", }; const char* flipper_application_preload_status_to_string(FlipperApplicationPreloadStatus status) { diff --git a/lib/flipper_application/plugins/composite_resolver.c b/lib/flipper_application/plugins/composite_resolver.c index 03dcd95e7..78b611328 100644 --- a/lib/flipper_application/plugins/composite_resolver.c +++ b/lib/flipper_application/plugins/composite_resolver.c @@ -4,7 +4,7 @@ #include #include -LIST_DEF(ElfApiInterfaceList, const ElfApiInterface*, M_POD_OPLIST) +LIST_DEF(ElfApiInterfaceList, const ElfApiInterface*, M_POD_OPLIST) // NOLINT #define M_OPL_ElfApiInterfaceList_t() LIST_OPLIST(ElfApiInterfaceList, M_POD_OPLIST) struct CompositeApiResolver { diff --git a/lib/flipper_application/plugins/plugin_manager.c b/lib/flipper_application/plugins/plugin_manager.c index d0e294220..5caec162a 100644 --- a/lib/flipper_application/plugins/plugin_manager.c +++ b/lib/flipper_application/plugins/plugin_manager.c @@ -11,7 +11,7 @@ #define TAG "PluginManager" -ARRAY_DEF(FlipperApplicationList, FlipperApplication*, M_PTR_OPLIST) +ARRAY_DEF(FlipperApplicationList, FlipperApplication*, M_PTR_OPLIST) // NOLINT #define M_OPL_FlipperApplicationList_t() ARRAY_OPLIST(FlipperApplicationList, M_PTR_OPLIST) struct PluginManager { diff --git a/lib/lfrfid/protocols/protocol_pac_stanley.c b/lib/lfrfid/protocols/protocol_pac_stanley.c index 67bc3bf48..4eabe3659 100644 --- a/lib/lfrfid/protocols/protocol_pac_stanley.c +++ b/lib/lfrfid/protocols/protocol_pac_stanley.c @@ -96,7 +96,7 @@ bool protocol_pac_stanley_decoder_feed(ProtocolPACStanley* protocol, bool level, if(duration > PAC_STANLEY_MAX_TIME) return false; - uint8_t pulses = (uint8_t)round((float)duration / PAC_STANLEY_CYCLE_LENGTH); + uint8_t pulses = (uint8_t)roundf((float)duration / PAC_STANLEY_CYCLE_LENGTH); // Handle last stopbit & preamble (1 sb, 8 bit preamble) if(pulses >= 9 && !protocol->got_preamble) { diff --git a/lib/nfc/SConscript b/lib/nfc/SConscript index c6caffdb0..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"), @@ -41,11 +43,13 @@ env.Append( File("protocols/iso14443_4a/iso14443_4a_listener.h"), File("protocols/mf_ultralight/mf_ultralight_listener.h"), File("protocols/mf_classic/mf_classic_listener.h"), + File("protocols/felica/felica_listener.h"), # Sync API File("protocols/iso14443_3a/iso14443_3a_poller_sync.h"), File("protocols/mf_ultralight/mf_ultralight_poller_sync.h"), File("protocols/mf_classic/mf_classic_poller_sync.h"), File("protocols/st25tb/st25tb_poller_sync.h"), + File("protocols/felica/felica_poller_sync.h"), # Misc File("helpers/nfc_util.h"), File("helpers/iso14443_crc.h"), diff --git a/lib/nfc/helpers/felica_crc.c b/lib/nfc/helpers/felica_crc.c index d5e0853a1..bba7cee34 100644 --- a/lib/nfc/helpers/felica_crc.c +++ b/lib/nfc/helpers/felica_crc.c @@ -6,6 +6,8 @@ #define FELICA_CRC_INIT (0x0000U) uint16_t felica_crc_calculate(const uint8_t* data, size_t length) { + furi_check(data); + uint16_t crc = FELICA_CRC_INIT; for(size_t i = 0; i < length; i++) { @@ -24,6 +26,7 @@ uint16_t felica_crc_calculate(const uint8_t* data, size_t length) { } void felica_crc_append(BitBuffer* buf) { + furi_check(buf); const uint8_t* data = bit_buffer_get_data(buf); const size_t data_size = bit_buffer_get_size_bytes(buf); @@ -32,6 +35,7 @@ void felica_crc_append(BitBuffer* buf) { } bool felica_crc_check(const BitBuffer* buf) { + furi_check(buf); const size_t data_size = bit_buffer_get_size_bytes(buf); if(data_size <= FELICA_CRC_SIZE) return false; @@ -45,6 +49,7 @@ bool felica_crc_check(const BitBuffer* buf) { } void felica_crc_trim(BitBuffer* buf) { + furi_check(buf); const size_t data_size = bit_buffer_get_size_bytes(buf); furi_assert(data_size > FELICA_CRC_SIZE); diff --git a/lib/nfc/nfc_mock.c b/lib/nfc/nfc_mock.c index df0e009e7..a17f0e104 100644 --- a/lib/nfc/nfc_mock.c +++ b/lib/nfc/nfc_mock.c @@ -3,6 +3,9 @@ #include #include #include +#include +#include +#include #include @@ -50,11 +53,31 @@ typedef struct { Iso14443_3aSelResp sel_resp[2]; } Iso14443_3aColResData; +typedef struct { + uint8_t length; + uint8_t polling_cmd; + uint16_t system_code; + uint8_t request_code; + uint8_t time_slot; +} FelicaPollingRequest; + +typedef struct { + uint8_t code; + FelicaIDm idm; + FelicaPMm pmm; +} FelicaSensfResData; + +typedef struct { + uint16_t system_code; + FelicaSensfResData sens_res; +} FelicaPTMemory; + struct Nfc { NfcState state; Iso14443_3aColResStatus col_res_status; Iso14443_3aColResData col_res_data; + FelicaPTMemory pt_memory; bool software_col_res_required; NfcEventCallback callback; @@ -243,6 +266,21 @@ static void nfc_worker_listener_pass_col_res(Nfc* instance, uint8_t* rx_data, ui NfcEvent event = {.type = NfcEventTypeListenerActivated}; instance->callback(event, instance->context); + processed = true; + } + } else if(rx_bits == 8 * 8) { + FelicaPollingRequest* request = (FelicaPollingRequest*)rx_data; + if(request->system_code == instance->pt_memory.system_code) { + uint8_t response_size = sizeof(FelicaSensfResData) + 1; + bit_buffer_reset(tx_buffer); + bit_buffer_append_byte(tx_buffer, response_size); + bit_buffer_append_bytes( + tx_buffer, (uint8_t*)&instance->pt_memory.sens_res, sizeof(FelicaSensfResData)); + felica_crc_append(tx_buffer); + nfc_listener_tx(instance, tx_buffer); + instance->col_res_status = Iso14443_3aColResStatusDone; + NfcEvent event = {.type = NfcEventTypeListenerActivated}; + instance->callback(event, instance->context); processed = true; } } @@ -470,6 +508,12 @@ NfcError nfc_felica_listener_set_sensf_res_data( furi_assert(idm_len == 8); furi_assert(pmm_len == 8); + instance->pt_memory.system_code = 0xFFFF; + instance->pt_memory.sens_res.code = 0x01; + instance->software_col_res_required = true; + memcpy(instance->pt_memory.sens_res.idm.data, idm, idm_len); + memcpy(instance->pt_memory.sens_res.pmm.data, pmm, pmm_len); + return NfcErrorNone; } diff --git a/lib/nfc/protocols/felica/felica.c b/lib/nfc/protocols/felica/felica.c index bc47e0642..89e610f86 100644 --- a/lib/nfc/protocols/felica/felica.c +++ b/lib/nfc/protocols/felica/felica.c @@ -298,15 +298,33 @@ bool felica_check_mac( furi_check(blocks); furi_check(data); - uint8_t first_block[8]; uint8_t mac[8]; - felica_prepare_first_block(FelicaMACTypeRead, blocks, block_count, first_block); + felica_calculate_mac_read(ctx, session_key, rc, blocks, block_count, data, mac); + uint8_t mac_offset = FELICA_DATA_BLOCK_SIZE * (block_count - 1); + uint8_t* mac_ptr = data + mac_offset; + return !memcmp(mac, mac_ptr, 8); +} + +void felica_calculate_mac_read( + mbedtls_des3_context* ctx, + const uint8_t* session_key, + const uint8_t* rc, + const uint8_t* blocks, + const uint8_t block_count, + const uint8_t* data, + uint8_t* mac) { + furi_check(ctx); + furi_check(session_key); + furi_check(rc); + furi_check(blocks); + furi_check(data); + furi_check(mac); + + uint8_t first_block[8]; + felica_prepare_first_block(FelicaMACTypeRead, blocks, block_count, first_block); uint8_t data_size_without_mac = FELICA_DATA_BLOCK_SIZE * (block_count - 1); felica_calculate_mac(ctx, session_key, rc, first_block, data, data_size_without_mac, mac); - - uint8_t* mac_ptr = data + data_size_without_mac; - return !memcmp(mac, mac_ptr, 8); } void felica_calculate_mac_write( diff --git a/lib/nfc/protocols/felica/felica.h b/lib/nfc/protocols/felica/felica.h index d032943d3..17229c150 100644 --- a/lib/nfc/protocols/felica/felica.h +++ b/lib/nfc/protocols/felica/felica.h @@ -12,7 +12,13 @@ extern "C" { #define FELICA_PMM_SIZE (8U) #define FELICA_DATA_BLOCK_SIZE (16U) -#define FELICA_BLOCKS_TOTAL_COUNT (27U) +#define FELICA_CMD_READ_WITHOUT_ENCRYPTION (0x06U) +#define FELICA_CMD_WRITE_WITHOUT_ENCRYPTION (0x08U) + +#define FELICA_SERVICE_RW_ACCESS (0x0009U) +#define FELICA_SERVICE_RO_ACCESS (0x000BU) + +#define FELICA_BLOCKS_TOTAL_COUNT (28U) #define FELICA_BLOCK_INDEX_REG (0x0EU) #define FELICA_BLOCK_INDEX_RC (0x80U) #define FELICA_BLOCK_INDEX_MAC (0x81U) @@ -54,6 +60,10 @@ typedef enum { FelicaErrorTimeout, } FelicaError; +typedef struct { + uint8_t data[FELICA_DATA_BLOCK_SIZE]; +} FelicaBlockData; + /** @brief Separate type for card key block. Used in authentication process */ typedef struct { uint8_t data[FELICA_DATA_BLOCK_SIZE]; @@ -76,6 +86,22 @@ typedef struct { FelicaAuthenticationStatus auth_status; /**< Authentication status*/ } FelicaAuthenticationContext; +/** + * @brief Stucture for holding Felica session key which is calculated from rc and ck. +*/ +typedef struct { + uint8_t data[FELICA_DATA_BLOCK_SIZE]; +} FelicaSessionKey; + +/** + * @brief Structure used to hold authentication related fields. +*/ +typedef struct { + mbedtls_des3_context des_context; /**< Context for mbedtls des functions. */ + FelicaSessionKey session_key; /**< Calculated session key. */ + FelicaAuthenticationContext context; /**< Public auth context provided to upper levels. */ +} FelicaAuthentication; + /** @brief Felica ID block */ typedef struct { uint8_t data[FELICA_IDM_SIZE]; @@ -128,6 +154,51 @@ typedef struct { FelicaFSUnion data; } FelicaData; +#pragma pack(push, 1) +typedef struct { + uint8_t code; + FelicaIDm idm; + uint8_t service_num; + uint16_t service_code; + uint8_t block_count; +} FelicaCommandHeader; +#pragma pack(pop) + +typedef struct { + uint8_t length; + uint8_t response_code; + FelicaIDm idm; + uint8_t SF1; + uint8_t SF2; +} FelicaCommandResponseHeader; + +typedef struct { + uint8_t service_code : 4; + uint8_t access_mode : 3; + uint8_t length : 1; + uint8_t block_number; +} FelicaBlockListElement; + +typedef struct { + uint8_t length; + uint8_t response_code; + FelicaIDm idm; + uint8_t SF1; + uint8_t SF2; + uint8_t block_count; + uint8_t data[]; +} FelicaPollerReadCommandResponse; + +typedef struct { + FelicaCommandResponseHeader header; + uint8_t block_count; + uint8_t data[]; +} FelicaListenerReadCommandResponse; + +typedef FelicaCommandResponseHeader FelicaListenerWriteCommandResponse; + +typedef FelicaCommandResponseHeader FelicaPollerWriteCommandResponse; + extern const NfcDeviceBase nfc_device_felica; FelicaData* felica_alloc(void); @@ -168,6 +239,15 @@ bool felica_check_mac( const uint8_t block_count, uint8_t* data); +void felica_calculate_mac_read( + mbedtls_des3_context* ctx, + const uint8_t* session_key, + const uint8_t* rc, + const uint8_t* blocks, + const uint8_t block_count, + const uint8_t* data, + uint8_t* mac); + void felica_calculate_mac_write( mbedtls_des3_context* ctx, const uint8_t* session_key, diff --git a/lib/nfc/protocols/felica/felica_listener.c b/lib/nfc/protocols/felica/felica_listener.c index 4e6c05785..90215eced 100644 --- a/lib/nfc/protocols/felica/felica_listener.c +++ b/lib/nfc/protocols/felica/felica_listener.c @@ -1,9 +1,14 @@ #include "felica_listener_i.h" #include "nfc/protocols/nfc_listener_base.h" +#include +#include -#define FELICA_LISTENER_MAX_BUFFER_SIZE (64) -#define TAG "Felica" +#define FELICA_LISTENER_MAX_BUFFER_SIZE (128) +#define FELICA_LISTENER_RESPONSE_CODE_READ (0x07) +#define FELICA_LISTENER_RESPONSE_CODE_WRITE (0x09) + +#define TAG "FelicaListener" FelicaListener* felica_listener_alloc(Nfc* nfc, FelicaData* data) { furi_assert(nfc); @@ -15,8 +20,11 @@ FelicaListener* felica_listener_alloc(Nfc* nfc, FelicaData* data) { instance->tx_buffer = bit_buffer_alloc(FELICA_LISTENER_MAX_BUFFER_SIZE); instance->rx_buffer = bit_buffer_alloc(FELICA_LISTENER_MAX_BUFFER_SIZE); + mbedtls_des3_init(&instance->auth.des_context); nfc_set_fdt_listen_fc(instance->nfc, FELICA_FDT_LISTEN_FC); + memcpy(instance->mc_shadow.data, instance->data->data.fs.mc.data, FELICA_DATA_BLOCK_SIZE); + instance->data->data.fs.state.data[0] = 0; nfc_config(instance->nfc, NfcModeListener, NfcTechFelica); nfc_felica_listener_set_sensf_res_data( nfc, data->idm.data, sizeof(data->idm), data->pmm.data, sizeof(data->pmm)); @@ -49,6 +57,99 @@ const FelicaData* felica_listener_get_data(const FelicaListener* instance) { return instance->data; } +static FelicaError felica_listener_command_handler_read( + FelicaListener* instance, + const FelicaListenerGenericRequest* const generic_request) { + const FelicaListenerReadRequest* request = (FelicaListenerReadRequest*)generic_request; + FURI_LOG_D(TAG, "Read cmd"); + + FelicaListenerReadCommandResponse* resp = malloc( + sizeof(FelicaCommandResponseHeader) + 1 + + FELICA_LISTENER_READ_BLOCK_COUNT_MAX * FELICA_DATA_BLOCK_SIZE); + + resp->header.response_code = FELICA_LISTENER_RESPONSE_CODE_READ; + resp->header.idm = request->base.header.idm; + resp->header.length = sizeof(FelicaCommandResponseHeader); + + if(felica_listener_validate_read_request_and_set_sf(instance, request, &resp->header)) { + resp->block_count = request->base.header.block_count; + resp->header.length++; + } else { + resp->block_count = 0; + } + + instance->mac_calc_start = 0; + memset(instance->requested_blocks, 0, sizeof(instance->requested_blocks)); + const FelicaBlockListElement* item = + felica_listener_block_list_item_get_first(instance, request); + for(uint8_t i = 0; i < resp->block_count; i++) { + instance->requested_blocks[i] = item->block_number; + FelicaCommanReadBlockHandler handler = + felica_listener_get_read_block_handler(item->block_number); + + handler(instance, item->block_number, i, resp); + + item = felica_listener_block_list_item_get_next(instance, item); + } + + bit_buffer_reset(instance->tx_buffer); + bit_buffer_append_bytes(instance->tx_buffer, (uint8_t*)resp, resp->header.length); + free(resp); + + return felica_listener_frame_exchange(instance, instance->tx_buffer); +} + +static FelicaError felica_listener_command_handler_write( + FelicaListener* instance, + const FelicaListenerGenericRequest* const generic_request) { + FURI_LOG_D(TAG, "Write cmd"); + + const FelicaListenerWriteRequest* request = (FelicaListenerWriteRequest*)generic_request; + const FelicaListenerWriteBlockData* data_ptr = + felica_listener_get_write_request_data_pointer(instance, generic_request); + + FelicaListenerWriteCommandResponse* resp = malloc(sizeof(FelicaListenerWriteCommandResponse)); + + resp->response_code = FELICA_LISTENER_RESPONSE_CODE_WRITE; + resp->idm = request->base.header.idm; + resp->length = sizeof(FelicaListenerWriteCommandResponse); + + if(felica_listener_validate_write_request_and_set_sf(instance, request, data_ptr, resp)) { + const FelicaBlockListElement* item = + felica_listener_block_list_item_get_first(instance, request); + for(uint8_t i = 0; i < request->base.header.block_count; i++) { + FelicaCommandWriteBlockHandler handler = + felica_listener_get_write_block_handler(item->block_number); + + handler(instance, item->block_number, &data_ptr->blocks[i]); + + item = felica_listener_block_list_item_get_next(instance, item); + } + felica_wcnt_increment(instance->data); + } + + bit_buffer_reset(instance->tx_buffer); + bit_buffer_append_bytes(instance->tx_buffer, (uint8_t*)resp, resp->length); + free(resp); + + return felica_listener_frame_exchange(instance, instance->tx_buffer); +} + +static FelicaError felica_listener_process_request( + FelicaListener* instance, + const FelicaListenerGenericRequest* generic_request) { + const uint8_t cmd_code = generic_request->header.code; + switch(cmd_code) { + case FELICA_CMD_READ_WITHOUT_ENCRYPTION: + return felica_listener_command_handler_read(instance, generic_request); + case FELICA_CMD_WRITE_WITHOUT_ENCRYPTION: + return felica_listener_command_handler_write(instance, generic_request); + default: + FURI_LOG_E(TAG, "FeliCa incorrect command"); + return FelicaErrorNotPresent; + } +} + NfcCommand felica_listener_run(NfcGenericEvent event, void* context) { furi_assert(context); furi_assert(event.protocol == NfcProtocolInvalid); @@ -58,14 +159,44 @@ NfcCommand felica_listener_run(NfcGenericEvent event, void* context) { NfcEvent* nfc_event = event.event_data; NfcCommand command = NfcCommandContinue; - if(nfc_event->type == NfcEventTypeListenerActivated) { + if(nfc_event->type == NfcEventTypeFieldOn) { + FURI_LOG_D(TAG, "Field On"); + } else if(nfc_event->type == NfcEventTypeListenerActivated) { instance->state = Felica_ListenerStateActivated; FURI_LOG_D(TAG, "Activated"); } else if(nfc_event->type == NfcEventTypeFieldOff) { instance->state = Felica_ListenerStateIdle; FURI_LOG_D(TAG, "Field Off"); + felica_listener_reset(instance); } else if(nfc_event->type == NfcEventTypeRxEnd) { FURI_LOG_D(TAG, "Rx Done"); + do { + if(!felica_crc_check(nfc_event->data.buffer)) { + FURI_LOG_E(TAG, "Wrong CRC"); + break; + } + + FelicaListenerGenericRequest* request = + (FelicaListenerGenericRequest*)bit_buffer_get_data(nfc_event->data.buffer); + + uint8_t size = bit_buffer_get_size_bytes(nfc_event->data.buffer) - 2; + if((request->length != size) || + (!felica_listener_check_block_list_size(instance, request))) { + FURI_LOG_E(TAG, "Wrong request length"); + break; + } + + if(!felica_listener_check_idm(instance, &request->header.idm)) { + FURI_LOG_E(TAG, "Wrong IDm"); + break; + } + + FelicaError error = felica_listener_process_request(instance, request); + if(error != FelicaErrorNone) { + FURI_LOG_E(TAG, "Processing error: %2X", error); + } + } while(false); + bit_buffer_reset(nfc_event->data.buffer); } return command; } diff --git a/lib/nfc/protocols/felica/felica_listener_i.c b/lib/nfc/protocols/felica/felica_listener_i.c new file mode 100644 index 000000000..caab11778 --- /dev/null +++ b/lib/nfc/protocols/felica/felica_listener_i.c @@ -0,0 +1,738 @@ +#include "felica_listener_i.h" + +#include + +#define FELICA_WCNT_MC2_FF_MAX_VALUE (0x00FFFFFFU) +#define FELICA_WCNT_MC2_00_MAX_VALUE (0x00FFFE00U) +#define FELICA_WCNT_MC2_00_WARNING_BEGIN_VALUE (0x00001027U) +#define FELICA_WCNT_MC2_00_WARNING_END_VALUE (0x00FFFDFFU) + +#define FELICA_MC_SP_REG_ALL_RW_BYTES_0_1 (0U) +#define FELICA_MC_ALL_BYTE (2U) +#define FELICA_MC_SYS_OP (3U) +#define FELICA_MC_RF_PRM (4U) +#define FELICA_MC_CKCKV_W_MAC_A (5U) +#define FELICA_MC_SP_REG_R_RESTR_BYTES_6_7 (6U) +#define FELICA_MC_SP_REG_W_RESTR_BYTES_8_9 (8U) +#define FELICA_MC_SP_REG_W_MAC_A_BYTES_10_11 (10U) +#define FELICA_MC_STATE_W_MAC_A (12U) +#define FELICA_MC_RESERVED_13 (13U) +#define FELICA_MC_RESERVED_14 (14U) +#define FELICA_MC_RESERVED_15 (15U) + +#define FELICA_MC_BYTE_GET(data, byte) (data->data.fs.mc.data[byte]) +#define FELICA_SYSTEM_BLOCK_RO_ACCESS(data) (FELICA_MC_BYTE_GET(data, FELICA_MC_ALL_BYTE) == 0x00) +#define FELICA_SYSTEM_BLOCK_RW_ACCESS(data) (FELICA_MC_BYTE_GET(data, FELICA_MC_ALL_BYTE) == 0xFF) + +#define FELICA_LISTENER_BLOCK_LIST_ITEM_SIZE_MIN ((uint8_t)sizeof(FelicaBlockListElement)) +#define FELICA_LISTENER_BLOCK_LIST_ITEM_SIZE(item) \ + ((item->length == 1) ? FELICA_LISTENER_BLOCK_LIST_ITEM_SIZE_MIN : \ + (FELICA_LISTENER_BLOCK_LIST_ITEM_SIZE_MIN + 1)) + +static uint32_t felica_wcnt_get_max_value(const FelicaData* data) { + if(!FELICA_SYSTEM_BLOCK_RO_ACCESS(data) && !FELICA_SYSTEM_BLOCK_RW_ACCESS(data)) { + furi_crash("MC[2] reserved values are forbidden"); + } + return (FELICA_SYSTEM_BLOCK_RW_ACCESS(data)) ? FELICA_WCNT_MC2_FF_MAX_VALUE : + FELICA_WCNT_MC2_00_MAX_VALUE; +} + +static bool felica_wcnt_check_warning_boundary(const FelicaData* data) { + const uint32_t* wcnt_ptr = (uint32_t*)data->data.fs.wcnt.data; + return ( + FELICA_SYSTEM_BLOCK_RO_ACCESS(data) && + ((*wcnt_ptr > FELICA_WCNT_MC2_00_WARNING_BEGIN_VALUE) && + (*wcnt_ptr < FELICA_WCNT_MC2_00_WARNING_END_VALUE))); +} + +static bool felica_wcnt_check_error_boundary(const FelicaData* data) { + const uint32_t wcnt_max = felica_wcnt_get_max_value(data); + const uint32_t* wcnt_ptr = (const uint32_t*)data->data.fs.wcnt.data; + return *wcnt_ptr != wcnt_max; +} + +void felica_wcnt_increment(FelicaData* data) { + furi_assert(data); + + const uint32_t wcnt_max = felica_wcnt_get_max_value(data); + uint32_t* wcnt_ptr = (uint32_t*)data->data.fs.wcnt.data; + if(*wcnt_ptr < wcnt_max) { + *wcnt_ptr += 1; + } +} + +static void felica_wcnt_post_process(FelicaData* data) { + uint32_t* wcnt_ptr = (uint32_t*)data->data.fs.wcnt.data; + + if(FELICA_SYSTEM_BLOCK_RO_ACCESS(data) && (*wcnt_ptr > FELICA_WCNT_MC2_00_MAX_VALUE)) { + *wcnt_ptr = 0; + } +} + +static bool + felica_listener_block_list_item_validate_block_number(const FelicaBlockListElement* item) { + bool valid = true; + if(item->length == 0) { + uint8_t D2_block_number = *(&item->block_number + 1); + valid = D2_block_number == 0; + } + return valid; +} + +static const FelicaBlockListElement* felica_listener_block_list_iterate( + FelicaListener* instance, + const FelicaBlockListElement* prev_item, + const uint8_t item_size) { + FelicaBlockListElement* next_item = NULL; + if(instance->request_size_buf >= FELICA_LISTENER_BLOCK_LIST_ITEM_SIZE_MIN) { + next_item = (FelicaBlockListElement*)((uint8_t*)prev_item + item_size); + + uint8_t next_item_size = FELICA_LISTENER_BLOCK_LIST_ITEM_SIZE(next_item); + + if(instance->request_size_buf < next_item_size) { + next_item = NULL; + instance->request_size_buf = 0; + } else { + instance->request_size_buf -= next_item_size; + } + } + + return next_item; +} + +const FelicaBlockListElement* felica_listener_block_list_item_get_first( + FelicaListener* instance, + const FelicaListenerRequest* request) { + furi_assert(instance); + furi_assert(request); + + instance->request_size_buf = request->base.length - sizeof(FelicaListenerGenericRequest); + return felica_listener_block_list_iterate(instance, request->list, 0); +} + +const FelicaBlockListElement* felica_listener_block_list_item_get_next( + FelicaListener* instance, + const FelicaBlockListElement* prev_item) { + furi_assert(instance); + furi_assert(prev_item); + + return felica_listener_block_list_iterate( + instance, prev_item, FELICA_LISTENER_BLOCK_LIST_ITEM_SIZE(prev_item)); +} + +const FelicaListenerWriteBlockData* felica_listener_get_write_request_data_pointer( + const FelicaListener* const instance, + const FelicaListenerGenericRequest* const generic_request) { + furi_assert(instance); + furi_assert(generic_request); + + return (const FelicaListenerWriteBlockData*)((uint8_t*)generic_request + + sizeof(FelicaListenerGenericRequest) + + instance->block_list_size); +} + +static bool felica_listener_check_write_request_data_size( + const FelicaListener* const instance, + const FelicaListenerRequest* request, + const uint8_t fact_item_cnt) { + uint8_t possible_data_size = fact_item_cnt * FELICA_DATA_BLOCK_SIZE; + uint8_t fact_data_size = + request->base.length - sizeof(FelicaListenerGenericRequest) - instance->block_list_size; + return (possible_data_size <= fact_data_size); +} + +static bool felica_listener_test_block_list_size_bounds(const FelicaListenerGenericRequest* req) { + bool valid = false; + if(req->header.code == FELICA_CMD_READ_WITHOUT_ENCRYPTION) { + valid = + (req->header.block_count >= FELICA_LISTENER_READ_BLOCK_COUNT_MIN && + req->header.block_count <= FELICA_LISTENER_READ_BLOCK_COUNT_MAX); + } else if(req->header.code == FELICA_CMD_WRITE_WITHOUT_ENCRYPTION) { + valid = + (req->header.block_count >= FELICA_LISTENER_WRITE_BLOCK_COUNT_MIN && + req->header.block_count <= FELICA_LISTENER_WRITE_BLOCK_COUNT_MAX); + } + + return valid; +} + +static uint8_t felica_listener_get_block_list_item_count_size( + FelicaListener* instance, + FelicaListenerRequest* request) { + uint8_t list_size = 0; + uint8_t item_cnt = 0; + + uint8_t max_item_cnt = (request->base.header.code == FELICA_CMD_READ_WITHOUT_ENCRYPTION) ? + FELICA_LISTENER_READ_BLOCK_COUNT_MAX : + FELICA_LISTENER_WRITE_BLOCK_COUNT_MAX; + + const FelicaBlockListElement* item = + felica_listener_block_list_item_get_first(instance, request); + + while((item != NULL) && (item_cnt < max_item_cnt)) { + item_cnt++; + + if(request->base.header.code == FELICA_CMD_WRITE_WITHOUT_ENCRYPTION) { + if(instance->request_size_buf >= FELICA_DATA_BLOCK_SIZE) { + instance->request_size_buf -= FELICA_DATA_BLOCK_SIZE; + } else { + instance->request_size_buf = 0; + } + } + + list_size += FELICA_LISTENER_BLOCK_LIST_ITEM_SIZE(item); + item = felica_listener_block_list_item_get_next(instance, item); + } + + instance->block_list_size = list_size; + return item_cnt; +} + +bool felica_listener_check_block_list_size( + FelicaListener* instance, + FelicaListenerGenericRequest* req) { + furi_assert(instance); + furi_assert(req); + + FelicaListenerRequest* request = (FelicaListenerRequest*)req; + bool valid = true; + + uint8_t item_cnt = felica_listener_get_block_list_item_count_size(instance, request); + valid = (item_cnt > 0); + + if(felica_listener_test_block_list_size_bounds(req) && + (item_cnt < request->base.header.block_count)) { + valid = false; + } + + if(valid && request->base.header.code == FELICA_CMD_WRITE_WITHOUT_ENCRYPTION) { + valid &= felica_listener_check_write_request_data_size(instance, request, item_cnt); + } + + return valid; +} + +bool felica_listener_check_idm(const FelicaListener* instance, const FelicaIDm* request_idm) { + furi_assert(instance); + furi_assert(request_idm); + + const FelicaIDm* idm = &instance->data->idm; + return memcmp(idm->data, request_idm->data, 8) == 0; +} + +void felica_listener_reset(FelicaListener* instance) { + furi_assert(instance); + + instance->auth.context.auth_status.internal = false; + instance->auth.context.auth_status.external = false; + instance->data->data.fs.state.data[0] = 0; + instance->rc_written = false; + memset(instance->auth.session_key.data, 0, FELICA_DATA_BLOCK_SIZE); + + memcpy(instance->data->data.fs.mc.data, instance->mc_shadow.data, FELICA_DATA_BLOCK_SIZE); + + felica_wcnt_post_process(instance->data); +} + +static uint8_t felica_listener_get_block_index(uint8_t number) { + if(number >= FELICA_BLOCK_INDEX_RC && number < FELICA_BLOCK_INDEX_WCNT) { + return number - 0x80 + FELICA_BLOCK_INDEX_REG + 1; + } else if(number >= FELICA_BLOCK_INDEX_WCNT && number <= FELICA_BLOCK_INDEX_STATE) { + return number - 0x90 + 9 + FELICA_BLOCK_INDEX_REG + 1; + } else if(number == FELICA_BLOCK_INDEX_CRC_CHECK) { + return number - 0x90 + 9 + FELICA_BLOCK_INDEX_REG + 2; + } + + return number; +} + +static bool felica_block_exists(uint8_t number) { + return !( + (number > FELICA_BLOCK_INDEX_REG && number < FELICA_BLOCK_INDEX_RC) || + (number > FELICA_BLOCK_INDEX_MC && number < FELICA_BLOCK_INDEX_WCNT) || + (number > FELICA_BLOCK_INDEX_STATE && number < FELICA_BLOCK_INDEX_CRC_CHECK) || + (number > FELICA_BLOCK_INDEX_CRC_CHECK)); +} + +static bool + felica_get_mc_bit(const FelicaListener* instance, uint8_t byte_index, uint8_t bit_number) { + uint8_t* mc = instance->data->data.fs.mc.data; + + uint16_t flags = *((uint16_t*)&mc[byte_index]); + bool bit = (flags & (1 << bit_number)) != 0; + return bit; +} + +static bool felica_block_requires_auth( + const FelicaListener* instance, + uint8_t command, + uint8_t block_number) { + bool need_auth = false; + if(block_number <= FELICA_BLOCK_INDEX_REG) { + uint8_t mc_flag_index = command; + need_auth = felica_get_mc_bit(instance, mc_flag_index, block_number); + } + return need_auth; +} + +static bool felica_block_is_readonly(const FelicaListener* instance, uint8_t block_number) { + bool readonly = false; + if(block_number <= FELICA_BLOCK_INDEX_REG) { + readonly = !felica_get_mc_bit(instance, FELICA_MC_SP_REG_ALL_RW_BYTES_0_1, block_number); + } else if( + ((block_number == FELICA_BLOCK_INDEX_ID) || (block_number == FELICA_BLOCK_INDEX_SER_C)) || + (block_number >= FELICA_BLOCK_INDEX_CKV && block_number <= FELICA_BLOCK_INDEX_CK)) { + const FelicaData* data = instance->data; + readonly = FELICA_SYSTEM_BLOCK_RO_ACCESS(data); + } else if( + (block_number == FELICA_BLOCK_INDEX_MAC) || (block_number == FELICA_BLOCK_INDEX_WCNT) || + (block_number == FELICA_BLOCK_INDEX_D_ID) || + (block_number == FELICA_BLOCK_INDEX_CRC_CHECK)) { + readonly = true; + } + return readonly; +} + +static bool felica_block_requires_mac(const FelicaListener* instance, uint8_t block_number) { + bool need_mac = false; + if(block_number <= FELICA_BLOCK_INDEX_REG) { + need_mac = felica_get_mc_bit(instance, FELICA_MC_SP_REG_W_MAC_A_BYTES_10_11, block_number); + } else if(block_number == FELICA_BLOCK_INDEX_CK || block_number == FELICA_BLOCK_INDEX_CKV) { + need_mac = felica_get_mc_bit(instance, FELICA_MC_CKCKV_W_MAC_A, 0); + } else if(block_number == FELICA_BLOCK_INDEX_STATE) { + need_mac = felica_get_mc_bit(instance, FELICA_MC_STATE_W_MAC_A, 0); + } + return need_mac; +} + +static void felica_handler_read_block( + FelicaListener* instance, + const uint8_t block_number, + const uint8_t resp_data_index, + FelicaListenerReadCommandResponse* response) { + uint8_t num = felica_listener_get_block_index(block_number); + memcpy( + &response->data[resp_data_index * FELICA_DATA_BLOCK_SIZE], + &instance->data->data.dump[num * (FELICA_DATA_BLOCK_SIZE + 2) + 2], + FELICA_DATA_BLOCK_SIZE); + response->header.length += FELICA_DATA_BLOCK_SIZE; +} + +static void felica_handler_read_all_zeros( + FelicaListener* instance, + const uint8_t block_number, + const uint8_t resp_data_index, + FelicaListenerReadCommandResponse* response) { + UNUSED(instance); + UNUSED(block_number); + + memset(&response->data[resp_data_index * FELICA_DATA_BLOCK_SIZE], 0, FELICA_DATA_BLOCK_SIZE); + response->header.length += FELICA_DATA_BLOCK_SIZE; +} + +static void felica_handler_read_mac_a_block( + FelicaListener* instance, + const uint8_t block_number, + const uint8_t resp_data_index, + FelicaListenerReadCommandResponse* response) { + if((resp_data_index != response->block_count - 1) || (resp_data_index == 0)) { + felica_handler_read_all_zeros(instance, block_number, resp_data_index, response); + instance->mac_calc_start = resp_data_index + 1; + } else { + felica_calculate_mac_read( + &instance->auth.des_context, + instance->auth.session_key.data, + instance->data->data.fs.rc.data, + &instance->requested_blocks[instance->mac_calc_start], + response->block_count - instance->mac_calc_start, + &response->data[instance->mac_calc_start * FELICA_DATA_BLOCK_SIZE], + instance->data->data.fs.mac_a.data); + felica_handler_read_block(instance, block_number, resp_data_index, response); + } +} + +FelicaCommanReadBlockHandler felica_listener_get_read_block_handler(const uint8_t block_number) { + FelicaCommanReadBlockHandler handler = felica_handler_read_block; + + if(block_number == FELICA_BLOCK_INDEX_RC || block_number == FELICA_BLOCK_INDEX_CK) { + handler = felica_handler_read_all_zeros; + } else if(block_number == FELICA_BLOCK_INDEX_MAC_A) { + handler = felica_handler_read_mac_a_block; + } + + return handler; +} + +static bool felica_validate_read_block_list( + FelicaListener* instance, + const FelicaListenerReadRequest* const request, + FelicaCommandResponseHeader* response) { + uint8_t mac_a_pos = 0; + bool mac_a_present = false, mac_present = false; + + const FelicaBlockListElement* item = + felica_listener_block_list_item_get_first(instance, request); + for(uint8_t i = 0; i < request->base.header.block_count; i++) { + if(item->service_code != 0) { + response->SF1 = (1 << i); + response->SF2 = 0xA3; + return false; + } else if(item->access_mode != 0) { + response->SF1 = (1 << i); + response->SF2 = 0xA7; + return false; + } else if( + !felica_listener_block_list_item_validate_block_number(item) || + !felica_block_exists(item->block_number) || + (felica_block_is_readonly(instance, item->block_number) && + request->base.header.service_code != FELICA_SERVICE_RO_ACCESS)) { + response->SF1 = (1 << i); + response->SF2 = 0xA8; + return false; + } else if(item->block_number == FELICA_BLOCK_INDEX_MAC) { + mac_present = true; + } else if(item->block_number == FELICA_BLOCK_INDEX_MAC_A) { + if(!instance->rc_written) { + response->SF1 = (1 << i); + response->SF2 = 0xB2; + return false; + } + if(!mac_a_present) { + mac_a_present = true; + mac_a_pos = i; + } + } else if( + felica_block_requires_auth(instance, request->base.header.code, item->block_number) && + !instance->auth.context.auth_status.external) { + response->SF1 = (1 << i); + response->SF2 = 0xB1; + return false; + } + + if(mac_a_present && mac_present) { + response->SF1 = (1 << mac_a_pos); + response->SF2 = 0xB0; + return false; + } + + item = felica_listener_block_list_item_get_next(instance, item); + } + return true; +} + +bool felica_listener_validate_read_request_and_set_sf( + FelicaListener* instance, + const FelicaListenerReadRequest* const request, + FelicaCommandResponseHeader* resp_header) { + furi_assert(instance); + furi_assert(request); + furi_assert(resp_header); + + bool valid = false; + do { + if(request->base.header.service_num != 0x01) { + resp_header->SF1 = 0xFF; + resp_header->SF2 = 0xA1; + break; + } + if((request->base.header.code == FELICA_CMD_READ_WITHOUT_ENCRYPTION) && + (request->base.header.block_count < FELICA_LISTENER_READ_BLOCK_COUNT_MIN || + request->base.header.block_count > FELICA_LISTENER_READ_BLOCK_COUNT_MAX)) { + resp_header->SF1 = 0xFF; + resp_header->SF2 = 0xA2; + break; + } + + if(request->base.header.service_code != FELICA_SERVICE_RO_ACCESS && + request->base.header.service_code != FELICA_SERVICE_RW_ACCESS) { + resp_header->SF1 = 0x01; + resp_header->SF2 = 0xA6; + break; + } + + if(!felica_validate_read_block_list(instance, request, resp_header)) break; + + resp_header->SF1 = 0x00; + resp_header->SF2 = 0x00; + valid = true; + } while(false); + + return valid; +} + +static bool felica_validate_write_block_list( + FelicaListener* instance, + const FelicaListenerWriteRequest* const request, + const FelicaListenerWriteBlockData* const data, + FelicaListenerWriteCommandResponse* response) { + const FelicaBlockListElement* items[FELICA_LISTENER_WRITE_BLOCK_COUNT_MAX] = {}; + items[0] = felica_listener_block_list_item_get_first(instance, request); + items[1] = felica_listener_block_list_item_get_next(instance, items[0]); + + bool write_with_mac = false; + if(request->base.header.block_count == FELICA_LISTENER_WRITE_BLOCK_COUNT_MAX && + items[1]->block_number == FELICA_BLOCK_INDEX_MAC_A) { + write_with_mac = true; + } else if( + request->base.header.block_count < FELICA_LISTENER_WRITE_BLOCK_COUNT_MIN || + request->base.header.block_count > FELICA_LISTENER_WRITE_BLOCK_COUNT_MAX) { + response->SF1 = 0x02; + response->SF2 = 0xB2; + return false; + } + + for(uint8_t i = 0; i < request->base.header.block_count; i++) { + const FelicaBlockListElement* item = items[i]; + if(!felica_listener_block_list_item_validate_block_number(item) || + !felica_block_exists(item->block_number) || + ((i == 1) && (item->block_number != FELICA_BLOCK_INDEX_MAC_A))) { + response->SF1 = (1 << i); + response->SF2 = 0xA8; + return false; + } + + if(felica_block_is_readonly(instance, item->block_number) || + (felica_block_requires_mac(instance, item->block_number) && !write_with_mac) || + ((i == 0) && (item->block_number == FELICA_BLOCK_INDEX_MAC_A))) { + response->SF1 = 0x01; + response->SF2 = 0xA8; + return false; + } + + if(item->service_code != 0) { + response->SF1 = (1 << i); + response->SF2 = 0xA3; + return false; + } else if(item->access_mode != 0) { + response->SF1 = (1 << i); + response->SF2 = 0xA7; + return false; + } else if((i == 1) && (item->block_number == FELICA_BLOCK_INDEX_MAC_A)) { + uint8_t calculated_mac[8]; + felica_calculate_mac_write( + &instance->auth.des_context, + instance->auth.session_key.data, + instance->data->data.fs.rc.data, + data->blocks[1].data + 8, + data->blocks[0].data, + calculated_mac); + + if(!instance->rc_written || (memcmp(calculated_mac, data->blocks[1].data, 8) != 0) || + !felica_wcnt_check_error_boundary(instance->data)) { + response->SF1 = 0x02; + response->SF2 = 0xB2; + return false; + } + } else if( + felica_block_requires_auth(instance, request->base.header.code, item->block_number) && + !instance->auth.context.auth_status.external) { + response->SF1 = 0x01; + response->SF2 = 0xB1; + return false; + } + } + return true; +} + +bool felica_listener_validate_write_request_and_set_sf( + FelicaListener* instance, + const FelicaListenerWriteRequest* const request, + const FelicaListenerWriteBlockData* const data, + FelicaListenerWriteCommandResponse* response) { + furi_assert(instance); + furi_assert(request); + furi_assert(data); + furi_assert(response); + + bool valid = false; + do { + if(request->base.header.service_num != 0x01) { + response->SF1 = 0xFF; + response->SF2 = 0xA1; + break; + } + + if((request->base.header.code == FELICA_CMD_WRITE_WITHOUT_ENCRYPTION) && + (request->base.header.block_count < 0x01 || request->base.header.block_count > 0x02)) { + response->SF1 = 0xFF; + response->SF2 = 0xA2; + break; + } + + if(request->base.header.service_code != FELICA_SERVICE_RW_ACCESS) { + response->SF1 = 0x01; + response->SF2 = 0xA6; + break; + } + + if(!felica_validate_write_block_list(instance, request, data, response)) break; + + if(felica_wcnt_check_warning_boundary(instance->data)) { + response->SF1 = 0xFF; + response->SF2 = 0x71; + } else { + response->SF1 = 0x00; + response->SF2 = 0x00; + } + valid = true; + } while(false); + return valid; +} + +static void felica_handler_write_block( + FelicaListener* instance, + const uint8_t block_number, + const FelicaBlockData* data_block) { + uint8_t num = felica_listener_get_block_index(block_number); + + memcpy( + &instance->data->data.dump[num * (FELICA_DATA_BLOCK_SIZE + 2) + 2], + data_block->data, + FELICA_DATA_BLOCK_SIZE); +} + +static void felica_handler_write_rc_block( + FelicaListener* instance, + const uint8_t block_number, + const FelicaBlockData* data_block) { + felica_handler_write_block(instance, block_number, data_block); + + felica_calculate_session_key( + &instance->auth.des_context, + instance->data->data.fs.ck.data, + instance->data->data.fs.rc.data, + instance->auth.session_key.data); + instance->rc_written = true; +} + +static void felica_handler_write_reg_block( + FelicaListener* instance, + const uint8_t block_number, + const FelicaBlockData* data_block) { + UNUSED(block_number); + + typedef struct { + uint32_t A; + uint32_t B; + uint64_t C; + } FELICA_REG_BLOCK; + + FELICA_REG_BLOCK* Reg = (FELICA_REG_BLOCK*)instance->data->data.fs.reg.data; + FELICA_REG_BLOCK* RegNew = (FELICA_REG_BLOCK*)data_block->data; + + if(Reg->A >= RegNew->A) { + Reg->A = RegNew->A; + } + + if(Reg->B >= RegNew->B) { + Reg->B = RegNew->B; + } + + if((Reg->A >= RegNew->A) && (Reg->B >= RegNew->B)) { + Reg->C = RegNew->C; + } +} + +static void felica_handler_write_mc_block( + FelicaListener* instance, + const uint8_t block_number, + const FelicaBlockData* data_block) { + UNUSED(block_number); + + bool mc_bits_permission = felica_get_mc_bit(instance, FELICA_MC_SP_REG_ALL_RW_BYTES_0_1, 15); + const FelicaData* data = instance->data; + bool mc_system_block_permission = FELICA_SYSTEM_BLOCK_RW_ACCESS(data); + for(uint8_t i = 0; i < FELICA_DATA_BLOCK_SIZE; i++) { + if((i <= 1) && mc_bits_permission) { + instance->mc_shadow.data[i] &= data_block->data[i]; + } else if( + (i >= FELICA_MC_ALL_BYTE && i <= FELICA_MC_CKCKV_W_MAC_A) && + (mc_system_block_permission)) { + instance->mc_shadow.data[i] = data_block->data[i]; + } else if( + (i >= FELICA_MC_SP_REG_R_RESTR_BYTES_6_7 && i <= FELICA_MC_STATE_W_MAC_A) && + (mc_bits_permission)) { + instance->mc_shadow.data[i] |= data_block->data[i]; + } else if(i >= FELICA_MC_RESERVED_13) { + instance->mc_shadow.data[i] = 0; + } + } +} + +static void felica_handler_write_state_block( + FelicaListener* instance, + const uint8_t block_number, + const FelicaBlockData* data_block) { + felica_handler_write_block(instance, block_number, data_block); + bool state = instance->data->data.fs.state.data[0] == 0x01; + instance->auth.context.auth_status.external = state; + instance->auth.context.auth_status.internal = state; +} + +static void felica_handler_write_id_block( + FelicaListener* instance, + const uint8_t block_number, + const FelicaBlockData* data_block) { + UNUSED(block_number); + memcpy(&instance->data->data.fs.id.data[8], &data_block->data[8], 8); +} + +FelicaCommandWriteBlockHandler + felica_listener_get_write_block_handler(const uint8_t block_number) { + FelicaCommandWriteBlockHandler handler = felica_handler_write_block; + switch(block_number) { + case FELICA_BLOCK_INDEX_RC: + handler = felica_handler_write_rc_block; + break; + case FELICA_BLOCK_INDEX_REG: + handler = felica_handler_write_reg_block; + break; + case FELICA_BLOCK_INDEX_MC: + handler = felica_handler_write_mc_block; + break; + case FELICA_BLOCK_INDEX_STATE: + handler = felica_handler_write_state_block; + break; + case FELICA_BLOCK_INDEX_ID: + handler = felica_handler_write_id_block; + break; + default: + handler = felica_handler_write_block; + break; + } + return handler; +} + +static FelicaError felica_listener_process_error(NfcError error) { + switch(error) { + case NfcErrorNone: + return FelicaErrorNone; + case NfcErrorTimeout: + return FelicaErrorTimeout; + default: + return FelicaErrorNotPresent; + } +} + +FelicaError + felica_listener_frame_exchange(const FelicaListener* instance, const BitBuffer* tx_buffer) { + furi_assert(instance); + furi_assert(tx_buffer); + + const size_t tx_bytes = bit_buffer_get_size_bytes(tx_buffer); + furi_assert(tx_bytes <= bit_buffer_get_capacity_bytes(instance->tx_buffer) - FELICA_CRC_SIZE); + + felica_crc_append(instance->tx_buffer); + + FelicaError ret = FelicaErrorNone; + + do { + NfcError error = nfc_listener_tx(instance->nfc, instance->tx_buffer); + if(error != NfcErrorNone) { + ret = felica_listener_process_error(error); + break; + } + } while(false); + + return ret; +} \ No newline at end of file diff --git a/lib/nfc/protocols/felica/felica_listener_i.h b/lib/nfc/protocols/felica/felica_listener_i.h index 4fa25a162..43c03a608 100644 --- a/lib/nfc/protocols/felica/felica_listener_i.h +++ b/lib/nfc/protocols/felica/felica_listener_i.h @@ -2,15 +2,72 @@ #include +#define FELICA_LISTENER_READ_BLOCK_COUNT_MAX (4U) +#define FELICA_LISTENER_READ_BLOCK_COUNT_MIN (1U) +#define FELICA_LISTENER_WRITE_BLOCK_COUNT_MAX (2U) +#define FELICA_LISTENER_WRITE_BLOCK_COUNT_MIN (1U) + typedef enum { Felica_ListenerStateIdle, Felica_ListenerStateActivated, } FelicaListenerState; +/** Generic Felica request same for both read and write operations. */ +typedef struct { + uint8_t length; + FelicaCommandHeader header; +} FelicaListenerGenericRequest; + +/** Generic request but with list of requested elements. */ +typedef struct { + FelicaListenerGenericRequest base; + FelicaBlockListElement list[]; +} FelicaListenerRequest; + +typedef FelicaListenerRequest FelicaListenerReadRequest; +typedef FelicaListenerRequest FelicaListenerWriteRequest; + +/** Struct for write request data. + * + * All data are placed right after @ref FelicaListenerRequest data. + */ +typedef struct { + FelicaBlockData blocks[FELICA_LISTENER_WRITE_BLOCK_COUNT_MAX]; +} FelicaListenerWriteBlockData; + +/** Write command handler signature. + * + * Depending on requested block write behaviour can be different, therefore + * different handlers are used. + */ +typedef void (*FelicaCommandWriteBlockHandler)( + FelicaListener* instance, + const uint8_t block_number, + const FelicaBlockData* data_block); + +/** Read command handler signature. + * + * Depending on requested block read behaviour can be different, therefore + * different handlers are used. + */ +typedef void (*FelicaCommanReadBlockHandler)( + FelicaListener* instance, + const uint8_t block_number, + const uint8_t resp_data_index, + FelicaListenerReadCommandResponse* response); + struct FelicaListener { Nfc* nfc; FelicaData* data; FelicaListenerState state; + FelicaAuthentication auth; + FelicaBlockData mc_shadow; + + uint8_t request_size_buf; + uint8_t block_list_size; + uint8_t requested_blocks[FELICA_LISTENER_READ_BLOCK_COUNT_MAX]; + uint8_t mac_calc_start; + bool rc_written; BitBuffer* tx_buffer; BitBuffer* rx_buffer; @@ -18,4 +75,170 @@ struct FelicaListener { NfcGenericEvent generic_event; NfcGenericCallback callback; void* context; -}; \ No newline at end of file +}; + +/** Resets card authentication state after field off, also resets session key and + * perform post process operations with some blocks. + * + * @param instance pointer to the listener instance to be used. + */ +void felica_listener_reset(FelicaListener* instance); + +/** Performs WCNT increasing in case of write operation. + * + * @param data pointer to Felica card data. + */ +void felica_wcnt_increment(FelicaData* data); + +/** Compares IDm of card loaded for emulation with IDm from request. + * + * @param instance pointer to the listener instance to be used. + * @param request_idm pointer to IDm block from request structure. + * + * @return True if IDms' are equal, otherwise false. + */ +bool felica_listener_check_idm(const FelicaListener* instance, const FelicaIDm* request_idm); + +/** This is the first request validation function. + * + * Its main aim is to check whether the input request is valid in general by + * counting the amount of data in request. Function iterates through block list + * elements and data, counts real amount of elements and real amount of + * coresponding elements data (in case of write operation). If block list + * element amount is invalid or request data is missing, such request will be + * ignored. + * + * @param instance pointer to the listener instance to be used. + * @param request pointer to received request. + * + * @return True if block element list contains valid amount of data, + * otherwise false. + */ +bool felica_listener_check_block_list_size( + FelicaListener* instance, + FelicaListenerGenericRequest* request); + +/** Used to take first element from block element list. + * + * Each element in block list can be 2 or 3-bytes length and they can be mixed + * together. Therefore before returning the element, function checks whether + * there is enough bytes in the request for this element, in order to avoid + * memory casting behind the request block. + * + * @param instance pointer to the listener instance to be used. + * @param request pointer to received request. + * + * @return Pointer to the first element of the list or NULL if there is not + * enough bytes in the request. + */ +const FelicaBlockListElement* felica_listener_block_list_item_get_first( + FelicaListener* instance, + const FelicaListenerRequest* request); + +/** Used to take next element from block element list. + * + * It uses pointer to the previous element, takes its length and calculates + * pointer to the next element if there is enough bytes left. + * + * @param instance pointer to the listener instance to be used. + * @param prev_item pointer to the previous item taken. + * + * @return Pointer to the next element of the list or NULL if there is not + * enough bytes in the request. + */ +const FelicaBlockListElement* felica_listener_block_list_item_get_next( + FelicaListener* instance, + const FelicaBlockListElement* prev_item); + +/** Calculates pointer to data blocks in case of write operation, because block + * list elements size can vary. + * + * For calculation it uses previously calculated block list size and + * FelicaListenerGenericRequest size. + * + * @param instance pointer to the listener instance to be used. + * @param generic_request pointer to received request. + * + * @return Pointer to data blocks for write operation. + */ +const FelicaListenerWriteBlockData* felica_listener_get_write_request_data_pointer( + const FelicaListener* const instance, + const FelicaListenerGenericRequest* const generic_request); + +/** Function validates write request data and sets Felica SF1 and SF2 flags + * directly to the response. + * + * When something is wrong with data in the request (for example non-existing + * block is requested) this function sets proper SF1 and SF2 flags which will be + * then send to reader. When every thing is fine SF1 and SF2 are 0. Also this + * function performs authentiocation checks by MAC_A calculation if request + * contains MAC_A. + * + * @param instance pointer to the listener instance to be used. + * @param request pointer to received write request. + * @param data pointer to data which request wants to write. + * @param response pointer to the response to where SF1 and SF2 flags will + * be populated. + * + * @return True if request is fine also SF1 and SF2 will be 0. False if + * something is wrong and SF1, SF2 will provide more detailed + * information about the error. + */ +bool felica_listener_validate_write_request_and_set_sf( + FelicaListener* instance, + const FelicaListenerWriteRequest* const request, + const FelicaListenerWriteBlockData* const data, + FelicaListenerWriteCommandResponse* response); + +/** Function validates read request data and sets Felica SF1 and SF2 flags + * directly to the response. + * + * When something is wrong with data in the request (for example non-existing + * block is requested) this function sets proper SF1 and SF2 flags which will be + * then send to reader. In such case there will be no any data in the request, + * only flags. In case when request is fine SF1 and SF2 will be 0 and data will + * be populated to the response after them. Attention! This function doesn't + * populate any data, it only allows further processing where those data will be + * populated. + * + * @param instance pointer to the listener instance to be used. + * @param request pointer to received write request. + * @param resp_header pointer to the response to where SF1 and SF2 and + * data flags will be populated. + * + * @return True if request is fine also SF1 and SF2 will be 0. False if + * something is wrong and SF1, SF2 will provide more detailed + * information about the error. + */ +bool felica_listener_validate_read_request_and_set_sf( + FelicaListener* instance, + const FelicaListenerReadRequest* const request, + FelicaCommandResponseHeader* resp_header); + +/** Function returns appropiate block handler for processing read operation + * depending on number of block. + * + * @param block_number number of block. + * + * @return pointer to block handler function. + */ +FelicaCommanReadBlockHandler felica_listener_get_read_block_handler(const uint8_t block_number); + +/** Function returns appropiate block handler for processing write operation + * depending on number of block. + * + * @param block_number number of block. + * + * @return pointer to block handler function. + */ +FelicaCommandWriteBlockHandler felica_listener_get_write_block_handler(const uint8_t block_number); + +/** Sends response data from buffer to reader. + * + * @param instance pointer to the listener instance to be used. + * @param tx_buffer buffer with response data. + * + * @return error code or FelicaErrorNone. + */ +FelicaError + felica_listener_frame_exchange(const FelicaListener* instance, const BitBuffer* tx_buffer); diff --git a/lib/nfc/protocols/felica/felica_poller.h b/lib/nfc/protocols/felica/felica_poller.h index d4366e767..b386f4b4b 100644 --- a/lib/nfc/protocols/felica/felica_poller.h +++ b/lib/nfc/protocols/felica/felica_poller.h @@ -24,22 +24,6 @@ typedef enum { FelicaPollerEventTypeRequestAuthContext, /**< Authentication context was requested by poller. */ } FelicaPollerEventType; -/** - * @brief Stucture for holding Felica session key which is calculated from rc and ck. -*/ -typedef struct { - uint8_t data[FELICA_DATA_BLOCK_SIZE]; -} FelicaSessionKey; - -/** - * @brief Structure used to hold authentication related fields. -*/ -typedef struct { - mbedtls_des3_context des_context; /**< Context for mbedtls des functions. */ - FelicaSessionKey session_key; /**< Calculated session key. */ - FelicaAuthenticationContext context; /**< Public auth context provided to upper levels. */ -} FelicaAuthentication; - /** * @brief Felica poller event data. */ diff --git a/lib/nfc/protocols/felica/felica_poller_i.c b/lib/nfc/protocols/felica/felica_poller_i.c index f7726be32..8ec4b2889 100644 --- a/lib/nfc/protocols/felica/felica_poller_i.c +++ b/lib/nfc/protocols/felica/felica_poller_i.c @@ -3,11 +3,6 @@ #include #define TAG "FelicaPoller" -#define FELICA_CMD_READ_WITHOUT_ENCRYPTION (0x06U) -#define FELICA_CMD_WRITE_WITHOUT_ENCRYPTION (0x08U) - -#define FELICA_SERVICE_RW_ACCESS (0x0009U) -#define FELICA_SERVICE_RO_ACCESS (0x000BU) static FelicaError felica_poller_process_error(NfcError error) { switch(error) { diff --git a/lib/nfc/protocols/felica/felica_poller_i.h b/lib/nfc/protocols/felica/felica_poller_i.h index f7df4c845..9857d96d6 100644 --- a/lib/nfc/protocols/felica/felica_poller_i.h +++ b/lib/nfc/protocols/felica/felica_poller_i.h @@ -55,41 +55,6 @@ typedef struct { uint8_t request_data[2]; } FelicaPollerPollingResponse; -typedef struct { - uint8_t service_code : 4; - uint8_t access_mode : 3; - uint8_t length : 1; - uint8_t block_number; -} FelicaBlockListElement; - -#pragma pack(push, 1) -typedef struct { - uint8_t code; - FelicaIDm idm; - uint8_t service_num; - uint16_t service_code; - uint8_t block_count; -} FelicaCommandHeader; -#pragma pack(pop) - -typedef struct { - uint8_t length; - uint8_t response_code; - FelicaIDm idm; - uint8_t SF1; - uint8_t SF2; - uint8_t block_count; - uint8_t data[]; -} FelicaPollerReadCommandResponse; - -typedef struct { - uint8_t length; - uint8_t response_code; - FelicaIDm idm; - uint8_t SF1; - uint8_t SF2; -} FelicaPollerWriteCommandResponse; - const FelicaData* felica_poller_get_data(FelicaPoller* instance); /** diff --git a/lib/nfc/protocols/felica/felica_poller_sync.c b/lib/nfc/protocols/felica/felica_poller_sync.c new file mode 100644 index 000000000..f20ff08e1 --- /dev/null +++ b/lib/nfc/protocols/felica/felica_poller_sync.c @@ -0,0 +1,70 @@ +#include "felica_poller_sync.h" + +#include "felica_poller_i.h" +#include + +#include + +#define FELICA_POLLER_FLAG_COMMAND_COMPLETE (1UL << 0) + +typedef struct { + FelicaAuthenticationContext auth_ctx; + FuriThreadId thread_id; + FelicaError error; + FelicaData data; +} Felica_PollerContext; + +NfcCommand felica_poller_read_callback(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.event_data); + furi_assert(event.instance); + furi_assert(event.protocol == NfcProtocolFelica); + + Felica_PollerContext* poller_context = context; + FelicaPoller* felica_poller = event.instance; + + FelicaPollerEvent* felica_event = event.event_data; + + if(felica_event->type == FelicaPollerEventTypeReady || + felica_event->type == FelicaPollerEventTypeIncomplete) { + felica_copy(&poller_context->data, felica_poller->data); + } else if(felica_event->type == FelicaPollerEventTypeRequestAuthContext) { + felica_event->data->auth_context->skip_auth = poller_context->auth_ctx.skip_auth; + memcpy( + felica_event->data->auth_context->card_key.data, + poller_context->auth_ctx.card_key.data, + FELICA_DATA_BLOCK_SIZE); + } + + furi_thread_flags_set(poller_context->thread_id, FELICA_POLLER_FLAG_COMMAND_COMPLETE); + + return NfcCommandStop; +} + +FelicaError felica_poller_sync_read(Nfc* nfc, FelicaData* data, const FelicaCardKey* card_key) { + furi_check(nfc); + furi_check(data); + + Felica_PollerContext poller_context = {}; + if(card_key == NULL) { + poller_context.auth_ctx.skip_auth = true; + } else { + poller_context.auth_ctx.skip_auth = false; + memcpy(poller_context.auth_ctx.card_key.data, card_key->data, FELICA_DATA_BLOCK_SIZE); + } + + poller_context.thread_id = furi_thread_get_current_id(); + NfcPoller* poller = nfc_poller_alloc(nfc, NfcProtocolFelica); + nfc_poller_start(poller, felica_poller_read_callback, &poller_context); + furi_thread_flags_wait(FELICA_POLLER_FLAG_COMMAND_COMPLETE, FuriFlagWaitAny, FuriWaitForever); + furi_thread_flags_clear(FELICA_POLLER_FLAG_COMMAND_COMPLETE); + + nfc_poller_stop(poller); + nfc_poller_free(poller); + + if(poller_context.error == FelicaErrorNone) { + *data = poller_context.data; + } + + return poller_context.error; +} \ No newline at end of file diff --git a/lib/nfc/protocols/felica/felica_poller_sync.h b/lib/nfc/protocols/felica/felica_poller_sync.h new file mode 100644 index 000000000..fca5a0bfc --- /dev/null +++ b/lib/nfc/protocols/felica/felica_poller_sync.h @@ -0,0 +1,14 @@ +#pragma once + +#include "felica.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +FelicaError felica_poller_sync_read(Nfc* nfc, FelicaData* data, const FelicaCardKey* card_key); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/iso14443_3a/iso14443_3a_poller_sync.c b/lib/nfc/protocols/iso14443_3a/iso14443_3a_poller_sync.c index 751a1c4ff..52ba7e845 100644 --- a/lib/nfc/protocols/iso14443_3a/iso14443_3a_poller_sync.c +++ b/lib/nfc/protocols/iso14443_3a/iso14443_3a_poller_sync.c @@ -1,6 +1,6 @@ #include "iso14443_3a_poller_sync.h" -#include "iso14443_3a_poller_i.h" +#include "iso14443_3a_poller_i.h" // IWYU pragma: keep #include #include diff --git a/lib/nfc/protocols/iso14443_3b/iso14443_3b_i.c b/lib/nfc/protocols/iso14443_3b/iso14443_3b_i.c deleted file mode 100644 index cac7ed826..000000000 --- a/lib/nfc/protocols/iso14443_3b/iso14443_3b_i.c +++ /dev/null @@ -1 +0,0 @@ -#include "iso14443_3b_i.h" diff --git a/lib/nfc/protocols/iso14443_4b/iso14443_4b.c b/lib/nfc/protocols/iso14443_4b/iso14443_4b.c index b859f9b8e..6f849b1d4 100644 --- a/lib/nfc/protocols/iso14443_4b/iso14443_4b.c +++ b/lib/nfc/protocols/iso14443_4b/iso14443_4b.c @@ -1,4 +1,4 @@ -#include "iso14443_4b_i.h" +#include "iso14443_4b_i.h" // IWYU pragma: keep #include #include 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/mf_ultralight/mf_ultralight.c b/lib/nfc/protocols/mf_ultralight/mf_ultralight.c index 74436dc08..6300801ab 100644 --- a/lib/nfc/protocols/mf_ultralight/mf_ultralight.c +++ b/lib/nfc/protocols/mf_ultralight/mf_ultralight.c @@ -570,6 +570,22 @@ uint16_t mf_ultralight_get_config_page_num(MfUltralightType type) { return mf_ultralight_features[type].config_page; } +uint8_t mf_ultralight_get_write_end_page(MfUltralightType type) { + furi_check(type < MfUltralightTypeNum); + furi_assert( + type == MfUltralightTypeUL11 || type == MfUltralightTypeUL21 || + type == MfUltralightTypeNTAG213 || type == MfUltralightTypeNTAG215 || + type == MfUltralightTypeNTAG216); + + uint8_t end_page = mf_ultralight_get_config_page_num(type); + if(type == MfUltralightTypeNTAG213 || type == MfUltralightTypeNTAG215 || + type == MfUltralightTypeNTAG216) { + end_page -= 1; + } + + return end_page; +} + uint8_t mf_ultralight_get_pwd_page_num(MfUltralightType type) { furi_check(type < MfUltralightTypeNum); diff --git a/lib/nfc/protocols/mf_ultralight/mf_ultralight.h b/lib/nfc/protocols/mf_ultralight/mf_ultralight.h index 5e348a0c9..6c6a83a17 100644 --- a/lib/nfc/protocols/mf_ultralight/mf_ultralight.h +++ b/lib/nfc/protocols/mf_ultralight/mf_ultralight.h @@ -210,6 +210,8 @@ uint32_t mf_ultralight_get_feature_support_set(MfUltralightType type); uint16_t mf_ultralight_get_config_page_num(MfUltralightType type); +uint8_t mf_ultralight_get_write_end_page(MfUltralightType type); + uint8_t mf_ultralight_get_pwd_page_num(MfUltralightType type); bool mf_ultralight_is_page_pwd_or_pack(MfUltralightType type, uint16_t page_num); diff --git a/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.c b/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.c index 0c7f9f803..bc4ebd515 100644 --- a/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.c +++ b/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.c @@ -616,7 +616,7 @@ static NfcCommand mf_ultralight_poller_handler_write_pages(MfUltralightPoller* i do { const MfUltralightData* write_data = instance->mfu_event.data->write_data; - uint8_t end_page = mf_ultralight_get_config_page_num(write_data->type) - 1; + uint8_t end_page = mf_ultralight_get_write_end_page(write_data->type); if(instance->current_page == end_page) { instance->state = MfUltralightPollerStateWriteSuccess; break; 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/lib/pulse_reader/pulse_reader.c b/lib/pulse_reader/pulse_reader.c index 0fcafe67c..996a37d1e 100644 --- a/lib/pulse_reader/pulse_reader.c +++ b/lib/pulse_reader/pulse_reader.c @@ -1,6 +1,5 @@ #include "pulse_reader.h" -#include #include #include #include diff --git a/lib/signal_reader/signal_reader.c b/lib/signal_reader/signal_reader.c index f457cd29c..8df2d5c72 100644 --- a/lib/signal_reader/signal_reader.c +++ b/lib/signal_reader/signal_reader.c @@ -1,6 +1,5 @@ #include "signal_reader.h" -#include #include #include #include diff --git a/lib/subghz/blocks/const.c b/lib/subghz/blocks/const.c deleted file mode 100644 index 15719b2ac..000000000 --- a/lib/subghz/blocks/const.c +++ /dev/null @@ -1 +0,0 @@ -#include "const.h" diff --git a/lib/subghz/protocols/bin_raw.c b/lib/subghz/protocols/bin_raw.c index a5332ee4e..8f62ccb1f 100644 --- a/lib/subghz/protocols/bin_raw.c +++ b/lib/subghz/protocols/bin_raw.c @@ -529,7 +529,8 @@ static bool bin_raw_type = BinRAWTypeGap; //looking for the last occurrence of gap ind = instance->data_raw_ind - 1; - while((ind > 0) && (DURATION_DIFF(abs(instance->data_raw[ind]), gap) > gap_delta)) { + while((ind > 0) && + (DURATION_DIFF(abs(instance->data_raw[ind]), (int32_t)gap) > gap_delta)) { ind--; } gap_ind = ind; @@ -544,10 +545,10 @@ static bool uint16_t bit_count = 0; do { gap_ind--; - data_temp = (int)(round((float)(instance->data_raw[gap_ind]) / instance->te)); + data_temp = (int)(roundf((float)(instance->data_raw[gap_ind]) / instance->te)); bin_raw_debug("%d ", data_temp); if(data_temp == 0) bit_count++; //there is noise in the package - for(size_t i = 0; i < abs(data_temp); i++) { + for(size_t i = 0; i < (size_t)abs(data_temp); i++) { bit_count++; if(ind) { ind--; @@ -563,7 +564,7 @@ static bool } } //split into full bytes if gap is caught - if(DURATION_DIFF(abs(instance->data_raw[gap_ind]), gap) < gap_delta) { + if(DURATION_DIFF(abs(instance->data_raw[gap_ind]), (int32_t)gap) < gap_delta) { instance->data_markup[data_markup_ind].byte_bias = ind >> 3; instance->data_markup[data_markup_ind++].bit_count = bit_count; bit_count = 0; @@ -807,11 +808,11 @@ static bool bin_raw_debug_tag(TAG, "Sequence analysis without gap\r\n"); ind = 0; for(size_t i = 0; i < instance->data_raw_ind; i++) { - int data_temp = (int)(round((float)(instance->data_raw[i]) / instance->te)); + int data_temp = (int)(roundf((float)(instance->data_raw[i]) / instance->te)); if(data_temp == 0) break; //found an interval 2 times shorter than TE, this is noise bin_raw_debug("%d ", data_temp); - for(size_t k = 0; k < abs(data_temp); k++) { + for(size_t k = 0; k < (size_t)abs(data_temp); k++) { if(data_temp > 0) { subghz_protocol_blocks_set_bit_array( true, instance->data, ind++, BIN_RAW_BUF_DATA_SIZE); diff --git a/lib/subghz/protocols/intertechno_v3.c b/lib/subghz/protocols/intertechno_v3.c index 7fe952995..b9af72a57 100644 --- a/lib/subghz/protocols/intertechno_v3.c +++ b/lib/subghz/protocols/intertechno_v3.c @@ -470,6 +470,6 @@ void subghz_protocol_decoder_intertechno_v3_get_string(void* context, FuriString output, "Ch:" CH_PATTERN " Dimm:%d%%\r\n", CNT_TO_CH(instance->generic.cnt), - (int)(6.67 * (float)instance->generic.btn)); + (int)(6.67f * (float)instance->generic.btn)); } } diff --git a/lib/subghz/protocols/princeton.c b/lib/subghz/protocols/princeton.c index 60b394e78..59175de8f 100644 --- a/lib/subghz/protocols/princeton.c +++ b/lib/subghz/protocols/princeton.c @@ -267,7 +267,7 @@ void subghz_protocol_decoder_princeton_feed(void* context, bool level, uint32_t instance->generic.data = instance->decoder.decode_data; instance->generic.data_count_bit = instance->decoder.decode_count_bit; - instance->guard_time = round((float)duration / instance->te); + instance->guard_time = roundf((float)duration / instance->te); if(instance->base.callback) instance->base.callback(&instance->base, instance->base.context); diff --git a/lib/subghz/protocols/protocol_items.c b/lib/subghz/protocols/protocol_items.c index 472a354e3..e50d52ac1 100644 --- a/lib/subghz/protocols/protocol_items.c +++ b/lib/subghz/protocols/protocol_items.c @@ -1,4 +1,4 @@ -#include "protocol_items.h" +#include "protocol_items.h" // IWYU pragma: keep const SubGhzProtocol* subghz_protocol_registry_items[] = { &subghz_protocol_gate_tx, diff --git a/lib/subghz/protocols/raw.c b/lib/subghz/protocols/raw.c index 12599891d..b5677f9c2 100644 --- a/lib/subghz/protocols/raw.c +++ b/lib/subghz/protocols/raw.c @@ -3,10 +3,7 @@ #include "../subghz_file_encoder_worker.h" #include "../blocks/const.h" -#include "../blocks/decoder.h" -#include "../blocks/encoder.h" #include "../blocks/generic.h" -#include "../blocks/math.h" #include #include diff --git a/lib/subghz/receiver.c b/lib/subghz/receiver.c index c70229a12..88fac4e34 100644 --- a/lib/subghz/receiver.c +++ b/lib/subghz/receiver.c @@ -1,7 +1,6 @@ #include "receiver.h" #include "registry.h" -#include "protocols/protocol_items.h" #include diff --git a/lib/subghz/subghz_setting.c b/lib/subghz/subghz_setting.c index aacf2b4fe..ed1120a20 100644 --- a/lib/subghz/subghz_setting.c +++ b/lib/subghz/subghz_setting.c @@ -1,6 +1,5 @@ #include "subghz_setting.h" -#include "types.h" -//#include "subghz_i.h" +#include "types.h" // IWYU pragma: keep #include #include diff --git a/lib/subghz/transmitter.c b/lib/subghz/transmitter.c index 4f7922c35..20247d339 100644 --- a/lib/subghz/transmitter.c +++ b/lib/subghz/transmitter.c @@ -2,7 +2,6 @@ #include "protocols/base.h" #include "registry.h" -#include "protocols/protocol_items.h" struct SubGhzTransmitter { const SubGhzProtocol* protocol; diff --git a/lib/subghz/types.h b/lib/subghz/types.h index 93f94cc74..8c7f50d5d 100644 --- a/lib/subghz/types.h +++ b/lib/subghz/types.h @@ -11,7 +11,7 @@ #include #include -#define SUBGHZ_APP_FOLDER ANY_PATH("subghz") +#define SUBGHZ_APP_FOLDER EXT_PATH("subghz") #define SUBGHZ_RAW_FOLDER EXT_PATH("subghz") #define SUBGHZ_APP_FILENAME_PREFIX "SubGHz" #define SUBGHZ_APP_FILENAME_EXTENSION ".sub" diff --git a/lib/toolbox/manchester_encoder.c b/lib/toolbox/manchester_encoder.c index 38b94eb86..239e53250 100644 --- a/lib/toolbox/manchester_encoder.c +++ b/lib/toolbox/manchester_encoder.c @@ -1,5 +1,4 @@ #include "manchester_encoder.h" -#include #include void manchester_encoder_reset(ManchesterEncoderState* state) { diff --git a/lib/toolbox/tar/tar_archive.c b/lib/toolbox/tar/tar_archive.c index 639f2eaec..25084aaa0 100644 --- a/lib/toolbox/tar/tar_archive.c +++ b/lib/toolbox/tar/tar_archive.c @@ -224,6 +224,11 @@ static int archive_extract_foreach_cb(mtar_t* tar, const mtar_header_t* header, FuriString* full_extracted_fname; if(header->type == MTAR_TDIR) { + // Skip "/" entry since concat would leave it dangling, also want caller to mkdir destination + if(strcmp(header->name, "/") == 0) { + return 0; + } + full_extracted_fname = furi_string_alloc(); path_concat(op_params->work_dir, header->name, full_extracted_fname); 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/f18/api_symbols.csv b/targets/f18/api_symbols.csv index 7eda31675..d732e991d 100644 --- a/targets/f18/api_symbols.csv +++ b/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,63.0,, +Version,+,64.2,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/bt/bt_service/bt_keys_storage.h,, Header,+,applications/services/cli/cli.h,, @@ -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* @@ -1583,6 +1590,7 @@ Function,+,furi_string_utf8_push,void,"FuriString*, FuriStringUnicodeValue" Function,+,furi_string_vprintf,int,"FuriString*, const char[], va_list" Function,+,furi_thread_alloc,FuriThread*, Function,+,furi_thread_alloc_ex,FuriThread*,"const char*, uint32_t, FuriThreadCallback, void*" +Function,-,furi_thread_alloc_service,FuriThread*,"const char*, uint32_t, FuriThreadCallback, void*" Function,-,furi_thread_disable_heap_trace,void,FuriThread* Function,+,furi_thread_enable_heap_trace,void,FuriThread* Function,+,furi_thread_enumerate,uint32_t,"FuriThreadId*, uint32_t" @@ -1605,7 +1613,6 @@ Function,+,furi_thread_get_state,FuriThreadState,FuriThread* Function,+,furi_thread_get_stdout_callback,FuriThreadStdoutWriteCallback, Function,+,furi_thread_is_suspended,_Bool,FuriThreadId Function,+,furi_thread_join,_Bool,FuriThread* -Function,+,furi_thread_mark_as_service,void,FuriThread* Function,+,furi_thread_resume,void,FuriThreadId Function,+,furi_thread_set_appid,void,"FuriThread*, const char*" Function,+,furi_thread_set_callback,void,"FuriThread*, FuriThreadCallback" @@ -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 4faa2b0bc..85150a58b 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,64.0,, +Version,+,64.2,, Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/bt/bt_service/bt_keys_storage.h,, @@ -136,7 +136,9 @@ Header,+,lib/nfc/nfc_listener.h,, Header,+,lib/nfc/nfc_poller.h,, Header,+,lib/nfc/nfc_scanner.h,, Header,+,lib/nfc/protocols/felica/felica.h,, +Header,+,lib/nfc/protocols/felica/felica_listener.h,, Header,+,lib/nfc/protocols/felica/felica_poller.h,, +Header,+,lib/nfc/protocols/felica/felica_poller_sync.h,, Header,+,lib/nfc/protocols/iso14443_3a/iso14443_3a.h,, Header,+,lib/nfc/protocols/iso14443_3a/iso14443_3a_listener.h,, Header,+,lib/nfc/protocols/iso14443_3a/iso14443_3a_poller.h,, @@ -154,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,, @@ -593,7 +597,9 @@ Function,-,acosh,double,double Function,-,acoshf,float,float Function,-,acoshl,long double,long double Function,-,acosl,long double,long double -Function,+,aligned_alloc,void*,"size_t, size_t" +Function,-,aligned_alloc,void*,"size_t, size_t" +Function,+,aligned_free,void,void* +Function,+,aligned_malloc,void*,"size_t, size_t" Function,-,arc4random,__uint32_t, Function,-,arc4random_buf,void,"void*, size_t" Function,-,arc4random_uniform,__uint32_t,__uint32_t @@ -1003,6 +1009,7 @@ Function,-,fdimf,float,"float, float" Function,-,fdiml,long double,"long double, long double" Function,-,fdopen,FILE*,"int, const char*" Function,+,felica_alloc,FelicaData*, +Function,+,felica_calculate_mac_read,void,"mbedtls_des3_context*, const uint8_t*, const uint8_t*, const uint8_t*, const uint8_t, const uint8_t*, uint8_t*" Function,+,felica_calculate_mac_write,void,"mbedtls_des3_context*, const uint8_t*, const uint8_t*, const uint8_t*, const uint8_t*, uint8_t*" Function,+,felica_calculate_session_key,void,"mbedtls_des3_context*, const uint8_t*, const uint8_t*, uint8_t*" Function,+,felica_check_mac,_Bool,"mbedtls_des3_context*, const uint8_t*, const uint8_t*, const uint8_t*, const uint8_t, uint8_t*" @@ -1014,6 +1021,7 @@ Function,+,felica_get_uid,const uint8_t*,"const FelicaData*, size_t*" Function,+,felica_is_equal,_Bool,"const FelicaData*, const FelicaData*" Function,+,felica_load,_Bool,"FelicaData*, FlipperFormat*, uint32_t" Function,+,felica_poller_activate,FelicaError,"FelicaPoller*, FelicaData*" +Function,+,felica_poller_sync_read,FelicaError,"Nfc*, FelicaData*, const FelicaCardKey*" Function,+,felica_reset,void,FelicaData* Function,+,felica_save,_Bool,"const FelicaData*, FlipperFormat*" Function,+,felica_set_uid,_Bool,"FelicaData*, const uint8_t*, size_t" @@ -1195,6 +1203,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* @@ -1789,6 +1804,7 @@ Function,+,furi_string_utf8_push,void,"FuriString*, FuriStringUnicodeValue" Function,+,furi_string_vprintf,int,"FuriString*, const char[], va_list" Function,+,furi_thread_alloc,FuriThread*, Function,+,furi_thread_alloc_ex,FuriThread*,"const char*, uint32_t, FuriThreadCallback, void*" +Function,-,furi_thread_alloc_service,FuriThread*,"const char*, uint32_t, FuriThreadCallback, void*" Function,-,furi_thread_disable_heap_trace,void,FuriThread* Function,+,furi_thread_enable_heap_trace,void,FuriThread* Function,+,furi_thread_enumerate,uint32_t,"FuriThreadId*, uint32_t" @@ -1811,7 +1827,6 @@ Function,+,furi_thread_get_state,FuriThreadState,FuriThread* Function,+,furi_thread_get_stdout_callback,FuriThreadStdoutWriteCallback, Function,+,furi_thread_is_suspended,_Bool,FuriThreadId Function,+,furi_thread_join,_Bool,FuriThread* -Function,+,furi_thread_mark_as_service,void,FuriThread* Function,+,furi_thread_resume,void,FuriThreadId Function,+,furi_thread_set_appid,void,"FuriThread*, const char*" Function,+,furi_thread_set_callback,void,"FuriThread*, FuriThreadCallback" @@ -2410,8 +2425,7 @@ Function,+,memchr,void*,"const void*, int, size_t" Function,+,memcmp,int,"const void*, const void*, size_t" Function,+,memcpy,void*,"void*, const void*, size_t" Function,-,memmem,void*,"const void*, size_t, const void*, size_t" -Function,+,memmgr_aux_pool_alloc,void*,size_t -Function,+,memmgr_aux_pool_get_free,size_t, +Function,-,memmgr_alloc_from_pool,void*,size_t Function,+,memmgr_get_free_heap,size_t, Function,+,memmgr_get_minimum_free_heap,size_t, Function,+,memmgr_get_total_heap,size_t, @@ -2419,7 +2433,8 @@ Function,+,memmgr_heap_disable_thread_trace,void,FuriThreadId Function,+,memmgr_heap_enable_thread_trace,void,FuriThreadId Function,+,memmgr_heap_get_max_free_block,size_t, Function,+,memmgr_heap_get_thread_memory,size_t,FuriThreadId -Function,+,memmgr_heap_walk_blocks,void,"BlockWalker, void*" +Function,+,memmgr_heap_printf_free_blocks,void, +Function,-,memmgr_pool_get_free,size_t, Function,-,memmgr_pool_get_max_block,size_t, Function,+,memmove,void*,"void*, const void*, size_t" Function,-,mempcpy,void*,"void*, const void*, size_t" @@ -2518,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* @@ -2531,6 +2559,7 @@ Function,+,mf_ultralight_get_pages_total,uint16_t,MfUltralightType Function,+,mf_ultralight_get_pwd_page_num,uint8_t,MfUltralightType Function,+,mf_ultralight_get_type_by_version,MfUltralightType,MfUltralightVersion* Function,+,mf_ultralight_get_uid,const uint8_t*,"const MfUltralightData*, size_t*" +Function,+,mf_ultralight_get_write_end_page,uint8_t,MfUltralightType Function,+,mf_ultralight_is_all_data_read,_Bool,const MfUltralightData* Function,+,mf_ultralight_is_counter_configured,_Bool,const MfUltralightData* Function,+,mf_ultralight_is_equal,_Bool,"const MfUltralightData*, const MfUltralightData*" @@ -3508,6 +3537,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" @@ -3828,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, 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/furi_hal/furi_hal_nfc_event.c b/targets/f7/furi_hal/furi_hal_nfc_event.c index e434e6a35..9bcd2f1fe 100644 --- a/targets/f7/furi_hal/furi_hal_nfc_event.c +++ b/targets/f7/furi_hal/furi_hal_nfc_event.c @@ -77,6 +77,9 @@ FuriHalNfcEvent furi_hal_nfc_wait_event_common(uint32_t timeout_ms) { if(irq & ST25R3916_IRQ_MASK_WU_A_X) { event |= FuriHalNfcEventListenerActive; } + if(irq & ST25R3916_IRQ_MASK_WU_F) { + event |= FuriHalNfcEventListenerActive; + } } if(event_flag & FuriHalNfcEventInternalTypeTimerFwtExpired) { event |= FuriHalNfcEventTimerFwtExpired; diff --git a/targets/f7/furi_hal/furi_hal_nfc_felica.c b/targets/f7/furi_hal/furi_hal_nfc_felica.c index 82239fbc1..89305877e 100644 --- a/targets/f7/furi_hal/furi_hal_nfc_felica.c +++ b/targets/f7/furi_hal/furi_hal_nfc_felica.c @@ -55,6 +55,7 @@ static FuriHalNfcError furi_hal_nfc_felica_poller_deinit(FuriHalSpiBusHandle* ha static FuriHalNfcError furi_hal_nfc_felica_listener_init(FuriHalSpiBusHandle* handle) { furi_assert(handle); + st25r3916_direct_cmd(handle, ST25R3916_CMD_SET_DEFAULT); st25r3916_write_reg( handle, ST25R3916_REG_OP_CONTROL, @@ -89,8 +90,8 @@ static FuriHalNfcError furi_hal_nfc_felica_listener_init(FuriHalSpiBusHandle* ha st25r3916_write_reg(handle, ST25R3916_REG_RX_CONF3, 0x00); // No gain reduction on AM and PM channels st25r3916_write_reg(handle, ST25R3916_REG_RX_CONF4, 0x00); - // 10% ASK modulation - st25r3916_write_reg(handle, ST25R3916_REG_TX_DRIVER, ST25R3916_REG_TX_DRIVER_am_mod_10percent); + // 40% ASK modulation + st25r3916_write_reg(handle, ST25R3916_REG_TX_DRIVER, ST25R3916_REG_TX_DRIVER_am_mod_40percent); // Correlator setup st25r3916_write_reg( @@ -142,9 +143,7 @@ FuriHalNfcError furi_hal_nfc_felica_listener_tx( FuriHalSpiBusHandle* handle, const uint8_t* tx_data, size_t tx_bits) { - UNUSED(handle); - UNUSED(tx_data); - UNUSED(tx_bits); + furi_hal_nfc_common_fifo_tx(handle, tx_data, tx_bits); return FuriHalNfcErrorNone; } diff --git a/targets/f7/furi_hal/furi_hal_serial_control.c b/targets/f7/furi_hal/furi_hal_serial_control.c index d2ab414c2..241716107 100644 --- a/targets/f7/furi_hal/furi_hal_serial_control.c +++ b/targets/f7/furi_hal/furi_hal_serial_control.c @@ -268,9 +268,8 @@ void furi_hal_serial_control_init(void) { furi_hal_serial_control->handles[FuriHalSerialIdLpuart].id = FuriHalSerialIdLpuart; furi_hal_serial_control->queue = furi_message_queue_alloc(8, sizeof(FuriHalSerialControlMessage)); - furi_hal_serial_control->thread = - furi_thread_alloc_ex("SerialControlDriver", 512, furi_hal_serial_control_thread, NULL); - furi_thread_mark_as_service(furi_hal_serial_control->thread); + furi_hal_serial_control->thread = furi_thread_alloc_service( + "SerialControlDriver", 512, furi_hal_serial_control_thread, NULL); furi_thread_set_priority(furi_hal_serial_control->thread, FuriThreadPriorityHighest); furi_hal_serial_control->log_config_serial_id = FuriHalSerialIdMax; // Start control plane thread diff --git a/targets/f7/furi_hal/furi_hal_usb.c b/targets/f7/furi_hal/furi_hal_usb.c index a482940e2..22d1523b6 100644 --- a/targets/f7/furi_hal/furi_hal_usb.c +++ b/targets/f7/furi_hal/furi_hal_usb.c @@ -120,8 +120,7 @@ void furi_hal_usb_init(void) { NVIC_EnableIRQ(USB_HP_IRQn); usb.queue = furi_message_queue_alloc(1, sizeof(UsbApiEventMessage)); - usb.thread = furi_thread_alloc_ex("UsbDriver", 1024, furi_hal_usb_thread, NULL); - furi_thread_mark_as_service(usb.thread); + usb.thread = furi_thread_alloc_service("UsbDriver", 1024, furi_hal_usb_thread, NULL); furi_thread_start(usb.thread); FURI_LOG_I(TAG, "Init OK"); diff --git a/targets/f7/inc/FreeRTOSConfig.h b/targets/f7/inc/FreeRTOSConfig.h index 36800565c..e6233f624 100644 --- a/targets/f7/inc/FreeRTOSConfig.h +++ b/targets/f7/inc/FreeRTOSConfig.h @@ -16,7 +16,7 @@ #define configUSE_PREEMPTION 1 #define configSUPPORT_STATIC_ALLOCATION 1 -#define configSUPPORT_DYNAMIC_ALLOCATION 1 +#define configSUPPORT_DYNAMIC_ALLOCATION 0 #define configUSE_IDLE_HOOK 0 #define configUSE_TICK_HOOK 0 #define configCPU_CLOCK_HZ (SystemCoreClock) @@ -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) 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 a02d2ddd8..90ed5f024 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,