Move serial interrupt queue into new file intq.c.
[pintos-anon] / src / devices / serial.c
1 #include "devices/serial.h"
2 #include <debug.h>
3 #include "devices/16550a.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
11 /* Transmission mode. */
12 static enum { UNINIT, POLL, QUEUE } mode;
13
14 /* Data to be transmitted. */
15 static struct intq txq;
16
17 static void set_serial (int bps, int bits, enum parity_type parity, int stop);
18 static void write_ier (void);
19 static intr_handler_func serial_interrupt;
20
21 /* Initializes the serial port device for polling mode.
22    Polling mode busy-waits for the serial port to become free
23    before writing to it.  It's slow, but until interrupts have
24    been initialized it's all we can do. */
25 void
26 serial_init_poll (void) 
27 {
28   ASSERT (mode == UNINIT);
29   outb (IER_REG, 0);                    /* Turn off all interrupts. */
30   outb (FCR_REG, 0);                    /* Disable FIFO. */
31   set_serial (9600, 8, NONE, 1);        /* 9600 bps, N-8-1. */
32   outb (MCR_REG, 8);                    /* Turn on OUT2 output line. */
33   mode = POLL;
34
35
36 /* Initializes the serial port device for queued interrupt-driven
37    I/O.  With interrupt-driven I/O we don't waste CPU time
38    waiting for the serial device to become ready. */
39 void
40 serial_init_queue (void) 
41 {
42   ASSERT (mode == POLL);
43   intq_init (&txq, "serial xmit");
44   intr_register (0x20 + 4, 0, INTR_OFF, serial_interrupt, "serial");
45   mode = QUEUE;
46 }
47
48 /* Sends BYTE to the serial port. */
49 void
50 serial_putc (uint8_t byte) 
51 {
52   if (mode == POLL || intr_context ())
53     {
54       /* Poll the serial port until it's ready for a byte, and
55          then transmit. */
56       while ((inb (LSR_REG) & LSR_THRE) == 0)
57         continue;
58       outb (THR_REG, byte);
59     }
60   else
61     {
62       /* Lock the queue, add a byte, and update the interrupt
63          enable register. */
64       intq_lock (&txq);
65       intq_putc (&txq, byte); 
66       write_ier ();
67       intq_unlock (&txq);
68     }
69 }
70
71 /* Configures the serial port for BPS bits per second, BITS bits
72    per byte, the given PARITY, and STOP stop bits. */
73 static void
74 set_serial (int bps, int bits, enum parity_type parity, int stop)
75 {
76   int baud_base = 1843200 / 16;         /* Base rate of 16550A. */
77   uint16_t divisor = baud_base / bps;   /* Clock rate divisor. */
78
79   /* Enable DLAB. */
80   outb (LCR_REG, make_lcr (bits, parity, stop, false, true));
81
82   /* Set baud rate. */
83   outb (LS_REG, divisor & 0xff);
84   outb (MS_REG, divisor >> 8);
85   
86   /* Reset DLAB. */
87   outb (LCR_REG, make_lcr (bits, parity, stop, false, false));
88 }
89
90 /* Update interrupt enable register.
91    If our transmit queue is empty, turn off transmit interrupt. */
92 static void
93 write_ier (void) 
94 {
95   outb (IER_REG, intq_empty (&txq) ? 0 : IER_XMIT);
96 }
97
98 /* Serial interrupt handler.
99    As long as we have a byte to transmit,
100    and the hardware is ready to accept a byte for transmission,
101    transmit a byte.
102    Then update interrupt enable register based on queue
103    status. */
104 static void
105 serial_interrupt (struct intr_frame *f UNUSED) 
106 {
107   while (!intq_empty (&txq) && (inb (LSR_REG) & LSR_THRE) != 0) 
108     outb (THR_REG, intq_getc (&txq));
109   write_ier ();
110 }