专业的linux驱动开发离不开中断处理,在处理中断,首先要注册中断,在linux下通过request_irq来注册中断的,不同内核版本,注册中断所需要的参数也不同,本文以linux-2.6.34为例,对比老版本进行说明。
request_irq()函数在include/linux/interrupt.h中定义,原型为:
static inline int __must_check
request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
const char *name, void *dev)
参数说明:
unsigned int irq:注册中断服务程序对应的硬件中断号;
irq_handler_t handler:注册的中断服务程序,函数指针,原型定义为:typedef irqreturn_t (*irq_handler_t)(int, void *);
unsigned long flags:中断标志,表明中断程序的属性,注意,在新老内核中,相关属性定义不同。具体参考后面的中断标志位说明;
const char *name:与该中断相关联的名称,在/proc/interrupt中可看到。
void *dev:中断服务程序的参数,可以为NULL,但在注册共享中断时,此参数不能为NULL。
中断标志位
新版本中的IRQF_XXX替代了老版本中的SA_XXX,主要标志包括以下:
IRQF_DISABLED:快速中断标志,对应老版本中的SA_INTERRUPT标志,表明在处理本中断时屏蔽所有中断,而在没设置此标志位的程序中,都是开中断处理的,可以进行中断嵌套。
IRQF_SAMPLE_RANDOM:用于随机数据产生;
IRQF_SHARED:用于共享中断,设置此标志时,request_irq最后一个参数dev不能为NULL,对应老版本内核的SA_SHIRQ;
IRQF_PROBE_SHARED:探测共享中断;
IRQF_TIMER:专用于定时中断;
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.
注册中断
相关实现在kernel/irq/manage.c中。
static inline int __must_check
request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
const char *name, void *dev)
{
return request_threaded_irq(irq, handler, NULL, flags, name, dev);
}
int request_threaded_irq(unsigned int irq, irq_handler_t handler,
irq_handler_t thread_fn, unsigned long irqflags,
const char *devname, void *dev_id)
{
struct irqaction *action;
struct irq_desc *desc;
int retval;
/*
* handle_IRQ_event() always ignores IRQF_DISABLED except for
* the _first_ irqaction (sigh). That can cause oopsing, but
* the behavior is classified as "will not fix" so we need to
* start nudging drivers away from using that idiom.
*/
if ((irqflags & (IRQF_SHARED|IRQF_DISABLED)) == //共享中断不能作为快速中断。
(IRQF_SHARED|IRQF_DISABLED)) {
pr_warning(
"IRQ %d/%s: IRQF_DISABLED is not guaranteed on shared IRQs\n",
irq, devname);
}
#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) //共享中断必须设置dev_id参数
return -EINVAL;
desc = irq_to_desc(irq); //找到该中断号对应的irq_desc,即全局数组irq_desc的第irq个表项。
if (!desc) //所有irq_desc在系统启动时,通过init_IRQ初始化。
return -EINVAL;
if (desc->status & IRQ_NOREQUEST) //若此中断禁止响应,则返回。
return -EINVAL;
if (!handler) { //中断服务程序不能为NULL
if (!thread_fn)
return -EINVAL;
handler = irq_default_primary_handler;
}
action = kzalloc(sizeof(struct irqaction), GFP_KERNEL);
if (!action) //中断的入口是irq_desc->handle_irq或__do_IRQ(),但我们注册的中断处理程序都是irq_desc下的一个action。
return -ENOMEM;
action->handler = handler;
action->thread_fn = thread_fn;
action->flags = irqflags;
action->name = devname;
action->dev_id = dev_id;
chip_bus_lock(irq, desc);
retval = __setup_irq(irq, desc, action); //注册中断处理,将action挂到irq_desc->action中,或初始化中断线程等...
chip_bus_sync_unlock(irq, desc);
if (retval)
kfree(action);
#ifdef CONFIG_DEBUG_SHIRQ //用于调试共享中断。
if (!retval && (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 disable the irq to make sure that a 'real' IRQ doesn't
* run in parallel with our fake.
*/
unsigned long flags;
disable_irq(irq);
local_irq_save(flags);
handler(irq, dev_id);
local_irq_restore(flags);
enable_irq(irq);
}
#endif
return retval;
}
文章评论(0条评论)
登录后参与讨论