Clarify comment.
[pintos-anon] / src / devices / serial.c
1 #include "devices/serial.h"
2 #include <debug.h>
3 #include "devices/intq.h"
4 #include "devices/timer.h"
5 #include "threads/io.h"
6 #include "threads/interrupt.h"
7 #include "threads/synch.h"
8 #include "threads/thread.h"
9 \f
10 /* Register definitions for the 16550A UART used in PCs.
11    The 16550A has a lot more going on than shown here, but this
12    is all we need.
13
14    Refer to [PC16650D] for hardware information. */
15
16 /* I/O port base address for the first serial port. */
17 #define IO_BASE 0x3f8
18
19 /* DLAB=0 registers. */
20 #define RBR_REG (IO_BASE + 0)   /* Receiver Buffer Reg. (read-only). */
21 #define THR_REG (IO_BASE + 0)   /* Transmitter Holding Reg. (write-only). */
22 #define IER_REG (IO_BASE + 1)   /* Interrupt Enable Reg. (read-only). */
23 #define FCR_REG (IO_BASE + 2)   /* FIFO Control Reg. (write-only). */
24 #define LCR_REG (IO_BASE + 3)   /* Line Control Register. */
25 #define MCR_REG (IO_BASE + 4)   /* MODEM Control Register. */
26 #define LSR_REG (IO_BASE + 5)   /* Line Status Register (read-only). */
27
28 /* DLAB=1 registers. */
29 #define LS_REG (IO_BASE + 0)    /* Divisor Latch (LSB). */
30 #define MS_REG (IO_BASE + 1)    /* Divisor Latch (MSB). */
31
32 /* Interrupt Enable Register bits. */
33 #define IER_XMIT 0x02           /* Interrupt when transmit finishes. */
34
35 /* Line Control Register bits. */
36 #define LCR_N81 0x03            /* No parity, 8 data bits, 1 stop bit. */
37 #define LCR_DLAB 0x80           /* Divisor Latch Access Bit (DLAB). */
38
39 /* MODEM Control Register. */
40 #define MCR_OUT2 0x08           /* Output line 2. */
41
42 /* Line Status Register. */
43 #define LSR_THRE 0x20           /* THR Empty. */
44 \f
45 /* Transmission mode. */
46 static enum { UNINIT, POLL, QUEUE } mode;
47
48 /* Data to be transmitted. */
49 static struct intq txq;
50
51 static void set_serial (int bps);
52 static void putc_poll (uint8_t);
53 static void write_ier (void);
54 static intr_handler_func serial_interrupt;
55
56 /* Initializes the serial port device for polling mode.
57    Polling mode busy-waits for the serial port to become free
58    before writing to it.  It's slow, but until interrupts have
59    been initialized it's all we can do. */
60 void
61 serial_init_poll (void) 
62 {
63   ASSERT (mode == UNINIT);
64   outb (IER_REG, 0);                    /* Turn off all interrupts. */
65   outb (FCR_REG, 0);                    /* Disable FIFO. */
66   set_serial (115200);                  /* 115.2 kbps, N-8-1. */
67   outb (MCR_REG, MCR_OUT2);             /* Turn on OUT2 output line. */
68   intq_init (&txq);
69   mode = POLL;
70
71
72 /* Initializes the serial port device for queued interrupt-driven
73    I/O.  With interrupt-driven I/O we don't waste CPU time
74    waiting for the serial device to become ready. */
75 void
76 serial_init_queue (void) 
77 {
78   ASSERT (mode == POLL);
79   intr_register_ext (0x20 + 4, serial_interrupt, "serial");
80   mode = QUEUE;
81 }
82
83 /* Sends BYTE to the serial port. */
84 void
85 serial_putc (uint8_t byte) 
86 {
87   enum intr_level old_level = intr_disable ();
88
89   if (mode == POLL)
90     {
91       /* If we're not set up for interrupt-driven I/O yet,
92          use dumb polling to transmit a byte. */
93       putc_poll (byte); 
94     }
95   else 
96     {
97       /* Otherwise, queue a byte and update the interrupt enable
98          register. */
99       if (old_level == INTR_OFF && intq_full (&txq)) 
100         {
101           /* Interrupts are off and the transmit queue is full.
102              If we wanted to wait for the queue to empty,
103              we'd have to reenable interrupts.
104              That's impolite, so we'll send a character via
105              polling instead. */
106           putc_poll (intq_getc (&txq)); 
107         }
108
109       intq_putc (&txq, byte); 
110       write_ier ();
111     }
112   
113   intr_set_level (old_level);
114 }
115
116 /* Flushes anything in the serial buffer out the port in polling
117    mode. */
118 void
119 serial_flush (void) 
120 {
121   enum intr_level old_level = intr_disable ();
122   while (!intq_empty (&txq))
123     putc_poll (intq_getc (&txq));
124   intr_set_level (old_level);
125 }
126 \f
127 /* Configures the serial port for BPS bits per second. */
128 static void
129 set_serial (int bps)
130 {
131   int baud_base = 1843200 / 16;         /* Base rate of 16550A. */
132   uint16_t divisor = baud_base / bps;   /* Clock rate divisor. */
133
134   ASSERT (bps >= 300 && bps <= 115200);
135
136   /* Enable DLAB. */
137   outb (LCR_REG, LCR_N81 | LCR_DLAB);
138
139   /* Set baud rate. */
140   outb (LS_REG, divisor & 0xff);
141   outb (MS_REG, divisor >> 8);
142   
143   /* Reset DLAB. */
144   outb (LCR_REG, LCR_N81);
145 }
146
147 /* Update interrupt enable register.
148    If our transmit queue is empty, turn off transmit interrupt. */
149 static void
150 write_ier (void) 
151 {
152   outb (IER_REG, intq_empty (&txq) ? 0 : IER_XMIT);
153 }
154
155 /* Polls the serial port until it's ready,
156    and then transmits BYTE. */
157 static void
158 putc_poll (uint8_t byte) 
159 {
160   ASSERT (intr_get_level () == INTR_OFF);
161
162   while ((inb (LSR_REG) & LSR_THRE) == 0)
163     continue;
164   outb (THR_REG, byte);
165 }
166
167 /* Serial interrupt handler.
168    As long as we have a byte to transmit,
169    and the hardware is ready to accept a byte for transmission,
170    transmit a byte.
171    Then update interrupt enable register based on queue
172    status. */
173 static void
174 serial_interrupt (struct intr_frame *f UNUSED) 
175 {
176   while (!intq_empty (&txq) && (inb (LSR_REG) & LSR_THRE) != 0) 
177     outb (THR_REG, intq_getc (&txq));
178   write_ier ();
179 }