2005-04-16 16:20:36 -06:00
|
|
|
/*
|
|
|
|
* linux/kernel/irq/manage.c
|
|
|
|
*
|
2006-06-29 03:24:50 -06:00
|
|
|
* Copyright (C) 1992, 1998-2006 Linus Torvalds, Ingo Molnar
|
|
|
|
* Copyright (C) 2005-2006 Thomas Gleixner
|
2005-04-16 16:20:36 -06:00
|
|
|
*
|
|
|
|
* This file contains driver APIs to the irq subsystem.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/irq.h>
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/random.h>
|
|
|
|
#include <linux/interrupt.h>
|
|
|
|
|
|
|
|
#include "internals.h"
|
|
|
|
|
|
|
|
#ifdef CONFIG_SMP
|
|
|
|
|
|
|
|
/**
|
|
|
|
* synchronize_irq - wait for pending IRQ handlers (on other CPUs)
|
2005-11-07 02:01:06 -07:00
|
|
|
* @irq: interrupt number to wait for
|
2005-04-16 16:20:36 -06:00
|
|
|
*
|
|
|
|
* This function waits for any pending IRQ handlers for this interrupt
|
|
|
|
* to complete before returning. If you use this function while
|
|
|
|
* holding a resource the IRQ handler may need you will deadlock.
|
|
|
|
*
|
|
|
|
* This function may be called - with care - from IRQ context.
|
|
|
|
*/
|
|
|
|
void synchronize_irq(unsigned int irq)
|
|
|
|
{
|
|
|
|
struct irq_desc *desc = irq_desc + irq;
|
|
|
|
|
2005-11-03 07:51:18 -07:00
|
|
|
if (irq >= NR_IRQS)
|
|
|
|
return;
|
|
|
|
|
2005-04-16 16:20:36 -06:00
|
|
|
while (desc->status & IRQ_INPROGRESS)
|
|
|
|
cpu_relax();
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(synchronize_irq);
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/**
|
|
|
|
* disable_irq_nosync - disable an irq without waiting
|
|
|
|
* @irq: Interrupt to disable
|
|
|
|
*
|
|
|
|
* Disable the selected interrupt line. Disables and Enables are
|
|
|
|
* nested.
|
|
|
|
* Unlike disable_irq(), this function does not ensure existing
|
|
|
|
* instances of the IRQ handler have completed before returning.
|
|
|
|
*
|
|
|
|
* This function may be called from IRQ context.
|
|
|
|
*/
|
|
|
|
void disable_irq_nosync(unsigned int irq)
|
|
|
|
{
|
2006-06-29 03:24:40 -06:00
|
|
|
struct irq_desc *desc = irq_desc + irq;
|
2005-04-16 16:20:36 -06:00
|
|
|
unsigned long flags;
|
|
|
|
|
2005-11-03 07:51:18 -07:00
|
|
|
if (irq >= NR_IRQS)
|
|
|
|
return;
|
|
|
|
|
2005-04-16 16:20:36 -06:00
|
|
|
spin_lock_irqsave(&desc->lock, flags);
|
|
|
|
if (!desc->depth++) {
|
|
|
|
desc->status |= IRQ_DISABLED;
|
[PATCH] genirq: rename desc->handler to desc->chip
This patch-queue improves the generic IRQ layer to be truly generic, by adding
various abstractions and features to it, without impacting existing
functionality.
While the queue can be best described as "fix and improve everything in the
generic IRQ layer that we could think of", and thus it consists of many
smaller features and lots of cleanups, the one feature that stands out most is
the new 'irq chip' abstraction.
The irq-chip abstraction is about describing and coding and IRQ controller
driver by mapping its raw hardware capabilities [and quirks, if needed] in a
straightforward way, without having to think about "IRQ flow"
(level/edge/etc.) type of details.
This stands in contrast with the current 'irq-type' model of genirq
architectures, which 'mixes' raw hardware capabilities with 'flow' details.
The patchset supports both types of irq controller designs at once, and
converts i386 and x86_64 to the new irq-chip design.
As a bonus side-effect of the irq-chip approach, chained interrupt controllers
(master/slave PIC constructs, etc.) are now supported by design as well.
The end result of this patchset intends to be simpler architecture-level code
and more consolidation between architectures.
We reused many bits of code and many concepts from Russell King's ARM IRQ
layer, the merging of which was one of the motivations for this patchset.
This patch:
rename desc->handler to desc->chip.
Originally i did not want to do this, because it's a big patch. But having
both "desc->handler", "desc->handle_irq" and "action->handler" caused a
large degree of confusion and made the code appear alot less clean than it
truly is.
I have also attempted a dual approach as well by introducing a
desc->chip alias - but that just wasnt robust enough and broke
frequently.
So lets get over with this quickly. The conversion was done automatically
via scripts and converts all the code in the kernel.
This renaming patch is the first one amongst the patches, so that the
remaining patches can stay flexible and can be merged and split up
without having some big monolithic patch act as a merge barrier.
[akpm@osdl.org: build fix]
[akpm@osdl.org: another build fix]
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-06-29 03:24:36 -06:00
|
|
|
desc->chip->disable(irq);
|
2005-04-16 16:20:36 -06:00
|
|
|
}
|
|
|
|
spin_unlock_irqrestore(&desc->lock, flags);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(disable_irq_nosync);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* disable_irq - disable an irq and wait for completion
|
|
|
|
* @irq: Interrupt to disable
|
|
|
|
*
|
|
|
|
* Disable the selected interrupt line. Enables and Disables are
|
|
|
|
* nested.
|
|
|
|
* This function waits for any pending IRQ handlers for this interrupt
|
|
|
|
* to complete before returning. If you use this function while
|
|
|
|
* holding a resource the IRQ handler may need you will deadlock.
|
|
|
|
*
|
|
|
|
* This function may be called - with care - from IRQ context.
|
|
|
|
*/
|
|
|
|
void disable_irq(unsigned int irq)
|
|
|
|
{
|
2006-06-29 03:24:40 -06:00
|
|
|
struct irq_desc *desc = irq_desc + irq;
|
2005-04-16 16:20:36 -06:00
|
|
|
|
2005-11-03 07:51:18 -07:00
|
|
|
if (irq >= NR_IRQS)
|
|
|
|
return;
|
|
|
|
|
2005-04-16 16:20:36 -06:00
|
|
|
disable_irq_nosync(irq);
|
|
|
|
if (desc->action)
|
|
|
|
synchronize_irq(irq);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(disable_irq);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* enable_irq - enable handling of an irq
|
|
|
|
* @irq: Interrupt to enable
|
|
|
|
*
|
|
|
|
* Undoes the effect of one call to disable_irq(). If this
|
|
|
|
* matches the last disable, processing of interrupts on this
|
|
|
|
* IRQ line is re-enabled.
|
|
|
|
*
|
|
|
|
* This function may be called from IRQ context.
|
|
|
|
*/
|
|
|
|
void enable_irq(unsigned int irq)
|
|
|
|
{
|
2006-06-29 03:24:40 -06:00
|
|
|
struct irq_desc *desc = irq_desc + irq;
|
2005-04-16 16:20:36 -06:00
|
|
|
unsigned long flags;
|
|
|
|
|
2005-11-03 07:51:18 -07:00
|
|
|
if (irq >= NR_IRQS)
|
|
|
|
return;
|
|
|
|
|
2005-04-16 16:20:36 -06:00
|
|
|
spin_lock_irqsave(&desc->lock, flags);
|
|
|
|
switch (desc->depth) {
|
|
|
|
case 0:
|
2006-07-01 05:35:45 -06:00
|
|
|
printk(KERN_WARNING "Unbalanced enable for IRQ %d\n", irq);
|
2005-04-16 16:20:36 -06:00
|
|
|
WARN_ON(1);
|
|
|
|
break;
|
|
|
|
case 1: {
|
2006-06-29 03:24:49 -06:00
|
|
|
unsigned int status = desc->status & ~IRQ_DISABLED;
|
|
|
|
|
|
|
|
/* Prevent probing on this irq: */
|
|
|
|
desc->status = status | IRQ_NOPROBE;
|
2006-06-29 03:24:48 -06:00
|
|
|
check_irq_resend(desc, irq);
|
2005-04-16 16:20:36 -06:00
|
|
|
/* fall-through */
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
desc->depth--;
|
|
|
|
}
|
|
|
|
spin_unlock_irqrestore(&desc->lock, flags);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(enable_irq);
|
|
|
|
|
2006-06-29 03:24:55 -06:00
|
|
|
/**
|
|
|
|
* set_irq_wake - control irq power management wakeup
|
|
|
|
* @irq: interrupt to control
|
|
|
|
* @on: enable/disable power management wakeup
|
|
|
|
*
|
2006-07-30 04:03:08 -06:00
|
|
|
* Enable/disable power management wakeup mode, which is
|
|
|
|
* disabled by default. Enables and disables must match,
|
|
|
|
* just as they match for non-wakeup mode support.
|
|
|
|
*
|
|
|
|
* Wakeup mode lets this IRQ wake the system from sleep
|
|
|
|
* states like "suspend to RAM".
|
2006-06-29 03:24:55 -06:00
|
|
|
*/
|
|
|
|
int set_irq_wake(unsigned int irq, unsigned int on)
|
|
|
|
{
|
|
|
|
struct irq_desc *desc = irq_desc + irq;
|
|
|
|
unsigned long flags;
|
|
|
|
int ret = -ENXIO;
|
2006-07-30 04:03:08 -06:00
|
|
|
int (*set_wake)(unsigned, unsigned) = desc->chip->set_wake;
|
2006-06-29 03:24:55 -06:00
|
|
|
|
2006-07-30 04:03:08 -06:00
|
|
|
/* wakeup-capable irqs can be shared between drivers that
|
|
|
|
* don't need to have the same sleep mode behaviors.
|
|
|
|
*/
|
2006-06-29 03:24:55 -06:00
|
|
|
spin_lock_irqsave(&desc->lock, flags);
|
2006-07-30 04:03:08 -06:00
|
|
|
if (on) {
|
|
|
|
if (desc->wake_depth++ == 0)
|
|
|
|
desc->status |= IRQ_WAKEUP;
|
|
|
|
else
|
|
|
|
set_wake = NULL;
|
|
|
|
} else {
|
|
|
|
if (desc->wake_depth == 0) {
|
|
|
|
printk(KERN_WARNING "Unbalanced IRQ %d "
|
|
|
|
"wake disable\n", irq);
|
|
|
|
WARN_ON(1);
|
|
|
|
} else if (--desc->wake_depth == 0)
|
|
|
|
desc->status &= ~IRQ_WAKEUP;
|
|
|
|
else
|
|
|
|
set_wake = NULL;
|
|
|
|
}
|
|
|
|
if (set_wake)
|
2006-06-29 03:24:55 -06:00
|
|
|
ret = desc->chip->set_wake(irq, on);
|
|
|
|
spin_unlock_irqrestore(&desc->lock, flags);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(set_irq_wake);
|
|
|
|
|
2005-04-16 16:20:36 -06:00
|
|
|
/*
|
|
|
|
* Internal function that tells the architecture code whether a
|
|
|
|
* particular irq has been exclusively allocated or is available
|
|
|
|
* for driver use.
|
|
|
|
*/
|
|
|
|
int can_request_irq(unsigned int irq, unsigned long irqflags)
|
|
|
|
{
|
|
|
|
struct irqaction *action;
|
|
|
|
|
2006-06-29 03:24:49 -06:00
|
|
|
if (irq >= NR_IRQS || irq_desc[irq].status & IRQ_NOREQUEST)
|
2005-04-16 16:20:36 -06:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
action = irq_desc[irq].action;
|
|
|
|
if (action)
|
2006-07-01 20:29:31 -06:00
|
|
|
if (irqflags & action->flags & IRQF_SHARED)
|
2005-04-16 16:20:36 -06:00
|
|
|
action = NULL;
|
|
|
|
|
|
|
|
return !action;
|
|
|
|
}
|
|
|
|
|
2006-06-29 03:24:51 -06:00
|
|
|
void compat_irq_chip_set_default_handler(struct irq_desc *desc)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* If the architecture still has not overriden
|
|
|
|
* the flow handler then zap the default. This
|
|
|
|
* should catch incorrect flow-type setting.
|
|
|
|
*/
|
|
|
|
if (desc->handle_irq == &handle_bad_irq)
|
|
|
|
desc->handle_irq = NULL;
|
|
|
|
}
|
|
|
|
|
2005-04-16 16:20:36 -06:00
|
|
|
/*
|
|
|
|
* Internal function to register an irqaction - typically used to
|
|
|
|
* allocate special interrupts that are part of the architecture.
|
|
|
|
*/
|
2006-06-29 03:24:40 -06:00
|
|
|
int setup_irq(unsigned int irq, struct irqaction *new)
|
2005-04-16 16:20:36 -06:00
|
|
|
{
|
|
|
|
struct irq_desc *desc = irq_desc + irq;
|
|
|
|
struct irqaction *old, **p;
|
2006-11-14 03:03:23 -07:00
|
|
|
const char *old_name = NULL;
|
2005-04-16 16:20:36 -06:00
|
|
|
unsigned long flags;
|
|
|
|
int shared = 0;
|
|
|
|
|
2005-11-03 07:51:18 -07:00
|
|
|
if (irq >= NR_IRQS)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2006-06-29 03:24:57 -06:00
|
|
|
if (desc->chip == &no_irq_chip)
|
2005-04-16 16:20:36 -06:00
|
|
|
return -ENOSYS;
|
|
|
|
/*
|
|
|
|
* Some drivers like serial.c use request_irq() heavily,
|
|
|
|
* so we have to be careful not to interfere with a
|
|
|
|
* running system.
|
|
|
|
*/
|
2006-07-01 20:29:31 -06:00
|
|
|
if (new->flags & IRQF_SAMPLE_RANDOM) {
|
2005-04-16 16:20:36 -06:00
|
|
|
/*
|
|
|
|
* This function might sleep, we want to call it first,
|
|
|
|
* outside of the atomic block.
|
|
|
|
* Yes, this might clear the entropy pool if the wrong
|
|
|
|
* driver is attempted to be loaded, without actually
|
|
|
|
* installing a new handler, but is this really a problem,
|
|
|
|
* only the sysadmin is able to do this.
|
|
|
|
*/
|
|
|
|
rand_initialize_irq(irq);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The following block of code has to be executed atomically
|
|
|
|
*/
|
2006-06-29 03:24:40 -06:00
|
|
|
spin_lock_irqsave(&desc->lock, flags);
|
2005-04-16 16:20:36 -06:00
|
|
|
p = &desc->action;
|
2006-06-29 03:24:40 -06:00
|
|
|
old = *p;
|
|
|
|
if (old) {
|
2006-06-29 03:24:56 -06:00
|
|
|
/*
|
|
|
|
* Can't share interrupts unless both agree to and are
|
|
|
|
* the same type (level, edge, polarity). So both flag
|
2006-07-01 20:29:31 -06:00
|
|
|
* fields must have IRQF_SHARED set and the bits which
|
2006-06-29 03:24:56 -06:00
|
|
|
* set the trigger type must match.
|
|
|
|
*/
|
2006-07-01 20:29:31 -06:00
|
|
|
if (!((old->flags & new->flags) & IRQF_SHARED) ||
|
2006-11-14 03:03:23 -07:00
|
|
|
((old->flags ^ new->flags) & IRQF_TRIGGER_MASK)) {
|
|
|
|
old_name = old->name;
|
2006-03-25 04:08:23 -07:00
|
|
|
goto mismatch;
|
2006-11-14 03:03:23 -07:00
|
|
|
}
|
2006-03-25 04:08:23 -07:00
|
|
|
|
2006-07-02 18:20:32 -06:00
|
|
|
#if defined(CONFIG_IRQ_PER_CPU)
|
2006-03-25 04:08:23 -07:00
|
|
|
/* All handlers must agree on per-cpuness */
|
2006-07-01 20:29:31 -06:00
|
|
|
if ((old->flags & IRQF_PERCPU) !=
|
|
|
|
(new->flags & IRQF_PERCPU))
|
2006-03-25 04:08:23 -07:00
|
|
|
goto mismatch;
|
|
|
|
#endif
|
2005-04-16 16:20:36 -06:00
|
|
|
|
|
|
|
/* add new interrupt at end of irq queue */
|
|
|
|
do {
|
|
|
|
p = &old->next;
|
|
|
|
old = *p;
|
|
|
|
} while (old);
|
|
|
|
shared = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
*p = new;
|
2006-07-02 18:20:32 -06:00
|
|
|
#if defined(CONFIG_IRQ_PER_CPU)
|
2006-07-01 20:29:31 -06:00
|
|
|
if (new->flags & IRQF_PERCPU)
|
2006-03-25 04:08:23 -07:00
|
|
|
desc->status |= IRQ_PER_CPU;
|
|
|
|
#endif
|
2005-04-16 16:20:36 -06:00
|
|
|
if (!shared) {
|
2006-06-29 03:24:51 -06:00
|
|
|
irq_chip_set_defaults(desc->chip);
|
2006-06-29 03:24:56 -06:00
|
|
|
|
|
|
|
/* Setup the type (level, edge polarity) if configured: */
|
2006-07-01 20:29:31 -06:00
|
|
|
if (new->flags & IRQF_TRIGGER_MASK) {
|
2006-06-29 03:24:56 -06:00
|
|
|
if (desc->chip && desc->chip->set_type)
|
|
|
|
desc->chip->set_type(irq,
|
2006-07-01 20:29:31 -06:00
|
|
|
new->flags & IRQF_TRIGGER_MASK);
|
2006-06-29 03:24:56 -06:00
|
|
|
else
|
|
|
|
/*
|
2006-07-01 20:29:31 -06:00
|
|
|
* IRQF_TRIGGER_* but the PIC does not support
|
2006-06-29 03:24:56 -06:00
|
|
|
* multiple flow-types?
|
|
|
|
*/
|
2006-07-01 20:29:31 -06:00
|
|
|
printk(KERN_WARNING "No IRQF_TRIGGER set_type "
|
2006-07-01 05:35:45 -06:00
|
|
|
"function for IRQ %d (%s)\n", irq,
|
|
|
|
desc->chip ? desc->chip->name :
|
|
|
|
"unknown");
|
2006-06-29 03:24:56 -06:00
|
|
|
} else
|
|
|
|
compat_irq_chip_set_default_handler(desc);
|
2006-06-29 03:24:51 -06:00
|
|
|
|
2006-06-29 03:24:50 -06:00
|
|
|
desc->status &= ~(IRQ_AUTODETECT | IRQ_WAITING |
|
|
|
|
IRQ_INPROGRESS);
|
|
|
|
|
|
|
|
if (!(desc->status & IRQ_NOAUTOEN)) {
|
|
|
|
desc->depth = 0;
|
|
|
|
desc->status &= ~IRQ_DISABLED;
|
|
|
|
if (desc->chip->startup)
|
|
|
|
desc->chip->startup(irq);
|
|
|
|
else
|
|
|
|
desc->chip->enable(irq);
|
2006-06-29 03:24:56 -06:00
|
|
|
} else
|
|
|
|
/* Undo nested disables: */
|
|
|
|
desc->depth = 1;
|
2005-04-16 16:20:36 -06:00
|
|
|
}
|
2007-01-23 15:16:31 -07:00
|
|
|
/* Reset broken irq detection when installing new handler */
|
|
|
|
desc->irq_count = 0;
|
|
|
|
desc->irqs_unhandled = 0;
|
2006-06-29 03:24:40 -06:00
|
|
|
spin_unlock_irqrestore(&desc->lock, flags);
|
2005-04-16 16:20:36 -06:00
|
|
|
|
|
|
|
new->irq = irq;
|
|
|
|
register_irq_proc(irq);
|
|
|
|
new->dir = NULL;
|
|
|
|
register_handler_proc(irq, new);
|
|
|
|
|
|
|
|
return 0;
|
2006-03-25 04:08:23 -07:00
|
|
|
|
|
|
|
mismatch:
|
2007-02-12 01:52:04 -07:00
|
|
|
#ifdef CONFIG_DEBUG_SHIRQ
|
2006-07-01 20:29:31 -06:00
|
|
|
if (!(new->flags & IRQF_PROBE_SHARED)) {
|
2006-07-01 05:35:45 -06:00
|
|
|
printk(KERN_ERR "IRQ handler type mismatch for IRQ %d\n", irq);
|
2006-11-14 03:03:23 -07:00
|
|
|
if (old_name)
|
|
|
|
printk(KERN_ERR "current handler: %s\n", old_name);
|
2006-04-27 19:39:18 -06:00
|
|
|
dump_stack();
|
|
|
|
}
|
2007-02-12 01:52:04 -07:00
|
|
|
#endif
|
2006-11-14 03:03:23 -07:00
|
|
|
spin_unlock_irqrestore(&desc->lock, flags);
|
2006-03-25 04:08:23 -07:00
|
|
|
return -EBUSY;
|
2005-04-16 16:20:36 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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 **p;
|
|
|
|
unsigned long flags;
|
2007-02-12 01:52:00 -07:00
|
|
|
irqreturn_t (*handler)(int, void *) = NULL;
|
2005-04-16 16:20:36 -06:00
|
|
|
|
2006-03-26 02:36:54 -07:00
|
|
|
WARN_ON(in_interrupt());
|
2005-04-16 16:20:36 -06:00
|
|
|
if (irq >= NR_IRQS)
|
|
|
|
return;
|
|
|
|
|
|
|
|
desc = irq_desc + irq;
|
2006-06-29 03:24:40 -06:00
|
|
|
spin_lock_irqsave(&desc->lock, flags);
|
2005-04-16 16:20:36 -06:00
|
|
|
p = &desc->action;
|
|
|
|
for (;;) {
|
2006-06-29 03:24:40 -06:00
|
|
|
struct irqaction *action = *p;
|
2005-04-16 16:20:36 -06:00
|
|
|
|
|
|
|
if (action) {
|
|
|
|
struct irqaction **pp = p;
|
|
|
|
|
|
|
|
p = &action->next;
|
|
|
|
if (action->dev_id != dev_id)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* Found it - now remove it from the list of entries */
|
|
|
|
*pp = action->next;
|
[PATCH] uml: add and use generic hw_controller_type->release
With Chris Wedgwood <cw@f00f.org>
Currently UML must explicitly call the UML-specific
free_irq_by_irq_and_dev() for each free_irq call it's done.
This is needed because ->shutdown and/or ->disable are only called when the
last "action" for that irq is removed.
Instead, for UML shared IRQs (UML IRQs are very often, if not always,
shared), for each dev_id some setup is done, which must be cleared on the
release of that fd. For instance, for each open console a new instance
(i.e. new dev_id) of the same IRQ is requested().
Exactly, a fd is stored in an array (pollfds), which is after read by a
host thread and passed to poll(). Each event registered by poll() triggers
an interrupt. So, for each free_irq() we must remove the corresponding
host fd from the table, which we do via this -release() method.
In this patch we add an appropriate hook for this, and remove all uses of
it by pointing the hook to the said procedure; this is safe to do since the
said procedure.
Also some cosmetic improvements are included.
This is heavily based on some work by Chris Wedgwood, which however didn't
get the patch merged for something I'd call a "misunderstanding" (the need
for this patch wasn't cleanly explained, thus adding the generic hook was
felt as undesirable).
Signed-off-by: Paolo 'Blaisorblade' Giarrusso <blaisorblade@yahoo.it>
CC: Ingo Molnar <mingo@redhat.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-06-21 18:16:19 -06:00
|
|
|
|
2005-06-21 18:16:24 -06:00
|
|
|
/* Currently used only by UML, might disappear one day.*/
|
|
|
|
#ifdef CONFIG_IRQ_RELEASE_METHOD
|
[PATCH] genirq: rename desc->handler to desc->chip
This patch-queue improves the generic IRQ layer to be truly generic, by adding
various abstractions and features to it, without impacting existing
functionality.
While the queue can be best described as "fix and improve everything in the
generic IRQ layer that we could think of", and thus it consists of many
smaller features and lots of cleanups, the one feature that stands out most is
the new 'irq chip' abstraction.
The irq-chip abstraction is about describing and coding and IRQ controller
driver by mapping its raw hardware capabilities [and quirks, if needed] in a
straightforward way, without having to think about "IRQ flow"
(level/edge/etc.) type of details.
This stands in contrast with the current 'irq-type' model of genirq
architectures, which 'mixes' raw hardware capabilities with 'flow' details.
The patchset supports both types of irq controller designs at once, and
converts i386 and x86_64 to the new irq-chip design.
As a bonus side-effect of the irq-chip approach, chained interrupt controllers
(master/slave PIC constructs, etc.) are now supported by design as well.
The end result of this patchset intends to be simpler architecture-level code
and more consolidation between architectures.
We reused many bits of code and many concepts from Russell King's ARM IRQ
layer, the merging of which was one of the motivations for this patchset.
This patch:
rename desc->handler to desc->chip.
Originally i did not want to do this, because it's a big patch. But having
both "desc->handler", "desc->handle_irq" and "action->handler" caused a
large degree of confusion and made the code appear alot less clean than it
truly is.
I have also attempted a dual approach as well by introducing a
desc->chip alias - but that just wasnt robust enough and broke
frequently.
So lets get over with this quickly. The conversion was done automatically
via scripts and converts all the code in the kernel.
This renaming patch is the first one amongst the patches, so that the
remaining patches can stay flexible and can be merged and split up
without having some big monolithic patch act as a merge barrier.
[akpm@osdl.org: build fix]
[akpm@osdl.org: another build fix]
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-06-29 03:24:36 -06:00
|
|
|
if (desc->chip->release)
|
|
|
|
desc->chip->release(irq, dev_id);
|
2005-06-21 18:16:24 -06:00
|
|
|
#endif
|
[PATCH] uml: add and use generic hw_controller_type->release
With Chris Wedgwood <cw@f00f.org>
Currently UML must explicitly call the UML-specific
free_irq_by_irq_and_dev() for each free_irq call it's done.
This is needed because ->shutdown and/or ->disable are only called when the
last "action" for that irq is removed.
Instead, for UML shared IRQs (UML IRQs are very often, if not always,
shared), for each dev_id some setup is done, which must be cleared on the
release of that fd. For instance, for each open console a new instance
(i.e. new dev_id) of the same IRQ is requested().
Exactly, a fd is stored in an array (pollfds), which is after read by a
host thread and passed to poll(). Each event registered by poll() triggers
an interrupt. So, for each free_irq() we must remove the corresponding
host fd from the table, which we do via this -release() method.
In this patch we add an appropriate hook for this, and remove all uses of
it by pointing the hook to the said procedure; this is safe to do since the
said procedure.
Also some cosmetic improvements are included.
This is heavily based on some work by Chris Wedgwood, which however didn't
get the patch merged for something I'd call a "misunderstanding" (the need
for this patch wasn't cleanly explained, thus adding the generic hook was
felt as undesirable).
Signed-off-by: Paolo 'Blaisorblade' Giarrusso <blaisorblade@yahoo.it>
CC: Ingo Molnar <mingo@redhat.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-06-21 18:16:19 -06:00
|
|
|
|
2005-04-16 16:20:36 -06:00
|
|
|
if (!desc->action) {
|
|
|
|
desc->status |= IRQ_DISABLED;
|
[PATCH] genirq: rename desc->handler to desc->chip
This patch-queue improves the generic IRQ layer to be truly generic, by adding
various abstractions and features to it, without impacting existing
functionality.
While the queue can be best described as "fix and improve everything in the
generic IRQ layer that we could think of", and thus it consists of many
smaller features and lots of cleanups, the one feature that stands out most is
the new 'irq chip' abstraction.
The irq-chip abstraction is about describing and coding and IRQ controller
driver by mapping its raw hardware capabilities [and quirks, if needed] in a
straightforward way, without having to think about "IRQ flow"
(level/edge/etc.) type of details.
This stands in contrast with the current 'irq-type' model of genirq
architectures, which 'mixes' raw hardware capabilities with 'flow' details.
The patchset supports both types of irq controller designs at once, and
converts i386 and x86_64 to the new irq-chip design.
As a bonus side-effect of the irq-chip approach, chained interrupt controllers
(master/slave PIC constructs, etc.) are now supported by design as well.
The end result of this patchset intends to be simpler architecture-level code
and more consolidation between architectures.
We reused many bits of code and many concepts from Russell King's ARM IRQ
layer, the merging of which was one of the motivations for this patchset.
This patch:
rename desc->handler to desc->chip.
Originally i did not want to do this, because it's a big patch. But having
both "desc->handler", "desc->handle_irq" and "action->handler" caused a
large degree of confusion and made the code appear alot less clean than it
truly is.
I have also attempted a dual approach as well by introducing a
desc->chip alias - but that just wasnt robust enough and broke
frequently.
So lets get over with this quickly. The conversion was done automatically
via scripts and converts all the code in the kernel.
This renaming patch is the first one amongst the patches, so that the
remaining patches can stay flexible and can be merged and split up
without having some big monolithic patch act as a merge barrier.
[akpm@osdl.org: build fix]
[akpm@osdl.org: another build fix]
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-06-29 03:24:36 -06:00
|
|
|
if (desc->chip->shutdown)
|
|
|
|
desc->chip->shutdown(irq);
|
2005-04-16 16:20:36 -06:00
|
|
|
else
|
[PATCH] genirq: rename desc->handler to desc->chip
This patch-queue improves the generic IRQ layer to be truly generic, by adding
various abstractions and features to it, without impacting existing
functionality.
While the queue can be best described as "fix and improve everything in the
generic IRQ layer that we could think of", and thus it consists of many
smaller features and lots of cleanups, the one feature that stands out most is
the new 'irq chip' abstraction.
The irq-chip abstraction is about describing and coding and IRQ controller
driver by mapping its raw hardware capabilities [and quirks, if needed] in a
straightforward way, without having to think about "IRQ flow"
(level/edge/etc.) type of details.
This stands in contrast with the current 'irq-type' model of genirq
architectures, which 'mixes' raw hardware capabilities with 'flow' details.
The patchset supports both types of irq controller designs at once, and
converts i386 and x86_64 to the new irq-chip design.
As a bonus side-effect of the irq-chip approach, chained interrupt controllers
(master/slave PIC constructs, etc.) are now supported by design as well.
The end result of this patchset intends to be simpler architecture-level code
and more consolidation between architectures.
We reused many bits of code and many concepts from Russell King's ARM IRQ
layer, the merging of which was one of the motivations for this patchset.
This patch:
rename desc->handler to desc->chip.
Originally i did not want to do this, because it's a big patch. But having
both "desc->handler", "desc->handle_irq" and "action->handler" caused a
large degree of confusion and made the code appear alot less clean than it
truly is.
I have also attempted a dual approach as well by introducing a
desc->chip alias - but that just wasnt robust enough and broke
frequently.
So lets get over with this quickly. The conversion was done automatically
via scripts and converts all the code in the kernel.
This renaming patch is the first one amongst the patches, so that the
remaining patches can stay flexible and can be merged and split up
without having some big monolithic patch act as a merge barrier.
[akpm@osdl.org: build fix]
[akpm@osdl.org: another build fix]
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-06-29 03:24:36 -06:00
|
|
|
desc->chip->disable(irq);
|
2005-04-16 16:20:36 -06:00
|
|
|
}
|
2006-06-29 03:24:40 -06:00
|
|
|
spin_unlock_irqrestore(&desc->lock, flags);
|
2005-04-16 16:20:36 -06:00
|
|
|
unregister_handler_proc(irq, action);
|
|
|
|
|
|
|
|
/* Make sure it's not being used on another CPU */
|
|
|
|
synchronize_irq(irq);
|
2007-02-12 01:52:00 -07:00
|
|
|
if (action->flags & IRQF_SHARED)
|
|
|
|
handler = action->handler;
|
2005-04-16 16:20:36 -06:00
|
|
|
kfree(action);
|
|
|
|
return;
|
|
|
|
}
|
2006-07-01 05:35:45 -06:00
|
|
|
printk(KERN_ERR "Trying to free already-free IRQ %d\n", irq);
|
2006-06-29 03:24:40 -06:00
|
|
|
spin_unlock_irqrestore(&desc->lock, flags);
|
2005-04-16 16:20:36 -06:00
|
|
|
return;
|
|
|
|
}
|
2007-02-12 01:52:00 -07:00
|
|
|
#ifdef CONFIG_DEBUG_SHIRQ
|
|
|
|
if (handler) {
|
|
|
|
/*
|
|
|
|
* It's a shared IRQ -- the driver ought to be prepared for it
|
|
|
|
* to happen even now it's being freed, so let's make sure....
|
|
|
|
* We do this after actually deregistering it, to make sure that
|
|
|
|
* a 'real' IRQ doesn't run in parallel with our fake
|
|
|
|
*/
|
|
|
|
handler(irq, dev_id);
|
|
|
|
}
|
|
|
|
#endif
|
2005-04-16 16:20:36 -06:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(free_irq);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* request_irq - allocate an interrupt line
|
|
|
|
* @irq: Interrupt line to allocate
|
|
|
|
* @handler: Function to be called when the IRQ occurs
|
|
|
|
* @irqflags: Interrupt type flags
|
|
|
|
* @devname: An ascii name for the claiming device
|
|
|
|
* @dev_id: A cookie passed back to the handler function
|
|
|
|
*
|
|
|
|
* This call allocates interrupt resources and enables the
|
|
|
|
* interrupt line and IRQ handling. From the point this
|
|
|
|
* call is made your handler function may be invoked. Since
|
|
|
|
* your handler function must clear any interrupt the board
|
|
|
|
* raises, you must take care both to initialise your hardware
|
|
|
|
* and to set up the interrupt handler in the right order.
|
|
|
|
*
|
|
|
|
* Dev_id must be globally unique. Normally the address of the
|
|
|
|
* device data structure is used as the cookie. Since the handler
|
|
|
|
* receives this value it makes sense to use it.
|
|
|
|
*
|
|
|
|
* If your interrupt is shared you must pass a non NULL dev_id
|
|
|
|
* as this is required when freeing the interrupt.
|
|
|
|
*
|
|
|
|
* Flags:
|
|
|
|
*
|
2006-07-01 20:29:31 -06:00
|
|
|
* IRQF_SHARED Interrupt is shared
|
|
|
|
* IRQF_DISABLED Disable local interrupts while processing
|
|
|
|
* IRQF_SAMPLE_RANDOM The interrupt can be used for entropy
|
2005-04-16 16:20:36 -06:00
|
|
|
*
|
|
|
|
*/
|
2006-10-05 06:06:34 -06:00
|
|
|
int request_irq(unsigned int irq, irq_handler_t handler,
|
2006-06-29 03:24:40 -06:00
|
|
|
unsigned long irqflags, const char *devname, void *dev_id)
|
2005-04-16 16:20:36 -06:00
|
|
|
{
|
2006-06-29 03:24:40 -06:00
|
|
|
struct irqaction *action;
|
2005-04-16 16:20:36 -06:00
|
|
|
int retval;
|
|
|
|
|
[PATCH] lockdep: core
Do 'make oldconfig' and accept all the defaults for new config options -
reboot into the kernel and if everything goes well it should boot up fine and
you should have /proc/lockdep and /proc/lockdep_stats files.
Typically if the lock validator finds some problem it will print out
voluminous debug output that begins with "BUG: ..." and which syslog output
can be used by kernel developers to figure out the precise locking scenario.
What does the lock validator do? It "observes" and maps all locking rules as
they occur dynamically (as triggered by the kernel's natural use of spinlocks,
rwlocks, mutexes and rwsems). Whenever the lock validator subsystem detects a
new locking scenario, it validates this new rule against the existing set of
rules. If this new rule is consistent with the existing set of rules then the
new rule is added transparently and the kernel continues as normal. If the
new rule could create a deadlock scenario then this condition is printed out.
When determining validity of locking, all possible "deadlock scenarios" are
considered: assuming arbitrary number of CPUs, arbitrary irq context and task
context constellations, running arbitrary combinations of all the existing
locking scenarios. In a typical system this means millions of separate
scenarios. This is why we call it a "locking correctness" validator - for all
rules that are observed the lock validator proves it with mathematical
certainty that a deadlock could not occur (assuming that the lock validator
implementation itself is correct and its internal data structures are not
corrupted by some other kernel subsystem). [see more details and conditionals
of this statement in include/linux/lockdep.h and
Documentation/lockdep-design.txt]
Furthermore, this "all possible scenarios" property of the validator also
enables the finding of complex, highly unlikely multi-CPU multi-context races
via single single-context rules, increasing the likelyhood of finding bugs
drastically. In practical terms: the lock validator already found a bug in
the upstream kernel that could only occur on systems with 3 or more CPUs, and
which needed 3 very unlikely code sequences to occur at once on the 3 CPUs.
That bug was found and reported on a single-CPU system (!). So in essence a
race will be found "piecemail-wise", triggering all the necessary components
for the race, without having to reproduce the race scenario itself! In its
short existence the lock validator found and reported many bugs before they
actually caused a real deadlock.
To further increase the efficiency of the validator, the mapping is not per
"lock instance", but per "lock-class". For example, all struct inode objects
in the kernel have inode->inotify_mutex. If there are 10,000 inodes cached,
then there are 10,000 lock objects. But ->inotify_mutex is a single "lock
type", and all locking activities that occur against ->inotify_mutex are
"unified" into this single lock-class. The advantage of the lock-class
approach is that all historical ->inotify_mutex uses are mapped into a single
(and as narrow as possible) set of locking rules - regardless of how many
different tasks or inode structures it took to build this set of rules. The
set of rules persist during the lifetime of the kernel.
To see the rough magnitude of checking that the lock validator does, here's a
portion of /proc/lockdep_stats, fresh after bootup:
lock-classes: 694 [max: 2048]
direct dependencies: 1598 [max: 8192]
indirect dependencies: 17896
all direct dependencies: 16206
dependency chains: 1910 [max: 8192]
in-hardirq chains: 17
in-softirq chains: 105
in-process chains: 1065
stack-trace entries: 38761 [max: 131072]
combined max dependencies: 2033928
hardirq-safe locks: 24
hardirq-unsafe locks: 176
softirq-safe locks: 53
softirq-unsafe locks: 137
irq-safe locks: 59
irq-unsafe locks: 176
The lock validator has observed 1598 actual single-thread locking patterns,
and has validated all possible 2033928 distinct locking scenarios.
More details about the design of the lock validator can be found in
Documentation/lockdep-design.txt, which can also found at:
http://redhat.com/~mingo/lockdep-patches/lockdep-design.txt
[bunk@stusta.de: cleanups]
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Arjan van de Ven <arjan@linux.intel.com>
Signed-off-by: Adrian Bunk <bunk@stusta.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-07-03 01:24:50 -06:00
|
|
|
#ifdef CONFIG_LOCKDEP
|
|
|
|
/*
|
|
|
|
* Lockdep wants atomic interrupt handlers:
|
|
|
|
*/
|
2007-02-14 01:33:16 -07:00
|
|
|
irqflags |= IRQF_DISABLED;
|
[PATCH] lockdep: core
Do 'make oldconfig' and accept all the defaults for new config options -
reboot into the kernel and if everything goes well it should boot up fine and
you should have /proc/lockdep and /proc/lockdep_stats files.
Typically if the lock validator finds some problem it will print out
voluminous debug output that begins with "BUG: ..." and which syslog output
can be used by kernel developers to figure out the precise locking scenario.
What does the lock validator do? It "observes" and maps all locking rules as
they occur dynamically (as triggered by the kernel's natural use of spinlocks,
rwlocks, mutexes and rwsems). Whenever the lock validator subsystem detects a
new locking scenario, it validates this new rule against the existing set of
rules. If this new rule is consistent with the existing set of rules then the
new rule is added transparently and the kernel continues as normal. If the
new rule could create a deadlock scenario then this condition is printed out.
When determining validity of locking, all possible "deadlock scenarios" are
considered: assuming arbitrary number of CPUs, arbitrary irq context and task
context constellations, running arbitrary combinations of all the existing
locking scenarios. In a typical system this means millions of separate
scenarios. This is why we call it a "locking correctness" validator - for all
rules that are observed the lock validator proves it with mathematical
certainty that a deadlock could not occur (assuming that the lock validator
implementation itself is correct and its internal data structures are not
corrupted by some other kernel subsystem). [see more details and conditionals
of this statement in include/linux/lockdep.h and
Documentation/lockdep-design.txt]
Furthermore, this "all possible scenarios" property of the validator also
enables the finding of complex, highly unlikely multi-CPU multi-context races
via single single-context rules, increasing the likelyhood of finding bugs
drastically. In practical terms: the lock validator already found a bug in
the upstream kernel that could only occur on systems with 3 or more CPUs, and
which needed 3 very unlikely code sequences to occur at once on the 3 CPUs.
That bug was found and reported on a single-CPU system (!). So in essence a
race will be found "piecemail-wise", triggering all the necessary components
for the race, without having to reproduce the race scenario itself! In its
short existence the lock validator found and reported many bugs before they
actually caused a real deadlock.
To further increase the efficiency of the validator, the mapping is not per
"lock instance", but per "lock-class". For example, all struct inode objects
in the kernel have inode->inotify_mutex. If there are 10,000 inodes cached,
then there are 10,000 lock objects. But ->inotify_mutex is a single "lock
type", and all locking activities that occur against ->inotify_mutex are
"unified" into this single lock-class. The advantage of the lock-class
approach is that all historical ->inotify_mutex uses are mapped into a single
(and as narrow as possible) set of locking rules - regardless of how many
different tasks or inode structures it took to build this set of rules. The
set of rules persist during the lifetime of the kernel.
To see the rough magnitude of checking that the lock validator does, here's a
portion of /proc/lockdep_stats, fresh after bootup:
lock-classes: 694 [max: 2048]
direct dependencies: 1598 [max: 8192]
indirect dependencies: 17896
all direct dependencies: 16206
dependency chains: 1910 [max: 8192]
in-hardirq chains: 17
in-softirq chains: 105
in-process chains: 1065
stack-trace entries: 38761 [max: 131072]
combined max dependencies: 2033928
hardirq-safe locks: 24
hardirq-unsafe locks: 176
softirq-safe locks: 53
softirq-unsafe locks: 137
irq-safe locks: 59
irq-unsafe locks: 176
The lock validator has observed 1598 actual single-thread locking patterns,
and has validated all possible 2033928 distinct locking scenarios.
More details about the design of the lock validator can be found in
Documentation/lockdep-design.txt, which can also found at:
http://redhat.com/~mingo/lockdep-patches/lockdep-design.txt
[bunk@stusta.de: cleanups]
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Arjan van de Ven <arjan@linux.intel.com>
Signed-off-by: Adrian Bunk <bunk@stusta.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-07-03 01:24:50 -06:00
|
|
|
#endif
|
2005-04-16 16:20:36 -06:00
|
|
|
/*
|
|
|
|
* Sanity-check: shared interrupts must pass in a real dev-ID,
|
|
|
|
* otherwise we'll have trouble later trying to figure out
|
|
|
|
* which interrupt is which (messes up the interrupt freeing
|
|
|
|
* logic etc).
|
|
|
|
*/
|
2006-07-01 20:29:31 -06:00
|
|
|
if ((irqflags & IRQF_SHARED) && !dev_id)
|
2005-04-16 16:20:36 -06:00
|
|
|
return -EINVAL;
|
|
|
|
if (irq >= NR_IRQS)
|
|
|
|
return -EINVAL;
|
2006-06-29 03:24:49 -06:00
|
|
|
if (irq_desc[irq].status & IRQ_NOREQUEST)
|
|
|
|
return -EINVAL;
|
2005-04-16 16:20:36 -06:00
|
|
|
if (!handler)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
action = kmalloc(sizeof(struct irqaction), GFP_ATOMIC);
|
|
|
|
if (!action)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
action->handler = handler;
|
|
|
|
action->flags = irqflags;
|
|
|
|
cpus_clear(action->mask);
|
|
|
|
action->name = devname;
|
|
|
|
action->next = NULL;
|
|
|
|
action->dev_id = dev_id;
|
|
|
|
|
2006-01-06 01:12:21 -07:00
|
|
|
select_smp_affinity(irq);
|
|
|
|
|
2007-02-12 01:52:00 -07:00
|
|
|
#ifdef CONFIG_DEBUG_SHIRQ
|
|
|
|
if (irqflags & IRQF_SHARED) {
|
|
|
|
/*
|
|
|
|
* It's a shared IRQ -- the driver ought to be prepared for it
|
|
|
|
* to happen immediately, so let's make sure....
|
|
|
|
* We do this before actually registering it, to make sure that
|
|
|
|
* a 'real' IRQ doesn't run in parallel with our fake
|
|
|
|
*/
|
|
|
|
if (irqflags & IRQF_DISABLED) {
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
local_irq_save(flags);
|
|
|
|
handler(irq, dev_id);
|
|
|
|
local_irq_restore(flags);
|
|
|
|
} else
|
|
|
|
handler(irq, dev_id);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2005-04-16 16:20:36 -06:00
|
|
|
retval = setup_irq(irq, action);
|
|
|
|
if (retval)
|
|
|
|
kfree(action);
|
|
|
|
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(request_irq);
|