Parse command-line options much earlier, so that thread_mlfqs is
[pintos.git] / src / devices / serial.c
blobf64074a0d1482bec6654b34b8e5b5644c8a76fec
1 #include "devices/serial.h"
2 #include <debug.h>
3 #include "devices/input.h"
4 #include "devices/intq.h"
5 #include "devices/timer.h"
6 #include "threads/io.h"
7 #include "threads/interrupt.h"
8 #include "threads/synch.h"
9 #include "threads/thread.h"
11 /* Register definitions for the 16550A UART used in PCs.
12 The 16550A has a lot more going on than shown here, but this
13 is all we need.
15 Refer to [PC16650D] for hardware information. */
17 /* I/O port base address for the first serial port. */
18 #define IO_BASE 0x3f8
20 /* DLAB=0 registers. */
21 #define RBR_REG (IO_BASE + 0) /* Receiver Buffer Reg. (read-only). */
22 #define THR_REG (IO_BASE + 0) /* Transmitter Holding Reg. (write-only). */
23 #define IER_REG (IO_BASE + 1) /* Interrupt Enable Reg.. */
25 /* DLAB=1 registers. */
26 #define LS_REG (IO_BASE + 0) /* Divisor Latch (LSB). */
27 #define MS_REG (IO_BASE + 1) /* Divisor Latch (MSB). */
29 /* DLAB-insensitive registers. */
30 #define IIR_REG (IO_BASE + 2) /* Interrupt Identification Reg. (read-only) */
31 #define FCR_REG (IO_BASE + 2) /* FIFO Control Reg. (write-only). */
32 #define LCR_REG (IO_BASE + 3) /* Line Control Register. */
33 #define MCR_REG (IO_BASE + 4) /* MODEM Control Register. */
34 #define LSR_REG (IO_BASE + 5) /* Line Status Register (read-only). */
36 /* Interrupt Enable Register bits. */
37 #define IER_RECV 0x01 /* Interrupt when data received. */
38 #define IER_XMIT 0x02 /* Interrupt when transmit finishes. */
40 /* Line Control Register bits. */
41 #define LCR_N81 0x03 /* No parity, 8 data bits, 1 stop bit. */
42 #define LCR_DLAB 0x80 /* Divisor Latch Access Bit (DLAB). */
44 /* MODEM Control Register. */
45 #define MCR_OUT2 0x08 /* Output line 2. */
47 /* Line Status Register. */
48 #define LSR_DR 0x01 /* Data Ready: received data byte is in RBR. */
49 #define LSR_THRE 0x20 /* THR Empty. */
51 /* Transmission mode. */
52 static enum { UNINIT, POLL, QUEUE } mode;
54 /* Data to be transmitted. */
55 static struct intq txq;
57 static void set_serial (int bps);
58 static void putc_poll (uint8_t);
59 static void write_ier (void);
60 static intr_handler_func serial_interrupt;
62 /* Initializes the serial port device for polling mode.
63 Polling mode busy-waits for the serial port to become free
64 before writing to it. It's slow, but until interrupts have
65 been initialized it's all we can do. */
66 static void
67 init_poll (void)
69 ASSERT (mode == UNINIT);
70 outb (IER_REG, 0); /* Turn off all interrupts. */
71 outb (FCR_REG, 0); /* Disable FIFO. */
72 set_serial (115200); /* 115.2 kbps, N-8-1. */
73 outb (MCR_REG, MCR_OUT2); /* Required to enable interrupts. */
74 intq_init (&txq);
75 mode = POLL;
78 /* Initializes the serial port device for queued interrupt-driven
79 I/O. With interrupt-driven I/O we don't waste CPU time
80 waiting for the serial device to become ready. */
81 void
82 serial_init_queue (void)
84 enum intr_level old_level;
86 if (mode == UNINIT)
87 init_poll ();
88 ASSERT (mode == POLL);
90 intr_register_ext (0x20 + 4, serial_interrupt, "serial");
91 mode = QUEUE;
92 old_level = intr_disable ();
93 write_ier ();
94 intr_set_level (old_level);
97 /* Sends BYTE to the serial port. */
98 void
99 serial_putc (uint8_t byte)
101 enum intr_level old_level = intr_disable ();
103 if (mode != QUEUE)
105 /* If we're not set up for interrupt-driven I/O yet,
106 use dumb polling to transmit a byte. */
107 if (mode == UNINIT)
108 init_poll ();
109 putc_poll (byte);
111 else
113 /* Otherwise, queue a byte and update the interrupt enable
114 register. */
115 if (old_level == INTR_OFF && intq_full (&txq))
117 /* Interrupts are off and the transmit queue is full.
118 If we wanted to wait for the queue to empty,
119 we'd have to reenable interrupts.
120 That's impolite, so we'll send a character via
121 polling instead. */
122 putc_poll (intq_getc (&txq));
125 intq_putc (&txq, byte);
126 write_ier ();
129 intr_set_level (old_level);
132 /* Flushes anything in the serial buffer out the port in polling
133 mode. */
134 void
135 serial_flush (void)
137 enum intr_level old_level = intr_disable ();
138 while (!intq_empty (&txq))
139 putc_poll (intq_getc (&txq));
140 intr_set_level (old_level);
143 /* The fullness of the input buffer may have changed. Reassess
144 whether we should block receive interrupts.
145 Called by the input buffer routines when characters are added
146 to or removed from the buffer. */
147 void
148 serial_notify (void)
150 ASSERT (intr_get_level () == INTR_OFF);
151 if (mode == QUEUE)
152 write_ier ();
155 /* Configures the serial port for BPS bits per second. */
156 static void
157 set_serial (int bps)
159 int base_rate = 1843200 / 16; /* Base rate of 16550A, in Hz. */
160 uint16_t divisor = base_rate / bps; /* Clock rate divisor. */
162 ASSERT (bps >= 300 && bps <= 115200);
164 /* Enable DLAB. */
165 outb (LCR_REG, LCR_N81 | LCR_DLAB);
167 /* Set data rate. */
168 outb (LS_REG, divisor & 0xff);
169 outb (MS_REG, divisor >> 8);
171 /* Reset DLAB. */
172 outb (LCR_REG, LCR_N81);
175 /* Update interrupt enable register. */
176 static void
177 write_ier (void)
179 uint8_t ier = 0;
181 ASSERT (intr_get_level () == INTR_OFF);
183 /* Enable transmit interrupt if we have any characters to
184 transmit. */
185 if (!intq_empty (&txq))
186 ier |= IER_XMIT;
188 /* Enable receive interrupt if we have room to store any
189 characters we receive. */
190 if (!input_full ())
191 ier |= IER_RECV;
193 outb (IER_REG, ier);
196 /* Polls the serial port until it's ready,
197 and then transmits BYTE. */
198 static void
199 putc_poll (uint8_t byte)
201 ASSERT (intr_get_level () == INTR_OFF);
203 while ((inb (LSR_REG) & LSR_THRE) == 0)
204 continue;
205 outb (THR_REG, byte);
208 /* Serial interrupt handler. */
209 static void
210 serial_interrupt (struct intr_frame *f UNUSED)
212 /* Inquire about interrupt in UART. Without this, we can
213 occasionally miss an interrupt running under QEMU. */
214 inb (IIR_REG);
216 /* As long as we have room to receive a byte, and the hardware
217 has a byte for us, receive a byte. */
218 while (!input_full () && (inb (LSR_REG) & LSR_DR) != 0)
219 input_putc (inb (RBR_REG));
221 /* As long as we have a byte to transmit, and the hardware is
222 ready to accept a byte for transmission, transmit a byte. */
223 while (!intq_empty (&txq) && (inb (LSR_REG) & LSR_THRE) != 0)
224 outb (THR_REG, intq_getc (&txq));
226 /* Update interrupt enable register based on queue status. */
227 write_ier ();