mirror of
https://github.com/DarkFlippers/unleashed-firmware.git
synced 2025-12-12 04:34:43 +04:00
Merge remote-tracking branch 'OFW/dev' into dev
This commit is contained in:
15
SConstruct
15
SConstruct
@@ -412,6 +412,21 @@ distenv.PhonyTarget(
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
# Measure CLI loopback performance
|
||||
distenv.PhonyTarget(
|
||||
"cli_perf",
|
||||
[
|
||||
[
|
||||
"${PYTHON3}",
|
||||
"${FBT_SCRIPT_DIR}/serial_cli_perf.py",
|
||||
"-p",
|
||||
"${FLIP_PORT}",
|
||||
"${ARGS}",
|
||||
]
|
||||
],
|
||||
)
|
||||
|
||||
# Update WiFi devboard firmware with release channel
|
||||
distenv.PhonyTarget(
|
||||
"devboard_flash",
|
||||
|
||||
@@ -6,6 +6,5 @@ App(
|
||||
entry_point="accessor_app",
|
||||
requires=["gui"],
|
||||
stack_size=4 * 1024,
|
||||
order=40,
|
||||
fap_category="Debug",
|
||||
)
|
||||
|
||||
@@ -8,7 +8,6 @@ App(
|
||||
"power",
|
||||
],
|
||||
stack_size=1 * 1024,
|
||||
order=130,
|
||||
fap_category="Debug",
|
||||
fap_libs=["assets"],
|
||||
)
|
||||
|
||||
@@ -5,6 +5,5 @@ App(
|
||||
entry_point="blink_test_app",
|
||||
requires=["gui"],
|
||||
stack_size=1 * 1024,
|
||||
order=10,
|
||||
fap_category="Debug",
|
||||
)
|
||||
|
||||
@@ -13,6 +13,5 @@ App(
|
||||
"bt_debug",
|
||||
],
|
||||
stack_size=1 * 1024,
|
||||
order=110,
|
||||
fap_category="Debug",
|
||||
)
|
||||
|
||||
@@ -10,6 +10,5 @@ App(
|
||||
"ccid_test",
|
||||
],
|
||||
stack_size=1 * 1024,
|
||||
order=120,
|
||||
fap_category="Debug",
|
||||
)
|
||||
|
||||
@@ -5,6 +5,5 @@ App(
|
||||
entry_point="direct_draw_app",
|
||||
requires=["gui", "input"],
|
||||
stack_size=2 * 1024,
|
||||
order=70,
|
||||
fap_category="Debug",
|
||||
)
|
||||
|
||||
@@ -6,6 +6,5 @@ App(
|
||||
requires=["gui"],
|
||||
fap_libs=["u8g2"],
|
||||
stack_size=1 * 1024,
|
||||
order=120,
|
||||
fap_category="Debug",
|
||||
)
|
||||
|
||||
@@ -5,6 +5,5 @@ App(
|
||||
entry_point="event_loop_blink_test_app",
|
||||
requires=["input"],
|
||||
stack_size=1 * 1024,
|
||||
order=20,
|
||||
fap_category="Debug",
|
||||
)
|
||||
|
||||
@@ -6,7 +6,6 @@ App(
|
||||
requires=["expansion_start"],
|
||||
fap_libs=["assets"],
|
||||
stack_size=1 * 1024,
|
||||
order=20,
|
||||
fap_category="Debug",
|
||||
fap_file_assets="assets",
|
||||
)
|
||||
|
||||
@@ -5,7 +5,6 @@ App(
|
||||
entry_point="file_browser_app",
|
||||
requires=["gui"],
|
||||
stack_size=2 * 1024,
|
||||
order=150,
|
||||
fap_category="Debug",
|
||||
fap_icon_assets="icons",
|
||||
)
|
||||
|
||||
@@ -5,6 +5,5 @@ App(
|
||||
entry_point="keypad_test_app",
|
||||
requires=["gui"],
|
||||
stack_size=1 * 1024,
|
||||
order=30,
|
||||
fap_category="Debug",
|
||||
)
|
||||
|
||||
@@ -11,6 +11,5 @@ App(
|
||||
"lfrfid_debug",
|
||||
],
|
||||
stack_size=1 * 1024,
|
||||
order=100,
|
||||
fap_category="Debug",
|
||||
)
|
||||
|
||||
8
applications/debug/loader_chaining_a/application.fam
Normal file
8
applications/debug/loader_chaining_a/application.fam
Normal file
@@ -0,0 +1,8 @@
|
||||
App(
|
||||
appid="loader_chaining_a",
|
||||
name="Loader Chaining Test: App A",
|
||||
apptype=FlipperAppType.DEBUG,
|
||||
entry_point="chaining_test_app_a",
|
||||
stack_size=1 * 1024,
|
||||
fap_category="Debug",
|
||||
)
|
||||
164
applications/debug/loader_chaining_a/loader_chaining_a.c
Normal file
164
applications/debug/loader_chaining_a/loader_chaining_a.c
Normal file
@@ -0,0 +1,164 @@
|
||||
#include <furi.h>
|
||||
#include <gui/gui.h>
|
||||
#include <gui/view_dispatcher.h>
|
||||
#include <gui/modules/submenu.h>
|
||||
#include <loader/loader.h>
|
||||
#include <dialogs/dialogs.h>
|
||||
|
||||
#define TAG "LoaderChainingA"
|
||||
#define CHAINING_TEST_B "/ext/apps/Debug/loader_chaining_b.fap"
|
||||
#define NONEXISTENT_APP "Some nonexistent app"
|
||||
|
||||
typedef struct {
|
||||
Gui* gui;
|
||||
ViewDispatcher* view_dispatcher;
|
||||
Submenu* submenu;
|
||||
|
||||
Loader* loader;
|
||||
|
||||
DialogsApp* dialogs;
|
||||
} LoaderChainingA;
|
||||
|
||||
typedef enum {
|
||||
LoaderChainingASubmenuLaunchB,
|
||||
LoaderChainingASubmenuLaunchBThenA,
|
||||
LoaderChainingASubmenuLaunchNonexistentSilent,
|
||||
LoaderChainingASubmenuLaunchNonexistentGui,
|
||||
LoaderChainingASubmenuLaunchNonexistentGuiThenA,
|
||||
} LoaderChainingASubmenu;
|
||||
|
||||
static void loader_chaining_a_submenu_callback(void* context, uint32_t index) {
|
||||
LoaderChainingA* app = context;
|
||||
|
||||
FuriString* self_path = furi_string_alloc();
|
||||
furi_check(loader_get_application_launch_path(app->loader, self_path));
|
||||
|
||||
switch(index) {
|
||||
case LoaderChainingASubmenuLaunchB:
|
||||
loader_enqueue_launch(app->loader, CHAINING_TEST_B, "Hello", LoaderDeferredLaunchFlagGui);
|
||||
view_dispatcher_stop(app->view_dispatcher);
|
||||
break;
|
||||
|
||||
case LoaderChainingASubmenuLaunchBThenA:
|
||||
loader_enqueue_launch(app->loader, CHAINING_TEST_B, "Hello", LoaderDeferredLaunchFlagGui);
|
||||
loader_enqueue_launch(
|
||||
app->loader,
|
||||
furi_string_get_cstr(self_path),
|
||||
"Hello to you from the future",
|
||||
LoaderDeferredLaunchFlagGui);
|
||||
|
||||
break;
|
||||
|
||||
case LoaderChainingASubmenuLaunchNonexistentSilent:
|
||||
loader_enqueue_launch(app->loader, NONEXISTENT_APP, NULL, LoaderDeferredLaunchFlagNone);
|
||||
break;
|
||||
|
||||
case LoaderChainingASubmenuLaunchNonexistentGui:
|
||||
loader_enqueue_launch(app->loader, NONEXISTENT_APP, NULL, LoaderDeferredLaunchFlagGui);
|
||||
break;
|
||||
|
||||
case LoaderChainingASubmenuLaunchNonexistentGuiThenA:
|
||||
loader_enqueue_launch(app->loader, NONEXISTENT_APP, NULL, LoaderDeferredLaunchFlagGui);
|
||||
loader_enqueue_launch(
|
||||
app->loader,
|
||||
furi_string_get_cstr(self_path),
|
||||
"Hello to you from the future",
|
||||
LoaderDeferredLaunchFlagGui);
|
||||
break;
|
||||
}
|
||||
|
||||
furi_string_free(self_path);
|
||||
view_dispatcher_stop(app->view_dispatcher);
|
||||
}
|
||||
|
||||
static bool loader_chaining_a_nav_callback(void* context) {
|
||||
LoaderChainingA* app = context;
|
||||
view_dispatcher_stop(app->view_dispatcher);
|
||||
return true;
|
||||
}
|
||||
|
||||
LoaderChainingA* loader_chaining_a_alloc(void) {
|
||||
LoaderChainingA* app = malloc(sizeof(LoaderChainingA));
|
||||
app->gui = furi_record_open(RECORD_GUI);
|
||||
app->loader = furi_record_open(RECORD_LOADER);
|
||||
app->dialogs = furi_record_open(RECORD_DIALOGS);
|
||||
app->view_dispatcher = view_dispatcher_alloc();
|
||||
app->submenu = submenu_alloc();
|
||||
|
||||
submenu_add_item(
|
||||
app->submenu,
|
||||
"Launch B",
|
||||
LoaderChainingASubmenuLaunchB,
|
||||
loader_chaining_a_submenu_callback,
|
||||
app);
|
||||
submenu_add_item(
|
||||
app->submenu,
|
||||
"Launch B, then A",
|
||||
LoaderChainingASubmenuLaunchBThenA,
|
||||
loader_chaining_a_submenu_callback,
|
||||
app);
|
||||
submenu_add_item(
|
||||
app->submenu,
|
||||
"Trigger error: silent",
|
||||
LoaderChainingASubmenuLaunchNonexistentSilent,
|
||||
loader_chaining_a_submenu_callback,
|
||||
app);
|
||||
submenu_add_item(
|
||||
app->submenu,
|
||||
"Trigger error: GUI",
|
||||
LoaderChainingASubmenuLaunchNonexistentGui,
|
||||
loader_chaining_a_submenu_callback,
|
||||
app);
|
||||
submenu_add_item(
|
||||
app->submenu,
|
||||
"Error, then launch A",
|
||||
LoaderChainingASubmenuLaunchNonexistentGuiThenA,
|
||||
loader_chaining_a_submenu_callback,
|
||||
app);
|
||||
|
||||
view_dispatcher_add_view(app->view_dispatcher, 0, submenu_get_view(app->submenu));
|
||||
view_dispatcher_set_navigation_event_callback(
|
||||
app->view_dispatcher, loader_chaining_a_nav_callback);
|
||||
view_dispatcher_set_event_callback_context(app->view_dispatcher, app);
|
||||
|
||||
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, 0);
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
void loader_chaining_a_free(LoaderChainingA* app) {
|
||||
furi_record_close(RECORD_DIALOGS);
|
||||
furi_record_close(RECORD_LOADER);
|
||||
furi_record_close(RECORD_GUI);
|
||||
view_dispatcher_remove_view(app->view_dispatcher, 0);
|
||||
submenu_free(app->submenu);
|
||||
view_dispatcher_free(app->view_dispatcher);
|
||||
free(app);
|
||||
}
|
||||
|
||||
int32_t chaining_test_app_a(const char* arg) {
|
||||
LoaderChainingA* app = loader_chaining_a_alloc();
|
||||
|
||||
if(arg) {
|
||||
if(strlen(arg)) {
|
||||
DialogMessage* message = dialog_message_alloc();
|
||||
FuriString* text;
|
||||
|
||||
dialog_message_set_header(message, "Hi, I am A", 64, 0, AlignCenter, AlignTop);
|
||||
text = furi_string_alloc_printf("Me from the past says:\n%s", arg);
|
||||
dialog_message_set_buttons(message, NULL, "ok!", NULL);
|
||||
|
||||
dialog_message_set_text(
|
||||
message, furi_string_get_cstr(text), 64, 32, AlignCenter, AlignCenter);
|
||||
dialog_message_show(app->dialogs, message);
|
||||
dialog_message_free(message);
|
||||
furi_string_free(text);
|
||||
}
|
||||
}
|
||||
|
||||
view_dispatcher_run(app->view_dispatcher);
|
||||
|
||||
loader_chaining_a_free(app);
|
||||
return 0;
|
||||
}
|
||||
8
applications/debug/loader_chaining_b/application.fam
Normal file
8
applications/debug/loader_chaining_b/application.fam
Normal file
@@ -0,0 +1,8 @@
|
||||
App(
|
||||
appid="loader_chaining_b",
|
||||
name="Loader Chaining Test: App B",
|
||||
apptype=FlipperAppType.DEBUG,
|
||||
entry_point="chaining_test_app_b",
|
||||
stack_size=1 * 1024,
|
||||
fap_category="Debug",
|
||||
)
|
||||
27
applications/debug/loader_chaining_b/loader_chaining_b.c
Normal file
27
applications/debug/loader_chaining_b/loader_chaining_b.c
Normal file
@@ -0,0 +1,27 @@
|
||||
#include <furi.h>
|
||||
#include <dialogs/dialogs.h>
|
||||
#include <loader/loader.h>
|
||||
|
||||
int32_t chaining_test_app_b(const char* arg) {
|
||||
if(!arg) return 0;
|
||||
|
||||
Loader* loader = furi_record_open(RECORD_LOADER);
|
||||
DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS);
|
||||
|
||||
DialogMessage* message = dialog_message_alloc();
|
||||
dialog_message_set_header(message, "Hi, I am B", 64, 0, AlignCenter, AlignTop);
|
||||
FuriString* text = furi_string_alloc_printf("And A told me:\n%s", arg);
|
||||
dialog_message_set_text(message, furi_string_get_cstr(text), 64, 32, AlignCenter, AlignCenter);
|
||||
dialog_message_set_buttons(message, "Just quit", NULL, "Launch A");
|
||||
DialogMessageButton result = dialog_message_show(dialogs, message);
|
||||
dialog_message_free(message);
|
||||
furi_string_free(text);
|
||||
|
||||
if(result == DialogMessageButtonRight)
|
||||
loader_enqueue_launch(
|
||||
loader, "/ext/apps/Debug/loader_chaining_a.fap", NULL, LoaderDeferredLaunchFlagGui);
|
||||
|
||||
furi_record_close(RECORD_LOADER);
|
||||
furi_record_close(RECORD_DIALOGS);
|
||||
return 0;
|
||||
}
|
||||
@@ -5,6 +5,5 @@ App(
|
||||
entry_point="locale_test_app",
|
||||
requires=["gui", "locale"],
|
||||
stack_size=2 * 1024,
|
||||
order=70,
|
||||
fap_category="Debug",
|
||||
)
|
||||
|
||||
@@ -5,6 +5,5 @@ App(
|
||||
entry_point="rpc_debug_app",
|
||||
requires=["gui", "rpc_start", "notification"],
|
||||
stack_size=2 * 1024,
|
||||
order=10,
|
||||
fap_category="Debug",
|
||||
)
|
||||
|
||||
@@ -5,7 +5,6 @@ App(
|
||||
entry_point="speaker_debug_app",
|
||||
requires=["gui", "notification"],
|
||||
stack_size=2 * 1024,
|
||||
order=10,
|
||||
fap_category="Debug",
|
||||
fap_libs=["music_worker"],
|
||||
)
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
#include <furi.h>
|
||||
#include <notification/notification.h>
|
||||
#include <music_worker/music_worker.h>
|
||||
#include <cli/cli.h>
|
||||
#include <toolbox/args.h>
|
||||
#include <toolbox/pipe.h>
|
||||
#include <toolbox/cli/cli_registry.h>
|
||||
#include <cli/cli_main_commands.h>
|
||||
|
||||
#define TAG "SpeakerDebug"
|
||||
|
||||
@@ -19,14 +21,14 @@ typedef struct {
|
||||
typedef struct {
|
||||
MusicWorker* music_worker;
|
||||
FuriMessageQueue* message_queue;
|
||||
Cli* cli;
|
||||
CliRegistry* cli_registry;
|
||||
} SpeakerDebugApp;
|
||||
|
||||
static SpeakerDebugApp* speaker_app_alloc(void) {
|
||||
SpeakerDebugApp* app = (SpeakerDebugApp*)malloc(sizeof(SpeakerDebugApp));
|
||||
app->music_worker = music_worker_alloc();
|
||||
app->message_queue = furi_message_queue_alloc(8, sizeof(SpeakerDebugAppMessage));
|
||||
app->cli = furi_record_open(RECORD_CLI);
|
||||
app->cli_registry = furi_record_open(RECORD_CLI);
|
||||
return app;
|
||||
}
|
||||
|
||||
@@ -37,8 +39,8 @@ static void speaker_app_free(SpeakerDebugApp* app) {
|
||||
free(app);
|
||||
}
|
||||
|
||||
static void speaker_app_cli(Cli* cli, FuriString* args, void* context) {
|
||||
UNUSED(cli);
|
||||
static void speaker_app_cli(PipeSide* pipe, FuriString* args, void* context) {
|
||||
UNUSED(pipe);
|
||||
|
||||
SpeakerDebugApp* app = (SpeakerDebugApp*)context;
|
||||
SpeakerDebugAppMessage message;
|
||||
@@ -95,7 +97,8 @@ static void speaker_app_run(SpeakerDebugApp* app, const char* arg) {
|
||||
return;
|
||||
}
|
||||
|
||||
cli_add_command(app->cli, CLI_COMMAND, CliCommandFlagParallelSafe, speaker_app_cli, app);
|
||||
cli_registry_add_command(
|
||||
app->cli_registry, CLI_COMMAND, CliCommandFlagParallelSafe, speaker_app_cli, app);
|
||||
|
||||
SpeakerDebugAppMessage message;
|
||||
FuriStatus status;
|
||||
@@ -110,7 +113,7 @@ static void speaker_app_run(SpeakerDebugApp* app, const char* arg) {
|
||||
}
|
||||
}
|
||||
|
||||
cli_delete_command(app->cli, CLI_COMMAND);
|
||||
cli_registry_delete_command(app->cli_registry, CLI_COMMAND);
|
||||
}
|
||||
|
||||
int32_t speaker_debug_app(void* arg) {
|
||||
|
||||
@@ -6,7 +6,6 @@ App(
|
||||
entry_point="subghz_test_app",
|
||||
requires=["gui"],
|
||||
stack_size=4 * 1024,
|
||||
order=50,
|
||||
fap_icon="subghz_test_10px.png",
|
||||
fap_category="Debug",
|
||||
fap_icon_assets="images",
|
||||
|
||||
@@ -5,6 +5,5 @@ App(
|
||||
entry_point="text_box_element_test_app",
|
||||
requires=["gui"],
|
||||
stack_size=1 * 1024,
|
||||
order=140,
|
||||
fap_category="Debug",
|
||||
)
|
||||
|
||||
@@ -5,6 +5,5 @@ App(
|
||||
entry_point="text_box_view_test_app",
|
||||
requires=["gui"],
|
||||
stack_size=1 * 1024,
|
||||
order=140,
|
||||
fap_category="Debug",
|
||||
)
|
||||
|
||||
@@ -4,10 +4,9 @@ App(
|
||||
entry_point="unit_tests_on_system_start",
|
||||
sources=["unit_tests.c", "test_runner.c", "unit_test_api_table.cpp"],
|
||||
cdefines=["APP_UNIT_TESTS"],
|
||||
requires=["system_settings", "subghz_start"],
|
||||
requires=["system_settings", "cli_subghz"],
|
||||
provides=["delay_test"],
|
||||
resources="resources",
|
||||
order=100,
|
||||
)
|
||||
|
||||
App(
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
|
||||
#include "tests/test_api.h"
|
||||
|
||||
#include <cli/cli.h>
|
||||
#include <toolbox/cli/cli_command.h>
|
||||
#include <toolbox/path.h>
|
||||
#include <toolbox/pipe.h>
|
||||
#include <loader/loader.h>
|
||||
#include <storage/storage.h>
|
||||
#include <notification/notification_messages.h>
|
||||
@@ -25,7 +26,7 @@ struct TestRunner {
|
||||
NotificationApp* notification;
|
||||
|
||||
// Temporary used things
|
||||
Cli* cli;
|
||||
PipeSide* pipe;
|
||||
FuriString* args;
|
||||
|
||||
// ELF related stuff
|
||||
@@ -38,14 +39,14 @@ struct TestRunner {
|
||||
int minunit_status;
|
||||
};
|
||||
|
||||
TestRunner* test_runner_alloc(Cli* cli, FuriString* args) {
|
||||
TestRunner* test_runner_alloc(PipeSide* pipe, FuriString* args) {
|
||||
TestRunner* instance = malloc(sizeof(TestRunner));
|
||||
|
||||
instance->storage = furi_record_open(RECORD_STORAGE);
|
||||
instance->loader = furi_record_open(RECORD_LOADER);
|
||||
instance->notification = furi_record_open(RECORD_NOTIFICATION);
|
||||
|
||||
instance->cli = cli;
|
||||
instance->pipe = pipe;
|
||||
instance->args = args;
|
||||
|
||||
instance->composite_resolver = composite_api_resolver_alloc();
|
||||
@@ -147,7 +148,7 @@ static void test_runner_run_internal(TestRunner* instance) {
|
||||
}
|
||||
|
||||
while(true) {
|
||||
if(cli_cmd_interrupt_received(instance->cli)) {
|
||||
if(cli_is_pipe_broken_or_is_etx_next_char(instance->pipe)) {
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <furi.h>
|
||||
#include <toolbox/pipe.h>
|
||||
|
||||
typedef struct TestRunner TestRunner;
|
||||
typedef struct Cli Cli;
|
||||
|
||||
TestRunner* test_runner_alloc(Cli* cli, FuriString* args);
|
||||
TestRunner* test_runner_alloc(PipeSide* pipe, FuriString* args);
|
||||
|
||||
void test_runner_free(TestRunner* isntance);
|
||||
void test_runner_free(TestRunner* instance);
|
||||
|
||||
void test_runner_run(TestRunner* isntance);
|
||||
void test_runner_run(TestRunner* instance);
|
||||
|
||||
@@ -25,16 +25,13 @@ MU_TEST(pipe_test_trivial) {
|
||||
mu_assert_int_eq(PIPE_SIZE - i, pipe_spaces_available(alice));
|
||||
mu_assert_int_eq(i, pipe_bytes_available(bob));
|
||||
|
||||
if(pipe_send(alice, &i, sizeof(uint8_t), 0) != sizeof(uint8_t)) {
|
||||
break;
|
||||
}
|
||||
if(pipe_spaces_available(alice) == 0) break;
|
||||
furi_check(pipe_send(alice, &i, sizeof(uint8_t)) == sizeof(uint8_t));
|
||||
|
||||
mu_assert_int_eq(PIPE_SIZE - i, pipe_spaces_available(bob));
|
||||
mu_assert_int_eq(i, pipe_bytes_available(alice));
|
||||
|
||||
if(pipe_send(bob, &i, sizeof(uint8_t), 0) != sizeof(uint8_t)) {
|
||||
break;
|
||||
}
|
||||
furi_check(pipe_send(bob, &i, sizeof(uint8_t)) == sizeof(uint8_t));
|
||||
}
|
||||
|
||||
pipe_free(alice);
|
||||
@@ -43,10 +40,9 @@ MU_TEST(pipe_test_trivial) {
|
||||
for(uint8_t i = 0;; ++i) {
|
||||
mu_assert_int_eq(PIPE_SIZE - i, pipe_bytes_available(bob));
|
||||
|
||||
if(pipe_bytes_available(bob) == 0) break;
|
||||
uint8_t value;
|
||||
if(pipe_receive(bob, &value, sizeof(uint8_t), 0) != sizeof(uint8_t)) {
|
||||
break;
|
||||
}
|
||||
furi_check(pipe_receive(bob, &value, sizeof(uint8_t)) == sizeof(uint8_t));
|
||||
|
||||
mu_assert_int_eq(i, value);
|
||||
}
|
||||
@@ -68,16 +64,15 @@ typedef struct {
|
||||
static void on_data_arrived(PipeSide* pipe, void* context) {
|
||||
AncillaryThreadContext* ctx = context;
|
||||
ctx->flag |= TestFlagDataArrived;
|
||||
uint8_t buffer[PIPE_SIZE];
|
||||
size_t size = pipe_receive(pipe, buffer, sizeof(buffer), 0);
|
||||
pipe_send(pipe, buffer, size, 0);
|
||||
uint8_t input;
|
||||
size_t size = pipe_receive(pipe, &input, sizeof(input));
|
||||
pipe_send(pipe, &input, size);
|
||||
}
|
||||
|
||||
static void on_space_freed(PipeSide* pipe, void* context) {
|
||||
UNUSED(pipe);
|
||||
AncillaryThreadContext* ctx = context;
|
||||
ctx->flag |= TestFlagSpaceFreed;
|
||||
const char* message = "Hi!";
|
||||
pipe_send(pipe, message, strlen(message), 0);
|
||||
}
|
||||
|
||||
static void on_became_broken(PipeSide* pipe, void* context) {
|
||||
@@ -117,22 +112,16 @@ MU_TEST(pipe_test_event_loop) {
|
||||
furi_thread_start(thread);
|
||||
|
||||
const char* message = "Hello!";
|
||||
pipe_send(alice, message, strlen(message), FuriWaitForever);
|
||||
pipe_send(alice, message, strlen(message));
|
||||
|
||||
char buffer_1[16];
|
||||
size_t size = pipe_receive(alice, buffer_1, sizeof(buffer_1), FuriWaitForever);
|
||||
size_t size = pipe_receive(alice, buffer_1, strlen(message));
|
||||
buffer_1[size] = 0;
|
||||
|
||||
char buffer_2[16];
|
||||
const char* expected_reply = "Hi!";
|
||||
size = pipe_receive(alice, buffer_2, sizeof(buffer_2), FuriWaitForever);
|
||||
buffer_2[size] = 0;
|
||||
|
||||
pipe_free(alice);
|
||||
furi_thread_join(thread);
|
||||
|
||||
mu_assert_string_eq(message, buffer_1);
|
||||
mu_assert_string_eq(expected_reply, buffer_2);
|
||||
mu_assert_int_eq(
|
||||
TestFlagDataArrived | TestFlagSpaceFreed | TestFlagBecameBroken,
|
||||
furi_thread_get_return_code(thread));
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
#include <rpc/rpc.h>
|
||||
#include <rpc/rpc_i.h>
|
||||
#include <cli/cli.h>
|
||||
#include <storage/storage.h>
|
||||
#include <loader/loader.h>
|
||||
#include <storage/filesystem_api_defines.h>
|
||||
|
||||
@@ -521,11 +521,6 @@ MU_TEST(test_storage_data_path) {
|
||||
// check that appsdata folder exists
|
||||
mu_check(storage_dir_exists(storage, APPS_DATA_PATH));
|
||||
|
||||
// check that cli folder exists
|
||||
mu_check(storage_dir_exists(storage, APPSDATA_APP_PATH("cli")));
|
||||
|
||||
storage_simply_remove(storage, APPSDATA_APP_PATH("cli"));
|
||||
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,21 +1,24 @@
|
||||
#include <furi.h>
|
||||
#include <cli/cli.h>
|
||||
#include <toolbox/pipe.h>
|
||||
#include <toolbox/cli/cli_command.h>
|
||||
#include <toolbox/cli/cli_registry.h>
|
||||
#include <cli/cli_main_commands.h>
|
||||
|
||||
#include "test_runner.h"
|
||||
|
||||
void unit_tests_cli(Cli* cli, FuriString* args, void* context) {
|
||||
UNUSED(cli);
|
||||
void unit_tests_cli(PipeSide* pipe, FuriString* args, void* context) {
|
||||
UNUSED(context);
|
||||
|
||||
TestRunner* test_runner = test_runner_alloc(cli, args);
|
||||
TestRunner* test_runner = test_runner_alloc(pipe, args);
|
||||
test_runner_run(test_runner);
|
||||
test_runner_free(test_runner);
|
||||
}
|
||||
|
||||
void unit_tests_on_system_start(void) {
|
||||
#ifdef SRV_CLI
|
||||
Cli* cli = furi_record_open(RECORD_CLI);
|
||||
cli_add_command(cli, "unit_tests", CliCommandFlagParallelSafe, unit_tests_cli, NULL);
|
||||
CliRegistry* registry = furi_record_open(RECORD_CLI);
|
||||
cli_registry_add_command(
|
||||
registry, "unit_tests", CliCommandFlagParallelSafe, unit_tests_cli, NULL);
|
||||
furi_record_close(RECORD_CLI);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -5,6 +5,5 @@ App(
|
||||
entry_point="usb_mouse_app",
|
||||
requires=["gui"],
|
||||
stack_size=1 * 1024,
|
||||
order=60,
|
||||
fap_category="Debug",
|
||||
)
|
||||
|
||||
@@ -5,6 +5,5 @@ App(
|
||||
entry_point="usb_test_app",
|
||||
requires=["gui"],
|
||||
stack_size=1 * 1024,
|
||||
order=50,
|
||||
fap_category="Debug",
|
||||
)
|
||||
|
||||
@@ -5,6 +5,5 @@ App(
|
||||
entry_point="vibro_test_app",
|
||||
requires=["gui"],
|
||||
stack_size=1 * 1024,
|
||||
order=20,
|
||||
fap_category="Debug",
|
||||
)
|
||||
|
||||
@@ -23,11 +23,6 @@ App(
|
||||
name="On start hooks",
|
||||
apptype=FlipperAppType.METAPACKAGE,
|
||||
provides=[
|
||||
"ibutton_start",
|
||||
"onewire_start",
|
||||
"subghz_start",
|
||||
"infrared_start",
|
||||
"lfrfid_start",
|
||||
"nfc_start",
|
||||
"cli",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -44,7 +44,7 @@ static void archive_loader_callback(const void* message, void* context) {
|
||||
const LoaderEvent* event = message;
|
||||
ArchiveApp* archive = (ArchiveApp*)context;
|
||||
|
||||
if(event->type == LoaderEventTypeApplicationStopped) {
|
||||
if(event->type == LoaderEventTypeNoMoreAppsInQueue) {
|
||||
view_dispatcher_send_custom_event(
|
||||
archive->view_dispatcher, ArchiveBrowserEventListRefresh);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#include "usb_uart_bridge.h"
|
||||
#include "usb_cdc.h"
|
||||
#include <cli/cli_vcp.h>
|
||||
#include <cli/cli.h>
|
||||
#include <toolbox/api_lock.h>
|
||||
#include <furi_hal.h>
|
||||
#include <furi_hal_usb_cdc.h>
|
||||
@@ -106,15 +105,15 @@ static void usb_uart_on_irq_rx_dma_cb(
|
||||
static void usb_uart_vcp_init(UsbUartBridge* usb_uart, uint8_t vcp_ch) {
|
||||
furi_hal_usb_unlock();
|
||||
if(vcp_ch == 0) {
|
||||
Cli* cli = furi_record_open(RECORD_CLI);
|
||||
cli_session_close(cli);
|
||||
furi_record_close(RECORD_CLI);
|
||||
CliVcp* cli_vcp = furi_record_open(RECORD_CLI_VCP);
|
||||
cli_vcp_disable(cli_vcp);
|
||||
furi_record_close(RECORD_CLI_VCP);
|
||||
furi_check(furi_hal_usb_set_config(&usb_cdc_single, NULL) == true);
|
||||
} else {
|
||||
furi_check(furi_hal_usb_set_config(&usb_cdc_dual, NULL) == true);
|
||||
Cli* cli = furi_record_open(RECORD_CLI);
|
||||
cli_session_open(cli, &cli_vcp);
|
||||
furi_record_close(RECORD_CLI);
|
||||
CliVcp* cli_vcp = furi_record_open(RECORD_CLI_VCP);
|
||||
cli_vcp_enable(cli_vcp);
|
||||
furi_record_close(RECORD_CLI_VCP);
|
||||
}
|
||||
furi_hal_cdc_set_callbacks(vcp_ch, (CdcCallbacks*)&cdc_cb, usb_uart);
|
||||
}
|
||||
@@ -123,9 +122,9 @@ static void usb_uart_vcp_deinit(UsbUartBridge* usb_uart, uint8_t vcp_ch) {
|
||||
UNUSED(usb_uart);
|
||||
furi_hal_cdc_set_callbacks(vcp_ch, NULL, NULL);
|
||||
if(vcp_ch != 0) {
|
||||
Cli* cli = furi_record_open(RECORD_CLI);
|
||||
cli_session_close(cli);
|
||||
furi_record_close(RECORD_CLI);
|
||||
CliVcp* cli_vcp = furi_record_open(RECORD_CLI_VCP);
|
||||
cli_vcp_disable(cli_vcp);
|
||||
furi_record_close(RECORD_CLI_VCP);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -309,9 +308,9 @@ static int32_t usb_uart_worker(void* context) {
|
||||
|
||||
furi_hal_usb_unlock();
|
||||
furi_check(furi_hal_usb_set_config(&usb_cdc_single, NULL) == true);
|
||||
Cli* cli = furi_record_open(RECORD_CLI);
|
||||
cli_session_open(cli, &cli_vcp);
|
||||
furi_record_close(RECORD_CLI);
|
||||
CliVcp* cli_vcp = furi_record_open(RECORD_CLI_VCP);
|
||||
cli_vcp_enable(cli_vcp);
|
||||
furi_record_close(RECORD_CLI_VCP);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -13,10 +13,10 @@ App(
|
||||
)
|
||||
|
||||
App(
|
||||
appid="ibutton_start",
|
||||
apptype=FlipperAppType.STARTUP,
|
||||
appid="cli_ikey",
|
||||
targets=["f7"],
|
||||
entry_point="ibutton_on_system_start",
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
entry_point="cli_ikey_ep",
|
||||
requires=["cli"],
|
||||
sources=["ibutton_cli.c"],
|
||||
order=60,
|
||||
)
|
||||
|
||||
@@ -1,26 +1,14 @@
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
|
||||
#include <cli/cli.h>
|
||||
#include <cli/cli_main_commands.h>
|
||||
#include <toolbox/args.h>
|
||||
#include <toolbox/pipe.h>
|
||||
|
||||
#include <ibutton/ibutton_key.h>
|
||||
#include <ibutton/ibutton_worker.h>
|
||||
#include <ibutton/ibutton_protocols.h>
|
||||
|
||||
static void ibutton_cli(Cli* cli, FuriString* args, void* context);
|
||||
|
||||
// app cli function
|
||||
void ibutton_on_system_start(void) {
|
||||
#ifdef SRV_CLI
|
||||
Cli* cli = furi_record_open(RECORD_CLI);
|
||||
cli_add_command(cli, "ikey", CliCommandFlagDefault, ibutton_cli, cli);
|
||||
furi_record_close(RECORD_CLI);
|
||||
#else
|
||||
UNUSED(ibutton_cli);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void ibutton_cli_print_usage(void) {
|
||||
printf("Usage:\r\n");
|
||||
printf("ikey read\r\n");
|
||||
@@ -92,7 +80,7 @@ static void ibutton_cli_worker_read_cb(void* context) {
|
||||
furi_event_flag_set(event, EVENT_FLAG_IBUTTON_COMPLETE);
|
||||
}
|
||||
|
||||
static void ibutton_cli_read(Cli* cli) {
|
||||
static void ibutton_cli_read(PipeSide* pipe) {
|
||||
iButtonProtocols* protocols = ibutton_protocols_alloc();
|
||||
iButtonWorker* worker = ibutton_worker_alloc(protocols);
|
||||
iButtonKey* key = ibutton_key_alloc(ibutton_protocols_get_max_data_size(protocols));
|
||||
@@ -113,7 +101,7 @@ static void ibutton_cli_read(Cli* cli) {
|
||||
break;
|
||||
}
|
||||
|
||||
if(cli_cmd_interrupt_received(cli)) break;
|
||||
if(cli_is_pipe_broken_or_is_etx_next_char(pipe)) break;
|
||||
}
|
||||
|
||||
ibutton_worker_stop(worker);
|
||||
@@ -138,7 +126,7 @@ static void ibutton_cli_worker_write_cb(void* context, iButtonWorkerWriteResult
|
||||
furi_event_flag_set(write_context->event, EVENT_FLAG_IBUTTON_COMPLETE);
|
||||
}
|
||||
|
||||
void ibutton_cli_write(Cli* cli, FuriString* args) {
|
||||
void ibutton_cli_write(PipeSide* pipe, FuriString* args) {
|
||||
iButtonProtocols* protocols = ibutton_protocols_alloc();
|
||||
iButtonWorker* worker = ibutton_worker_alloc(protocols);
|
||||
iButtonKey* key = ibutton_key_alloc(ibutton_protocols_get_max_data_size(protocols));
|
||||
@@ -181,7 +169,7 @@ void ibutton_cli_write(Cli* cli, FuriString* args) {
|
||||
}
|
||||
}
|
||||
|
||||
if(cli_cmd_interrupt_received(cli)) break;
|
||||
if(cli_is_pipe_broken_or_is_etx_next_char(pipe)) break;
|
||||
}
|
||||
} while(false);
|
||||
|
||||
@@ -195,7 +183,7 @@ void ibutton_cli_write(Cli* cli, FuriString* args) {
|
||||
furi_event_flag_free(write_context.event);
|
||||
}
|
||||
|
||||
void ibutton_cli_emulate(Cli* cli, FuriString* args) {
|
||||
void ibutton_cli_emulate(PipeSide* pipe, FuriString* args) {
|
||||
iButtonProtocols* protocols = ibutton_protocols_alloc();
|
||||
iButtonWorker* worker = ibutton_worker_alloc(protocols);
|
||||
iButtonKey* key = ibutton_key_alloc(ibutton_protocols_get_max_data_size(protocols));
|
||||
@@ -214,7 +202,7 @@ void ibutton_cli_emulate(Cli* cli, FuriString* args) {
|
||||
|
||||
ibutton_worker_emulate_start(worker, key);
|
||||
|
||||
while(!cli_cmd_interrupt_received(cli)) {
|
||||
while(!cli_is_pipe_broken_or_is_etx_next_char(pipe)) {
|
||||
furi_delay_ms(100);
|
||||
};
|
||||
|
||||
@@ -228,8 +216,7 @@ void ibutton_cli_emulate(Cli* cli, FuriString* args) {
|
||||
ibutton_protocols_free(protocols);
|
||||
};
|
||||
|
||||
void ibutton_cli(Cli* cli, FuriString* args, void* context) {
|
||||
UNUSED(cli);
|
||||
static void execute(PipeSide* pipe, FuriString* args, void* context) {
|
||||
UNUSED(context);
|
||||
FuriString* cmd;
|
||||
cmd = furi_string_alloc();
|
||||
@@ -241,14 +228,16 @@ void ibutton_cli(Cli* cli, FuriString* args, void* context) {
|
||||
}
|
||||
|
||||
if(furi_string_cmp_str(cmd, "read") == 0) {
|
||||
ibutton_cli_read(cli);
|
||||
ibutton_cli_read(pipe);
|
||||
} else if(furi_string_cmp_str(cmd, "write") == 0) {
|
||||
ibutton_cli_write(cli, args);
|
||||
ibutton_cli_write(pipe, args);
|
||||
} else if(furi_string_cmp_str(cmd, "emulate") == 0) {
|
||||
ibutton_cli_emulate(cli, args);
|
||||
ibutton_cli_emulate(pipe, args);
|
||||
} else {
|
||||
ibutton_cli_print_usage();
|
||||
}
|
||||
|
||||
furi_string_free(cmd);
|
||||
}
|
||||
|
||||
CLI_COMMAND_INTERFACE(ikey, execute, CliCommandFlagDefault, 1024, CLI_APPID);
|
||||
|
||||
@@ -15,14 +15,14 @@ App(
|
||||
)
|
||||
|
||||
App(
|
||||
appid="infrared_start",
|
||||
apptype=FlipperAppType.STARTUP,
|
||||
appid="cli_ir",
|
||||
targets=["f7"],
|
||||
entry_point="infrared_on_system_start",
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
entry_point="cli_ir_ep",
|
||||
requires=["cli"],
|
||||
sources=[
|
||||
"infrared_cli.c",
|
||||
"infrared_brute_force.c",
|
||||
"infrared_signal.c",
|
||||
],
|
||||
order=20,
|
||||
)
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
#include <cli/cli.h>
|
||||
#include <cli/cli_i.h>
|
||||
#include <cli/cli_main_commands.h>
|
||||
#include <infrared.h>
|
||||
#include <infrared_worker.h>
|
||||
#include <furi_hal_infrared.h>
|
||||
#include <flipper_format.h>
|
||||
#include <toolbox/args.h>
|
||||
#include <toolbox/strint.h>
|
||||
#include <toolbox/pipe.h>
|
||||
#include <m-dict.h>
|
||||
|
||||
#include "infrared_signal.h"
|
||||
@@ -19,14 +19,14 @@
|
||||
|
||||
DICT_DEF2(dict_signals, FuriString*, FURI_STRING_OPLIST, int, M_DEFAULT_OPLIST)
|
||||
|
||||
static void infrared_cli_start_ir_rx(Cli* cli, FuriString* args);
|
||||
static void infrared_cli_start_ir_tx(Cli* cli, FuriString* args);
|
||||
static void infrared_cli_process_decode(Cli* cli, FuriString* args);
|
||||
static void infrared_cli_process_universal(Cli* cli, FuriString* args);
|
||||
static void infrared_cli_start_ir_rx(PipeSide* pipe, FuriString* args);
|
||||
static void infrared_cli_start_ir_tx(PipeSide* pipe, FuriString* args);
|
||||
static void infrared_cli_process_decode(PipeSide* pipe, FuriString* args);
|
||||
static void infrared_cli_process_universal(PipeSide* pipe, FuriString* args);
|
||||
|
||||
static const struct {
|
||||
const char* cmd;
|
||||
void (*process_function)(Cli* cli, FuriString* args);
|
||||
void (*process_function)(PipeSide* pipe, FuriString* args);
|
||||
} infrared_cli_commands[] = {
|
||||
{.cmd = "rx", .process_function = infrared_cli_start_ir_rx},
|
||||
{.cmd = "tx", .process_function = infrared_cli_start_ir_tx},
|
||||
@@ -38,7 +38,7 @@ static void signal_received_callback(void* context, InfraredWorkerSignal* receiv
|
||||
furi_assert(received_signal);
|
||||
char buf[100];
|
||||
size_t buf_cnt;
|
||||
Cli* cli = (Cli*)context;
|
||||
PipeSide* pipe = (PipeSide*)context;
|
||||
|
||||
if(infrared_worker_signal_is_decoded(received_signal)) {
|
||||
const InfraredMessage* message = infrared_worker_get_decoded_signal(received_signal);
|
||||
@@ -52,20 +52,20 @@ static void signal_received_callback(void* context, InfraredWorkerSignal* receiv
|
||||
ROUND_UP_TO(infrared_get_protocol_command_length(message->protocol), 4),
|
||||
message->command,
|
||||
message->repeat ? " R" : "");
|
||||
cli_write(cli, (uint8_t*)buf, buf_cnt);
|
||||
pipe_send(pipe, buf, buf_cnt);
|
||||
} else {
|
||||
const uint32_t* timings;
|
||||
size_t timings_cnt;
|
||||
infrared_worker_get_raw_signal(received_signal, &timings, &timings_cnt);
|
||||
|
||||
buf_cnt = snprintf(buf, sizeof(buf), "RAW, %zu samples:\r\n", timings_cnt);
|
||||
cli_write(cli, (uint8_t*)buf, buf_cnt);
|
||||
pipe_send(pipe, buf, buf_cnt);
|
||||
for(size_t i = 0; i < timings_cnt; ++i) {
|
||||
buf_cnt = snprintf(buf, sizeof(buf), "%lu ", timings[i]);
|
||||
cli_write(cli, (uint8_t*)buf, buf_cnt);
|
||||
pipe_send(pipe, buf, buf_cnt);
|
||||
}
|
||||
buf_cnt = snprintf(buf, sizeof(buf), "\r\n");
|
||||
cli_write(cli, (uint8_t*)buf, buf_cnt);
|
||||
pipe_send(pipe, buf, buf_cnt);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,9 +124,7 @@ static void infrared_cli_print_usage(void) {
|
||||
infrared_cli_print_universal_remotes();
|
||||
}
|
||||
|
||||
static void infrared_cli_start_ir_rx(Cli* cli, FuriString* args) {
|
||||
UNUSED(cli);
|
||||
|
||||
static void infrared_cli_start_ir_rx(PipeSide* pipe, FuriString* args) {
|
||||
bool enable_decoding = true;
|
||||
|
||||
if(!furi_string_empty(args)) {
|
||||
@@ -142,10 +140,10 @@ static void infrared_cli_start_ir_rx(Cli* cli, FuriString* args) {
|
||||
InfraredWorker* worker = infrared_worker_alloc();
|
||||
infrared_worker_rx_enable_signal_decoding(worker, enable_decoding);
|
||||
infrared_worker_rx_start(worker);
|
||||
infrared_worker_rx_set_received_signal_callback(worker, signal_received_callback, cli);
|
||||
infrared_worker_rx_set_received_signal_callback(worker, signal_received_callback, pipe);
|
||||
|
||||
printf("Receiving %s INFRARED...\r\nPress Ctrl+C to abort\r\n", enable_decoding ? "" : "RAW");
|
||||
while(!cli_cmd_interrupt_received(cli)) {
|
||||
while(!cli_is_pipe_broken_or_is_etx_next_char(pipe)) {
|
||||
furi_delay_ms(50);
|
||||
}
|
||||
|
||||
@@ -214,8 +212,8 @@ static bool infrared_cli_parse_raw(const char* str, InfraredSignal* signal) {
|
||||
return infrared_signal_is_valid(signal);
|
||||
}
|
||||
|
||||
static void infrared_cli_start_ir_tx(Cli* cli, FuriString* args) {
|
||||
UNUSED(cli);
|
||||
static void infrared_cli_start_ir_tx(PipeSide* pipe, FuriString* args) {
|
||||
UNUSED(pipe);
|
||||
const char* str = furi_string_get_cstr(args);
|
||||
InfraredSignal* signal = infrared_signal_alloc();
|
||||
|
||||
@@ -335,8 +333,8 @@ static bool infrared_cli_decode_file(FlipperFormat* input_file, FlipperFormat* o
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void infrared_cli_process_decode(Cli* cli, FuriString* args) {
|
||||
UNUSED(cli);
|
||||
static void infrared_cli_process_decode(PipeSide* pipe, FuriString* args) {
|
||||
UNUSED(pipe);
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
FlipperFormat* input_file = flipper_format_buffered_file_alloc(storage);
|
||||
FlipperFormat* output_file = NULL;
|
||||
@@ -455,8 +453,10 @@ static void infrared_cli_list_remote_signals(FuriString* remote_name) {
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
}
|
||||
|
||||
static void
|
||||
infrared_cli_brute_force_signals(Cli* cli, FuriString* remote_name, FuriString* signal_name) {
|
||||
static void infrared_cli_brute_force_signals(
|
||||
PipeSide* pipe,
|
||||
FuriString* remote_name,
|
||||
FuriString* signal_name) {
|
||||
InfraredBruteForce* brute_force = infrared_brute_force_alloc();
|
||||
FuriString* remote_path = furi_string_alloc_printf(
|
||||
"%s/%s.ir", INFRARED_ASSETS_FOLDER, furi_string_get_cstr(remote_name));
|
||||
@@ -490,7 +490,7 @@ static void
|
||||
while(running) {
|
||||
running = infrared_brute_force_send(brute_force, current_signal);
|
||||
|
||||
if(cli_cmd_interrupt_received(cli)) break;
|
||||
if(cli_is_pipe_broken_or_is_etx_next_char(pipe)) break;
|
||||
|
||||
printf("\r%d%% complete.", (int)((float)current_signal++ / (float)signal_count * 100));
|
||||
fflush(stdout);
|
||||
@@ -504,7 +504,7 @@ static void
|
||||
infrared_brute_force_free(brute_force);
|
||||
}
|
||||
|
||||
static void infrared_cli_process_universal(Cli* cli, FuriString* args) {
|
||||
static void infrared_cli_process_universal(PipeSide* pipe, FuriString* args) {
|
||||
FuriString* arg1 = furi_string_alloc();
|
||||
FuriString* arg2 = furi_string_alloc();
|
||||
|
||||
@@ -519,14 +519,14 @@ static void infrared_cli_process_universal(Cli* cli, FuriString* args) {
|
||||
} else if(furi_string_equal_str(arg1, "list")) {
|
||||
infrared_cli_list_remote_signals(arg2);
|
||||
} else {
|
||||
infrared_cli_brute_force_signals(cli, arg1, arg2);
|
||||
infrared_cli_brute_force_signals(pipe, arg1, arg2);
|
||||
}
|
||||
|
||||
furi_string_free(arg1);
|
||||
furi_string_free(arg2);
|
||||
}
|
||||
|
||||
static void infrared_cli_start_ir(Cli* cli, FuriString* args, void* context) {
|
||||
static void execute(PipeSide* pipe, FuriString* args, void* context) {
|
||||
UNUSED(context);
|
||||
if(furi_hal_infrared_is_busy()) {
|
||||
printf("INFRARED is busy. Exiting.");
|
||||
@@ -546,19 +546,12 @@ static void infrared_cli_start_ir(Cli* cli, FuriString* args, void* context) {
|
||||
}
|
||||
|
||||
if(i < COUNT_OF(infrared_cli_commands)) {
|
||||
infrared_cli_commands[i].process_function(cli, args);
|
||||
infrared_cli_commands[i].process_function(pipe, args);
|
||||
} else {
|
||||
infrared_cli_print_usage();
|
||||
}
|
||||
|
||||
furi_string_free(command);
|
||||
}
|
||||
void infrared_on_system_start(void) {
|
||||
#ifdef SRV_CLI
|
||||
Cli* cli = (Cli*)furi_record_open(RECORD_CLI);
|
||||
cli_add_command(cli, "ir", CliCommandFlagDefault, infrared_cli_start_ir, NULL);
|
||||
furi_record_close(RECORD_CLI);
|
||||
#else
|
||||
UNUSED(infrared_cli_start_ir);
|
||||
#endif
|
||||
}
|
||||
|
||||
CLI_COMMAND_INTERFACE(ir, execute, CliCommandFlagDefault, 2048, CLI_APPID);
|
||||
|
||||
@@ -13,10 +13,10 @@ App(
|
||||
)
|
||||
|
||||
App(
|
||||
appid="lfrfid_start",
|
||||
appid="cli_rfid",
|
||||
targets=["f7"],
|
||||
apptype=FlipperAppType.STARTUP,
|
||||
entry_point="lfrfid_on_system_start",
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
entry_point="cli_rfid_ep",
|
||||
requires=["cli"],
|
||||
sources=["lfrfid_cli.c"],
|
||||
order=50,
|
||||
)
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <stdarg.h>
|
||||
#include <cli/cli.h>
|
||||
#include <cli/cli_main_commands.h>
|
||||
#include <lib/toolbox/args.h>
|
||||
#include <lib/lfrfid/lfrfid_worker.h>
|
||||
#include <storage/storage.h>
|
||||
#include <toolbox/stream/file_stream.h>
|
||||
#include <toolbox/pipe.h>
|
||||
|
||||
#include <toolbox/varint.h>
|
||||
|
||||
@@ -14,15 +15,6 @@
|
||||
#include <lfrfid/lfrfid_raw_file.h>
|
||||
#include <toolbox/pulse_protocols/pulse_glue.h>
|
||||
|
||||
static void lfrfid_cli(Cli* cli, FuriString* args, void* context);
|
||||
|
||||
// app cli function
|
||||
void lfrfid_on_system_start(void) {
|
||||
Cli* cli = furi_record_open(RECORD_CLI);
|
||||
cli_add_command(cli, "rfid", CliCommandFlagDefault, lfrfid_cli, NULL);
|
||||
furi_record_close(RECORD_CLI);
|
||||
}
|
||||
|
||||
static void lfrfid_cli_print_usage(void) {
|
||||
printf("Usage:\r\n");
|
||||
printf("rfid read <optional: normal | indala> - read in ASK/PSK mode\r\n");
|
||||
@@ -49,7 +41,7 @@ static void lfrfid_cli_read_callback(LFRFIDWorkerReadResult result, ProtocolId p
|
||||
furi_event_flag_set(context->event, 1 << result);
|
||||
}
|
||||
|
||||
static void lfrfid_cli_read(Cli* cli, FuriString* args) {
|
||||
static void lfrfid_cli_read(PipeSide* pipe, FuriString* args) {
|
||||
FuriString* type_string;
|
||||
type_string = furi_string_alloc();
|
||||
LFRFIDWorkerReadType type = LFRFIDWorkerReadTypeAuto;
|
||||
@@ -96,7 +88,7 @@ static void lfrfid_cli_read(Cli* cli, FuriString* args) {
|
||||
}
|
||||
}
|
||||
|
||||
if(cli_cmd_interrupt_received(cli)) break;
|
||||
if(cli_is_pipe_broken_or_is_etx_next_char(pipe)) break;
|
||||
}
|
||||
|
||||
lfrfid_worker_stop(worker);
|
||||
@@ -192,7 +184,7 @@ static void lfrfid_cli_write_callback(LFRFIDWorkerWriteResult result, void* ctx)
|
||||
furi_event_flag_set(events, 1 << result);
|
||||
}
|
||||
|
||||
static void lfrfid_cli_write(Cli* cli, FuriString* args) {
|
||||
static void lfrfid_cli_write(PipeSide* pipe, FuriString* args) {
|
||||
ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax);
|
||||
ProtocolId protocol;
|
||||
|
||||
@@ -212,7 +204,7 @@ static void lfrfid_cli_write(Cli* cli, FuriString* args) {
|
||||
(1 << LFRFIDWorkerWriteProtocolCannotBeWritten) |
|
||||
(1 << LFRFIDWorkerWriteFobCannotBeWritten);
|
||||
|
||||
while(!cli_cmd_interrupt_received(cli)) {
|
||||
while(!cli_is_pipe_broken_or_is_etx_next_char(pipe)) {
|
||||
uint32_t flags = furi_event_flag_wait(event, available_flags, FuriFlagWaitAny, 100);
|
||||
if(flags != (unsigned)FuriFlagErrorTimeout) {
|
||||
if(FURI_BIT(flags, LFRFIDWorkerWriteOK)) {
|
||||
@@ -239,7 +231,7 @@ static void lfrfid_cli_write(Cli* cli, FuriString* args) {
|
||||
furi_event_flag_free(event);
|
||||
}
|
||||
|
||||
static void lfrfid_cli_emulate(Cli* cli, FuriString* args) {
|
||||
static void lfrfid_cli_emulate(PipeSide* pipe, FuriString* args) {
|
||||
ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax);
|
||||
ProtocolId protocol;
|
||||
|
||||
@@ -254,7 +246,7 @@ static void lfrfid_cli_emulate(Cli* cli, FuriString* args) {
|
||||
lfrfid_worker_emulate_start(worker, protocol);
|
||||
|
||||
printf("Emulating RFID...\r\nPress Ctrl+C to abort\r\n");
|
||||
while(!cli_cmd_interrupt_received(cli)) {
|
||||
while(!cli_is_pipe_broken_or_is_etx_next_char(pipe)) {
|
||||
furi_delay_ms(100);
|
||||
}
|
||||
printf("Emulation stopped\r\n");
|
||||
@@ -265,8 +257,8 @@ static void lfrfid_cli_emulate(Cli* cli, FuriString* args) {
|
||||
protocol_dict_free(dict);
|
||||
}
|
||||
|
||||
static void lfrfid_cli_raw_analyze(Cli* cli, FuriString* args) {
|
||||
UNUSED(cli);
|
||||
static void lfrfid_cli_raw_analyze(PipeSide* pipe, FuriString* args) {
|
||||
UNUSED(pipe);
|
||||
FuriString *filepath, *info_string;
|
||||
filepath = furi_string_alloc();
|
||||
info_string = furi_string_alloc();
|
||||
@@ -392,9 +384,7 @@ static void lfrfid_cli_raw_read_callback(LFRFIDWorkerReadRawResult result, void*
|
||||
furi_event_flag_set(event, 1 << result);
|
||||
}
|
||||
|
||||
static void lfrfid_cli_raw_read(Cli* cli, FuriString* args) {
|
||||
UNUSED(cli);
|
||||
|
||||
static void lfrfid_cli_raw_read(PipeSide* pipe, FuriString* args) {
|
||||
FuriString *filepath, *type_string;
|
||||
filepath = furi_string_alloc();
|
||||
type_string = furi_string_alloc();
|
||||
@@ -452,7 +442,7 @@ static void lfrfid_cli_raw_read(Cli* cli, FuriString* args) {
|
||||
}
|
||||
}
|
||||
|
||||
if(cli_cmd_interrupt_received(cli)) break;
|
||||
if(cli_is_pipe_broken_or_is_etx_next_char(pipe)) break;
|
||||
}
|
||||
|
||||
if(overrun) {
|
||||
@@ -479,9 +469,7 @@ static void lfrfid_cli_raw_emulate_callback(LFRFIDWorkerEmulateRawResult result,
|
||||
furi_event_flag_set(event, 1 << result);
|
||||
}
|
||||
|
||||
static void lfrfid_cli_raw_emulate(Cli* cli, FuriString* args) {
|
||||
UNUSED(cli);
|
||||
|
||||
static void lfrfid_cli_raw_emulate(PipeSide* pipe, FuriString* args) {
|
||||
FuriString* filepath;
|
||||
filepath = furi_string_alloc();
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
@@ -527,7 +515,7 @@ static void lfrfid_cli_raw_emulate(Cli* cli, FuriString* args) {
|
||||
}
|
||||
}
|
||||
|
||||
if(cli_cmd_interrupt_received(cli)) break;
|
||||
if(cli_is_pipe_broken_or_is_etx_next_char(pipe)) break;
|
||||
}
|
||||
|
||||
if(overrun) {
|
||||
@@ -548,7 +536,7 @@ static void lfrfid_cli_raw_emulate(Cli* cli, FuriString* args) {
|
||||
furi_string_free(filepath);
|
||||
}
|
||||
|
||||
static void lfrfid_cli(Cli* cli, FuriString* args, void* context) {
|
||||
static void execute(PipeSide* pipe, FuriString* args, void* context) {
|
||||
UNUSED(context);
|
||||
FuriString* cmd;
|
||||
cmd = furi_string_alloc();
|
||||
@@ -560,20 +548,22 @@ static void lfrfid_cli(Cli* cli, FuriString* args, void* context) {
|
||||
}
|
||||
|
||||
if(furi_string_cmp_str(cmd, "read") == 0) {
|
||||
lfrfid_cli_read(cli, args);
|
||||
lfrfid_cli_read(pipe, args);
|
||||
} else if(furi_string_cmp_str(cmd, "write") == 0) {
|
||||
lfrfid_cli_write(cli, args);
|
||||
lfrfid_cli_write(pipe, args);
|
||||
} else if(furi_string_cmp_str(cmd, "emulate") == 0) {
|
||||
lfrfid_cli_emulate(cli, args);
|
||||
lfrfid_cli_emulate(pipe, args);
|
||||
} else if(furi_string_cmp_str(cmd, "raw_read") == 0) {
|
||||
lfrfid_cli_raw_read(cli, args);
|
||||
lfrfid_cli_raw_read(pipe, args);
|
||||
} else if(furi_string_cmp_str(cmd, "raw_emulate") == 0) {
|
||||
lfrfid_cli_raw_emulate(cli, args);
|
||||
lfrfid_cli_raw_emulate(pipe, args);
|
||||
} else if(furi_string_cmp_str(cmd, "raw_analyze") == 0) {
|
||||
lfrfid_cli_raw_analyze(cli, args);
|
||||
lfrfid_cli_raw_analyze(pipe, args);
|
||||
} else {
|
||||
lfrfid_cli_print_usage();
|
||||
}
|
||||
|
||||
furi_string_free(cmd);
|
||||
}
|
||||
|
||||
CLI_COMMAND_INTERFACE(rfid, execute, CliCommandFlagDefault, 2048, CLI_APPID);
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
#include <assets_icons.h>
|
||||
#include <gui/view_dispatcher.h>
|
||||
#include <gui/scene_manager.h>
|
||||
#include <cli/cli.h>
|
||||
#include <notification/notification_messages.h>
|
||||
|
||||
#include <gui/modules/submenu.h>
|
||||
|
||||
@@ -321,10 +321,10 @@ App(
|
||||
)
|
||||
|
||||
App(
|
||||
appid="nfc_start",
|
||||
appid="cli_nfc",
|
||||
targets=["f7"],
|
||||
apptype=FlipperAppType.STARTUP,
|
||||
entry_point="nfc_on_system_start",
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
entry_point="cli_nfc_ep",
|
||||
requires=["cli"],
|
||||
sources=["nfc_cli.c"],
|
||||
order=30,
|
||||
)
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#include <assets_icons.h>
|
||||
#include <gui/view_dispatcher.h>
|
||||
#include <gui/scene_manager.h>
|
||||
#include <cli/cli.h>
|
||||
#include <notification/notification_messages.h>
|
||||
|
||||
#include <gui/modules/submenu.h>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <cli/cli.h>
|
||||
#include <cli/cli_main_commands.h>
|
||||
#include <lib/toolbox/args.h>
|
||||
#include <lib/toolbox/hex.h>
|
||||
#include <lib/toolbox/bit_buffer.h>
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <lib/nfc/protocols/iso14443_4a/iso14443_4a_poller.h>
|
||||
#include <lib/nfc/protocols/iso14443_4b/iso14443_4b_poller.h>
|
||||
#include <lib/nfc/protocols/iso15693_3/iso15693_3_poller.h>
|
||||
#include <toolbox/pipe.h>
|
||||
|
||||
#include <furi_hal_nfc.h>
|
||||
|
||||
@@ -38,7 +39,7 @@ static void nfc_cli_print_usage(void) {
|
||||
}
|
||||
}
|
||||
|
||||
static void nfc_cli_field(Cli* cli, FuriString* args) {
|
||||
static void nfc_cli_field(PipeSide* pipe, FuriString* args) {
|
||||
UNUSED(args);
|
||||
// Check if nfc worker is not busy
|
||||
if(furi_hal_nfc_is_hal_ready() != FuriHalNfcErrorNone) {
|
||||
@@ -53,7 +54,7 @@ static void nfc_cli_field(Cli* cli, FuriString* args) {
|
||||
printf("Field is on. Don't leave device in this mode for too long.\r\n");
|
||||
printf("Press Ctrl+C to abort\r\n");
|
||||
|
||||
while(!cli_cmd_interrupt_received(cli)) {
|
||||
while(!cli_is_pipe_broken_or_is_etx_next_char(pipe)) {
|
||||
furi_delay_ms(50);
|
||||
}
|
||||
|
||||
@@ -199,7 +200,7 @@ static void nfc_cli_apdu(Cli* cli, FuriString* args) {
|
||||
furi_string_free(data);
|
||||
}
|
||||
|
||||
static void nfc_cli(Cli* cli, FuriString* args, void* context) {
|
||||
static void execute(PipeSide* pipe, FuriString* args, void* context) {
|
||||
UNUSED(context);
|
||||
FuriString* cmd;
|
||||
cmd = furi_string_alloc();
|
||||
@@ -215,7 +216,7 @@ static void nfc_cli(Cli* cli, FuriString* args, void* context) {
|
||||
}
|
||||
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
|
||||
if(furi_string_cmp_str(cmd, "field") == 0) {
|
||||
nfc_cli_field(cli, args);
|
||||
nfc_cli_field(pipe, args);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -226,12 +227,4 @@ static void nfc_cli(Cli* cli, FuriString* args, void* context) {
|
||||
furi_string_free(cmd);
|
||||
}
|
||||
|
||||
void nfc_on_system_start(void) {
|
||||
#ifdef SRV_CLI
|
||||
Cli* cli = furi_record_open(RECORD_CLI);
|
||||
cli_add_command(cli, "nfc", CliCommandFlagDefault, nfc_cli, NULL);
|
||||
furi_record_close(RECORD_CLI);
|
||||
#else
|
||||
UNUSED(nfc_cli);
|
||||
#endif
|
||||
}
|
||||
CLI_COMMAND_INTERFACE(nfc, execute, CliCommandFlagDefault, 1024, CLI_APPID);
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
App(
|
||||
appid="onewire_start",
|
||||
apptype=FlipperAppType.STARTUP,
|
||||
entry_point="onewire_on_system_start",
|
||||
order=60,
|
||||
appid="cli_onewire",
|
||||
targets=["f7"],
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
entry_point="cli_onewire_ep",
|
||||
requires=["cli"],
|
||||
sources=["onewire_cli.c"],
|
||||
)
|
||||
|
||||
@@ -1,32 +1,20 @@
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
|
||||
#include <cli/cli_main_commands.h>
|
||||
#include <power/power_service/power.h>
|
||||
|
||||
#include <cli/cli.h>
|
||||
#include <toolbox/cli/cli_command.h>
|
||||
#include <toolbox/args.h>
|
||||
|
||||
#include <one_wire/one_wire_host.h>
|
||||
|
||||
static void onewire_cli(Cli* cli, FuriString* args, void* context);
|
||||
|
||||
void onewire_on_system_start(void) {
|
||||
#ifdef SRV_CLI
|
||||
Cli* cli = furi_record_open(RECORD_CLI);
|
||||
cli_add_command(cli, "onewire", CliCommandFlagDefault, onewire_cli, cli);
|
||||
furi_record_close(RECORD_CLI);
|
||||
#else
|
||||
UNUSED(onewire_cli);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void onewire_cli_print_usage(void) {
|
||||
printf("Usage:\r\n");
|
||||
printf("onewire search\r\n");
|
||||
};
|
||||
|
||||
static void onewire_cli_search(Cli* cli) {
|
||||
UNUSED(cli);
|
||||
static void onewire_cli_search(PipeSide* pipe) {
|
||||
UNUSED(pipe);
|
||||
OneWireHost* onewire = onewire_host_alloc(&gpio_ibutton);
|
||||
Power* power = furi_record_open(RECORD_POWER);
|
||||
uint8_t address[8];
|
||||
@@ -58,7 +46,7 @@ static void onewire_cli_search(Cli* cli) {
|
||||
furi_record_close(RECORD_POWER);
|
||||
}
|
||||
|
||||
void onewire_cli(Cli* cli, FuriString* args, void* context) {
|
||||
static void execute(PipeSide* pipe, FuriString* args, void* context) {
|
||||
UNUSED(context);
|
||||
FuriString* cmd;
|
||||
cmd = furi_string_alloc();
|
||||
@@ -70,8 +58,10 @@ void onewire_cli(Cli* cli, FuriString* args, void* context) {
|
||||
}
|
||||
|
||||
if(furi_string_cmp_str(cmd, "search") == 0) {
|
||||
onewire_cli_search(cli);
|
||||
onewire_cli_search(pipe);
|
||||
}
|
||||
|
||||
furi_string_free(cmd);
|
||||
}
|
||||
|
||||
CLI_COMMAND_INTERFACE(onewire, execute, CliCommandFlagDefault, 1024, CLI_APPID);
|
||||
|
||||
@@ -11,7 +11,6 @@ App(
|
||||
"dialogs",
|
||||
],
|
||||
provides=[
|
||||
"subghz_start",
|
||||
"subghz_load_dangerous_settings",
|
||||
],
|
||||
icon="A_Sub1ghz_14",
|
||||
@@ -24,12 +23,12 @@ App(
|
||||
)
|
||||
|
||||
App(
|
||||
appid="subghz_start",
|
||||
appid="cli_subghz",
|
||||
targets=["f7"],
|
||||
apptype=FlipperAppType.STARTUP,
|
||||
entry_point="subghz_on_system_start",
|
||||
requires=["subghz"],
|
||||
order=40,
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
entry_point="cli_subghz_ep",
|
||||
requires=["cli"],
|
||||
sources=["subghz_cli.c", "helpers/subghz_chat.c"],
|
||||
)
|
||||
|
||||
App(
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "subghz_chat.h"
|
||||
#include <lib/subghz/subghz_tx_rx_worker.h>
|
||||
#include <toolbox/pipe.h>
|
||||
|
||||
#define TAG "SubGhzChat"
|
||||
|
||||
@@ -14,7 +15,7 @@ struct SubGhzChatWorker {
|
||||
FuriMessageQueue* event_queue;
|
||||
uint32_t last_time_rx_data;
|
||||
|
||||
Cli* cli;
|
||||
PipeSide* pipe;
|
||||
};
|
||||
|
||||
/** Worker thread
|
||||
@@ -30,7 +31,7 @@ static int32_t subghz_chat_worker_thread(void* context) {
|
||||
event.event = SubGhzChatEventUserEntrance;
|
||||
furi_message_queue_put(instance->event_queue, &event, 0);
|
||||
while(instance->worker_running) {
|
||||
if(cli_read_timeout(instance->cli, (uint8_t*)&c, 1, 1000) == 1) {
|
||||
if(pipe_receive(instance->pipe, (uint8_t*)&c, 1) == 1) {
|
||||
event.event = SubGhzChatEventInputData;
|
||||
event.c = c;
|
||||
furi_message_queue_put(instance->event_queue, &event, FuriWaitForever);
|
||||
@@ -55,10 +56,10 @@ static void subghz_chat_worker_update_rx_event_chat(void* context) {
|
||||
furi_message_queue_put(instance->event_queue, &event, FuriWaitForever);
|
||||
}
|
||||
|
||||
SubGhzChatWorker* subghz_chat_worker_alloc(Cli* cli) {
|
||||
SubGhzChatWorker* subghz_chat_worker_alloc(PipeSide* pipe) {
|
||||
SubGhzChatWorker* instance = malloc(sizeof(SubGhzChatWorker));
|
||||
|
||||
instance->cli = cli;
|
||||
instance->pipe = pipe;
|
||||
|
||||
instance->thread =
|
||||
furi_thread_alloc_ex("SubGhzChat", 2048, subghz_chat_worker_thread, instance);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
#include "../subghz_i.h"
|
||||
#include <lib/subghz/devices/devices.h>
|
||||
#include <cli/cli.h>
|
||||
#include <toolbox/pipe.h>
|
||||
|
||||
typedef struct SubGhzChatWorker SubGhzChatWorker;
|
||||
|
||||
@@ -19,7 +19,7 @@ typedef struct {
|
||||
char c;
|
||||
} SubGhzChatEvent;
|
||||
|
||||
SubGhzChatWorker* subghz_chat_worker_alloc(Cli* cli);
|
||||
SubGhzChatWorker* subghz_chat_worker_alloc(PipeSide* pipe);
|
||||
void subghz_chat_worker_free(SubGhzChatWorker* instance);
|
||||
bool subghz_chat_worker_start(
|
||||
SubGhzChatWorker* instance,
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
#include <furi_hal.h>
|
||||
|
||||
#include <applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h>
|
||||
#include <cli/cli_main_commands.h>
|
||||
#include <toolbox/cli/cli_ansi.h>
|
||||
|
||||
#include <lib/subghz/subghz_keystore.h>
|
||||
#include <lib/subghz/receiver.h>
|
||||
@@ -16,6 +18,7 @@
|
||||
|
||||
#include <lib/toolbox/args.h>
|
||||
#include <lib/toolbox/strint.h>
|
||||
#include <toolbox/pipe.h>
|
||||
|
||||
#include "helpers/subghz_chat.h"
|
||||
|
||||
@@ -73,7 +76,7 @@ static SubGhzEnvironment* subghz_cli_environment_init(void) {
|
||||
return environment;
|
||||
}
|
||||
|
||||
void subghz_cli_command_tx_carrier(Cli* cli, FuriString* args, void* context) {
|
||||
void subghz_cli_command_tx_carrier(PipeSide* pipe, FuriString* args, void* context) {
|
||||
UNUSED(context);
|
||||
uint32_t frequency = 433920000;
|
||||
|
||||
@@ -103,7 +106,7 @@ void subghz_cli_command_tx_carrier(Cli* cli, FuriString* args, void* context) {
|
||||
if(furi_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)) {
|
||||
while(!cli_is_pipe_broken_or_is_etx_next_char(pipe)) {
|
||||
furi_delay_ms(250);
|
||||
}
|
||||
} else {
|
||||
@@ -116,7 +119,7 @@ void subghz_cli_command_tx_carrier(Cli* cli, FuriString* args, void* context) {
|
||||
furi_hal_power_suppress_charge_exit();
|
||||
}
|
||||
|
||||
void subghz_cli_command_rx_carrier(Cli* cli, FuriString* args, void* context) {
|
||||
void subghz_cli_command_rx_carrier(PipeSide* pipe, FuriString* args, void* context) {
|
||||
UNUSED(context);
|
||||
uint32_t frequency = 433920000;
|
||||
|
||||
@@ -144,7 +147,7 @@ void subghz_cli_command_rx_carrier(Cli* cli, FuriString* args, void* context) {
|
||||
|
||||
furi_hal_subghz_rx();
|
||||
|
||||
while(!cli_cmd_interrupt_received(cli)) {
|
||||
while(!cli_is_pipe_broken_or_is_etx_next_char(pipe)) {
|
||||
furi_delay_ms(250);
|
||||
printf("RSSI: %03.1fdbm\r", (double)furi_hal_subghz_get_rssi());
|
||||
fflush(stdout);
|
||||
@@ -177,7 +180,7 @@ static const SubGhzDevice* subghz_cli_command_get_device(uint32_t* device_ind) {
|
||||
return device;
|
||||
}
|
||||
|
||||
void subghz_cli_command_tx(Cli* cli, FuriString* args, void* context) {
|
||||
void subghz_cli_command_tx(PipeSide* pipe, FuriString* args, void* context) {
|
||||
UNUSED(context);
|
||||
uint32_t frequency = 433920000;
|
||||
uint32_t key = 0x0074BADE;
|
||||
@@ -247,7 +250,9 @@ void subghz_cli_command_tx(Cli* cli, FuriString* args, void* context) {
|
||||
|
||||
furi_hal_power_suppress_charge_enter();
|
||||
if(subghz_devices_start_async_tx(device, subghz_transmitter_yield, transmitter)) {
|
||||
while(!(subghz_devices_is_async_complete_tx(device) || cli_cmd_interrupt_received(cli))) {
|
||||
while(
|
||||
!(subghz_devices_is_async_complete_tx(device) ||
|
||||
cli_is_pipe_broken_or_is_etx_next_char(pipe))) {
|
||||
printf(".");
|
||||
fflush(stdout);
|
||||
furi_delay_ms(333);
|
||||
@@ -303,7 +308,7 @@ static void subghz_cli_command_rx_callback(
|
||||
furi_string_free(text);
|
||||
}
|
||||
|
||||
void subghz_cli_command_rx(Cli* cli, FuriString* args, void* context) {
|
||||
void subghz_cli_command_rx(PipeSide* pipe, FuriString* args, void* context) {
|
||||
UNUSED(context);
|
||||
uint32_t frequency = 433920000;
|
||||
uint32_t device_ind = 0; // 0 - CC1101_INT, 1 - CC1101_EXT
|
||||
@@ -359,7 +364,7 @@ void subghz_cli_command_rx(Cli* cli, FuriString* args, void* context) {
|
||||
frequency,
|
||||
device_ind);
|
||||
LevelDuration level_duration;
|
||||
while(!cli_cmd_interrupt_received(cli)) {
|
||||
while(!cli_is_pipe_broken_or_is_etx_next_char(pipe)) {
|
||||
int ret = furi_stream_buffer_receive(
|
||||
instance->stream, &level_duration, sizeof(LevelDuration), 10);
|
||||
if(ret == sizeof(LevelDuration)) {
|
||||
@@ -392,7 +397,7 @@ void subghz_cli_command_rx(Cli* cli, FuriString* args, void* context) {
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void subghz_cli_command_rx_raw(Cli* cli, FuriString* args, void* context) {
|
||||
void subghz_cli_command_rx_raw(PipeSide* pipe, FuriString* args, void* context) {
|
||||
UNUSED(context);
|
||||
uint32_t frequency = 433920000;
|
||||
|
||||
@@ -430,7 +435,7 @@ void subghz_cli_command_rx_raw(Cli* cli, FuriString* args, void* context) {
|
||||
printf("Listening at %lu. Press CTRL+C to stop\r\n", frequency);
|
||||
LevelDuration level_duration;
|
||||
size_t counter = 0;
|
||||
while(!cli_cmd_interrupt_received(cli)) {
|
||||
while(!cli_is_pipe_broken_or_is_etx_next_char(pipe)) {
|
||||
int ret = furi_stream_buffer_receive(
|
||||
instance->stream, &level_duration, sizeof(LevelDuration), 10);
|
||||
if(ret == 0) {
|
||||
@@ -466,7 +471,7 @@ void subghz_cli_command_rx_raw(Cli* cli, FuriString* args, void* context) {
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void subghz_cli_command_decode_raw(Cli* cli, FuriString* args, void* context) {
|
||||
void subghz_cli_command_decode_raw(PipeSide* pipe, FuriString* args, void* context) {
|
||||
UNUSED(context);
|
||||
FuriString* file_name = furi_string_alloc();
|
||||
furi_string_set(file_name, EXT_PATH("subghz/test.sub"));
|
||||
@@ -534,7 +539,7 @@ void subghz_cli_command_decode_raw(Cli* cli, FuriString* args, void* context) {
|
||||
furi_string_get_cstr(file_name));
|
||||
|
||||
LevelDuration level_duration;
|
||||
while(!cli_cmd_interrupt_received(cli)) {
|
||||
while(!cli_is_pipe_broken_or_is_etx_next_char(pipe)) {
|
||||
furi_delay_us(500); //you need to have time to read from the file from the SD card
|
||||
level_duration = subghz_file_encoder_worker_get_level_duration(file_worker_encoder);
|
||||
if(!level_duration_is_reset(level_duration)) {
|
||||
@@ -579,7 +584,7 @@ static FuriHalSubGhzPreset subghz_cli_get_preset_name(const char* preset_name) {
|
||||
return preset;
|
||||
}
|
||||
|
||||
void subghz_cli_command_tx_from_file(Cli* cli, FuriString* args, void* context) { // -V524
|
||||
void subghz_cli_command_tx_from_file(PipeSide* pipe, FuriString* args, void* context) { // -V524
|
||||
UNUSED(context);
|
||||
FuriString* file_name;
|
||||
file_name = furi_string_alloc();
|
||||
@@ -783,7 +788,7 @@ void subghz_cli_command_tx_from_file(Cli* cli, FuriString* args, void* context)
|
||||
if(subghz_devices_start_async_tx(device, subghz_transmitter_yield, transmitter)) {
|
||||
while(
|
||||
!(subghz_devices_is_async_complete_tx(device) ||
|
||||
cli_cmd_interrupt_received(cli))) {
|
||||
cli_is_pipe_broken_or_is_etx_next_char(pipe))) {
|
||||
printf(".");
|
||||
fflush(stdout);
|
||||
furi_delay_ms(333);
|
||||
@@ -797,11 +802,11 @@ void subghz_cli_command_tx_from_file(Cli* cli, FuriString* args, void* context)
|
||||
if(!strcmp(furi_string_get_cstr(temp_str), "RAW")) {
|
||||
subghz_transmitter_stop(transmitter);
|
||||
repeat--;
|
||||
if(!cli_cmd_interrupt_received(cli) && repeat)
|
||||
if(!cli_is_pipe_broken_or_is_etx_next_char(pipe) && repeat)
|
||||
subghz_transmitter_deserialize(transmitter, fff_data_raw);
|
||||
}
|
||||
|
||||
} while(!cli_cmd_interrupt_received(cli) &&
|
||||
} while(!cli_is_pipe_broken_or_is_etx_next_char(pipe) &&
|
||||
(repeat && !strcmp(furi_string_get_cstr(temp_str), "RAW")));
|
||||
|
||||
subghz_devices_sleep(device);
|
||||
@@ -851,8 +856,8 @@ static void subghz_cli_command_print_usage(void) {
|
||||
}
|
||||
}
|
||||
|
||||
static void subghz_cli_command_encrypt_keeloq(Cli* cli, FuriString* args) {
|
||||
UNUSED(cli);
|
||||
static void subghz_cli_command_encrypt_keeloq(PipeSide* pipe, FuriString* args) {
|
||||
UNUSED(pipe);
|
||||
uint8_t iv[16];
|
||||
|
||||
FuriString* source = furi_string_alloc();
|
||||
@@ -892,8 +897,8 @@ static void subghz_cli_command_encrypt_keeloq(Cli* cli, FuriString* args) {
|
||||
furi_string_free(source);
|
||||
}
|
||||
|
||||
static void subghz_cli_command_encrypt_raw(Cli* cli, FuriString* args) {
|
||||
UNUSED(cli);
|
||||
static void subghz_cli_command_encrypt_raw(PipeSide* pipe, FuriString* args) {
|
||||
UNUSED(pipe);
|
||||
uint8_t iv[16];
|
||||
|
||||
FuriString* source = furi_string_alloc();
|
||||
@@ -927,7 +932,7 @@ static void subghz_cli_command_encrypt_raw(Cli* cli, FuriString* args) {
|
||||
furi_string_free(source);
|
||||
}
|
||||
|
||||
static void subghz_cli_command_chat(Cli* cli, FuriString* args) {
|
||||
static void subghz_cli_command_chat(PipeSide* pipe, FuriString* args) {
|
||||
uint32_t frequency = 433920000;
|
||||
uint32_t device_ind = 0; // 0 - CC1101_INT, 1 - CC1101_EXT
|
||||
|
||||
@@ -963,7 +968,7 @@ static void subghz_cli_command_chat(Cli* cli, FuriString* args) {
|
||||
return;
|
||||
}
|
||||
|
||||
SubGhzChatWorker* subghz_chat = subghz_chat_worker_alloc(cli);
|
||||
SubGhzChatWorker* subghz_chat = subghz_chat_worker_alloc(pipe);
|
||||
|
||||
if(!subghz_chat_worker_start(subghz_chat, device, frequency)) {
|
||||
printf("Startup error SubGhzChatWorker\r\n");
|
||||
@@ -1000,13 +1005,12 @@ static void subghz_cli_command_chat(Cli* cli, FuriString* args) {
|
||||
chat_event = subghz_chat_worker_get_event_chat(subghz_chat);
|
||||
switch(chat_event.event) {
|
||||
case SubGhzChatEventInputData:
|
||||
if(chat_event.c == CliSymbolAsciiETX) {
|
||||
if(chat_event.c == CliKeyETX) {
|
||||
printf("\r\n");
|
||||
chat_event.event = SubGhzChatEventUserExit;
|
||||
subghz_chat_worker_put_event_chat(subghz_chat, &chat_event);
|
||||
break;
|
||||
} else if(
|
||||
(chat_event.c == CliSymbolAsciiBackspace) || (chat_event.c == CliSymbolAsciiDel)) {
|
||||
} else if((chat_event.c == CliKeyBackspace) || (chat_event.c == CliKeyDEL)) {
|
||||
size_t len = furi_string_utf8_length(input);
|
||||
if(len > furi_string_utf8_length(name)) {
|
||||
printf("%s", "\e[D\e[1P");
|
||||
@@ -1028,7 +1032,7 @@ static void subghz_cli_command_chat(Cli* cli, FuriString* args) {
|
||||
}
|
||||
furi_string_set(input, sysmsg);
|
||||
}
|
||||
} else if(chat_event.c == CliSymbolAsciiCR) {
|
||||
} else if(chat_event.c == CliKeyCR) {
|
||||
printf("\r\n");
|
||||
furi_string_push_back(input, '\r');
|
||||
furi_string_push_back(input, '\n');
|
||||
@@ -1042,7 +1046,7 @@ static void subghz_cli_command_chat(Cli* cli, FuriString* args) {
|
||||
furi_string_printf(input, "%s", furi_string_get_cstr(name));
|
||||
printf("%s", furi_string_get_cstr(input));
|
||||
fflush(stdout);
|
||||
} else if(chat_event.c == CliSymbolAsciiLF) {
|
||||
} else if(chat_event.c == CliKeyLF) {
|
||||
//cut out the symbol \n
|
||||
} else {
|
||||
putc(chat_event.c, stdout);
|
||||
@@ -1096,7 +1100,7 @@ static void subghz_cli_command_chat(Cli* cli, FuriString* args) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!cli_is_connected(cli)) {
|
||||
if(!cli_is_pipe_broken_or_is_etx_next_char(pipe)) {
|
||||
printf("\r\n");
|
||||
chat_event.event = SubGhzChatEventUserExit;
|
||||
subghz_chat_worker_put_event_chat(subghz_chat, &chat_event);
|
||||
@@ -1121,8 +1125,9 @@ static void subghz_cli_command_chat(Cli* cli, FuriString* args) {
|
||||
printf("\r\nExit chat\r\n");
|
||||
}
|
||||
|
||||
static void subghz_cli_command(Cli* cli, FuriString* args, void* context) {
|
||||
FuriString* cmd = furi_string_alloc();
|
||||
static void execute(PipeSide* pipe, FuriString* args, void* context) {
|
||||
FuriString* cmd;
|
||||
cmd = furi_string_alloc();
|
||||
|
||||
do {
|
||||
if(!args_read_string_and_trim(args, cmd)) {
|
||||
@@ -1131,53 +1136,53 @@ static void subghz_cli_command(Cli* cli, FuriString* args, void* context) {
|
||||
}
|
||||
|
||||
if(furi_string_cmp_str(cmd, "chat") == 0) {
|
||||
subghz_cli_command_chat(cli, args);
|
||||
subghz_cli_command_chat(pipe, args);
|
||||
break;
|
||||
}
|
||||
|
||||
if(furi_string_cmp_str(cmd, "tx") == 0) {
|
||||
subghz_cli_command_tx(cli, args, context);
|
||||
subghz_cli_command_tx(pipe, args, context);
|
||||
break;
|
||||
}
|
||||
|
||||
if(furi_string_cmp_str(cmd, "rx") == 0) {
|
||||
subghz_cli_command_rx(cli, args, context);
|
||||
subghz_cli_command_rx(pipe, args, context);
|
||||
break;
|
||||
}
|
||||
|
||||
if(furi_string_cmp_str(cmd, "rx_raw") == 0) {
|
||||
subghz_cli_command_rx_raw(cli, args, context);
|
||||
subghz_cli_command_rx_raw(pipe, args, context);
|
||||
break;
|
||||
}
|
||||
|
||||
if(furi_string_cmp_str(cmd, "decode_raw") == 0) {
|
||||
subghz_cli_command_decode_raw(cli, args, context);
|
||||
subghz_cli_command_decode_raw(pipe, args, context);
|
||||
break;
|
||||
}
|
||||
|
||||
if(furi_string_cmp_str(cmd, "tx_from_file") == 0) {
|
||||
subghz_cli_command_tx_from_file(cli, args, context);
|
||||
subghz_cli_command_tx_from_file(pipe, args, context);
|
||||
break;
|
||||
}
|
||||
|
||||
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
|
||||
if(furi_string_cmp_str(cmd, "encrypt_keeloq") == 0) {
|
||||
subghz_cli_command_encrypt_keeloq(cli, args);
|
||||
subghz_cli_command_encrypt_keeloq(pipe, args);
|
||||
break;
|
||||
}
|
||||
|
||||
if(furi_string_cmp_str(cmd, "encrypt_raw") == 0) {
|
||||
subghz_cli_command_encrypt_raw(cli, args);
|
||||
subghz_cli_command_encrypt_raw(pipe, args);
|
||||
break;
|
||||
}
|
||||
|
||||
if(furi_string_cmp_str(cmd, "tx_carrier") == 0) {
|
||||
subghz_cli_command_tx_carrier(cli, args, context);
|
||||
subghz_cli_command_tx_carrier(pipe, args, context);
|
||||
break;
|
||||
}
|
||||
|
||||
if(furi_string_cmp_str(cmd, "rx_carrier") == 0) {
|
||||
subghz_cli_command_rx_carrier(cli, args, context);
|
||||
subghz_cli_command_rx_carrier(pipe, args, context);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1188,14 +1193,4 @@ static void subghz_cli_command(Cli* cli, FuriString* args, void* context) {
|
||||
furi_string_free(cmd);
|
||||
}
|
||||
|
||||
void subghz_on_system_start(void) {
|
||||
#ifdef SRV_CLI
|
||||
Cli* cli = furi_record_open(RECORD_CLI);
|
||||
|
||||
cli_add_command(cli, "subghz", CliCommandFlagDefault, subghz_cli_command, NULL);
|
||||
|
||||
furi_record_close(RECORD_CLI);
|
||||
#else
|
||||
UNUSED(subghz_cli_command);
|
||||
#endif
|
||||
}
|
||||
CLI_COMMAND_INTERFACE(subghz, execute, CliCommandFlagDefault, 2048, CLI_APPID);
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
#include <cli/cli.h>
|
||||
|
||||
void subghz_on_system_start(void);
|
||||
|
||||
@@ -3,6 +3,7 @@ App(
|
||||
name="Basic services",
|
||||
apptype=FlipperAppType.METAPACKAGE,
|
||||
provides=[
|
||||
"cli_vcp",
|
||||
"crypto_start",
|
||||
"rpc_start",
|
||||
"expansion_start",
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <cli/cli.h>
|
||||
#include <lib/toolbox/args.h>
|
||||
#include <toolbox/pipe.h>
|
||||
#include <cli/cli_main_commands.h>
|
||||
#include <toolbox/cli/cli_registry.h>
|
||||
|
||||
#include <ble/ble.h>
|
||||
#include "bt_settings.h"
|
||||
#include "bt_service/bt.h"
|
||||
#include <profiles/serial_profile.h>
|
||||
|
||||
static void bt_cli_command_hci_info(Cli* cli, FuriString* args, void* context) {
|
||||
UNUSED(cli);
|
||||
static void bt_cli_command_hci_info(PipeSide* pipe, FuriString* args, void* context) {
|
||||
UNUSED(pipe);
|
||||
UNUSED(args);
|
||||
UNUSED(context);
|
||||
FuriString* buffer;
|
||||
@@ -19,7 +21,7 @@ static void bt_cli_command_hci_info(Cli* cli, FuriString* args, void* context) {
|
||||
furi_string_free(buffer);
|
||||
}
|
||||
|
||||
static void bt_cli_command_carrier_tx(Cli* cli, FuriString* args, void* context) {
|
||||
static void bt_cli_command_carrier_tx(PipeSide* pipe, FuriString* args, void* context) {
|
||||
UNUSED(context);
|
||||
int channel = 0;
|
||||
int power = 0;
|
||||
@@ -41,7 +43,7 @@ static void bt_cli_command_carrier_tx(Cli* cli, FuriString* args, void* context)
|
||||
printf("Press CTRL+C to stop\r\n");
|
||||
furi_hal_bt_start_tone_tx(channel, 0x19 + power);
|
||||
|
||||
while(!cli_cmd_interrupt_received(cli)) {
|
||||
while(!cli_is_pipe_broken_or_is_etx_next_char(pipe)) {
|
||||
furi_delay_ms(250);
|
||||
}
|
||||
furi_hal_bt_stop_tone_tx();
|
||||
@@ -51,7 +53,7 @@ static void bt_cli_command_carrier_tx(Cli* cli, FuriString* args, void* context)
|
||||
} while(false);
|
||||
}
|
||||
|
||||
static void bt_cli_command_carrier_rx(Cli* cli, FuriString* args, void* context) {
|
||||
static void bt_cli_command_carrier_rx(PipeSide* pipe, FuriString* args, void* context) {
|
||||
UNUSED(context);
|
||||
int channel = 0;
|
||||
|
||||
@@ -69,7 +71,7 @@ static void bt_cli_command_carrier_rx(Cli* cli, FuriString* args, void* context)
|
||||
|
||||
furi_hal_bt_start_packet_rx(channel, 1);
|
||||
|
||||
while(!cli_cmd_interrupt_received(cli)) {
|
||||
while(!cli_is_pipe_broken_or_is_etx_next_char(pipe)) {
|
||||
furi_delay_ms(250);
|
||||
printf("RSSI: %6.1f dB\r", (double)furi_hal_bt_get_rssi());
|
||||
fflush(stdout);
|
||||
@@ -82,7 +84,7 @@ static void bt_cli_command_carrier_rx(Cli* cli, FuriString* args, void* context)
|
||||
} while(false);
|
||||
}
|
||||
|
||||
static void bt_cli_command_packet_tx(Cli* cli, FuriString* args, void* context) {
|
||||
static void bt_cli_command_packet_tx(PipeSide* pipe, FuriString* args, void* context) {
|
||||
UNUSED(context);
|
||||
int channel = 0;
|
||||
int pattern = 0;
|
||||
@@ -119,7 +121,7 @@ static void bt_cli_command_packet_tx(Cli* cli, FuriString* args, void* context)
|
||||
printf("Press CTRL+C to stop\r\n");
|
||||
furi_hal_bt_start_packet_tx(channel, pattern, datarate);
|
||||
|
||||
while(!cli_cmd_interrupt_received(cli)) {
|
||||
while(!cli_is_pipe_broken_or_is_etx_next_char(pipe)) {
|
||||
furi_delay_ms(250);
|
||||
}
|
||||
furi_hal_bt_stop_packet_test();
|
||||
@@ -130,7 +132,7 @@ static void bt_cli_command_packet_tx(Cli* cli, FuriString* args, void* context)
|
||||
} while(false);
|
||||
}
|
||||
|
||||
static void bt_cli_command_packet_rx(Cli* cli, FuriString* args, void* context) {
|
||||
static void bt_cli_command_packet_rx(PipeSide* pipe, FuriString* args, void* context) {
|
||||
UNUSED(context);
|
||||
int channel = 0;
|
||||
int datarate = 1;
|
||||
@@ -152,7 +154,7 @@ static void bt_cli_command_packet_rx(Cli* cli, FuriString* args, void* context)
|
||||
printf("Press CTRL+C to stop\r\n");
|
||||
furi_hal_bt_start_packet_rx(channel, datarate);
|
||||
|
||||
while(!cli_cmd_interrupt_received(cli)) {
|
||||
while(!cli_is_pipe_broken_or_is_etx_next_char(pipe)) {
|
||||
furi_delay_ms(250);
|
||||
printf("RSSI: %03.1f dB\r", (double)furi_hal_bt_get_rssi());
|
||||
fflush(stdout);
|
||||
@@ -179,7 +181,7 @@ static void bt_cli_print_usage(void) {
|
||||
}
|
||||
}
|
||||
|
||||
static void bt_cli(Cli* cli, FuriString* args, void* context) {
|
||||
static void bt_cli(PipeSide* pipe, FuriString* args, void* context) {
|
||||
UNUSED(context);
|
||||
furi_record_open(RECORD_BT);
|
||||
|
||||
@@ -194,24 +196,24 @@ static void bt_cli(Cli* cli, FuriString* args, void* context) {
|
||||
break;
|
||||
}
|
||||
if(furi_string_cmp_str(cmd, "hci_info") == 0) {
|
||||
bt_cli_command_hci_info(cli, args, NULL);
|
||||
bt_cli_command_hci_info(pipe, args, NULL);
|
||||
break;
|
||||
}
|
||||
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug) && furi_hal_bt_is_testing_supported()) {
|
||||
if(furi_string_cmp_str(cmd, "tx_carrier") == 0) {
|
||||
bt_cli_command_carrier_tx(cli, args, NULL);
|
||||
bt_cli_command_carrier_tx(pipe, args, NULL);
|
||||
break;
|
||||
}
|
||||
if(furi_string_cmp_str(cmd, "rx_carrier") == 0) {
|
||||
bt_cli_command_carrier_rx(cli, args, NULL);
|
||||
bt_cli_command_carrier_rx(pipe, args, NULL);
|
||||
break;
|
||||
}
|
||||
if(furi_string_cmp_str(cmd, "tx_packet") == 0) {
|
||||
bt_cli_command_packet_tx(cli, args, NULL);
|
||||
bt_cli_command_packet_tx(pipe, args, NULL);
|
||||
break;
|
||||
}
|
||||
if(furi_string_cmp_str(cmd, "rx_packet") == 0) {
|
||||
bt_cli_command_packet_rx(cli, args, NULL);
|
||||
bt_cli_command_packet_rx(pipe, args, NULL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -229,8 +231,8 @@ static void bt_cli(Cli* cli, FuriString* args, void* context) {
|
||||
|
||||
void bt_on_system_start(void) {
|
||||
#ifdef SRV_CLI
|
||||
Cli* cli = furi_record_open(RECORD_CLI);
|
||||
cli_add_command(cli, RECORD_BT, CliCommandFlagDefault, bt_cli, NULL);
|
||||
CliRegistry* registry = furi_record_open(RECORD_CLI);
|
||||
cli_registry_add_command(registry, "bt", CliCommandFlagDefault, bt_cli, NULL);
|
||||
furi_record_close(RECORD_CLI);
|
||||
#else
|
||||
UNUSED(bt_cli);
|
||||
|
||||
@@ -1,10 +1,51 @@
|
||||
App(
|
||||
appid="cli",
|
||||
name="CliSrv",
|
||||
apptype=FlipperAppType.SERVICE,
|
||||
entry_point="cli_srv",
|
||||
apptype=FlipperAppType.STARTUP,
|
||||
entry_point="cli_on_system_start",
|
||||
cdefines=["SRV_CLI"],
|
||||
stack_size=4 * 1024,
|
||||
order=30,
|
||||
sdk_headers=["cli.h", "cli_vcp.h"],
|
||||
sources=[
|
||||
"cli_command_gpio.c",
|
||||
"cli_main_commands.c",
|
||||
"cli_main_shell.c",
|
||||
],
|
||||
# This STARTUP has to be processed before those that depend on the "cli" record.
|
||||
# "cli" used to be a SERVICE, but it's been converted into a STARTUP in order to
|
||||
# reduce RAM usage. The "block until record has been created" mechanism
|
||||
# unfortunately leads to a deadlock if the STARTUPs are processed sequentially.
|
||||
order=0,
|
||||
)
|
||||
|
||||
App(
|
||||
appid="cli_vcp",
|
||||
name="CliVcpSrv",
|
||||
apptype=FlipperAppType.SERVICE,
|
||||
entry_point="cli_vcp_srv",
|
||||
stack_size=1024,
|
||||
order=40,
|
||||
sdk_headers=["cli_vcp.h"],
|
||||
sources=["cli_vcp.c"],
|
||||
)
|
||||
|
||||
App(
|
||||
appid="cli_hello_world",
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
entry_point="cli_hello_world_ep",
|
||||
requires=["cli"],
|
||||
sources=["commands/hello_world.c"],
|
||||
)
|
||||
|
||||
App(
|
||||
appid="cli_neofetch",
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
entry_point="cli_neofetch_ep",
|
||||
requires=["cli"],
|
||||
sources=["commands/neofetch.c"],
|
||||
)
|
||||
|
||||
App(
|
||||
appid="cli_subshell_demo",
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
entry_point="cli_subshell_demo_ep",
|
||||
requires=["cli"],
|
||||
sources=["commands/subshell_demo.c"],
|
||||
)
|
||||
|
||||
@@ -1,484 +0,0 @@
|
||||
#include "cli_i.h"
|
||||
#include "cli_commands.h"
|
||||
#include "cli_vcp.h"
|
||||
#include <furi_hal_version.h>
|
||||
#include <loader/loader.h>
|
||||
|
||||
#define TAG "CliSrv"
|
||||
|
||||
#define CLI_INPUT_LEN_LIMIT 256
|
||||
|
||||
Cli* cli_alloc(void) {
|
||||
Cli* cli = malloc(sizeof(Cli));
|
||||
|
||||
CliCommandTree_init(cli->commands);
|
||||
|
||||
cli->last_line = furi_string_alloc();
|
||||
cli->line = furi_string_alloc();
|
||||
|
||||
cli->session = NULL;
|
||||
|
||||
cli->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
|
||||
|
||||
cli->idle_sem = furi_semaphore_alloc(1, 0);
|
||||
|
||||
return cli;
|
||||
}
|
||||
|
||||
void cli_putc(Cli* cli, char c) {
|
||||
furi_check(cli);
|
||||
if(cli->session != NULL) {
|
||||
cli->session->tx((uint8_t*)&c, 1);
|
||||
}
|
||||
}
|
||||
|
||||
char cli_getc(Cli* cli) {
|
||||
furi_check(cli);
|
||||
char c = 0;
|
||||
if(cli->session != NULL) {
|
||||
if(cli->session->rx((uint8_t*)&c, 1, FuriWaitForever) == 0) {
|
||||
cli_reset(cli);
|
||||
furi_delay_tick(10);
|
||||
}
|
||||
} else {
|
||||
cli_reset(cli);
|
||||
furi_delay_tick(10);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
void cli_write(Cli* cli, const uint8_t* buffer, size_t size) {
|
||||
furi_check(cli);
|
||||
if(cli->session != NULL) {
|
||||
cli->session->tx(buffer, size);
|
||||
}
|
||||
}
|
||||
|
||||
size_t cli_read(Cli* cli, uint8_t* buffer, size_t size) {
|
||||
furi_check(cli);
|
||||
if(cli->session != NULL) {
|
||||
return cli->session->rx(buffer, size, FuriWaitForever);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
size_t cli_read_timeout(Cli* cli, uint8_t* buffer, size_t size, uint32_t timeout) {
|
||||
furi_check(cli);
|
||||
if(cli->session != NULL) {
|
||||
return cli->session->rx(buffer, size, timeout);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool cli_is_connected(Cli* cli) {
|
||||
furi_check(cli);
|
||||
if(cli->session != NULL) {
|
||||
return cli->session->is_connected();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool cli_cmd_interrupt_received(Cli* cli) {
|
||||
furi_check(cli);
|
||||
char c = '\0';
|
||||
if(cli_is_connected(cli)) {
|
||||
if(cli->session->rx((uint8_t*)&c, 1, 0) == 1) {
|
||||
return c == CliSymbolAsciiETX;
|
||||
}
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void cli_print_usage(const char* cmd, const char* usage, const char* arg) {
|
||||
furi_check(cmd);
|
||||
furi_check(arg);
|
||||
furi_check(usage);
|
||||
|
||||
printf("%s: illegal option -- %s\r\nusage: %s %s", cmd, arg, cmd, usage);
|
||||
}
|
||||
|
||||
void cli_motd(void) {
|
||||
printf("\r\n"
|
||||
" _.-------.._ -,\r\n"
|
||||
" .-\"```\"--..,,_/ /`-, -, \\ \r\n"
|
||||
" .:\" /:/ /'\\ \\ ,_..., `. | |\r\n"
|
||||
" / ,----/:/ /`\\ _\\~`_-\"` _;\r\n"
|
||||
" ' / /`\"\"\"'\\ \\ \\.~`_-' ,-\"'/ \r\n"
|
||||
" | | | 0 | | .-' ,/` /\r\n"
|
||||
" | ,..\\ \\ ,.-\"` ,/` /\r\n"
|
||||
" ; : `/`\"\"\\` ,/--==,/-----,\r\n"
|
||||
" | `-...| -.___-Z:_______J...---;\r\n"
|
||||
" : ` _-'\r\n"
|
||||
" _L_ _ ___ ___ ___ ___ ____--\"`___ _ ___\r\n"
|
||||
"| __|| | |_ _|| _ \\| _ \\| __|| _ \\ / __|| | |_ _|\r\n"
|
||||
"| _| | |__ | | | _/| _/| _| | / | (__ | |__ | |\r\n"
|
||||
"|_| |____||___||_| |_| |___||_|_\\ \\___||____||___|\r\n"
|
||||
"\r\n"
|
||||
"Welcome to Flipper Zero Command Line Interface!\r\n"
|
||||
"Read the manual: https://docs.flipper.net/development/cli\r\n"
|
||||
"Run `help` or `?` to list available commands\r\n"
|
||||
"\r\n");
|
||||
|
||||
const Version* firmware_version = furi_hal_version_get_firmware_version();
|
||||
if(firmware_version) {
|
||||
printf(
|
||||
"Firmware version: %s %s (%s%s built on %s)\r\n",
|
||||
version_get_gitbranch(firmware_version),
|
||||
version_get_version(firmware_version),
|
||||
version_get_githash(firmware_version),
|
||||
version_get_dirty_flag(firmware_version) ? "-dirty" : "",
|
||||
version_get_builddate(firmware_version));
|
||||
}
|
||||
}
|
||||
|
||||
void cli_nl(Cli* cli) {
|
||||
UNUSED(cli);
|
||||
printf("\r\n");
|
||||
}
|
||||
|
||||
void cli_prompt(Cli* cli) {
|
||||
UNUSED(cli);
|
||||
printf("\r\n>: %s", furi_string_get_cstr(cli->line));
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
void cli_reset(Cli* cli) {
|
||||
// cli->last_line is cleared and cli->line's buffer moved to cli->last_line
|
||||
furi_string_move(cli->last_line, cli->line);
|
||||
// Reiniting cli->line
|
||||
cli->line = furi_string_alloc();
|
||||
cli->cursor_position = 0;
|
||||
}
|
||||
|
||||
static void cli_handle_backspace(Cli* cli) {
|
||||
if(cli->cursor_position > 0) {
|
||||
furi_assert(furi_string_size(cli->line) > 0);
|
||||
// Other side
|
||||
printf("\e[D\e[1P");
|
||||
fflush(stdout);
|
||||
// Our side
|
||||
furi_string_replace_at(cli->line, cli->cursor_position - 1, 1, "");
|
||||
|
||||
cli->cursor_position--;
|
||||
} else {
|
||||
cli_putc(cli, CliSymbolAsciiBell);
|
||||
}
|
||||
}
|
||||
|
||||
static void cli_normalize_line(Cli* cli) {
|
||||
furi_string_trim(cli->line);
|
||||
cli->cursor_position = furi_string_size(cli->line);
|
||||
}
|
||||
|
||||
static void cli_execute_command(Cli* cli, CliCommand* command, FuriString* args) {
|
||||
if(!(command->flags & CliCommandFlagInsomniaSafe)) {
|
||||
furi_hal_power_insomnia_enter();
|
||||
}
|
||||
|
||||
// Ensure that we running alone
|
||||
if(!(command->flags & CliCommandFlagParallelSafe)) {
|
||||
Loader* loader = furi_record_open(RECORD_LOADER);
|
||||
bool safety_lock = loader_lock(loader);
|
||||
if(safety_lock) {
|
||||
// Execute command
|
||||
command->callback(cli, args, command->context);
|
||||
loader_unlock(loader);
|
||||
} else {
|
||||
printf("Other application is running, close it first");
|
||||
}
|
||||
furi_record_close(RECORD_LOADER);
|
||||
} else {
|
||||
// Execute command
|
||||
command->callback(cli, args, command->context);
|
||||
}
|
||||
|
||||
if(!(command->flags & CliCommandFlagInsomniaSafe)) {
|
||||
furi_hal_power_insomnia_exit();
|
||||
}
|
||||
}
|
||||
|
||||
static void cli_handle_enter(Cli* cli) {
|
||||
cli_normalize_line(cli);
|
||||
|
||||
if(furi_string_size(cli->line) == 0) {
|
||||
cli_prompt(cli);
|
||||
return;
|
||||
}
|
||||
|
||||
// Command and args container
|
||||
FuriString* command;
|
||||
command = furi_string_alloc();
|
||||
FuriString* args;
|
||||
args = furi_string_alloc();
|
||||
|
||||
// Split command and args
|
||||
size_t ws = furi_string_search_char(cli->line, ' ');
|
||||
if(ws == FURI_STRING_FAILURE) {
|
||||
furi_string_set(command, cli->line);
|
||||
} else {
|
||||
furi_string_set_n(command, cli->line, 0, ws);
|
||||
furi_string_set_n(args, cli->line, ws, furi_string_size(cli->line));
|
||||
furi_string_trim(args);
|
||||
}
|
||||
|
||||
// Search for command
|
||||
furi_check(furi_mutex_acquire(cli->mutex, FuriWaitForever) == FuriStatusOk);
|
||||
CliCommand* cli_command_ptr = CliCommandTree_get(cli->commands, command);
|
||||
|
||||
if(cli_command_ptr) { //-V547
|
||||
CliCommand cli_command;
|
||||
memcpy(&cli_command, cli_command_ptr, sizeof(CliCommand));
|
||||
furi_check(furi_mutex_release(cli->mutex) == FuriStatusOk);
|
||||
cli_nl(cli);
|
||||
cli_execute_command(cli, &cli_command, args);
|
||||
} else {
|
||||
furi_check(furi_mutex_release(cli->mutex) == FuriStatusOk);
|
||||
cli_nl(cli);
|
||||
printf(
|
||||
"`%s` command not found, use `help` or `?` to list all available commands",
|
||||
furi_string_get_cstr(command));
|
||||
cli_putc(cli, CliSymbolAsciiBell);
|
||||
}
|
||||
|
||||
cli_reset(cli);
|
||||
cli_prompt(cli);
|
||||
|
||||
// Cleanup command and args
|
||||
furi_string_free(command);
|
||||
furi_string_free(args);
|
||||
}
|
||||
|
||||
static void cli_handle_autocomplete(Cli* cli) {
|
||||
cli_normalize_line(cli);
|
||||
|
||||
if(furi_string_size(cli->line) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
cli_nl(cli);
|
||||
|
||||
// Prepare common base for autocomplete
|
||||
FuriString* common;
|
||||
common = furi_string_alloc();
|
||||
// Iterate throw commands
|
||||
for
|
||||
M_EACH(cli_command, cli->commands, CliCommandTree_t) {
|
||||
// Process only if starts with line buffer
|
||||
if(furi_string_start_with(*cli_command->key_ptr, cli->line)) {
|
||||
// Show autocomplete option
|
||||
printf("%s\r\n", furi_string_get_cstr(*cli_command->key_ptr));
|
||||
// Process common base for autocomplete
|
||||
if(furi_string_size(common) > 0) {
|
||||
// Choose shortest string
|
||||
const size_t key_size = furi_string_size(*cli_command->key_ptr);
|
||||
const size_t common_size = furi_string_size(common);
|
||||
const size_t min_size = key_size > common_size ? common_size : key_size;
|
||||
size_t i = 0;
|
||||
while(i < min_size) {
|
||||
// Stop when do not match
|
||||
if(furi_string_get_char(*cli_command->key_ptr, i) !=
|
||||
furi_string_get_char(common, i)) {
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
// Cut right part if any
|
||||
furi_string_left(common, i);
|
||||
} else {
|
||||
// Start with something
|
||||
furi_string_set(common, *cli_command->key_ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Replace line buffer if autocomplete better
|
||||
if(furi_string_size(common) > furi_string_size(cli->line)) {
|
||||
furi_string_set(cli->line, common);
|
||||
cli->cursor_position = furi_string_size(cli->line);
|
||||
}
|
||||
// Cleanup
|
||||
furi_string_free(common);
|
||||
// Show prompt
|
||||
cli_prompt(cli);
|
||||
}
|
||||
|
||||
static void cli_handle_escape(Cli* cli, char c) {
|
||||
if(c == 'A') {
|
||||
// Use previous command if line buffer is empty
|
||||
if(furi_string_size(cli->line) == 0 && furi_string_cmp(cli->line, cli->last_line) != 0) {
|
||||
// Set line buffer and cursor position
|
||||
furi_string_set(cli->line, cli->last_line);
|
||||
cli->cursor_position = furi_string_size(cli->line);
|
||||
// Show new line to user
|
||||
printf("%s", furi_string_get_cstr(cli->line));
|
||||
}
|
||||
} else if(c == 'B') {
|
||||
} else if(c == 'C') {
|
||||
if(cli->cursor_position < furi_string_size(cli->line)) {
|
||||
cli->cursor_position++;
|
||||
printf("\e[C");
|
||||
}
|
||||
} else if(c == 'D') {
|
||||
if(cli->cursor_position > 0) {
|
||||
cli->cursor_position--;
|
||||
printf("\e[D");
|
||||
}
|
||||
}
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
void cli_process_input(Cli* cli) {
|
||||
char in_chr = cli_getc(cli);
|
||||
size_t rx_len;
|
||||
|
||||
if(in_chr == CliSymbolAsciiTab) {
|
||||
cli_handle_autocomplete(cli);
|
||||
} else if(in_chr == CliSymbolAsciiSOH) {
|
||||
furi_delay_ms(33); // We are too fast, Minicom is not ready yet
|
||||
cli_motd();
|
||||
cli_prompt(cli);
|
||||
} else if(in_chr == CliSymbolAsciiETX) {
|
||||
cli_reset(cli);
|
||||
cli_prompt(cli);
|
||||
} else if(in_chr == CliSymbolAsciiEOT) {
|
||||
cli_reset(cli);
|
||||
} else if(in_chr == CliSymbolAsciiEsc) {
|
||||
rx_len = cli_read(cli, (uint8_t*)&in_chr, 1);
|
||||
if((rx_len > 0) && (in_chr == '[')) {
|
||||
cli_read(cli, (uint8_t*)&in_chr, 1);
|
||||
cli_handle_escape(cli, in_chr);
|
||||
} else {
|
||||
cli_putc(cli, CliSymbolAsciiBell);
|
||||
}
|
||||
} else if(in_chr == CliSymbolAsciiBackspace || in_chr == CliSymbolAsciiDel) {
|
||||
cli_handle_backspace(cli);
|
||||
} else if(in_chr == CliSymbolAsciiCR) {
|
||||
cli_handle_enter(cli);
|
||||
} else if(
|
||||
(in_chr >= 0x20 && in_chr < 0x7F) && //-V560
|
||||
(furi_string_size(cli->line) < CLI_INPUT_LEN_LIMIT)) {
|
||||
if(cli->cursor_position == furi_string_size(cli->line)) {
|
||||
furi_string_push_back(cli->line, in_chr);
|
||||
cli_putc(cli, in_chr);
|
||||
} else {
|
||||
// Insert character to line buffer
|
||||
const char in_str[2] = {in_chr, 0};
|
||||
furi_string_replace_at(cli->line, cli->cursor_position, 0, in_str);
|
||||
|
||||
// Print character in replace mode
|
||||
printf("\e[4h%c\e[4l", in_chr);
|
||||
fflush(stdout);
|
||||
}
|
||||
cli->cursor_position++;
|
||||
} else {
|
||||
cli_putc(cli, CliSymbolAsciiBell);
|
||||
}
|
||||
}
|
||||
|
||||
void cli_add_command(
|
||||
Cli* cli,
|
||||
const char* name,
|
||||
CliCommandFlag flags,
|
||||
CliCallback callback,
|
||||
void* context) {
|
||||
furi_check(cli);
|
||||
FuriString* name_str;
|
||||
name_str = furi_string_alloc_set(name);
|
||||
furi_string_trim(name_str);
|
||||
|
||||
size_t name_replace;
|
||||
do {
|
||||
name_replace = furi_string_replace(name_str, " ", "_");
|
||||
} while(name_replace != FURI_STRING_FAILURE);
|
||||
|
||||
CliCommand c;
|
||||
c.callback = callback;
|
||||
c.context = context;
|
||||
c.flags = flags;
|
||||
|
||||
furi_check(furi_mutex_acquire(cli->mutex, FuriWaitForever) == FuriStatusOk);
|
||||
CliCommandTree_set_at(cli->commands, name_str, c);
|
||||
furi_check(furi_mutex_release(cli->mutex) == FuriStatusOk);
|
||||
|
||||
furi_string_free(name_str);
|
||||
}
|
||||
|
||||
void cli_delete_command(Cli* cli, const char* name) {
|
||||
furi_check(cli);
|
||||
FuriString* name_str;
|
||||
name_str = furi_string_alloc_set(name);
|
||||
furi_string_trim(name_str);
|
||||
|
||||
size_t name_replace;
|
||||
do {
|
||||
name_replace = furi_string_replace(name_str, " ", "_");
|
||||
} while(name_replace != FURI_STRING_FAILURE);
|
||||
|
||||
furi_check(furi_mutex_acquire(cli->mutex, FuriWaitForever) == FuriStatusOk);
|
||||
CliCommandTree_erase(cli->commands, name_str);
|
||||
furi_check(furi_mutex_release(cli->mutex) == FuriStatusOk);
|
||||
|
||||
furi_string_free(name_str);
|
||||
}
|
||||
|
||||
void cli_session_open(Cli* cli, const void* session) {
|
||||
furi_check(cli);
|
||||
|
||||
furi_check(furi_mutex_acquire(cli->mutex, FuriWaitForever) == FuriStatusOk);
|
||||
cli->session = session;
|
||||
if(cli->session != NULL) {
|
||||
cli->session->init();
|
||||
furi_thread_set_stdout_callback(cli->session->tx_stdout, NULL);
|
||||
} else {
|
||||
furi_thread_set_stdout_callback(NULL, NULL);
|
||||
}
|
||||
furi_semaphore_release(cli->idle_sem);
|
||||
furi_check(furi_mutex_release(cli->mutex) == FuriStatusOk);
|
||||
}
|
||||
|
||||
void cli_session_close(Cli* cli) {
|
||||
furi_check(cli);
|
||||
|
||||
furi_check(furi_mutex_acquire(cli->mutex, FuriWaitForever) == FuriStatusOk);
|
||||
if(cli->session != NULL) {
|
||||
cli->session->deinit();
|
||||
}
|
||||
cli->session = NULL;
|
||||
furi_thread_set_stdout_callback(NULL, NULL);
|
||||
furi_check(furi_mutex_release(cli->mutex) == FuriStatusOk);
|
||||
}
|
||||
|
||||
int32_t cli_srv(void* p) {
|
||||
UNUSED(p);
|
||||
Cli* cli = cli_alloc();
|
||||
|
||||
// Init basic cli commands
|
||||
cli_commands_init(cli);
|
||||
|
||||
furi_record_create(RECORD_CLI, cli);
|
||||
|
||||
if(cli->session != NULL) {
|
||||
furi_thread_set_stdout_callback(cli->session->tx_stdout, NULL);
|
||||
} else {
|
||||
furi_thread_set_stdout_callback(NULL, NULL);
|
||||
}
|
||||
|
||||
if(furi_hal_rtc_get_boot_mode() == FuriHalRtcBootModeNormal) {
|
||||
cli_session_open(cli, &cli_vcp);
|
||||
} else {
|
||||
FURI_LOG_W(TAG, "Skipping start in special boot mode");
|
||||
}
|
||||
|
||||
while(1) {
|
||||
if(cli->session != NULL) {
|
||||
cli_process_input(cli);
|
||||
} else {
|
||||
furi_check(furi_semaphore_acquire(cli->idle_sem, FuriWaitForever) == FuriStatusOk);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,134 +0,0 @@
|
||||
/**
|
||||
* @file cli.h
|
||||
* Cli API
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <furi.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
CliSymbolAsciiSOH = 0x01,
|
||||
CliSymbolAsciiETX = 0x03,
|
||||
CliSymbolAsciiEOT = 0x04,
|
||||
CliSymbolAsciiBell = 0x07,
|
||||
CliSymbolAsciiBackspace = 0x08,
|
||||
CliSymbolAsciiTab = 0x09,
|
||||
CliSymbolAsciiLF = 0x0A,
|
||||
CliSymbolAsciiCR = 0x0D,
|
||||
CliSymbolAsciiEsc = 0x1B,
|
||||
CliSymbolAsciiUS = 0x1F,
|
||||
CliSymbolAsciiSpace = 0x20,
|
||||
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 */
|
||||
CliCommandFlagInsomniaSafe = (1 << 1), /**< Safe to run with insomnia mode on */
|
||||
} CliCommandFlag;
|
||||
|
||||
#define RECORD_CLI "cli"
|
||||
|
||||
/** Cli type anonymous structure */
|
||||
typedef struct Cli Cli;
|
||||
|
||||
/** Cli callback function pointer. Implement this interface and use
|
||||
* add_cli_command
|
||||
* @param args string with what was passed after command
|
||||
* @param context pointer to whatever you gave us on cli_add_command
|
||||
*/
|
||||
typedef void (*CliCallback)(Cli* cli, FuriString* args, void* context);
|
||||
|
||||
/** Add cli command Registers you command callback
|
||||
*
|
||||
* @param cli pointer to cli instance
|
||||
* @param name command name
|
||||
* @param flags CliCommandFlag
|
||||
* @param callback callback function
|
||||
* @param context pointer to whatever we need to pass to callback
|
||||
*/
|
||||
void cli_add_command(
|
||||
Cli* cli,
|
||||
const char* name,
|
||||
CliCommandFlag flags,
|
||||
CliCallback callback,
|
||||
void* context);
|
||||
|
||||
/** Print unified cmd usage tip
|
||||
*
|
||||
* @param cmd cmd name
|
||||
* @param usage usage tip
|
||||
* @param arg arg passed by user
|
||||
*/
|
||||
void cli_print_usage(const char* cmd, const char* usage, const char* arg);
|
||||
|
||||
/** Delete cli command
|
||||
*
|
||||
* @param cli pointer to cli instance
|
||||
* @param name command name
|
||||
*/
|
||||
void cli_delete_command(Cli* cli, const char* name);
|
||||
|
||||
/** Read from terminal
|
||||
*
|
||||
* @param cli Cli instance
|
||||
* @param buffer pointer to buffer
|
||||
* @param size size of buffer in bytes
|
||||
*
|
||||
* @return bytes read
|
||||
*/
|
||||
size_t cli_read(Cli* cli, uint8_t* buffer, size_t size);
|
||||
|
||||
/** Non-blocking read from terminal
|
||||
*
|
||||
* @param cli Cli instance
|
||||
* @param buffer pointer to buffer
|
||||
* @param size size of buffer in bytes
|
||||
* @param timeout timeout value in ms
|
||||
*
|
||||
* @return bytes read
|
||||
*/
|
||||
size_t cli_read_timeout(Cli* cli, uint8_t* buffer, size_t size, uint32_t timeout);
|
||||
|
||||
/** Non-blocking check for interrupt command received
|
||||
*
|
||||
* @param cli Cli instance
|
||||
*
|
||||
* @return true if received
|
||||
*/
|
||||
bool cli_cmd_interrupt_received(Cli* cli);
|
||||
|
||||
/** Write to terminal Do it only from inside of cli call.
|
||||
*
|
||||
* @param cli Cli instance
|
||||
* @param buffer pointer to buffer
|
||||
* @param size size of buffer in bytes
|
||||
*/
|
||||
void cli_write(Cli* cli, const uint8_t* buffer, size_t size);
|
||||
|
||||
/** Read character
|
||||
*
|
||||
* @param cli Cli instance
|
||||
*
|
||||
* @return char
|
||||
*/
|
||||
char cli_getc(Cli* cli);
|
||||
|
||||
/** New line Send new ine sequence
|
||||
*/
|
||||
void cli_nl(Cli* cli);
|
||||
|
||||
void cli_session_open(Cli* cli, const void* session);
|
||||
|
||||
void cli_session_close(Cli* cli);
|
||||
|
||||
bool cli_is_connected(Cli* cli);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -3,6 +3,8 @@
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <lib/toolbox/args.h>
|
||||
#include <toolbox/pipe.h>
|
||||
#include <toolbox/cli/cli_command.h>
|
||||
|
||||
void cli_command_gpio_print_usage(void) {
|
||||
printf("Usage:\r\n");
|
||||
@@ -70,8 +72,8 @@ static GpioParseReturn gpio_command_parse(FuriString* args, size_t* pin_num, uin
|
||||
return ret;
|
||||
}
|
||||
|
||||
void cli_command_gpio_mode(Cli* cli, FuriString* args, void* context) {
|
||||
UNUSED(cli);
|
||||
void cli_command_gpio_mode(PipeSide* pipe, FuriString* args, void* context) {
|
||||
UNUSED(pipe);
|
||||
UNUSED(context);
|
||||
|
||||
size_t num = 0;
|
||||
@@ -93,7 +95,7 @@ void cli_command_gpio_mode(Cli* cli, FuriString* args, void* context) {
|
||||
if(gpio_pins[num].debug) { //-V779
|
||||
printf(
|
||||
"Changing this pin mode may damage hardware. Are you sure you want to continue? (y/n)?\r\n");
|
||||
char c = cli_getc(cli);
|
||||
char c = getchar();
|
||||
if(c != 'y' && c != 'Y') {
|
||||
printf("Cancelled.\r\n");
|
||||
return;
|
||||
@@ -110,8 +112,8 @@ void cli_command_gpio_mode(Cli* cli, FuriString* args, void* context) {
|
||||
}
|
||||
}
|
||||
|
||||
void cli_command_gpio_read(Cli* cli, FuriString* args, void* context) {
|
||||
UNUSED(cli);
|
||||
void cli_command_gpio_read(PipeSide* pipe, FuriString* args, void* context) {
|
||||
UNUSED(pipe);
|
||||
UNUSED(context);
|
||||
|
||||
size_t num = 0;
|
||||
@@ -131,7 +133,8 @@ void cli_command_gpio_read(Cli* cli, FuriString* args, void* context) {
|
||||
printf("Pin %s <= %u", gpio_pins[num].name, val);
|
||||
}
|
||||
|
||||
void cli_command_gpio_set(Cli* cli, FuriString* args, void* context) {
|
||||
void cli_command_gpio_set(PipeSide* pipe, FuriString* args, void* context) {
|
||||
UNUSED(pipe);
|
||||
UNUSED(context);
|
||||
|
||||
size_t num = 0;
|
||||
@@ -159,7 +162,7 @@ void cli_command_gpio_set(Cli* cli, FuriString* args, void* context) {
|
||||
if(gpio_pins[num].debug) {
|
||||
printf(
|
||||
"Setting this pin may damage hardware. Are you sure you want to continue? (y/n)?\r\n");
|
||||
char c = cli_getc(cli);
|
||||
char c = getchar();
|
||||
if(c != 'y' && c != 'Y') {
|
||||
printf("Cancelled.\r\n");
|
||||
return;
|
||||
@@ -170,7 +173,7 @@ void cli_command_gpio_set(Cli* cli, FuriString* args, void* context) {
|
||||
printf("Pin %s => %u", gpio_pins[num].name, !!value);
|
||||
}
|
||||
|
||||
void cli_command_gpio(Cli* cli, FuriString* args, void* context) {
|
||||
void cli_command_gpio(PipeSide* pipe, FuriString* args, void* context) {
|
||||
FuriString* cmd;
|
||||
cmd = furi_string_alloc();
|
||||
|
||||
@@ -181,17 +184,17 @@ void cli_command_gpio(Cli* cli, FuriString* args, void* context) {
|
||||
}
|
||||
|
||||
if(furi_string_cmp_str(cmd, "mode") == 0) {
|
||||
cli_command_gpio_mode(cli, args, context);
|
||||
cli_command_gpio_mode(pipe, args, context);
|
||||
break;
|
||||
}
|
||||
|
||||
if(furi_string_cmp_str(cmd, "set") == 0) {
|
||||
cli_command_gpio_set(cli, args, context);
|
||||
cli_command_gpio_set(pipe, args, context);
|
||||
break;
|
||||
}
|
||||
|
||||
if(furi_string_cmp_str(cmd, "read") == 0) {
|
||||
cli_command_gpio_read(cli, args, context);
|
||||
cli_command_gpio_read(pipe, args, context);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "cli_i.h"
|
||||
#include <toolbox/pipe.h>
|
||||
|
||||
void cli_command_gpio(Cli* cli, FuriString* args, void* context);
|
||||
void cli_command_gpio(PipeSide* pipe, FuriString* args, void* context);
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "cli_i.h"
|
||||
|
||||
void cli_commands_init(Cli* cli);
|
||||
@@ -1,68 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "cli.h"
|
||||
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
|
||||
#include <m-dict.h>
|
||||
#include <m-bptree.h>
|
||||
#include <m-array.h>
|
||||
|
||||
#include "cli_vcp.h"
|
||||
|
||||
#define CLI_LINE_SIZE_MAX
|
||||
#define CLI_COMMANDS_TREE_RANK 4
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
CliCallback callback;
|
||||
void* context;
|
||||
uint32_t flags;
|
||||
} CliCommand;
|
||||
|
||||
struct CliSession {
|
||||
void (*init)(void);
|
||||
void (*deinit)(void);
|
||||
size_t (*rx)(uint8_t* buffer, size_t size, uint32_t timeout);
|
||||
size_t (*rx_stdin)(uint8_t* buffer, size_t size, uint32_t timeout, void* context);
|
||||
void (*tx)(const uint8_t* buffer, size_t size);
|
||||
void (*tx_stdout)(const char* data, size_t size, void* context);
|
||||
bool (*is_connected)(void);
|
||||
};
|
||||
|
||||
BPTREE_DEF2(
|
||||
CliCommandTree,
|
||||
CLI_COMMANDS_TREE_RANK,
|
||||
FuriString*,
|
||||
FURI_STRING_OPLIST,
|
||||
CliCommand,
|
||||
M_POD_OPLIST)
|
||||
|
||||
#define M_OPL_CliCommandTree_t() BPTREE_OPLIST(CliCommandTree, M_POD_OPLIST)
|
||||
|
||||
struct Cli {
|
||||
CliCommandTree_t commands;
|
||||
FuriMutex* mutex;
|
||||
FuriSemaphore* idle_sem;
|
||||
FuriString* last_line;
|
||||
FuriString* line;
|
||||
const CliSession* session;
|
||||
|
||||
size_t cursor_position;
|
||||
};
|
||||
|
||||
Cli* cli_alloc(void);
|
||||
|
||||
void cli_reset(Cli* cli);
|
||||
|
||||
void cli_putc(Cli* cli, char c);
|
||||
|
||||
void cli_stdout_callback(void* _cookie, const char* data, size_t size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "cli_commands.h"
|
||||
#include "cli_main_commands.h"
|
||||
#include "cli_command_gpio.h"
|
||||
#include <toolbox/cli/cli_ansi.h>
|
||||
|
||||
#include <core/thread.h>
|
||||
#include <furi_hal.h>
|
||||
@@ -11,6 +12,7 @@
|
||||
#include <loader/loader.h>
|
||||
#include <lib/toolbox/args.h>
|
||||
#include <lib/toolbox/strint.h>
|
||||
#include <toolbox/pipe.h>
|
||||
|
||||
// Close to ISO, `date +'%Y-%m-%d %H:%M:%S %u'`
|
||||
#define CLI_DATE_FORMAT "%.4d-%.2d-%.2d %.2d:%.2d:%.2d %d"
|
||||
@@ -34,8 +36,8 @@ void cli_command_info_callback(const char* key, const char* value, bool last, vo
|
||||
* @param args The arguments
|
||||
* @param context The context
|
||||
*/
|
||||
void cli_command_info(Cli* cli, FuriString* args, void* context) {
|
||||
UNUSED(cli);
|
||||
void cli_command_info(PipeSide* pipe, FuriString* args, void* context) {
|
||||
UNUSED(pipe);
|
||||
|
||||
if(context) {
|
||||
furi_hal_info_get(cli_command_info_callback, '_', NULL);
|
||||
@@ -53,56 +55,16 @@ void cli_command_info(Cli* cli, FuriString* args, void* context) {
|
||||
}
|
||||
}
|
||||
|
||||
void cli_command_help(Cli* cli, FuriString* args, void* context) {
|
||||
UNUSED(args);
|
||||
UNUSED(context);
|
||||
printf("Commands available:");
|
||||
|
||||
// Command count
|
||||
const size_t commands_count = CliCommandTree_size(cli->commands);
|
||||
const size_t commands_count_mid = commands_count / 2 + commands_count % 2;
|
||||
|
||||
// Use 2 iterators from start and middle to show 2 columns
|
||||
CliCommandTree_it_t it_left;
|
||||
CliCommandTree_it(it_left, cli->commands);
|
||||
CliCommandTree_it_t it_right;
|
||||
CliCommandTree_it(it_right, cli->commands);
|
||||
for(size_t i = 0; i < commands_count_mid; i++)
|
||||
CliCommandTree_next(it_right);
|
||||
|
||||
// Iterate throw tree
|
||||
for(size_t i = 0; i < commands_count_mid; i++) {
|
||||
printf("\r\n");
|
||||
// Left Column
|
||||
if(!CliCommandTree_end_p(it_left)) {
|
||||
printf("%-30s", furi_string_get_cstr(*CliCommandTree_ref(it_left)->key_ptr));
|
||||
CliCommandTree_next(it_left);
|
||||
}
|
||||
// Right Column
|
||||
if(!CliCommandTree_end_p(it_right)) {
|
||||
printf("%s", furi_string_get_cstr(*CliCommandTree_ref(it_right)->key_ptr));
|
||||
CliCommandTree_next(it_right);
|
||||
}
|
||||
};
|
||||
|
||||
if(furi_string_size(args) > 0) {
|
||||
cli_nl(cli);
|
||||
printf("`");
|
||||
printf("%s", furi_string_get_cstr(args));
|
||||
printf("` command not found");
|
||||
}
|
||||
}
|
||||
|
||||
void cli_command_uptime(Cli* cli, FuriString* args, void* context) {
|
||||
UNUSED(cli);
|
||||
void cli_command_uptime(PipeSide* pipe, FuriString* args, void* context) {
|
||||
UNUSED(pipe);
|
||||
UNUSED(args);
|
||||
UNUSED(context);
|
||||
uint32_t uptime = furi_get_tick() / furi_kernel_get_tick_frequency();
|
||||
printf("Uptime: %luh%lum%lus", uptime / 60 / 60, uptime / 60 % 60, uptime % 60);
|
||||
}
|
||||
|
||||
void cli_command_date(Cli* cli, FuriString* args, void* context) {
|
||||
UNUSED(cli);
|
||||
void cli_command_date(PipeSide* pipe, FuriString* args, void* context) {
|
||||
UNUSED(pipe);
|
||||
UNUSED(context);
|
||||
|
||||
DateTime datetime = {0};
|
||||
@@ -170,21 +132,12 @@ void cli_command_date(Cli* cli, FuriString* args, void* context) {
|
||||
}
|
||||
}
|
||||
|
||||
void cli_command_src(Cli* cli, FuriString* args, void* context) {
|
||||
// Quality of life feature for people exploring CLI on lab.flipper.net/cli
|
||||
// By Yousef AK
|
||||
UNUSED(cli);
|
||||
UNUSED(args);
|
||||
UNUSED(context);
|
||||
|
||||
printf("https://github.com/DarkFlippers/unleashed-firmware");
|
||||
}
|
||||
|
||||
#define CLI_COMMAND_LOG_RING_SIZE 2048
|
||||
#define CLI_COMMAND_LOG_BUFFER_SIZE 64
|
||||
|
||||
void cli_command_log_tx_callback(const uint8_t* buffer, size_t size, void* context) {
|
||||
furi_stream_buffer_send(context, buffer, size, 0);
|
||||
PipeSide* pipe = context;
|
||||
pipe_send(pipe, buffer, size);
|
||||
}
|
||||
|
||||
bool cli_command_log_level_set_from_string(FuriString* level) {
|
||||
@@ -206,16 +159,13 @@ bool cli_command_log_level_set_from_string(FuriString* level) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void cli_command_log(Cli* cli, FuriString* args, void* context) {
|
||||
void cli_command_log(PipeSide* pipe, FuriString* args, void* context) {
|
||||
UNUSED(context);
|
||||
FuriStreamBuffer* ring = furi_stream_buffer_alloc(CLI_COMMAND_LOG_RING_SIZE, 1);
|
||||
uint8_t buffer[CLI_COMMAND_LOG_BUFFER_SIZE];
|
||||
FuriLogLevel previous_level = furi_log_get_level();
|
||||
bool restore_log_level = false;
|
||||
|
||||
if(furi_string_size(args) > 0) {
|
||||
if(!cli_command_log_level_set_from_string(args)) {
|
||||
furi_stream_buffer_free(ring);
|
||||
return;
|
||||
}
|
||||
restore_log_level = true;
|
||||
@@ -227,16 +177,15 @@ void cli_command_log(Cli* cli, FuriString* args, void* context) {
|
||||
|
||||
FuriLogHandler log_handler = {
|
||||
.callback = cli_command_log_tx_callback,
|
||||
.context = ring,
|
||||
.context = pipe,
|
||||
};
|
||||
|
||||
furi_log_add_handler(log_handler);
|
||||
|
||||
printf("Use <log ?> to list available log levels\r\n");
|
||||
printf("Press CTRL+C to stop...\r\n");
|
||||
while(!cli_cmd_interrupt_received(cli)) {
|
||||
size_t ret = furi_stream_buffer_receive(ring, buffer, CLI_COMMAND_LOG_BUFFER_SIZE, 50);
|
||||
cli_write(cli, buffer, ret);
|
||||
while(!cli_is_pipe_broken_or_is_etx_next_char(pipe)) {
|
||||
furi_delay_ms(100);
|
||||
}
|
||||
|
||||
furi_log_remove_handler(log_handler);
|
||||
@@ -245,12 +194,10 @@ void cli_command_log(Cli* cli, FuriString* args, void* context) {
|
||||
// There will be strange behaviour if log level is set from settings while log command is running
|
||||
furi_log_set_level(previous_level);
|
||||
}
|
||||
|
||||
furi_stream_buffer_free(ring);
|
||||
}
|
||||
|
||||
void cli_command_sysctl_debug(Cli* cli, FuriString* args, void* context) {
|
||||
UNUSED(cli);
|
||||
void cli_command_sysctl_debug(PipeSide* pipe, FuriString* args, void* context) {
|
||||
UNUSED(pipe);
|
||||
UNUSED(context);
|
||||
if(!furi_string_cmp(args, "0")) {
|
||||
furi_hal_rtc_reset_flag(FuriHalRtcFlagDebug);
|
||||
@@ -263,8 +210,8 @@ void cli_command_sysctl_debug(Cli* cli, FuriString* args, void* context) {
|
||||
}
|
||||
}
|
||||
|
||||
void cli_command_sysctl_heap_track(Cli* cli, FuriString* args, void* context) {
|
||||
UNUSED(cli);
|
||||
void cli_command_sysctl_heap_track(PipeSide* pipe, FuriString* args, void* context) {
|
||||
UNUSED(pipe);
|
||||
UNUSED(context);
|
||||
if(!furi_string_cmp(args, "none")) {
|
||||
furi_hal_rtc_set_heap_track_mode(FuriHalRtcHeapTrackModeNone);
|
||||
@@ -298,7 +245,7 @@ void cli_command_sysctl_print_usage(void) {
|
||||
#endif
|
||||
}
|
||||
|
||||
void cli_command_sysctl(Cli* cli, FuriString* args, void* context) {
|
||||
void cli_command_sysctl(PipeSide* pipe, FuriString* args, void* context) {
|
||||
FuriString* cmd;
|
||||
cmd = furi_string_alloc();
|
||||
|
||||
@@ -309,12 +256,12 @@ void cli_command_sysctl(Cli* cli, FuriString* args, void* context) {
|
||||
}
|
||||
|
||||
if(furi_string_cmp_str(cmd, "debug") == 0) {
|
||||
cli_command_sysctl_debug(cli, args, context);
|
||||
cli_command_sysctl_debug(pipe, args, context);
|
||||
break;
|
||||
}
|
||||
|
||||
if(furi_string_cmp_str(cmd, "heap_track") == 0) {
|
||||
cli_command_sysctl_heap_track(cli, args, context);
|
||||
cli_command_sysctl_heap_track(pipe, args, context);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -324,8 +271,8 @@ void cli_command_sysctl(Cli* cli, FuriString* args, void* context) {
|
||||
furi_string_free(cmd);
|
||||
}
|
||||
|
||||
void cli_command_vibro(Cli* cli, FuriString* args, void* context) {
|
||||
UNUSED(cli);
|
||||
void cli_command_vibro(PipeSide* pipe, FuriString* args, void* context) {
|
||||
UNUSED(pipe);
|
||||
UNUSED(context);
|
||||
|
||||
if(!furi_string_cmp(args, "0")) {
|
||||
@@ -351,8 +298,8 @@ void cli_command_vibro(Cli* cli, FuriString* args, void* context) {
|
||||
}
|
||||
}
|
||||
|
||||
void cli_command_led(Cli* cli, FuriString* args, void* context) {
|
||||
UNUSED(cli);
|
||||
void cli_command_led(PipeSide* pipe, FuriString* args, void* context) {
|
||||
UNUSED(pipe);
|
||||
UNUSED(context);
|
||||
// Get first word as light name
|
||||
NotificationMessage notification_led_message;
|
||||
@@ -406,23 +353,23 @@ void cli_command_led(Cli* cli, FuriString* args, void* context) {
|
||||
furi_record_close(RECORD_NOTIFICATION);
|
||||
}
|
||||
|
||||
static void cli_command_top(Cli* cli, FuriString* args, void* context) {
|
||||
UNUSED(cli);
|
||||
static void cli_command_top(PipeSide* pipe, FuriString* args, void* context) {
|
||||
UNUSED(context);
|
||||
|
||||
int interval = 1000;
|
||||
args_read_int_and_trim(args, &interval);
|
||||
|
||||
FuriThreadList* thread_list = furi_thread_list_alloc();
|
||||
while(!cli_cmd_interrupt_received(cli)) {
|
||||
while(!cli_is_pipe_broken_or_is_etx_next_char(pipe)) {
|
||||
uint32_t tick = furi_get_tick();
|
||||
furi_thread_enumerate(thread_list);
|
||||
|
||||
if(interval) printf("\e[2J\e[0;0f"); // Clear display and return to 0
|
||||
if(interval) printf(ANSI_CURSOR_POS("1", "1"));
|
||||
|
||||
uint32_t uptime = tick / furi_kernel_get_tick_frequency();
|
||||
printf(
|
||||
"Threads: %zu, ISR Time: %0.2f%%, Uptime: %luh%lum%lus\r\n",
|
||||
"Threads: %zu, ISR Time: %0.2f%%, Uptime: %luh%lum%lus" ANSI_ERASE_LINE(
|
||||
ANSI_ERASE_FROM_CURSOR_TO_END) "\r\n",
|
||||
furi_thread_list_size(thread_list),
|
||||
(double)furi_thread_list_get_isr_time(thread_list),
|
||||
uptime / 60 / 60,
|
||||
@@ -430,14 +377,16 @@ static void cli_command_top(Cli* cli, FuriString* args, void* context) {
|
||||
uptime % 60);
|
||||
|
||||
printf(
|
||||
"Heap: total %zu, free %zu, minimum %zu, max block %zu\r\n\r\n",
|
||||
"Heap: total %zu, free %zu, minimum %zu, max block %zu" ANSI_ERASE_LINE(
|
||||
ANSI_ERASE_FROM_CURSOR_TO_END) "\r\n" ANSI_ERASE_LINE(ANSI_ERASE_FROM_CURSOR_TO_END) "\r\n",
|
||||
memmgr_get_total_heap(),
|
||||
memmgr_get_free_heap(),
|
||||
memmgr_get_minimum_free_heap(),
|
||||
memmgr_heap_get_max_free_block());
|
||||
|
||||
printf(
|
||||
"%-17s %-20s %-10s %5s %12s %6s %10s %7s %5s\r\n",
|
||||
"%-17s %-20s %-10s %5s %12s %6s %10s %7s %5s" ANSI_ERASE_LINE(
|
||||
ANSI_ERASE_FROM_CURSOR_TO_END) "\r\n",
|
||||
"AppID",
|
||||
"Name",
|
||||
"State",
|
||||
@@ -446,12 +395,13 @@ static void cli_command_top(Cli* cli, FuriString* args, void* context) {
|
||||
"Stack",
|
||||
"Stack Min",
|
||||
"Heap",
|
||||
"CPU");
|
||||
"%CPU");
|
||||
|
||||
for(size_t i = 0; i < furi_thread_list_size(thread_list); i++) {
|
||||
const FuriThreadListItem* item = furi_thread_list_get_at(thread_list, i);
|
||||
printf(
|
||||
"%-17s %-20s %-10s %5d 0x%08lx %6lu %10lu %7zu %5.1f\r\n",
|
||||
"%-17s %-20s %-10s %5d 0x%08lx %6lu %10lu %7zu %5.1f" ANSI_ERASE_LINE(
|
||||
ANSI_ERASE_FROM_CURSOR_TO_END) "\r\n",
|
||||
item->app_id,
|
||||
item->name,
|
||||
item->state,
|
||||
@@ -463,6 +413,9 @@ static void cli_command_top(Cli* cli, FuriString* args, void* context) {
|
||||
(double)item->cpu);
|
||||
}
|
||||
|
||||
printf(ANSI_ERASE_DISPLAY(ANSI_ERASE_FROM_CURSOR_TO_END));
|
||||
fflush(stdout);
|
||||
|
||||
if(interval > 0) {
|
||||
furi_delay_ms(interval);
|
||||
} else {
|
||||
@@ -472,8 +425,8 @@ static void cli_command_top(Cli* cli, FuriString* args, void* context) {
|
||||
furi_thread_list_free(thread_list);
|
||||
}
|
||||
|
||||
void cli_command_free(Cli* cli, FuriString* args, void* context) {
|
||||
UNUSED(cli);
|
||||
void cli_command_free(PipeSide* pipe, FuriString* args, void* context) {
|
||||
UNUSED(pipe);
|
||||
UNUSED(args);
|
||||
UNUSED(context);
|
||||
|
||||
@@ -486,16 +439,16 @@ void cli_command_free(Cli* cli, FuriString* args, void* context) {
|
||||
printf("Maximum pool block: %zu\r\n", memmgr_pool_get_max_block());
|
||||
}
|
||||
|
||||
void cli_command_free_blocks(Cli* cli, FuriString* args, void* context) {
|
||||
UNUSED(cli);
|
||||
void cli_command_free_blocks(PipeSide* pipe, FuriString* args, void* context) {
|
||||
UNUSED(pipe);
|
||||
UNUSED(args);
|
||||
UNUSED(context);
|
||||
|
||||
memmgr_heap_printf_free_blocks();
|
||||
}
|
||||
|
||||
void cli_command_i2c(Cli* cli, FuriString* args, void* context) {
|
||||
UNUSED(cli);
|
||||
void cli_command_i2c(PipeSide* pipe, FuriString* args, void* context) {
|
||||
UNUSED(pipe);
|
||||
UNUSED(args);
|
||||
UNUSED(context);
|
||||
|
||||
@@ -517,26 +470,53 @@ void cli_command_i2c(Cli* cli, FuriString* args, void* context) {
|
||||
furi_hal_i2c_release(&furi_hal_i2c_handle_external);
|
||||
}
|
||||
|
||||
void cli_commands_init(Cli* cli) {
|
||||
cli_add_command(cli, "!", CliCommandFlagParallelSafe, cli_command_info, (void*)true);
|
||||
cli_add_command(cli, "info", CliCommandFlagParallelSafe, cli_command_info, NULL);
|
||||
cli_add_command(cli, "device_info", CliCommandFlagParallelSafe, cli_command_info, (void*)true);
|
||||
cli_add_command(cli, "source", CliCommandFlagParallelSafe, cli_command_src, NULL);
|
||||
/**
|
||||
* Echoes any bytes it receives except ASCII ETX (0x03, Ctrl+C)
|
||||
*/
|
||||
void cli_command_echo(PipeSide* pipe, FuriString* args, void* context) {
|
||||
UNUSED(args);
|
||||
UNUSED(context);
|
||||
|
||||
cli_add_command(cli, "?", CliCommandFlagParallelSafe, cli_command_help, NULL);
|
||||
cli_add_command(cli, "help", CliCommandFlagParallelSafe, cli_command_help, NULL);
|
||||
uint8_t buffer[256];
|
||||
|
||||
cli_add_command(cli, "uptime", CliCommandFlagDefault, cli_command_uptime, 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, "l", CliCommandFlagParallelSafe, cli_command_log, NULL);
|
||||
cli_add_command(cli, "sysctl", CliCommandFlagDefault, cli_command_sysctl, NULL);
|
||||
cli_add_command(cli, "top", CliCommandFlagParallelSafe, cli_command_top, NULL);
|
||||
cli_add_command(cli, "free", CliCommandFlagParallelSafe, cli_command_free, NULL);
|
||||
cli_add_command(cli, "free_blocks", CliCommandFlagParallelSafe, cli_command_free_blocks, NULL);
|
||||
while(true) {
|
||||
size_t to_read = CLAMP(pipe_bytes_available(pipe), sizeof(buffer), 1UL);
|
||||
size_t read = pipe_receive(pipe, buffer, to_read);
|
||||
if(read < to_read) break;
|
||||
|
||||
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", CliCommandFlagDefault, cli_command_gpio, NULL);
|
||||
cli_add_command(cli, "i2c", CliCommandFlagDefault, cli_command_i2c, NULL);
|
||||
if(memchr(buffer, CliKeyETX, read)) break;
|
||||
|
||||
size_t written = pipe_send(pipe, buffer, read);
|
||||
if(written < read) break;
|
||||
}
|
||||
}
|
||||
|
||||
void cli_main_commands_init(CliRegistry* registry) {
|
||||
cli_registry_add_command(
|
||||
registry, "!", CliCommandFlagParallelSafe, cli_command_info, (void*)true);
|
||||
cli_registry_add_command(registry, "info", CliCommandFlagParallelSafe, cli_command_info, NULL);
|
||||
cli_registry_add_command(
|
||||
registry, "device_info", CliCommandFlagParallelSafe, cli_command_info, (void*)true);
|
||||
|
||||
cli_registry_add_command(
|
||||
registry, "uptime", CliCommandFlagParallelSafe, cli_command_uptime, NULL);
|
||||
cli_registry_add_command(registry, "date", CliCommandFlagParallelSafe, cli_command_date, NULL);
|
||||
cli_registry_add_command(registry, "log", CliCommandFlagParallelSafe, cli_command_log, NULL);
|
||||
cli_registry_add_command(registry, "sysctl", CliCommandFlagDefault, cli_command_sysctl, NULL);
|
||||
cli_registry_add_command(registry, "top", CliCommandFlagParallelSafe, cli_command_top, NULL);
|
||||
cli_registry_add_command(registry, "free", CliCommandFlagParallelSafe, cli_command_free, NULL);
|
||||
cli_registry_add_command(
|
||||
registry, "free_blocks", CliCommandFlagDefault, cli_command_free_blocks, NULL);
|
||||
cli_registry_add_command(registry, "echo", CliCommandFlagParallelSafe, cli_command_echo, NULL);
|
||||
|
||||
cli_registry_add_command(registry, "vibro", CliCommandFlagDefault, cli_command_vibro, NULL);
|
||||
cli_registry_add_command(registry, "led", CliCommandFlagDefault, cli_command_led, NULL);
|
||||
cli_registry_add_command(registry, "gpio", CliCommandFlagDefault, cli_command_gpio, NULL);
|
||||
cli_registry_add_command(registry, "i2c", CliCommandFlagDefault, cli_command_i2c, NULL);
|
||||
}
|
||||
|
||||
void cli_on_system_start(void) {
|
||||
CliRegistry* registry = cli_registry_alloc();
|
||||
cli_main_commands_init(registry);
|
||||
furi_record_create(RECORD_CLI, registry);
|
||||
}
|
||||
9
applications/services/cli/cli_main_commands.h
Normal file
9
applications/services/cli/cli_main_commands.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <toolbox/cli/cli_command.h>
|
||||
#include <toolbox/cli/cli_registry.h>
|
||||
|
||||
#define RECORD_CLI "cli"
|
||||
#define CLI_APPID "cli"
|
||||
|
||||
void cli_main_commands_init(CliRegistry* registry);
|
||||
46
applications/services/cli/cli_main_shell.c
Normal file
46
applications/services/cli/cli_main_shell.c
Normal file
@@ -0,0 +1,46 @@
|
||||
#include "cli_main_shell.h"
|
||||
#include "cli_main_commands.h"
|
||||
#include <toolbox/cli/cli_ansi.h>
|
||||
#include <toolbox/cli/shell/cli_shell.h>
|
||||
#include <furi_hal_version.h>
|
||||
|
||||
void cli_main_motd(void* context) {
|
||||
UNUSED(context);
|
||||
printf(ANSI_FLIPPER_BRAND_ORANGE
|
||||
"\r\n"
|
||||
" _.-------.._ -,\r\n"
|
||||
" .-\"```\"--..,,_/ /`-, -, \\ \r\n"
|
||||
" .:\" /:/ /'\\ \\ ,_..., `. | |\r\n"
|
||||
" / ,----/:/ /`\\ _\\~`_-\"` _;\r\n"
|
||||
" ' / /`\"\"\"'\\ \\ \\.~`_-' ,-\"'/ \r\n"
|
||||
" | | | 0 | | .-' ,/` /\r\n"
|
||||
" | ,..\\ \\ ,.-\"` ,/` /\r\n"
|
||||
" ; : `/`\"\"\\` ,/--==,/-----,\r\n"
|
||||
" | `-...| -.___-Z:_______J...---;\r\n"
|
||||
" : ` _-'\r\n"
|
||||
" _L_ _ ___ ___ ___ ___ ____--\"`___ _ ___\r\n"
|
||||
"| __|| | |_ _|| _ \\| _ \\| __|| _ \\ / __|| | |_ _|\r\n"
|
||||
"| _| | |__ | | | _/| _/| _| | / | (__ | |__ | |\r\n"
|
||||
"|_| |____||___||_| |_| |___||_|_\\ \\___||____||___|\r\n"
|
||||
"\r\n" ANSI_FG_BR_WHITE "Welcome to Flipper Zero Command Line Interface!\r\n"
|
||||
"Read the manual: https://docs.flipper.net/development/cli\r\n"
|
||||
"Run `help` or `?` to list available commands\r\n"
|
||||
"\r\n" ANSI_RESET);
|
||||
|
||||
const Version* firmware_version = furi_hal_version_get_firmware_version();
|
||||
if(firmware_version) {
|
||||
printf(
|
||||
"Firmware version: %s %s (%s%s built on %s)\r\n",
|
||||
version_get_gitbranch(firmware_version),
|
||||
version_get_version(firmware_version),
|
||||
version_get_githash(firmware_version),
|
||||
version_get_dirty_flag(firmware_version) ? "-dirty" : "",
|
||||
version_get_builddate(firmware_version));
|
||||
}
|
||||
}
|
||||
|
||||
const CliCommandExternalConfig cli_main_ext_config = {
|
||||
.search_directory = "/ext/apps_data/cli/plugins",
|
||||
.fal_prefix = "cli_",
|
||||
.appid = CLI_APPID,
|
||||
};
|
||||
7
applications/services/cli/cli_main_shell.h
Normal file
7
applications/services/cli/cli_main_shell.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <toolbox/cli/cli_command.h>
|
||||
|
||||
void cli_main_motd(void* context);
|
||||
|
||||
extern const CliCommandExternalConfig cli_main_ext_config;
|
||||
@@ -1,323 +1,308 @@
|
||||
#include "cli_i.h" // IWYU pragma: keep
|
||||
#include "cli_vcp.h"
|
||||
#include <furi_hal_usb_cdc.h>
|
||||
#include <furi_hal.h>
|
||||
#include <furi.h>
|
||||
#include <stdint.h>
|
||||
#include <toolbox/pipe.h>
|
||||
#include <toolbox/cli/shell/cli_shell.h>
|
||||
#include "cli_main_shell.h"
|
||||
#include "cli_main_commands.h"
|
||||
|
||||
#define TAG "CliVcp"
|
||||
|
||||
#define USB_CDC_PKT_LEN CDC_DATA_SZ
|
||||
#define VCP_RX_BUF_SIZE (USB_CDC_PKT_LEN * 3)
|
||||
#define VCP_TX_BUF_SIZE (USB_CDC_PKT_LEN * 3)
|
||||
|
||||
#define VCP_BUF_SIZE (USB_CDC_PKT_LEN * 3)
|
||||
#define VCP_IF_NUM 0
|
||||
#define VCP_MESSAGE_Q_LEN 8
|
||||
|
||||
#ifdef CLI_VCP_DEBUG
|
||||
#define VCP_DEBUG(...) FURI_LOG_D(TAG, __VA_ARGS__)
|
||||
#ifdef CLI_VCP_TRACE
|
||||
#define VCP_TRACE(...) FURI_LOG_T(__VA_ARGS__)
|
||||
#else
|
||||
#define VCP_DEBUG(...)
|
||||
#define VCP_TRACE(...)
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
VcpEvtStop = (1 << 0),
|
||||
VcpEvtConnect = (1 << 1),
|
||||
VcpEvtDisconnect = (1 << 2),
|
||||
VcpEvtStreamRx = (1 << 3),
|
||||
VcpEvtRx = (1 << 4),
|
||||
VcpEvtStreamTx = (1 << 5),
|
||||
VcpEvtTx = (1 << 6),
|
||||
} WorkerEvtFlags;
|
||||
|
||||
#define VCP_THREAD_FLAG_ALL \
|
||||
(VcpEvtStop | VcpEvtConnect | VcpEvtDisconnect | VcpEvtRx | VcpEvtTx | VcpEvtStreamRx | \
|
||||
VcpEvtStreamTx)
|
||||
|
||||
typedef struct {
|
||||
FuriThread* thread;
|
||||
enum {
|
||||
CliVcpMessageTypeEnable,
|
||||
CliVcpMessageTypeDisable,
|
||||
} type;
|
||||
union {};
|
||||
} CliVcpMessage;
|
||||
|
||||
FuriStreamBuffer* tx_stream;
|
||||
FuriStreamBuffer* rx_stream;
|
||||
typedef enum {
|
||||
CliVcpInternalEventConnected = (1 << 0),
|
||||
CliVcpInternalEventDisconnected = (1 << 1),
|
||||
CliVcpInternalEventTxDone = (1 << 2),
|
||||
CliVcpInternalEventRx = (1 << 3),
|
||||
} CliVcpInternalEvent;
|
||||
|
||||
volatile bool connected;
|
||||
volatile bool running;
|
||||
#define CliVcpInternalEventAll \
|
||||
(CliVcpInternalEventConnected | CliVcpInternalEventDisconnected | CliVcpInternalEventTxDone | \
|
||||
CliVcpInternalEventRx)
|
||||
|
||||
FuriHalUsbInterface* usb_if_prev;
|
||||
struct CliVcp {
|
||||
FuriEventLoop* event_loop;
|
||||
FuriMessageQueue* message_queue; // <! external messages
|
||||
FuriThreadId thread_id;
|
||||
|
||||
uint8_t data_buffer[USB_CDC_PKT_LEN];
|
||||
} CliVcp;
|
||||
bool is_enabled, is_connected;
|
||||
FuriHalUsbInterface* previous_interface;
|
||||
|
||||
static int32_t vcp_worker(void* context);
|
||||
static void vcp_on_cdc_tx_complete(void* context);
|
||||
static void vcp_on_cdc_rx(void* context);
|
||||
static void vcp_state_callback(void* context, uint8_t state);
|
||||
static void vcp_on_cdc_control_line(void* context, uint8_t state);
|
||||
PipeSide* own_pipe;
|
||||
PipeSide* shell_pipe;
|
||||
bool is_currently_transmitting;
|
||||
size_t previous_tx_length;
|
||||
|
||||
static CdcCallbacks cdc_cb = {
|
||||
vcp_on_cdc_tx_complete,
|
||||
vcp_on_cdc_rx,
|
||||
vcp_state_callback,
|
||||
vcp_on_cdc_control_line,
|
||||
NULL,
|
||||
CliRegistry* main_registry;
|
||||
CliShell* shell;
|
||||
};
|
||||
|
||||
static CliVcp* vcp = NULL;
|
||||
// ============
|
||||
// Data copying
|
||||
// ============
|
||||
|
||||
static const uint8_t ascii_soh = 0x01;
|
||||
static const uint8_t ascii_eot = 0x04;
|
||||
/**
|
||||
* Called in the following cases:
|
||||
* - previous transfer has finished;
|
||||
* - new data became available to send.
|
||||
*/
|
||||
static void cli_vcp_maybe_send_data(CliVcp* cli_vcp) {
|
||||
if(cli_vcp->is_currently_transmitting) return;
|
||||
if(!cli_vcp->own_pipe) return;
|
||||
|
||||
static void cli_vcp_init(void) {
|
||||
if(vcp == NULL) {
|
||||
vcp = malloc(sizeof(CliVcp));
|
||||
vcp->tx_stream = furi_stream_buffer_alloc(VCP_TX_BUF_SIZE, 1);
|
||||
vcp->rx_stream = furi_stream_buffer_alloc(VCP_RX_BUF_SIZE, 1);
|
||||
uint8_t buf[USB_CDC_PKT_LEN];
|
||||
size_t to_receive_from_pipe = MIN(sizeof(buf), pipe_bytes_available(cli_vcp->own_pipe));
|
||||
size_t length = pipe_receive(cli_vcp->own_pipe, buf, to_receive_from_pipe);
|
||||
if(length > 0 || cli_vcp->previous_tx_length == USB_CDC_PKT_LEN) {
|
||||
VCP_TRACE(TAG, "cdc_send length=%zu", length);
|
||||
cli_vcp->is_currently_transmitting = true;
|
||||
furi_hal_cdc_send(VCP_IF_NUM, buf, length);
|
||||
}
|
||||
furi_assert(vcp->thread == NULL);
|
||||
|
||||
vcp->connected = false;
|
||||
|
||||
vcp->thread = furi_thread_alloc_ex("CliVcpWorker", 1024, vcp_worker, NULL);
|
||||
furi_thread_start(vcp->thread);
|
||||
|
||||
FURI_LOG_I(TAG, "Init OK");
|
||||
cli_vcp->previous_tx_length = length;
|
||||
}
|
||||
|
||||
static void cli_vcp_deinit(void) {
|
||||
furi_thread_flags_set(furi_thread_get_id(vcp->thread), VcpEvtStop);
|
||||
furi_thread_join(vcp->thread);
|
||||
furi_thread_free(vcp->thread);
|
||||
vcp->thread = NULL;
|
||||
/**
|
||||
* Called in the following cases:
|
||||
* - new data arrived at the endpoint;
|
||||
* - data was read out of the pipe.
|
||||
*/
|
||||
static void cli_vcp_maybe_receive_data(CliVcp* cli_vcp) {
|
||||
if(!cli_vcp->own_pipe) return;
|
||||
if(pipe_spaces_available(cli_vcp->own_pipe) < USB_CDC_PKT_LEN) return;
|
||||
|
||||
uint8_t buf[USB_CDC_PKT_LEN];
|
||||
size_t length = furi_hal_cdc_receive(VCP_IF_NUM, buf, sizeof(buf));
|
||||
VCP_TRACE(TAG, "cdc_receive length=%zu", length);
|
||||
furi_check(pipe_send(cli_vcp->own_pipe, buf, length) == length);
|
||||
}
|
||||
|
||||
static int32_t vcp_worker(void* context) {
|
||||
UNUSED(context);
|
||||
bool tx_idle = true;
|
||||
size_t missed_rx = 0;
|
||||
uint8_t last_tx_pkt_len = 0;
|
||||
// =============
|
||||
// CDC callbacks
|
||||
// =============
|
||||
|
||||
// Switch USB to VCP mode (if it is not set yet)
|
||||
vcp->usb_if_prev = furi_hal_usb_get_config();
|
||||
if((vcp->usb_if_prev != &usb_cdc_single) && (vcp->usb_if_prev != &usb_cdc_dual)) {
|
||||
static void cli_vcp_signal_internal_event(CliVcp* cli_vcp, CliVcpInternalEvent event) {
|
||||
furi_thread_flags_set(cli_vcp->thread_id, event);
|
||||
}
|
||||
|
||||
static void cli_vcp_cdc_tx_done(void* context) {
|
||||
CliVcp* cli_vcp = context;
|
||||
cli_vcp_signal_internal_event(cli_vcp, CliVcpInternalEventTxDone);
|
||||
}
|
||||
|
||||
static void cli_vcp_cdc_rx(void* context) {
|
||||
CliVcp* cli_vcp = context;
|
||||
cli_vcp_signal_internal_event(cli_vcp, CliVcpInternalEventRx);
|
||||
}
|
||||
|
||||
static void cli_vcp_cdc_state_callback(void* context, CdcState state) {
|
||||
CliVcp* cli_vcp = context;
|
||||
if(state == CdcStateDisconnected) {
|
||||
cli_vcp_signal_internal_event(cli_vcp, CliVcpInternalEventDisconnected);
|
||||
}
|
||||
// `Connected` events are generated by DTR going active
|
||||
}
|
||||
|
||||
static void cli_vcp_cdc_ctrl_line_callback(void* context, CdcCtrlLine ctrl_lines) {
|
||||
CliVcp* cli_vcp = context;
|
||||
if(ctrl_lines & CdcCtrlLineDTR) {
|
||||
cli_vcp_signal_internal_event(cli_vcp, CliVcpInternalEventConnected);
|
||||
} else {
|
||||
cli_vcp_signal_internal_event(cli_vcp, CliVcpInternalEventDisconnected);
|
||||
}
|
||||
}
|
||||
|
||||
static CdcCallbacks cdc_callbacks = {
|
||||
.tx_ep_callback = cli_vcp_cdc_tx_done,
|
||||
.rx_ep_callback = cli_vcp_cdc_rx,
|
||||
.state_callback = cli_vcp_cdc_state_callback,
|
||||
.ctrl_line_callback = cli_vcp_cdc_ctrl_line_callback,
|
||||
.config_callback = NULL,
|
||||
};
|
||||
|
||||
// ======================
|
||||
// Pipe callback handlers
|
||||
// ======================
|
||||
|
||||
static void cli_vcp_data_from_shell(PipeSide* pipe, void* context) {
|
||||
UNUSED(pipe);
|
||||
CliVcp* cli_vcp = context;
|
||||
cli_vcp_maybe_send_data(cli_vcp);
|
||||
}
|
||||
|
||||
static void cli_vcp_shell_ready(PipeSide* pipe, void* context) {
|
||||
UNUSED(pipe);
|
||||
CliVcp* cli_vcp = context;
|
||||
cli_vcp_maybe_receive_data(cli_vcp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes messages arriving from other threads
|
||||
*/
|
||||
static void cli_vcp_message_received(FuriEventLoopObject* object, void* context) {
|
||||
CliVcp* cli_vcp = context;
|
||||
CliVcpMessage message;
|
||||
furi_check(furi_message_queue_get(object, &message, 0) == FuriStatusOk);
|
||||
|
||||
switch(message.type) {
|
||||
case CliVcpMessageTypeEnable:
|
||||
if(cli_vcp->is_enabled) return;
|
||||
FURI_LOG_D(TAG, "Enabling");
|
||||
cli_vcp->is_enabled = true;
|
||||
|
||||
// switch usb mode
|
||||
cli_vcp->previous_interface = furi_hal_usb_get_config();
|
||||
furi_hal_usb_set_config(&usb_cdc_single, NULL);
|
||||
}
|
||||
furi_hal_cdc_set_callbacks(VCP_IF_NUM, &cdc_cb, NULL);
|
||||
furi_hal_cdc_set_callbacks(VCP_IF_NUM, &cdc_callbacks, cli_vcp);
|
||||
break;
|
||||
|
||||
FURI_LOG_D(TAG, "Start");
|
||||
vcp->running = true;
|
||||
case CliVcpMessageTypeDisable:
|
||||
if(!cli_vcp->is_enabled) return;
|
||||
FURI_LOG_D(TAG, "Disabling");
|
||||
cli_vcp->is_enabled = false;
|
||||
|
||||
while(1) {
|
||||
uint32_t flags =
|
||||
furi_thread_flags_wait(VCP_THREAD_FLAG_ALL, FuriFlagWaitAny, FuriWaitForever);
|
||||
furi_assert(!(flags & FuriFlagError));
|
||||
|
||||
// VCP session opened
|
||||
if(flags & VcpEvtConnect) {
|
||||
VCP_DEBUG("Connect");
|
||||
|
||||
if(vcp->connected == false) {
|
||||
vcp->connected = true;
|
||||
furi_stream_buffer_send(vcp->rx_stream, &ascii_soh, 1, FuriWaitForever);
|
||||
}
|
||||
}
|
||||
|
||||
// VCP session closed
|
||||
if(flags & VcpEvtDisconnect) {
|
||||
VCP_DEBUG("Disconnect");
|
||||
|
||||
if(vcp->connected == true) {
|
||||
vcp->connected = false;
|
||||
furi_stream_buffer_receive(vcp->tx_stream, vcp->data_buffer, USB_CDC_PKT_LEN, 0);
|
||||
furi_stream_buffer_send(vcp->rx_stream, &ascii_eot, 1, FuriWaitForever);
|
||||
}
|
||||
}
|
||||
|
||||
// Rx buffer was read, maybe there is enough space for new data?
|
||||
if((flags & VcpEvtStreamRx) && (missed_rx > 0)) {
|
||||
VCP_DEBUG("StreamRx");
|
||||
|
||||
if(furi_stream_buffer_spaces_available(vcp->rx_stream) >= USB_CDC_PKT_LEN) {
|
||||
flags |= VcpEvtRx;
|
||||
missed_rx--;
|
||||
}
|
||||
}
|
||||
|
||||
// New data received
|
||||
if(flags & VcpEvtRx) {
|
||||
if(furi_stream_buffer_spaces_available(vcp->rx_stream) >= USB_CDC_PKT_LEN) {
|
||||
int32_t len = furi_hal_cdc_receive(VCP_IF_NUM, vcp->data_buffer, USB_CDC_PKT_LEN);
|
||||
VCP_DEBUG("Rx %ld", len);
|
||||
|
||||
if(len > 0) {
|
||||
furi_check(
|
||||
furi_stream_buffer_send(
|
||||
vcp->rx_stream, vcp->data_buffer, len, FuriWaitForever) ==
|
||||
(size_t)len);
|
||||
}
|
||||
} else {
|
||||
VCP_DEBUG("Rx missed");
|
||||
missed_rx++;
|
||||
}
|
||||
}
|
||||
|
||||
// New data in Tx buffer
|
||||
if(flags & VcpEvtStreamTx) {
|
||||
VCP_DEBUG("StreamTx");
|
||||
|
||||
if(tx_idle) {
|
||||
flags |= VcpEvtTx;
|
||||
}
|
||||
}
|
||||
|
||||
// CDC write transfer done
|
||||
if(flags & VcpEvtTx) {
|
||||
size_t len =
|
||||
furi_stream_buffer_receive(vcp->tx_stream, vcp->data_buffer, USB_CDC_PKT_LEN, 0);
|
||||
|
||||
VCP_DEBUG("Tx %d", len);
|
||||
|
||||
if(len > 0) { // Some data left in Tx buffer. Sending it now
|
||||
tx_idle = false;
|
||||
furi_hal_cdc_send(VCP_IF_NUM, vcp->data_buffer, len);
|
||||
last_tx_pkt_len = len;
|
||||
} else { // There is nothing to send.
|
||||
if(last_tx_pkt_len == 64) {
|
||||
// Send extra zero-length packet if last packet len is 64 to indicate transfer end
|
||||
furi_hal_cdc_send(VCP_IF_NUM, NULL, 0);
|
||||
} else {
|
||||
// Set flag to start next transfer instantly
|
||||
tx_idle = true;
|
||||
}
|
||||
last_tx_pkt_len = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if(flags & VcpEvtStop) {
|
||||
vcp->connected = false;
|
||||
vcp->running = false;
|
||||
// restore usb mode
|
||||
furi_hal_cdc_set_callbacks(VCP_IF_NUM, NULL, NULL);
|
||||
// Restore previous USB mode (if it was set during init)
|
||||
if((vcp->usb_if_prev != &usb_cdc_single) && (vcp->usb_if_prev != &usb_cdc_dual)) {
|
||||
furi_hal_usb_unlock();
|
||||
furi_hal_usb_set_config(vcp->usb_if_prev, NULL);
|
||||
}
|
||||
furi_stream_buffer_receive(vcp->tx_stream, vcp->data_buffer, USB_CDC_PKT_LEN, 0);
|
||||
furi_stream_buffer_send(vcp->rx_stream, &ascii_eot, 1, FuriWaitForever);
|
||||
furi_hal_usb_set_config(cli_vcp->previous_interface, NULL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
FURI_LOG_D(TAG, "End");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static size_t cli_vcp_rx(uint8_t* buffer, size_t size, uint32_t timeout) {
|
||||
furi_assert(vcp);
|
||||
furi_assert(buffer);
|
||||
/**
|
||||
* Processes messages arriving from CDC event callbacks
|
||||
*/
|
||||
static void cli_vcp_internal_event_happened(void* context) {
|
||||
CliVcp* cli_vcp = context;
|
||||
CliVcpInternalEvent event = furi_thread_flags_wait(CliVcpInternalEventAll, FuriFlagWaitAny, 0);
|
||||
furi_check(!(event & FuriFlagError));
|
||||
|
||||
if(vcp->running == false) {
|
||||
if(event & CliVcpInternalEventDisconnected) {
|
||||
if(!cli_vcp->is_connected) return;
|
||||
FURI_LOG_D(TAG, "Disconnected");
|
||||
cli_vcp->is_connected = false;
|
||||
|
||||
// disconnect our side of the pipe
|
||||
pipe_detach_from_event_loop(cli_vcp->own_pipe);
|
||||
pipe_free(cli_vcp->own_pipe);
|
||||
cli_vcp->own_pipe = NULL;
|
||||
}
|
||||
|
||||
if(event & CliVcpInternalEventConnected) {
|
||||
if(cli_vcp->is_connected) return;
|
||||
FURI_LOG_D(TAG, "Connected");
|
||||
cli_vcp->is_connected = true;
|
||||
|
||||
// wait for previous shell to stop
|
||||
furi_check(!cli_vcp->own_pipe);
|
||||
if(cli_vcp->shell) {
|
||||
cli_shell_join(cli_vcp->shell);
|
||||
cli_shell_free(cli_vcp->shell);
|
||||
pipe_free(cli_vcp->shell_pipe);
|
||||
}
|
||||
|
||||
// start shell thread
|
||||
PipeSideBundle bundle = pipe_alloc(VCP_BUF_SIZE, 1);
|
||||
cli_vcp->own_pipe = bundle.alices_side;
|
||||
cli_vcp->shell_pipe = bundle.bobs_side;
|
||||
pipe_attach_to_event_loop(cli_vcp->own_pipe, cli_vcp->event_loop);
|
||||
pipe_set_callback_context(cli_vcp->own_pipe, cli_vcp);
|
||||
pipe_set_data_arrived_callback(
|
||||
cli_vcp->own_pipe, cli_vcp_data_from_shell, FuriEventLoopEventFlagEdge);
|
||||
pipe_set_space_freed_callback(
|
||||
cli_vcp->own_pipe, cli_vcp_shell_ready, FuriEventLoopEventFlagEdge);
|
||||
furi_delay_ms(33); // we are too fast, minicom isn't ready yet
|
||||
cli_vcp->shell = cli_shell_alloc(
|
||||
cli_main_motd, NULL, cli_vcp->shell_pipe, cli_vcp->main_registry, &cli_main_ext_config);
|
||||
cli_shell_start(cli_vcp->shell);
|
||||
}
|
||||
|
||||
if(event & CliVcpInternalEventRx) {
|
||||
VCP_TRACE(TAG, "Rx");
|
||||
cli_vcp_maybe_receive_data(cli_vcp);
|
||||
}
|
||||
|
||||
if(event & CliVcpInternalEventTxDone) {
|
||||
VCP_TRACE(TAG, "TxDone");
|
||||
cli_vcp->is_currently_transmitting = false;
|
||||
cli_vcp_maybe_send_data(cli_vcp);
|
||||
}
|
||||
}
|
||||
|
||||
// ============
|
||||
// Thread stuff
|
||||
// ============
|
||||
|
||||
static CliVcp* cli_vcp_alloc(void) {
|
||||
CliVcp* cli_vcp = malloc(sizeof(CliVcp));
|
||||
cli_vcp->thread_id = furi_thread_get_current_id();
|
||||
|
||||
cli_vcp->event_loop = furi_event_loop_alloc();
|
||||
|
||||
cli_vcp->message_queue = furi_message_queue_alloc(VCP_MESSAGE_Q_LEN, sizeof(CliVcpMessage));
|
||||
furi_event_loop_subscribe_message_queue(
|
||||
cli_vcp->event_loop,
|
||||
cli_vcp->message_queue,
|
||||
FuriEventLoopEventIn,
|
||||
cli_vcp_message_received,
|
||||
cli_vcp);
|
||||
|
||||
furi_event_loop_subscribe_thread_flags(
|
||||
cli_vcp->event_loop, cli_vcp_internal_event_happened, cli_vcp);
|
||||
|
||||
cli_vcp->main_registry = furi_record_open(RECORD_CLI);
|
||||
|
||||
return cli_vcp;
|
||||
}
|
||||
|
||||
int32_t cli_vcp_srv(void* p) {
|
||||
UNUSED(p);
|
||||
|
||||
if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) {
|
||||
FURI_LOG_W(TAG, "Skipping start in special boot mode");
|
||||
furi_thread_suspend(furi_thread_get_current_id());
|
||||
return 0;
|
||||
}
|
||||
|
||||
VCP_DEBUG("rx %u start", size);
|
||||
CliVcp* cli_vcp = cli_vcp_alloc();
|
||||
furi_record_create(RECORD_CLI_VCP, cli_vcp);
|
||||
furi_event_loop_run(cli_vcp->event_loop);
|
||||
|
||||
size_t rx_cnt = 0;
|
||||
|
||||
while(size > 0) {
|
||||
size_t batch_size = size;
|
||||
if(batch_size > VCP_RX_BUF_SIZE) batch_size = VCP_RX_BUF_SIZE;
|
||||
|
||||
size_t len = furi_stream_buffer_receive(vcp->rx_stream, buffer, batch_size, timeout);
|
||||
VCP_DEBUG("rx %u ", batch_size);
|
||||
|
||||
if(len == 0) break;
|
||||
if(vcp->running == false) {
|
||||
// EOT command is received after VCP session close
|
||||
rx_cnt += len;
|
||||
break;
|
||||
}
|
||||
furi_thread_flags_set(furi_thread_get_id(vcp->thread), VcpEvtStreamRx);
|
||||
size -= len;
|
||||
buffer += len;
|
||||
rx_cnt += len;
|
||||
}
|
||||
|
||||
VCP_DEBUG("rx %u end", size);
|
||||
return rx_cnt;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static size_t cli_vcp_rx_stdin(uint8_t* data, size_t size, uint32_t timeout, void* context) {
|
||||
UNUSED(context);
|
||||
return cli_vcp_rx(data, size, timeout);
|
||||
// ==========
|
||||
// Public API
|
||||
// ==========
|
||||
|
||||
void cli_vcp_enable(CliVcp* cli_vcp) {
|
||||
CliVcpMessage message = {
|
||||
.type = CliVcpMessageTypeEnable,
|
||||
};
|
||||
furi_message_queue_put(cli_vcp->message_queue, &message, FuriWaitForever);
|
||||
}
|
||||
|
||||
static void cli_vcp_tx(const uint8_t* buffer, size_t size) {
|
||||
furi_assert(vcp);
|
||||
furi_assert(buffer);
|
||||
|
||||
if(vcp->running == false) {
|
||||
return;
|
||||
}
|
||||
|
||||
VCP_DEBUG("tx %u start", size);
|
||||
|
||||
while(size > 0 && vcp->connected) {
|
||||
size_t batch_size = size;
|
||||
if(batch_size > USB_CDC_PKT_LEN) batch_size = USB_CDC_PKT_LEN;
|
||||
|
||||
furi_stream_buffer_send(vcp->tx_stream, buffer, batch_size, FuriWaitForever);
|
||||
furi_thread_flags_set(furi_thread_get_id(vcp->thread), VcpEvtStreamTx);
|
||||
VCP_DEBUG("tx %u", batch_size);
|
||||
|
||||
size -= batch_size;
|
||||
buffer += batch_size;
|
||||
}
|
||||
|
||||
VCP_DEBUG("tx %u end", size);
|
||||
void cli_vcp_disable(CliVcp* cli_vcp) {
|
||||
CliVcpMessage message = {
|
||||
.type = CliVcpMessageTypeDisable,
|
||||
};
|
||||
furi_message_queue_put(cli_vcp->message_queue, &message, FuriWaitForever);
|
||||
}
|
||||
|
||||
static void cli_vcp_tx_stdout(const char* data, size_t size, void* context) {
|
||||
UNUSED(context);
|
||||
cli_vcp_tx((const uint8_t*)data, size);
|
||||
}
|
||||
|
||||
static void vcp_state_callback(void* context, uint8_t state) {
|
||||
UNUSED(context);
|
||||
if(state == 0) {
|
||||
furi_thread_flags_set(furi_thread_get_id(vcp->thread), VcpEvtDisconnect);
|
||||
}
|
||||
}
|
||||
|
||||
static void vcp_on_cdc_control_line(void* context, uint8_t state) {
|
||||
UNUSED(context);
|
||||
// bit 0: DTR state, bit 1: RTS state
|
||||
bool dtr = state & (1 << 0);
|
||||
|
||||
if(dtr == true) {
|
||||
furi_thread_flags_set(furi_thread_get_id(vcp->thread), VcpEvtConnect);
|
||||
} else {
|
||||
furi_thread_flags_set(furi_thread_get_id(vcp->thread), VcpEvtDisconnect);
|
||||
}
|
||||
}
|
||||
|
||||
static void vcp_on_cdc_rx(void* context) {
|
||||
UNUSED(context);
|
||||
uint32_t ret = furi_thread_flags_set(furi_thread_get_id(vcp->thread), VcpEvtRx);
|
||||
furi_check(!(ret & FuriFlagError));
|
||||
}
|
||||
|
||||
static void vcp_on_cdc_tx_complete(void* context) {
|
||||
UNUSED(context);
|
||||
furi_thread_flags_set(furi_thread_get_id(vcp->thread), VcpEvtTx);
|
||||
}
|
||||
|
||||
static bool cli_vcp_is_connected(void) {
|
||||
furi_assert(vcp);
|
||||
return vcp->connected;
|
||||
}
|
||||
|
||||
const CliSession cli_vcp = {
|
||||
cli_vcp_init,
|
||||
cli_vcp_deinit,
|
||||
cli_vcp_rx,
|
||||
cli_vcp_rx_stdin,
|
||||
cli_vcp_tx,
|
||||
cli_vcp_tx_stdout,
|
||||
cli_vcp_is_connected,
|
||||
};
|
||||
|
||||
@@ -9,9 +9,12 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct CliSession CliSession;
|
||||
#define RECORD_CLI_VCP "cli_vcp"
|
||||
|
||||
extern const CliSession cli_vcp;
|
||||
typedef struct CliVcp CliVcp;
|
||||
|
||||
void cli_vcp_enable(CliVcp* cli_vcp);
|
||||
void cli_vcp_disable(CliVcp* cli_vcp);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
10
applications/services/cli/commands/hello_world.c
Normal file
10
applications/services/cli/commands/hello_world.c
Normal file
@@ -0,0 +1,10 @@
|
||||
#include "../cli_main_commands.h"
|
||||
|
||||
static void execute(PipeSide* pipe, FuriString* args, void* context) {
|
||||
UNUSED(pipe);
|
||||
UNUSED(args);
|
||||
UNUSED(context);
|
||||
puts("Hello, World!");
|
||||
}
|
||||
|
||||
CLI_COMMAND_INTERFACE(hello_world, execute, CliCommandFlagParallelSafe, 768, CLI_APPID);
|
||||
160
applications/services/cli/commands/neofetch.c
Normal file
160
applications/services/cli/commands/neofetch.c
Normal file
@@ -0,0 +1,160 @@
|
||||
#include "../cli_main_commands.h"
|
||||
#include <toolbox/cli/cli_ansi.h>
|
||||
#include <toolbox/version.h>
|
||||
#include <furi_hal.h>
|
||||
#include <furi_hal_info.h>
|
||||
#include <FreeRTOS.h>
|
||||
#include <FreeRTOS-Kernel/include/task.h>
|
||||
|
||||
static void execute(PipeSide* pipe, FuriString* args, void* context) {
|
||||
UNUSED(pipe);
|
||||
UNUSED(args);
|
||||
UNUSED(context);
|
||||
|
||||
static const char* const neofetch_logo[] = {
|
||||
" _.-------.._ -,",
|
||||
" .-\"```\"--..,,_/ /`-, -, \\ ",
|
||||
" .:\" /:/ /'\\ \\ ,_..., `. | |",
|
||||
" / ,----/:/ /`\\ _\\~`_-\"` _;",
|
||||
" ' / /`\"\"\"'\\ \\ \\.~`_-' ,-\"'/ ",
|
||||
" | | | 0 | | .-' ,/` /",
|
||||
" | ,..\\ \\ ,.-\"` ,/` /",
|
||||
"; : `/`\"\"\\` ,/--==,/-----,",
|
||||
"| `-...| -.___-Z:_______J...---;",
|
||||
": ` _-'",
|
||||
};
|
||||
#define NEOFETCH_COLOR ANSI_FLIPPER_BRAND_ORANGE
|
||||
|
||||
// Determine logo parameters
|
||||
size_t logo_height = COUNT_OF(neofetch_logo), logo_width = 0;
|
||||
for(size_t i = 0; i < logo_height; i++)
|
||||
logo_width = MAX(logo_width, strlen(neofetch_logo[i]));
|
||||
logo_width += 4; // space between logo and info
|
||||
|
||||
// Format hostname delimiter
|
||||
const size_t size_of_hostname = 4 + strlen(furi_hal_version_get_name_ptr());
|
||||
char delimiter[64];
|
||||
memset(delimiter, '-', size_of_hostname);
|
||||
delimiter[size_of_hostname] = '\0';
|
||||
|
||||
// Get heap info
|
||||
size_t heap_total = memmgr_get_total_heap();
|
||||
size_t heap_used = heap_total - memmgr_get_free_heap();
|
||||
uint16_t heap_percent = (100 * heap_used) / heap_total;
|
||||
|
||||
// Get storage info
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
uint64_t ext_total, ext_free, ext_used, ext_percent;
|
||||
storage_common_fs_info(storage, "/ext", &ext_total, &ext_free);
|
||||
ext_used = ext_total - ext_free;
|
||||
ext_percent = (100 * ext_used) / ext_total;
|
||||
ext_used /= 1024 * 1024;
|
||||
ext_total /= 1024 * 1024;
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
|
||||
// Get battery info
|
||||
uint16_t charge_percent = furi_hal_power_get_pct();
|
||||
const char* charge_state;
|
||||
if(furi_hal_power_is_charging()) {
|
||||
if((charge_percent < 100) && (!furi_hal_power_is_charging_done())) {
|
||||
charge_state = "charging";
|
||||
} else {
|
||||
charge_state = "charged";
|
||||
}
|
||||
} else {
|
||||
charge_state = "discharging";
|
||||
}
|
||||
|
||||
// Get misc info
|
||||
uint32_t uptime = furi_get_tick() / furi_kernel_get_tick_frequency();
|
||||
const Version* version = version_get();
|
||||
uint16_t major, minor;
|
||||
furi_hal_info_get_api_version(&major, &minor);
|
||||
|
||||
// Print ASCII art with info
|
||||
const size_t info_height = 16;
|
||||
for(size_t i = 0; i < MAX(logo_height, info_height); i++) {
|
||||
printf(NEOFETCH_COLOR "%-*s", logo_width, (i < logo_height) ? neofetch_logo[i] : "");
|
||||
switch(i) {
|
||||
case 0: // you@<hostname>
|
||||
printf("you" ANSI_RESET "@" NEOFETCH_COLOR "%s", furi_hal_version_get_name_ptr());
|
||||
break;
|
||||
case 1: // delimiter
|
||||
printf(ANSI_RESET "%s", delimiter);
|
||||
break;
|
||||
case 2: // OS: FURI <edition> <branch> <version> <commit> (SDK <maj>.<min>)
|
||||
printf(
|
||||
"OS" ANSI_RESET ": FURI %s %s %s %s (SDK %hu.%hu)",
|
||||
version_get_version(version),
|
||||
version_get_gitbranch(version),
|
||||
version_get_version(version),
|
||||
version_get_githash(version),
|
||||
major,
|
||||
minor);
|
||||
break;
|
||||
case 3: // Host: <model> <hostname>
|
||||
printf(
|
||||
"Host" ANSI_RESET ": %s %s",
|
||||
furi_hal_version_get_model_code(),
|
||||
furi_hal_version_get_device_name_ptr());
|
||||
break;
|
||||
case 4: // Kernel: FreeRTOS <maj>.<min>.<build>
|
||||
printf(
|
||||
"Kernel" ANSI_RESET ": FreeRTOS %d.%d.%d",
|
||||
tskKERNEL_VERSION_MAJOR,
|
||||
tskKERNEL_VERSION_MINOR,
|
||||
tskKERNEL_VERSION_BUILD);
|
||||
break;
|
||||
case 5: // Uptime: ?h?m?s
|
||||
printf(
|
||||
"Uptime" ANSI_RESET ": %luh%lum%lus",
|
||||
uptime / 60 / 60,
|
||||
uptime / 60 % 60,
|
||||
uptime % 60);
|
||||
break;
|
||||
case 6: // ST7567 128x64 @ 1 bpp in 1.4"
|
||||
printf("Display" ANSI_RESET ": ST7567 128x64 @ 1 bpp in 1.4\"");
|
||||
break;
|
||||
case 7: // DE: GuiSrv
|
||||
printf("DE" ANSI_RESET ": GuiSrv");
|
||||
break;
|
||||
case 8: // Shell: CliSrv
|
||||
printf("Shell" ANSI_RESET ": CliShell");
|
||||
break;
|
||||
case 9: // CPU: STM32WB55RG @ 64 MHz
|
||||
printf("CPU" ANSI_RESET ": STM32WB55RG @ 64 MHz");
|
||||
break;
|
||||
case 10: // Memory: <used> / <total> B (??%)
|
||||
printf(
|
||||
"Memory" ANSI_RESET ": %zu / %zu B (%hu%%)", heap_used, heap_total, heap_percent);
|
||||
break;
|
||||
case 11: // Disk (/ext): <used> / <total> MiB (??%)
|
||||
printf(
|
||||
"Disk (/ext)" ANSI_RESET ": %llu / %llu MiB (%llu%%)",
|
||||
ext_used,
|
||||
ext_total,
|
||||
ext_percent);
|
||||
break;
|
||||
case 12: // Battery: ??% (<state>)
|
||||
printf("Battery" ANSI_RESET ": %hu%% (%s)" ANSI_RESET, charge_percent, charge_state);
|
||||
break;
|
||||
case 13: // empty space
|
||||
break;
|
||||
case 14: // Colors (line 1)
|
||||
for(size_t j = 30; j <= 37; j++)
|
||||
printf("\e[%dm███", j);
|
||||
break;
|
||||
case 15: // Colors (line 2)
|
||||
for(size_t j = 90; j <= 97; j++)
|
||||
printf("\e[%dm███", j);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
printf("\r\n");
|
||||
}
|
||||
printf(ANSI_RESET);
|
||||
#undef NEOFETCH_COLOR
|
||||
}
|
||||
|
||||
CLI_COMMAND_INTERFACE(neofetch, execute, CliCommandFlagParallelSafe, 2048, CLI_APPID);
|
||||
43
applications/services/cli/commands/subshell_demo.c
Normal file
43
applications/services/cli/commands/subshell_demo.c
Normal file
@@ -0,0 +1,43 @@
|
||||
#include "../cli_main_commands.h"
|
||||
#include <toolbox/cli/cli_registry.h>
|
||||
#include <toolbox/cli/shell/cli_shell.h>
|
||||
#include <toolbox/cli/cli_ansi.h>
|
||||
|
||||
#define RAINBOW_SUBCOMMAND \
|
||||
ANSI_FG_RED "s" ANSI_FG_YELLOW "u" ANSI_FG_BLUE "b" ANSI_FG_GREEN "c" ANSI_FG_MAGENTA \
|
||||
"o" ANSI_FG_RED "m" ANSI_FG_YELLOW "m" ANSI_FG_BLUE "a" ANSI_FG_GREEN \
|
||||
"n" ANSI_FG_MAGENTA "d"
|
||||
|
||||
static void subcommand(PipeSide* pipe, FuriString* args, void* context) {
|
||||
UNUSED(pipe);
|
||||
UNUSED(args);
|
||||
UNUSED(context);
|
||||
printf("This is a ✨" RAINBOW_SUBCOMMAND ANSI_RESET "✨!");
|
||||
}
|
||||
|
||||
static void motd(void* context) {
|
||||
UNUSED(context);
|
||||
printf("\r\n");
|
||||
printf("+------------------------------------+\r\n");
|
||||
printf("| Hello world! |\r\n");
|
||||
printf("| This is the " ANSI_FG_GREEN "MOTD" ANSI_RESET " for our " ANSI_FG_BLUE
|
||||
"subshell" ANSI_RESET "! |\r\n");
|
||||
printf("+------------------------------------+\r\n");
|
||||
}
|
||||
|
||||
static void execute(PipeSide* pipe, FuriString* args, void* context) {
|
||||
UNUSED(args);
|
||||
UNUSED(context);
|
||||
CliRegistry* registry = cli_registry_alloc();
|
||||
cli_registry_add_command(registry, "subcommand", CliCommandFlagParallelSafe, subcommand, NULL);
|
||||
|
||||
CliShell* shell = cli_shell_alloc(motd, NULL, pipe, registry, NULL);
|
||||
cli_shell_set_prompt(shell, "subshell");
|
||||
cli_shell_start(shell);
|
||||
cli_shell_join(shell);
|
||||
|
||||
cli_shell_free(shell);
|
||||
cli_registry_free(registry);
|
||||
}
|
||||
|
||||
CLI_COMMAND_INTERFACE(subshell_demo, execute, CliCommandFlagParallelSafe, 2048, CLI_APPID);
|
||||
@@ -2,7 +2,10 @@
|
||||
#include <furi.h>
|
||||
|
||||
#include <lib/toolbox/args.h>
|
||||
#include <cli/cli.h>
|
||||
#include <toolbox/pipe.h>
|
||||
#include <cli/cli_main_commands.h>
|
||||
#include <toolbox/cli/cli_registry.h>
|
||||
#include <toolbox/cli/cli_ansi.h>
|
||||
|
||||
void crypto_cli_print_usage(void) {
|
||||
printf("Usage:\r\n");
|
||||
@@ -17,7 +20,7 @@ void crypto_cli_print_usage(void) {
|
||||
"\tstore_key <key_slot:int> <key_type:str> <key_size:int> <key_data:hex>\t - Store key in secure enclave. !!! NON-REVERSIBLE OPERATION - READ MANUAL FIRST !!!\r\n");
|
||||
}
|
||||
|
||||
void crypto_cli_encrypt(Cli* cli, FuriString* args) {
|
||||
void crypto_cli_encrypt(PipeSide* pipe, FuriString* args) {
|
||||
int key_slot = 0;
|
||||
bool key_loaded = false;
|
||||
uint8_t iv[16];
|
||||
@@ -44,15 +47,15 @@ void crypto_cli_encrypt(Cli* cli, FuriString* args) {
|
||||
FuriString* input;
|
||||
input = furi_string_alloc();
|
||||
char c;
|
||||
while(cli_read(cli, (uint8_t*)&c, 1) == 1) {
|
||||
if(c == CliSymbolAsciiETX) {
|
||||
while(pipe_receive(pipe, (uint8_t*)&c, 1) == 1) {
|
||||
if(c == CliKeyETX) {
|
||||
printf("\r\n");
|
||||
break;
|
||||
} else if(c >= 0x20 && c < 0x7F) {
|
||||
putc(c, stdout);
|
||||
fflush(stdout);
|
||||
furi_string_push_back(input, c);
|
||||
} else if(c == CliSymbolAsciiCR) {
|
||||
} else if(c == CliKeyCR) {
|
||||
printf("\r\n");
|
||||
furi_string_cat(input, "\r\n");
|
||||
}
|
||||
@@ -92,7 +95,7 @@ void crypto_cli_encrypt(Cli* cli, FuriString* args) {
|
||||
}
|
||||
}
|
||||
|
||||
void crypto_cli_decrypt(Cli* cli, FuriString* args) {
|
||||
void crypto_cli_decrypt(PipeSide* pipe, FuriString* args) {
|
||||
int key_slot = 0;
|
||||
bool key_loaded = false;
|
||||
uint8_t iv[16];
|
||||
@@ -119,15 +122,15 @@ void crypto_cli_decrypt(Cli* cli, FuriString* args) {
|
||||
FuriString* hex_input;
|
||||
hex_input = furi_string_alloc();
|
||||
char c;
|
||||
while(cli_read(cli, (uint8_t*)&c, 1) == 1) {
|
||||
if(c == CliSymbolAsciiETX) {
|
||||
while(pipe_receive(pipe, (uint8_t*)&c, 1) == 1) {
|
||||
if(c == CliKeyETX) {
|
||||
printf("\r\n");
|
||||
break;
|
||||
} else if(c >= 0x20 && c < 0x7F) {
|
||||
putc(c, stdout);
|
||||
fflush(stdout);
|
||||
furi_string_push_back(hex_input, c);
|
||||
} else if(c == CliSymbolAsciiCR) {
|
||||
} else if(c == CliKeyCR) {
|
||||
printf("\r\n");
|
||||
}
|
||||
}
|
||||
@@ -164,8 +167,8 @@ void crypto_cli_decrypt(Cli* cli, FuriString* args) {
|
||||
}
|
||||
}
|
||||
|
||||
void crypto_cli_has_key(Cli* cli, FuriString* args) {
|
||||
UNUSED(cli);
|
||||
void crypto_cli_has_key(PipeSide* pipe, FuriString* args) {
|
||||
UNUSED(pipe);
|
||||
int key_slot = 0;
|
||||
uint8_t iv[16] = {0};
|
||||
|
||||
@@ -186,8 +189,8 @@ void crypto_cli_has_key(Cli* cli, FuriString* args) {
|
||||
} while(0);
|
||||
}
|
||||
|
||||
void crypto_cli_store_key(Cli* cli, FuriString* args) {
|
||||
UNUSED(cli);
|
||||
void crypto_cli_store_key(PipeSide* pipe, FuriString* args) {
|
||||
UNUSED(pipe);
|
||||
int key_slot = 0;
|
||||
int key_size = 0;
|
||||
FuriString* key_type;
|
||||
@@ -279,7 +282,7 @@ void crypto_cli_store_key(Cli* cli, FuriString* args) {
|
||||
furi_string_free(key_type);
|
||||
}
|
||||
|
||||
static void crypto_cli(Cli* cli, FuriString* args, void* context) {
|
||||
static void crypto_cli(PipeSide* pipe, FuriString* args, void* context) {
|
||||
UNUSED(context);
|
||||
FuriString* cmd;
|
||||
cmd = furi_string_alloc();
|
||||
@@ -291,22 +294,22 @@ static void crypto_cli(Cli* cli, FuriString* args, void* context) {
|
||||
}
|
||||
|
||||
if(furi_string_cmp_str(cmd, "encrypt") == 0) {
|
||||
crypto_cli_encrypt(cli, args);
|
||||
crypto_cli_encrypt(pipe, args);
|
||||
break;
|
||||
}
|
||||
|
||||
if(furi_string_cmp_str(cmd, "decrypt") == 0) {
|
||||
crypto_cli_decrypt(cli, args);
|
||||
crypto_cli_decrypt(pipe, args);
|
||||
break;
|
||||
}
|
||||
|
||||
if(furi_string_cmp_str(cmd, "has_key") == 0) {
|
||||
crypto_cli_has_key(cli, args);
|
||||
crypto_cli_has_key(pipe, args);
|
||||
break;
|
||||
}
|
||||
|
||||
if(furi_string_cmp_str(cmd, "store_key") == 0) {
|
||||
crypto_cli_store_key(cli, args);
|
||||
crypto_cli_store_key(pipe, args);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -318,8 +321,8 @@ static void crypto_cli(Cli* cli, FuriString* args, void* context) {
|
||||
|
||||
void crypto_on_system_start(void) {
|
||||
#ifdef SRV_CLI
|
||||
Cli* cli = furi_record_open(RECORD_CLI);
|
||||
cli_add_command(cli, "crypto", CliCommandFlagDefault, crypto_cli, NULL);
|
||||
CliRegistry* registry = furi_record_open(RECORD_CLI);
|
||||
cli_registry_add_command(registry, "crypto", CliCommandFlagDefault, crypto_cli, NULL);
|
||||
furi_record_close(RECORD_CLI);
|
||||
#else
|
||||
UNUSED(crypto_cli);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#include "desktop_i.h"
|
||||
|
||||
#include <cli/cli.h>
|
||||
#include <cli/cli_vcp.h>
|
||||
|
||||
#include <gui/gui_i.h>
|
||||
@@ -30,9 +29,7 @@ static void desktop_loader_callback(const void* message, void* context) {
|
||||
if(event->type == LoaderEventTypeApplicationBeforeLoad) {
|
||||
view_dispatcher_send_custom_event(desktop->view_dispatcher, DesktopGlobalBeforeAppStarted);
|
||||
furi_check(furi_semaphore_acquire(desktop->animation_semaphore, 3000) == FuriStatusOk);
|
||||
} else if(
|
||||
event->type == LoaderEventTypeApplicationLoadFailed ||
|
||||
event->type == LoaderEventTypeApplicationStopped) {
|
||||
} else if(event->type == LoaderEventTypeNoMoreAppsInQueue) {
|
||||
view_dispatcher_send_custom_event(desktop->view_dispatcher, DesktopGlobalAfterAppFinished);
|
||||
}
|
||||
}
|
||||
@@ -405,9 +402,9 @@ void desktop_lock(Desktop* desktop) {
|
||||
furi_hal_rtc_set_flag(FuriHalRtcFlagLock);
|
||||
|
||||
if(desktop_pin_code_is_set()) {
|
||||
Cli* cli = furi_record_open(RECORD_CLI);
|
||||
cli_session_close(cli);
|
||||
furi_record_close(RECORD_CLI);
|
||||
CliVcp* cli_vcp = furi_record_open(RECORD_CLI_VCP);
|
||||
cli_vcp_disable(cli_vcp);
|
||||
furi_record_close(RECORD_CLI_VCP);
|
||||
}
|
||||
|
||||
desktop_auto_lock_inhibit(desktop);
|
||||
@@ -435,9 +432,9 @@ void desktop_unlock(Desktop* desktop) {
|
||||
furi_hal_rtc_set_pin_fails(0);
|
||||
|
||||
if(desktop_pin_code_is_set()) {
|
||||
Cli* cli = furi_record_open(RECORD_CLI);
|
||||
cli_session_open(cli, &cli_vcp);
|
||||
furi_record_close(RECORD_CLI);
|
||||
CliVcp* cli_vcp = furi_record_open(RECORD_CLI_VCP);
|
||||
cli_vcp_enable(cli_vcp);
|
||||
furi_record_close(RECORD_CLI_VCP);
|
||||
}
|
||||
|
||||
DesktopStatus status = {.locked = false};
|
||||
@@ -534,6 +531,10 @@ int32_t desktop_srv(void* p) {
|
||||
|
||||
if(desktop_pin_code_is_set()) {
|
||||
desktop_lock(desktop);
|
||||
} else {
|
||||
CliVcp* cli_vcp = furi_record_open(RECORD_CLI_VCP);
|
||||
cli_vcp_enable(cli_vcp);
|
||||
furi_record_close(RECORD_CLI_VCP);
|
||||
}
|
||||
|
||||
if(storage_file_exists(desktop->storage, SLIDESHOW_FS_PATH)) {
|
||||
|
||||
@@ -4,9 +4,11 @@
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <furi.h>
|
||||
#include <cli/cli.h>
|
||||
#include <furi_hal_gpio.h>
|
||||
#include <furi_hal_vibro.h>
|
||||
#include <toolbox/cli/cli_command.h>
|
||||
#include <cli/cli_main_commands.h>
|
||||
#include <toolbox/pipe.h>
|
||||
|
||||
#define INPUT_DEBOUNCE_TICKS_HALF (INPUT_DEBOUNCE_TICKS / 2)
|
||||
#define INPUT_PRESS_TICKS 150
|
||||
@@ -28,7 +30,7 @@ typedef struct {
|
||||
} InputPinState;
|
||||
|
||||
/** Input CLI command handler */
|
||||
void input_cli(Cli* cli, FuriString* args, void* context);
|
||||
void input_cli(PipeSide* pipe, FuriString* args, void* context);
|
||||
|
||||
// #define INPUT_DEBUG
|
||||
|
||||
@@ -100,8 +102,10 @@ int32_t input_srv(void* p) {
|
||||
#endif
|
||||
|
||||
#ifdef SRV_CLI
|
||||
Cli* cli = furi_record_open(RECORD_CLI);
|
||||
cli_add_command(cli, "input", CliCommandFlagParallelSafe, input_cli, event_pubsub);
|
||||
CliRegistry* registry = furi_record_open(RECORD_CLI);
|
||||
cli_registry_add_command(
|
||||
registry, "input", CliCommandFlagParallelSafe, input_cli, event_pubsub);
|
||||
furi_record_close(RECORD_CLI);
|
||||
#endif
|
||||
|
||||
InputPinState pin_states[input_pins_count];
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
#include "input.h"
|
||||
|
||||
#include <furi.h>
|
||||
#include <cli/cli.h>
|
||||
#include <toolbox/cli/cli_command.h>
|
||||
#include <toolbox/args.h>
|
||||
#include <toolbox/pipe.h>
|
||||
|
||||
static void input_cli_usage(void) {
|
||||
printf("Usage:\r\n");
|
||||
@@ -19,7 +20,7 @@ static void input_cli_dump_events_callback(const void* value, void* ctx) {
|
||||
furi_message_queue_put(input_queue, value, FuriWaitForever);
|
||||
}
|
||||
|
||||
static void input_cli_dump(Cli* cli, FuriString* args, FuriPubSub* event_pubsub) {
|
||||
static void input_cli_dump(PipeSide* pipe, FuriString* args, FuriPubSub* event_pubsub) {
|
||||
UNUSED(args);
|
||||
FuriMessageQueue* input_queue = furi_message_queue_alloc(8, sizeof(InputEvent));
|
||||
FuriPubSubSubscription* input_subscription =
|
||||
@@ -27,7 +28,7 @@ static void input_cli_dump(Cli* cli, FuriString* args, FuriPubSub* event_pubsub)
|
||||
|
||||
InputEvent input_event;
|
||||
printf("Press CTRL+C to stop\r\n");
|
||||
while(!cli_cmd_interrupt_received(cli)) {
|
||||
while(!cli_is_pipe_broken_or_is_etx_next_char(pipe)) {
|
||||
if(furi_message_queue_get(input_queue, &input_event, 100) == FuriStatusOk) {
|
||||
printf(
|
||||
"key: %s type: %s\r\n",
|
||||
@@ -47,8 +48,8 @@ static void input_cli_send_print_usage(void) {
|
||||
printf("\t\t <type>\t - one of 'press', 'release', 'short', 'long'\r\n");
|
||||
}
|
||||
|
||||
static void input_cli_send(Cli* cli, FuriString* args, FuriPubSub* event_pubsub) {
|
||||
UNUSED(cli);
|
||||
static void input_cli_send(PipeSide* pipe, FuriString* args, FuriPubSub* event_pubsub) {
|
||||
UNUSED(pipe);
|
||||
InputEvent event;
|
||||
FuriString* key_str;
|
||||
key_str = furi_string_alloc();
|
||||
@@ -97,8 +98,7 @@ static void input_cli_send(Cli* cli, FuriString* args, FuriPubSub* event_pubsub)
|
||||
furi_string_free(key_str);
|
||||
}
|
||||
|
||||
void input_cli(Cli* cli, FuriString* args, void* context) {
|
||||
furi_assert(cli);
|
||||
void input_cli(PipeSide* pipe, FuriString* args, void* context) {
|
||||
furi_assert(context);
|
||||
FuriPubSub* event_pubsub = context;
|
||||
FuriString* cmd;
|
||||
@@ -110,11 +110,11 @@ void input_cli(Cli* cli, FuriString* args, void* context) {
|
||||
break;
|
||||
}
|
||||
if(furi_string_cmp_str(cmd, "dump") == 0) {
|
||||
input_cli_dump(cli, args, event_pubsub);
|
||||
input_cli_dump(pipe, args, event_pubsub);
|
||||
break;
|
||||
}
|
||||
if(furi_string_cmp_str(cmd, "send") == 0) {
|
||||
input_cli_send(cli, args, event_pubsub);
|
||||
input_cli_send(pipe, args, event_pubsub);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -168,6 +168,13 @@ static void loader_show_gui_error(
|
||||
furi_record_close(RECORD_DIALOGS);
|
||||
}
|
||||
|
||||
static void loader_generic_synchronous_request(Loader* loader, LoaderMessage* message) {
|
||||
furi_check(loader);
|
||||
message->api_lock = api_lock_alloc_locked();
|
||||
furi_message_queue_put(loader->queue, message, FuriWaitForever);
|
||||
api_lock_wait_unlock_and_free(message->api_lock);
|
||||
}
|
||||
|
||||
LoaderStatus
|
||||
loader_start(Loader* loader, const char* name, const char* args, FuriString* error_message) {
|
||||
furi_check(loader);
|
||||
@@ -203,16 +210,12 @@ void loader_start_detached_with_gui_error(Loader* loader, const char* name, cons
|
||||
}
|
||||
|
||||
bool loader_lock(Loader* loader) {
|
||||
furi_check(loader);
|
||||
|
||||
LoaderMessage message;
|
||||
LoaderMessageBoolResult result;
|
||||
message.type = LoaderMessageTypeLock;
|
||||
message.api_lock = api_lock_alloc_locked();
|
||||
message.bool_value = &result;
|
||||
furi_message_queue_put(loader->queue, &message, FuriWaitForever);
|
||||
api_lock_wait_unlock_and_free(message.api_lock);
|
||||
|
||||
LoaderMessage message = {
|
||||
.type = LoaderMessageTypeLock,
|
||||
.bool_value = &result,
|
||||
};
|
||||
loader_generic_synchronous_request(loader, &message);
|
||||
return result.value;
|
||||
}
|
||||
|
||||
@@ -226,16 +229,12 @@ void loader_unlock(Loader* loader) {
|
||||
}
|
||||
|
||||
bool loader_is_locked(Loader* loader) {
|
||||
furi_check(loader);
|
||||
|
||||
LoaderMessage message;
|
||||
LoaderMessageBoolResult result;
|
||||
message.type = LoaderMessageTypeIsLocked;
|
||||
message.api_lock = api_lock_alloc_locked();
|
||||
message.bool_value = &result;
|
||||
furi_message_queue_put(loader->queue, &message, FuriWaitForever);
|
||||
api_lock_wait_unlock_and_free(message.api_lock);
|
||||
|
||||
LoaderMessage message = {
|
||||
.type = LoaderMessageTypeIsLocked,
|
||||
.bool_value = &result,
|
||||
};
|
||||
loader_generic_synchronous_request(loader, &message);
|
||||
return result.value;
|
||||
}
|
||||
|
||||
@@ -257,42 +256,63 @@ FuriPubSub* loader_get_pubsub(Loader* loader) {
|
||||
}
|
||||
|
||||
bool loader_signal(Loader* loader, uint32_t signal, void* arg) {
|
||||
furi_check(loader);
|
||||
|
||||
LoaderMessageBoolResult result;
|
||||
|
||||
LoaderMessage message = {
|
||||
.type = LoaderMessageTypeSignal,
|
||||
.api_lock = api_lock_alloc_locked(),
|
||||
.signal.signal = signal,
|
||||
.signal.arg = arg,
|
||||
.bool_value = &result,
|
||||
};
|
||||
|
||||
furi_message_queue_put(loader->queue, &message, FuriWaitForever);
|
||||
api_lock_wait_unlock_and_free(message.api_lock);
|
||||
|
||||
loader_generic_synchronous_request(loader, &message);
|
||||
return result.value;
|
||||
}
|
||||
|
||||
bool loader_get_application_name(Loader* loader, FuriString* name) {
|
||||
furi_check(loader);
|
||||
|
||||
LoaderMessageBoolResult result;
|
||||
|
||||
LoaderMessage message = {
|
||||
.type = LoaderMessageTypeGetApplicationName,
|
||||
.api_lock = api_lock_alloc_locked(),
|
||||
.application_name = name,
|
||||
.bool_value = &result,
|
||||
};
|
||||
|
||||
furi_message_queue_put(loader->queue, &message, FuriWaitForever);
|
||||
api_lock_wait_unlock_and_free(message.api_lock);
|
||||
|
||||
loader_generic_synchronous_request(loader, &message);
|
||||
return result.value;
|
||||
}
|
||||
|
||||
bool loader_get_application_launch_path(Loader* loader, FuriString* name) {
|
||||
LoaderMessageBoolResult result;
|
||||
LoaderMessage message = {
|
||||
.type = LoaderMessageTypeGetApplicationLaunchPath,
|
||||
.application_name = name,
|
||||
.bool_value = &result,
|
||||
};
|
||||
loader_generic_synchronous_request(loader, &message);
|
||||
return result.value;
|
||||
}
|
||||
|
||||
void loader_enqueue_launch(
|
||||
Loader* loader,
|
||||
const char* name,
|
||||
const char* args,
|
||||
LoaderDeferredLaunchFlag flags) {
|
||||
LoaderMessage message = {
|
||||
.type = LoaderMessageTypeEnqueueLaunch,
|
||||
.defer_start =
|
||||
{
|
||||
.name_or_path = strdup(name),
|
||||
.args = args ? strdup(args) : NULL,
|
||||
.flags = flags,
|
||||
},
|
||||
};
|
||||
loader_generic_synchronous_request(loader, &message);
|
||||
}
|
||||
|
||||
void loader_clear_launch_queue(Loader* loader) {
|
||||
LoaderMessage message = {
|
||||
.type = LoaderMessageTypeClearLaunchQueue,
|
||||
};
|
||||
loader_generic_synchronous_request(loader, &message);
|
||||
}
|
||||
|
||||
// callbacks
|
||||
|
||||
static void loader_menu_closed_callback(void* context) {
|
||||
@@ -329,12 +349,10 @@ static Loader* loader_alloc(void) {
|
||||
Loader* loader = malloc(sizeof(Loader));
|
||||
loader->pubsub = furi_pubsub_alloc();
|
||||
loader->queue = furi_message_queue_alloc(1, sizeof(LoaderMessage));
|
||||
loader->loader_menu = NULL;
|
||||
loader->loader_applications = NULL;
|
||||
loader->app.args = NULL;
|
||||
loader->app.thread = NULL;
|
||||
loader->app.insomniac = false;
|
||||
loader->app.fap = NULL;
|
||||
loader->gui = furi_record_open(RECORD_GUI);
|
||||
loader->view_holder = view_holder_alloc();
|
||||
loader->loading = loading_alloc();
|
||||
view_holder_attach_to_gui(loader->view_holder, loader->gui);
|
||||
return loader;
|
||||
}
|
||||
|
||||
@@ -706,6 +724,10 @@ static LoaderMessageLoaderStatusResult loader_do_start_by_name(
|
||||
LoaderStatusErrorUnknownApp, error_message, "Application \"%s\" not found", name);
|
||||
} while(false);
|
||||
|
||||
if(status.value == LoaderStatusOk) {
|
||||
loader->app.launch_path = furi_string_alloc_set_str(name);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -723,6 +745,57 @@ static void loader_do_unlock(Loader* loader) {
|
||||
loader->app.thread = NULL;
|
||||
}
|
||||
|
||||
static void loader_do_emit_queue_empty_event(Loader* loader) {
|
||||
FURI_LOG_I(TAG, "Launch queue empty");
|
||||
LoaderEvent event;
|
||||
event.type = LoaderEventTypeNoMoreAppsInQueue;
|
||||
furi_pubsub_publish(loader->pubsub, &event);
|
||||
}
|
||||
|
||||
static bool loader_do_deferred_launch(Loader* loader, LoaderDeferredLaunchRecord* record);
|
||||
|
||||
static void loader_do_next_deferred_launch_if_available(Loader* loader) {
|
||||
LoaderDeferredLaunchRecord record;
|
||||
if(loader_queue_pop(&loader->launch_queue, &record)) {
|
||||
loader_do_deferred_launch(loader, &record);
|
||||
loader_queue_item_clear(&record);
|
||||
} else {
|
||||
loader_do_emit_queue_empty_event(loader);
|
||||
}
|
||||
}
|
||||
|
||||
static bool loader_do_deferred_launch(Loader* loader, LoaderDeferredLaunchRecord* record) {
|
||||
furi_assert(loader);
|
||||
furi_assert(record);
|
||||
|
||||
bool is_successful = false;
|
||||
FuriString* error_message = furi_string_alloc();
|
||||
view_holder_set_view(loader->view_holder, loading_get_view(loader->loading));
|
||||
view_holder_send_to_front(loader->view_holder);
|
||||
|
||||
do {
|
||||
const char* app_name_str = record->name_or_path;
|
||||
const char* app_args = record->args;
|
||||
FURI_LOG_I(TAG, "Deferred launch: %s", app_name_str);
|
||||
|
||||
LoaderMessageLoaderStatusResult result =
|
||||
loader_do_start_by_name(loader, app_name_str, app_args, error_message);
|
||||
if(result.value == LoaderStatusOk) {
|
||||
is_successful = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if(record->flags & LoaderDeferredLaunchFlagGui)
|
||||
loader_show_gui_error(result, app_name_str, error_message);
|
||||
|
||||
loader_do_next_deferred_launch_if_available(loader);
|
||||
} while(false);
|
||||
|
||||
view_holder_set_view(loader->view_holder, NULL);
|
||||
furi_string_free(error_message);
|
||||
return is_successful;
|
||||
}
|
||||
|
||||
static void loader_do_app_closed(Loader* loader) {
|
||||
furi_assert(loader->app.thread);
|
||||
|
||||
@@ -747,11 +820,15 @@ static void loader_do_app_closed(Loader* loader) {
|
||||
loader->app.thread = NULL;
|
||||
}
|
||||
|
||||
furi_string_free(loader->app.launch_path);
|
||||
|
||||
FURI_LOG_I(TAG, "Application stopped. Free heap: %zu", memmgr_get_free_heap());
|
||||
|
||||
LoaderEvent event;
|
||||
event.type = LoaderEventTypeApplicationStopped;
|
||||
furi_pubsub_publish(loader->pubsub, &event);
|
||||
|
||||
loader_do_next_deferred_launch_if_available(loader);
|
||||
}
|
||||
|
||||
static bool loader_is_application_running(Loader* loader) {
|
||||
@@ -776,6 +853,15 @@ static bool loader_do_get_application_name(Loader* loader, FuriString* name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool loader_do_get_application_launch_path(Loader* loader, FuriString* path) {
|
||||
if(loader_is_application_running(loader)) {
|
||||
furi_string_set(path, loader->app.launch_path);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// app
|
||||
|
||||
int32_t loader_srv(void* p) {
|
||||
@@ -798,16 +884,20 @@ int32_t loader_srv(void* p) {
|
||||
while(true) {
|
||||
if(furi_message_queue_get(loader->queue, &message, FuriWaitForever) == FuriStatusOk) {
|
||||
switch(message.type) {
|
||||
case LoaderMessageTypeStartByName:
|
||||
*(message.status_value) = loader_do_start_by_name(
|
||||
case LoaderMessageTypeStartByName: {
|
||||
LoaderMessageLoaderStatusResult status = loader_do_start_by_name(
|
||||
loader, message.start.name, message.start.args, message.start.error_message);
|
||||
*(message.status_value) = status;
|
||||
if(status.value != LoaderStatusOk) loader_do_emit_queue_empty_event(loader);
|
||||
api_lock_unlock(message.api_lock);
|
||||
break;
|
||||
}
|
||||
case LoaderMessageTypeStartByNameDetachedWithGuiError: {
|
||||
FuriString* error_message = furi_string_alloc();
|
||||
LoaderMessageLoaderStatusResult status = loader_do_start_by_name(
|
||||
loader, message.start.name, message.start.args, error_message);
|
||||
loader_show_gui_error(status, message.start.name, error_message);
|
||||
if(status.value != LoaderStatusOk) loader_do_emit_queue_empty_event(loader);
|
||||
if(message.start.name) free((void*)message.start.name);
|
||||
if(message.start.args) free((void*)message.start.args);
|
||||
furi_string_free(error_message);
|
||||
@@ -846,6 +936,19 @@ int32_t loader_srv(void* p) {
|
||||
loader_do_get_application_name(loader, message.application_name);
|
||||
api_lock_unlock(message.api_lock);
|
||||
break;
|
||||
case LoaderMessageTypeGetApplicationLaunchPath:
|
||||
message.bool_value->value =
|
||||
loader_do_get_application_launch_path(loader, message.application_name);
|
||||
api_lock_unlock(message.api_lock);
|
||||
break;
|
||||
case LoaderMessageTypeEnqueueLaunch:
|
||||
furi_check(loader_queue_push(&loader->launch_queue, &message.defer_start));
|
||||
api_lock_unlock(message.api_lock);
|
||||
break;
|
||||
case LoaderMessageTypeClearLaunchQueue:
|
||||
loader_queue_clear(&loader->launch_queue);
|
||||
api_lock_unlock(message.api_lock);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,13 +22,19 @@ typedef enum {
|
||||
typedef enum {
|
||||
LoaderEventTypeApplicationBeforeLoad,
|
||||
LoaderEventTypeApplicationLoadFailed,
|
||||
LoaderEventTypeApplicationStopped
|
||||
LoaderEventTypeApplicationStopped,
|
||||
LoaderEventTypeNoMoreAppsInQueue, //<! The normal `Stopped` event still fires before this one
|
||||
} LoaderEventType;
|
||||
|
||||
typedef struct {
|
||||
LoaderEventType type;
|
||||
} LoaderEvent;
|
||||
|
||||
typedef enum {
|
||||
LoaderDeferredLaunchFlagNone = 0,
|
||||
LoaderDeferredLaunchFlagGui = (1 << 1), //<! Report launch to the user via a dialog
|
||||
} LoaderDeferredLaunchFlag;
|
||||
|
||||
/**
|
||||
* @brief Start application
|
||||
* @param[in] instance loader instance
|
||||
@@ -95,7 +101,7 @@ FuriPubSub* loader_get_pubsub(Loader* instance);
|
||||
*
|
||||
* @param[in] instance pointer to the loader instance
|
||||
* @param[in] signal signal value to be sent
|
||||
* @param[in,out] arg optional argument (can be of any value, including NULL)
|
||||
* @param[inout] arg optional argument (can be of any value, including NULL)
|
||||
*
|
||||
* @return true if the signal was handled by the application, false otherwise
|
||||
*/
|
||||
@@ -105,11 +111,49 @@ bool loader_signal(Loader* instance, uint32_t signal, void* arg);
|
||||
* @brief Get the name of the currently running application
|
||||
*
|
||||
* @param[in] instance pointer to the loader instance
|
||||
* @param[in,out] name pointer to the string to contain the name (must be allocated)
|
||||
* @param[inout] name pointer to the string to contain the name (must be allocated)
|
||||
* @return true if it was possible to get an application name, false otherwise
|
||||
*/
|
||||
bool loader_get_application_name(Loader* instance, FuriString* name);
|
||||
|
||||
/**
|
||||
* @brief Get the launch path or name of the currently running application
|
||||
*
|
||||
* This is the string that was supplied to `loader_start` such that the current
|
||||
* app is running now. It might be a name (in the case of internal apps) or a
|
||||
* path (in the case of external apps). This value can be used to launch the
|
||||
* same app again.
|
||||
*
|
||||
* @param[in] instance pointer to the loader instance
|
||||
* @param[inout] name pointer to the string to contain the path or name
|
||||
* (must be allocated)
|
||||
* @return true if it was possible to get an application path, false otherwise
|
||||
*/
|
||||
bool loader_get_application_launch_path(Loader* instance, FuriString* name);
|
||||
|
||||
/**
|
||||
* @brief Enqueues a request to launch an application after the current one
|
||||
*
|
||||
* @param[in] instance pointer to the loader instance
|
||||
* @param[in] name pointer to the name or path of the application, or NULL to
|
||||
* cancel a previous request
|
||||
* @param[in] args pointer to argument to provide to the next app
|
||||
* @param[in] flags additional flags. see enum documentation for more info
|
||||
*/
|
||||
void loader_enqueue_launch(
|
||||
Loader* instance,
|
||||
const char* name,
|
||||
const char* args,
|
||||
LoaderDeferredLaunchFlag flags);
|
||||
|
||||
/**
|
||||
* @brief Removes all requests to launch applications after the current one
|
||||
* exits
|
||||
*
|
||||
* @param[in] instance pointer to the loader instance
|
||||
*/
|
||||
void loader_clear_launch_queue(Loader* instance);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -120,7 +120,7 @@ static void loader_pubsub_callback(const void* message, void* context) {
|
||||
const LoaderEvent* event = message;
|
||||
const FuriThreadId thread_id = (FuriThreadId)context;
|
||||
|
||||
if(event->type == LoaderEventTypeApplicationStopped) {
|
||||
if(event->type == LoaderEventTypeNoMoreAppsInQueue) {
|
||||
furi_thread_flags_set(thread_id, APPLICATION_STOP_EVENT);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
#include "loader.h"
|
||||
|
||||
#include <furi.h>
|
||||
#include <cli/cli.h>
|
||||
#include <toolbox/cli/cli_command.h>
|
||||
#include <cli/cli_main_commands.h>
|
||||
#include <applications.h>
|
||||
#include <lib/toolbox/args.h>
|
||||
#include <lib/toolbox/strint.h>
|
||||
#include <notification/notification_messages.h>
|
||||
#include <toolbox/pipe.h>
|
||||
|
||||
static void loader_cli_print_usage(void) {
|
||||
printf("Usage:\r\n");
|
||||
@@ -110,8 +112,8 @@ static void loader_cli_signal(FuriString* args, Loader* loader) {
|
||||
}
|
||||
}
|
||||
|
||||
static void loader_cli(Cli* cli, FuriString* args, void* context) {
|
||||
UNUSED(cli);
|
||||
static void loader_cli(PipeSide* pipe, FuriString* args, void* context) {
|
||||
UNUSED(pipe);
|
||||
UNUSED(context);
|
||||
Loader* loader = furi_record_open(RECORD_LOADER);
|
||||
|
||||
@@ -140,8 +142,9 @@ static void loader_cli(Cli* cli, FuriString* args, void* context) {
|
||||
|
||||
void loader_on_system_start(void) {
|
||||
#ifdef SRV_CLI
|
||||
Cli* cli = furi_record_open(RECORD_CLI);
|
||||
cli_add_command(cli, RECORD_LOADER, CliCommandFlagParallelSafe, loader_cli, NULL);
|
||||
CliRegistry* registry = furi_record_open(RECORD_CLI);
|
||||
cli_registry_add_command(
|
||||
registry, RECORD_LOADER, CliCommandFlagParallelSafe, loader_cli, NULL);
|
||||
furi_record_close(RECORD_CLI);
|
||||
#else
|
||||
UNUSED(loader_cli);
|
||||
|
||||
@@ -2,11 +2,20 @@
|
||||
#include <furi.h>
|
||||
#include <toolbox/api_lock.h>
|
||||
#include <flipper_application/flipper_application.h>
|
||||
|
||||
#include <gui/gui.h>
|
||||
#include <gui/view_holder.h>
|
||||
#include <gui/modules/loading.h>
|
||||
|
||||
#include <m-array.h>
|
||||
|
||||
#include "loader.h"
|
||||
#include "loader_menu.h"
|
||||
#include "loader_applications.h"
|
||||
#include "loader_queue.h"
|
||||
|
||||
typedef struct {
|
||||
FuriString* launch_path;
|
||||
char* args;
|
||||
FuriThread* thread;
|
||||
bool insomniac;
|
||||
@@ -19,6 +28,12 @@ struct Loader {
|
||||
LoaderMenu* loader_menu;
|
||||
LoaderApplications* loader_applications;
|
||||
LoaderAppData app;
|
||||
|
||||
LoaderLaunchQueue launch_queue;
|
||||
|
||||
Gui* gui;
|
||||
ViewHolder* view_holder;
|
||||
Loading* loading;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
@@ -33,6 +48,9 @@ typedef enum {
|
||||
LoaderMessageTypeStartByNameDetachedWithGuiError,
|
||||
LoaderMessageTypeSignal,
|
||||
LoaderMessageTypeGetApplicationName,
|
||||
LoaderMessageTypeGetApplicationLaunchPath,
|
||||
LoaderMessageTypeEnqueueLaunch,
|
||||
LoaderMessageTypeClearLaunchQueue,
|
||||
} LoaderMessageType;
|
||||
|
||||
typedef struct {
|
||||
@@ -72,6 +90,7 @@ typedef struct {
|
||||
|
||||
union {
|
||||
LoaderMessageStartByName start;
|
||||
LoaderDeferredLaunchRecord defer_start;
|
||||
LoaderMessageSignal signal;
|
||||
FuriString* application_name;
|
||||
};
|
||||
|
||||
32
applications/services/loader/loader_queue.c
Normal file
32
applications/services/loader/loader_queue.c
Normal file
@@ -0,0 +1,32 @@
|
||||
#include "loader_queue.h"
|
||||
|
||||
void loader_queue_item_clear(LoaderDeferredLaunchRecord* item) {
|
||||
free(item->args);
|
||||
free(item->name_or_path);
|
||||
}
|
||||
|
||||
bool loader_queue_pop(LoaderLaunchQueue* queue, LoaderDeferredLaunchRecord* item) {
|
||||
if(!queue->item_cnt) return false;
|
||||
|
||||
*item = queue->items[0];
|
||||
queue->item_cnt--;
|
||||
memmove(
|
||||
&queue->items[0], &queue->items[1], queue->item_cnt * sizeof(LoaderDeferredLaunchRecord));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool loader_queue_push(LoaderLaunchQueue* queue, LoaderDeferredLaunchRecord* item) {
|
||||
if(queue->item_cnt == LOADER_QUEUE_MAX_SIZE) return false;
|
||||
|
||||
queue->items[queue->item_cnt] = *item;
|
||||
queue->item_cnt++;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void loader_queue_clear(LoaderLaunchQueue* queue) {
|
||||
for(size_t i = 0; i < queue->item_cnt; i++)
|
||||
loader_queue_item_clear(&queue->items[i]);
|
||||
queue->item_cnt = 0;
|
||||
}
|
||||
53
applications/services/loader/loader_queue.h
Normal file
53
applications/services/loader/loader_queue.h
Normal file
@@ -0,0 +1,53 @@
|
||||
#pragma once
|
||||
|
||||
#include <furi.h>
|
||||
|
||||
#include "loader.h"
|
||||
|
||||
#define LOADER_QUEUE_MAX_SIZE 4
|
||||
|
||||
typedef struct {
|
||||
char* name_or_path;
|
||||
char* args;
|
||||
LoaderDeferredLaunchFlag flags;
|
||||
} LoaderDeferredLaunchRecord;
|
||||
|
||||
typedef struct {
|
||||
LoaderDeferredLaunchRecord items[LOADER_QUEUE_MAX_SIZE];
|
||||
size_t item_cnt;
|
||||
} LoaderLaunchQueue;
|
||||
|
||||
/**
|
||||
* @brief Frees internal data in a `DeferredLaunchRecord`
|
||||
*
|
||||
* @param[out] item Record to clear
|
||||
*/
|
||||
void loader_queue_item_clear(LoaderDeferredLaunchRecord* item);
|
||||
|
||||
/**
|
||||
* @brief Fetches the next item from the launch queue
|
||||
*
|
||||
* @param[inout] queue Queue instance
|
||||
* @param[out] item Item output
|
||||
*
|
||||
* @return `true` if `item` was populated, `false` if queue is empty
|
||||
*/
|
||||
bool loader_queue_pop(LoaderLaunchQueue* queue, LoaderDeferredLaunchRecord* item);
|
||||
|
||||
/**
|
||||
* @brief Puts an item into the launch queue
|
||||
*
|
||||
* @param[inout] queue Queue instance
|
||||
* @param[in] item Item to put in the queue
|
||||
*
|
||||
* @return `true` if the item was put into the queue, `false` if there's no more
|
||||
* space left
|
||||
*/
|
||||
bool loader_queue_push(LoaderLaunchQueue* queue, LoaderDeferredLaunchRecord* item);
|
||||
|
||||
/**
|
||||
* @brief Clears the launch queue
|
||||
*
|
||||
* @param[inout] queue Queue instance
|
||||
*/
|
||||
void loader_queue_clear(LoaderLaunchQueue* queue);
|
||||
@@ -1,7 +1,6 @@
|
||||
#include "namechanger.h"
|
||||
#include <furi_hal.h>
|
||||
#include <furi_hal_version.h>
|
||||
#include <cli/cli.h>
|
||||
#include <cli/cli_vcp.h>
|
||||
#include <bt/bt_service/bt.h>
|
||||
#include <storage/storage.h>
|
||||
@@ -79,7 +78,7 @@ int32_t namechanger_on_system_start(void* p) {
|
||||
|
||||
// Wait for all required services to start and create their records
|
||||
uint8_t timeout = 0;
|
||||
while(!furi_record_exists(RECORD_CLI) || !furi_record_exists(RECORD_BT) ||
|
||||
while(!furi_record_exists(RECORD_CLI_VCP) || !furi_record_exists(RECORD_BT) ||
|
||||
!furi_record_exists(RECORD_STORAGE)) {
|
||||
timeout++;
|
||||
if(timeout > 250) {
|
||||
@@ -91,11 +90,11 @@ int32_t namechanger_on_system_start(void* p) {
|
||||
// Hehe bad code now here, bad bad bad, very bad, bad example, dont take it, make it better
|
||||
|
||||
if(namechanger_init()) {
|
||||
Cli* cli = furi_record_open(RECORD_CLI);
|
||||
cli_session_close(cli);
|
||||
CliVcp* cli = furi_record_open(RECORD_CLI_VCP);
|
||||
cli_vcp_disable(cli);
|
||||
furi_delay_ms(2); // why i added delays here
|
||||
cli_session_open(cli, &cli_vcp);
|
||||
furi_record_close(RECORD_CLI);
|
||||
cli_vcp_enable(cli);
|
||||
furi_record_close(RECORD_CLI_VCP);
|
||||
|
||||
furi_delay_ms(3);
|
||||
Bt* bt = furi_record_open(RECORD_BT);
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
#include "power_cli.h"
|
||||
|
||||
#include <furi_hal.h>
|
||||
#include <cli/cli.h>
|
||||
#include <toolbox/cli/cli_command.h>
|
||||
#include <cli/cli_main_commands.h>
|
||||
#include <lib/toolbox/args.h>
|
||||
#include <power/power_service/power.h>
|
||||
#include <toolbox/pipe.h>
|
||||
|
||||
void power_cli_off(Cli* cli, FuriString* args) {
|
||||
UNUSED(cli);
|
||||
void power_cli_off(PipeSide* pipe, FuriString* args) {
|
||||
UNUSED(pipe);
|
||||
UNUSED(args);
|
||||
Power* power = furi_record_open(RECORD_POWER);
|
||||
printf("It's now safe to disconnect USB from your flipper\r\n");
|
||||
@@ -14,22 +16,22 @@ void power_cli_off(Cli* cli, FuriString* args) {
|
||||
power_off(power);
|
||||
}
|
||||
|
||||
void power_cli_reboot(Cli* cli, FuriString* args) {
|
||||
UNUSED(cli);
|
||||
void power_cli_reboot(PipeSide* pipe, FuriString* args) {
|
||||
UNUSED(pipe);
|
||||
UNUSED(args);
|
||||
Power* power = furi_record_open(RECORD_POWER);
|
||||
power_reboot(power, PowerBootModeNormal);
|
||||
}
|
||||
|
||||
void power_cli_reboot2dfu(Cli* cli, FuriString* args) {
|
||||
UNUSED(cli);
|
||||
void power_cli_reboot2dfu(PipeSide* pipe, FuriString* args) {
|
||||
UNUSED(pipe);
|
||||
UNUSED(args);
|
||||
Power* power = furi_record_open(RECORD_POWER);
|
||||
power_reboot(power, PowerBootModeDfu);
|
||||
}
|
||||
|
||||
void power_cli_5v(Cli* cli, FuriString* args) {
|
||||
UNUSED(cli);
|
||||
void power_cli_5v(PipeSide* pipe, FuriString* args) {
|
||||
UNUSED(pipe);
|
||||
Power* power = furi_record_open(RECORD_POWER);
|
||||
if(!furi_string_cmp(args, "0")) {
|
||||
power_enable_otg(power, false);
|
||||
@@ -42,8 +44,8 @@ void power_cli_5v(Cli* cli, FuriString* args) {
|
||||
furi_record_close(RECORD_POWER);
|
||||
}
|
||||
|
||||
void power_cli_3v3(Cli* cli, FuriString* args) {
|
||||
UNUSED(cli);
|
||||
void power_cli_3v3(PipeSide* pipe, FuriString* args) {
|
||||
UNUSED(pipe);
|
||||
if(!furi_string_cmp(args, "0")) {
|
||||
furi_hal_power_disable_external_3_3v();
|
||||
} else if(!furi_string_cmp(args, "1")) {
|
||||
@@ -67,7 +69,7 @@ static void power_cli_command_print_usage(void) {
|
||||
}
|
||||
}
|
||||
|
||||
void power_cli(Cli* cli, FuriString* args, void* context) {
|
||||
void power_cli(PipeSide* pipe, FuriString* args, void* context) {
|
||||
UNUSED(context);
|
||||
FuriString* cmd;
|
||||
cmd = furi_string_alloc();
|
||||
@@ -79,28 +81,28 @@ void power_cli(Cli* cli, FuriString* args, void* context) {
|
||||
}
|
||||
|
||||
if(furi_string_cmp_str(cmd, "off") == 0) {
|
||||
power_cli_off(cli, args);
|
||||
power_cli_off(pipe, args);
|
||||
break;
|
||||
}
|
||||
|
||||
if(furi_string_cmp_str(cmd, "reboot") == 0) {
|
||||
power_cli_reboot(cli, args);
|
||||
power_cli_reboot(pipe, args);
|
||||
break;
|
||||
}
|
||||
|
||||
if(furi_string_cmp_str(cmd, "reboot2dfu") == 0) {
|
||||
power_cli_reboot2dfu(cli, args);
|
||||
power_cli_reboot2dfu(pipe, args);
|
||||
break;
|
||||
}
|
||||
|
||||
if(furi_string_cmp_str(cmd, "5v") == 0) {
|
||||
power_cli_5v(cli, args);
|
||||
power_cli_5v(pipe, args);
|
||||
break;
|
||||
}
|
||||
|
||||
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
|
||||
if(furi_string_cmp_str(cmd, "3v3") == 0) {
|
||||
power_cli_3v3(cli, args);
|
||||
power_cli_3v3(pipe, args);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -113,10 +115,8 @@ void power_cli(Cli* cli, FuriString* args, void* context) {
|
||||
|
||||
void power_on_system_start(void) {
|
||||
#ifdef SRV_CLI
|
||||
Cli* cli = furi_record_open(RECORD_CLI);
|
||||
|
||||
cli_add_command(cli, "power", CliCommandFlagParallelSafe, power_cli, NULL);
|
||||
|
||||
CliRegistry* registry = furi_record_open(RECORD_CLI);
|
||||
cli_registry_add_command(registry, "power", CliCommandFlagParallelSafe, power_cli, NULL);
|
||||
furi_record_close(RECORD_CLI);
|
||||
#else
|
||||
UNUSED(power_cli);
|
||||
|
||||
@@ -9,7 +9,8 @@
|
||||
|
||||
#include <furi.h>
|
||||
|
||||
#include <cli/cli.h>
|
||||
#include <toolbox/cli/cli_command.h>
|
||||
#include <cli/cli_main_commands.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <m-dict.h>
|
||||
@@ -435,9 +436,14 @@ void rpc_on_system_start(void* p) {
|
||||
|
||||
rpc->busy_mutex = furi_mutex_alloc(FuriMutexTypeNormal);
|
||||
|
||||
Cli* cli = furi_record_open(RECORD_CLI);
|
||||
cli_add_command(
|
||||
cli, "start_rpc_session", CliCommandFlagParallelSafe, rpc_cli_command_start_session, rpc);
|
||||
CliRegistry* registry = furi_record_open(RECORD_CLI);
|
||||
cli_registry_add_command(
|
||||
registry,
|
||||
"start_rpc_session",
|
||||
CliCommandFlagParallelSafe,
|
||||
rpc_cli_command_start_session,
|
||||
rpc);
|
||||
furi_record_close(RECORD_CLI);
|
||||
|
||||
furi_record_create(RECORD_RPC, rpc);
|
||||
}
|
||||
|
||||
@@ -1,25 +1,26 @@
|
||||
#include <cli/cli.h>
|
||||
#include <toolbox/cli/cli_command.h>
|
||||
#include <cli/cli_main_commands.h>
|
||||
#include <furi.h>
|
||||
#include <rpc/rpc.h>
|
||||
#include <furi_hal.h>
|
||||
#include <toolbox/pipe.h>
|
||||
|
||||
#define TAG "RpcCli"
|
||||
|
||||
typedef struct {
|
||||
Cli* cli;
|
||||
PipeSide* pipe;
|
||||
bool session_close_request;
|
||||
FuriSemaphore* terminate_semaphore;
|
||||
} CliRpc;
|
||||
|
||||
#define CLI_READ_BUFFER_SIZE 64
|
||||
#define CLI_READ_BUFFER_SIZE 64UL
|
||||
|
||||
static void rpc_cli_send_bytes_callback(void* context, uint8_t* bytes, size_t bytes_len) {
|
||||
furi_assert(context);
|
||||
furi_assert(bytes);
|
||||
furi_assert(bytes_len > 0);
|
||||
CliRpc* cli_rpc = context;
|
||||
|
||||
cli_write(cli_rpc->cli, bytes, bytes_len);
|
||||
pipe_send(cli_rpc->pipe, bytes, bytes_len);
|
||||
}
|
||||
|
||||
static void rpc_cli_session_close_callback(void* context) {
|
||||
@@ -36,9 +37,9 @@ static void rpc_cli_session_terminated_callback(void* context) {
|
||||
furi_semaphore_release(cli_rpc->terminate_semaphore);
|
||||
}
|
||||
|
||||
void rpc_cli_command_start_session(Cli* cli, FuriString* args, void* context) {
|
||||
void rpc_cli_command_start_session(PipeSide* pipe, FuriString* args, void* context) {
|
||||
UNUSED(args);
|
||||
furi_assert(cli);
|
||||
furi_assert(pipe);
|
||||
furi_assert(context);
|
||||
Rpc* rpc = context;
|
||||
|
||||
@@ -53,7 +54,7 @@ void rpc_cli_command_start_session(Cli* cli, FuriString* args, void* context) {
|
||||
return;
|
||||
}
|
||||
|
||||
CliRpc cli_rpc = {.cli = cli, .session_close_request = false};
|
||||
CliRpc cli_rpc = {.pipe = pipe, .session_close_request = false};
|
||||
cli_rpc.terminate_semaphore = furi_semaphore_alloc(1, 0);
|
||||
rpc_session_set_context(rpc_session, &cli_rpc);
|
||||
rpc_session_set_send_bytes_callback(rpc_session, rpc_cli_send_bytes_callback);
|
||||
@@ -64,8 +65,9 @@ void rpc_cli_command_start_session(Cli* cli, FuriString* args, void* context) {
|
||||
size_t size_received = 0;
|
||||
|
||||
while(1) {
|
||||
size_received = cli_read_timeout(cli_rpc.cli, buffer, CLI_READ_BUFFER_SIZE, 50);
|
||||
if(!cli_is_connected(cli_rpc.cli) || cli_rpc.session_close_request) {
|
||||
size_t to_receive = CLAMP(pipe_bytes_available(cli_rpc.pipe), CLI_READ_BUFFER_SIZE, 1UL);
|
||||
size_received = pipe_receive(cli_rpc.pipe, buffer, to_receive);
|
||||
if(size_received < to_receive || cli_rpc.session_close_request) {
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include <pb_decode.h>
|
||||
#include <pb_encode.h>
|
||||
#include <flipper.pb.h>
|
||||
#include <cli/cli.h>
|
||||
#include <toolbox/pipe.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -46,7 +46,7 @@ void rpc_desktop_free(void* ctx);
|
||||
void rpc_debug_print_message(const PB_Main* message);
|
||||
void rpc_debug_print_data(const char* prefix, uint8_t* buffer, size_t size);
|
||||
|
||||
void rpc_cli_command_start_session(Cli* cli, FuriString* args, void* context);
|
||||
void rpc_cli_command_start_session(PipeSide* pipe, FuriString* args, void* context);
|
||||
|
||||
PB_CommandStatus rpc_system_storage_get_error(FS_Error fs_error);
|
||||
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
|
||||
#include <cli/cli.h>
|
||||
#include <toolbox/cli/cli_command.h>
|
||||
#include <toolbox/cli/cli_ansi.h>
|
||||
#include <cli/cli_main_commands.h>
|
||||
#include <lib/toolbox/args.h>
|
||||
#include <lib/toolbox/dir_walk.h>
|
||||
#include <lib/toolbox/md5_calc.h>
|
||||
@@ -10,6 +12,7 @@
|
||||
#include <storage/storage.h>
|
||||
#include <storage/storage_sd_api.h>
|
||||
#include <power/power_service/power.h>
|
||||
#include <toolbox/pipe.h>
|
||||
|
||||
#define MAX_NAME_LENGTH 255
|
||||
|
||||
@@ -19,8 +22,8 @@ static void storage_cli_print_error(FS_Error error) {
|
||||
printf("Storage error: %s\r\n", storage_error_get_desc(error));
|
||||
}
|
||||
|
||||
static void storage_cli_info(Cli* cli, FuriString* path, FuriString* args) {
|
||||
UNUSED(cli);
|
||||
static void storage_cli_info(PipeSide* pipe, FuriString* path, FuriString* args) {
|
||||
UNUSED(pipe);
|
||||
UNUSED(args);
|
||||
Storage* api = furi_record_open(RECORD_STORAGE);
|
||||
|
||||
@@ -69,13 +72,14 @@ static void storage_cli_info(Cli* cli, FuriString* path, FuriString* args) {
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
}
|
||||
|
||||
static void storage_cli_format(Cli* cli, FuriString* path, FuriString* args) {
|
||||
static void storage_cli_format(PipeSide* pipe, FuriString* path, FuriString* args) {
|
||||
UNUSED(pipe);
|
||||
UNUSED(args);
|
||||
if(furi_string_cmp_str(path, STORAGE_INT_PATH_PREFIX) == 0) {
|
||||
storage_cli_print_error(FSE_NOT_IMPLEMENTED);
|
||||
} else if(furi_string_cmp_str(path, STORAGE_EXT_PATH_PREFIX) == 0) {
|
||||
printf("Formatting SD card, All data will be lost! Are you sure (y/n)?\r\n");
|
||||
char answer = cli_getc(cli);
|
||||
char answer = getchar();
|
||||
if(answer == 'y' || answer == 'Y') {
|
||||
Storage* api = furi_record_open(RECORD_STORAGE);
|
||||
printf("Formatting, please wait...\r\n");
|
||||
@@ -96,8 +100,8 @@ static void storage_cli_format(Cli* cli, FuriString* path, FuriString* args) {
|
||||
}
|
||||
}
|
||||
|
||||
static void storage_cli_list(Cli* cli, FuriString* path, FuriString* args) {
|
||||
UNUSED(cli);
|
||||
static void storage_cli_list(PipeSide* pipe, FuriString* path, FuriString* args) {
|
||||
UNUSED(pipe);
|
||||
UNUSED(args);
|
||||
if(furi_string_cmp_str(path, "/") == 0) {
|
||||
printf("\t[D] int\r\n");
|
||||
@@ -134,13 +138,13 @@ static void storage_cli_list(Cli* cli, FuriString* path, FuriString* args) {
|
||||
}
|
||||
}
|
||||
|
||||
static void storage_cli_tree(Cli* cli, FuriString* path, FuriString* args) {
|
||||
static void storage_cli_tree(PipeSide* pipe, FuriString* path, FuriString* args) {
|
||||
UNUSED(args);
|
||||
if(furi_string_cmp_str(path, "/") == 0) {
|
||||
furi_string_set(path, STORAGE_INT_PATH_PREFIX);
|
||||
storage_cli_tree(cli, path, NULL);
|
||||
storage_cli_tree(pipe, path, NULL);
|
||||
furi_string_set(path, STORAGE_EXT_PATH_PREFIX);
|
||||
storage_cli_tree(cli, path, NULL);
|
||||
storage_cli_tree(pipe, path, NULL);
|
||||
} else {
|
||||
Storage* api = furi_record_open(RECORD_STORAGE);
|
||||
DirWalk* dir_walk = dir_walk_alloc(api);
|
||||
@@ -176,8 +180,8 @@ static void storage_cli_tree(Cli* cli, FuriString* path, FuriString* args) {
|
||||
}
|
||||
}
|
||||
|
||||
static void storage_cli_read(Cli* cli, FuriString* path, FuriString* args) {
|
||||
UNUSED(cli);
|
||||
static void storage_cli_read(PipeSide* pipe, FuriString* path, FuriString* args) {
|
||||
UNUSED(pipe);
|
||||
UNUSED(args);
|
||||
Storage* api = furi_record_open(RECORD_STORAGE);
|
||||
File* file = storage_file_alloc(api);
|
||||
@@ -208,7 +212,8 @@ static void storage_cli_read(Cli* cli, FuriString* path, FuriString* args) {
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
}
|
||||
|
||||
static void storage_cli_write(Cli* cli, FuriString* path, FuriString* args) {
|
||||
static void storage_cli_write(PipeSide* pipe, FuriString* path, FuriString* args) {
|
||||
UNUSED(pipe);
|
||||
UNUSED(args);
|
||||
Storage* api = furi_record_open(RECORD_STORAGE);
|
||||
File* file = storage_file_alloc(api);
|
||||
@@ -222,9 +227,9 @@ static void storage_cli_write(Cli* cli, FuriString* path, FuriString* args) {
|
||||
uint32_t read_index = 0;
|
||||
|
||||
while(true) {
|
||||
uint8_t symbol = cli_getc(cli);
|
||||
uint8_t symbol = getchar();
|
||||
|
||||
if(symbol == CliSymbolAsciiETX) {
|
||||
if(symbol == CliKeyETX) {
|
||||
size_t write_size = read_index % buffer_size;
|
||||
|
||||
if(write_size > 0) {
|
||||
@@ -263,7 +268,8 @@ static void storage_cli_write(Cli* cli, FuriString* path, FuriString* args) {
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
}
|
||||
|
||||
static void storage_cli_read_chunks(Cli* cli, FuriString* path, FuriString* args) {
|
||||
static void storage_cli_read_chunks(PipeSide* pipe, FuriString* path, FuriString* args) {
|
||||
UNUSED(pipe);
|
||||
Storage* api = furi_record_open(RECORD_STORAGE);
|
||||
File* file = storage_file_alloc(api);
|
||||
|
||||
@@ -280,7 +286,7 @@ static void storage_cli_read_chunks(Cli* cli, FuriString* path, FuriString* args
|
||||
uint8_t* data = malloc(buffer_size);
|
||||
while(file_size > 0) {
|
||||
printf("\r\nReady?\r\n");
|
||||
cli_getc(cli);
|
||||
getchar();
|
||||
|
||||
size_t read_size = storage_file_read(file, data, buffer_size);
|
||||
for(size_t i = 0; i < read_size; i++) {
|
||||
@@ -302,31 +308,34 @@ static void storage_cli_read_chunks(Cli* cli, FuriString* path, FuriString* args
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
}
|
||||
|
||||
static void storage_cli_write_chunk(Cli* cli, FuriString* path, FuriString* args) {
|
||||
static void storage_cli_write_chunk(PipeSide* pipe, FuriString* path, FuriString* args) {
|
||||
Storage* api = furi_record_open(RECORD_STORAGE);
|
||||
File* file = storage_file_alloc(api);
|
||||
|
||||
uint32_t buffer_size;
|
||||
if(strint_to_uint32(furi_string_get_cstr(args), NULL, &buffer_size, 10) !=
|
||||
uint32_t need_to_read;
|
||||
if(strint_to_uint32(furi_string_get_cstr(args), NULL, &need_to_read, 10) !=
|
||||
StrintParseNoError) {
|
||||
storage_cli_print_usage();
|
||||
} else {
|
||||
if(storage_file_open(file, furi_string_get_cstr(path), FSAM_WRITE, FSOM_OPEN_APPEND)) {
|
||||
printf("Ready\r\n");
|
||||
|
||||
if(buffer_size) {
|
||||
const size_t buffer_size = 1024;
|
||||
uint8_t* buffer = malloc(buffer_size);
|
||||
|
||||
size_t read_bytes = cli_read(cli, buffer, buffer_size);
|
||||
while(need_to_read) {
|
||||
size_t to_read_this_time = MIN(buffer_size, need_to_read);
|
||||
size_t read_this_time = pipe_receive(pipe, buffer, to_read_this_time);
|
||||
if(read_this_time != to_read_this_time) break;
|
||||
|
||||
size_t written_size = storage_file_write(file, buffer, read_bytes);
|
||||
|
||||
if(written_size != buffer_size) {
|
||||
size_t wrote_this_time = storage_file_write(file, buffer, read_this_time);
|
||||
if(wrote_this_time != read_this_time) {
|
||||
storage_cli_print_error(storage_file_get_error(file));
|
||||
break;
|
||||
}
|
||||
need_to_read -= read_this_time;
|
||||
}
|
||||
|
||||
free(buffer);
|
||||
}
|
||||
} else {
|
||||
storage_cli_print_error(storage_file_get_error(file));
|
||||
}
|
||||
@@ -337,8 +346,8 @@ static void storage_cli_write_chunk(Cli* cli, FuriString* path, FuriString* args
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
}
|
||||
|
||||
static void storage_cli_stat(Cli* cli, FuriString* path, FuriString* args) {
|
||||
UNUSED(cli);
|
||||
static void storage_cli_stat(PipeSide* pipe, FuriString* path, FuriString* args) {
|
||||
UNUSED(pipe);
|
||||
UNUSED(args);
|
||||
Storage* api = furi_record_open(RECORD_STORAGE);
|
||||
|
||||
@@ -379,8 +388,8 @@ static void storage_cli_stat(Cli* cli, FuriString* path, FuriString* args) {
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
}
|
||||
|
||||
static void storage_cli_timestamp(Cli* cli, FuriString* path, FuriString* args) {
|
||||
UNUSED(cli);
|
||||
static void storage_cli_timestamp(PipeSide* pipe, FuriString* path, FuriString* args) {
|
||||
UNUSED(pipe);
|
||||
UNUSED(args);
|
||||
Storage* api = furi_record_open(RECORD_STORAGE);
|
||||
|
||||
@@ -396,8 +405,8 @@ static void storage_cli_timestamp(Cli* cli, FuriString* path, FuriString* args)
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
}
|
||||
|
||||
static void storage_cli_copy(Cli* cli, FuriString* old_path, FuriString* args) {
|
||||
UNUSED(cli);
|
||||
static void storage_cli_copy(PipeSide* pipe, FuriString* old_path, FuriString* args) {
|
||||
UNUSED(pipe);
|
||||
Storage* api = furi_record_open(RECORD_STORAGE);
|
||||
FuriString* new_path;
|
||||
new_path = furi_string_alloc();
|
||||
@@ -417,8 +426,8 @@ static void storage_cli_copy(Cli* cli, FuriString* old_path, FuriString* args) {
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
}
|
||||
|
||||
static void storage_cli_remove(Cli* cli, FuriString* path, FuriString* args) {
|
||||
UNUSED(cli);
|
||||
static void storage_cli_remove(PipeSide* pipe, FuriString* path, FuriString* args) {
|
||||
UNUSED(pipe);
|
||||
UNUSED(args);
|
||||
Storage* api = furi_record_open(RECORD_STORAGE);
|
||||
FS_Error error = storage_common_remove(api, furi_string_get_cstr(path));
|
||||
@@ -430,8 +439,8 @@ static void storage_cli_remove(Cli* cli, FuriString* path, FuriString* args) {
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
}
|
||||
|
||||
static void storage_cli_rename(Cli* cli, FuriString* old_path, FuriString* args) {
|
||||
UNUSED(cli);
|
||||
static void storage_cli_rename(PipeSide* pipe, FuriString* old_path, FuriString* args) {
|
||||
UNUSED(pipe);
|
||||
Storage* api = furi_record_open(RECORD_STORAGE);
|
||||
FuriString* new_path;
|
||||
new_path = furi_string_alloc();
|
||||
@@ -451,8 +460,8 @@ static void storage_cli_rename(Cli* cli, FuriString* old_path, FuriString* args)
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
}
|
||||
|
||||
static void storage_cli_mkdir(Cli* cli, FuriString* path, FuriString* args) {
|
||||
UNUSED(cli);
|
||||
static void storage_cli_mkdir(PipeSide* pipe, FuriString* path, FuriString* args) {
|
||||
UNUSED(pipe);
|
||||
UNUSED(args);
|
||||
Storage* api = furi_record_open(RECORD_STORAGE);
|
||||
FS_Error error = storage_common_mkdir(api, furi_string_get_cstr(path));
|
||||
@@ -464,8 +473,8 @@ static void storage_cli_mkdir(Cli* cli, FuriString* path, FuriString* args) {
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
}
|
||||
|
||||
static void storage_cli_md5(Cli* cli, FuriString* path, FuriString* args) {
|
||||
UNUSED(cli);
|
||||
static void storage_cli_md5(PipeSide* pipe, FuriString* path, FuriString* args) {
|
||||
UNUSED(pipe);
|
||||
UNUSED(args);
|
||||
Storage* api = furi_record_open(RECORD_STORAGE);
|
||||
File* file = storage_file_alloc(api);
|
||||
@@ -491,8 +500,8 @@ static bool tar_extract_file_callback(const char* name, bool is_directory, void*
|
||||
return true;
|
||||
}
|
||||
|
||||
static void storage_cli_extract(Cli* cli, FuriString* old_path, FuriString* args) {
|
||||
UNUSED(cli);
|
||||
static void storage_cli_extract(PipeSide* pipe, FuriString* old_path, FuriString* args) {
|
||||
UNUSED(pipe);
|
||||
FuriString* new_path = furi_string_alloc();
|
||||
|
||||
if(!args_read_probably_quoted_string_and_trim(args, new_path)) {
|
||||
@@ -526,7 +535,7 @@ static void storage_cli_extract(Cli* cli, FuriString* old_path, FuriString* args
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
}
|
||||
|
||||
typedef void (*StorageCliCommandCallback)(Cli* cli, FuriString* path, FuriString* args);
|
||||
typedef void (*StorageCliCommandCallback)(PipeSide* pipe, FuriString* path, FuriString* args);
|
||||
|
||||
typedef struct {
|
||||
const char* command;
|
||||
@@ -631,7 +640,7 @@ static void storage_cli_print_usage(void) {
|
||||
}
|
||||
}
|
||||
|
||||
void storage_cli(Cli* cli, FuriString* args, void* context) {
|
||||
void storage_cli(PipeSide* pipe, FuriString* args, void* context) {
|
||||
UNUSED(context);
|
||||
FuriString* cmd;
|
||||
FuriString* path;
|
||||
@@ -653,7 +662,7 @@ void storage_cli(Cli* cli, FuriString* args, void* context) {
|
||||
for(; i < COUNT_OF(storage_cli_commands); ++i) {
|
||||
const StorageCliCommand* command_descr = &storage_cli_commands[i];
|
||||
if(furi_string_cmp_str(cmd, command_descr->command) == 0) {
|
||||
command_descr->impl(cli, path, args);
|
||||
command_descr->impl(pipe, path, args);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -667,11 +676,12 @@ void storage_cli(Cli* cli, FuriString* args, void* context) {
|
||||
furi_string_free(cmd);
|
||||
}
|
||||
|
||||
static void storage_cli_factory_reset(Cli* cli, FuriString* args, void* context) {
|
||||
static void storage_cli_factory_reset(PipeSide* pipe, FuriString* args, void* context) {
|
||||
UNUSED(pipe);
|
||||
UNUSED(args);
|
||||
UNUSED(context);
|
||||
printf("All data will be lost! Are you sure (y/n)?\r\n");
|
||||
char c = cli_getc(cli);
|
||||
char c = getchar();
|
||||
if(c == 'y' || c == 'Y') {
|
||||
printf("Data will be wiped after reboot.\r\n");
|
||||
|
||||
@@ -687,10 +697,15 @@ static void storage_cli_factory_reset(Cli* cli, FuriString* args, void* context)
|
||||
|
||||
void storage_on_system_start(void) {
|
||||
#ifdef SRV_CLI
|
||||
Cli* cli = furi_record_open(RECORD_CLI);
|
||||
cli_add_command(cli, RECORD_STORAGE, CliCommandFlagParallelSafe, storage_cli, NULL);
|
||||
cli_add_command(
|
||||
cli, "factory_reset", CliCommandFlagParallelSafe, storage_cli_factory_reset, NULL);
|
||||
CliRegistry* registry = furi_record_open(RECORD_CLI);
|
||||
cli_registry_add_command(
|
||||
registry,
|
||||
"storage",
|
||||
CliCommandFlagParallelSafe | CliCommandFlagUseShellThread,
|
||||
storage_cli,
|
||||
NULL);
|
||||
cli_registry_add_command(
|
||||
registry, "factory_reset", CliCommandFlagParallelSafe, storage_cli_factory_reset, NULL);
|
||||
furi_record_close(RECORD_CLI);
|
||||
#else
|
||||
UNUSED(storage_cli_factory_reset);
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
#include "js_app_i.h"
|
||||
#include <toolbox/path.h>
|
||||
#include <assets_icons.h>
|
||||
#include <cli/cli.h>
|
||||
#include <toolbox/cli/cli_command.h>
|
||||
#include <cli/cli_main_commands.h>
|
||||
#include <toolbox/pipe.h>
|
||||
|
||||
#define TAG "JS app"
|
||||
|
||||
@@ -131,12 +133,14 @@ int32_t js_app(void* arg) {
|
||||
} //-V773
|
||||
|
||||
typedef struct {
|
||||
Cli* cli;
|
||||
PipeSide* pipe;
|
||||
FuriSemaphore* exit_sem;
|
||||
} JsCliContext;
|
||||
|
||||
static void js_cli_print(JsCliContext* ctx, const char* msg) {
|
||||
cli_write(ctx->cli, (uint8_t*)msg, strlen(msg));
|
||||
UNUSED(ctx);
|
||||
UNUSED(msg);
|
||||
pipe_send(ctx->pipe, msg, strlen(msg));
|
||||
}
|
||||
|
||||
static void js_cli_exit(JsCliContext* ctx) {
|
||||
@@ -170,7 +174,7 @@ static void js_cli_callback(JsThreadEvent event, const char* msg, void* context)
|
||||
}
|
||||
}
|
||||
|
||||
void js_cli_execute(Cli* cli, FuriString* args, void* context) {
|
||||
void js_cli_execute(PipeSide* pipe, FuriString* args, void* context) {
|
||||
UNUSED(context);
|
||||
|
||||
const char* path = furi_string_get_cstr(args);
|
||||
@@ -187,14 +191,14 @@ void js_cli_execute(Cli* cli, FuriString* args, void* context) {
|
||||
break;
|
||||
}
|
||||
|
||||
JsCliContext ctx = {.cli = cli};
|
||||
JsCliContext ctx = {.pipe = pipe};
|
||||
ctx.exit_sem = furi_semaphore_alloc(1, 0);
|
||||
|
||||
printf("Running script %s, press CTRL+C to stop\r\n", path);
|
||||
JsThread* js_thread = js_thread_run(path, js_cli_callback, &ctx);
|
||||
|
||||
while(furi_semaphore_acquire(ctx.exit_sem, 100) != FuriStatusOk) {
|
||||
if(cli_cmd_interrupt_received(cli)) break;
|
||||
if(cli_is_pipe_broken_or_is_etx_next_char(pipe)) break;
|
||||
}
|
||||
|
||||
js_thread_stop(js_thread);
|
||||
@@ -206,8 +210,8 @@ void js_cli_execute(Cli* cli, FuriString* args, void* context) {
|
||||
|
||||
void js_app_on_system_start(void) {
|
||||
#ifdef SRV_CLI
|
||||
Cli* cli = furi_record_open(RECORD_CLI);
|
||||
cli_add_command(cli, "js", CliCommandFlagDefault, js_cli_execute, NULL);
|
||||
CliRegistry* registry = furi_record_open(RECORD_CLI);
|
||||
cli_registry_add_command(registry, "js", CliCommandFlagDefault, js_cli_execute, NULL);
|
||||
furi_record_close(RECORD_CLI);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -202,12 +202,15 @@ static JsSdkCompatStatus
|
||||
return JsSdkCompatStatusCompatible;
|
||||
}
|
||||
|
||||
#define JS_SDK_COMPAT_ARGS \
|
||||
int32_t major, minor; \
|
||||
JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_INT32(&major), JS_ARG_INT32(&minor));
|
||||
static const JsValueDeclaration js_sdk_version_arg_list[] = {
|
||||
JS_VALUE_SIMPLE(JsValueTypeInt32),
|
||||
JS_VALUE_SIMPLE(JsValueTypeInt32),
|
||||
};
|
||||
static const JsValueArguments js_sdk_version_args = JS_VALUE_ARGS(js_sdk_version_arg_list);
|
||||
|
||||
void js_sdk_compatibility_status(struct mjs* mjs) {
|
||||
JS_SDK_COMPAT_ARGS;
|
||||
int32_t major, minor;
|
||||
JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_sdk_version_args, &major, &minor);
|
||||
JsSdkCompatStatus status = js_internal_sdk_compatibility_status(major, minor);
|
||||
switch(status) {
|
||||
case JsSdkCompatStatusCompatible:
|
||||
@@ -223,7 +226,8 @@ void js_sdk_compatibility_status(struct mjs* mjs) {
|
||||
}
|
||||
|
||||
void js_is_sdk_compatible(struct mjs* mjs) {
|
||||
JS_SDK_COMPAT_ARGS;
|
||||
int32_t major, minor;
|
||||
JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_sdk_version_args, &major, &minor);
|
||||
JsSdkCompatStatus status = js_internal_sdk_compatibility_status(major, minor);
|
||||
mjs_return(mjs, mjs_mk_boolean(mjs, status == JsSdkCompatStatusCompatible));
|
||||
}
|
||||
@@ -246,7 +250,8 @@ static bool js_internal_compat_ask_user(const char* message) {
|
||||
}
|
||||
|
||||
void js_check_sdk_compatibility(struct mjs* mjs) {
|
||||
JS_SDK_COMPAT_ARGS;
|
||||
int32_t major, minor;
|
||||
JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_sdk_version_args, &major, &minor);
|
||||
JsSdkCompatStatus status = js_internal_sdk_compatibility_status(major, minor);
|
||||
if(status != JsSdkCompatStatusCompatible) {
|
||||
FURI_LOG_E(
|
||||
@@ -308,15 +313,20 @@ static bool js_internal_supports_all_of(struct mjs* mjs, mjs_val_t feature_arr)
|
||||
return true;
|
||||
}
|
||||
|
||||
static const JsValueDeclaration js_sdk_features_arg_list[] = {
|
||||
JS_VALUE_SIMPLE(JsValueTypeAnyArray),
|
||||
};
|
||||
static const JsValueArguments js_sdk_features_args = JS_VALUE_ARGS(js_sdk_features_arg_list);
|
||||
|
||||
void js_does_sdk_support(struct mjs* mjs) {
|
||||
mjs_val_t features;
|
||||
JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_ARR(&features));
|
||||
JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_sdk_features_args, &features);
|
||||
mjs_return(mjs, mjs_mk_boolean(mjs, js_internal_supports_all_of(mjs, features)));
|
||||
}
|
||||
|
||||
void js_check_sdk_features(struct mjs* mjs) {
|
||||
mjs_val_t features;
|
||||
JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_ARR(&features));
|
||||
JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_sdk_features_args, &features);
|
||||
if(!js_internal_supports_all_of(mjs, features)) {
|
||||
FURI_LOG_E(TAG, "Script requests unsupported features");
|
||||
|
||||
|
||||
@@ -7,6 +7,10 @@
|
||||
#include <flipper_application/plugins/plugin_manager.h>
|
||||
#include <flipper_application/plugins/composite_resolver.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define PLUGIN_APP_ID "js"
|
||||
#define PLUGIN_API_VERSION 1
|
||||
|
||||
@@ -65,226 +69,6 @@ typedef enum {
|
||||
JsForeignMagic_JsEventLoopContract,
|
||||
} JsForeignMagic;
|
||||
|
||||
// Are you tired of your silly little JS+C glue code functions being 75%
|
||||
// argument validation code and 25% actual logic? Introducing: ASS (Argument
|
||||
// Schema for Scripts)! ASS is a set of macros that reduce the typical
|
||||
// boilerplate code of "check argument count, get arguments, validate arguments,
|
||||
// extract C values from arguments" down to just one line!
|
||||
|
||||
/**
|
||||
* When passed as the second argument to `JS_FETCH_ARGS_OR_RETURN`, signifies
|
||||
* that the function requires exactly as many arguments as were specified.
|
||||
*/
|
||||
#define JS_EXACTLY ==
|
||||
/**
|
||||
* When passed as the second argument to `JS_FETCH_ARGS_OR_RETURN`, signifies
|
||||
* that the function requires at least as many arguments as were specified.
|
||||
*/
|
||||
#define JS_AT_LEAST >=
|
||||
|
||||
typedef struct {
|
||||
const char* name;
|
||||
size_t value;
|
||||
} JsEnumMapping;
|
||||
|
||||
#define JS_ENUM_MAP(var_name, ...) \
|
||||
static const JsEnumMapping var_name##_mapping[] = { \
|
||||
{NULL, sizeof(var_name)}, \
|
||||
__VA_ARGS__, \
|
||||
{NULL, 0}, \
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
const char* name;
|
||||
size_t offset;
|
||||
} JsObjectMapping;
|
||||
|
||||
#define JS_OBJ_MAP(var_name, ...) \
|
||||
static const JsObjectMapping var_name##_mapping[] = { \
|
||||
__VA_ARGS__, \
|
||||
{NULL, 0}, \
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
void* out;
|
||||
int (*validator)(mjs_val_t);
|
||||
void (*converter)(struct mjs*, mjs_val_t*, void* out, const void* extra);
|
||||
const char* expected_type;
|
||||
bool (*extended_validator)(struct mjs*, mjs_val_t, const void* extra);
|
||||
const void* extra_data;
|
||||
} _js_arg_decl;
|
||||
|
||||
static inline void _js_to_int32(struct mjs* mjs, mjs_val_t* in, void* out, const void* extra) {
|
||||
UNUSED(extra);
|
||||
*(int32_t*)out = mjs_get_int32(mjs, *in);
|
||||
}
|
||||
#define JS_ARG_INT32(out) ((_js_arg_decl){out, mjs_is_number, _js_to_int32, "number", NULL, NULL})
|
||||
|
||||
static inline void _js_to_ptr(struct mjs* mjs, mjs_val_t* in, void* out, const void* extra) {
|
||||
UNUSED(extra);
|
||||
*(void**)out = mjs_get_ptr(mjs, *in);
|
||||
}
|
||||
#define JS_ARG_PTR(out) \
|
||||
((_js_arg_decl){out, mjs_is_foreign, _js_to_ptr, "opaque pointer", NULL, NULL})
|
||||
|
||||
static inline void _js_to_string(struct mjs* mjs, mjs_val_t* in, void* out, const void* extra) {
|
||||
UNUSED(extra);
|
||||
*(const char**)out = mjs_get_string(mjs, in, NULL);
|
||||
}
|
||||
#define JS_ARG_STR(out) ((_js_arg_decl){out, mjs_is_string, _js_to_string, "string", NULL, NULL})
|
||||
|
||||
static inline void _js_to_bool(struct mjs* mjs, mjs_val_t* in, void* out, const void* extra) {
|
||||
UNUSED(extra);
|
||||
*(bool*)out = !!mjs_get_bool(mjs, *in);
|
||||
}
|
||||
#define JS_ARG_BOOL(out) ((_js_arg_decl){out, mjs_is_boolean, _js_to_bool, "boolean", NULL, NULL})
|
||||
|
||||
static inline void _js_passthrough(struct mjs* mjs, mjs_val_t* in, void* out, const void* extra) {
|
||||
UNUSED(extra);
|
||||
UNUSED(mjs);
|
||||
*(mjs_val_t*)out = *in;
|
||||
}
|
||||
#define JS_ARG_ANY(out) ((_js_arg_decl){out, NULL, _js_passthrough, "any", NULL, NULL})
|
||||
#define JS_ARG_OBJ(out) ((_js_arg_decl){out, mjs_is_object, _js_passthrough, "any", NULL, NULL})
|
||||
#define JS_ARG_FN(out) \
|
||||
((_js_arg_decl){out, mjs_is_function, _js_passthrough, "function", NULL, NULL})
|
||||
#define JS_ARG_ARR(out) ((_js_arg_decl){out, mjs_is_array, _js_passthrough, "array", NULL, NULL})
|
||||
|
||||
static inline bool _js_validate_struct(struct mjs* mjs, mjs_val_t val, const void* extra) {
|
||||
JsForeignMagic expected_magic = (JsForeignMagic)(size_t)extra;
|
||||
JsForeignMagic struct_magic = *(JsForeignMagic*)mjs_get_ptr(mjs, val);
|
||||
return struct_magic == expected_magic;
|
||||
}
|
||||
#define JS_ARG_STRUCT(type, out) \
|
||||
((_js_arg_decl){ \
|
||||
out, \
|
||||
mjs_is_foreign, \
|
||||
_js_to_ptr, \
|
||||
#type, \
|
||||
_js_validate_struct, \
|
||||
(void*)JsForeignMagic##_##type})
|
||||
|
||||
static inline bool _js_validate_obj_w_struct(struct mjs* mjs, mjs_val_t val, const void* extra) {
|
||||
JsForeignMagic expected_magic = (JsForeignMagic)(size_t)extra;
|
||||
JsForeignMagic struct_magic = *(JsForeignMagic*)JS_GET_INST(mjs, val);
|
||||
return struct_magic == expected_magic;
|
||||
}
|
||||
#define JS_ARG_OBJ_WITH_STRUCT(type, out) \
|
||||
((_js_arg_decl){ \
|
||||
out, \
|
||||
mjs_is_object, \
|
||||
_js_passthrough, \
|
||||
#type, \
|
||||
_js_validate_obj_w_struct, \
|
||||
(void*)JsForeignMagic##_##type})
|
||||
|
||||
static inline bool _js_validate_enum(struct mjs* mjs, mjs_val_t val, const void* extra) {
|
||||
for(const JsEnumMapping* mapping = (JsEnumMapping*)extra + 1; mapping->name; mapping++)
|
||||
if(strcmp(mapping->name, mjs_get_string(mjs, &val, NULL)) == 0) return true;
|
||||
return false;
|
||||
}
|
||||
static inline void
|
||||
_js_convert_enum(struct mjs* mjs, mjs_val_t* val, void* out, const void* extra) {
|
||||
const JsEnumMapping* mapping = (JsEnumMapping*)extra;
|
||||
size_t size = mapping->value; // get enum size from first entry
|
||||
for(mapping++; mapping->name; mapping++) {
|
||||
if(strcmp(mapping->name, mjs_get_string(mjs, val, NULL)) == 0) {
|
||||
if(size == 1)
|
||||
*(uint8_t*)out = mapping->value;
|
||||
else if(size == 2)
|
||||
*(uint16_t*)out = mapping->value;
|
||||
else if(size == 4)
|
||||
*(uint32_t*)out = mapping->value;
|
||||
else if(size == 8)
|
||||
*(uint64_t*)out = mapping->value;
|
||||
return;
|
||||
}
|
||||
}
|
||||
// unreachable, thanks to _js_validate_enum
|
||||
}
|
||||
#define JS_ARG_ENUM(var_name, name) \
|
||||
((_js_arg_decl){ \
|
||||
&var_name, \
|
||||
mjs_is_string, \
|
||||
_js_convert_enum, \
|
||||
name " enum", \
|
||||
_js_validate_enum, \
|
||||
var_name##_mapping})
|
||||
|
||||
static inline bool _js_validate_object(struct mjs* mjs, mjs_val_t val, const void* extra) {
|
||||
for(const JsObjectMapping* mapping = (JsObjectMapping*)extra; mapping->name; mapping++)
|
||||
if(mjs_get(mjs, val, mapping->name, ~0) == MJS_UNDEFINED) return false;
|
||||
return true;
|
||||
}
|
||||
static inline void
|
||||
_js_convert_object(struct mjs* mjs, mjs_val_t* val, void* out, const void* extra) {
|
||||
const JsObjectMapping* mapping = (JsObjectMapping*)extra;
|
||||
for(; mapping->name; mapping++) {
|
||||
mjs_val_t field_val = mjs_get(mjs, *val, mapping->name, ~0);
|
||||
*(mjs_val_t*)((uint8_t*)out + mapping->offset) = field_val;
|
||||
}
|
||||
}
|
||||
#define JS_ARG_OBJECT(var_name, name) \
|
||||
((_js_arg_decl){ \
|
||||
&var_name, \
|
||||
mjs_is_object, \
|
||||
_js_convert_object, \
|
||||
name " object", \
|
||||
_js_validate_object, \
|
||||
var_name##_mapping})
|
||||
|
||||
/**
|
||||
* @brief Validates and converts a JS value with a declarative interface
|
||||
*
|
||||
* Example: `int32_t my_value; JS_CONVERT_OR_RETURN(mjs, &mjs_val, JS_ARG_INT32(&my_value), "value source");`
|
||||
*
|
||||
* @warning This macro executes `return;` by design in case of a validation failure
|
||||
*/
|
||||
#define JS_CONVERT_OR_RETURN(mjs, value, decl, source, ...) \
|
||||
if(decl.validator) \
|
||||
if(!decl.validator(*value)) \
|
||||
JS_ERROR_AND_RETURN( \
|
||||
mjs, \
|
||||
MJS_BAD_ARGS_ERROR, \
|
||||
source ": expected %s", \
|
||||
##__VA_ARGS__, \
|
||||
decl.expected_type); \
|
||||
if(decl.extended_validator) \
|
||||
if(!decl.extended_validator(mjs, *value, decl.extra_data)) \
|
||||
JS_ERROR_AND_RETURN( \
|
||||
mjs, \
|
||||
MJS_BAD_ARGS_ERROR, \
|
||||
source ": expected %s", \
|
||||
##__VA_ARGS__, \
|
||||
decl.expected_type); \
|
||||
decl.converter(mjs, value, decl.out, decl.extra_data);
|
||||
|
||||
//-V:JS_FETCH_ARGS_OR_RETURN:1008
|
||||
/**
|
||||
* @brief Fetches and validates the arguments passed to a JS function
|
||||
*
|
||||
* Example: `int32_t my_arg; JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_INT32(&my_arg));`
|
||||
*
|
||||
* @warning This macro executes `return;` by design in case of an argument count
|
||||
* mismatch or a validation failure
|
||||
*/
|
||||
#define JS_FETCH_ARGS_OR_RETURN(mjs, arg_operator, ...) \
|
||||
_js_arg_decl _js_args[] = {__VA_ARGS__}; \
|
||||
int _js_arg_cnt = COUNT_OF(_js_args); \
|
||||
mjs_val_t _js_arg_vals[_js_arg_cnt]; \
|
||||
if(!(mjs_nargs(mjs) arg_operator _js_arg_cnt)) \
|
||||
JS_ERROR_AND_RETURN( \
|
||||
mjs, \
|
||||
MJS_BAD_ARGS_ERROR, \
|
||||
"expected %s%d arguments, got %d", \
|
||||
#arg_operator, \
|
||||
_js_arg_cnt, \
|
||||
mjs_nargs(mjs)); \
|
||||
for(int _i = 0; _i < _js_arg_cnt; _i++) { \
|
||||
_js_arg_vals[_i] = mjs_arg(mjs, _i); \
|
||||
JS_CONVERT_OR_RETURN(mjs, &_js_arg_vals[_i], _js_args[_i], "argument %d", _i); \
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Prepends an error, sets the JS return value to `undefined` and returns
|
||||
* from the C function
|
||||
@@ -359,3 +143,7 @@ void js_does_sdk_support(struct mjs* mjs);
|
||||
* @brief `checkSdkFeatures` function
|
||||
*/
|
||||
void js_check_sdk_features(struct mjs* mjs);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -198,18 +198,15 @@ static void js_require(struct mjs* mjs) {
|
||||
}
|
||||
|
||||
static void js_parse_int(struct mjs* mjs) {
|
||||
const char* str;
|
||||
JS_FETCH_ARGS_OR_RETURN(mjs, JS_AT_LEAST, JS_ARG_STR(&str));
|
||||
static const JsValueDeclaration js_parse_int_arg_list[] = {
|
||||
JS_VALUE_SIMPLE(JsValueTypeString),
|
||||
JS_VALUE_SIMPLE_W_DEFAULT(JsValueTypeInt32, int32_val, 10),
|
||||
};
|
||||
static const JsValueArguments js_parse_int_args = JS_VALUE_ARGS(js_parse_int_arg_list);
|
||||
|
||||
int32_t base = 10;
|
||||
if(mjs_nargs(mjs) >= 2) {
|
||||
mjs_val_t base_arg = mjs_arg(mjs, 1);
|
||||
if(!mjs_is_number(base_arg)) {
|
||||
mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, "Base must be a number");
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
}
|
||||
base = mjs_get_int(mjs, base_arg);
|
||||
}
|
||||
const char* str;
|
||||
int32_t base;
|
||||
JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_parse_int_args, &str, &base);
|
||||
|
||||
int32_t num;
|
||||
if(strint_to_int32(str, NULL, &num, base) != StrintParseNoError) {
|
||||
|
||||
@@ -11,6 +11,10 @@
|
||||
#include <mjs_primitive_public.h>
|
||||
#include <mjs_array_buf_public.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define INST_PROP_NAME "_"
|
||||
|
||||
typedef enum {
|
||||
@@ -23,3 +27,7 @@ bool js_delay_with_flags(struct mjs* mjs, uint32_t time);
|
||||
void js_flags_set(struct mjs* mjs, uint32_t flags);
|
||||
|
||||
uint32_t js_flags_wait(struct mjs* mjs, uint32_t flags, uint32_t timeout);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -144,10 +144,16 @@ static void js_event_loop_subscribe(struct mjs* mjs) {
|
||||
JsEventLoop* module = JS_GET_CONTEXT(mjs);
|
||||
|
||||
// get arguments
|
||||
static const JsValueDeclaration js_loop_subscribe_arg_list[] = {
|
||||
JS_VALUE_SIMPLE(JsValueTypeRawPointer),
|
||||
JS_VALUE_SIMPLE(JsValueTypeFunction),
|
||||
};
|
||||
static const JsValueArguments js_loop_subscribe_args =
|
||||
JS_VALUE_ARGS(js_loop_subscribe_arg_list);
|
||||
|
||||
JsEventLoopContract* contract;
|
||||
mjs_val_t callback;
|
||||
JS_FETCH_ARGS_OR_RETURN(
|
||||
mjs, JS_AT_LEAST, JS_ARG_STRUCT(JsEventLoopContract, &contract), JS_ARG_FN(&callback));
|
||||
JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_loop_subscribe_args, &contract, &callback);
|
||||
|
||||
// create subscription object
|
||||
JsEventLoopSubscription* subscription = malloc(sizeof(JsEventLoopSubscription));
|
||||
@@ -242,20 +248,22 @@ static void js_event_loop_stop(struct mjs* mjs) {
|
||||
* event
|
||||
*/
|
||||
static void js_event_loop_timer(struct mjs* mjs) {
|
||||
// get arguments
|
||||
const char* mode_str;
|
||||
int32_t interval;
|
||||
JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_STR(&mode_str), JS_ARG_INT32(&interval));
|
||||
JsEventLoop* module = JS_GET_CONTEXT(mjs);
|
||||
static const JsValueEnumVariant js_loop_timer_mode_variants[] = {
|
||||
{"periodic", FuriEventLoopTimerTypePeriodic},
|
||||
{"oneshot", FuriEventLoopTimerTypeOnce},
|
||||
};
|
||||
|
||||
static const JsValueDeclaration js_loop_timer_arg_list[] = {
|
||||
JS_VALUE_ENUM(FuriEventLoopTimerType, js_loop_timer_mode_variants),
|
||||
JS_VALUE_SIMPLE(JsValueTypeInt32),
|
||||
};
|
||||
static const JsValueArguments js_loop_timer_args = JS_VALUE_ARGS(js_loop_timer_arg_list);
|
||||
|
||||
FuriEventLoopTimerType mode;
|
||||
if(strcasecmp(mode_str, "periodic") == 0) {
|
||||
mode = FuriEventLoopTimerTypePeriodic;
|
||||
} else if(strcasecmp(mode_str, "oneshot") == 0) {
|
||||
mode = FuriEventLoopTimerTypeOnce;
|
||||
} else {
|
||||
JS_ERROR_AND_RETURN(mjs, MJS_BAD_ARGS_ERROR, "argument 0: unknown mode");
|
||||
}
|
||||
int32_t interval;
|
||||
JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_loop_timer_args, &mode, &interval);
|
||||
|
||||
JsEventLoop* module = JS_GET_CONTEXT(mjs);
|
||||
|
||||
// make timer contract
|
||||
JsEventLoopContract* contract = malloc(sizeof(JsEventLoopContract));
|
||||
@@ -293,8 +301,14 @@ static mjs_val_t
|
||||
*/
|
||||
static void js_event_loop_queue_send(struct mjs* mjs) {
|
||||
// get arguments
|
||||
static const JsValueDeclaration js_loop_q_send_arg_list[] = {
|
||||
JS_VALUE_SIMPLE(JsValueTypeAny),
|
||||
};
|
||||
static const JsValueArguments js_loop_q_send_args = JS_VALUE_ARGS(js_loop_q_send_arg_list);
|
||||
|
||||
mjs_val_t message;
|
||||
JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_ANY(&message));
|
||||
JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_loop_q_send_args, &message);
|
||||
|
||||
JsEventLoopContract* contract = JS_GET_CONTEXT(mjs);
|
||||
|
||||
// send message
|
||||
@@ -311,8 +325,14 @@ static void js_event_loop_queue_send(struct mjs* mjs) {
|
||||
*/
|
||||
static void js_event_loop_queue(struct mjs* mjs) {
|
||||
// get arguments
|
||||
static const JsValueDeclaration js_loop_q_arg_list[] = {
|
||||
JS_VALUE_SIMPLE(JsValueTypeInt32),
|
||||
};
|
||||
static const JsValueArguments js_loop_q_args = JS_VALUE_ARGS(js_loop_q_arg_list);
|
||||
|
||||
int32_t length;
|
||||
JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_INT32(&length));
|
||||
JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_loop_q_args, &length);
|
||||
|
||||
JsEventLoop* module = JS_GET_CONTEXT(mjs);
|
||||
|
||||
// make queue contract
|
||||
|
||||
@@ -54,83 +54,114 @@ static void js_gpio_int_cb(void* arg) {
|
||||
* ```
|
||||
*/
|
||||
static void js_gpio_init(struct mjs* mjs) {
|
||||
// deconstruct mode object
|
||||
mjs_val_t mode_arg;
|
||||
JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_OBJ(&mode_arg));
|
||||
mjs_val_t direction_arg = mjs_get(mjs, mode_arg, "direction", ~0);
|
||||
mjs_val_t out_mode_arg = mjs_get(mjs, mode_arg, "outMode", ~0);
|
||||
mjs_val_t in_mode_arg = mjs_get(mjs, mode_arg, "inMode", ~0);
|
||||
mjs_val_t edge_arg = mjs_get(mjs, mode_arg, "edge", ~0);
|
||||
mjs_val_t pull_arg = mjs_get(mjs, mode_arg, "pull", ~0);
|
||||
// direction variants
|
||||
typedef enum {
|
||||
JsGpioDirectionIn,
|
||||
JsGpioDirectionOut,
|
||||
} JsGpioDirection;
|
||||
static const JsValueEnumVariant js_gpio_direction_variants[] = {
|
||||
{"in", JsGpioDirectionIn},
|
||||
{"out", JsGpioDirectionOut},
|
||||
};
|
||||
static const JsValueDeclaration js_gpio_direction =
|
||||
JS_VALUE_ENUM(JsGpioDirection, js_gpio_direction_variants);
|
||||
|
||||
// get strings
|
||||
const char* direction = mjs_get_string(mjs, &direction_arg, NULL);
|
||||
const char* out_mode = mjs_get_string(mjs, &out_mode_arg, NULL);
|
||||
const char* in_mode = mjs_get_string(mjs, &in_mode_arg, NULL);
|
||||
const char* edge = mjs_get_string(mjs, &edge_arg, NULL);
|
||||
const char* pull = mjs_get_string(mjs, &pull_arg, NULL);
|
||||
if(!direction)
|
||||
JS_ERROR_AND_RETURN(
|
||||
mjs, MJS_BAD_ARGS_ERROR, "Expected string in \"direction\" field of mode object");
|
||||
if(!out_mode) out_mode = "open_drain";
|
||||
if(!in_mode) in_mode = "plain_digital";
|
||||
if(!edge) edge = "rising";
|
||||
// inMode variants
|
||||
typedef enum {
|
||||
JsGpioInModeAnalog = (0 << 0),
|
||||
JsGpioInModePlainDigital = (1 << 0),
|
||||
JsGpioInModeInterrupt = (2 << 0),
|
||||
JsGpioInModeEvent = (3 << 0),
|
||||
} JsGpioInMode;
|
||||
static const JsValueEnumVariant js_gpio_in_mode_variants[] = {
|
||||
{"analog", JsGpioInModeAnalog},
|
||||
{"plain_digital", JsGpioInModePlainDigital},
|
||||
{"interrupt", JsGpioInModeInterrupt},
|
||||
{"event", JsGpioInModeEvent},
|
||||
};
|
||||
static const JsValueDeclaration js_gpio_in_mode =
|
||||
JS_VALUE_ENUM_W_DEFAULT(JsGpioInMode, js_gpio_in_mode_variants, JsGpioInModePlainDigital);
|
||||
|
||||
// outMode variants
|
||||
typedef enum {
|
||||
JsGpioOutModePushPull,
|
||||
JsGpioOutModeOpenDrain,
|
||||
} JsGpioOutMode;
|
||||
static const JsValueEnumVariant js_gpio_out_mode_variants[] = {
|
||||
{"push_pull", JsGpioOutModePushPull},
|
||||
{"open_drain", JsGpioOutModeOpenDrain},
|
||||
};
|
||||
static const JsValueDeclaration js_gpio_out_mode =
|
||||
JS_VALUE_ENUM_W_DEFAULT(JsGpioOutMode, js_gpio_out_mode_variants, JsGpioOutModeOpenDrain);
|
||||
|
||||
// edge variants
|
||||
typedef enum {
|
||||
JsGpioEdgeRising = (0 << 2),
|
||||
JsGpioEdgeFalling = (1 << 2),
|
||||
JsGpioEdgeBoth = (2 << 2),
|
||||
} JsGpioEdge;
|
||||
static const JsValueEnumVariant js_gpio_edge_variants[] = {
|
||||
{"rising", JsGpioEdgeRising},
|
||||
{"falling", JsGpioEdgeFalling},
|
||||
{"both", JsGpioEdgeBoth},
|
||||
};
|
||||
static const JsValueDeclaration js_gpio_edge =
|
||||
JS_VALUE_ENUM_W_DEFAULT(JsGpioEdge, js_gpio_edge_variants, JsGpioEdgeRising);
|
||||
|
||||
// pull variants
|
||||
static const JsValueEnumVariant js_gpio_pull_variants[] = {
|
||||
{"up", GpioPullUp},
|
||||
{"down", GpioPullDown},
|
||||
};
|
||||
static const JsValueDeclaration js_gpio_pull =
|
||||
JS_VALUE_ENUM_W_DEFAULT(GpioPull, js_gpio_pull_variants, GpioPullNo);
|
||||
|
||||
// complete mode object
|
||||
static const JsValueObjectField js_gpio_mode_object_fields[] = {
|
||||
{"direction", &js_gpio_direction},
|
||||
{"inMode", &js_gpio_in_mode},
|
||||
{"outMode", &js_gpio_out_mode},
|
||||
{"edge", &js_gpio_edge},
|
||||
{"pull", &js_gpio_pull},
|
||||
};
|
||||
|
||||
// function args
|
||||
static const JsValueDeclaration js_gpio_init_arg_list[] = {
|
||||
JS_VALUE_OBJECT_W_DEFAULTS(js_gpio_mode_object_fields),
|
||||
};
|
||||
static const JsValueArguments js_gpio_init_args = JS_VALUE_ARGS(js_gpio_init_arg_list);
|
||||
|
||||
JsGpioDirection direction;
|
||||
JsGpioInMode in_mode;
|
||||
JsGpioOutMode out_mode;
|
||||
JsGpioEdge edge;
|
||||
GpioPull pull;
|
||||
JS_VALUE_PARSE_ARGS_OR_RETURN(
|
||||
mjs, &js_gpio_init_args, &direction, &in_mode, &out_mode, &edge, &pull);
|
||||
|
||||
// convert strings to mode
|
||||
GpioMode mode;
|
||||
if(strcmp(direction, "out") == 0) {
|
||||
if(strcmp(out_mode, "push_pull") == 0)
|
||||
mode = GpioModeOutputPushPull;
|
||||
else if(strcmp(out_mode, "open_drain") == 0)
|
||||
mode = GpioModeOutputOpenDrain;
|
||||
else
|
||||
JS_ERROR_AND_RETURN(mjs, MJS_BAD_ARGS_ERROR, "invalid outMode");
|
||||
} else if(strcmp(direction, "in") == 0) {
|
||||
if(strcmp(in_mode, "analog") == 0) {
|
||||
mode = GpioModeAnalog;
|
||||
} else if(strcmp(in_mode, "plain_digital") == 0) {
|
||||
mode = GpioModeInput;
|
||||
} else if(strcmp(in_mode, "interrupt") == 0) {
|
||||
if(strcmp(edge, "rising") == 0)
|
||||
mode = GpioModeInterruptRise;
|
||||
else if(strcmp(edge, "falling") == 0)
|
||||
mode = GpioModeInterruptFall;
|
||||
else if(strcmp(edge, "both") == 0)
|
||||
mode = GpioModeInterruptRiseFall;
|
||||
else
|
||||
JS_ERROR_AND_RETURN(mjs, MJS_BAD_ARGS_ERROR, "invalid edge");
|
||||
} else if(strcmp(in_mode, "event") == 0) {
|
||||
if(strcmp(edge, "rising") == 0)
|
||||
mode = GpioModeEventRise;
|
||||
else if(strcmp(edge, "falling") == 0)
|
||||
mode = GpioModeEventFall;
|
||||
else if(strcmp(edge, "both") == 0)
|
||||
mode = GpioModeEventRiseFall;
|
||||
else
|
||||
JS_ERROR_AND_RETURN(mjs, MJS_BAD_ARGS_ERROR, "invalid edge");
|
||||
if(direction == JsGpioDirectionOut) {
|
||||
static const GpioMode js_gpio_out_mode_lut[] = {
|
||||
[JsGpioOutModePushPull] = GpioModeOutputPushPull,
|
||||
[JsGpioOutModeOpenDrain] = GpioModeOutputOpenDrain,
|
||||
};
|
||||
mode = js_gpio_out_mode_lut[out_mode];
|
||||
} else {
|
||||
JS_ERROR_AND_RETURN(mjs, MJS_BAD_ARGS_ERROR, "invalid inMode");
|
||||
}
|
||||
} else {
|
||||
JS_ERROR_AND_RETURN(mjs, MJS_BAD_ARGS_ERROR, "invalid direction");
|
||||
static const GpioMode js_gpio_in_mode_lut[] = {
|
||||
[JsGpioInModeAnalog] = GpioModeAnalog,
|
||||
[JsGpioInModePlainDigital] = GpioModeInput,
|
||||
[JsGpioInModeInterrupt | JsGpioEdgeRising] = GpioModeInterruptRise,
|
||||
[JsGpioInModeInterrupt | JsGpioEdgeFalling] = GpioModeInterruptFall,
|
||||
[JsGpioInModeInterrupt | JsGpioEdgeBoth] = GpioModeInterruptRiseFall,
|
||||
[JsGpioInModeEvent | JsGpioEdgeRising] = GpioModeEventRise,
|
||||
[JsGpioInModeEvent | JsGpioEdgeFalling] = GpioModeEventFall,
|
||||
[JsGpioInModeEvent | JsGpioEdgeBoth] = GpioModeEventRiseFall,
|
||||
};
|
||||
mode = js_gpio_in_mode_lut[in_mode | edge];
|
||||
}
|
||||
|
||||
// convert pull
|
||||
GpioPull pull_mode;
|
||||
if(!pull) {
|
||||
pull_mode = GpioPullNo;
|
||||
} else if(strcmp(pull, "up") == 0) {
|
||||
pull_mode = GpioPullUp;
|
||||
} else if(strcmp(pull, "down") == 0) {
|
||||
pull_mode = GpioPullDown;
|
||||
} else {
|
||||
JS_ERROR_AND_RETURN(mjs, MJS_BAD_ARGS_ERROR, "invalid pull");
|
||||
}
|
||||
|
||||
// init GPIO
|
||||
JsGpioPinInst* manager_data = JS_GET_CONTEXT(mjs);
|
||||
furi_hal_gpio_init(manager_data->pin, mode, pull_mode, GpioSpeedVeryHigh);
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
furi_hal_gpio_init(manager_data->pin, mode, pull, GpioSpeedVeryHigh);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -146,8 +177,13 @@ static void js_gpio_init(struct mjs* mjs) {
|
||||
* ```
|
||||
*/
|
||||
static void js_gpio_write(struct mjs* mjs) {
|
||||
static const JsValueDeclaration js_gpio_write_arg_list[] = {
|
||||
JS_VALUE_SIMPLE(JsValueTypeBool),
|
||||
};
|
||||
static const JsValueArguments js_gpio_write_args = JS_VALUE_ARGS(js_gpio_write_arg_list);
|
||||
bool level;
|
||||
JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_BOOL(&level));
|
||||
JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_gpio_write_args, &level);
|
||||
|
||||
JsGpioPinInst* manager_data = JS_GET_CONTEXT(mjs);
|
||||
furi_hal_gpio_write(manager_data->pin, level);
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
@@ -261,9 +297,16 @@ static void js_gpio_is_pwm_supported(struct mjs* mjs) {
|
||||
* ```
|
||||
*/
|
||||
static void js_gpio_pwm_write(struct mjs* mjs) {
|
||||
JsGpioPinInst* manager_data = JS_GET_CONTEXT(mjs);
|
||||
static const JsValueDeclaration js_gpio_pwm_write_arg_list[] = {
|
||||
JS_VALUE_SIMPLE(JsValueTypeInt32),
|
||||
JS_VALUE_SIMPLE(JsValueTypeInt32),
|
||||
};
|
||||
static const JsValueArguments js_gpio_pwm_write_args =
|
||||
JS_VALUE_ARGS(js_gpio_pwm_write_arg_list);
|
||||
int32_t frequency, duty;
|
||||
JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_INT32(&frequency), JS_ARG_INT32(&duty));
|
||||
JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_gpio_pwm_write_args, &frequency, &duty);
|
||||
|
||||
JsGpioPinInst* manager_data = JS_GET_CONTEXT(mjs);
|
||||
if(manager_data->pwm_output == FuriHalPwmOutputIdNone) {
|
||||
JS_ERROR_AND_RETURN(mjs, MJS_BAD_ARGS_ERROR, "PWM is not supported on this pin");
|
||||
}
|
||||
@@ -326,8 +369,13 @@ static void js_gpio_pwm_stop(struct mjs* mjs) {
|
||||
* ```
|
||||
*/
|
||||
static void js_gpio_get(struct mjs* mjs) {
|
||||
static const JsValueDeclaration js_gpio_get_arg_list[] = {
|
||||
JS_VALUE_SIMPLE(JsValueTypeAny),
|
||||
};
|
||||
static const JsValueArguments js_gpio_get_args = JS_VALUE_ARGS(js_gpio_get_arg_list);
|
||||
mjs_val_t name_arg;
|
||||
JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_ANY(&name_arg));
|
||||
JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_gpio_get_args, &name_arg);
|
||||
|
||||
const char* name_string = mjs_get_string(mjs, &name_arg, NULL);
|
||||
const GpioPinRecord* pin_record = NULL;
|
||||
|
||||
|
||||
@@ -3,8 +3,14 @@
|
||||
#include <assets_icons.h>
|
||||
|
||||
static void js_gui_file_picker_pick_file(struct mjs* mjs) {
|
||||
static const JsValueDeclaration js_picker_arg_list[] = {
|
||||
JS_VALUE_SIMPLE(JsValueTypeString),
|
||||
JS_VALUE_SIMPLE(JsValueTypeString),
|
||||
};
|
||||
static const JsValueArguments js_picker_args = JS_VALUE_ARGS(js_picker_arg_list);
|
||||
|
||||
const char *base_path, *extension;
|
||||
JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_STR(&base_path), JS_ARG_STR(&extension));
|
||||
JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_picker_args, &base_path, &extension);
|
||||
|
||||
DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS);
|
||||
const DialogsFileBrowserOptions browser_options = {
|
||||
|
||||
@@ -39,9 +39,14 @@ typedef struct {
|
||||
FxbmIconWrapperList_t fxbm_list;
|
||||
} JsGuiIconInst;
|
||||
|
||||
static const JsValueDeclaration js_icon_get_arg_list[] = {
|
||||
JS_VALUE_SIMPLE(JsValueTypeString),
|
||||
};
|
||||
static const JsValueArguments js_icon_get_args = JS_VALUE_ARGS(js_icon_get_arg_list);
|
||||
|
||||
static void js_gui_icon_get_builtin(struct mjs* mjs) {
|
||||
const char* icon_name;
|
||||
JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_STR(&icon_name));
|
||||
JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_icon_get_args, &icon_name);
|
||||
|
||||
for(size_t i = 0; i < COUNT_OF(builtin_icons); i++) {
|
||||
if(strcmp(icon_name, builtin_icons[i].name) == 0) {
|
||||
@@ -55,7 +60,7 @@ static void js_gui_icon_get_builtin(struct mjs* mjs) {
|
||||
|
||||
static void js_gui_icon_load_fxbm(struct mjs* mjs) {
|
||||
const char* fxbm_path;
|
||||
JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_STR(&fxbm_path));
|
||||
JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_icon_get_args, &fxbm_path);
|
||||
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
File* file = storage_file_alloc(storage);
|
||||
|
||||
@@ -68,8 +68,14 @@ static bool js_gui_vd_nav_callback(void* context) {
|
||||
* @brief `viewDispatcher.sendCustom`
|
||||
*/
|
||||
static void js_gui_vd_send_custom(struct mjs* mjs) {
|
||||
static const JsValueDeclaration js_gui_vd_send_custom_arg_list[] = {
|
||||
JS_VALUE_SIMPLE(JsValueTypeInt32),
|
||||
};
|
||||
static const JsValueArguments js_gui_vd_send_custom_args =
|
||||
JS_VALUE_ARGS(js_gui_vd_send_custom_arg_list);
|
||||
|
||||
int32_t event;
|
||||
JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_INT32(&event));
|
||||
JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_gui_vd_send_custom_args, &event);
|
||||
|
||||
JsGui* module = JS_GET_CONTEXT(mjs);
|
||||
view_dispatcher_send_custom_event(module->dispatcher, (uint32_t)event);
|
||||
@@ -79,15 +85,25 @@ static void js_gui_vd_send_custom(struct mjs* mjs) {
|
||||
* @brief `viewDispatcher.sendTo`
|
||||
*/
|
||||
static void js_gui_vd_send_to(struct mjs* mjs) {
|
||||
enum {
|
||||
SendDirToFront,
|
||||
SendDirToBack,
|
||||
} send_direction;
|
||||
JS_ENUM_MAP(send_direction, {"front", SendDirToFront}, {"back", SendDirToBack});
|
||||
JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_ENUM(send_direction, "SendDirection"));
|
||||
typedef enum {
|
||||
JsSendDirToFront,
|
||||
JsSendDirToBack,
|
||||
} JsSendDir;
|
||||
static const JsValueEnumVariant js_send_dir_variants[] = {
|
||||
{"front", JsSendDirToFront},
|
||||
{"back", JsSendDirToBack},
|
||||
};
|
||||
static const JsValueDeclaration js_gui_vd_send_to_arg_list[] = {
|
||||
JS_VALUE_ENUM(JsSendDir, js_send_dir_variants),
|
||||
};
|
||||
static const JsValueArguments js_gui_vd_send_to_args =
|
||||
JS_VALUE_ARGS(js_gui_vd_send_to_arg_list);
|
||||
|
||||
JsSendDir send_direction;
|
||||
JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_gui_vd_send_to_args, &send_direction);
|
||||
|
||||
JsGui* module = JS_GET_CONTEXT(mjs);
|
||||
if(send_direction == SendDirToBack) {
|
||||
if(send_direction == JsSendDirToBack) {
|
||||
view_dispatcher_send_to_back(module->dispatcher);
|
||||
} else {
|
||||
view_dispatcher_send_to_front(module->dispatcher);
|
||||
@@ -98,8 +114,15 @@ static void js_gui_vd_send_to(struct mjs* mjs) {
|
||||
* @brief `viewDispatcher.switchTo`
|
||||
*/
|
||||
static void js_gui_vd_switch_to(struct mjs* mjs) {
|
||||
static const JsValueDeclaration js_gui_vd_switch_to_arg_list[] = {
|
||||
JS_VALUE_SIMPLE(JsValueTypeAny),
|
||||
};
|
||||
static const JsValueArguments js_gui_vd_switch_to_args =
|
||||
JS_VALUE_ARGS(js_gui_vd_switch_to_arg_list);
|
||||
|
||||
mjs_val_t view;
|
||||
JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_OBJ(&view));
|
||||
JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_gui_vd_switch_to_args, &view);
|
||||
|
||||
JsGuiViewData* view_data = JS_GET_INST(mjs, view);
|
||||
mjs_val_t vd_obj = mjs_get_this(mjs);
|
||||
JsGui* module = JS_GET_INST(mjs, vd_obj);
|
||||
@@ -267,9 +290,16 @@ static bool
|
||||
* @brief `View.set`
|
||||
*/
|
||||
static void js_gui_view_set(struct mjs* mjs) {
|
||||
static const JsValueDeclaration js_gui_view_set_arg_list[] = {
|
||||
JS_VALUE_SIMPLE(JsValueTypeString),
|
||||
JS_VALUE_SIMPLE(JsValueTypeAny),
|
||||
};
|
||||
static const JsValueArguments js_gui_view_set_args = JS_VALUE_ARGS(js_gui_view_set_arg_list);
|
||||
|
||||
const char* name;
|
||||
mjs_val_t value;
|
||||
JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_STR(&name), JS_ARG_ANY(&value));
|
||||
JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_gui_view_set_args, &name, &value);
|
||||
|
||||
JsGuiViewData* data = JS_GET_CONTEXT(mjs);
|
||||
bool success = js_gui_view_assign(mjs, name, value, data);
|
||||
UNUSED(success);
|
||||
@@ -280,12 +310,19 @@ static void js_gui_view_set(struct mjs* mjs) {
|
||||
* @brief `View.addChild`
|
||||
*/
|
||||
static void js_gui_view_add_child(struct mjs* mjs) {
|
||||
static const JsValueDeclaration js_gui_view_add_child_arg_list[] = {
|
||||
JS_VALUE_SIMPLE(JsValueTypeAny),
|
||||
};
|
||||
static const JsValueArguments js_gui_view_add_child_args =
|
||||
JS_VALUE_ARGS(js_gui_view_add_child_arg_list);
|
||||
|
||||
mjs_val_t child;
|
||||
JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_gui_view_add_child_args, &child);
|
||||
|
||||
JsGuiViewData* data = JS_GET_CONTEXT(mjs);
|
||||
if(!data->descriptor->add_child || !data->descriptor->reset_children)
|
||||
JS_ERROR_AND_RETURN(mjs, MJS_BAD_ARGS_ERROR, "this View can't have children");
|
||||
|
||||
mjs_val_t child;
|
||||
JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_ANY(&child));
|
||||
bool success = data->descriptor->add_child(mjs, data->specific_view, data->custom_data, child);
|
||||
UNUSED(success);
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
@@ -307,12 +344,19 @@ static void js_gui_view_reset_children(struct mjs* mjs) {
|
||||
* @brief `View.setChildren`
|
||||
*/
|
||||
static void js_gui_view_set_children(struct mjs* mjs) {
|
||||
static const JsValueDeclaration js_gui_view_set_children_arg_list[] = {
|
||||
JS_VALUE_SIMPLE(JsValueTypeAnyArray),
|
||||
};
|
||||
static const JsValueArguments js_gui_view_set_children_args =
|
||||
JS_VALUE_ARGS(js_gui_view_set_children_arg_list);
|
||||
|
||||
mjs_val_t children;
|
||||
JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_gui_view_set_children_args, &children);
|
||||
|
||||
JsGuiViewData* data = JS_GET_CONTEXT(mjs);
|
||||
if(!data->descriptor->add_child || !data->descriptor->reset_children)
|
||||
JS_ERROR_AND_RETURN(mjs, MJS_BAD_ARGS_ERROR, "this View can't have children");
|
||||
|
||||
mjs_val_t children;
|
||||
JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_ARR(&children));
|
||||
js_gui_view_internal_set_children(mjs, children, data);
|
||||
}
|
||||
|
||||
@@ -365,7 +409,6 @@ static mjs_val_t js_gui_make_view(struct mjs* mjs, const JsViewDescriptor* descr
|
||||
* @brief `ViewFactory.make`
|
||||
*/
|
||||
static void js_gui_vf_make(struct mjs* mjs) {
|
||||
JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY); // 0 args
|
||||
const JsViewDescriptor* descriptor = JS_GET_CONTEXT(mjs);
|
||||
mjs_return(mjs, js_gui_make_view(mjs, descriptor));
|
||||
}
|
||||
@@ -374,8 +417,15 @@ static void js_gui_vf_make(struct mjs* mjs) {
|
||||
* @brief `ViewFactory.makeWith`
|
||||
*/
|
||||
static void js_gui_vf_make_with(struct mjs* mjs) {
|
||||
mjs_val_t props;
|
||||
JS_FETCH_ARGS_OR_RETURN(mjs, JS_AT_LEAST, JS_ARG_OBJ(&props));
|
||||
static const JsValueDeclaration js_gui_vf_make_with_arg_list[] = {
|
||||
JS_VALUE_SIMPLE(JsValueTypeAnyObject),
|
||||
JS_VALUE_SIMPLE(JsValueTypeAny),
|
||||
};
|
||||
static const JsValueArguments js_gui_vf_make_with_args =
|
||||
JS_VALUE_ARGS(js_gui_vf_make_with_arg_list);
|
||||
|
||||
mjs_val_t props, children;
|
||||
JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_gui_vf_make_with_args, &props, &children);
|
||||
const JsViewDescriptor* descriptor = JS_GET_CONTEXT(mjs);
|
||||
|
||||
// make the object like normal
|
||||
@@ -396,14 +446,10 @@ static void js_gui_vf_make_with(struct mjs* mjs) {
|
||||
}
|
||||
|
||||
// assign children
|
||||
if(mjs_nargs(mjs) >= 2) {
|
||||
if(mjs_is_array(children)) {
|
||||
if(!data->descriptor->add_child || !data->descriptor->reset_children)
|
||||
JS_ERROR_AND_RETURN(mjs, MJS_BAD_ARGS_ERROR, "this View can't have children");
|
||||
|
||||
mjs_val_t children = mjs_arg(mjs, 1);
|
||||
if(!mjs_is_array(children))
|
||||
JS_ERROR_AND_RETURN(mjs, MJS_BAD_ARGS_ERROR, "argument 1: expected array");
|
||||
|
||||
if(!js_gui_view_internal_set_children(mjs, children, data)) return;
|
||||
}
|
||||
|
||||
|
||||
@@ -35,60 +35,62 @@ static void
|
||||
}
|
||||
|
||||
static void js_serial_setup(struct mjs* mjs) {
|
||||
FuriHalSerialId serial_id;
|
||||
int32_t baudrate;
|
||||
JS_ENUM_MAP(serial_id, {"lpuart", FuriHalSerialIdLpuart}, {"usart", FuriHalSerialIdUsart});
|
||||
JS_FETCH_ARGS_OR_RETURN(
|
||||
mjs, JS_AT_LEAST, JS_ARG_ENUM(serial_id, "SerialId"), JS_ARG_INT32(&baudrate));
|
||||
static const JsValueEnumVariant js_serial_id_variants[] = {
|
||||
{"lpuart", FuriHalSerialIdLpuart},
|
||||
{"usart", FuriHalSerialIdUsart},
|
||||
};
|
||||
|
||||
FuriHalSerialDataBits data_bits = FuriHalSerialDataBits8;
|
||||
FuriHalSerialParity parity = FuriHalSerialParityNone;
|
||||
FuriHalSerialStopBits stop_bits = FuriHalSerialStopBits1;
|
||||
if(mjs_nargs(mjs) > 2) {
|
||||
struct framing {
|
||||
mjs_val_t data_bits;
|
||||
mjs_val_t parity;
|
||||
mjs_val_t stop_bits;
|
||||
} framing;
|
||||
JS_OBJ_MAP(
|
||||
framing,
|
||||
{"dataBits", offsetof(struct framing, data_bits)},
|
||||
{"parity", offsetof(struct framing, parity)},
|
||||
{"stopBits", offsetof(struct framing, stop_bits)});
|
||||
JS_ENUM_MAP(
|
||||
data_bits,
|
||||
static const JsValueEnumVariant js_serial_data_bit_variants[] = {
|
||||
{"6", FuriHalSerialDataBits6},
|
||||
{"7", FuriHalSerialDataBits7},
|
||||
{"8", FuriHalSerialDataBits8},
|
||||
{"9", FuriHalSerialDataBits9});
|
||||
JS_ENUM_MAP(
|
||||
parity,
|
||||
{"9", FuriHalSerialDataBits9},
|
||||
};
|
||||
static const JsValueDeclaration js_serial_data_bits = JS_VALUE_ENUM_W_DEFAULT(
|
||||
FuriHalSerialDataBits, js_serial_data_bit_variants, FuriHalSerialDataBits8);
|
||||
|
||||
static const JsValueEnumVariant js_serial_parity_variants[] = {
|
||||
{"none", FuriHalSerialParityNone},
|
||||
{"even", FuriHalSerialParityEven},
|
||||
{"odd", FuriHalSerialParityOdd});
|
||||
JS_ENUM_MAP(
|
||||
stop_bits,
|
||||
{"odd", FuriHalSerialParityOdd},
|
||||
};
|
||||
static const JsValueDeclaration js_serial_parity = JS_VALUE_ENUM_W_DEFAULT(
|
||||
FuriHalSerialParity, js_serial_parity_variants, FuriHalSerialParityNone);
|
||||
|
||||
static const JsValueEnumVariant js_serial_stop_bit_variants[] = {
|
||||
{"0.5", FuriHalSerialStopBits0_5},
|
||||
{"1", FuriHalSerialStopBits1},
|
||||
{"1.5", FuriHalSerialStopBits1_5},
|
||||
{"2", FuriHalSerialStopBits2});
|
||||
mjs_val_t framing_obj = mjs_arg(mjs, 2);
|
||||
JS_CONVERT_OR_RETURN(mjs, &framing_obj, JS_ARG_OBJECT(framing, "Framing"), "argument 2");
|
||||
JS_CONVERT_OR_RETURN(
|
||||
mjs, &framing.data_bits, JS_ARG_ENUM(data_bits, "DataBits"), "argument 2: dataBits");
|
||||
JS_CONVERT_OR_RETURN(
|
||||
mjs, &framing.parity, JS_ARG_ENUM(parity, "Parity"), "argument 2: parity");
|
||||
JS_CONVERT_OR_RETURN(
|
||||
mjs, &framing.stop_bits, JS_ARG_ENUM(stop_bits, "StopBits"), "argument 2: stopBits");
|
||||
}
|
||||
{"2", FuriHalSerialStopBits2},
|
||||
};
|
||||
static const JsValueDeclaration js_serial_stop_bits = JS_VALUE_ENUM_W_DEFAULT(
|
||||
FuriHalSerialStopBits, js_serial_stop_bit_variants, FuriHalSerialStopBits1);
|
||||
|
||||
static const JsValueObjectField js_serial_framing_fields[] = {
|
||||
{"dataBits", &js_serial_data_bits},
|
||||
{"parity", &js_serial_parity},
|
||||
{"stopBits", &js_serial_stop_bits},
|
||||
};
|
||||
|
||||
static const JsValueDeclaration js_serial_setup_arg_list[] = {
|
||||
JS_VALUE_ENUM(FuriHalSerialId, js_serial_id_variants),
|
||||
JS_VALUE_SIMPLE(JsValueTypeInt32),
|
||||
JS_VALUE_OBJECT_W_DEFAULTS(js_serial_framing_fields),
|
||||
};
|
||||
static const JsValueArguments js_serial_setup_args = JS_VALUE_ARGS(js_serial_setup_arg_list);
|
||||
|
||||
FuriHalSerialId serial_id;
|
||||
int32_t baudrate;
|
||||
FuriHalSerialDataBits data_bits = FuriHalSerialDataBits8;
|
||||
FuriHalSerialParity parity = FuriHalSerialParityNone;
|
||||
FuriHalSerialStopBits stop_bits = FuriHalSerialStopBits1;
|
||||
JS_VALUE_PARSE_ARGS_OR_RETURN(
|
||||
mjs, &js_serial_setup_args, &serial_id, &baudrate, &data_bits, &parity, &stop_bits);
|
||||
|
||||
JsSerialInst* serial = JS_GET_CONTEXT(mjs);
|
||||
|
||||
if(serial->setup_done) {
|
||||
mjs_prepend_errorf(mjs, MJS_INTERNAL_ERROR, "Serial is already configured");
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
return;
|
||||
}
|
||||
if(serial->setup_done)
|
||||
JS_ERROR_AND_RETURN(mjs, MJS_INTERNAL_ERROR, "Serial is already configured");
|
||||
|
||||
expansion_disable(furi_record_open(RECORD_EXPANSION));
|
||||
furi_record_close(RECORD_EXPANSION);
|
||||
@@ -123,28 +125,20 @@ static void js_serial_deinit(JsSerialInst* js_serial) {
|
||||
}
|
||||
|
||||
static void js_serial_end(struct mjs* mjs) {
|
||||
mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0);
|
||||
JsSerialInst* serial = mjs_get_ptr(mjs, obj_inst);
|
||||
JsSerialInst* serial = JS_GET_CONTEXT(mjs);
|
||||
furi_assert(serial);
|
||||
|
||||
if(!serial->setup_done) {
|
||||
mjs_prepend_errorf(mjs, MJS_INTERNAL_ERROR, "Serial is not configured");
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
return;
|
||||
}
|
||||
if(!serial->setup_done)
|
||||
JS_ERROR_AND_RETURN(mjs, MJS_INTERNAL_ERROR, "Serial is not configured");
|
||||
|
||||
js_serial_deinit(serial);
|
||||
}
|
||||
|
||||
static void js_serial_write(struct mjs* mjs) {
|
||||
mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0);
|
||||
JsSerialInst* serial = mjs_get_ptr(mjs, obj_inst);
|
||||
JsSerialInst* serial = JS_GET_CONTEXT(mjs);
|
||||
furi_assert(serial);
|
||||
if(!serial->setup_done) {
|
||||
mjs_prepend_errorf(mjs, MJS_INTERNAL_ERROR, "Serial is not configured");
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
return;
|
||||
}
|
||||
if(!serial->setup_done)
|
||||
JS_ERROR_AND_RETURN(mjs, MJS_INTERNAL_ERROR, "Serial is not configured");
|
||||
|
||||
bool args_correct = true;
|
||||
|
||||
@@ -228,43 +222,20 @@ static size_t js_serial_receive(JsSerialInst* serial, char* buf, size_t len, uin
|
||||
return bytes_read;
|
||||
}
|
||||
|
||||
static const JsValueDeclaration js_serial_read_arg_list[] = {
|
||||
JS_VALUE_SIMPLE(JsValueTypeInt32),
|
||||
JS_VALUE_SIMPLE_W_DEFAULT(JsValueTypeInt32, int32_val, INT32_MAX),
|
||||
};
|
||||
static const JsValueArguments js_serial_read_args = JS_VALUE_ARGS(js_serial_read_arg_list);
|
||||
|
||||
static void js_serial_read(struct mjs* mjs) {
|
||||
mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0);
|
||||
JsSerialInst* serial = mjs_get_ptr(mjs, obj_inst);
|
||||
JsSerialInst* serial = JS_GET_CONTEXT(mjs);
|
||||
furi_assert(serial);
|
||||
if(!serial->setup_done) {
|
||||
mjs_prepend_errorf(mjs, MJS_INTERNAL_ERROR, "Serial is not configured");
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
return;
|
||||
}
|
||||
if(!serial->setup_done)
|
||||
JS_ERROR_AND_RETURN(mjs, MJS_INTERNAL_ERROR, "Serial is not configured");
|
||||
|
||||
size_t read_len = 0;
|
||||
uint32_t timeout = FuriWaitForever;
|
||||
|
||||
do {
|
||||
size_t num_args = mjs_nargs(mjs);
|
||||
if(num_args == 1) {
|
||||
mjs_val_t arg = mjs_arg(mjs, 0);
|
||||
if(!mjs_is_number(arg)) {
|
||||
break;
|
||||
}
|
||||
read_len = mjs_get_int32(mjs, arg);
|
||||
} else if(num_args == 2) {
|
||||
mjs_val_t len_arg = mjs_arg(mjs, 0);
|
||||
mjs_val_t timeout_arg = mjs_arg(mjs, 1);
|
||||
if((!mjs_is_number(len_arg)) || (!mjs_is_number(timeout_arg))) {
|
||||
break;
|
||||
}
|
||||
read_len = mjs_get_int32(mjs, len_arg);
|
||||
timeout = mjs_get_int32(mjs, timeout_arg);
|
||||
}
|
||||
} while(0);
|
||||
|
||||
if(read_len == 0) {
|
||||
mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, "");
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
return;
|
||||
}
|
||||
int32_t read_len, timeout;
|
||||
JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_serial_read_args, &read_len, &timeout);
|
||||
|
||||
char* read_buf = malloc(read_len);
|
||||
size_t bytes_read = js_serial_receive(serial, read_buf, read_len, timeout);
|
||||
@@ -278,37 +249,19 @@ static void js_serial_read(struct mjs* mjs) {
|
||||
}
|
||||
|
||||
static void js_serial_readln(struct mjs* mjs) {
|
||||
mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0);
|
||||
JsSerialInst* serial = mjs_get_ptr(mjs, obj_inst);
|
||||
JsSerialInst* serial = JS_GET_CONTEXT(mjs);
|
||||
furi_assert(serial);
|
||||
if(!serial->setup_done) {
|
||||
mjs_prepend_errorf(mjs, MJS_INTERNAL_ERROR, "Serial is not configured");
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
return;
|
||||
}
|
||||
if(!serial->setup_done)
|
||||
JS_ERROR_AND_RETURN(mjs, MJS_INTERNAL_ERROR, "Serial is not configured");
|
||||
|
||||
bool args_correct = false;
|
||||
uint32_t timeout = FuriWaitForever;
|
||||
static const JsValueDeclaration js_serial_readln_arg_list[] = {
|
||||
JS_VALUE_SIMPLE(JsValueTypeInt32),
|
||||
};
|
||||
static const JsValueArguments js_serial_readln_args = JS_VALUE_ARGS(js_serial_readln_arg_list);
|
||||
|
||||
do {
|
||||
size_t num_args = mjs_nargs(mjs);
|
||||
if(num_args > 1) {
|
||||
break;
|
||||
} else if(num_args == 1) {
|
||||
mjs_val_t arg = mjs_arg(mjs, 0);
|
||||
if(!mjs_is_number(arg)) {
|
||||
break;
|
||||
}
|
||||
timeout = mjs_get_int32(mjs, arg);
|
||||
}
|
||||
args_correct = true;
|
||||
} while(0);
|
||||
int32_t timeout;
|
||||
JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_serial_readln_args, &timeout);
|
||||
|
||||
if(!args_correct) {
|
||||
mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, "");
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
return;
|
||||
}
|
||||
FuriString* rx_buf = furi_string_alloc();
|
||||
size_t bytes_read = 0;
|
||||
char read_char = 0;
|
||||
@@ -335,42 +288,13 @@ static void js_serial_readln(struct mjs* mjs) {
|
||||
}
|
||||
|
||||
static void js_serial_read_bytes(struct mjs* mjs) {
|
||||
mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0);
|
||||
JsSerialInst* serial = mjs_get_ptr(mjs, obj_inst);
|
||||
JsSerialInst* serial = JS_GET_CONTEXT(mjs);
|
||||
furi_assert(serial);
|
||||
if(!serial->setup_done) {
|
||||
mjs_prepend_errorf(mjs, MJS_INTERNAL_ERROR, "Serial is not configured");
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
return;
|
||||
}
|
||||
if(!serial->setup_done)
|
||||
JS_ERROR_AND_RETURN(mjs, MJS_INTERNAL_ERROR, "Serial is not configured");
|
||||
|
||||
size_t read_len = 0;
|
||||
uint32_t timeout = FuriWaitForever;
|
||||
|
||||
do {
|
||||
size_t num_args = mjs_nargs(mjs);
|
||||
if(num_args == 1) {
|
||||
mjs_val_t arg = mjs_arg(mjs, 0);
|
||||
if(!mjs_is_number(arg)) {
|
||||
break;
|
||||
}
|
||||
read_len = mjs_get_int32(mjs, arg);
|
||||
} else if(num_args == 2) {
|
||||
mjs_val_t len_arg = mjs_arg(mjs, 0);
|
||||
mjs_val_t timeout_arg = mjs_arg(mjs, 1);
|
||||
if((!mjs_is_number(len_arg)) || (!mjs_is_number(timeout_arg))) {
|
||||
break;
|
||||
}
|
||||
read_len = mjs_get_int32(mjs, len_arg);
|
||||
timeout = mjs_get_int32(mjs, timeout_arg);
|
||||
}
|
||||
} while(0);
|
||||
|
||||
if(read_len == 0) {
|
||||
mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, "");
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
return;
|
||||
}
|
||||
int32_t read_len, timeout;
|
||||
JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_serial_read_args, &read_len, &timeout);
|
||||
|
||||
char* read_buf = malloc(read_len);
|
||||
size_t bytes_read = js_serial_receive(serial, read_buf, read_len, timeout);
|
||||
@@ -399,27 +323,19 @@ static char* js_serial_receive_any(JsSerialInst* serial, size_t* len, uint32_t t
|
||||
}
|
||||
|
||||
static void js_serial_read_any(struct mjs* mjs) {
|
||||
mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0);
|
||||
JsSerialInst* serial = mjs_get_ptr(mjs, obj_inst);
|
||||
JsSerialInst* serial = JS_GET_CONTEXT(mjs);
|
||||
furi_assert(serial);
|
||||
if(!serial->setup_done) {
|
||||
mjs_prepend_errorf(mjs, MJS_INTERNAL_ERROR, "Serial is not configured");
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
return;
|
||||
}
|
||||
if(!serial->setup_done)
|
||||
JS_ERROR_AND_RETURN(mjs, MJS_INTERNAL_ERROR, "Serial is not configured");
|
||||
|
||||
uint32_t timeout = FuriWaitForever;
|
||||
static const JsValueDeclaration js_serial_read_any_arg_list[] = {
|
||||
JS_VALUE_SIMPLE_W_DEFAULT(JsValueTypeInt32, int32_val, INT32_MAX),
|
||||
};
|
||||
static const JsValueArguments js_serial_read_any_args =
|
||||
JS_VALUE_ARGS(js_serial_read_any_arg_list);
|
||||
|
||||
do {
|
||||
size_t num_args = mjs_nargs(mjs);
|
||||
if(num_args == 1) {
|
||||
mjs_val_t timeout_arg = mjs_arg(mjs, 0);
|
||||
if(!mjs_is_number(timeout_arg)) {
|
||||
break;
|
||||
}
|
||||
timeout = mjs_get_int32(mjs, timeout_arg);
|
||||
}
|
||||
} while(0);
|
||||
int32_t timeout;
|
||||
JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_serial_read_any_args, &timeout);
|
||||
|
||||
size_t bytes_read = 0;
|
||||
char* read_buf = js_serial_receive_any(serial, &bytes_read, timeout);
|
||||
@@ -663,16 +579,19 @@ static void* js_serial_create(struct mjs* mjs, mjs_val_t* object, JsModules* mod
|
||||
UNUSED(modules);
|
||||
JsSerialInst* js_serial = malloc(sizeof(JsSerialInst));
|
||||
js_serial->mjs = mjs;
|
||||
|
||||
mjs_val_t serial_obj = mjs_mk_object(mjs);
|
||||
mjs_set(mjs, serial_obj, INST_PROP_NAME, ~0, mjs_mk_foreign(mjs, js_serial));
|
||||
mjs_set(mjs, serial_obj, "setup", ~0, MJS_MK_FN(js_serial_setup));
|
||||
mjs_set(mjs, serial_obj, "end", ~0, MJS_MK_FN(js_serial_end));
|
||||
mjs_set(mjs, serial_obj, "write", ~0, MJS_MK_FN(js_serial_write));
|
||||
mjs_set(mjs, serial_obj, "read", ~0, MJS_MK_FN(js_serial_read));
|
||||
mjs_set(mjs, serial_obj, "readln", ~0, MJS_MK_FN(js_serial_readln));
|
||||
mjs_set(mjs, serial_obj, "readBytes", ~0, MJS_MK_FN(js_serial_read_bytes));
|
||||
mjs_set(mjs, serial_obj, "readAny", ~0, MJS_MK_FN(js_serial_read_any));
|
||||
mjs_set(mjs, serial_obj, "expect", ~0, MJS_MK_FN(js_serial_expect));
|
||||
JS_ASSIGN_MULTI(mjs, serial_obj) {
|
||||
JS_FIELD(INST_PROP_NAME, mjs_mk_foreign(mjs, js_serial));
|
||||
JS_FIELD("setup", MJS_MK_FN(js_serial_setup));
|
||||
JS_FIELD("end", MJS_MK_FN(js_serial_end));
|
||||
JS_FIELD("write", MJS_MK_FN(js_serial_write));
|
||||
JS_FIELD("read", MJS_MK_FN(js_serial_read));
|
||||
JS_FIELD("readln", MJS_MK_FN(js_serial_readln));
|
||||
JS_FIELD("readBytes", MJS_MK_FN(js_serial_read_bytes));
|
||||
JS_FIELD("readAny", MJS_MK_FN(js_serial_read_any));
|
||||
JS_FIELD("expect", MJS_MK_FN(js_serial_expect));
|
||||
}
|
||||
*object = serial_obj;
|
||||
|
||||
return js_serial;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user