summaryrefslogtreecommitdiff
path: root/common/uart_buffering.c
diff options
context:
space:
mode:
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';