00001 #include "devices/serial.h"
00002
00003 #include <debug.h>
00004 #include "devices/input.h"
00005 #include "devices/intq.h"
00006 #include "devices/timer.h"
00007 #include "threads/io.h"
00008 #include "threads/interrupt.h"
00009 #include "threads/synch.h"
00010 #include "threads/thread.h"
00011
00012
00013
00014
00015
00016
00017
00018
00019 #define IO_BASE 0x3f8
00020
00021
00022 #define RBR_REG (IO_BASE + 0)
00023 #define THR_REG (IO_BASE + 0)
00024 #define IER_REG (IO_BASE + 1)
00025
00026
00027 #define LS_REG (IO_BASE + 0)
00028 #define MS_REG (IO_BASE + 1)
00029
00030
00031 #define IIR_REG (IO_BASE + 2)
00032 #define FCR_REG (IO_BASE + 2)
00033 #define LCR_REG (IO_BASE + 3)
00034 #define MCR_REG (IO_BASE + 4)
00035 #define LSR_REG (IO_BASE + 5)
00036
00037
00038 #define IER_RECV 0x01
00039 #define IER_XMIT 0x02
00040
00041
00042 #define LCR_N81 0x03
00043 #define LCR_DLAB 0x80
00044
00045
00046 #define MCR_OUT2 0x08
00047
00048
00049 #define LSR_DR 0x01
00050 #define LSR_THRE 0x20
00051
00052
00053 static enum { UNINIT, POLL, QUEUE } mode;
00054
00055
00056 static struct intq txq;
00057
00058 static void set_serial (int bps);
00059 static void putc_poll (uint8_t);
00060 static void write_ier (void);
00061 static intr_handler_func serial_interrupt;
00062
00063
00064
00065
00066
00067 static void
00068 init_poll (void)
00069 {
00070 ASSERT (mode == UNINIT);
00071 outb (IER_REG, 0);
00072 outb (FCR_REG, 0);
00073 set_serial (9600);
00074 outb (MCR_REG, MCR_OUT2);
00075 intq_init (&txq);
00076 mode = POLL;
00077 }
00078
00079
00080
00081
00082 void
00083 serial_init_queue (void)
00084 {
00085 enum intr_level old_level;
00086
00087 if (mode == UNINIT)
00088 init_poll ();
00089 ASSERT (mode == POLL);
00090
00091 intr_register_ext (0x20 + 4, serial_interrupt, "serial");
00092 mode = QUEUE;
00093 old_level = intr_disable ();
00094 write_ier ();
00095 intr_set_level (old_level);
00096 }
00097
00098
00099 void
00100 serial_putc (uint8_t byte)
00101 {
00102 enum intr_level old_level = intr_disable ();
00103
00104 if (mode != QUEUE)
00105 {
00106
00107
00108 if (mode == UNINIT)
00109 init_poll ();
00110 putc_poll (byte);
00111 }
00112 else
00113 {
00114
00115
00116 if (old_level == INTR_OFF && intq_full (&txq))
00117 {
00118
00119
00120
00121
00122
00123 putc_poll (intq_getc (&txq));
00124 }
00125
00126 intq_putc (&txq, byte);
00127 write_ier ();
00128 }
00129
00130 intr_set_level (old_level);
00131 }
00132
00133
00134
00135 void
00136 serial_flush (void)
00137 {
00138 enum intr_level old_level = intr_disable ();
00139 while (!intq_empty (&txq))
00140 putc_poll (intq_getc (&txq));
00141 intr_set_level (old_level);
00142 }
00143
00144
00145
00146
00147
00148 void
00149 serial_notify (void)
00150 {
00151 ASSERT (intr_get_level () == INTR_OFF);
00152 if (mode == QUEUE)
00153 write_ier ();
00154 }
00155
00156
00157 static void
00158 set_serial (int bps)
00159 {
00160 int base_rate = 1843200 / 16;
00161 uint16_t divisor = base_rate / bps;
00162
00163 ASSERT (bps >= 300 && bps <= 115200);
00164
00165
00166 outb (LCR_REG, LCR_N81 | LCR_DLAB);
00167
00168
00169 outb (LS_REG, divisor & 0xff);
00170 outb (MS_REG, divisor >> 8);
00171
00172
00173 outb (LCR_REG, LCR_N81);
00174 }
00175
00176
00177 static void
00178 write_ier (void)
00179 {
00180 uint8_t ier = 0;
00181
00182 ASSERT (intr_get_level () == INTR_OFF);
00183
00184
00185
00186 if (!intq_empty (&txq))
00187 ier |= IER_XMIT;
00188
00189
00190
00191 if (!input_full ())
00192 ier |= IER_RECV;
00193
00194 outb (IER_REG, ier);
00195 }
00196
00197
00198
00199 static void
00200 putc_poll (uint8_t byte)
00201 {
00202 ASSERT (intr_get_level () == INTR_OFF);
00203
00204 while ((inb (LSR_REG) & LSR_THRE) == 0)
00205 continue;
00206 outb (THR_REG, byte);
00207 }
00208
00209
00210 static void
00211 serial_interrupt (struct intr_frame *f UNUSED)
00212 {
00213
00214
00215 inb (IIR_REG);
00216
00217
00218
00219 while (!input_full () && (inb (LSR_REG) & LSR_DR) != 0)
00220 input_putc (inb (RBR_REG));
00221
00222
00223
00224 while (!intq_empty (&txq) && (inb (LSR_REG) & LSR_THRE) != 0)
00225 outb (THR_REG, intq_getc (&txq));
00226
00227
00228 write_ier ();
00229 }