summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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 4bfe375510..f86ab58a17 100644
--- a/include/config.h
+++ b/include/config.h
@@ -174,6 +174,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