xv6, line by line
kernel/uart.c

kernel/uart.c

C · 155 lines · annotated 100% · kernel · upstream

About this file

The driver for xv6’s only character device, a UART of the 16550 family. QEMU connects this UART to the terminal you started it from, so every byte xv6 prints leaves through here and every key you type arrives here.

The file talks to the chip through a handful of byte-wide registers at physical address UART0 (0x10000000), using memory-mapped I/O. It offers three ways to use them:

  • uartwrite sends a buffer for a write() system call. When the chip is busy it puts the calling process to sleep until the UART raises a “transmitter empty” interrupt.
  • uartputc_sync sends one byte by spinning until the chip is ready. Kernel messages (printk) and the echo of typed characters use it, because they may run where sleeping is not allowed.
  • uartintr is the interrupt handler. It wakes a waiting writer and hands every received byte to consoleintr in kernel/console.c.

This file knows nothing about lines, backspace or processes reading input; that is the job of kernel/console.c, the layer above it.

Read before: kernel/memlayout.h (where the UART lives). Read next: kernel/console.c, then kernel/printk.c.

1//
2// low-level driver for 16550a UART.
3//
5#include "types.h"
6#include "param.h"
8#include "riscv.h"
9#include "spinlock.h"
11#include "proc.h"
12#include "defs.h"
14// the UART control registers are memory-mapped
15// at address UART0. this macro returns the
16// address of one of the registers.
17#define Reg(reg) ((volatile unsigned char *)(UART0 + (reg)))
19#define ReadReg(reg) (*(Reg(reg)))
20#define WriteReg(reg, v) (*(Reg(reg)) = (v))
22// the UART control registers.
23// some have different meanings for read vs write.
24// see http://byterunner.com/16550.html
25#define RHR 0 // receive holding register (for input bytes)
26#define THR 0 // transmit holding register (for output bytes)
27#define IER 1 // interrupt enable register
28#define IER_RX_ENABLE (1 << 0) // receiver interrupts
29#define IER_TX_ENABLE (1 << 1) // transmit interrupts
30#define FCR 2 // FIFO control register
31#define FCR_FIFO_ENABLE (1 << 0)
32#define FCR_FIFO_CLEAR (3 << 1) // clear the content of the two FIFOs
33#define ISR 2 // interrupt status register
34#define LCR 3 // line control register
35#define LCR_EIGHT_BITS (3 << 0)
36#define LCR_BAUD_LATCH (1 << 7) // special mode to set baud rate
37#define LSR 5 // line status register
38#define LSR_RX_READY (1 << 0) // input is waiting to be read from RHR
39#define LSR_TX_IDLE (1 << 5) // THR can accept another character to send
41// for sending threads to serialize their writes
42static struct sleeplock tx_lock;
43static int tx_chan; // &tx_chan is the "wait channel"
45extern volatile int panicking; // from printk.c
46extern volatile int panicked; // from printk.c
48void
51 // disable interrupts.
52 WriteReg(IER, 0x00);
54 // special mode to set baud rate.
57 // LSB for baud rate of 38.4K.
58 WriteReg(0, 0x03);
60 // MSB for baud rate of 38.4K.
61 WriteReg(1, 0x00);
63 // leave set-baud mode,
64 // and set word length to 8 bits, no parity.
67 // reset and enable FIFOs.
70 // enable transmit and receive interrupts.
76// transmit buf[] to the uart. it blocks if the
77// uart is busy, so it cannot be called from
78// interrupts, only from write() system calls.
79void
80uartwrite(char buf[], int n)
84 int i = 0;
85 while (i < n) {
89 i += 1;
90 } else {
92 }
93 }
98// write a byte to the uart without using
99// interrupts, for use by kernel printk() and
100// to echo characters. it spins waiting for the uart's
101// output register to be empty.
102void
105 if (panicking == 0)
108 if (panicked) {
109 for (;;)
110 ;
111 }
113 // wait for UART to set Transmit Holding Empty in LSR.
114 while ((ReadReg(LSR) & LSR_TX_IDLE) == 0)
115 ;
118 if (panicking == 0)
122// try to read one input character from the UART.
123// return -1 if none is waiting.
124static int
127 // is input ready?
129 return ReadReg(RHR);
130 } else {
131 return -1;
132 }
135// handle a uart interrupt, raised because input has
136// arrived, or the uart is ready for more output, or
137// both. called from devintr().
138void
141 ReadReg(ISR); // acknowledge the interrupt
144 // UART finished transmitting; wake up sending thread.
146 }
148 // read and process incoming characters, if any.
149 while (1) {
150 int c = uartgetc();
151 if (c == -1)
152 break;
154 }