[FL-1546, FL-1534, FL-1550] Drop F5, Certification preparation, Global application start lock (#585)

* Firmware: drop F5 target
* Rename app-loader to loader
* Update code owners file
* Loader: global application start lock API, minor refactoring
* Archive: update loader usage
* Cli: Command flags, global application start lock
* Apps: update cli API usage
* Bootloader: minor refactoring
* Firmware: minor build refactoring
* SubGhz: GUI packet test
* SubGhz: drop packet transmission and unused presets
* Github: drop F5 from build
* Archive: favorites
* Archive: a little bit more of Favorites
This commit is contained in:
あく
2021-07-18 21:09:00 +03:00
committed by GitHub
parent fbb81483ae
commit 421a0f6b97
235 changed files with 763 additions and 26959 deletions
-257
View File
@@ -1,257 +0,0 @@
#include "app-loader.h"
#include "api-hal-delay.h"
#define APP_LOADER_TAG "app_loader"
typedef struct {
FuriThread* thread;
const FlipperApplication* current_app;
string_t args;
Cli* cli;
size_t free_heap_size;
} AppLoaderState;
static AppLoaderState state;
// TODO add mutex for contex
static void app_loader_menu_callback(void* _ctx) {
furi_assert(_ctx);
const FlipperApplication* flipper_app = (FlipperApplication*)_ctx;
furi_assert(flipper_app->app);
furi_assert(flipper_app->name);
if(furi_thread_get_state(state.thread) != FuriThreadStateStopped) {
FURI_LOG_E(APP_LOADER_TAG, "Can't start app. %s is running", state.current_app->name);
return;
}
api_hal_power_insomnia_enter();
state.current_app = flipper_app;
FURI_LOG_I(APP_LOADER_TAG, "Starting furi application: %s", state.current_app->name);
furi_thread_set_name(state.thread, flipper_app->name);
furi_thread_set_stack_size(state.thread, flipper_app->stack_size);
furi_thread_set_context(state.thread, NULL);
furi_thread_set_callback(state.thread, flipper_app->app);
furi_thread_start(state.thread);
}
static void app_loader_cli_callback(Cli* cli, string_t args, void* _ctx) {
furi_assert(_ctx);
const FlipperApplication* flipper_app = (FlipperApplication*)_ctx;
furi_assert(flipper_app->app);
furi_assert(flipper_app->name);
if(furi_thread_get_state(state.thread) != FuriThreadStateStopped) {
printf("Can't start, furi application is running");
return;
}
api_hal_power_insomnia_enter();
state.current_app = flipper_app;
printf("Starting furi application %s", state.current_app->name);
furi_thread_set_name(state.thread, flipper_app->name);
furi_thread_set_stack_size(state.thread, flipper_app->stack_size);
furi_thread_set_callback(state.thread, flipper_app->app);
furi_thread_start(state.thread);
}
bool app_loader_start(const char* name, const char* args) {
furi_assert(name);
const FlipperApplication* flipper_app = NULL;
// Search for application
for(size_t i = 0; i < FLIPPER_APPS_COUNT; i++) {
if(!strcmp(FLIPPER_APPS[i].name, name)) {
flipper_app = &FLIPPER_APPS[i];
break;
}
}
if(!flipper_app) {
FURI_LOG_E(APP_LOADER_TAG, "Can't find application with name %s", name);
return false;
}
if(furi_thread_get_state(state.thread) != FuriThreadStateStopped) {
FURI_LOG_E(APP_LOADER_TAG, "Can't start app. %s is running", state.current_app->name);
return false;
}
state.current_app = flipper_app;
if(args) {
string_set_str(state.args, args);
string_strim(state.args);
FURI_LOG_I(APP_LOADER_TAG, "Start %s app with args: %s", name, args);
} else {
string_clean(state.args);
FURI_LOG_I(APP_LOADER_TAG, "Start %s app with no args", name);
}
furi_thread_set_name(state.thread, flipper_app->name);
furi_thread_set_stack_size(state.thread, flipper_app->stack_size);
furi_thread_set_context(state.thread, (void*)string_get_cstr(state.args));
furi_thread_set_callback(state.thread, flipper_app->app);
return furi_thread_start(state.thread);
}
void app_loader_thread_state_callback(FuriThreadState thread_state, void* context) {
furi_assert(context);
AppLoaderState* state = context;
if(thread_state == FuriThreadStateRunning) {
state->free_heap_size = xPortGetFreeHeapSize();
} else if(thread_state == FuriThreadStateStopped) {
/*
* Current Leak Sanitizer assumes that memory is allocated and freed
* inside one thread. Timers are allocated in one task, but freed in
* Timer-Task thread, and xTimerDelete() just put command to queue.
* To avoid some bad cases there are few fixes:
* 1) delay for Timer to process commands
* 2) there are 'heap diff' which shows difference in heap before task
* started and after task completed. In process of leakage monitoring
* both values should be taken into account.
*/
delay(20);
int heap_diff = state->free_heap_size - xPortGetFreeHeapSize();
FURI_LOG_I(
APP_LOADER_TAG,
"Application thread stopped. Heap allocation balance: %d. Thread allocation balance: %d.",
heap_diff,
furi_thread_get_heap_size(state->thread));
api_hal_power_insomnia_exit();
}
}
int32_t app_loader(void* p) {
FURI_LOG_I(APP_LOADER_TAG, "Starting");
state.thread = furi_thread_alloc();
furi_thread_enable_heap_trace(state.thread);
furi_thread_set_state_context(state.thread, &state);
furi_thread_set_state_callback(state.thread, app_loader_thread_state_callback);
string_init(state.args);
ValueMutex* menu_mutex = furi_record_open("menu");
state.cli = furi_record_open("cli");
// Main menu
FURI_LOG_I(APP_LOADER_TAG, "Building main menu");
with_value_mutex(
menu_mutex, (Menu * menu) {
for(size_t i = 0; i < FLIPPER_APPS_COUNT; i++) {
// Add menu item
menu_item_add(
menu,
menu_item_alloc_function(
FLIPPER_APPS[i].name,
icon_animation_alloc(FLIPPER_APPS[i].icon),
app_loader_menu_callback,
(void*)&FLIPPER_APPS[i]));
// Add cli command
string_t cli_name;
string_init_set_str(cli_name, "app_");
string_cat_str(cli_name, FLIPPER_APPS[i].name);
cli_add_command(
state.cli,
string_get_cstr(cli_name),
app_loader_cli_callback,
(void*)&FLIPPER_APPS[i]);
string_clear(cli_name);
}
});
// Plugins
FURI_LOG_I(APP_LOADER_TAG, "Building plugins menu");
with_value_mutex(
menu_mutex, (Menu * menu) {
MenuItem* menu_plugins =
menu_item_alloc_menu("Plugins", icon_animation_alloc(&A_Plugins_14));
for(size_t i = 0; i < FLIPPER_PLUGINS_COUNT; i++) {
// Add menu item
menu_item_subitem_add(
menu_plugins,
menu_item_alloc_function(
FLIPPER_PLUGINS[i].name,
icon_animation_alloc(FLIPPER_PLUGINS[i].icon),
app_loader_menu_callback,
(void*)&FLIPPER_PLUGINS[i]));
// Add cli command
string_t cli_name;
string_init_set_str(cli_name, "app_");
string_cat_str(cli_name, FLIPPER_PLUGINS[i].name);
cli_add_command(
state.cli,
string_get_cstr(cli_name),
app_loader_cli_callback,
(void*)&FLIPPER_PLUGINS[i]);
string_clear(cli_name);
}
menu_item_add(menu, menu_plugins);
});
// Debug
FURI_LOG_I(APP_LOADER_TAG, "Building debug menu");
with_value_mutex(
menu_mutex, (Menu * menu) {
MenuItem* menu_debug =
menu_item_alloc_menu("Debug tools", icon_animation_alloc(&A_Settings_14));
for(size_t i = 0; i < FLIPPER_DEBUG_APPS_COUNT; i++) {
// Add menu item
menu_item_subitem_add(
menu_debug,
menu_item_alloc_function(
FLIPPER_DEBUG_APPS[i].name,
icon_animation_alloc(FLIPPER_DEBUG_APPS[i].icon),
app_loader_menu_callback,
(void*)&FLIPPER_DEBUG_APPS[i]));
// Add cli command
string_t cli_name;
string_init_set_str(cli_name, "app_");
string_cat_str(cli_name, FLIPPER_DEBUG_APPS[i].name);
cli_add_command(
state.cli,
string_get_cstr(cli_name),
app_loader_cli_callback,
(void*)&FLIPPER_DEBUG_APPS[i]);
string_clear(cli_name);
}
menu_item_add(menu, menu_debug);
});
// Settings
FURI_LOG_I(APP_LOADER_TAG, "Building settings menu");
with_value_mutex(
menu_mutex, (Menu * menu) {
MenuItem* menu_debug =
menu_item_alloc_menu("Settings", icon_animation_alloc(&A_Settings_14));
for(size_t i = 0; i < FLIPPER_SETTINGS_APPS_COUNT; i++) {
// Add menu item
menu_item_subitem_add(
menu_debug,
menu_item_alloc_function(
FLIPPER_SETTINGS_APPS[i].name,
icon_animation_alloc(FLIPPER_SETTINGS_APPS[i].icon),
app_loader_menu_callback,
(void*)&FLIPPER_SETTINGS_APPS[i]));
}
menu_item_add(menu, menu_debug);
});
// Call on start hooks
for(size_t i = 0; i < FLIPPER_ON_SYSTEM_START_COUNT; i++) {
(*FLIPPER_ON_SYSTEM_START[i])();
}
FURI_LOG_I(APP_LOADER_TAG, "Started");
while(1) {
osThreadSuspend(osThreadGetId());
}
return 0;
}
-15
View File
@@ -1,15 +0,0 @@
#include <furi.h>
#include <cli/cli.h>
#include "menu/menu.h"
#include "menu/menu_item.h"
#include "applications.h"
#include <assets_icons.h>
#include <api-hal.h>
/**
* Start application
* @param name - application name
* @param args - application arguments
* @retval true on success
*/
bool app_loader_start(const char* name, const char* args);
+2 -6
View File
@@ -14,7 +14,7 @@ int32_t coreglitch_demo_0(void* p);
int32_t u8g2_qrcode(void* p);
int32_t gui_task(void* p);
int32_t irda(void* p);
int32_t app_loader(void* p);
int32_t loader(void* p);
int32_t nfc_task(void* p);
int32_t dolphin_task(void* p);
int32_t power_task(void* p);
@@ -78,7 +78,7 @@ const FlipperApplication FLIPPER_SERVICES[] = {
#ifdef SRV_MENU
{.app = menu_task, .name = "menu_task", .stack_size = 1024, .icon = &A_Plugins_14},
{.app = app_loader, .name = "app_loader", .stack_size = 1024, .icon = &A_Plugins_14},
{.app = loader, .name = "loader", .stack_size = 1024, .icon = &A_Plugins_14},
#endif
#ifdef SRV_SD_FILESYSTEM
@@ -197,10 +197,6 @@ const FlipperApplication FLIPPER_APPS[] = {
{.app = app_gpio_test, .name = "GPIO", .stack_size = 1024, .icon = &A_GPIO_14},
#endif
#ifdef APP_ARCHIVE
{.app = app_archive, .name = "Archive", .stack_size = 4096, .icon = &A_FileManager_14},
#endif
};
const size_t FLIPPER_APPS_COUNT = sizeof(FLIPPER_APPS) / sizeof(FlipperApplication);
+6 -6
View File
@@ -19,31 +19,31 @@ extern const FlipperApplication FLIPPER_SERVICES[];
extern const size_t FLIPPER_SERVICES_COUNT;
/* Apps list
* Spawned by app-loader
* Spawned by loader
*/
extern const FlipperApplication FLIPPER_APPS[];
extern const size_t FLIPPER_APPS_COUNT;
/* On system start hooks
* Called by app-loader, after OS initialization complete
* Called by loader, after OS initialization complete
*/
extern const FlipperOnStartHook FLIPPER_ON_SYSTEM_START[];
extern const size_t FLIPPER_ON_SYSTEM_START_COUNT;
/* Plugins list
* Spawned by app-loader
* Spawned by loader
*/
extern const FlipperApplication FLIPPER_PLUGINS[];
extern const size_t FLIPPER_PLUGINS_COUNT;
/* Debug menu apps
* Spawned by app-loader
* Spawned by loader
*/
extern const FlipperApplication FLIPPER_DEBUG_APPS[];
extern const size_t FLIPPER_DEBUG_APPS_COUNT;
/* Seperate scene app holder
* Spawned by app-loader
* Spawned by loader
*/
extern const FlipperApplication FLIPPER_SCENE;
extern const FlipperApplication FLIPPER_SCENE_APPS[];
@@ -52,7 +52,7 @@ extern const size_t FLIPPER_SCENE_APPS_COUNT;
extern const FlipperApplication FLIPPER_ARCHIVE;
/* Settings list
* Spawned by app-loader
* Spawned by loader
*/
extern const FlipperApplication FLIPPER_SETTINGS_APPS[];
extern const size_t FLIPPER_SETTINGS_APPS_COUNT;
+16 -13
View File
@@ -2,13 +2,13 @@
static bool archive_get_filenames(ArchiveApp* archive);
static bool is_favourite(ArchiveApp* archive, ArchiveFile_t* file) {
static bool is_favorite(ArchiveApp* archive, ArchiveFile_t* file) {
FS_Common_Api* common_api = &archive->fs_api->common;
FileInfo file_info;
FS_Error fr;
string_t path;
string_init_printf(path, "favourites/%s", string_get_cstr(file->name));
string_init_printf(path, "favorites/%s", string_get_cstr(file->name));
fr = common_api->info(string_get_cstr(path), &file_info, NULL, 0);
FURI_LOG_I("FAV", "%d", fr);
@@ -223,11 +223,11 @@ static uint32_t archive_previous_callback(void* context) {
}
/* file menu */
static void archive_add_to_favourites(ArchiveApp* archive) {
static void archive_add_to_favorites(ArchiveApp* archive) {
furi_assert(archive);
FS_Common_Api* common_api = &archive->fs_api->common;
common_api->mkdir("favourites");
common_api->mkdir("favorites");
FS_File_Api* file_api = &archive->fs_api->file;
File src;
@@ -246,7 +246,7 @@ static void archive_add_to_favourites(ArchiveApp* archive) {
"%s/%s",
string_get_cstr(archive->browser.path),
string_get_cstr(archive->browser.name));
string_init_printf(buffer_dst, "/favourites/%s", string_get_cstr(archive->browser.name));
string_init_printf(buffer_dst, "/favorites/%s", string_get_cstr(archive->browser.name));
fr = file_api->open(&src, string_get_cstr(buffer_src), FSAM_READ, FSOM_OPEN_EXISTING);
FURI_LOG_I("FATFS", "OPEN: %d", fr);
@@ -341,7 +341,7 @@ static void archive_show_file_menu(ArchiveApp* archive) {
selected = files_array_get(model->files, model->idx);
model->menu = true;
model->menu_idx = 0;
selected->fav = is_favourite(archive, selected);
selected->fav = is_favorite(archive, selected);
return true;
});
@@ -364,7 +364,7 @@ static void archive_open_app(ArchiveApp* archive, const char* app_name, const ch
furi_assert(archive);
furi_assert(app_name);
app_loader_start(app_name, args);
loader_start(archive->loader, app_name, args);
}
static void archive_delete_file(ArchiveApp* archive, ArchiveFile_t* file, bool fav, bool orig) {
@@ -381,7 +381,7 @@ static void archive_delete_file(ArchiveApp* archive, ArchiveFile_t* file, bool f
common_api->remove(string_get_cstr(path));
} else { // remove from favorites
string_printf(path, "favourites/%s", string_get_cstr(file->name));
string_printf(path, "favorites/%s", string_get_cstr(file->name));
common_api->remove(string_get_cstr(path));
if(orig) { // remove original file
@@ -434,11 +434,11 @@ static void archive_file_menu_callback(ArchiveApp* archive) {
break;
case 1:
if(is_known_app(selected->type)) {
if(!is_favourite(archive, selected)) {
if(!is_favorite(archive, selected)) {
string_set(archive->browser.name, selected->name);
archive_add_to_favourites(archive);
archive_add_to_favorites(archive);
} else {
// delete from favourites
// delete from favorites
archive_delete_file(archive, selected, true, false);
}
archive_close_file_menu(archive);
@@ -452,7 +452,7 @@ static void archive_file_menu_callback(ArchiveApp* archive) {
break;
case 3:
// confirmation?
if(is_favourite(archive, selected)) {
if(is_favorite(archive, selected)) {
//delete both fav & original
archive_delete_file(archive, selected, true, true);
} else {
@@ -608,6 +608,8 @@ void archive_free(ArchiveApp* archive) {
archive->fs_api = NULL;
furi_record_close("gui");
archive->gui = NULL;
furi_record_close("loader");
archive->loader = NULL;
furi_thread_free(archive->app_thread);
furi_check(osMessageQueueDelete(archive->event_queue) == osOK);
@@ -620,6 +622,7 @@ ArchiveApp* archive_alloc() {
archive->event_queue = osMessageQueueNew(8, sizeof(AppEvent), NULL);
archive->app_thread = furi_thread_alloc();
archive->gui = furi_record_open("gui");
archive->loader = furi_record_open("loader");
archive->fs_api = furi_record_open("sdcard");
archive->text_input = text_input_alloc();
archive->view_archive_main = view_alloc();
@@ -649,7 +652,7 @@ ArchiveApp* archive_alloc() {
view_dispatcher_attach_to_gui(
archive->view_dispatcher, archive->gui, ViewDispatcherTypeFullscreen);
view_dispatcher_switch_to_view(archive->view_dispatcher, ArchiveTabFavourites);
view_dispatcher_switch_to_view(archive->view_dispatcher, ArchiveTabFavorites);
return archive;
}
+3 -2
View File
@@ -6,7 +6,7 @@
#include <gui/gui_i.h>
#include <gui/view_dispatcher.h>
#include <gui/modules/text_input.h>
#include <app-loader/app-loader.h>
#include <loader/loader.h>
#include <m-string.h>
#include <m-array.h>
@@ -41,7 +41,7 @@ static const char* known_ext[] = {
};
static const char* tab_default_paths[] = {
[ArchiveTabFavourites] = "favourites",
[ArchiveTabFavorites] = "favorites",
[ArchiveTabIButton] = "ibutton",
[ArchiveTabNFC] = "nfc",
[ArchiveTabSubOne] = "subone",
@@ -112,6 +112,7 @@ typedef struct {
struct ArchiveApp {
osMessageQueueId_t event_queue;
FuriThread* app_thread;
Loader* loader;
Gui* gui;
ViewDispatcher* view_dispatcher;
View* view_archive_main;
+1 -1
View File
@@ -1,7 +1,7 @@
#include "archive_views.h"
static const char* ArchiveTabNames[] = {
[ArchiveTabFavourites] = "Favourites",
[ArchiveTabFavorites] = "Favorites",
[ArchiveTabIButton] = "iButton",
[ArchiveTabNFC] = "NFC",
[ArchiveTabSubOne] = "SubOne",
+1 -1
View File
@@ -23,7 +23,7 @@ typedef enum {
} ArchiveFileTypeEnum;
typedef enum {
ArchiveTabFavourites,
ArchiveTabFavorites,
ArchiveTabLFRFID,
ArchiveTabSubOne,
ArchiveTabNFC,
+5 -5
View File
@@ -5,11 +5,11 @@
void bt_cli_init() {
Cli* cli = furi_record_open("cli");
cli_add_command(cli, "bt_info", bt_cli_command_info, NULL);
cli_add_command(cli, "bt_tx_carrier", bt_cli_command_carrier_tx, NULL);
cli_add_command(cli, "bt_rx_carrier", bt_cli_command_carrier_rx, NULL);
cli_add_command(cli, "bt_tx_pt", bt_cli_command_packet_tx, NULL);
cli_add_command(cli, "bt_rx_pt", bt_cli_command_packet_rx, NULL);
cli_add_command(cli, "bt_info", CliCommandFlagDefault, bt_cli_command_info, NULL);
cli_add_command(cli, "bt_tx_carrier", CliCommandFlagDefault, bt_cli_command_carrier_tx, NULL);
cli_add_command(cli, "bt_rx_carrier", CliCommandFlagDefault, bt_cli_command_carrier_rx, NULL);
cli_add_command(cli, "bt_tx_pt", CliCommandFlagDefault, bt_cli_command_packet_tx, NULL);
cli_add_command(cli, "bt_rx_pt", CliCommandFlagDefault, bt_cli_command_packet_rx, NULL);
furi_record_close("cli");
}
+1 -1
View File
@@ -4,7 +4,7 @@ void bt_view_test_carrier_draw(Canvas* canvas, void* model) {
BtViewTestCarrierModel* m = model;
canvas_clear(canvas);
canvas_set_font(canvas, FontSecondary);
canvas_draw_str(canvas, 0, 12, "Performing Cattier test");
canvas_draw_str(canvas, 0, 12, "Performing Carrier test");
if(m->type == BtStateCarrierTx) {
canvas_draw_str(canvas, 0, 24, "Manual Carrier TX");
} else if(m->type == BtStateHoppingTx) {
+29 -5
View File
@@ -1,7 +1,9 @@
#include "cli_i.h"
#include "cli_commands.h"
#include <version.h>
#include <api-hal-version.h>
#include <loader/loader.h>
Cli* cli_alloc() {
Cli* cli = furi_alloc(sizeof(Cli));
@@ -166,10 +168,26 @@ static void cli_handle_enter(Cli* cli) {
CliCommand* cli_command = CliCommandTree_get(cli->commands, command);
if(cli_command) {
cli_nl(cli);
// Execute command
cli_command->callback(cli, args, cli_command->context);
// Clear line
cli_reset(cli);
// Ensure that we running alone
if(!(cli_command->flags & CliCommandFlagParallelSafe)) {
Loader* loader = furi_record_open("loader");
bool safety_lock = loader_lock(loader);
if(safety_lock) {
// Execute command
cli_command->callback(cli, args, cli_command->context);
loader_unlock(loader);
// Clear line
cli_reset(cli);
} else {
printf("Other application is running, close it first");
}
furi_record_close("loader");
} else {
// Execute command
cli_command->callback(cli, args, cli_command->context);
// Clear line
cli_reset(cli);
}
} else {
cli_nl(cli);
printf(
@@ -310,7 +328,12 @@ void cli_process_input(Cli* cli) {
}
}
void cli_add_command(Cli* cli, const char* name, CliCallback callback, void* context) {
void cli_add_command(
Cli* cli,
const char* name,
CliCommandFlag flags,
CliCallback callback,
void* context) {
string_t name_str;
string_init_set_str(name_str, name);
string_strim(name_str);
@@ -323,6 +346,7 @@ void cli_add_command(Cli* cli, const char* name, CliCallback callback, void* con
CliCommand c;
c.callback = callback;
c.context = context;
c.flags = flags;
furi_check(osMutexAcquire(cli->mutex, osWaitForever) == osOK);
CliCommandTree_set_at(cli->commands, name_str, c);
+12 -1
View File
@@ -20,6 +20,12 @@ typedef enum {
CliSymbolAsciiDel = 0x7F,
} CliSymbols;
typedef enum {
CliCommandFlagDefault = 0, /** Default, loader lock is used */
CliCommandFlagParallelSafe =
(1 << 0), /** Safe to run in parallel with other apps, loader lock is not used */
} CliCommandFlag;
/* Cli type
* Anonymous structure. Use cli_i.h if you need to go deeper.
*/
@@ -39,7 +45,12 @@ typedef void (*CliCallback)(Cli* cli, string_t args, void* context);
* @param callback - callback function
* @param context - pointer to whatever we need to pass to callback
*/
void cli_add_command(Cli* cli, const char* name, CliCallback callback, void* context);
void cli_add_command(
Cli* cli,
const char* name,
CliCommandFlag flags,
CliCallback callback,
void* context);
/* Print unified cmd usage tip
* @param cmd - cmd name
+12 -11
View File
@@ -376,17 +376,18 @@ void cli_command_free(Cli* cli, string_t args, void* context) {
}
void cli_commands_init(Cli* cli) {
cli_add_command(cli, "!", cli_command_device_info, NULL);
cli_add_command(cli, "device_info", cli_command_device_info, NULL);
cli_add_command(cli, "!", CliCommandFlagParallelSafe, cli_command_device_info, NULL);
cli_add_command(cli, "device_info", CliCommandFlagParallelSafe, cli_command_device_info, NULL);
cli_add_command(cli, "?", cli_command_help, NULL);
cli_add_command(cli, "help", cli_command_help, NULL);
cli_add_command(cli, "?", CliCommandFlagParallelSafe, cli_command_help, NULL);
cli_add_command(cli, "help", CliCommandFlagParallelSafe, cli_command_help, NULL);
cli_add_command(cli, "date", cli_command_date, NULL);
cli_add_command(cli, "log", cli_command_log, NULL);
cli_add_command(cli, "vibro", cli_command_vibro, NULL);
cli_add_command(cli, "led", cli_command_led, NULL);
cli_add_command(cli, "gpio_set", cli_command_gpio_set, NULL);
cli_add_command(cli, "ps", cli_command_ps, NULL);
cli_add_command(cli, "free", cli_command_free, NULL);
cli_add_command(cli, "date", CliCommandFlagParallelSafe, cli_command_date, NULL);
cli_add_command(cli, "log", CliCommandFlagParallelSafe, cli_command_log, NULL);
cli_add_command(cli, "ps", CliCommandFlagParallelSafe, cli_command_ps, NULL);
cli_add_command(cli, "free", CliCommandFlagParallelSafe, cli_command_free, NULL);
cli_add_command(cli, "vibro", CliCommandFlagDefault, cli_command_vibro, NULL);
cli_add_command(cli, "led", CliCommandFlagDefault, cli_command_led, NULL);
cli_add_command(cli, "gpio_set", CliCommandFlagDefault, cli_command_gpio_set, NULL);
}
+1
View File
@@ -15,6 +15,7 @@
typedef struct {
CliCallback callback;
void* context;
uint32_t flags;
} CliCommand;
BPTREE_DEF2(
+2 -1
View File
@@ -414,7 +414,8 @@ Gui* gui_alloc() {
subscribe_pubsub(gui->input_events, gui_input_events_callback, gui);
// Cli
gui->cli = furi_record_open("cli");
cli_add_command(gui->cli, "screen_stream", gui_cli_screen_stream, gui);
cli_add_command(
gui->cli, "screen_stream", CliCommandFlagParallelSafe, gui_cli_screen_stream, gui);
return gui;
}
+1 -1
View File
@@ -14,7 +14,7 @@ void ibutton_cli(Cli* cli, string_t args, void* context);
// app cli function
extern "C" void ibutton_cli_init() {
Cli* cli = static_cast<Cli*>(furi_record_open("cli"));
cli_add_command(cli, "tm", ibutton_cli, cli);
cli_add_command(cli, "tm", CliCommandFlagDefault, ibutton_cli, cli);
furi_record_close("cli");
}
+1 -1
View File
@@ -99,7 +99,7 @@ int32_t input_task() {
input->cli = furi_record_open("cli");
if(input->cli) {
cli_add_command(input->cli, "input_send", input_cli_send, input);
cli_add_command(input->cli, "input_send", CliCommandFlagDefault, input_cli_send, input);
}
const size_t pin_count = input_pins_count;
+2 -2
View File
@@ -167,7 +167,7 @@ static void irda_cli_start_ir_tx(Cli* cli, string_t args, void* context) {
extern "C" void irda_cli_init() {
Cli* cli = (Cli*)furi_record_open("cli");
cli_add_command(cli, "ir_rx", irda_cli_start_ir_rx, NULL);
cli_add_command(cli, "ir_tx", irda_cli_start_ir_tx, NULL);
cli_add_command(cli, "ir_rx", CliCommandFlagDefault, irda_cli_start_ir_rx, NULL);
cli_add_command(cli, "ir_tx", CliCommandFlagDefault, irda_cli_start_ir_tx, NULL);
furi_record_close("cli");
}
+1 -1
View File
@@ -12,7 +12,7 @@ void lfrfid_cli(Cli* cli, string_t args, void* context);
// app cli function
extern "C" void lfrfid_cli_init() {
Cli* cli = static_cast<Cli*>(furi_record_open("cli"));
cli_add_command(cli, "rfid", lfrfid_cli, NULL);
cli_add_command(cli, "rfid", CliCommandFlagDefault, lfrfid_cli, NULL);
furi_record_close("cli");
}
+312
View File
@@ -0,0 +1,312 @@
#include "loader_i.h"
static Loader* loader_instance = NULL;
static void loader_menu_callback(void* _ctx) {
const FlipperApplication* flipper_app = _ctx;
furi_assert(flipper_app->app);
furi_assert(flipper_app->name);
if(!loader_lock(loader_instance)) return;
if(furi_thread_get_state(loader_instance->thread) != FuriThreadStateStopped) {
FURI_LOG_E(
LOADER_LOG_TAG, "Can't start app. %s is running", loader_instance->current_app->name);
return;
}
api_hal_power_insomnia_enter();
loader_instance->current_app = flipper_app;
FURI_LOG_I(
LOADER_LOG_TAG, "Starting furi application: %s", loader_instance->current_app->name);
furi_thread_set_name(loader_instance->thread, flipper_app->name);
furi_thread_set_stack_size(loader_instance->thread, flipper_app->stack_size);
furi_thread_set_context(loader_instance->thread, NULL);
furi_thread_set_callback(loader_instance->thread, flipper_app->app);
furi_thread_start(loader_instance->thread);
}
static void loader_cli_callback(Cli* cli, string_t args, void* _ctx) {
furi_assert(_ctx);
const FlipperApplication* flipper_app = (FlipperApplication*)_ctx;
furi_assert(flipper_app->app);
furi_assert(flipper_app->name);
if(furi_thread_get_state(loader_instance->thread) != FuriThreadStateStopped) {
printf("Can't start, furi application is running");
return;
}
loader_instance->lock_semaphore++;
api_hal_power_insomnia_enter();
loader_instance->current_app = flipper_app;
printf("Starting furi application %s", loader_instance->current_app->name);
furi_thread_set_name(loader_instance->thread, flipper_app->name);
furi_thread_set_stack_size(loader_instance->thread, flipper_app->stack_size);
furi_thread_set_callback(loader_instance->thread, flipper_app->app);
furi_thread_start(loader_instance->thread);
}
bool loader_start(Loader* instance, const char* name, const char* args) {
furi_assert(name);
const FlipperApplication* flipper_app = NULL;
// Search for application
for(size_t i = 0; i < FLIPPER_APPS_COUNT; i++) {
if(strcmp(FLIPPER_APPS[i].name, name) == 0) {
flipper_app = &FLIPPER_APPS[i];
break;
}
}
if(!flipper_app) {
FURI_LOG_E(LOADER_LOG_TAG, "Can't find application with name %s", name);
return false;
}
loader_lock(instance);
if(furi_thread_get_state(instance->thread) != FuriThreadStateStopped) {
FURI_LOG_E(LOADER_LOG_TAG, "Can't start app. %s is running", instance->current_app->name);
return false;
}
instance->current_app = flipper_app;
if(args) {
string_set_str(instance->args, args);
string_strim(instance->args);
FURI_LOG_I(LOADER_LOG_TAG, "Start %s app with args: %s", name, args);
} else {
string_clean(instance->args);
FURI_LOG_I(LOADER_LOG_TAG, "Start %s app with no args", name);
}
furi_thread_set_name(instance->thread, flipper_app->name);
furi_thread_set_stack_size(instance->thread, flipper_app->stack_size);
furi_thread_set_context(instance->thread, (void*)string_get_cstr(instance->args));
furi_thread_set_callback(instance->thread, flipper_app->app);
return furi_thread_start(instance->thread);
}
bool loader_lock(Loader* instance) {
bool ret = false;
furi_check(osMutexAcquire(instance->mutex, osWaitForever) == osOK);
if(instance->lock_semaphore == 0) {
instance->lock_semaphore++;
ret = true;
}
furi_check(osMutexRelease(instance->mutex) == osOK);
return ret;
}
void loader_unlock(Loader* instance) {
furi_check(osMutexAcquire(instance->mutex, osWaitForever) == osOK);
furi_check(instance->lock_semaphore > 0);
instance->lock_semaphore--;
furi_check(osMutexRelease(instance->mutex) == osOK);
}
static void loader_thread_state_callback(FuriThreadState thread_state, void* context) {
furi_assert(context);
Loader* instance = context;
if(thread_state == FuriThreadStateRunning) {
instance->free_heap_size = xPortGetFreeHeapSize();
} else if(thread_state == FuriThreadStateStopped) {
/*
* Current Leak Sanitizer assumes that memory is allocated and freed
* inside one thread. Timers are allocated in one task, but freed in
* Timer-Task thread, and xTimerDelete() just put command to queue.
* To avoid some bad cases there are few fixes:
* 1) delay for Timer to process commands
* 2) there are 'heap diff' which shows difference in heap before task
* started and after task completed. In process of leakage monitoring
* both values should be taken into account.
*/
delay(20);
int heap_diff = instance->free_heap_size - xPortGetFreeHeapSize();
FURI_LOG_I(
LOADER_LOG_TAG,
"Application thread stopped. Heap allocation balance: %d. Thread allocation balance: %d.",
heap_diff,
furi_thread_get_heap_size(instance->thread));
api_hal_power_insomnia_exit();
loader_unlock(instance);
}
}
static Loader* loader_alloc() {
Loader* instance = furi_alloc(sizeof(Loader));
instance->thread = furi_thread_alloc();
furi_thread_enable_heap_trace(instance->thread);
furi_thread_set_state_context(instance->thread, instance);
furi_thread_set_state_callback(instance->thread, loader_thread_state_callback);
string_init(instance->args);
instance->mutex = osMutexNew(NULL);
instance->menu_vm = furi_record_open("menu");
instance->cli = furi_record_open("cli");
return instance;
}
static void loader_free(Loader* instance) {
furi_assert(instance);
furi_record_close("cli");
furi_record_close("menu");
osMutexDelete(instance->mutex);
string_clear(instance->args);
furi_thread_free(instance->thread);
free(instance);
}
static void loader_build_menu() {
FURI_LOG_I(LOADER_LOG_TAG, "Building main menu");
with_value_mutex(
loader_instance->menu_vm, (Menu * menu) {
for(size_t i = 0; i < FLIPPER_APPS_COUNT; i++) {
// Add menu item
menu_item_add(
menu,
menu_item_alloc_function(
FLIPPER_APPS[i].name,
icon_animation_alloc(FLIPPER_APPS[i].icon),
loader_menu_callback,
(void*)&FLIPPER_APPS[i]));
// Add cli command
string_t cli_name;
string_init_set_str(cli_name, "app_");
string_cat_str(cli_name, FLIPPER_APPS[i].name);
cli_add_command(
loader_instance->cli,
string_get_cstr(cli_name),
CliCommandFlagDefault,
loader_cli_callback,
(void*)&FLIPPER_APPS[i]);
string_clear(cli_name);
}
});
FURI_LOG_I(LOADER_LOG_TAG, "Building plugins menu");
with_value_mutex(
loader_instance->menu_vm, (Menu * menu) {
MenuItem* menu_plugins =
menu_item_alloc_menu("Plugins", icon_animation_alloc(&A_Plugins_14));
for(size_t i = 0; i < FLIPPER_PLUGINS_COUNT; i++) {
// Add menu item
menu_item_subitem_add(
menu_plugins,
menu_item_alloc_function(
FLIPPER_PLUGINS[i].name,
icon_animation_alloc(FLIPPER_PLUGINS[i].icon),
loader_menu_callback,
(void*)&FLIPPER_PLUGINS[i]));
// Add cli command
string_t cli_name;
string_init_set_str(cli_name, "app_");
string_cat_str(cli_name, FLIPPER_PLUGINS[i].name);
cli_add_command(
loader_instance->cli,
string_get_cstr(cli_name),
CliCommandFlagDefault,
loader_cli_callback,
(void*)&FLIPPER_PLUGINS[i]);
string_clear(cli_name);
}
menu_item_add(menu, menu_plugins);
});
FURI_LOG_I(LOADER_LOG_TAG, "Building debug menu");
with_value_mutex(
loader_instance->menu_vm, (Menu * menu) {
MenuItem* menu_debug =
menu_item_alloc_menu("Debug tools", icon_animation_alloc(&A_Settings_14));
for(size_t i = 0; i < FLIPPER_DEBUG_APPS_COUNT; i++) {
// Add menu item
menu_item_subitem_add(
menu_debug,
menu_item_alloc_function(
FLIPPER_DEBUG_APPS[i].name,
icon_animation_alloc(FLIPPER_DEBUG_APPS[i].icon),
loader_menu_callback,
(void*)&FLIPPER_DEBUG_APPS[i]));
// Add cli command
string_t cli_name;
string_init_set_str(cli_name, "app_");
string_cat_str(cli_name, FLIPPER_DEBUG_APPS[i].name);
cli_add_command(
loader_instance->cli,
string_get_cstr(cli_name),
CliCommandFlagDefault,
loader_cli_callback,
(void*)&FLIPPER_DEBUG_APPS[i]);
string_clear(cli_name);
}
menu_item_add(menu, menu_debug);
});
FURI_LOG_I(LOADER_LOG_TAG, "Building settings menu");
with_value_mutex(
loader_instance->menu_vm, (Menu * menu) {
MenuItem* menu_debug =
menu_item_alloc_menu("Settings", icon_animation_alloc(&A_Settings_14));
for(size_t i = 0; i < FLIPPER_SETTINGS_APPS_COUNT; i++) {
// Add menu item
menu_item_subitem_add(
menu_debug,
menu_item_alloc_function(
FLIPPER_SETTINGS_APPS[i].name,
icon_animation_alloc(FLIPPER_SETTINGS_APPS[i].icon),
loader_menu_callback,
(void*)&FLIPPER_SETTINGS_APPS[i]));
}
menu_item_add(menu, menu_debug);
});
}
int32_t loader(void* p) {
FURI_LOG_I(LOADER_LOG_TAG, "Starting");
loader_instance = loader_alloc();
loader_build_menu();
// Call on start hooks
for(size_t i = 0; i < FLIPPER_ON_SYSTEM_START_COUNT; i++) {
(*FLIPPER_ON_SYSTEM_START[i])();
}
FURI_LOG_I(LOADER_LOG_TAG, "Started");
furi_record_create("loader", loader_instance);
while(1) {
osThreadSuspend(osThreadGetId());
}
loader_free(loader_instance);
return 0;
}
+20
View File
@@ -0,0 +1,20 @@
#pragma once
#include <stdbool.h>
typedef struct Loader Loader;
/** Start application
* @param name - application name
* @param args - application arguments
* @retval true on success
*/
bool loader_start(Loader* instance, const char* name, const char* args);
/** Lock application start
* @retval true on success
*/
bool loader_lock(Loader* instance);
/** Unlock application start */
void loader_unlock(Loader* instance);
+22
View File
@@ -0,0 +1,22 @@
#include "loader.h"
#include <furi.h>
#include <api-hal.h>
#include <cli/cli.h>
#include <menu/menu.h>
#include <menu/menu_item.h>
#include <applications.h>
#include <assets_icons.h>
#define LOADER_LOG_TAG "loader"
struct Loader {
FuriThread* thread;
const FlipperApplication* current_app;
string_t args;
Cli* cli;
ValueMutex* menu_vm;
size_t free_heap_size;
osMutexId_t mutex;
volatile uint8_t lock_semaphore;
};
+2 -2
View File
@@ -5,8 +5,8 @@
void nfc_cli_init() {
Cli* cli = furi_record_open("cli");
cli_add_command(cli, "nfc_detect", nfc_cli_detect, NULL);
cli_add_command(cli, "nfc_emulate", nfc_cli_emulate, NULL);
cli_add_command(cli, "nfc_detect", CliCommandFlagDefault, nfc_cli_detect, NULL);
cli_add_command(cli, "nfc_emulate", CliCommandFlagDefault, nfc_cli_emulate, NULL);
furi_record_close("cli");
}
+7 -6
View File
@@ -44,10 +44,11 @@ void power_cli_otg(Cli* cli, string_t args, void* context) {
}
void power_cli_init(Cli* cli, Power* power) {
cli_add_command(cli, "poweroff", power_cli_poweroff, power);
cli_add_command(cli, "reboot", power_cli_reboot, power);
cli_add_command(cli, "factory_reset", power_cli_factory_reset, power);
cli_add_command(cli, "dfu", power_cli_dfu, power);
cli_add_command(cli, "power_info", power_cli_info, power);
cli_add_command(cli, "power_otg", power_cli_otg, power);
cli_add_command(cli, "poweroff", CliCommandFlagParallelSafe, power_cli_poweroff, power);
cli_add_command(cli, "reboot", CliCommandFlagParallelSafe, power_cli_reboot, power);
cli_add_command(
cli, "factory_reset", CliCommandFlagParallelSafe, power_cli_factory_reset, power);
cli_add_command(cli, "dfu", CliCommandFlagParallelSafe, power_cli_dfu, power);
cli_add_command(cli, "power_info", CliCommandFlagParallelSafe, power_cli_info, power);
cli_add_command(cli, "power_otg", CliCommandFlagParallelSafe, power_cli_otg, power);
}
+2 -2
View File
@@ -103,10 +103,10 @@ void SdTest::run() {
// read_benchmark and write_benchmark signatures are same. so we must use tags
auto cli_read_cb = cbc::obtain_connector<0>(this, &SdTest::cli_read_benchmark);
cli_add_command(cli, "sd_read_test", cli_read_cb, this);
cli_add_command(cli, "sd_read_test", CliCommandFlagDefault, cli_read_cb, this);
auto cli_write_cb = cbc::obtain_connector<1>(this, &SdTest::cli_write_benchmark);
cli_add_command(cli, "sd_write_test", cli_write_cb, this);
cli_add_command(cli, "sd_write_test", CliCommandFlagDefault, cli_write_cb, this);
detect_sd_card();
get_sd_card_info();
+2 -2
View File
@@ -648,8 +648,8 @@ int32_t sd_filesystem(void* p) {
gui_add_view_port(gui, sd_app->icon.view_port, GuiLayerStatusBarLeft);
cli_add_command(cli, "sd_format", cli_sd_format, sd_app);
cli_add_command(cli, "sd_info", cli_sd_info, sd_app);
cli_add_command(cli, "sd_format", CliCommandFlagDefault, cli_sd_format, sd_app);
cli_add_command(cli, "sd_info", CliCommandFlagDefault, cli_sd_info, sd_app);
// add api record
furi_record_create("sdcard", fs_api);
+87 -160
View File
@@ -3,17 +3,11 @@
#include <furi.h>
#include <api-hal.h>
#include <stream_buffer.h>
#include <lib/subghz/protocols/subghz_protocol.h>
#define CC1101_FREQUENCY_RANGE_STR \
"300000000...348000000 or 387000000...464000000 or 779000000...928000000"
static const uint8_t subghz_test_packet_data[] = {
0x30, // 48bytes to transmit
0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77,
};
bool subghz_check_frequency_range(uint32_t frequency) {
if(!(frequency >= 300000000 && frequency <= 348000000) &&
!(frequency >= 387000000 && frequency <= 464000000) &&
@@ -26,42 +20,45 @@ bool subghz_check_frequency_range(uint32_t frequency) {
void subghz_cli_init() {
Cli* cli = furi_record_open("cli");
cli_add_command(cli, "subghz_tx_carrier", subghz_cli_command_tx_carrier, NULL);
cli_add_command(cli, "subghz_rx_carrier", subghz_cli_command_rx_carrier, NULL);
cli_add_command(cli, "subghz_tx_pt", subghz_cli_command_tx_pt, NULL);
cli_add_command(cli, "subghz_rx_pt", subghz_cli_command_rx_pt, NULL);
cli_add_command(cli, "subghz_tx", subghz_cli_command_tx, NULL);
cli_add_command(cli, "subghz_rx", subghz_cli_command_rx, NULL);
cli_add_command(
cli, "subghz_tx_carrier", CliCommandFlagDefault, subghz_cli_command_tx_carrier, NULL);
cli_add_command(
cli, "subghz_rx_carrier", CliCommandFlagDefault, subghz_cli_command_rx_carrier, NULL);
cli_add_command(cli, "subghz_tx", CliCommandFlagDefault, subghz_cli_command_tx, NULL);
cli_add_command(cli, "subghz_rx", CliCommandFlagDefault, subghz_cli_command_rx, NULL);
furi_record_close("cli");
}
void subghz_cli_command_tx_carrier(Cli* cli, string_t args, void* context) {
uint32_t frequency = 0;
int ret = sscanf(string_get_cstr(args), "%lu", &frequency);
if(ret != 1) {
printf("sscanf returned %d, frequency: %lu\r\n", ret, frequency);
cli_print_usage("subghz_tx_carrier", "<Frequency in HZ>", string_get_cstr(args));
return;
}
uint32_t frequency = 433920000;
if(!subghz_check_frequency_range(frequency)) {
printf(
"Frequency must be in " CC1101_FREQUENCY_RANGE_STR " range, not %lu\r\n", frequency);
return;
if(string_size(args)) {
int ret = sscanf(string_get_cstr(args), "%lu", &frequency);
if(ret != 1) {
printf("sscanf returned %d, frequency: %lu\r\n", ret, frequency);
cli_print_usage("subghz_tx_carrier", "<Frequency in HZ>", string_get_cstr(args));
return;
}
if(!subghz_check_frequency_range(frequency)) {
printf(
"Frequency must be in " CC1101_FREQUENCY_RANGE_STR " range, not %lu\r\n",
frequency);
return;
}
}
api_hal_subghz_reset();
api_hal_subghz_load_preset(ApiHalSubGhzPresetOokAsync);
frequency = api_hal_subghz_set_frequency_and_path(frequency);
printf("Transmitting at frequency %lu Hz\r\n", frequency);
printf("Press CTRL+C to stop\r\n");
hal_gpio_init(&gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow);
hal_gpio_write(&gpio_cc1101_g0, false);
hal_gpio_write(&gpio_cc1101_g0, true);
api_hal_subghz_tx();
printf("Transmitting at frequency %lu Hz\r\n", frequency);
printf("Press CTRL+C to stop\r\n");
while(!cli_cmd_interrupt_received(cli)) {
osDelay(250);
}
@@ -71,18 +68,21 @@ void subghz_cli_command_tx_carrier(Cli* cli, string_t args, void* context) {
}
void subghz_cli_command_rx_carrier(Cli* cli, string_t args, void* context) {
uint32_t frequency = 0;
int ret = sscanf(string_get_cstr(args), "%lu", &frequency);
if(ret != 1) {
printf("sscanf returned %d, frequency: %lu\r\n", ret, frequency);
cli_print_usage("subghz_tx_carrier", "<Frequency in HZ>", string_get_cstr(args));
return;
}
uint32_t frequency = 433920000;
if(!subghz_check_frequency_range(frequency)) {
printf(
"Frequency must be in " CC1101_FREQUENCY_RANGE_STR " range, not %lu\r\n", frequency);
return;
if(string_size(args)) {
int ret = sscanf(string_get_cstr(args), "%lu", &frequency);
if(ret != 1) {
printf("sscanf returned %d, frequency: %lu\r\n", ret, frequency);
cli_print_usage("subghz_tx_carrier", "<Frequency in HZ>", string_get_cstr(args));
return;
}
if(!subghz_check_frequency_range(frequency)) {
printf(
"Frequency must be in " CC1101_FREQUENCY_RANGE_STR " range, not %lu\r\n",
frequency);
return;
}
}
api_hal_subghz_reset();
@@ -103,109 +103,9 @@ void subghz_cli_command_rx_carrier(Cli* cli, string_t args, void* context) {
api_hal_subghz_sleep();
}
void subghz_cli_command_tx_pt(Cli* cli, string_t args, void* context) {
uint32_t frequency = 0;
uint32_t pattern;
uint32_t count;
int ret = sscanf(string_get_cstr(args), "%lu %lu %lu", &frequency, &pattern, &count);
if(ret != 3) {
printf(
"sscanf returned %d, frequency: %lu; pattern: %lu; count: %lu\r\n",
ret,
frequency,
pattern,
count);
cli_print_usage(
"subghz_tx_pt", "<Frequency in HZ> <Pattern> <Count>", string_get_cstr(args));
return;
}
if(!subghz_check_frequency_range(frequency)) {
printf(
"Frequency must be in " CC1101_FREQUENCY_RANGE_STR " range, not %lu\r\n", frequency);
return;
}
if(pattern > 1) {
printf("Pattern must be 1, not %lu\r\n", pattern);
}
api_hal_subghz_reset();
api_hal_subghz_idle();
api_hal_subghz_load_preset(ApiHalSubGhzPreset2FskPacket);
frequency = api_hal_subghz_set_frequency_and_path(frequency);
hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow);
uint8_t status = api_hal_subghz_get_status();
FURI_LOG_D("SUBGHZ CLI", "Status: %02X", status);
while(!cli_cmd_interrupt_received(cli) && count) {
api_hal_subghz_idle();
api_hal_subghz_write_packet(subghz_test_packet_data, sizeof(subghz_test_packet_data));
api_hal_subghz_tx();
while(!hal_gpio_read(&gpio_cc1101_g0)) osDelay(1); // Wait for sync
while(hal_gpio_read(&gpio_cc1101_g0)) osDelay(1); // Wait end of transaction
count--;
}
api_hal_subghz_sleep();
api_hal_subghz_set_path(ApiHalSubGhzPathIsolate);
}
void subghz_cli_command_rx_pt(Cli* cli, string_t args, void* context) {
uint32_t frequency = 0;
int ret = sscanf(string_get_cstr(args), "%lu", &frequency);
if(ret != 1) {
printf("sscanf returned %d, frequency: %lu\r\n", ret, frequency);
cli_print_usage("subghz_rx_pt", "<Frequency in HZ>", string_get_cstr(args));
return;
}
if(!subghz_check_frequency_range(frequency)) {
printf(
"Frequency must be in " CC1101_FREQUENCY_RANGE_STR " range, not %lu\r\n", frequency);
return;
}
api_hal_subghz_reset();
api_hal_subghz_idle();
api_hal_subghz_load_preset(ApiHalSubGhzPreset2FskPacket);
frequency = api_hal_subghz_set_frequency_and_path(frequency);
hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow);
uint8_t status = api_hal_subghz_get_status();
FURI_LOG_D("SUBGHZ CLI", "Status: %02X", status);
printf("Start receiving packets. Press CTRL+C to stop\r\n");
api_hal_subghz_flush_rx();
api_hal_subghz_rx();
uint32_t packet_cnt = 0;
while(!cli_cmd_interrupt_received(cli)) {
if(hal_gpio_read(&gpio_cc1101_g0)) {
while(hal_gpio_read(&gpio_cc1101_g0))
; // Wait reception
packet_cnt++;
api_hal_subghz_idle();
api_hal_subghz_flush_rx();
api_hal_subghz_rx();
}
}
printf("Received %lu packets", packet_cnt);
api_hal_subghz_sleep();
api_hal_subghz_set_path(ApiHalSubGhzPathIsolate);
hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
}
#define SUBGHZ_PT_SHORT 260
#define SUBGHZ_PT_SHORT 376
#define SUBGHZ_PT_LONG (SUBGHZ_PT_SHORT * 3)
#define SUBGHZ_PT_GUARD 8060
#define SUBGHZ_PT_GUARD 10600
void subghz_cli_command_tx(Cli* cli, string_t args, void* context) {
uint32_t frequency = 433920000;
@@ -227,7 +127,6 @@ void subghz_cli_command_tx(Cli* cli, string_t args, void* context) {
string_get_cstr(args));
return;
}
if(!subghz_check_frequency_range(frequency)) {
printf(
"Frequency must be in " CC1101_FREQUENCY_RANGE_STR " range, not %lu\r\n",
@@ -255,8 +154,14 @@ void subghz_cli_command_tx(Cli* cli, string_t args, void* context) {
subghz_test_data[pos++] = SUBGHZ_PT_SHORT;
subghz_test_data[pos++] = SUBGHZ_PT_SHORT + SUBGHZ_PT_GUARD;
printf(
"Transmitting at %lu, key %lx, repeat %u. Press CTRL+C to stop\r\n",
frequency,
key,
repeat);
api_hal_subghz_reset();
api_hal_subghz_load_preset(ApiHalSubGhzPresetMP);
api_hal_subghz_load_preset(ApiHalSubGhzPresetOokAsync);
frequency = api_hal_subghz_set_frequency_and_path(frequency);
api_hal_subghz_start_async_tx(subghz_test_data, subghz_test_data_size, repeat);
@@ -267,25 +172,36 @@ void subghz_cli_command_tx(Cli* cli, string_t args, void* context) {
api_hal_subghz_sleep();
}
#include <fl_subghz/protocols/subghz_protocol.h>
typedef struct {
volatile bool overrun;
StreamBufferHandle_t stream;
size_t packet_count;
} SubGhzCliCommandRx;
volatile bool subghz_cli_overrun = false;
static void subghz_cli_command_rx_callback(bool level, uint32_t duration, void* context) {
SubGhzCliCommandRx* instance = context;
void subghz_cli_command_rx_callback(bool level, uint32_t duration, void* context) {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
LevelDuration level_duration = level_duration_make(level, duration);
if(subghz_cli_overrun) {
subghz_cli_overrun = false;
if(instance->overrun) {
instance->overrun = false;
level_duration = level_duration_reset();
}
size_t ret = xStreamBufferSendFromISR(
context, &level_duration, sizeof(LevelDuration), &xHigherPriorityTaskWoken);
if(sizeof(LevelDuration) != ret) subghz_cli_overrun = true;
instance->stream, &level_duration, sizeof(LevelDuration), &xHigherPriorityTaskWoken);
if(sizeof(LevelDuration) != ret) instance->overrun = true;
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
static void subghz_cli_command_rx_text_callback(string_t text, void* context) {
SubGhzCliCommandRx* instance = context;
instance->packet_count++;
printf(string_get_cstr(text));
}
void subghz_cli_command_rx(Cli* cli, string_t args, void* context) {
uint32_t frequency = 433920000;
if(string_size(args)) {
int ret = sscanf(string_get_cstr(args), "%lu", &frequency);
if(ret != 1) {
@@ -293,7 +209,6 @@ void subghz_cli_command_rx(Cli* cli, string_t args, void* context) {
cli_print_usage("subghz_rx", "<Frequency in HZ>", string_get_cstr(args));
return;
}
if(!subghz_check_frequency_range(frequency)) {
printf(
"Frequency must be in " CC1101_FREQUENCY_RANGE_STR " range, not %lu\r\n",
@@ -302,26 +217,32 @@ void subghz_cli_command_rx(Cli* cli, string_t args, void* context) {
}
}
api_hal_subghz_reset();
api_hal_subghz_load_preset(ApiHalSubGhzPresetMP);
frequency = api_hal_subghz_set_frequency_and_path(frequency);
hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow);
// Allocate context and buffers
SubGhzCliCommandRx* instance = furi_alloc(sizeof(SubGhzCliCommandRx));
instance->stream = xStreamBufferCreate(sizeof(LevelDuration) * 1024, sizeof(LevelDuration));
furi_check(instance->stream);
SubGhzProtocol* protocol = subghz_protocol_alloc();
subghz_protocol_load_keeloq_file(protocol, "/assets/subghz/keeloq_mfcodes");
subghz_protocol_load_nice_flor_s_file(protocol, "/assets/subghz/nice_floor_s_rx");
subghz_protocol_enable_dump_text(protocol, NULL, NULL);
subghz_protocol_enable_dump_text(protocol, subghz_cli_command_rx_text_callback, instance);
StreamBufferHandle_t rx_stream =
xStreamBufferCreate(sizeof(LevelDuration) * 1024, sizeof(LevelDuration));
// Configure radio
api_hal_subghz_reset();
api_hal_subghz_load_preset(ApiHalSubGhzPresetOokAsync);
frequency = api_hal_subghz_set_frequency_and_path(frequency);
hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow);
api_hal_subghz_set_async_rx_callback(subghz_cli_command_rx_callback, rx_stream);
// Prepare and start RX
api_hal_subghz_set_async_rx_callback(subghz_cli_command_rx_callback, instance);
api_hal_subghz_start_async_rx();
// Wait for packets to arrive
printf("Listening at %lu. Press CTRL+C to stop\r\n", frequency);
LevelDuration level_duration;
while(!cli_cmd_interrupt_received(cli)) {
int ret = xStreamBufferReceive(rx_stream, &level_duration, sizeof(LevelDuration), 10);
int ret =
xStreamBufferReceive(instance->stream, &level_duration, sizeof(LevelDuration), 10);
if(ret == sizeof(LevelDuration)) {
if(level_duration_is_reset(level_duration)) {
printf(".");
@@ -334,8 +255,14 @@ void subghz_cli_command_rx(Cli* cli, string_t args, void* context) {
}
}
// Shutdown radio
api_hal_subghz_stop_async_rx();
api_hal_subghz_sleep();
printf("\r\nPackets recieved %u\r\n", instance->packet_count);
// Cleanup
subghz_protocol_free(protocol);
vStreamBufferDelete(rx_stream);
vStreamBufferDelete(instance->stream);
free(instance);
}
+3 -3
View File
@@ -8,8 +8,8 @@
#include <gui/elements.h>
#include <notification/notification-messages.h>
#include <fl_subghz/subghz_worker.h>
#include <fl_subghz/protocols/subghz_protocol.h>
#include <lib/subghz/subghz_worker.h>
#include <lib/subghz/protocols/subghz_protocol.h>
#include <assets_icons.h>
@@ -143,7 +143,7 @@ void subghz_capture_enter(void* context) {
api_hal_subghz_reset();
api_hal_subghz_idle();
api_hal_subghz_load_preset(ApiHalSubGhzPresetMP);
api_hal_subghz_load_preset(ApiHalSubGhzPresetOokAsync);
with_view_model(
subghz_capture->view, (SubghzCaptureModel * model) {
+5 -5
View File
@@ -14,8 +14,8 @@ static const uint8_t subghz_static_keys[][4] = {
{0xE3, 0x4A, 0x4E},
};
#define SUBGHZ_PT_ONE 376
#define SUBGHZ_PT_ZERO (SUBGHZ_PT_ONE * 3)
#define SUBGHZ_PT_SHORT 376
#define SUBGHZ_PT_LONG (SUBGHZ_PT_SHORT * 3)
#define SUBGHZ_PT_GUARD 10600
struct SubghzStatic {
@@ -101,13 +101,13 @@ bool subghz_static_input(InputEvent* event, void* context) {
bool value = (key[byte] >> (7 - bit)) & 1;
// Payload send
hal_gpio_write(&gpio_cc1101_g0, true);
delay_us(value ? SUBGHZ_PT_ONE : SUBGHZ_PT_ZERO);
delay_us(value ? SUBGHZ_PT_SHORT : SUBGHZ_PT_LONG);
hal_gpio_write(&gpio_cc1101_g0, false);
delay_us(value ? SUBGHZ_PT_ZERO : SUBGHZ_PT_ONE);
delay_us(value ? SUBGHZ_PT_LONG : SUBGHZ_PT_SHORT);
}
// Last bit
hal_gpio_write(&gpio_cc1101_g0, true);
delay_us(SUBGHZ_PT_ONE);
delay_us(SUBGHZ_PT_SHORT);
hal_gpio_write(&gpio_cc1101_g0, false);
// Guard time
delay_us(10600);
+124 -58
View File
@@ -5,18 +5,23 @@
#include <furi.h>
#include <api-hal.h>
#include <input/input.h>
#include <toolbox/level_duration.h>
#include <lib/subghz/protocols/subghz_protocol_princeton.h>
static const uint8_t subghz_test_packet_data[] = {
0x30, // 48bytes to transmit
'F', 'L', 'I', 'P', 'P', 'E', 'R', ' ', 'T', 'E', 'S', 'T', ' ', 'P', 'A', 'C',
'K', 'E', 'T', ' ', 'D', 'A', 'T', 'A', ' ', 'A', 'N', 'D', ' ', 'P', 'A', 'D',
'F', 'L', 'I', 'P', 'P', 'E', 'R', ' ', 'T', 'E', 'S', 'T', ' ', 'P', 'A', 'C',
#define SUBGHZ_TEST_PACKET_COUNT 1000
};
#define SUBGHZ_PT_SHORT 376
#define SUBGHZ_PT_LONG (SUBGHZ_PT_SHORT * 3)
#define SUBGHZ_PT_GUARD 10600
struct SubghzTestPacket {
View* view;
osTimerId timer;
size_t tx_buffer_size;
uint32_t* tx_buffer;
SubGhzProtocolPrinceton* princeton;
volatile size_t packet_rx;
};
typedef enum {
@@ -29,10 +34,42 @@ typedef struct {
uint32_t real_frequency;
ApiHalSubGhzPath path;
float rssi;
size_t packets;
SubghzTestPacketModelStatus status;
} SubghzTestPacketModel;
void subghz_test_packet_draw(Canvas* canvas, SubghzTestPacketModel* model) {
volatile bool subghz_test_packet_overrun = false;
static void subghz_test_packet_rx_callback(bool level, uint32_t duration, void* context) {
furi_assert(context);
SubghzTestPacket* instance = context;
subghz_protocol_princeton_parse(instance->princeton, level, duration);
}
static void subghz_test_packet_rx_pt_callback(SubGhzProtocolCommon* parser, void* context) {
furi_assert(context);
SubghzTestPacket* instance = context;
instance->packet_rx++;
}
static void subghz_test_packet_rssi_timer_callback(void* context) {
furi_assert(context);
SubghzTestPacket* instance = context;
with_view_model(
instance->view, (SubghzTestPacketModel * model) {
if(model->status == SubghzTestPacketModelStatusRx) {
model->rssi = api_hal_subghz_get_rssi();
model->packets = instance->packet_rx;
} else {
model->packets =
SUBGHZ_TEST_PACKET_COUNT - api_hal_subghz_get_async_tx_repeat_left();
}
return true;
});
}
static void subghz_test_packet_draw(Canvas* canvas, SubghzTestPacketModel* model) {
char buffer[64];
canvas_set_color(canvas, ColorBlack);
@@ -62,6 +99,10 @@ void subghz_test_packet_draw(Canvas* canvas, SubghzTestPacketModel* model) {
}
snprintf(buffer, sizeof(buffer), "Path: %d - %s", model->path, path_name);
canvas_draw_str(canvas, 0, 31, buffer);
snprintf(buffer, sizeof(buffer), "Packets: %d", model->packets);
canvas_draw_str(canvas, 0, 42, buffer);
if(model->status == SubghzTestPacketModelStatusRx) {
snprintf(
buffer,
@@ -69,24 +110,27 @@ void subghz_test_packet_draw(Canvas* canvas, SubghzTestPacketModel* model) {
"RSSI: %ld.%ld dBm",
(int32_t)(model->rssi),
(int32_t)fabs(model->rssi * 10) % 10);
canvas_draw_str(canvas, 0, 42, buffer);
canvas_draw_str(canvas, 0, 53, buffer);
} else {
canvas_draw_str(canvas, 0, 42, "TX");
canvas_draw_str(canvas, 0, 53, "TX");
}
}
bool subghz_test_packet_input(InputEvent* event, void* context) {
static bool subghz_test_packet_input(InputEvent* event, void* context) {
furi_assert(context);
SubghzTestPacket* subghz_test_packet = context;
SubghzTestPacket* instance = context;
if(event->key == InputKeyBack) {
return false;
}
with_view_model(
subghz_test_packet->view, (SubghzTestPacketModel * model) {
osTimerStop(subghz_test_packet->timer);
api_hal_subghz_idle();
instance->view, (SubghzTestPacketModel * model) {
if(model->status == SubghzTestPacketModelStatusRx) {
api_hal_subghz_stop_async_rx();
} else {
api_hal_subghz_stop_async_tx();
}
if(event->type == InputTypeShort) {
if(event->key == InputKeyLeft) {
@@ -111,12 +155,10 @@ bool subghz_test_packet_input(InputEvent* event, void* context) {
}
if(model->status == SubghzTestPacketModelStatusRx) {
api_hal_subghz_rx();
osTimerStart(subghz_test_packet->timer, 1024 / 4);
api_hal_subghz_start_async_rx();
} else {
api_hal_subghz_write_packet(
subghz_test_packet_data, sizeof(subghz_test_packet_data));
api_hal_subghz_tx();
api_hal_subghz_start_async_tx(
instance->tx_buffer, instance->tx_buffer_size, SUBGHZ_TEST_PACKET_COUNT);
}
return true;
@@ -127,15 +169,35 @@ bool subghz_test_packet_input(InputEvent* event, void* context) {
void subghz_test_packet_enter(void* context) {
furi_assert(context);
SubghzTestPacket* subghz_test_packet = context;
SubghzTestPacket* instance = context;
instance->tx_buffer_size = 25 * 2 * sizeof(uint32_t);
instance->tx_buffer = furi_alloc(instance->tx_buffer_size);
const uint32_t key = 0x00ABCDEF;
size_t pos = 0;
for(uint8_t i = 0; i < 24; i++) {
uint8_t byte = i / 8;
uint8_t bit = i % 8;
bool value = (((uint8_t*)&key)[2 - byte] >> (7 - bit)) & 1;
if(value) {
instance->tx_buffer[pos++] = SUBGHZ_PT_SHORT;
instance->tx_buffer[pos++] = SUBGHZ_PT_LONG;
} else {
instance->tx_buffer[pos++] = SUBGHZ_PT_LONG;
instance->tx_buffer[pos++] = SUBGHZ_PT_SHORT;
}
}
instance->tx_buffer[pos++] = SUBGHZ_PT_SHORT;
instance->tx_buffer[pos++] = SUBGHZ_PT_SHORT + SUBGHZ_PT_GUARD;
api_hal_subghz_reset();
api_hal_subghz_load_preset(ApiHalSubGhzPreset2FskPacket);
hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow);
api_hal_subghz_load_preset(ApiHalSubGhzPresetOokAsync);
api_hal_subghz_set_async_rx_callback(subghz_test_packet_rx_callback, instance);
with_view_model(
subghz_test_packet->view, (SubghzTestPacketModel * model) {
instance->view, (SubghzTestPacketModel * model) {
model->frequency = subghz_frequencies_433_92;
model->real_frequency =
api_hal_subghz_set_frequency(subghz_frequencies[model->frequency]);
@@ -145,30 +207,29 @@ void subghz_test_packet_enter(void* context) {
return true;
});
api_hal_subghz_rx();
api_hal_subghz_start_async_rx();
osTimerStart(subghz_test_packet->timer, 1024 / 4);
osTimerStart(instance->timer, 1024 / 4);
}
void subghz_test_packet_exit(void* context) {
furi_assert(context);
SubghzTestPacket* subghz_test_packet = context;
SubghzTestPacket* instance = context;
osTimerStop(subghz_test_packet->timer);
osTimerStop(instance->timer);
// Reinitialize IC to default state
api_hal_subghz_sleep();
}
void subghz_test_packet_rssi_timer_callback(void* context) {
furi_assert(context);
SubghzTestPacket* subghz_test_packet = context;
with_view_model(
subghz_test_packet->view, (SubghzTestPacketModel * model) {
model->rssi = api_hal_subghz_get_rssi();
instance->view, (SubghzTestPacketModel * model) {
if(model->status == SubghzTestPacketModelStatusRx) {
api_hal_subghz_stop_async_rx();
} else {
api_hal_subghz_stop_async_tx();
}
return true;
});
api_hal_subghz_set_async_rx_callback(NULL, NULL);
api_hal_subghz_sleep();
}
uint32_t subghz_test_packet_back(void* context) {
@@ -176,33 +237,38 @@ uint32_t subghz_test_packet_back(void* context) {
}
SubghzTestPacket* subghz_test_packet_alloc() {
SubghzTestPacket* subghz_test_packet = furi_alloc(sizeof(SubghzTestPacket));
SubghzTestPacket* instance = furi_alloc(sizeof(SubghzTestPacket));
// View allocation and configuration
subghz_test_packet->view = view_alloc();
view_allocate_model(
subghz_test_packet->view, ViewModelTypeLockFree, sizeof(SubghzTestPacketModel));
view_set_context(subghz_test_packet->view, subghz_test_packet);
view_set_draw_callback(subghz_test_packet->view, (ViewDrawCallback)subghz_test_packet_draw);
view_set_input_callback(subghz_test_packet->view, subghz_test_packet_input);
view_set_enter_callback(subghz_test_packet->view, subghz_test_packet_enter);
view_set_exit_callback(subghz_test_packet->view, subghz_test_packet_exit);
view_set_previous_callback(subghz_test_packet->view, subghz_test_packet_back);
instance->view = view_alloc();
view_allocate_model(instance->view, ViewModelTypeLockFree, sizeof(SubghzTestPacketModel));
view_set_context(instance->view, instance);
view_set_draw_callback(instance->view, (ViewDrawCallback)subghz_test_packet_draw);
view_set_input_callback(instance->view, subghz_test_packet_input);
view_set_enter_callback(instance->view, subghz_test_packet_enter);
view_set_exit_callback(instance->view, subghz_test_packet_exit);
view_set_previous_callback(instance->view, subghz_test_packet_back);
subghz_test_packet->timer = osTimerNew(
subghz_test_packet_rssi_timer_callback, osTimerPeriodic, subghz_test_packet, NULL);
instance->timer =
osTimerNew(subghz_test_packet_rssi_timer_callback, osTimerPeriodic, instance, NULL);
return subghz_test_packet;
instance->princeton = subghz_protocol_princeton_alloc();
subghz_protocol_common_set_callback(
(SubGhzProtocolCommon*)instance->princeton, subghz_test_packet_rx_pt_callback, instance);
return instance;
}
void subghz_test_packet_free(SubghzTestPacket* subghz_test_packet) {
furi_assert(subghz_test_packet);
osTimerDelete(subghz_test_packet->timer);
view_free(subghz_test_packet->view);
free(subghz_test_packet);
void subghz_test_packet_free(SubghzTestPacket* instance) {
furi_assert(instance);
subghz_protocol_princeton_free(instance->princeton);
osTimerDelete(instance->timer);
view_free(instance->view);
free(instance);
}
View* subghz_test_packet_get_view(SubghzTestPacket* subghz_test_packet) {
furi_assert(subghz_test_packet);
return subghz_test_packet->view;
View* subghz_test_packet_get_view(SubghzTestPacket* instance) {
furi_assert(instance);
return instance->view;
}