您的位置:首页 > 运维架构 > Linux

linux中断之中断注册

2012-11-01 08:47 246 查看
专业的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;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: