Move serial interrupt queue into new file intq.c.
authorBen Pfaff <blp@cs.stanford.edu>
Tue, 7 Sep 2004 23:49:03 +0000 (23:49 +0000)
committerBen Pfaff <blp@cs.stanford.edu>
Tue, 7 Sep 2004 23:49:03 +0000 (23:49 +0000)
Remove serial receive code.

src/Makefile.build
src/devices/intq.c [new file with mode: 0644]
src/devices/intq.h [new file with mode: 0644]
src/devices/serial.c
src/devices/serial.h
src/threads/init.c

index 3886b5d0b7f279ed21afd6ad4e9c39e83aa2cfed..e55ce566e8dfefc61c45824744c4102ef2f5ff75 100644 (file)
@@ -32,6 +32,7 @@ devices_SRC += devices/kbd.c          # Keyboard device.
 devices_SRC += devices/vga.c           # Video device.
 devices_SRC += devices/serial.c                # Serial port device.
 devices_SRC += devices/disk.c          # IDE disk device.
+devices_SRC += devices/intq.c          # Interrupt queue.
 
 # Library code shared between kernel and user programs.
 lib_SRC  = lib/debug.c         # Debug helpers.
diff --git a/src/devices/intq.c b/src/devices/intq.c
new file mode 100644 (file)
index 0000000..7f73991
--- /dev/null
@@ -0,0 +1,144 @@
+#include "devices/intq.h"
+#include <debug.h>
+#include "threads/thread.h"
+
+static int next (int pos);
+static bool owned_by_current_thread (const struct intq *);
+static void wait (struct intq *q, struct thread **waiter);
+static void signal (struct intq *q, struct thread **waiter);
+
+/* Initializes interrupt queue Q, naming it NAME (for debugging
+   purposes). */
+void
+intq_init (struct intq *q, const char *name) 
+{
+  lock_init (&q->lock, name);
+  q->not_full = q->not_empty = NULL;
+  q->head = q->tail = 0;
+}
+
+/* Locks out other threads from Q (with Q's lock) and interrupt
+   handlers (by disabling interrupts). */
+void
+intq_lock (struct intq *q) 
+{
+  ASSERT (!intr_context ());
+  ASSERT (!owned_by_current_thread (q));
+
+  lock_acquire (&q->lock);
+  q->old_level = intr_disable ();
+}
+
+/* Unlocks Q. */
+void
+intq_unlock (struct intq *q) 
+{
+  ASSERT (!intr_context ());
+  ASSERT (owned_by_current_thread (q));
+
+  lock_release (&q->lock);
+  intr_set_level (q->old_level);
+}
+
+/* Returns true if Q is empty, false otherwise. */
+bool
+intq_empty (const struct intq *q) 
+{
+  ASSERT (intr_get_level () == INTR_OFF);
+  return q->head == q->tail;
+}
+
+/* Returns true if Q is full, false otherwise. */
+bool
+intq_full (const struct intq *q) 
+{
+  ASSERT (intr_get_level () == INTR_OFF);
+  return next (q->head) == q->tail;
+}
+
+/* Removes a byte from Q and returns it.
+   Q must not be empty if called from an interrupt handler.
+   Otherwise, if Q is empty, first waits until a byte is added.
+   Either Q must be locked or we must be in an interrupt
+   handler. */   
+uint8_t
+intq_getc (struct intq *q) 
+{
+  uint8_t byte;
+  
+  ASSERT (owned_by_current_thread (q));
+  while (intq_empty (q))
+    wait (q, &q->not_empty);
+
+  byte = q->buf[q->tail];
+  q->tail = next (q->tail);
+  signal (q, &q->not_full);
+  return byte;
+}
+
+/* Adds BYTE to the end of Q.
+   Q must not be full if called from an interrupt handler.
+   Otherwise, if Q is full, first waits until a byte is removed.
+   Either Q must be locked or we must be in an interrupt
+   handler. */   
+void
+intq_putc (struct intq *q, uint8_t byte) 
+{
+  ASSERT (owned_by_current_thread (q));
+  while (intq_full (q))
+    wait (q, &q->not_full);
+
+  q->buf[q->head] = byte;
+  q->head = next (q->head);
+  signal (q, &q->not_empty);
+}
+\f
+/* Returns the position after POS within an intq. */
+static int
+next (int pos) 
+{
+  return (pos + 1) % INTQ_BUFSIZE;
+}
+
+/* Returns true if Q is "owned by" the current thread; that is,
+   if Q is locked by the current thread or if we're in an
+   external interrupt handler. */
+static bool
+owned_by_current_thread (const struct intq *q) 
+{
+  return (intr_context ()
+          || (lock_held_by_current_thread (&q->lock)
+              && intr_get_level () == INTR_OFF));
+}
+
+/* WAITER must be the address of Q's not_empty or not_full
+   member.  Waits until the given condition is true. */
+static void
+wait (struct intq *q, struct thread **waiter) 
+{
+  ASSERT (!intr_context ());
+  ASSERT (owned_by_current_thread (q));
+  ASSERT ((waiter == &q->not_empty && intq_empty (q))
+          || (waiter == &q->not_full && intq_full (q)));
+
+  *waiter = thread_current ();
+  thread_block ();
+}
+
+/* WAITER must be the address of Q's not_empty or not_full
+   member, and the associated condition must be true.  If a
+   thread is waiting for the condition, wakes it up and resets
+   the waiting thread. */
+static void
+signal (struct intq *q, struct thread **waiter) 
+{
+  ASSERT (owned_by_current_thread (q));
+  ASSERT ((waiter == &q->not_empty && !intq_empty (q))
+          || (waiter == &q->not_full && !intq_full (q)));
+
+  if (*waiter != NULL) 
+    {
+      thread_unblock (*waiter);
+      *waiter = NULL;
+    }
+}
diff --git a/src/devices/intq.h b/src/devices/intq.h
new file mode 100644 (file)
index 0000000..5c278fa
--- /dev/null
@@ -0,0 +1,57 @@
+#ifndef DEVICES_INTQ_H
+#define DEVICES_INTQ_H
+
+#include "threads/interrupt.h"
+#include "threads/synch.h"
+
+/* An "interrupt queue", a circular buffer shared between
+   kernel threads and external interrupt handlers.
+
+   A kernel thread that touches an interrupt queue must bracket
+   its accesses with calls to intq_lock() and intq_unlock().
+   These functions take a lock associated with the queue (which
+   locks out other kernel threads) and disable interrupts (which
+   locks out interrupt handlers).
+
+   An external interrupt handler that touches an interrupt queue
+   need not take any special precautions.  Interrupts are
+   disabled in an external interrupt handler, so other code will
+   not interfere.  The interrupt cannot occur during an update to
+   the interrupt queue by a kernel thread because kernel threads
+   disable interrupts while touching interrupt queues.
+
+   Incidentally, this has the structure of a "monitor".  Normally
+   we'd use locks and condition variables from threads/synch.h to
+   implement a monitor.  Unfortunately, those are intended only
+   to protect kernel threads from one another, not from interrupt
+   handlers. */
+
+/* Queue buffer size, in bytes. */
+#define INTQ_BUFSIZE 8
+
+/* A circular queue of bytes. */
+struct intq
+  {
+    /* Mutual exclusion. */
+    enum intr_level old_level;  /* Excludes interrupt handlers. */
+    struct lock lock;           /* Excludes kernel threads. */
+
+    /* Waiting threads. */
+    struct thread *not_full;    /* Thread waiting for not-full condition. */
+    struct thread *not_empty;   /* Thread waiting for not-empty condition. */
+
+    /* Queue. */
+    uint8_t buf[INTQ_BUFSIZE];  /* Buffer. */
+    int head;                   /* New data is written here. */
+    int tail;                   /* Old data is read here. */
+  };
+
+void intq_init (struct intq *, const char *);
+void intq_lock (struct intq *);
+void intq_unlock (struct intq *);
+bool intq_empty (const struct intq *);
+bool intq_full (const struct intq *);
+uint8_t intq_getc (struct intq *);
+void intq_putc (struct intq *, uint8_t);
+
+#endif /* devices/intq.h */
index a27c50a0ab835fc02e888ebce02b9828054fa7c9..64ce90f1b38df441526c4500c3e276c5ce604c40 100644 (file)
@@ -1,39 +1,23 @@
 #include "devices/serial.h"
 #include <debug.h>
 #include "devices/16550a.h"
