韦东山uboot_内核_根文件系统学习笔记5.7-第005课_字符设备驱动_第007节_字符设备驱动程序之中断方式的按键驱动_Linux异常处理结构

一 单片机下的中断处理:

①分辨是哪个中断;
②调用处理函数;
③清中断。
下面结合代码查看

#include "s3c24xx.h"

void EINT_Handle()
{
    
    
    unsigned long oft = INTOFFSET;
    unsigned long val;
    
    switch( oft )
    {
    
    
    	// 分辨是哪个中断;
        // S2被按下
        case 0: 
        {
    
       
            GPFDAT |= (0x7<<4);   // 所有LED熄灭
            GPFDAT &= ~(1<<4);      // LED1点亮
            break;
        }
        
        // S3被按下
        case 2:
        {
    
       
            GPFDAT |= (0x7<<4);   // 所有LED熄灭
            GPFDAT &= ~(1<<5);      // LED2点亮
            break;
        }

        // K4被按下
        case 5:
        {
    
       
            GPFDAT |= (0x7<<4);   // 所有LED熄灭
            GPFDAT &= ~(1<<6);      // LED4点亮                
            break;
        }

        default:
            break;
    }

    //清中断
    if( oft == 5 ) 
        EINTPEND = (1<<11);   // EINT8_23合用IRQ5
    SRCPND = 1<<oft;
    INTPND = 1<<oft;
}

二 LINUX下的中断处理:

总结:
①按下按键
②CPU自动进入异常模式

//arch/arm/kernel/entry-armv.s
b	vector_irq + stubs_offset

||
||调用
||

.long	__irq_usr			@  0  (USR_26 / USR_32)

||
||调用
||

__irq_usr:
	usr_entry
	...
	irq_handler

||
||调用
||

/*
 * Interrupt handling.  Preserves r7, r8, r9
 */
	.macro	irq_handler
	get_irqnr_preamble r5, lr
1:	get_irqnr_and_base r0, r6, r5, lr
	movne	r1, sp
	@
	@ routine called with r0 = irq number, r1 = struct pt_regs *
	@
	adrne	lr, 1b
	bne	asm_do_IRQ

1. asm_do_IRQ函数作用:

①分辨是哪个中断;②调用处理函数;③清中断。

asmlinkage void __exception asm_do_IRQ(unsigned int irq, struct pt_regs *regs)
{
    
    
	struct pt_regs *old_regs = set_irq_regs(regs);
	//注释1:
	//irq_desc是个数组,定义于kernel/irq,参见下文
	//这里根据中断编号找出数组的一个元素
	struct irq_desc *desc = irq_desc + irq;

	/*
	 * Some hardware gives randomly wrong interrupts.  Rather
	 * than crashing, do something sensible.
	 */
	if (irq >= NR_IRQS)
		desc = &bad_irq_desc;

	irq_enter();
	//注释2:
	//该函数实现了这三个功能:
	//①分辨是哪个中断;②调用处理函数;③清中断
	//函数原型参见注释2
	desc_handle_irq(irq, desc);

	/* AT91 specific workaround */
	irq_finish(irq);

	irq_exit();
	set_irq_regs(old_regs);
}

注释1:irq_desc定义,一个中断描述数组,下标是中断号

struct irq_desc irq_desc[NR_IRQS] __cacheline_aligned_in_smp = {
    
    
	[0 ... NR_IRQS-1] = {
    
    
		.status = IRQ_DISABLED,
		.chip = &no_irq_chip,
		.handle_irq = handle_bad_irq,
		.depth = 1,
		.lock = __SPIN_LOCK_UNLOCKED(irq_desc->lock),
#ifdef CONFIG_SMP
		.affinity = CPU_MASK_ALL
#endif
	}
};

注释2:desc_handle_irq函数原型

static inline void desc_handle_irq(unsigned int irq, struct irq_desc *desc)
{
    
    
	//注释3:函数定义在kernel/irq/chip.c
	//desc->handle_irq = handle_edge_irq
	desc->handle_irq(irq, desc);
}

2.irq_desc数组元素初始化

irq_desc.handle_irq = handle_edge_irq;
chip = s3c_irqext_chip;

(1) irq_desc.handle_irq函数原型的初始化过程:

