summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRandall Spangler <rspangler@chromium.org>2013-08-01 14:36:43 -0700
committerDave Parker <dparker@chromium.org>2013-08-13 16:40:14 -0700
commitef84fb00e00800c7981d51030570d716875e8395 (patch)
tree37cd8e1001c0e6ec778f9afe9e352d0412e53a22
parent75647bc936943ad8299f288bdb67a4779c9b4e34 (diff)
downloadchrome-ec-ef84fb00e00800c7981d51030570d716875e8395.tar.gz
CHERRY-PICK:Move input character processing from UART interrupt to console task
Previously, processing of arrow keys and control characters was done in the interrupt handler itself. This increased the impact of UART input on other interrupts and high-priority tasks. It also makes it harder to implement DMA-based UART input on STM32L (in an imminent CL), since the processing affected the circular UART input buffer in-place. This change turns uart_buffering.c back into a dumb I/O buffering module, and puts all the command line editing and history support into console.c. Console history is done via a simple array of input lines instead of a packed circular buffer of characters. This is a little less RAM-efficient, but is easier to implement and read. History depth is controlled via CONFIG_CONSOLE_HISTORY, and is 3 for STM32F and 8 for other platforms. If we really need a greater history depth, we can look into implementing a packed circular buffer again, but this time at task time in console.c. Also added a 'history' command to print the current console history. BUG=chrome-os-partner:20485 BRANCH=none TEST=console_edit unit test passes; 'history' command prints the last commands Change-Id: I18301f692c103c8c9e906f966ee660438c328133 Original-Change-Id: I142a0be0d67718c58341e4569f4e2908f191d8b0 Signed-off-by: Randall Spangler <rspangler@chromium.org> Reviewed-on: https://gerrit.chromium.org/gerrit/64363 Reviewed-by: Vic Yang <victoryang@chromium.org> Reviewed-on: https://gerrit.chromium.org/gerrit/65764 Reviewed-by: Dave Parker <dparker@chromium.org> Tested-by: Dave Parker <dparker@chromium.org>
-rw-r--r--chip/stm32/config-stm32f100.h4
-rw-r--r--common/console.c400
-rw-r--r--common/uart_buffering.c513
-rw-r--r--include/config.h7
-rw-r--r--include/uart.h26
5 files changed, 453 insertions, 497 deletions
diff --git a/chip/stm32/config-stm32f100.h b/chip/stm32/config-stm32f100.h
index ab4440f34d..92b5ab4dca 100644
--- a/chip/stm32/config-stm32f100.h
+++ b/chip/stm32/config-stm32f100.h
@@ -36,3 +36,7 @@
/* Number of IRQ vectors on the NVIC */
#define CONFIG_IRQ_COUNT 61
+
+/* Reduced history because of limited RAM */
+#undef CONFIG_CONSOLE_HISTORY
+#define CONFIG_CONSOLE_HISTORY 3
diff --git a/common/console.c b/common/console.c
index dd12613f9a..78fd96a7c7 100644
--- a/common/console.c
+++ b/common/console.c
@@ -16,9 +16,49 @@
#define PROMPT "> "
+/* ASCII control character; for example, CTRL('C') = ^C */
+#define CTRL(c) ((c) - '@')
+
+#ifdef CONFIG_CONSOLE_HISTORY
+/* History buffers */
+static char history[CONFIG_CONSOLE_HISTORY][CONFIG_CONSOLE_INPUT_LINE_SIZE];
+static int history_next, history_pos;
+#endif
+
/* Current console command line */
static char input_buf[CONFIG_CONSOLE_INPUT_LINE_SIZE];
+/* Length of current line */
+static int input_len;
+
+/* Cursor position in current line */
+static int input_pos;
+
+/* Was last received character a carriage return? */
+static int last_rx_was_cr;
+
+/* State of input escape code */
+static enum {
+ ESC_OUTSIDE, /* Not in escape code */
+ ESC_START, /* Got ESC */
+ ESC_BAD, /* Bad escape sequence */
+ ESC_BRACKET, /* Got ESC [ */
+ ESC_BRACKET_1, /* Got ESC [ 1 */
+ ESC_BRACKET_3, /* Got ESC [ 3 */
+ ESC_O, /* Got ESC O */
+} esc_state;
+
+/* Extended key code values, from multi-byte escape sequences */
+enum extended_key_code {
+ KEY_UP_ARROW = 0x100,
+ KEY_DOWN_ARROW,
+ KEY_RIGHT_ARROW,
+ KEY_LEFT_ARROW,
+ KEY_END,
+ KEY_HOME,
+ KEY_DEL
+};
+
/**
* Split a line of input into words.
*
@@ -34,11 +74,14 @@ static int split_words(char *input, int *argc, char **argv)
{
char *c;
int in_word = 0;
+ int in_line = 1;
/* Parse input into words */
*argc = 0;
- for (c = input; *c; c++) {
- if (isspace(*c)) {
+ for (c = input; in_line; c++) {
+ if (!*c)
+ in_line = 0;
+ if (isspace(*c) || !*c) {
if (in_word) {
/* Ending a word */
*c = '\0';
@@ -150,16 +193,327 @@ static void console_init(void)
ccputs(PROMPT);
}
-static void console_process(void)
+static void move_cursor_right(void)
+{
+ if (input_pos == input_len)
+ return;
+
+ ccputs("\x1b[1C");
+ input_pos++;
+}
+
+static void move_cursor_end(void)
+{
+ if (input_pos == input_len)
+ return;
+
+ ccprintf("\x1b[%dC", input_len - input_pos);
+ input_pos = input_len;
+}
+
+static void move_cursor_left(void)
{
- /*
- * Process all pending console commands. Need to do this all at once
- * since our interrupt may have been triggered multiple times.
- */
- while (uart_peek('\n') >= 0) {
- uart_gets(input_buf, sizeof(input_buf));
+ if (input_pos == 0)
+ return;
+
+ ccputs("\x1b[1D");
+ input_pos--;
+}
+
+static void move_cursor_begin(void)
+{
+ if (input_pos == 0)
+ return;
+
+ ccprintf("\x1b[%dD", input_pos);
+ input_pos = 0;
+}
+
+static void repeat_char(char c, int cnt)
+{
+ while (cnt--)
+ uart_putc(c);
+}
+
+#ifdef CONFIG_CONSOLE_HISTORY
+
+/**
+ * Load input history
+ *
+ * @param idx History index to load
+ */
+static void load_history(int idx)
+{
+ /* Copy history */
+ strzcpy(input_buf, history[idx], CONFIG_CONSOLE_INPUT_LINE_SIZE);
+
+ /* Print history */
+ move_cursor_begin();
+ ccputs(input_buf);
+
+ /* Clear everything past end of history */
+ input_pos = strlen(input_buf);
+ if (input_len > input_pos) {
+ repeat_char(' ', input_len - input_pos);
+ repeat_char('\b', input_len - input_pos);
+ }
+ input_len = input_pos;
+}
+
+/**
+ * Save line to the next history slot
+ */
+static void save_history(void)
+{
+ strzcpy(history[history_next], input_buf,
+ CONFIG_CONSOLE_INPUT_LINE_SIZE);
+}
+
+#endif /* CONFIG_CONSOLE_HISTORY */
+
+static void handle_backspace(void)
+{
+ if (!input_pos)
+ return; /* Already at beginning of line */
+
+ /* Move cursor back */
+ uart_putc('\b');
+
+ /* Print and move anything following the cursor position */
+ if (input_pos != input_len) {
+ ccputs(input_buf + input_pos);
+ memmove(input_buf + input_pos - 1,
+ input_buf + input_pos,
+ input_len - input_pos + 1);
+ } else {
+ input_buf[input_len - 1] = '\0';
+ }
+
+ /* Space over last character and move cursor to correct position */
+ uart_putc(' ');
+ repeat_char('\b', input_len - input_pos + 1);
+
+ input_len--;
+ input_pos--;
+}
+
+/**
+ * Escape code handler
+ *
+ * @param c Next received character.
+ * @return Key code, or -1 if character was eaten
+ */
+static int handle_esc(int c)
+{
+ switch (esc_state) {
+ case ESC_START:
+ if (c == '[') {
+ esc_state = ESC_BRACKET;
+ return -1;
+ } else if (c == 'O') {
+ esc_state = ESC_O;
+ return -1;
+ }
+ break;
+
+ case ESC_BRACKET:
+ if (c == '1') {
+ esc_state = ESC_BRACKET_1;
+ return -1;
+ } else if (c == '3') {
+ esc_state = ESC_BRACKET_3;
+ return -1;
+ }
+
+ if (c == 'A')
+ return KEY_UP_ARROW;
+ else if (c == 'B')
+ return KEY_DOWN_ARROW;
+ else if (c == 'C')
+ return KEY_RIGHT_ARROW;
+ else if (c == 'D')
+ return KEY_LEFT_ARROW;
+ break;
+
+ case ESC_O:
+ if (c == 'F')
+ return KEY_END;
+ break;
+
+ case ESC_BRACKET_1:
+ if (c == '~')
+ return KEY_HOME;
+ break;
+
+ case ESC_BRACKET_3:
+ if (c == '~')
+ return KEY_DEL;
+ break;
+
+ default:
+ break;
+ }
+
+ /* Check if the escape code is done */
+ if (isalpha(c) || c == '~')
+ esc_state = ESC_OUTSIDE;
+ else
+ esc_state = ESC_BAD;
+
+ return -1;
+}
+
+static void console_handle_char(int c)
+{
+ /* Translate CR and CRLF to LF (newline) */
+ if (c == '\r') {
+ last_rx_was_cr = 1;
+ c = '\n';
+ } else if (c == '\n' && last_rx_was_cr) {
+ last_rx_was_cr = 0;
+ return;
+ } else {
+ last_rx_was_cr = 0;
+ }
+
+ /* Handle terminal escape sequences (ESC [ ...) */
+ if (c == 0x1B) {
+ esc_state = ESC_START;
+ return;
+ } else if (esc_state) {
+ c = handle_esc(c);
+ if (c != -1)
+ esc_state = ESC_OUTSIDE;
+ }
+
+ switch (c) {
+ case KEY_DEL:
+ if (input_pos == input_len)
+ break; /* Already at end */
+
+ move_cursor_right();
+
+ /* Drop through to backspace handling */
+ case '\b':
+ case 0x7f:
+ handle_backspace();
+ break;
+
+ case '\n':
+ /* Terminate this line */
+ uart_puts("\r\n");
+
+#ifdef CONFIG_CONSOLE_HISTORY
+ /* Save command in history buffer */
+ if (input_len) {
+ save_history();
+ history_next = (history_next + 1) %
+ CONFIG_CONSOLE_HISTORY;
+ history_pos = history_next;
+ }
+#endif
+
+ /* Handle command */
handle_command(input_buf);
+
+ /* Start new line */
+ input_pos = input_len = 0;
+ input_buf[0] = '\0';
+
+ /* Reprint prompt */
ccputs(PROMPT);
+ break;
+
+ case CTRL('A'):
+ case KEY_HOME:
+ move_cursor_begin();
+ break;
+
+ case CTRL('B'):
+ case KEY_LEFT_ARROW:
+ move_cursor_left();
+ break;
+
+ case CTRL('E'):
+ case KEY_END:
+ move_cursor_end();
+ break;
+
+ case CTRL('F'):
+ case KEY_RIGHT_ARROW:
+ move_cursor_right();
+ break;
+
+ case CTRL('K'):
+ /* Kill to end of line */
+ if (input_pos == input_len)
+ break;
+
+ repeat_char(' ', input_len - input_pos);
+ repeat_char('\b', input_len - input_pos);
+ input_len = input_pos;
+ input_buf[input_len] = '\0';
+ break;
+
+ case CTRL('L'):
+ /* Reprint current */
+ ccputs("\x0c" PROMPT);
+ ccputs(input_buf);
+ repeat_char('\b', input_len - input_pos);
+ break;
+
+#ifdef CONFIG_CONSOLE_HISTORY
+
+ case CTRL('P'):
+ case KEY_UP_ARROW:
+ /* History previous */
+ if (history_pos == history_next)
+ save_history();
+
+ if (--history_pos < 0)
+ history_pos = CONFIG_CONSOLE_HISTORY - 1;
+
+ load_history(history_pos);
+ break;
+
+ case CTRL('N'):
+ case KEY_DOWN_ARROW:
+ /* History next */
+ if (history_pos == history_next)
+ save_history();
+
+ if (++history_pos >= CONFIG_CONSOLE_HISTORY)
+ history_pos = 0;
+
+ load_history(history_pos);
+ break;
+
+#endif /* CONFIG_CONSOLE_HISTORY */
+
+ default:
+ /* Ignore non-printing characters */
+ if (!isprint(c))
+ break;
+
+ /* Ignore if line is full (leaving room for terminating null) */
+ if (input_len >= sizeof(input_buf) - 1)
+ break;
+
+ /* Print character */
+ uart_putc(c);
+
+ /* If not at end of line, print rest of line and move it down */
+ if (input_pos != input_len) {
+ ccputs(input_buf + input_pos);
+ memmove(input_buf + input_pos + 1,
+ input_buf + input_pos,
+ input_len - input_pos + 1);
+ repeat_char('\b', input_len - input_pos);
+ }
+
+ /* Add character to buffer and terminate it */
+ input_buf[input_pos++] = c;
+ input_buf[++input_len] = '\0';
}
}
@@ -183,9 +537,12 @@ void console_task(void)
console_init();
while (1) {
- console_process();
- /* Wait for the next command message */
- task_wait_event(-1);
+ int c = uart_getc();
+
+ if (c == -1)
+ task_wait_event(-1); /* Wait for more input */
+ else
+ console_handle_char(c);
}
}
@@ -274,3 +631,22 @@ DECLARE_CONSOLE_COMMAND(forceen, command_force_enabled,
"Force enable console",
NULL);
#endif
+
+#ifdef CONFIG_CONSOLE_HISTORY
+static int command_history(int argc, char **argv)
+{
+ int i;
+
+ for (i = 0; i < CONFIG_CONSOLE_HISTORY; i++) {
+ int idx = (history_next + i) % CONFIG_CONSOLE_HISTORY;
+ if (history[idx][0])
+ ccprintf("%s\n", history[idx]);
+ }
+
+ return EC_SUCCESS;
+}
+DECLARE_CONSOLE_COMMAND(history, command_history,
+ NULL,
+ "Print console history",
+ NULL);
+#endif
diff --git a/common/uart_buffering.c b/common/uart_buffering.c
index 42d9d4e487..2ccb967e81 100644
--- a/common/uart_buffering.c
+++ b/common/uart_buffering.c
@@ -16,14 +16,10 @@
#include "uart.h"
#include "util.h"
-#define HISTORY_SIZE 8
-
/* Macros to advance in the circular buffers */
#define TX_BUF_NEXT(i) (((i) + 1) & (CONFIG_UART_TX_BUF_SIZE - 1))
#define RX_BUF_NEXT(i) (((i) + 1) & (CONFIG_UART_RX_BUF_SIZE - 1))
#define RX_BUF_PREV(i) (((i) - 1) & (CONFIG_UART_RX_BUF_SIZE - 1))
-#define CMD_HIST_NEXT(i) (((i) + 1) & (HISTORY_SIZE - 1))
-#define CMD_HIST_PREV(i) (((i) - 1) & (HISTORY_SIZE - 1))
/* Macros to calculate difference of pointers in the circular buffers. */
#define TX_BUF_DIFF(i, j) (((i) - (j)) & (CONFIG_UART_TX_BUF_SIZE - 1))
@@ -39,35 +35,10 @@ static volatile int tx_buf_tail;
static volatile char rx_buf[CONFIG_UART_RX_BUF_SIZE];
static volatile int rx_buf_head;
static volatile int rx_buf_tail;
-static volatile char rx_cur_buf[CONFIG_CONSOLE_INPUT_LINE_SIZE];
-static volatile int rx_cur_buf_tail;
-static volatile int rx_cur_buf_head;
-static volatile int rx_cur_buf_ptr;
-static int last_rx_was_cr;
static int tx_snapshot_head;
static int tx_snapshot_tail;
static int uart_suspended;
-static enum {
- ESC_OUTSIDE, /* Not in escape code */
- ESC_START, /* Got ESC */
- ESC_BAD, /* Bad escape sequence */
- ESC_BRACKET, /* Got ESC [ */
- ESC_BRACKET_1, /* Got ESC [ 1 */
- ESC_BRACKET_3, /* Got ESC [ 3 */
- ESC_O, /* Got ESC O */
-} esc_state;
-
-/* Command history */
-struct cmd_history_t {
- volatile int head;
- volatile int tail;
-};
-static struct cmd_history_t cmd_history[HISTORY_SIZE];
-static volatile int cmd_history_head;
-static volatile int cmd_history_tail;
-static volatile int cmd_history_ptr;
-
/**
* Put a single character into the transmit buffer.
*
@@ -95,385 +66,6 @@ static int __tx_char(void *context, int c)
}
/**
- * Write a number directly to the UART.
- *
- * @param val number to write; must be >1.
- */
-static void uart_write_int(int val)
-{
- if (val <= 0)
- return;
-
- if (val > 9)
- uart_write_int(val / 10);
-
- uart_write_char((val % 10) + '0');
-}
-
-static void move_rx_ptr_fwd(void)
-{
- if (rx_cur_buf_ptr != rx_cur_buf_head) {
- ++rx_cur_buf_ptr;
- uart_write_char(0x1B);
- uart_write_char('[');
- uart_write_char('1');
- uart_write_char('C');
- }
-}
-
-static void move_rx_ptr_end(void)
-{
- if (rx_cur_buf_ptr == rx_cur_buf_head)
- return;
-
- uart_write_char(0x1B);
- uart_write_char('[');
- uart_write_int(rx_cur_buf_head - rx_cur_buf_ptr);
- uart_write_char('C');
-
- rx_cur_buf_ptr = rx_cur_buf_head;
-}
-
-static void move_rx_ptr_bwd(void)
-{
- if (rx_cur_buf_ptr != 0) {
- --rx_cur_buf_ptr;
- uart_write_char(0x1B);
- uart_write_char('[');
- uart_write_char('1');
- uart_write_char('D');
- }
-}
-
-static void move_rx_ptr_begin(void)
-{
- if (rx_cur_buf_ptr == 0)
- return;
-
- uart_write_char(0x1B);
- uart_write_char('[');
- uart_write_int(rx_cur_buf_ptr);
- uart_write_char('D');
-
- rx_cur_buf_ptr = 0;
-}
-
-static void repeat_char(char c, int cnt)
-{
- while (cnt--)
- uart_write_char(c);
-}
-
-static void handle_backspace(void)
-{
- int ptr;
-
- if (!rx_cur_buf_ptr)
- return; /* Already at beginning of line */
-
- /* Move cursor back */
- uart_write_char('\b');
-
- /* Move text after cursor and also update rx buffer. */
- for (ptr = rx_cur_buf_ptr; ptr < rx_cur_buf_head; ++ptr) {
- uart_write_char(rx_cur_buf[ptr]);
- rx_cur_buf[ptr - 1] = rx_cur_buf[ptr];
- }
-
- /* Space over last character and move cursor to correct position */
- uart_write_char(' ');
- repeat_char('\b', ptr - rx_cur_buf_ptr + 1);
-
- --rx_cur_buf_head;
- --rx_cur_buf_ptr;
-}
-
-static void handle_kill(void)
-{
- if (rx_cur_buf_ptr == rx_cur_buf_head)
- return;
-
- /* Space over all following characters */
- repeat_char(' ', rx_cur_buf_head - rx_cur_buf_ptr);
- repeat_char('\b', rx_cur_buf_head - rx_cur_buf_ptr);
-
- rx_cur_buf_head = rx_cur_buf_ptr;
-}
-
-static void reprint_current(void)
-{
- int ptr;
-
- uart_write_char(CTRL('L'));
- uart_write_char('>');
- uart_write_char(' ');
-
- for (ptr = 0; ptr < rx_cur_buf_head; ptr++)
- uart_write_char(rx_cur_buf[ptr]);
-
- repeat_char('\b', ptr - rx_cur_buf_ptr);
-}
-
-static void insert_char(char c)
-{
- int ptr;
-
- /* On overflow, discard input */
- if (rx_cur_buf_head == CONFIG_CONSOLE_INPUT_LINE_SIZE && c != '\n')
- return;
-
- /* Move buffer ptr to the end if 'c' is new line */
- if (c == '\n')
- rx_cur_buf_ptr = rx_cur_buf_head;
-
- /* Move text after cursor. */
- for (ptr = rx_cur_buf_ptr; ptr < rx_cur_buf_head; ++ptr)
- uart_write_char(rx_cur_buf[ptr]);
-
- /* Insert character and move cursor to correct position */
- repeat_char('\b', ptr - rx_cur_buf_ptr);
- for (ptr = rx_cur_buf_head; ptr > rx_cur_buf_ptr; --ptr)
- rx_cur_buf[ptr] = rx_cur_buf[ptr - 1];
- rx_cur_buf[rx_cur_buf_ptr] = c;
- ++rx_cur_buf_head;
- ++rx_cur_buf_ptr;
-}
-
-static int rx_buf_space_available(void)
-{
- if (cmd_history_head == cmd_history_tail)
- return CONFIG_UART_RX_BUF_SIZE;
- return RX_BUF_DIFF(cmd_history[cmd_history_tail].tail,
- cmd_history[CMD_HIST_PREV(cmd_history_head)].head);
-}
-
-static void history_save(void)
-{
- int ptr;
- int tail, head;
- int hist_id;
-
- /* If not enough space in rx buffer, discard the oldest history */
- while (rx_buf_space_available() < rx_cur_buf_head)
- cmd_history_tail = CMD_HIST_NEXT(cmd_history_tail);
-
- /* If history buffer is full, discard the oldest one */
- hist_id = cmd_history_head;
- cmd_history_head = CMD_HIST_NEXT(cmd_history_head);
- if (cmd_history_head == cmd_history_tail)
- cmd_history_tail = CMD_HIST_NEXT(cmd_history_tail);
-
- /* Copy the current command, but do not save the '\n' */
- if (hist_id == cmd_history_tail)
- tail = 0;
- else
- tail = RX_BUF_NEXT(cmd_history[CMD_HIST_PREV(hist_id)].head);
- head = tail;
- for (ptr = 0; ptr < rx_cur_buf_head; ++ptr, head = RX_BUF_NEXT(head))
- rx_buf[head] = rx_cur_buf[ptr];
- if (rx_buf[RX_BUF_PREV(head)] == '\n') {
- head = RX_BUF_PREV(head);
- rx_buf[head] = '\0';
- }
-
- cmd_history[hist_id].head = head;
- cmd_history[hist_id].tail = tail;
-}
-
-static void history_load(int id)
-{
- int head = cmd_history[id].head;
- int tail = cmd_history[id].tail;
- int ptr;
-
- cmd_history_ptr = id;
-
- /* Move cursor back to begin of the line. */
- repeat_char('\b', rx_cur_buf_ptr);
-
- /* Load command and print it. */
- for (ptr = tail, rx_cur_buf_ptr = 0; ptr != head;
- ptr = RX_BUF_NEXT(ptr), ++rx_cur_buf_ptr) {
- rx_cur_buf[rx_cur_buf_ptr] = rx_buf[ptr];
- uart_write_char(rx_buf[ptr]);
- }
-
- /* If needed, space over the remaining text. */
- if (rx_cur_buf_ptr < rx_cur_buf_head) {
- repeat_char(' ', rx_cur_buf_head - rx_cur_buf_ptr);
- repeat_char('\b', rx_cur_buf_head - rx_cur_buf_ptr);
- }
-
- rx_cur_buf_head = rx_cur_buf_ptr;
-}
-
-static void history_prev(void)
-{
- if (cmd_history_ptr == cmd_history_tail)
- return;
-
- /*
- * Stash the current command if we are not currently using history.
- * Prevent loading history if there is no space to stash current
- * command.
- */
- if (cmd_history_ptr == cmd_history_head) {
- int last_id = CMD_HIST_PREV(cmd_history_head);
- int last_len = RX_BUF_DIFF(cmd_history[last_id].head,
- cmd_history[last_id].tail);
- if (last_len + rx_cur_buf_head > CONFIG_UART_RX_BUF_SIZE)
- return;
-
- history_save();
- }
-
- cmd_history_ptr = CMD_HIST_PREV(cmd_history_ptr);
- history_load(cmd_history_ptr);
-}
-
-static void history_next(void)
-{
- if (cmd_history_ptr == cmd_history_head)
- return;
-
- cmd_history_ptr = CMD_HIST_NEXT(cmd_history_ptr);
- history_load(cmd_history_ptr);
-
- /* Remove the stashed command if we just loaded it. */
- if (cmd_history_ptr == CMD_HIST_PREV(cmd_history_head))
- cmd_history_head = cmd_history_ptr;
-}
-
-/**
- * Escape code handler
- *
- * @param c Next received character.
- */
-static void handle_esc(int c)
-{
- switch (esc_state) {
- case ESC_START:
- if (c == '[') {
- esc_state = ESC_BRACKET;
- return;
- } else if (c == 'O') {
- esc_state = ESC_O;
- return;
- }
- break;
-
- case ESC_BRACKET:
- if (c == '1') {
- esc_state = ESC_BRACKET_1;
- return;
- } else if (c == '3') {
- esc_state = ESC_BRACKET_3;
- return;
- }
-
- if (c == 'A') /* Up key */
- history_prev();
- else if (c == 'B') /* Down key */
- history_next();
- else if (c == 'C') /* Right key */
- move_rx_ptr_fwd();
- else if (c == 'D') /* Left key */
- move_rx_ptr_bwd();
- break;
-
- case ESC_O:
- if (c == 'F') /* End key */
- move_rx_ptr_end();
- break;
-
- case ESC_BRACKET_1:
- if (c == '~') /* Home key */
- move_rx_ptr_begin();
- break;
-
- case ESC_BRACKET_3:
- if (c == '~') { /* Del key */
- if (rx_cur_buf_ptr != rx_cur_buf_head) {
- move_rx_ptr_fwd();
- handle_backspace();
- }
- }
- break;
-
- default:
- break;
- }
-
- /* Check if the escape code is done */
- if (isalpha(c) || c == '~')
- esc_state = ESC_OUTSIDE;
- else
- esc_state = ESC_BAD;
-}
-
-/**
- * Handle next character of console input.
- */
-static void handle_console_char(int c)
-{
- /* Translate CR and CRLF to LF (newline) */
- if (c == '\r') {
- last_rx_was_cr = 1;
- c = '\n';
- } else if (c == '\n' && last_rx_was_cr) {
- last_rx_was_cr = 0;
- return;
- } else {
- last_rx_was_cr = 0;
- }
-
- /* Handle terminal escape sequences (ESC [ ...) */
- if (c == 0x1B) {
- esc_state = ESC_START;
- return;
- } else if (esc_state) {
- handle_esc(c);
- return;
- }
-
- /* Handle control characters */
- if (c == '\b' || c == 0x7f) {
- handle_backspace();
- } else if (c == CTRL('A')) {
- move_rx_ptr_begin();
- } else if (c == CTRL('E')) {
- move_rx_ptr_end();
- } else if (c == CTRL('L')) {
- reprint_current();
- } else if (c == CTRL('K')) {
- handle_kill();
- } else if (c == CTRL('N')) {
- history_next();
- } else if (c == CTRL('P')) {
- history_prev();
- } else if (c == CTRL('Q')) {
- uart_suspended = 1;
- uart_tx_stop();
- } else if (c == CTRL('S')) {
- uart_suspended = 0;
- if (uart_tx_stopped())
- uart_tx_start();
- } else if (c == '\n') { /* Newline */
- uart_write_char('\r');
- uart_write_char('\n');
- insert_char(c);
- console_has_input();
- } else if (isprint(c)) {
- /*
- * Normal printable character. Echo directly to the transmit
- * FIFO so we don't interfere with the transmit buffer.
- */
- uart_write_char(c);
- insert_char(c);
- }
-}
-
-/**
* Copy output from buffer until TX fifo full or output buffer empty.
*
* May be called from interrupt context.
@@ -491,9 +83,33 @@ static void fill_tx_fifo(void)
*/
void uart_process(void)
{
+ int got_input = 0;
+
/* Copy input from buffer until RX fifo empty */
- while (uart_rx_available())
- handle_console_char(uart_read_char());
+ while (uart_rx_available()) {
+ int c = uart_read_char();
+ int rx_buf_next = RX_BUF_NEXT(rx_buf_head);
+
+ if (c == CTRL('Q')) {
+ /* Software flow control - XOFF */
+ uart_suspended = 1;
+ uart_tx_stop();
+ } else if (c == CTRL('S')) {
+ /* Software flow control - XON */
+ uart_suspended = 0;
+ if (uart_tx_stopped())
+ uart_tx_start();
+ } else if (rx_buf_next != rx_buf_tail) {
+ /* Buffer all other input */
+ rx_buf[rx_buf_head] = c;
+ rx_buf_head = rx_buf_next;
+ }
+
+ got_input = 1;
+ }
+
+ if (got_input)
+ console_has_input();
if (uart_suspended)
return;
@@ -506,6 +122,16 @@ void uart_process(void)
uart_tx_stop();
}
+int uart_putc(int c)
+{
+ int rv = __tx_char(NULL, c);
+
+ if (!uart_suspended && uart_tx_stopped())
+ uart_tx_start();
+
+ return rv ? EC_ERROR_OVERFLOW : EC_SUCCESS;
+}
+
int uart_puts(const char *outstr)
{
/* Put all characters in the output buffer */
@@ -591,44 +217,12 @@ void uart_flush_input(void)
uart_process();
/* Clear the input buffer */
- rx_cur_buf_head = 0;
rx_buf_tail = rx_buf_head;
/* Re-enable interrupts */
uart_enable_interrupt();
}
-int uart_peek(int c)
-{
- int index = -1;
- int i = 0;
-
- /*
- * Disable interrupts while we pull characters out, because the
- * interrupt handler can also modify the tail pointer.
- */
- uart_disable_interrupt();
-
- /*
- * Call interrupt handler to empty the hardware FIFO. The minimum
- * FIFO trigger depth is 1/8 (2 chars), so this is the only way to
- * ensure we've pulled the very last character out of the FIFO.
- */
- uart_process();
-
- for (i = 0; i < rx_cur_buf_head; ++i) {
- if (rx_cur_buf[i] == c) {
- index = i;
- break;
- }
- }
-
- /* Re-enable interrupts */
- uart_enable_interrupt();
-
- return index;
-}
-
int uart_getc(void)
{
int c;
@@ -657,37 +251,20 @@ int uart_gets(char *dest, int size)
int got = 0;
int c;
- /*
- * Disable interrupts while we pull characters out, because the
- * interrupt handler can also modify the tail pointer.
- */
- uart_disable_interrupt();
-
- /* Call interrupt handler to empty the hardware FIFO */
- uart_process();
-
- /* Remove the stashed command if any. */
- if (cmd_history_ptr != cmd_history_head)
- cmd_history_head = CMD_HIST_PREV(cmd_history_head);
+ /* Read characters */
+ while (got < size - 1) {
+ c = uart_getc();
- /* Record last command. */
- if (!(rx_cur_buf_head == 1 && rx_cur_buf[0] == '\n'))
- history_save();
- cmd_history_ptr = cmd_history_head;
+ /* Stop on input buffer empty */
+ if (c == -1)
+ break;
- /* Read characters */
- while (got < size - 1 && got < rx_cur_buf_head) {
- c = rx_cur_buf[got];
dest[got++] = c;
+
+ /* Stop after newline */
if (c == '\n')
- break; /* Stop on newline */
+ break;
}
- rx_cur_buf_ptr = 0;
- rx_cur_buf_head = 0;
- rx_cur_buf_tail = rx_cur_buf_head;
-
- /* Re-enable interrupts */
- uart_enable_interrupt();
/* Null-terminate */
dest[got] = '\0';
diff --git a/include/config.h b/include/config.h
index 635c47f9b3..3133fc0503 100644
--- a/include/config.h
+++ b/include/config.h
@@ -165,6 +165,13 @@
*/
#define CONFIG_CONSOLE_CMDHELP
+/*
+ * Number of entries in console history buffer.
+ *
+ * Boards may #undef this to reduce memory usage.
+ */
+#define CONFIG_CONSOLE_HISTORY 8
+
/* Max length of a single line of input */
#define CONFIG_CONSOLE_INPUT_LINE_SIZE 80
diff --git a/include/uart.h b/include/uart.h
index d4685cb2e7..05d4aee036 100644
--- a/include/uart.h
+++ b/include/uart.h
@@ -32,6 +32,14 @@ int uart_init_done(void);
*/
/**
+ * Put a single character to the UART, like putchar().
+ *
+ * @param c Character to put
+ * @return EC_SUCCESS, or non-zero if output was truncated.
+ */
+int uart_putc(int c);
+
+/**
* Put a null-terminated string to the UART, like fputs().
*
* @return EC_SUCCESS, or non-zero if output was truncated.
@@ -77,16 +85,6 @@ void uart_flush_output(void);
void uart_flush_input(void);
/**
- * Non-destructively check for a character in the input buffer.
- *
- * @param c Character to search for
- *
- * @return the offset into the input buffer of the first match, or -1 if no
- * match found in the input buffer.
- */
-int uart_peek(int c);
-
-/**
* Read a single character of input, similar to fgetc().
*
* @return the character, or -1 if no input waiting.
@@ -99,13 +97,7 @@ int uart_getc(void);
* Reads input until one of the following conditions is met:
* (1) <size-1> characters have been read.
* (2) A newline ('\n') has been read.
- * (3) The input buffer is empty.
- *
- * Condition (3) means this call never blocks. This is important
- * because it prevents a race condition where the caller calls
- * uart_peek() to see if input is waiting, or is notified by the
- * callack that input is waiting, but then the input buffer overflows
- * or someone else grabs the input before uart_gets() is called.
+ * (3) The input buffer is empty (this keeps the call from blocking).
*
* Characters are stored in <dest> and are null-terminated.
* Characters include the newline if present, so that the caller can