upgrade to fedora-2.6.12-1.1398.FC4 + vserver 2.0.rc7
[linux-2.6.git] / kernel / irq / manage.c
index 5202e4c..47678cd 100644 (file)
@@ -146,9 +146,14 @@ int can_request_irq(unsigned int irq, unsigned long irqflags)
        return !action;
 }
 
-/*
- * Internal function to register an irqaction - typically used to
- * allocate special interrupts that are part of the architecture.
+/**
+ *     setup_irq - register an irqaction structure
+ *     @irq: Interrupt to register
+ *     @irqaction: The irqaction structure to be registered
+ *
+ *     Normally called by request_irq, this function can be used
+ *     directly to allocate special interrupts that are part of the
+ *     architecture.
  */
 int setup_irq(unsigned int irq, struct irqaction * new)
 {
@@ -217,28 +222,27 @@ int setup_irq(unsigned int irq, struct irqaction * new)
        return 0;
 }
 
-/**
- *     free_irq - free an interrupt
- *     @irq: Interrupt line to free
- *     @dev_id: Device identity to free
+/*
+ *     teardown_irq - unregister an irqaction
+ *     @irq: Interrupt line being freed
+ *     @old: Pointer to the irqaction that is to be unregistered
  *
- *     Remove an interrupt handler. The handler is removed and if the
- *     interrupt line is no longer in use by any driver it is disabled.
- *     On a shared IRQ the caller must ensure the interrupt is disabled
- *     on the card it drives before calling this function. The function
- *     does not return until any executing interrupts for this IRQ
- *     have completed.
+ *     This function is called by free_irq and does the actual
+ *     business of unregistering the handler. It exists as a 
+ *     seperate function to enable handlers to be unregistered 
+ *     for irqactions that have been allocated statically at 
+ *     boot time.
  *
  *     This function must not be called from interrupt context.
  */
-void free_irq(unsigned int irq, void *dev_id)
+int teardown_irq(unsigned int irq, struct irqaction * old)
 {
        struct irq_desc *desc;
        struct irqaction **p;
        unsigned long flags;
 
        if (irq >= NR_IRQS)
-               return;
+               return -ENOENT;
 
        desc = irq_desc + irq;
        spin_lock_irqsave(&desc->lock,flags);
@@ -250,7 +254,7 @@ void free_irq(unsigned int irq, void *dev_id)
                        struct irqaction **pp = p;
 
                        p = &action->next;
-                       if (action->dev_id != dev_id)
+                       if (action != old)
                                continue;
 
                        /* Found it - now remove it from the list of entries */
@@ -267,13 +271,52 @@ void free_irq(unsigned int irq, void *dev_id)
 
                        /* Make sure it's not being used on another CPU */
                        synchronize_irq(irq);
-                       kfree(action);
-                       return;
+                       return 0;
                }
-               printk(KERN_ERR "Trying to free free IRQ%d\n",irq);
+               printk(KERN_ERR "Trying to teardown free IRQ%d\n",irq);
+               spin_unlock_irqrestore(&desc->lock,flags);
+               return -ENOENT;
+       }
+}
+
+/**
+ *     free_irq - free an interrupt
+ *     @irq: Interrupt line to free
+ *     @dev_id: Device identity to free
+ *
+ *     Remove an interrupt handler. The handler is removed and if the
+ *     interrupt line is no longer in use by any driver it is disabled.
+ *     On a shared IRQ the caller must ensure the interrupt is disabled
+ *     on the card it drives before calling this function. The function
+ *     does not return until any executing interrupts for this IRQ
+ *     have completed.
+ *
+ *     This function must not be called from interrupt context.
+ */
+void free_irq(unsigned int irq, void *dev_id)
+{
+       struct irq_desc *desc;
+       struct irqaction *action;
+       unsigned long flags;
+
+       if (irq >= NR_IRQS)
+               return;
+
+       desc = irq_desc + irq;
+       spin_lock_irqsave(&desc->lock,flags);
+       for (action = desc->action; action != NULL; action = action->next) {
+               if (action->dev_id != dev_id)
+                       continue;
+
                spin_unlock_irqrestore(&desc->lock,flags);
+
+               if (teardown_irq(irq, action) == 0)
+                       kfree(action);
                return;
        }
+       printk(KERN_ERR "Trying to free free IRQ%d\n",irq);
+       spin_unlock_irqrestore(&desc->lock,flags);
+       return;
 }
 
 EXPORT_SYMBOL(free_irq);