//arch/arm/plat-s3c24xx
void __init s3c24xx_init_irq(void)
{
    
    
	//...
	/* external interrupts */
	for (irqno = IRQ_EINT4; irqno <= IRQ_EINT23; irqno++) {
    
    
		irqdbf("registering irq %d (extended s3c irq)\n", irqno);
		set_irq_chip(irqno, &s3c_irqext_chip);
		//--------调用set_irq_handler-----------
		set_irq_handler(irqno, handle_edge_irq);
		//--------------------------------------
		set_irq_flags(irqno, IRQF_VALID);
	}
	//...
}

||
||调用
||

//include/linux/irq.h
//irq = irqno = IRQ_EINT4
//handle = handle_edge_irq
static inline void set_irq_handler(unsigned int irq, irq_flow_handler_t handle)
{
    
    
	__set_irq_handler(irq, handle, 0, NULL);
}

||
||调用
||

//kernel/irq/chip.c
//irq = IRQ_EINT4
//handle = handle_edge_irq
//is_chained = 0
//*name = NULL
void __set_irq_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained, const char *name)
{
    
    
	struct irq_desc *desc;
	unsigned long flags;
	//根据注释1,这里根据中断编号找出数组的一个元素
	desc = irq_desc + irq;
	//这里把handle_edge_irq注册进来!对应于某个irq
	desc->handle_irq = handle;
	desc->name = name;
}

(2) irq_desc.chip原型的初始化过程:

//arch/arm/plat-s3c24xx
void __init s3c24xx_init_irq(void)
{
    
    
	//...
	/* external interrupts */
	for (irqno = IRQ_EINT4; irqno <= IRQ_EINT23; irqno++) {
    
    
		irqdbf("registering irq %d (extended s3c irq)\n", irqno);
		//--------------------------------------
		set_irq_chip(irqno, &s3c_irqext_chip);
		//--------------------------------------
		set_irq_handler(irqno, handle_edge_irq);
		set_irq_flags(irqno, IRQF_VALID);
	}
	//...
}

||
||调用
||

//kernel/irq/chip.c
//irq=IRQ_EINT4
//*chip=s3c_irqext_chip
int set_irq_chip(unsigned int irq, struct irq_chip *chip)
{
    
    
	struct irq_desc *desc;
	desc = irq_desc + irq;
	desc->chip = chip;
}

3.handle_edge_irq函数处理过程

根据前面的分析,这是外部边沿触发中断处理函数

void fastcall handle_edge_irq(unsigned int irq, struct irq_desc *desc)
{
    
    
	/*
	 * If we're currently running this IRQ, or its disabled,
	 * we shouldn't process the IRQ. Mark it pending, handle
	 * the necessary masking and go out
	 * 出错处理
	 */
	if (unlikely((desc->status & (IRQ_INPROGRESS | IRQ_DISABLED)) ||
		    !desc->action)) {
    
    
		desc->status |= (IRQ_PENDING | IRQ_MASKED);
		mask_ack_irq(desc, irq);
		goto out_unlock;
	}
	/* 发生中断计数值自加 */
	kstat_cpu(cpu).irqs[irq]++;

	//-------------第一步-----------------
	/* Start handling the irq开始处理中断*/
	desc->chip->ack(irq);

	/* Mark the IRQ currently in progress.*/
	desc->status |= IRQ_INPROGRESS;

	do {
    
    
		struct irqaction *action = desc->action;
		irqreturn_t action_ret;
		
		/*错误处理,action是个链表,如果俩表为空,则...*/
		if (unlikely(!action)) {
    
    
			desc->chip->mask(irq);
			goto out_unlock;
		}

		/* 中断嵌套:如果新的中断到来,而当前中断还没有处理完成
		 * 必须把新的中断屏蔽掉
		 * When another irq arrived while we were handling
		 * one, we could have masked the irq.
		 * Renable it, if it was not disabled in meantime.
		 */
		if (unlikely((desc->status &
			       (IRQ_PENDING | IRQ_MASKED | IRQ_DISABLED)) ==
			      (IRQ_PENDING | IRQ_MASKED))) {
    
    
			desc->chip->unmask(irq);
			desc->status &= ~IRQ_MASKED;
		}

		desc->status &= ~IRQ_PENDING;
		spin_unlock(&desc->lock);
		//-------------第二步-----------------
		/* 真正处理过程*/
		action_ret = handle_IRQ_event(irq, action);
		if (!noirqdebug)
			note_interrupt(irq, desc, action_ret);
		spin_lock(&desc->lock);

	} while ((desc->status & (IRQ_PENDING | IRQ_DISABLED)) == IRQ_PENDING);

	desc->status &= ~IRQ_INPROGRESS;
out_unlock:
	spin_unlock(&desc->lock);
}

