Linux驱动开发-5、中断处理模型

中断处理模型

1、概念解析

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

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

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

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

 

2、分配IRQ(中断)

a) 现代设备可直接分配

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

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

d) 系统中活动的IRQ列表

i. /proc/interrupts 文件中由记录

 

 

3、中断结构体详解

a) struct irq_desc 中的主要成员

i.  * @irq_data: per irq and chip data passed down to chip functions

ii.  * @action: the irq action chain/* IRQ action list */

1. 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*/

}

具体联系图解:

 

 

图片来至国嵌教学

4、Linux 中断驱动设计函数解析

a) 头文件:<linux/interrupt.h>

b) 中断函数服务函数实现

i. static irqreturn_t xxx_interrupt(int irq,void *dev_id);

ii. 参数说明:

1.、irq:中断号a) 所在目录linux/arch/arm/mach-xxx/include/mach/irqs.h

2、dev_id:传递给中断处理函数的变量,无传参一般写为0


iii. 函数返回:

1. 0表示成功

2. -INCAL表示中断号无效或处理函数指针为 NULL

3. -EBUSY表示中断已经被占用且不能共享

 

c) 注册中断处理函数

i. int request_irq(unsigned int irq, irq_handler_t handler,

unsigned long flags, const char *devname, void *dev_id)

参数说明:

irq:中断号

 在源码目录linux/arch/arm/mach-xxx/include/mach/irqs.h中有定义

hander:中断函数服务函数

flags:中断类型标志:中断触发方式----- <linux/interrupt.h>

devname:产生中断的设备名


d) 注销中断处理函数

i. void free_irq(unsigned int irq, void *dev_id)

1. irq:注册的中断号

2. dev_id:传递给中断处理函数的数据

 

 

5、使能与屏蔽中断函数解析

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才会有响应

 

6、编程套路

a) 编写中断服务函数

b) 请求(注册)中断号

c) 释放(注销)中断号

模板:

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

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

 

7、小结:

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

i. 申请资源

ii. 注销资源

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

8、下面就操起小刀测试

a) 开发板:tq210

b) 注意事项:

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

ii. 命令: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);  /*按键12设置成外部中断*/

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

你可能感兴趣的:(编程,linux,嵌入式,TQ210,Linux驱动开发)