mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-04-10 12:25:55 +00:00
Merge b371169dc9
into 52f5593f28
This commit is contained in:
commit
ca8344d217
@ -23,6 +23,29 @@
|
||||
|
||||
#include <io/rgb_led.h>
|
||||
#include <rtl/cli.h>
|
||||
#include <sys/systimer.h>
|
||||
|
||||
static bool g_prodtest_rgbled_start = false;
|
||||
|
||||
static void prodtest_rgbled_timer_cb(void* context) {
|
||||
if (g_prodtest_rgbled_start) {
|
||||
rgb_led_set_color(0);
|
||||
g_prodtest_rgbled_start = false;
|
||||
}
|
||||
}
|
||||
|
||||
void prodtest_rgbled_init(void) {
|
||||
static systimer_t* timer = NULL;
|
||||
|
||||
timer = systimer_create(prodtest_rgbled_timer_cb, NULL);
|
||||
|
||||
if (timer == NULL) {
|
||||
return;
|
||||
}
|
||||
systimer_set(timer, 1000);
|
||||
rgb_led_set_color(0xFF00);
|
||||
g_prodtest_rgbled_start = true;
|
||||
}
|
||||
|
||||
static void prodtest_rgbled_set(cli_t* cli) {
|
||||
uint32_t r = 0;
|
||||
@ -53,6 +76,7 @@ static void prodtest_rgbled_set(cli_t* cli) {
|
||||
|
||||
uint32_t rgb = (r << 16) | (g << 8) | b;
|
||||
|
||||
g_prodtest_rgbled_start = false;
|
||||
rgb_led_set_color(rgb);
|
||||
|
||||
cli_ok(cli, "");
|
||||
|
24
core/embed/projects/prodtest/cmd/prodtest_rgbled.h
Normal file
24
core/embed/projects/prodtest/cmd/prodtest_rgbled.h
Normal file
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* This file is part of the Trezor project, https://trezor.io/
|
||||
*
|
||||
* Copyright (c) SatoshiLabs
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <trezor_types.h>
|
||||
|
||||
void prodtest_rgbled_init(void);
|
@ -24,6 +24,7 @@
|
||||
#include <io/usb.h>
|
||||
#include <rtl/cli.h>
|
||||
#include <sys/system.h>
|
||||
#include <sys/systick.h>
|
||||
#include <util/flash_otp.h>
|
||||
#include <util/rsod.h>
|
||||
#include <util/unit_properties.h>
|
||||
@ -66,6 +67,7 @@
|
||||
|
||||
#ifdef USE_RGB_LED
|
||||
#include <io/rgb_led.h>
|
||||
#include "cmd/prodtest_rgbled.h"
|
||||
#endif
|
||||
|
||||
#ifdef USE_HASH_PROCESSOR
|
||||
@ -105,6 +107,10 @@ cli_t g_cli = {0};
|
||||
|
||||
#define VCP_IFACE 0
|
||||
|
||||
static bool console_can_read(void *context) {
|
||||
return usb_vcp_can_read(VCP_IFACE);
|
||||
}
|
||||
|
||||
static size_t console_read(void *context, char *buf, size_t size) {
|
||||
return usb_vcp_read_blocking(VCP_IFACE, (uint8_t *)buf, size, -1);
|
||||
}
|
||||
@ -209,6 +215,7 @@ static void drivers_init(void) {
|
||||
#endif
|
||||
#ifdef USE_RGB_LED
|
||||
rgb_led_init();
|
||||
prodtest_rgbled_init();
|
||||
#endif
|
||||
#ifdef USE_BLE
|
||||
unit_properties_init();
|
||||
@ -233,7 +240,7 @@ int main(void) {
|
||||
show_welcome_screen();
|
||||
|
||||
// Initialize command line interface
|
||||
cli_init(&g_cli, console_read, console_write, NULL);
|
||||
cli_init(&g_cli, console_can_read, console_read, console_write, NULL);
|
||||
|
||||
extern cli_command_t _prodtest_cli_cmd_section_start;
|
||||
extern cli_command_t _prodtest_cli_cmd_section_end;
|
||||
@ -248,7 +255,47 @@ int main(void) {
|
||||
pair_optiga(&g_cli);
|
||||
#endif
|
||||
|
||||
cli_run_loop(&g_cli);
|
||||
#if defined USE_BUTTON && defined USE_POWERCTL
|
||||
uint32_t btn_deadline = 0;
|
||||
#endif
|
||||
|
||||
while (true) {
|
||||
cli_event_t cli_event = {0};
|
||||
|
||||
bool cli_event_received = cli_get_event(&g_cli, &cli_event);
|
||||
|
||||
if (cli_event_received) {
|
||||
if (cli_event.type == CLI_EVENT_COMMAND_RECEIVED) {
|
||||
cli_process_command(&g_cli, cli_event.data.command_received.command);
|
||||
}
|
||||
}
|
||||
|
||||
#if defined USE_BUTTON && defined USE_POWERCTL
|
||||
button_event_t btn_event = {0};
|
||||
bool btn = button_get_event(&btn_event);
|
||||
|
||||
if (btn) {
|
||||
if (btn_event.button == BTN_POWER &&
|
||||
btn_event.event_type == BTN_EVENT_DOWN) {
|
||||
btn_deadline = ticks_timeout(1000);
|
||||
}
|
||||
if (btn_event.button == BTN_POWER &&
|
||||
btn_event.event_type == BTN_EVENT_UP) {
|
||||
if (ticks_expired(systick_ms())) {
|
||||
powerctl_hibernate();
|
||||
rgb_led_set_color(0xffff00);
|
||||
systick_delay_ms(1000);
|
||||
rgb_led_set_color(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (button_is_down(BTN_POWER) && ticks_expired(btn_deadline)) {
|
||||
rgb_led_set_color(0xff0000);
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -11,9 +11,10 @@
|
||||
#define ESC_COLOR_GRAY "\e[37m"
|
||||
#define ESC_COLOR_RESET "\e[39m"
|
||||
|
||||
bool cli_init(cli_t* cli, cli_read_cb_t read, cli_write_cb_t write,
|
||||
void* callback_context) {
|
||||
bool cli_init(cli_t* cli, cli_can_read_cb_t can_read, cli_read_cb_t read,
|
||||
cli_write_cb_t write, void* callback_context) {
|
||||
memset(cli, 0, sizeof(cli_t));
|
||||
cli->can_read = can_read;
|
||||
cli->read = read;
|
||||
cli->write = write;
|
||||
cli->callback_context = callback_context;
|
||||
@ -331,163 +332,170 @@ static char cli_autocomplete(cli_t* cli, const char* prefix) {
|
||||
return next_char;
|
||||
}
|
||||
|
||||
// Reads a line from the console input and stores it in the `cli->line` buffer
|
||||
// Reads next char and processes it.
|
||||
//
|
||||
// Returns false if the input line is too long
|
||||
static bool cli_readln(cli_t* cli) {
|
||||
// Returns 1 if the input line is complete,
|
||||
// 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) {
|
||||
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;
|
||||
int ch = cli_readch(cli);
|
||||
|
||||
buf[0] = '\0';
|
||||
|
||||
for (;;) {
|
||||
int ch = cli_readch(cli);
|
||||
|
||||
switch (ch) {
|
||||
case ESC_SEQ('A'): // ESC[A
|
||||
// Up arrow - search history backwards
|
||||
if (hist_idx == 0) {
|
||||
hist_prefix = len;
|
||||
switch (ch) {
|
||||
case ESC_SEQ('A'): // ESC[A
|
||||
// Up arrow - search history backwards
|
||||
if (cli->hist_idx == 0) {
|
||||
cli->hist_prefix = cli->len;
|
||||
}
|
||||
const char* hist_line =
|
||||
cli_history_rev(cli, &cli->hist_idx, buf, cli->hist_prefix);
|
||||
if (hist_line != NULL) {
|
||||
if (cli->cursor > 0) {
|
||||
// Move the cursor to the beginning of the line
|
||||
cli_printf(cli, "\e[%dD", cli->cursor);
|
||||
}
|
||||
// Replace original text
|
||||
strcpy(buf, hist_line);
|
||||
cli->len = cli->cursor = strlen(buf);
|
||||
cli_printf(cli, "%s\e[K", buf);
|
||||
}
|
||||
return 0;
|
||||
|
||||
case ESC_SEQ('B'): // ESC[B
|
||||
// Down arrow - search history forwards
|
||||
if (cli->hist_idx > 0) {
|
||||
const char* hist_line =
|
||||
cli_history_rev(cli, &hist_idx, buf, hist_prefix);
|
||||
cli_history_fwd(cli, &cli->hist_idx, buf, cli->hist_prefix);
|
||||
if (hist_line != NULL) {
|
||||
if (cursor > 0) {
|
||||
if (cli->cursor > 0) {
|
||||
// Move the cursor to the beginning of the line
|
||||
cli_printf(cli, "\e[%dD", cursor);
|
||||
cli_printf(cli, "\e[%dD", cli->cursor);
|
||||
}
|
||||
// Replace original text
|
||||
strcpy(buf, hist_line);
|
||||
len = cursor = strlen(buf);
|
||||
cli->len = cli->cursor = strlen(buf);
|
||||
cli_printf(cli, "%s\e[K", buf);
|
||||
}
|
||||
continue;
|
||||
|
||||
case ESC_SEQ('B'): // ESC[B
|
||||
// Down arrow - search history forwards
|
||||
if (hist_idx > 0) {
|
||||
const char* hist_line =
|
||||
cli_history_fwd(cli, &hist_idx, buf, hist_prefix);
|
||||
if (hist_line != NULL) {
|
||||
if (cursor > 0) {
|
||||
// Move the cursor to the beginning of the line
|
||||
cli_printf(cli, "\e[%dD", cursor);
|
||||
}
|
||||
// Replace original text
|
||||
strcpy(buf, hist_line);
|
||||
len = cursor = strlen(buf);
|
||||
cli_printf(cli, "%s\e[K", buf);
|
||||
} else {
|
||||
if (cursor > hist_prefix) {
|
||||
cli_printf(cli, "\e[%dD", cursor - hist_prefix);
|
||||
}
|
||||
cli_printf(cli, "\e[K");
|
||||
len = cursor = hist_prefix;
|
||||
buf[len] = '\0';
|
||||
} else {
|
||||
if (cli->cursor > cli->hist_prefix) {
|
||||
cli_printf(cli, "\e[%dD", cli->cursor - cli->hist_prefix);
|
||||
}
|
||||
cli_printf(cli, "\e[K");
|
||||
cli->len = cli->cursor = cli->hist_prefix;
|
||||
buf[cli->len] = '\0';
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Reset the history index, if the user types something else
|
||||
hist_idx = 0;
|
||||
// Reset the history index, if the user types something else
|
||||
cli->hist_idx = 0;
|
||||
|
||||
switch (ch) {
|
||||
case ESC_SEQ('C'): // ESC[C
|
||||
// Right arrow
|
||||
if (cursor < len) {
|
||||
if (cli->interactive) {
|
||||
cli_printf(cli, "\e[C");
|
||||
}
|
||||
cursor++;
|
||||
}
|
||||
break;
|
||||
|
||||
case ESC_SEQ('D'): // ESC[D
|
||||
// Left arrow
|
||||
if (cursor > 0) {
|
||||
if (cli->interactive) {
|
||||
cli_printf(cli, "\e[D");
|
||||
}
|
||||
cursor--;
|
||||
}
|
||||
break;
|
||||
|
||||
case '\b':
|
||||
case 0x7F:
|
||||
// backspace => delete last character
|
||||
if (cursor == 0) break;
|
||||
switch (ch) {
|
||||
case ESC_SEQ('C'): // ESC[C
|
||||
// Right arrow
|
||||
if (cli->cursor < cli->len) {
|
||||
if (cli->interactive) {
|
||||
cli_printf(cli, "\e[C");
|
||||
}
|
||||
cli->cursor++;
|
||||
}
|
||||
break;
|
||||
|
||||
case ESC_SEQ('D'): // ESC[D
|
||||
// Left arrow
|
||||
if (cli->cursor > 0) {
|
||||
if (cli->interactive) {
|
||||
// Move the cursor left
|
||||
cli_printf(cli, "\e[D");
|
||||
}
|
||||
--cursor;
|
||||
// do not break, fall through
|
||||
cli->cursor--;
|
||||
}
|
||||
break;
|
||||
|
||||
case ESC_SEQ(3): // ESC[3~
|
||||
// Delete
|
||||
if (cursor < len) {
|
||||
// Delete the character at the cursor
|
||||
memmove(&buf[cursor], &buf[cursor + 1], len - cursor);
|
||||
--len;
|
||||
if (cli->interactive) {
|
||||
// Print the rest of the line and move the cursor back
|
||||
cli_printf(cli, "%s \b", &buf[cursor]);
|
||||
if (cursor < len) {
|
||||
cli_printf(cli, "\e[%dD", len - cursor);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case '\b':
|
||||
case 0x7F:
|
||||
// backspace => delete last character
|
||||
if (cli->cursor == 0) break;
|
||||
if (cli->interactive) {
|
||||
// Move the cursor left
|
||||
cli_printf(cli, "\e[D");
|
||||
}
|
||||
--cli->cursor;
|
||||
// do not break, fall through
|
||||
|
||||
case '\r':
|
||||
case '\n':
|
||||
// end of line
|
||||
case ESC_SEQ(3): // ESC[3~
|
||||
// Delete
|
||||
if (cli->cursor < cli->len) {
|
||||
// Delete the character at the cursor
|
||||
memmove(&buf[cli->cursor], &buf[cli->cursor + 1],
|
||||
cli->len - cli->cursor);
|
||||
--cli->len;
|
||||
if (cli->interactive) {
|
||||
cli_printf(cli, "\r\n");
|
||||
}
|
||||
return len < CLI_LINE_BUFFER_SIZE;
|
||||
|
||||
case '\t':
|
||||
// tab => autocomplete
|
||||
if (cli->interactive && len == cursor) {
|
||||
char ch;
|
||||
while ((ch = cli_autocomplete(cli, buf)) != '\0') {
|
||||
if (len < CLI_LINE_BUFFER_SIZE - 1) {
|
||||
cli_printf(cli, "%c", ch);
|
||||
buf[len++] = ch;
|
||||
buf[len] = '\0';
|
||||
cursor++;
|
||||
}
|
||||
// Print the rest of the line and move the cursor back
|
||||
cli_printf(cli, "%s \b", &buf[cli->cursor]);
|
||||
if (cli->cursor < cli->len) {
|
||||
cli_printf(cli, "\e[%dD", cli->len - cli->cursor);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if (ch >= 0x20 && ch <= 0x7E) {
|
||||
// Printable character
|
||||
if (len < CLI_LINE_BUFFER_SIZE - 1) {
|
||||
// Insert the character at the cursor
|
||||
++len;
|
||||
memmove(&buf[cursor + 1], &buf[cursor], len - cursor);
|
||||
buf[cursor] = ch;
|
||||
// Print new character and the rest of the line
|
||||
if (cli->interactive) {
|
||||
cli_printf(cli, "%s", &buf[cursor]);
|
||||
}
|
||||
++cursor;
|
||||
if (cli->interactive && cursor < len) {
|
||||
// Move the cursor back
|
||||
cli_printf(cli, "\e[%dD", len - cursor);
|
||||
}
|
||||
case '\r':
|
||||
case '\n':
|
||||
// end of line
|
||||
if (cli->interactive) {
|
||||
cli_printf(cli, "\r\n");
|
||||
}
|
||||
if (cli->len < CLI_LINE_BUFFER_SIZE) {
|
||||
return 1;
|
||||
}
|
||||
return -1;
|
||||
|
||||
case '\t':
|
||||
// tab => autocomplete
|
||||
if (cli->interactive && cli->len == cli->cursor) {
|
||||
char ch;
|
||||
while ((ch = cli_autocomplete(cli, buf)) != '\0') {
|
||||
if (cli->len < CLI_LINE_BUFFER_SIZE - 1) {
|
||||
cli_printf(cli, "%c", ch);
|
||||
buf[cli->len++] = ch;
|
||||
buf[cli->len] = '\0';
|
||||
cli->cursor++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if (ch >= 0x20 && ch <= 0x7E) {
|
||||
// Printable character
|
||||
if (cli->len < CLI_LINE_BUFFER_SIZE - 1) {
|
||||
// Insert the character at the cursor
|
||||
++cli->len;
|
||||
memmove(&buf[cli->cursor + 1], &buf[cli->cursor],
|
||||
cli->len - cli->cursor);
|
||||
buf[cli->cursor] = ch;
|
||||
// Print new character and the rest of the line
|
||||
if (cli->interactive) {
|
||||
cli_printf(cli, "%s", &buf[cli->cursor]);
|
||||
}
|
||||
++cli->cursor;
|
||||
if (cli->interactive && cli->cursor < cli->len) {
|
||||
// Move the cursor back
|
||||
cli_printf(cli, "\e[%dD", cli->len - cli->cursor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cli_clear_line(cli_t* cli) {
|
||||
cli->len = 0;
|
||||
cli->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
|
||||
@ -526,80 +534,111 @@ static bool cli_split_args(cli_t* cli) {
|
||||
return *cstr_skip_whitespace(buf) == '\0';
|
||||
}
|
||||
|
||||
void cli_run_loop(cli_t* cli) {
|
||||
while (true) {
|
||||
if (cli->interactive) {
|
||||
if (cli->final_status) {
|
||||
// Finalize the last command with an empty line
|
||||
cli_printf(cli, "\r\n");
|
||||
bool cli_get_event(cli_t* cli, cli_event_t* event) {
|
||||
memset(event, 0, sizeof(cli_event_t));
|
||||
|
||||
if (!cli->can_read(cli->callback_context)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cli->cmd_active) {
|
||||
// Some command is already active or waiting for processing
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read the next line
|
||||
int res = cli_process_char(cli);
|
||||
|
||||
if (res < 0) {
|
||||
cli_error(cli, CLI_ERROR_FATAL, "Input line too long.");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (res == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
cli_history_add(cli, cli->line_buffer);
|
||||
|
||||
// Split command line into arguments
|
||||
if (!cli_split_args(cli)) {
|
||||
cli_error(cli, CLI_ERROR_FATAL, "Too many arguments.");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// Empty line?
|
||||
if (*cli->cmd_name == '\0') {
|
||||
// Switch to interactive mode if two empty lines are entered
|
||||
if (++cli->empty_lines >= 2 && !cli->interactive) {
|
||||
cli->interactive = true;
|
||||
// Print the welcome message
|
||||
const cli_command_t* cmd = cli_find_command(cli, "$intro");
|
||||
if (cmd != NULL) {
|
||||
cmd->func(cli);
|
||||
}
|
||||
// Print the prompt
|
||||
cli_printf(cli, "> ");
|
||||
} else if (cli->interactive) {
|
||||
cli_printf(cli, "> ");
|
||||
}
|
||||
|
||||
cli->final_status = false;
|
||||
cli->aborted = false;
|
||||
|
||||
// Read the next line
|
||||
if (!cli_readln(cli)) {
|
||||
cli_error(cli, CLI_ERROR_FATAL, "Input line too long.");
|
||||
continue;
|
||||
}
|
||||
|
||||
cli_history_add(cli, cli->line_buffer);
|
||||
|
||||
// Split command line into arguments
|
||||
if (!cli_split_args(cli)) {
|
||||
cli_error(cli, CLI_ERROR_FATAL, "Too many arguments.");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Empty line?
|
||||
if (*cli->cmd_name == '\0') {
|
||||
// Switch to interactive mode if two empty lines are entered
|
||||
if (++cli->empty_lines >= 2 && !cli->interactive) {
|
||||
cli->interactive = true;
|
||||
// Print the welcome message
|
||||
const cli_command_t* cmd = cli_find_command(cli, "$intro");
|
||||
if (cmd != NULL) {
|
||||
cmd->func(cli);
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
cli->empty_lines = 0;
|
||||
|
||||
// Quit interactive mode on `.+ENTER`
|
||||
if ((strcmp(cli->cmd_name, ".") == 0)) {
|
||||
if (cli->interactive) {
|
||||
cli->interactive = false;
|
||||
cli_trace(cli, "Exiting interactive mode...");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Find the command handler
|
||||
cli->current_cmd = cli_find_command(cli, cli->cmd_name);
|
||||
|
||||
if (cli->current_cmd == NULL) {
|
||||
cli_error(cli, CLI_ERROR_INVALID_CMD, "Invalid command '%s', try 'help'.",
|
||||
cli->cmd_name);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Call the command handler
|
||||
cli->current_cmd->func(cli);
|
||||
|
||||
if (!cli->final_status) {
|
||||
// Command handler hasn't send final status
|
||||
if (cli->aborted) {
|
||||
cli_error(cli, CLI_ERROR_ABORT, "");
|
||||
} else {
|
||||
cli_error(cli, CLI_ERROR_FATAL,
|
||||
"Command handler didn't finish properly.");
|
||||
}
|
||||
}
|
||||
goto cleanup;
|
||||
}
|
||||
cli->empty_lines = 0;
|
||||
|
||||
// Quit interactive mode on `.+ENTER`
|
||||
if ((strcmp(cli->cmd_name, ".") == 0)) {
|
||||
if (cli->interactive) {
|
||||
cli->interactive = false;
|
||||
cli_trace(cli, "Exiting interactive mode...");
|
||||
}
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// Find the command handler
|
||||
const cli_command_t* cmd = cli_find_command(cli, cli->cmd_name);
|
||||
|
||||
if (cmd == NULL) {
|
||||
cli_error(cli, CLI_ERROR_INVALID_CMD, "Invalid command '%s', try 'help'.",
|
||||
cli->cmd_name);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
event->type = CLI_EVENT_COMMAND_RECEIVED;
|
||||
event->data.command_received.command = cmd;
|
||||
cli->cmd_active = true;
|
||||
return true;
|
||||
|
||||
cleanup:
|
||||
cli_clear_line(cli);
|
||||
return false;
|
||||
}
|
||||
|
||||
void cli_process_command(cli_t* cli, const cli_command_t* cmd) {
|
||||
cli->current_cmd = cmd;
|
||||
cli->final_status = false;
|
||||
cli->aborted = false;
|
||||
|
||||
// Call the command handler
|
||||
cmd->func(cli);
|
||||
|
||||
if (!cli->final_status) {
|
||||
// Command handler hasn't send 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");
|
||||
}
|
||||
if (cli->interactive) {
|
||||
// Print the prompt
|
||||
cli_printf(cli, "> ");
|
||||
}
|
||||
|
||||
cli->cmd_active = false;
|
||||
cli_clear_line(cli);
|
||||
}
|
||||
|
||||
// Return position of the argument with the given name in
|
||||
|
@ -77,9 +77,12 @@ typedef struct {
|
||||
typedef size_t (*cli_write_cb_t)(void* ctx, const char* buf, size_t len);
|
||||
// Callback for reading characters from console input
|
||||
typedef size_t (*cli_read_cb_t)(void* ctx, char* buf, size_t len);
|
||||
// Callback for can read assessment
|
||||
typedef bool (*cli_can_read_cb_t)(void* ctx);
|
||||
|
||||
struct cli {
|
||||
// I/O callbacks
|
||||
cli_can_read_cb_t can_read;
|
||||
cli_read_cb_t read;
|
||||
cli_write_cb_t write;
|
||||
void* callback_context;
|
||||
@ -90,6 +93,14 @@ struct cli {
|
||||
|
||||
// Current line buffer
|
||||
char line_buffer[CLI_LINE_BUFFER_SIZE];
|
||||
int len; // number of characters in the buffer (excluding '\0')
|
||||
int cursor; // cursor position in the buffer
|
||||
int hist_idx;
|
||||
int hist_prefix;
|
||||
|
||||
// Command active flag
|
||||
bool cmd_active; // true if the command is pending or being processed
|
||||
|
||||
// Command name (pointer to the line buffer)
|
||||
const char* cmd_name;
|
||||
// Number of parsed arguments
|
||||
@ -115,16 +126,32 @@ struct cli {
|
||||
volatile bool aborted;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
CLI_EVENT_COMMAND_RECEIVED,
|
||||
} cli_event_type_t;
|
||||
|
||||
typedef struct {
|
||||
cli_event_type_t type;
|
||||
union {
|
||||
struct {
|
||||
const cli_command_t* command;
|
||||
} command_received;
|
||||
} data;
|
||||
} cli_event_t;
|
||||
|
||||
// Initializes the command line structure
|
||||
bool cli_init(cli_t* cli, cli_read_cb_t read, cli_write_cb_t write,
|
||||
void* callback_context);
|
||||
bool cli_init(cli_t* cli, cli_can_read_cb_t can_read, cli_read_cb_t read,
|
||||
cli_write_cb_t write, void* callback_context);
|
||||
|
||||
// Registers the command handlers
|
||||
void cli_set_commands(cli_t* cli, const cli_command_t* cmd_array,
|
||||
size_t cmd_count);
|
||||
|
||||
// Runs the CLI command loop
|
||||
void cli_run_loop(cli_t* cli);
|
||||
// Polls the CLI for events
|
||||
bool cli_get_event(cli_t* cli, cli_event_t* event);
|
||||
|
||||
// Processes the command line
|
||||
void cli_process_command(cli_t* cli, const cli_command_t* cmd);
|
||||
|
||||
// Returne the number of arguments in the command line
|
||||
size_t cli_arg_count(cli_t* cli);
|
||||
|
Loading…
Reference in New Issue
Block a user