把以上函数精简一下:

void fastcall handle_edge_irq(unsigned int irq, struct irq_desc *desc)
{
    
    
	//-------------第一步-----------------
	/* Start handling the irq开始处理中断*/
	desc->chip->ack(irq);

	do {
    
    
		//-------------第二步-----------------
		/* 真正处理过程*/
		action_ret = handle_IRQ_event(irq, action);
	} while (..);
}

根据前面的分析:

irq_desc.handle_irq = handle_edge_irq;
chip = s3c_irqext_chip;
//arch/arm/plat-s3c24xx
static struct irq_chip s3c_irqext_chip = {
    
    
	.name		= "s3c-ext",
	.mask		= s3c_irqext_mask,
	.unmask		= s3c_irqext_unmask,
	.ack		= s3c_irqext_ack,
	.set_type	= s3c_irqext_type,
	.set_wake	= s3c_irqext_wake
};
  1. 第一步desc->chip->ack(irq)s3c_irqext_ack清中断
static void s3c_irqext_ack(unsigned int irqno)
{
    
    
	unsigned long req;
	unsigned long bit;
	unsigned long mask;

	bit = 1UL << (irqno - EXTINT_OFF);
	//
	mask = __raw_readl(S3C24XX_EINTMASK);
	__raw_writel(bit, S3C24XX_EINTPEND);
	req = __raw_readl(S3C24XX_EINTPEND);
	req &= ~mask;

	/* not sure if we should be acking the parent irq... */

	if (irqno <= IRQ_EINT7 ) {
    
    
		if ((req & 0xf0) == 0)
			s3c_irq_ack(IRQ_EINT4t7);
	} else {
    
    
		if ((req >> 8) == 0)
			s3c_irq_ack(IRQ_EINT8t23);
	}
}

static inline void s3c_irq_ack(unsigned int irqno)
{
    
    
	unsigned long bitval = 1UL << (irqno - IRQ_EINT0);

	__raw_writel(bitval, S3C2410_SRCPND);
	__raw_writel(bitval, S3C2410_INTPND);
}
  1. 处理中断action_ret = handle_IRQ_event(irq, action);
//kernel/irq/handle.c
irqreturn_t handle_IRQ_event(unsigned int irq, struct irqaction *action)
{
    
    
	do {
    
    
		ret = action->handler(irq, action->dev_id);	//执行链表中的每一个函数
		if (ret == IRQ_HANDLED)
			status |= action->flags;
		retval |= ret;
		action = action->next;						//依次取出链表每一个成员
	} while (action);
}

如何向action链表中添加注册的函数呢?
使用request_irq函数

插叙:struct irq_desc *desc结构定义分析

void fastcall handle_edge_irq(unsigned int irq, struct irq_desc *desc)

分析struct irq_desc *desc结构体

//include/linux/irq.h
struct irq_desc {
    
    
	irq_flow_handler_t	handle_irq;//根据2中的分析:handle_edge_irq
	struct irq_chip		*chip;     //与芯片相关的底层处理函数--根据2中的分析:s3c_irqext_chip,参见下面的注释1
	struct msi_desc		*msi_desc;
	void			*handler_data;
	void			*chip_data;
	struct irqaction	*action;	/* 中断函数执行列表 IRQ action list,参见注释2*/
	unsigned int		status;		/* IRQ status */

	unsigned int		depth;		/* nested irq disables */
	unsigned int		wake_depth;	/* nested wake enables */
	unsigned int		irq_count;	/* For detecting broken IRQs */
	unsigned int		irqs_unhandled;
	spinlock_t		lock;
#ifdef CONFIG_SMP
	cpumask_t		affinity;
	unsigned int		cpu;
#endif
#if defined(CONFIG_GENERIC_PENDING_IRQ) || defined(CONFIG_IRQBALANCE)
	cpumask_t		pending_mask;
#endif
#ifdef CONFIG_PROC_FS
	struct proc_dir_entry	*dir;
#endif
	const char		*name;
} ____cacheline_internodealigned_in_smp;
  1. 注释1 irq_chip
