diff --git a/.clangd b/.clangd new file mode 100644 index 00000000000..12e13751d2c --- /dev/null +++ b/.clangd @@ -0,0 +1,17 @@ +CompileFlags: + Add: + - -Wno-unknown-warning-option + - -Wno-format + Remove: + - -mword-relocations + +Diagnostics: + ClangTidy: + FastCheckFilter: None + +--- + +If: + PathMatch: .*\.h +Diagnostics: + UnusedIncludes: None diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml index 40f72bd0ba5..d35ca0c17e5 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/.gitignore b/.gitignore index d60dcec3f5d..56c8363383f 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,9 @@ compile_commands.json # JetBrains IDEs .idea/ +# Sublime Text +.sublime-project.sublime-workspace + # Python VirtEnvironments .env .venv diff --git a/.sublime-project b/.sublime-project new file mode 100644 index 00000000000..5d751aaf630 --- /dev/null +++ b/.sublime-project @@ -0,0 +1,21 @@ +{ + "folders": + [ + { + "path": ".", + } + ], + "settings": { + "LSP": { + "clangd": { + "initializationOptions": { + "clangd.compile-commands-dir": "build/latest", + "clangd.header-insertion": "never", + "clangd.query-driver": "**", + "clangd.clang-tidy": true, + }, + "enabled": true, + }, + }, + }, +} diff --git a/.vscode/example/settings.json b/.vscode/example/settings.json index a5919490133..9afabf92688 100644 --- a/.vscode/example/settings.json +++ b/.vscode/example/settings.json @@ -19,6 +19,8 @@ "clangd.arguments": [ // We might be able to tighten this a bit more to only include the correct toolchain. "--query-driver=**", - "--compile-commands-dir=${workspaceFolder}/build/latest" + "--compile-commands-dir=${workspaceFolder}/build/latest", + "--clang-tidy", + "--header-insertion=never" ] } \ No newline at end of file diff --git a/applications/debug/accessor/accessor_app.cpp b/applications/debug/accessor/accessor_app.cpp index 2e40b3c35d7..8d43acc13b5 100644 --- a/applications/debug/accessor/accessor_app.cpp +++ b/applications/debug/accessor/accessor_app.cpp @@ -34,12 +34,16 @@ void AccessorApp::run(void) { AccessorApp::AccessorApp() : text_store{0} { notification = static_cast(furi_record_open(RECORD_NOTIFICATION)); + expansion = static_cast(furi_record_open(RECORD_EXPANSION)); onewire_host = onewire_host_alloc(&gpio_ibutton); + expansion_disable(expansion); furi_hal_power_enable_otg(); } AccessorApp::~AccessorApp() { furi_hal_power_disable_otg(); + expansion_enable(expansion); + furi_record_close(RECORD_EXPANSION); furi_record_close(RECORD_NOTIFICATION); onewire_host_free(onewire_host); } diff --git a/applications/debug/accessor/accessor_app.h b/applications/debug/accessor/accessor_app.h index bfd5c06e152..890552f5f79 100644 --- a/applications/debug/accessor/accessor_app.h +++ b/applications/debug/accessor/accessor_app.h @@ -6,6 +6,7 @@ #include "helpers/wiegand.h" #include #include +#include class AccessorApp { public: @@ -51,4 +52,5 @@ class AccessorApp { OneWireHost* onewire_host; NotificationApp* notification; + Expansion* expansion; }; diff --git a/applications/debug/accessor/scene/accessor_scene_start.cpp b/applications/debug/accessor/scene/accessor_scene_start.cpp index d31001d2d7a..79f51f06103 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 4b5dcd62764..5c5a3bd4532 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 7dd2c9e969d..638878da26f 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 50b356696c5..6993901d243 100644 --- a/applications/debug/ccid_test/ccid_test_app.c +++ b/applications/debug/ccid_test/ccid_test_app.c @@ -6,8 +6,9 @@ #include #include #include - +#include "iso7816_callbacks.h" #include "iso7816_t0_apdu.h" +#include "iso7816_atr.h" typedef enum { EventTypeInput, @@ -33,38 +34,6 @@ typedef enum { CcidTestSubmenuIndexInsertSmartcardReader } SubmenuIndex; -void icc_power_on_callback(uint8_t* atrBuffer, uint32_t* atrlen, void* context) { - UNUSED(context); - - iso7816_answer_to_reset(atrBuffer, atrlen); -} - -//dataBlock points to the buffer -//dataBlockLen tells reader how nany bytes should be read -void xfr_datablock_callback( - const uint8_t* dataBlock, - uint32_t dataBlockLen, - uint8_t* responseDataBlock, - uint32_t* responseDataBlockLen, - void* context) { - UNUSED(context); - - struct ISO7816_Command_APDU commandAPDU; - iso7816_read_command_apdu(&commandAPDU, dataBlock, dataBlockLen); - - struct ISO7816_Response_APDU responseAPDU; - //class not supported - responseAPDU.SW1 = 0x6E; - responseAPDU.SW2 = 0x00; - - iso7816_write_response_apdu(&responseAPDU, responseDataBlock, responseDataBlockLen); -} - -static const CcidCallbacks ccid_cb = { - icc_power_on_callback, - xfr_datablock_callback, -}; - static void ccid_test_app_render_callback(Canvas* canvas, void* ctx) { UNUSED(ctx); canvas_clear(canvas); @@ -103,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; @@ -127,6 +95,86 @@ void ccid_test_app_free(CcidTestApp* app) { free(app); } +void ccid_icc_power_on_callback(uint8_t* atrBuffer, uint32_t* atrlen, void* context) { + UNUSED(context); + + iso7816_icc_power_on_callback(atrBuffer, atrlen); +} + +void ccid_xfr_datablock_callback( + const uint8_t* pcToReaderDataBlock, + uint32_t pcToReaderDataBlockLen, + uint8_t* readerToPcDataBlock, + uint32_t* readerToPcDataBlockLen, + void* context) { + UNUSED(context); + + iso7816_xfr_datablock_callback( + pcToReaderDataBlock, pcToReaderDataBlockLen, readerToPcDataBlock, readerToPcDataBlockLen); +} + +static const CcidCallbacks ccid_cb = { + ccid_icc_power_on_callback, + ccid_xfr_datablock_callback, +}; + +void iso7816_answer_to_reset(Iso7816Atr* atr) { + //minimum valid ATR: https://smartcard-atr.apdu.fr/parse?ATR=3B+00 + atr->TS = 0x3B; + atr->T0 = 0x00; +} + +void iso7816_process_command( + const struct ISO7816_Command_APDU* commandAPDU, + struct ISO7816_Response_APDU* responseAPDU, + const uint8_t* commandApduDataBuffer, + uint8_t commandApduDataBufferLen, + uint8_t* responseApduDataBuffer, + uint8_t* responseApduDataBufferLen) { + //example 1: sends a command with no body, receives a response with no body + //sends APDU 0x01:0x02:0x00:0x00 + //receives SW1=0x90, SW2=0x00 + if(commandAPDU->CLA == 0x01 && commandAPDU->INS == 0x01) { + responseAPDU->SW1 = 0x90; + responseAPDU->SW2 = 0x00; + } + //example 2: sends a command with no body, receives a response with a body with two bytes + //sends APDU 0x01:0x02:0x00:0x00 + //receives 'bc' (0x62, 0x63) SW1=0x80, SW2=0x10 + else if(commandAPDU->CLA == 0x01 && commandAPDU->INS == 0x02) { + responseApduDataBuffer[0] = 0x62; + responseApduDataBuffer[1] = 0x63; + + *responseApduDataBufferLen = 2; + + responseAPDU->SW1 = 0x90; + responseAPDU->SW2 = 0x00; + } + //example 3: ends a command with a body with two bytes, receives a response with a body with two bytes + //sends APDU 0x01:0x03:0x00:0x00:0x02:CA:FE + //receives (0xCA, 0xFE) SW1=0x90, SW2=0x02 + else if( + commandAPDU->CLA == 0x01 && commandAPDU->INS == 0x03 && commandApduDataBufferLen == 2 && + commandAPDU->Lc == 2) { + //echo command body to response body + responseApduDataBuffer[0] = commandApduDataBuffer[0]; + responseApduDataBuffer[1] = commandApduDataBuffer[1]; + + *responseApduDataBufferLen = 2; + + responseAPDU->SW1 = 0x90; + responseAPDU->SW2 = 0x00; + } else { + responseAPDU->SW1 = 0x6A; + responseAPDU->SW2 = 0x00; + } +} + +static const Iso7816Callbacks iso87816_cb = { + iso7816_answer_to_reset, + iso7816_process_command, +}; + int32_t ccid_test_app(void* p) { UNUSED(p); @@ -135,14 +183,16 @@ int32_t ccid_test_app(void* p) { //setup CCID USB // On linux: set VID PID using: /usr/lib/pcsc/drivers/ifd-ccid.bundle/Contents/Info.plist - app->ccid_cfg.vid = 0x1234; - app->ccid_cfg.pid = 0x5678; + app->ccid_cfg.vid = 0x076B; + app->ccid_cfg.pid = 0x3A21; FuriHalUsbInterface* usb_mode_prev = furi_hal_usb_get_config(); furi_hal_usb_unlock(); - furi_hal_ccid_set_callbacks((CcidCallbacks*)&ccid_cb); + furi_hal_ccid_set_callbacks((CcidCallbacks*)&ccid_cb, NULL); furi_check(furi_hal_usb_set_config(&usb_ccid, &app->ccid_cfg) == true); + iso7816_set_callbacks((Iso7816Callbacks*)&iso87816_cb); + //handle button events CcidTestAppEvent event; while(1) { @@ -161,7 +211,9 @@ int32_t ccid_test_app(void* p) { //tear down USB furi_hal_usb_set_config(usb_mode_prev, NULL); - furi_hal_ccid_set_callbacks(NULL); + furi_hal_ccid_set_callbacks(NULL, NULL); + + iso7816_set_callbacks(NULL); //teardown view ccid_test_app_free(app); diff --git a/applications/debug/ccid_test/iso7816_atr.h b/applications/debug/ccid_test/iso7816_atr.h new file mode 100644 index 00000000000..050457f8c19 --- /dev/null +++ b/applications/debug/ccid_test/iso7816_atr.h @@ -0,0 +1,9 @@ +#ifndef _ISO7816_ATR_H_ +#define _ISO7816_ATR_H_ + +typedef struct { + uint8_t TS; + uint8_t T0; +} Iso7816Atr; + +#endif //_ISO7816_ATR_H_ diff --git a/applications/debug/ccid_test/iso7816_callbacks.c b/applications/debug/ccid_test/iso7816_callbacks.c new file mode 100644 index 00000000000..1a66fa7755d --- /dev/null +++ b/applications/debug/ccid_test/iso7816_callbacks.c @@ -0,0 +1,76 @@ +// transforms low level calls such as XFRCallback or ICC Power on to a structured one +// an application can register these calls and listen for the callbacks defined in Iso7816Callbacks + +#include "iso7816_t0_apdu.h" +#include "iso7816_atr.h" +#include "iso7816_callbacks.h" +#include +#include +#include + +#define ISO7816_RESPONSE_BUFFER_SIZE 255 + +static Iso7816Callbacks* callbacks = NULL; + +void iso7816_set_callbacks(Iso7816Callbacks* cb) { + callbacks = cb; +} + +void iso7816_icc_power_on_callback(uint8_t* atrBuffer, uint32_t* atrlen) { + Iso7816Atr atr; + callbacks->iso7816_answer_to_reset(&atr); + + furi_assert(atr.T0 == 0x00); + + uint8_t AtrBuffer[2] = {atr.TS, atr.T0}; + + *atrlen = 2; + + memcpy(atrBuffer, AtrBuffer, sizeof(uint8_t) * (*atrlen)); +} + +//dataBlock points to the buffer +//dataBlockLen tells reader how nany bytes should be read +void iso7816_xfr_datablock_callback( + const uint8_t* pcToReaderDataBlock, + uint32_t pcToReaderDataBlockLen, + uint8_t* readerToPcDataBlock, + uint32_t* readerToPcDataBlockLen) { + struct ISO7816_Response_APDU responseAPDU; + uint8_t responseApduDataBuffer[ISO7816_RESPONSE_BUFFER_SIZE]; + uint8_t responseApduDataBufferLen = 0; + + if(callbacks != NULL) { + struct ISO7816_Command_APDU commandAPDU; + + const uint8_t* commandApduDataBuffer = NULL; + uint8_t commandApduDataBufferLen = 0; + + iso7816_read_command_apdu(&commandAPDU, pcToReaderDataBlock, pcToReaderDataBlockLen); + + if(commandAPDU.Lc > 0) { + commandApduDataBufferLen = commandAPDU.Lc; + commandApduDataBuffer = &pcToReaderDataBlock[5]; + } + + callbacks->iso7816_process_command( + &commandAPDU, + &responseAPDU, + commandApduDataBuffer, + commandApduDataBufferLen, + responseApduDataBuffer, + &responseApduDataBufferLen); + + } else { + //class not supported + responseAPDU.SW1 = 0x6E; + responseAPDU.SW2 = 0x00; + } + + iso7816_write_response_apdu( + &responseAPDU, + readerToPcDataBlock, + readerToPcDataBlockLen, + responseApduDataBuffer, + responseApduDataBufferLen); +} diff --git a/applications/debug/ccid_test/iso7816_callbacks.h b/applications/debug/ccid_test/iso7816_callbacks.h new file mode 100644 index 00000000000..3d337d23a0b --- /dev/null +++ b/applications/debug/ccid_test/iso7816_callbacks.h @@ -0,0 +1,28 @@ +#ifndef _ISO7816_CALLBACKS_H_ +#define _ISO7816_CALLBACKS_H_ + +#include +#include "iso7816_atr.h" +#include "iso7816_t0_apdu.h" + +typedef struct { + void (*iso7816_answer_to_reset)(Iso7816Atr* atr); + void (*iso7816_process_command)( + const struct ISO7816_Command_APDU* command, + struct ISO7816_Response_APDU* response, + const uint8_t* commandApduDataBuffer, + uint8_t commandApduDataBufferLen, + uint8_t* responseApduDataBuffer, + uint8_t* responseApduDataBufferLen); +} Iso7816Callbacks; + +void iso7816_set_callbacks(Iso7816Callbacks* cb); + +void iso7816_icc_power_on_callback(uint8_t* atrBuffer, uint32_t* atrlen); +void iso7816_xfr_datablock_callback( + const uint8_t* dataBlock, + uint32_t dataBlockLen, + uint8_t* responseDataBlock, + uint32_t* responseDataBlockLen); + +#endif //_ISO7816_CALLBACKS_H_ \ No newline at end of file diff --git a/applications/debug/ccid_test/iso7816_t0_apdu.c b/applications/debug/ccid_test/iso7816_t0_apdu.c index 7c690a6944e..5fb695af193 100644 --- a/applications/debug/ccid_test/iso7816_t0_apdu.c +++ b/applications/debug/ccid_test/iso7816_t0_apdu.c @@ -4,22 +4,14 @@ #include #include "iso7816_t0_apdu.h" -void iso7816_answer_to_reset(uint8_t* dataBuffer, uint32_t* atrlen) { - //minimum valid ATR: https://smartcard-atr.apdu.fr/parse?ATR=3B+00 - uint8_t AtrBuffer[2] = { - 0x3B, //TS (direct convention) - 0x00 // T0 (Y(1): b0000, K: 0 (historical bytes)) - }; - *atrlen = 2; - - memcpy(dataBuffer, AtrBuffer, sizeof(uint8_t) * (*atrlen)); -} - +//reads dataBuffer with dataLen size, translate it into a ISO7816_Command_APDU type +//extra data will be pointed to commandDataBuffer void iso7816_read_command_apdu( struct ISO7816_Command_APDU* command, const uint8_t* dataBuffer, uint32_t dataLen) { UNUSED(dataLen); + command->CLA = dataBuffer[0]; command->INS = dataBuffer[1]; command->P1 = dataBuffer[2]; @@ -27,11 +19,30 @@ void iso7816_read_command_apdu( command->Lc = dataBuffer[4]; } +//data buffer countains the whole APU response (response + trailer (SW1+SW2)) void iso7816_write_response_apdu( const struct ISO7816_Response_APDU* response, - uint8_t* dataBuffer, - uint32_t* dataLen) { - dataBuffer[0] = response->SW1; - dataBuffer[1] = response->SW2; - *dataLen = 2; + uint8_t* readerToPcDataBlock, + uint32_t* readerToPcDataBlockLen, + uint8_t* responseDataBuffer, + uint32_t responseDataLen) { + uint32_t responseDataBufferIndex = 0; + + //response body + if(responseDataLen > 0) { + while(responseDataBufferIndex < responseDataLen) { + readerToPcDataBlock[responseDataBufferIndex] = + responseDataBuffer[responseDataBufferIndex]; + responseDataBufferIndex++; + } + } + + //trailer + readerToPcDataBlock[responseDataBufferIndex] = response->SW1; + responseDataBufferIndex++; + + readerToPcDataBlock[responseDataBufferIndex] = response->SW2; + responseDataBufferIndex++; + + *readerToPcDataBlockLen = responseDataBufferIndex; } \ No newline at end of file diff --git a/applications/debug/ccid_test/iso7816_t0_apdu.h b/applications/debug/ccid_test/iso7816_t0_apdu.h index 5ca13eb6041..48a18944067 100644 --- a/applications/debug/ccid_test/iso7816_t0_apdu.h +++ b/applications/debug/ccid_test/iso7816_t0_apdu.h @@ -2,6 +2,8 @@ #define _ISO7816_T0_APDU_H_ #include +#include "iso7816_atr.h" +#include "core/common_defines.h" struct ISO7816_Command_APDU { //header @@ -20,13 +22,15 @@ struct ISO7816_Response_APDU { uint8_t SW2; } FURI_PACKED; -void iso7816_answer_to_reset(uint8_t* atrBuffer, uint32_t* atrlen); +void iso7816_answer_to_reset(Iso7816Atr* atr); void iso7816_read_command_apdu( struct ISO7816_Command_APDU* command, const uint8_t* dataBuffer, uint32_t dataLen); void iso7816_write_response_apdu( const struct ISO7816_Response_APDU* response, - uint8_t* dataBuffer, - uint32_t* dataLen); + uint8_t* readerToPcDataBlock, + uint32_t* readerToPcDataBlockLen, + uint8_t* responseDataBuffer, + uint32_t responseDataLen); #endif //_ISO7816_T0_APDU_H_ diff --git a/applications/debug/display_test/display_test.c b/applications/debug/display_test/display_test.c index ac1b6c65df2..3028a13b905 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 6f70f09beec..00000000000 --- 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 9e8881defaf..bc4036120a6 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 6b9501e5b72..ddca372e38f 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 ff439ef0f8d..fad16ddb418 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 0ec6635a0eb..e0daee6a611 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 a941cd4bb50..2c82380f796 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 d50f10ad46e..674e7c73798 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 22ad662c57a..b7b514bd3c8 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 2b9475d2b21..8002f14b66e 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 00000000000..93ba4ba6cf0 --- /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 6af807086e8..517a4fd24a1 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 d12aeb2aa1c..1487cdb4808 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 e7704381d08..b65a35bf5a8 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 8f615329715..15984083d1c 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 7d90f18de15..9ba3e59d19f 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 e42e00f9a70..14cd078d5d2 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 c2474e75548..e0102a575ae 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 79d9b0a57c2..ea4fea28ce9 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 1a2eaf222dc..d26acf57702 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 821b1ba22ae..de6964cbe00 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 d016ca4a2df..41d78da057d 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 00000000000..06afc4fd9bb --- /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 837fd608592..43a1d20ebb6 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 fdf15e6f581..d9956b0ae63 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 6a2b5a3d267..b192f152793 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 eab9a917d8f..be579d2b8c0 100644 --- a/applications/debug/unit_tests/tests/furi/furi_test.c +++ b/applications/debug/unit_tests/tests/furi/furi_test.c @@ -1,14 +1,13 @@ -#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_event_loop(void); static int foo = 0; @@ -39,15 +38,19 @@ MU_TEST(mu_test_furi_memmgr) { test_furi_memmgr(); } +MU_TEST(mu_test_furi_event_loop) { + test_furi_event_loop(); +} + MU_TEST_SUITE(test_suite) { MU_SUITE_CONFIGURE(&test_setup, &test_teardown); - MU_RUN_TEST(test_check); // v2 tests MU_RUN_TEST(mu_test_furi_create_open); MU_RUN_TEST(mu_test_furi_pubsub); MU_RUN_TEST(mu_test_furi_memmgr); + MU_RUN_TEST(mu_test_furi_event_loop); } int run_minunit_test_furi(void) { diff --git a/applications/debug/unit_tests/tests/furi_hal/furi_hal_tests.c b/applications/debug/unit_tests/tests/furi_hal/furi_hal_tests.c index 985daa03d73..4b515206c9c 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 0a264a18aa9..26527c66986 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 949d8c0488e..df835006e49 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 922577aa864..cf6a1322002 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 fc380ecc496..8bf7753c0e8 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 fcbab0ae8a8..b2c888f1ae4 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 4de364a09d5..1e440707643 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 d121acba649..ae332c96d29 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 9ced106abd3..4e2975218f4 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 be7b331f965..f5b9e762d7b 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 1d887ced067..65a25cf4943 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 a9c22c72323..4f1ba2fd7ee 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 030a4223fde..6a41293592a 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 3fdf5115abc..6a9d94c167f 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 8c2fa4687fe..fc659aea906 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 6e716e69a30..2b7710451f4 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 c8ad67625f1..43befc055b8 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 51457fe81fe..ea9cdbdc353 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 ba928c4993c..c08deb60a26 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 45d5b47cc90..cbeb4824d72 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 37f860a9a58..7b6552dfba2 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 6cdf12c6257..de06b968409 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 d5f6fce2360..1acf3acb153 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 a5d0df94c3a..3f01d709031 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 f3d047d67d6..5e714576d98 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 b82bcc1f9af..9288a4a4d1b 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 1a019cf595c..9410c8537d8 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 b0660f3e629..ceb18065369 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 6abaa48cd59..7a51e3d8696 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 00000000000..eebed2a8d41 --- /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 00000000000..7f2e63dd1d6 --- /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 00000000000..8640fa16d26 --- /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 00000000000..5aa8436a9d8 --- /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 cd16374bc86..a3028905fc1 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 215ffc4553a..66839aacc1e 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 fca1c3185f2..6c199f11462 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 64a9ac5dc66..de9a47facb4 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 00000000000..58e6d5276a4 --- /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 7c4a3d19db2..d62ee2d8ed4 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 44f9963afe3..eaa05414904 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 4111cca813b..a963f44ac95 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 995434631ad..624caf30ed7 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 07d7bccf93d..f9906b51340 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 bcc0cc433d9..d14be476802 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 0413173e6e8..3e0656550f7 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 @@ -80,6 +80,14 @@ SubGhzProtocolStatus subghz_txrx_gen_data_protocol_and_te( FURI_LOG_E(TAG, "Unable to update Te"); } } + if(ret == SubGhzProtocolStatusOk) { + uint32_t guard_time = 30; + if(!flipper_format_update_uint32( + instance->fff_data, "Guard_time", (uint32_t*)&guard_time, 1)) { + ret = SubGhzProtocolStatusErrorParserOthers; + FURI_LOG_E(TAG, "Unable to update Guard_time"); + } + } return ret; } diff --git a/applications/main/subghz/helpers/subghz_types.h b/applications/main/subghz/helpers/subghz_types.h index e71c22dd564..5c17dc3d49d 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 53f13b68e0e..5862ad7c7f3 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 d5c2b391ce2..0150f299646 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 b48a821da23..da861d718c9 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 1f8e4d83d6a..c324d0d2e24 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 f2ab6577026..b12c946813d 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 61bd1acc851..ce123dab364 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 a65830f4b19..61e362d5bdf 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 d52eca9b6cf..904663e0b9b 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 0de48c442a3..e7eda9c7107 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 679dafaf151..b96a22db62b 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 b553d00de02..243ad101f8a 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 ea332f544a6..39b5c78b905 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 133868dde51..b59426f022c 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 eff3eb1ab7d..322c0d49905 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 f9b3f44bac9..ab41ea95689 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 2a2e91f2189..b61cfa15d3b 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 389275b8695..59eb863889a 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 7ba70421954..709d69768fd 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_commands.c b/applications/services/cli/cli_commands.c index 43f1c01c4da..7bf86c90cf5 100644 --- a/applications/services/cli/cli_commands.c +++ b/applications/services/cli/cli_commands.c @@ -382,37 +382,69 @@ void cli_command_led(Cli* cli, FuriString* args, void* context) { furi_record_close(RECORD_NOTIFICATION); } -void cli_command_ps(Cli* cli, FuriString* args, void* context) { +static void cli_command_top(Cli* cli, FuriString* args, void* context) { UNUSED(cli); - UNUSED(args); UNUSED(context); - const uint8_t threads_num_max = 32; - FuriThreadId threads_ids[threads_num_max]; - uint32_t thread_num = furi_thread_enumerate(threads_ids, threads_num_max); - printf( - "%-17s %-20s %-5s %-13s %-6s %-8s %s\r\n", - "AppID", - "Name", - "Prio", - "Stack start", - "Heap", - "Stack", - "Stack min free"); - for(uint8_t i = 0; i < thread_num; i++) { - TaskControlBlock* tcb = (TaskControlBlock*)threads_ids[i]; - size_t thread_heap = memmgr_heap_get_thread_memory(threads_ids[i]); + int interval = 1000; + args_read_int_and_trim(args, &interval); + + FuriThreadList* thread_list = furi_thread_list_alloc(); + while(!cli_cmd_interrupt_received(cli)) { + uint32_t tick = furi_get_tick(); + furi_thread_enumerate(thread_list); + + if(interval) printf("\e[2J\e[0;0f"); // Clear display and return to 0 + + uint32_t uptime = tick / furi_kernel_get_tick_frequency(); + printf( + "Threads: %zu, Uptime: %luh%lum%lus\r\n", + furi_thread_list_size(thread_list), + uptime / 60 / 60, + uptime / 60 % 60, + uptime % 60); + printf( - "%-17s %-20s %-5d 0x%-11lx %-6zu %-8lu %-8lu\r\n", - furi_thread_get_appid(threads_ids[i]), - furi_thread_get_name(threads_ids[i]), - furi_thread_get_priority(threads_ids[i]), - (uint32_t)tcb->pxStack, - thread_heap == MEMMGR_HEAP_UNKNOWN ? 0u : thread_heap, - (uint32_t)(tcb->pxEndOfStack - tcb->pxStack + 1) * sizeof(StackType_t), - furi_thread_get_stack_space(threads_ids[i])); + "Heap: total %zu, free %zu, minimum %zu, max block %zu\r\n\r\n", + memmgr_get_total_heap(), + memmgr_get_free_heap(), + memmgr_get_minimum_free_heap(), + memmgr_heap_get_max_free_block()); + + printf( + "%-17s %-20s %-10s %5s %12s %6s %10s %7s %5s\r\n", + "AppID", + "Name", + "State", + "Prio", + "Stack start", + "Stack", + "Stack Min", + "Heap", + "CPU"); + + for(size_t i = 0; i < furi_thread_list_size(thread_list); i++) { + const FuriThreadListItem* item = furi_thread_list_get_at(thread_list, i); + printf( + "%-17s %-20s %-10s %5d 0x%08lx %6lu %10lu %7zu %5.1f\r\n", + item->app_id, + item->name, + item->state, + item->priority, + item->stack_address, + item->stack_size, + item->stack_min_free, + item->heap, + (double)item->cpu); + } + + if(interval > 0) { + furi_delay_ms(interval); + } else { + break; + } } - printf("\r\nTotal: %lu", thread_num); + furi_thread_list_free(thread_list); } void cli_command_free(Cli* cli, FuriString* args, void* context) { @@ -472,7 +504,7 @@ void cli_commands_init(Cli* cli) { cli_add_command(cli, "date", CliCommandFlagParallelSafe, cli_command_date, NULL); cli_add_command(cli, "log", CliCommandFlagParallelSafe, cli_command_log, NULL); cli_add_command(cli, "sysctl", CliCommandFlagDefault, cli_command_sysctl, NULL); - cli_add_command(cli, "ps", CliCommandFlagParallelSafe, cli_command_ps, NULL); + cli_add_command(cli, "top", CliCommandFlagParallelSafe, cli_command_top, NULL); cli_add_command(cli, "free", CliCommandFlagParallelSafe, cli_command_free, NULL); cli_add_command(cli, "free_blocks", CliCommandFlagParallelSafe, cli_command_free_blocks, NULL); diff --git a/applications/services/cli/cli_vcp.c b/applications/services/cli/cli_vcp.c index 58c5c887ff3..cdabaaa0544 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 c99cc7b5d09..1db61f1abf6 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 9585b2771e0..cea039671d3 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 748f9a55588..8b0c6d75380 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 374b569f051..0b1149d6c22 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 105b2b37a24..5951a8e4e34 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 c4cd5748f3d..6d432858a50 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 41b1c3e7511..5f3778d1323 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 f6de987bfa4..b1c0d4c8523 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 e3336ad76d6..ed6f9f12e77 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 9f8b5f51aea..1b6bdab9c58 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 0c1160d408e..87f12d84a7e 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 f24ecc8ead3..2811ba7d25d 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 84e4d5706ec..de8c1cb8625 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 b9e610c6231..942a0d25a73 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 ec1289d5b9c..31856aa294e 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 73d1d876a61..9df3eedb98d 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 79d151c590a..b1558f1e95b 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 69d28c405e6..a71f403c543 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 5f3d5cb284c..4a75241e6c6 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 55c2286d9a9..724060e53b8 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 06b63fd902f..5b38c5c7916 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 6e015ed75a7..85d640f30ce 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 f887b90ac4c..f456ba36846 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 d768b324a2f..0aa9dd005d0 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 45014f6f4d0..5c9c75e4a97 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 50171e0cc42..ea3315d8d8d 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 a35c2fa3881..51d02354355 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 d4c2f61e798..bf1cd2be660 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(16, 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(16, 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); @@ -277,7 +298,7 @@ void view_dispatcher_handle_input(ViewDispatcher* view_dispatcher, InputEvent* e } else if(view_dispatcher->ongoing_input_view && event->type == InputTypeRelease) { FURI_LOG_D( TAG, - "View changed while key press %p -> %p. Sending key: %s, type: %s, sequence: %p to previous view port", + "View changed while key press %p -> %p. Sending key: %s, type: %s, sequence: %p to previous view", view_dispatcher->ongoing_input_view, view_dispatcher->current_view, input_get_key_name(event->key), @@ -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 f8567ea1ac9..7627e5a0b0b 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 f30a84e6bd6..fcf426c3177 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/gui/view_port.c b/applications/services/gui/view_port.c index 7f1da3b700e..39156411df2 100644 --- a/applications/services/gui/view_port.c +++ b/applications/services/gui/view_port.c @@ -139,12 +139,17 @@ uint8_t view_port_get_height(const ViewPort* view_port) { void view_port_enabled_set(ViewPort* view_port, bool enabled) { furi_check(view_port); - furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk); + + // We are not going to lockup system, but will notify you instead + // Make sure that you don't call viewport methods inside of another mutex, especially one that is used in draw call + if(furi_mutex_acquire(view_port->mutex, 2) != FuriStatusOk) { + FURI_LOG_W(TAG, "ViewPort lockup: see %s:%d", __FILE__, __LINE__ - 3); + } if(view_port->is_enabled != enabled) { view_port->is_enabled = enabled; if(view_port->gui) gui_update(view_port->gui); } - furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk); + furi_mutex_release(view_port->mutex); } bool view_port_is_enabled(const ViewPort* view_port) { @@ -236,8 +241,13 @@ void view_port_set_orientation(ViewPort* view_port, ViewPortOrientation orientat } ViewPortOrientation view_port_get_orientation(const ViewPort* view_port) { - furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk); + furi_check(view_port); + // We are not going to lockup system, but will notify you instead + // Make sure that you don't call viewport methods inside of another mutex, especially one that is used in draw call + if(furi_mutex_acquire(view_port->mutex, 2) != FuriStatusOk) { + FURI_LOG_W(TAG, "ViewPort lockup: see %s:%d", __FILE__, __LINE__ - 3); + } ViewPortOrientation orientation = view_port->orientation; - furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk); + furi_mutex_release(view_port->mutex); return orientation; } diff --git a/applications/services/loader/loader.c b/applications/services/loader/loader.c index c02eefd0445..d4a4d581ae8 100644 --- a/applications/services/loader/loader.c +++ b/applications/services/loader/loader.c @@ -428,13 +428,19 @@ static LoaderStatus loader_do_start_by_name( do { // check lock if(loader_do_is_locked(loader)) { - const char* current_thread_name = - furi_thread_get_name(furi_thread_get_id(loader->app.thread)); - status = loader_make_status_error( - LoaderStatusErrorAppStarted, - error_message, - "Loader is locked, please close the \"%s\" first", - current_thread_name); + if(loader->app.thread == (FuriThread*)LOADER_MAGIC_THREAD_VALUE) { + status = loader_make_status_error( + LoaderStatusErrorAppStarted, error_message, "Loader is locked"); + } else { + const char* current_thread_name = + furi_thread_get_name(furi_thread_get_id(loader->app.thread)); + + status = loader_make_status_error( + LoaderStatusErrorAppStarted, + error_message, + "Loader is locked, please close the \"%s\" first", + current_thread_name); + } break; } diff --git a/applications/services/notification/notification_app_api.c b/applications/services/notification/notification_app_api.c index 9c9a2680ead..90e3f236e56 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 2db9bb1c33c..278854e13cc 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 054a0286f8f..32371fa82ed 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 0d72b43d551..a70e8436306 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 151d73d61e5..306b25777b9 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); - - bool backup_ok = - lfs_backup_create(fs_api, request->content.storage_backup_create_request.archive_path); + rpc_system_storage_reset_state(rpc_storage, session, true); - 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); - - bool backup_ok = - lfs_backup_unpack(fs_api, request->content.storage_backup_restore_request.archive_path); + rpc_system_storage_reset_state(rpc_storage, session, true); - 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 a377751ea1e..adc0e2465ac 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 2f21fd999fc..f31a9689441 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 4cef4ba9865..170e6bca565 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 0ebf85c64b7..efa39f1f08c 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 8dec260165c..7b3e5b96b9e 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 d43f150c6e9..aae53cd2a68 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 dd2ec2dbc99..c626d18449c 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 17c0ad36b8a..4619a1593c6 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 d84ae811051..b48221343fe 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 bd7f1ce1647..aee9fd16dac 100644 --- a/applications/system/snake_game/snake_game.c +++ b/applications/system/snake_game/snake_game.c @@ -136,15 +136,17 @@ static void snake_game_render_callback(Canvas* const canvas, void* ctx) { furi_mutex_release(snake_state->mutex); } -static void snake_game_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { - furi_assert(event_queue); +static void snake_game_input_callback(InputEvent* input_event, void* context) { + furi_assert(context); + FuriMessageQueue* event_queue = context; SnakeEvent event = {.type = EventTypeKey, .input = *input_event}; furi_message_queue_put(event_queue, &event, FuriWaitForever); } -static void snake_game_update_timer_callback(FuriMessageQueue* event_queue) { - furi_assert(event_queue); +static void snake_game_update_timer_callback(void* context) { + furi_assert(context); + FuriMessageQueue* event_queue = context; SnakeEvent event = {.type = EventTypeTick}; furi_message_queue_put(event_queue, &event, 0); @@ -325,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 1956b83bba9..816de200a4a 160000 --- a/assets/protobuf +++ b/assets/protobuf @@ -1 +1 @@ -Subproject commit 1956b83bba99313ee8d8386e5d35d0549341ca26 +Subproject commit 816de200a4a43efc25c5b92d6a57fc982d7e988a diff --git a/documentation/AppManifests.md b/documentation/AppManifests.md index b612df1b79f..98a38ffd85f 100644 --- a/documentation/AppManifests.md +++ b/documentation/AppManifests.md @@ -36,7 +36,7 @@ Only two parameters are mandatory: **appid** and **apptype**. Others are optiona - **requires**: list of application IDs to include in the build configuration when the current application is referenced in the list of applications to build. - **conflicts**: list of application IDs with which the current application conflicts. If any of them is found in the constructed application list, `fbt` will abort the firmware build process. - **provides**: functionally identical to **_requires_** field. -- **stack_size**: stack size in bytes to allocate for an application on its startup. Note that allocating a stack too small for an app to run will cause a system crash due to stack overflow, and allocating too much stack space will reduce usable heap memory size for apps to process data. _Note: you can use `ps` and `free` CLI commands to profile your app's memory usage._ +- **stack_size**: stack size in bytes to allocate for an application on its startup. Note that allocating a stack too small for an app to run will cause a system crash due to stack overflow, and allocating too much stack space will reduce usable heap memory size for apps to process data. _Note: you can use `top` and `free` CLI commands to profile your app's memory usage._ - **icon**: animated icon name from built-in assets to be used when building the app as a part of the firmware. - **order**: order of an application within its group when sorting entries in it. The lower the order is, the closer to the start of the list the item is placed. _Used for ordering startup hooks and menu entries._ - **sdk_headers**: list of C header files from this app's code to include in API definitions for external applications. diff --git a/firmware.scons b/firmware.scons index bf3f46a9bf5..62b1184ebde 100644 --- a/firmware.scons +++ b/firmware.scons @@ -20,6 +20,7 @@ env = ENV.Clone( "fbt_resources", ], COMPILATIONDB_USE_ABSPATH=False, + COMPILATIONDB_USE_BINARY_ABSPATH=True, BUILD_DIR=fw_build_meta["build_dir"], IS_BASE_FIRMWARE=fw_build_meta["type"] == "firmware", FW_FLAVOR=fw_build_meta["flavor"], diff --git a/furi/core/base.h b/furi/core/base.h index 642ff2b6cda..92a52a7978b 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/check.c b/furi/core/check.c index 90989474a3d..ba05a675f45 100644 --- a/furi/core/check.c +++ b/furi/core/check.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -110,8 +111,14 @@ static void __furi_print_heap_info(void) { static void __furi_print_name(bool isr) { if(isr) { + uint8_t exception_number = __get_IPSR(); + const char* name = furi_hal_interrupt_get_name(exception_number); furi_log_puts("[ISR "); - __furi_put_uint32_as_text(__get_IPSR()); + if(name) { + furi_log_puts(name); + } else { + __furi_put_uint32_as_text(__get_IPSR()); + } furi_log_puts("] "); } else { const char* name = pcTaskGetName(NULL); diff --git a/furi/core/common_defines.h b/furi/core/common_defines.h index beb9f6519ca..0f6230c19f1 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 ccbee93d66b..c2e04e2fda8 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)); + + furi_check(xEventGroupCreateStatic(&instance->container) == (EventGroupHandle_t)instance); - return ((FuriEventFlag*)handle); + 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 7200144cde2..fbc3bb00496 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 00000000000..f228346928c --- /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 00000000000..7221a90bca9 --- /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 00000000000..8ddd10966f9 --- /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 db3d6a7160e..52c0e285eed 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 768adc05dfa..ba9a7336a9c 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/memmgr_heap.c b/furi/core/memmgr_heap.c index 3cee0d37795..3827ddde34c 100644 --- a/furi/core/memmgr_heap.c +++ b/furi/core/memmgr_heap.c @@ -39,6 +39,7 @@ #include #include #include +#include #include #include @@ -56,10 +57,6 @@ task.h is included from an application file. */ #error This feature is broken, logging transport must be replaced with RTT #endif -#if(configSUPPORT_DYNAMIC_ALLOCATION == 0) -#error This file must not be used if configSUPPORT_DYNAMIC_ALLOCATION is 0 -#endif - /* Block sizes must not get too small. */ #define heapMINIMUM_BLOCK_SIZE ((size_t)(xHeapStructSize << 1)) @@ -67,8 +64,6 @@ task.h is included from an application file. */ #define heapBITS_PER_BYTE ((size_t)8) /* Heap start end symbols provided by linker */ -extern const void __heap_start__; -extern const void __heap_end__; uint8_t* ucHeap = (uint8_t*)&__heap_start__; /* Define the linked list structure. This is used to link free blocks in order diff --git a/furi/core/message_queue.c b/furi/core/message_queue.c index 6e8ab186987..cda775abe96 100644 --- a/furi/core/message_queue.c +++ b/furi/core/message_queue.c @@ -1,24 +1,52 @@ -#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 { + StaticQueue_t container; + + // 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)); - QueueHandle_t handle = xQueueCreate(msg_count, msg_size); - furi_check(handle); + FuriMessageQueue* instance = malloc(sizeof(FuriMessageQueue) + msg_count * msg_size); + + // 3 things happens here: + // - create queue + // - check results + // - ensure that queue container is first in the FuriMessageQueue structure + // + // As a bonus it guarantees that FuriMessageQueue* can be casted into StaticQueue_t* or QueueHandle_t. + furi_check( + xQueueCreateStatic(msg_count, msg_size, instance->buffer, &instance->container) == + (void*)instance); - return ((FuriMessageQueue*)handle); + return instance; } 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); } FuriStatus @@ -57,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) { @@ -96,33 +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); - StaticQueue_t* mq = (StaticQueue_t*)instance; - uint32_t capacity; - - /* capacity = pxQueue->uxLength */ - capacity = mq->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); - StaticQueue_t* mq = (StaticQueue_t*)instance; - uint32_t size; - - /* size = pxQueue->uxItemSize */ - size = mq->uxDummy4[2]; - - /* Return maximum message size */ - return (size); + return instance->container.uxItemSize; } uint32_t furi_message_queue_get_count(FuriMessageQueue* instance) { @@ -137,30 +159,26 @@ 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) { furi_check(instance); - StaticQueue_t* mq = (StaticQueue_t*)instance; uint32_t space; uint32_t isrm; if(furi_kernel_is_irq_or_masked() != 0U) { isrm = taskENTER_CRITICAL_FROM_ISR(); - /* space = pxQueue->uxLength - pxQueue->uxMessagesWaiting; */ - space = mq->uxDummy4[1] - mq->uxDummy4[0]; + space = instance->container.uxLength - instance->container.uxMessagesWaiting; taskEXIT_CRITICAL_FROM_ISR(isrm); } else { - space = (uint32_t)uxQueueSpacesAvailable((QueueHandle_t)mq); + space = (uint32_t)uxQueueSpacesAvailable((QueueHandle_t)instance); } - /* Return number of available slots */ - return (space); + return space; } FuriStatus furi_message_queue_reset(FuriMessageQueue* instance) { @@ -176,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 8d7f389e127..4c5b5af3c90 100644 --- a/furi/core/message_queue.h +++ b/furi/core/message_queue.h @@ -4,13 +4,13 @@ */ #pragma once -#include "core/base.h" +#include "base.h" #ifdef __cplusplus extern "C" { #endif -typedef void FuriMessageQueue; +typedef struct FuriMessageQueue FuriMessageQueue; /** Allocate furi message queue * diff --git a/furi/core/message_queue_i.h b/furi/core/message_queue_i.h new file mode 100644 index 00000000000..aa24cfe54fc --- /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 e32be1a3969..f59ae83ada9 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; - - hMutex = (SemaphoreHandle_t)((uint32_t)instance & ~1U); - - /* Extract recursive mutex flag */ - rmtx = (uint32_t)instance & 1U; + SemaphoreHandle_t hMutex = (SemaphoreHandle_t)(instance); + const uint8_t mutex_type = instance->container.ucQueueType; - 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(mutex_type == queueQUEUE_TYPE_RECURSIVE_MUTEX) { + 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_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; - - hMutex = (SemaphoreHandle_t)((uint32_t)instance & ~1U); - - /* Extract recursive mutex flag */ - rmtx = (uint32_t)instance & 1U; + SemaphoreHandle_t hMutex = (SemaphoreHandle_t)(instance); + const uint8_t mutex_type = instance->container.ucQueueType; - 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; - FuriThreadId owner; + SemaphoreHandle_t hMutex = (SemaphoreHandle_t)instance; - hMutex = (SemaphoreHandle_t)((uint32_t)instance & ~1U); + FuriThreadId owner; - 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 aa55fa7bc15..60372a29299 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 bcabc74a76c..7c1730632b1 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 e9bd8edcd63..fa384369a70 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 503eec472b0..6413eb65f53 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 19d056bfe22..c6b9a1176a1 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 8bd00d91c57..879520010a4 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; + + FuriStreamBuffer* stream_buffer = malloc(sizeof(FuriStreamBuffer) + buffer_size); + StreamBufferHandle_t hStreamBuffer = xStreamBufferCreateStatic( + buffer_size, trigger_level, stream_buffer->buffer, &stream_buffer->container); - return handle; + 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 5ddc4941630..eef8ee51073 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 f9f73b4f75a..4e9477712a0 100644 --- a/furi/core/thread.c +++ b/furi/core/thread.c @@ -1,4 +1,5 @@ #include "thread.h" +#include "thread_list.h" #include "kernel.h" #include "memmgr.h" #include "memmgr_heap.h" @@ -13,9 +14,13 @@ #include #include +#include + #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 +30,9 @@ struct FuriThreadStdout { }; struct FuriThread { + StaticTask_t container; + StackType_t* stack_buffer; + FuriThreadState state; int32_t ret; @@ -39,7 +47,7 @@ struct FuriThread { FuriThreadPriority priority; - TaskHandle_t task_handle; + size_t stack_size; size_t heap_size; FuriThreadStdout output; @@ -48,10 +56,12 @@ struct FuriThread { // this ensures that the size of this structure is minimal bool is_service; bool heap_trace_enabled; - - configSTACK_DEPTH_TYPE stack_size; + volatile bool is_active; }; +// 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); @@ -83,34 +93,28 @@ static void furi_thread_body(void* context) { furi_check(thread->state == FuriThreadStateStarting); furi_thread_set_state(thread, FuriThreadStateRunning); - TaskHandle_t task_handle = xTaskGetCurrentTaskHandle(); if(thread->heap_trace_enabled == true) { - memmgr_heap_enable_thread_trace((FuriThreadId)task_handle); + memmgr_heap_enable_thread_trace(thread); } 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); + thread->heap_size = memmgr_heap_get_thread_memory(thread); furi_log_print_format( thread->heap_size ? FuriLogLevelError : FuriLogLevelInfo, TAG, "%s allocation balance: %zu", thread->name ? thread->name : "Thread", thread->heap_size); - memmgr_heap_disable_thread_trace((FuriThreadId)task_handle); + memmgr_heap_disable_thread_trace(thread); } 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 +124,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 +150,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 +195,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); + furi_check(!thread->is_active); - 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 +216,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 +226,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 +308,24 @@ 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( + + thread->is_active = true; + + furi_check( + xTaskCreateStatic( furi_thread_body, thread->name, - stack, + stack_depth, thread, priority, - memmgr_alloc_from_pool(sizeof(StackType_t) * stack), - memmgr_alloc_from_pool(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->stack_buffer, + &thread->container) == (TaskHandle_t)thread); } void furi_thread_cleanup_tcb_event(TaskHandle_t task) { @@ -292,21 +333,23 @@ void furi_thread_cleanup_tcb_event(TaskHandle_t task) { if(thread) { // clear thread local storage vTaskSetThreadLocalStoragePointer(task, 0, NULL); - furi_check(thread->task_handle == task); - thread->task_handle = NULL; + furi_check(thread == (FuriThread*)task); + thread->is_active = false; } } 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 !!! // // If your thread exited, but your app stuck here: some other thread uses // all cpu time, which delays kernel from releasing task handle - while(thread->task_handle) { + while(thread->is_active) { furi_delay_ms(10); } @@ -315,7 +358,7 @@ bool furi_thread_join(FuriThread* thread) { FuriThreadId furi_thread_get_id(FuriThread* thread) { furi_check(thread); - return thread->task_handle; + return thread; } void furi_thread_enable_heap_trace(FuriThread* thread) { @@ -387,7 +430,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 +459,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 +477,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,36 +547,74 @@ uint32_t furi_thread_flags_wait(uint32_t flags, uint32_t options, uint32_t timeo } /* Return flags before clearing */ - return (rflags); + return rflags; +} + +static const char* furi_thread_state_name(eTaskState state) { + switch(state) { + case eRunning: + return "Running"; + case eReady: + return "Ready"; + case eBlocked: + return "Blocked"; + case eSuspended: + return "Suspended"; + case eDeleted: + return "Deleted"; + case eInvalid: + return "Invalid"; + default: + return "?"; + } } -uint32_t furi_thread_enumerate(FuriThreadId* thread_array, uint32_t array_item_count) { - uint32_t i, count; - TaskStatus_t* task; +bool furi_thread_enumerate(FuriThreadList* thread_list) { + furi_check(thread_list); + furi_check(!FURI_IS_IRQ_MODE()); - if(FURI_IS_IRQ_MODE() || (thread_array == NULL) || (array_item_count == 0U)) { - count = 0U; - } else { - vTaskSuspendAll(); + bool result = false; - count = uxTaskGetNumberOfTasks(); - task = pvPortMalloc(count * sizeof(TaskStatus_t)); - configRUN_TIME_COUNTER_TYPE total_run_time; + vTaskSuspendAll(); + do { + uint32_t tick = furi_get_tick(); + uint32_t count = uxTaskGetNumberOfTasks(); - if(task != NULL) { - count = uxTaskGetSystemState(task, count, &total_run_time); + TaskStatus_t* task = pvPortMalloc(count * sizeof(TaskStatus_t)); - for(i = 0U; (i < count) && (i < array_item_count); i++) { - thread_array[i] = (FuriThreadId)task[i].xHandle; - } - count = i; + if(!task) break; + + configRUN_TIME_COUNTER_TYPE total_run_time; + count = uxTaskGetSystemState(task, count, &total_run_time); + for(uint32_t i = 0U; i < count; i++) { + TaskControlBlock* tcb = (TaskControlBlock*)task[i].xHandle; + + FuriThreadListItem* item = + furi_thread_list_get_or_insert(thread_list, (FuriThread*)task[i].xHandle); + + item->thread = (FuriThreadId)task[i].xHandle; + item->app_id = furi_thread_get_appid(item->thread); + item->name = task[i].pcTaskName; + item->priority = task[i].uxCurrentPriority; + item->stack_address = (uint32_t)tcb->pxStack; + size_t thread_heap = memmgr_heap_get_thread_memory(item->thread); + item->heap = thread_heap == MEMMGR_HEAP_UNKNOWN ? 0u : thread_heap; + item->stack_size = (tcb->pxEndOfStack - tcb->pxStack + 1) * sizeof(StackType_t); + item->stack_min_free = furi_thread_get_stack_space(item->thread); + item->state = furi_thread_state_name(task[i].eCurrentState); + item->counter_previous = item->counter_current; + item->counter_current = task[i].ulRunTimeCounter; + item->tick = tick; } - (void)xTaskResumeAll(); vPortFree(task); - } + furi_thread_list_process(thread_list, total_run_time, tick); + + result = true; + } while(false); + (void)xTaskResumeAll(); - return (count); + return result; } const char* furi_thread_get_name(FuriThreadId thread_id) { @@ -546,7 +627,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 +641,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 +654,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 f21ee9df3e4..9c113bd4943 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,88 @@ 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 */ +/** FuriThreadList type */ +typedef struct FuriThreadList FuriThreadList; + +/** + * @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 +130,339 @@ FuriThread* furi_thread_alloc_ex( FuriThreadCallback callback, void* context); -/** Release FuriThread +/** + * @brief Delete a FuriThread instance. + * + * The thread MUST be stopped when calling this function. * - * @warning see furi_thread_join + * @warning see furi_thread_join for caveats on stopping a thread. * - * @param thread FuriThread instance + * @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. + * + * The thread MUST be stopped when calling this function. * - * @param thread FuriThread instance - * @param name string + * @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. + * + * 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 thread FuriThread instance - * @param stack_size stack size in bytes + * @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. + * + * The thread MUST be stopped when calling this function. * - * @param thread FuriThread instance - * @param callback FuriThreadCallback, called upon thread run + * @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. + * + * The thread MUST be stopped when calling this function. * - * @param thread FuriThread instance - * @param priority FuriThreadPriority value + * @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. + * + * The thread MUST be stopped when calling this function. * - * @param thread FuriThread instance - * @param context pointer to context + * @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 - * - * @param thread FuriThread instance +/** + * @brief Get the state of a FuriThread isntance. * - * @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 - * - * @param thread FuriThread instance +/** + * @brief Get the unique identifier of a 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. + * + * The thread MUST be stopped when calling this function. * - * @param thread FuriThread instance + * @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. + * + * The thread MUST be stopped when calling this function. * - * @param thread FuriThread instance + * @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. + * + * This value is equal to the return value of the thread callback function. * - * @param thread FuriThread instance + * The thread MUST be stopped when calling this function. * - * @return return code + * @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 - * - * @param thread_array array of FuriThreadId, where thread ids will be stored - * @param array_item_count array size - * @return uint32_t threads count + * @brief Enumerate all threads. + * + * @param[out] thread_list pointer to the FuriThreadList container + * + * @return true on success, false otherwise */ -uint32_t furi_thread_enumerate(FuriThreadId* thread_array, uint32_t array_item_count); +bool furi_thread_enumerate(FuriThreadList* thread_list); /** - * @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/thread_list.c b/furi/core/thread_list.c new file mode 100644 index 00000000000..65ee11ad388 --- /dev/null +++ b/furi/core/thread_list.c @@ -0,0 +1,110 @@ +#include "thread_list.h" +#include "check.h" + +#include +#include + +ARRAY_DEF(FuriThreadListItemArray, FuriThreadListItem*, M_PTR_OPLIST) // NOLINT + +#define M_OPL_FuriThreadListItemArray_t() ARRAY_OPLIST(FuriThreadListItemArray, M_PTR_OPLIST) + +DICT_DEF2( + FuriThreadListItemDict, + uint32_t, + M_DEFAULT_OPLIST, + FuriThreadListItem*, + M_PTR_OPLIST) // NOLINT + +#define M_OPL_FuriThreadListItemDict_t() \ + DICT_OPLIST(FuriThreadListItemDict, M_DEFAULT_OPLIST, M_PTR_OPLIST) + +struct FuriThreadList { + FuriThreadListItemArray_t items; + FuriThreadListItemDict_t search; + uint32_t runtime_previous; + uint32_t runtime_current; +}; + +FuriThreadList* furi_thread_list_alloc(void) { + FuriThreadList* instance = malloc(sizeof(FuriThreadList)); + + FuriThreadListItemArray_init(instance->items); + FuriThreadListItemDict_init(instance->search); + + return instance; +} + +void furi_thread_list_free(FuriThreadList* instance) { + furi_check(instance); + + FuriThreadListItemArray_it_t it; + FuriThreadListItemArray_it(it, instance->items); + while(!FuriThreadListItemArray_end_p(it)) { + FuriThreadListItem* item = *FuriThreadListItemArray_cref(it); + free(item); + FuriThreadListItemArray_next(it); + } + + FuriThreadListItemDict_clear(instance->search); + FuriThreadListItemArray_clear(instance->items); + + free(instance); +} + +size_t furi_thread_list_size(FuriThreadList* instance) { + furi_check(instance); + return FuriThreadListItemArray_size(instance->items); +} + +FuriThreadListItem* furi_thread_list_get_at(FuriThreadList* instance, size_t position) { + furi_check(instance); + furi_check(position < furi_thread_list_size(instance)); + + return *FuriThreadListItemArray_get(instance->items, position); +} + +FuriThreadListItem* furi_thread_list_get_or_insert(FuriThreadList* instance, FuriThread* thread) { + furi_check(instance); + + FuriThreadListItem** item_ptr = FuriThreadListItemDict_get(instance->search, (uint32_t)thread); + if(item_ptr) { + return *item_ptr; + } + + FuriThreadListItem* item = malloc(sizeof(FuriThreadListItem)); + + FuriThreadListItemArray_push_back(instance->items, item); + FuriThreadListItemDict_set_at(instance->search, (uint32_t)thread, item); + + return item; +} + +void furi_thread_list_process(FuriThreadList* instance, uint32_t runtime, uint32_t tick) { + furi_check(instance); + + instance->runtime_previous = instance->runtime_current; + instance->runtime_current = runtime; + + uint32_t runtime_counter = instance->runtime_current - instance->runtime_previous; + + FuriThreadListItemArray_it_t it; + FuriThreadListItemArray_it(it, instance->items); + while(!FuriThreadListItemArray_end_p(it)) { + FuriThreadListItem* item = *FuriThreadListItemArray_cref(it); + if(item->tick != tick) { + FuriThreadListItemArray_remove(instance->items, it); + (void)FuriThreadListItemDict_erase(instance->search, (uint32_t)item->thread); + free(item); + } else { + uint32_t item_counter = item->counter_current - item->counter_previous; + if(item_counter && item->counter_previous && item->counter_current) { + item->cpu = (float)item_counter / (float)runtime_counter * 100.0f; + if(item->cpu > 200.0f) item->cpu = 0.0f; + } else { + item->cpu = 0.0f; + } + + FuriThreadListItemArray_next(it); + } + } +} diff --git a/furi/core/thread_list.h b/furi/core/thread_list.h new file mode 100644 index 00000000000..bf15e4032be --- /dev/null +++ b/furi/core/thread_list.h @@ -0,0 +1,81 @@ +#pragma once + +#include "base.h" +#include "common_defines.h" +#include "thread.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + FuriThread* thread; /**< Pointer to FuriThread, valid while it is running */ + const char* app_id; /**< Thread application id, valid while it is running */ + const char* name; /**< Thread name, valid while it is running */ + FuriThreadPriority priority; /**< Thread priority */ + uint32_t stack_address; /**< Thread stack address */ + size_t heap; /**< Thread heap size if tracking enabled, 0 - otherwise */ + uint32_t stack_size; /**< Thread stack size */ + uint32_t stack_min_free; /**< Thread minimum of the stack size ever reached */ + const char* + state; /**< Thread state, can be: "Running", "Ready", "Blocked", "Suspended", "Deleted", "Invalid" */ + float cpu; /**< Thread CPU usage time in percents (including interrupts happened while running) */ + + // Service variables + uint32_t counter_previous; /**< Thread previous runtime counter */ + uint32_t counter_current; /**< Thread current runtime counter */ + uint32_t tick; /**< Thread last seen tick */ +} FuriThreadListItem; + +/** Anonymous FuriThreadList type */ +typedef struct FuriThreadList FuriThreadList; + +/** Allocate FuriThreadList instance + * + * @return FuriThreadList instance + */ +FuriThreadList* furi_thread_list_alloc(void); + +/** Free FuriThreadList instance + * + * @param instance The FuriThreadList instance to free + */ +void furi_thread_list_free(FuriThreadList* instance); + +/** Get FuriThreadList instance size + * + * @param instance The instance + * + * @return Item count + */ +size_t furi_thread_list_size(FuriThreadList* instance); + +/** Get item at position + * + * @param instance The FuriThreadList instance + * @param[in] position The position of the item + * + * @return The FuriThreadListItem instance + */ +FuriThreadListItem* furi_thread_list_get_at(FuriThreadList* instance, size_t position); + +/** Get item by thread FuriThread pointer + * + * @param instance The FuriThreadList instance + * @param thread The FuriThread pointer + * + * @return The FuriThreadListItem instance + */ +FuriThreadListItem* furi_thread_list_get_or_insert(FuriThreadList* instance, FuriThread* thread); + +/** Process items in the FuriThreadList instance + * + * @param instance The instance + * @param[in] runtime The runtime of the system since start + * @param[in] tick The tick when processing happened + */ +void furi_thread_list_process(FuriThreadList* instance, uint32_t runtime, uint32_t tick); + +#ifdef __cplusplus +} +#endif diff --git a/furi/core/timer.c b/furi/core/timer.c index 2e688dcc321..1ca56f0fa45 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; +}; -static void TimerCallback(TimerHandle_t hTimer) { - TimerCallback_t* callb; - - /* Retrieve pointer to callback function and context */ - callb = (TimerCallback_t*)pvTimerGetTimerID(hTimer); +// IMPORTANT: container MUST be the FIRST struct member +static_assert(offsetof(FuriTimer, container) == 0); - /* Remove dynamic allocation flag */ - callb = (TimerCallback_t*)((uint32_t)callb & ~1U); - - if(callb != NULL) { - callb->func(callb->context); - } +static void TimerCallback(TimerHandle_t hTimer) { + 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; +} + +static void furi_timer_epilogue(void* context, uint32_t arg) { + furi_assert(context); + UNUSED(arg); - /* 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); + FuriTimer* instance = context; - /* Return timer ID */ - return ((FuriTimer*)hTimer); + 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; - - 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); + furi_check(xTimerDelete(hTimer, portMAX_DELAY) == pdPASS); + furi_check(xTimerPendFunctionCall(furi_timer_epilogue, instance, 0, portMAX_DELAY) == pdPASS); - /* 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 f8f40c56267..00056db18eb 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 c7ba3b4fb1b..6d6215a9da9 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); @@ -67,4 +66,4 @@ void vApplicationGetTimerTaskMemory( *tcb_ptr = memmgr_alloc_from_pool(sizeof(StaticTask_t)); *stack_ptr = memmgr_alloc_from_pool(sizeof(StackType_t) * configTIMER_TASK_STACK_DEPTH); *stack_size = configTIMER_TASK_STACK_DEPTH; -} \ No newline at end of file +} diff --git a/furi/furi.c b/furi/furi.c index 628c47ea24b..dca674da572 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 d8aec91c0f7..400cf1d6439 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" @@ -15,6 +16,7 @@ #include "core/record.h" #include "core/semaphore.h" #include "core/thread.h" +#include "core/thread_list.h" #include "core/timer.h" #include "core/string.h" #include "core/stream_buffer.h" diff --git a/lib/ble_profile/extra_services/hid_service.c b/lib/ble_profile/extra_services/hid_service.c index 92422d5d505..e46d2010c52 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 e755d2d6038..30a5b559a18 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 ec3fc22ee69..5fc25025708 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 3c4a07f2f97..a3509896f46 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 03dcd95e7a7..78b61132875 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 d0e294220e8..5caec162a6d 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/lfrfid_protocols.c b/lib/lfrfid/protocols/lfrfid_protocols.c index 175d64c2b27..f0c28f6753a 100644 --- a/lib/lfrfid/protocols/lfrfid_protocols.c +++ b/lib/lfrfid/protocols/lfrfid_protocols.c @@ -18,6 +18,7 @@ #include "protocol_keri.h" #include "protocol_gallagher.h" #include "protocol_nexwatch.h" +#include "protocol_securakey.h" const ProtocolBase* lfrfid_protocols[] = { [LFRFIDProtocolEM4100] = &protocol_em4100, @@ -41,4 +42,5 @@ const ProtocolBase* lfrfid_protocols[] = { [LFRFIDProtocolKeri] = &protocol_keri, [LFRFIDProtocolGallagher] = &protocol_gallagher, [LFRFIDProtocolNexwatch] = &protocol_nexwatch, + [LFRFIDProtocolSecurakey] = &protocol_securakey, }; diff --git a/lib/lfrfid/protocols/lfrfid_protocols.h b/lib/lfrfid/protocols/lfrfid_protocols.h index 89aa51c2518..c90e842af15 100644 --- a/lib/lfrfid/protocols/lfrfid_protocols.h +++ b/lib/lfrfid/protocols/lfrfid_protocols.h @@ -29,6 +29,7 @@ typedef enum { LFRFIDProtocolKeri, LFRFIDProtocolGallagher, LFRFIDProtocolNexwatch, + LFRFIDProtocolSecurakey, LFRFIDProtocolMax, } LFRFIDProtocol; diff --git a/lib/lfrfid/protocols/protocol_pac_stanley.c b/lib/lfrfid/protocols/protocol_pac_stanley.c index 67bc3bf48b3..4eabe365920 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/lfrfid/protocols/protocol_securakey.c b/lib/lfrfid/protocols/protocol_securakey.c new file mode 100644 index 00000000000..55048a592f1 --- /dev/null +++ b/lib/lfrfid/protocols/protocol_securakey.c @@ -0,0 +1,294 @@ +// The timing parameters and data structure used in this file +// are based on the knowledge found in Proxmark3's firmware: +// https://github.com/RfidResearchGroup/proxmark3/blob/1c52152d30f7744c0336633317ea6640dbcdc796/client/src/cmdlfsecurakey.c +// PM3's repo has mentioned the existence of non-26-or-32-bit formats. +// Those are not supported here for preventing false positives. +#include +#include +#include +#include +#include "lfrfid_protocols.h" +#include + +#define TAG "SECURAKEY" +#define SECURAKEY_ENCODED_SIZE_BITS (84) +#define SECURAKEY_PREAMBLE_SIZE_BITS (12) +#define SECURAKEY_ENCODED_FULL_SIZE_BITS \ + (SECURAKEY_ENCODED_SIZE_BITS + SECURAKEY_PREAMBLE_SIZE_BITS) +#define SECURAKEY_ENCODED_FULL_SIZE_BYTE (SECURAKEY_ENCODED_FULL_SIZE_BITS / 8) +#define SECURAKEY_DECODED_DATA_SIZE_BITS \ + (48) // 16-bit for facility code/number, 16-bit for card number, 16-bit for two checksum +#define SECURAKEY_DECODED_DATA_SIZE_BYTES (SECURAKEY_DECODED_DATA_SIZE_BITS / 8) +#define LFRFID_FREQUENCY (125000) +#define SECURAKEY_CLOCK_PER_BIT (40) // RF/40 +#define SECURAKEY_READ_LONG_TIME \ + (1000000 / (LFRFID_FREQUENCY / SECURAKEY_CLOCK_PER_BIT)) // 1000000 micro sec / sec +#define SECURAKEY_READ_SHORT_TIME (SECURAKEY_READ_LONG_TIME / 2) +#define SECURAKEY_READ_JITTER_TIME (SECURAKEY_READ_SHORT_TIME * 40 / 100) // 40% jitter tolerance +#define SECURAKEY_READ_SHORT_TIME_LOW \ + (SECURAKEY_READ_SHORT_TIME - \ + SECURAKEY_READ_JITTER_TIME) // these are used for manchester decoding +#define SECURAKEY_READ_SHORT_TIME_HIGH (SECURAKEY_READ_SHORT_TIME + SECURAKEY_READ_JITTER_TIME) +#define SECURAKEY_READ_LONG_TIME_LOW (SECURAKEY_READ_LONG_TIME - SECURAKEY_READ_JITTER_TIME) +#define SECURAKEY_READ_LONG_TIME_HIGH (SECURAKEY_READ_LONG_TIME + SECURAKEY_READ_JITTER_TIME) + +typedef struct { + uint8_t data[SECURAKEY_DECODED_DATA_SIZE_BYTES]; + uint8_t encoded_data[SECURAKEY_ENCODED_FULL_SIZE_BYTE]; + uint8_t encoded_data_index; + bool encoded_polarity; + ManchesterState decoder_manchester_state; + uint8_t bit_format; +} ProtocolSecurakey; + +ProtocolSecurakey* protocol_securakey_alloc(void) { + ProtocolSecurakey* protocol = malloc(sizeof(ProtocolSecurakey)); + return (void*)protocol; +}; + +void protocol_securakey_free(ProtocolSecurakey* protocol) { + free(protocol); +}; + +uint8_t* protocol_securakey_get_data(ProtocolSecurakey* protocol) { + return protocol->data; +}; + +static bool protocol_securakey_can_be_decoded(ProtocolSecurakey* protocol) { + // check 11 bits preamble + if(bit_lib_get_bits_16(protocol->encoded_data, 0, SECURAKEY_PREAMBLE_SIZE_BITS) == + 0b011111111100) { + if(bit_lib_get_bits(protocol->encoded_data, 13, 6) == 26 || + bit_lib_get_bits(protocol->encoded_data, 13, 6) == 32) { + return true; + } else { + return false; + } + } else { + return false; + } +}; + +static void protocol_securakey_decode(ProtocolSecurakey* protocol) { + memset(protocol->data, 0, SECURAKEY_DECODED_DATA_SIZE_BYTES); + // encoded_data looks like this (citation: pm3 repo): + // 26-bit format (1-bit even parity bit, 8-bit facility number, 16-bit card number, 1-bit odd parity bit) + // preamble ??bitlen reserved EPf fffffffc cccccccc cccccccOP CS? CS2? + // 0111111111 0 01011010 0 00000000 0 00000010 0 00110110 0 00111110 0 01100010 0 00001111 0 01100000 0 00000000 0 0000 + + // 32-bit format (1-bit even parity bit, 14-bit facility number, 16-bit card number, 1-bit odd parity bit) + // preamble ??bitlen reserved EPfffffff fffffffc cccccccc cccccccOP CS? CS2? + // 0111111111 0 01100000 0 00000000 0 10000100 0 11001010 0 01011011 0 01010110 0 00010110 0 11100000 0 00000000 0 0000 + + // left two 0 paddings in the beginning for easier parsing (00011010 = 011010) + // get facility number (f) + if(bit_lib_get_bits(protocol->encoded_data, 13, 6) == 26) { + FURI_LOG_D(TAG, "26-bit Securakey detected"); + protocol->bit_format = 26; + bit_lib_copy_bits(protocol->data, 8, 1, protocol->encoded_data, 36); + // have to skip one spacer + bit_lib_copy_bits(protocol->data, 9, 7, protocol->encoded_data, 38); + } else if(bit_lib_get_bits(protocol->encoded_data, 13, 6) == 32) { + FURI_LOG_D(TAG, "32-bit Securakey detected"); + protocol->bit_format = 32; + // same two 0 paddings here, otherwise should be bit_lib_copy_bits(protocol->data, 8, 7, protocol->encoded_data, 30); + bit_lib_copy_bits(protocol->data, 2, 7, protocol->encoded_data, 30); + // have to skip one spacer + bit_lib_copy_bits(protocol->data, 9, 7, protocol->encoded_data, 38); + } + + // get card number (c) + bit_lib_copy_bits(protocol->data, 16, 1, protocol->encoded_data, 45); + // same skips here + bit_lib_copy_bits(protocol->data, 17, 8, protocol->encoded_data, 47); + bit_lib_copy_bits(protocol->data, 25, 7, protocol->encoded_data, 56); + + // unsure about CS yet, might as well just save it + // CS1 + bit_lib_copy_bits(protocol->data, 32, 8, protocol->encoded_data, 65); + // CS2 + bit_lib_copy_bits(protocol->data, 40, 8, protocol->encoded_data, 74); + + // (decoded) data looks like this (pp are zero paddings): + // 26-bit format (1-bit EP, 8-bit facility number, 16-bit card number, 1-bit OP) + // pppppppp ffffffff cccccccc cccccccc CS1 CS2 + // 00000000 00011011 00011111 00110001 00001111 01100000 + + // 32-bit format (1-bit EP, 14-bit facility number, 16-bit card number, 1-bit OP) + // ppffffff ffffffff cccccccc cccccccc CS1 CS2 + // 00000010 01100101 00101101 10101011 00010110 11100000 +}; + +void protocol_securakey_decoder_start(ProtocolSecurakey* protocol) { + memset(protocol->encoded_data, 0, SECURAKEY_ENCODED_FULL_SIZE_BYTE); + manchester_advance( + protocol->decoder_manchester_state, + ManchesterEventReset, + &protocol->decoder_manchester_state, + NULL); +}; + +bool protocol_securakey_decoder_feed(ProtocolSecurakey* protocol, bool level, uint32_t duration) { + bool result = false; + // this is where we do manchester demodulation on already ASK-demoded data + ManchesterEvent event = ManchesterEventReset; + if(duration > SECURAKEY_READ_SHORT_TIME_LOW && duration < SECURAKEY_READ_SHORT_TIME_HIGH) { + if(!level) { + event = ManchesterEventShortHigh; + } else { + event = ManchesterEventShortLow; + } + } else if(duration > SECURAKEY_READ_LONG_TIME_LOW && duration < SECURAKEY_READ_LONG_TIME_HIGH) { + if(!level) { + event = ManchesterEventLongHigh; + } else { + event = ManchesterEventLongLow; + } + } + // append a new bit to the encoded bit stream + if(event != ManchesterEventReset) { + bool data; + bool data_ok = manchester_advance( + protocol->decoder_manchester_state, event, &protocol->decoder_manchester_state, &data); + if(data_ok) { + bit_lib_push_bit(protocol->encoded_data, SECURAKEY_ENCODED_FULL_SIZE_BYTE, data); + if(protocol_securakey_can_be_decoded(protocol)) { + protocol_securakey_decode(protocol); + result = true; + } + } + } + return result; +}; + +void protocol_securakey_render_data(ProtocolSecurakey* protocol, FuriString* result) { + if(bit_lib_get_bits(protocol->data, 0, 8) == 0) { + protocol->bit_format = 26; + } else { + protocol->bit_format = 32; + } + furi_string_printf( + result, + "%u-bit format\nFacility code: %u\nCard number: %u", + protocol->bit_format, + bit_lib_get_bits_16(protocol->data, 0, 16), + bit_lib_get_bits_16(protocol->data, 16, 16)); +}; + +bool protocol_securakey_encoder_start(ProtocolSecurakey* protocol) { + // set all of our encoded_data bits to zeros. + memset(protocol->encoded_data, 0, SECURAKEY_ENCODED_FULL_SIZE_BYTE); + + // write the preamble to the beginning of the encoded_data + bit_lib_set_bits(protocol->encoded_data, 0, 0b01111111, 8); + bit_lib_set_bits(protocol->encoded_data, 8, 0b11001, 5); + + if(bit_lib_get_bits(protocol->data, 0, 8) == 0) { + protocol->bit_format = 26; + // set bit length + bit_lib_set_bits(protocol->encoded_data, 13, protocol->bit_format, 6); + // set even parity & odd parity + if(!bit_lib_test_parity(protocol->data, 8, 12, BitLibParityOdd, 12)) { + bit_lib_set_bit(protocol->encoded_data, 35, 1); + } + if(bit_lib_test_parity(protocol->data, 20, 12, BitLibParityOdd, 12)) { + bit_lib_set_bit(protocol->encoded_data, 63, 1); + } + // write facility number (f) + bit_lib_copy_bits(protocol->encoded_data, 36, 1, protocol->data, 8); + // have to skip one spacer + bit_lib_copy_bits(protocol->encoded_data, 38, 7, protocol->data, 9); + + } else { + protocol->bit_format = 32; + // set bit length + bit_lib_set_bits(protocol->encoded_data, 13, protocol->bit_format, 6); + // set EP & OP + if(!bit_lib_test_parity(protocol->data, 2, 15, BitLibParityOdd, 15)) { + bit_lib_set_bit(protocol->encoded_data, 29, 1); + } + if(bit_lib_test_parity(protocol->data, 17, 15, BitLibParityOdd, 15)) { + bit_lib_set_bit(protocol->encoded_data, 63, 1); + } + // write facility number (f) + bit_lib_copy_bits(protocol->encoded_data, 30, 7, protocol->data, 2); + // have to skip one spacer + bit_lib_copy_bits(protocol->encoded_data, 38, 7, protocol->data, 3); + } + + // write card number (c) + bit_lib_copy_bits(protocol->encoded_data, 45, 1, protocol->data, 16); + // same skips here + bit_lib_copy_bits(protocol->encoded_data, 47, 8, protocol->data, 17); + bit_lib_copy_bits(protocol->encoded_data, 56, 7, protocol->data, 25); + + // unsure about CS yet might as well just copy it from saved + // CS1 + bit_lib_copy_bits(protocol->encoded_data, 65, 8, protocol->data, 32); + // CS2 + bit_lib_copy_bits(protocol->encoded_data, 74, 8, protocol->data, 40); + + // for sending we start at bit 0. + protocol->encoded_data_index = 0; + protocol->encoded_polarity = true; + return true; +}; + +LevelDuration protocol_securakey_encoder_yield(ProtocolSecurakey* protocol) { + bool level = bit_lib_get_bit(protocol->encoded_data, protocol->encoded_data_index); + uint32_t duration = SECURAKEY_CLOCK_PER_BIT / 2; + if(protocol->encoded_polarity) { + protocol->encoded_polarity = false; + } else { + level = !level; + protocol->encoded_polarity = true; + bit_lib_increment_index(protocol->encoded_data_index, SECURAKEY_ENCODED_FULL_SIZE_BITS); + } + return level_duration_make(level, duration); +}; + +bool protocol_securakey_write_data(ProtocolSecurakey* protocol, void* data) { + LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data; + bool result = false; + // Correct protocol data by redecoding + protocol_securakey_encoder_start(protocol); + protocol_securakey_decode(protocol); + protocol_securakey_encoder_start(protocol); + // Write T5577 + if(request->write_type == LFRFIDWriteTypeT5577) { + request->t5577.block[0] = + (LFRFID_T5577_MODULATION_MANCHESTER | LFRFID_T5577_BITRATE_RF_40 | + (3 + << LFRFID_T5577_MAXBLOCK_SHIFT)); // we only need 3 32-bit blocks for our 96-bit encoded data + request->t5577.block[1] = bit_lib_get_bits_32(protocol->encoded_data, 0, 32); + request->t5577.block[2] = bit_lib_get_bits_32(protocol->encoded_data, 32, 32); + request->t5577.block[3] = bit_lib_get_bits_32(protocol->encoded_data, 64, 32); + request->t5577.blocks_to_write = 4; + result = true; + } + return result; +}; + +const ProtocolBase protocol_securakey = { + .name = "Radio Key", + .manufacturer = "Securakey", + .data_size = SECURAKEY_DECODED_DATA_SIZE_BYTES, + .features = LFRFIDFeatureASK, + .validate_count = 3, + .alloc = (ProtocolAlloc)protocol_securakey_alloc, + .free = (ProtocolFree)protocol_securakey_free, + .get_data = (ProtocolGetData)protocol_securakey_get_data, + .decoder = + { + .start = (ProtocolDecoderStart)protocol_securakey_decoder_start, + .feed = (ProtocolDecoderFeed)protocol_securakey_decoder_feed, + }, + .encoder = + { + .start = (ProtocolEncoderStart)protocol_securakey_encoder_start, + .yield = (ProtocolEncoderYield)protocol_securakey_encoder_yield, + }, + .render_data = (ProtocolRenderData)protocol_securakey_render_data, + .render_brief_data = (ProtocolRenderData)protocol_securakey_render_data, + .write_data = (ProtocolWriteData)protocol_securakey_write_data, +}; \ No newline at end of file diff --git a/lib/lfrfid/protocols/protocol_securakey.h b/lib/lfrfid/protocols/protocol_securakey.h new file mode 100644 index 00000000000..ad990f119c1 --- /dev/null +++ b/lib/lfrfid/protocols/protocol_securakey.h @@ -0,0 +1,4 @@ +#pragma once +#include + +extern const ProtocolBase protocol_securakey; \ No newline at end of file diff --git a/lib/nfc/SConscript b/lib/nfc/SConscript index c6caffdb083..82b87ea2aa1 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 d5e0853a109..bba7cee346e 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 df0e009e79a..a17f0e10465 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 bc47e064226..89e610f86a3 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 d032943d35d..17229c150ac 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 4e6c0578547..90215eced29 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 00000000000..caab11778f6 --- /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 4fa25a16209..43c03a608e4 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 d4366e7672a..b386f4b4b39 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 f7726be32c9..8ec4b2889ab 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 f7df4c84502..9857d96d602 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 00000000000..f20ff08e19d --- /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 00000000000..fca5a0bfcb6 --- /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 751a1c4ff84..52ba7e8456d 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 cac7ed82665..00000000000 --- 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 b859f9b8e47..6f849b1d463 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 00000000000..32d8d6c5996 --- /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 00000000000..31559ffdc8b --- /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 00000000000..d5fe5be82f7 --- /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 00000000000..1b80030f93d --- /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 00000000000..c93ec9e67c1 --- /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 00000000000..7e892366fa0 --- /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 00000000000..366eb5116e8 --- /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 00000000000..cab906f1d15 --- /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 00000000000..c7b547a8489 --- /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 74436dc0895..6300801ab8e 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 5e348a0c91a..6c6a83a1702 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 0c7f9f8038c..bc4ebd51559 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 870bcafd9e0..6a145445cba 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 7553c74de1b..c007740b7b4 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 63f05c8d9e4..40201843e1a 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 ee634533356..cf74972f74a 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 0fcafe67cbe..996a37d1e6f 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 f457cd29c7d..8df2d5c72af 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 15719b2acf7..00000000000 --- 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 a5332ee4e25..8f62ccb1f64 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 7fe95299555..b9af72a579b 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 aa15b8b41c9..59175de8f3c 100644 --- a/lib/subghz/protocols/princeton.c +++ b/lib/subghz/protocols/princeton.c @@ -13,6 +13,7 @@ */ #define TAG "SubGhzProtocolPrinceton" +#define PRINCETON_GUARD_TIME_DEFALUT 30 //GUARD_TIME = PRINCETON_GUARD_TIME_DEFALUT * TE static const SubGhzBlockConst subghz_protocol_princeton_const = { .te_short = 390, @@ -29,6 +30,7 @@ struct SubGhzProtocolDecoderPrinceton { uint32_t te; uint32_t last_data; + uint32_t guard_time; }; struct SubGhzProtocolEncoderPrinceton { @@ -38,6 +40,7 @@ struct SubGhzProtocolEncoderPrinceton { SubGhzBlockGeneric generic; uint32_t te; + uint32_t guard_time; }; typedef enum { @@ -135,8 +138,9 @@ static bool //Send Stop bit instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)instance->te); - //Send PT_GUARD - instance->encoder.upload[index++] = level_duration_make(false, (uint32_t)instance->te * 30); + //Send PT_GUARD_TIME + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)instance->te * instance->guard_time); return true; } @@ -165,6 +169,11 @@ SubGhzProtocolStatus break; } //optional parameter parameter + if(!flipper_format_read_uint32( + flipper_format, "Guard_time", (uint32_t*)&instance->guard_time, 1)) { + instance->guard_time = PRINCETON_GUARD_TIME_DEFALUT; + } + flipper_format_read_uint32( flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); @@ -235,6 +244,7 @@ void subghz_protocol_decoder_princeton_feed(void* context, bool level, uint32_t instance->decoder.decode_data = 0; instance->decoder.decode_count_bit = 0; instance->te = 0; + instance->guard_time = PRINCETON_GUARD_TIME_DEFALUT; } break; case PrincetonDecoderStepSaveDuration: @@ -257,6 +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 = roundf((float)duration / instance->te); if(instance->base.callback) instance->base.callback(&instance->base, instance->base.context); @@ -323,6 +334,12 @@ SubGhzProtocolStatus subghz_protocol_decoder_princeton_serialize( FURI_LOG_E(TAG, "Unable to add TE"); ret = SubGhzProtocolStatusErrorParserTe; } + if((ret == SubGhzProtocolStatusOk) && + !flipper_format_write_uint32(flipper_format, "Guard_time", &instance->guard_time, 1)) { + FURI_LOG_E(TAG, "Unable to add Guard_time"); + ret = SubGhzProtocolStatusErrorParserOthers; + } + return ret; } @@ -349,6 +366,10 @@ SubGhzProtocolStatus ret = SubGhzProtocolStatusErrorParserTe; break; } + if(!flipper_format_read_uint32( + flipper_format, "Guard_time", (uint32_t*)&instance->guard_time, 1)) { + instance->guard_time = PRINCETON_GUARD_TIME_DEFALUT; + } } while(false); return ret; diff --git a/lib/subghz/protocols/protocol_items.c b/lib/subghz/protocols/protocol_items.c index 472a354e384..e50d52ac1d3 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 12599891d54..b5677f9c29b 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 c70229a12dd..88fac4e34b6 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 aacf2b4fec4..ed1120a2002 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 4f7922c353e..20247d339f3 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 93f94cc7483..8c7f50d5dca 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 38b94eb86ac..239e5325012 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 639f2eaec7a..25084aaa045 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/ccache.py b/scripts/fbt_tools/ccache.py index 63577ab781f..a7e546422b2 100644 --- a/scripts/fbt_tools/ccache.py +++ b/scripts/fbt_tools/ccache.py @@ -12,3 +12,4 @@ def generate(env): env["LINK"] = env["CXX"] env["CXX_NOCACHE"] = env["CXX"] env["CXX"] = "$CCACHE $CXX_NOCACHE" + env.AppendUnique(COMPILATIONDB_OMIT_BINARIES=["ccache"]) diff --git a/scripts/fbt_tools/compilation_db.py b/scripts/fbt_tools/compilation_db.py index 3d5e469f4e0..6bad96b2dc1 100644 --- a/scripts/fbt_tools/compilation_db.py +++ b/scripts/fbt_tools/compilation_db.py @@ -32,7 +32,7 @@ import fnmatch import itertools import json -from shlex import quote +from shlex import join, split import SCons from SCons.Tool.asm import ASPPSuffixes, ASSuffixes @@ -108,6 +108,10 @@ def emit_compilation_db_entry(target, source, env): return emit_compilation_db_entry +def __is_value_true(value): + return value in [True, 1, "True", "true"] + + def compilation_db_entry_action(target, source, env, **kw): """ Create a dictionary with evaluated command line, target, source @@ -126,16 +130,19 @@ def compilation_db_entry_action(target, source, env, **kw): env=env["__COMPILATIONDB_ENV"], ) - # We assume first non-space character is the executable - executable = command.split(" ", 1)[0] - if not (tool_path := _TOOL_PATH_CACHE.get(executable, None)): - tool_path = env.WhereIs(executable) or executable - _TOOL_PATH_CACHE[executable] = tool_path - # If there are spaces in the executable path, we need to quote it - if " " in tool_path: - tool_path = quote(tool_path) - # Replacing the executable with the full path - command = tool_path + command[len(executable) :] + cmdline = split(command) + binaries_to_omit = env["COMPILATIONDB_OMIT_BINARIES"] + while (executable := cmdline[0]) in binaries_to_omit: + cmdline.pop(0) + + if __is_value_true(env["COMPILATIONDB_USE_BINARY_ABSPATH"]): + if not (tool_path := _TOOL_PATH_CACHE.get(executable, None)): + tool_path = env.WhereIs(executable) or executable + _TOOL_PATH_CACHE[executable] = tool_path + # Replacing the executable with the full path + executable = tool_path + + command = join((executable, *cmdline[1:])) entry = { "directory": env.Dir("#").abspath, @@ -150,7 +157,7 @@ def compilation_db_entry_action(target, source, env, **kw): def write_compilation_db(target, source, env): entries = [] - use_abspath = env["COMPILATIONDB_USE_ABSPATH"] in [True, 1, "True", "true"] + use_abspath = __is_value_true(env["COMPILATIONDB_USE_ABSPATH"]) use_path_filter = env.subst("$COMPILATIONDB_PATH_FILTER") use_srcpath_filter = env.subst("$COMPILATIONDB_SRCPATH_FILTER") @@ -225,6 +232,8 @@ def generate(env, **kwargs): COMPILATIONDB_USE_ABSPATH=False, COMPILATIONDB_PATH_FILTER="", COMPILATIONDB_SRCPATH_FILTER="", + COMPILATIONDB_OMIT_BINARIES=[], + COMPILATIONDB_USE_BINARY_ABSPATH=False, ) components_by_suffix = itertools.chain( diff --git a/scripts/fbt_tools/fbt_hwtarget.py b/scripts/fbt_tools/fbt_hwtarget.py index a3b0d4a788c..cce24709895 100644 --- a/scripts/fbt_tools/fbt_hwtarget.py +++ b/scripts/fbt_tools/fbt_hwtarget.py @@ -76,7 +76,9 @@ def _processTargetDefinitions(self, target_id): 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/scripts/ufbt/SConstruct b/scripts/ufbt/SConstruct index 784d6616121..26b1046ee34 100644 --- a/scripts/ufbt/SConstruct +++ b/scripts/ufbt/SConstruct @@ -102,6 +102,7 @@ env = core_env.Clone( core_env.subst("$SDK_DEFINITION"), load_version_only=True ).version, APPCHECK_COMSTR="\tAPPCHK\t${SOURCE}\n\t\tTarget: ${TARGET_HW}, API: ${UFBT_API_VERSION}", + COMPILATIONDB_USE_BINARY_ABSPATH=True, ) wrap_tempfile(env, "LINKCOM") diff --git a/targets/f18/api_symbols.csv b/targets/f18/api_symbols.csv index 04ceb928cbc..4b40107ce35 100644 --- a/targets/f18/api_symbols.csv +++ b/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,62.3,, +Version,+,65.0,, 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* @@ -1157,7 +1164,7 @@ Function,+,furi_hal_bus_is_enabled,_Bool,FuriHalBus Function,+,furi_hal_bus_reset,void,FuriHalBus Function,+,furi_hal_ccid_ccid_insert_smartcard,void, Function,+,furi_hal_ccid_ccid_remove_smartcard,void, -Function,+,furi_hal_ccid_set_callbacks,void,CcidCallbacks* +Function,+,furi_hal_ccid_set_callbacks,void,"CcidCallbacks*, void*" Function,+,furi_hal_cdc_get_ctrl_line_state,uint8_t,uint8_t Function,+,furi_hal_cdc_get_port_settings,usb_cdc_line_coding*,uint8_t Function,+,furi_hal_cdc_receive,int32_t,"uint8_t, uint8_t*, uint16_t" @@ -1264,6 +1271,7 @@ Function,+,furi_hal_info_get,void,"PropertyValueCallback, char, void*" Function,+,furi_hal_info_get_api_version,void,"uint16_t*, uint16_t*" Function,-,furi_hal_init,void, Function,-,furi_hal_init_early,void, +Function,+,furi_hal_interrupt_get_name,const char*,uint8_t Function,-,furi_hal_interrupt_init,void, Function,+,furi_hal_interrupt_set_isr,void,"FuriHalInterruptId, FuriHalInterruptISR, void*" Function,+,furi_hal_interrupt_set_isr_ex,void,"FuriHalInterruptId, FuriHalInterruptPriority, FuriHalInterruptISR, void*" @@ -1583,9 +1591,10 @@ 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" +Function,+,furi_thread_enumerate,_Bool,FuriThreadList* Function,+,furi_thread_flags_clear,uint32_t,uint32_t Function,+,furi_thread_flags_get,uint32_t, Function,+,furi_thread_flags_set,uint32_t,"FuriThreadId, uint32_t" @@ -1605,7 +1614,12 @@ 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_list_alloc,FuriThreadList*, +Function,+,furi_thread_list_free,void,FuriThreadList* +Function,+,furi_thread_list_get_at,FuriThreadListItem*,"FuriThreadList*, size_t" +Function,+,furi_thread_list_get_or_insert,FuriThreadListItem*,"FuriThreadList*, FuriThread*" +Function,+,furi_thread_list_process,void,"FuriThreadList*, uint32_t, uint32_t" +Function,+,furi_thread_list_size,size_t,FuriThreadList* 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 +2710,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 e7a97aff95c..d64571963e5 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,62.3,, +Version,+,65.0,, 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,, @@ -1005,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*" @@ -1016,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" @@ -1197,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* @@ -1256,7 +1269,7 @@ Function,+,furi_hal_bus_is_enabled,_Bool,FuriHalBus Function,+,furi_hal_bus_reset,void,FuriHalBus Function,+,furi_hal_ccid_ccid_insert_smartcard,void, Function,+,furi_hal_ccid_ccid_remove_smartcard,void, -Function,+,furi_hal_ccid_set_callbacks,void,CcidCallbacks* +Function,+,furi_hal_ccid_set_callbacks,void,"CcidCallbacks*, void*" Function,+,furi_hal_cdc_get_ctrl_line_state,uint8_t,uint8_t Function,+,furi_hal_cdc_get_port_settings,usb_cdc_line_coding*,uint8_t Function,+,furi_hal_cdc_receive,int32_t,"uint8_t, uint8_t*, uint16_t" @@ -1383,6 +1396,7 @@ Function,+,furi_hal_infrared_is_busy,_Bool, Function,+,furi_hal_infrared_set_tx_output,void,FuriHalInfraredTxPin Function,-,furi_hal_init,void, Function,-,furi_hal_init_early,void, +Function,+,furi_hal_interrupt_get_name,const char*,uint8_t Function,-,furi_hal_interrupt_init,void, Function,+,furi_hal_interrupt_set_isr,void,"FuriHalInterruptId, FuriHalInterruptISR, void*" Function,+,furi_hal_interrupt_set_isr_ex,void,"FuriHalInterruptId, FuriHalInterruptPriority, FuriHalInterruptISR, void*" @@ -1791,9 +1805,10 @@ 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" +Function,+,furi_thread_enumerate,_Bool,FuriThreadList* Function,+,furi_thread_flags_clear,uint32_t,uint32_t Function,+,furi_thread_flags_get,uint32_t, Function,+,furi_thread_flags_set,uint32_t,"FuriThreadId, uint32_t" @@ -1813,7 +1828,12 @@ 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_list_alloc,FuriThreadList*, +Function,+,furi_thread_list_free,void,FuriThreadList* +Function,+,furi_thread_list_get_at,FuriThreadListItem*,"FuriThreadList*, size_t" +Function,+,furi_thread_list_get_or_insert,FuriThreadListItem*,"FuriThreadList*, FuriThread*" +Function,+,furi_thread_list_process,void,"FuriThreadList*, uint32_t, uint32_t" +Function,+,furi_thread_list_size,size_t,FuriThreadList* Function,+,furi_thread_resume,void,FuriThreadId Function,+,furi_thread_set_appid,void,"FuriThread*, const char*" Function,+,furi_thread_set_callback,void,"FuriThread*, FuriThreadCallback" @@ -2520,6 +2540,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* @@ -2533,6 +2566,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*" @@ -3510,6 +3544,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" @@ -3830,6 +3865,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 01bb021b655..b6496290a2f 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._*) - - KEEP (*(.init)) - KEEP (*(.fini)) - } - - .rodata : - { - *(.rodata) - *(.rodata1) - *(.rodata.*) - } - - .data : - { - *(.data) - *(.data1) - *(.data.*) - } - - - .bss : - { - *(.bss) - *(.bss*) - *(.sbss) - *(.sbss*) - *(COMMON) - } - - .ARM.attributes : - { - *(.ARM.attributes) - *(.ARM.attributes.*) - } - - /DISCARD/ : - { - *(.comment) - *(.comment.*) - *(.llvmbc) - *(.llvmcmd) - } +SECTIONS { + .text 0x00000000 : ALIGN(4) { + *(.text) + *(.stub) + *(.text*) + *(.text.*) + *(.text._*) + + KEEP (*(.init)) + KEEP (*(.fini)) + } + + .rodata : { + *(.rodata) + *(.rodata1) + *(.rodata.*) + } + + .data : { + *(.data) + *(.data1) + *(.data.*) + } + + + .bss : { + *(.bss) + *(.bss*) + *(.sbss) + *(.sbss*) + *(COMMON) + } + + .ARM.attributes : { + *(.ARM.attributes) + *(.ARM.attributes.*) + } + + /DISCARD/ : { + *(.comment) + *(.comment.*) + *(.llvmbc) + *(.llvmcmd) + } } diff --git a/targets/f7/furi_hal/furi_hal.c b/targets/f7/furi_hal/furi_hal.c index 3a58fe196b6..7424b5e821f 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 9cf64acc589..8999edaedb2 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 5c2c315ef4f..15a9a819fa3 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,7 +313,10 @@ void USB_LP_IRQHandler(void) { #endif } -void USB_HP_IRQHandler(void) { +void USB_HP_IRQHandler(void) { //-V524 +#ifndef FURI_RAM_EXEC + usbd_poll(&udev); +#endif } void IPCC_C1_TX_IRQHandler(void) { @@ -347,4 +345,157 @@ void USART1_IRQHandler(void) { void LPUART1_IRQHandler(void) { furi_hal_interrupt_call(FuriHalInterruptIdLpUart1); -} \ No newline at end of file +} + +const char* furi_hal_interrupt_get_name(uint8_t exception_number) { + int32_t id = (int32_t)exception_number - 16; + + switch(id) { + case -14: + return "NMI"; + case -13: + return "HardFault"; + case -12: + return "MemMgmt"; + case -11: + return "BusFault"; + case -10: + return "UsageFault"; + case -5: + return "SVC"; + case -4: + return "DebugMon"; + case -2: + return "PendSV"; + case -1: + return "SysTick"; + case 0: + return "WWDG"; + case 1: + return "PVD_PVM"; + case 2: + return "TAMP"; + case 3: + return "RTC_WKUP"; + case 4: + return "FLASH"; + case 5: + return "RCC"; + case 6: + return "EXTI0"; + case 7: + return "EXTI1"; + case 8: + return "EXTI2"; + case 9: + return "EXTI3"; + case 10: + return "EXTI4"; + case 11: + return "DMA1_Ch1"; + case 12: + return "DMA1_Ch2"; + case 13: + return "DMA1_Ch3"; + case 14: + return "DMA1_Ch4"; + case 15: + return "DMA1_Ch5"; + case 16: + return "DMA1_Ch6"; + case 17: + return "DMA1_Ch7"; + case 18: + return "ADC1"; + case 19: + return "USB_HP"; + case 20: + return "USB_LP"; + case 21: + return "C2SEV_PWR_C2H"; + case 22: + return "COMP"; + case 23: + return "EXTI9_5"; + case 24: + return "TIM1_BRK"; + case 25: + return "TIM1_UP_TIM16"; + case 26: + return "TIM1_TRG_COM_TIM17"; + case 27: + return "TIM1_CC"; + case 28: + return "TIM2"; + case 29: + return "PKA"; + case 30: + return "I2C1_EV"; + case 31: + return "I2C1_ER"; + case 32: + return "I2C3_EV"; + case 33: + return "I2C3_ER"; + case 34: + return "SPI1"; + case 35: + return "SPI2"; + case 36: + return "USART1"; + case 37: + return "LPUART1"; + case 38: + return "SAI1"; + case 39: + return "TSC"; + case 40: + return "EXTI15_10"; + case 41: + return "RTC_Alarm"; + case 42: + return "CRS"; + case 43: + return "PWR_SOTF_BLE"; + case 44: + return "IPCC_C1_RX"; + case 45: + return "IPCC_C1_TX"; + case 46: + return "HSEM"; + case 47: + return "LPTIM1"; + case 48: + return "LPTIM2"; + case 49: + return "LCD"; + case 50: + return "QUADSPI"; + case 51: + return "AES1"; + case 52: + return "AES2"; + case 53: + return "RNG"; + case 54: + return "FPU"; + case 55: + return "DMA2_Ch1"; + case 56: + return "DMA2_Ch2"; + case 57: + return "DMA2_Ch3"; + case 58: + return "DMA2_Ch4"; + case 59: + return "DMA2_Ch5"; + case 60: + return "DMA2_Ch6"; + case 61: + return "DMA2_Ch7"; + case 62: + return "DMAMUX1_OVR"; + default: + return NULL; + } +} diff --git a/targets/f7/furi_hal/furi_hal_interrupt.h b/targets/f7/furi_hal/furi_hal_interrupt.h index 74bb768ce7d..c06ec23d20f 100644 --- a/targets/f7/furi_hal/furi_hal_interrupt.h +++ b/targets/f7/furi_hal/furi_hal_interrupt.h @@ -110,6 +110,14 @@ void furi_hal_interrupt_set_isr_ex( FuriHalInterruptISR isr, void* context); +/** Get interrupt name by exception number. + * Exception number can be obtained from IPSR register. + * + * @param exception_number + * @return const char* or NULL if interrupt name is not found + */ +const char* furi_hal_interrupt_get_name(uint8_t exception_number); + #ifdef __cplusplus } #endif diff --git a/targets/f7/furi_hal/furi_hal_mpu.c b/targets/f7/furi_hal/furi_hal_mpu.c index 16724c97557..5fe3ab66bbc 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 e434e6a3562..9bcd2f1fe63 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 82239fbc124..89305877eff 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 d2ab414c264..24171610760 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 a482940e216..22d1523b636 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/furi_hal/furi_hal_usb_ccid.c b/targets/f7/furi_hal/furi_hal_usb_ccid.c index d8b43fc6c7b..5e2fe77b776 100644 --- a/targets/f7/furi_hal/furi_hal_usb_ccid.c +++ b/targets/f7/furi_hal/furi_hal_usb_ccid.c @@ -94,7 +94,7 @@ static const struct CcidConfigDescriptor ccid_cfg_desc = { .bConfigurationValue = 1, .iConfiguration = NO_DESCRIPTOR, .bmAttributes = USB_CFG_ATTR_RESERVED | USB_CFG_ATTR_SELFPOWERED, - .bMaxPower = USB_CFG_POWER_MA(100), + .bMaxPower = USB_CFG_POWER_MA(500), }, .intf_0 = { @@ -184,6 +184,7 @@ static usbd_device* usb_dev; static bool connected = false; static bool smartcard_inserted = true; static CcidCallbacks* callbacks[CCID_TOTAL_SLOTS] = {NULL}; +static void* cb_ctx[CCID_TOTAL_SLOTS]; static void* ccid_set_string_descr(char* str) { furi_check(str); @@ -330,7 +331,9 @@ void CALLBACK_CCID_IccPowerOn( if(smartcard_inserted) { if(callbacks[CCID_SLOT_INDEX] != NULL) { callbacks[CCID_SLOT_INDEX]->icc_power_on_callback( - responseDataBlock->abData, &responseDataBlock->dwLength, NULL); + responseDataBlock->abData, + &responseDataBlock->dwLength, + cb_ctx[CCID_SLOT_INDEX]); responseDataBlock->bStatus = CCID_COMMANDSTATUS_PROCESSEDWITHOUTERROR | CCID_ICCSTATUS_PRESENTANDACTIVE; } else { @@ -364,7 +367,7 @@ void CALLBACK_CCID_XfrBlock( receivedXfrBlock->dwLength, responseDataBlock->abData, &responseDataBlock->dwLength, - NULL); + cb_ctx[CCID_SLOT_INDEX]); responseDataBlock->bStatus = CCID_COMMANDSTATUS_PROCESSEDWITHOUTERROR | CCID_ICCSTATUS_PRESENTANDACTIVE; } else { @@ -389,8 +392,9 @@ void furi_hal_ccid_ccid_remove_smartcard(void) { smartcard_inserted = false; } -void furi_hal_ccid_set_callbacks(CcidCallbacks* cb) { +void furi_hal_ccid_set_callbacks(CcidCallbacks* cb, void* context) { callbacks[CCID_SLOT_INDEX] = cb; + cb_ctx[CCID_SLOT_INDEX] = context; } static void ccid_rx_ep_callback(usbd_device* dev, uint8_t event, uint8_t ep) { diff --git a/targets/f7/furi_hal/furi_hal_usb_cdc.c b/targets/f7/furi_hal/furi_hal_usb_cdc.c index 4679df14fb8..8525022c09f 100644 --- a/targets/f7/furi_hal/furi_hal_usb_cdc.c +++ b/targets/f7/furi_hal/furi_hal_usb_cdc.c @@ -7,13 +7,13 @@ #include "usb.h" #include "usb_cdc.h" -#define CDC0_RXD_EP 0x02 +#define CDC0_RXD_EP 0x01 #define CDC0_TXD_EP 0x82 -#define CDC0_NTF_EP 0x81 +#define CDC0_NTF_EP 0x83 #define CDC1_RXD_EP 0x04 -#define CDC1_TXD_EP 0x84 -#define CDC1_NTF_EP 0x83 +#define CDC1_TXD_EP 0x85 +#define CDC1_NTF_EP 0x86 #define CDC_NTF_SZ 0x08 @@ -75,7 +75,7 @@ static const struct CdcConfigDescriptorSingle cdc_cfg_desc_single = { .bConfigurationValue = 1, .iConfiguration = NO_DESCRIPTOR, .bmAttributes = USB_CFG_ATTR_RESERVED | USB_CFG_ATTR_SELFPOWERED, - .bMaxPower = USB_CFG_POWER_MA(100), + .bMaxPower = USB_CFG_POWER_MA(500), }, .iad_0 = { @@ -188,7 +188,7 @@ static const struct CdcConfigDescriptorDual .bConfigurationValue = 1, .iConfiguration = NO_DESCRIPTOR, .bmAttributes = USB_CFG_ATTR_RESERVED | USB_CFG_ATTR_SELFPOWERED, - .bMaxPower = USB_CFG_POWER_MA(100), + .bMaxPower = USB_CFG_POWER_MA(500), }, .iad_0 = { diff --git a/targets/f7/furi_hal/furi_hal_usb_hid.c b/targets/f7/furi_hal/furi_hal_usb_hid.c index 5d1a9c46e4f..9599aa1c12b 100644 --- a/targets/f7/furi_hal/furi_hal_usb_hid.c +++ b/targets/f7/furi_hal/furi_hal_usb_hid.c @@ -155,7 +155,7 @@ static const struct HidConfigDescriptor hid_cfg_desc = { .bConfigurationValue = 1, .iConfiguration = NO_DESCRIPTOR, .bmAttributes = USB_CFG_ATTR_RESERVED | USB_CFG_ATTR_SELFPOWERED, - .bMaxPower = USB_CFG_POWER_MA(100), + .bMaxPower = USB_CFG_POWER_MA(500), }, .intf_0 = { diff --git a/targets/f7/furi_hal/furi_hal_usb_u2f.c b/targets/f7/furi_hal/furi_hal_usb_u2f.c index 2a014093e91..390070c8f63 100644 --- a/targets/f7/furi_hal/furi_hal_usb_u2f.c +++ b/targets/f7/furi_hal/furi_hal_usb_u2f.c @@ -79,7 +79,7 @@ static const struct HidConfigDescriptor hid_u2f_cfg_desc = { .bConfigurationValue = 1, .iConfiguration = NO_DESCRIPTOR, .bmAttributes = USB_CFG_ATTR_RESERVED | USB_CFG_ATTR_SELFPOWERED, - .bMaxPower = USB_CFG_POWER_MA(100), + .bMaxPower = USB_CFG_POWER_MA(500), }, .iad_0 = { diff --git a/targets/f7/inc/FreeRTOSConfig.h b/targets/f7/inc/FreeRTOSConfig.h index 36800565c32..37aac1eb092 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) @@ -29,13 +29,9 @@ // #define configTOTAL_HEAP_SIZE ((size_t)0) #define configMAX_TASK_NAME_LEN (32) -/* Run-time stats - broken ATM, to be fixed */ -/* #define configGENERATE_RUN_TIME_STATS 1 -#define configRUN_TIME_COUNTER_TYPE uint64_t #define portGET_RUN_TIME_COUNTER_VALUE() (DWT->CYCCNT) #define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() -*/ #define configUSE_TRACE_FACILITY 1 #define configUSE_16_BIT_TICKS 0 @@ -86,8 +82,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 00000000000..4b56a11bef3 --- /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 00000000000..7123885765f --- /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 00000000000..1c1cfdcc37e --- /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 77430fda8b8..00000000000 --- 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 e6cb4aabee1..d15474e529d 100644 --- a/targets/f7/src/update.c +++ b/targets/f7/src/update.c @@ -78,21 +78,21 @@ static bool flipper_update_load_stage(const FuriString* work_dir, UpdateManifest furi_string_free(loader_img_path); void* img = malloc(stat.fsize); - uint32_t bytes_read = 0; + uint32_t read_total = 0; + uint16_t read_current = 0; const uint16_t MAX_READ = 0xFFFF; uint32_t crc = 0; do { - uint16_t size_read = 0; - if(f_read(&file, img + bytes_read, MAX_READ, &size_read) != FR_OK) { //-V769 + if(f_read(&file, img + read_total, MAX_READ, &read_current) != FR_OK) { //-V769 break; } - crc = crc32_calc_buffer(crc, img + bytes_read, size_read); - bytes_read += size_read; - } while(bytes_read == MAX_READ); + crc = crc32_calc_buffer(crc, img + read_total, read_current); + read_total += read_current; + } while(read_current == MAX_READ); do { - if((bytes_read != stat.fsize) || (crc != manifest->staged_loader_crc)) { + if((read_total != stat.fsize) || (crc != manifest->staged_loader_crc)) { break; } @@ -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 c5c2b3fc3d8..00000000000 --- 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 df4c5b72662..3fb78964591 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 */ - -/* 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 -} - -/* Define output sections */ -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 +_stack_size = 0x1000; /* required amount of stack */ - /* 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); - .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) +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 +} +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); - _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 + .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) - _stack_size; + __heap_end__ = .; + . = . + _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 } - - diff --git a/targets/f7/stm32wb55xx_ram_fw.ld b/targets/f7/stm32wb55xx_ram_fw.ld index 0ac9be4df81..cae30b6e99a 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 */ - -/* 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 -} - -/* Define output sections */ -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 +_stack_size = 0x1000; /* required amount of stack */ - /* 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); - .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) +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 +} +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); - _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 + .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) - _stack_size; + __heap_end__ = .; + . = . + _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 } - - diff --git a/targets/f7/target.json b/targets/f7/target.json index eae92a5cd67..665864d7d95 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 719df0275b6..cf483553f1f 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 7a5759c1758..1d91b123dc8 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, diff --git a/targets/furi_hal_include/furi_hal_usb_ccid.h b/targets/furi_hal_include/furi_hal_usb_ccid.h index 500bafa3dfd..cbd0bf092b9 100644 --- a/targets/furi_hal_include/furi_hal_usb_ccid.h +++ b/targets/furi_hal_include/furi_hal_usb_ccid.h @@ -19,14 +19,14 @@ typedef struct { typedef struct { void (*icc_power_on_callback)(uint8_t* dataBlock, uint32_t* dataBlockLen, void* context); void (*xfr_datablock_callback)( - const uint8_t* dataBlock, - uint32_t dataBlockLen, - uint8_t* responseDataBlock, - uint32_t* responseDataBlockLen, + const uint8_t* pcToReaderDataBlock, + uint32_t pcToReaderDataBlockLen, + uint8_t* readerToPcDataBlock, + uint32_t* readerToPcDataBlockLen, void* context); } CcidCallbacks; -void furi_hal_ccid_set_callbacks(CcidCallbacks* cb); +void furi_hal_ccid_set_callbacks(CcidCallbacks* cb, void* context); void furi_hal_ccid_ccid_insert_smartcard(void); void furi_hal_ccid_ccid_remove_smartcard(void);