中断处理模型

Hntea bio photo By Hntea

概念解析

  • I/O操作的不确定因素以及处理器和I/O设备之间速度不匹配,设备往往通过某种硬件信号异步唤起处理器的注意

  • 这些硬件信号就称为 中断,每个设备都被分配了一个相关的标示符,别称为中断请求号(IRQ)

  • 处理器检测到某一 IRQ 号对应的中断产生时,它将停止工作并启动IRQ所对应的中断服务程序

  • 共享中断:多个设备使用同一个中断源;注销区分用设备号dev_id和 服务函数区分,共享中断的多个设备在申请中断时都要使用SA_SHIRQ标志,而一个设备以SA_SHIRQ申请某中断成功的前提是之前申请该中断的所有设备也都以SA_SHIRQ标志申请

分配IRQ(中断)号

  • 现代设备可直接分配

  • TQ210的硬件中断号是从32开始的,0-31的中断号分配给软中断

  • 现代设备能够响应对IRQ的查询(系统启动过程中由BIOS分配)

  • 系统中活动的IRQ列表:/proc/interrupts 文件中由记录

中断结构体详解

  • struct irq_desc 中的主要成员
/ * @irq_data: per irq and chip data passed down to chip functions
 * @action: the irq action chain/* IRQ action list */

 struct irqaction {
	
	irq_handler_t handler; /*用户注册的中断处理函数放在这里*/
	
	unsigned long flags; /*中断模式*/
	
	void *dev_id; /*设备号*/
	
	struct irqaction *next; /*将共享中断连接成单向链表*/
	
	int irq; /*中断号*/
	
	irq_handler_t thread_fn;
	
	struct task_struct *thread;
	
	unsigned long thread_flags;
	
	unsigned long thread_mask;
	
	const char *name; /*中断设备名*/
	
	struct proc_dir_entry *dir; /*pointer to the proc/irq/NN/name entry*/
}


Linux中断驱动设计函数解析

  • 头文件:<linux/interrupt.h>

  • 中断函数服务函数实现

/*
 参数说明:
	 irq:中断号 所在目录linux/arch/arm/mach-xxx/include/mach/irqs.h
	dev_id:传递给中断处理函数的变量,无传参一般写为0
函数返回:
	0表示成功
	-INCAL表示中断号无效或处理函数指针为 NULL
	-EBUSY表示中断已经被占用且不能共享
*/
static irqreturn_t xxx_interrupt(int irq,void *dev_id);
  • 注册中断处理函数
/*
参数说明:
irq:中断号
 在源码目录linux/arch/arm/mach-xxx/include/mach/irqs.h中有定义
hander:中断函数服务函数
flags:中断类型标志:中断触发方式----- <linux/interrupt.h>
devname:产生中断的设备名
*/
int request_irq(unsigned int irq, irq_handler_t handler,
	unsigned long flags, const char *devname, void *dev_id);
  • 注销中断处理函数
/*
 irq:注册的中断号
 dev_id:传递给中断处理函数的数据
*/
void free_irq(unsigned int irq, void *dev_id);
  • 使能与屏蔽中断函数解析

a) 屏蔽一个中断源

i. void disable_irq(int irq):立即返回

ii. void disable_irq_nosync(int irq):等待目前的中断处理完

iii. void enable_irq(int irq):取消屏蔽

注:这三个函数作用于可编程中断控制器,对系统内的所有CPU都生效

b) 屏蔽CPU内所有中断

i. void local_irq_restore(unsigned long flags);

ii. void local_irq_enable(void)

注:local_开头的方法作用范围是本CPU内,也就是上面的中断与屏蔽,只有执行该local_xxx_xxx()函数的CPU才会有响应

编程套路

  1. 编写中断服务函数

  2. 请求(注册)中断号

  3. 释放(注销)中断号

模板

<.......> 头文件

static irqreturn_t xxx_interrupt(int irq,void *dev_id)

{

.....

}

static int xxx_init()

{

int ret = 0;

ret = request_irq(irq_num,xxx_interrupt,

xxxflag, “devname”, 0/....)

return ret;

}

static void xxx_exit()

{

free(irq,xxx_interrupt);

}

module_init(xxx_init);

