三读内核中断处理(5):中断系统提供的API

来源:互联网 发布:ssm框架源码 编辑:程序博客网 时间:2024/06/06 17:26

快乐虾

http://blog.csdn.net/lights_joy/

lights@hb165.com

  

本文适用于

ADSP-BF561

优视BF561EVB开发板

uclinux-2008r1.5-rc3(smp patch)

Visual DSP++ 5.0(update 5)

  

欢迎转载,但请保留作者信息

 

中断系统提供的API大都在kernel/irq/manage.c中。

1.1    synchronize_irq

这个函数用于在两个核间进行中断的同步:

/**

 *   synchronize_irq - wait for pending IRQ handlers (on other CPUs)

 *   @irq: interrupt number to wait for

 *

 *   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;

 

     if (irq >= NR_IRQS)

         return;

 

     while (desc->status & IRQ_INPROGRESS)

         cpu_relax();

}

这个函数仅用于使用SMP的情况。由于每一个核都可以同等地处理每一个外部中断,因此有时候需要进行同步,从代码可以看出,它就是简单地等待一个中断处理完成。

1.2    disable_irq_nosync

这个函数用于关闭指定中断:

/**

 *   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)

{

     struct irq_desc *desc = irq_desc + irq;

     unsigned long flags;

 

     if (irq >= NR_IRQS)

         return;

 

     spin_lock_irqsave(&desc->lock, flags);

     if (!desc->depth++) {

         desc->status |= IRQ_DISABLED;

         desc->chip->disable(irq);

     }

     spin_unlock_irqrestore(&desc->lock, flags);

}

可以看出,这个函数直接调用irq_chip的成员disable这一回调函数,野蛮的关闭硬件中断!

1.3    disable_irq

这个函数将关闭硬件中断并等待(可能有的)中断处理完成。

/**

 *   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)

{

     struct irq_desc *desc = irq_desc + irq;

 

     if (irq >= NR_IRQS)

         return;

 

     disable_irq_nosync(irq);

     if (desc->action)

         synchronize_irq(irq);

}

1.4    enable_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)

{

     struct irq_desc *desc = irq_desc + irq;

     unsigned long flags;

 

     if (irq >= NR_IRQS)

         return;

 

     spin_lock_irqsave(&desc->lock, flags);

     switch (desc->depth) {

     case 0:

         printk(KERN_WARNING "Unbalanced enable for IRQ %d/n", irq);

         WARN_ON(1);

         break;

     case 1: {

         unsigned int status = desc->status & ~IRQ_DISABLED;

 

         /* Prevent probing on this irq: */

         desc->status = status | IRQ_NOPROBE;

         check_irq_resend(desc, irq);

         /* fall-through */

     }

     default:

         desc->depth--;

     }

     spin_unlock_irqrestore(&desc->lock, flags);

}

在系统初始化完成的时候,每个irq_desc->depth值都将初始化为1。因而这个函数将直接执行check_irq_resend启用中断。

1.5    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;

     irqreturn_t (*handler)(int, void *) = NULL;

 

     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);

              if (action->flags & IRQF_SHARED)

                   handler = action->handler;

              kfree(action);

              return;

         }

         printk(KERN_ERR "Trying to free already-free IRQ %d/n", irq);

         spin_unlock_irqrestore(&desc->lock, flags);

         return;

     }

#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

}

1.6    request_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:

 *

 *   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

          */

         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

 

     retval = setup_irq(irq, action);

     if (retval)

         kfree(action);

 

     return retval;

}

这里直接用kmalloc来分配irqaction其实挺浪费空间的。当然由于中断个数有限,其浪费的空间数量也有限,呵呵。

 

 

 

 

 

 

 

 

原创粉丝点击