中断是个老掉牙的概念了,无需在解释了。
中断到来时,CPU自动运行到某一个地址,那么中断很多,会形成很多个地址,那么多了就成了一个表格了,哎呀,中断向量表。。。。。
因为现在有amlogic的项目,所以就以amlogic的平台作为分析对象,讲解实际的使用方法以及注意事项。
amlogic_gpio_to_irq(pdata->key[i].pin, MOD_NAME,
AML_GPIO_IRQ(irq_keyup, FILTER_NUM7,GPIO_IRQ_RISING));
amlogic_gpio_to_irq(pdata->key[i].pin, MOD_NAME,
AML_GPIO_IRQ(irq_keydown, FILTER_NUM7,GPIO_IRQ_FALLING));
request_irq(irq_keyup + INT_GPIO_0, kp_isr, IRQF_DISABLED, "irq_keyup", kp)
这是一个普通GPIO的中断的申请。
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);
}
request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev)
irq:是中断号,但是这个irq的话一般不会固定的去定义,一般会有一个函数叫做xxx_gpio_to_irq,通过实际的GPIO端口,返回gpio的实际在内核中的irq的中断号。
handler:中断服务函数,
flags:
/*
* These flags used only by the kernel as part of the
* irq handling routines.
*
* IRQF_DISABLED - keep irqs disabled when calling the action handler.
* DEPRECATED. This flag is a NOOP and scheduled to be removed
* IRQF_SHARED - allow sharing the irq among several devices
* IRQF_PROBE_SHARED - set by callers when they expect sharing mismatches to occur
* IRQF_TIMER - Flag to mark this interrupt as timer interrupt
* 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.
* IRQF_NO_SUSPEND - Do not disable this IRQ during suspend
* IRQF_FORCE_RESUME - Force enable it on resume even if IRQF_NO_SUSPEND is set
* IRQF_NO_THREAD - Interrupt cannot be threaded
* IRQF_EARLY_RESUME - Resume IRQ early during syscore instead of at device
* resume time.
*/
#define IRQF_DISABLED 0x00000020
#define IRQF_SHARED 0x00000080
#define IRQF_PROBE_SHARED 0x00000100
#define __IRQF_TIMER 0x00000200
#define IRQF_PERCPU 0x00000400
#define IRQF_NOBALANCING 0x00000800
#define IRQF_IRQPOLL 0x00001000
#define IRQF_ONESHOT 0x00002000
#define IRQF_NO_SUSPEND 0x00004000
#define IRQF_FORCE_RESUME 0x00008000
#define IRQF_NO_THREAD 0x00010000
#define IRQF_EARLY_RESUME 0x00020000
name:随便的一个和驱动相关的的字符串
dev:其实是dev_id,如果是普通的单独的中断,直接填写NULL,如果是共享中断的话,一定需要dev_id来区分,否侧会有很大的影响。
@1: 中断的启动以及关闭
!!void enable_irq(unsigned int 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 only when
* desc->irq_data.chip->bus_lock and desc->chip->bus_sync_unlock are NULL !
*/
void enable_irq(unsigned int irq)
!!void disable_irq(unsigned int 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)
{
if (!__disable_irq_nosync(irq))
synchronize_irq(irq);
}
好,看一个中断服务函数,这个代码是msm高通平台上本人调试的一个TP驱动
static irqreturn_t msg21xx_interrupt(int irq, void *dev_id)
{
printk(KERN_INFO"msg21xx_interrupt,irq=%d\n",irq);
disable_irq_nosync(msg21xx_irq);
if(chip_type == CTP_ID_MSG22XX)
schedule_work(&msg21xx_wq);
else if(chip_type == CTP_ID_MSG28XX)
schedule_work(&msg28xx_wq);
else{
enable_irq(msg21xx_irq);
}
return IRQ_HANDLED;
}
当中断发生时,进入中断服务函数,关闭中断。
disable_irq_nosync(msg21xx_irq);
这个函数是立即返回函数,速度要比disable_irq速度快。
schedule_work(&msg21xx_wq);
启动&msg21xx_wq的任务后直接返回IRQ_HANDLED,常见使用的是第一个和第二个。
/**
* enum irqreturn
* @IRQ_NONE interrupt was not from this device
* @IRQ_HANDLED interrupt was handled by this device
* @IRQ_WAKE_THREAD handler requests to wake the handler thread
*/
enum irqreturn {
IRQ_NONE = (0 << 0),
IRQ_HANDLED = (1 << 0),
IRQ_WAKE_THREAD = (1 << 1),
};
local_irq_save(flags);
其中flags是unsigned long型数据,保存当前的中断状态。
@3:恢复中断状态
local_irq_restore(flags);
其中flags是调用local_irq_save(flags);中的flags。
local_irq_save(flags);
your_function();
local_irq_restore(flags);
其中的your_function一定要简短,否则会遇到系统响应慢,或者其他不可预知的后果。
@4:
local_irq_disable()
local_irq_enable()
以上的这两个函数也应该是成对的出现的,相对于disable_irq来说,其关闭中断就是关闭中断了,不会要求必须是disable和enable使用的次数必须是一致的,
不会维护对多次调用的跟踪(linux设备驱动开发)。
中断的顶半部和底半部
中断的的顶半部分是,实际上就是reques_irq中定义的中断例程。而实际的底半部分是之前我们定义的work或者是tasklet的机制,因为很多在tasklet中是不允许休眠的,所以我一般采用的是workqueue。但是当tasklet中能够尽快的处理中断,所以当任务中没有休眠的东西时,可以采用tasklet的机制来处理中断的底半部分。
tasklet_schedule(&dw->tasklet);
这个是在中断服务函数中调用的典型例子,就是1句话。
在工作队列中是
schedule_work(&msg28xx_wq);