+#include "devices/intq.h"
 #include "devices/timer.h"
 #include "threads/io.h"
 #include "threads/interrupt.h"
 #include "threads/synch.h"
 #include "threads/thread.h"
 
-#define QUEUE_BUFSIZE 8
+/* Transmission mode. */
+static enum { UNINIT, POLL, QUEUE } mode;
 
-struct queue 
-  {
-    enum intr_level old_level;
-    struct lock lock;
-    
-    struct thread *not_full, *not_empty;
-    uint8_t buf[QUEUE_BUFSIZE];
-    int head, tail;
-  };
-
-static struct queue recv_queue, xmit_queue;
-static bool polled_io;
+/* Data to be transmitted. */
+static struct intq txq;
 
 static void set_serial (int bps, int bits, enum parity_type parity, int stop);
-static void update_ier (void);
+static void write_ier (void);
 static intr_handler_func serial_interrupt;
 
-static void queue_init (struct queue *, const char *);
-static void queue_lock (struct queue *);
-static void queue_unlock (struct queue *);
-static bool queue_empty (const struct queue *);
-static bool queue_full (const struct queue *);
-static uint8_t queue_getc (struct queue *);
-static void queue_putc (struct queue *, uint8_t);
-
 /* Initializes the serial port device for polling mode.
    Polling mode busy-waits for the serial port to become free
    before writing to it.  It's slow, but until interrupts have
@@ -41,12 +25,12 @@ static void queue_putc (struct queue *, uint8_t);
 void
 serial_init_poll (void) 
 {
-  ASSERT (!polled_io);
-  polled_io = true;
+  ASSERT (mode == UNINIT);
   outb (IER_REG, 0);                    /* Turn off all interrupts. */
   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. */
