summaryrefslogtreecommitdiff
path: root/linux
diff options
context:
space:
mode:
authorSamuel Thibault <samuel.thibault@ens-lyon.org>2020-07-10 00:23:32 +0200
committerSamuel Thibault <samuel.thibault@ens-lyon.org>2020-07-10 00:31:38 +0200
commit6054cda4de2341b9a77ec4421411725f3684006b (patch)
tree02622e721dffe9fdb73e35805de2fbf8a2a5aea8 /linux
parent2dbf108457d0a0057cc63d5b3b89fd4da48d2a72 (diff)
Add hardware interrupt notification mechanism
This allows privileged userland drivers to get notifications of hardware interrupts. Initial work by Zheng Da, reworked by Damien Zammit and myself. * Makefrag.am (libkernel_a_SOURCES): Add device/intr.c and device/intr.h. (include_device_HEADERS): Add include/device/notify.defs and include/device/notify.h. * device/dev_hdr.h (name_equal): Add declaration. * device/ds_routines.c: Include <device/intr.h> (ds_device_intr_register, ds_device_intr_ack): New functions. * device/intr.c, device/intr.h: New files. * doc/mach.texi (Device Interrupt): New section. * i386/Makefrag.am (libkernel_a_SOURCES): Add i386/i386/irq.c and i386/i386/irq.h. * i386/i386/irq.c, i386/i386/irq.h: New files. * i386/i386at/conf.c: Include <device/intr.h>. (irqname): New macro. (dev_name_list): Add irq device. * include/device/device.defs (device_intr_register, device_intr_ack): New RPCs. * include/device/notify.defs, include/device/notify.h: New files. * kern/startup.c: Include <device/intr.h> (start_kernel_threads): Start intr_thread thread. * linux/dev/arch/i386/kernel/irq.c: Include <device/intr.h> (linux_action): Add user_intr field. (linux_intr): Call user_intr action if any. (mask_irq, unmask_irq): Move functions to i386/i386/pic.c (__disable_irq, __enable_irq): Move functions to i386/i386/irq.c. (install_user_intr_handler): New function. (request_irq): Initialize user_intr field. * linux/src/include/asm-i386/irq.h (__disable_irq, __enable_irq): Remove prototypes. * i386/i386/pic.c (mask_irq, unmask_irq): New functions. * i386/i386/pic.h (mask_irq, unmask_irq): New prototypes.
Diffstat (limited to 'linux')
-rw-r--r--linux/dev/arch/i386/kernel/irq.c152
-rw-r--r--linux/src/include/asm-i386/irq.h2
2 files changed, 73 insertions, 81 deletions
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);