module_exit(xxx_exit);

 

小结:

  1. 对比之前的字符设备和混杂字符设备,LINUX模块编程的套路就是

    • 申请资源
    • 注销资源
  2. 一个明显的特点是彼此之间没有干扰,就像使用LINUX中断的流程和使用LINUX字符设备的流程都可以分割出来,没半毛钱关系。

8、下面就操起小刀测试

a) 开发板:tq210

b) 注意事项:

测试该驱动时,应该查看中断号是否被使用了,如果被使用了,从新配置内核,把驱动选项中的输入设备中的按键支持去掉;命令:

cat /proc/interrupts

按键中断驱动程序设计

/******************************************

author : hntea

date: 2016/3/14

function : key interrupt test

******************************************/

#include<linux/module.h>

#include<linux/init.h>

#include<linux/miscdevice.h>

#include<linux/fs.h>

#include<linux/types.h>

#include<linux/io.h>

#include<linux/interrupt.h>

#define DEV_MINIR 11 /*自己随便取咯*/

#define DEV_NAME "key" /*自己随便取咯,该设备名会在 /dev目录下生成相应的设备文件*/

#define KEY_GPH0CON  0xE0200C00 /*key_1 /key_2 EXIT0/1*/

#define EXT_INT_0_CON  0xE0200E00 /*配置电平触发方式,在中断注册程序中已经解释*/

#define EXT_INT_0_MASK    0xE0200F00 /*外部中断掩码寄存器*/

#define VIC0INTENABLE  0xF2000010 /*中断使能寄存器*/

#define EXINT0ENABLE (1) /*打开中断0*/

#define EXINT1ENABLE (1<<1) /*打开中断1*/

/******************************************

函数名: key_hardinit

函数功能:硬件初始化

******************************************/

void key_hardinit(void)

{
	
	unsigned int *key_ioconfig = 0;
	
	unsigned int *irq_mask = 0;
	
	unsigned int *irq_enable = 0;
	
	unsigned int temdata = 0;
	
	/*GPIO初始化 32位寄存器*/
	
	key_ioconfig = ioremap(KEY_GPH0CON,4);  /*按键1和2设置成外部中断*/
	
	temdata = ioread32(key_ioconfig);
	
	temdata &= (~0xff);
	
	temdata |= 0xff;
	
	iowrite32(temdata,key_ioconfig);
	
	/*中断控制初始化,这个在中断注册时内核帮我们初始化了*/

}

/******************************************

函数名: key_interrupt

函数功能:中断服务函数入口

******************************************/

static irqreturn_t key_interrupt (int irq, void *dev_id)

{

   printk("key interrupt .........\n");

    printk(“i get dev_id is:%d”,(int)dev_id);

    return 0;

}

 

/******************************************

函数名: key_open

函数功能:文件操作

******************************************/

int  key_open(struct inode *inode, struct file *filp)

{

	key_hardinit();
	
	return 0;

}


/******************************************

函数名:

函数功能:文件操作

******************************************/

struct file_operations key_fop =

{
	
	.open = key_open,

};

 

struct miscdevice key=

{

	.minor = DEV_MINIR ,
	
	.name = DEV_NAME,
	
	.fops = &key_fop,

};

static int keyInit(void)

{
	
	int ret = 0;
	
	/*1、注册设备文件*/
	
	ret = misc_register(&key);
	
	/*2、注册中断号*/
	
	ret = request_irq(IRQ_EINT0,key_interrupt,
	
	IRQF_SHARED, "key", 1); /
	
	 
	
	printk("ret val is:%d\n",ret); //-16:中断号已经被申请;-22:中断号错误
	
	if(ret < 0)
	
	{
	
	printk("EXINT0 request fail!\n");
	
	}else{
	
	printk("EXINT0 request sucess!\n");
	
	}
	
	return 0;

}

static void keyExit(void)

{

	int ret = 0;
	
	/*注销中断*/
	
	 free_irq(IRQ_EINT0, 1);
	
	/*注销混杂设备*/
	
	ret = misc_deregister(&key);
	
	/*注销设备*/

}

MODULE_LICENSE("GPL");

MODULE_AUTHOR("Hntea");

module_init(keyInit);

module_exit(keyExit);