📄 irq.c
字号:
/* * Linux IRQ management. * 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. *//* * linux/arch/i386/kernel/irq.c * * Copyright (C) 1992 Linus Torvalds */#include <sys/types.h>#include <mach/mach_types.h>#include <mach/vm_param.h>#include <kern/assert.h>#include <i386/spl.h>#include <i386/pic.h>#include <i386/pit.h>#define MACH_INCLUDE#include <linux/mm.h>#include <linux/interrupt.h>#include <linux/ptrace.h>#include <linux/delay.h>#include <linux/kernel_stat.h>#include <linux/malloc.h>#include <linux/ioport.h>#include <asm/system.h>#include <asm/bitops.h>#include <asm/irq.h>#include <asm/io.h>extern int linux_timer_intr (void);extern spl_t splhigh (void);extern spl_t spl0 (void);extern void form_pic_mask (void);/* * XXX Move this into more suitable place... * Set if the machine has an EISA bus. */int EISA_bus = 0;/* * Priority at which a Linux handler should be called. * This is used at the time of an IRQ allocation. It is * set by emulation routines for each class of device. */spl_t linux_intr_pri;/* * Flag indicating an interrupt is being handled. */unsigned long intr_count = 0;/* * List of Linux interrupt handlers. */struct linux_action{ void (*handler) (int, void *, struct pt_regs *); void *dev_id; struct linux_action *next; unsigned long flags;};static struct linux_action *irq_action[16] ={ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};extern spl_t curr_ipl;extern int curr_pic_mask;extern int pic_mask[];extern int intnull (), prtnull ();/* * Generic interrupt handler for Linux devices. * Set up a fake `struct pt_regs' then call the real handler. */static intlinux_intr (int irq){ struct pt_regs regs; struct linux_action *action = *(irq_action + irq); kstat.interrupts[irq]++; intr_count++; while (action) { action->handler (irq, action->dev_id, ®s); action = action->next; } intr_count--; /* Not used. by OKUJI Yoshinori. */ return 0;}/* * Mask an IRQ. */static inline voidmask_irq (unsigned int irq_nr){ int i; for (i = 0; i < intpri[irq_nr]; i++) pic_mask[i] |= 1 << irq_nr; if (curr_pic_mask != pic_mask[curr_ipl]) { curr_pic_mask = pic_mask[curr_ipl]; 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 voidunmask_irq (unsigned int irq_nr){ int mask, i; mask = 1 << irq_nr; if (irq_nr >= 8) mask |= 1 << 2; for (i = 0; i < intpri[irq_nr]; i++) pic_mask[i] &= ~mask; if (curr_pic_mask != pic_mask[curr_ipl]) { curr_pic_mask = pic_mask[curr_ipl]; if (irq_nr < 8) outb (curr_pic_mask & 0xff, PIC_MASTER_OCW); else outb (curr_pic_mask >> 8, PIC_SLAVE_OCW); }}voiddisable_irq (unsigned int irq_nr){ unsigned long flags; assert (irq_nr < NR_IRQS); save_flags (flags); cli (); mask_irq (irq_nr); restore_flags (flags);}voidenable_irq (unsigned int irq_nr){ unsigned long flags; assert (irq_nr < NR_IRQS); save_flags (flags); cli (); unmask_irq (irq_nr); restore_flags (flags);}/* * Default interrupt handler for Linux. */intlinux_bad_intr (int irq){ mask_irq (irq); return 0;}static intsetup_x86_irq (int irq, struct linux_action *new){ int shared = 0; struct linux_action *old, **p; unsigned long flags; p = irq_action + irq; if ((old = *p) != NULL) { /* Can't share interrupts unless both agree to */ if (!(old->flags & new->flags & SA_SHIRQ)) return (-LINUX_EBUSY); /* Can't share interrupts unless both are same type */ if ((old->flags ^ new->flags) & SA_INTERRUPT) return (-LINUX_EBUSY); /* add new interrupt at end of irq queue */ do { p = &old->next; old = *p; } while (old); shared = 1; } save_flags (flags); cli (); *p = new; if (!shared) { ivect[irq] = linux_intr; iunit[irq] = irq; intpri[irq] = linux_intr_pri; unmask_irq (irq); } restore_flags (flags); return 0;}/* * Attach a handler to an IRQ. */intrequest_irq (unsigned int irq, void (*handler) (int, void *, struct pt_regs *), unsigned long flags, const char *device, void *dev_id){ struct linux_action *action; int retval; assert (irq < 16); if (!handler) return -LINUX_EINVAL; /* * 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_ENOMEM; action->handler = handler; action->next = NULL; action->dev_id = dev_id; action->flags = flags; retval = setup_x86_irq (irq, action); if (retval) linux_kfree (action); return retval;}/* * Deallocate an irq. */voidfree_irq (unsigned int irq, void *dev_id){ struct linux_action *action, **p; unsigned long flags; if (irq > 15) panic ("free_irq: bad irq number"); for (p = irq_action + irq; (action = *p) != NULL; p = &action->next) { if (action->dev_id != dev_id) continue; save_flags (flags); cli (); *p = action->next; if (!irq_action[irq]) { mask_irq (irq); ivect[irq] = linux_bad_intr; iunit[irq] = irq; intpri[irq] = SPL0; } restore_flags (flags); linux_kfree (action); return; } panic ("free_irq: bad irq number");}/* * Set for an irq probe. */unsigned longprobe_irq_on (void){ unsigned i, irqs = 0; unsigned long delay; assert (curr_ipl == 0); /* * Allocate all available IRQs. */ for (i = 15; i > 0; i--) { if (!irq_action[i] && ivect[i] == linux_bad_intr) { intpri[i] = linux_intr_pri; enable_irq (i); irqs |= 1 << i; } } /* * Wait for spurious interrupts to mask themselves out. */ for (delay = jiffies + HZ / 10; delay > jiffies;) ; return (irqs & ~curr_pic_mask);}/* * Return the result of an irq probe. */intprobe_irq_off (unsigned long irqs){ unsigned int i; assert (curr_ipl == 0); irqs &= curr_pic_mask; /* * Disable unnecessary IRQs. */ for (i = 15; i > 0; i--) { if (!irq_action[i] && ivect[i] == linux_bad_intr) { disable_irq (i); intpri[i] = SPL0; } } /* * Return IRQ number. */ if (!irqs) return 0; i = ffz (~irqs); if (irqs != (irqs & (1 << i))) i = -i; return i;}/* * Reserve IRQs used by Mach drivers. * Must be called before Linux IRQ detection, after Mach IRQ detection. */static voidreserve_mach_irqs (void){ unsigned int i; for (i = 0; i < 16; i++) { if (ivect[i] != prtnull && ivect[i] != intnull) /* Set non-NULL value. */ irq_action[i] = (struct linux_action *) -1; }}static int (*old_clock_handler) ();static int old_clock_pri;voidinit_IRQ (void){ int i; char *p; int latch = (CLKNUM + hz / 2) / hz; /* * Ensure interrupts are disabled. */ (void) splhigh (); /* * Program counter 0 of 8253 to interrupt hz times per second. */ outb_p (PIT_C0 | PIT_SQUAREMODE | PIT_READMODE, PITCTL_PORT); outb_p (latch && 0xff, PITCTR0_PORT); outb (latch >> 8, PITCTR0_PORT); /* * Install our clock interrupt handler. */ old_clock_handler = ivect[0]; old_clock_pri = intpri[0]; ivect[0] = linux_timer_intr; intpri[0] = SPLHI; reserve_mach_irqs (); for (i = 1; i < 16; i++) { /* * irq2 and irq13 should be igonored. */ if (i == 2 || i == 13) continue; if (ivect[i] == prtnull || ivect[i] == intnull) { ivect[i] = linux_bad_intr; iunit[i] = i; intpri[i] = SPL0; } } form_pic_mask (); /* * Enable interrupts. */ (void) spl0 (); /* * Check if the machine has an EISA bus. */ p = (char *) 0x0FFFD9; if (*p++ == 'E' && *p++ == 'I' && *p++ == 'S' && *p == 'A') EISA_bus = 1; /* * Permanently allocate standard device ports. */ request_region (0x00, 0x20, "dma1"); request_region (0x20, 0x20, "pic1"); request_region (0x40, 0x20, "timer"); request_region (0x70, 0x10, "rtc"); request_region (0x80, 0x20, "dma page reg"); request_region (0xa0, 0x20, "pic2"); request_region (0xc0, 0x20, "dma2"); request_region (0xf0, 0x10, "npu");}voidrestore_IRQ (void){ /* * Disable interrupts. */ (void) splhigh (); /* * Restore clock interrupt handler. */ ivect[0] = old_clock_handler; intpri[0] = old_clock_pri; form_pic_mask ();}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -