linux 底层驱动中断分析和一般使用(顶半部与底半部)

中断是个老掉牙的概念了,无需在解释了。

中断到来时,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

当然,也有可能有其他平台上自定义的flags。

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

需要注意的是这两个函数一定是成对出现的,假如多一次调用disable_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),
};

@2:禁止当前处理器上的所有中断

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



你可能感兴趣的:(linux驱动,linux)