[PATCH v3] hv: shell: improve console to buffer history cmds


Minggui Cao
 

1. buffer max to 8 history commands.
2. support up/down key to select history buffered commands

Signed-off-by: Minggui Cao <minggui.cao@...>

v2-->v3:
1. cobile input_line with buffered_cmds as one to buffer/input
2. simplify some logic to handle up/down key

---
hypervisor/debug/shell.c | 141 ++++++++++++++++++++++++++--------
hypervisor/debug/shell_priv.h | 9 ++-
2 files changed, 119 insertions(+), 31 deletions(-)

diff --git a/hypervisor/debug/shell.c b/hypervisor/debug/shell.c
index 588e4aa0f..ca0cf2d65 100644
--- a/hypervisor/debug/shell.c
+++ b/hypervisor/debug/shell.c
@@ -33,7 +33,6 @@ static char shell_log_buf[SHELL_LOG_BUF_SIZE];
/* Input Line Other - Switch to the "other" input line (there are only two
* input lines total).
*/
-#define SHELL_INPUT_LINE_OTHER(v) (((v) + 1U) & 0x1U)

static int32_t shell_cmd_help(__unused int32_t argc, __unused char **argv);
static int32_t shell_version(__unused int32_t argc, __unused char **argv);
@@ -158,6 +157,14 @@ static struct shell_cmd shell_cmds[] = {
},
};

+/* for function key: up/down key */
+enum function_key {
+ KEY_NONE,
+
+ KEY_UP = 0x5B41,
+ KEY_DOWN = 0x5B42,
+};
+
/* The initial log level*/
uint16_t console_loglevel = CONFIG_CONSOLE_LOGLEVEL_DEFAULT;
uint16_t mem_loglevel = CONFIG_MEM_LOGLEVEL_DEFAULT;
@@ -271,14 +278,85 @@ static uint16_t sanitize_vmid(uint16_t vmid)
return sanitized_vmid;
}

