f4d0de8114a6818d51730aadedfd116b1570cb40
[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 void
67 serial_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   ASSERT (mode == POLL);
87
88   intr_register_ext (0x20 + 4, serial_interrupt, "serial");
89   mode = QUEUE;
90   old_level = intr_disable ();
91   write_ier ();
92   intr_set_level (old_level);
93 }
94
95 /* Sends BYTE to the serial port. */
96 void
97 serial_putc (uint8_t byte) 
98 {
99   enum intr_level old_level = intr_disable ();
100
101   if (mode == POLL)
102     {
103       /* If we're not set up for interrupt-driven I/O yet,
104          use dumb polling to transmit a byte. */
105       putc_poll (byte); 
106     }
107   else 
108     {
109       /* Otherwise, queue a byte and update the interrupt enable
110          register. */
111       if (old_level == INTR_OFF && intq_full (&txq)) 
112         {
113           /* Interrupts are off and the transmit queue is full.
114              If we wanted to wait for the queue to empty,
115              we'd have to reenable interrupts.
116              That's impolite, so we'll send a character via
117              polling instead. */
118           putc_poll (intq_getc (&txq)); 
119         }
120
121       intq_putc (&txq, byte); 
122       write_ier ();
123     }
124   
125   intr_set_level (old_level);
126 }
127
128 /* Flushes anything in the serial buffer out the port in polling
129    mode. */
130 void
131 serial_flush (void) 
132 {
133   enum intr_level old_level = intr_disable ();
134   while (!intq_empty (&txq))
135     putc_poll (intq_getc (&txq));
136   intr_set_level (old_level);
137 }
138
139 /* The fullness of the input buffer may have changed.  Reassess
140    whether we should block receive interrupts.
141    Called by the input buffer routines when characters are added
142    to or removed from the buffer. */
143 void
144 serial_notify (void) 
145 {
146   ASSERT (intr_get_level () == INTR_OFF);
147   if (mode == QUEUE)
148     write_ier ();
149 }
150 \f
151 /* Configures the serial port for BPS bits per second. */
152 static void
153 set_serial (int bps)
154 {
155   int base_rate = 1843200 / 16;         /* Base rate of 16550A, in Hz. */
156   uint16_t divisor = base_rate / bps;   /* Clock rate divisor. */
157
158   ASSERT (bps >= 300 && bps <= 115200);
159
160   /* Enable DLAB. */
161   outb (LCR_REG, LCR_N81 | LCR_DLAB);
162
163   /* Set data rate. */
164   outb (LS_REG, divisor & 0xff);
165   outb (MS_REG, divisor >> 8);
166   
167   /* Reset DLAB. */
168   outb (LCR_REG, LCR_N81);
169 }
170
171 /* Update interrupt enable register. */
172 static void
173 write_ier (void) 
174 {
175   uint8_t ier = 0;
176
177   ASSERT (intr_get_level () == INTR_OFF);
178
179   /* Enable transmit interrupt if we have any characters to
180      transmit. */
181   if (!intq_empty (&txq))
182     ier |= IER_XMIT;
183
184   /* Enable receive interrupt if we have room to store any
185      characters we receive. */
186   if (!input_full ())
187     ier |= IER_RECV;
188   
189   outb (IER_REG, ier);
190 }
191
192 /* Polls the serial port until it's ready,
193    and then transmits BYTE. */
194 static void
195 putc_poll (uint8_t byte) 
196 {
197   ASSERT (intr_get_level () == INTR_OFF);
198
199   while ((inb (LSR_REG) & LSR_THRE) == 0)
200     continue;
201   outb (THR_REG, byte);
202 }
203
204 /* Serial interrupt handler. */
205 static void
206 serial_interrupt (struct intr_frame *f UNUSED) 
207 {
208   /* Inquire about interrupt in UART.  Without this, we can
209      occasionally miss an interrupt running under QEMU. */
210   inb (IIR_REG);
211
212   /* As long as we have room to receive a byte, and the hardware
213      has a byte for us, receive a byte.  */
214   while (!input_full () && (inb (LSR_REG) & LSR_DR) != 0)
215     input_putc (inb (RBR_REG));
216
217   /* As long as we have a byte to transmit, and the hardware is
218      ready to accept a byte for transmission, transmit a byte. */
219   while (!intq_empty (&txq) && (inb (LSR_REG) & LSR_THRE) != 0) 
220     outb (THR_REG, intq_getc (&txq));
221
222   /* Update interrupt enable register based on queue status. */
223   write_ier ();
224 }