Add console functionality

Keys I keep hitting should work like I expect them to.

Home or Ctrl+A = move to beginning of line
End or Ctrl+E = move to end of line
Del = delete-right
Ctrl+K = delete to end of line
Ctrl+L = clear screen and reprint current line
Ctrl+N = next command
Ctrl+P = previous command

Also, improve filtering of escape sequences and non-printable
characters, so hitting unsupported keys or control codes doesn't mess
up the current line of input.

BUG=chrome-os-partner:11666
TEST=manual

  type 'fhelpbar'
  home -> cursor moves to beginning of line
  Ctrl+E -> cursor moves to end of line
  Ctrl+A -> cursor moves to beginning of line
    (of course, if you're using Minicom, you'll need to type Ctrl+A A, since
     Minicom uses Ctrl+A as its control key)
  del -> 'helpbar'
  end -> cursor moves to end of line
  left-arrow 3 times -> cursor moves under 'b'
  Ctrl+L -> screen clears, cursor still under 'b'
  Ctrl+K -> 'help'
  Ctrl+Y Page-Up Page-Down -> nothing printed
  enter -> prints known commands (output of 'help' command)
  Ctrl+P -> 'help'
  Ctrl+N -> empty command line

Change-Id: Id893c93b26db8f3deed6ea8be5aab88a3daaead4
Signed-off-by: Randall Spangler <rspangler@chromium.org>
Reviewed-on: https://gerrit.chromium.org/gerrit/28143
This commit is contained in:
Randall Spangler
2012-07-22 16:16:01 -07:00
committed by Gerrit
parent 085b31222b
commit e27e2f0229
3 changed files with 228 additions and 83 deletions

View File

@@ -42,6 +42,9 @@
/* Macro to calculate difference of pointers in the circular receive buffer. */
#define RX_BUF_DIFF(i, j) (((i) - (j)) & (CONFIG_UART_RX_BUF_SIZE - 1))
/* ASCII control character; for example, CTRL('C') = ^C */
#define CTRL(c) ((c) - '@')
/* Transmit and receive buffers */
static volatile char tx_buf[CONFIG_UART_TX_BUF_SIZE];
static volatile int tx_buf_head;
@@ -54,8 +57,16 @@ 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;
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 {
@@ -92,6 +103,23 @@ 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) {
@@ -103,6 +131,18 @@ static void move_rx_ptr_fwd(void)
}
}
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)
{
@@ -115,6 +155,18 @@ static void move_rx_ptr_bwd(void)
}
}
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)
{
@@ -122,31 +174,55 @@ static void repeat_char(char c, int cnt)
uart_write_char(c);
}
static void handle_backspace(void)
{
if (rx_cur_buf_ptr != 0) {
/* Move texts after cursor and also update rx buffer. */
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];
}
int ptr;
/* Space over last character and move cursor back to correct
* position.
*/
uart_write_char(' ');
repeat_char('\b', ptr - rx_cur_buf_ptr + 1);
if (!rx_cur_buf_ptr)
return; /* Already at beginning of line */
--rx_cur_buf_head;
--rx_cur_buf_ptr;
/* Move cursor back */
uart_write_char('\b');
/* Move texts 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];
}
else
/* Cursor moves pass the first character. Move it back. */
uart_write_char(' ');
/* 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)
{
@@ -292,6 +368,127 @@ static void history_next(void)
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 == '\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);
}
}
/* Helper for UART processing */
void uart_process(void)
@@ -300,70 +497,13 @@ void uart_process(void)
while (uart_rx_available()) {
int c = uart_read_char();
/* Handle console mode echoing and translation */
if (console_mode) {
/* 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;
continue;
} else {
last_rx_was_cr = 0;
}
/* Handle left and right key, and eat other terminal
* escape sequences (ESC [ ...).
* Would be really cool if we used arrow keys to edit
* command history, but for now it's sufficient just to
* keep them from causing problems. */
if (c == 0x1B) {
in_escape = 1;
esc_seq_char = c;
continue;
} else if (in_escape) {
if (esc_seq_char == 0x1B && c == '[')
esc_seq_char = '[';
else if (esc_seq_char == '[') {
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
esc_seq_char = 0;
if (isalpha(c) || c == '~') {
esc_seq_char = 0;
in_escape = 0;
}
continue;
}
/* Echo characters directly to the transmit FIFO so we
* don't interfere with the transmit buffer. */
if (c == '\n')
uart_write_char('\r');
uart_write_char(c == 0x7f ? '\b' : c);
/* Handle backspace if we can */
if (c == '\b' || c == 0x7f) {
handle_backspace();
continue;
}
/* Handle console mode echoing and translation */
handle_console_char(c);
} else {
/* Not in console mode, so simply store character */
insert_char(c);
}
insert_char(c);
/* Call console callback on newline, if in console mode */
if (console_mode && c == '\n')
console_has_input();
}
/* Copy output from buffer until TX fifo full or output buffer empty */

View File

@@ -1,4 +1,4 @@
/* Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
@@ -35,6 +35,10 @@ int isalpha(int c)
return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
}
int isprint(int c)
{
return c >= ' ' && c <= '~';
}
int tolower(int c)
{

View File

@@ -66,6 +66,7 @@ int atoi(const char *nptr);
int isdigit(int c);
int isspace(int c);
int isalpha(int c);
int isprint(int c);
void *memcpy(void *dest, const void *src, int len);
void *memset(void *dest, int c, int len);
void *memmove(void *dest, const void *src, int len);