1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2025-06-28 02:42:34 +00:00

refactor(core/prodtest): make cli non-blocking, event-loop compatible

[no changelog]
This commit is contained in:
tychovrahe 2025-03-20 14:46:31 +01:00 committed by TychoVrahe
parent 08bcedcaa5
commit f579d31684
3 changed files with 241 additions and 207 deletions

View File

@ -106,7 +106,7 @@ cli_t g_cli = {0};
#define VCP_IFACE 0 #define VCP_IFACE 0
static size_t console_read(void *context, char *buf, size_t size) { static size_t console_read(void *context, char *buf, size_t size) {
return usb_vcp_read_blocking(VCP_IFACE, (uint8_t *)buf, size, -1); return usb_vcp_read(VCP_IFACE, (uint8_t *)buf, size);
} }
static size_t console_write(void *context, const char *buf, size_t size) { static size_t console_write(void *context, const char *buf, size_t size) {
@ -248,7 +248,11 @@ int main(void) {
pair_optiga(&g_cli); pair_optiga(&g_cli);
#endif #endif
cli_run_loop(&g_cli); while (true) {
if (usb_vcp_can_read(VCP_IFACE)) {
cli_process_io(&g_cli);
}
}
return 0; return 0;
} }

View File

@ -277,7 +277,11 @@ static int cli_readch(cli_t* cli) {
for (;;) { for (;;) {
char ch; char ch;
cli->read(cli->callback_context, &ch, 1); size_t len = cli->read(cli->callback_context, &ch, 1);
if (len != 1) {
return 0;
}
if (ch == '\e') { if (ch == '\e') {
// Escape sequence start // Escape sequence start
@ -331,114 +335,107 @@ static char cli_autocomplete(cli_t* cli, const char* prefix) {
return next_char; return next_char;
} }
// Reads a line from the console input and stores it in the `cli->line` buffer // Processes a received character
// //
// Returns false if the input line is too long // Returns 1 if the input line is complete,
static bool cli_readln(cli_t* cli) { // returns 0 if more characters are needed,
// returns negative value if the input line is too long
static int cli_process_char(cli_t* cli, int ch) {
char* buf = cli->line_buffer; char* buf = cli->line_buffer;
int len = 0; // number of characters in the buffer (excluding '\0')
int cursor = 0; // cursor position in the buffer
int hist_idx = 0;
int hist_prefix = 0;
buf[0] = '\0';
for (;;) {
int ch = cli_readch(cli);
switch (ch) { switch (ch) {
case ESC_SEQ('A'): // ESC[A case ESC_SEQ('A'): // ESC[A
// Up arrow - search history backwards // Up arrow - search history backwards
if (hist_idx == 0) { if (cli->hist_idx == 0) {
hist_prefix = len; cli->hist_prefix = cli->line_len;
} }
const char* hist_line = const char* hist_line =
cli_history_rev(cli, &hist_idx, buf, hist_prefix); cli_history_rev(cli, &cli->hist_idx, buf, cli->hist_prefix);
if (hist_line != NULL) { if (hist_line != NULL) {
if (cursor > 0) { if (cli->line_cursor > 0) {
// Move the cursor to the beginning of the line // Move the cursor to the beginning of the line
cli_printf(cli, "\e[%dD", cursor); cli_printf(cli, "\e[%dD", cli->line_cursor);
} }
// Replace original text // Replace original text
strcpy(buf, hist_line); strcpy(buf, hist_line);
len = cursor = strlen(buf); cli->line_len = cli->line_cursor = strlen(buf);
cli_printf(cli, "%s\e[K", buf); cli_printf(cli, "%s\e[K", buf);
} }
continue; return 0;
case ESC_SEQ('B'): // ESC[B case ESC_SEQ('B'): // ESC[B
// Down arrow - search history forwards // Down arrow - search history forwards
if (hist_idx > 0) { if (cli->hist_idx > 0) {
const char* hist_line = const char* hist_line =
cli_history_fwd(cli, &hist_idx, buf, hist_prefix); cli_history_fwd(cli, &cli->hist_idx, buf, cli->hist_prefix);
if (hist_line != NULL) { if (hist_line != NULL) {
if (cursor > 0) { if (cli->line_cursor > 0) {
// Move the cursor to the beginning of the line // Move the cursor to the beginning of the line
cli_printf(cli, "\e[%dD", cursor); cli_printf(cli, "\e[%dD", cli->line_cursor);
} }
// Replace original text // Replace original text
strcpy(buf, hist_line); strcpy(buf, hist_line);
len = cursor = strlen(buf); cli->line_len = cli->line_cursor = strlen(buf);
cli_printf(cli, "%s\e[K", buf); cli_printf(cli, "%s\e[K", buf);
} else { } else {
if (cursor > hist_prefix) { if (cli->line_cursor > cli->hist_prefix) {
cli_printf(cli, "\e[%dD", cursor - hist_prefix); cli_printf(cli, "\e[%dD", cli->line_cursor - cli->hist_prefix);
} }
cli_printf(cli, "\e[K"); cli_printf(cli, "\e[K");
len = cursor = hist_prefix; cli->line_len = cli->line_cursor = cli->hist_prefix;
buf[len] = '\0'; buf[cli->line_len] = '\0';
} }
} }
continue; return 0;
} }
// Reset the history index, if the user types something else // Reset the history index, if the user types something else
hist_idx = 0; cli->hist_idx = 0;
switch (ch) { switch (ch) {
case ESC_SEQ('C'): // ESC[C case ESC_SEQ('C'): // ESC[C
// Right arrow // Right arrow
if (cursor < len) { if (cli->line_cursor < cli->line_len) {
if (cli->interactive) { if (cli->interactive) {
cli_printf(cli, "\e[C"); cli_printf(cli, "\e[C");
} }
cursor++; cli->line_cursor++;
} }
break; break;
case ESC_SEQ('D'): // ESC[D case ESC_SEQ('D'): // ESC[D
// Left arrow // Left arrow
if (cursor > 0) { if (cli->line_cursor > 0) {
if (cli->interactive) { if (cli->interactive) {
cli_printf(cli, "\e[D"); cli_printf(cli, "\e[D");
} }
cursor--; cli->line_cursor--;
} }
break; break;
case '\b': case '\b':
case 0x7F: case 0x7F:
// backspace => delete last character // backspace => delete last character
if (cursor == 0) break; if (cli->line_cursor == 0) break;
if (cli->interactive) { if (cli->interactive) {
// Move the cursor left // Move the cursor left
cli_printf(cli, "\e[D"); cli_printf(cli, "\e[D");
} }
--cursor; --cli->line_cursor;
// do not break, fall through // do not break, fall through
case ESC_SEQ(3): // ESC[3~ case ESC_SEQ(3): // ESC[3~
// Delete // Delete
if (cursor < len) { if (cli->line_cursor < cli->line_len) {
// Delete the character at the cursor // Delete the character at the cursor
memmove(&buf[cursor], &buf[cursor + 1], len - cursor); memmove(&buf[cli->line_cursor], &buf[cli->line_cursor + 1],
--len; cli->line_len - cli->line_cursor);
--cli->line_len;
if (cli->interactive) { if (cli->interactive) {
// Print the rest of the line and move the cursor back // Print the rest of the line and move the cursor back
cli_printf(cli, "%s \b", &buf[cursor]); cli_printf(cli, "%s \b", &buf[cli->line_cursor]);
if (cursor < len) { if (cli->line_cursor < cli->line_len) {
cli_printf(cli, "\e[%dD", len - cursor); cli_printf(cli, "\e[%dD", cli->line_len - cli->line_cursor);
} }
} }
} }
@ -450,18 +447,21 @@ static bool cli_readln(cli_t* cli) {
if (cli->interactive) { if (cli->interactive) {
cli_printf(cli, "\r\n"); cli_printf(cli, "\r\n");
} }
return len < CLI_LINE_BUFFER_SIZE; if (cli->line_len < CLI_LINE_BUFFER_SIZE) {
return 1;
}
return -1;
case '\t': case '\t':
// tab => autocomplete // tab => autocomplete
if (cli->interactive && len == cursor) { if (cli->interactive && cli->line_len == cli->line_cursor) {
char ch; char ch;
while ((ch = cli_autocomplete(cli, buf)) != '\0') { while ((ch = cli_autocomplete(cli, buf)) != '\0') {
if (len < CLI_LINE_BUFFER_SIZE - 1) { if (cli->line_len < CLI_LINE_BUFFER_SIZE - 1) {
cli_printf(cli, "%c", ch); cli_printf(cli, "%c", ch);
buf[len++] = ch; buf[cli->line_len++] = ch;
buf[len] = '\0'; buf[cli->line_len] = '\0';
cursor++; cli->line_cursor++;
} }
} }
} }
@ -470,24 +470,33 @@ static bool cli_readln(cli_t* cli) {
default: default:
if (ch >= 0x20 && ch <= 0x7E) { if (ch >= 0x20 && ch <= 0x7E) {
// Printable character // Printable character
if (len < CLI_LINE_BUFFER_SIZE - 1) { if (cli->line_len < CLI_LINE_BUFFER_SIZE - 1) {
// Insert the character at the cursor // Insert the character at the cursor
++len; ++cli->line_len;
memmove(&buf[cursor + 1], &buf[cursor], len - cursor); memmove(&buf[cli->line_cursor + 1], &buf[cli->line_cursor],
buf[cursor] = ch; cli->line_len - cli->line_cursor);
buf[cli->line_cursor] = ch;
// Print new character and the rest of the line // Print new character and the rest of the line
if (cli->interactive) { if (cli->interactive) {
cli_printf(cli, "%s", &buf[cursor]); cli_printf(cli, "%s", &buf[cli->line_cursor]);
} }
++cursor; ++cli->line_cursor;
if (cli->interactive && cursor < len) { if (cli->interactive && cli->line_cursor < cli->line_len) {
// Move the cursor back // Move the cursor back
cli_printf(cli, "\e[%dD", len - cursor); cli_printf(cli, "\e[%dD", cli->line_len - cli->line_cursor);
} }
} }
} }
} }
return 0;
} }
static void cli_clear_line(cli_t* cli) {
cli->line_len = 0;
cli->line_cursor = 0;
cli->hist_idx = 0;
cli->hist_prefix = 0;
memset(cli->line_buffer, 0, sizeof(cli->line_buffer));
} }
// Splits the command line into arguments // Splits the command line into arguments
@ -526,24 +535,41 @@ static bool cli_split_args(cli_t* cli) {
return *cstr_skip_whitespace(buf) == '\0'; return *cstr_skip_whitespace(buf) == '\0';
} }
void cli_run_loop(cli_t* cli) { static void cli_process_command(cli_t* cli, const cli_command_t* cmd) {
while (true) { cli->current_cmd = cmd;
if (cli->interactive) {
if (cli->final_status) {
// Finalize the last command with an empty line
cli_printf(cli, "\r\n");
}
// Print the prompt
cli_printf(cli, "> ");
}
cli->final_status = false; cli->final_status = false;
cli->aborted = false; cli->aborted = false;
// Read the next line // Call the command handler
if (!cli_readln(cli)) { cmd->func(cli);
if (!cli->final_status) {
// Command handler hasn't sent final status
if (cli->aborted) {
cli_error(cli, CLI_ERROR_ABORT, "");
} else {
cli_error(cli, CLI_ERROR_FATAL,
"Command handler didn't finish properly.");
}
} else {
// Finalize the last command with an empty line
cli_printf(cli, "\r\n");
}
}
void cli_process_io(cli_t* cli) {
int res;
do {
int ch = cli_readch(cli);
if (ch == 0) {
return;
}
res = cli_process_char(cli, ch);
} while (res == 0);
if (res < 0) {
cli_error(cli, CLI_ERROR_FATAL, "Input line too long."); cli_error(cli, CLI_ERROR_FATAL, "Input line too long.");
continue; goto cleanup;
} }
cli_history_add(cli, cli->line_buffer); cli_history_add(cli, cli->line_buffer);
@ -551,7 +577,7 @@ void cli_run_loop(cli_t* cli) {
// Split command line into arguments // Split command line into arguments
if (!cli_split_args(cli)) { if (!cli_split_args(cli)) {
cli_error(cli, CLI_ERROR_FATAL, "Too many arguments."); cli_error(cli, CLI_ERROR_FATAL, "Too many arguments.");
continue; goto cleanup;
} }
// Empty line? // Empty line?
@ -565,7 +591,7 @@ void cli_run_loop(cli_t* cli) {
cmd->func(cli); cmd->func(cli);
} }
} }
continue; goto cleanup;
} }
cli->empty_lines = 0; cli->empty_lines = 0;
@ -575,31 +601,26 @@ void cli_run_loop(cli_t* cli) {
cli->interactive = false; cli->interactive = false;
cli_trace(cli, "Exiting interactive mode..."); cli_trace(cli, "Exiting interactive mode...");
} }
continue; goto cleanup;
} }
// Find the command handler // Find the command handler
cli->current_cmd = cli_find_command(cli, cli->cmd_name); const cli_command_t* cmd = cli_find_command(cli, cli->cmd_name);
if (cli->current_cmd == NULL) { if (cmd == NULL) {
cli_error(cli, CLI_ERROR_INVALID_CMD, "Invalid command '%s', try 'help'.", cli_error(cli, CLI_ERROR_INVALID_CMD, "Invalid command '%s', try 'help'.",
cli->cmd_name); cli->cmd_name);
continue; goto cleanup;
} }
// Call the command handler cli_process_command(cli, cmd);
cli->current_cmd->func(cli);
if (!cli->final_status) { cleanup:
// Command handler hasn't send final status if (cli->interactive) {
if (cli->aborted) { // Print the prompt
cli_error(cli, CLI_ERROR_ABORT, ""); cli_printf(cli, "> ");
} else {
cli_error(cli, CLI_ERROR_FATAL,
"Command handler didn't finish properly.");
}
}
} }
cli_clear_line(cli);
} }
// Return position of the argument with the given name in // Return position of the argument with the given name in

View File

@ -90,6 +90,15 @@ struct cli {
// Current line buffer // Current line buffer
char line_buffer[CLI_LINE_BUFFER_SIZE]; char line_buffer[CLI_LINE_BUFFER_SIZE];
// number of characters in the buffer (excluding '\0')
int line_len;
// cursor position in the buffer
int line_cursor;
// currently selected history entry
int hist_idx;
// prefix to search in the history
int hist_prefix;
// Command name (pointer to the line buffer) // Command name (pointer to the line buffer)
const char* cmd_name; const char* cmd_name;
// Number of parsed arguments // Number of parsed arguments
@ -123,8 +132,8 @@ bool cli_init(cli_t* cli, cli_read_cb_t read, cli_write_cb_t write,
void cli_set_commands(cli_t* cli, const cli_command_t* cmd_array, void cli_set_commands(cli_t* cli, const cli_command_t* cmd_array,
size_t cmd_count); size_t cmd_count);
// Runs the CLI command loop // Process the newly received characters from the console input
void cli_run_loop(cli_t* cli); void cli_process_io(cli_t* cli);
// Returne the number of arguments in the command line // Returne the number of arguments in the command line
size_t cli_arg_count(cli_t* cli); size_t cli_arg_count(cli_t* cli);