/**
 * struct irq_chip - hardware interrupt chip descriptor
 * 硬件中断处理,与具体芯片相关的一些操作
 * @name:		name for /proc/interrupts
 * @startup:		start up the interrupt (defaults to ->enable if NULL) 启动中断
 * @shutdown:		shut down the interrupt (defaults to ->disable if NULL) 关闭中断
 * @enable:		enable the interrupt (defaults to chip->unmask if NULL) 使能中断
 * @disable:		disable the interrupt (defaults to chip->mask if NULL) 禁止中断
 * @ack:		start of a new interrupt 开启新的中断(清除当前中断)
 * @mask:		mask an interrupt source 屏蔽中断源
 * @mask_ack:		ack and mask an interrupt source
 * @unmask:		unmask an interrupt source
 * @eoi:		end of interrupt - chip level
 * @end:		end of interrupt - flow level
 * @set_affinity:	set the CPU affinity on SMP machines
 * @retrigger:		resend an IRQ to the CPU
 * @set_type:		set the flow type (IRQ_TYPE_LEVEL/etc.) of an IRQ
 * @set_wake:		enable/disable power-management wake-on of an IRQ
 *
 * @release:		release function solely used by UML
 * @typename:		obsoleted by name, kept as migration helper
 */
struct irq_chip {
    
    
	const char	*name;
	unsigned int	(*startup)(unsigned int irq);
	void		(*shutdown)(unsigned int irq);
	void		(*enable)(unsigned int irq);
	void		(*disable)(unsigned int irq);

	void		(*ack)(unsigned int irq);
	void		(*mask)(unsigned int irq);
	void		(*mask_ack)(unsigned int irq);
	void		(*unmask)(unsigned int irq);
	void		(*eoi)(unsigned int irq);

	void		(*end)(unsigned int irq);
	void		(*set_affinity)(unsigned int irq, cpumask_t dest);
	int		(*retrigger)(unsigned int irq);
	int		(*set_type)(unsigned int irq, unsigned int flow_type);
	int		(*set_wake)(unsigned int irq, unsigned int on);

	/* Currently used only by UML, might disappear one day.*/
#ifdef CONFIG_IRQ_RELEASE_METHOD
	void		(*release)(unsigned int irq, void *dev_id);
#endif
	/*
	 * For compatibility, ->typename is copied into ->name.
	 * Will disappear.
	 */
	const char	*typename;
};
  1. 注释2 struct irqaction *action分析-中断函数执行链表
struct irqaction {
    
    
	irq_handler_t handler;
	unsigned long flags;
	cpumask_t mask;
	const char *name;
	void *dev_id;
	struct irqaction *next;
	int irq;
	struct proc_dir_entry *dir;
};

4.request_irq函数分析-注册中断程序使用

(1)函数声明:

/**
 *	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 卸载irq的时候需要用到本参数
 *	*/
 int request_irq(
 unsigned int irq, 
 irq_handler_t handler,
 unsigned long irqflags, 
 const char *devname, 
 void *dev_id)

函数实现

int request_irq(unsigned int irq, irq_handler_t handler,
		unsigned long irqflags, const char *devname, void *dev_id)
{
    
    
	struct irqaction *action;
	int retval;
	//1.分配一个结构
	action = kmalloc(sizeof(struct irqaction), GFP_ATOMIC);
	//使用输入参数来初始化这个action结构
	action->handler = handler;
	action->flags = irqflags;
	cpus_clear(action->mask);
	action->name = devname;
	action->next = NULL;
	action->dev_id = dev_id;
	//2.使用分配的这个action结构
	retval = setup_irq(irq, action);
	if (retval)
		kfree(action);

	return retval;
}

(2)setup_irq(irq, action)函数分析

