From 04e50c9f893db9ea1fd4517a0a2c81c5efcd2be8 Mon Sep 17 00:00:00 2001 From: hedger Date: Sat, 5 Nov 2022 15:47:59 +0400 Subject: [PATCH 01/12] fbt: fixes for ufbt pt3 (#1970) * fbt: replaced debug dir paths with FBT_DEBUG_DIR * scripts: updated requirements.txt * fbt: fixed wrong import * fbt: removed delayed import for file2image * fbt: added UPDATE_BUNDLE_DIR internal var * fbt: cleaner internal management of extapps * applications: added fap_libs for core apps to link with resources when building with --extra-ext-apps * fbt: removed deprecation stub for faps * fbt: added quotation for icons build cmd * fbt: reworked BUILD_DIR & fap work dir handling; fap debug: using debug elf path from fbt * fbt: explicit LIB_DIST_DIR --- SConstruct | 30 ++++--- applications/main/bad_usb/application.fam | 1 + applications/main/gpio/application.fam | 1 + applications/main/ibutton/application.fam | 1 + applications/main/infrared/application.fam | 1 + applications/main/lfrfid/application.fam | 1 + applications/main/u2f/application.fam | 1 + debug/flipperapps.py | 46 ++++++++--- fbt_options.py | 4 +- firmware.scons | 13 ++-- scripts/fbt/elfmanifest.py | 3 +- scripts/fbt/sdk/cache.py | 3 + scripts/fbt_tools/fbt_assets.py | 2 +- scripts/fbt_tools/fbt_debugopts.py | 16 ++-- scripts/fbt_tools/fbt_dist.py | 4 +- scripts/fbt_tools/fbt_extapps.py | 91 +++++++++++++--------- scripts/requirements.txt | 10 ++- site_scons/commandline.scons | 2 +- site_scons/environ.scons | 2 +- site_scons/extapps.scons | 73 +++++++---------- 20 files changed, 179 insertions(+), 126 deletions(-) diff --git a/SConstruct b/SConstruct index 13a698c81..67eac3825 100644 --- a/SConstruct +++ b/SConstruct @@ -43,6 +43,7 @@ distenv = coreenv.Clone( "jflash", ], ENV=os.environ, + UPDATE_BUNDLE_DIR="dist/${DIST_DIR}/f${TARGET_HW}-update-${DIST_SUFFIX}", ) firmware_env = distenv.AddFwProject( @@ -140,21 +141,28 @@ distenv.Default(basic_dist) dist_dir = distenv.GetProjetDirName() fap_dist = [ distenv.Install( - f"#/dist/{dist_dir}/apps/debug_elf", - firmware_env["FW_EXTAPPS"]["debug"].values(), + distenv.Dir(f"#/dist/{dist_dir}/apps/debug_elf"), + list( + app_artifact.debug + for app_artifact in firmware_env["FW_EXTAPPS"].applications.values() + ), ), - *( - distenv.Install(f"#/dist/{dist_dir}/apps/{dist_entry[0]}", dist_entry[1]) - for dist_entry in firmware_env["FW_EXTAPPS"]["dist"].values() + distenv.Install( + f"#/dist/{dist_dir}/apps", + "#/assets/resources/apps", ), ] -Depends(fap_dist, firmware_env["FW_EXTAPPS"]["validators"].values()) +Depends( + fap_dist, + list( + app_artifact.validator + for app_artifact in firmware_env["FW_EXTAPPS"].applications.values() + ), +) Alias("fap_dist", fap_dist) # distenv.Default(fap_dist) -distenv.Depends( - firmware_env["FW_RESOURCES"], firmware_env["FW_EXTAPPS"]["resources_dist"] -) +distenv.Depends(firmware_env["FW_RESOURCES"], firmware_env["FW_EXTAPPS"].resources_dist) # Target for bundling core2 package for qFlipper @@ -192,6 +200,7 @@ firmware_debug = distenv.PhonyTarget( source=firmware_env["FW_ELF"], GDBOPTS="${GDBOPTS_BASE}", GDBREMOTE="${OPENOCD_GDB_PIPE}", + FBT_FAP_DEBUG_ELF_ROOT=firmware_env.subst("$FBT_FAP_DEBUG_ELF_ROOT"), ) distenv.Depends(firmware_debug, firmware_flash) @@ -201,6 +210,7 @@ distenv.PhonyTarget( source=firmware_env["FW_ELF"], GDBOPTS="${GDBOPTS_BASE} ${GDBOPTS_BLACKMAGIC}", GDBREMOTE="${BLACKMAGIC_ADDR}", + FBT_FAP_DEBUG_ELF_ROOT=firmware_env.subst("$FBT_FAP_DEBUG_ELF_ROOT"), ) # Debug alien elf @@ -209,7 +219,7 @@ distenv.PhonyTarget( "${GDBPYCOM}", GDBOPTS="${GDBOPTS_BASE}", GDBREMOTE="${OPENOCD_GDB_PIPE}", - GDBPYOPTS='-ex "source debug/PyCortexMDebug/PyCortexMDebug.py" ', + GDBPYOPTS='-ex "source ${FBT_DEBUG_DIR}/PyCortexMDebug/PyCortexMDebug.py" ', ) distenv.PhonyTarget( diff --git a/applications/main/bad_usb/application.fam b/applications/main/bad_usb/application.fam index 4da34f0de..2442dd3aa 100644 --- a/applications/main/bad_usb/application.fam +++ b/applications/main/bad_usb/application.fam @@ -11,4 +11,5 @@ App( stack_size=2 * 1024, icon="A_BadUsb_14", order=70, + fap_libs=["assets"], ) diff --git a/applications/main/gpio/application.fam b/applications/main/gpio/application.fam index 64f8db5b0..efeb8b6fe 100644 --- a/applications/main/gpio/application.fam +++ b/applications/main/gpio/application.fam @@ -8,4 +8,5 @@ App( stack_size=1 * 1024, icon="A_GPIO_14", order=50, + fap_libs=["assets"], ) diff --git a/applications/main/ibutton/application.fam b/applications/main/ibutton/application.fam index 0bc6f8a9b..77bb9a33c 100644 --- a/applications/main/ibutton/application.fam +++ b/applications/main/ibutton/application.fam @@ -12,6 +12,7 @@ App( icon="A_iButton_14", stack_size=2 * 1024, order=60, + fap_libs=["assets"], ) App( diff --git a/applications/main/infrared/application.fam b/applications/main/infrared/application.fam index 6f76ed429..9c5eaf392 100644 --- a/applications/main/infrared/application.fam +++ b/applications/main/infrared/application.fam @@ -12,6 +12,7 @@ App( icon="A_Infrared_14", stack_size=3 * 1024, order=40, + fap_libs=["assets"], ) App( diff --git a/applications/main/lfrfid/application.fam b/applications/main/lfrfid/application.fam index 4a1498181..150a6f3db 100644 --- a/applications/main/lfrfid/application.fam +++ b/applications/main/lfrfid/application.fam @@ -14,6 +14,7 @@ App( icon="A_125khz_14", stack_size=2 * 1024, order=20, + fap_libs=["assets"], ) App( diff --git a/applications/main/u2f/application.fam b/applications/main/u2f/application.fam index 6b32e0225..82010ffb4 100644 --- a/applications/main/u2f/application.fam +++ b/applications/main/u2f/application.fam @@ -11,4 +11,5 @@ App( stack_size=2 * 1024, icon="A_U2F_14", order=80, + fap_libs=["assets"], ) diff --git a/debug/flipperapps.py b/debug/flipperapps.py index 8e1aa2daf..e815e40b1 100644 --- a/debug/flipperapps.py +++ b/debug/flipperapps.py @@ -1,5 +1,5 @@ from dataclasses import dataclass -from typing import Tuple, Dict +from typing import Optional, Tuple, Dict, ClassVar import struct import posixpath import os @@ -22,14 +22,18 @@ class AppState: debug_link_elf: str = "" debug_link_crc: int = 0 + DEBUG_ELF_ROOT: ClassVar[Optional[str]] = None + def __post_init__(self): if self.other_sections is None: self.other_sections = {} - def get_original_elf_path(self, elf_path="build/latest/.extapps") -> str: + def get_original_elf_path(self) -> str: + if self.DEBUG_ELF_ROOT is None: + raise ValueError("DEBUG_ELF_ROOT not set; call fap-set-debug-elf-root") return ( - posixpath.join(elf_path, self.debug_link_elf) - if elf_path + posixpath.join(self.DEBUG_ELF_ROOT, self.debug_link_elf) + if self.DEBUG_ELF_ROOT else self.debug_link_elf ) @@ -84,7 +88,9 @@ class AppState: if debug_link_size := int(app_state["debug_link_info"]["debug_link_size"]): debug_link_data = ( gdb.selected_inferior() - .read_memory(int(app_state["debug_link_info"]["debug_link"]), debug_link_size) + .read_memory( + int(app_state["debug_link_info"]["debug_link"]), debug_link_size + ) .tobytes() ) state.debug_link_elf, state.debug_link_crc = AppState.parse_debug_link_data( @@ -103,6 +109,29 @@ class AppState: return state +class SetFapDebugElfRoot(gdb.Command): + """Set path to original ELF files for debug info""" + + def __init__(self): + super().__init__( + "fap-set-debug-elf-root", gdb.COMMAND_FILES, gdb.COMPLETE_FILENAME + ) + self.dont_repeat() + + def invoke(self, arg, from_tty): + AppState.DEBUG_ELF_ROOT = arg + try: + global helper + print(f"Set '{arg}' as debug info lookup path for Flipper external apps") + helper.attach_fw() + gdb.events.stop.connect(helper.handle_stop) + except gdb.error as e: + print(f"Support for Flipper external apps debug is not available: {e}") + + +SetFapDebugElfRoot() + + class FlipperAppDebugHelper: def __init__(self): self.app_ptr = None @@ -149,9 +178,4 @@ class FlipperAppDebugHelper: helper = FlipperAppDebugHelper() -try: - helper.attach_fw() - print("Support for Flipper external apps debug is enabled") - gdb.events.stop.connect(helper.handle_stop) -except gdb.error as e: - print(f"Support for Flipper external apps debug is not available: {e}") +print("Support for Flipper external apps debug is loaded") diff --git a/fbt_options.py b/fbt_options.py index 6ef9759e3..11124b936 100644 --- a/fbt_options.py +++ b/fbt_options.py @@ -49,12 +49,12 @@ OPENOCD_OPTS = [ "-c", "transport select hla_swd", "-f", - "debug/stm32wbx.cfg", + "${FBT_DEBUG_DIR}/stm32wbx.cfg", "-c", "stm32wbx.cpu configure -rtos auto", ] -SVD_FILE = "debug/STM32WB55_CM4.svd" +SVD_FILE = "${FBT_DEBUG_DIR}/STM32WB55_CM4.svd" # Look for blackmagic probe on serial ports and local network BLACKMAGIC = "auto" diff --git a/firmware.scons b/firmware.scons index a0c1ab339..6feb73ac3 100644 --- a/firmware.scons +++ b/firmware.scons @@ -20,8 +20,7 @@ env = ENV.Clone( BUILD_DIR=fw_build_meta["build_dir"], IS_BASE_FIRMWARE=fw_build_meta["type"] == "firmware", FW_FLAVOR=fw_build_meta["flavor"], - PLUGIN_ELF_DIR="${BUILD_DIR}", - LIB_DIST_DIR="${BUILD_DIR}/lib", + LIB_DIST_DIR=fw_build_meta["build_dir"].Dir("lib"), LINT_SOURCES=[ "applications", ], @@ -142,12 +141,14 @@ for app_dir, _ in env["APPDIRS"]: fwenv.PrepareApplicationsBuild() -# Build external apps +# Build external apps + configure SDK if env["IS_BASE_FIRMWARE"]: - extapps = fwenv["FW_EXTAPPS"] = SConscript( - "site_scons/extapps.scons", exports={"ENV": fwenv} + fwenv.SetDefault(FBT_FAP_DEBUG_ELF_ROOT="${BUILD_DIR}/.extapps") + fwenv["FW_EXTAPPS"] = SConscript( + "site_scons/extapps.scons", + exports={"ENV": fwenv}, ) - fw_artifacts.append(extapps["sdk_tree"]) + fw_artifacts.append(fwenv["FW_EXTAPPS"].sdk_tree) # Add preprocessor definitions for current set of apps diff --git a/scripts/fbt/elfmanifest.py b/scripts/fbt/elfmanifest.py index 313a64c09..17bceddf4 100644 --- a/scripts/fbt/elfmanifest.py +++ b/scripts/fbt/elfmanifest.py @@ -5,6 +5,7 @@ import struct from dataclasses import dataclass, field from .appmanifest import FlipperApplication +from flipper.assets.icon import file2image _MANIFEST_MAGIC = 0x52474448 @@ -53,8 +54,6 @@ def assemble_manifest_data( ): image_data = b"" if app_manifest.fap_icon: - from flipper.assets.icon import file2image - image = file2image(os.path.join(app_manifest._apppath, app_manifest.fap_icon)) if (image.width, image.height) != (10, 10): raise ValueError( diff --git a/scripts/fbt/sdk/cache.py b/scripts/fbt/sdk/cache.py index 62d42798c..756c37827 100644 --- a/scripts/fbt/sdk/cache.py +++ b/scripts/fbt/sdk/cache.py @@ -89,6 +89,9 @@ class SdkCache: syms.update(map(lambda e: e.name, self.get_variables())) return syms + def get_disabled_names(self): + return set(map(lambda e: e.name, self.disabled_entries)) + def get_functions(self): return self._filter_enabled(self.sdk.functions) diff --git a/scripts/fbt_tools/fbt_assets.py b/scripts/fbt_tools/fbt_assets.py index 521c37e90..e17487358 100644 --- a/scripts/fbt_tools/fbt_assets.py +++ b/scripts/fbt_tools/fbt_assets.py @@ -139,7 +139,7 @@ def generate(env): BUILDERS={ "IconBuilder": Builder( action=Action( - '${PYTHON3} "${ASSETS_COMPILER}" icons ${ABSPATHGETTERFUNC(SOURCE)} ${TARGET.dir} --filename ${ICON_FILE_NAME}', + '${PYTHON3} "${ASSETS_COMPILER}" icons "${ABSPATHGETTERFUNC(SOURCE)}" "${TARGET.dir}" --filename ${ICON_FILE_NAME}', "${ICONSCOMSTR}", ), emitter=icons_emitter, diff --git a/scripts/fbt_tools/fbt_debugopts.py b/scripts/fbt_tools/fbt_debugopts.py index 0c0ce3d32..c3be5ca47 100644 --- a/scripts/fbt_tools/fbt_debugopts.py +++ b/scripts/fbt_tools/fbt_debugopts.py @@ -1,7 +1,6 @@ from re import search from SCons.Errors import UserError -from fbt_options import OPENOCD_OPTS def _get_device_serials(search_str="STLink"): @@ -20,6 +19,9 @@ def GetDevices(env): def generate(env, **kw): env.AddMethod(GetDevices) + env.SetDefault( + FBT_DEBUG_DIR="${ROOT_DIR}/debug", + ) if (adapter_serial := env.subst("$OPENOCD_ADAPTER_SERIAL")) != "auto": env.Append( @@ -36,7 +38,7 @@ def generate(env, **kw): env.SetDefault( OPENOCD_GDB_PIPE=[ - "|openocd -c 'gdb_port pipe; log_output debug/openocd.log' ${[SINGLEQUOTEFUNC(OPENOCD_OPTS)]}" + "|openocd -c 'gdb_port pipe; log_output ${FBT_DEBUG_DIR}/openocd.log' ${[SINGLEQUOTEFUNC(OPENOCD_OPTS)]}" ], GDBOPTS_BASE=[ "-ex", @@ -58,17 +60,19 @@ def generate(env, **kw): ], GDBPYOPTS=[ "-ex", - "source debug/FreeRTOS/FreeRTOS.py", + "source ${FBT_DEBUG_DIR}/FreeRTOS/FreeRTOS.py", "-ex", - "source debug/flipperapps.py", + "source ${FBT_DEBUG_DIR}/flipperapps.py", "-ex", - "source debug/PyCortexMDebug/PyCortexMDebug.py", + "fap-set-debug-elf-root ${FBT_FAP_DEBUG_ELF_ROOT}", + "-ex", + "source ${FBT_DEBUG_DIR}/PyCortexMDebug/PyCortexMDebug.py", "-ex", "svd_load ${SVD_FILE}", "-ex", "compare-sections", ], - JFLASHPROJECT="${ROOT_DIR.abspath}/debug/fw.jflash", + JFLASHPROJECT="${FBT_DEBUG_DIR}/fw.jflash", ) diff --git a/scripts/fbt_tools/fbt_dist.py b/scripts/fbt_tools/fbt_dist.py index fb59e5b95..f0b443486 100644 --- a/scripts/fbt_tools/fbt_dist.py +++ b/scripts/fbt_tools/fbt_dist.py @@ -22,7 +22,7 @@ def GetProjetDirName(env, project=None): def create_fw_build_targets(env, configuration_name): flavor = GetProjetDirName(env, configuration_name) - build_dir = env.Dir("build").Dir(flavor).abspath + build_dir = env.Dir("build").Dir(flavor) return env.SConscript( "firmware.scons", variant_dir=build_dir, @@ -131,7 +131,7 @@ def generate(env): "UsbInstall": Builder( action=[ Action( - '${PYTHON3} "${SELFUPDATE_SCRIPT}" dist/${DIST_DIR}/f${TARGET_HW}-update-${DIST_SUFFIX}/update.fuf' + '${PYTHON3} "${SELFUPDATE_SCRIPT}" ${UPDATE_BUNDLE_DIR}/update.fuf' ), Touch("${TARGET}"), ] diff --git a/scripts/fbt_tools/fbt_extapps.py b/scripts/fbt_tools/fbt_extapps.py index fb4dc2f16..f1906191b 100644 --- a/scripts/fbt_tools/fbt_extapps.py +++ b/scripts/fbt_tools/fbt_extapps.py @@ -1,6 +1,9 @@ +from dataclasses import dataclass, field +from typing import Optional from SCons.Builder import Builder from SCons.Action import Action from SCons.Errors import UserError +from SCons.Node import NodeList import SCons.Warnings from fbt.elfmanifest import assemble_manifest_data @@ -16,6 +19,15 @@ import shutil from ansi.color import fg +@dataclass +class FlipperExternalAppInfo: + app: FlipperApplication + compact: NodeList = field(default_factory=NodeList) + debug: NodeList = field(default_factory=NodeList) + validator: NodeList = field(default_factory=NodeList) + installer: NodeList = field(default_factory=NodeList) + + def BuildAppElf(env, app): ext_apps_work_dir = env.subst("$EXT_APPS_WORK_DIR") app_work_dir = os.path.join(ext_apps_work_dir, app.appid) @@ -26,15 +38,7 @@ def BuildAppElf(env, app): app_alias = f"fap_{app.appid}" - # Deprecation stub - legacy_app_taget_name = f"{app_env['FIRMWARE_BUILD_CFG']}_{app.appid}" - - def legacy_app_build_stub(**kw): - raise UserError( - f"Target name '{legacy_app_taget_name}' is deprecated, use '{app_alias}' instead" - ) - - app_env.PhonyTarget(legacy_app_taget_name, Action(legacy_app_build_stub, None)) + app_artifacts = FlipperExternalAppInfo(app) externally_built_files = [] if app.fap_extbuild: @@ -115,20 +119,22 @@ def BuildAppElf(env, app): CPPPATH=env.Dir(app_work_dir), ) - app_elf_raw = app_env.Program( + app_artifacts.debug = app_env.Program( os.path.join(ext_apps_work_dir, f"{app.appid}_d"), app_sources, APP_ENTRY=app.entry_point, ) - app_env.Clean(app_elf_raw, [*externally_built_files, app_env.Dir(app_work_dir)]) + app_env.Clean( + app_artifacts.debug, [*externally_built_files, app_env.Dir(app_work_dir)] + ) - app_elf_dump = app_env.ObjDump(app_elf_raw) + app_elf_dump = app_env.ObjDump(app_artifacts.debug) app_env.Alias(f"{app_alias}_list", app_elf_dump) - app_elf_augmented = app_env.EmbedAppMetadata( + app_artifacts.compact = app_env.EmbedAppMetadata( os.path.join(ext_apps_work_dir, app.appid), - app_elf_raw, + app_artifacts.debug, APP=app, ) @@ -139,19 +145,21 @@ def BuildAppElf(env, app): } app_env.Depends( - app_elf_augmented, + app_artifacts.compact, [app_env["SDK_DEFINITION"], app_env.Value(manifest_vals)], ) if app.fap_icon: app_env.Depends( - app_elf_augmented, + app_artifacts.compact, app_env.File(f"{app._apppath}/{app.fap_icon}"), ) - app_elf_import_validator = app_env.ValidateAppImports(app_elf_augmented) - app_env.AlwaysBuild(app_elf_import_validator) - app_env.Alias(app_alias, app_elf_import_validator) - return (app_elf_augmented, app_elf_raw, app_elf_import_validator) + app_artifacts.validator = app_env.ValidateAppImports(app_artifacts.compact) + app_env.AlwaysBuild(app_artifacts.validator) + app_env.Alias(app_alias, app_artifacts.validator) + + env["EXT_APPS"][app.appid] = app_artifacts + return app_artifacts def prepare_app_metadata(target, source, env): @@ -182,11 +190,17 @@ def validate_app_imports(target, source, env): app_syms.add(line.split()[0]) unresolved_syms = app_syms - sdk_cache.get_valid_names() if unresolved_syms: - SCons.Warnings.warn( - SCons.Warnings.LinkWarning, - fg.brightyellow(f"{source[0].path}: app won't run. Unresolved symbols: ") - + fg.brightmagenta(f"{unresolved_syms}"), - ) + warning_msg = fg.brightyellow( + f"{source[0].path}: app won't run. Unresolved symbols: " + ) + fg.brightmagenta(f"{unresolved_syms}") + disabled_api_syms = unresolved_syms.intersection(sdk_cache.get_disabled_names()) + if disabled_api_syms: + warning_msg += ( + fg.brightyellow(" (in API, but disabled: ") + + fg.brightmagenta(f"{disabled_api_syms}") + + fg.brightyellow(")") + ) + SCons.Warnings.warn(SCons.Warnings.LinkWarning, warning_msg), def GetExtAppFromPath(env, app_dir): @@ -208,26 +222,26 @@ def GetExtAppFromPath(env, app_dir): if not app: raise UserError(f"Failed to resolve application for given APPSRC={app_dir}") - app_elf = env["_extapps"]["compact"].get(app.appid, None) - if not app_elf: + app_artifacts = env["EXT_APPS"].get(app.appid, None) + if not app_artifacts: raise UserError( f"Application {app.appid} is not configured for building as external" ) - app_validator = env["_extapps"]["validators"].get(app.appid, None) - - return (app, app_elf[0], app_validator[0]) + return app_artifacts def fap_dist_emitter(target, source, env): target_dir = target[0] target = [] - for dist_entry in env["_extapps"]["dist"].values(): - target.append(target_dir.Dir(dist_entry[0]).File(dist_entry[1][0].name)) - - for compact_entry in env["_extapps"]["compact"].values(): - source.extend(compact_entry) + for _, app_artifacts in env["EXT_APPS"].items(): + source.extend(app_artifacts.compact) + target.append( + target_dir.Dir(app_artifacts.app.fap_category).File( + app_artifacts.compact[0].name + ) + ) return (target, source) @@ -244,10 +258,9 @@ def fap_dist_action(target, source, env): def generate(env, **kw): env.SetDefault( - EXT_APPS_WORK_DIR=kw.get("EXT_APPS_WORK_DIR"), + EXT_APPS_WORK_DIR="${FBT_FAP_DEBUG_ELF_ROOT}", APP_RUN_SCRIPT="${FBT_SCRIPT_DIR}/runfap.py", ) - if not env["VERBOSE"]: env.SetDefault( FAPDISTCOMSTR="\tFAPDIST\t${TARGET}", @@ -256,6 +269,10 @@ def generate(env, **kw): APPCHECK_COMSTR="\tAPPCHK\t${SOURCE}", ) + env.SetDefault( + EXT_APPS={}, # appid -> FlipperExternalAppInfo + ) + env.AddMethod(BuildAppElf) env.AddMethod(GetExtAppFromPath) env.Append( diff --git a/scripts/requirements.txt b/scripts/requirements.txt index 35cac7742..5b6fac5f7 100644 --- a/scripts/requirements.txt +++ b/scripts/requirements.txt @@ -1,7 +1,9 @@ -pyserial==3.5 +ansi==0.3.6 +black==22.6.0 +colorlog==6.7.0 heatshrink2==0.11.0 Pillow==9.1.1 -grpcio==1.47.0 -grpcio-tools==1.47.0 -protobuf==3.20.2 +protobuf==3.20.1 +pyserial==3.5 python3-protobuf==2.5.0 +SCons==4.4.0 diff --git a/site_scons/commandline.scons b/site_scons/commandline.scons index cadb417f8..044de6b30 100644 --- a/site_scons/commandline.scons +++ b/site_scons/commandline.scons @@ -147,7 +147,7 @@ vars.AddVariables( PathVariable( "SVD_FILE", help="Path to SVD file", - validator=PathVariable.PathIsFile, + validator=PathVariable.PathAccept, default="", ), PathVariable( diff --git a/site_scons/environ.scons b/site_scons/environ.scons index 96424caad..acdc83e2a 100644 --- a/site_scons/environ.scons +++ b/site_scons/environ.scons @@ -61,8 +61,8 @@ coreenv = VAR_ENV.Clone( ABSPATHGETTERFUNC=extract_abs_dir_path, # Setting up temp file parameters - to overcome command line length limits TEMPFILEARGESCFUNC=tempfile_arg_esc_func, - FBT_SCRIPT_DIR=Dir("#/scripts"), ROOT_DIR=Dir("#"), + FBT_SCRIPT_DIR="${ROOT_DIR}/scripts", ) # If DIST_SUFFIX is set in environment, is has precedence (set by CI) diff --git a/site_scons/extapps.scons b/site_scons/extapps.scons index bb1d65ffc..3d2a98788 100644 --- a/site_scons/extapps.scons +++ b/site_scons/extapps.scons @@ -1,4 +1,6 @@ +from dataclasses import dataclass, field from SCons.Errors import UserError +from SCons.Node import NodeList Import("ENV") @@ -7,14 +9,7 @@ from fbt.appmanifest import FlipperAppType appenv = ENV["APPENV"] = ENV.Clone( tools=[ - ( - "fbt_extapps", - { - "EXT_APPS_WORK_DIR": ENV.subst( - "${BUILD_DIR}/.extapps", - ) - }, - ), + "fbt_extapps", "fbt_assets", "fbt_sdk", ] @@ -60,22 +55,11 @@ appenv.AppendUnique( ) -extapps = appenv["_extapps"] = { - "compact": {}, - "debug": {}, - "validators": {}, - "dist": {}, - "resources_dist": None, - "sdk_tree": None, -} - - -def build_app_as_external(env, appdef): - compact_elf, debug_elf, validator = env.BuildAppElf(appdef) - extapps["compact"][appdef.appid] = compact_elf - extapps["debug"][appdef.appid] = debug_elf - extapps["validators"][appdef.appid] = validator - extapps["dist"][appdef.appid] = (appdef.fap_category, compact_elf) +@dataclass +class FlipperExtAppBuildArtifacts: + applications: dict = field(default_factory=dict) + resources_dist: NodeList = field(default_factory=NodeList) + sdk_tree: NodeList = field(default_factory=NodeList) apps_to_build_as_faps = [ @@ -85,38 +69,39 @@ apps_to_build_as_faps = [ if appenv["DEBUG_TOOLS"]: apps_to_build_as_faps.append(FlipperAppType.DEBUG) -for apptype in apps_to_build_as_faps: - for app in appenv["APPBUILD"].get_apps_of_type(apptype, True): - build_app_as_external(appenv, app) +known_extapps = [ + app + for apptype in apps_to_build_as_faps + for app in appenv["APPBUILD"].get_apps_of_type(apptype, True) +] # Ugly access to global option if extra_app_list := GetOption("extra_ext_apps"): - for extra_app in extra_app_list.split(","): - build_app_as_external(appenv, appenv["APPMGR"].get(extra_app)) + known_extapps.extend(map(appenv["APPMGR"].get, extra_app_list.split(","))) + +for app in known_extapps: + appenv.BuildAppElf(app) if appenv["FORCE"]: - appenv.AlwaysBuild(extapps["compact"].values()) + appenv.AlwaysBuild( + list(app_artifact.compact for app_artifact in appenv["EXT_APPS"].values()) + ) -# Deprecation stub -def legacy_app_build_stub(**kw): - raise UserError(f"Target name 'firmware_extapps' is deprecated, use 'faps' instead") +Alias( + "faps", list(app_artifact.validator for app_artifact in appenv["EXT_APPS"].values()) +) - -appenv.PhonyTarget("firmware_extapps", appenv.Action(legacy_app_build_stub, None)) - - -Alias("faps", extapps["compact"].values()) -Alias("faps", extapps["validators"].values()) - -extapps["resources_dist"] = appenv.FapDist(appenv.Dir("#/assets/resources/apps"), []) +extapps = FlipperExtAppBuildArtifacts() +extapps.applications = appenv["EXT_APPS"] +extapps.resources_dist = appenv.FapDist(appenv.Dir("#/assets/resources/apps"), []) if appsrc := appenv.subst("$APPSRC"): app_manifest, fap_file, app_validator = appenv.GetExtAppFromPath(appsrc) appenv.PhonyTarget( "launch_app", - '${PYTHON3} "${APP_RUN_SCRIPT}" ${SOURCE} --fap_dst_dir "/ext/apps/${FAP_CATEGORY}"', + '${PYTHON3} "${APP_RUN_SCRIPT}" "${SOURCE}" --fap_dst_dir "/ext/apps/${FAP_CATEGORY}"', source=fap_file, FAP_CATEGORY=app_manifest.fap_category, ) @@ -131,12 +116,14 @@ sdk_source = appenv.SDKPrebuilder( (appenv["SDK_HEADERS"], appenv["FW_ASSETS_HEADERS"]), ) # Extra deps on headers included in deeper levels +# Available on second and subsequent builds Depends(sdk_source, appenv.ProcessSdkDepends(f"{sdk_origin_path}.d")) appenv["SDK_DIR"] = appenv.Dir("${BUILD_DIR}/sdk") -sdk_tree = extapps["sdk_tree"] = appenv.SDKTree(appenv["SDK_DIR"], sdk_origin_path) +sdk_tree = appenv.SDKTree(appenv["SDK_DIR"], sdk_origin_path) # AlwaysBuild(sdk_tree) Alias("sdk_tree", sdk_tree) +extapps.sdk_tree = sdk_tree sdk_apicheck = appenv.SDKSymUpdater(appenv["SDK_DEFINITION"], sdk_origin_path) Precious(sdk_apicheck) From e8913f2e33d8fbbbee0e3d0ebcf77f1e65bc6ece Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Sun, 6 Nov 2022 00:07:24 +0900 Subject: [PATCH 02/12] Code cleanup: srand, PVS warnings (#1974) * Remove srand invocation * PVS High priority fixes * PVS High errors part 2 * Furi: heap tracing inheritance * Furi add __builtin_unreachable to furi_thread_catch --- applications/main/bad_usb/bad_usb_script.c | 6 +--- applications/main/fap_loader/fap_loader_app.c | 2 +- applications/main/gpio/usb_uart_bridge.c | 4 +-- applications/main/lfrfid/lfrfid.c | 2 +- .../nfc_scene_mf_classic_read_success.c | 2 +- .../nfc_scene_mf_ultralight_read_success.c | 2 +- applications/main/subghz/subghz_i.c | 6 ---- applications/main/u2f/u2f_hid.c | 2 +- applications/plugins/snake_game/snake_game.c | 1 - .../protocols/ambient_weather.c | 1 - applications/services/cli/cli_command_gpio.c | 33 +++++++++++-------- applications/services/cli/cli_vcp.c | 4 +-- applications/services/crypto/crypto_cli.c | 4 +-- applications/services/gui/gui.c | 2 +- .../gui/modules/file_browser_worker.c | 2 +- .../widget_elements/widget_element_button.c | 2 +- applications/services/gui/view_dispatcher.c | 4 +-- applications/services/loader/loader.c | 4 +-- applications/services/storage/storage_cli.c | 4 +-- .../services/storage/storage_external_api.c | 4 +-- firmware/targets/f7/ble_glue/gap.c | 3 +- .../targets/f7/furi_hal/furi_hal_crypto.c | 2 +- furi/core/check.h | 4 +-- furi/core/event_flag.c | 6 ++-- furi/core/memmgr_heap.c | 6 ++-- furi/core/mutex.c | 6 ++-- furi/core/semaphore.c | 6 ++-- furi/core/stream_buffer.c | 4 +-- furi/core/string.c | 4 +-- furi/core/thread.c | 7 ++++ lib/flipper_format/flipper_format_stream.c | 2 +- .../common/infrared_common_decoder.c | 6 ++-- lib/lfrfid/lfrfid_worker.c | 3 +- lib/print/printf_tiny.c | 2 +- lib/subghz/protocols/power_smart.c | 1 - lib/subghz/subghz_keystore.c | 2 +- lib/toolbox/random_name.c | 6 ---- 37 files changed, 76 insertions(+), 85 deletions(-) diff --git a/applications/main/bad_usb/bad_usb_script.c b/applications/main/bad_usb/bad_usb_script.c index 33b3f5030..ae6181149 100644 --- a/applications/main/bad_usb/bad_usb_script.c +++ b/applications/main/bad_usb/bad_usb_script.c @@ -338,10 +338,6 @@ static int32_t furi_hal_hid_kb_release(key); return (0); } - if(error != NULL) { - strncpy(error, "Unknown error", error_len); - } - return SCRIPT_STATE_ERROR; } static bool ducky_set_usb_id(BadUsbScript* bad_usb, const char* line) { @@ -656,7 +652,7 @@ static int32_t bad_usb_worker(void* context) { BadUsbScript* bad_usb_script_open(FuriString* file_path) { furi_assert(file_path); - BadUsbScript* bad_usb = malloc(sizeof(BadUsbScript)); + BadUsbScript* bad_usb = malloc(sizeof(BadUsbScript)); //-V773 bad_usb->file_path = furi_string_alloc(); furi_string_set(bad_usb->file_path, file_path); diff --git a/applications/main/fap_loader/fap_loader_app.c b/applications/main/fap_loader/fap_loader_app.c index 9b4c92335..10cec086a 100644 --- a/applications/main/fap_loader/fap_loader_app.c +++ b/applications/main/fap_loader/fap_loader_app.c @@ -155,7 +155,7 @@ static bool fap_loader_select_app(FapLoader* loader) { } static FapLoader* fap_loader_alloc(const char* path) { - FapLoader* loader = malloc(sizeof(FapLoader)); + FapLoader* loader = malloc(sizeof(FapLoader)); //-V773 loader->fap_path = furi_string_alloc_set(path); loader->storage = furi_record_open(RECORD_STORAGE); loader->dialogs = furi_record_open(RECORD_DIALOGS); diff --git a/applications/main/gpio/usb_uart_bridge.c b/applications/main/gpio/usb_uart_bridge.c index a5caceafa..a1ab40329 100644 --- a/applications/main/gpio/usb_uart_bridge.c +++ b/applications/main/gpio/usb_uart_bridge.c @@ -184,7 +184,7 @@ static int32_t usb_uart_worker(void* context) { while(1) { uint32_t events = furi_thread_flags_wait(WORKER_ALL_RX_EVENTS, FuriFlagWaitAny, FuriWaitForever); - furi_check((events & FuriFlagError) == 0); + furi_check(!(events & FuriFlagError)); if(events & WorkerEvtStop) break; if(events & WorkerEvtRxDone) { size_t len = furi_stream_buffer_receive( @@ -288,7 +288,7 @@ static int32_t usb_uart_tx_thread(void* context) { while(1) { uint32_t events = furi_thread_flags_wait(WORKER_ALL_TX_EVENTS, FuriFlagWaitAny, FuriWaitForever); - furi_check((events & FuriFlagError) == 0); + furi_check(!(events & FuriFlagError)); if(events & WorkerEvtTxStop) break; if(events & WorkerEvtCdcRx) { furi_check(furi_mutex_acquire(usb_uart->usb_mutex, FuriWaitForever) == FuriStatusOk); diff --git a/applications/main/lfrfid/lfrfid.c b/applications/main/lfrfid/lfrfid.c index 513227306..d391c5e89 100644 --- a/applications/main/lfrfid/lfrfid.c +++ b/applications/main/lfrfid/lfrfid.c @@ -32,7 +32,7 @@ static void rpc_command_callback(RpcAppSystemEvent rpc_event, void* context) { } static LfRfid* lfrfid_alloc() { - LfRfid* lfrfid = malloc(sizeof(LfRfid)); + LfRfid* lfrfid = malloc(sizeof(LfRfid)); //-V773 lfrfid->storage = furi_record_open(RECORD_STORAGE); lfrfid->dialogs = furi_record_open(RECORD_DIALOGS); diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_read_success.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_read_success.c index ae31e92cc..444c9a540 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_read_success.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_read_success.c @@ -24,7 +24,7 @@ void nfc_scene_mf_classic_read_success_on_enter(void* context) { widget_add_button_element( widget, GuiButtonTypeRight, "More", nfc_scene_mf_classic_read_success_widget_callback, nfc); - FuriString* temp_str; + FuriString* temp_str = NULL; if(furi_string_size(nfc->dev->dev_data.parsed_data)) { temp_str = furi_string_alloc_set(nfc->dev->dev_data.parsed_data); } else { diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_success.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_success.c index 63bffbf36..cb5ccd6e8 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_success.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_success.c @@ -31,7 +31,7 @@ void nfc_scene_mf_ultralight_read_success_on_enter(void* context) { nfc_scene_mf_ultralight_read_success_widget_callback, nfc); - FuriString* temp_str; + FuriString* temp_str = NULL; if(furi_string_size(nfc->dev->dev_data.parsed_data)) { temp_str = furi_string_alloc_set(nfc->dev->dev_data.parsed_data); } else { diff --git a/applications/main/subghz/subghz_i.c b/applications/main/subghz/subghz_i.c index beefd8024..a887b6543 100644 --- a/applications/main/subghz/subghz_i.c +++ b/applications/main/subghz/subghz_i.c @@ -513,12 +513,6 @@ bool subghz_path_is_file(FuriString* path) { } uint32_t subghz_random_serial(void) { - static bool rand_generator_inited = false; - - if(!rand_generator_inited) { - srand(DWT->CYCCNT); - rand_generator_inited = true; - } return (uint32_t)rand(); } diff --git a/applications/main/u2f/u2f_hid.c b/applications/main/u2f/u2f_hid.c index 4922d6a5a..b6e86384d 100644 --- a/applications/main/u2f/u2f_hid.c +++ b/applications/main/u2f/u2f_hid.c @@ -203,7 +203,7 @@ static int32_t u2f_hid_worker(void* context) { WorkerEvtStop | WorkerEvtConnect | WorkerEvtDisconnect | WorkerEvtRequest, FuriFlagWaitAny, FuriWaitForever); - furi_check((flags & FuriFlagError) == 0); + furi_check(!(flags & FuriFlagError)); if(flags & WorkerEvtStop) break; if(flags & WorkerEvtConnect) { u2f_set_state(u2f_hid->u2f_instance, 1); diff --git a/applications/plugins/snake_game/snake_game.c b/applications/plugins/snake_game/snake_game.c index f9309c280..ef4ae2ee8 100644 --- a/applications/plugins/snake_game/snake_game.c +++ b/applications/plugins/snake_game/snake_game.c @@ -318,7 +318,6 @@ static void int32_t snake_game_app(void* p) { UNUSED(p); - srand(DWT->CYCCNT); FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(SnakeEvent)); diff --git a/applications/plugins/weather_station/protocols/ambient_weather.c b/applications/plugins/weather_station/protocols/ambient_weather.c index 07f5330fc..5fede684e 100644 --- a/applications/plugins/weather_station/protocols/ambient_weather.c +++ b/applications/plugins/weather_station/protocols/ambient_weather.c @@ -200,7 +200,6 @@ void ws_protocol_decoder_ambient_weather_feed(void* context, bool level, uint32_ ((instance->decoder.decode_data & AMBIENT_WEATHER_PACKET_HEADER_MASK) == AMBIENT_WEATHER_PACKET_HEADER_2)) { if(ws_protocol_ambient_weather_check_crc(instance)) { - instance->decoder.decode_data = instance->decoder.decode_data; instance->generic.data = instance->decoder.decode_data; instance->generic.data_count_bit = ws_protocol_ambient_weather_const.min_count_bit_for_found; diff --git a/applications/services/cli/cli_command_gpio.c b/applications/services/cli/cli_command_gpio.c index d072ce00c..3e7301cdc 100644 --- a/applications/services/cli/cli_command_gpio.c +++ b/applications/services/cli/cli_command_gpio.c @@ -61,15 +61,20 @@ static void gpio_print_pins(void) { } } -typedef enum { OK, ERR_CMD_SYNTAX, ERR_PIN, ERR_VALUE } GpioParseError; +typedef enum { + GpioParseReturnOk, + GpioParseReturnCmdSyntaxError, + GpioParseReturnPinError, + GpioParseReturnValueError +} GpioParseReturn; -static GpioParseError gpio_command_parse(FuriString* args, size_t* pin_num, uint8_t* value) { +static GpioParseReturn gpio_command_parse(FuriString* args, size_t* pin_num, uint8_t* value) { FuriString* pin_name; pin_name = furi_string_alloc(); size_t ws = furi_string_search_char(args, ' '); if(ws == FURI_STRING_FAILURE) { - return ERR_CMD_SYNTAX; + return GpioParseReturnCmdSyntaxError; } furi_string_set_n(pin_name, args, 0, ws); @@ -78,7 +83,7 @@ static GpioParseError gpio_command_parse(FuriString* args, size_t* pin_num, uint if(!pin_name_to_int(pin_name, pin_num)) { furi_string_free(pin_name); - return ERR_PIN; + return GpioParseReturnPinError; } furi_string_free(pin_name); @@ -88,10 +93,10 @@ static GpioParseError gpio_command_parse(FuriString* args, size_t* pin_num, uint } else if(!furi_string_cmp(args, "1")) { *value = 1; } else { - return ERR_VALUE; + return GpioParseReturnValueError; } - return OK; + return GpioParseReturnOk; } void cli_command_gpio_mode(Cli* cli, FuriString* args, void* context) { @@ -101,15 +106,15 @@ void cli_command_gpio_mode(Cli* cli, FuriString* args, void* context) { size_t num = 0; uint8_t value = 255; - GpioParseError err = gpio_command_parse(args, &num, &value); + GpioParseReturn err = gpio_command_parse(args, &num, &value); - if(ERR_CMD_SYNTAX == err) { + if(err == GpioParseReturnCmdSyntaxError) { cli_print_usage("gpio mode", " <0|1>", furi_string_get_cstr(args)); return; - } else if(ERR_PIN == err) { + } else if(err == GpioParseReturnPinError) { gpio_print_pins(); return; - } else if(ERR_VALUE == err) { + } else if(err == GpioParseReturnValueError) { printf("Value is invalid. Enter 1 for input or 0 for output"); return; } @@ -161,15 +166,15 @@ void cli_command_gpio_set(Cli* cli, FuriString* args, void* context) { size_t num = 0; uint8_t value = 0; - GpioParseError err = gpio_command_parse(args, &num, &value); + GpioParseReturn err = gpio_command_parse(args, &num, &value); - if(ERR_CMD_SYNTAX == err) { + if(err == GpioParseReturnCmdSyntaxError) { cli_print_usage("gpio set", " <0|1>", furi_string_get_cstr(args)); return; - } else if(ERR_PIN == err) { + } else if(err == GpioParseReturnPinError) { gpio_print_pins(); return; - } else if(ERR_VALUE == err) { + } else if(err == GpioParseReturnValueError) { printf("Value is invalid. Enter 1 for high or 0 for low"); return; } diff --git a/applications/services/cli/cli_vcp.c b/applications/services/cli/cli_vcp.c index 1e27e185b..94b82950d 100644 --- a/applications/services/cli/cli_vcp.c +++ b/applications/services/cli/cli_vcp.c @@ -103,7 +103,7 @@ static int32_t vcp_worker(void* context) { while(1) { uint32_t flags = furi_thread_flags_wait(VCP_THREAD_FLAG_ALL, FuriFlagWaitAny, FuriWaitForever); - furi_assert((flags & FuriFlagError) == 0); + furi_assert(!(flags & FuriFlagError)); // VCP session opened if(flags & VcpEvtConnect) { @@ -303,7 +303,7 @@ static void vcp_on_cdc_control_line(void* context, uint8_t state) { static void vcp_on_cdc_rx(void* context) { UNUSED(context); uint32_t ret = furi_thread_flags_set(furi_thread_get_id(vcp->thread), VcpEvtRx); - furi_check((ret & FuriFlagError) == 0); + furi_check(!(ret & FuriFlagError)); } static void vcp_on_cdc_tx_complete(void* context) { diff --git a/applications/services/crypto/crypto_cli.c b/applications/services/crypto/crypto_cli.c index a64a3ad0b..1b26ba9fb 100644 --- a/applications/services/crypto/crypto_cli.c +++ b/applications/services/crypto/crypto_cli.c @@ -167,7 +167,7 @@ void crypto_cli_decrypt(Cli* cli, FuriString* args) { void crypto_cli_has_key(Cli* cli, FuriString* args) { UNUSED(cli); int key_slot = 0; - uint8_t iv[16]; + uint8_t iv[16] = {0}; do { if(!args_read_int_and_trim(args, &key_slot) || !(key_slot > 0 && key_slot <= 100)) { @@ -249,7 +249,7 @@ void crypto_cli_store_key(Cli* cli, FuriString* args) { } if(key_slot > 0) { - uint8_t iv[16]; + uint8_t iv[16] = {0}; if(key_slot > 1) { if(!furi_hal_crypto_store_load_key(key_slot - 1, iv)) { printf( diff --git a/applications/services/gui/gui.c b/applications/services/gui/gui.c index 2d06d70c7..9f6ebcd76 100644 --- a/applications/services/gui/gui.c +++ b/applications/services/gui/gui.c @@ -436,7 +436,7 @@ void gui_add_framebuffer_callback(Gui* gui, GuiCanvasCommitCallback callback, vo const CanvasCallbackPair p = {callback, context}; gui_lock(gui); - furi_assert(CanvasCallbackPairArray_count(gui->canvas_callback_pair, p) == 0); + furi_assert(!CanvasCallbackPairArray_count(gui->canvas_callback_pair, p)); CanvasCallbackPairArray_push_back(gui->canvas_callback_pair, p); gui_unlock(gui); diff --git a/applications/services/gui/modules/file_browser_worker.c b/applications/services/gui/modules/file_browser_worker.c index fdaf8273f..b9b2b2d8f 100644 --- a/applications/services/gui/modules/file_browser_worker.c +++ b/applications/services/gui/modules/file_browser_worker.c @@ -359,7 +359,7 @@ static int32_t browser_worker(void* context) { BrowserWorker* file_browser_worker_alloc(FuriString* path, const char* filter_ext, bool skip_assets) { - BrowserWorker* browser = malloc(sizeof(BrowserWorker)); + BrowserWorker* browser = malloc(sizeof(BrowserWorker)); //-V773 idx_last_array_init(browser->idx_last); diff --git a/applications/services/gui/modules/widget_elements/widget_element_button.c b/applications/services/gui/modules/widget_elements/widget_element_button.c index be33b1897..e3267058e 100644 --- a/applications/services/gui/modules/widget_elements/widget_element_button.c +++ b/applications/services/gui/modules/widget_elements/widget_element_button.c @@ -60,7 +60,7 @@ WidgetElement* widget_element_button_create( ButtonCallback callback, void* context) { // Allocate and init model - GuiButtonModel* model = malloc(sizeof(GuiButtonModel)); + GuiButtonModel* model = malloc(sizeof(GuiButtonModel)); //-V773 model->button_type = button_type; model->callback = callback; model->context = context; diff --git a/applications/services/gui/view_dispatcher.c b/applications/services/gui/view_dispatcher.c index 8de452d20..4034cc0b4 100644 --- a/applications/services/gui/view_dispatcher.c +++ b/applications/services/gui/view_dispatcher.c @@ -23,7 +23,7 @@ void view_dispatcher_free(ViewDispatcher* view_dispatcher) { gui_remove_view_port(view_dispatcher->gui, view_dispatcher->view_port); } // Crash if not all views were freed - furi_assert(ViewDict_size(view_dispatcher->views) == 0); + furi_assert(!ViewDict_size(view_dispatcher->views)); ViewDict_clear(view_dispatcher->views); // Free ViewPort @@ -157,7 +157,7 @@ void view_dispatcher_remove_view(ViewDispatcher* view_dispatcher, uint32_t view_ view_dispatcher->ongoing_input_view = NULL; } // Remove view - ViewDict_erase(view_dispatcher->views, view_id); + furi_check(ViewDict_erase(view_dispatcher->views, view_id)); view_set_update_callback(view, NULL); view_set_update_callback_context(view, NULL); diff --git a/applications/services/loader/loader.c b/applications/services/loader/loader.c index bc456536c..62dbad95f 100644 --- a/applications/services/loader/loader.c +++ b/applications/services/loader/loader.c @@ -269,7 +269,7 @@ static void loader_thread_state_callback(FuriThreadState thread_state, void* con event.type = LoaderEventTypeApplicationStarted; furi_pubsub_publish(loader_instance->pubsub, &event); - if(!loader_instance->application->flags & FlipperApplicationFlagInsomniaSafe) { + if(!(loader_instance->application->flags & FlipperApplicationFlagInsomniaSafe)) { furi_hal_power_insomnia_enter(); } } else if(thread_state == FuriThreadStateStopped) { @@ -284,7 +284,7 @@ static void loader_thread_state_callback(FuriThreadState thread_state, void* con loader_instance->application_arguments = NULL; } - if(!loader_instance->application->flags & FlipperApplicationFlagInsomniaSafe) { + if(!(loader_instance->application->flags & FlipperApplicationFlagInsomniaSafe)) { furi_hal_power_insomnia_exit(); } loader_unlock(instance); diff --git a/applications/services/storage/storage_cli.c b/applications/services/storage/storage_cli.c index 0efcb5e2c..c83f16499 100644 --- a/applications/services/storage/storage_cli.c +++ b/applications/services/storage/storage_cli.c @@ -275,7 +275,7 @@ static void storage_cli_read_chunks(Cli* cli, FuriString* path, FuriString* args uint32_t buffer_size; int parsed_count = sscanf(furi_string_get_cstr(args), "%lu", &buffer_size); - if(parsed_count == EOF || parsed_count != 1) { + if(parsed_count != 1) { storage_cli_print_usage(); } else if(storage_file_open(file, furi_string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) { uint64_t file_size = storage_file_size(file); @@ -315,7 +315,7 @@ static void storage_cli_write_chunk(Cli* cli, FuriString* path, FuriString* args uint32_t buffer_size; int parsed_count = sscanf(furi_string_get_cstr(args), "%lu", &buffer_size); - if(parsed_count == EOF || parsed_count != 1) { + if(parsed_count != 1) { storage_cli_print_usage(); } else { if(storage_file_open(file, furi_string_get_cstr(path), FSAM_WRITE, FSOM_OPEN_APPEND)) { diff --git a/applications/services/storage/storage_external_api.c b/applications/services/storage/storage_external_api.c index 6854ef7f3..2c3a7bfc9 100644 --- a/applications/services/storage/storage_external_api.c +++ b/applications/services/storage/storage_external_api.c @@ -545,8 +545,8 @@ static FS_Error FS_Error storage_common_merge(Storage* storage, const char* old_path, const char* new_path) { FS_Error error; - const char* new_path_tmp; - FuriString* new_path_next; + const char* new_path_tmp = NULL; + FuriString* new_path_next = NULL; new_path_next = furi_string_alloc(); FileInfo fileinfo; diff --git a/firmware/targets/f7/ble_glue/gap.c b/firmware/targets/f7/ble_glue/gap.c index aa8cd2c90..cf27df3a3 100644 --- a/firmware/targets/f7/ble_glue/gap.c +++ b/firmware/targets/f7/ble_glue/gap.c @@ -179,7 +179,7 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { case EVT_BLUE_GAP_PASS_KEY_REQUEST: { // Generate random PIN code - uint32_t pin = rand() % 999999; + uint32_t pin = rand() % 999999; //-V1064 aci_gap_pass_key_resp(gap->service.connection_handle, pin); if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagLock)) { FURI_LOG_I(TAG, "Pass key request event. Pin: ******"); @@ -478,7 +478,6 @@ bool gap_init(GapConfig* config, GapEventCallback on_event_cb, void* context) { gap = malloc(sizeof(Gap)); gap->config = config; - srand(DWT->CYCCNT); // Create advertising timer gap->advertise_timer = furi_timer_alloc(gap_advetise_timer_callback, FuriTimerTypeOnce, NULL); // Initialization of GATT & GAP layer diff --git a/firmware/targets/f7/furi_hal/furi_hal_crypto.c b/firmware/targets/f7/furi_hal/furi_hal_crypto.c index dbd8c58c2..e0ed3ab9b 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_crypto.c +++ b/firmware/targets/f7/furi_hal/furi_hal_crypto.c @@ -91,7 +91,7 @@ bool furi_hal_crypto_verify_key(uint8_t key_slot) { uint8_t keys_nb = 0; uint8_t valid_keys_nb = 0; uint8_t last_valid_slot = ENCLAVE_FACTORY_KEY_SLOTS; - uint8_t empty_iv[16]; + uint8_t empty_iv[16] = {0}; furi_hal_crypto_verify_enclave(&keys_nb, &valid_keys_nb); if(key_slot <= ENCLAVE_FACTORY_KEY_SLOTS) { // It's a factory key if(key_slot > keys_nb) return false; diff --git a/furi/core/check.h b/furi/core/check.h index 78efc1451..192c5260e 100644 --- a/furi/core/check.h +++ b/furi/core/check.h @@ -46,7 +46,7 @@ FURI_NORETURN void __furi_halt(); /** Check condition and crash if check failed */ #define furi_check(__e) \ do { \ - if((__e) == 0) { \ + if(!(__e)) { \ furi_crash("furi_check failed\r\n"); \ } \ } while(0) @@ -55,7 +55,7 @@ FURI_NORETURN void __furi_halt(); #ifdef FURI_DEBUG #define furi_assert(__e) \ do { \ - if((__e) == 0) { \ + if(!(__e)) { \ furi_crash("furi_assert failed\r\n"); \ } \ } while(0) diff --git a/furi/core/event_flag.c b/furi/core/event_flag.c index 5d2a49910..07dd30a16 100644 --- a/furi/core/event_flag.c +++ b/furi/core/event_flag.c @@ -25,7 +25,7 @@ uint32_t furi_event_flag_set(FuriEventFlag* instance, uint32_t flags) { uint32_t rflags; BaseType_t yield; - if(FURI_IS_IRQ_MODE() != 0U) { + if(FURI_IS_IRQ_MODE()) { yield = pdFALSE; if(xEventGroupSetBitsFromISR(hEventGroup, (EventBits_t)flags, &yield) == pdFAIL) { rflags = (uint32_t)FuriStatusErrorResource; @@ -48,7 +48,7 @@ uint32_t furi_event_flag_clear(FuriEventFlag* instance, uint32_t flags) { EventGroupHandle_t hEventGroup = (EventGroupHandle_t)instance; uint32_t rflags; - if(FURI_IS_IRQ_MODE() != 0U) { + if(FURI_IS_IRQ_MODE()) { rflags = xEventGroupGetBitsFromISR(hEventGroup); if(xEventGroupClearBitsFromISR(hEventGroup, (EventBits_t)flags) == pdFAIL) { @@ -73,7 +73,7 @@ uint32_t furi_event_flag_get(FuriEventFlag* instance) { EventGroupHandle_t hEventGroup = (EventGroupHandle_t)instance; uint32_t rflags; - if(FURI_IS_IRQ_MODE() != 0U) { + if(FURI_IS_IRQ_MODE()) { rflags = xEventGroupGetBitsFromISR(hEventGroup); } else { rflags = xEventGroupGetBits(hEventGroup); diff --git a/furi/core/memmgr_heap.c b/furi/core/memmgr_heap.c index 32b2875cc..ac51b4a20 100644 --- a/furi/core/memmgr_heap.c +++ b/furi/core/memmgr_heap.c @@ -150,8 +150,7 @@ void memmgr_heap_disable_thread_trace(FuriThreadId thread_id) { vTaskSuspendAll(); { memmgr_heap_thread_trace_depth++; - furi_check(MemmgrHeapThreadDict_get(memmgr_heap_thread_dict, (uint32_t)thread_id) != NULL); - MemmgrHeapThreadDict_erase(memmgr_heap_thread_dict, (uint32_t)thread_id); + furi_check(MemmgrHeapThreadDict_erase(memmgr_heap_thread_dict, (uint32_t)thread_id)); memmgr_heap_thread_trace_depth--; } (void)xTaskResumeAll(); @@ -212,7 +211,8 @@ static inline void traceFREE(void* pointer, size_t size) { MemmgrHeapAllocDict_t* alloc_dict = MemmgrHeapThreadDict_get(memmgr_heap_thread_dict, (uint32_t)thread_id); if(alloc_dict) { - MemmgrHeapAllocDict_erase(*alloc_dict, (uint32_t)pointer); + // In some cases thread may want to release memory that was not allocated by it + (void)MemmgrHeapAllocDict_erase(*alloc_dict, (uint32_t)pointer); } memmgr_heap_thread_trace_depth--; } diff --git a/furi/core/mutex.c b/furi/core/mutex.c index 78ea05196..ab66b0f18 100644 --- a/furi/core/mutex.c +++ b/furi/core/mutex.c @@ -45,7 +45,7 @@ FuriStatus furi_mutex_acquire(FuriMutex* instance, uint32_t timeout) { stat = FuriStatusOk; - if(FURI_IS_IRQ_MODE() != 0U) { + if(FURI_IS_IRQ_MODE()) { stat = FuriStatusErrorISR; } else if(hMutex == NULL) { stat = FuriStatusErrorParameter; @@ -85,7 +85,7 @@ FuriStatus furi_mutex_release(FuriMutex* instance) { stat = FuriStatusOk; - if(FURI_IS_IRQ_MODE() != 0U) { + if(FURI_IS_IRQ_MODE()) { stat = FuriStatusErrorISR; } else if(hMutex == NULL) { stat = FuriStatusErrorParameter; @@ -111,7 +111,7 @@ FuriThreadId furi_mutex_get_owner(FuriMutex* instance) { hMutex = (SemaphoreHandle_t)((uint32_t)instance & ~1U); - if((FURI_IS_IRQ_MODE() != 0U) || (hMutex == NULL)) { + if((FURI_IS_IRQ_MODE()) || (hMutex == NULL)) { owner = 0; } else { owner = (FuriThreadId)xSemaphoreGetMutexHolder(hMutex); diff --git a/furi/core/semaphore.c b/furi/core/semaphore.c index a204cbe6e..8c99bfc54 100644 --- a/furi/core/semaphore.c +++ b/furi/core/semaphore.c @@ -45,7 +45,7 @@ FuriStatus furi_semaphore_acquire(FuriSemaphore* instance, uint32_t timeout) { stat = FuriStatusOk; - if(FURI_IS_IRQ_MODE() != 0U) { + if(FURI_IS_IRQ_MODE()) { if(timeout != 0U) { stat = FuriStatusErrorParameter; } else { @@ -80,7 +80,7 @@ FuriStatus furi_semaphore_release(FuriSemaphore* instance) { stat = FuriStatusOk; - if(FURI_IS_IRQ_MODE() != 0U) { + if(FURI_IS_IRQ_MODE()) { yield = pdFALSE; if(xSemaphoreGiveFromISR(hSemaphore, &yield) != pdTRUE) { @@ -104,7 +104,7 @@ uint32_t furi_semaphore_get_count(FuriSemaphore* instance) { SemaphoreHandle_t hSemaphore = (SemaphoreHandle_t)instance; uint32_t count; - if(FURI_IS_IRQ_MODE() != 0U) { + if(FURI_IS_IRQ_MODE()) { count = (uint32_t)uxSemaphoreGetCountFromISR(hSemaphore); } else { count = (uint32_t)uxSemaphoreGetCount(hSemaphore); diff --git a/furi/core/stream_buffer.c b/furi/core/stream_buffer.c index b9d0629fe..2df84fa5b 100644 --- a/furi/core/stream_buffer.c +++ b/furi/core/stream_buffer.c @@ -23,7 +23,7 @@ size_t furi_stream_buffer_send( uint32_t timeout) { size_t ret; - if(FURI_IS_IRQ_MODE() != 0U) { + if(FURI_IS_IRQ_MODE()) { BaseType_t yield; ret = xStreamBufferSendFromISR(stream_buffer, data, length, &yield); portYIELD_FROM_ISR(yield); @@ -41,7 +41,7 @@ size_t furi_stream_buffer_receive( uint32_t timeout) { size_t ret; - if(FURI_IS_IRQ_MODE() != 0U) { + if(FURI_IS_IRQ_MODE()) { BaseType_t yield; ret = xStreamBufferReceiveFromISR(stream_buffer, data, length, &yield); portYIELD_FROM_ISR(yield); diff --git a/furi/core/string.c b/furi/core/string.c index 099f70c11..901b1f625 100644 --- a/furi/core/string.c +++ b/furi/core/string.c @@ -29,13 +29,13 @@ FuriString* furi_string_alloc() { } FuriString* furi_string_alloc_set(const FuriString* s) { - FuriString* string = malloc(sizeof(FuriString)); + FuriString* string = malloc(sizeof(FuriString)); //-V773 string_init_set(string->string, s->string); return string; } FuriString* furi_string_alloc_set_str(const char cstr[]) { - FuriString* string = malloc(sizeof(FuriString)); + FuriString* string = malloc(sizeof(FuriString)); //-V773 string_init_set(string->string, cstr); return string; } diff --git a/furi/core/thread.c b/furi/core/thread.c index 508146f63..157e022e9 100644 --- a/furi/core/thread.c +++ b/furi/core/thread.c @@ -50,6 +50,7 @@ static int32_t __furi_thread_stdout_flush(FuriThread* thread); __attribute__((__noreturn__)) void furi_thread_catch() { asm volatile("nop"); // extra magic furi_crash("You are doing it wrong"); + __builtin_unreachable(); } static void furi_thread_set_state(FuriThread* thread, FuriThreadState state) { @@ -112,6 +113,12 @@ FuriThread* furi_thread_alloc() { FuriThread* thread = malloc(sizeof(FuriThread)); thread->output.buffer = furi_string_alloc(); thread->is_service = false; + + if(furi_thread_get_current_id()) { + FuriThread* parent = pvTaskGetThreadLocalStoragePointer(NULL, 0); + if(parent) thread->heap_trace_enabled = parent->heap_trace_enabled; + } + return thread; } diff --git a/lib/flipper_format/flipper_format_stream.c b/lib/flipper_format/flipper_format_stream.c index 41934a3b1..9cce95d47 100644 --- a/lib/flipper_format/flipper_format_stream.c +++ b/lib/flipper_format/flipper_format_stream.c @@ -313,7 +313,7 @@ bool flipper_format_stream_write_value_line(Stream* stream, FlipperStreamWriteDa furi_crash("Unknown FF type"); } - if((size_t)(i + 1) < write_data->data_size) { + if(((size_t)i + 1) < write_data->data_size) { furi_string_cat(value, " "); } diff --git a/lib/infrared/encoder_decoder/common/infrared_common_decoder.c b/lib/infrared/encoder_decoder/common/infrared_common_decoder.c index bff4c73db..7f1c3a4fd 100644 --- a/lib/infrared/encoder_decoder/common/infrared_common_decoder.c +++ b/lib/infrared/encoder_decoder/common/infrared_common_decoder.c @@ -85,8 +85,8 @@ static InfraredStatus infrared_common_decode_bits(InfraredCommonDecoder* decoder if(timings->min_split_time && !level) { if(timing > timings->min_split_time) { /* long low timing - check if we're ready for any of protocol modification */ - for(size_t i = 0; decoder->protocol->databit_len[i] && - (i < COUNT_OF(decoder->protocol->databit_len)); + for(size_t i = 0; i < COUNT_OF(decoder->protocol->databit_len) && + decoder->protocol->databit_len[i]; ++i) { if(decoder->protocol->databit_len[i] == decoder->databit_cnt) { return InfraredStatusReady; @@ -199,7 +199,7 @@ InfraredMessage* infrared_common_decoder_check_ready(InfraredCommonDecoder* deco bool found_length = false; for(size_t i = 0; - decoder->protocol->databit_len[i] && (i < COUNT_OF(decoder->protocol->databit_len)); + i < COUNT_OF(decoder->protocol->databit_len) && decoder->protocol->databit_len[i]; ++i) { if(decoder->protocol->databit_len[i] == decoder->databit_cnt) { found_length = true; diff --git a/lib/lfrfid/lfrfid_worker.c b/lib/lfrfid/lfrfid_worker.c index 8b4f8b6a9..f3e37fa3c 100644 --- a/lib/lfrfid/lfrfid_worker.c +++ b/lib/lfrfid/lfrfid_worker.c @@ -140,9 +140,8 @@ size_t lfrfid_worker_dict_get_data_size(LFRFIDWorker* worker, LFRFIDProtocol pro static int32_t lfrfid_worker_thread(void* thread_context) { LFRFIDWorker* worker = thread_context; - bool running = true; - while(running) { + while(true) { uint32_t flags = furi_thread_flags_wait(LFRFIDEventAll, FuriFlagWaitAny, FuriWaitForever); if(flags != FuriFlagErrorTimeout) { // stop thread diff --git a/lib/print/printf_tiny.c b/lib/print/printf_tiny.c index 0db11922d..6e47f6528 100644 --- a/lib/print/printf_tiny.c +++ b/lib/print/printf_tiny.c @@ -541,7 +541,7 @@ static size_t _etoa( exp2 = (int)(expval * 3.321928094887362 + 0.5); const double z = expval * 2.302585092994046 - exp2 * 0.6931471805599453; const double z2 = z * z; - conv.U = (uint64_t)(exp2 + 1023) << 52U; + conv.U = ((uint64_t)exp2 + 1023) << 52U; // compute exp(z) using continued fractions, see https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex conv.F *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14))))); // correct for rounding errors diff --git a/lib/subghz/protocols/power_smart.c b/lib/subghz/protocols/power_smart.c index 63ca78711..1e8d10e95 100644 --- a/lib/subghz/protocols/power_smart.c +++ b/lib/subghz/protocols/power_smart.c @@ -312,7 +312,6 @@ void subghz_protocol_decoder_power_smart_feed( if((instance->decoder.decode_data & POWER_SMART_PACKET_HEADER_MASK) == POWER_SMART_PACKET_HEADER) { if(subghz_protocol_power_smart_chek_valid(instance->decoder.decode_data)) { - instance->decoder.decode_data = instance->decoder.decode_data; instance->generic.data = instance->decoder.decode_data; instance->generic.data_count_bit = subghz_protocol_power_smart_const.min_count_bit_for_found; diff --git a/lib/subghz/subghz_keystore.c b/lib/subghz/subghz_keystore.c index 1b4b3b716..e06bd9796 100644 --- a/lib/subghz/subghz_keystore.c +++ b/lib/subghz/subghz_keystore.c @@ -464,7 +464,7 @@ bool subghz_keystore_raw_encrypted_save( } stream_write_cstring(output_stream, encrypted_line); - } while(ret > 0 && result); + } while(result); flipper_format_free(output_flipper_format); diff --git a/lib/toolbox/random_name.c b/lib/toolbox/random_name.c index 5a5374398..64e712c7c 100644 --- a/lib/toolbox/random_name.c +++ b/lib/toolbox/random_name.c @@ -5,12 +5,6 @@ #include void set_random_name(char* name, uint8_t max_name_size) { - static bool rand_generator_inited = false; - - if(!rand_generator_inited) { - srand(DWT->CYCCNT); - rand_generator_inited = true; - } const char* prefix[] = { "ancient", "hollow", "strange", "disappeared", "unknown", "unthinkable", "unnamable", "nameless", "my", "concealed", From 0a86ad43ca2ccf00f3f71e288bb7552b48e14dce Mon Sep 17 00:00:00 2001 From: hedger Date: Sun, 6 Nov 2022 11:39:50 +0400 Subject: [PATCH 03/12] fbt: fix for launch_app (#1978) --- site_scons/extapps.scons | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/site_scons/extapps.scons b/site_scons/extapps.scons index 3d2a98788..670d71fd0 100644 --- a/site_scons/extapps.scons +++ b/site_scons/extapps.scons @@ -98,14 +98,14 @@ extapps.applications = appenv["EXT_APPS"] extapps.resources_dist = appenv.FapDist(appenv.Dir("#/assets/resources/apps"), []) if appsrc := appenv.subst("$APPSRC"): - app_manifest, fap_file, app_validator = appenv.GetExtAppFromPath(appsrc) + app_artifacts = appenv.GetExtAppFromPath(appsrc) appenv.PhonyTarget( "launch_app", '${PYTHON3} "${APP_RUN_SCRIPT}" "${SOURCE}" --fap_dst_dir "/ext/apps/${FAP_CATEGORY}"', - source=fap_file, - FAP_CATEGORY=app_manifest.fap_category, + source=app_artifacts.compact, + FAP_CATEGORY=app_artifacts.app.fap_category, ) - appenv.Alias("launch_app", app_validator) + appenv.Alias("launch_app", app_artifacts.validator) # SDK management From 65005e71d2524e2c82d9bb6631f655b3c442d2e5 Mon Sep 17 00:00:00 2001 From: Skorpionm <85568270+Skorpionm@users.noreply.github.com> Date: Sun, 6 Nov 2022 21:30:02 +0400 Subject: [PATCH 04/12] WS: fix show negative temperature (#1980) --- .../plugins/weather_station/protocols/acurite_592txr.c | 5 ++--- .../plugins/weather_station/protocols/acurite_606tx.c | 5 ++--- .../plugins/weather_station/protocols/ambient_weather.c | 5 ++--- applications/plugins/weather_station/protocols/gt_wt_03.c | 5 ++--- applications/plugins/weather_station/protocols/infactory.c | 5 ++--- .../weather_station/protocols/lacrosse_tx141thbv2.c | 5 ++--- applications/plugins/weather_station/protocols/nexus_th.c | 5 ++--- .../plugins/weather_station/protocols/thermopro_tx4.c | 5 ++--- .../weather_station/views/weather_station_receiver_info.c | 7 +------ 9 files changed, 17 insertions(+), 30 deletions(-) diff --git a/applications/plugins/weather_station/protocols/acurite_592txr.c b/applications/plugins/weather_station/protocols/acurite_592txr.c index 4d7f59544..5384a3c91 100644 --- a/applications/plugins/weather_station/protocols/acurite_592txr.c +++ b/applications/plugins/weather_station/protocols/acurite_592txr.c @@ -293,7 +293,7 @@ void ws_protocol_decoder_acurite_592txr_get_string(void* context, FuriString* ou "%s %dbit\r\n" "Key:0x%lX%08lX\r\n" "Sn:0x%lX Ch:%d Bat:%d\r\n" - "Temp:%d.%d C Hum:%d%%", + "Temp:%3.1f C Hum:%d%%", instance->generic.protocol_name, instance->generic.data_count_bit, (uint32_t)(instance->generic.data >> 32), @@ -301,7 +301,6 @@ void ws_protocol_decoder_acurite_592txr_get_string(void* context, FuriString* ou instance->generic.id, instance->generic.channel, instance->generic.battery_low, - (int16_t)instance->generic.temp, - abs(((int16_t)(instance->generic.temp * 10) - (((int16_t)instance->generic.temp) * 10))), + (double)instance->generic.temp, instance->generic.humidity); } diff --git a/applications/plugins/weather_station/protocols/acurite_606tx.c b/applications/plugins/weather_station/protocols/acurite_606tx.c index 3c9144057..4cb5d18b8 100644 --- a/applications/plugins/weather_station/protocols/acurite_606tx.c +++ b/applications/plugins/weather_station/protocols/acurite_606tx.c @@ -234,7 +234,7 @@ void ws_protocol_decoder_acurite_606tx_get_string(void* context, FuriString* out "%s %dbit\r\n" "Key:0x%lX%08lX\r\n" "Sn:0x%lX Ch:%d Bat:%d\r\n" - "Temp:%d.%d C Hum:%d%%", + "Temp:%3.1f C Hum:%d%%", instance->generic.protocol_name, instance->generic.data_count_bit, (uint32_t)(instance->generic.data >> 32), @@ -242,7 +242,6 @@ void ws_protocol_decoder_acurite_606tx_get_string(void* context, FuriString* out instance->generic.id, instance->generic.channel, instance->generic.battery_low, - (int16_t)instance->generic.temp, - abs(((int16_t)(instance->generic.temp * 10) - (((int16_t)instance->generic.temp) * 10))), + (double)instance->generic.temp, instance->generic.humidity); } diff --git a/applications/plugins/weather_station/protocols/ambient_weather.c b/applications/plugins/weather_station/protocols/ambient_weather.c index 5fede684e..5ae22b790 100644 --- a/applications/plugins/weather_station/protocols/ambient_weather.c +++ b/applications/plugins/weather_station/protocols/ambient_weather.c @@ -263,7 +263,7 @@ void ws_protocol_decoder_ambient_weather_get_string(void* context, FuriString* o "%s %dbit\r\n" "Key:0x%lX%08lX\r\n" "Sn:0x%lX Ch:%d Bat:%d\r\n" - "Temp:%d.%d C Hum:%d%%", + "Temp:%3.1f C Hum:%d%%", instance->generic.protocol_name, instance->generic.data_count_bit, (uint32_t)(instance->generic.data >> 32), @@ -271,7 +271,6 @@ void ws_protocol_decoder_ambient_weather_get_string(void* context, FuriString* o instance->generic.id, instance->generic.channel, instance->generic.battery_low, - (int16_t)instance->generic.temp, - abs(((int16_t)(instance->generic.temp * 10) - (((int16_t)instance->generic.temp) * 10))), + (double)instance->generic.temp, instance->generic.humidity); } diff --git a/applications/plugins/weather_station/protocols/gt_wt_03.c b/applications/plugins/weather_station/protocols/gt_wt_03.c index 04bca9ac1..7831cf069 100644 --- a/applications/plugins/weather_station/protocols/gt_wt_03.c +++ b/applications/plugins/weather_station/protocols/gt_wt_03.c @@ -327,7 +327,7 @@ void ws_protocol_decoder_gt_wt_03_get_string(void* context, FuriString* output) "%s %dbit\r\n" "Key:0x%lX%08lX\r\n" "Sn:0x%lX Ch:%d Bat:%d\r\n" - "Temp:%d.%d C Hum:%d%%", + "Temp:%3.1f C Hum:%d%%", instance->generic.protocol_name, instance->generic.data_count_bit, (uint32_t)(instance->generic.data >> 32), @@ -335,7 +335,6 @@ void ws_protocol_decoder_gt_wt_03_get_string(void* context, FuriString* output) instance->generic.id, instance->generic.channel, instance->generic.battery_low, - (int16_t)instance->generic.temp, - abs(((int16_t)(instance->generic.temp * 10) - (((int16_t)instance->generic.temp) * 10))), + (double)instance->generic.temp, instance->generic.humidity); } diff --git a/applications/plugins/weather_station/protocols/infactory.c b/applications/plugins/weather_station/protocols/infactory.c index b08a4e9db..2d444d981 100644 --- a/applications/plugins/weather_station/protocols/infactory.c +++ b/applications/plugins/weather_station/protocols/infactory.c @@ -283,7 +283,7 @@ void ws_protocol_decoder_infactory_get_string(void* context, FuriString* output) "%s %dbit\r\n" "Key:0x%lX%08lX\r\n" "Sn:0x%lX Ch:%d Bat:%d\r\n" - "Temp:%d.%d C Hum:%d%%", + "Temp:%3.1f C Hum:%d%%", instance->generic.protocol_name, instance->generic.data_count_bit, (uint32_t)(instance->generic.data >> 32), @@ -291,7 +291,6 @@ void ws_protocol_decoder_infactory_get_string(void* context, FuriString* output) instance->generic.id, instance->generic.channel, instance->generic.battery_low, - (int16_t)instance->generic.temp, - abs(((int16_t)(instance->generic.temp * 10) - (((int16_t)instance->generic.temp) * 10))), + (double)instance->generic.temp, instance->generic.humidity); } diff --git a/applications/plugins/weather_station/protocols/lacrosse_tx141thbv2.c b/applications/plugins/weather_station/protocols/lacrosse_tx141thbv2.c index d4b89be87..e4b612250 100644 --- a/applications/plugins/weather_station/protocols/lacrosse_tx141thbv2.c +++ b/applications/plugins/weather_station/protocols/lacrosse_tx141thbv2.c @@ -284,7 +284,7 @@ void ws_protocol_decoder_lacrosse_tx141thbv2_get_string(void* context, FuriStrin "%s %dbit\r\n" "Key:0x%lX%08lX\r\n" "Sn:0x%lX Ch:%d Bat:%d\r\n" - "Temp:%d.%d C Hum:%d%%", + "Temp:%3.1f C Hum:%d%%", instance->generic.protocol_name, instance->generic.data_count_bit, (uint32_t)(instance->generic.data >> 32), @@ -292,7 +292,6 @@ void ws_protocol_decoder_lacrosse_tx141thbv2_get_string(void* context, FuriStrin instance->generic.id, instance->generic.channel, instance->generic.battery_low, - (int16_t)instance->generic.temp, - abs(((int16_t)(instance->generic.temp * 10) - (((int16_t)instance->generic.temp) * 10))), + (double)instance->generic.temp, instance->generic.humidity); } diff --git a/applications/plugins/weather_station/protocols/nexus_th.c b/applications/plugins/weather_station/protocols/nexus_th.c index c3d823eda..7d4a77aea 100644 --- a/applications/plugins/weather_station/protocols/nexus_th.c +++ b/applications/plugins/weather_station/protocols/nexus_th.c @@ -247,7 +247,7 @@ void ws_protocol_decoder_nexus_th_get_string(void* context, FuriString* output) "%s %dbit\r\n" "Key:0x%lX%08lX\r\n" "Sn:0x%lX Ch:%d Bat:%d\r\n" - "Temp:%d.%d C Hum:%d%%", + "Temp:%3.1f C Hum:%d%%", instance->generic.protocol_name, instance->generic.data_count_bit, (uint32_t)(instance->generic.data >> 32), @@ -255,7 +255,6 @@ void ws_protocol_decoder_nexus_th_get_string(void* context, FuriString* output) instance->generic.id, instance->generic.channel, instance->generic.battery_low, - (int16_t)instance->generic.temp, - abs(((int16_t)(instance->generic.temp * 10) - (((int16_t)instance->generic.temp) * 10))), + (double)instance->generic.temp, instance->generic.humidity); } diff --git a/applications/plugins/weather_station/protocols/thermopro_tx4.c b/applications/plugins/weather_station/protocols/thermopro_tx4.c index 9a2eacb2f..0882bc33d 100644 --- a/applications/plugins/weather_station/protocols/thermopro_tx4.c +++ b/applications/plugins/weather_station/protocols/thermopro_tx4.c @@ -246,7 +246,7 @@ void ws_protocol_decoder_thermopro_tx4_get_string(void* context, FuriString* out "%s %dbit\r\n" "Key:0x%lX%08lX\r\n" "Sn:0x%lX Ch:%d Bat:%d\r\n" - "Temp:%d.%d C Hum:%d%%", + "Temp:%3.1f C Hum:%d%%", instance->generic.protocol_name, instance->generic.data_count_bit, (uint32_t)(instance->generic.data >> 32), @@ -254,7 +254,6 @@ void ws_protocol_decoder_thermopro_tx4_get_string(void* context, FuriString* out instance->generic.id, instance->generic.channel, instance->generic.battery_low, - (int16_t)instance->generic.temp, - abs(((int16_t)(instance->generic.temp * 10) - (((int16_t)instance->generic.temp) * 10))), + (double)instance->generic.temp, instance->generic.humidity); } diff --git a/applications/plugins/weather_station/views/weather_station_receiver_info.c b/applications/plugins/weather_station/views/weather_station_receiver_info.c index 34ec122d1..49b447f10 100644 --- a/applications/plugins/weather_station/views/weather_station_receiver_info.c +++ b/applications/plugins/weather_station/views/weather_station_receiver_info.c @@ -75,12 +75,7 @@ void ws_view_receiver_info_draw(Canvas* canvas, WSReceiverInfoModel* model) { if(model->generic->temp != WS_NO_TEMPERATURE) { canvas_draw_icon(canvas, 18, 42, &I_Therm_7x16); - snprintf( - buffer, - sizeof(buffer), - "%3.2d.%d C", - (int16_t)model->generic->temp, - abs(((int16_t)(model->generic->temp * 10) - (((int16_t)model->generic->temp) * 10)))); + snprintf(buffer, sizeof(buffer), "%3.1f C", (double)model->generic->temp); canvas_draw_str_aligned(canvas, 63, 46, AlignRight, AlignTop, buffer); canvas_draw_circle(canvas, 55, 45, 1); } From aa2ecbe80f77d6b7132cd4fdb83eb49fba297b31 Mon Sep 17 00:00:00 2001 From: Samuel Stauffer Date: Mon, 7 Nov 2022 06:38:04 -0800 Subject: [PATCH 05/12] infrared: add Kaseikyo IR protocol (#1965) * infrared: add Kaseikyo IR protocol Add Kaseikyo IR protocol support. This protocol is also called the Panasonic protocol and is used by a number of manufacturers including Denon. The protocol includes a vendor field and a number of fields that are vendor specific. To support the format of address+command used by flipper the vendor+genre1+genre2+id fields are encoded into the address while the data is used for the command. There are older versions of the protocol that used a reverse bit order that are not supported. Protocol information: - https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/src/ir_Kaseikyo.hpp - http://www.hifi-remote.com/johnsfine/DecodeIR.html#Kaseikyo - https://www.denon.com/-/media/files/documentmaster/denonna/avr-x3700h_avc-x3700h_ir_code_v01_04062020.doc * Format and add unit test to Kaseikyo IR codec. Co-authored-by: Georgii Surkov <37121527+gsurkov@users.noreply.github.com> --- .../debug/unit_tests/infrared/infrared_test.c | 12 ++ .../unit_tests/infrared/test_kaseikyo.irtest | 105 ++++++++++++++++++ .../common/infrared_common_protocol_defs.c | 23 ++++ lib/infrared/encoder_decoder/infrared.c | 14 +++ lib/infrared/encoder_decoder/infrared.h | 1 + .../infrared_protocol_defs_i.h | 51 +++++++++ .../kaseikyo/infrared_decoder_kaseikyo.c | 54 +++++++++ .../kaseikyo/infrared_encoder_kaseikyo.c | 45 ++++++++ .../kaseikyo/infrared_kaseikyo_spec.c | 17 +++ 9 files changed, 322 insertions(+) create mode 100644 assets/unit_tests/infrared/test_kaseikyo.irtest create mode 100644 lib/infrared/encoder_decoder/kaseikyo/infrared_decoder_kaseikyo.c create mode 100644 lib/infrared/encoder_decoder/kaseikyo/infrared_encoder_kaseikyo.c create mode 100644 lib/infrared/encoder_decoder/kaseikyo/infrared_kaseikyo_spec.c diff --git a/applications/debug/unit_tests/infrared/infrared_test.c b/applications/debug/unit_tests/infrared/infrared_test.c index 8879c8fc8..2bcb95da8 100644 --- a/applications/debug/unit_tests/infrared/infrared_test.c +++ b/applications/debug/unit_tests/infrared/infrared_test.c @@ -424,6 +424,7 @@ MU_TEST(infrared_test_decoder_mixed) { infrared_test_run_decoder(InfraredProtocolRC5, 5); infrared_test_run_decoder(InfraredProtocolSamsung32, 1); infrared_test_run_decoder(InfraredProtocolSIRC, 3); + infrared_test_run_decoder(InfraredProtocolKaseikyo, 1); } MU_TEST(infrared_test_decoder_nec) { @@ -489,6 +490,15 @@ MU_TEST(infrared_test_encoder_rc6) { infrared_test_run_encoder(InfraredProtocolRC6, 1); } +MU_TEST(infrared_test_decoder_kaseikyo) { + infrared_test_run_decoder(InfraredProtocolKaseikyo, 1); + infrared_test_run_decoder(InfraredProtocolKaseikyo, 2); + infrared_test_run_decoder(InfraredProtocolKaseikyo, 3); + infrared_test_run_decoder(InfraredProtocolKaseikyo, 4); + infrared_test_run_decoder(InfraredProtocolKaseikyo, 5); + infrared_test_run_decoder(InfraredProtocolKaseikyo, 6); +} + MU_TEST(infrared_test_encoder_decoder_all) { infrared_test_run_encoder_decoder(InfraredProtocolNEC, 1); infrared_test_run_encoder_decoder(InfraredProtocolNECext, 1); @@ -498,6 +508,7 @@ MU_TEST(infrared_test_encoder_decoder_all) { infrared_test_run_encoder_decoder(InfraredProtocolRC6, 1); infrared_test_run_encoder_decoder(InfraredProtocolRC5, 1); infrared_test_run_encoder_decoder(InfraredProtocolSIRC, 1); + infrared_test_run_encoder_decoder(InfraredProtocolKaseikyo, 1); } MU_TEST_SUITE(infrared_test) { @@ -515,6 +526,7 @@ MU_TEST_SUITE(infrared_test) { MU_RUN_TEST(infrared_test_decoder_nec); MU_RUN_TEST(infrared_test_decoder_samsung32); MU_RUN_TEST(infrared_test_decoder_necext1); + MU_RUN_TEST(infrared_test_decoder_kaseikyo); MU_RUN_TEST(infrared_test_decoder_mixed); MU_RUN_TEST(infrared_test_encoder_decoder_all); } diff --git a/assets/unit_tests/infrared/test_kaseikyo.irtest b/assets/unit_tests/infrared/test_kaseikyo.irtest new file mode 100644 index 000000000..d0142fecd --- /dev/null +++ b/assets/unit_tests/infrared/test_kaseikyo.irtest @@ -0,0 +1,105 @@ +Filetype: IR tests file +Version: 1 +# +name: decoder_input1 +type: raw +data: 1000000 3363 1685 407 436 411 432 415 1240 434 410 437 1245 439 404 433 1249 435 408 439 431 406 1249 435 435 412 405 442 1241 433 1249 435 408 439 405 442 428 409 434 413 430 407 411 436 433 414 429 408 1248 436 407 440 1243 441 428 409 434 413 431 406 1249 435 1248 436 406 441 1242 442 1240 434 409 438 431 416 428 409 408 439 430 407 411 436 407 440 429 408 436 411 432 415 402 435 1247 437 1245 439 1243 441 1238 436 +# +name: decoder_expected1 +type: parsed_array +count: 1 +# +protocol: Kaseikyo +address: 41 54 32 00 +command: 1B 00 00 00 +repeat: false +# +name: decoder_input2 +type: raw +data: 1000000 3365 1683 409 434 413 431 406 1276 408 435 412 1270 414 429 408 1248 436 434 413 430 407 1275 409 434 413 431 406 1276 408 1248 436 433 414 430 407 437 410 433 414 429 408 436 411 432 415 428 409 1246 438 432 415 1267 407 437 410 433 414 429 408 436 411 432 415 1266 408 1250 434 1248 436 432 415 429 408 435 412 432 415 428 409 434 413 430 407 437 410 433 414 429 408 436 411 432 415 428 409 435 412 1240 434 +# +name: decoder_expected2 +type: parsed_array +count: 1 +# +protocol: Kaseikyo +address: 41 54 32 00 +command: 1C 00 00 00 +repeat: false +# +name: decoder_input3 +type: raw +data: 1000000 3361 1661 442 427 410 434 413 1243 441 428 409 1247 437 432 415 1241 433 410 437 407 440 1242 432 437 410 407 440 1242 442 1241 433 436 411 407 440 430 407 436 411 406 441 402 435 435 412 431 416 1240 434 410 437 1245 439 404 433 411 436 407 440 403 434 436 411 432 415 429 408 1249 435 1247 437 1245 439 430 407 1250 434 434 413 404 433 438 409 434 413 1243 441 1241 433 410 437 1245 439 430 407 1250 434 432 415 +# +name: decoder_expected3 +type: parsed_array +count: 1 +# +protocol: Kaseikyo +address: 41 54 32 00 +command: 70 01 00 00 +repeat: false +# +name: decoder_input4 +type: raw +data: 1000000 3365 1656 436 406 441 402 435 1248 436 406 441 1242 432 410 437 1246 438 404 433 410 437 1246 438 404 433 437 410 1245 491 1190 442 401 436 435 412 431 416 427 410 433 414 429 408 435 412 431 416 1240 434 435 412 1244 440 1241 433 436 411 433 414 402 435 409 438 405 442 402 435 1247 437 1244 440 1241 433 437 410 1245 439 430 407 410 437 406 441 402 435 409 438 1243 441 402 435 1247 437 406 441 1240 434 433 414 +# +name: decoder_expected4 +type: parsed_array +count: 1 +# +protocol: Kaseikyo +address: 43 54 32 00 +command: 70 01 00 00 +repeat: false +# +name: decoder_input5 +type: raw +data: 1000000 3357 1665 438 431 416 428 409 1247 437 432 415 1241 433 436 411 1245 439 430 407 436 411 1245 439 430 407 437 410 1246 438 1243 441 428 409 436 411 432 415 428 409 435 412 431 416 427 410 434 413 1243 441 427 410 1247 437 1245 439 430 407 437 410 1246 438 1244 440 429 408 1250 434 1248 488 355 440 429 408 436 411 432 415 428 408 435 412 431 416 428 409 1247 437 432 415 428 409 1248 436 1246 490 1191 441 1240 434 +# +name: decoder_expected5 +type: parsed_array +count: 1 +# +protocol: Kaseikyo +address: 43 54 32 00 +command: 1B 00 00 00 +repeat: false +# +name: decoder_input6 +type: raw +data: 1000000 3358 1664 439 430 407 437 410 1245 439 430 407 1250 434 434 413 1243 441 428 409 435 412 1244 440 428 409 435 412 1244 440 1242 432 437 410 434 413 430 407 436 411 432 415 428 409 435 412 431 416 1240 434 435 412 1244 440 1242 442 427 410 434 413 1243 441 427 409 1247 437 433 414 429 408 436 411 432 415 428 409 435 412 431 416 427 410 434 413 1243 441 1240 486 357 438 432 415 1240 434 436 411 432 415 425 412 +# +name: decoder_expected6 +type: parsed_array +count: 1 +# +protocol: Kaseikyo +address: 43 54 32 00 +command: 05 00 00 00 +repeat: false +# +name: encoder_decoder_input1 +type: parsed_array +count: 4 +# +protocol: Kaseikyo +address: 41 54 32 00 +command: 1B 00 00 00 +repeat: false +# +protocol: Kaseikyo +address: 41 54 32 00 +command: 70 01 00 00 +repeat: false +# +protocol: Kaseikyo +address: 43 54 32 00 +command: 05 00 00 00 +repeat: false +# +protocol: Kaseikyo +address: 43 54 32 00 +command: 1B 00 00 00 +repeat: false +# diff --git a/lib/infrared/encoder_decoder/common/infrared_common_protocol_defs.c b/lib/infrared/encoder_decoder/common/infrared_common_protocol_defs.c index e8f7664a7..3dd26e9d8 100644 --- a/lib/infrared/encoder_decoder/common/infrared_common_protocol_defs.c +++ b/lib/infrared/encoder_decoder/common/infrared_common_protocol_defs.c @@ -115,3 +115,26 @@ const InfraredCommonProtocolSpec protocol_sirc = { .decode_repeat = NULL, .encode_repeat = infrared_encoder_sirc_encode_repeat, }; + +const InfraredCommonProtocolSpec protocol_kaseikyo = { + .timings = + { + .preamble_mark = INFRARED_KASEIKYO_PREAMBLE_MARK, + .preamble_space = INFRARED_KASEIKYO_PREAMBLE_SPACE, + .bit1_mark = INFRARED_KASEIKYO_BIT1_MARK, + .bit1_space = INFRARED_KASEIKYO_BIT1_SPACE, + .bit0_mark = INFRARED_KASEIKYO_BIT0_MARK, + .bit0_space = INFRARED_KASEIKYO_BIT0_SPACE, + .preamble_tolerance = INFRARED_KASEIKYO_PREAMBLE_TOLERANCE, + .bit_tolerance = INFRARED_KASEIKYO_BIT_TOLERANCE, + .silence_time = INFRARED_KASEIKYO_SILENCE, + .min_split_time = INFRARED_KASEIKYO_MIN_SPLIT_TIME, + }, + .databit_len[0] = 48, + .no_stop_bit = false, + .decode = infrared_common_decode_pdwm, + .encode = infrared_common_encode_pdwm, + .interpret = infrared_decoder_kaseikyo_interpret, + .decode_repeat = NULL, + .encode_repeat = NULL, +}; diff --git a/lib/infrared/encoder_decoder/infrared.c b/lib/infrared/encoder_decoder/infrared.c index 71bd6bb6d..2c5ef0fff 100644 --- a/lib/infrared/encoder_decoder/infrared.c +++ b/lib/infrared/encoder_decoder/infrared.c @@ -110,6 +110,20 @@ static const InfraredEncoderDecoder infrared_encoder_decoder[] = { .free = infrared_encoder_sirc_free}, .get_protocol_spec = infrared_sirc_get_spec, }, + { + .decoder = + {.alloc = infrared_decoder_kaseikyo_alloc, + .decode = infrared_decoder_kaseikyo_decode, + .reset = infrared_decoder_kaseikyo_reset, + .check_ready = infrared_decoder_kaseikyo_check_ready, + .free = infrared_decoder_kaseikyo_free}, + .encoder = + {.alloc = infrared_encoder_kaseikyo_alloc, + .encode = infrared_encoder_kaseikyo_encode, + .reset = infrared_encoder_kaseikyo_reset, + .free = infrared_encoder_kaseikyo_free}, + .get_protocol_spec = infrared_kaseikyo_get_spec, + }, }; static int infrared_find_index_by_protocol(InfraredProtocol protocol); diff --git a/lib/infrared/encoder_decoder/infrared.h b/lib/infrared/encoder_decoder/infrared.h index 945913000..086950f1e 100644 --- a/lib/infrared/encoder_decoder/infrared.h +++ b/lib/infrared/encoder_decoder/infrared.h @@ -31,6 +31,7 @@ typedef enum { InfraredProtocolSIRC, InfraredProtocolSIRC15, InfraredProtocolSIRC20, + InfraredProtocolKaseikyo, InfraredProtocolMAX, } InfraredProtocol; diff --git a/lib/infrared/encoder_decoder/infrared_protocol_defs_i.h b/lib/infrared/encoder_decoder/infrared_protocol_defs_i.h index 575961f13..6146f7b4e 100644 --- a/lib/infrared/encoder_decoder/infrared_protocol_defs_i.h +++ b/lib/infrared/encoder_decoder/infrared_protocol_defs_i.h @@ -267,3 +267,54 @@ InfraredStatus infrared_encoder_sirc_encode_repeat( bool* level); extern const InfraredCommonProtocolSpec protocol_sirc; + +/*************************************************************************************************** +* Kaseikyo protocol description +* https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/src/ir_Kaseikyo.hpp +**************************************************************************************************** +* Preamble Preamble Pulse Distance/Width Pause Preamble Preamble +* mark space Modulation up to period repeat repeat +* mark space +* +* 3360 1665 48 bit ...130000 3456 1728 +* __________ _ _ _ _ _ _ _ _ _ _ _ _ _ ___________ +* ____ __________ _ _ _ __ __ __ _ _ __ __ _ _ ________________ ___________ +* +***************************************************************************************************/ + +#define INFRARED_KASEIKYO_UNIT 432 +#define INFRARED_KASEIKYO_PREAMBLE_MARK (8 * INFRARED_KASEIKYO_UNIT) +#define INFRARED_KASEIKYO_PREAMBLE_SPACE (4 * INFRARED_KASEIKYO_UNIT) +#define INFRARED_KASEIKYO_BIT1_MARK INFRARED_KASEIKYO_UNIT +#define INFRARED_KASEIKYO_BIT1_SPACE (3 * INFRARED_KASEIKYO_UNIT) +#define INFRARED_KASEIKYO_BIT0_MARK INFRARED_KASEIKYO_UNIT +#define INFRARED_KASEIKYO_BIT0_SPACE INFRARED_KASEIKYO_UNIT +#define INFRARED_KASEIKYO_REPEAT_PERIOD 130000 +#define INFRARED_KASEIKYO_SILENCE INFRARED_KASEIKYO_REPEAT_PERIOD +#define INFRARED_KASEIKYO_MIN_SPLIT_TIME INFRARED_KASEIKYO_REPEAT_PAUSE_MIN +#define INFRARED_KASEIKYO_REPEAT_PAUSE_MIN 4000 +#define INFRARED_KASEIKYO_REPEAT_PAUSE_MAX 150000 +#define INFRARED_KASEIKYO_REPEAT_MARK INFRARED_KASEIKYO_PREAMBLE_MARK +#define INFRARED_KASEIKYO_REPEAT_SPACE (INFRARED_KASEIKYO_REPEAT_PERIOD - 56000) +#define INFRARED_KASEIKYO_PREAMBLE_TOLERANCE 200 // us +#define INFRARED_KASEIKYO_BIT_TOLERANCE 120 // us + +void* infrared_decoder_kaseikyo_alloc(void); +void infrared_decoder_kaseikyo_reset(void* decoder); +void infrared_decoder_kaseikyo_free(void* decoder); +InfraredMessage* infrared_decoder_kaseikyo_check_ready(void* decoder); +InfraredMessage* infrared_decoder_kaseikyo_decode(void* decoder, bool level, uint32_t duration); +void* infrared_encoder_kaseikyo_alloc(void); +InfraredStatus + infrared_encoder_kaseikyo_encode(void* encoder_ptr, uint32_t* duration, bool* level); +void infrared_encoder_kaseikyo_reset(void* encoder_ptr, const InfraredMessage* message); +void infrared_encoder_kaseikyo_free(void* encoder_ptr); +bool infrared_decoder_kaseikyo_interpret(InfraredCommonDecoder* decoder); +InfraredStatus infrared_decoder_kaseikyo_decode_repeat(InfraredCommonDecoder* decoder); +InfraredStatus infrared_encoder_kaseikyo_encode_repeat( + InfraredCommonEncoder* encoder, + uint32_t* duration, + bool* level); +const InfraredProtocolSpecification* infrared_kaseikyo_get_spec(InfraredProtocol protocol); + +extern const InfraredCommonProtocolSpec protocol_kaseikyo; diff --git a/lib/infrared/encoder_decoder/kaseikyo/infrared_decoder_kaseikyo.c b/lib/infrared/encoder_decoder/kaseikyo/infrared_decoder_kaseikyo.c new file mode 100644 index 000000000..b8db81d7e --- /dev/null +++ b/lib/infrared/encoder_decoder/kaseikyo/infrared_decoder_kaseikyo.c @@ -0,0 +1,54 @@ +#include "infrared.h" +#include "infrared_protocol_defs_i.h" +#include +#include +#include +#include "../infrared_i.h" + +InfraredMessage* infrared_decoder_kaseikyo_check_ready(void* ctx) { + return infrared_common_decoder_check_ready(ctx); +} + +bool infrared_decoder_kaseikyo_interpret(InfraredCommonDecoder* decoder) { + furi_assert(decoder); + + bool result = false; + uint16_t vendor_id = ((uint16_t)(decoder->data[1]) << 8) | (uint16_t)decoder->data[0]; + uint8_t vendor_parity = decoder->data[2] & 0x0f; + uint8_t genre1 = decoder->data[2] >> 4; + uint8_t genre2 = decoder->data[3] & 0x0f; + uint16_t data = (uint16_t)(decoder->data[3] >> 4) | ((uint16_t)(decoder->data[4] & 0x3f) << 4); + uint8_t id = decoder->data[4] >> 6; + uint8_t parity = decoder->data[5]; + + uint8_t vendor_parity_check = decoder->data[0] ^ decoder->data[1]; + vendor_parity_check = (vendor_parity_check & 0xf) ^ (vendor_parity_check >> 4); + uint8_t parity_check = decoder->data[2] ^ decoder->data[3] ^ decoder->data[4]; + + if(vendor_parity == vendor_parity_check && parity == parity_check) { + decoder->message.command = (uint32_t)data; + decoder->message.address = ((uint32_t)id << 24) | ((uint32_t)vendor_id << 8) | + ((uint32_t)genre1 << 4) | (uint32_t)genre2; + decoder->message.protocol = InfraredProtocolKaseikyo; + decoder->message.repeat = false; + result = true; + } + + return result; +} + +void* infrared_decoder_kaseikyo_alloc(void) { + return infrared_common_decoder_alloc(&protocol_kaseikyo); +} + +InfraredMessage* infrared_decoder_kaseikyo_decode(void* decoder, bool level, uint32_t duration) { + return infrared_common_decode(decoder, level, duration); +} + +void infrared_decoder_kaseikyo_free(void* decoder) { + infrared_common_decoder_free(decoder); +} + +void infrared_decoder_kaseikyo_reset(void* decoder) { + infrared_common_decoder_reset(decoder); +} diff --git a/lib/infrared/encoder_decoder/kaseikyo/infrared_encoder_kaseikyo.c b/lib/infrared/encoder_decoder/kaseikyo/infrared_encoder_kaseikyo.c new file mode 100644 index 000000000..5814c7255 --- /dev/null +++ b/lib/infrared/encoder_decoder/kaseikyo/infrared_encoder_kaseikyo.c @@ -0,0 +1,45 @@ +#include +#include "common/infrared_common_i.h" +#include +#include "../infrared_i.h" +#include "infrared_protocol_defs_i.h" +#include + +void infrared_encoder_kaseikyo_reset(void* encoder_ptr, const InfraredMessage* message) { + furi_assert(encoder_ptr); + + InfraredCommonEncoder* encoder = encoder_ptr; + infrared_common_encoder_reset(encoder); + + uint32_t address = message->address; + uint16_t command = message->command; + + uint8_t id = (address >> 24) & 3; + uint16_t vendor_id = (address >> 8) & 0xffff; + uint8_t genre1 = (address >> 4) & 0xf; + uint8_t genre2 = address & 0xf; + + encoder->data[0] = (uint8_t)(vendor_id & 0xff); + encoder->data[1] = (uint8_t)(vendor_id >> 8); + uint8_t vendor_parity = encoder->data[0] ^ encoder->data[1]; + vendor_parity = (vendor_parity & 0xf) ^ (vendor_parity >> 4); + encoder->data[2] = (vendor_parity & 0xf) | (genre1 << 4); + encoder->data[3] = (genre2 & 0xf) | ((uint8_t)(command & 0xf) << 4); + encoder->data[4] = (id << 6) | (uint8_t)(command >> 4); + encoder->data[5] = encoder->data[2] ^ encoder->data[3] ^ encoder->data[4]; + + encoder->bits_to_encode = encoder->protocol->databit_len[0]; +} + +void* infrared_encoder_kaseikyo_alloc(void) { + return infrared_common_encoder_alloc(&protocol_kaseikyo); +} + +void infrared_encoder_kaseikyo_free(void* encoder_ptr) { + infrared_common_encoder_free(encoder_ptr); +} + +InfraredStatus + infrared_encoder_kaseikyo_encode(void* encoder_ptr, uint32_t* duration, bool* level) { + return infrared_common_encode(encoder_ptr, duration, level); +} diff --git a/lib/infrared/encoder_decoder/kaseikyo/infrared_kaseikyo_spec.c b/lib/infrared/encoder_decoder/kaseikyo/infrared_kaseikyo_spec.c new file mode 100644 index 000000000..87c86c7b3 --- /dev/null +++ b/lib/infrared/encoder_decoder/kaseikyo/infrared_kaseikyo_spec.c @@ -0,0 +1,17 @@ +#include "../infrared_i.h" +#include "infrared_protocol_defs_i.h" + +static const InfraredProtocolSpecification infrared_kaseikyo_protocol_specification = { + .name = "Kaseikyo", + .address_length = 26, + .command_length = 10, + .frequency = INFRARED_COMMON_CARRIER_FREQUENCY, + .duty_cycle = INFRARED_COMMON_DUTY_CYCLE, +}; + +const InfraredProtocolSpecification* infrared_kaseikyo_get_spec(InfraredProtocol protocol) { + if(protocol == InfraredProtocolKaseikyo) + return &infrared_kaseikyo_protocol_specification; + else + return NULL; +} From 2d6c2886ae429b634736fe9da6f48caef155d2e3 Mon Sep 17 00:00:00 2001 From: hedger Date: Mon, 7 Nov 2022 18:54:41 +0400 Subject: [PATCH 06/12] fbt: compile_db fixes (#1981) * fbt: forked compilation_db tool * fbt: fixes for static analysis * pvs-studio: ignoring more generic warnings * fbt: util: added extract_abs_dir * vscode: added fap-set-debug-elf-root for debug configurations --- .github/workflows/pvs_studio.yml | 2 +- .gitignore | 2 + .pvsconfig | 1 + .vscode/example/launch.json | 4 + SConstruct | 8 +- firmware.scons | 5 +- scripts/fbt/util.py | 12 +- scripts/fbt_tools/compilation_db.py | 278 ++++++++++++++++++++++++++++ scripts/fbt_tools/fbt_extapps.py | 3 +- 9 files changed, 307 insertions(+), 8 deletions(-) create mode 100644 scripts/fbt_tools/compilation_db.py diff --git a/.github/workflows/pvs_studio.yml b/.github/workflows/pvs_studio.yml index f28fad20d..9de493a44 100644 --- a/.github/workflows/pvs_studio.yml +++ b/.github/workflows/pvs_studio.yml @@ -57,7 +57,7 @@ jobs: - name: 'Generate compile_comands.json' run: | - FBT_TOOLCHAIN_PATH=/runner/_work ./fbt COMPACT=1 version_json proto_ver icons firmware_cdb dolphin_internal dolphin_blocking + FBT_TOOLCHAIN_PATH=/runner/_work ./fbt COMPACT=1 version_json proto_ver icons firmware_cdb dolphin_internal dolphin_blocking _fap_icons - name: 'Static code analysis' run: | diff --git a/.gitignore b/.gitignore index 38a31bf01..61c594ed1 100644 --- a/.gitignore +++ b/.gitignore @@ -54,3 +54,5 @@ openocd.log # PVS Studio temporary files .PVS-Studio/ PVS-Studio.log + +.gdbinit diff --git a/.pvsconfig b/.pvsconfig index d17eaa5a0..5f1ffb7cb 100644 --- a/.pvsconfig +++ b/.pvsconfig @@ -5,6 +5,7 @@ //-V:BPTREE_DEF2:779,1086,557,773,512 //-V:DICT_DEF2:779,524,776,760,1044,1001,729,590,568,747,685 //-V:ALGO_DEF:1048,747,1044 +//-V:TUPLE_DEF2:524,590,1001,760 # Non-severe malloc/null pointer deref warnings //-V::522:2,3 diff --git a/.vscode/example/launch.json b/.vscode/example/launch.json index c8b0c601d..5c46d3979 100644 --- a/.vscode/example/launch.json +++ b/.vscode/example/launch.json @@ -38,6 +38,7 @@ "postAttachCommands": [ // "compare-sections", "source debug/flipperapps.py", + "fap-set-debug-elf-root build/latest/.extapps", // "source debug/FreeRTOS/FreeRTOS.py", // "svd_load debug/STM32WB55_CM4.svd" ] @@ -59,6 +60,7 @@ "set confirm off", "set mem inaccessible-by-default off", "source debug/flipperapps.py", + "fap-set-debug-elf-root build/latest/.extapps", // "compare-sections", ] // "showDevDebugOutput": "raw", @@ -76,6 +78,7 @@ "rtos": "FreeRTOS", "postAttachCommands": [ "source debug/flipperapps.py", + "fap-set-debug-elf-root build/latest/.extapps", ] // "showDevDebugOutput": "raw", }, @@ -95,6 +98,7 @@ ], "postAttachCommands": [ "source debug/flipperapps.py", + "fap-set-debug-elf-root build/latest/.extapps", ], // "showDevDebugOutput": "raw", }, diff --git a/SConstruct b/SConstruct index 67eac3825..3ee89f646 100644 --- a/SConstruct +++ b/SConstruct @@ -200,7 +200,9 @@ firmware_debug = distenv.PhonyTarget( source=firmware_env["FW_ELF"], GDBOPTS="${GDBOPTS_BASE}", GDBREMOTE="${OPENOCD_GDB_PIPE}", - FBT_FAP_DEBUG_ELF_ROOT=firmware_env.subst("$FBT_FAP_DEBUG_ELF_ROOT"), + FBT_FAP_DEBUG_ELF_ROOT=firmware_env.subst("$FBT_FAP_DEBUG_ELF_ROOT").replace( + "\\", "/" + ), ) distenv.Depends(firmware_debug, firmware_flash) @@ -210,7 +212,9 @@ distenv.PhonyTarget( source=firmware_env["FW_ELF"], GDBOPTS="${GDBOPTS_BASE} ${GDBOPTS_BLACKMAGIC}", GDBREMOTE="${BLACKMAGIC_ADDR}", - FBT_FAP_DEBUG_ELF_ROOT=firmware_env.subst("$FBT_FAP_DEBUG_ELF_ROOT"), + FBT_FAP_DEBUG_ELF_ROOT=firmware_env.subst("$FBT_FAP_DEBUG_ELF_ROOT").replace( + "\\", "/" + ), ) # Debug alien elf diff --git a/firmware.scons b/firmware.scons index 6feb73ac3..6a9237477 100644 --- a/firmware.scons +++ b/firmware.scons @@ -264,7 +264,10 @@ fw_artifacts.extend( fwcdb = fwenv.CompilationDatabase() # without filtering, both updater & firmware commands would be generated in same file -fwenv.Replace(COMPILATIONDB_PATH_FILTER=fwenv.subst("*${FW_FLAVOR}*")) +fwenv.Replace( + COMPILATIONDB_PATH_FILTER=fwenv.subst("*${FW_FLAVOR}*"), + COMPILATIONDB_SRCPATH_FILTER="*.c*", +) AlwaysBuild(fwcdb) Precious(fwcdb) NoClean(fwcdb) diff --git a/scripts/fbt/util.py b/scripts/fbt/util.py index f5404458e..b8e9c5928 100644 --- a/scripts/fbt/util.py +++ b/scripts/fbt/util.py @@ -43,12 +43,18 @@ def single_quote(arg_list): return " ".join(f"'{arg}'" if " " in arg else str(arg) for arg in arg_list) -def extract_abs_dir_path(node): +def extract_abs_dir(node): if isinstance(node, SCons.Node.FS.EntryProxy): node = node.get() for repo_dir in node.get_all_rdirs(): if os.path.exists(repo_dir.abspath): - return repo_dir.abspath + return repo_dir - raise StopError(f"Can't find absolute path for {node.name}") + +def extract_abs_dir_path(node): + abs_dir_node = extract_abs_dir(node) + if abs_dir_node is None: + raise StopError(f"Can't find absolute path for {node.name}") + + return abs_dir_node.abspath diff --git a/scripts/fbt_tools/compilation_db.py b/scripts/fbt_tools/compilation_db.py new file mode 100644 index 000000000..17ff6aaa3 --- /dev/null +++ b/scripts/fbt_tools/compilation_db.py @@ -0,0 +1,278 @@ +""" +Implements the ability for SCons to emit a compilation database for the MongoDB project. See +http://clang.llvm.org/docs/JSONCompilationDatabase.html for details on what a compilation +database is, and why you might want one. The only user visible entry point here is +'env.CompilationDatabase'. This method takes an optional 'target' to name the file that +should hold the compilation database, otherwise, the file defaults to compile_commands.json, +which is the name that most clang tools search for by default. +""" + +# Copyright 2020 MongoDB Inc. +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +import json +import itertools +import fnmatch +import SCons + +from SCons.Tool.cxx import CXXSuffixes +from SCons.Tool.cc import CSuffixes +from SCons.Tool.asm import ASSuffixes, ASPPSuffixes + +# TODO: Is there a better way to do this than this global? Right now this exists so that the +# emitter we add can record all of the things it emits, so that the scanner for the top level +# compilation database can access the complete list, and also so that the writer has easy +# access to write all of the files. But it seems clunky. How can the emitter and the scanner +# communicate more gracefully? +__COMPILATION_DB_ENTRIES = [] + + +# We make no effort to avoid rebuilding the entries. Someday, perhaps we could and even +# integrate with the cache, but there doesn't seem to be much call for it. +class __CompilationDbNode(SCons.Node.Python.Value): + def __init__(self, value): + SCons.Node.Python.Value.__init__(self, value) + self.Decider(changed_since_last_build_node) + + +def changed_since_last_build_node(child, target, prev_ni, node): + """Dummy decider to force always building""" + return True + + +def make_emit_compilation_DB_entry(comstr): + """ + Effectively this creates a lambda function to capture: + * command line + * source + * target + :param comstr: unevaluated command line + :return: an emitter which has captured the above + """ + user_action = SCons.Action.Action(comstr) + + def emit_compilation_db_entry(target, source, env): + """ + This emitter will be added to each c/c++ object build to capture the info needed + for clang tools + :param target: target node(s) + :param source: source node(s) + :param env: Environment for use building this node + :return: target(s), source(s) + """ + + dbtarget = __CompilationDbNode(source) + + entry = env.__COMPILATIONDB_Entry( + target=dbtarget, + source=[], + __COMPILATIONDB_UOUTPUT=target, + __COMPILATIONDB_USOURCE=source, + __COMPILATIONDB_UACTION=user_action, + __COMPILATIONDB_ENV=env, + ) + + # TODO: Technically, these next two lines should not be required: it should be fine to + # cache the entries. However, they don't seem to update properly. Since they are quick + # to re-generate disable caching and sidestep this problem. + env.AlwaysBuild(entry) + env.NoCache(entry) + + __COMPILATION_DB_ENTRIES.append(dbtarget) + + return target, source + + return emit_compilation_db_entry + + +def compilation_db_entry_action(target, source, env, **kw): + """ + Create a dictionary with evaluated command line, target, source + and store that info as an attribute on the target + (Which has been stored in __COMPILATION_DB_ENTRIES array + :param target: target node(s) + :param source: source node(s) + :param env: Environment for use building this node + :param kw: + :return: None + """ + + command = env["__COMPILATIONDB_UACTION"].strfunction( + target=env["__COMPILATIONDB_UOUTPUT"], + source=env["__COMPILATIONDB_USOURCE"], + env=env["__COMPILATIONDB_ENV"], + ) + + entry = { + "directory": env.Dir("#").abspath, + "command": command, + "file": env["__COMPILATIONDB_USOURCE"][0], + "output": env["__COMPILATIONDB_UOUTPUT"][0], + } + + target[0].write(entry) + + +def write_compilation_db(target, source, env): + entries = [] + + use_abspath = env["COMPILATIONDB_USE_ABSPATH"] in [True, 1, "True", "true"] + use_path_filter = env.subst("$COMPILATIONDB_PATH_FILTER") + use_srcpath_filter = env.subst("$COMPILATIONDB_SRCPATH_FILTER") + + for s in __COMPILATION_DB_ENTRIES: + entry = s.read() + source_file = entry["file"] + output_file = entry["output"] + + if source_file.rfile().srcnode().exists(): + source_file = source_file.rfile().srcnode() + + if use_abspath: + source_file = source_file.abspath + output_file = output_file.abspath + else: + source_file = source_file.path + output_file = output_file.path + + # print("output_file, path_filter", output_file, use_path_filter) + if use_path_filter and not fnmatch.fnmatch(output_file, use_path_filter): + continue + + if use_srcpath_filter and not fnmatch.fnmatch(source_file, use_srcpath_filter): + continue + + path_entry = { + "directory": entry["directory"], + "command": entry["command"], + "file": source_file, + "output": output_file, + } + + entries.append(path_entry) + + with open(target[0].path, "w") as output_file: + json.dump( + entries, output_file, sort_keys=True, indent=4, separators=(",", ": ") + ) + + +def scan_compilation_db(node, env, path): + return __COMPILATION_DB_ENTRIES + + +def compilation_db_emitter(target, source, env): + """fix up the source/targets""" + + # Someone called env.CompilationDatabase('my_targetname.json') + if not target and len(source) == 1: + target = source + + # Default target name is compilation_db.json + if not target: + target = [ + "compile_commands.json", + ] + + # No source should have been passed. Drop it. + if source: + source = [] + + return target, source + + +def generate(env, **kwargs): + static_obj, shared_obj = SCons.Tool.createObjBuilders(env) + + env.SetDefault( + COMPILATIONDB_COMSTR=kwargs.get( + "COMPILATIONDB_COMSTR", "Building compilation database $TARGET" + ), + COMPILATIONDB_USE_ABSPATH=False, + COMPILATIONDB_PATH_FILTER="", + COMPILATIONDB_SRCPATH_FILTER="", + ) + + components_by_suffix = itertools.chain( + itertools.product( + CSuffixes, + [ + (static_obj, SCons.Defaults.StaticObjectEmitter, "$CCCOM"), + (shared_obj, SCons.Defaults.SharedObjectEmitter, "$SHCCCOM"), + ], + ), + itertools.product( + CXXSuffixes, + [ + (static_obj, SCons.Defaults.StaticObjectEmitter, "$CXXCOM"), + (shared_obj, SCons.Defaults.SharedObjectEmitter, "$SHCXXCOM"), + ], + ), + itertools.product( + ASSuffixes, + [(static_obj, SCons.Defaults.StaticObjectEmitter, "$ASCOM")], + [(shared_obj, SCons.Defaults.SharedObjectEmitter, "$ASCOM")], + ), + itertools.product( + ASPPSuffixes, + [(static_obj, SCons.Defaults.StaticObjectEmitter, "$ASPPCOM")], + [(shared_obj, SCons.Defaults.SharedObjectEmitter, "$ASPPCOM")], + ), + ) + + for entry in components_by_suffix: + suffix = entry[0] + builder, base_emitter, command = entry[1] + + # Assumes a dictionary emitter + emitter = builder.emitter.get(suffix, False) + if emitter: + # We may not have tools installed which initialize all or any of + # cxx, cc, or assembly. If not skip resetting the respective emitter. + builder.emitter[suffix] = SCons.Builder.ListEmitter( + [ + emitter, + make_emit_compilation_DB_entry(command), + ] + ) + + env.Append( + BUILDERS={ + "__COMPILATIONDB_Entry": SCons.Builder.Builder( + action=SCons.Action.Action(compilation_db_entry_action, None), + ), + "CompilationDatabase": SCons.Builder.Builder( + action=SCons.Action.Action( + write_compilation_db, "$COMPILATIONDB_COMSTR" + ), + target_scanner=SCons.Scanner.Scanner( + function=scan_compilation_db, node_class=None + ), + emitter=compilation_db_emitter, + suffix="json", + ), + } + ) + + +def exists(env): + return True diff --git a/scripts/fbt_tools/fbt_extapps.py b/scripts/fbt_tools/fbt_extapps.py index f1906191b..a4116e513 100644 --- a/scripts/fbt_tools/fbt_extapps.py +++ b/scripts/fbt_tools/fbt_extapps.py @@ -57,11 +57,12 @@ def BuildAppElf(env, app): ) if app.fap_icon_assets: - app_env.CompileIcons( + fap_icons = app_env.CompileIcons( app_env.Dir(app_work_dir), app._appdir.Dir(app.fap_icon_assets), icon_bundle_name=f"{app.appid}_icons", ) + app_env.Alias("_fap_icons", fap_icons) private_libs = [] From 4d11213494be5bb0614f6cae50b7c57c42314a31 Mon Sep 17 00:00:00 2001 From: Sergey Gavrilov Date: Tue, 8 Nov 2022 02:15:58 +1000 Subject: [PATCH 07/12] DAP-Link: show error if usb is locked (#1982) --- applications/plugins/dap_link/dap_link.c | 20 ++++++++++++++++++ .../dap_link/icons/ActiveConnection_50x64.png | Bin 0 -> 3842 bytes 2 files changed, 20 insertions(+) create mode 100644 applications/plugins/dap_link/icons/ActiveConnection_50x64.png diff --git a/applications/plugins/dap_link/dap_link.c b/applications/plugins/dap_link/dap_link.c index 58d032b91..443d77c5e 100644 --- a/applications/plugins/dap_link/dap_link.c +++ b/applications/plugins/dap_link/dap_link.c @@ -13,6 +13,8 @@ #include "dap_config.h" #include "gui/dap_gui.h" #include "usb/dap_v2_usb.h" +#include +#include "dap_link_icons.h" /***************************************************************************/ /****************************** DAP COMMON *********************************/ @@ -495,6 +497,24 @@ DapConfig* dap_app_get_config(DapApp* app) { int32_t dap_link_app(void* p) { UNUSED(p); + if(furi_hal_usb_is_locked()) { + DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); + DialogMessage* message = dialog_message_alloc(); + dialog_message_set_header(message, "Connection\nis active!", 3, 2, AlignLeft, AlignTop); + dialog_message_set_text( + message, + "Disconnect from\nPC or phone to\nuse this function.", + 3, + 30, + AlignLeft, + AlignTop); + dialog_message_set_icon(message, &I_ActiveConnection_50x64, 78, 0); + dialog_message_show(dialogs, message); + dialog_message_free(message); + furi_record_close(RECORD_DIALOGS); + return -1; + } + // alloc app DapApp* app = dap_app_alloc(); app_handle = app; diff --git a/applications/plugins/dap_link/icons/ActiveConnection_50x64.png b/applications/plugins/dap_link/icons/ActiveConnection_50x64.png new file mode 100644 index 0000000000000000000000000000000000000000..1d7686dddf8a33b724c7528ed36435514b7518b2 GIT binary patch literal 3842 zcmaJ@c|278_rI3PzAvFNMm&{e7)wmXzKj~%*ehv_!7y86EF(lkN?EdHO(@h*N=UY3 zZ7fkFOO`ANjU^;YzwvyZp6~CEU%&f$-FwgH-1qx^&gYzS@9SQ-wYK2rk>&vafZq~f zielZNtkaN-gLNhGzPAJb9uu62iLIrH35ZM~dExL_00Y=T+{c5+j+w|kQsr%QBj$9h<5`_= zvcrYX!$Oz~3!5J{Yi6=$wz_EDf)T3YU<@oW!^@U{0@_p^+Qfji z{lF9ZXP!JjG63Ldp~hg~AwMwx-BN!KFi@N{EC~$c9Vq4kZm|LBM=TDr8@>e2J4T|E z*&7;xT)H7xm9wFgEyA?|YQY{+y9Wr2b4d_1JP$;q8!LAJARTtVV==bq+y8?q5g)7dgSlylFvP4D0V9$wxB1&@2RYM*2Ee`$=9#$v)`Zg50U)VMn4d_fO_zVCwU-q9ZN|r>nZ~=g6Zsf5iM*H|)iP0MbvR)mm zX^><`?=>~#JKUfrWW0AW;sDRR{i#M$4h^sY&gV}!q;rKc#)ZmXsq661jES6$oFhx_ zJ-Xh>mnd2e79;EtHvsP9l1z`|1fvm}w<8KbvoT_J;N~_;0ei8rZ=xGQ zep!VgrhDtG;m?GjHW2j2){Pnq_2kH>b{y~70}Njj$x7d7$@TA{Y6`kVq~`hcNS7ai zM^xk$_MG|>Kn22X#9<o9w4gy=lixvN5r_{#|i7A{B^lOlzA`ErqJE@$p5SJfN;0w)#Olq-aYY%~RXz{(O_ z%;}2X6~bj973UHN?Vl#O zo<`6?X^E8yf(bUaH``xNR*J!zV(3vS=!YEM5?|Ykp^Tw_FKxV1c+#^>GnWeo=>-GDxZ+2$( z%J(2X{%HOytq6}JQhrhwr3&{~Nf`v8?m_r4=|hvevTZ0%U6c;Xw8 z6j+K=N_fi5LkCBHM}t1vLtckRj)ITQIfXqicYJ31xtROC#G}6AgN`qYwM)BDL8y4! zZaeq~S?sF6{&Z&Ub^0AAeJ7gJs?!I$W&hbZ9FmdU6nD#^1-PDhDcgqnxs9U@J1o=ZU`e~ zO8Q%M@AG%7`I#>>hf6*Z-j8&^o5LP$TB&Brw7b2AGmXA4uDeWJ==hvnm|57kk}v}~ z7kJL~+-B_|n`c>yIsIycwxOmoW3`Nn=VAJA?9Z-Q4*eE=_PZf>uhl)M1CPS%J z)5G^|{Z0d8l7FF1nj*R4APEU;{bZQNa~6 zW`U2XlEq1-OKyaT9X$qpsQT5e+@5-Yx~|+$pLE^yu8muYFTVNW#E@?VCD5Dhi$~!x z^O;o}ep6z1f z1nIeIxh90_MBNcddulLs1!Qas*>5vdNVGaAx_mV=%EqiN?^d2&S!LBpz1!2-PAO|T zBPYU4e)>e)mliGPwdO?V@dbnVUhr2K~e%8)od3fYrijw-bkkU&C;l!DLfKNDPqs70K9uQBSi z^L0a>_p(H2ZNd}Vswd9|s)AjY#=!MvFD2w-?InX$)!k6lp24`q-Y|v_<7w))?Su=; zaoLwPyc~zR(tH2DiPB|f&6MKgb_TKZ`{@@Lade8OBhxpn?~K!>W0EQEbTYlD^v4tP zs_6-5Yxlm;RT^P%@YBi4Hw$x!xq>+&eciSG@yS|WqrSJ%i~J=rOSh(E+zBT?QSXKL zuEuqicfRT5&_Zi1oav~b4=vx*&R+}3zU0Pm+AeuiS@%(Ku)lsJ=;DgNm4o6ZJ~5N$ zYo03wJNwm|g{=~Mzg-@Qm-djUuAdGcsj>*NY0inic>m(QH8bX%FO`HJeq3Mwl$(Ik zzI6xzBTr>UkOngsGJ>9yPahL#G@5$#*XV=Li=S=3-0ONh{JL{A{Zi#B*BpYT)C;Q* zpsVB)a^d%CnO|<^XCFLw(4wyLS2$DsGbW%_E8aOLH~R>DX=Czo(&s|Y!klbt1Ni&& zVcI%!E8Wk{&aKwlq&vqzlKKr<>Av2+@@XdCZLx;@9lY)_q)>UP1YQca2q$lkBOae2 z&0*IW3(k6_)bCbvCwiFgF8%av==1;Z{W#xnzWcSSAX9+*TFy@LuXoqRdo4OF`sB^! zZ^dWJ%F6Id*DiZ@C5;z8Efnp36YlhjHs}9nW^{XE^HjIX*1#g~Mr?O|DXn;g!hBTx z7}hG^DqGVVN>R;RsP-f;Y7m-&1&lmN9$1hi0qu=NVbPwn3+-4v0N^-+b8w-$SRr8;5deQ<~n3f4Zv+5r>d zhtc%}8|Z`df?+HH0+xyf1rzW@e^@Xa{I@QQW$(HnV9?(XsvjKupQK!@Y(XX@3Kn!+ z6{>|JenB{I4w0|DQ^+Y6b~LlOgJ=YP-Ao4YacQ|DgoJzi59d z3j5!D|4(6m2O1d*L1Fz#0Tc|YcV6~A`jDt3e;*PV1l3U0 z1Rb$LV{pV>&(XgrR#q@eqCXW)#9%E=;b4}CDh}rf(>5`OnnI83nw#sGsH>Zq7@2Dr znVK4znQH22Le)*pe{)Sqm;eHnNd3+A{4dw&kKEmXAdp#+O|cYQAlB2ILLz|v-Zc#O z=Uk5eQSTqF=bv-Y`6Cy?N(Qpq+yB+;-!9ew?VA4%FKhAd_+yEznWwOZTSahmj`d>f zwM9CZ{rdHbWjZ##3kLu;K}%C3hv32CR3nMkATHDNP50`@*G0JbZdhsG&#ag}kt-x* zbi6EjpiYUf^utT&I-ggwTw)8K9Wu<#NjKCWviOGnxNwI<3!$qd0;#|wTaC0<=DJ&4 z-o}fdK$^-X*DQay#`Ty87;GIAW(;r{nhujLM{vr&Ry`!wB1~-L(Uq&iu{k>R-V8os2N6zY@I0ry5ZRP(0CFwaUqp$rweNmLEX}M Date: Tue, 8 Nov 2022 19:56:49 +0300 Subject: [PATCH 08/12] Update toolchain to version 19. Update codeowners. Fix amap analyze. (#1986) * Up toolchain to 19 * Fix amap_analyse.yml * Github: update codeowners Co-authored-by: Aleksandr Kutuzov --- .github/CODEOWNERS | 9 +++++++-- .github/workflows/amap_analyse.yml | 2 +- scripts/toolchain/fbtenv.cmd | 2 +- scripts/toolchain/fbtenv.sh | 2 +- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index c9b8ff3f5..6b77482c6 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -18,7 +18,7 @@ /applications/main/gpio/ @skotopes @DrZlo13 @hedger @nminaylov /applications/main/ibutton/ @skotopes @DrZlo13 @hedger @gsurkov /applications/main/infrared/ @skotopes @DrZlo13 @hedger @gsurkov -/applications/main/nfc/ @skotopes @DrZlo13 @hedger @gornekich +/applications/main/nfc/ @skotopes @DrZlo13 @hedger @gornekich @Astrrra /applications/main/subghz/ @skotopes @DrZlo13 @hedger @Skorpionm /applications/main/u2f/ @skotopes @DrZlo13 @hedger @nminaylov @@ -40,6 +40,8 @@ /applications/system/storage_move_to_sd/ @skotopes @DrZlo13 @hedger @nminaylov +/applications/debug/unit_tests/ @skotopes @DrZlo13 @hedger @nminaylov @gornekich @Astrrra @gsurkov @Skorpionm + # Documentation /documentation/ @skotopes @DrZlo13 @hedger @drunkbatya /scripts/toolchain/ @skotopes @DrZlo13 @hedger @drunkbatya @@ -54,6 +56,9 @@ /lib/mbedtls/ @skotopes @DrZlo13 @hedger @nminaylov /lib/micro-ecc/ @skotopes @DrZlo13 @hedger @nminaylov /lib/nanopb/ @skotopes @DrZlo13 @hedger @nminaylov -/lib/nfc/ @skotopes @DrZlo13 @hedger @gornekich +/lib/nfc/ @skotopes @DrZlo13 @hedger @gornekich @Astrrra /lib/one_wire/ @skotopes @DrZlo13 @hedger @gsurkov /lib/subghz/ @skotopes @DrZlo13 @hedger @Skorpionm + +# CI/CD +/.github/workflows/ @skotopes @DrZlo13 @hedger @drunkbatya diff --git a/.github/workflows/amap_analyse.yml b/.github/workflows/amap_analyse.yml index a50c5436f..cfb1eab14 100644 --- a/.github/workflows/amap_analyse.yml +++ b/.github/workflows/amap_analyse.yml @@ -91,7 +91,7 @@ jobs: export RODATA_SIZE="$(get_size ".rodata")" export DATA_SIZE="$(get_size ".data")" export FREE_FLASH_SIZE="$(get_size ".free_flash")" - python3 -m pip install mariadb + python3 -m pip install mariadb==1.1.4 python3 scripts/amap_mariadb_insert.py \ ${{ secrets.AMAP_MARIADB_USER }} \ ${{ secrets.AMAP_MARIADB_PASSWORD }} \ diff --git a/scripts/toolchain/fbtenv.cmd b/scripts/toolchain/fbtenv.cmd index 6e87bf95a..44a2551f7 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=17" +set "FLIPPER_TOOLCHAIN_VERSION=19" if [%FBT_TOOLCHAIN_ROOT%] == [] ( set "FBT_TOOLCHAIN_ROOT=%FBT_ROOT%\toolchain\x86_64-windows" diff --git a/scripts/toolchain/fbtenv.sh b/scripts/toolchain/fbtenv.sh index d3fdb8cea..852e00394 100755 --- a/scripts/toolchain/fbtenv.sh +++ b/scripts/toolchain/fbtenv.sh @@ -5,7 +5,7 @@ # public variables DEFAULT_SCRIPT_PATH="$(pwd -P)"; SCRIPT_PATH="${SCRIPT_PATH:-$DEFAULT_SCRIPT_PATH}"; -FBT_TOOLCHAIN_VERSION="${FBT_TOOLCHAIN_VERSION:-"17"}"; +FBT_TOOLCHAIN_VERSION="${FBT_TOOLCHAIN_VERSION:-"19"}"; FBT_TOOLCHAIN_PATH="${FBT_TOOLCHAIN_PATH:-$SCRIPT_PATH}"; fbtenv_show_usage() From 328d049b6a56ed3ff9e92ad47a3778381b8db64e Mon Sep 17 00:00:00 2001 From: Samuel Stauffer Date: Tue, 8 Nov 2022 09:07:55 -0800 Subject: [PATCH 09/12] Add Acurite 609TXC protocol to weather station (#1987) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- .../protocols/acurite_609txc.c | 247 ++++++++++++++++++ .../protocols/acurite_609txc.h | 79 ++++++ .../protocols/protocol_items.c | 1 + .../protocols/protocol_items.h | 1 + 4 files changed, 328 insertions(+) create mode 100644 applications/plugins/weather_station/protocols/acurite_609txc.c create mode 100644 applications/plugins/weather_station/protocols/acurite_609txc.h diff --git a/applications/plugins/weather_station/protocols/acurite_609txc.c b/applications/plugins/weather_station/protocols/acurite_609txc.c new file mode 100644 index 000000000..aeb0785eb --- /dev/null +++ b/applications/plugins/weather_station/protocols/acurite_609txc.c @@ -0,0 +1,247 @@ +#include "acurite_609txc.h" + +#define TAG "WSProtocolAcurite_609TXC" + +/* + * Help + * https://github.com/merbanan/rtl_433/blob/5bef4e43133ac4c0e2d18d36f87c52b4f9458453/src/devices/acurite.c#L216 + * + * 0000 1111 | 0011 0000 | 0101 1100 | 0000 0000 | 1110 0111 + * iiii iiii | buuu tttt | tttt tttt | hhhh hhhh | cccc cccc + * - i: identification; changes on battery switch + * - c: checksum (sum of previous by bytes) + * - u: unknown + * - b: battery low; flag to indicate low battery voltage + * - t: temperature; in °C * 10, 12 bit with complement + * - h: humidity + * + */ + +static const SubGhzBlockConst ws_protocol_acurite_609txc_const = { + .te_short = 500, + .te_long = 1000, + .te_delta = 150, + .min_count_bit_for_found = 40, +}; + +struct WSProtocolDecoderAcurite_609TXC { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + WSBlockGeneric generic; +}; + +struct WSProtocolEncoderAcurite_609TXC { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + WSBlockGeneric generic; +}; + +typedef enum { + Acurite_609TXCDecoderStepReset = 0, + Acurite_609TXCDecoderStepSaveDuration, + Acurite_609TXCDecoderStepCheckDuration, +} Acurite_609TXCDecoderStep; + +const SubGhzProtocolDecoder ws_protocol_acurite_609txc_decoder = { + .alloc = ws_protocol_decoder_acurite_609txc_alloc, + .free = ws_protocol_decoder_acurite_609txc_free, + + .feed = ws_protocol_decoder_acurite_609txc_feed, + .reset = ws_protocol_decoder_acurite_609txc_reset, + + .get_hash_data = ws_protocol_decoder_acurite_609txc_get_hash_data, + .serialize = ws_protocol_decoder_acurite_609txc_serialize, + .deserialize = ws_protocol_decoder_acurite_609txc_deserialize, + .get_string = ws_protocol_decoder_acurite_609txc_get_string, +}; + +const SubGhzProtocolEncoder ws_protocol_acurite_609txc_encoder = { + .alloc = NULL, + .free = NULL, + + .deserialize = NULL, + .stop = NULL, + .yield = NULL, +}; + +const SubGhzProtocol ws_protocol_acurite_609txc = { + .name = WS_PROTOCOL_ACURITE_609TXC_NAME, + .type = SubGhzProtocolWeatherStation, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | + SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, + + .decoder = &ws_protocol_acurite_609txc_decoder, + .encoder = &ws_protocol_acurite_609txc_encoder, +}; + +void* ws_protocol_decoder_acurite_609txc_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + WSProtocolDecoderAcurite_609TXC* instance = malloc(sizeof(WSProtocolDecoderAcurite_609TXC)); + instance->base.protocol = &ws_protocol_acurite_609txc; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void ws_protocol_decoder_acurite_609txc_free(void* context) { + furi_assert(context); + WSProtocolDecoderAcurite_609TXC* instance = context; + free(instance); +} + +void ws_protocol_decoder_acurite_609txc_reset(void* context) { + furi_assert(context); + WSProtocolDecoderAcurite_609TXC* instance = context; + instance->decoder.parser_step = Acurite_609TXCDecoderStepReset; +} + +static bool ws_protocol_acurite_609txc_check(WSProtocolDecoderAcurite_609TXC* instance) { + if(!instance->decoder.decode_data) return false; + uint8_t crc = (uint8_t)(instance->decoder.decode_data >> 32) + + (uint8_t)(instance->decoder.decode_data >> 24) + + (uint8_t)(instance->decoder.decode_data >> 16) + + (uint8_t)(instance->decoder.decode_data >> 8); + return (crc == (instance->decoder.decode_data & 0xFF)); +} + +/** + * Analysis of received data + * @param instance Pointer to a WSBlockGeneric* instance + */ +static void ws_protocol_acurite_609txc_remote_controller(WSBlockGeneric* instance) { + instance->id = (instance->data >> 32) & 0xFF; + instance->battery_low = (instance->data >> 31) & 1; + + instance->channel = WS_NO_CHANNEL; + + // Temperature in Celsius is encoded as a 12 bit integer value + // multiplied by 10 using the 4th - 6th nybbles (bytes 1 & 2) + // negative values are recovered by sign extend from int16_t. + int16_t temp_raw = + (int16_t)(((instance->data >> 12) & 0xf000) | ((instance->data >> 16) << 4)); + instance->temp = (temp_raw >> 4) * 0.1f; + instance->humidity = (instance->data >> 8) & 0xff; + instance->btn = WS_NO_BTN; +} + +void ws_protocol_decoder_acurite_609txc_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + WSProtocolDecoderAcurite_609TXC* instance = context; + + switch(instance->decoder.parser_step) { + case Acurite_609TXCDecoderStepReset: + if((!level) && (DURATION_DIFF(duration, ws_protocol_acurite_609txc_const.te_short * 17) < + ws_protocol_acurite_609txc_const.te_delta * 8)) { + //Found syncPrefix + instance->decoder.parser_step = Acurite_609TXCDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } + break; + + case Acurite_609TXCDecoderStepSaveDuration: + if(level) { + instance->decoder.te_last = duration; + instance->decoder.parser_step = Acurite_609TXCDecoderStepCheckDuration; + } else { + instance->decoder.parser_step = Acurite_609TXCDecoderStepReset; + } + break; + + case Acurite_609TXCDecoderStepCheckDuration: + if(!level) { + if(DURATION_DIFF(instance->decoder.te_last, ws_protocol_acurite_609txc_const.te_short) < + ws_protocol_acurite_609txc_const.te_delta) { + if((DURATION_DIFF(duration, ws_protocol_acurite_609txc_const.te_short) < + ws_protocol_acurite_609txc_const.te_delta) || + (duration > ws_protocol_acurite_609txc_const.te_long * 3)) { + //Found syncPostfix + instance->decoder.parser_step = Acurite_609TXCDecoderStepReset; + if((instance->decoder.decode_count_bit == + ws_protocol_acurite_609txc_const.min_count_bit_for_found) && + ws_protocol_acurite_609txc_check(instance)) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + ws_protocol_acurite_609txc_remote_controller(&instance->generic); + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } else if( + DURATION_DIFF(duration, ws_protocol_acurite_609txc_const.te_long) < + ws_protocol_acurite_609txc_const.te_delta * 2) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = Acurite_609TXCDecoderStepSaveDuration; + } else if( + DURATION_DIFF(duration, ws_protocol_acurite_609txc_const.te_long * 2) < + ws_protocol_acurite_609txc_const.te_delta * 4) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = Acurite_609TXCDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = Acurite_609TXCDecoderStepReset; + } + } else { + instance->decoder.parser_step = Acurite_609TXCDecoderStepReset; + } + } else { + instance->decoder.parser_step = Acurite_609TXCDecoderStepReset; + } + break; + } +} + +uint8_t ws_protocol_decoder_acurite_609txc_get_hash_data(void* context) { + furi_assert(context); + WSProtocolDecoderAcurite_609TXC* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool ws_protocol_decoder_acurite_609txc_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + WSProtocolDecoderAcurite_609TXC* instance = context; + return ws_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool ws_protocol_decoder_acurite_609txc_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + WSProtocolDecoderAcurite_609TXC* instance = context; + bool ret = false; + do { + if(!ws_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + ws_protocol_acurite_609txc_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; +} + +void ws_protocol_decoder_acurite_609txc_get_string(void* context, FuriString* output) { + furi_assert(context); + WSProtocolDecoderAcurite_609TXC* instance = context; + furi_string_printf( + output, + "%s %dbit\r\n" + "Key:0x%lX%08lX\r\n" + "Sn:0x%lX Ch:%d Bat:%d\r\n" + "Temp:%3.1f C Hum:%d%%", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data >> 40), + (uint32_t)(instance->generic.data), + instance->generic.id, + instance->generic.channel, + instance->generic.battery_low, + (double)instance->generic.temp, + instance->generic.humidity); +} diff --git a/applications/plugins/weather_station/protocols/acurite_609txc.h b/applications/plugins/weather_station/protocols/acurite_609txc.h new file mode 100644 index 000000000..f87c20e9b --- /dev/null +++ b/applications/plugins/weather_station/protocols/acurite_609txc.h @@ -0,0 +1,79 @@ +#pragma once + +#include + +#include +#include +#include +#include "ws_generic.h" +#include + +#define WS_PROTOCOL_ACURITE_609TXC_NAME "Acurite-609TXC" + +typedef struct WSProtocolDecoderAcurite_609TXC WSProtocolDecoderAcurite_609TXC; +typedef struct WSProtocolEncoderAcurite_609TXC WSProtocolEncoderAcurite_609TXC; + +extern const SubGhzProtocolDecoder ws_protocol_acurite_609txc_decoder; +extern const SubGhzProtocolEncoder ws_protocol_acurite_609txc_encoder; +extern const SubGhzProtocol ws_protocol_acurite_609txc; + +/** + * Allocate WSProtocolDecoderAcurite_609TXC. + * @param environment Pointer to a SubGhzEnvironment instance + * @return WSProtocolDecoderAcurite_609TXC* pointer to a WSProtocolDecoderAcurite_609TXC instance + */ +void* ws_protocol_decoder_acurite_609txc_alloc(SubGhzEnvironment* environment); + +/** + * Free WSProtocolDecoderAcurite_609TXC. + * @param context Pointer to a WSProtocolDecoderAcurite_609TXC instance + */ +void ws_protocol_decoder_acurite_609txc_free(void* context); + +/** + * Reset decoder WSProtocolDecoderAcurite_609TXC. + * @param context Pointer to a WSProtocolDecoderAcurite_609TXC instance + */ +void ws_protocol_decoder_acurite_609txc_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a WSProtocolDecoderAcurite_609TXC instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void ws_protocol_decoder_acurite_609txc_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a WSProtocolDecoderAcurite_609TXC instance + * @return hash Hash sum + */ +uint8_t ws_protocol_decoder_acurite_609txc_get_hash_data(void* context); + +/** + * Serialize data WSProtocolDecoderAcurite_609TXC. + * @param context Pointer to a WSProtocolDecoderAcurite_609TXC instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool ws_protocol_decoder_acurite_609txc_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data WSProtocolDecoderAcurite_609TXC. + * @param context Pointer to a WSProtocolDecoderAcurite_609TXC instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool ws_protocol_decoder_acurite_609txc_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a WSProtocolDecoderAcurite_609TXC instance + * @param output Resulting text + */ +void ws_protocol_decoder_acurite_609txc_get_string(void* context, FuriString* output); diff --git a/applications/plugins/weather_station/protocols/protocol_items.c b/applications/plugins/weather_station/protocols/protocol_items.c index d7f6458ab..d2b20e51a 100644 --- a/applications/plugins/weather_station/protocols/protocol_items.c +++ b/applications/plugins/weather_station/protocols/protocol_items.c @@ -6,6 +6,7 @@ const SubGhzProtocol* weather_station_protocol_registry_items[] = { &ws_protocol_nexus_th, &ws_protocol_gt_wt_03, &ws_protocol_acurite_606tx, + &ws_protocol_acurite_609txc, &ws_protocol_lacrosse_tx141thbv2, &ws_protocol_oregon2, &ws_protocol_acurite_592txr, diff --git a/applications/plugins/weather_station/protocols/protocol_items.h b/applications/plugins/weather_station/protocols/protocol_items.h index 76c085ab4..45b297e10 100644 --- a/applications/plugins/weather_station/protocols/protocol_items.h +++ b/applications/plugins/weather_station/protocols/protocol_items.h @@ -6,6 +6,7 @@ #include "nexus_th.h" #include "gt_wt_03.h" #include "acurite_606tx.h" +#include "acurite_609txc.h" #include "lacrosse_tx141thbv2.h" #include "oregon2.h" #include "acurite_592txr.h" From 9f0aef330efc48028d74009f00e81ec16d4656d2 Mon Sep 17 00:00:00 2001 From: Georgii Surkov <37121527+gsurkov@users.noreply.github.com> Date: Tue, 8 Nov 2022 20:38:28 +0300 Subject: [PATCH 10/12] [FL-2956] Initial unit test docs (#1984) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- documentation/UnitTests.md | 49 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 documentation/UnitTests.md diff --git a/documentation/UnitTests.md b/documentation/UnitTests.md new file mode 100644 index 000000000..3f56a9a90 --- /dev/null +++ b/documentation/UnitTests.md @@ -0,0 +1,49 @@ +# Unit tests +## Intro +Unit tests are special pieces of code that apply known inputs to the feature code and check the results to see if they were correct. +They are crucial for writing robust, bug-free code. + +Flipper Zero firmware includes a separate application called [unit_tests](/applications/debug/unit_tests). +It is run directly on the Flipper Zero in order to employ its hardware features and to rule out any platform-related differences. + +When contributing code to the Flipper Zero firmware, it is highly desirable to supply unit tests along with the proposed features. +Running existing unit tests is useful to ensure that the new code doesn't introduce any regressions. + +## Running unit tests +In order to run the unit tests, follow these steps: +1. Compile the firmware with the tests enabled: `./fbt FIRMWARE_APP_SET=unit_tests`. +2. Flash the firmware using your preferred method. +3. Copy the [assets/unit_tests](assets/unit_tests) folder to the root your Flipper Zero's SD card. +4. Launch the CLI session and run the `unit_tests` command. + +**NOTE:** To run a particular test (and skip all others), specify its name as the command argument. +See [test_index.c](applications/debug/unit_tests/test_index.c) for the complete list of test names. + +## Adding unit tests +### General +#### Entry point +The common entry point for all tests it the [unit_tests](applications/debug/unit_tests) application. Test-specific code is placed into an arbitrarily named subdirectory and is then called from the [test_index.c](applications/debug/unit_tests/test_index.c) source file. +#### Test assets +Some unit tests require external data in order to function. These files (commonly called assets) reside in the [assets/unit_tests](/assets/unit_tests) directory in their respective subdirectories. Asset files can be of any type (plain text, FlipperFormat(FFF), binary etc). +### Application-specific +#### Infrared +Each infrared protocol has a corresponding set of unit tests, so it makes sense to implement one when adding support for a new protocol. +In order to add unit tests for your protocol, follow these steps: +1. Create a file named `test_.irtest` in the [assets](assets/unit_tests/infrared) directory. +2. Fill it with the test data (more on it below). +3. Add the test code to [infrared_test.c](applications/debug/unit_tests/infrared/infrared_test.c). +4. Update the [assets](assets/unit_tests/infrared) on your Flipper Zero and run the tests to see if they pass. + +##### Test data format +Each unit test has 3 sections: +1. `decoder` - takes in raw signal and outputs decoded messages. +2. `encoder` - takes in decoded messages and outputs raw signal. +3. `encoder_decoder` - takes in decoded messages, turns them into raw signal and then decodes again. + +Infrared test asset files have an `.irtest` extension and are regular `.ir` files with a few additions. +Decoder input data has signal names `decoder_input_N`, where N is a test sequence number. Expected data goes under the name `decoder_expected_N`. When testing the encoder these two are switched. + +Decoded data is represented in arrays (since a single raw signal may decode to several messages). If there is only one signal, then it has to be an array of size 1. Use the existing files as syntax examples. + +##### Getting raw signals +Recording raw IR signals is possible using Flipper Zero. Launch the CLI session, run `ir rx raw`, then point the remote towards Flipper's receiver and send the signals. The raw signal data will be printed to the console in a convenient format. From c89e5e11a43589649278050b94fd7c2ef5f3b824 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Thu, 10 Nov 2022 02:33:09 +0900 Subject: [PATCH 11/12] Furi: show thread allocation balance for child threads (#1992) --- applications/services/loader/loader.c | 6 +----- furi/core/thread.c | 12 ++++++++++-- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/applications/services/loader/loader.c b/applications/services/loader/loader.c index 62dbad95f..712576e14 100644 --- a/applications/services/loader/loader.c +++ b/applications/services/loader/loader.c @@ -273,11 +273,7 @@ static void loader_thread_state_callback(FuriThreadState thread_state, void* con furi_hal_power_insomnia_enter(); } } else if(thread_state == FuriThreadStateStopped) { - FURI_LOG_I( - TAG, - "Application thread stopped. Free heap: %d. Thread allocation balance: %d.", - memmgr_get_free_heap(), - furi_thread_get_heap_size(instance->application_thread)); + FURI_LOG_I(TAG, "Application stopped. Free heap: %d", memmgr_get_free_heap()); if(loader_instance->application_arguments) { free(loader_instance->application_arguments); diff --git a/furi/core/thread.c b/furi/core/thread.c index 157e022e9..8320a47e8 100644 --- a/furi/core/thread.c +++ b/furi/core/thread.c @@ -12,6 +12,8 @@ #include #include +#define TAG "FuriThread" + #define THREAD_NOTIFY_INDEX 1 // Index 0 is used for stream buffers typedef struct FuriThreadStdout FuriThreadStdout; @@ -82,6 +84,12 @@ static void furi_thread_body(void* context) { if(thread->heap_trace_enabled == true) { furi_delay_ms(33); thread->heap_size = memmgr_heap_get_thread_memory((FuriThreadId)task_handle); + furi_log_print_format( + thread->heap_size ? FuriLogLevelError : FuriLogLevelInfo, + TAG, + "%s allocation balance: %d", + thread->name ? thread->name : "Thread", + thread->heap_size); memmgr_heap_disable_thread_trace((FuriThreadId)task_handle); } @@ -89,8 +97,8 @@ static void furi_thread_body(void* context) { if(thread->is_service) { FURI_LOG_E( - "Service", - "%s thread exited. Thread memory cannot be reclaimed.", + TAG, + "%s service thread exited. Thread memory cannot be reclaimed.", thread->name ? thread->name : ""); } From 3985b456c39c644314282ad426cb757c770236b8 Mon Sep 17 00:00:00 2001 From: gornekich Date: Wed, 9 Nov 2022 22:12:55 +0400 Subject: [PATCH 12/12] NFC: fix crash on MFC read (#1993) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * nfc: fix nfc_worker_stop logic * nfc: fix stop order Co-authored-by: あく --- lib/nfc/nfc_worker.c | 10 +++++----- lib/nfc/nfc_worker.h | 1 - 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index e1e379a06..5ce543636 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -70,12 +70,12 @@ void nfc_worker_start( void nfc_worker_stop(NfcWorker* nfc_worker) { furi_assert(nfc_worker); - if(nfc_worker->state == NfcWorkerStateBroken || nfc_worker->state == NfcWorkerStateReady) { - return; + furi_assert(nfc_worker->thread); + if(furi_thread_get_state(nfc_worker->thread) != FuriThreadStateStopped) { + furi_hal_nfc_stop(); + nfc_worker_change_state(nfc_worker, NfcWorkerStateStop); + furi_thread_join(nfc_worker->thread); } - furi_hal_nfc_stop(); - nfc_worker_change_state(nfc_worker, NfcWorkerStateStop); - furi_thread_join(nfc_worker->thread); } void nfc_worker_change_state(NfcWorker* nfc_worker, NfcWorkerState state) { diff --git a/lib/nfc/nfc_worker.h b/lib/nfc/nfc_worker.h index ce3a18241..ddee34c95 100644 --- a/lib/nfc/nfc_worker.h +++ b/lib/nfc/nfc_worker.h @@ -7,7 +7,6 @@ typedef struct NfcWorker NfcWorker; typedef enum { // Init states NfcWorkerStateNone, - NfcWorkerStateBroken, NfcWorkerStateReady, // Main worker states NfcWorkerStateRead,