diff options
author | Frans Kaashoek <[email protected]> | 2020-08-10 13:05:17 -0400 |
---|---|---|
committer | GitHub <[email protected]> | 2020-08-10 13:05:17 -0400 |
commit | c31d35d8031c88b7e1ea8657cc9806dfdd4c3ef9 (patch) | |
tree | a6c903e1c61c08f4cb87700c320752a737081dcb /kernel/uart.c | |
parent | 90eb90b5e203299427c3fde8c996a48835fc93cf (diff) | |
parent | d8fe1773b26758c7c7b8f36724cd822555b33612 (diff) | |
download | xv6-labs-c31d35d8031c88b7e1ea8657cc9806dfdd4c3ef9.tar.gz xv6-labs-c31d35d8031c88b7e1ea8657cc9806dfdd4c3ef9.tar.bz2 xv6-labs-c31d35d8031c88b7e1ea8657cc9806dfdd4c3ef9.zip |
Merge branch 'riscv' into riscv
Diffstat (limited to 'kernel/uart.c')
-rw-r--r-- | kernel/uart.c | 121 |
1 files changed, 105 insertions, 16 deletions
diff --git a/kernel/uart.c b/kernel/uart.c index 3a5cdc4..daf9f04 100644 --- a/kernel/uart.c +++ b/kernel/uart.c @@ -18,18 +18,35 @@ // the UART control registers. // some have different meanings for // read vs write. -// http://byterunner.com/16550.html -#define RHR 0 // receive holding register (for input bytes) -#define THR 0 // transmit holding register (for output bytes) -#define IER 1 // interrupt enable register -#define FCR 2 // FIFO control register -#define ISR 2 // interrupt status register -#define LCR 3 // line control register -#define LSR 5 // line status register +// see http://byterunner.com/16550.html +#define RHR 0 // receive holding register (for input bytes) +#define THR 0 // transmit holding register (for output bytes) +#define IER 1 // interrupt enable register +#define IER_TX_ENABLE (1<<0) +#define IER_RX_ENABLE (1<<1) +#define FCR 2 // FIFO control register +#define FCR_FIFO_ENABLE (1<<0) +#define FCR_FIFO_CLEAR (3<<1) // clear the content of the two FIFOs +#define ISR 2 // interrupt status register +#define LCR 3 // line control register +#define LCR_EIGHT_BITS (3<<0) +#define LCR_BAUD_LATCH (1<<7) // special mode to set baud rate +#define LSR 5 // line status register +#define LSR_RX_READY (1<<0) // input is waiting to be read from RHR +#define LSR_TX_IDLE (1<<5) // THR can accept another character to send #define ReadReg(reg) (*(Reg(reg))) #define WriteReg(reg, v) (*(Reg(reg)) = (v)) +// the transmit output buffer. +struct spinlock uart_tx_lock; +#define UART_TX_BUF_SIZE 32 +char uart_tx_buf[UART_TX_BUF_SIZE]; +int uart_tx_w; // write next to uart_tx_buf[uart_tx_w++] +int uart_tx_r; // read next from uart_tx_buf[uar_tx_r++] + +void uartstart(); + void uartinit(void) { @@ -37,7 +54,7 @@ uartinit(void) WriteReg(IER, 0x00); // special mode to set baud rate. - WriteReg(LCR, 0x80); + WriteReg(LCR, LCR_BAUD_LATCH); // LSB for baud rate of 38.4K. WriteReg(0, 0x03); @@ -47,23 +64,87 @@ uartinit(void) // leave set-baud mode, // and set word length to 8 bits, no parity. - WriteReg(LCR, 0x03); + WriteReg(LCR, LCR_EIGHT_BITS); // reset and enable FIFOs. - WriteReg(FCR, 0x07); + WriteReg(FCR, FCR_FIFO_ENABLE | FCR_FIFO_CLEAR); + + // enable transmit and receive interrupts. + WriteReg(IER, IER_TX_ENABLE | IER_RX_ENABLE); - // enable receive interrupts. - WriteReg(IER, 0x01); + initlock(&uart_tx_lock, "uart"); } -// write one output character to the UART. +// add a character to the output buffer and tell the +// UART to start sending if it isn't already. +// blocks if the output buffer is full. +// because it may block, it can't be called +// from interrupts; it's only suitable for use +// by write(). void uartputc(int c) { + acquire(&uart_tx_lock); + while(1){ + if(((uart_tx_w + 1) % UART_TX_BUF_SIZE) == uart_tx_r){ + // buffer is full. + // wait for uartstart() to open up space in the buffer. + sleep(&uart_tx_r, &uart_tx_lock); + } else { + uart_tx_buf[uart_tx_w] = c; + uart_tx_w = (uart_tx_w + 1) % UART_TX_BUF_SIZE; + uartstart(); + release(&uart_tx_lock); + return; + } + } +} + +// alternate version of uartputc() that doesn't +// use interrupts, for use by kernel printf() and +// to echo characters. it spins waiting for the uart's +// output register to be empty. +void +uartputc_sync(int c) +{ + push_off(); + // wait for Transmit Holding Empty to be set in LSR. - while((ReadReg(LSR) & (1 << 5)) == 0) + while((ReadReg(LSR) & LSR_TX_IDLE) == 0) ; WriteReg(THR, c); + + pop_off(); +} + +// if the UART is idle, and a character is waiting +// in the transmit buffer, send it. +// caller must hold uart_tx_lock. +// called from both the top- and bottom-half. +void +uartstart() +{ + while(1){ + if(uart_tx_w == uart_tx_r){ + // transmit buffer is empty. + return; + } + + if((ReadReg(LSR) & LSR_TX_IDLE) == 0){ + // the UART transmit holding register is full, + // so we cannot give it another byte. + // it will interrupt when it's ready for a new byte. + return; + } + + int c = uart_tx_buf[uart_tx_r]; + uart_tx_r = (uart_tx_r + 1) % UART_TX_BUF_SIZE; + + // maybe uartputc() is waiting for space in the buffer. + wakeup(&uart_tx_r); + + WriteReg(THR, c); + } } // read one input character from the UART. @@ -79,14 +160,22 @@ uartgetc(void) } } -// trap.c calls here when the uart interrupts. +// handle a uart interrupt, raised because input has +// arrived, or the uart is ready for more output, or +// both. called from trap.c. void uartintr(void) { + // read and process incoming characters. while(1){ int c = uartgetc(); if(c == -1) break; consoleintr(c); } + + // send buffered characters. + acquire(&uart_tx_lock); + uartstart(); + release(&uart_tx_lock); } |