The unified diff between revisions [961b04dd..] and [4f22e7ef..] is displayed below. It can also be downloaded as a raw diff.
This diff has been restricted to the following files: 'uart.c'
# # old_revision [961b04ddb07ba2b5dd6bccfa66a03e442e40d8f0] # new_revision [4f22e7ef7d3064e3b51a5b868a4722f3f13c747b] # # patch "uart.c" # from [b336e227dc442fef728c0a695671328c7a265dd1] # to [7a38c486dc1280695e1e62c6d3a76d6c9f849f67] # ============================================================ --- uart.c b336e227dc442fef728c0a695671328c7a265dd1 +++ uart.c 7a38c486dc1280695e1e62c6d3a76d6c9f849f67 @@ -1,4 +1,7 @@ #include "uart.h" +#include "types.h" +#include "interrupt.h" +#include "event.h" #define UARTBASE 0xE000C000 @@ -22,6 +25,21 @@ #define U0THRE ((UREG(LSR) & (1<<5))) /* UART0 transmitter holding register is empty */ #define U0DR ((UREG(LSR) & (1<<0))) /* UART0 data ready */ +#define UART_TXBUFSIZE 128 +#define UART_RXBUFSIZE 128 + +char uart_txbuf[UART_TXBUFSIZE]; +char uart_rxbuf[UART_RXBUFSIZE]; +volatile int uart_txread; +volatile int uart_txwrite; +volatile int uart_rxread; +volatile int uart_rxwrite; +volatile bool tx_running; + +void __attribute__((interrupt("IRQ"))) uart_interrupt_handler(void); + +#ifdef USE_UART + void init_uart(void) { UREG(FDR) = 0x10; /* DivAddVal = 0, MulVal = 1 */ @@ -31,13 +49,96 @@ void init_uart(void) UREG(DLL) = 0x08; /* 14745600 / (16*115200) */ UREG(LCR) = 0x13; UREG(FCR) = 0x07; + + uart_txread = 0; + uart_txwrite = 0; + uart_rxread = 0; + uart_rxwrite = 0; + tx_running = FALSE; + interrupt_register(UART0, uart_interrupt_handler); + + UREG(IER) = 0x03; /* RBR and THRE interrupt enable */ } void putch(char c) { - while (!U0THRE); - UREG(THR) = c; + /* Wait for space in the buffer */ + while (uart_txread == ((uart_txwrite+1) % UART_TXBUFSIZE)); + + if (uart_txread == uart_txwrite) { + if (U0THRE) { + tx_running = TRUE; + UREG(THR) = c; + return; + } + } + + uart_txbuf[uart_txwrite] = c; + uart_txwrite = (uart_txwrite + 1) % UART_TXBUFSIZE; + + if (!tx_running) { + if (uart_txread != uart_txwrite) { + tx_running = TRUE; + uart_txread = (uart_txread + 1) % UART_TXBUFSIZE; + UREG(THR) = c; + } + } } +void __attribute__((interrupt("IRQ"))) uart_interrupt_handler(void) +{ + bool active = FALSE; + int source; + int i; + /* uart_txread and uart_txwrite are volatile. We don't need + * to treat them as such in this handler, so let the compiler + * have an easier time. + */ + int local_txwrite; + int local_txread; + int local_rxwrite; + int local_rxread; + + source = UREG(IIR); + + switch (source & 0x0e) { + case 0x4: /* Receive Data Available */ + case 0xc: /* Character Time-out Indicator */ + local_rxwrite = uart_rxwrite; + local_rxread = uart_rxread; + while (U0DR) { + unsigned char c = UREG(RBR); + if (local_rxread != + ((local_rxwrite+1) % UART_RXBUFSIZE)) { + uart_rxbuf[local_rxwrite] = c; + local_rxwrite = (local_rxwrite + 1) % + UART_RXBUFSIZE; + } + } + uart_rxwrite = local_rxwrite; + event_set(EVENT_UART_INPUT); + break; + + case 0x2: /* THRE interrupt */ + local_txwrite = uart_txwrite; + local_txread = uart_txread; + for (i = 0; (i < 16) && (local_txwrite != local_txread); i++) { + UREG(THR) = uart_txbuf[local_txread]; + local_txread = (local_txread + 1) % UART_TXBUFSIZE; + active = TRUE; + } + uart_txread = local_txread; + if (!active) + tx_running = FALSE; + break; + + case 0x6: /* Receive Line Status */ + default: /* Anything else */ + break; + } + + interrupt_clear(); +} + void putstr(char *s) { while (*s) putch(*s++); } @@ -56,6 +157,31 @@ void putint(unsigned int n) { putstr(s+i); } +void putint_s(int n) { + char s[12]; + int i; + int neg; + + /* OK, technically, this might not work properly for the most + * negative possible number. Oh well. + */ + neg = (n < 0); + if (neg) + n = -n; + + i = 11; + s[i] = '\0'; + + do { + s[--i] = n % 10 + '0'; + } while ((n /= 10) > 0); + + if (neg) + s[--i] = '-'; + + putstr(s+i); +} + void puthex(unsigned int n) { char s[9]; int i; @@ -73,7 +199,12 @@ void puthex(unsigned int n) { putstr(s+i); } -char getch(void) { - while (!U0DR); - return UREG(RBR); +bool getch(char *c) { + if (uart_rxread == uart_rxwrite) + return FALSE; + + *c = uart_rxbuf[uart_rxread]; + uart_rxread = (uart_rxread + 1) % UART_RXBUFSIZE; + return TRUE; } +#endif