diff options
-rw-r--r-- | chip/stm32/config-stm32f100.h | 4 | ||||
-rw-r--r-- | common/console.c | 400 | ||||
-rw-r--r-- | common/uart_buffering.c | 513 | ||||
-rw-r--r-- | include/config.h | 7 | ||||
-rw-r--r-- | include/uart.h | 26 |
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 |