+static void clear_input_line(uint32_t len)
+{
+ while (len > 0) {
+ len--;
+ shell_puts("\b");
+ shell_puts(" \b");
+ }
+}
+
+static void move_chars(char *dest, char *src, uint32_t size, bool from_head)
+{
+ uint32_t i = 0;
+
+ if (from_head) {
+ for (i = 0; i < size; i++) {
+ *(dest + i) = *(src + i);
+ }
+ } else {
+ for (i = size; i > 0; i--) {
+ *(dest + i - 1) = *(src + i - 1);
+ }
+ }
+}
+
+static void handle_updown_key(enum function_key key_value)
+{
+ uint32_t current_select = p_shell->to_select_index;
+
+ /* update p_shell->to_select_index as up/down key */
+ if (key_value == KEY_UP) {
+ /* if the ring buffer not full, just decrease one until to 0; if full, need handle over flow case */
+ if ((p_shell->total_buffered_cmds < MAX_BUFFERED_CMDS) && (p_shell->to_select_index > 0)) {
+ current_select = p_shell->to_select_index - 1;
+ } else if (p_shell->total_buffered_cmds == MAX_BUFFERED_CMDS) {
+ current_select = (p_shell->to_select_index + MAX_BUFFERED_CMDS - 1) % MAX_BUFFERED_CMDS;
+ }
+ } else {
+ /* if down key and current is active line, not need update */
+ if (p_shell->to_select_index != p_shell->input_line_active) {
+ current_select = (p_shell->to_select_index + 1) % MAX_BUFFERED_CMDS;
+ }
+ }
+
+ /* go up/down until current input line: user will know it is end to select */
+ if (current_select != p_shell->input_line_active) {
+ p_shell->to_select_index = current_select;
+ }
+
+ if (strcmp(p_shell->buffered_line[current_select], p_shell->buffered_line[p_shell->input_line_active]) != 0) {
+
+ clear_input_line(p_shell->input_line_len);
+ shell_puts(p_shell->buffered_line[current_select]);
+
+ size_t len = strnlen_s(p_shell->buffered_line[current_select], SHELL_CMD_MAX_LEN);
+
+ move_chars(p_shell->buffered_line[p_shell->input_line_active],
+ p_shell->buffered_line[current_select], len + 1, true);
+ p_shell->input_line_len = len;
+ }
+}
+
static void shell_handle_special_char(char ch)
{
+ enum function_key key_value = KEY_NONE;
+
switch (ch) {
- /* Escape character */
+ /* original function key value: ESC + key, so consume the next 2 characters */
case 0x1b:
- /* Consume the next 2 characters */
- (void) shell_getc();
- (void) shell_getc();
+ key_value = (shell_getc() << 8) | shell_getc();
+
+ switch (key_value) {
+ case KEY_UP:
+ case KEY_DOWN:
+ handle_updown_key(key_value);
+ break;
+ default:
+ break;
+ }
+
break;
default:
/*
@@ -308,8 +386,7 @@ static bool shell_input_line(void)
p_shell->input_line_len--;

/* Null terminate the last character to erase it */
- p_shell->input_line[p_shell->input_line_active]
- [p_shell->input_line_len] = 0;
+ p_shell->buffered_line[p_shell->input_line_active][p_shell->input_line_len] = 0;

/* Echo backspace */
shell_puts("\b");
@@ -345,12 +422,10 @@ static bool shell_input_line(void)
/* See if a "standard" prINTable ASCII character received */
if ((ch >= 32) && (ch <= 126)) {
/* Add character to string */
- p_shell->input_line[p_shell->input_line_active]
- [p_shell->input_line_len] = ch;
+ p_shell->buffered_line[p_shell->input_line_active][p_shell->input_line_len] = ch;
/* Echo back the input */
- shell_puts(&p_shell->input_line
- [p_shell->input_line_active]
- [p_shell->input_line_len]);
+ shell_puts(&p_shell->buffered_line[p_shell->input_line_active]
+ [p_shell->input_line_len]);

/* Move to next character in string */
p_shell->input_line_len++;
@@ -427,31 +502,34 @@ static int32_t shell_process(void)
{
int32_t status;
char *p_input_line;
+ bool to_buffer = true;

- /* Check for the repeat command character in active input line.
- */
- if (p_shell->input_line[p_shell->input_line_active][0] == '.') {
- /* Repeat the last command (using inactive input line).
- */
- p_input_line =
- &p_shell->input_line[SHELL_INPUT_LINE_OTHER
- (p_shell->input_line_active)][0];
- } else {
- /* Process current command (using active input line). */
- p_input_line =
- &p_shell->input_line[p_shell->input_line_active][0];
+ /* Process current command (using active input line). */
+ p_input_line = p_shell->buffered_line[p_shell->input_line_active];
+
+ if (p_shell->total_buffered_cmds > 0) {
+ uint32_t former_index = (p_shell->input_line_active + MAX_BUFFERED_CMDS - 1) % MAX_BUFFERED_CMDS;

- /* Switch active input line. */
- p_shell->input_line_active =
- SHELL_INPUT_LINE_OTHER(p_shell->input_line_active);
+ /* just buffer current cmd if it is not same with last buffered one */
+ if (strcmp(p_input_line, p_shell->buffered_line[former_index]) == 0) {
+ to_buffer = false;
+ p_input_line = p_shell->buffered_line[former_index];
+ }
+ }
+
+ if (to_buffer && (strnlen_s(p_input_line, SHELL_CMD_MAX_LEN) > 0)) {
+ p_shell->input_line_active = (p_shell->input_line_active + 1) % MAX_BUFFERED_CMDS;
+ if (p_shell->total_buffered_cmds < MAX_BUFFERED_CMDS)
+ p_shell->total_buffered_cmds++;
}

+ p_shell->to_select_index = p_shell->input_line_active;
+
/* Process command */
status = shell_process_cmd(p_input_line);

/* Now that the command is processed, zero fill the input buffer */
- (void)memset((void *) p_shell->input_line[p_shell->input_line_active],
- 0, SHELL_CMD_MAX_LEN + 1U);
+ (void)memset((void *) p_shell->buffered_line[p_shell->input_line_active], 0, SHELL_CMD_MAX_LEN + 1U);

/* Process command and return result to caller */
return status;
@@ -490,8 +568,11 @@ void shell_init(void)
p_shell->cmds = shell_cmds;
p_shell->cmd_count = ARRAY_SIZE(shell_cmds);

+ p_shell->total_buffered_cmds = 0;
+ p_shell->to_select_index = 0;
+
/* Zero fill the input buffer */
- (void)memset((void *)p_shell->input_line[p_shell->input_line_active], 0U,
+ (void)memset((void *)p_shell->buffered_line[p_shell->input_line_active], 0U,
SHELL_CMD_MAX_LEN + 1U);
}

diff --git a/hypervisor/debug/shell_priv.h b/hypervisor/debug/shell_priv.h
index addad6555..3a4f90584 100644
--- a/hypervisor/debug/shell_priv.h
+++ b/hypervisor/debug/shell_priv.h
@@ -26,11 +26,18 @@ struct shell_cmd {

};

+#define MAX_BUFFERED_CMDS 9
+
/* Shell Control Block */
struct shell {
- char input_line[2][SHELL_CMD_MAX_LEN + 1U]; /* current & last */
+ /* a ring buffer to buffer former commands and use one as current active input */
+ char buffered_line[MAX_BUFFERED_CMDS][SHELL_CMD_MAX_LEN + 1U];
uint32_t input_line_len; /* Length of current input line */
uint32_t input_line_active; /* Active input line index */
+
+ uint32_t total_buffered_cmds;
+ uint32_t to_select_index; /* used for up/down key to select former cmds */
+
struct shell_cmd *cmds; /* cmds supported */
uint32_t cmd_count; /* Count of cmds supported */
};
--
2.25.1

Join acrn-dev@lists.projectacrn.org to automatically receive all group messages.