a27c50a0ab835fc02e888ebce02b9828054fa7c9
[pintos-anon] / src / devices / serial.c
1 #include "devices/serial.h"
2 #include <debug.h>
3 #include "devices/16550a.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
10 #define QUEUE_BUFSIZE 8
11
12 struct queue 
13   {
14     enum intr_level old_level;
15     struct lock lock;
16     
17     struct thread *not_full, *not_empty;
18     uint8_t buf[QUEUE_BUFSIZE];
19     int head, tail;
20   };
21
22 static struct queue recv_queue, xmit_queue;
23 static bool polled_io;
24
25 static void set_serial (int bps, int bits, enum parity_type parity, int stop);
26 static void update_ier (void);
27 static intr_handler_func serial_interrupt;
28
29 static void queue_init (struct queue *, const char *);
30 static void queue_lock (struct queue *);
31 static void queue_unlock (struct queue *);
32 static bool queue_empty (const struct queue *);
33 static bool queue_full (const struct queue *);
34 static uint8_t queue_getc (struct queue *);
35 static void queue_putc (struct queue *, uint8_t);
36
37 /* Initializes the serial port device for polling mode.
38    Polling mode busy-waits for the serial port to become free
39    before writing to it.  It's slow, but until interrupts have
40    been initialized it's all we can do. */
41 void
42 serial_init_poll (void) 
43 {
44   ASSERT (!polled_io);
45   polled_io = true;
46   outb (IER_REG, 0);                    /* Turn off all interrupts. */
47   outb (FCR_REG, 0);                    /* Disable FIFO. */
48   set_serial (9600, 8, NONE, 1);        /* 9600 bps, N-8-1. */
49   outb (MCR_REG, 8);                    /* Turn on OUT2 output line. */
50
51
52 /* Initializes the serial port device for queued interrupt-driven
53    I/O.  With interrupt-driven I/O we don't waste CPU time
54    waiting for the serial device to become ready. */
55 void
56 serial_init_queue (void) 
57 {
58   ASSERT (polled_io);
59   polled_io = false;
60   queue_init (&recv_queue, "serial recv");
61   queue_init (&xmit_queue, "serial xmit");
62   intr_register (0x20 + 4, 0, INTR_OFF, serial_interrupt, "serial");
63 }
64
65 static void putc_poll (uint8_t);
66 static void putc_queue (uint8_t);
67
68 /* Sends BYTE to the serial port. */
69 void
70 serial_putc (uint8_t byte) 
71 {
72   if (polled_io || intr_context ())
73     putc_poll (byte);
74   else
75     putc_queue (byte); 
76 }
77
78 static void
79 putc_poll (uint8_t byte)
80 {
81   while ((inb (LSR_REG) & LSR_THRE) == 0)
82     continue;
83   outb (THR_REG, byte);
84 }
85
86 static void
87 putc_queue (uint8_t byte)
88 {
89   queue_lock (&xmit_queue);
90
91   queue_putc (&xmit_queue, byte); 
92   update_ier ();
93
94   queue_unlock (&xmit_queue);
95 }
96
97 /* Reads and returns a byte from the serial port. */
98 uint8_t
99 serial_getc (void) 
100 {
101   uint8_t byte;
102   queue_lock (&recv_queue);
103
104   /* Read a byte. */
105   byte = queue_getc (&recv_queue);
106   update_ier ();
107
108   queue_unlock (&recv_queue);
109   return byte;
110 }
111
112 /* Configures the first serial port for BPS bits per second,
113    BITS bits per byte, the given PARITY, and STOP stop bits. */
114 static void
115 set_serial (int bps, int bits, enum parity_type parity, int stop)
116 {
117   int baud_base = 1843200 / 16;         /* Base rate of 16550A. */
118   uint16_t divisor = baud_base / bps;   /* Clock rate divisor. */
119
120   /* Enable DLAB. */
121   outb (LCR_REG, make_lcr (bits, parity, stop, false, true));
122
123   /* Set baud rate. */
124   outb (LS_REG, divisor & 0xff);
125   outb (MS_REG, divisor >> 8);
126   
127   /* Reset DLAB. */
128   outb (LCR_REG, make_lcr (bits, parity, stop, false, false));
129 }
130
131 /* Update interrupt enable register.
132    If our transmit queue is empty, turn off transmit interrupt.
133    If our receive queue is full, turn off receive interrupt. */
134 static void
135 update_ier (void) 
136 {
137   uint8_t ier = 0;
138   
139   ASSERT (intr_get_level () == INTR_OFF);
140
141   if (!queue_empty (&xmit_queue))
142     ier |= IER_XMIT;
143   if (!queue_full (&recv_queue))
144     ier |= IER_RECV;
145   outb (IER_REG, ier);
146 }
147
148 static void
149 serial_interrupt (struct intr_frame *f UNUSED) 
150 {
151   /* As long as we have space in our buffer for a byte,
152      and the hardware has a byte to give us,
153      receive a byte. */
154   while (!queue_full (&recv_queue) && (inb (LSR_REG) & LSR_DR) != 0)
155     {
156       uint8_t byte = inb (RBR_REG);
157       //outb (0x0402, byte);
158       queue_putc (&recv_queue, byte); 
159     }
160   
161   /* As long as we have a byte to transmit,
162      and the hardware is ready to accept a byte for transmission,
163      transmit a byte. */
164   while (!queue_empty (&xmit_queue) && (inb (LSR_REG) & LSR_THRE) != 0) 
165     outb (THR_REG, queue_getc (&xmit_queue));
166   
167   /* Update interrupt enable register based on queue status. */
168   update_ier ();
169 }
170 \f
171 static int next (int pos);
172 static bool owned_by_current_thread (const struct queue *);
173 static void wait (struct queue *q, struct thread **waiter);
174 static void signal (struct queue *q, struct thread **waiter);
175
176 static void
177 queue_init (struct queue *q, const char *name) 
178 {
179   lock_init (&q->lock, name);
180   q->not_full = q->not_empty = NULL;
181   q->head = q->tail = 0;
182 }
183
184 static void
185 queue_lock (struct queue *q) 
186 {
187   lock_acquire (&q->lock);
188   q->old_level = intr_disable ();
189 }
190
191 static void
192 queue_unlock (struct queue *q) 
193 {
194   ASSERT (!intr_context ());
195   ASSERT (owned_by_current_thread (q));
196
197   lock_release (&q->lock);
198   intr_set_level (q->old_level);
199 }
200
201 static bool
202 queue_empty (const struct queue *q) 
203 {
204   ASSERT (intr_get_level () == INTR_OFF);
205   return q->head == q->tail;
206 }
207
208 static bool
209 queue_full (const struct queue *q) 
210 {
211   ASSERT (intr_get_level () == INTR_OFF);
212   return next (q->head) == q->tail;
213 }
214
215 static uint8_t
216 queue_getc (struct queue *q) 
217 {
218   uint8_t byte;
219   
220   ASSERT (owned_by_current_thread (q));
221   while (queue_empty (q))
222     wait (q, &q->not_empty);
223
224   byte = q->buf[q->tail];
225   q->tail = next (q->tail);
226   signal (q, &q->not_full);
227   return byte;
228 }
229
230 static void
231 queue_putc (struct queue *q, uint8_t byte) 
232 {
233   ASSERT (owned_by_current_thread (q));
234   while (queue_full (q))
235     wait (q, &q->not_full);
236
237   q->buf[q->head] = byte;
238   q->head = next (q->head);
239   signal (q, &q->not_empty);
240 }
241
242 static int
243 next (int pos) 
244 {
245   return (pos + 1) % QUEUE_BUFSIZE;
246 }
247
248 static bool
249 owned_by_current_thread (const struct queue *q) 
250 {
251   return (intr_context ()
252           || (lock_held_by_current_thread (&q->lock)
253               && intr_get_level () == INTR_OFF));
254 }
255
256 static void
257 wait (struct queue *q, struct thread **waiter) 
258 {
259   ASSERT (!intr_context ());
260   ASSERT (owned_by_current_thread (q));
261
262   *waiter = thread_current ();
263   thread_block ();
264 }
265
266 static void
267 signal (struct queue *q, struct thread **waiter) 
268 {
269   ASSERT (owned_by_current_thread (q));
270
271   if (*waiter != NULL) 
272     {
273       thread_unblock (*waiter);
274       *waiter = NULL;
275     }
276 }