11#include "cli_control.h"
22
3+ #include <FreeRTOS.h>
34#include <cli/cli.h>
45#include <cli/cli_i.h>
56#include <cli/cli_vcp.h>
6- #include "cligui_main_i.h"
7- #include <FreeRTOS .h>
7+ #include <loader/loader.h>
8+ #include <loader/loader_i .h>
89
9- volatile bool gotCallbackSet = false;
10+ FuriStreamBuffer * cli_tx_stream = NULL ;
11+ FuriStreamBuffer * cli_rx_stream = NULL ;
12+
13+ static volatile bool restore_tx_stdout = false;
1014
11- FuriStreamBuffer * tx_stream ;
12- FuriStreamBuffer * rx_stream ;
13- static FuriThread * volatile cliThread = NULL ;
14- static FuriThread * prev_appthread = NULL ;
15- static void tx_handler_stdout (const char * buffer , size_t size ) {
16- furi_stream_buffer_send (tx_stream , buffer , size , FuriWaitForever );
17- }
1815static void tx_handler (const uint8_t * buffer , size_t size ) {
19- furi_thread_set_stdout_callback (tx_handler_stdout );
20- cliThread = furi_thread_get_current ();
21- furi_stream_buffer_send (tx_stream , buffer , size , FuriWaitForever );
16+ furi_stream_buffer_send (cli_tx_stream , buffer , size , FuriWaitForever );
2217}
18+
19+ static void tx_handler_stdout (const char * buffer , size_t size ) {
20+ tx_handler ((const uint8_t * )buffer , size );
21+ }
22+
2323static size_t real_rx_handler (uint8_t * buffer , size_t size , uint32_t timeout ) {
2424 size_t rx_cnt = 0 ;
25- while (size > 0 ) {
25+ while (size > 0 ) {
2626 size_t batch_size = size ;
27- if (batch_size > 128 ) batch_size = 128 ;
28- size_t len = furi_stream_buffer_receive (rx_stream , buffer , batch_size , timeout );
29- if (len == 0 ) break ;
27+ if (batch_size > 128 )
28+ batch_size = 128 ;
29+ size_t len = furi_stream_buffer_receive (cli_rx_stream , buffer , batch_size , timeout );
30+ if (len == 0 )
31+ break ;
3032 size -= len ;
3133 buffer += len ;
3234 rx_cnt += len ;
3335 }
36+ if (restore_tx_stdout ) {
37+ furi_thread_set_stdout_callback (cli_vcp .tx_stdout );
38+ } else {
39+ furi_thread_set_stdout_callback (tx_handler_stdout );
40+ }
3441 return rx_cnt ;
3542}
3643
37- static CliCommand_internal * getInternalCliCommand (Cli * cli , const char * name ) {
38- FuriString * target_command = furi_string_alloc ();
39- furi_string_set_str (target_command , name );
40- CliCommand_internal * command =
41- CliCommandTree_internal_get (((Cli_internal * )cli )-> commands , target_command );
42- furi_string_free (target_command );
43- return command ;
44- }
44+ static CliSession * session ;
4545
46- static void session_init (void ) {
47- }
46+ static void session_init (void ) {}
4847static void session_deinit (void ) {
48+ free (session );
49+ session = NULL ;
4950}
51+
5052static bool session_connected (void ) {
5153 return true;
5254}
53- static CliSession session ;
54- void latch_tx_handler () {
55+
56+ void clicontrol_hijack (size_t tx_size , size_t rx_size ) {
57+ if (cli_rx_stream != NULL && cli_tx_stream != NULL ) {
58+ return ;
59+ }
60+
5561 Cli * global_cli = furi_record_open (RECORD_CLI );
5662
57- CliCommand_internal * help_command = getInternalCliCommand ( global_cli , "help" );
58- cliThread = help_command -> context ;
63+ cli_rx_stream = furi_stream_buffer_alloc ( rx_size , 1 );
64+ cli_tx_stream = furi_stream_buffer_alloc ( tx_size , 1 ) ;
5965
60- furi_thread_set_stdout_callback (tx_handler_stdout );
61- if (cliThread != NULL ) {
62- ((FuriThread_internal * )cliThread )-> output .write_callback = & tx_handler_stdout ;
63- }
66+ session = (CliSession * )malloc (sizeof (CliSession ));
67+ session -> tx = & tx_handler ;
68+ session -> rx = & real_rx_handler ;
69+ session -> tx_stdout = & tx_handler_stdout ;
70+ session -> init = & session_init ;
71+ session -> deinit = & session_deinit ;
72+ session -> is_connected = & session_connected ;
6473
65- rx_stream = furi_stream_buffer_alloc (128 , 1 );
66- tx_stream = furi_stream_buffer_alloc (128 , 1 );
74+ CliCommandTree_it_t cmd_iterator ;
75+ for (CliCommandTree_it (cmd_iterator , global_cli -> commands );
76+ !CliCommandTree_end_p (cmd_iterator );
77+ CliCommandTree_next (cmd_iterator )) {
78+ CliCommand * t = CliCommandTree_cref (cmd_iterator )-> value_ptr ;
79+ // Move CliCommandFlagParallelSafe to another bit
80+ t -> flags ^= ((t -> flags & (CliCommandFlagParallelSafe << 8 )) ^
81+ ((t -> flags & CliCommandFlagParallelSafe ) << 8 ));
82+ // Set parallel safe
83+ t -> flags |= CliCommandFlagParallelSafe ;
84+ }
6785
68- session .tx = & tx_handler ;
69- session .rx = & real_rx_handler ;
70- session .tx_stdout = & tx_handler_stdout ;
71- session .init = & session_init ;
72- session .deinit = & session_deinit ;
73- session .is_connected = & session_connected ;
86+ // Session switcharooney
87+ FuriThreadStdoutWriteCallback prev_stdout = furi_thread_get_stdout_callback ();
7488 cli_session_close (global_cli );
75- cli_session_open (global_cli , & session );
76- // Unlock loader-lock
77- Loader * loader = furi_record_open (RECORD_LOADER );
78- Loader_internal * loader_i = (Loader_internal * )loader ;
79- prev_appthread = loader_i -> app .thread ;
80- loader_i -> app .thread = NULL ;
81- furi_record_close (RECORD_LOADER );
89+ restore_tx_stdout = false;
90+ cli_session_open (global_cli , session );
91+ furi_thread_set_stdout_callback (prev_stdout );
92+
8293 furi_record_close (RECORD_CLI );
8394}
84- void unlatch_tx_handler (bool persist ) {
85- Cli * global_cli = furi_record_open (RECORD_CLI );
86- // Stash cliThread if not null
87- if (cliThread != NULL ) {
88- CliCommand_internal * help_command = getInternalCliCommand (global_cli , "help" );
89- help_command -> context = cliThread ;
95+
96+ void clicontrol_unhijack (bool persist ) {
97+ if (cli_rx_stream == NULL && cli_tx_stream == NULL ) {
98+ return ;
9099 }
91- // Switch to new session
92- if (persist ) {
93- // Use dummy debug firmware function as is_connected
100+
101+ // Consume remaining tx data
102+ if (furi_stream_buffer_bytes_available (cli_tx_stream ) > 0 ) {
103+ char sink = 0 ;
104+ while (!furi_stream_buffer_is_empty (cli_tx_stream )) {
105+ furi_stream_buffer_receive (cli_tx_stream , & sink , 1 , FuriWaitForever );
106+ }
107+ }
108+
109+ Cli * global_cli = furi_record_open (RECORD_CLI );
110+
111+ if (persist ) {
112+ // Don't trigger a terminal reset as the session switches
94113 cli_vcp .is_connected = & furi_hal_version_do_i_belong_here ;
95114 } else {
96- // Send CTRL-C
115+ // Send CTRL-C a few times
97116 char eot = 0x03 ;
98- furi_stream_buffer_send (rx_stream , & eot , 1 , FuriWaitForever );
117+ furi_stream_buffer_send (cli_rx_stream , & eot , 1 , FuriWaitForever );
118+ furi_stream_buffer_send (cli_rx_stream , & eot , 1 , FuriWaitForever );
119+ furi_stream_buffer_send (cli_rx_stream , & eot , 1 , FuriWaitForever );
99120 }
121+
122+ // Restore command flags
123+ CliCommandTree_it_t cmd_iterator ;
124+ for (CliCommandTree_it (cmd_iterator , global_cli -> commands );
125+ !CliCommandTree_end_p (cmd_iterator );
126+ CliCommandTree_next (cmd_iterator )) {
127+ CliCommand * t = CliCommandTree_cref (cmd_iterator )-> value_ptr ;
128+ t -> flags ^= (((t -> flags & CliCommandFlagParallelSafe ) >> 8 ) ^
129+ ((t -> flags & (CliCommandFlagParallelSafe << 8 )) >> 8 ));
130+ }
131+
132+ restore_tx_stdout = true; // Ready for next rx call
133+
134+ // Session switcharooney again
135+ FuriThreadStdoutWriteCallback prev_stdout = furi_thread_get_stdout_callback ();
136+ cli_session_close (global_cli );
100137 cli_session_open (global_cli , & cli_vcp );
138+ furi_thread_set_stdout_callback (prev_stdout );
101139 furi_record_close (RECORD_CLI );
102- // Unblock waiting rx handler
103- furi_stream_buffer_send (rx_stream , "_" , 1 , FuriWaitForever );
104- // Reconfigure stdout_callback to cli_vcp
105- if (cliThread != NULL ) {
106- ((FuriThread_internal * )cliThread )-> output .write_callback = cli_vcp .tx_stdout ;
107- }
108- // At this point, all cli_vcp functions should be back.
109- furi_stream_buffer_free (rx_stream );
110- furi_stream_buffer_free (tx_stream );
111- // Re-lock loader (to avoid crash on automatic unlock)
112- Loader * loader = furi_record_open (RECORD_LOADER );
113- Loader_internal * loader_i = (Loader_internal * )loader ;
114- loader_i -> app .thread = prev_appthread ;
115- furi_record_close (RECORD_LOADER );
140+
141+ // Unblock waiting rx handler, restore old cli_vcp.tx_stdout
142+ furi_stream_buffer_send (cli_rx_stream , "_" , 1 , FuriWaitForever );
143+
144+ // At this point, all cli_vcp functions should be restored.
145+
146+ furi_stream_buffer_free (cli_rx_stream );
147+ furi_stream_buffer_free (cli_tx_stream );
148+ cli_rx_stream = NULL ;
149+ cli_tx_stream = NULL ;
116150}
0 commit comments