mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-04-24 03:29:57 -07:00
Merge branch 'ul-dev' into xfw-dev
This commit is contained in:
4
.gitmodules
vendored
4
.gitmodules
vendored
@@ -28,6 +28,6 @@
|
||||
[submodule "lib/cxxheaderparser"]
|
||||
path = lib/cxxheaderparser
|
||||
url = https://github.com/robotpy/cxxheaderparser.git
|
||||
[submodule "applications/plugins/dap_link/lib/free-dap"]
|
||||
path = applications/plugins/dap_link/lib/free-dap
|
||||
[submodule "applications/external/dap_link/lib/free-dap"]
|
||||
path = applications/external/dap_link/lib/free-dap
|
||||
url = https://github.com/ataradov/free-dap.git
|
||||
|
||||
@@ -1 +1 @@
|
||||
--ignore-ccache -C gccarm --rules-config .pvsconfig -e lib/fatfs -e lib/fnv1a-hash -e lib/FreeRTOS-Kernel -e lib/heatshrink -e lib/libusb_stm32 -e lib/littlefs -e lib/mbedtls -e lib/micro-ecc -e lib/microtar -e lib/mlib -e lib/qrcode -e lib/ST25RFAL002 -e lib/STM32CubeWB -e lib/u8g2 -e lib/nanopb -e */arm-none-eabi/* -e applications/plugins/dap_link/lib/free-dap
|
||||
--ignore-ccache -C gccarm --rules-config .pvsconfig -e lib/fatfs -e lib/fnv1a-hash -e lib/FreeRTOS-Kernel -e lib/heatshrink -e lib/libusb_stm32 -e lib/littlefs -e lib/mbedtls -e lib/micro-ecc -e lib/microtar -e lib/mlib -e lib/qrcode -e lib/ST25RFAL002 -e lib/STM32CubeWB -e lib/u8g2 -e lib/nanopb -e */arm-none-eabi/* -e applications/external/dap_link/lib/free-dap
|
||||
|
||||
27
SConstruct
27
SConstruct
@@ -139,34 +139,33 @@ if GetOption("fullenv") or any(
|
||||
basic_dist = distenv.DistCommand("fw_dist", distenv["DIST_DEPENDS"])
|
||||
distenv.Default(basic_dist)
|
||||
|
||||
dist_dir = distenv.GetProjectDirName()
|
||||
dist_dir_name = distenv.GetProjectDirName()
|
||||
dist_dir = distenv.Dir(f"#/dist/{dist_dir_name}")
|
||||
external_apps_artifacts = firmware_env["FW_EXTAPPS"]
|
||||
external_app_list = external_apps_artifacts.application_map.values()
|
||||
|
||||
fap_dist = [
|
||||
distenv.Install(
|
||||
distenv.Dir(f"#/dist/{dist_dir}/apps/debug_elf"),
|
||||
list(
|
||||
app_artifact.debug
|
||||
for app_artifact in firmware_env["FW_EXTAPPS"].applications.values()
|
||||
),
|
||||
dist_dir.Dir("debug_elf"),
|
||||
list(app_artifact.debug for app_artifact in external_app_list),
|
||||
),
|
||||
*(
|
||||
distenv.Install(
|
||||
f"#/dist/{dist_dir}/apps/{app_artifact.app.fap_category}",
|
||||
app_artifact.compact[0],
|
||||
dist_dir.File(dist_entry[1]).dir,
|
||||
app_artifact.compact,
|
||||
)
|
||||
for app_artifact in firmware_env["FW_EXTAPPS"].applications.values()
|
||||
for app_artifact in external_app_list
|
||||
for dist_entry in app_artifact.dist_entries
|
||||
),
|
||||
]
|
||||
Depends(
|
||||
fap_dist,
|
||||
list(
|
||||
app_artifact.validator
|
||||
for app_artifact in firmware_env["FW_EXTAPPS"].applications.values()
|
||||
),
|
||||
list(app_artifact.validator for app_artifact in external_app_list),
|
||||
)
|
||||
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"], external_apps_artifacts.resources_dist)
|
||||
|
||||
# Copy all faps to device
|
||||
|
||||
|
||||
@@ -36,15 +36,20 @@ Applications for main Flipper menu.
|
||||
- `u2f` - U2F Application
|
||||
|
||||
|
||||
## plugins
|
||||
## External
|
||||
|
||||
Extra apps for Plugins & App Loader menus.
|
||||
External applications deployed to SD Card
|
||||
|
||||
- `bt_hid_app` - BT Remote controller
|
||||
- `clock` - Clock application
|
||||
- `dap_link` - DAP Link OnChip debugger
|
||||
- `hid_app` - USB/BT Remote controller
|
||||
- `music_player` - Music player app (demo)
|
||||
- `picopass` - Picopass tool
|
||||
- `nfc_magic` - NFC MFC Magic card application
|
||||
- `picopass` - Picopass reader / writer
|
||||
- `signal_generator` - Signal generator app: PWM and clock generator
|
||||
- `snake_game` - Snake game application
|
||||
|
||||
- `spi_mem_manager` - SPI Memory reader / flasher
|
||||
- `weather_station` - SubGHz weather station
|
||||
|
||||
## services
|
||||
|
||||
|
||||
@@ -7,6 +7,6 @@ App(
|
||||
requires=["gui"],
|
||||
stack_size=1 * 1024,
|
||||
order=60,
|
||||
fap_icon="../../plugins/mousejacker/mouse_10px.png",
|
||||
fap_icon="../../external/mousejacker/mouse_10px.png",
|
||||
fap_category="Debug",
|
||||
)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# Placeholder
|
||||
App(
|
||||
appid="example_apps",
|
||||
name="Example apps bundle",
|
||||
|
||||
31
applications/examples/example_plugins/application.fam
Normal file
31
applications/examples/example_plugins/application.fam
Normal file
@@ -0,0 +1,31 @@
|
||||
App(
|
||||
appid="example_plugins",
|
||||
name="Example: App w/plugin",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="example_plugins_app",
|
||||
stack_size=2 * 1024,
|
||||
fap_category="Examples",
|
||||
)
|
||||
|
||||
App(
|
||||
appid="example_plugins_multi",
|
||||
name="Example: App w/plugins",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="example_plugins_multi_app",
|
||||
stack_size=2 * 1024,
|
||||
fap_category="Examples",
|
||||
)
|
||||
|
||||
App(
|
||||
appid="example_plugin1",
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
entry_point="example_plugin1_ep",
|
||||
requires=["example_plugins", "example_plugins_multi"],
|
||||
)
|
||||
|
||||
App(
|
||||
appid="example_plugin2",
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
entry_point="example_plugin2_ep",
|
||||
requires=["example_plugins_multi"],
|
||||
)
|
||||
70
applications/examples/example_plugins/example_plugins.c
Normal file
70
applications/examples/example_plugins/example_plugins.c
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* An example of a plugin host application.
|
||||
* Loads a single plugin and calls its methods.
|
||||
*/
|
||||
|
||||
#include "plugin_interface.h"
|
||||
|
||||
#include <furi.h>
|
||||
|
||||
#include <flipper_application/flipper_application.h>
|
||||
#include <loader/firmware_api/firmware_api.h>
|
||||
#include <storage/storage.h>
|
||||
|
||||
#define TAG "example_plugins"
|
||||
|
||||
int32_t example_plugins_app(void* p) {
|
||||
UNUSED(p);
|
||||
|
||||
FURI_LOG_I(TAG, "Starting");
|
||||
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
|
||||
FlipperApplication* app = flipper_application_alloc(storage, firmware_api_interface);
|
||||
|
||||
do {
|
||||
FlipperApplicationPreloadStatus preload_res =
|
||||
flipper_application_preload(app, APP_DATA_PATH("plugins/example_plugin1.fal"));
|
||||
|
||||
if(preload_res != FlipperApplicationPreloadStatusSuccess) {
|
||||
FURI_LOG_E(TAG, "Failed to preload plugin");
|
||||
break;
|
||||
}
|
||||
|
||||
if(!flipper_application_is_plugin(app)) {
|
||||
FURI_LOG_E(TAG, "Plugin file is not a library");
|
||||
break;
|
||||
}
|
||||
|
||||
FlipperApplicationLoadStatus load_status = flipper_application_map_to_memory(app);
|
||||
if(load_status != FlipperApplicationLoadStatusSuccess) {
|
||||
FURI_LOG_E(TAG, "Failed to load plugin file");
|
||||
break;
|
||||
}
|
||||
|
||||
const FlipperAppPluginDescriptor* app_descriptor =
|
||||
flipper_application_plugin_get_descriptor(app);
|
||||
|
||||
FURI_LOG_I(
|
||||
TAG,
|
||||
"Loaded plugin for appid '%s', API %lu",
|
||||
app_descriptor->appid,
|
||||
app_descriptor->ep_api_version);
|
||||
|
||||
furi_check(app_descriptor->ep_api_version == PLUGIN_API_VERSION);
|
||||
furi_check(strcmp(app_descriptor->appid, PLUGIN_APP_ID) == 0);
|
||||
|
||||
const ExamplePlugin* plugin = app_descriptor->entry_point;
|
||||
|
||||
FURI_LOG_I(TAG, "Plugin name: %s", plugin->name);
|
||||
FURI_LOG_I(TAG, "Plugin method1: %d", plugin->method1());
|
||||
FURI_LOG_I(TAG, "Plugin method2(7,8): %d", plugin->method2(7, 8));
|
||||
FURI_LOG_I(TAG, "Plugin method2(1337,228): %d", plugin->method2(1337, 228));
|
||||
} while(false);
|
||||
flipper_application_free(app);
|
||||
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
FURI_LOG_I(TAG, "Goodbye!");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* An example of an advanced plugin host application.
|
||||
* It uses PluginManager to load all plugins from a directory
|
||||
*/
|
||||
|
||||
#include "plugin_interface.h"
|
||||
|
||||
#include <flipper_application/flipper_application.h>
|
||||
#include <flipper_application/plugins/plugin_manager.h>
|
||||
#include <loader/firmware_api/firmware_api.h>
|
||||
|
||||
#include <furi.h>
|
||||
|
||||
#define TAG "example_plugins"
|
||||
|
||||
int32_t example_plugins_multi_app(void* p) {
|
||||
UNUSED(p);
|
||||
|
||||
FURI_LOG_I(TAG, "Starting");
|
||||
|
||||
PluginManager* manager =
|
||||
plugin_manager_alloc(PLUGIN_APP_ID, PLUGIN_API_VERSION, firmware_api_interface);
|
||||
|
||||
if(plugin_manager_load_all(manager, APP_DATA_PATH("plugins")) != PluginManagerErrorNone) {
|
||||
FURI_LOG_E(TAG, "Failed to load all libs");
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t plugin_count = plugin_manager_get_count(manager);
|
||||
FURI_LOG_I(TAG, "Loaded %lu plugin(s)", plugin_count);
|
||||
|
||||
for(uint32_t i = 0; i < plugin_count; i++) {
|
||||
const ExamplePlugin* plugin = plugin_manager_get_ep(manager, i);
|
||||
FURI_LOG_I(TAG, "plugin name: %s", plugin->name);
|
||||
FURI_LOG_I(TAG, "plugin method1: %d", plugin->method1());
|
||||
FURI_LOG_I(TAG, "plugin method2(7,8): %d", plugin->method2(7, 8));
|
||||
}
|
||||
|
||||
plugin_manager_free(manager);
|
||||
FURI_LOG_I(TAG, "Goodbye!");
|
||||
|
||||
return 0;
|
||||
}
|
||||
32
applications/examples/example_plugins/plugin1.c
Normal file
32
applications/examples/example_plugins/plugin1.c
Normal file
@@ -0,0 +1,32 @@
|
||||
/* A simple plugin implementing example_plugins application's plugin interface */
|
||||
|
||||
#include "plugin_interface.h"
|
||||
|
||||
#include <flipper_application/flipper_application.h>
|
||||
|
||||
static int example_plugin1_method1() {
|
||||
return 42;
|
||||
}
|
||||
|
||||
static int example_plugin1_method2(int arg1, int arg2) {
|
||||
return arg1 + arg2;
|
||||
}
|
||||
|
||||
/* Actual implementation of app<>plugin interface */
|
||||
static const ExamplePlugin example_plugin1 = {
|
||||
.name = "Demo App Plugin 1",
|
||||
.method1 = &example_plugin1_method1,
|
||||
.method2 = &example_plugin1_method2,
|
||||
};
|
||||
|
||||
/* Plugin descriptor to comply with basic plugin specification */
|
||||
static const FlipperAppPluginDescriptor example_plugin1_descriptor = {
|
||||
.appid = PLUGIN_APP_ID,
|
||||
.ep_api_version = PLUGIN_API_VERSION,
|
||||
.entry_point = &example_plugin1,
|
||||
};
|
||||
|
||||
/* Plugin entry point - must return a pointer to const descriptor */
|
||||
const FlipperAppPluginDescriptor* example_plugin1_ep() {
|
||||
return &example_plugin1_descriptor;
|
||||
}
|
||||
32
applications/examples/example_plugins/plugin2.c
Normal file
32
applications/examples/example_plugins/plugin2.c
Normal file
@@ -0,0 +1,32 @@
|
||||
/* Second plugin implementing example_plugins application's plugin interface */
|
||||
|
||||
#include "plugin_interface.h"
|
||||
|
||||
#include <flipper_application/flipper_application.h>
|
||||
|
||||
static int example_plugin2_method1() {
|
||||
return 1337;
|
||||
}
|
||||
|
||||
static int example_plugin2_method2(int arg1, int arg2) {
|
||||
return arg1 - arg2;
|
||||
}
|
||||
|
||||
/* Actual implementation of app<>plugin interface */
|
||||
static const ExamplePlugin example_plugin2 = {
|
||||
.name = "Demo App Plugin 2",
|
||||
.method1 = &example_plugin2_method1,
|
||||
.method2 = &example_plugin2_method2,
|
||||
};
|
||||
|
||||
/* Plugin descriptor to comply with basic plugin specification */
|
||||
static const FlipperAppPluginDescriptor example_plugin2_descriptor = {
|
||||
.appid = PLUGIN_APP_ID,
|
||||
.ep_api_version = PLUGIN_API_VERSION,
|
||||
.entry_point = &example_plugin2,
|
||||
};
|
||||
|
||||
/* Plugin entry point - must return a pointer to const descriptor */
|
||||
const FlipperAppPluginDescriptor* example_plugin2_ep() {
|
||||
return &example_plugin2_descriptor;
|
||||
}
|
||||
12
applications/examples/example_plugins/plugin_interface.h
Normal file
12
applications/examples/example_plugins/plugin_interface.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
/* Common interface between a plugin and host applicaion */
|
||||
|
||||
#define PLUGIN_APP_ID "example_plugins"
|
||||
#define PLUGIN_API_VERSION 1
|
||||
|
||||
typedef struct {
|
||||
const char* name;
|
||||
int (*method1)();
|
||||
int (*method2)(int, int);
|
||||
} ExamplePlugin;
|
||||
25
applications/examples/example_plugins_advanced/app_api.c
Normal file
25
applications/examples/example_plugins_advanced/app_api.c
Normal file
@@ -0,0 +1,25 @@
|
||||
#include "app_api.h"
|
||||
|
||||
/* Actual implementation of app's API and its private state */
|
||||
|
||||
static uint32_t accumulator = 0;
|
||||
|
||||
void app_api_accumulator_set(uint32_t value) {
|
||||
accumulator = value;
|
||||
}
|
||||
|
||||
uint32_t app_api_accumulator_get() {
|
||||
return accumulator;
|
||||
}
|
||||
|
||||
void app_api_accumulator_add(uint32_t value) {
|
||||
accumulator += value;
|
||||
}
|
||||
|
||||
void app_api_accumulator_sub(uint32_t value) {
|
||||
accumulator -= value;
|
||||
}
|
||||
|
||||
void app_api_accumulator_mul(uint32_t value) {
|
||||
accumulator *= value;
|
||||
}
|
||||
25
applications/examples/example_plugins_advanced/app_api.h
Normal file
25
applications/examples/example_plugins_advanced/app_api.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
/*
|
||||
* This file contains an API that is internally implemented by the application
|
||||
* It is also exposed to plugins to allow them to use the application's API.
|
||||
*/
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void app_api_accumulator_set(uint32_t value);
|
||||
|
||||
uint32_t app_api_accumulator_get();
|
||||
|
||||
void app_api_accumulator_add(uint32_t value);
|
||||
|
||||
void app_api_accumulator_sub(uint32_t value);
|
||||
|
||||
void app_api_accumulator_mul(uint32_t value);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <flipper_application/api_hashtable/api_hashtable.h>
|
||||
|
||||
/*
|
||||
* Resolver interface with private application's symbols.
|
||||
* Implementation is contained in app_api_table.c
|
||||
*/
|
||||
extern const ElfApiInterface* const application_api_interface;
|
||||
@@ -0,0 +1,27 @@
|
||||
#include <flipper_application/api_hashtable/api_hashtable.h>
|
||||
#include <flipper_application/api_hashtable/compilesort.hpp>
|
||||
|
||||
/*
|
||||
* This file contains an implementation of a symbol table
|
||||
* with private app's symbols. It is used by composite API resolver
|
||||
* to load plugins that use internal application's APIs.
|
||||
*/
|
||||
#include "app_api_table_i.h"
|
||||
|
||||
static_assert(!has_hash_collisions(app_api_table), "Detected API method hash collision!");
|
||||
|
||||
constexpr HashtableApiInterface applicaton_hashtable_api_interface{
|
||||
{
|
||||
.api_version_major = 0,
|
||||
.api_version_minor = 0,
|
||||
/* generic resolver using pre-sorted array */
|
||||
.resolver_callback = &elf_resolve_from_hashtable,
|
||||
},
|
||||
/* pointers to application's API table boundaries */
|
||||
.table_cbegin = app_api_table.cbegin(),
|
||||
.table_cend = app_api_table.cend(),
|
||||
};
|
||||
|
||||
/* Casting to generic resolver to use in Composite API resolver */
|
||||
extern "C" const ElfApiInterface* const application_api_interface =
|
||||
&applicaton_hashtable_api_interface;
|
||||
@@ -0,0 +1,13 @@
|
||||
#include "app_api.h"
|
||||
|
||||
/*
|
||||
* A list of app's private functions and objects to expose for plugins.
|
||||
* It is used to generate a table of symbols for import resolver to use.
|
||||
* TBD: automatically generate this table from app's header files
|
||||
*/
|
||||
static constexpr auto app_api_table = sort(create_array_t<sym_entry>(
|
||||
API_METHOD(app_api_accumulator_set, void, (uint32_t)),
|
||||
API_METHOD(app_api_accumulator_get, uint32_t, ()),
|
||||
API_METHOD(app_api_accumulator_add, void, (uint32_t)),
|
||||
API_METHOD(app_api_accumulator_sub, void, (uint32_t)),
|
||||
API_METHOD(app_api_accumulator_mul, void, (uint32_t))));
|
||||
@@ -0,0 +1,24 @@
|
||||
App(
|
||||
appid="example_advanced_plugins",
|
||||
name="Example: advanced plugins",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="example_advanced_plugins_app",
|
||||
stack_size=2 * 1024,
|
||||
fap_category="Examples",
|
||||
)
|
||||
|
||||
App(
|
||||
appid="advanced_plugin1",
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
entry_point="advanced_plugin1_ep",
|
||||
requires=["example_advanced_plugins"],
|
||||
sources=["plugin1.c"],
|
||||
)
|
||||
|
||||
App(
|
||||
appid="advanced_plugin2",
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
entry_point="advanced_plugin2_ep",
|
||||
requires=["example_advanced_plugins"],
|
||||
sources=["plugin2.c"],
|
||||
)
|
||||
@@ -0,0 +1,48 @@
|
||||
#include "app_api.h"
|
||||
#include "plugin_interface.h"
|
||||
#include "app_api_interface.h"
|
||||
|
||||
#include <flipper_application/flipper_application.h>
|
||||
#include <flipper_application/plugins/plugin_manager.h>
|
||||
#include <flipper_application/plugins/composite_resolver.h>
|
||||
|
||||
#include <loader/firmware_api/firmware_api.h>
|
||||
|
||||
#define TAG "example_advanced_plugins"
|
||||
|
||||
int32_t example_advanced_plugins_app(void* p) {
|
||||
UNUSED(p);
|
||||
|
||||
FURI_LOG_I(TAG, "Starting");
|
||||
|
||||
CompositeApiResolver* resolver = composite_api_resolver_alloc();
|
||||
composite_api_resolver_add(resolver, firmware_api_interface);
|
||||
composite_api_resolver_add(resolver, application_api_interface);
|
||||
|
||||
PluginManager* manager = plugin_manager_alloc(
|
||||
PLUGIN_APP_ID, PLUGIN_API_VERSION, composite_api_resolver_get(resolver));
|
||||
|
||||
do {
|
||||
if(plugin_manager_load_all(manager, APP_DATA_PATH("plugins")) != PluginManagerErrorNone) {
|
||||
FURI_LOG_E(TAG, "Failed to load all libs");
|
||||
break;
|
||||
}
|
||||
|
||||
uint32_t plugin_count = plugin_manager_get_count(manager);
|
||||
FURI_LOG_I(TAG, "Loaded libs: %lu", plugin_count);
|
||||
|
||||
for(uint32_t i = 0; i < plugin_count; i++) {
|
||||
const AdvancedPlugin* plugin = plugin_manager_get_ep(manager, i);
|
||||
FURI_LOG_I(TAG, "plugin name: %s. Calling methods", plugin->name);
|
||||
plugin->method1(228);
|
||||
plugin->method2();
|
||||
FURI_LOG_I(TAG, "Accumulator: %lu", app_api_accumulator_get());
|
||||
}
|
||||
} while(0);
|
||||
|
||||
plugin_manager_free(manager);
|
||||
composite_api_resolver_free(resolver);
|
||||
FURI_LOG_I(TAG, "Goodbye!");
|
||||
|
||||
return 0;
|
||||
}
|
||||
40
applications/examples/example_plugins_advanced/plugin1.c
Normal file
40
applications/examples/example_plugins_advanced/plugin1.c
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* This plugin uses both firmware's API interface and private application headers.
|
||||
* It can be loaded by a plugin manager that uses CompoundApiInterface,
|
||||
* which combines both interfaces.
|
||||
*/
|
||||
|
||||
#include "app_api.h"
|
||||
#include "plugin_interface.h"
|
||||
|
||||
#include <flipper_application/flipper_application.h>
|
||||
#include <furi.h>
|
||||
|
||||
static void advanced_plugin1_method1(int arg1) {
|
||||
/* This function is implemented inside host application */
|
||||
app_api_accumulator_add(arg1);
|
||||
}
|
||||
|
||||
static void advanced_plugin1_method2() {
|
||||
/* Accumulator value is stored inside host application */
|
||||
FURI_LOG_I("TEST", "Plugin 1, accumulator: %lu", app_api_accumulator_get());
|
||||
}
|
||||
|
||||
/* Actual implementation of app<>plugin interface */
|
||||
static const AdvancedPlugin advanced_plugin1 = {
|
||||
.name = "Advanced Plugin 1",
|
||||
.method1 = &advanced_plugin1_method1,
|
||||
.method2 = &advanced_plugin1_method2,
|
||||
};
|
||||
|
||||
/* Plugin descriptor to comply with basic plugin specification */
|
||||
static const FlipperAppPluginDescriptor advanced_plugin1_descriptor = {
|
||||
.appid = PLUGIN_APP_ID,
|
||||
.ep_api_version = PLUGIN_API_VERSION,
|
||||
.entry_point = &advanced_plugin1,
|
||||
};
|
||||
|
||||
/* Plugin entry point - must return a pointer to const descriptor */
|
||||
const FlipperAppPluginDescriptor* advanced_plugin1_ep() {
|
||||
return &advanced_plugin1_descriptor;
|
||||
}
|
||||
40
applications/examples/example_plugins_advanced/plugin2.c
Normal file
40
applications/examples/example_plugins_advanced/plugin2.c
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* This plugin uses both firmware's API interface and private application headers.
|
||||
* It can be loaded by a plugin manager that uses CompoundApiInterface,
|
||||
* which combines both interfaces.
|
||||
*/
|
||||
|
||||
#include "app_api.h"
|
||||
#include "plugin_interface.h"
|
||||
|
||||
#include <flipper_application/flipper_application.h>
|
||||
#include <furi.h>
|
||||
|
||||
static void advanced_plugin2_method1(int arg1) {
|
||||
/* This function is implemented inside host application */
|
||||
app_api_accumulator_mul(arg1);
|
||||
}
|
||||
|
||||
static void advanced_plugin2_method2() {
|
||||
/* Accumulator value is stored inside host application */
|
||||
FURI_LOG_I("TEST", "Plugin 2, accumulator: %lu", app_api_accumulator_get());
|
||||
}
|
||||
|
||||
/* Actual implementation of app<>plugin interface */
|
||||
static const AdvancedPlugin advanced_plugin2 = {
|
||||
.name = "Advanced Plugin 2",
|
||||
.method1 = &advanced_plugin2_method1,
|
||||
.method2 = &advanced_plugin2_method2,
|
||||
};
|
||||
|
||||
/* Plugin descriptor to comply with basic plugin specification */
|
||||
static const FlipperAppPluginDescriptor advanced_plugin2_descriptor = {
|
||||
.appid = PLUGIN_APP_ID,
|
||||
.ep_api_version = PLUGIN_API_VERSION,
|
||||
.entry_point = &advanced_plugin2,
|
||||
};
|
||||
|
||||
/* Plugin entry point - must return a pointer to const descriptor */
|
||||
const FlipperAppPluginDescriptor* advanced_plugin2_ep() {
|
||||
return &advanced_plugin2_descriptor;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
/* Common interface between a plugin and host applicaion */
|
||||
|
||||
#define PLUGIN_APP_ID "example_plugins_advanced"
|
||||
#define PLUGIN_API_VERSION 1
|
||||
|
||||
typedef struct {
|
||||
const char* name;
|
||||
void (*method1)(int);
|
||||
void (*method2)();
|
||||
} AdvancedPlugin;
|
||||
10
applications/external/application.fam
vendored
10
applications/external/application.fam
vendored
@@ -1,10 +1,6 @@
|
||||
# Placeholder
|
||||
App(
|
||||
appid="basic_plugins",
|
||||
name="Basic applications for plug-in menu",
|
||||
appid="external_apps",
|
||||
name="External apps bundle",
|
||||
apptype=FlipperAppType.METAPACKAGE,
|
||||
provides=[
|
||||
"music_player",
|
||||
"music_beeper",
|
||||
"snake_game",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -3,7 +3,6 @@ App(
|
||||
name="Arkanoid",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="arkanoid_game_app",
|
||||
cdefines=["APP_ARKANOID_GAME"],
|
||||
requires=["gui"],
|
||||
stack_size=1 * 1024,
|
||||
order=20,
|
||||
|
||||
@@ -3,7 +3,6 @@ App(
|
||||
name="BlackJack",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="blackjack_app",
|
||||
cdefines=["APP_BLACKJACK"],
|
||||
requires=["gui", "storage", "canvas"],
|
||||
stack_size=2 * 1024,
|
||||
order=30,
|
||||
|
||||
1
applications/external/doom/application.fam
vendored
1
applications/external/doom/application.fam
vendored
@@ -3,7 +3,6 @@ App(
|
||||
name="DOOM",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="doom_app",
|
||||
cdefines=["APP_DOOM_GAME"],
|
||||
requires=[
|
||||
"gui",
|
||||
"music_player",
|
||||
|
||||
@@ -3,7 +3,6 @@ App(
|
||||
name="DTMF Dolphin",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="dtmf_dolphin_app",
|
||||
cdefines=["DTMF_DOLPHIN"],
|
||||
requires=[
|
||||
"storage",
|
||||
"gui",
|
||||
|
||||
@@ -3,7 +3,6 @@ App(
|
||||
name="[ESP8266] Deauther",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="esp8266_deauth_app",
|
||||
cdefines=["APP_ESP8266_deauth"],
|
||||
requires=["gui"],
|
||||
stack_size=2 * 1024,
|
||||
order=20,
|
||||
|
||||
@@ -352,7 +352,12 @@ int32_t esp8266_deauth_app(void* p) {
|
||||
#else
|
||||
#if ENABLE_MODULE_POWER
|
||||
app->m_context = Initializing;
|
||||
furi_hal_power_enable_otg();
|
||||
uint8_t attempts = 0;
|
||||
while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) {
|
||||
furi_hal_power_enable_otg();
|
||||
furi_delay_ms(10);
|
||||
}
|
||||
furi_delay_ms(200);
|
||||
#else
|
||||
app->m_context = ModuleActive;
|
||||
#endif
|
||||
@@ -409,7 +414,11 @@ int32_t esp8266_deauth_app(void* p) {
|
||||
app->m_wifiDeauthModuleAttached = true;
|
||||
#if ENABLE_MODULE_POWER
|
||||
app->m_context = Initializing;
|
||||
furi_hal_power_enable_otg();
|
||||
uint8_t attempts2 = 0;
|
||||
while(!furi_hal_power_is_otg_enabled() && attempts2++ < 3) {
|
||||
furi_hal_power_enable_otg();
|
||||
furi_delay_ms(10);
|
||||
}
|
||||
#else
|
||||
app->m_context = ModuleActive;
|
||||
#endif
|
||||
@@ -533,7 +542,9 @@ int32_t esp8266_deauth_app(void* p) {
|
||||
DEAUTH_APP_LOG_I("App freed");
|
||||
|
||||
#if ENABLE_MODULE_POWER
|
||||
furi_hal_power_disable_otg();
|
||||
if(furi_hal_power_is_otg_enabled()) {
|
||||
furi_hal_power_disable_otg();
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -3,7 +3,6 @@ App(
|
||||
name="Flappy Bird",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="flappy_game_app",
|
||||
cdefines=["APP_FLAPPY_GAME"],
|
||||
requires=["gui"],
|
||||
stack_size=4 * 1024,
|
||||
order=100,
|
||||
|
||||
@@ -3,7 +3,6 @@ App(
|
||||
name="RFID Fuzzer",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="flipfrid_start",
|
||||
cdefines=["APP_FLIP_FRID"],
|
||||
requires=["gui", "storage", "dialogs", "input", "notification"],
|
||||
stack_size=1 * 1024,
|
||||
order=180,
|
||||
|
||||
@@ -3,7 +3,6 @@ App(
|
||||
name="[GPIO] i2c Tools",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="i2ctools_app",
|
||||
cdefines=["APP_I2CTOOLS"],
|
||||
requires=["gui"],
|
||||
stack_size=2 * 1024,
|
||||
order=175,
|
||||
|
||||
1
applications/external/game15/application.fam
vendored
1
applications/external/game15/application.fam
vendored
@@ -3,7 +3,6 @@ App(
|
||||
name="Game 15",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="game15_app",
|
||||
cdefines=["APP_GAME15"],
|
||||
requires=["gui"],
|
||||
stack_size=1 * 1024,
|
||||
fap_icon="game15_10px.png",
|
||||
|
||||
@@ -4,7 +4,6 @@ App(
|
||||
name="2048",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="game_2048_app",
|
||||
cdefines=["APP_GAME_2048"],
|
||||
requires=[
|
||||
"gui",
|
||||
],
|
||||
|
||||
@@ -3,7 +3,6 @@ App(
|
||||
name="[NMEA] GPS",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="gps_app",
|
||||
cdefines=["APP_GPS"],
|
||||
requires=["gui"],
|
||||
stack_size=1 * 1024,
|
||||
order=35,
|
||||
|
||||
@@ -3,7 +3,6 @@ App(
|
||||
name="[HC-SR] Dist. Sensor",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="hc_sr04_app",
|
||||
cdefines=["APP_HC_SR04"],
|
||||
requires=[
|
||||
"gui",
|
||||
],
|
||||
|
||||
@@ -3,7 +3,6 @@ App(
|
||||
name="Hex Viewer",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="hex_viewer_app",
|
||||
cdefines=["APP_HEX_VIEWER"],
|
||||
requires=[
|
||||
"gui",
|
||||
"dialogs",
|
||||
|
||||
@@ -3,7 +3,6 @@ App(
|
||||
name="iButton Fuzzer",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="ibtnfuzzer_start",
|
||||
cdefines=["APP_IBTN_FUZZ"],
|
||||
requires=["gui", "storage", "dialogs", "input", "notification"],
|
||||
stack_size=1 * 1024,
|
||||
order=15,
|
||||
|
||||
@@ -3,7 +3,6 @@ App(
|
||||
name="[BH1750] Lightmeter",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="lightmeter_app",
|
||||
cdefines=["APP_LIGHTMETER"],
|
||||
requires=[
|
||||
"gui",
|
||||
],
|
||||
|
||||
@@ -3,7 +3,6 @@ App(
|
||||
name="Metronome",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="metronome_app",
|
||||
cdefines=["APP_METRONOME"],
|
||||
requires=[
|
||||
"gui",
|
||||
],
|
||||
|
||||
@@ -3,7 +3,6 @@ App(
|
||||
name="Minesweeper",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="minesweeper_app",
|
||||
cdefines=["APP_MINESWEEPER"],
|
||||
requires=["gui"],
|
||||
stack_size=8 * 1024,
|
||||
fap_category="Games",
|
||||
|
||||
@@ -3,7 +3,6 @@ App(
|
||||
name="Morse Code",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="morse_code_app",
|
||||
cdefines=["APP_MORSE_CODE"],
|
||||
requires=[
|
||||
"gui",
|
||||
],
|
||||
|
||||
@@ -3,7 +3,6 @@ App(
|
||||
name="[NRF24] Mouse Jacker",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="mousejacker_app",
|
||||
cdefines=["APP_MOUSEJACKER"],
|
||||
requires=[
|
||||
"gui",
|
||||
"dialogs",
|
||||
|
||||
@@ -3,7 +3,6 @@ App(
|
||||
name="Multi Converter",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="multi_converter_app",
|
||||
cdefines=["APP_DEC_HEX_CONVERTER"],
|
||||
requires=["gui"],
|
||||
stack_size=1 * 1024,
|
||||
order=160,
|
||||
|
||||
@@ -3,7 +3,6 @@ App(
|
||||
name="Music Player",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="music_player_app",
|
||||
cdefines=["APP_MUSIC_PLAYER"],
|
||||
requires=[
|
||||
"gui",
|
||||
"dialogs",
|
||||
@@ -12,8 +11,8 @@ App(
|
||||
stack_size=2 * 1024,
|
||||
order=45,
|
||||
fap_icon="icons/music_10px.png",
|
||||
fap_icon_assets="icons",
|
||||
fap_category="Music",
|
||||
fap_icon_assets="icons",
|
||||
)
|
||||
|
||||
App(
|
||||
|
||||
@@ -3,7 +3,6 @@ App(
|
||||
name="[NRF24] Sniffer",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="nrfsniff_app",
|
||||
cdefines=["APP_NRFSNIFF"],
|
||||
requires=["gui"],
|
||||
stack_size=2 * 1024,
|
||||
order=60,
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
#define ICLASS_ELITE_DICT_FLIPPER_NAME APP_DATA_PATH("assets/iclass_elite_dict.txt")
|
||||
#define ICLASS_ELITE_DICT_USER_NAME APP_DATA_PATH("assets/iclass_elite_dict_user.txt")
|
||||
#define ICLASS_STANDARD_DICT_FLIPPER_NAME APP_DATA_PATH("assets/iclass_standard_dict.txt")
|
||||
|
||||
#define TAG "IclassEliteDict"
|
||||
|
||||
@@ -25,6 +26,9 @@ bool iclass_elite_dict_check_presence(IclassEliteDictType dict_type) {
|
||||
(storage_common_stat(storage, ICLASS_ELITE_DICT_FLIPPER_NAME, NULL) == FSE_OK);
|
||||
} else if(dict_type == IclassEliteDictTypeUser) {
|
||||
dict_present = (storage_common_stat(storage, ICLASS_ELITE_DICT_USER_NAME, NULL) == FSE_OK);
|
||||
} else if(dict_type == IclassStandardDictTypeFlipper) {
|
||||
dict_present =
|
||||
(storage_common_stat(storage, ICLASS_STANDARD_DICT_FLIPPER_NAME, NULL) == FSE_OK);
|
||||
}
|
||||
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
@@ -52,6 +56,15 @@ IclassEliteDict* iclass_elite_dict_alloc(IclassEliteDictType dict_type) {
|
||||
buffered_file_stream_close(dict->stream);
|
||||
break;
|
||||
}
|
||||
} else if(dict_type == IclassStandardDictTypeFlipper) {
|
||||
if(!buffered_file_stream_open(
|
||||
dict->stream,
|
||||
ICLASS_STANDARD_DICT_FLIPPER_NAME,
|
||||
FSAM_READ,
|
||||
FSOM_OPEN_EXISTING)) {
|
||||
buffered_file_stream_close(dict->stream);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Read total amount of keys
|
||||
@@ -148,4 +161,4 @@ bool iclass_elite_dict_add_key(IclassEliteDict* dict, uint8_t* key) {
|
||||
|
||||
furi_string_free(key_str);
|
||||
return key_added;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
typedef enum {
|
||||
IclassEliteDictTypeUser,
|
||||
IclassEliteDictTypeFlipper,
|
||||
IclassStandardDictTypeFlipper,
|
||||
} IclassEliteDictType;
|
||||
|
||||
typedef struct IclassEliteDict IclassEliteDict;
|
||||
@@ -25,4 +26,4 @@ bool iclass_elite_dict_get_next_key(IclassEliteDict* dict, uint8_t* key);
|
||||
|
||||
bool iclass_elite_dict_rewind(IclassEliteDict* dict);
|
||||
|
||||
bool iclass_elite_dict_add_key(IclassEliteDict* dict, uint8_t* key);
|
||||
bool iclass_elite_dict_add_key(IclassEliteDict* dict, uint8_t* key);
|
||||
|
||||
46
applications/external/picopass/picopass_worker.c
vendored
46
applications/external/picopass/picopass_worker.c
vendored
@@ -172,14 +172,18 @@ ReturnCode picopass_read_preauth(PicopassBlock* AA1) {
|
||||
return ERR_NONE;
|
||||
}
|
||||
|
||||
static ReturnCode picopass_auth_dict(
|
||||
uint8_t* csn,
|
||||
PicopassPacs* pacs,
|
||||
uint8_t* div_key,
|
||||
IclassEliteDictType dict_type,
|
||||
bool elite) {
|
||||
static ReturnCode
|
||||
picopass_auth_dict(PicopassWorker* picopass_worker, IclassEliteDictType dict_type) {
|
||||
rfalPicoPassReadCheckRes rcRes;
|
||||
rfalPicoPassCheckRes chkRes;
|
||||
bool elite = (dict_type != IclassStandardDictTypeFlipper);
|
||||
|
||||
PicopassDeviceData* dev_data = picopass_worker->dev_data;
|
||||
PicopassBlock* AA1 = dev_data->AA1;
|
||||
PicopassPacs* pacs = &dev_data->pacs;
|
||||
|
||||
uint8_t* csn = AA1[PICOPASS_CSN_BLOCK_INDEX].data;
|
||||
uint8_t* div_key = AA1[PICOPASS_KD_BLOCK_INDEX].data;
|
||||
|
||||
ReturnCode err = ERR_PARAM;
|
||||
|
||||
@@ -204,7 +208,8 @@ static ReturnCode picopass_auth_dict(
|
||||
while(iclass_elite_dict_get_next_key(dict, key)) {
|
||||
FURI_LOG_D(
|
||||
TAG,
|
||||
"Try to auth with key %zu %02x%02x%02x%02x%02x%02x%02x%02x",
|
||||
"Try to %s auth with key %zu %02x%02x%02x%02x%02x%02x%02x%02x",
|
||||
elite ? "elite" : "standard",
|
||||
index++,
|
||||
key[0],
|
||||
key[1],
|
||||
@@ -230,6 +235,8 @@ static ReturnCode picopass_auth_dict(
|
||||
memcpy(pacs->key, key, PICOPASS_BLOCK_LEN);
|
||||
break;
|
||||
}
|
||||
|
||||
if(picopass_worker->state != PicopassWorkerStateDetect) break;
|
||||
}
|
||||
|
||||
iclass_elite_dict_free(dict);
|
||||
@@ -237,38 +244,23 @@ static ReturnCode picopass_auth_dict(
|
||||
return err;
|
||||
}
|
||||
|
||||
ReturnCode picopass_auth(PicopassBlock* AA1, PicopassPacs* pacs) {
|
||||
ReturnCode picopass_auth(PicopassWorker* picopass_worker) {
|
||||
ReturnCode err;
|
||||
|
||||
FURI_LOG_I(TAG, "Starting system dictionary attack [Standard KDF]");
|
||||
err = picopass_auth_dict(
|
||||
AA1[PICOPASS_CSN_BLOCK_INDEX].data,
|
||||
pacs,
|
||||
AA1[PICOPASS_KD_BLOCK_INDEX].data,
|
||||
IclassEliteDictTypeFlipper,
|
||||
false);
|
||||
err = picopass_auth_dict(picopass_worker, IclassStandardDictTypeFlipper);
|
||||
if(err == ERR_NONE) {
|
||||
return ERR_NONE;
|
||||
}
|
||||
|
||||
FURI_LOG_I(TAG, "Starting user dictionary attack [Elite KDF]");
|
||||
err = picopass_auth_dict(
|
||||
AA1[PICOPASS_CSN_BLOCK_INDEX].data,
|
||||
pacs,
|
||||
AA1[PICOPASS_KD_BLOCK_INDEX].data,
|
||||
IclassEliteDictTypeUser,
|
||||
true);
|
||||
err = picopass_auth_dict(picopass_worker, IclassEliteDictTypeUser);
|
||||
if(err == ERR_NONE) {
|
||||
return ERR_NONE;
|
||||
}
|
||||
|
||||
FURI_LOG_I(TAG, "Starting system dictionary attack [Elite KDF]");
|
||||
err = picopass_auth_dict(
|
||||
AA1[PICOPASS_CSN_BLOCK_INDEX].data,
|
||||
pacs,
|
||||
AA1[PICOPASS_KD_BLOCK_INDEX].data,
|
||||
IclassEliteDictTypeFlipper,
|
||||
true);
|
||||
err = picopass_auth_dict(picopass_worker, IclassEliteDictTypeFlipper);
|
||||
if(err == ERR_NONE) {
|
||||
return ERR_NONE;
|
||||
}
|
||||
@@ -520,7 +512,7 @@ void picopass_worker_detect(PicopassWorker* picopass_worker) {
|
||||
}
|
||||
|
||||
if(nextState == PicopassWorkerEventSuccess) {
|
||||
err = picopass_auth(AA1, pacs);
|
||||
err = picopass_auth(picopass_worker);
|
||||
if(err != ERR_NONE) {
|
||||
FURI_LOG_E(TAG, "picopass_try_auth error %d", err);
|
||||
nextState = PicopassWorkerEventFail;
|
||||
|
||||
@@ -3,7 +3,6 @@ App(
|
||||
name="POCSAG Pager",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="pocsag_pager_app",
|
||||
cdefines=["APP_POCSAG_PAGER"],
|
||||
requires=["gui"],
|
||||
stack_size=4 * 1024,
|
||||
order=50,
|
||||
|
||||
@@ -124,6 +124,11 @@ POCSAGPagerApp* pocsag_pager_app_alloc() {
|
||||
|
||||
// Enable power for External CC1101 if it is connected
|
||||
furi_hal_subghz_enable_ext_power();
|
||||
// Auto switch to internal radio if external radio is not available
|
||||
furi_delay_ms(15);
|
||||
if(!furi_hal_subghz_check_radio()) {
|
||||
furi_hal_subghz_set_radio_type(SubGhzRadioInternal);
|
||||
}
|
||||
|
||||
furi_hal_power_suppress_charge_enter();
|
||||
|
||||
|
||||
5
applications/external/protoview/app.c
vendored
5
applications/external/protoview/app.c
vendored
@@ -169,6 +169,11 @@ ProtoViewApp* protoview_app_alloc() {
|
||||
|
||||
// Enable power for External CC1101 if it is connected
|
||||
furi_hal_subghz_enable_ext_power();
|
||||
// Auto switch to internal radio if external radio is not available
|
||||
furi_delay_ms(15);
|
||||
if(!furi_hal_subghz_check_radio()) {
|
||||
furi_hal_subghz_set_radio_type(SubGhzRadioInternal);
|
||||
}
|
||||
|
||||
furi_hal_power_suppress_charge_enter();
|
||||
app->running = 1;
|
||||
|
||||
@@ -3,7 +3,6 @@ App(
|
||||
name="ProtoView",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="protoview_app_entry",
|
||||
cdefines=["APP_PROTOVIEW"],
|
||||
requires=["gui"],
|
||||
stack_size=8 * 1024,
|
||||
order=50,
|
||||
|
||||
@@ -3,7 +3,6 @@ App(
|
||||
name="[GPIO] Sentry Safe",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="sentry_safe_app",
|
||||
cdefines=["APP_SENTRY_SAFE"],
|
||||
requires=["gui"],
|
||||
stack_size=1 * 1024,
|
||||
order=40,
|
||||
|
||||
@@ -3,7 +3,6 @@ App(
|
||||
name="[GPIO] Signal Generator",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="signal_gen_app",
|
||||
cdefines=["APP_SIGNAL_GEN"],
|
||||
requires=["gui"],
|
||||
stack_size=1 * 1024,
|
||||
order=50,
|
||||
|
||||
@@ -3,7 +3,6 @@ App(
|
||||
name="Snake Game",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="snake_game_app",
|
||||
cdefines=["APP_SNAKE_GAME"],
|
||||
requires=["gui"],
|
||||
stack_size=1 * 1024,
|
||||
order=210,
|
||||
|
||||
@@ -3,7 +3,6 @@ App(
|
||||
name="Solitaire",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="solitaire_app",
|
||||
cdefines=["APP_SOLITAIRE"],
|
||||
requires=["gui", "storage", "canvas"],
|
||||
stack_size=2 * 1024,
|
||||
order=30,
|
||||
|
||||
@@ -3,7 +3,6 @@ App(
|
||||
name="Spectrum Analyzer",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="spectrum_analyzer_app",
|
||||
cdefines=["APP_SPECTRUM_ANALYZER"],
|
||||
requires=["gui"],
|
||||
stack_size=2 * 1024,
|
||||
order=12,
|
||||
|
||||
@@ -405,6 +405,11 @@ int32_t spectrum_analyzer_app(void* p) {
|
||||
|
||||
// Enable power for External CC1101 if it is connected
|
||||
furi_hal_subghz_enable_ext_power();
|
||||
// Auto switch to internal radio if external radio is not available
|
||||
furi_delay_ms(15);
|
||||
if(!furi_hal_subghz_check_radio()) {
|
||||
furi_hal_subghz_set_radio_type(SubGhzRadioInternal);
|
||||
}
|
||||
|
||||
furi_hal_power_suppress_charge_enter();
|
||||
|
||||
|
||||
1
applications/external/subbrute
vendored
Submodule
1
applications/external/subbrute
vendored
Submodule
Submodule applications/external/subbrute added at f9a4508e50
@@ -3,7 +3,6 @@ App(
|
||||
name="Sub-GHz Playlist",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="playlist_app",
|
||||
cdefines=["APP_PLAYLIST"],
|
||||
requires=["storage", "gui", "dialogs", "subghz"],
|
||||
stack_size=2 * 1024,
|
||||
order=14,
|
||||
|
||||
@@ -713,6 +713,11 @@ int32_t playlist_app(void* p) {
|
||||
|
||||
// Enable power for External CC1101 if it is connected
|
||||
furi_hal_subghz_enable_ext_power();
|
||||
// Auto switch to internal radio if external radio is not available
|
||||
furi_delay_ms(15);
|
||||
if(!furi_hal_subghz_check_radio()) {
|
||||
furi_hal_subghz_set_radio_type(SubGhzRadioInternal);
|
||||
}
|
||||
|
||||
furi_hal_power_suppress_charge_enter();
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <lib/subghz/protocols/alutech_at_4n.h>
|
||||
#include <lib/subghz/protocols/nice_flor_s.h>
|
||||
#include <lib/subghz/protocols/somfy_telis.h>
|
||||
#include <lib/subghz/protocols/secplus_v2.h>
|
||||
|
||||
#define SUBREMOTEMAP_FOLDER "/ext/subghz/remote"
|
||||
#define SUBREMOTEMAP_EXTENSION ".txt"
|
||||
@@ -491,6 +492,7 @@ void subghz_remote_tx_stop(SubGHzRemote* app) {
|
||||
alutech_reset_original_btn();
|
||||
nice_flors_reset_original_btn();
|
||||
somfy_telis_reset_original_btn();
|
||||
secplus2_reset_original_btn();
|
||||
star_line_reset_mfname();
|
||||
star_line_reset_kl_type();
|
||||
}
|
||||
@@ -738,6 +740,11 @@ SubGHzRemote* subghz_remote_alloc(void) {
|
||||
|
||||
// Enable power for External CC1101 if it is connected
|
||||
furi_hal_subghz_enable_ext_power();
|
||||
// Auto switch to internal radio if external radio is not available
|
||||
furi_delay_ms(15);
|
||||
if(!furi_hal_subghz_check_radio()) {
|
||||
furi_hal_subghz_set_radio_type(SubGhzRadioInternal);
|
||||
}
|
||||
|
||||
furi_hal_power_suppress_charge_enter();
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ App(
|
||||
name="[SWD] Probe",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="swd_probe_app_main",
|
||||
cdefines=["APP_SWD_PROBE"],
|
||||
requires=["notification", "gui", "storage", "dialogs", "cli"],
|
||||
stack_size=2 * 1024,
|
||||
order=10,
|
||||
|
||||
@@ -3,7 +3,6 @@ App(
|
||||
name="Tetris",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="tetris_game_app",
|
||||
cdefines=["APP_TETRIS_GAME"],
|
||||
requires=["gui"],
|
||||
stack_size=2 * 1024,
|
||||
order=240,
|
||||
|
||||
@@ -3,7 +3,6 @@ App(
|
||||
name="Text Viewer",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="text_viewer_app",
|
||||
cdefines=["APP_TEXT_VIEWER"],
|
||||
requires=[
|
||||
"gui",
|
||||
"dialogs",
|
||||
|
||||
@@ -3,7 +3,6 @@ App(
|
||||
name="Tic Tac Toe",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="tictactoe_game_app",
|
||||
cdefines=["APP_TICTACTOE_GAME"],
|
||||
requires=["gui"],
|
||||
stack_size=1 * 1024,
|
||||
order=250,
|
||||
|
||||
1
applications/external/totp/application.fam
vendored
1
applications/external/totp/application.fam
vendored
@@ -3,7 +3,6 @@ App(
|
||||
name="Authenticator",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="totp_app",
|
||||
cdefines=["APP_TOTP"],
|
||||
requires=["gui", "cli", "dialogs", "storage", "input", "notification"],
|
||||
stack_size=2 * 1024,
|
||||
order=20,
|
||||
|
||||
@@ -3,7 +3,6 @@ App(
|
||||
name="[GPIO] UART Terminal",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="uart_terminal_app",
|
||||
cdefines=["APP_UART_TERMINAL"],
|
||||
requires=["gui"],
|
||||
stack_size=1 * 1024,
|
||||
order=90,
|
||||
|
||||
@@ -3,7 +3,6 @@ App(
|
||||
name="[GPIO] Unitemp",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="unitemp_app",
|
||||
cdefines=["UNITEMP_APP"],
|
||||
requires=[
|
||||
"gui",
|
||||
],
|
||||
|
||||
@@ -3,7 +3,6 @@ App(
|
||||
name="WAV Player",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="wav_player_app",
|
||||
cdefines=["APP_WAV_PLAYER"],
|
||||
stack_size=4 * 1024,
|
||||
order=46,
|
||||
fap_icon="wav_10px.png",
|
||||
|
||||
@@ -4,7 +4,6 @@ App(
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
targets=["f7"],
|
||||
entry_point="weather_station_app",
|
||||
cdefines=["APP_WEATHER_STATION"],
|
||||
requires=["gui"],
|
||||
stack_size=4 * 1024,
|
||||
order=50,
|
||||
|
||||
@@ -107,6 +107,11 @@ WeatherStationApp* weather_station_app_alloc() {
|
||||
|
||||
// Enable power for External CC1101 if it is connected
|
||||
furi_hal_subghz_enable_ext_power();
|
||||
// Auto switch to internal radio if external radio is not available
|
||||
furi_delay_ms(15);
|
||||
if(!furi_hal_subghz_check_radio()) {
|
||||
furi_hal_subghz_set_radio_type(SubGhzRadioInternal);
|
||||
}
|
||||
|
||||
furi_hal_power_suppress_charge_enter();
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ App(
|
||||
name="[ESP32] WiFi Marauder",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="wifi_marauder_app",
|
||||
cdefines=["APP_WIFI_MARAUDER"],
|
||||
requires=["gui"],
|
||||
stack_size=1 * 1024,
|
||||
order=90,
|
||||
|
||||
@@ -14,10 +14,26 @@ void wifi_marauder_console_output_handle_rx_data_cb(uint8_t* buf, size_t len, vo
|
||||
// Null-terminate buf and append to text box store
|
||||
buf[len] = '\0';
|
||||
furi_string_cat_printf(app->text_box_store, "%s", buf);
|
||||
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, WifiMarauderEventRefreshConsoleOutput);
|
||||
}
|
||||
|
||||
void wifi_marauder_console_output_handle_rx_packets_cb(uint8_t* buf, size_t len, void* context) {
|
||||
furi_assert(context);
|
||||
WifiMarauderApp* app = context;
|
||||
|
||||
// If it is a sniff function, open the pcap file for recording
|
||||
if(strncmp("sniff", app->selected_tx_string, strlen("sniff")) == 0 && !app->is_writing) {
|
||||
app->is_writing = true;
|
||||
if(!app->capture_file || !storage_file_is_open(app->capture_file)) {
|
||||
wifi_marauder_create_pcap_file(app);
|
||||
}
|
||||
}
|
||||
|
||||
if(app->is_writing) {
|
||||
storage_file_write(app->capture_file, buf, len);
|
||||
}
|
||||
}
|
||||
|
||||
void wifi_marauder_scene_console_output_on_enter(void* context) {
|
||||
WifiMarauderApp* app = context;
|
||||
|
||||
@@ -33,8 +49,8 @@ void wifi_marauder_scene_console_output_on_enter(void* context) {
|
||||
furi_string_reset(app->text_box_store);
|
||||
app->text_box_store_strlen = 0;
|
||||
if(0 == strncmp("help", app->selected_tx_string, strlen("help"))) {
|
||||
const char* help_msg =
|
||||
"Marauder companion v0.3.0\nFor app support/feedback,\nreach out to me:\n@cococode#6011 (discord)\n0xchocolate (github)\n";
|
||||
const char* help_msg = "Marauder companion " WIFI_MARAUDER_APP_VERSION
|
||||
"\nby @0xchocolate\nmodified by @tcpassos\n";
|
||||
furi_string_cat_str(app->text_box_store, help_msg);
|
||||
app->text_box_store_strlen += strlen(help_msg);
|
||||
}
|
||||
@@ -54,7 +70,11 @@ void wifi_marauder_scene_console_output_on_enter(void* context) {
|
||||
|
||||
// Register callback to receive data
|
||||
wifi_marauder_uart_set_handle_rx_data_cb(
|
||||
app->uart, wifi_marauder_console_output_handle_rx_data_cb); // setup callback for rx thread
|
||||
app->uart,
|
||||
wifi_marauder_console_output_handle_rx_data_cb); // setup callback for general log rx thread
|
||||
wifi_marauder_uart_set_handle_rx_data_cb(
|
||||
app->lp_uart,
|
||||
wifi_marauder_console_output_handle_rx_packets_cb); // setup callback for packets rx thread
|
||||
|
||||
// Send command with newline '\n'
|
||||
if(app->is_command && app->selected_tx_string) {
|
||||
@@ -84,9 +104,15 @@ void wifi_marauder_scene_console_output_on_exit(void* context) {
|
||||
|
||||
// Unregister rx callback
|
||||
wifi_marauder_uart_set_handle_rx_data_cb(app->uart, NULL);
|
||||
wifi_marauder_uart_set_handle_rx_data_cb(app->lp_uart, NULL);
|
||||
|
||||
// Automatically stop the scan when exiting view
|
||||
if(app->is_command) {
|
||||
wifi_marauder_uart_tx((uint8_t*)("stopscan\n"), strlen("stopscan\n"));
|
||||
}
|
||||
}
|
||||
|
||||
app->is_writing = false;
|
||||
if(app->capture_file && storage_file_is_open(app->capture_file)) {
|
||||
storage_file_close(app->capture_file);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,9 @@ WifiMarauderApp* wifi_marauder_app_alloc() {
|
||||
WifiMarauderApp* app = malloc(sizeof(WifiMarauderApp));
|
||||
|
||||
app->gui = furi_record_open(RECORD_GUI);
|
||||
app->dialogs = furi_record_open(RECORD_DIALOGS);
|
||||
app->storage = furi_record_open(RECORD_STORAGE);
|
||||
app->capture_file = storage_file_alloc(app->storage);
|
||||
|
||||
app->view_dispatcher = view_dispatcher_alloc();
|
||||
app->scene_manager = scene_manager_alloc(&wifi_marauder_scene_handlers, app);
|
||||
@@ -67,6 +70,14 @@ WifiMarauderApp* wifi_marauder_app_alloc() {
|
||||
return app;
|
||||
}
|
||||
|
||||
void wifi_marauder_make_app_folder(WifiMarauderApp* app) {
|
||||
furi_assert(app);
|
||||
|
||||
if(!storage_simply_mkdir(app->storage, MARAUDER_APP_FOLDER)) {
|
||||
dialog_message_show_storage_error(app->dialogs, "Cannot create\napp folder");
|
||||
}
|
||||
}
|
||||
|
||||
void wifi_marauder_app_free(WifiMarauderApp* app) {
|
||||
furi_assert(app);
|
||||
|
||||
@@ -77,33 +88,47 @@ void wifi_marauder_app_free(WifiMarauderApp* app) {
|
||||
text_box_free(app->text_box);
|
||||
furi_string_free(app->text_box_store);
|
||||
text_input_free(app->text_input);
|
||||
storage_file_free(app->capture_file);
|
||||
|
||||
// View dispatcher
|
||||
view_dispatcher_free(app->view_dispatcher);
|
||||
scene_manager_free(app->scene_manager);
|
||||
|
||||
wifi_marauder_uart_free(app->uart);
|
||||
wifi_marauder_uart_free(app->lp_uart);
|
||||
|
||||
// Close records
|
||||
furi_record_close(RECORD_GUI);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
furi_record_close(RECORD_DIALOGS);
|
||||
|
||||
free(app);
|
||||
}
|
||||
|
||||
int32_t wifi_marauder_app(void* p) {
|
||||
UNUSED(p);
|
||||
furi_hal_power_enable_otg();
|
||||
furi_delay_ms(300);
|
||||
|
||||
uint8_t attempts = 0;
|
||||
while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) {
|
||||
furi_hal_power_enable_otg();
|
||||
furi_delay_ms(10);
|
||||
}
|
||||
furi_delay_ms(200);
|
||||
|
||||
WifiMarauderApp* wifi_marauder_app = wifi_marauder_app_alloc();
|
||||
|
||||
wifi_marauder_app->uart = wifi_marauder_uart_init(wifi_marauder_app);
|
||||
wifi_marauder_make_app_folder(wifi_marauder_app);
|
||||
|
||||
wifi_marauder_app->uart = wifi_marauder_usart_init(wifi_marauder_app);
|
||||
wifi_marauder_app->lp_uart = wifi_marauder_lp_uart_init(wifi_marauder_app);
|
||||
|
||||
view_dispatcher_run(wifi_marauder_app->view_dispatcher);
|
||||
|
||||
wifi_marauder_app_free(wifi_marauder_app);
|
||||
|
||||
furi_hal_power_disable_otg();
|
||||
if(furi_hal_power_is_otg_enabled()) {
|
||||
furi_hal_power_disable_otg();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define WIFI_MARAUDER_APP_VERSION "v0.3.1"
|
||||
|
||||
typedef struct WifiMarauderApp WifiMarauderApp;
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "scenes/wifi_marauder_scene.h"
|
||||
#include "wifi_marauder_custom_event.h"
|
||||
#include "wifi_marauder_uart.h"
|
||||
#include "wifi_marauder_pcap.h"
|
||||
|
||||
#include <gui/gui.h>
|
||||
#include <gui/view_dispatcher.h>
|
||||
@@ -14,11 +15,16 @@
|
||||
#include <gui/modules/text_input.h>
|
||||
#include <gui/modules/variable_item_list.h>
|
||||
|
||||
#include <storage/storage.h>
|
||||
#include <dialogs/dialogs.h>
|
||||
|
||||
#define NUM_MENU_ITEMS (16)
|
||||
|
||||
#define WIFI_MARAUDER_TEXT_BOX_STORE_SIZE (4096)
|
||||
#define WIFI_MARAUDER_TEXT_INPUT_STORE_SIZE (512)
|
||||
|
||||
#define MARAUDER_APP_FOLDER EXT_PATH("apps_data/marauder")
|
||||
|
||||
struct WifiMarauderApp {
|
||||
Gui* gui;
|
||||
ViewDispatcher* view_dispatcher;
|
||||
@@ -29,11 +35,14 @@ struct WifiMarauderApp {
|
||||
size_t text_box_store_strlen;
|
||||
TextBox* text_box;
|
||||
TextInput* text_input;
|
||||
//Widget* widget;
|
||||
Storage* storage;
|
||||
File* capture_file;
|
||||
DialogsApp* dialogs;
|
||||
|
||||
VariableItemList* var_item_list;
|
||||
|
||||
WifiMarauderUart* uart;
|
||||
WifiMarauderUart* lp_uart;
|
||||
int selected_menu_index;
|
||||
int selected_option_index[NUM_MENU_ITEMS];
|
||||
const char* selected_tx_string;
|
||||
@@ -41,6 +50,7 @@ struct WifiMarauderApp {
|
||||
bool is_custom_tx_string;
|
||||
bool focus_console_start;
|
||||
bool show_stopscan_tip;
|
||||
bool is_writing;
|
||||
|
||||
// For input source and destination MAC in targeted deauth attack
|
||||
int special_case_input_step;
|
||||
|
||||
33
applications/external/wifi_marauder_companion/wifi_marauder_pcap.c
vendored
Normal file
33
applications/external/wifi_marauder_companion/wifi_marauder_pcap.c
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
#include "wifi_marauder_app_i.h"
|
||||
#include "wifi_marauder_pcap.h"
|
||||
|
||||
void wifi_marauder_get_prefix_from_cmd(char* dest, const char* command) {
|
||||
int start, end, delta;
|
||||
start = strlen("sniff");
|
||||
end = strcspn(command, " ");
|
||||
delta = end - start;
|
||||
strncpy(dest, command + start, end - start);
|
||||
dest[delta] = '\0';
|
||||
}
|
||||
|
||||
void wifi_marauder_create_pcap_file(WifiMarauderApp* app) {
|
||||
char prefix[10];
|
||||
char capture_file_path[100];
|
||||
wifi_marauder_get_prefix_from_cmd(prefix, app->selected_tx_string);
|
||||
|
||||
int i = 0;
|
||||
do {
|
||||
snprintf(
|
||||
capture_file_path,
|
||||
sizeof(capture_file_path),
|
||||
"%s/%s_%d.pcap",
|
||||
MARAUDER_APP_FOLDER,
|
||||
prefix,
|
||||
i);
|
||||
i++;
|
||||
} while(storage_file_exists(app->storage, capture_file_path));
|
||||
|
||||
if(!storage_file_open(app->capture_file, capture_file_path, FSAM_WRITE, FSOM_CREATE_ALWAYS)) {
|
||||
dialog_message_show_storage_error(app->dialogs, "Cannot open pcap file");
|
||||
}
|
||||
}
|
||||
11
applications/external/wifi_marauder_companion/wifi_marauder_pcap.h
vendored
Normal file
11
applications/external/wifi_marauder_companion/wifi_marauder_pcap.h
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include "furi_hal.h"
|
||||
|
||||
/**
|
||||
* Creates a PCAP file to store incoming packets.
|
||||
* The file name will have a prefix according to the type of scan being performed by the application (Eg: raw_0.pcap)
|
||||
*
|
||||
* @param app Application context
|
||||
*/
|
||||
void wifi_marauder_create_pcap_file(WifiMarauderApp* app);
|
||||
@@ -2,10 +2,12 @@
|
||||
#include "wifi_marauder_uart.h"
|
||||
|
||||
#define UART_CH (FuriHalUartIdUSART1)
|
||||
#define LP_UART_CH (FuriHalUartIdLPUART1)
|
||||
#define BAUDRATE (115200)
|
||||
|
||||
struct WifiMarauderUart {
|
||||
WifiMarauderApp* app;
|
||||
FuriHalUartId channel;
|
||||
FuriThread* rx_thread;
|
||||
FuriStreamBuffer* rx_stream;
|
||||
uint8_t rx_buf[RX_BUF_SIZE + 1];
|
||||
@@ -60,25 +62,42 @@ void wifi_marauder_uart_tx(uint8_t* data, size_t len) {
|
||||
furi_hal_uart_tx(UART_CH, data, len);
|
||||
}
|
||||
|
||||
WifiMarauderUart* wifi_marauder_uart_init(WifiMarauderApp* app) {
|
||||
void wifi_marauder_lp_uart_tx(uint8_t* data, size_t len) {
|
||||
furi_hal_uart_tx(LP_UART_CH, data, len);
|
||||
}
|
||||
|
||||
WifiMarauderUart*
|
||||
wifi_marauder_uart_init(WifiMarauderApp* app, FuriHalUartId channel, const char* thread_name) {
|
||||
WifiMarauderUart* uart = malloc(sizeof(WifiMarauderUart));
|
||||
|
||||
uart->app = app;
|
||||
uart->channel = channel;
|
||||
uart->rx_stream = furi_stream_buffer_alloc(RX_BUF_SIZE, 1);
|
||||
uart->rx_thread = furi_thread_alloc();
|
||||
furi_thread_set_name(uart->rx_thread, "WifiMarauderUartRxThread");
|
||||
furi_thread_set_name(uart->rx_thread, thread_name);
|
||||
furi_thread_set_stack_size(uart->rx_thread, 1024);
|
||||
furi_thread_set_context(uart->rx_thread, uart);
|
||||
furi_thread_set_callback(uart->rx_thread, uart_worker);
|
||||
furi_thread_start(uart->rx_thread);
|
||||
|
||||
furi_hal_console_disable();
|
||||
furi_hal_uart_set_br(UART_CH, BAUDRATE);
|
||||
furi_hal_uart_set_irq_cb(UART_CH, wifi_marauder_uart_on_irq_cb, uart);
|
||||
if(channel == FuriHalUartIdUSART1) {
|
||||
furi_hal_console_disable();
|
||||
} else if(channel == FuriHalUartIdLPUART1) {
|
||||
furi_hal_uart_init(channel, BAUDRATE);
|
||||
}
|
||||
furi_hal_uart_set_br(channel, BAUDRATE);
|
||||
furi_hal_uart_set_irq_cb(channel, wifi_marauder_uart_on_irq_cb, uart);
|
||||
|
||||
return uart;
|
||||
}
|
||||
|
||||
WifiMarauderUart* wifi_marauder_usart_init(WifiMarauderApp* app) {
|
||||
return wifi_marauder_uart_init(app, UART_CH, "WifiMarauderUartRxThread");
|
||||
}
|
||||
|
||||
WifiMarauderUart* wifi_marauder_lp_uart_init(WifiMarauderApp* app) {
|
||||
return wifi_marauder_uart_init(app, LP_UART_CH, "WifiMarauderLPUartRxThread");
|
||||
}
|
||||
|
||||
void wifi_marauder_uart_free(WifiMarauderUart* uart) {
|
||||
furi_assert(uart);
|
||||
|
||||
@@ -86,7 +105,7 @@ void wifi_marauder_uart_free(WifiMarauderUart* uart) {
|
||||
furi_thread_join(uart->rx_thread);
|
||||
furi_thread_free(uart->rx_thread);
|
||||
|
||||
furi_hal_uart_set_irq_cb(UART_CH, NULL, NULL);
|
||||
furi_hal_uart_set_irq_cb(uart->channel, NULL, NULL);
|
||||
furi_hal_console_enable();
|
||||
|
||||
free(uart);
|
||||
|
||||
@@ -10,5 +10,7 @@ void wifi_marauder_uart_set_handle_rx_data_cb(
|
||||
WifiMarauderUart* uart,
|
||||
void (*handle_rx_data_cb)(uint8_t* buf, size_t len, void* context));
|
||||
void wifi_marauder_uart_tx(uint8_t* data, size_t len);
|
||||
WifiMarauderUart* wifi_marauder_uart_init(WifiMarauderApp* app);
|
||||
void wifi_marauder_lp_uart_tx(uint8_t* data, size_t len);
|
||||
WifiMarauderUart* wifi_marauder_usart_init(WifiMarauderApp* app);
|
||||
WifiMarauderUart* wifi_marauder_lp_uart_init(WifiMarauderApp* app);
|
||||
void wifi_marauder_uart_free(WifiMarauderUart* uart);
|
||||
|
||||
@@ -3,7 +3,6 @@ App(
|
||||
name="[WiFi] Scanner",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="wifi_scanner_app",
|
||||
cdefines=["APP_WIFI_SCANNER"],
|
||||
requires=["gui"],
|
||||
stack_size=2 * 1024,
|
||||
order=70,
|
||||
|
||||
@@ -663,7 +663,12 @@ int32_t wifi_scanner_app(void* p) {
|
||||
#else
|
||||
app->m_context = Initializing;
|
||||
#if ENABLE_MODULE_POWER
|
||||
furi_hal_power_enable_otg();
|
||||
uint8_t attempts = 0;
|
||||
while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) {
|
||||
furi_hal_power_enable_otg();
|
||||
furi_delay_ms(10);
|
||||
}
|
||||
furi_delay_ms(200);
|
||||
#endif // ENABLE_MODULE_POWER
|
||||
#endif // ENABLE_MODULE_DETECTION
|
||||
|
||||
@@ -722,7 +727,12 @@ int32_t wifi_scanner_app(void* p) {
|
||||
app->m_wifiModuleAttached = true;
|
||||
app->m_context = Initializing;
|
||||
#if ENABLE_MODULE_POWER
|
||||
furi_hal_power_enable_otg();
|
||||
uint8_t attempts2 = 0;
|
||||
while(!furi_hal_power_is_otg_enabled() && attempts2++ < 3) {
|
||||
furi_hal_power_enable_otg();
|
||||
furi_delay_ms(10);
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -851,7 +861,9 @@ int32_t wifi_scanner_app(void* p) {
|
||||
WIFI_APP_LOG_I("App freed");
|
||||
|
||||
#if ENABLE_MODULE_POWER
|
||||
furi_hal_power_disable_otg();
|
||||
if(furi_hal_power_is_otg_enabled()) {
|
||||
furi_hal_power_disable_otg();
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -3,7 +3,6 @@ App(
|
||||
name="Zombiez",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="zombiez_game_app",
|
||||
cdefines=["APP_ZOMBIEZ_GAME"],
|
||||
requires=["gui"],
|
||||
stack_size=2 * 1024,
|
||||
order=280,
|
||||
|
||||
@@ -7,6 +7,7 @@ App(
|
||||
requires=[
|
||||
"gui",
|
||||
"storage",
|
||||
"loader",
|
||||
],
|
||||
stack_size=int(1.5 * 1024),
|
||||
icon="A_Plugins_14",
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
#include "compilesort.hpp"
|
||||
#include "elf_hashtable.h"
|
||||
#include "elf_hashtable_entry.h"
|
||||
#include "elf_hashtable_checks.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <algorithm>
|
||||
|
||||
/* Generated table */
|
||||
#include <symbols.h>
|
||||
|
||||
#define TAG "elf_hashtable"
|
||||
|
||||
static_assert(!has_hash_collisions(elf_api_table), "Detected API method hash collision!");
|
||||
|
||||
/**
|
||||
* Get function address by function name
|
||||
* @param name function name
|
||||
* @param address output for function address
|
||||
* @return true if the table contains a function
|
||||
*/
|
||||
|
||||
bool elf_resolve_from_hashtable(const char* name, Elf32_Addr* address) {
|
||||
bool result = false;
|
||||
uint32_t gnu_sym_hash = elf_gnu_hash(name);
|
||||
|
||||
sym_entry key = {
|
||||
.hash = gnu_sym_hash,
|
||||
.address = 0,
|
||||
};
|
||||
|
||||
auto find_res = std::lower_bound(elf_api_table.cbegin(), elf_api_table.cend(), key);
|
||||
if((find_res == elf_api_table.cend() || (find_res->hash != gnu_sym_hash))) {
|
||||
FURI_LOG_W(TAG, "Can't find symbol '%s' (hash %lx)!", name, gnu_sym_hash);
|
||||
result = false;
|
||||
} else {
|
||||
result = true;
|
||||
*address = find_res->address;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
const ElfApiInterface hashtable_api_interface = {
|
||||
.api_version_major = (elf_api_version >> 16),
|
||||
.api_version_minor = (elf_api_version & 0xFFFF),
|
||||
.resolver_callback = &elf_resolve_from_hashtable,
|
||||
};
|
||||
@@ -1,14 +0,0 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
#include <flipper_application/elf/elf_api_interface.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern const ElfApiInterface hashtable_api_interface;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,18 +0,0 @@
|
||||
/**
|
||||
* Check for multiple entries with the same hash value at compilation time.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <array>
|
||||
#include "elf_hashtable_entry.h"
|
||||
|
||||
template <std::size_t N>
|
||||
constexpr bool has_hash_collisions(const std::array<sym_entry, N> api_methods) {
|
||||
for(std::size_t i = 0; i < (N - 1); ++i) {
|
||||
if(api_methods[i].hash == api_methods[i + 1].hash) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct sym_entry {
|
||||
uint32_t hash;
|
||||
uint32_t address;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
#include <array>
|
||||
#include <algorithm>
|
||||
|
||||
#define API_METHOD(x, ret_type, args_type) \
|
||||
sym_entry { \
|
||||
.hash = elf_gnu_hash(#x), .address = (uint32_t)(static_cast<ret_type(*) args_type>(x)) \
|
||||
}
|
||||
|
||||
#define API_VARIABLE(x, var_type) \
|
||||
sym_entry { \
|
||||
.hash = elf_gnu_hash(#x), .address = (uint32_t)(&(x)), \
|
||||
}
|
||||
|
||||
constexpr bool operator<(const sym_entry& k1, const sym_entry& k2) {
|
||||
return k1.hash < k2.hash;
|
||||
}
|
||||
|
||||
constexpr uint32_t elf_gnu_hash(const char* s) {
|
||||
uint32_t h = 0x1505;
|
||||
for(unsigned char c = *s; c != '\0'; c = *++s) {
|
||||
h = (h << 5) + h + c;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -7,7 +7,7 @@
|
||||
#include <dialogs/dialogs.h>
|
||||
#include <toolbox/path.h>
|
||||
#include <flipper_application/flipper_application.h>
|
||||
#include "elf_cpp/elf_hashtable.h"
|
||||
#include <loader/firmware_api/firmware_api.h>
|
||||
#include "fap_loader_app.h"
|
||||
|
||||
#define TAG "fap_loader_app"
|
||||
@@ -27,7 +27,7 @@ bool fap_loader_load_name_and_icon(
|
||||
Storage* storage,
|
||||
uint8_t** icon_ptr,
|
||||
FuriString* item_name) {
|
||||
FlipperApplication* app = flipper_application_alloc(storage, &hashtable_api_interface);
|
||||
FlipperApplication* app = flipper_application_alloc(storage, firmware_api_interface);
|
||||
|
||||
FlipperApplicationPreloadStatus preload_res =
|
||||
flipper_application_preload_manifest(app, furi_string_get_cstr(path));
|
||||
@@ -71,7 +71,7 @@ static bool fap_loader_run_selected_app(FapLoader* loader) {
|
||||
bool show_error = true;
|
||||
do {
|
||||
file_selected = true;
|
||||
loader->app = flipper_application_alloc(loader->storage, &hashtable_api_interface);
|
||||
loader->app = flipper_application_alloc(loader->storage, firmware_api_interface);
|
||||
size_t start = furi_get_tick();
|
||||
|
||||
FURI_LOG_I(TAG, "FAP Loader is loading %s", furi_string_get_cstr(loader->fap_path));
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include <furi_hal.h>
|
||||
#include <cli/cli.h>
|
||||
#include <lib/toolbox/args.h>
|
||||
#include <lib/toolbox/hex.h>
|
||||
|
||||
#include <lib/nfc/nfc_types.h>
|
||||
#include <lib/nfc/nfc_device.h>
|
||||
@@ -12,6 +13,7 @@ static void nfc_cli_print_usage() {
|
||||
printf("Cmd list:\r\n");
|
||||
printf("\tdetect\t - detect nfc device\r\n");
|
||||
printf("\temulate\t - emulate predefined nfca card\r\n");
|
||||
printf("\tapdu\t - Send APDU and print response \r\n");
|
||||
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
|
||||
printf("\tfield\t - turn field on\r\n");
|
||||
}
|
||||
@@ -98,6 +100,67 @@ static void nfc_cli_field(Cli* cli, FuriString* args) {
|
||||
furi_hal_nfc_sleep();
|
||||
}
|
||||
|
||||
static void nfc_cli_apdu(Cli* cli, FuriString* args) {
|
||||
UNUSED(cli);
|
||||
if(furi_hal_nfc_is_busy()) {
|
||||
printf("Nfc is busy\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
furi_hal_nfc_exit_sleep();
|
||||
FuriString* data = NULL;
|
||||
data = furi_string_alloc();
|
||||
FuriHalNfcTxRxContext tx_rx = {};
|
||||
FuriHalNfcDevData dev_data = {};
|
||||
uint8_t* req_buffer = NULL;
|
||||
uint8_t* resp_buffer = NULL;
|
||||
size_t apdu_size = 0;
|
||||
size_t resp_size = 0;
|
||||
|
||||
do {
|
||||
if(!args_read_string_and_trim(args, data)) {
|
||||
printf(
|
||||
"Use like `nfc apdu 00a404000e325041592e5359532e444446303100 00a4040008a0000003010102` \r\n");
|
||||
break;
|
||||
}
|
||||
|
||||
printf("detecting tag\r\n");
|
||||
if(!furi_hal_nfc_detect(&dev_data, 300)) {
|
||||
printf("Failed to detect tag\r\n");
|
||||
break;
|
||||
}
|
||||
do {
|
||||
apdu_size = furi_string_size(data) / 2;
|
||||
req_buffer = malloc(apdu_size);
|
||||
hex_chars_to_uint8(furi_string_get_cstr(data), req_buffer);
|
||||
|
||||
memcpy(tx_rx.tx_data, req_buffer, apdu_size);
|
||||
tx_rx.tx_bits = apdu_size * 8;
|
||||
tx_rx.tx_rx_type = FuriHalNfcTxRxTypeDefault;
|
||||
|
||||
printf("Sending APDU:%s to Tag\r\n", furi_string_get_cstr(data));
|
||||
if(!furi_hal_nfc_tx_rx(&tx_rx, 300)) {
|
||||
printf("Failed to tx_rx\r\n");
|
||||
break;
|
||||
}
|
||||
resp_size = (tx_rx.rx_bits / 8) * 2;
|
||||
resp_buffer = malloc(resp_size);
|
||||
uint8_to_hex_chars(tx_rx.rx_data, resp_buffer, resp_size);
|
||||
resp_buffer[resp_size] = 0;
|
||||
printf("Response: %s\r\n", resp_buffer);
|
||||
free(req_buffer);
|
||||
free(resp_buffer);
|
||||
req_buffer = NULL;
|
||||
resp_buffer = NULL;
|
||||
} while(args_read_string_and_trim(args, data));
|
||||
} while(false);
|
||||
|
||||
free(req_buffer);
|
||||
free(resp_buffer);
|
||||
furi_string_free(data);
|
||||
furi_hal_nfc_sleep();
|
||||
}
|
||||
|
||||
static void nfc_cli(Cli* cli, FuriString* args, void* context) {
|
||||
UNUSED(context);
|
||||
FuriString* cmd;
|
||||
@@ -117,6 +180,11 @@ static void nfc_cli(Cli* cli, FuriString* args, void* context) {
|
||||
break;
|
||||
}
|
||||
|
||||
if(furi_string_cmp_str(cmd, "apdu") == 0) {
|
||||
nfc_cli_apdu(cli, args);
|
||||
break;
|
||||
}
|
||||
|
||||
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
|
||||
if(furi_string_cmp_str(cmd, "field") == 0) {
|
||||
nfc_cli_field(cli, args);
|
||||
|
||||
@@ -115,7 +115,8 @@ bool nfc_scene_mf_classic_dict_attack_on_event(void* context, SceneManagerEvent
|
||||
consumed = true;
|
||||
}
|
||||
} else if(event.event == NfcWorkerEventAborted) {
|
||||
if(state == DictAttackStateUserDictInProgress) {
|
||||
if(state == DictAttackStateUserDictInProgress &&
|
||||
dict_attack_get_card_state(nfc->dict_attack)) {
|
||||
nfc_scene_mf_classic_dict_attack_prepare_view(nfc, state);
|
||||
consumed = true;
|
||||
} else {
|
||||
|
||||
@@ -11,6 +11,7 @@ struct DictAttack {
|
||||
View* view;
|
||||
DictAttackCallback callback;
|
||||
void* context;
|
||||
bool card_present;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
@@ -162,6 +163,7 @@ void dict_attack_set_header(DictAttack* dict_attack, const char* header) {
|
||||
|
||||
void dict_attack_set_card_detected(DictAttack* dict_attack, MfClassicType type) {
|
||||
furi_assert(dict_attack);
|
||||
dict_attack->card_present = true;
|
||||
with_view_model(
|
||||
dict_attack->view,
|
||||
DictAttackViewModel * model,
|
||||
@@ -175,6 +177,7 @@ void dict_attack_set_card_detected(DictAttack* dict_attack, MfClassicType type)
|
||||
|
||||
void dict_attack_set_card_removed(DictAttack* dict_attack) {
|
||||
furi_assert(dict_attack);
|
||||
dict_attack->card_present = false;
|
||||
with_view_model(
|
||||
dict_attack->view,
|
||||
DictAttackViewModel * model,
|
||||
@@ -182,6 +185,11 @@ void dict_attack_set_card_removed(DictAttack* dict_attack) {
|
||||
true);
|
||||
}
|
||||
|
||||
bool dict_attack_get_card_state(DictAttack* dict_attack) {
|
||||
furi_assert(dict_attack);
|
||||
return dict_attack->card_present;
|
||||
}
|
||||
|
||||
void dict_attack_set_sector_read(DictAttack* dict_attack, uint8_t sec_read) {
|
||||
furi_assert(dict_attack);
|
||||
with_view_model(
|
||||
|
||||
@@ -25,6 +25,8 @@ void dict_attack_set_card_detected(DictAttack* dict_attack, MfClassicType type);
|
||||
|
||||
void dict_attack_set_card_removed(DictAttack* dict_attack);
|
||||
|
||||
bool dict_attack_get_card_state(DictAttack* dict_attack);
|
||||
|
||||
void dict_attack_set_sector_read(DictAttack* dict_attack, uint8_t sec_read);
|
||||
|
||||
void dict_attack_set_keys_found(DictAttack* dict_attack, uint8_t keys_found);
|
||||
|
||||
@@ -11,7 +11,14 @@ typedef enum {
|
||||
SubmenuIndexBFTClone,
|
||||
SubmenuIndexBFTMitto,
|
||||
SubmenuIndexSomfyTelis,
|
||||
SubmenuIndexPricenton,
|
||||
SubmenuIndexBeninca433,
|
||||
SubmenuIndexBeninca868,
|
||||
SubmenuIndexIronLogic,
|
||||
SubmenuIndexElmesElectronic,
|
||||
SubmenuIndexSommer_FM_434,
|
||||
SubmenuIndexSommer_FM_868,
|
||||
SubmenuIndexDTMNeo433,
|
||||
SubmenuIndexGibidi433,
|
||||
SubmenuIndexNiceFlo12bit,
|
||||
SubmenuIndexNiceFlo24bit,
|
||||
SubmenuIndexNiceFlorS_433_92,
|
||||
@@ -19,14 +26,19 @@ typedef enum {
|
||||
SubmenuIndexNiceSmilo_433_92,
|
||||
SubmenuIndexCAME12bit,
|
||||
SubmenuIndexCAME24bit,
|
||||
SubmenuIndexBETT_433,
|
||||
SubmenuIndexCAME12bit868,
|
||||
SubmenuIndexCAME24bit868,
|
||||
SubmenuIndexCAMETwee,
|
||||
SubmenuIndexCAMESpace,
|
||||
SubmenuIndexPricenton,
|
||||
SubmenuIndexPricenton315,
|
||||
SubmenuIndexBETT_433,
|
||||
SubmenuIndexLinear_300_00,
|
||||
SubmenuIndexNeroSketch,
|
||||
SubmenuIndexNeroRadio,
|
||||
SubmenuIndexGateTX,
|
||||
SubmenuIndexDoorHan_315_00,
|
||||
SubmenuIndexDoorHan_433_92,
|
||||
SubmenuIndexLinear_300_00,
|
||||
SubmenuIndexLiftMaster_315_00,
|
||||
SubmenuIndexLiftMaster_390_00,
|
||||
SubmenuIndexLiftMaster_433_00,
|
||||
|
||||
@@ -37,10 +37,13 @@ const char* const debug_counter_text[DEBUG_COUNTER_COUNT] = {
|
||||
static void subghz_scene_ext_module_changed(VariableItem* item) {
|
||||
SubGhz* subghz = variable_item_get_context(item);
|
||||
value_index_exm = variable_item_get_current_value_index(item);
|
||||
UNUSED(subghz);
|
||||
|
||||
variable_item_set_current_value_text(item, radio_modules_variables_text[value_index_exm]);
|
||||
|
||||
subghz->last_settings->external_module_enabled = value_index_exm == 1;
|
||||
subghz_last_settings_save(subghz->last_settings);
|
||||
}
|
||||
|
||||
static void subghz_ext_module_start_var_list_enter_callback(void* context, uint32_t index) {
|
||||
SubGhz* subghz = context;
|
||||
view_dispatcher_send_custom_event(subghz->view_dispatcher, index);
|
||||
@@ -85,6 +88,7 @@ static void subghz_scene_receiver_config_set_debug_counter(VariableItem* item) {
|
||||
}
|
||||
|
||||
static void subghz_scene_receiver_config_set_ext_mod_power(VariableItem* item) {
|
||||
SubGhz* subghz = variable_item_get_context(item);
|
||||
uint8_t index = variable_item_get_current_value_index(item);
|
||||
|
||||
variable_item_set_current_value_text(item, ext_mod_power_text[index]);
|
||||
@@ -95,6 +99,9 @@ static void subghz_scene_receiver_config_set_ext_mod_power(VariableItem* item) {
|
||||
} else {
|
||||
furi_hal_subghz_enable_ext_power();
|
||||
}
|
||||
|
||||
subghz->last_settings->external_module_power_5v_disable = index == 1;
|
||||
subghz_last_settings_save(subghz->last_settings);
|
||||
}
|
||||
|
||||
void subghz_scene_ext_module_settings_on_enter(void* context) {
|
||||
@@ -181,7 +188,7 @@ bool subghz_scene_ext_module_settings_on_event(void* context, SceneManagerEvent
|
||||
// Check if module is present, if no -> show error
|
||||
if(!furi_hal_subghz_check_radio()) {
|
||||
value_index_exm = 0;
|
||||
furi_hal_subghz_set_radio_type(value_index_exm);
|
||||
furi_hal_subghz_set_radio_type(SubGhzRadioInternal);
|
||||
furi_string_set(subghz->error_str, "Please connect\nexternal radio");
|
||||
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowErrorSub);
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <lib/subghz/protocols/alutech_at_4n.h>
|
||||
#include <lib/subghz/protocols/nice_flor_s.h>
|
||||
#include <lib/subghz/protocols/somfy_telis.h>
|
||||
#include <lib/subghz/protocols/secplus_v2.h>
|
||||
|
||||
void subghz_scene_receiver_info_callback(GuiButtonType result, InputType type, void* context) {
|
||||
furi_assert(context);
|
||||
@@ -240,6 +241,7 @@ void subghz_scene_receiver_info_on_exit(void* context) {
|
||||
alutech_reset_original_btn();
|
||||
nice_flors_reset_original_btn();
|
||||
somfy_telis_reset_original_btn();
|
||||
secplus2_reset_original_btn();
|
||||
star_line_reset_mfname();
|
||||
star_line_reset_kl_type();
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <lib/subghz/protocols/alutech_at_4n.h>
|
||||
#include <lib/subghz/protocols/nice_flor_s.h>
|
||||
#include <lib/subghz/protocols/somfy_telis.h>
|
||||
#include <lib/subghz/protocols/secplus_v2.h>
|
||||
|
||||
#include "xtreme/assets.h"
|
||||
|
||||
@@ -118,6 +119,7 @@ void subghz_scene_rpc_on_exit(void* context) {
|
||||
alutech_reset_original_btn();
|
||||
nice_flors_reset_original_btn();
|
||||
somfy_telis_reset_original_btn();
|
||||
secplus2_reset_original_btn();
|
||||
star_line_reset_mfname();
|
||||
star_line_reset_kl_type();
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user