summaryrefslogtreecommitdiff
path: root/device/intr.c
diff options
context:
space:
mode:
Diffstat (limited to 'device/intr.c')
-rw-r--r--device/intr.c198
1 files changed, 198 insertions, 0 deletions
diff --git a/device/intr.c b/device/intr.c
new file mode 100644
index 0000000..6fca328
--- /dev/null
+++ b/device/intr.c
@@ -0,0 +1,198 @@
+#include <device/intr.h>
+#include <device/ds_routines.h>
+#include <kern/queue.h>
+#include <kern/printf.h>
+
+#ifndef MACH_XEN
+// TODO this is only for x86 system
+#define sti() __asm__ __volatile__ ("sti": : :"memory")
+#define cli() __asm__ __volatile__ ("cli": : :"memory")
+
+static boolean_t deliver_intr (int line, ipc_port_t dest_port);
+
+struct intr_entry
+{
+ queue_chain_t chain;
+ ipc_port_t dest;
+ int line;
+ /* The number of interrupts occur since last run of intr_thread. */
+ int interrupts;
+};
+
+static queue_head_t intr_queue;
+/* The total number of unprocessed interrupts. */
+static int tot_num_intr;
+
+static struct intr_entry *
+search_intr (int line, ipc_port_t dest)
+{
+ struct intr_entry *e;
+ queue_iterate (&intr_queue, e, struct intr_entry *, chain)
+ {
+ if (e->dest == dest && e->line == line)
+ return e;
+ }
+ return NULL;
+}
+
+/* This function can only be used in the interrupt handler. */
+void
+queue_intr (int line, ipc_port_t dest)
+{
+ extern void intr_thread ();
+ struct intr_entry *e;
+
+ cli ();
+ e = search_intr (line, dest);
+ assert (e);
+ e->interrupts++;
+ tot_num_intr++;
+ sti ();
+
+ thread_wakeup ((event_t) &intr_thread);
+}
+
+/* insert an interrupt entry in the queue.
+ * This entry exists in the queue until
+ * the corresponding interrupt port is removed.*/
+int
+insert_intr_entry (int line, ipc_port_t dest)
+{
+ int err = 0;
+ struct intr_entry *e, *new;
+ int free = 0;
+
+ new = (struct intr_entry *) kalloc (sizeof (*new));
+ if (new == NULL)
+ return D_NO_MEMORY;
+
+ /* check whether the intr entry has been in the queue. */
+ cli ();
+ e = search_intr (line, dest);
+ if (e)
+ {
+ printf ("the interrupt entry for line %d and port %p has been inserted\n",
+ line, dest);
+ free = 1;
+ err = D_ALREADY_OPEN;
+ goto out;
+ }
+ new->line = line;
+ new->dest = dest;
+ new->interrupts = 0;
+ queue_enter (&intr_queue, new, struct intr_entry *, chain);
+out:
+ sti ();
+ if (free)
+ kfree ((vm_offset_t) new, sizeof (*new));
+ return err;
+}
+
+/* this function should be called when line is disabled. */
+void mark_intr_removed (int line, ipc_port_t dest)
+{
+ struct intr_entry *e;
+
+ e = search_intr (line, dest);
+ if (e)
+ e->dest = NULL;
+}
+
+void
+intr_thread ()
+{
+ struct intr_entry *e;
+ int line;
+ ipc_port_t dest;
+ queue_init (&intr_queue);
+
+ for (;;)
+ {
+ assert_wait ((event_t) &intr_thread, FALSE);
+ cli ();
+ while (tot_num_intr)
+ {
+ int del = 0;
+
+ queue_iterate (&intr_queue, e, struct intr_entry *, chain)
+ {
+ /* if an entry doesn't have dest port,
+ * we should remove it. */
+ if (e->dest == NULL)
+ {
+ del = 1;
+ break;
+ }
+
+ if (e->interrupts)
+ {
+ line = e->line;
+ dest = e->dest;
+ e->interrupts--;
+ tot_num_intr--;
+
+ sti ();
+ deliver_intr (line, dest);
+ cli ();
+ }
+ }
+
+ /* remove the entry without dest port from the queue and free it. */
+ if (del)
+ {
+ assert (!queue_empty (&intr_queue));
+ queue_remove (&intr_queue, e, struct intr_entry *, chain);
+ sti ();
+ kfree ((vm_offset_t) e, sizeof (*e));
+ cli ();
+ }
+ }
+ sti ();
+ thread_block (NULL);
+ }
+}
+
+static boolean_t
+deliver_intr (int line, ipc_port_t dest_port)
+{
+ ipc_kmsg_t kmsg;
+ mach_intr_notification_t *n;
+ mach_port_t dest = (mach_port_t) dest_port;
+
+ if (dest == MACH_PORT_NULL)
+ return FALSE;
+
+ kmsg = ikm_alloc(sizeof *n);
+ if (kmsg == IKM_NULL)
+ return FALSE;
+
+ ikm_init(kmsg, sizeof *n);
+ n = (mach_intr_notification_t *) &kmsg->ikm_header;
+
+ mach_msg_header_t *m = &n->intr_header;
+ mach_msg_type_t *t = &n->intr_type;
+
+ m->msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND, 0);
+ m->msgh_size = sizeof *n;
+ m->msgh_seqno = INTR_NOTIFY_MSGH_SEQNO;
+ m->msgh_local_port = MACH_PORT_NULL;
+ m->msgh_remote_port = MACH_PORT_NULL;
+ m->msgh_id = MACH_INTR_NOTIFY;
+
+ t->msgt_name = MACH_MSG_TYPE_INTEGER_32;
+ t->msgt_size = 32;
+ t->msgt_number = 1;
+ t->msgt_inline = TRUE;
+ t->msgt_longform = FALSE;
+ t->msgt_deallocate = FALSE;
+ t->msgt_unused = 0;
+
+ n->intr_header.msgh_remote_port = dest;
+ n->line = line;
+
+ ipc_port_copy_send (dest_port);
+ ipc_mqueue_send_always(kmsg);
+
+ return TRUE;
+}
+#endif /* MACH_XEN */