diff options
Diffstat (limited to 'common/uart_buffering.c')
-rw-r--r-- | common/uart_buffering.c | 106 |
1 files changed, 83 insertions, 23 deletions
diff --git a/common/uart_buffering.c b/common/uart_buffering.c index 560e783c41..779835ae24 100644 --- a/common/uart_buffering.c +++ b/common/uart_buffering.c @@ -9,10 +9,12 @@ #include "common.h" #include "console.h" +#include "hooks.h" #include "host_command.h" #include "printf.h" #include "system.h" #include "task.h" +#include "timer.h" #include "uart.h" #include "util.h" @@ -28,6 +30,14 @@ /* ASCII control character; for example, CTRL('C') = ^C */ #define CTRL(c) ((c) - '@') +/* + * Interval between rechecking the receive DMA head pointer, after a character + * of input has been detected by the normal tick task. There will be + * CONFIG_UART_RX_DMA_RECHECKS rechecks between this tick and the next tick. + */ +#define RX_DMA_RECHECK_INTERVAL (HOOK_TICK_INTERVAL / \ + (CONFIG_UART_RX_DMA_RECHECKS + 1)) + /* Transmit and receive buffers */ static volatile char tx_buf[CONFIG_UART_TX_BUF_SIZE]; static volatile int tx_buf_head; @@ -70,7 +80,7 @@ static int __tx_char(void *context, int c) /** * Process UART output via DMA */ -static void uart_process_output_dma(void) +void uart_process_output(void) { /* Size of current DMA transfer */ static int tx_dma_in_progress; @@ -81,6 +91,9 @@ static void uart_process_output_dma(void) */ int head = tx_buf_head; + if (uart_suspended) + return; + /* If DMA is still busy, nothing to do. */ if(!uart_tx_dma_ready()) return; @@ -108,16 +121,13 @@ static void uart_process_output_dma(void) uart_tx_dma_start((char *)(tx_buf + tx_buf_tail), tx_dma_in_progress); } -#endif /* CONFIG_UART_TX_DMA */ +#else /* !CONFIG_UART_TX_DMA */ void uart_process_output(void) { if (uart_suspended) return; -#ifdef CONFIG_UART_TX_DMA - uart_process_output_dma(); -#else /* Copy output from buffer until TX fifo full or output buffer empty */ while (uart_tx_ready() && (tx_buf_head != tx_buf_tail)) { uart_write_char(tx_buf[tx_buf_tail]); @@ -127,9 +137,57 @@ void uart_process_output(void) /* If output buffer is empty, disable transmit interrupt */ if (tx_buf_tail == tx_buf_head) uart_tx_stop(); -#endif } +#endif /* !CONFIG_UART_TX_DMA */ + +#ifdef CONFIG_UART_RX_DMA + +void uart_process_input(void) +{ + static int fast_rechecks; + int cur_head = rx_buf_head; + + int i; + + /* Update receive buffer head from current DMA receive pointer */ + rx_buf_head = uart_rx_dma_head(); + + /* Handle software flow control characters */ + for (i = cur_head; i != rx_buf_head; i = RX_BUF_NEXT(i)) { + int c = rx_buf[i]; + + 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; + uart_tx_start(); + } + } + + if (rx_buf_head != cur_head) { + console_has_input(); + fast_rechecks = CONFIG_UART_RX_DMA_RECHECKS; + } + + /* + * Input is checked once a tick when the console is idle. When input + * is received, check more frequently for a bit, so that the console is + * more responsive. + */ + if (fast_rechecks) { + fast_rechecks--; + hook_call_deferred(uart_process_input, RX_DMA_RECHECK_INTERVAL); + } +} +DECLARE_HOOK(HOOK_TICK, uart_process_input, HOOK_PRIO_DEFAULT); +DECLARE_DEFERRED(uart_process_input); + +#else /* !CONFIG_UART_RX_DMA */ + void uart_process_input(void) { int got_input = 0; @@ -151,15 +209,16 @@ void uart_process_input(void) /* Buffer all other input */ rx_buf[rx_buf_head] = c; rx_buf_head = rx_buf_next; + got_input = 1; } - - got_input = 1; } if (got_input) console_has_input(); } +#endif /* !CONFIG_UART_RX_DMA */ + int uart_putc(int c) { int rv = __tx_char(NULL, c); @@ -241,26 +300,27 @@ void uart_flush_output(void) int uart_getc(void) { - int c; - - /* Disable interrupts */ - uart_disable_interrupt(); - - /* Call interrupt handler to empty the hardware FIFO */ - uart_process_input(); - - if (rx_buf_tail == rx_buf_head) { - c = -1; /* No pending input */ - } else { - c = rx_buf[rx_buf_tail]; + /* Look for a non-flow-control character */ + while(rx_buf_tail != rx_buf_head) { + int c = rx_buf[rx_buf_tail]; rx_buf_tail = RX_BUF_NEXT(rx_buf_tail); + + if (c != CTRL('Q') && c != CTRL('S')) + return c; } - /* Re-enable interrupts */ - uart_enable_interrupt(); + /* If we're still here, no input */ + return -1; +} - return c; +#ifdef CONFIG_UART_RX_DMA +static void uart_rx_dma_init(void) +{ + /* Start receiving */ + uart_rx_dma_start((char *)rx_buf, CONFIG_UART_RX_BUF_SIZE); } +DECLARE_HOOK(HOOK_INIT, uart_rx_dma_init, HOOK_PRIO_DEFAULT); +#endif /*****************************************************************************/ /* Host commands */ |