Only poll on output if the txq is full and interrupts are off, and
[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 putc_poll (uint8_t);
19 static void write_ier (void);
20 static intr_handler_func serial_interrupt;
21
22 /* Initializes the serial port device for polling mode.
23    Polling mode busy-waits for the serial port to become free
24    before writing to it.  It's slow, but until interrupts have
25    been initialized it's all we can do. */
26 void
27 serial_init_poll (void) 
28 {
29   ASSERT (mode == UNINIT);
30   outb (IER_REG, 0);                    /* Turn off all interrupts. */
31   outb (FCR_REG, 0);                    /* Disable FIFO. */
32   set_serial (9600, 8, NONE, 1);        /* 9600 bps, N-8-1. */
33   outb (MCR_REG, 8);                    /* Turn on OUT2 output line. */
34   intq_init (&txq, "serial xmit");
35   mode = POLL;
36
37
38 /* Initializes the serial port device for queued interrupt-driven
39    I/O.  With interrupt-driven I/O we don't waste CPU time
40    waiting for the serial device to become ready. */
41 void
42 serial_init_queue (void) 
43 {
44   ASSERT (mode == POLL);
45   intr_register (0x20 + 4, 0, INTR_OFF, serial_interrupt, "serial");
46   mode = QUEUE;
47 }
48
49 /* Sends BYTE to the serial port. */
50 void
51 serial_putc (uint8_t byte) 
52 {
53   enum intr_level old_level = intr_disable ();
54
55   if (mode == POLL)
56     {
57       /* If we're not set up for interrupt-driven I/O yet,
58          use dumb polling to transmit a byte. */
59       putc_poll (byte); 
60     }
61   else 
62     {
63       /* Otherwise, queue a byte and update the interrupt enable
64          register. */
65       if (old_level == INTR_OFF && intq_full (&txq)) 
66         {
67           /* Interrupts are off and the transmit queue is full.
68              If we wanted to wait for the queue to empty,
69              we'd have to reenable interrupts.
70              That's impolite, so we'll send a character via
71              polling instead. */
72           putc_poll (intq_getc (&txq)); 
73         }
74
75       intq_putc (&txq, byte); 
76       write_ier ();
77     }
78   
79   intr_set_level (old_level);
80 }
81
82 /* Flushes anything in the serial buffer out the port in polling
83    mode. */
84 void
85 serial_flush (void) 
86 {
87   enum intr_level old_level = intr_disable ();
88   while (!intq_empty (&txq))
89     putc_poll (intq_getc (&txq));
90   intr_set_level (old_level);
91 }
92
93 /* Configures the serial port for BPS bits per second, BITS bits
94    per byte, the given PARITY, and STOP stop bits. */
95 static void
96 set_serial (int bps, int bits, enum parity_type parity, int stop)
97 {
98   int baud_base = 1843200 / 16;         /* Base rate of 16550A. */
99   uint16_t divisor = baud_base / bps;   /* Clock rate divisor. */
100
101   /* Enable DLAB. */
102   outb (LCR_REG, make_lcr (bits, parity, stop, false, true));
103
104   /* Set baud rate. */
105   outb (LS_REG, divisor & 0xff);
106   outb (MS_REG, divisor >> 8);
107   
108   /* Reset DLAB. */
109   outb (LCR_REG, make_lcr (bits, parity, stop, false, false));
110 }
111
112 /* Update interrupt enable register.
113    If our transmit queue is empty, turn off transmit interrupt. */
114 static void
115 write_ier (void) 
116 {
117   outb (IER_REG, intq_empty (&txq) ? 0 : IER_XMIT);
118 }
119
120
121 /* Polls the serial port until it's ready,
122    and then transmits BYTE. */
123 static void
124 putc_poll (uint8_t byte) 
125 {
126   ASSERT (intr_get_level () == INTR_OFF);
127
128   while ((inb (LSR_REG) & LSR_THRE) == 0)
129     continue;
130   outb (THR_REG, byte);
131 }
132
133 /* Serial interrupt handler.
134    As long as we have a byte to transmit,
135    and the hardware is ready to accept a byte for transmission,
136    transmit a byte.
137    Then update interrupt enable register based on queue
138    status. */
139 static void
140 serial_interrupt (struct intr_frame *f UNUSED) 
141 {
142   while (!intq_empty (&txq) && (inb (LSR_REG) & LSR_THRE) != 0) 
143     outb (THR_REG, intq_getc (&txq));
144   write_ier ();
145 }