diff options
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); |