summaryrefslogtreecommitdiff
path: root/common/uart_buffering.c
diff options
context:
space:
mode:
authorRandall Spangler <rspangler@chromium.org>2013-08-01 14:36:43 -0700
committerDave Parker <dparker@chromium.org>2013-08-09 23:35:27 -0700
commite73ba46897a09ce7f1d60d344b15182468f40cde (patch)
treea7fc5036669c075d9640c1100410abda5ce016eb /common/uart_buffering.c
parent831e6f469a9c98e8a226cf22ded6cc9f6606e808 (diff)
downloadchrome-ec-e73ba46897a09ce7f1d60d344b15182468f40cde.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: I00bde51b7937b8277455b090917d0a1a82782e87 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/65442 Commit-Queue: Dave Parker <dparker@chromium.org> Reviewed-by: Dave Parker <dparker@chromium.org> Tested-by: Dave Parker <dparker@chromium.org>
Diffstat (limited to 'common/uart_buffering.c')
-rw-r--r--common/uart_buffering.c513
1 files changed, 45 insertions, 468 deletions
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';