+  mode = POLL;
 } 
 
 /* Initializes the serial port device for queued interrupt-driven
@@ -55,62 +39,37 @@ serial_init_poll (void)
 void
 serial_init_queue (void) 
 {
-  ASSERT (polled_io);
-  polled_io = false;
-  queue_init (&recv_queue, "serial recv");
-  queue_init (&xmit_queue, "serial xmit");
+  ASSERT (mode == POLL);
+  intq_init (&txq, "serial xmit");
   intr_register (0x20 + 4, 0, INTR_OFF, serial_interrupt, "serial");
+  mode = QUEUE;
 }
 
-static void putc_poll (uint8_t);
-static void putc_queue (uint8_t);
-
 /* Sends BYTE to the serial port. */
 void
 serial_putc (uint8_t byte) 
 {
-  if (polled_io || intr_context ())
-    putc_poll (byte);
+  if (mode == POLL || intr_context ())
+    {
+      /* 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);
+    }
   else
-    putc_queue (byte); 
-}
-
-static void
-putc_poll (uint8_t byte)
-{
-  while ((inb (LSR_REG) & LSR_THRE) == 0)
-    continue;
-  outb (THR_REG, byte);
-}
-
-static void
-putc_queue (uint8_t byte)
-{
-  queue_lock (&xmit_queue);
-
-  queue_putc (&xmit_queue, byte); 
-  update_ier ();
-
-  queue_unlock (&xmit_queue);
-}
-
-/* Reads and returns a byte from the serial port. */
-uint8_t
-serial_getc (void) 
-{
-  uint8_t byte;
-  queue_lock (&recv_queue);
-
-  /* Read a byte. */
-  byte = queue_getc (&recv_queue);
-  update_ier ();
-
-  queue_unlock (&recv_queue);
-  return byte;
+    {
+      /* Lock the queue, add a byte, and update the interrupt
+         enable register. */
+      intq_lock (&txq);
+      intq_putc (&txq, byte); 
+      write_ier ();
+      intq_unlock (&txq);
+    }
 }
 
-/* Configures the first serial port for BPS bits per second,
-   BITS bits per byte, the given PARITY, and STOP stop bits. */
+/* Configures the serial port for BPS bits per second, BITS bits
+   per byte, the given PARITY, and STOP stop bits. */
 static void
 set_serial (int bps, int bits, enum parity_type parity, int stop)
 {
@@ -129,148 +88,23 @@ set_serial (int bps, int bits, enum parity_type parity, int stop)
 }
 
 /* Update interrupt enable register.
-   If our transmit queue is empty, turn off transmit interrupt.
-   If our receive queue is full, turn off receive interrupt. */
+   If our transmit queue is empty, turn off transmit interrupt. */
 static void
-update_ier (void) 
+write_ier (void) 
 {
-  uint8_t ier = 0;
-  
-  ASSERT (intr_get_level () == INTR_OFF);
-
-  if (!queue_empty (&xmit_queue))
-    ier |= IER_XMIT;
-  if (!queue_full (&recv_queue))
-    ier |= IER_RECV;
-  outb (IER_REG, ier);
+  outb (IER_REG, intq_empty (&txq) ? 0 : IER_XMIT);
 }
 
+/* Serial interrupt handler.
+   As long as we have a byte to transmit,
+   and the hardware is ready to accept a byte for transmission,
+   transmit a byte.
+   Then update interrupt enable register based on queue
+   status. */
 static void
 serial_interrupt (struct intr_frame *f UNUSED) 
 {
-  /* As long as we have space in our buffer for a byte,
-     and the hardware has a byte to give us,
-     receive a byte. */
-  while (!queue_full (&recv_queue) && (inb (LSR_REG) & LSR_DR) != 0)
-    {
-      uint8_t byte = inb (RBR_REG);
-      //outb (0x0402, byte);
-      queue_putc (&recv_queue, byte); 
-    }
-  
-  /* As long as we have a byte to transmit,
-     and the hardware is ready to accept a byte for transmission,
-     transmit a byte. */
-  while (!queue_empty (&xmit_queue) && (inb (LSR_REG) & LSR_THRE) != 0) 
-    outb (THR_REG, queue_getc (&xmit_queue));
-  
-  /* Update interrupt enable register based on queue status. */
-  update_ier ();
-}
-\f
-static int next (int pos);
-static bool owned_by_current_thread (const struct queue *);
-static void wait (struct queue *q, struct thread **waiter);
-static void signal (struct queue *q, struct thread **waiter);
-
-static void
-queue_init (struct queue *q, const char *name) 
-{
-  lock_init (&q->lock, name);
-  q->not_full = q->not_empty = NULL;
-  q->head = q->tail = 0;
-}
-
-static void
-queue_lock (struct queue *q) 
-{
-  lock_acquire (&q->lock);
-  q->old_level = intr_disable ();
-}
-
-static void
-queue_unlock (struct queue *q) 
-{
-  ASSERT (!intr_context ());
-  ASSERT (owned_by_current_thread (q));
-
-  lock_release (&q->lock);
-  intr_set_level (q->old_level);
-}
-
-static bool
-queue_empty (const struct queue *q) 
-{
-  ASSERT (intr_get_level () == INTR_OFF);
-  return q->head == q->tail;
-}
-
-static bool
-queue_full (const struct queue *q) 
-{
-  ASSERT (intr_get_level () == INTR_OFF);
-  return next (q->head) == q->tail;
-}
-
-static uint8_t
-queue_getc (struct queue *q) 
-{
-  uint8_t byte;
-  
-  ASSERT (owned_by_current_thread (q));
-  while (queue_empty (q))
-    wait (q, &q->not_empty);
-
-  byte = q->buf[q->tail];
-  q->tail = next (q->tail);
-  signal (q, &q->not_full);
-  return byte;
-}
-
-static void
-queue_putc (struct queue *q, uint8_t byte) 
-{
-  ASSERT (owned_by_current_thread (q));
-  while (queue_full (q))
-    wait (q, &q->not_full);
-
-  q->buf[q->head] = byte;
-  q->head = next (q->head);
-  signal (q, &q->not_empty);
-}
-
-static int
-next (int pos) 
-{
-  return (pos + 1) % QUEUE_BUFSIZE;
-}
-
-static bool
-owned_by_current_thread (const struct queue *q) 
-{
-  return (intr_context ()
-          || (lock_held_by_current_thread (&q->lock)
-              && intr_get_level () == INTR_OFF));
-}
-
-static void
-wait (struct queue *q, struct thread **waiter) 
-{
-  ASSERT (!intr_context ());
-  ASSERT (owned_by_current_thread (q));
-
-  *waiter = thread_current ();
-  thread_block ();
-}
-
-static void
-signal (struct queue *q, struct thread **waiter) 
-{
-  ASSERT (owned_by_current_thread (q));
-
-  if (*waiter != NULL) 
-    {
-      thread_unblock (*waiter);
-      *waiter = NULL;
-    }
+  while (!intq_empty (&txq) && (inb (LSR_REG) & LSR_THRE) != 0) 
+    outb (THR_REG, intq_getc (&txq));
+  write_ier ();
 }
index 3ef124ba2d1e0cce9151be1a70d7b7a9fed8defd..3500ffac5223dcdc7ef1b1f814519fcaaf061bb4 100644 (file)
@@ -3,8 +3,8 @@
 
 #include <stdint.h>
 
-void serial_init (int phase);
+void serial_init_poll (void);
+void serial_init_queue (void);
 void serial_putc (uint8_t);
-uint8_t serial_getc (void);
 
 #endif /* devices/serial.h */
index 2684d17d5023621594e89bc35a2e647da20178a1..35ce92a2e255c7b68865931877dba832c9ff048a 100644 (file)
@@ -55,7 +55,7 @@ main (void)
   /* Needed by printf(), so initialize them very early. */
   ram_init ();
   vga_init ();
-  serial_init (1);
+  serial_init_poll ();
 
   /* Greet user. */
   printf ("Booting cnachos86 with %'d kB RAM...\n", ram_pages * 4);
@@ -87,7 +87,7 @@ main (void)
 
   /* Start thread scheduler and enable interrupts. */
   thread_start ();
-  serial_init (2);
+  serial_init_queue ();
 
 #ifdef FILESYS
   /* Initialize filesystem. */
@@ -98,9 +98,6 @@ main (void)
 
   printf ("Boot complete.\n");
   
-  for (;;) 
-    putchar (serial_getc ());
-
 #ifdef USERPROG
   /* Run a user program. */
   if (initial_program != NULL)