Selaa lähdekoodia

Merge cli_bridge from https://github.com/ranchordo/flipperzero-cli-bridge

Willy-JL 1 vuosi sitten
vanhempi
commit
6787fd715d
4 muutettua tiedostoa jossa 119 lisäystä ja 83 poistoa
  1. 1 0
      cli_bridge/.gitignore
  2. 101 67
      cli_bridge/cli_control.c
  3. 4 4
      cli_bridge/cli_control.h
  4. 13 12
      cli_bridge/cligui_main.c

+ 1 - 0
cli_bridge/.gitignore

@@ -0,0 +1 @@
+.vscode

+ 101 - 67
cli_bridge/cli_control.c

@@ -1,115 +1,149 @@
 #include "cli_control.h"
 
+#include <FreeRTOS.h>
 #include <cli/cli.h>
 #include <cli/cli_i.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 <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) {
-    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) {
     size_t rx_cnt = 0;
     while(size > 0) {
         size_t batch_size = size;
         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;
         size -= len;
         buffer += 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;
 }
 
-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_deinit(void) {
+    free(session);
+    session = NULL;
 }
+
 static bool session_connected(void) {
     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);
 
-    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_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);
 }
-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) {
-        // 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;
     } else {
-        // Send CTRL-C
+        // Send CTRL-C a few times
         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);
+    furi_thread_set_stdout_callback(prev_stdout);
     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;
 }

+ 4 - 4
cli_bridge/cli_control.h

@@ -2,7 +2,7 @@
 
 #include <furi.h>
 #include <furi_hal.h>
-extern void latch_tx_handler();
-extern void unlatch_tx_handler(bool persist);
-extern FuriStreamBuffer* tx_stream;
-extern FuriStreamBuffer* rx_stream;
+extern void clicontrol_hijack(size_t tx_size, size_t rx_size);
+extern void clicontrol_unhijack(bool persist);
+extern FuriStreamBuffer* cli_tx_stream;
+extern FuriStreamBuffer* cli_rx_stream;

+ 13 - 12
cli_bridge/cligui_main.c

@@ -2,7 +2,6 @@
 #include "cli_control.h"
 #include "text_input.h"
 #include "console_output.h"
-#include <gui/view_dispatcher_i.h>
 
 static bool cligui_custom_event_cb(void* context, uint32_t event) {
     UNUSED(event);
@@ -42,9 +41,9 @@ static void cligui_tick_event_cb(void* context) {
     UNUSED(app);
 }
 
-ViewPortInputCallback prev_input_callback;
 volatile bool persistent_exit = false;
-static void input_callback_wrapper(InputEvent* event, void* context) {
+static void input_callback(const void* event_ptr, void* context) {
+    InputEvent* event = (InputEvent*)event_ptr;
     CliguiApp* app = context;
     if(event->type == InputTypeLong && event->key == InputKeyBack) {
         persistent_exit = false;
@@ -61,7 +60,6 @@ static void input_callback_wrapper(InputEvent* event, void* context) {
     } else {
         console_output_input_handler(app, event);
     }
-    prev_input_callback(event, app->view_dispatcher);
 }
 
 int32_t cligui_main(void* p) {
@@ -69,16 +67,16 @@ int32_t cligui_main(void* p) {
     CliguiApp* cligui = malloc(sizeof(CliguiApp));
     cligui->data = malloc(sizeof(CliguiData));
 
-    latch_tx_handler();
-    cligui->data->streams.app_tx = rx_stream;
-    cligui->data->streams.app_rx = tx_stream;
+    clicontrol_hijack(512, 512);
+    cligui->data->streams.app_tx = cli_rx_stream;
+    cligui->data->streams.app_rx = cli_tx_stream;
 
     cligui->gui = furi_record_open(RECORD_GUI);
     cligui->view_dispatcher = view_dispatcher_alloc();
-    prev_input_callback = cligui->view_dispatcher->view_port->input_callback;
-    view_port_input_callback_set(
-        cligui->view_dispatcher->view_port, input_callback_wrapper, cligui);
-
+    FuriPubSub* input_events = furi_record_open(RECORD_INPUT_EVENTS);
+    FuriPubSubSubscription* input_events_sub =
+        furi_pubsub_subscribe(input_events, input_callback, (void*)cligui);
+    view_dispatcher_enable_queue(cligui->view_dispatcher);
     view_dispatcher_set_event_callback_context(cligui->view_dispatcher, cligui);
     view_dispatcher_set_custom_event_callback(cligui->view_dispatcher, cligui_custom_event_cb);
     view_dispatcher_set_navigation_event_callback(cligui->view_dispatcher, cligui_back_event_cb);
@@ -121,9 +119,12 @@ int32_t cligui_main(void* p) {
     text_input_free(cligui->text_input);
     view_dispatcher_free(cligui->view_dispatcher);
 
-    unlatch_tx_handler(persistent_exit);
+    clicontrol_unhijack(persistent_exit);
+
+    furi_pubsub_unsubscribe(input_events, input_events_sub);
 
     furi_record_close(RECORD_GUI);
+    furi_record_close(RECORD_INPUT_EVENTS);
 
     free(cligui->data);
     free(cligui);