-
-
Notifications
You must be signed in to change notification settings - Fork 56
/
cli_control.c
149 lines (123 loc) · 4.71 KB
/
cli_control.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
#include "cli_control.h"
#include <FreeRTOS.h>
#include <cli/cli.h>
#include <cli/cli_i.h>
#include <cli/cli_vcp.h>
#include <loader/loader.h>
#include <loader/loader_i.h>
FuriStreamBuffer* cli_tx_stream = NULL;
FuriStreamBuffer* cli_rx_stream = NULL;
static volatile bool restore_tx_stdout = false;
static void tx_handler(const uint8_t* buffer, size_t size) {
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(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 CliSession* session;
static void session_init(void) {
}
static void session_deinit(void) {
free(session);
session = NULL;
}
static bool session_connected(void) {
return true;
}
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_rx_stream = furi_stream_buffer_alloc(rx_size, 1);
cli_tx_stream = furi_stream_buffer_alloc(tx_size, 1);
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;
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 switcharooney
FuriThreadStdoutWriteCallback prev_stdout = furi_thread_get_stdout_callback();
cli_session_close(global_cli);
restore_tx_stdout = false;
cli_session_open(global_cli, session);
furi_thread_set_stdout_callback(prev_stdout);
furi_record_close(RECORD_CLI);
}
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);
}
}
Cli* global_cli = furi_record_open(RECORD_CLI);
if(persist) {
// 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 a few times
char eot = 0x03;
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, 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;
}