快乐虾
http://blog.csdn.net/lights_joy/
本文适用于
ADSP-BF561
uclinux-2008r1.5-rc3(smp patch)
Visual DSP++ 5.0(update 5)
欢迎转载,但请保留作者信息
中断系统提供的API大都在kernel/irq/manage.c中。
这个函数用于在两个核间进行中断的同步:
/**
* 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的情况。由于每一个核都可以同等地处理每一个外部中断,因此有时候需要进行同步,从代码可以看出,它就是简单地等待一个中断处理完成。
这个函数用于关闭指定中断:
/**
* 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这一回调函数,野蛮的关闭硬件中断!
这个函数将关闭硬件中断并等待(可能有的)中断处理完成。
/**
* 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);
}
这个函数用于启用一个指定的中断。
/**
* 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启用中断。
这个函数用于释放中断:
/**
* 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
}
这个函数用以自己指定一个中断的处理函数。
/**
* 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其实挺浪费空间的。当然由于中断个数有限,其浪费的空间数量也有限,呵呵。