Linux--中断

来源:互联网 发布:游泳 肌肉 知乎 编辑:程序博客网 时间:2024/05/26 09:55
专业的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;
}

原创粉丝点击