summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefrag.am4
-rw-r--r--device/dev_hdr.h9
-rw-r--r--device/ds_routines.c54
-rw-r--r--device/intr.c283
-rw-r--r--device/intr.h62
-rw-r--r--doc/mach.texi22
-rw-r--r--i386/Makefrag.am2
-rw-r--r--i386/i386/irq.c66
-rw-r--r--i386/i386/irq.h27
-rw-r--r--i386/i386/pic.c72
-rw-r--r--i386/i386/pic.h2
-rw-r--r--i386/i386at/conf.c8
-rw-r--r--include/device/device.defs18
-rw-r--r--include/device/notify.defs36
-rw-r--r--include/device/notify.h34
-rw-r--r--kern/startup.c4
-rw-r--r--linux/dev/arch/i386/kernel/irq.c152
-rw-r--r--linux/src/include/asm-i386/irq.h2
18 files changed, 776 insertions, 81 deletions
diff --git a/Makefrag.am b/Makefrag.am
index 9cf976a5..ea612275 100644
--- a/Makefrag.am
+++ b/Makefrag.am
@@ -314,6 +314,8 @@ libkernel_a_SOURCES += \
device/ds_routines.h \
device/if_ether.h \
device/if_hdr.h \
+ device/intr.c \
+ device/intr.h \
device/io_req.h \
device/net_io.c \
device/net_io.h \
@@ -361,6 +363,8 @@ include_device_HEADERS = \
include/device/device_types.h \
include/device/disk_status.h \
include/device/net_status.h \
+ include/device/notify.defs \
+ include/device/notify.h \
include/device/tape_status.h \
include/device/tty_status.h
diff --git a/device/dev_hdr.h b/device/dev_hdr.h
index ad98e0bb..4bd12c1c 100644
--- a/device/dev_hdr.h
+++ b/device/dev_hdr.h
@@ -146,4 +146,13 @@ extern void dev_set_indirection(
dev_ops_t ops,
int unit);
+/*
+ * compare device name
+ */
+extern boolean_t __attribute__ ((pure))
+name_equal(
+ const char *src,
+ int len,
+ const char *target);
+
#endif /* _DEVICE_DEV_HDR_H_ */
diff --git a/device/ds_routines.c b/device/ds_routines.c
index fc051e8f..78ff51fe 100644
--- a/device/ds_routines.c
+++ b/device/ds_routines.c
@@ -92,6 +92,7 @@
#include <device/device_port.h>
#include <device/device_reply.user.h>
#include <device/device_emul.h>
+#include <device/intr.h>
#include <machine/machspl.h>
@@ -319,6 +320,59 @@ ds_device_map (device_t dev, vm_prot_t prot, vm_offset_t offset,
offset, size, pager, unmap);
}
+/* TODO: missing deregister support */
+io_return_t
+ds_device_intr_register (device_t dev, int id,
+ int flags, ipc_port_t receive_port)
+{
+ kern_return_t err;
+ mach_device_t mdev = dev->emul_data;
+
+ /* Refuse if device is dead or not completely open. */
+ if (dev == DEVICE_NULL)
+ return D_NO_SUCH_DEVICE;
+
+ /* No flag is defined for now */
+ if (flags != 0)
+ return D_INVALID_OPERATION;
+
+ /* Must be called on the irq device only */
+ if (! name_equal(mdev->dev_ops->d_name, 3, "irq"))
+ return D_INVALID_OPERATION;
+
+ user_intr_t *e = insert_intr_entry (&irqtab, id, receive_port);
+ if (!e)
+ return D_NO_MEMORY;
+
+ // TODO The original port should be replaced
+ // when the same device driver calls it again,
+ // in order to handle the case that the device driver crashes and restarts.
+ err = install_user_intr_handler (&irqtab, id, flags, e);
+ if (err == D_SUCCESS)
+ {
+ /* If the port is installed successfully, increase its reference by 1.
+ * Thus, the port won't be destroyed after its task is terminated. */
+ ip_reference (receive_port);
+ }
+ return err;
+}
+
+kern_return_t
+ds_device_intr_ack (device_t dev, ipc_port_t receive_port)
+{
+ mach_device_t mdev = dev->emul_data;
+
+ /* Refuse if device is dead or not completely open. */
+ if (dev == DEVICE_NULL)
+ return D_NO_SUCH_DEVICE;
+
+ /* Must be called on the irq device only */
+ if (! name_equal(mdev->dev_ops->d_name, 3, "irq"))
+ return D_INVALID_OPERATION;
+
+ return irq_acknowledge(receive_port);
+}
+
boolean_t
ds_notify (mach_msg_header_t *msg)
{
diff --git a/device/intr.c b/device/intr.c
new file mode 100644
index 00000000..fbb9f495
--- /dev/null
+++ b/device/intr.c
@@ -0,0 +1,283 @@
+/*
+ * Copyright (c) 2010, 2011, 2016, 2019 Free Software Foundation, Inc.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * THE FREE SOFTWARE FOUNDATIONALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. THE FREE SOFTWARE FOUNDATION DISCLAIMS ANY LIABILITY OF ANY KIND
+ * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ */
+
+#include <device/intr.h>
+#include <device/device_types.h>
+#include <device/device_port.h>
+#include <device/notify.h>
+#include <kern/printf.h>
+#include <machine/spl.h>
+#include <machine/irq.h>
+#include <ipc/ipc_space.h>
+
+#ifndef MACH_XEN
+
+queue_head_t main_intr_queue;
+static boolean_t deliver_intr (int id, ipc_port_t dst_port);
+
+static user_intr_t *
+search_intr (struct irqdev *dev, ipc_port_t dst_port)
+{
+ user_intr_t *e;
+ queue_iterate (dev->intr_queue, e, user_intr_t *, chain)
+ {
+ if (e->dst_port == dst_port)
+ return e;
+ }
+ return NULL;
+}
+
+kern_return_t
+irq_acknowledge (ipc_port_t receive_port)
+{
+ user_intr_t *e;
+ kern_return_t ret = 0;
+
+ spl_t s = splhigh ();
+ e = search_intr (&irqtab, receive_port);
+
+ if (!e)
+ printf("didn't find user intr for interrupt !?\n");
+ else
+ {
+ if (!e->n_unacked)
+ ret = D_INVALID_OPERATION;
+ else
+ e->n_unacked--;
+ }
+ splx (s);
+
+ if (ret)
+ return ret;
+
+ if (irqtab.irqdev_ack)
+ (*(irqtab.irqdev_ack)) (&irqtab, e->id);
+
+ __enable_irq (irqtab.irq[e->id]);
+
+ return D_SUCCESS;
+}
+
+/* This function can only be used in the interrupt handler. */
+static void
+queue_intr (struct irqdev *dev, int id, user_intr_t *e)
+{
+ /* Until userland has handled the IRQ in the driver, we have to keep it
+ * disabled. Level-triggered interrupts would keep raising otherwise. */
+ __disable_irq (dev->irq[id]);
+
+ spl_t s = splhigh ();
+ e->n_unacked++;
+ e->interrupts++;
+ dev->tot_num_intr++;
+ splx (s);
+
+ thread_wakeup ((event_t) &intr_thread);
+}
+
+int
+deliver_user_intr (struct irqdev *dev, int id, user_intr_t *e)
+{
+ /* The reference of the port was increased
+ * when the port was installed.
+ * If the reference is 1, it means the port should
+ * have been destroyed and I destroy it now. */
+ if (e->dst_port
+ && e->dst_port->ip_references == 1)
+ {
+ printf ("irq handler [%d]: release a dead delivery port %p entry %p\n", id, e->dst_port, e);
+ ipc_port_release (e->dst_port);
+ e->dst_port = MACH_PORT_NULL;
+ thread_wakeup ((event_t) &intr_thread);
+ return 0;
+ }
+ else
+ {
+ queue_intr (dev, id, e);
+ return 1;
+ }
+}
+
+/* insert an interrupt entry in the queue.
+ * This entry exists in the queue until
+ * the corresponding interrupt port is removed.*/
+user_intr_t *
+insert_intr_entry (struct irqdev *dev, int id, ipc_port_t dst_port)
+{
+ user_intr_t *e, *new, *ret;
+ int free = 0;
+
+ new = (user_intr_t *) kalloc (sizeof (*new));
+ if (new == NULL)
+ return NULL;
+
+ /* check whether the intr entry has been in the queue. */
+ spl_t s = splhigh ();
+ e = search_intr (dev, dst_port);
+ if (e)
+ {
+ printf ("the interrupt entry for irq[%d] and port %p has already been inserted\n", id, dst_port);
+ free = 1;
+ ret = NULL;
+ goto out;
+ }
+ printf("irq handler [%d]: new delivery port %p entry %p\n", id, dst_port, new);
+ ret = new;
+ new->id = id;
+ new->dst_port = dst_port;
+ new->interrupts = 0;
+ new->n_unacked = 0;
+
+ queue_enter (dev->intr_queue, new, user_intr_t *, chain);
+out:
+ splx (s);
+ if (free)
+ kfree ((vm_offset_t) new, sizeof (*new));
+ return ret;
+}
+
+void
+intr_thread (void)
+{
+ user_intr_t *e;
+ int id;
+ ipc_port_t dst_port;
+ queue_init (&main_intr_queue);
+
+ for (;;)
+ {
+ assert_wait ((event_t) &intr_thread, FALSE);
+ /* Make sure we wake up from times to times to check for aborted processes */
+ thread_set_timeout (hz);
+ spl_t s = splhigh ();
+
+ /* Check for aborted processes */
+ queue_iterate (&main_intr_queue, e, user_intr_t *, chain)
+ {
+ if ((!e->dst_port || e->dst_port->ip_references == 1) && e->n_unacked)
+ {
+ printf ("irq handler [%d]: release dead delivery %d unacked irqs port %p entry %p\n", e->id, e->n_unacked, e->dst_port, e);
+ /* The reference of the port was increased
+ * when the port was installed.
+ * If the reference is 1, it means the port should
+ * have been destroyed and I clear unacked irqs now, so the Linux
+ * handling can trigger, and we will cleanup later after the Linux
+ * handler is cleared. */
+ /* TODO: rather immediately remove from Linux handler */
+ while (e->n_unacked)
+ {
+ __enable_irq (irqtab.irq[e->id]);
+ e->n_unacked--;
+ }
+ }
+ }
+
+ /* Now check for interrupts */
+ while (irqtab.tot_num_intr)
+ {
+ int del = 0;
+
+ queue_iterate (&main_intr_queue, e, user_intr_t *, chain)
+ {
+ /* if an entry doesn't have dest port,
+ * we should remove it. */
+ if (e->dst_port == MACH_PORT_NULL)
+ {
+ clear_wait (current_thread (), 0, 0);
+ del = 1;
+ break;
+ }
+
+ if (e->interrupts)
+ {
+ clear_wait (current_thread (), 0, 0);
+ id = e->id;
+ dst_port = e->dst_port;
+ e->interrupts--;
+ irqtab.tot_num_intr--;
+
+ splx (s);
+ deliver_intr (id, dst_port);
+ s = splhigh ();
+ }
+ }
+
+ /* remove the entry without dest port from the queue and free it. */
+ if (del)
+ {
+ assert (!queue_empty (&main_intr_queue));
+ queue_remove (&main_intr_queue, e, user_intr_t *, chain);
+ if (e->n_unacked)
+ printf("irq handler [%d]: still %d unacked irqs in entry %p\n", e->id, e->n_unacked, e);
+ while (e->n_unacked)
+ {
+ __enable_irq (irqtab.irq[e->id]);
+ e->n_unacked--;
+ }
+ printf("irq handler [%d]: removed entry %p\n", e->id, e);
+ splx (s);
+ kfree ((vm_offset_t) e, sizeof (*e));
+ s = splhigh ();
+ }
+ }
+ splx (s);
+ thread_block (NULL);
+ }
+}
+
+static boolean_t
+deliver_intr (int id, ipc_port_t dst_port)
+{
+ ipc_kmsg_t kmsg;
+ device_intr_notification_t *n;
+ mach_port_t dest = (mach_port_t) dst_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 = (device_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 = DEVICE_NOTIFY_MSGH_SEQNO;
+ m->msgh_local_port = MACH_PORT_NULL;
+ m->msgh_remote_port = MACH_PORT_NULL;
+ m->msgh_id = DEVICE_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->id = id;
+
+ ipc_port_copy_send (dst_port);
+ ipc_mqueue_send_always(kmsg);
+
+ return TRUE;
+}
+
+#endif /* MACH_XEN */
diff --git a/device/intr.h b/device/intr.h
new file mode 100644
index 00000000..cd3e0bce
--- /dev/null
+++ b/device/intr.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2010, 2011, 2019 Free Software Foundation, Inc.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * THE FREE SOFTWARE FOUNDATIONALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. THE FREE SOFTWARE FOUNDATION DISCLAIMS ANY LIABILITY OF ANY KIND
+ * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ */
+
+#ifndef __INTR_H__
+#define __INTR_H__
+
+#ifndef MACH_XEN
+
+#include <mach/kern_return.h>
+#include <mach/port.h>
+#include <kern/queue.h>
+#include <ipc/ipc_port.h>
+#include <device/conf.h>
+
+#define DEVICE_NOTIFY_MSGH_SEQNO 0
+
+#include <sys/types.h>
+
+struct irqdev;
+#include <machine/irq.h>
+
+typedef struct {
+ queue_chain_t chain;
+ int interrupts; /* Number of interrupts occurred since last run of intr_thread */
+ int n_unacked; /* Number of times irqs were disabled for this */
+ ipc_port_t dst_port; /* Notification port */
+ int id; /* Mapping to machine dependent irq_t array elem */
+} user_intr_t;
+
+struct irqdev {
+ char *name;
+ void (*irqdev_ack)(struct irqdev *dev, int id);
+
+ queue_head_t *intr_queue;
+ int tot_num_intr; /* Total number of unprocessed interrupts */
+
+ /* Machine dependent */
+ irq_t irq[NINTR];
+};
+
+extern queue_head_t main_intr_queue;
+extern int install_user_intr_handler (struct irqdev *dev, int id, unsigned long flags, user_intr_t *e);
+extern int deliver_user_intr (struct irqdev *dev, int id, user_intr_t *e);
+extern user_intr_t *insert_intr_entry (struct irqdev *dev, int id, ipc_port_t receive_port);
+
+void intr_thread (void);
+kern_return_t irq_acknowledge (ipc_port_t receive_port);
+
+#endif /* MACH_XEN */
+
+#endif
diff --git a/doc/mach.texi b/doc/mach.texi
index 91ec96ee..11b0a066 100644
--- a/doc/mach.texi
+++ b/doc/mach.texi
@@ -286,6 +286,7 @@ Device Interface
* Device Map:: Mapping devices into virtual memory.
* Device Status:: Querying and manipulating a device.
* Device Filter:: Filtering packets arriving on a device.
+* Device Interrupt:: Getting hardware interrupt notifications.
Kernel Debugger
@@ -6305,6 +6306,7 @@ All constants and functions in this chapter are defined in
* Device Map:: Mapping devices into virtual memory.
* Device Status:: Querying and manipulating a device.
* Device Filter:: Filtering packets arriving on a device.
+* Device Interrupt:: Getting hardware interrupt notifications.
@end menu
@@ -6699,6 +6701,26 @@ a device port or the device is dead or not completely open.
@end deftypefun
+@node Device Interrupt
+@section Device Interrupt
+
+@deftypefun kern_return_t device_intr_register (@w{device_t @var{device}}, @w{int @var{id}}, @w{int @var{flags}}, @w{mach_port_t @var{receive_port}})
+The function @code{device_intr_register} registers for receiving hardware
+interrupt events through @var{device_intr_notify} notifications. The hardware
+interrupt identifier is specified by @var{id}. @var{flags} must be set to 0. The
+notifications will be sent on the @var{receive_port} send right.
+@code{device_intr_register} is only available on the dedicated @code{irq} device.
+@end deftypefun
+
+@deftypefun kern_return_t device_intr_ack (@w{device_t @var{device}}, @w{mach_port_t @var{receive_port}})
+On a hardware interrupt, the kernel disables the interrupt line before sending
+notifications. To prevent from interrupt losses, the interrupt is kept disabled
+until @code{device_intr_ack} is called to acknowledge the interrupt.
+@var{receive_port} is the send right on which the interrupt notification was
+received.
+@end deftypefun
+
+
@node Kernel Debugger
@chapter Kernel Debugger
diff --git a/i386/Makefrag.am b/i386/Makefrag.am
index f38c0785..59571416 100644
--- a/i386/Makefrag.am
+++ b/i386/Makefrag.am
@@ -102,6 +102,8 @@ libkernel_a_SOURCES += \
i386/i386/io_perm.c \
i386/i386/io_perm.h \
i386/i386/ipl.h \
+ i386/i386/irq.c \
+ i386/i386/irq.h \
i386/i386/ktss.c \
i386/i386/ktss.h \
i386/i386/kttd_interface.c \
diff --git a/i386/i386/irq.c b/i386/i386/irq.c
new file mode 100644
index 00000000..c65d2ea2
--- /dev/null
+++ b/i386/i386/irq.c
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 1995 Shantanu Goel
+ * Copyright (C) 2020 Free Software Foundation, Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <i386/irq.h>
+#include <device/intr.h>
+#include <mach/kern_return.h>
+#include <kern/queue.h>
+#include <machine/machspl.h>
+
+extern queue_head_t main_intr_queue;
+
+static void
+irq_eoi (struct irqdev *dev, int id)
+{
+ /* TODO EOI(dev->irq[id]) */
+}
+
+static unsigned int ndisabled_irq[NINTR];
+
+void
+__disable_irq (irq_t irq_nr)
+{
+ assert (irq_nr < NINTR);
+
+ spl_t s = splhigh();
+ ndisabled_irq[irq_nr]++;
+ assert (ndisabled_irq[irq_nr] > 0);
+ if (ndisabled_irq[irq_nr] == 1)
+ mask_irq (irq_nr);
+ splx(s);
+}
+
+void
+__enable_irq (irq_t irq_nr)
+{
+ assert (irq_nr < NINTR);
+
+ spl_t s = splhigh();
+ assert (ndisabled_irq[irq_nr] > 0);
+ ndisabled_irq[irq_nr]--;
+ if (ndisabled_irq[irq_nr] == 0)
+ unmask_irq (irq_nr);
+ splx(s);
+}
+
+struct irqdev irqtab = {
+ "irq", irq_eoi, &main_intr_queue, 0,
+ {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
+};
+
diff --git a/i386/i386/irq.h b/i386/i386/irq.h
new file mode 100644
index 00000000..d48a8e92
--- /dev/null
+++ b/i386/i386/irq.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2020 Free Software Foundation, Inc.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * THE FREE SOFTWARE FOUNDATION ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. THE FREE SOFTWARE FOUNDATION DISCLAIMS ANY LIABILITY OF ANY KIND
+ * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ */
+
+#ifndef _I386_IRQ_H
+#define _I386_IRQ_H
+
+#include <i386/pic.h>
+
+typedef unsigned int irq_t;
+
+void __enable_irq (irq_t irq);
+void __disable_irq (irq_t irq);
+
+extern struct irqdev irqtab;
+
+#endif
diff --git a/i386/i386/pic.c b/i386/i386/pic.c
index 0feebc6f..62ed9ed1 100644
--- a/i386/i386/pic.c
+++ b/i386/i386/pic.c
@@ -49,6 +49,24 @@ OTHER TORTIOUS ACTION, ARISING OUR OF OR IN CONNECTION
WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+/*
+ * Copyright (C) 1995 Shantanu Goel.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
#include <sys/types.h>
#include <kern/printf.h>
#include <i386/ipl.h>
@@ -185,3 +203,57 @@ intnull(int unit_dev)
}
}
+
+/*
+ * Mask a PIC IRQ.
+ */
+inline void
+mask_irq (unsigned int irq_nr)
+{
+ int new_pic_mask = curr_pic_mask | 1 << irq_nr;
+
+ if (curr_pic_mask != new_pic_mask)
+ {
+ curr_pic_mask = new_pic_mask;
+ if (irq_nr < 8)
+ {
+ outb (PIC_MASTER_OCW, curr_pic_mask & 0xff);
+ }
+ else
+ {
+ outb (PIC_SLAVE_OCW, curr_pic_mask >> 8);
+ }
+ }
+}
+
+/*
+ * Unmask a PIC IRQ.
+ */
+inline void
+unmask_irq (unsigned int irq_nr)
+{
+ int mask;
+ int new_pic_mask;
+
+ mask = 1 << irq_nr;
+ if (irq_nr >= 8)
+ {
+ mask |= 1 << 2;
+ }
+
+ new_pic_mask = curr_pic_mask & ~mask;
+
+ if (curr_pic_mask != new_pic_mask)
+ {
+ curr_pic_mask = new_pic_mask;
+ if (irq_nr < 8)
+ {
+ outb (PIC_MASTER_OCW, curr_pic_mask & 0xff);
+ }
+ else
+ {
+ outb (PIC_SLAVE_OCW, curr_pic_mask >> 8);
+ }
+ }
+}
+
diff --git a/i386/i386/pic.h b/i386/i386/pic.h
index f492de5e..6434bf08 100644
--- a/i386/i386/pic.h
+++ b/i386/i386/pic.h
@@ -180,6 +180,8 @@ WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
extern void picinit (void);
extern int curr_pic_mask;
extern void intnull(int unit);
+extern inline void mask_irq (unsigned int irq_nr);
+extern inline void unmask_irq (unsigned int irq_nr);
#endif /* __ASSEMBLER__ */
#endif /* _I386_PIC_H_ */
diff --git a/i386/i386at/conf.c b/i386/i386at/conf.c
index fe7c7c09..ca5d0dfb 100644
--- a/i386/i386at/conf.c
+++ b/i386/i386at/conf.c
@@ -68,6 +68,9 @@
#define hypcnname "hyp"
#endif /* MACH_HYP */
+#include <device/intr.h>
+#define irqname "irq"
+
/*
* List of devices - console must be at slot 0
*/
@@ -149,6 +152,11 @@ struct dev_ops dev_name_list[] =
nodev },
#endif /* MACH_HYP */
+ { irqname, nulldev_open, nulldev_close, nulldev_read,
+ nulldev_write,nulldev_getstat,nulldev_setstat, nomap,
+ nodev, nulldev, nulldev_portdeath,0,
+ nodev },
+
};
int dev_name_count = sizeof(dev_name_list)/sizeof(dev_name_list[0]);
diff --git a/include/device/device.defs b/include/device/device.defs
index 409146f5..ec4b5bf8 100644
--- a/include/device/device.defs
+++ b/include/device/device.defs
@@ -142,3 +142,21 @@ routine device_set_filter(
in filter : filter_array_t
);
+routine device_intr_register(
+ device : device_t;
+ in id : int;
+ in flags : int;
+ in receive_port : mach_port_send_t
+ );
+
+/*
+ * Acknowledge the specified interrupt notification.
+ */
+/*
+ * When an IRQ happens and an intr notification is thus sent, the IRQ line
+ * is kept disabled until the notification is acknowledged with this RPC
+ */
+routine device_intr_ack(
+ device : device_t;
+ in receive_port : mach_port_send_t);
+
diff --git a/include/device/notify.defs b/include/device/notify.defs
new file mode 100644
index 00000000..7919b339
--- /dev/null
+++ b/include/device/notify.defs
@@ -0,0 +1,36 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+
+subsystem notify 100;
+
+#include <mach/std_types.defs>
+
+serverprefix do_;
+serverdemux device_intr_notify_server;
+
+simpleroutine device_intr_notify(
+ notify : notify_port_t;
+ id : int);
diff --git a/include/device/notify.h b/include/device/notify.h
new file mode 100644
index 00000000..addf9114
--- /dev/null
+++ b/include/device/notify.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2010 Free Software Foundation, Inc.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * THE FREE SOFTWARE FOUNDATIONALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. THE FREE SOFTWARE FOUNDATION DISCLAIMS ANY LIABILITY OF ANY KIND
+ * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ */
+
+/*
+ * Device notification definitions.
+ */
+
+#ifndef _MACH_DEVICE_NOTIFY_H_
+#define _MACH_DEVICE_NOTIFY_H_
+
+#include <mach/port.h>
+#include <mach/message.h>
+
+typedef struct
+{
+ mach_msg_header_t intr_header;
+ mach_msg_type_t intr_type;
+ int id;
+} device_intr_notification_t;
+
+#define DEVICE_INTR_NOTIFY 100
+
+#endif /* _MACH_DEVICE_NOTIFY_H_ */
diff --git a/kern/startup.c b/kern/startup.c
index 56e05b3f..1f873192 100644
--- a/kern/startup.c
+++ b/kern/startup.c
@@ -63,6 +63,7 @@
#include <machine/model_dep.h>
#include <mach/version.h>
#include <device/device_init.h>
+#include <device/intr.h>
#if MACH_KDB
#include <device/cons.h>
@@ -228,6 +229,9 @@ void start_kernel_threads(void)
(void) kernel_thread(kernel_task, reaper_thread, (char *) 0);
(void) kernel_thread(kernel_task, swapin_thread, (char *) 0);
(void) kernel_thread(kernel_task, sched_thread, (char *) 0);
+#ifndef MACH_XEN
+ (void) kernel_thread(kernel_task, intr_thread, (char *)0);
+#endif /* MACH_XEN */
#if NCPUS > 1
/*
diff --git a/linux/dev/arch/i386/kernel/irq.c b/linux/dev/arch/i386/kernel/irq.c
index 18448638..1e911f33 100644
--- a/linux/dev/arch/i386/kernel/irq.c
+++ b/linux/dev/arch/i386/kernel/irq.c
@@ -50,6 +50,8 @@
#include <linux/dev/glue/glue.h>
#include <machine/machspl.h>
+#include <device/intr.h>
+
#if 0
/* XXX: This is the way it's done in linux 2.2. GNU Mach currently uses intr_count. It should be made using local_{bh/irq}_count instead (through hardirq_enter/exit) for SMP support. */
unsigned int local_bh_count[NR_CPUS];
@@ -76,6 +78,7 @@ struct linux_action
void *dev_id;
struct linux_action *next;
unsigned long flags;
+ user_intr_t *user_intr;
};
static struct linux_action *irq_action[16] =
@@ -95,6 +98,7 @@ linux_intr (int irq)
{
struct pt_regs regs;
struct linux_action *action = *(irq_action + irq);
+ struct linux_action **prev = &irq_action[irq];
unsigned long flags;
kstat.interrupts[irq]++;
@@ -106,93 +110,35 @@ linux_intr (int irq)
while (action)
{
- action->handler (irq, action->dev_id, &regs);
+ // TODO I might need to check whether the interrupt belongs to
+ // the current device. But I don't do it for now.
+ if (action->user_intr)
+ {
+ if (!deliver_user_intr(&irqtab, irq, action->user_intr))
+ {
+ *prev = action->next;
+ linux_kfree(action);
+ action = *prev;
+ continue;
+ }
+ }
+ else if (action->handler)
+ action->handler (irq, action->dev_id, &regs);
+ prev = &action->next;
action = action->next;
}
- restore_flags (flags);
-
- intr_count--;
-}
-
-/*
- * Mask an IRQ.
- */
-static inline void
-mask_irq (unsigned int irq_nr)
-{
- int new_pic_mask = curr_pic_mask | 1 << irq_nr;
-
- if (curr_pic_mask != new_pic_mask)
- {
- curr_pic_mask = new_pic_mask;
- if (irq_nr < 8)
- outb (curr_pic_mask & 0xff, PIC_MASTER_OCW);
- else
- outb (curr_pic_mask >> 8, PIC_SLAVE_OCW);
- }
-}
-
-/*
- * Unmask an IRQ.
- */
-static inline void
-unmask_irq (unsigned int irq_nr)
-{
- int mask;
- int new_pic_mask;
-
- mask = 1 << irq_nr;
- if (irq_nr >= 8)
- mask |= 1 << 2;
-
- new_pic_mask = curr_pic_mask & ~mask;
-
- if (curr_pic_mask != new_pic_mask)
+ if (!irq_action[irq])
{
- curr_pic_mask = new_pic_mask;
- if (irq_nr < 8)
- outb (curr_pic_mask & 0xff, PIC_MASTER_OCW);
- else
- outb (curr_pic_mask >> 8, PIC_SLAVE_OCW);
+ /* No handler any more, disable interrupt */
+ mask_irq (irq);
+ ivect[irq] = intnull;
+ iunit[irq] = irq;
}
-}
-
-/* Count how many subsystems requested to disable each IRQ */
-static unsigned ndisabled_irq[NR_IRQS];
-
-/* These disable/enable IRQs for real after counting how many subsystems
- * requested that */
-void
-__disable_irq (unsigned int irq_nr)
-{
- unsigned long flags;
-
- assert (irq_nr < NR_IRQS);
- save_flags (flags);
- cli ();
- ndisabled_irq[irq_nr]++;
- assert (ndisabled_irq[irq_nr] > 0);
- if (ndisabled_irq[irq_nr] == 1)
- mask_irq (irq_nr);
restore_flags (flags);
-}
-void
-__enable_irq (unsigned int irq_nr)
-{
- unsigned long flags;
-
- assert (irq_nr < NR_IRQS);
-
- save_flags (flags);
- cli ();
- assert (ndisabled_irq[irq_nr] > 0);
- ndisabled_irq[irq_nr]--;
- if (ndisabled_irq[irq_nr] == 0)
- unmask_irq (irq_nr);
- restore_flags (flags);
+ intr_count--;
}
/* IRQ mask according to Linux drivers */
@@ -273,6 +219,53 @@ setup_x86_irq (int irq, struct linux_action *new)
return 0;
}
+int
+install_user_intr_handler (struct irqdev *dev, int id, unsigned long flags,
+ user_intr_t *user_intr)
+{
+ struct linux_action *action;
+ struct linux_action *old;
+ int retval;
+
+ unsigned int irq = dev->irq[id];
+
+ assert (irq < 16);
+
+ /* Test whether the irq handler has been set */
+ // TODO I need to protect the array when iterating it.
+ old = irq_action[irq];
+ while (old)
+ {
+ if (old->user_intr && old->user_intr->dst_port == user_intr->dst_port)
+ {
+ printk ("The interrupt handler has already been installed on line %d", irq);
+ return linux_to_mach_error (-EAGAIN);
+ }
+ old = old->next;
+ }
+
+ /*
+ * Hmm... Should I use `kalloc()' ?
+ * By OKUJI Yoshinori.
+ */
+ action = (struct linux_action *)
+ linux_kmalloc (sizeof (struct linux_action), GFP_KERNEL);
+ if (action == NULL)
+ return linux_to_mach_error (-ENOMEM);
+
+ action->handler = NULL;
+ action->next = NULL;
+ action->dev_id = NULL;
+ action->flags = SA_SHIRQ;
+ action->user_intr = user_intr;
+
+ retval = setup_x86_irq (irq, action);
+ if (retval)
+ linux_kfree (action);
+
+ return linux_to_mach_error (retval);
+}
+
/*
* Attach a handler to an IRQ.
*/
@@ -301,6 +294,7 @@ request_irq (unsigned int irq, void (*handler) (int, void *, struct pt_regs *),
action->next = NULL;
action->dev_id = dev_id;
action->flags = flags;
+ action->user_intr = NULL;
retval = setup_x86_irq (irq, action);
if (retval)
diff --git a/linux/src/include/asm-i386/irq.h b/linux/src/include/asm-i386/irq.h
index d7d1e3c5..c75744a5 100644
--- a/linux/src/include/asm-i386/irq.h
+++ b/linux/src/include/asm-i386/irq.h
@@ -16,8 +16,6 @@
#define TIMER_IRQ 0
-extern void __disable_irq(unsigned int);
-extern void __enable_irq(unsigned int);
extern void disable_irq(unsigned int);
extern void enable_irq(unsigned int);