int setup_irq(unsigned int irq, struct irqaction *new)
{
    
    
	struct irq_desc *desc = irq_desc + irq;
	p = &desc->action;
	old = *p;
	
	/* add new interrupt at end of irq queue 
	   把新的函数加到链表队尾*/
	do {
    
    
		p = &old->next;
		old = *p;
	} while (old);
	
	/* Setup the type (level, edge polarity) if configured: 
		设置中断引脚触发类型,参见注释1*/
	desc->chip->set_type(irq,new->flags & IRQF_TRIGGER_MASK);
	
	/* 使能中断*/
	if (desc->chip->startup)
		desc->chip->startup(irq);
	else
		desc->chip->enable(irq);
}

注释1:

desc->chip->set_type(irq,new->flags & IRQF_TRIGGER_MASK)函数分析:调用与芯片相匹配的具体代码初始化中断引脚,中断类型等
根据插叙的分析desc->chip=s3c_irqext_chip

static struct irq_chip s3c_irqext_chip = {
    
    
	.name		= "s3c-ext",
	.mask		= s3c_irqext_mask,
	.unmask		= s3c_irqext_unmask,
	.ack		= s3c_irqext_ack,
	.set_type	= s3c_irqext_type,
	.set_wake	= s3c_irqext_wake
};

所以调用desc->chip->set_type(irq,new->flags & IRQF_TRIGGER_MASK)相当于调用s3c_irqext_type(unsigned int irq, unsigned int type)

int s3c_irqext_type(unsigned int irq, unsigned int type)
{
    
    
	void __iomem *extint_reg;
	void __iomem *gpcon_reg;
	unsigned long gpcon_offset, extint_offset;
	unsigned long newvalue = 0, value;

	if ((irq >= IRQ_EINT0) && (irq <= IRQ_EINT3))
	{
    
    
		gpcon_reg = S3C2410_GPFCON;
		extint_reg = S3C24XX_EXTINT0;
		gpcon_offset = (irq - IRQ_EINT0) * 2;
		extint_offset = (irq - IRQ_EINT0) * 4;
	}
	...

	/* Set the GPIO to external interrupt mode */
	value = __raw_readl(gpcon_reg);
	value = (value & ~(3 << gpcon_offset)) | (0x02 << gpcon_offset);
	__raw_writel(value, gpcon_reg);

	/* Set the external interrupt to pointed trigger type */
	switch (type)
	{
    
    
		case IRQT_NOEDGE:
			printk(KERN_WARNING "No edge setting!\n");
			break;

		case IRQT_RISING:
			newvalue = S3C2410_EXTINT_RISEEDGE;
			break;

		case IRQT_FALLING:
			newvalue = S3C2410_EXTINT_FALLEDGE;
			break;

		case IRQT_BOTHEDGE:
			newvalue = S3C2410_EXTINT_BOTHEDGE;
			break;
		...
	}

	value = __raw_readl(extint_reg);
	value = (value & ~(7 << extint_offset)) | (newvalue << extint_offset);
	__raw_writel(value, extint_reg);

	return 0;
}

5.free_irq函数分析-释放irq

①把函数移出链表;
②禁止中断

/**
 *	free_irq - free an interrupt		释放中断
 *	@irq: Interrupt line to free		中断标号
 *	@dev_id: Device identity to free	设备号
 */
 void free_irq(unsigned int irq, void *dev_id)
{
    
    
	struct irq_desc *desc;
	struct irqaction **p;
	/*查找对应的中断号*/
	desc = irq_desc + irq;
	p = &desc->action;
	
	/*循环查找action链表*/
	for (;;) {
    
    
		struct irqaction *action = *p;
		if (action) {
    
    
			struct irqaction **pp = p;
			p = &action->next;
			
			/*判断这个action的dev_id是否等于传进来的dev_id,
			若相等,则卸载掉;
			若不想等,则查找下一个。*/
			if (action->dev_id != dev_id)
				continue;

			/* Found it - now remove it from the list of entries 卸载掉*/
			*pp = action->next;
			desc->chip->release(irq, dev_id);
			
			/* 若链表已空,屏蔽或者禁止该中断*/
			if (!desc->action) {
    
    
				if (desc->chip->shutdown)
					desc->chip->shutdown(irq);
				else
					desc->chip->disable(irq);
			}
}

猜你喜欢

转载自blog.csdn.net/xiaoaojianghu09/article/details/104593597