patch-1.3.70 linux/arch/i386/kernel/irq.c

Next file: linux/arch/i386/kernel/smp.c
Previous file: linux/arch/i386/boot/bootsect.S
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v1.3.69/linux/arch/i386/kernel/irq.c linux/arch/i386/kernel/irq.c
@@ -23,6 +23,7 @@
 #include <linux/ioport.h>
 #include <linux/interrupt.h>
 #include <linux/timex.h>
+#include <linux/malloc.h>
 #include <linux/random.h>
 
 #include <asm/system.h>
@@ -32,6 +33,7 @@
 #include <asm/smp.h>
 
 #define CR0_NE 32
+#define TIMER_IRQ 0                     /* Keep this in sync with time.c */
 
 static unsigned char cache_21 = 0xff;
 static unsigned char cache_A1 = 0xff;
@@ -158,36 +160,34 @@
 /*
  * Initial irq handlers.
  */
-struct irqaction {
-	void (*handler)(int, struct pt_regs *);
-	unsigned long flags;
-	unsigned long mask;
-	const char *name;
-};
-
-static struct irqaction irq_action[16] = {
-	{ NULL, 0, 0, NULL }, { NULL, 0, 0, NULL },
-	{ NULL, 0, 0, NULL }, { NULL, 0, 0, NULL },
-	{ NULL, 0, 0, NULL }, { NULL, 0, 0, NULL },
-	{ NULL, 0, 0, NULL }, { NULL, 0, 0, NULL },
-	{ NULL, 0, 0, NULL }, { NULL, 0, 0, NULL },
-	{ NULL, 0, 0, NULL }, { NULL, 0, 0, NULL },
-	{ NULL, 0, 0, NULL }, { NULL, 0, 0, NULL },
-	{ NULL, 0, 0, NULL }, { NULL, 0, 0, NULL }
+static struct irqaction timer_irq   = { NULL, 0, 0, NULL, NULL, NULL};
+static struct irqaction cascade_irq = { NULL, 0, 0, NULL, NULL, NULL};
+static struct irqaction math_irq    = { NULL, 0, 0, NULL, NULL, NULL};
+
+static struct irqaction *irq_action[16] = {
+	  NULL, NULL, NULL, NULL, NULL, NULL , NULL, NULL,
+	  NULL, NULL, NULL, NULL, NULL, NULL , NULL, NULL
 };
 
 int get_irq_list(char *buf)
 {
 	int i, len = 0;
-	struct irqaction * action = irq_action;
+	struct irqaction * action;
 
-	for (i = 0 ; i < 16 ; i++, action++) {
-		if (!action->handler)
-			continue;
-		len += sprintf(buf+len, "%3d: %8d %c %s\n",
+	for (i = 0 ; i < 16 ; i++) {
+	        action = *(i + irq_action);
+		if (!action) 
+		        continue;
+		len += sprintf(buf+len, "%2d: %8d %c %s",
 			i, kstat.interrupts[i],
 			(action->flags & SA_INTERRUPT) ? '+' : ' ',
 			action->name);
+		for (action=action->next; action; action = action->next) {
+			len += sprintf(buf+len, ",%s %s",
+				(action->flags & SA_INTERRUPT) ? " +" : "",
+				action->name);
+		}
+		len += sprintf(buf+len, "\n");
 	}
 /*
  *	Linus - should you add NMI counts here ?????
@@ -203,7 +203,7 @@
 
 int get_smp_prof_list(char *buf) {
         int i,j, len = 0;
-        struct irqaction * action = irq_action;
+        struct irqaction * action;
         unsigned long sum_spins = 0;
         unsigned long sum_spins_syscall = 0;
         unsigned long sum_spins_sys_idle = 0;
@@ -222,7 +222,8 @@
         for (i=0;i<smp_num_cpus;i++)
           len += sprintf(buf+len,"        P%1d ",i);
         len += sprintf(buf+len,"\n");
-        for (i = 0 ; i < NR_IRQS ; i++, action++) {
+        for (i = 0 ; i < NR_IRQS ; i++) {
+	        action = *(i + irq_action);
                 if (!action->handler)
                         continue;
                 len += sprintf(buf+len, "%3d: %10d ",
@@ -232,6 +233,11 @@
                 len += sprintf(buf+len, "%c %s\n",
                         (action->flags & SA_INTERRUPT) ? '+' : ' ',
                         action->name);
+		for (action=action->next; action; action = action->next) {
+			len += sprintf(buf+len, ",%s %s",
+				(action->flags & SA_INTERRUPT) ? " +" : "",
+				action->name);
+		}
         }
         len+=sprintf(buf+len, "LCK: %10lu",
                 sum_spins);
@@ -273,7 +279,8 @@
  */
 asmlinkage void do_IRQ(int irq, struct pt_regs * regs)
 {
-	struct irqaction * action = irq + irq_action;
+	struct irqaction * action = *(irq + irq_action);
+
 #ifdef __SMP__
 	if(smp_threads_ready && active_kernel_processor!=smp_processor_id())
 		panic("IRQ %d: active processor set wrongly(%d not %d).\n", irq, active_kernel_processor, smp_processor_id());
@@ -283,9 +290,13 @@
 #ifdef __SMP_PROF__
         int_count[smp_processor_id()][irq]++;
 #endif
-	if (action->flags & SA_SAMPLE_RANDOM)
-		add_interrupt_randomness(irq);
-	action->handler(irq, regs);
+	while (action) {
+	    if (action->flags & SA_SAMPLE_RANDOM) {
+	        add_interrupt_randomness(irq);
+            }
+	    action->handler(irq, action->dev_id, regs);
+	    action = action->next;
+	}
 }
 
 /*
@@ -295,7 +306,7 @@
  */
 asmlinkage void do_fast_IRQ(int irq)
 {
-	struct irqaction * action = irq + irq_action;
+	struct irqaction * action = *(irq + irq_action);
 #ifdef __SMP__
 	/* IRQ 13 is allowed - thats an invalidate */
 	if(smp_threads_ready && active_kernel_processor!=smp_processor_id() && irq!=13)
@@ -306,56 +317,95 @@
 #ifdef __SMP_PROF__
         int_count[smp_processor_id()][irq]++;
 #endif
-	if (action->flags & SA_SAMPLE_RANDOM)
+	while (action) {
+	    if (action->flags & SA_SAMPLE_RANDOM)
 		add_interrupt_randomness(irq);
-	action->handler(irq, NULL);
+	    action->handler(irq, action->dev_id, NULL);
+	    action = action->next;
+	}
 }
 
 #define SA_PROBE SA_ONESHOT
 
-int request_irq(unsigned int irq, void (*handler)(int, struct pt_regs *),
-	unsigned long irqflags, const char * devname)
+int request_irq(unsigned int irq, 
+		void (*handler)(int, void *, struct pt_regs *),
+		unsigned long irqflags, 
+		const char * devname,
+		void *dev_id)
 {
-	struct irqaction * action;
+	struct irqaction * action, *tmp = NULL;
 	unsigned long flags;
 
 	if (irq > 15)
-		return -EINVAL;
-	action = irq + irq_action;
-	if (action->handler)
-		return -EBUSY;
+	    return -EINVAL;
 	if (!handler)
-		return -EINVAL;
+	    return -EINVAL;
+	action = *(irq + irq_action);
+	if (action) {
+	    if ((action->flags & SA_SHIRQ) && (irqflags & SA_SHIRQ)) {
+		for (tmp = action; tmp->next; tmp = tmp->next);
+	    } else {
+		return -EBUSY;
+	    }
+	    if ((action->flags & SA_INTERRUPT) ^ (irqflags & SA_INTERRUPT)) {
+	      printk("Attempt to mix fast and slow interrupts on IRQ%d denied\n", irq);
+	      return -EBUSY;
+	    }   
+	}
 	if (irqflags & SA_SAMPLE_RANDOM)
 		rand_initialize_irq(irq);
 	save_flags(flags);
 	cli();
+	if (irq == 2)
+	    action = &cascade_irq;
+	else if (irq == 13)
+	  action = &math_irq;
+	else if (irq == TIMER_IRQ)
+	  action = &timer_irq;
+	else
+	  action = (struct irqaction *)kmalloc(sizeof(struct irqaction), GFP_KERNEL);
+
+	if (!action) { 
+	    restore_flags(flags);
+	    return -ENOMEM;
+	}
+
 	action->handler = handler;
 	action->flags = irqflags;
 	action->mask = 0;
 	action->name = devname;
-	if (!(action->flags & SA_PROBE)) { /* SA_ONESHOT is used by probing */
+	action->next = NULL;
+	action->dev_id = dev_id;
+
+	if (tmp) {
+	    tmp->next = action;
+	} else {
+	    *(irq + irq_action) = action;
+	    if (!(action->flags & SA_PROBE)) {/* SA_ONESHOT used by probing */
 		if (action->flags & SA_INTERRUPT)
-			set_intr_gate(0x20+irq,fast_interrupt[irq]);
+		  set_intr_gate(0x20+irq,fast_interrupt[irq]);
 		else
-			set_intr_gate(0x20+irq,interrupt[irq]);
-	}
-	if (irq < 8) {
+		  set_intr_gate(0x20+irq,interrupt[irq]);
+	    }
+	    if (irq < 8) {
 		cache_21 &= ~(1<<irq);
 		outb(cache_21,0x21);
-	} else {
+	    } else {
 		cache_21 &= ~(1<<2);
 		cache_A1 &= ~(1<<(irq-8));
 		outb(cache_21,0x21);
 		outb(cache_A1,0xA1);
+	    }
 	}
+
 	restore_flags(flags);
 	return 0;
 }
 		
-void free_irq(unsigned int irq)
+void free_irq(unsigned int irq, void *dev_id)
 {
-	struct irqaction * action = irq + irq_action;
+	struct irqaction * action = *(irq + irq_action);
+	struct irqaction * tmp = NULL;
 	unsigned long flags;
 
 	if (irq > 15) {
@@ -366,20 +416,43 @@
 		printk("Trying to free free IRQ%d\n",irq);
 		return;
 	}
+	if (dev_id) {
+	    for (; action; action = action->next) {
+	        if (action->dev_id == dev_id) break;
+		tmp = action;
+	    }
+	    if (!action) {
+		printk("Trying to free free shared IRQ%d\n",irq);
+		return;
+	    }
+	} else if (action->flags & SA_SHIRQ) {
+	    printk("Trying to free shared IRQ%d with NULL device ID\n", irq);
+	    return;
+	}
 	save_flags(flags);
 	cli();
-	if (irq < 8) {
+	if (action && tmp) {
+	    tmp->next = action->next;
+	} else {
+	    *(irq + irq_action) = action->next;
+	}
+
+	if ((irq == 2) || (irq == 13) | (irq == TIMER_IRQ))
+	  memset(action, 0, sizeof(struct irqaction));
+	else 
+	  kfree_s(action, sizeof(struct irqaction));
+	
+	if (!(*(irq + irq_action))) {
+	    if (irq < 8) {
 		cache_21 |= 1 << irq;
 		outb(cache_21,0x21);
-	} else {
+	    } else {
 		cache_A1 |= 1 << (irq-8);
 		outb(cache_A1,0xA1);
+	    }
+	    set_intr_gate(0x20+irq,bad_interrupt[irq]);
 	}
-	set_intr_gate(0x20+irq,bad_interrupt[irq]);
-	action->handler = NULL;
-	action->flags = 0;
-	action->mask = 0;
-	action->name = NULL;
+
 	restore_flags(flags);
 }
 
@@ -398,7 +471,7 @@
  */
  
 
-static void math_error_irq(int cpl, struct pt_regs *regs)
+static void math_error_irq(int cpl, void *dev_id, struct pt_regs *regs)
 {
 	outb(0,0xF0);
 	if (ignore_irq13 || !hard_math)
@@ -408,7 +481,7 @@
 
 #endif
 
-static void no_action(int cpl, struct pt_regs * regs) { }
+static void no_action(int cpl, void *dev_id, struct pt_regs *regs) { }
 
 unsigned long probe_irq_on (void)
 {
@@ -417,7 +490,7 @@
 
 	/* first, snaffle up any unassigned irqs */
 	for (i = 15; i > 0; i--) {
-		if (!request_irq(i, no_action, SA_PROBE, "probe")) {
+		if (!request_irq(i, no_action, SA_PROBE, "probe", NULL)) {
 			enable_irq(i);
 			irqs |= (1 << i);
 		}
@@ -431,7 +504,7 @@
 	for (i = 15; i > 0; i--) {
 		if (irqs & (1 << i) & irqmask) {
 			irqs ^= (1 << i);
-			free_irq(i);
+			free_irq(i, NULL);
 		}
 	}
 #ifdef DEBUG
@@ -447,7 +520,7 @@
 	irqmask = (((unsigned int)cache_A1)<<8) | (unsigned int)cache_21;
 	for (i = 15; i > 0; i--) {
 		if (irqs & (1 << i)) {
-			free_irq(i);
+			free_irq(i, NULL);
 		}
 	}
 #ifdef DEBUG
@@ -481,13 +554,13 @@
 #ifdef __SMP__	
 	set_intr_gate(0x20+i, interrupt[i]);	/* IRQ '16' - IPI for rescheduling */
 #endif	
-	if (request_irq(2, no_action, SA_INTERRUPT, "cascade"))
+	if (request_irq(2, no_action, SA_INTERRUPT, "cascade", NULL))
 		printk("Unable to get IRQ2 for cascade.\n");
 #ifndef __SMP__		
-	if (request_irq(13,math_error_irq, 0, "math error"))
+	if (request_irq(13, math_error_irq, 0, "math error", NULL))
 		printk("Unable to get IRQ13 for math-error handler.\n");
 #else
-	if (request_irq(13, smp_message_irq, SA_INTERRUPT, "IPI"))
+	if (request_irq(13, smp_message_irq, SA_INTERRUPT, "IPI", NULL))
 		printk("Unable to get IRQ13 for IPI.\n");
 #endif				
 	request_region(0x20,0x20,"pic1");

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov with Sam's (original) version
of this