diff options
author | Samuel Thibault <samuel.thibault@ens-lyon.org> | 2020-07-10 00:23:32 +0200 |
---|---|---|
committer | Samuel Thibault <samuel.thibault@ens-lyon.org> | 2020-07-10 00:31:38 +0200 |
commit | 6054cda4de2341b9a77ec4421411725f3684006b (patch) | |
tree | 02622e721dffe9fdb73e35805de2fbf8a2a5aea8 /linux | |
parent | 2dbf108457d0a0057cc63d5b3b89fd4da48d2a72 (diff) | |
download | gnumach-6054cda4de2341b9a77ec4421411725f3684006b.tar.gz gnumach-6054cda4de2341b9a77ec4421411725f3684006b.tar.bz2 gnumach-6054cda4de2341b9a77ec4421411725f3684006b.zip |
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.c | 152 | ||||
-rw-r--r-- | linux/src/include/asm-i386/irq.h | 2 |
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, ®s); + // 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, ®s); + 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); |