1. request_irq() --- 注册中断处理函数:
int request_irq(unsigned int irq, irq_handler_t handler,
unsigned long irqflags, const char *devname, void *dev_id);
/**
* 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:
*
* IRQF_SHARED Interrupt is shared
* IRQF_DISABLED Disable local interrupts while processing
* IRQF_SAMPLE_RANDOM The interrupt can be used for entropy
*
*/
int request_irq(unsigned int irq, irq_handler_t handler,
unsigned long irqflags, const char *devname, void *dev_id)
{
struct irqaction *action;
int retval;
#ifdef CONFIG_LOCKDEP
/*
* Lockdep wants atomic interrupt handlers:
*/
irqflags |= IRQF_DISABLED;
#endif
/*
* 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).
*/
if ((irqflags & IRQF_SHARED) && !dev_id)
return -EINVAL;
if (irq >= NR_IRQS)
return -EINVAL;
if (irq_desc[irq].status & IRQ_NOREQUEST)
return -EINVAL;
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;
select_smp_affinity(irq);
#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
*/
unsigned long flags;
local_irq_save(flags);
handler(irq, dev_id);
local_irq_restore(flags);
}
#endif
retval = setup_irq(irq, action);
if (retval)
kfree(action);
return retval;
}
EXPORT_SYMBOL(request_irq);
参数:
unsigned int irq --- 要分配的中断号。
对某些设备,如传统PC上的系统时钟或键盘,irq值是预先确定的。
对大多数设备来说,这个irq值是可以通过探测获取,或者通过编程动态确定的。
irq_handler_t handler --- 这是一个指针,指向指定的中断处理函数。
只要系统一接收到中断,该函数就被调用。 以后具体讨论。
unsigned long irqflags --- 中断标志位。
可以是0,也可以是一个或多个标志的位掩码。 定义在<linux/interrupt.h> 中
/*
* These flags used only by the kernel as part of the
* irq handling routines.
*
* IRQF_DISABLED - keep irqs disabled when calling the action handler.
* DEPRECATED. This flag is a NOOP and scheduled to be removed
* IRQF_SAMPLE_RANDOM - irq is used to feed the random generator
* IRQF_SHARED - allow sharing the irq among several devices
* IRQF_PROBE_SHARED - set by callers when they expect sharing mismatches to occur
* IRQF_TIMER - Flag to mark this interrupt as timer interrupt
* IRQF_PERCPU - Interrupt is per cpu
* IRQF_NOBALANCING - Flag to exclude this interrupt from irq balancing
* IRQF_IRQPOLL - Interrupt is used for polling (only the interrupt that is
* registered first in an shared interrupt is considered for
* performance reasons)
* IRQF_ONESHOT - Interrupt is not reenabled after the hardirq handler finished.
* Used by threaded interrupts which need to keep the
* irq line disabled until the threaded handler has been run.
* IRQF_NO_SUSPEND - Do not disable this IRQ during suspend
*
*/
#define IRQF_DISABLED 0x00000020 //在处理本中断期间,要禁止所有其他中断。
#define IRQF_SAMPLE_RANDOM 0x00000040
#define IRQF_SHARED 0x00000080 //表示可以在多个中断处理程序之间共享中断线。在同一个中断线上注册的每个处理程序必须指定这个标志。
#define IRQF_PROBE_SHARED 0x00000100
#define __IRQF_TIMER 0x00000200
#define IRQF_PERCPU 0x00000400
#define IRQF_NOBALANCING 0x00000800
#define IRQF_IRQPOLL 0x00001000
#define IRQF_ONESHOT 0x00002000
#define IRQF_NO_SUSPEND 0x00004000
#define IRQF_TIMER (__IRQF_TIMER | IRQF_NO_SUSPEND) //为定时器的中断处理指定的标志。
/*
* These correspond to the IORESOURCE_IRQ_* defines in
* linux/ioport.h to select the interrupt line behaviour. When
* requesting an interrupt without specifying a IRQF_TRIGGER, the
* setting should be assumed to be "as already configured", which
* may be as per machine or firmware initialisation.
*/
#define IRQF_TRIGGER_NONE 0x00000000
#define IRQF_TRIGGER_RISING 0x00000001 //上升沿中断触发方式
#define IRQF_TRIGGER_FALLING 0x00000002 //下降沿触发中断
#define IRQF_TRIGGER_HIGH 0x00000004 //高电平触发
#define IRQF_TRIGGER_LOW 0x00000008 //低电平触发
#define IRQF_TRIGGER_MASK (IRQF_TRIGGER_HIGH | IRQF_TRIGGER_LOW | \
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)
#define IRQF_TRIGGER_PROBE 0x00000010
const char *name --- 指定该中断的设备名。
这个设备名会被 /proc/interrupts 和 /proc/irq 文件引用。
void *dev --- 用于共享中断线。
在释放中断时,dev将提供为唯一的信息标志(cookie), 以便从共享中断线的诸多中断处理程序中删除指定的那个。
如果没有这个参数,内核就不可能知道在给定的中断线上到底要删除哪个。
如果无须共享中断线,可以将该值赋空值(NULL),但是如果中断线是共享的,就必须传递唯一的信息。
返回值:
irq_request( )执行成功,会返回0 ,失败返回非0.
最常见的错误时-EBUSY, 表示给定的中断线已经在使用。
注意:
request_irq()可能会睡眠,因此,不能在中断上下文或其他不允许阻塞的代码中调用该函数。
造成request_irq()可能会睡眠的原因:
在注册中断过程中,内核会在 /proc/irq 文件中创建一个与中断对应的项。函数 proc_mkdir()就是用来创建这个新 procfs 项的, proc_mkdir()通过调用 proc_create(),对这个新的procfs 项进行配置,而 proc_create() 会调用 kmalloc() 来请求分配内存。 由于kmalloc() 是可以睡眠的,所以这就是 request_irq()可能会睡眠的原因。
中断例子:
在一个驱动程序中请求一个中断,并通过request_irq()安装中断处理函数:
if(request_irq(irqn, my_interrupt, IRQF_SHARED, "my_device", my_dev)){
printk(KERN_ERR "my_device: cannot register IRQ %d\n", irqn);
return -EIO;
}
irqn --- 请求的中断号。
my_interrupt --- 中断处理函数
IRQF_SHARED --- 中断号可以共享
“my_device” --- 设备名
my_dev --- dev 的形参
2. 释放中断: void free_irq(unsigned int irq, void *dev_id);
卸载驱动程序时,需要注销相应的中断处理程序,并释放中断号。
如果指定的中断号 不是共享的,那么,该函数删除中断处理程序的同时,将禁用这条中断线。
如果中断号是共享的,则仅删除dev所对应的中断处理函数,而该中断号,只有在删除了最后一个中断处理函数之后才会被禁用。
所以可以看出, dev 就是用来区分共享同一中断号上面的多个中断处理函数,并让free_irq() 仅仅删除指定的中断处理函数。
/**
* 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;
WARN_ON(in_interrupt());
if (irq >= NR_IRQS)
return;
desc = irq_desc + irq;
spin_lock_irqsave(&desc->lock, flags);
p = &desc->action;
for (;;) {
struct irqaction *action = *p;
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;
/* Currently used only by UML, might disappear one day.*/
#ifdef CONFIG_IRQ_RELEASE_METHOD
if (desc->chip->release)
desc->chip->release(irq, dev_id);
#endif
if (!desc->action) {
desc->status |= IRQ_DISABLED;
if (desc->chip->shutdown)
desc->chip->shutdown(irq);
else
desc->chip->disable(irq);
}
spin_unlock_irqrestore(&desc->lock, flags);
unregister_handler_proc(irq, action);
/* Make sure it's not being used on another CPU */
synchronize_irq(irq);
#ifdef CONFIG_DEBUG_SHIRQ
/*
* 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
*/
if (action->flags & IRQF_SHARED) {
local_irq_save(flags);
action->handler(irq, dev_id);
local_irq_restore(flags);
}
#endif
kfree(action);
return;
}
printk(KERN_ERR "Trying to free already-free IRQ %d\n", irq);
spin_unlock_irqrestore(&desc->lock, flags);
return;
}
}
3. irqreturn_t:中断处理程序只能使用两种返回值:
a. 如果正确处理了IRQ, 则返回IRQ_HANDLED.
b. 如果ISR不负责该IRQ, 则返回IRQ_NONE.
/*
* For 2.4.x compatibility, 2.4.x can use
*
* typedef void irqreturn_t;
* #define IRQ_NONE
* #define IRQ_HANDLED
* #define IRQ_RETVAL(x)
*
* To mix old-style and new-style irq handler returns.
*
* IRQ_NONE means we didn't handle it.
* IRQ_HANDLED means that we did have a valid interrupt and handled it.
* IRQ_RETVAL(x) selects on the two depending on x being non-zero (for handled)
*/
typedef int irqreturn_t;
#define IRQ_NONE (0)
#define IRQ_HANDLED (1)
#define IRQ_RETVAL(x) ((x) != 0)
#endif