+
+/* Update interrupt enable register.
+ If our transmit queue is empty, turn off transmit interrupt.
+ If our receive queue is full, turn off receive interrupt. */
+static void
+update_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);
+}
+
+static void
+serial_interrupt (struct intr_frame *f UNUSED)
+{
+ /* As long as the hardware is ready to take a byte
+ and we have a byte to give it,
+ transmit a byte. */
+ while ((inb (LSR_REG) & LSR_THRE) != 0 && !queue_empty (&xmit_queue))
+ outb (THR_REG, queue_getc (&xmit_queue));
+
+ /* As long as the hardware has a byte to give us
+ and we have space in our buffer for a byte,
+ receive a byte. */
+ while ((inb (LSR_REG) & LSR_DR) != 0 && !queue_full (&recv_queue))
+ queue_putc (&recv_queue, inb (RBR_REG));
+
+ /* Update interrupt enable register based on queue status. */
+ update_ier ();
+}
+\f
+static size_t next (size_t 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 q->head == next (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->head];
+ q->head = next (q->head);
+ signal (q, &q->not_full);
+ return byte;
+}
+
+static void
+queue_putc (struct queue *q, uint8_t byte)
+{
+ ASSERT (intr_get_level () == INTR_OFF);
+
+ ASSERT (owned_by_current_thread (q));
+ while (queue_full (q))
+ wait (q, &q->not_full);
+
+ q->buf[q->tail] = byte;
+ q->tail = next (q->tail);
+ signal (q, &q->not_empty);
+}
+
+static size_t
+next (size_t 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;
+ }
+}