From 14dabf523ac382ddc7c17f65f79440993edc47c5 Mon Sep 17 00:00:00 2001 From: hedger Date: Mon, 12 Feb 2024 02:04:12 +0000 Subject: [PATCH 1/5] New toolchain with gcc 12 (#3254) * changes for xPack 12.3 * support for gcc 13.2 * Update tools name * Add new linux toolchain * Fixed copro submodule * Fix gdb-py * Fixes for c++ apps * Fix gdb-py3, add udev rules * Fixed udev rules location * Add MacOS arm, fix fbt toolchain download * Fixed downloading error file * fbt: fixed linker warnings; removed gcc 10 from list of supported toolchains * ufbt: fixed supported toolchain versions * nfc: replaced local malloc with calloc * restored code with Warray-bounds to older state * Update fbtenv.cmd * Suppressing warnings * Bump to 25 * Bump to 26 * lint: reformatted macros for new clang-format * Bump to 27 * Fix m type word * Bump to 28 * furi: added FURI_DEPRECATED macro * scripts: toolchain download on Windows: fixing partially extracted cases Co-authored-by: DrunkBatya --- .../debug/unit_tests/furi/furi_memmgr_test.c | 1 + .../main/nfc/scenes/nfc_scene_set_type.c | 2 +- ...subghz_frequency_analyzer_log_item_array.h | 3 + applications/services/dolphin/dolphin.c | 2 +- applications/services/gui/canvas.c | 1 - applications/services/gui/canvas_i.h | 4 +- applications/services/rpc/rpc_storage.c | 3 + fbt_options.py | 3 +- furi/core/common_defines.h | 4 ++ furi/core/core_defines.h | 6 +- furi/core/dangerous_defines.h | 31 +++++----- furi/core/string.h | 20 ++++--- lib/digital_signal/digital_signal.h | 6 +- .../iso14443_3a/iso14443_3a_poller_i.h | 2 +- lib/nfc/protocols/mf_classic/crypto1.c | 2 +- lib/pulse_reader/pulse_reader.c | 4 +- lib/u8g2/u8x8.h | 4 +- scripts/debug/41-udev.rules | 10 ++++ scripts/fbt_tools/gdb.py | 2 +- scripts/toolchain/fbtenv.cmd | 4 +- scripts/toolchain/fbtenv.sh | 56 +++++-------------- .../toolchain/windows-toolchain-download.ps1 | 9 ++- scripts/ufbt/SConstruct | 4 +- site_scons/cc.scons | 2 + site_scons/extapps.scons | 1 - site_scons/firmwareopts.scons | 1 - targets/f18/api_symbols.csv | 14 +++-- targets/f7/api_symbols.csv | 14 +++-- targets/f7/ble_glue/gap.c | 2 +- targets/f7/furi_hal/furi_hal_flash.c | 2 +- targets/f7/furi_hal/furi_hal_memory.c | 4 +- targets/f7/furi_hal/furi_hal_os.c | 4 +- .../f7/platform_specific/cxx_virtual_stub.c | 6 ++ .../f7/platform_specific/cxx_virtual_stub.h | 13 +++++ 34 files changed, 141 insertions(+), 105 deletions(-) create mode 100644 scripts/debug/41-udev.rules create mode 100644 targets/f7/platform_specific/cxx_virtual_stub.c create mode 100644 targets/f7/platform_specific/cxx_virtual_stub.h diff --git a/applications/debug/unit_tests/furi/furi_memmgr_test.c b/applications/debug/unit_tests/furi/furi_memmgr_test.c index a28632cf4..9012eed78 100644 --- a/applications/debug/unit_tests/furi/furi_memmgr_test.c +++ b/applications/debug/unit_tests/furi/furi_memmgr_test.c @@ -2,6 +2,7 @@ #include #include #include +#include void test_furi_memmgr() { void* ptr; diff --git a/applications/main/nfc/scenes/nfc_scene_set_type.c b/applications/main/nfc/scenes/nfc_scene_set_type.c index b5102f801..fc5f90f0c 100644 --- a/applications/main/nfc/scenes/nfc_scene_set_type.c +++ b/applications/main/nfc/scenes/nfc_scene_set_type.c @@ -10,7 +10,7 @@ enum SubmenuIndex { static void nfc_scene_set_type_init_edit_data(Iso14443_3aData* data, size_t uid_len) { // Easiest way to create a zero'd buffer of given length - uint8_t* uid = malloc(uid_len); + uint8_t* uid = calloc(1, uid_len); iso14443_3a_set_uid(data, uid, uid_len); free(uid); } diff --git a/applications/main/subghz/helpers/subghz_frequency_analyzer_log_item_array.h b/applications/main/subghz/helpers/subghz_frequency_analyzer_log_item_array.h index 2fa70284a..6e6b553e0 100644 --- a/applications/main/subghz/helpers/subghz_frequency_analyzer_log_item_array.h +++ b/applications/main/subghz/helpers/subghz_frequency_analyzer_log_item_array.h @@ -40,6 +40,8 @@ ARRAY_DEF(SubGhzFrequencyAnalyzerLogItemArray, SubGhzFrequencyAnalyzerLogItem_t) ARRAY_OPLIST(SubGhzFrequencyAnalyzerLogItemArray, M_OPL_SubGhzFrequencyAnalyzerLogItem_t()) ALGO_DEF(SubGhzFrequencyAnalyzerLogItemArray, SubGhzFrequencyAnalyzerLogItemArray_t) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Warray-bounds" FUNC_OBJ_INS_DEF( SubGhzFrequencyAnalyzerLogItemArray_compare_by /* name of the instance */, SubGhzFrequencyAnalyzerLogItemArray_cmp_obj /* name of the interface */, @@ -76,3 +78,4 @@ FUNC_OBJ_INS_DEF( (order_by, SubGhzFrequencyAnalyzerLogOrderBy)) #define M_OPL_SubGhzFrequencyAnalyzerLogItemArray_compare_by_t() \ FUNC_OBJ_INS_OPLIST(SubGhzFrequencyAnalyzerLogItemArray_compare_by, M_DEFAULT_OPLIST) +#pragma GCC diagnostic pop diff --git a/applications/services/dolphin/dolphin.c b/applications/services/dolphin/dolphin.c index bef7c4a28..520a01978 100644 --- a/applications/services/dolphin/dolphin.c +++ b/applications/services/dolphin/dolphin.c @@ -8,7 +8,7 @@ #define DOLPHIN_LOCK_EVENT_FLAG (0x1) #define TAG "Dolphin" -#define HOURS_IN_TICKS(x) ((x)*60 * 60 * 1000) +#define HOURS_IN_TICKS(x) ((x) * 60 * 60 * 1000) static void dolphin_update_clear_limits_timer_period(Dolphin* dolphin); diff --git a/applications/services/gui/canvas.c b/applications/services/gui/canvas.c index 897f0ae13..9c9457123 100644 --- a/applications/services/gui/canvas.c +++ b/applications/services/gui/canvas.c @@ -1,5 +1,4 @@ #include "canvas_i.h" -#include "icon_i.h" #include "icon_animation_i.h" #include diff --git a/applications/services/gui/canvas_i.h b/applications/services/gui/canvas_i.h index 73502b719..0982830c9 100644 --- a/applications/services/gui/canvas_i.h +++ b/applications/services/gui/canvas_i.h @@ -122,7 +122,7 @@ void canvas_draw_u8g2_bitmap( uint8_t width, uint8_t height, const uint8_t* bitmap, - uint8_t rotation); + IconRotation rotation); /** Add canvas commit callback. * @@ -147,4 +147,4 @@ void canvas_remove_framebuffer_callback( #ifdef __cplusplus } -#endif \ No newline at end of file +#endif diff --git a/applications/services/rpc/rpc_storage.c b/applications/services/rpc/rpc_storage.c index a934d1c31..151d73d61 100644 --- a/applications/services/rpc/rpc_storage.c +++ b/applications/services/rpc/rpc_storage.c @@ -395,9 +395,12 @@ static void rpc_system_storage_read_process(const PB_Main* request, void* contex response->has_next = fs_operation_success && (size_left > 0); } else { +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Warray-bounds" response->content.storage_read_response.file.data = malloc(PB_BYTES_ARRAY_T_ALLOCSIZE(0)); response->content.storage_read_response.file.data->size = 0; +#pragma GCC diagnostic pop response->content.storage_read_response.has_file = true; response->has_next = false; fs_operation_success = true; diff --git a/fbt_options.py b/fbt_options.py index 277790a40..9e4d821a4 100644 --- a/fbt_options.py +++ b/fbt_options.py @@ -38,7 +38,8 @@ COPRO_STACK_ADDR = "0x0" COPRO_STACK_BIN_DIR = posixpath.join(COPRO_CUBE_DIR, "firmware") # Supported toolchain versions -FBT_TOOLCHAIN_VERSIONS = (" 10.3.",) +# Also specify in scripts/ufbt/SConstruct +FBT_TOOLCHAIN_VERSIONS = (" 12.3.", " 13.2.") OPENOCD_OPTS = [ "-f", diff --git a/furi/core/common_defines.h b/furi/core/common_defines.h index 2fc12dedb..820297863 100644 --- a/furi/core/common_defines.h +++ b/furi/core/common_defines.h @@ -13,6 +13,10 @@ extern "C" { #define FURI_WARN_UNUSED __attribute__((warn_unused_result)) #endif +#ifndef FURI_DEPRECATED +#define FURI_DEPRECATED __attribute__((deprecated)) +#endif + #ifndef FURI_WEAK #define FURI_WEAK __attribute__((weak)) #endif diff --git a/furi/core/core_defines.h b/furi/core/core_defines.h index 4309c20c5..732a90cb5 100644 --- a/furi/core/core_defines.h +++ b/furi/core/core_defines.h @@ -84,9 +84,9 @@ extern "C" { #endif #ifndef REVERSE_BYTES_U32 -#define REVERSE_BYTES_U32(x) \ - ((((x)&0x000000FF) << 24) | (((x)&0x0000FF00) << 8) | (((x)&0x00FF0000) >> 8) | \ - (((x)&0xFF000000) >> 24)) +#define REVERSE_BYTES_U32(x) \ + ((((x) & 0x000000FF) << 24) | (((x) & 0x0000FF00) << 8) | (((x) & 0x00FF0000) >> 8) | \ + (((x) & 0xFF000000) >> 24)) #endif #ifndef FURI_BIT diff --git a/furi/core/dangerous_defines.h b/furi/core/dangerous_defines.h index b477710ac..683df2f61 100644 --- a/furi/core/dangerous_defines.h +++ b/furi/core/dangerous_defines.h @@ -26,19 +26,20 @@ *tmp_x = y; \ *tmp_x; \ }) -#define FURI_CONST_ASSIGN(x, y) \ - _Generic((x), signed char \ - : FURI_CONST_ASSIGN_(signed char, x, y), unsigned char \ - : FURI_CONST_ASSIGN_(unsigned char, x, y), short \ - : FURI_CONST_ASSIGN_(short, x, y), unsigned short \ - : FURI_CONST_ASSIGN_(unsigned short, x, y), int \ - : FURI_CONST_ASSIGN_(int, x, y), unsigned \ - : FURI_CONST_ASSIGN_(unsigned, x, y), long \ - : FURI_CONST_ASSIGN_(long, x, y), unsigned long \ - : FURI_CONST_ASSIGN_(unsigned long, x, y), long long \ - : FURI_CONST_ASSIGN_(long long, x, y), unsigned long long \ - : FURI_CONST_ASSIGN_(unsigned long long, x, y), float \ - : FURI_CONST_ASSIGN_(float, x, y), double \ - : FURI_CONST_ASSIGN_(double, x, y), long double \ - : FURI_CONST_ASSIGN_(long double, x, y)) +#define FURI_CONST_ASSIGN(x, y) \ + _Generic( \ + (x), \ + signed char: FURI_CONST_ASSIGN_(signed char, x, y), \ + unsigned char: FURI_CONST_ASSIGN_(unsigned char, x, y), \ + short: FURI_CONST_ASSIGN_(short, x, y), \ + unsigned short: FURI_CONST_ASSIGN_(unsigned short, x, y), \ + int: FURI_CONST_ASSIGN_(int, x, y), \ + unsigned: FURI_CONST_ASSIGN_(unsigned, x, y), \ + long: FURI_CONST_ASSIGN_(long, x, y), \ + unsigned long: FURI_CONST_ASSIGN_(unsigned long, x, y), \ + long long: FURI_CONST_ASSIGN_(long long, x, y), \ + unsigned long long: FURI_CONST_ASSIGN_(unsigned long long, x, y), \ + float: FURI_CONST_ASSIGN_(float, x, y), \ + double: FURI_CONST_ASSIGN_(double, x, y), \ + long double: FURI_CONST_ASSIGN_(long double, x, y)) #endif diff --git a/furi/core/string.h b/furi/core/string.h index 7529deacd..0e3e6a88e 100644 --- a/furi/core/string.h +++ b/furi/core/string.h @@ -568,26 +568,30 @@ void furi_string_utf8_decode(char c, FuriStringUTF8State* state, FuriStringUnico /** * @brief Select for 1 argument */ -#define FURI_STRING_SELECT1(func1, func2, a) \ - _Generic((a), char* : func2, const char* : func2, FuriString* : func1, const FuriString* : func1)(a) +#define FURI_STRING_SELECT1(func1, func2, a) \ + _Generic((a), char*: func2, const char*: func2, FuriString*: func1, const FuriString*: func1)( \ + a) /** * @brief Select for 2 arguments */ -#define FURI_STRING_SELECT2(func1, func2, a, b) \ - _Generic((b), char* : func2, const char* : func2, FuriString* : func1, const FuriString* : func1)(a, b) +#define FURI_STRING_SELECT2(func1, func2, a, b) \ + _Generic((b), char*: func2, const char*: func2, FuriString*: func1, const FuriString*: func1)( \ + a, b) /** * @brief Select for 3 arguments */ -#define FURI_STRING_SELECT3(func1, func2, a, b, c) \ - _Generic((b), char* : func2, const char* : func2, FuriString* : func1, const FuriString* : func1)(a, b, c) +#define FURI_STRING_SELECT3(func1, func2, a, b, c) \ + _Generic((b), char*: func2, const char*: func2, FuriString*: func1, const FuriString*: func1)( \ + a, b, c) /** * @brief Select for 4 arguments */ -#define FURI_STRING_SELECT4(func1, func2, a, b, c, d) \ - _Generic((b), char* : func2, const char* : func2, FuriString* : func1, const FuriString* : func1)(a, b, c, d) +#define FURI_STRING_SELECT4(func1, func2, a, b, c, d) \ + _Generic((b), char*: func2, const char*: func2, FuriString*: func1, const FuriString*: func1)( \ + a, b, c, d) /** * @brief Allocate new FuriString and set it content to string (or C string). diff --git a/lib/digital_signal/digital_signal.h b/lib/digital_signal/digital_signal.h index f29facfd8..1ab637009 100644 --- a/lib/digital_signal/digital_signal.h +++ b/lib/digital_signal/digital_signal.h @@ -26,9 +26,9 @@ extern "C" { // DigitalSignal uses 10 picosecond time units (1 tick = 10 ps). // Use the macros below to convert the time from other units. -#define DIGITAL_SIGNAL_MS(x) ((x)*100000000UL) -#define DIGITAL_SIGNAL_US(x) ((x)*100000UL) -#define DIGITAL_SIGNAL_NS(x) ((x)*100UL) +#define DIGITAL_SIGNAL_MS(x) ((x) * 100000000UL) +#define DIGITAL_SIGNAL_US(x) ((x) * 100000UL) +#define DIGITAL_SIGNAL_NS(x) ((x) * 100UL) #define DIGITAL_SIGNAL_PS(x) ((x) / 10UL) typedef struct DigitalSignal DigitalSignal; diff --git a/lib/nfc/protocols/iso14443_3a/iso14443_3a_poller_i.h b/lib/nfc/protocols/iso14443_3a/iso14443_3a_poller_i.h index 764f1a6b5..664d54598 100644 --- a/lib/nfc/protocols/iso14443_3a/iso14443_3a_poller_i.h +++ b/lib/nfc/protocols/iso14443_3a/iso14443_3a_poller_i.h @@ -11,7 +11,7 @@ extern "C" { #define ISO14443_3A_POLLER_MAX_BUFFER_SIZE (512U) #define ISO14443_3A_POLLER_SEL_CMD(cascade_lvl) (0x93 + 2 * (cascade_lvl)) -#define ISO14443_3A_POLLER_SEL_PAR(bytes, bits) (((bytes) << 4 & 0xf0U) | ((bits)&0x0fU)) +#define ISO14443_3A_POLLER_SEL_PAR(bytes, bits) (((bytes) << 4 & 0xf0U) | ((bits) & 0x0fU)) #define ISO14443_3A_POLLER_SDD_CL (0x88U) typedef enum { diff --git a/lib/nfc/protocols/mf_classic/crypto1.c b/lib/nfc/protocols/mf_classic/crypto1.c index 02bc677ba..0758b05fb 100644 --- a/lib/nfc/protocols/mf_classic/crypto1.c +++ b/lib/nfc/protocols/mf_classic/crypto1.c @@ -6,7 +6,7 @@ // Algorithm from https://github.com/RfidResearchGroup/proxmark3.git #define SWAPENDIAN(x) \ - ((x) = ((x) >> 8 & 0xff00ff) | ((x)&0xff00ff) << 8, (x) = (x) >> 16 | (x) << 16) + ((x) = ((x) >> 8 & 0xff00ff) | ((x) & 0xff00ff) << 8, (x) = (x) >> 16 | (x) << 16) #define LF_POLY_ODD (0x29CE5C) #define LF_POLY_EVEN (0x870804) diff --git a/lib/pulse_reader/pulse_reader.c b/lib/pulse_reader/pulse_reader.c index 1c3cb4a58..0fcafe67c 100644 --- a/lib/pulse_reader/pulse_reader.c +++ b/lib/pulse_reader/pulse_reader.c @@ -212,8 +212,8 @@ uint32_t pulse_reader_receive(PulseReader* signal, int timeout_us) { /* probably larger values, so choose a wider data type */ if(signal->unit_divider > 1) { - delta_unit = - (uint32_t)((uint64_t)delta * (uint64_t)signal->unit_multiplier / signal->unit_divider); + delta_unit = (uint32_t)((uint64_t)delta * (uint64_t)signal->unit_multiplier / + signal->unit_divider); } else { delta_unit = delta * signal->unit_multiplier; } diff --git a/lib/u8g2/u8x8.h b/lib/u8g2/u8x8.h index 389fdfa9b..834284f5d 100644 --- a/lib/u8g2/u8x8.h +++ b/lib/u8g2/u8x8.h @@ -688,8 +688,8 @@ uint8_t u8x8_byte_sed1520(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ #define U8X8_MSG_GPIO(x) (64 + (x)) #ifdef U8X8_USE_PINS -#define u8x8_GetPinIndex(u8x8, msg) ((msg)&0x3f) -#define u8x8_GetPinValue(u8x8, msg) ((u8x8)->pins[(msg)&0x3f]) +#define u8x8_GetPinIndex(u8x8, msg) ((msg) & 0x3f) +#define u8x8_GetPinValue(u8x8, msg) ((u8x8)->pins[(msg) & 0x3f]) #endif #define U8X8_MSG_GPIO_D0 U8X8_MSG_GPIO(U8X8_PIN_D0) diff --git a/scripts/debug/41-udev.rules b/scripts/debug/41-udev.rules new file mode 100644 index 000000000..6b4b37b92 --- /dev/null +++ b/scripts/debug/41-udev.rules @@ -0,0 +1,10 @@ +#Flipper Zero serial port +SUBSYSTEMS=="usb", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="5740", ATTRS{manufacturer}=="Flipper Devices Inc.", TAG+="uaccess", GROUP="dialout" +#Flipper Zero DFU +SUBSYSTEMS=="usb", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="df11", ATTRS{manufacturer}=="STMicroelectronics", TAG+="uaccess", GROUP="dialout" +#Flipper ESP32s2 BlackMagic +SUBSYSTEMS=="usb", ATTRS{idVendor}=="303a", ATTRS{idProduct}=="40??", ATTRS{manufacturer}=="Flipper Devices Inc.", TAG+="uaccess", GROUP="dialout" +#Flipper U2F +SUBSYSTEMS=="usb", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="5741", ATTRS{manufacturer}=="Flipper Devices Inc.", ENV{ID_SECURITY_TOKEN}="1" +#ST-Link-V3 +SUBSYSTEMS=="usb", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="37??", ATTRS{manufacturer}=="STMicroelectronics", TAG+="uaccess", GROUP="dialout" diff --git a/scripts/fbt_tools/gdb.py b/scripts/fbt_tools/gdb.py index ea29e9c92..f1c90040d 100644 --- a/scripts/fbt_tools/gdb.py +++ b/scripts/fbt_tools/gdb.py @@ -1,7 +1,7 @@ def generate(env): env.SetDefault( GDB="gdb", - GDBPY="gdb-py", + GDBPY="gdb-py3", GDBCOM="$GDB $GDBOPTS $SOURCES", # no $TARGET GDBPYCOM="$GDBPY $GDBOPTS $GDBPYOPTS $SOURCES", # no $TARGET ) diff --git a/scripts/toolchain/fbtenv.cmd b/scripts/toolchain/fbtenv.cmd index 51708b8c4..186cbf28a 100644 --- a/scripts/toolchain/fbtenv.cmd +++ b/scripts/toolchain/fbtenv.cmd @@ -13,7 +13,7 @@ if not ["%FBT_NOENV%"] == [""] ( exit /b 0 ) -set "FLIPPER_TOOLCHAIN_VERSION=23" +set "FLIPPER_TOOLCHAIN_VERSION=28" if ["%FBT_TOOLCHAIN_PATH%"] == [""] ( set "FBT_TOOLCHAIN_PATH=%FBT_ROOT%" @@ -46,7 +46,7 @@ set "HOME=%USERPROFILE%" set "PYTHONHOME=%FBT_TOOLCHAIN_ROOT%\python" set "PYTHONPATH=" set "PYTHONNOUSERSITE=1" -set "PATH=%FBT_TOOLCHAIN_ROOT%\python;%FBT_TOOLCHAIN_ROOT%\bin;%FBT_TOOLCHAIN_ROOT%\protoc\bin;%FBT_TOOLCHAIN_ROOT%\openocd\bin;%PATH%" +set "PATH=%FBT_TOOLCHAIN_ROOT%\bin;%FBT_TOOLCHAIN_ROOT%\python;%PATH%" set "PROMPT=(fbt) %PROMPT%" :already_set diff --git a/scripts/toolchain/fbtenv.sh b/scripts/toolchain/fbtenv.sh index 990776b27..ea7c9019b 100755 --- a/scripts/toolchain/fbtenv.sh +++ b/scripts/toolchain/fbtenv.sh @@ -4,7 +4,7 @@ # public variables DEFAULT_SCRIPT_PATH="$(pwd -P)"; -FBT_TOOLCHAIN_VERSION="${FBT_TOOLCHAIN_VERSION:-"23"}"; +FBT_TOOLCHAIN_VERSION="${FBT_TOOLCHAIN_VERSION:-"28"}"; if [ -z ${FBT_TOOLCHAIN_PATH+x} ] ; then FBT_TOOLCHAIN_PATH_WAS_SET=0; @@ -27,7 +27,7 @@ fbtenv_show_usage() fbtenv_curl() { - curl --progress-bar -SLo "$1" "$2"; + curl --progress-bar -SLo "$1" "$2" -w "%{http_code}" | grep -q 200; } fbtenv_wget() @@ -38,11 +38,7 @@ fbtenv_wget() fbtenv_restore_env() { TOOLCHAIN_ARCH_DIR_SED="$(echo "$TOOLCHAIN_ARCH_DIR" | sed 's/\//\\\//g')" - PATH="$(echo "$PATH" | sed "s/$TOOLCHAIN_ARCH_DIR_SED\/python\/bin://g")"; PATH="$(echo "$PATH" | sed "s/$TOOLCHAIN_ARCH_DIR_SED\/bin://g")"; - PATH="$(echo "$PATH" | sed "s/$TOOLCHAIN_ARCH_DIR_SED\/protobuf\/bin://g")"; - PATH="$(echo "$PATH" | sed "s/$TOOLCHAIN_ARCH_DIR_SED\/openocd\/bin://g")"; - PATH="$(echo "$PATH" | sed "s/$TOOLCHAIN_ARCH_DIR_SED\/openssl\/bin://g")"; if [ -n "${PS1:-""}" ]; then PS1="$(echo "$PS1" | sed 's/\[fbt\]//g')"; elif [ -n "${PROMPT:-""}" ]; then @@ -57,7 +53,7 @@ fbtenv_restore_env() unset REQUESTS_CA_BUNDLE; fi - if [ "$SYS_TYPE" = "Linux" ]; then + if [ "$SYS_TYPE" = "linux" ]; then if [ -n "$SAVED_TERMINFO_DIRS" ]; then export TERMINFO_DIRS="$SAVED_TERMINFO_DIRS"; else @@ -135,38 +131,17 @@ fbtenv_check_env_vars() fbtenv_get_kernel_type() { - SYS_TYPE="$(uname -s)"; + SYS_TYPE="$(uname -s | tr '[:upper:]' '[:lower:]')"; ARCH_TYPE="$(uname -m)"; - if [ "$ARCH_TYPE" != "x86_64" ] && [ "$SYS_TYPE" != "Darwin" ]; then - echo "We only provide toolchain for x86_64 CPUs, sorry.."; - return 1; - fi - if [ "$SYS_TYPE" = "Darwin" ]; then - fbtenv_check_rosetta || return 1; - TOOLCHAIN_ARCH_DIR="$FBT_TOOLCHAIN_PATH/toolchain/x86_64-darwin"; - TOOLCHAIN_URL="https://update.flipperzero.one/builds/toolchain/gcc-arm-none-eabi-10.3-x86_64-darwin-flipper-$FBT_TOOLCHAIN_VERSION.tar.gz"; - elif [ "$SYS_TYPE" = "Linux" ]; then - TOOLCHAIN_ARCH_DIR="$FBT_TOOLCHAIN_PATH/toolchain/x86_64-linux"; - TOOLCHAIN_URL="https://update.flipperzero.one/builds/toolchain/gcc-arm-none-eabi-10.3-x86_64-linux-flipper-$FBT_TOOLCHAIN_VERSION.tar.gz"; - elif echo "$SYS_TYPE" | grep -q "MINGW"; then + if echo "$SYS_TYPE" | grep -q "MINGW"; then echo "In MinGW shell, use \"[u]fbt.cmd\" instead of \"[u]fbt\""; return 1; - else + elif [ $SYS_TYPE != "linux" ] && [ $SYS_TYPE != "darwin" ]; then echo "Your system configuration is not supported. Sorry.. Please report us your configuration."; return 1; fi - return 0; -} - -fbtenv_check_rosetta() -{ - if [ "$ARCH_TYPE" = "arm64" ]; then - if ! pgrep -q oahd; then - echo "Flipper Zero Toolchain needs Rosetta2 to run under Apple Silicon"; - echo "Please install it by typing 'softwareupdate --install-rosetta --agree-to-license'"; - return 1; - fi - fi + TOOLCHAIN_ARCH_DIR="$FBT_TOOLCHAIN_PATH/toolchain/$ARCH_TYPE-$SYS_TYPE"; + TOOLCHAIN_URL="https://update.flipperzero.one/builds/toolchain/gcc-arm-none-eabi-12.3-$ARCH_TYPE-$SYS_TYPE-flipper-$FBT_TOOLCHAIN_VERSION.tar.gz"; return 0; } @@ -196,7 +171,10 @@ fbtenv_download_toolchain_tar() { echo "Downloading toolchain:"; mkdir -p "$FBT_TOOLCHAIN_PATH/toolchain" || return 1; - "$FBT_DOWNLOADER" "$FBT_TOOLCHAIN_PATH/toolchain/$TOOLCHAIN_TAR.part" "$TOOLCHAIN_URL" || return 1; + "$FBT_DOWNLOADER" "$FBT_TOOLCHAIN_PATH/toolchain/$TOOLCHAIN_TAR.part" "$TOOLCHAIN_URL" || { + echo "Failed to download $TOOLCHAIN_URL"; + return 1; + }; # restoring oroginal filename if file downloaded successfully mv "$FBT_TOOLCHAIN_PATH/toolchain/$TOOLCHAIN_TAR.part" "$FBT_TOOLCHAIN_PATH/toolchain/$TOOLCHAIN_TAR" echo "done"; @@ -328,11 +306,7 @@ fbtenv_main() fbtenv_check_download_toolchain || return 1; fbtenv_set_shell_prompt; fbtenv_print_config; - PATH="$TOOLCHAIN_ARCH_DIR/python/bin:$PATH"; PATH="$TOOLCHAIN_ARCH_DIR/bin:$PATH"; - PATH="$TOOLCHAIN_ARCH_DIR/protobuf/bin:$PATH"; - PATH="$TOOLCHAIN_ARCH_DIR/openocd/bin:$PATH"; - PATH="$TOOLCHAIN_ARCH_DIR/openssl/bin:$PATH"; export PATH; export SAVED_SSL_CERT_FILE="${SSL_CERT_FILE:-""}"; @@ -341,15 +315,15 @@ fbtenv_main() export SAVED_PYTHONPATH="${PYTHONPATH:-""}"; export SAVED_PYTHONHOME="${PYTHONHOME:-""}"; - export SSL_CERT_FILE="$TOOLCHAIN_ARCH_DIR/python/lib/python3.11/site-packages/certifi/cacert.pem"; + export SSL_CERT_FILE="$TOOLCHAIN_ARCH_DIR/lib/python3.11/site-packages/certifi/cacert.pem"; export REQUESTS_CA_BUNDLE="$SSL_CERT_FILE"; export PYTHONNOUSERSITE=1; export PYTHONPATH=; export PYTHONHOME=; - if [ "$SYS_TYPE" = "Linux" ]; then + if [ "$SYS_TYPE" = "linux" ]; then export SAVED_TERMINFO_DIRS="${TERMINFO_DIRS:-""}"; - export TERMINFO_DIRS="$TOOLCHAIN_ARCH_DIR/ncurses/share/terminfo"; + export TERMINFO_DIRS="$TOOLCHAIN_ARCH_DIR/share/terminfo"; fi } diff --git a/scripts/toolchain/windows-toolchain-download.ps1 b/scripts/toolchain/windows-toolchain-download.ps1 index f30b157da..9a4d2e18f 100644 --- a/scripts/toolchain/windows-toolchain-download.ps1 +++ b/scripts/toolchain/windows-toolchain-download.ps1 @@ -6,8 +6,8 @@ $download_dir = (Get-Item "$PSScriptRoot\..\..").FullName $toolchain_version = $args[0] $toolchain_target_path = $args[1] -$toolchain_url = "https://update.flipperzero.one/builds/toolchain/gcc-arm-none-eabi-10.3-x86_64-windows-flipper-$toolchain_version.zip" -$toolchain_dist_folder = "gcc-arm-none-eabi-10.3-x86_64-windows-flipper" +$toolchain_url = "https://update.flipperzero.one/builds/toolchain/gcc-arm-none-eabi-12.3-x86_64-windows-flipper-$toolchain_version.zip" +$toolchain_dist_folder = "gcc-arm-none-eabi-12.3-x86_64-windows-flipper" $toolchain_zip = "$toolchain_dist_folder-$toolchain_version.zip" $toolchain_zip_temp_path = "$download_dir\$toolchain_zip" @@ -29,6 +29,11 @@ if (!(Test-Path -LiteralPath "$toolchain_target_path\..")) { New-Item "$toolchain_target_path\.." -ItemType Directory -Force } +if (Test-Path -LiteralPath "$toolchain_dist_temp_path") { + Write-Host "Cleaning up temp toolchain path.." + Remove-Item -LiteralPath "$toolchain_dist_temp_path" -Force -Recurse +} + Write-Host -NoNewline "Extracting Windows toolchain.." # This is faster than Expand-Archive Add-Type -Assembly "System.IO.Compression.Filesystem" diff --git a/scripts/ufbt/SConstruct b/scripts/ufbt/SConstruct index 8df1ae110..a26c2e404 100644 --- a/scripts/ufbt/SConstruct +++ b/scripts/ufbt/SConstruct @@ -74,7 +74,7 @@ env = core_env.Clone( "crosscc", { "toolchain_prefix": "arm-none-eabi-", - "versions": (" 10.3",), + "versions": (" 12.3.", " 13.2."), }, ), "fwbin", @@ -361,7 +361,7 @@ for template_file in project_template_dir.Dir(".vscode").glob("*"): dist_env.WhereIs("arm-none-eabi-gcc") ), "@UFBT_TOOLCHAIN_GDB_PY@": _path_as_posix( - dist_env.WhereIs("arm-none-eabi-gdb-py") + dist_env.WhereIs("arm-none-eabi-gdb-py3") ), "@UFBT_TOOLCHAIN_OPENOCD@": _path_as_posix(dist_env.WhereIs("openocd")), "@UFBT_APP_DIR@": _path_as_posix(original_app_dir.abspath), diff --git a/site_scons/cc.scons b/site_scons/cc.scons index 55ab72ba6..029f9d415 100644 --- a/site_scons/cc.scons +++ b/site_scons/cc.scons @@ -23,6 +23,7 @@ ENV.AppendUnique( "-Wall", "-Wextra", "-Werror", + "-Wno-error=deprecated-declarations", "-Wno-address-of-packed-member", "-Wredundant-decls", "-Wdouble-promotion", @@ -44,5 +45,6 @@ ENV.AppendUnique( "-mfpu=fpv4-sp-d16", "-mlittle-endian", "-mthumb", + "-Wl,--no-warn-rwx-segment", ], ) diff --git a/site_scons/extapps.scons b/site_scons/extapps.scons index 769b3eb15..22d0be867 100644 --- a/site_scons/extapps.scons +++ b/site_scons/extapps.scons @@ -41,7 +41,6 @@ appenv.AppendUnique( "-Xlinker", "-Map=${TARGET}.map", "-specs=nano.specs", - "-specs=nosys.specs", ], LIBS=[ "m", diff --git a/site_scons/firmwareopts.scons b/site_scons/firmwareopts.scons index e4cc8db58..6af861324 100644 --- a/site_scons/firmwareopts.scons +++ b/site_scons/firmwareopts.scons @@ -35,7 +35,6 @@ else: ENV.AppendUnique( LINKFLAGS=[ "-specs=nano.specs", - "-specs=nosys.specs", "-Wl,--gc-sections", "-Wl,--undefined=uxTopUsedPriority", "-Wl,--wrap,_malloc_r", diff --git a/targets/f18/api_symbols.csv b/targets/f18/api_symbols.csv index ffb664a3e..4d6f9e32b 100644 --- a/targets/f18/api_symbols.csv +++ b/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,54.0,, +Version,+,55.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -176,6 +176,7 @@ Header,+,targets/f7/furi_hal/furi_hal_serial_control.h,, Header,+,targets/f7/furi_hal/furi_hal_serial_types.h,, Header,+,targets/f7/furi_hal/furi_hal_spi_types.h,, Header,+,targets/f7/furi_hal/furi_hal_usb_cdc.h,, +Header,+,targets/f7/platform_specific/cxx_virtual_stub.h,, Header,+,targets/f7/platform_specific/intrinsic_export.h,, Header,+,targets/f7/platform_specific/math_wrapper.h,, Header,+,targets/furi_hal_include/furi_hal.h,, @@ -306,6 +307,7 @@ Function,+,__aeabi_uldivmod,void*,"uint64_t, uint64_t" Function,-,__assert,void,"const char*, int, const char*" Function,+,__assert_func,void,"const char*, int, const char*, const char*" Function,+,__clear_cache,void,"void*, void*" +Function,+,__cxa_pure_virtual,void, Function,-,__eprintf,void,"const char*, const char*, unsigned int, const char*" Function,+,__errno,int*, Function,-,__fpclassifyd,int,double @@ -393,6 +395,7 @@ Function,-,_fsetpos_r,int,"_reent*, FILE*, const fpos_t*" Function,-,_ftell_r,long,"_reent*, FILE*" Function,-,_ftello_r,_off_t,"_reent*, FILE*" Function,-,_funopen_r,FILE*,"_reent*, const void*, int (*)(void*, char*, int), int (*)(void*, const char*, int), fpos_t (*)(void*, fpos_t, int), int (*)(void*)" +Function,-,_fwalk_sglue,int,"_reent*, int (*)(_reent*, __FILE*), _glue*" Function,-,_fwrite_r,size_t,"_reent*, const void*, size_t, size_t, FILE*" Function,-,_fwrite_unlocked_r,size_t,"_reent*, const void*, size_t, size_t, FILE*" Function,-,_getc_r,int,"_reent*, FILE*" @@ -2062,7 +2065,6 @@ Function,+,protocol_dict_get_write_data,_Bool,"ProtocolDict*, size_t, void*" Function,+,protocol_dict_render_brief_data,void,"ProtocolDict*, FuriString*, size_t" Function,+,protocol_dict_render_data,void,"ProtocolDict*, FuriString*, size_t" Function,+,protocol_dict_set_data,void,"ProtocolDict*, size_t, const uint8_t*, size_t" -Function,-,pselect,int,"int, fd_set*, fd_set*, fd_set*, const timespec*, const sigset_t*" Function,-,pulse_reader_alloc,PulseReader*,"const GpioPin*, uint32_t" Function,-,pulse_reader_free,void,PulseReader* Function,-,pulse_reader_receive,uint32_t,"PulseReader*, int" @@ -2153,7 +2155,6 @@ Function,+,scene_manager_stop,void,SceneManager* Function,+,sd_api_get_fs_type_text,const char*,SDFsType Function,-,secure_getenv,char*,const char* Function,-,seed48,unsigned short*,unsigned short[3] -Function,-,select,int,"int, fd_set*, fd_set*, fd_set*, timeval*" Function,-,serial_svc_is_started,_Bool, Function,-,serial_svc_notify_buffer_is_empty,void, Function,-,serial_svc_set_callbacks,void,"uint16_t, SerialServiceEventCallback, void*" @@ -2563,8 +2564,13 @@ Variable,-,ITM_RxBuffer,volatile int32_t, Variable,-,MSIRangeTable,const uint32_t[16], Variable,-,SmpsPrescalerTable,const uint32_t[4][6], Variable,+,SystemCoreClock,uint32_t, +Variable,-,__atexit,_atexit*, +Variable,-,__atexit0,_atexit, +Variable,-,__sf,__FILE[3], +Variable,-,__sglue,_glue, +Variable,-,__stdio_exit_handler,void (*)(), Variable,+,_ctype_,const char[], -Variable,+,_global_impure_ptr,_reent*, +Variable,+,_impure_data,_reent, Variable,+,_impure_ptr,_reent*, Variable,-,_sys_errlist,const char*[], Variable,-,_sys_nerr,int, diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index f852a69be..837ad9ffc 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,54.0,, +Version,+,55.0,, Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, @@ -243,6 +243,7 @@ Header,+,targets/f7/furi_hal/furi_hal_spi_types.h,, Header,+,targets/f7/furi_hal/furi_hal_subghz.h,, Header,+,targets/f7/furi_hal/furi_hal_target_hw.h,, Header,+,targets/f7/furi_hal/furi_hal_usb_cdc.h,, +Header,+,targets/f7/platform_specific/cxx_virtual_stub.h,, Header,+,targets/f7/platform_specific/intrinsic_export.h,, Header,+,targets/f7/platform_specific/math_wrapper.h,, Header,+,targets/furi_hal_include/furi_hal.h,, @@ -375,6 +376,7 @@ Function,+,__aeabi_uldivmod,void*,"uint64_t, uint64_t" Function,-,__assert,void,"const char*, int, const char*" Function,+,__assert_func,void,"const char*, int, const char*, const char*" Function,+,__clear_cache,void,"void*, void*" +Function,+,__cxa_pure_virtual,void, Function,-,__eprintf,void,"const char*, const char*, unsigned int, const char*" Function,+,__errno,int*, Function,-,__fpclassifyd,int,double @@ -462,6 +464,7 @@ Function,-,_fsetpos_r,int,"_reent*, FILE*, const fpos_t*" Function,-,_ftell_r,long,"_reent*, FILE*" Function,-,_ftello_r,_off_t,"_reent*, FILE*" Function,-,_funopen_r,FILE*,"_reent*, const void*, int (*)(void*, char*, int), int (*)(void*, const char*, int), fpos_t (*)(void*, fpos_t, int), int (*)(void*)" +Function,-,_fwalk_sglue,int,"_reent*, int (*)(_reent*, __FILE*), _glue*" Function,-,_fwrite_r,size_t,"_reent*, const void*, size_t, size_t, FILE*" Function,-,_fwrite_unlocked_r,size_t,"_reent*, const void*, size_t, size_t, FILE*" Function,-,_getc_r,int,"_reent*, FILE*" @@ -2646,7 +2649,6 @@ Function,+,protocol_dict_get_write_data,_Bool,"ProtocolDict*, size_t, void*" Function,+,protocol_dict_render_brief_data,void,"ProtocolDict*, FuriString*, size_t" Function,+,protocol_dict_render_data,void,"ProtocolDict*, FuriString*, size_t" Function,+,protocol_dict_set_data,void,"ProtocolDict*, size_t, const uint8_t*, size_t" -Function,-,pselect,int,"int, fd_set*, fd_set*, fd_set*, const timespec*, const sigset_t*" Function,-,pulse_reader_alloc,PulseReader*,"const GpioPin*, uint32_t" Function,-,pulse_reader_free,void,PulseReader* Function,-,pulse_reader_receive,uint32_t,"PulseReader*, int" @@ -2737,7 +2739,6 @@ Function,+,scene_manager_stop,void,SceneManager* Function,+,sd_api_get_fs_type_text,const char*,SDFsType Function,-,secure_getenv,char*,const char* Function,-,seed48,unsigned short*,unsigned short[3] -Function,-,select,int,"int, fd_set*, fd_set*, fd_set*, timeval*" Function,-,serial_svc_is_started,_Bool, Function,-,serial_svc_notify_buffer_is_empty,void, Function,-,serial_svc_set_callbacks,void,"uint16_t, SerialServiceEventCallback, void*" @@ -3347,8 +3348,13 @@ Variable,-,ITM_RxBuffer,volatile int32_t, Variable,-,MSIRangeTable,const uint32_t[16], Variable,-,SmpsPrescalerTable,const uint32_t[4][6], Variable,+,SystemCoreClock,uint32_t, +Variable,-,__atexit,_atexit*, +Variable,-,__atexit0,_atexit, +Variable,-,__sf,__FILE[3], +Variable,-,__sglue,_glue, +Variable,-,__stdio_exit_handler,void (*)(), Variable,+,_ctype_,const char[], -Variable,+,_global_impure_ptr,_reent*, +Variable,+,_impure_data,_reent, Variable,+,_impure_ptr,_reent*, Variable,-,_sys_errlist,const char*[], Variable,-,_sys_nerr,int, diff --git a/targets/f7/ble_glue/gap.c b/targets/f7/ble_glue/gap.c index f0533567e..8e3ec58b7 100644 --- a/targets/f7/ble_glue/gap.c +++ b/targets/f7/ble_glue/gap.c @@ -11,7 +11,7 @@ #define FAST_ADV_TIMEOUT 30000 #define INITIAL_ADV_TIMEOUT 60000 -#define GAP_INTERVAL_TO_MS(x) (uint16_t)((x)*1.25) +#define GAP_INTERVAL_TO_MS(x) (uint16_t)((x) * 1.25) typedef struct { uint16_t gap_svc_handle; diff --git a/targets/f7/furi_hal/furi_hal_flash.c b/targets/f7/furi_hal/furi_hal_flash.c index 7ac7a8bd1..37eec744c 100644 --- a/targets/f7/furi_hal/furi_hal_flash.c +++ b/targets/f7/furi_hal/furi_hal_flash.c @@ -43,7 +43,7 @@ */ #define FURI_HAL_FLASH_C2_LOCK_TIMEOUT_MS (3000U) /* 3 seconds */ -#define IS_ADDR_ALIGNED_64BITS(__VALUE__) (((__VALUE__)&0x7U) == (0x00UL)) +#define IS_ADDR_ALIGNED_64BITS(__VALUE__) (((__VALUE__) & 0x7U) == (0x00UL)) #define IS_FLASH_PROGRAM_ADDRESS(__VALUE__) \ (((__VALUE__) >= FLASH_BASE) && ((__VALUE__) <= (FLASH_BASE + FLASH_SIZE - 8UL)) && \ (((__VALUE__) % 8UL) == 0UL)) diff --git a/targets/f7/furi_hal/furi_hal_memory.c b/targets/f7/furi_hal/furi_hal_memory.c index 3f8df1f44..0d60a3af5 100644 --- a/targets/f7/furi_hal/furi_hal_memory.c +++ b/targets/f7/furi_hal/furi_hal_memory.c @@ -46,8 +46,8 @@ void furi_hal_memory_init() { } uint32_t sram2a_busy_size = (uint32_t)&__sram2a_free__ - (uint32_t)&__sram2a_start__; - uint32_t sram2a_unprotected_size = (sbrsa)*1024; - uint32_t sram2b_unprotected_size = (snbrsa)*1024; + uint32_t sram2a_unprotected_size = (sbrsa) * 1024; + uint32_t sram2b_unprotected_size = (snbrsa) * 1024; memory->region[SRAM_A].start = (uint8_t*)&__sram2a_free__; memory->region[SRAM_B].start = (uint8_t*)&__sram2b_start__; diff --git a/targets/f7/furi_hal/furi_hal_os.c b/targets/f7/furi_hal/furi_hal_os.c index 85f2d2e45..efa760b47 100644 --- a/targets/f7/furi_hal/furi_hal_os.c +++ b/targets/f7/furi_hal/furi_hal_os.c @@ -17,8 +17,8 @@ #define FURI_HAL_IDLE_TIMER_CLK_HZ 32768 #define FURI_HAL_OS_TICK_HZ configTICK_RATE_HZ -#define FURI_HAL_OS_IDLE_CNT_TO_TICKS(x) (((x)*FURI_HAL_OS_TICK_HZ) / FURI_HAL_IDLE_TIMER_CLK_HZ) -#define FURI_HAL_OS_TICKS_TO_IDLE_CNT(x) (((x)*FURI_HAL_IDLE_TIMER_CLK_HZ) / FURI_HAL_OS_TICK_HZ) +#define FURI_HAL_OS_IDLE_CNT_TO_TICKS(x) (((x) * FURI_HAL_OS_TICK_HZ) / FURI_HAL_IDLE_TIMER_CLK_HZ) +#define FURI_HAL_OS_TICKS_TO_IDLE_CNT(x) (((x) * FURI_HAL_IDLE_TIMER_CLK_HZ) / FURI_HAL_OS_TICK_HZ) #define FURI_HAL_IDLE_TIMER_TICK_PER_EPOCH (FURI_HAL_OS_IDLE_CNT_TO_TICKS(FURI_HAL_IDLE_TIMER_MAX)) #define FURI_HAL_OS_MAX_SLEEP (FURI_HAL_IDLE_TIMER_TICK_PER_EPOCH - 1) diff --git a/targets/f7/platform_specific/cxx_virtual_stub.c b/targets/f7/platform_specific/cxx_virtual_stub.c new file mode 100644 index 000000000..a81e5a5e0 --- /dev/null +++ b/targets/f7/platform_specific/cxx_virtual_stub.c @@ -0,0 +1,6 @@ +#include "cxx_virtual_stub.h" +#include + +void __cxa_pure_virtual() { + furi_crash("C++ pure virtual call"); +} diff --git a/targets/f7/platform_specific/cxx_virtual_stub.h b/targets/f7/platform_specific/cxx_virtual_stub.h new file mode 100644 index 000000000..46211030e --- /dev/null +++ b/targets/f7/platform_specific/cxx_virtual_stub.h @@ -0,0 +1,13 @@ +#pragma once +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +void __cxa_pure_virtual(); + +#ifdef __cplusplus +} +#endif From 6836a7b7c51c8c26a18c75a63af7e4af689adbe9 Mon Sep 17 00:00:00 2001 From: Georgii Surkov <37121527+gsurkov@users.noreply.github.com> Date: Mon, 12 Feb 2024 05:16:34 +0300 Subject: [PATCH 2/5] [FL-3764] Expansion module service improvements (#3429) * Separate expansion control and worker threads * Add edge case checks * Reduce expansion control thread stack size, add comments * Fix crash when disabling expansion modules * Show a different RPC icon for expansion modules * Restore expansion interrupt on changing logging settings * Improve responsiveness in heavy games at the expense of dropped frames * Improve furi_hal_serial API * Fix a typo * Remove too optimistic furi_check, replace with condition * Fix premature RX interrupt during serial configuration * Disable expansion interrupt if the handle was acquired * Do not use a timer callback Co-authored-by: Aleksandr Kutuzov --- .../services/expansion/application.fam | 2 +- applications/services/expansion/expansion.c | 574 ++++++------------ .../services/expansion/expansion_worker.c | 396 ++++++++++++ .../services/expansion/expansion_worker.h | 78 +++ applications/services/rpc/rpc.c | 6 + applications/services/rpc/rpc_gui.c | 38 +- .../StatusBar/Exp_module_connected_12x8.png | Bin 0 -> 3612 bytes targets/f18/api_symbols.csv | 3 +- targets/f7/api_symbols.csv | 3 +- targets/f7/furi_hal/furi_hal_serial.c | 22 +- targets/f7/furi_hal/furi_hal_serial.h | 10 + targets/f7/furi_hal/furi_hal_serial_control.c | 77 ++- 12 files changed, 793 insertions(+), 416 deletions(-) create mode 100644 applications/services/expansion/expansion_worker.c create mode 100644 applications/services/expansion/expansion_worker.h create mode 100644 assets/icons/StatusBar/Exp_module_connected_12x8.png diff --git a/applications/services/expansion/application.fam b/applications/services/expansion/application.fam index 1402e8413..dbdde0a52 100644 --- a/applications/services/expansion/application.fam +++ b/applications/services/expansion/application.fam @@ -8,5 +8,5 @@ App( ], requires=["rpc_start"], provides=["expansion_settings"], - order=10, + order=150, ) diff --git a/applications/services/expansion/expansion.c b/applications/services/expansion/expansion.c index e385734b7..8f260f915 100644 --- a/applications/services/expansion/expansion.c +++ b/applications/services/expansion/expansion.c @@ -1,19 +1,17 @@ #include "expansion.h" -#include -#include #include #include +#include -#include - +#include "expansion_worker.h" #include "expansion_settings.h" -#include "expansion_protocol.h" #define TAG "ExpansionSrv" -#define EXPANSION_BUFFER_SIZE (sizeof(ExpansionFrame) + sizeof(ExpansionFrameChecksum)) +#define EXPANSION_CONTROL_QUEUE_SIZE (8UL) +#define EXPANSION_CONTROL_STACK_SIZE (768UL) typedef enum { ExpansionStateDisabled, @@ -22,368 +20,196 @@ typedef enum { } ExpansionState; typedef enum { - ExpansionSessionStateHandShake, - ExpansionSessionStateConnected, - ExpansionSessionStateRpcActive, -} ExpansionSessionState; + ExpansionMessageTypeEnable, + ExpansionMessageTypeDisable, + ExpansionMessageTypeSetListenSerial, + ExpansionMessageTypeModuleConnected, + ExpansionMessageTypeModuleDisconnected, +} ExpansionMessageType; -typedef enum { - ExpansionSessionExitReasonUnknown, - ExpansionSessionExitReasonUser, - ExpansionSessionExitReasonError, - ExpansionSessionExitReasonTimeout, -} ExpansionSessionExitReason; +typedef union { + FuriHalSerialId serial_id; +} ExpansionMessageData; -typedef enum { - ExpansionFlagStop = 1 << 0, - ExpansionFlagData = 1 << 1, - ExpansionFlagError = 1 << 2, -} ExpansionFlag; - -#define EXPANSION_ALL_FLAGS (ExpansionFlagData | ExpansionFlagStop) +typedef struct { + ExpansionMessageType type; + ExpansionMessageData data; + FuriApiLock api_lock; +} ExpansionMessage; struct Expansion { - ExpansionState state; - ExpansionSessionState session_state; - ExpansionSessionExitReason exit_reason; - FuriStreamBuffer* rx_buf; - FuriSemaphore* tx_semaphore; - FuriMutex* state_mutex; - FuriThread* worker_thread; + FuriThread* thread; + FuriMessageQueue* queue; FuriHalSerialId serial_id; - FuriHalSerialHandle* serial_handle; - RpcSession* rpc_session; + ExpansionWorker* worker; + ExpansionState state; }; -static void expansion_detect_callback(void* context); - -// Called in UART IRQ context -static void expansion_serial_rx_callback( - FuriHalSerialHandle* handle, - FuriHalSerialRxEvent event, - void* context) { - furi_assert(handle); - furi_assert(context); - - Expansion* instance = context; - - if(event == FuriHalSerialRxEventData) { - const uint8_t data = furi_hal_serial_async_rx(handle); - furi_stream_buffer_send(instance->rx_buf, &data, sizeof(data), 0); - furi_thread_flags_set(furi_thread_get_id(instance->worker_thread), ExpansionFlagData); - } -} - -static size_t expansion_receive_callback(uint8_t* data, size_t data_size, void* context) { - Expansion* instance = context; - - size_t received_size = 0; - - while(true) { - received_size += furi_stream_buffer_receive( - instance->rx_buf, data + received_size, data_size - received_size, 0); - - if(received_size == data_size) break; - - const uint32_t flags = furi_thread_flags_wait( - EXPANSION_ALL_FLAGS, FuriFlagWaitAny, furi_ms_to_ticks(EXPANSION_PROTOCOL_TIMEOUT_MS)); - - if(flags & FuriFlagError) { - if(flags == (unsigned)FuriFlagErrorTimeout) { - // Exiting due to timeout - instance->exit_reason = ExpansionSessionExitReasonTimeout; - } else { - // Exiting due to an unspecified error - instance->exit_reason = ExpansionSessionExitReasonError; - } - break; - } else if(flags & ExpansionFlagStop) { - // Exiting due to explicit request - instance->exit_reason = ExpansionSessionExitReasonUser; - break; - } else if(flags & ExpansionFlagError) { - // Exiting due to RPC error - instance->exit_reason = ExpansionSessionExitReasonError; - break; - } else if(flags & ExpansionFlagData) { - // Go to buffer reading - continue; - } - } - - return received_size; -} - -static inline bool expansion_receive_frame(Expansion* instance, ExpansionFrame* frame) { - return expansion_protocol_decode(frame, expansion_receive_callback, instance) == - ExpansionProtocolStatusOk; -} - -static size_t expansion_send_callback(const uint8_t* data, size_t data_size, void* context) { - Expansion* instance = context; - furi_hal_serial_tx(instance->serial_handle, data, data_size); - furi_hal_serial_tx_wait_complete(instance->serial_handle); - return data_size; -} - -static inline bool expansion_send_frame(Expansion* instance, const ExpansionFrame* frame) { - return expansion_protocol_encode(frame, expansion_send_callback, instance) == - ExpansionProtocolStatusOk; -} - -static bool expansion_send_heartbeat(Expansion* instance) { - const ExpansionFrame frame = { - .header.type = ExpansionFrameTypeHeartbeat, - .content.heartbeat = {}, - }; - - return expansion_send_frame(instance, &frame); -} - -static bool expansion_send_status_response(Expansion* instance, ExpansionFrameError error) { - const ExpansionFrame frame = { - .header.type = ExpansionFrameTypeStatus, - .content.status.error = error, - }; - - return expansion_send_frame(instance, &frame); -} - -static bool - expansion_send_data_response(Expansion* instance, const uint8_t* data, size_t data_size) { - furi_assert(data_size <= EXPANSION_PROTOCOL_MAX_DATA_SIZE); - - ExpansionFrame frame = { - .header.type = ExpansionFrameTypeData, - .content.data.size = data_size, - }; - - memcpy(frame.content.data.bytes, data, data_size); - return expansion_send_frame(instance, &frame); -} - -// Called in Rpc session thread context -static void expansion_rpc_send_callback(void* context, uint8_t* data, size_t data_size) { - Expansion* instance = context; - - for(size_t sent_data_size = 0; sent_data_size < data_size;) { - if(furi_semaphore_acquire( - instance->tx_semaphore, furi_ms_to_ticks(EXPANSION_PROTOCOL_TIMEOUT_MS)) != - FuriStatusOk) { - furi_thread_flags_set(furi_thread_get_id(instance->worker_thread), ExpansionFlagError); - break; - } - - const size_t current_data_size = - MIN(data_size - sent_data_size, EXPANSION_PROTOCOL_MAX_DATA_SIZE); - if(!expansion_send_data_response(instance, data + sent_data_size, current_data_size)) - break; - sent_data_size += current_data_size; - } -} - -static bool expansion_rpc_session_open(Expansion* instance) { - Rpc* rpc = furi_record_open(RECORD_RPC); - instance->rpc_session = rpc_session_open(rpc, RpcOwnerUart); - - if(instance->rpc_session) { - instance->tx_semaphore = furi_semaphore_alloc(1, 1); - rpc_session_set_context(instance->rpc_session, instance); - rpc_session_set_send_bytes_callback(instance->rpc_session, expansion_rpc_send_callback); - } - - return instance->rpc_session != NULL; -} - -static void expansion_rpc_session_close(Expansion* instance) { - if(instance->rpc_session) { - rpc_session_close(instance->rpc_session); - furi_semaphore_free(instance->tx_semaphore); - } - - furi_record_close(RECORD_RPC); -} - -static bool - expansion_handle_session_state_handshake(Expansion* instance, const ExpansionFrame* rx_frame) { - bool success = false; - - do { - if(rx_frame->header.type != ExpansionFrameTypeBaudRate) break; - const uint32_t baud_rate = rx_frame->content.baud_rate.baud; - - FURI_LOG_D(TAG, "Proposed baud rate: %lu", baud_rate); - - if(furi_hal_serial_is_baud_rate_supported(instance->serial_handle, baud_rate)) { - instance->session_state = ExpansionSessionStateConnected; - // Send response at previous baud rate - if(!expansion_send_status_response(instance, ExpansionFrameErrorNone)) break; - furi_hal_serial_set_br(instance->serial_handle, baud_rate); - - } else { - if(!expansion_send_status_response(instance, ExpansionFrameErrorBaudRate)) break; - FURI_LOG_E(TAG, "Bad baud rate"); - } - success = true; - } while(false); - - return success; -} - -static bool - expansion_handle_session_state_connected(Expansion* instance, const ExpansionFrame* rx_frame) { - bool success = false; - - do { - if(rx_frame->header.type == ExpansionFrameTypeControl) { - if(rx_frame->content.control.command != ExpansionFrameControlCommandStartRpc) break; - instance->session_state = ExpansionSessionStateRpcActive; - if(!expansion_rpc_session_open(instance)) break; - if(!expansion_send_status_response(instance, ExpansionFrameErrorNone)) break; - - } else if(rx_frame->header.type == ExpansionFrameTypeHeartbeat) { - if(!expansion_send_heartbeat(instance)) break; - - } else { - break; - } - success = true; - } while(false); - - return success; -} - -static bool - expansion_handle_session_state_rpc_active(Expansion* instance, const ExpansionFrame* rx_frame) { - bool success = false; - - do { - if(rx_frame->header.type == ExpansionFrameTypeData) { - if(!expansion_send_status_response(instance, ExpansionFrameErrorNone)) break; - - const size_t size_consumed = rpc_session_feed( - instance->rpc_session, - rx_frame->content.data.bytes, - rx_frame->content.data.size, - EXPANSION_PROTOCOL_TIMEOUT_MS); - if(size_consumed != rx_frame->content.data.size) break; - - } else if(rx_frame->header.type == ExpansionFrameTypeControl) { - if(rx_frame->content.control.command != ExpansionFrameControlCommandStopRpc) break; - instance->session_state = ExpansionSessionStateConnected; - expansion_rpc_session_close(instance); - if(!expansion_send_status_response(instance, ExpansionFrameErrorNone)) break; - - } else if(rx_frame->header.type == ExpansionFrameTypeStatus) { - if(rx_frame->content.status.error != ExpansionFrameErrorNone) break; - furi_semaphore_release(instance->tx_semaphore); - - } else if(rx_frame->header.type == ExpansionFrameTypeHeartbeat) { - if(!expansion_send_heartbeat(instance)) break; - - } else { - break; - } - success = true; - } while(false); - - return success; -} - -static inline void expansion_state_machine(Expansion* instance) { - typedef bool (*ExpansionSessionStateHandler)(Expansion*, const ExpansionFrame*); - - static const ExpansionSessionStateHandler expansion_handlers[] = { - [ExpansionSessionStateHandShake] = expansion_handle_session_state_handshake, - [ExpansionSessionStateConnected] = expansion_handle_session_state_connected, - [ExpansionSessionStateRpcActive] = expansion_handle_session_state_rpc_active, - }; - - ExpansionFrame rx_frame; - - while(true) { - if(!expansion_receive_frame(instance, &rx_frame)) break; - if(!expansion_handlers[instance->session_state](instance, &rx_frame)) break; - } -} - -static void expansion_worker_pending_callback(void* context, uint32_t arg) { - furi_assert(context); - UNUSED(arg); - - Expansion* instance = context; - furi_thread_join(instance->worker_thread); - - // Do not re-enable detection interrupt on user-requested exit - if(instance->exit_reason != ExpansionSessionExitReasonUser) { - furi_check(furi_mutex_acquire(instance->state_mutex, FuriWaitForever) == FuriStatusOk); - instance->state = ExpansionStateEnabled; - furi_hal_serial_control_set_expansion_callback( - instance->serial_id, expansion_detect_callback, instance); - furi_mutex_release(instance->state_mutex); - } -} - -static int32_t expansion_worker(void* context) { - furi_assert(context); - Expansion* instance = context; - - furi_hal_power_insomnia_enter(); - furi_hal_serial_control_set_expansion_callback(instance->serial_id, NULL, NULL); - - instance->serial_handle = furi_hal_serial_control_acquire(instance->serial_id); - furi_check(instance->serial_handle); - - FURI_LOG_D(TAG, "Service started"); - - instance->rx_buf = furi_stream_buffer_alloc(EXPANSION_BUFFER_SIZE, 1); - instance->session_state = ExpansionSessionStateHandShake; - instance->exit_reason = ExpansionSessionExitReasonUnknown; - - furi_hal_serial_init(instance->serial_handle, EXPANSION_PROTOCOL_DEFAULT_BAUD_RATE); - - furi_hal_serial_async_rx_start( - instance->serial_handle, expansion_serial_rx_callback, instance, false); - - if(expansion_send_heartbeat(instance)) { - expansion_state_machine(instance); - } - - if(instance->session_state == ExpansionSessionStateRpcActive) { - expansion_rpc_session_close(instance); - } - - FURI_LOG_D(TAG, "Service stopped"); - - furi_hal_serial_control_release(instance->serial_handle); - furi_stream_buffer_free(instance->rx_buf); - - furi_hal_power_insomnia_exit(); - furi_timer_pending_callback(expansion_worker_pending_callback, instance, 0); - - return 0; -} +static const char* const expansion_uart_names[] = { + "USART", + "LPUART", +}; // Called from the serial control thread static void expansion_detect_callback(void* context) { furi_assert(context); Expansion* instance = context; - furi_check(furi_mutex_acquire(instance->state_mutex, FuriWaitForever) == FuriStatusOk); + ExpansionMessage message = { + .type = ExpansionMessageTypeModuleConnected, + .api_lock = NULL, // Not locking the API here to avoid a deadlock + }; - if(instance->state == ExpansionStateEnabled) { - instance->state = ExpansionStateRunning; - furi_thread_start(instance->worker_thread); + // Not waiting for available queue space, discarding message if there is none + const FuriStatus status = furi_message_queue_put(instance->queue, &message, 0); + UNUSED(status); +} + +static void expansion_worker_callback(void* context) { + furi_assert(context); + Expansion* instance = context; + + ExpansionMessage message = { + .type = ExpansionMessageTypeModuleDisconnected, + .api_lock = NULL, // Not locking the API here to avoid a deadlock + }; + + const FuriStatus status = furi_message_queue_put(instance->queue, &message, FuriWaitForever); + furi_check(status == FuriStatusOk); +} + +static void + expansion_control_handler_enable(Expansion* instance, const ExpansionMessageData* data) { + UNUSED(data); + + if(instance->state != ExpansionStateDisabled) { + return; } - furi_mutex_release(instance->state_mutex); + ExpansionSettings settings = {0}; + if(!expansion_settings_load(&settings)) { + expansion_settings_save(&settings); + } + + if(settings.uart_index < FuriHalSerialIdMax) { + instance->state = ExpansionStateEnabled; + instance->serial_id = settings.uart_index; + furi_hal_serial_control_set_expansion_callback( + instance->serial_id, expansion_detect_callback, instance); + + FURI_LOG_D(TAG, "Detection enabled on %s", expansion_uart_names[instance->serial_id]); + } +} + +static void + expansion_control_handler_disable(Expansion* instance, const ExpansionMessageData* data) { + UNUSED(data); + + if(instance->state == ExpansionStateDisabled) { + return; + } else if(instance->state == ExpansionStateRunning) { + expansion_worker_stop(instance->worker); + expansion_worker_free(instance->worker); + } else { + furi_hal_serial_control_set_expansion_callback(instance->serial_id, NULL, NULL); + } + + instance->state = ExpansionStateDisabled; + + FURI_LOG_D(TAG, "Detection disabled"); +} + +static void expansion_control_handler_set_listen_serial( + Expansion* instance, + const ExpansionMessageData* data) { + furi_check(data->serial_id < FuriHalSerialIdMax); + + if(instance->state == ExpansionStateRunning) { + expansion_worker_stop(instance->worker); + expansion_worker_free(instance->worker); + + } else if(instance->state == ExpansionStateEnabled) { + furi_hal_serial_control_set_expansion_callback(instance->serial_id, NULL, NULL); + } + + instance->state = ExpansionStateEnabled; + instance->serial_id = data->serial_id; + + furi_hal_serial_control_set_expansion_callback( + instance->serial_id, expansion_detect_callback, instance); + + FURI_LOG_D(TAG, "Listen serial changed to %s", expansion_uart_names[instance->serial_id]); +} + +static void expansion_control_handler_module_connected( + Expansion* instance, + const ExpansionMessageData* data) { + UNUSED(data); + if(instance->state != ExpansionStateEnabled) { + return; + } + + furi_hal_serial_control_set_expansion_callback(instance->serial_id, NULL, NULL); + + instance->state = ExpansionStateRunning; + instance->worker = expansion_worker_alloc(instance->serial_id); + + expansion_worker_set_callback(instance->worker, expansion_worker_callback, instance); + expansion_worker_start(instance->worker); +} + +static void expansion_control_handler_module_disconnected( + Expansion* instance, + const ExpansionMessageData* data) { + UNUSED(data); + if(instance->state != ExpansionStateRunning) { + return; + } + + instance->state = ExpansionStateEnabled; + expansion_worker_free(instance->worker); + furi_hal_serial_control_set_expansion_callback( + instance->serial_id, expansion_detect_callback, instance); +} + +typedef void (*ExpansionControlHandler)(Expansion*, const ExpansionMessageData*); + +static const ExpansionControlHandler expansion_control_handlers[] = { + [ExpansionMessageTypeEnable] = expansion_control_handler_enable, + [ExpansionMessageTypeDisable] = expansion_control_handler_disable, + [ExpansionMessageTypeSetListenSerial] = expansion_control_handler_set_listen_serial, + [ExpansionMessageTypeModuleConnected] = expansion_control_handler_module_connected, + [ExpansionMessageTypeModuleDisconnected] = expansion_control_handler_module_disconnected, +}; + +static int32_t expansion_control(void* context) { + furi_assert(context); + Expansion* instance = context; + + for(;;) { + ExpansionMessage message; + + FuriStatus status = furi_message_queue_get(instance->queue, &message, FuriWaitForever); + furi_check(status == FuriStatusOk); + + furi_check(message.type < COUNT_OF(expansion_control_handlers)); + expansion_control_handlers[message.type](instance, &message.data); + + if(message.api_lock != NULL) { + api_lock_unlock(message.api_lock); + } + } + + return 0; } static Expansion* expansion_alloc() { Expansion* instance = malloc(sizeof(Expansion)); - instance->state_mutex = furi_mutex_alloc(FuriMutexTypeNormal); - instance->worker_thread = furi_thread_alloc_ex(TAG, 768, expansion_worker, instance); + instance->queue = + furi_message_queue_alloc(EXPANSION_CONTROL_QUEUE_SIZE, sizeof(ExpansionMessage)); + instance->thread = + furi_thread_alloc_ex(TAG, EXPANSION_CONTROL_STACK_SIZE, expansion_control, instance); return instance; } @@ -393,6 +219,7 @@ void expansion_on_system_start(void* arg) { Expansion* instance = expansion_alloc(); furi_record_create(RECORD_EXPANSION, instance); + furi_thread_start(instance->thread); expansion_enable(instance); } @@ -400,42 +227,39 @@ void expansion_on_system_start(void* arg) { // Public API functions void expansion_enable(Expansion* instance) { - ExpansionSettings settings = {}; - if(!expansion_settings_load(&settings)) { - expansion_settings_save(&settings); - } else if(settings.uart_index < FuriHalSerialIdMax) { - expansion_set_listen_serial(instance, settings.uart_index); - } + furi_check(instance); + + ExpansionMessage message = { + .type = ExpansionMessageTypeEnable, + .api_lock = api_lock_alloc_locked(), + }; + + furi_message_queue_put(instance->queue, &message, FuriWaitForever); + api_lock_wait_unlock_and_free(message.api_lock); } void expansion_disable(Expansion* instance) { - furi_check(furi_mutex_acquire(instance->state_mutex, FuriWaitForever) == FuriStatusOk); + furi_check(instance); - if(instance->state == ExpansionStateRunning) { - furi_thread_flags_set(furi_thread_get_id(instance->worker_thread), ExpansionFlagStop); - furi_thread_join(instance->worker_thread); - } else if(instance->state == ExpansionStateEnabled) { - FURI_LOG_D(TAG, "Detection disabled"); - furi_hal_serial_control_set_expansion_callback(instance->serial_id, NULL, NULL); - } + ExpansionMessage message = { + .type = ExpansionMessageTypeDisable, + .api_lock = api_lock_alloc_locked(), + }; - instance->state = ExpansionStateDisabled; - - furi_mutex_release(instance->state_mutex); + furi_message_queue_put(instance->queue, &message, FuriWaitForever); + api_lock_wait_unlock_and_free(message.api_lock); } void expansion_set_listen_serial(Expansion* instance, FuriHalSerialId serial_id) { - expansion_disable(instance); + furi_check(instance); + furi_check(serial_id < FuriHalSerialIdMax); - furi_check(furi_mutex_acquire(instance->state_mutex, FuriWaitForever) == FuriStatusOk); + ExpansionMessage message = { + .type = ExpansionMessageTypeSetListenSerial, + .data.serial_id = serial_id, + .api_lock = api_lock_alloc_locked(), + }; - instance->serial_id = serial_id; - instance->state = ExpansionStateEnabled; - - furi_hal_serial_control_set_expansion_callback( - instance->serial_id, expansion_detect_callback, instance); - - furi_mutex_release(instance->state_mutex); - - FURI_LOG_D(TAG, "Detection enabled"); + furi_message_queue_put(instance->queue, &message, FuriWaitForever); + api_lock_wait_unlock_and_free(message.api_lock); } diff --git a/applications/services/expansion/expansion_worker.c b/applications/services/expansion/expansion_worker.c new file mode 100644 index 000000000..fd92063d2 --- /dev/null +++ b/applications/services/expansion/expansion_worker.c @@ -0,0 +1,396 @@ +#include "expansion_worker.h" + +#include +#include +#include + +#include +#include + +#include "expansion_protocol.h" + +#define TAG "ExpansionSrv" + +#define EXPANSION_WORKER_STACK_SZIE (768UL) +#define EXPANSION_WORKER_BUFFER_SIZE (sizeof(ExpansionFrame) + sizeof(ExpansionFrameChecksum)) + +typedef enum { + ExpansionWorkerStateHandShake, + ExpansionWorkerStateConnected, + ExpansionWorkerStateRpcActive, +} ExpansionWorkerState; + +typedef enum { + ExpansionWorkerExitReasonUnknown, + ExpansionWorkerExitReasonUser, + ExpansionWorkerExitReasonError, + ExpansionWorkerExitReasonTimeout, +} ExpansionWorkerExitReason; + +typedef enum { + ExpansionWorkerFlagStop = 1 << 0, + ExpansionWorkerFlagData = 1 << 1, + ExpansionWorkerFlagError = 1 << 2, +} ExpansionWorkerFlag; + +#define EXPANSION_ALL_FLAGS (ExpansionWorkerFlagData | ExpansionWorkerFlagStop) + +struct ExpansionWorker { + FuriThread* thread; + FuriStreamBuffer* rx_buf; + FuriSemaphore* tx_semaphore; + + FuriHalSerialId serial_id; + FuriHalSerialHandle* serial_handle; + + RpcSession* rpc_session; + + ExpansionWorkerState state; + ExpansionWorkerExitReason exit_reason; + ExpansionWorkerCallback callback; + void* cb_context; +}; + +// Called in UART IRQ context +static void expansion_worker_serial_rx_callback( + FuriHalSerialHandle* handle, + FuriHalSerialRxEvent event, + void* context) { + furi_assert(handle); + furi_assert(context); + + ExpansionWorker* instance = context; + + if(event & (FuriHalSerialRxEventNoiseError | FuriHalSerialRxEventFrameError | + FuriHalSerialRxEventOverrunError)) { + furi_thread_flags_set(furi_thread_get_id(instance->thread), ExpansionWorkerFlagError); + } else if(event & FuriHalSerialRxEventData) { + while(furi_hal_serial_async_rx_available(handle)) { + const uint8_t data = furi_hal_serial_async_rx(handle); + furi_stream_buffer_send(instance->rx_buf, &data, sizeof(data), 0); + } + furi_thread_flags_set(furi_thread_get_id(instance->thread), ExpansionWorkerFlagData); + } +} + +static size_t expansion_worker_receive_callback(uint8_t* data, size_t data_size, void* context) { + ExpansionWorker* instance = context; + + size_t received_size = 0; + + while(true) { + received_size += furi_stream_buffer_receive( + instance->rx_buf, data + received_size, data_size - received_size, 0); + + if(received_size == data_size) break; + + const uint32_t flags = furi_thread_flags_wait( + EXPANSION_ALL_FLAGS, FuriFlagWaitAny, furi_ms_to_ticks(EXPANSION_PROTOCOL_TIMEOUT_MS)); + + if(flags & FuriFlagError) { + if(flags == (unsigned)FuriFlagErrorTimeout) { + // Exiting due to timeout + instance->exit_reason = ExpansionWorkerExitReasonTimeout; + } else { + // Exiting due to an unspecified error + instance->exit_reason = ExpansionWorkerExitReasonError; + } + break; + } else if(flags & ExpansionWorkerFlagStop) { + // Exiting due to explicit request + instance->exit_reason = ExpansionWorkerExitReasonUser; + break; + } else if(flags & ExpansionWorkerFlagError) { + // Exiting due to RPC error + instance->exit_reason = ExpansionWorkerExitReasonError; + break; + } else if(flags & ExpansionWorkerFlagData) { + // Go to buffer reading + continue; + } + } + + return received_size; +} + +static inline bool + expansion_worker_receive_frame(ExpansionWorker* instance, ExpansionFrame* frame) { + return expansion_protocol_decode(frame, expansion_worker_receive_callback, instance) == + ExpansionProtocolStatusOk; +} + +static size_t + expansion_worker_send_callback(const uint8_t* data, size_t data_size, void* context) { + ExpansionWorker* instance = context; + furi_hal_serial_tx(instance->serial_handle, data, data_size); + furi_hal_serial_tx_wait_complete(instance->serial_handle); + return data_size; +} + +static inline bool + expansion_worker_send_frame(ExpansionWorker* instance, const ExpansionFrame* frame) { + return expansion_protocol_encode(frame, expansion_worker_send_callback, instance) == + ExpansionProtocolStatusOk; +} + +static bool expansion_worker_send_heartbeat(ExpansionWorker* instance) { + const ExpansionFrame frame = { + .header.type = ExpansionFrameTypeHeartbeat, + .content.heartbeat = {}, + }; + + return expansion_worker_send_frame(instance, &frame); +} + +static bool + expansion_worker_send_status_response(ExpansionWorker* instance, ExpansionFrameError error) { + const ExpansionFrame frame = { + .header.type = ExpansionFrameTypeStatus, + .content.status.error = error, + }; + + return expansion_worker_send_frame(instance, &frame); +} + +static bool expansion_worker_send_data_response( + ExpansionWorker* instance, + const uint8_t* data, + size_t data_size) { + furi_assert(data_size <= EXPANSION_PROTOCOL_MAX_DATA_SIZE); + + ExpansionFrame frame = { + .header.type = ExpansionFrameTypeData, + .content.data.size = data_size, + }; + + memcpy(frame.content.data.bytes, data, data_size); + return expansion_worker_send_frame(instance, &frame); +} + +// Called in Rpc session thread context +static void expansion_worker_rpc_send_callback(void* context, uint8_t* data, size_t data_size) { + ExpansionWorker* instance = context; + + for(size_t sent_data_size = 0; sent_data_size < data_size;) { + if(furi_semaphore_acquire( + instance->tx_semaphore, furi_ms_to_ticks(EXPANSION_PROTOCOL_TIMEOUT_MS)) != + FuriStatusOk) { + furi_thread_flags_set(furi_thread_get_id(instance->thread), ExpansionWorkerFlagError); + break; + } + + const size_t current_data_size = + MIN(data_size - sent_data_size, EXPANSION_PROTOCOL_MAX_DATA_SIZE); + if(!expansion_worker_send_data_response(instance, data + sent_data_size, current_data_size)) + break; + sent_data_size += current_data_size; + } +} + +static bool expansion_worker_rpc_session_open(ExpansionWorker* instance) { + Rpc* rpc = furi_record_open(RECORD_RPC); + instance->rpc_session = rpc_session_open(rpc, RpcOwnerUart); + + if(instance->rpc_session) { + instance->tx_semaphore = furi_semaphore_alloc(1, 1); + rpc_session_set_context(instance->rpc_session, instance); + rpc_session_set_send_bytes_callback( + instance->rpc_session, expansion_worker_rpc_send_callback); + } + + return instance->rpc_session != NULL; +} + +static void expansion_worker_rpc_session_close(ExpansionWorker* instance) { + if(instance->rpc_session) { + rpc_session_close(instance->rpc_session); + furi_semaphore_free(instance->tx_semaphore); + } + + furi_record_close(RECORD_RPC); +} + +static bool expansion_worker_handle_state_handshake( + ExpansionWorker* instance, + const ExpansionFrame* rx_frame) { + bool success = false; + + do { + if(rx_frame->header.type != ExpansionFrameTypeBaudRate) break; + const uint32_t baud_rate = rx_frame->content.baud_rate.baud; + + FURI_LOG_D(TAG, "Proposed baud rate: %lu", baud_rate); + + if(furi_hal_serial_is_baud_rate_supported(instance->serial_handle, baud_rate)) { + instance->state = ExpansionWorkerStateConnected; + // Send response at previous baud rate + if(!expansion_worker_send_status_response(instance, ExpansionFrameErrorNone)) break; + furi_hal_serial_set_br(instance->serial_handle, baud_rate); + + } else { + if(!expansion_worker_send_status_response(instance, ExpansionFrameErrorBaudRate)) + break; + FURI_LOG_E(TAG, "Bad baud rate"); + } + success = true; + } while(false); + + return success; +} + +static bool expansion_worker_handle_state_connected( + ExpansionWorker* instance, + const ExpansionFrame* rx_frame) { + bool success = false; + + do { + if(rx_frame->header.type == ExpansionFrameTypeControl) { + if(rx_frame->content.control.command != ExpansionFrameControlCommandStartRpc) break; + instance->state = ExpansionWorkerStateRpcActive; + if(!expansion_worker_rpc_session_open(instance)) break; + if(!expansion_worker_send_status_response(instance, ExpansionFrameErrorNone)) break; + + } else if(rx_frame->header.type == ExpansionFrameTypeHeartbeat) { + if(!expansion_worker_send_heartbeat(instance)) break; + + } else { + break; + } + success = true; + } while(false); + + return success; +} + +static bool expansion_worker_handle_state_rpc_active( + ExpansionWorker* instance, + const ExpansionFrame* rx_frame) { + bool success = false; + + do { + if(rx_frame->header.type == ExpansionFrameTypeData) { + if(!expansion_worker_send_status_response(instance, ExpansionFrameErrorNone)) break; + + const size_t size_consumed = rpc_session_feed( + instance->rpc_session, + rx_frame->content.data.bytes, + rx_frame->content.data.size, + EXPANSION_PROTOCOL_TIMEOUT_MS); + if(size_consumed != rx_frame->content.data.size) break; + + } else if(rx_frame->header.type == ExpansionFrameTypeControl) { + if(rx_frame->content.control.command != ExpansionFrameControlCommandStopRpc) break; + instance->state = ExpansionWorkerStateConnected; + expansion_worker_rpc_session_close(instance); + if(!expansion_worker_send_status_response(instance, ExpansionFrameErrorNone)) break; + + } else if(rx_frame->header.type == ExpansionFrameTypeStatus) { + if(rx_frame->content.status.error != ExpansionFrameErrorNone) break; + furi_semaphore_release(instance->tx_semaphore); + + } else if(rx_frame->header.type == ExpansionFrameTypeHeartbeat) { + if(!expansion_worker_send_heartbeat(instance)) break; + + } else { + break; + } + success = true; + } while(false); + + return success; +} + +typedef bool (*ExpansionWorkerStateHandler)(ExpansionWorker*, const ExpansionFrame*); + +static const ExpansionWorkerStateHandler expansion_handlers[] = { + [ExpansionWorkerStateHandShake] = expansion_worker_handle_state_handshake, + [ExpansionWorkerStateConnected] = expansion_worker_handle_state_connected, + [ExpansionWorkerStateRpcActive] = expansion_worker_handle_state_rpc_active, +}; + +static inline void expansion_worker_state_machine(ExpansionWorker* instance) { + ExpansionFrame rx_frame; + + while(true) { + if(!expansion_worker_receive_frame(instance, &rx_frame)) break; + if(!expansion_handlers[instance->state](instance, &rx_frame)) break; + } +} + +static int32_t expansion_worker(void* context) { + furi_assert(context); + ExpansionWorker* instance = context; + + furi_hal_power_insomnia_enter(); + + instance->serial_handle = furi_hal_serial_control_acquire(instance->serial_id); + furi_check(instance->serial_handle); + + FURI_LOG_D(TAG, "Worker started"); + + instance->state = ExpansionWorkerStateHandShake; + instance->exit_reason = ExpansionWorkerExitReasonUnknown; + + furi_hal_serial_init(instance->serial_handle, EXPANSION_PROTOCOL_DEFAULT_BAUD_RATE); + + furi_hal_serial_async_rx_start( + instance->serial_handle, expansion_worker_serial_rx_callback, instance, true); + + if(expansion_worker_send_heartbeat(instance)) { + expansion_worker_state_machine(instance); + } + + if(instance->state == ExpansionWorkerStateRpcActive) { + expansion_worker_rpc_session_close(instance); + } + + FURI_LOG_D(TAG, "Worker stopped"); + + furi_hal_serial_control_release(instance->serial_handle); + furi_hal_power_insomnia_exit(); + + // Do not invoke worker callback on user-requested exit + if((instance->exit_reason != ExpansionWorkerExitReasonUser) && (instance->callback != NULL)) { + instance->callback(instance->cb_context); + } + + return 0; +} + +ExpansionWorker* expansion_worker_alloc(FuriHalSerialId serial_id) { + ExpansionWorker* instance = malloc(sizeof(ExpansionWorker)); + + instance->thread = furi_thread_alloc_ex( + TAG "Worker", EXPANSION_WORKER_STACK_SZIE, expansion_worker, instance); + instance->rx_buf = furi_stream_buffer_alloc(EXPANSION_WORKER_BUFFER_SIZE, 1); + instance->serial_id = serial_id; + + // Improves responsiveness in heavy games at the expense of dropped frames + furi_thread_set_priority(instance->thread, FuriThreadPriorityLow); + + return instance; +} + +void expansion_worker_free(ExpansionWorker* instance) { + furi_stream_buffer_free(instance->rx_buf); + furi_thread_join(instance->thread); + furi_thread_free(instance->thread); + free(instance); +} + +void expansion_worker_set_callback( + ExpansionWorker* instance, + ExpansionWorkerCallback callback, + void* context) { + instance->callback = callback; + instance->cb_context = context; +} + +void expansion_worker_start(ExpansionWorker* instance) { + furi_thread_start(instance->thread); +} + +void expansion_worker_stop(ExpansionWorker* instance) { + furi_thread_flags_set(furi_thread_get_id(instance->thread), ExpansionWorkerFlagStop); + furi_thread_join(instance->thread); +} diff --git a/applications/services/expansion/expansion_worker.h b/applications/services/expansion/expansion_worker.h new file mode 100644 index 000000000..761f79c1d --- /dev/null +++ b/applications/services/expansion/expansion_worker.h @@ -0,0 +1,78 @@ +/** + * @file expansion_worker.h + * @brief Expansion module handling thread wrapper. + * + * The worker is started each time an expansion module is detected + * and handles all of the communication protocols. Likewise, it is stopped + * upon module disconnection or communication error. + * + * @warning This file is a private implementation detail. Please do not attempt to use it in applications. + */ +#pragma once + +#include + +/** + * @brief Expansion worker opaque type declaration. + */ +typedef struct ExpansionWorker ExpansionWorker; + +/** + * @brief Worker callback type. + * + * @see expansion_worker_set_callback() + * + * @param[in,out] context pointer to a user-defined object. + */ +typedef void (*ExpansionWorkerCallback)(void* context); + +/** + * @brief Create an expansion worker instance. + * + * @param[in] serial_id numerical identifier of the serial to be used by the worker. + * @returns pointer to the created instance. + */ +ExpansionWorker* expansion_worker_alloc(FuriHalSerialId serial_id); + +/** + * @brief Delete an expansion worker instance. + * + * @param[in,out] instance pointer to the instance to be deleted. + */ +void expansion_worker_free(ExpansionWorker* instance); + +/** + * @brief Set the module disconnect callback. + * + * The callback will be triggered upon worker stop EXCEPT + * when it was stopped via an expansion_worker_stop() call. + * + * In other words, the callback will ONLY be triggered if the worker was + * stopped due to the user disconnecting/resetting/powering down the module, + * or due to some communication error. + * + * @param[in,out] instance pointer to the worker instance to be modified. + * @param[in] callback pointer to the callback function to be called under the above conditions. + * @param[in] context pointer to a user-defined object, will be passed as a parameter to the callback. + */ +void expansion_worker_set_callback( + ExpansionWorker* instance, + ExpansionWorkerCallback callback, + void* context); + +/** + * @brief Start the expansion module worker. + * + * @param[in,out] instance pointer to the worker instance to be started. + */ +void expansion_worker_start(ExpansionWorker* instance); + +/** + * @brief Stop the expansion module worker. + * + * If the worker was stopped via this call (and not because of module disconnect/ + * protocol error), the callback will not be triggered. + * + * @param[in,out] instance pointer to the worker instance to be stopped. + */ +void expansion_worker_stop(ExpansionWorker* instance); diff --git a/applications/services/rpc/rpc.c b/applications/services/rpc/rpc.c index 3179dbb55..d33ea178c 100644 --- a/applications/services/rpc/rpc.c +++ b/applications/services/rpc/rpc.c @@ -189,6 +189,12 @@ bool rpc_pb_stream_read(pb_istream_t* istream, pb_byte_t* buf, size_t count) { furi_assert(session); furi_assert(istream->bytes_left); + /* TODO FL-3768 this function may be called after + marking the worker for termination */ + if(session->terminate) { + return false; + } + uint32_t flags = 0; size_t bytes_received = 0; diff --git a/applications/services/rpc/rpc_gui.c b/applications/services/rpc/rpc_gui.c index 98860332d..ee3526590 100644 --- a/applications/services/rpc/rpc_gui.c +++ b/applications/services/rpc/rpc_gui.c @@ -54,6 +54,7 @@ typedef enum { typedef struct { RpcSession* session; Gui* gui; + const Icon* icon; // Receive part ViewPort* virtual_display_view_port; @@ -380,10 +381,19 @@ static void rpc_system_gui_virtual_display_frame_process(const PB_Main* request, (void)session; } +static const Icon* rpc_system_gui_get_owner_icon(RpcOwner owner) { + switch(owner) { + case RpcOwnerUart: + return &I_Exp_module_connected_12x8; + default: + return &I_Rpc_active_7x8; + } +} + static void rpc_active_session_icon_draw_callback(Canvas* canvas, void* context) { - UNUSED(context); furi_assert(canvas); - canvas_draw_icon(canvas, 0, 0, &I_Rpc_active_7x8); + RpcGuiSystem* rpc_gui = context; + canvas_draw_icon(canvas, 0, 0, rpc_gui->icon); } void* rpc_system_gui_alloc(RpcSession* session) { @@ -394,16 +404,16 @@ void* rpc_system_gui_alloc(RpcSession* session) { rpc_gui->session = session; // Active session icon - rpc_gui->rpc_session_active_viewport = view_port_alloc(); - view_port_set_width(rpc_gui->rpc_session_active_viewport, icon_get_width(&I_Rpc_active_7x8)); - view_port_draw_callback_set( - rpc_gui->rpc_session_active_viewport, rpc_active_session_icon_draw_callback, session); - if(rpc_session_get_owner(rpc_gui->session) != RpcOwnerBle) { - view_port_enabled_set(rpc_gui->rpc_session_active_viewport, true); - } else { - view_port_enabled_set(rpc_gui->rpc_session_active_viewport, false); + const RpcOwner owner = rpc_session_get_owner(rpc_gui->session); + if(owner != RpcOwnerBle) { + rpc_gui->icon = rpc_system_gui_get_owner_icon(owner); + rpc_gui->rpc_session_active_viewport = view_port_alloc(); + view_port_set_width(rpc_gui->rpc_session_active_viewport, icon_get_width(rpc_gui->icon)); + view_port_draw_callback_set( + rpc_gui->rpc_session_active_viewport, rpc_active_session_icon_draw_callback, rpc_gui); + gui_add_view_port( + rpc_gui->gui, rpc_gui->rpc_session_active_viewport, GuiLayerStatusBarLeft); } - gui_add_view_port(rpc_gui->gui, rpc_gui->rpc_session_active_viewport, GuiLayerStatusBarLeft); RpcHandler rpc_handler = { .message_handler = NULL, @@ -445,8 +455,10 @@ void rpc_system_gui_free(void* context) { rpc_gui->virtual_display_not_empty = false; } - gui_remove_view_port(rpc_gui->gui, rpc_gui->rpc_session_active_viewport); - view_port_free(rpc_gui->rpc_session_active_viewport); + if(rpc_gui->rpc_session_active_viewport) { + gui_remove_view_port(rpc_gui->gui, rpc_gui->rpc_session_active_viewport); + view_port_free(rpc_gui->rpc_session_active_viewport); + } if(rpc_gui->is_streaming) { rpc_gui->is_streaming = false; diff --git a/assets/icons/StatusBar/Exp_module_connected_12x8.png b/assets/icons/StatusBar/Exp_module_connected_12x8.png new file mode 100644 index 0000000000000000000000000000000000000000..a5f09668267a55680aaa66355966bdaddcc173e1 GIT binary patch literal 3612 zcmaJ@cT`i^x4sNWZz4rRoDh&Eq)}pGLP+?eE*)E@!P9chufOLUf-f000tJ zmINo>D$aX_h532kZ|H?)01!2!e?zI($=vmd4qC}jl0dv1eDryWBC&j!mi3W>WE(veJ?bvayudN zPVAM=jfs#uzHjT+K6Yh|j61OVLu6Y^>%=s~BwSRD z1xO25QB(ppRr!GRPz>e}xIyR{z!&&hOc-de%W8n_kJ*;`jLSH|7j~Jifs=IDMmS6s zFbPh;6#ypYd|}z?>dru}0N~N(E`59rgk`U1gzy3GH&o>Ka+3hi9%?KB z@HqmM^&Y!o29RKYthY@I3V5#zz^q)oErI)wfrcJwu}VN#9Dq5-Mri>8A%I7_s%ju` zIRlV2UvkC#ai~IiR-0F;j7m(68p?0m$?>vX-VE%Ktmx@6OsJMTt^4`8;5OLPb82g*x0Gc9027?hzAYVS)xNMJ zf?PKH|utbb^Fxni5YzxTSxWzUh1i5A}qll5~qsdhvg?(U2x_8Yb5O zV5yeb_(oHhKg=g`qL;h9XuN5Wdk_fowz}>Q0H+9Q5MpPQahosz5OTw{@0%*EH15@D z5ZvFmYot-^yNBUroLWm0P8ugl3ER*1&?<|-X-0S7SJw7`zA;hOZ*cU9PG(6$np~bp zVp&q($ig{|nswnK0;cW4d&nw7v3#CaNU90K3K>!BDvg!`GVyAlj#ybouuYPICEV!> z5#(a2yo6={f{Hm|{xs=GJAjDz9$jYvzY$*PhU@zU0P_m7qF*T$`S z^*-PV$b_3Uh!_fRaUk*mQ%jO9$^N}%5oo`f{_Y_WqzM0I$0iMMa*@qL^{2c4Y}xCH zfX9$5byVNVB<_Jqhs5GrkZLkeJY%gmBR!)(BS=k0n7Z7_bQ&$n zBkndxJdP{Rt9H8Q7*n)I|6C@jJ5t`tCcpf4%IkeT3O;M}d!IN@caNqVuupsC+TS&2 z@hR;S{|Z-JB3dr8EtaOKn6+1EFHMoV_g=bff#U0U#Wo@g3ICEln<16KbRlV1YhRE{ z>J&|}PEk)$(uNX?OA1QHO2k~2UAl;g#iyJvR+zi!IZqXfyh$x}Dak(i#aYRjR(zx= zri50+BpTlhb=!;+bZfS4cEI0t%zbcrs;(6z=A~E_sH5U5Q5t{;hQ=g~sZLU}tZdbI*l z;+wCOS4-eN_bXgT&v|w#EXS5vI2Ket`)&V4=q7QK@vU&xLYyR6C9WnoEZ(yh^TNQN ziBX2O4gcYYKiO=8&nKsl(YMd%@v~YDV=% zGR;$g_V`6b^Fnfc^2MhYf7`?gNyH3FmMR}t4h?w7mPH>!k7f4UTrj@)VkLDBN+b13 z_rBMgs@~J3)k4p9&U$S@Q=ruZL@vk1$Js=5LDGyMSxT*=?e){voK^pm6T$tf*;!Rt zP4h*=_ZAEn=nF9;w_fVs(#mnu3wYJsj{o2|(`PJpD+T5|UKZ+rcL=9FnpK5*L6cm| z-`#(l&n;OaFDe*)HDceIGMd65#}H#~r(Yr)9=AX4a%g7eoQ@jDUsBzsYzJ@m0fk`) z`Dpxc;ilp4zig}rUr>3TV2mE7eytpwH@%u$V=8Bw7*QTE(^#(J)BgLla+S5&^q!<1 zx@6SR&`)n)36VdNSh1vieeg5`Zr=z#V4U!ckV+WKqQM3PZoM&&I#cFUL$~Pl`>2D5OK%PqKpY zdYV?E3gwF-GCJr~6Wdt(!#DoNR9d@8{_4S{J=}u{qCri{O*AmH&#*7{J^qCD0sAFO zZM)#>zbDlh1gX8y%h#EyxvWuqtwB`S8w)wIvF zkKX6I%BWG5TQB_LA43nM2zB^YT`Sw=rYl;FEBHh_uK7+|sFFr?^?u$R*NI9k%{kOP z|FWuc_!{!&;;rp@nI@Tu>(%+uHmuGwXHbcb;RO)KLpNdb*Do6YmmNh~J2Y3;m;y$nVzu`WBQz>b~m#J}~TF zx3+22@od0hi-ly{ug|u!7rD9;h7vBn-E5syWf6j==hj5V*A7Uaht6LODVq>_{p)#UnlkhZ zE^{fL)z8DJm zbHro#c6G?CSI_tvDdyr+Y0gBy*>?Qwk-8&2CzhUYmMVv5_P9QA&Af*`&G^jujM++l z?a)?xvo^D$=7ev}LU8?~5bw1)r5t3>=t_OiAM)F82Xau@(fZ|J=Gu$3)p7aJP`1Wq zPgm_AS7V{P&Y|w~ipjj`AbvDvz}S6j-JVxg@QaZbba8}p^Kvt!|@K6J}qHq|-U zp6C_q>xH6#jg3H59A7N>FHt1BqaZw! zO$DJKa9uAL3=Tr_EPX@7VH669MCyPLFoYfyW(Y+X=pyvc21qnqAN2PF=0#*vywOes z^S|Tqc37|vhr>ccp1b#7|GP36|9A&* zoT&fN`>(_S#2^+G>O>7-o@0CQn&+*$6N-h#v#DeblTBnY&;G3BQ6DCU8Q{ZYfe;1| zI7riu>_zw6Innw7v9m*4`2}#ueqK~70v608fzas`v>DRWzz}X=h(qELaJZS7DZvzm z(}U?9hN1NJ5a#+nu>_{qIR@2_^Ak(?4;J}fu{#sNVDTaosBHRqD#e`5WPpBTji&#L z7Cj_^V1P6?$01SX|E}dPEahLc{JFxjN_0J>T0PUFnOfPTq&-_#U zctg+TO}C7y{w3bc5Iky2G~=z?+uQkrw?=uEx)s5c7&zqVL7`B>01!V^$P#8k=%X}T jX9eAd?S;d@hXFo8KvWul)ngp;c?iJD%$`tz^N9FgMHepu literal 0 HcmV?d00001 diff --git a/targets/f18/api_symbols.csv b/targets/f18/api_symbols.csv index 4d6f9e32b..5a5fa4cfc 100644 --- a/targets/f18/api_symbols.csv +++ b/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,55.0,, +Version,+,55.1,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -1276,6 +1276,7 @@ Function,+,furi_hal_sd_presence_init,void, Function,+,furi_hal_sd_read_blocks,FuriStatus,"uint32_t*, uint32_t, uint32_t" Function,+,furi_hal_sd_write_blocks,FuriStatus,"const uint32_t*, uint32_t, uint32_t" Function,+,furi_hal_serial_async_rx,uint8_t,FuriHalSerialHandle* +Function,+,furi_hal_serial_async_rx_available,_Bool,FuriHalSerialHandle* Function,+,furi_hal_serial_async_rx_start,void,"FuriHalSerialHandle*, FuriHalSerialAsyncRxCallback, void*, _Bool" Function,+,furi_hal_serial_async_rx_stop,void,FuriHalSerialHandle* Function,+,furi_hal_serial_control_acquire,FuriHalSerialHandle*,FuriHalSerialId diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 837ad9ffc..eee91ce1c 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,55.0,, +Version,+,55.1,, Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, @@ -1442,6 +1442,7 @@ Function,+,furi_hal_sd_presence_init,void, Function,+,furi_hal_sd_read_blocks,FuriStatus,"uint32_t*, uint32_t, uint32_t" Function,+,furi_hal_sd_write_blocks,FuriStatus,"const uint32_t*, uint32_t, uint32_t" Function,+,furi_hal_serial_async_rx,uint8_t,FuriHalSerialHandle* +Function,+,furi_hal_serial_async_rx_available,_Bool,FuriHalSerialHandle* Function,+,furi_hal_serial_async_rx_start,void,"FuriHalSerialHandle*, FuriHalSerialAsyncRxCallback, void*, _Bool" Function,+,furi_hal_serial_async_rx_stop,void,FuriHalSerialHandle* Function,+,furi_hal_serial_control_acquire,FuriHalSerialHandle*,FuriHalSerialId diff --git a/targets/f7/furi_hal/furi_hal_serial.c b/targets/f7/furi_hal/furi_hal_serial.c index 1296ee620..e0e2d8d52 100644 --- a/targets/f7/furi_hal/furi_hal_serial.c +++ b/targets/f7/furi_hal/furi_hal_serial.c @@ -730,6 +730,13 @@ static void furi_hal_serial_async_rx_configure( FuriHalSerialHandle* handle, FuriHalSerialAsyncRxCallback callback, void* context) { + // Handle must be configured before enabling RX interrupt + // as it might be triggered right away on a misconfigured handle + furi_hal_serial[handle->id].rx_byte_callback = callback; + furi_hal_serial[handle->id].handle = handle; + furi_hal_serial[handle->id].rx_dma_callback = NULL; + furi_hal_serial[handle->id].context = context; + if(handle->id == FuriHalSerialIdUsart) { if(callback) { furi_hal_serial_usart_deinit_dma_rx(); @@ -753,10 +760,6 @@ static void furi_hal_serial_async_rx_configure( LL_LPUART_DisableIT_RXNE_RXFNE(LPUART1); } } - furi_hal_serial[handle->id].rx_byte_callback = callback; - furi_hal_serial[handle->id].handle = handle; - furi_hal_serial[handle->id].rx_dma_callback = NULL; - furi_hal_serial[handle->id].context = context; } void furi_hal_serial_async_rx_start( @@ -782,6 +785,17 @@ void furi_hal_serial_async_rx_stop(FuriHalSerialHandle* handle) { furi_hal_serial_async_rx_configure(handle, NULL, NULL); } +bool furi_hal_serial_async_rx_available(FuriHalSerialHandle* handle) { + furi_check(FURI_IS_IRQ_MODE()); + furi_assert(handle->id < FuriHalSerialIdMax); + + if(handle->id == FuriHalSerialIdUsart) { + return LL_USART_IsActiveFlag_RXNE_RXFNE(USART1); + } else { + return LL_LPUART_IsActiveFlag_RXNE_RXFNE(LPUART1); + } +} + uint8_t furi_hal_serial_async_rx(FuriHalSerialHandle* handle) { furi_check(FURI_IS_IRQ_MODE()); furi_assert(handle->id < FuriHalSerialIdMax); diff --git a/targets/f7/furi_hal/furi_hal_serial.h b/targets/f7/furi_hal/furi_hal_serial.h index 975406670..00010d83c 100644 --- a/targets/f7/furi_hal/furi_hal_serial.h +++ b/targets/f7/furi_hal/furi_hal_serial.h @@ -130,6 +130,16 @@ void furi_hal_serial_async_rx_start( */ void furi_hal_serial_async_rx_stop(FuriHalSerialHandle* handle); +/** Check if there is data available for reading + * + * @warning This function must be called only from the callback + * FuriHalSerialAsyncRxCallback + * + * @param handle Serial handle + * @return true if data is available for reading, false otherwise + */ +bool furi_hal_serial_async_rx_available(FuriHalSerialHandle* handle); + /** Get data Serial receive * * @warning This function must be called only from the callback diff --git a/targets/f7/furi_hal/furi_hal_serial_control.c b/targets/f7/furi_hal/furi_hal_serial_control.c index 37454823b..d2ab414c2 100644 --- a/targets/f7/furi_hal/furi_hal_serial_control.c +++ b/targets/f7/furi_hal/furi_hal_serial_control.c @@ -47,6 +47,7 @@ typedef struct { FuriHalSerialHandle* log_serial; // Expansion detection + FuriHalSerialHandle* expansion_serial; FuriHalSerialControlExpansionCallback expansion_cb; void* expansion_ctx; } FuriHalSerialControl; @@ -58,7 +59,36 @@ static void furi_hal_serial_control_log_callback(const uint8_t* data, size_t siz furi_hal_serial_tx(handle, data, size); } +static void furi_hal_serial_control_expansion_irq_callback(void* context) { + UNUSED(context); + + FuriHalSerialControlMessage message; + message.type = FuriHalSerialControlMessageTypeExpansionIrq; + message.api_lock = NULL; + furi_message_queue_put(furi_hal_serial_control->queue, &message, 0); +} + +static void + furi_hal_serial_control_enable_expansion_irq(FuriHalSerialHandle* handle, bool enable) { + const GpioPin* gpio = furi_hal_serial_get_gpio_pin(handle, FuriHalSerialDirectionRx); + + if(enable) { + furi_hal_serial_disable_direction(handle, FuriHalSerialDirectionRx); + furi_hal_gpio_add_int_callback(gpio, furi_hal_serial_control_expansion_irq_callback, NULL); + furi_hal_gpio_init(gpio, GpioModeInterruptFall, GpioPullUp, GpioSpeedLow); + } else { + furi_hal_gpio_remove_int_callback(gpio); + furi_hal_serial_enable_direction(handle, FuriHalSerialDirectionRx); + } +} + static void furi_hal_serial_control_log_set_handle(FuriHalSerialHandle* handle) { + // Disable expansion module detection before reconfiguring UARTs + if(furi_hal_serial_control->expansion_serial) { + furi_hal_serial_control_enable_expansion_irq( + furi_hal_serial_control->expansion_serial, false); + } + if(furi_hal_serial_control->log_serial) { furi_log_remove_handler(furi_hal_serial_control->log_handler); furi_hal_serial_deinit(furi_hal_serial_control->log_serial); @@ -74,15 +104,12 @@ static void furi_hal_serial_control_log_set_handle(FuriHalSerialHandle* handle) furi_hal_serial_control->log_handler.context = furi_hal_serial_control->log_serial; furi_log_add_handler(furi_hal_serial_control->log_handler); } -} -static void furi_hal_serial_control_expansion_irq_callback(void* context) { - UNUSED(context); - - FuriHalSerialControlMessage message; - message.type = FuriHalSerialControlMessageTypeExpansionIrq; - message.api_lock = NULL; - furi_message_queue_put(furi_hal_serial_control->queue, &message, 0); + // Re-enable expansion module detection (if applicable) + if(furi_hal_serial_control->expansion_serial) { + furi_hal_serial_control_enable_expansion_irq( + furi_hal_serial_control->expansion_serial, true); + } } static bool furi_hal_serial_control_handler_stop(void* input, void* output) { @@ -93,16 +120,21 @@ static bool furi_hal_serial_control_handler_stop(void* input, void* output) { static bool furi_hal_serial_control_handler_acquire(void* input, void* output) { FuriHalSerialId serial_id = *(FuriHalSerialId*)input; - if(furi_hal_serial_control->handles[serial_id].in_use) { + FuriHalSerialHandle* handle = &furi_hal_serial_control->handles[serial_id]; + + if(handle->in_use) { *(FuriHalSerialHandle**)output = NULL; } else { // Logging if(furi_hal_serial_control->log_config_serial_id == serial_id) { furi_hal_serial_control_log_set_handle(NULL); + // Expansion + } else if(furi_hal_serial_control->expansion_serial == handle) { + furi_hal_serial_control_enable_expansion_irq(handle, false); } // Return handle - furi_hal_serial_control->handles[serial_id].in_use = true; - *(FuriHalSerialHandle**)output = &furi_hal_serial_control->handles[serial_id]; + handle->in_use = true; + *(FuriHalSerialHandle**)output = handle; } return true; @@ -116,9 +148,12 @@ static bool furi_hal_serial_control_handler_release(void* input, void* output) { furi_hal_serial_deinit(handle); handle->in_use = false; - // Return back logging if(furi_hal_serial_control->log_config_serial_id == handle->id) { + // Return back logging furi_hal_serial_control_log_set_handle(handle); + } else if(furi_hal_serial_control->expansion_serial == handle) { + // Re-enable expansion + furi_hal_serial_control_enable_expansion_irq(handle, true); } return true; @@ -157,24 +192,24 @@ static bool furi_hal_serial_control_handler_expansion_set_callback(void* input, FuriHalSerialControlMessageExpCallback* message_input = input; FuriHalSerialHandle* handle = &furi_hal_serial_control->handles[message_input->id]; - const GpioPin* gpio = furi_hal_serial_get_gpio_pin(handle, FuriHalSerialDirectionRx); - if(message_input->callback) { + const bool enable_irq = message_input->callback != NULL; + + if(enable_irq) { + furi_check(furi_hal_serial_control->expansion_serial == NULL); furi_check(furi_hal_serial_control->expansion_cb == NULL); - - furi_hal_serial_disable_direction(handle, FuriHalSerialDirectionRx); - furi_hal_gpio_add_int_callback(gpio, furi_hal_serial_control_expansion_irq_callback, NULL); - furi_hal_gpio_init(gpio, GpioModeInterruptFall, GpioPullUp, GpioSpeedLow); + furi_hal_serial_control->expansion_serial = handle; } else { + furi_check(furi_hal_serial_control->expansion_serial == handle); furi_check(furi_hal_serial_control->expansion_cb != NULL); - - furi_hal_gpio_remove_int_callback(gpio); - furi_hal_serial_enable_direction(handle, FuriHalSerialDirectionRx); + furi_hal_serial_control->expansion_serial = NULL; } furi_hal_serial_control->expansion_cb = message_input->callback; furi_hal_serial_control->expansion_ctx = message_input->context; + furi_hal_serial_control_enable_expansion_irq(handle, enable_irq); + return true; } From b0df85294417c706f1286133c9cdcf0005a23044 Mon Sep 17 00:00:00 2001 From: gornekich Date: Mon, 12 Feb 2024 02:23:44 +0000 Subject: [PATCH 3/5] NFC: fix retry scene navigation logic (#3439) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * nfc app: fix retry scene navigation logic * nfc app: fix navigation in exit confirm scene Co-authored-by: あく --- applications/main/nfc/scenes/nfc_scene_exit_confirm.c | 7 +++++++ applications/main/nfc/scenes/nfc_scene_retry_confirm.c | 6 ++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/applications/main/nfc/scenes/nfc_scene_exit_confirm.c b/applications/main/nfc/scenes/nfc_scene_exit_confirm.c index 16593cc89..7c4a3d19d 100644 --- a/applications/main/nfc/scenes/nfc_scene_exit_confirm.c +++ b/applications/main/nfc/scenes/nfc_scene_exit_confirm.c @@ -31,6 +31,13 @@ bool nfc_scene_exit_confirm_on_event(void* context, SceneManagerEvent event) { 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))) { + 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)); } else { consumed = scene_manager_search_and_switch_to_previous_scene( nfc->scene_manager, NfcSceneStart); diff --git a/applications/main/nfc/scenes/nfc_scene_retry_confirm.c b/applications/main/nfc/scenes/nfc_scene_retry_confirm.c index b6ad8144d..579373624 100644 --- a/applications/main/nfc/scenes/nfc_scene_retry_confirm.c +++ b/applications/main/nfc/scenes/nfc_scene_retry_confirm.c @@ -31,8 +31,10 @@ bool nfc_scene_retry_confirm_on_event(void* context, SceneManagerEvent event) { if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSlixUnlock)) { consumed = scene_manager_search_and_switch_to_previous_scene( nfc->scene_manager, NfcSceneSlixUnlock); - } else if(scene_manager_has_previous_scene( - nfc->scene_manager, NfcSceneMfClassicDictAttack)) { + } 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))) { consumed = scene_manager_search_and_switch_to_previous_scene( nfc->scene_manager, NfcSceneMfClassicDictAttack); } else if(scene_manager_has_previous_scene( From 6d09bebf14d8bef47300c05776551a42e047377a Mon Sep 17 00:00:00 2001 From: Georgii Surkov <37121527+gsurkov@users.noreply.github.com> Date: Mon, 12 Feb 2024 05:30:10 +0300 Subject: [PATCH 4/5] [FL-3769] Check universal remote files before loading (#3438) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Improve code readability * Check universal remote files before loading Co-authored-by: あく --- applications/main/infrared/infrared_app_i.h | 2 +- .../main/infrared/infrared_brute_force.c | 25 +++-- applications/main/infrared/infrared_signal.c | 102 ++++++++++++------ applications/main/infrared/infrared_signal.h | 13 ++- 4 files changed, 98 insertions(+), 44 deletions(-) diff --git a/applications/main/infrared/infrared_app_i.h b/applications/main/infrared/infrared_app_i.h index c35d3fa41..7a9202b28 100644 --- a/applications/main/infrared/infrared_app_i.h +++ b/applications/main/infrared/infrared_app_i.h @@ -121,7 +121,7 @@ struct InfraredApp { InfraredProgressView* progress; /**< Custom view for showing brute force progress. */ FuriString* file_path; /**< Full path to the currently loaded file. */ - FuriString* button_name; /** Name of the button requested in RPC mode. */ + FuriString* button_name; /**< Name of the button requested in RPC mode. */ /** Arbitrary text storage for various inputs. */ char text_store[INFRARED_TEXT_STORE_NUM][INFRARED_TEXT_STORE_SIZE + 1]; InfraredAppState app_state; /**< Application state. */ diff --git a/applications/main/infrared/infrared_brute_force.c b/applications/main/infrared/infrared_brute_force.c index 373c7270f..b941a4760 100644 --- a/applications/main/infrared/infrared_brute_force.c +++ b/applications/main/infrared/infrared_brute_force.c @@ -57,20 +57,31 @@ bool infrared_brute_force_calculate_messages(InfraredBruteForce* brute_force) { Storage* storage = furi_record_open(RECORD_STORAGE); FlipperFormat* ff = flipper_format_buffered_file_alloc(storage); + FuriString* signal_name = furi_string_alloc(); + InfraredSignal* signal = infrared_signal_alloc(); + + do { + if(!flipper_format_buffered_file_open_existing(ff, brute_force->db_filename)) break; + + bool signals_valid = false; + while(infrared_signal_read_name(ff, signal_name)) { + signals_valid = infrared_signal_read_body(signal, ff) && + infrared_signal_is_valid(signal); + if(!signals_valid) break; - success = flipper_format_buffered_file_open_existing(ff, brute_force->db_filename); - if(success) { - FuriString* signal_name; - signal_name = furi_string_alloc(); - while(flipper_format_read_string(ff, "name", signal_name)) { InfraredBruteForceRecord* record = InfraredBruteForceRecordDict_get(brute_force->records, signal_name); if(record) { //-V547 ++(record->count); } } - furi_string_free(signal_name); - } + + if(!signals_valid) break; + success = true; + } while(false); + + infrared_signal_free(signal); + furi_string_free(signal_name); flipper_format_free(ff); furi_record_close(RECORD_STORAGE); diff --git a/applications/main/infrared/infrared_signal.c b/applications/main/infrared/infrared_signal.c index 2b0d34791..eec8c19cb 100644 --- a/applications/main/infrared/infrared_signal.c +++ b/applications/main/infrared/infrared_signal.c @@ -8,7 +8,23 @@ #define TAG "InfraredSignal" +// Common keys #define INFRARED_SIGNAL_NAME_KEY "name" +#define INFRARED_SIGNAL_TYPE_KEY "type" + +// Type key values +#define INFRARED_SIGNAL_TYPE_RAW "raw" +#define INFRARED_SIGNAL_TYPE_PARSED "parsed" + +// Raw signal keys +#define INFRARED_SIGNAL_DATA_KEY "data" +#define INFRARED_SIGNAL_FREQUENCY_KEY "frequency" +#define INFRARED_SIGNAL_DUTY_CYCLE_KEY "duty_cycle" + +// Parsed signal keys +#define INFRARED_SIGNAL_PROTOCOL_KEY "protocol" +#define INFRARED_SIGNAL_ADDRESS_KEY "address" +#define INFRARED_SIGNAL_COMMAND_KEY "command" struct InfraredSignal { bool is_raw; @@ -88,18 +104,23 @@ static bool infrared_signal_is_raw_valid(const InfraredRawSignal* raw) { static inline bool infrared_signal_save_message(const InfraredMessage* message, FlipperFormat* ff) { const char* protocol_name = infrared_get_protocol_name(message->protocol); - return flipper_format_write_string_cstr(ff, "type", "parsed") && - flipper_format_write_string_cstr(ff, "protocol", protocol_name) && - flipper_format_write_hex(ff, "address", (uint8_t*)&message->address, 4) && - flipper_format_write_hex(ff, "command", (uint8_t*)&message->command, 4); + return flipper_format_write_string_cstr( + ff, INFRARED_SIGNAL_TYPE_KEY, INFRARED_SIGNAL_TYPE_PARSED) && + flipper_format_write_string_cstr(ff, INFRARED_SIGNAL_PROTOCOL_KEY, protocol_name) && + flipper_format_write_hex( + ff, INFRARED_SIGNAL_ADDRESS_KEY, (uint8_t*)&message->address, 4) && + flipper_format_write_hex( + ff, INFRARED_SIGNAL_COMMAND_KEY, (uint8_t*)&message->command, 4); } static inline bool infrared_signal_save_raw(const InfraredRawSignal* raw, FlipperFormat* ff) { furi_assert(raw->timings_size <= MAX_TIMINGS_AMOUNT); - return flipper_format_write_string_cstr(ff, "type", "raw") && - flipper_format_write_uint32(ff, "frequency", &raw->frequency, 1) && - flipper_format_write_float(ff, "duty_cycle", &raw->duty_cycle, 1) && - flipper_format_write_uint32(ff, "data", raw->timings, raw->timings_size); + return flipper_format_write_string_cstr( + ff, INFRARED_SIGNAL_TYPE_KEY, INFRARED_SIGNAL_TYPE_RAW) && + flipper_format_write_uint32(ff, INFRARED_SIGNAL_FREQUENCY_KEY, &raw->frequency, 1) && + flipper_format_write_float(ff, INFRARED_SIGNAL_DUTY_CYCLE_KEY, &raw->duty_cycle, 1) && + flipper_format_write_uint32( + ff, INFRARED_SIGNAL_DATA_KEY, raw->timings, raw->timings_size); } static inline bool infrared_signal_read_message(InfraredSignal* signal, FlipperFormat* ff) { @@ -108,61 +129,72 @@ static inline bool infrared_signal_read_message(InfraredSignal* signal, FlipperF bool success = false; do { - if(!flipper_format_read_string(ff, "protocol", buf)) break; + if(!flipper_format_read_string(ff, INFRARED_SIGNAL_PROTOCOL_KEY, buf)) break; InfraredMessage message; message.protocol = infrared_get_protocol_by_name(furi_string_get_cstr(buf)); - success = flipper_format_read_hex(ff, "address", (uint8_t*)&message.address, 4) && - flipper_format_read_hex(ff, "command", (uint8_t*)&message.command, 4) && - infrared_signal_is_message_valid(&message); - - if(!success) break; + if(!flipper_format_read_hex(ff, INFRARED_SIGNAL_ADDRESS_KEY, (uint8_t*)&message.address, 4)) + break; + if(!flipper_format_read_hex(ff, INFRARED_SIGNAL_COMMAND_KEY, (uint8_t*)&message.command, 4)) + break; + if(!infrared_signal_is_message_valid(&message)) break; infrared_signal_set_message(signal, &message); - } while(0); + success = true; + } while(false); furi_string_free(buf); return success; } static inline bool infrared_signal_read_raw(InfraredSignal* signal, FlipperFormat* ff) { - uint32_t timings_size, frequency; - float duty_cycle; + bool success = false; - bool success = flipper_format_read_uint32(ff, "frequency", &frequency, 1) && - flipper_format_read_float(ff, "duty_cycle", &duty_cycle, 1) && - flipper_format_get_value_count(ff, "data", &timings_size); + do { + uint32_t frequency; + if(!flipper_format_read_uint32(ff, INFRARED_SIGNAL_FREQUENCY_KEY, &frequency, 1)) break; - if(!success || timings_size > MAX_TIMINGS_AMOUNT) { - return false; - } + float duty_cycle; + if(!flipper_format_read_float(ff, INFRARED_SIGNAL_DUTY_CYCLE_KEY, &duty_cycle, 1)) break; - uint32_t* timings = malloc(sizeof(uint32_t) * timings_size); - success = flipper_format_read_uint32(ff, "data", timings, timings_size); + uint32_t timings_size; + if(!flipper_format_get_value_count(ff, INFRARED_SIGNAL_DATA_KEY, &timings_size)) break; - if(success) { + if(timings_size > MAX_TIMINGS_AMOUNT) break; + + uint32_t* timings = malloc(sizeof(uint32_t) * timings_size); + if(!flipper_format_read_uint32(ff, INFRARED_SIGNAL_DATA_KEY, timings, timings_size)) { + free(timings); + break; + } infrared_signal_set_raw_signal(signal, timings, timings_size, frequency, duty_cycle); - } + free(timings); + + success = true; + } while(false); - free(timings); return success; } -static bool infrared_signal_read_body(InfraredSignal* signal, FlipperFormat* ff) { +bool infrared_signal_read_body(InfraredSignal* signal, FlipperFormat* ff) { FuriString* tmp = furi_string_alloc(); bool success = false; do { - if(!flipper_format_read_string(ff, "type", tmp)) break; - if(furi_string_equal(tmp, "raw")) { - success = infrared_signal_read_raw(signal, ff); - } else if(furi_string_equal(tmp, "parsed")) { - success = infrared_signal_read_message(signal, ff); + if(!flipper_format_read_string(ff, INFRARED_SIGNAL_TYPE_KEY, tmp)) break; + + if(furi_string_equal(tmp, INFRARED_SIGNAL_TYPE_RAW)) { + if(!infrared_signal_read_raw(signal, ff)) break; + } else if(furi_string_equal(tmp, INFRARED_SIGNAL_TYPE_PARSED)) { + if(!infrared_signal_read_message(signal, ff)) break; } else { - FURI_LOG_E(TAG, "Unknown signal type"); + FURI_LOG_E(TAG, "Unknown signal type: %s", furi_string_get_cstr(tmp)); + break; } + + success = true; } while(false); furi_string_free(tmp); diff --git a/applications/main/infrared/infrared_signal.h b/applications/main/infrared/infrared_signal.h index cfa4cfa94..69b677f23 100644 --- a/applications/main/infrared/infrared_signal.h +++ b/applications/main/infrared/infrared_signal.h @@ -127,7 +127,7 @@ void infrared_signal_set_message(InfraredSignal* signal, const InfraredMessage* const InfraredMessage* infrared_signal_get_message(const InfraredSignal* signal); /** - * @brief Read a signal from a FlipperFormat file into an InfraredSignal instance. + * @brief Read a signal and its name from a FlipperFormat file into an InfraredSignal instance. * * The file must be allocated and open prior to this call. The seek position determines * which signal will be read (if there is more than one in the file). Calling this function @@ -151,6 +151,17 @@ bool infrared_signal_read(InfraredSignal* signal, FlipperFormat* ff, FuriString* */ bool infrared_signal_read_name(FlipperFormat* ff, FuriString* name); +/** + * @brief Read a signal from a FlipperFormat file. + * + * Same behaviour as infrared_signal_read(), but only the body is read. + * + * @param[in,out] ff pointer to the FlipperFormat file instance to read from. + * @param[out] body pointer to the InfraredSignal instance to hold the signal body. Must be properly allocated. + * @returns true if a signal body was successfully read, false otherwise (e.g. syntax error). + */ +bool infrared_signal_read_body(InfraredSignal* signal, FlipperFormat* ff); + /** * @brief Read a signal with a particular name from a FlipperFormat file into an InfraredSignal instance. * From 389affd904d141b8d58c54ccf7c8beb534877f52 Mon Sep 17 00:00:00 2001 From: Alessandro Rossi Date: Mon, 12 Feb 2024 03:35:16 +0100 Subject: [PATCH 5/5] Fixed MyKey check LockID (#3412) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: gornekich Co-authored-by: あく --- applications/main/nfc/plugins/supported_cards/mykey.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/main/nfc/plugins/supported_cards/mykey.c b/applications/main/nfc/plugins/supported_cards/mykey.c index 1b7493307..e25e1e0a1 100644 --- a/applications/main/nfc/plugins/supported_cards/mykey.c +++ b/applications/main/nfc/plugins/supported_cards/mykey.c @@ -13,7 +13,7 @@ static bool mykey_is_blank(const St25tbData* data) { } static bool mykey_has_lockid(const St25tbData* data) { - return (data->blocks[5] & 0xFF) == 0x7F; + return (data->blocks[5] >> 24) == 0x7F; } static bool check_invalid_low_nibble(uint8_t value) {