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