f64074a0d1482bec6654b34b8e5b5644c8a76fec
[pintos-anon] / src / devices / serial.c
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"
10 \f
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.
14
15    Refer to [PC16650D] for hardware information. */
16
17 /* I/O port base address for the first serial port. */
18 #define IO_BASE 0x3f8
19
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.. */
24
25 /* DLAB=1 registers. */
26 #define LS_REG (IO_BASE + 0)    /* Divisor Latch (LSB). */
27 #define MS_REG (IO_BASE + 1)    /* Divisor Latch (MSB). */
28
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). */
35
36 /* Interrupt Enable Register bits. */
37 #define IER_RECV 0x01           /* Interrupt when data received. */
38 #define IER_XMIT 0x02           /* Interrupt when transmit finishes. */
39
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). */
43
44 /* MODEM Control Register. */
45 #define MCR_OUT2 0x08           /* Output line 2. */
46
47 /* Line Status Register. */
48 #define LSR_DR 0x01             /* Data Ready: received data byte is in RBR. */
49 #define LSR_THRE 0x20           /* THR Empty. */
50 \f
51 /* Transmission mode. */
52 static enum { UNINIT, POLL, QUEUE } mode;
53
54 /* Data to be transmitted. */
55 static struct intq txq;
56
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;
61
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) 
68 {
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;
76
77
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) 
83 {
84   enum intr_level old_level;
85
86   if (mode == UNINIT)
87     init_poll ();
88   ASSERT (mode == POLL);
89
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);
95 }
96
97 /* Sends BYTE to the serial port. */
98 void
99 serial_putc (uint8_t byte) 
100 {
101   enum intr_level old_level = intr_disable ();
102
103   if (mode != QUEUE)
104     {
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); 
110     }
111   else 
112     {
113       /* Otherwise, queue a byte and update the interrupt enable
114          register. */
115       if (old_level == INTR_OFF && intq_full (&txq)) 
116         {
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)); 
123         }
124
125       intq_putc (&txq, byte); 
126       write_ier ();
127     }
128   
129   intr_set_level (old_level);
130 }
131
132 /* Flushes anything in the serial buffer out the port in polling
133    mode. */
134 void
135 serial_flush (void) 
136 {
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);
141 }
142
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) 
149 {
150   ASSERT (intr_get_level () == INTR_OFF);
151   if (mode == QUEUE)
152     write_ier ();
153 }
154 \f
155 /* Configures the serial port for BPS bits per second. */
156 static void
157 set_serial (int bps)
158 {
159   int base_rate = 1843200 / 16;         /* Base rate of 16550A, in Hz. */
160   uint16_t divisor = base_rate / bps;   /* Clock rate divisor. */
161
162   ASSERT (bps >= 300 && bps <= 115200);
163
164   /* Enable DLAB. */
165   outb (LCR_REG, LCR_N81 | LCR_DLAB);
166
167   /* Set data rate. */
168   outb (LS_REG, divisor & 0xff);
169   outb (MS_REG, divisor >> 8);
170   
171   /* Reset DLAB. */
172   outb (LCR_REG, LCR_N81);
173 }
174
175 /* Update interrupt enable register. */
176 static void
177 write_ier (void) 
178 {
179   uint8_t ier = 0;
180
181   ASSERT (intr_get_level () == INTR_OFF);
182
183   /* Enable transmit interrupt if we have any characters to
184      transmit. */
185   if (!intq_empty (&txq))
186     ier |= IER_XMIT;
187
188   /* Enable receive interrupt if we have room to store any
189      characters we receive. */
190   if (!input_full ())
191     ier |= IER_RECV;
192   
193   outb (IER_REG, ier);
194 }
195
196 /* Polls the serial port until it's ready,
197    and then transmits BYTE. */
198 static void
199 putc_poll (uint8_t byte) 
200 {
201   ASSERT (intr_get_level () == INTR_OFF);
202
203   while ((inb (LSR_REG) & LSR_THRE) == 0)
204     continue;
205   outb (THR_REG, byte);
206 }
207
208 /* Serial interrupt handler. */
209 static void
210 serial_interrupt (struct intr_frame *f UNUSED) 
211 {
212   /* Inquire about interrupt in UART.  Without this, we can
213      occasionally miss an interrupt running under QEMU. */
214   inb (IIR_REG);
215
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));
220
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));
225
226   /* Update interrupt enable register based on queue status. */
227   write_ier ();
228 }