X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;ds=sidebyside;f=src%2Fdevices%2Fserial.c;h=230a3206d6456bfd4fa676649b124a59cd0bfc97;hb=eb718e3b5a5470b11e58dcc652f79e115272257a;hp=64ce90f1b38df441526c4500c3e276c5ce604c40;hpb=f31b7fd0769b818f910546ec97284cceb7450593;p=pintos-anon diff --git a/src/devices/serial.c b/src/devices/serial.c index 64ce90f..230a320 100644 --- a/src/devices/serial.c +++ b/src/devices/serial.c @@ -15,6 +15,7 @@ static enum { UNINIT, POLL, QUEUE } mode; static struct intq txq; static void set_serial (int bps, int bits, enum parity_type parity, int stop); +static void putc_poll (uint8_t); static void write_ier (void); static intr_handler_func serial_interrupt; @@ -30,6 +31,7 @@ serial_init_poll (void) outb (FCR_REG, 0); /* Disable FIFO. */ set_serial (9600, 8, NONE, 1); /* 9600 bps, N-8-1. */ outb (MCR_REG, 8); /* Turn on OUT2 output line. */ + intq_init (&txq, "serial xmit"); mode = POLL; } @@ -40,7 +42,6 @@ void serial_init_queue (void) { ASSERT (mode == POLL); - intq_init (&txq, "serial xmit"); intr_register (0x20 + 4, 0, INTR_OFF, serial_interrupt, "serial"); mode = QUEUE; } @@ -49,23 +50,36 @@ serial_init_queue (void) void serial_putc (uint8_t byte) { - if (mode == POLL || intr_context ()) + enum intr_level old_level = intr_disable (); + + if (mode == POLL || old_level == INTR_OFF) { - /* Poll the serial port until it's ready for a byte, and - then transmit. */ - while ((inb (LSR_REG) & LSR_THRE) == 0) - continue; - outb (THR_REG, byte); + /* If we're not set up for interrupt-driven I/O yet, + or if interrupts are off, + use dumb polling for serial I/O. */ + serial_flush (); + putc_poll (byte); } else { - /* Lock the queue, add a byte, and update the interrupt - enable register. */ - intq_lock (&txq); + /* Otherwise, queue a byte and update the interrupt enable + register. */ intq_putc (&txq, byte); write_ier (); - intq_unlock (&txq); } + + intr_set_level (old_level); +} + +/* Flushes anything in the serial buffer out the port in polling + mode. */ +void +serial_flush (void) +{ + enum intr_level old_level = intr_disable (); + while (!intq_empty (&txq)) + putc_poll (intq_getc (&txq)); + intr_set_level (old_level); } /* Configures the serial port for BPS bits per second, BITS bits @@ -95,6 +109,19 @@ write_ier (void) outb (IER_REG, intq_empty (&txq) ? 0 : IER_XMIT); } + +/* Polls the serial port until it's ready, + and then transmits BYTE. */ +static void +putc_poll (uint8_t byte) +{ + ASSERT (intr_get_level () == INTR_OFF); + + while ((inb (LSR_REG) & LSR_THRE) == 0) + continue; + outb (THR_REG, byte); +} + /* Serial interrupt handler. As long as we have a byte to transmit, and the hardware is ready to accept a byte for transmission,