1 #include "devices/serial.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"
11 /* Transmission mode. */
12 static enum { UNINIT, POLL, QUEUE } mode;
14 /* Data to be transmitted. */
15 static struct intq txq;
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;
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. */
27 serial_init_poll (void)
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");
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. */
42 serial_init_queue (void)
44 ASSERT (mode == POLL);
45 intr_register (0x20 + 4, 0, INTR_OFF, serial_interrupt, "serial");
49 /* Sends BYTE to the serial port. */
51 serial_putc (uint8_t byte)
53 enum intr_level old_level = intr_disable ();
57 /* If we're not set up for interrupt-driven I/O yet,
58 use dumb polling to transmit a byte. */
63 /* Otherwise, queue a byte and update the interrupt enable
65 if (old_level == INTR_OFF && intq_full (&txq))
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
72 putc_poll (intq_getc (&txq));
75 intq_putc (&txq, byte);
79 intr_set_level (old_level);
82 /* Flushes anything in the serial buffer out the port in polling
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);
93 /* Configures the serial port for BPS bits per second, BITS bits
94 per byte, the given PARITY, and STOP stop bits. */
96 set_serial (int bps, int bits, enum parity_type parity, int stop)
98 int baud_base = 1843200 / 16; /* Base rate of 16550A. */
99 uint16_t divisor = baud_base / bps; /* Clock rate divisor. */
102 outb (LCR_REG, make_lcr (bits, parity, stop, false, true));
105 outb (LS_REG, divisor & 0xff);
106 outb (MS_REG, divisor >> 8);
109 outb (LCR_REG, make_lcr (bits, parity, stop, false, false));
112 /* Update interrupt enable register.
113 If our transmit queue is empty, turn off transmit interrupt. */
117 outb (IER_REG, intq_empty (&txq) ? 0 : IER_XMIT);
121 /* Polls the serial port until it's ready,
122 and then transmits BYTE. */
124 putc_poll (uint8_t byte)
126 ASSERT (intr_get_level () == INTR_OFF);
128 while ((inb (LSR_REG) & LSR_THRE) == 0)
130 outb (THR_REG, byte);
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,
137 Then update interrupt enable register based on queue
140 serial_interrupt (struct intr_frame *f UNUSED)
142 while (!intq_empty (&txq) && (inb (LSR_REG) & LSR_THRE) != 0)
143 outb (THR_REG, intq_getc (&txq));