summaryrefslogtreecommitdiff
path: root/common/uart_buffering.c
diff options
context:
space:
mode:
authorVic Yang <victoryang@google.com>2012-02-01 17:25:01 +0800
committerVic Yang <victoryang@google.com>2012-02-07 10:23:59 +0800
commit730f099c83b67861a269560e2bb5da1bd6bee503 (patch)
treef31cbcfcc2eeac7f387e7c11a53aa3960c28c28c /common/uart_buffering.c
parentd3e1de758cfa3a7dd990f0fb63eddbb1ae870418 (diff)
downloadchrome-ec-730f099c83b67861a269560e2bb5da1bd6bee503.tar.gz
Handle up/down arrow keys for UART console.
Record commands used previously and use up/down arrow key to navigate in the command history. Also removed the command '.' of repeating last command as we can use up arrow key now. Also changed the behaviour of uart_write_char() to be blocking on transmit FIFO full, so that we do not lose echoed character and do not need to flush. BUG=chrome-os-partner:7815 TEST=Type 'help' and enter. Then type 'aaaa' and up arrow key, should show 'help', and pressing enter prints help. Type 'hellp' and enter. Then type 'aaaaaa' and up arrow key, should show 'hellp'. Should be able to use left/right arrow key and backspace to correct it to 'help', and pressing enter prints help. Type 'help' and enter. Then type 'aaa', up arrow key, and down arrow key. Should show 'aaa'. Change-Id: I65c615d61bf63acb31bea329aa91a3202d4db0ad
Diffstat (limited to 'common/uart_buffering.c')
-rw-r--r--common/uart_buffering.c239
1 files changed, 191 insertions, 48 deletions
diff --git a/common/uart_buffering.c b/common/uart_buffering.c
index 6b43041ce3..4706ce895d 100644
--- a/common/uart_buffering.c
+++ b/common/uart_buffering.c
@@ -15,13 +15,19 @@
/* Buffer sizes; should be power of 2 */
#define TX_BUF_SIZE 512
#define RX_BUF_SIZE 128 /* suggest larger than 80 to copy&paste script. */
+#define HISTORY_SIZE 8
-/* Macros to advance in the circular transmit and receive buffers */
+/* The size limit of single command */
+#define RX_LINE_SIZE 80
+
+/* Macros to advance in the circular buffers */
#define TX_BUF_NEXT(i) (((i) + 1) & (TX_BUF_SIZE - 1))
#define RX_BUF_NEXT(i) (((i) + 1) & (RX_BUF_SIZE - 1))
#define RX_BUF_PREV(i) (((i) - 1) & (RX_BUF_SIZE - 1))
+#define CMD_HIST_NEXT(i) (((i) + 1) & (HISTORY_SIZE - 1))
+#define CMD_HIST_PREV(i) (((i) - 1) & (HISTORY_SIZE - 1))
-/* Macro to calculate difference of pointers in the circular receive buffers */
+/* Macro to calculate difference of pointers in the circular receive buffer. */
#define RX_BUF_DIFF(i, j) (((i) - (j)) & (RX_BUF_SIZE - 1))
/* Transmit and receive buffers */
@@ -31,11 +37,24 @@ static volatile int tx_buf_tail;
static volatile char rx_buf[RX_BUF_SIZE];
static volatile int rx_buf_head;
static volatile int rx_buf_tail;
-static volatile int rx_buf_ptr;
+static volatile char rx_cur_buf[RX_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 in_escape;
static char esc_seq_char;
+/* 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;
+
static int console_mode = 1;
/* TODO: should have an API to set raw mode for the UART. In raw
@@ -64,8 +83,8 @@ static int __tx_char(int c)
static void move_rx_ptr_fwd(void)
{
- if (rx_buf_ptr != rx_buf_head) {
- rx_buf_ptr = RX_BUF_NEXT(rx_buf_ptr);
+ if (rx_cur_buf_ptr != rx_cur_buf_head) {
+ ++rx_cur_buf_ptr;
uart_write_char(0x1B);
uart_write_char('[');
uart_write_char('1');
@@ -75,8 +94,8 @@ static void move_rx_ptr_fwd(void)
static void move_rx_ptr_bwd(void)
{
- if (rx_buf_ptr != rx_buf_tail) {
- rx_buf_ptr = RX_BUF_PREV(rx_buf_ptr);
+ if (rx_cur_buf_ptr != 0) {
+ --rx_cur_buf_ptr;
uart_write_char(0x1B);
uart_write_char('[');
uart_write_char('1');
@@ -84,30 +103,30 @@ static void move_rx_ptr_bwd(void)
}
}
-static void move_cursor_back(int dist)
+static void repeat_char(char c, int cnt)
{
- while (dist--)
- uart_write_char('\b');
+ while (cnt--)
+ uart_write_char(c);
}
static void handle_backspace(void)
{
- if (rx_buf_ptr != rx_buf_tail) {
+ if (rx_cur_buf_ptr != 0) {
/* Move texts after cursor and also update rx buffer. */
- int ptr = rx_buf_ptr;
- while (ptr != rx_buf_head) {
- uart_write_char(rx_buf[ptr]);
- rx_buf[RX_BUF_PREV(ptr)] = rx_buf[ptr];
- ptr = RX_BUF_NEXT(ptr);
+ int ptr;
+ 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 back to correct
* position.
*/
uart_write_char(' ');
- move_cursor_back(RX_BUF_DIFF(ptr, rx_buf_ptr) + 1);
+ repeat_char('\b', ptr - rx_cur_buf_ptr + 1);
- rx_buf_head = RX_BUF_PREV(rx_buf_head);
- rx_buf_ptr = RX_BUF_PREV(rx_buf_ptr);
+ --rx_cur_buf_head;
+ --rx_cur_buf_ptr;
}
else
/* Cursor moves pass the first character. Move it back. */
@@ -118,30 +137,139 @@ static void insert_char(char c)
{
int ptr;
+ /* On overflow, discard input */
+ if (rx_cur_buf_head == RX_LINE_SIZE)
+ return;
+
/* Move buffer ptr to the end if 'c' is new line */
if (c == '\n')
- rx_buf_ptr = rx_buf_head;
+ rx_cur_buf_ptr = rx_cur_buf_head;
/* Move text after cursor. */
- ptr = rx_buf_ptr;
- while (ptr != rx_buf_head) {
- uart_write_char(rx_buf[ptr]);
- ptr = RX_BUF_NEXT(ptr);
- }
+ for (ptr = rx_cur_buf_ptr; ptr < rx_cur_buf_head; ++ptr)
+ uart_write_char(rx_cur_buf[ptr]);
+
/* Insert character to rx buffer and move cursor to correct
* position.
*/
- while (ptr != rx_buf_ptr) {
- rx_buf[ptr] = rx_buf[RX_BUF_PREV(ptr)];
- uart_write_char('\b');
- ptr = RX_BUF_PREV(ptr);
+ 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;
+
+ /* Insert character directly into rx_buf if not in console mode. */
+ if (!console_mode) {
+ rx_buf[rx_buf_head] = c;
+ rx_buf_head = RX_BUF_NEXT(rx_buf_head);
+ if (rx_buf_tail == rx_buf_head)
+ rx_buf_tail = RX_BUF_NEXT(rx_buf_tail);
}
- rx_buf[rx_buf_ptr] = c;
- rx_buf_head = RX_BUF_NEXT(rx_buf_head);
- rx_buf_ptr = RX_BUF_NEXT(rx_buf_ptr);
- /* On overflow, discard oldest output */
- if (rx_buf_head == rx_buf_tail)
- rx_buf_tail = RX_BUF_NEXT(rx_buf_tail);
+}
+
+static int rx_buf_space_available(void)
+{
+ if (cmd_history_head == cmd_history_tail)
+ return 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 there is 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 we do not save the '\n' */
+ if (hist_id == cmd_history_tail)
+ tail = 0;
+ else
+ tail = cmd_history[CMD_HIST_PREV(hist_id)].head + 1;
+ 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 > 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;
}
/* Helper for UART processing */
@@ -177,10 +305,14 @@ void uart_process(void)
if (esc_seq_char == 0x1B && c == '[')
esc_seq_char = '[';
else if (esc_seq_char == '[') {
- if (c == 'D') /* Left key */
- move_rx_ptr_bwd();
+ 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();
esc_seq_char = 0;
}
else
@@ -194,9 +326,7 @@ void uart_process(void)
}
/* Echo characters directly to the transmit FIFO so we
- * don't interfere with the transmit buffer. This
- * means that if a lot of output is happening, input
- * characters won't always be properly echoed. */
+ * don't interfere with the transmit buffer. */
if (c == '\n')
uart_write_char('\r');
uart_write_char(c);
@@ -229,6 +359,9 @@ void uart_process(void)
void uart_set_console_mode(int enable)
{
console_mode = enable;
+
+ if (!enable)
+ rx_cur_buf_ptr = rx_cur_buf_head;
}
@@ -429,6 +562,7 @@ 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 */
@@ -450,10 +584,9 @@ int uart_peek(int c)
* ensure we've pulled the very last character out of the FIFO. */
uart_process();
- for (i = rx_buf_tail; i != rx_buf_head; i = RX_BUF_NEXT(i)) {
- if (rx_buf[i] == c) {
- index = (RX_BUF_SIZE + i - rx_buf_tail) &
- (RX_BUF_SIZE - 1);
+ for (i = 0; i < rx_cur_buf_head; ++i) {
+ if (rx_cur_buf[i] == c) {
+ index = i;
break;
}
}
@@ -501,15 +634,25 @@ int uart_gets(char *dest, int size)
/* 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);
+
+ /* Record last command. */
+ if (!(rx_cur_buf_head == 1 && rx_cur_buf[0] == '\n'))
+ history_save();
+ cmd_history_ptr = cmd_history_head;
+
/* Read characters */
- while (got < size - 1 && rx_buf_tail != rx_buf_head) {
- c = rx_buf[rx_buf_tail];
+ while (got < size - 1 && got < rx_cur_buf_head) {
+ c = rx_cur_buf[got];
dest[got++] = c;
- rx_buf_tail = RX_BUF_NEXT(rx_buf_tail);
if (c == '\n')
break; /* Stop on newline */
}
- rx_buf_ptr = rx_buf_tail;
+ rx_cur_buf_ptr = 0;
+ rx_cur_buf_head = 0;
+ rx_cur_buf_tail = rx_cur_buf_head;
/* Re-enable interrupts */
uart_enable_interrupt();