|
@@ -1,115 +1,149 @@
|
|
|
#include "cli_control.h"
|
|
#include "cli_control.h"
|
|
|
|
|
|
|
|
|
|
+#include <FreeRTOS.h>
|
|
|
#include <cli/cli.h>
|
|
#include <cli/cli.h>
|
|
|
#include <cli/cli_i.h>
|
|
#include <cli/cli_i.h>
|
|
|
#include <cli/cli_vcp.h>
|
|
#include <cli/cli_vcp.h>
|
|
|
-#include <furi/core/thread_i.h>
|
|
|
|
|
-#include "cligui_main_i.h"
|
|
|
|
|
|
|
+#include <loader/loader.h>
|
|
|
#include <loader/loader_i.h>
|
|
#include <loader/loader_i.h>
|
|
|
-#include <FreeRTOS.h>
|
|
|
|
|
|
|
|
|
|
-volatile bool gotCallbackSet = false;
|
|
|
|
|
|
|
+FuriStreamBuffer* cli_tx_stream = NULL;
|
|
|
|
|
+FuriStreamBuffer* cli_rx_stream = NULL;
|
|
|
|
|
+
|
|
|
|
|
+static volatile bool restore_tx_stdout = false;
|
|
|
|
|
|
|
|
-FuriStreamBuffer* tx_stream;
|
|
|
|
|
-FuriStreamBuffer* rx_stream;
|
|
|
|
|
-static FuriThread* volatile cliThread = NULL;
|
|
|
|
|
-static FuriThread* prev_appthread = NULL;
|
|
|
|
|
-static void tx_handler_stdout(const char* buffer, size_t size) {
|
|
|
|
|
- furi_stream_buffer_send(tx_stream, buffer, size, FuriWaitForever);
|
|
|
|
|
-}
|
|
|
|
|
static void tx_handler(const uint8_t* buffer, size_t size) {
|
|
static void tx_handler(const uint8_t* buffer, size_t size) {
|
|
|
- furi_thread_set_stdout_callback(tx_handler_stdout);
|
|
|
|
|
- cliThread = furi_thread_get_current();
|
|
|
|
|
- furi_stream_buffer_send(tx_stream, buffer, size, FuriWaitForever);
|
|
|
|
|
|
|
+ furi_stream_buffer_send(cli_tx_stream, buffer, size, FuriWaitForever);
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+static void tx_handler_stdout(const char* buffer, size_t size) {
|
|
|
|
|
+ tx_handler((const uint8_t*)buffer, size);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
static size_t real_rx_handler(uint8_t* buffer, size_t size, uint32_t timeout) {
|
|
static size_t real_rx_handler(uint8_t* buffer, size_t size, uint32_t timeout) {
|
|
|
size_t rx_cnt = 0;
|
|
size_t rx_cnt = 0;
|
|
|
while(size > 0) {
|
|
while(size > 0) {
|
|
|
size_t batch_size = size;
|
|
size_t batch_size = size;
|
|
|
if(batch_size > 128) batch_size = 128;
|
|
if(batch_size > 128) batch_size = 128;
|
|
|
- size_t len = furi_stream_buffer_receive(rx_stream, buffer, batch_size, timeout);
|
|
|
|
|
|
|
+ size_t len = furi_stream_buffer_receive(cli_rx_stream, buffer, batch_size, timeout);
|
|
|
if(len == 0) break;
|
|
if(len == 0) break;
|
|
|
size -= len;
|
|
size -= len;
|
|
|
buffer += len;
|
|
buffer += len;
|
|
|
rx_cnt += len;
|
|
rx_cnt += len;
|
|
|
}
|
|
}
|
|
|
|
|
+ if(restore_tx_stdout) {
|
|
|
|
|
+ furi_thread_set_stdout_callback(cli_vcp.tx_stdout);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ furi_thread_set_stdout_callback(tx_handler_stdout);
|
|
|
|
|
+ }
|
|
|
return rx_cnt;
|
|
return rx_cnt;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-static CliCommand* getCliCommand(Cli* cli, const char* name) {
|
|
|
|
|
- FuriString* target_command = furi_string_alloc();
|
|
|
|
|
- furi_string_set_str(target_command, name);
|
|
|
|
|
- CliCommand* command = CliCommandTree_get(cli->commands, target_command);
|
|
|
|
|
- furi_string_free(target_command);
|
|
|
|
|
- return command;
|
|
|
|
|
-}
|
|
|
|
|
|
|
+static CliSession* session;
|
|
|
|
|
|
|
|
static void session_init(void) {
|
|
static void session_init(void) {
|
|
|
}
|
|
}
|
|
|
static void session_deinit(void) {
|
|
static void session_deinit(void) {
|
|
|
|
|
+ free(session);
|
|
|
|
|
+ session = NULL;
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
static bool session_connected(void) {
|
|
static bool session_connected(void) {
|
|
|
return true;
|
|
return true;
|
|
|
}
|
|
}
|
|
|
-static CliSession session;
|
|
|
|
|
-void latch_tx_handler() {
|
|
|
|
|
|
|
+
|
|
|
|
|
+void clicontrol_hijack(size_t tx_size, size_t rx_size) {
|
|
|
|
|
+ if(cli_rx_stream != NULL && cli_tx_stream != NULL) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
Cli* global_cli = furi_record_open(RECORD_CLI);
|
|
Cli* global_cli = furi_record_open(RECORD_CLI);
|
|
|
|
|
|
|
|
- CliCommand* help_command = getCliCommand(global_cli, "help");
|
|
|
|
|
- cliThread = help_command->context;
|
|
|
|
|
|
|
+ cli_rx_stream = furi_stream_buffer_alloc(rx_size, 1);
|
|
|
|
|
+ cli_tx_stream = furi_stream_buffer_alloc(tx_size, 1);
|
|
|
|
|
|
|
|
- furi_thread_set_stdout_callback(tx_handler_stdout);
|
|
|
|
|
- if(cliThread != NULL) {
|
|
|
|
|
- cliThread->output.write_callback = &tx_handler_stdout;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ session = (CliSession*)malloc(sizeof(CliSession));
|
|
|
|
|
+ session->tx = &tx_handler;
|
|
|
|
|
+ session->rx = &real_rx_handler;
|
|
|
|
|
+ session->tx_stdout = &tx_handler_stdout;
|
|
|
|
|
+ session->init = &session_init;
|
|
|
|
|
+ session->deinit = &session_deinit;
|
|
|
|
|
+ session->is_connected = &session_connected;
|
|
|
|
|
|
|
|
- rx_stream = furi_stream_buffer_alloc(128, 1);
|
|
|
|
|
- tx_stream = furi_stream_buffer_alloc(128, 1);
|
|
|
|
|
|
|
+ CliCommandTree_it_t cmd_iterator;
|
|
|
|
|
+ for(CliCommandTree_it(cmd_iterator, global_cli->commands); !CliCommandTree_end_p(cmd_iterator);
|
|
|
|
|
+ CliCommandTree_next(cmd_iterator)) {
|
|
|
|
|
+ CliCommand* t = CliCommandTree_cref(cmd_iterator)->value_ptr;
|
|
|
|
|
+ // Move CliCommandFlagParallelSafe to another bit
|
|
|
|
|
+ t->flags ^=
|
|
|
|
|
+ ((t->flags & (CliCommandFlagParallelSafe << 8)) ^
|
|
|
|
|
+ ((t->flags & CliCommandFlagParallelSafe) << 8));
|
|
|
|
|
+ // Set parallel safe
|
|
|
|
|
+ t->flags |= CliCommandFlagParallelSafe;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- session.tx = &tx_handler;
|
|
|
|
|
- session.rx = &real_rx_handler;
|
|
|
|
|
- session.tx_stdout = &tx_handler_stdout;
|
|
|
|
|
- session.init = &session_init;
|
|
|
|
|
- session.deinit = &session_deinit;
|
|
|
|
|
- session.is_connected = &session_connected;
|
|
|
|
|
|
|
+ // Session switcharooney
|
|
|
|
|
+ FuriThreadStdoutWriteCallback prev_stdout = furi_thread_get_stdout_callback();
|
|
|
cli_session_close(global_cli);
|
|
cli_session_close(global_cli);
|
|
|
- cli_session_open(global_cli, &session);
|
|
|
|
|
- // Unlock loader-lock
|
|
|
|
|
- Loader* loader = furi_record_open(RECORD_LOADER);
|
|
|
|
|
- prev_appthread = loader->app.thread;
|
|
|
|
|
- loader->app.thread = NULL;
|
|
|
|
|
- furi_record_close(RECORD_LOADER);
|
|
|
|
|
|
|
+ restore_tx_stdout = false;
|
|
|
|
|
+ cli_session_open(global_cli, session);
|
|
|
|
|
+ furi_thread_set_stdout_callback(prev_stdout);
|
|
|
|
|
+
|
|
|
furi_record_close(RECORD_CLI);
|
|
furi_record_close(RECORD_CLI);
|
|
|
}
|
|
}
|
|
|
-void unlatch_tx_handler(bool persist) {
|
|
|
|
|
- Cli* global_cli = furi_record_open(RECORD_CLI);
|
|
|
|
|
- // Stash cliThread if not null
|
|
|
|
|
- if(cliThread != NULL) {
|
|
|
|
|
- CliCommand* help_command = getCliCommand(global_cli, "help");
|
|
|
|
|
- help_command->context = cliThread;
|
|
|
|
|
|
|
+
|
|
|
|
|
+void clicontrol_unhijack(bool persist) {
|
|
|
|
|
+ if(cli_rx_stream == NULL && cli_tx_stream == NULL) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Consume remaining tx data
|
|
|
|
|
+ if(furi_stream_buffer_bytes_available(cli_tx_stream) > 0) {
|
|
|
|
|
+ char sink = 0;
|
|
|
|
|
+ while(!furi_stream_buffer_is_empty(cli_tx_stream)) {
|
|
|
|
|
+ furi_stream_buffer_receive(cli_tx_stream, &sink, 1, FuriWaitForever);
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
- // Switch to new session
|
|
|
|
|
|
|
+
|
|
|
|
|
+ Cli* global_cli = furi_record_open(RECORD_CLI);
|
|
|
|
|
+
|
|
|
if(persist) {
|
|
if(persist) {
|
|
|
- // Use dummy debug firmware function as is_connected
|
|
|
|
|
|
|
+ // Don't trigger a terminal reset as the session switches
|
|
|
cli_vcp.is_connected = &furi_hal_version_do_i_belong_here;
|
|
cli_vcp.is_connected = &furi_hal_version_do_i_belong_here;
|
|
|
} else {
|
|
} else {
|
|
|
- // Send CTRL-C
|
|
|
|
|
|
|
+ // Send CTRL-C a few times
|
|
|
char eot = 0x03;
|
|
char eot = 0x03;
|
|
|
- furi_stream_buffer_send(rx_stream, &eot, 1, FuriWaitForever);
|
|
|
|
|
|
|
+ furi_stream_buffer_send(cli_rx_stream, &eot, 1, FuriWaitForever);
|
|
|
|
|
+ furi_stream_buffer_send(cli_rx_stream, &eot, 1, FuriWaitForever);
|
|
|
|
|
+ furi_stream_buffer_send(cli_rx_stream, &eot, 1, FuriWaitForever);
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ // Restore command flags
|
|
|
|
|
+ CliCommandTree_it_t cmd_iterator;
|
|
|
|
|
+ for(CliCommandTree_it(cmd_iterator, global_cli->commands); !CliCommandTree_end_p(cmd_iterator);
|
|
|
|
|
+ CliCommandTree_next(cmd_iterator)) {
|
|
|
|
|
+ CliCommand* t = CliCommandTree_cref(cmd_iterator)->value_ptr;
|
|
|
|
|
+ t->flags ^=
|
|
|
|
|
+ (((t->flags & CliCommandFlagParallelSafe) >> 8) ^
|
|
|
|
|
+ ((t->flags & (CliCommandFlagParallelSafe << 8)) >> 8));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ restore_tx_stdout = true; // Ready for next rx call
|
|
|
|
|
+
|
|
|
|
|
+ // Session switcharooney again
|
|
|
|
|
+ FuriThreadStdoutWriteCallback prev_stdout = furi_thread_get_stdout_callback();
|
|
|
|
|
+ cli_session_close(global_cli);
|
|
|
cli_session_open(global_cli, &cli_vcp);
|
|
cli_session_open(global_cli, &cli_vcp);
|
|
|
|
|
+ furi_thread_set_stdout_callback(prev_stdout);
|
|
|
furi_record_close(RECORD_CLI);
|
|
furi_record_close(RECORD_CLI);
|
|
|
- // Unblock waiting rx handler
|
|
|
|
|
- furi_stream_buffer_send(rx_stream, "_", 1, FuriWaitForever);
|
|
|
|
|
- // Reconfigure stdout_callback to cli_vcp
|
|
|
|
|
- if(cliThread != NULL) {
|
|
|
|
|
- cliThread->output.write_callback = cli_vcp.tx_stdout;
|
|
|
|
|
- }
|
|
|
|
|
- // At this point, all cli_vcp functions should be back.
|
|
|
|
|
- furi_stream_buffer_free(rx_stream);
|
|
|
|
|
- furi_stream_buffer_free(tx_stream);
|
|
|
|
|
- // Re-lock loader (to avoid crash on automatic unlock)
|
|
|
|
|
- Loader* loader = furi_record_open(RECORD_LOADER);
|
|
|
|
|
- loader->app.thread = prev_appthread;
|
|
|
|
|
- furi_record_close(RECORD_LOADER);
|
|
|
|
|
|
|
+
|
|
|
|
|
+ // Unblock waiting rx handler, restore old cli_vcp.tx_stdout
|
|
|
|
|
+ furi_stream_buffer_send(cli_rx_stream, "_", 1, FuriWaitForever);
|
|
|
|
|
+
|
|
|
|
|
+ // At this point, all cli_vcp functions should be restored.
|
|
|
|
|
+
|
|
|
|
|
+ furi_stream_buffer_free(cli_rx_stream);
|
|
|
|
|
+ furi_stream_buffer_free(cli_tx_stream);
|
|
|
|
|
+ cli_rx_stream = NULL;
|
|
|
|
|
+ cli_tx_stream = NULL;
|
|
|
}
|
|
}
|