linux块设备驱动中断程序,linux设备驱动归纳总结(六):1.中断的实现

linux设备驱动归纳总结(六):1.中断的实现

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

一、什么是中断

中断分两种:

1)中断,又叫外部中断或异步中断,它的产生是由于外设向处理器发出中断请求。其中外部中断也有两种,这是由配置寄存器设定的:普通中断请求(IRQ)和快速中断请求(FIQ)。一般地,linux下很少使用快速中断请求。

2)异常,又叫内部中断或同步中断,它的产生是由于处理器执行指令出错。

在以下的内容我是要介绍由于外部设备产生的中断。

这里我还有两个名词要说清楚

1)中断请求线:在后面也叫中断号,每个中断都会通过一个唯一的数值来标识,而这个值就称做中断请求线

2)在2440芯片中,有些中断是需要共享一个中断寄存器中的一位,如EINT4——EINT7,它们是共享寄存器SRCPEND的第4位。具体可以查看芯片手册。

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

二、什么是中断处理函数

在相应一个中断是,内核会执行该信号对应的一个函数,该函数就叫做该中断对应的中断处理函数。一般来说,中断的优先级是最高的,一但接收到中断,内核就会调用对应的中断处理函数。

中断处理函数运行在中断上下文中。中断上下文与内核上下文有一点区别:

内核上下文是指应用层调用系统调用陷入内核执行,内核代表陷入的进程执行操作。函数中可以通过current查看当前进程(即应用层的进程)的信息,并且可以睡眠。

中断上下文中,不能通过current查看调用它的应用层进程的信息,同时,处于中断上下文时,不能睡眠。

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

一、从硬件角度看中断

中断的产生到处理器获得中断这段过程中,还要通过中断处理器来筛选信号。

先温习一下S3C2440芯片手册的知识:中断是如何产生的,中断处理器本身如何处理中断。先看一下一幅经典的图,这是介绍中断控制器的工作流程:

从硬件上的分类,有两种不同的中断类型:

1)自己占有SORCPND寄存器的一位(without

sub-register)。

2)几个中断共同享用SRCPND寄存器的一位(with

sub-register)。

其实两种都差不多,只是多了两步的检测。我以自己占用一位的中断来举例,如EINT1,在我的开发板,EINT1上接了一个按键。

1)当我按下按键产生电平变化,传到S3C2440的中断控制器上(即将要进入上面图的流程图)。

2)首先,信号要经过寄存器SRCPND,SRCPND是用来配置当前的处理器要接收什么中断,如果该寄存器配置成接收EINT1中断(对应位置一),则允许继续下一步。

3)然后,信号经过寄存器MASK,这是用来设置当前系统需要屏蔽的中断。注意,这里的屏蔽跟上一个寄存器的不接收中断是不一样的。这里的屏蔽是指,中断是接受了,但是由于某种原因,先暂时不屏蔽产生的中断。

4)通过INTPND寄存器,查看当前是否有相同的中断已经被请求(如果是,INTPND对应位置一)。

5)如果没有相同的中断在请求,中断处理器才会把这个信号传给处理器,这时处理器才会知道有EINT0的中断真正来了,要对信号进行处理了。

注:如果设定了EINT0是快速中断模式(FIQ),中断通过SRCPND寄存器后就会通过MODE寄存器的判断,确定是FIQ后,中断控制器优先将该中断传给CPU处理。

6)对应传来的中断类型(IRQ或FIQ),通过CPSR寄存器切换到对应的工作模式(ARM有七种工作模式)。

7)切换工作模式后,进入指定的中断处理入口执行中断处理函数。

注意:第6、7步在linux下的实现相对复杂,不像在裸板程序,只需要切换一下工作模式,执行相应的函数就可以了。迟点会介绍linux如何实现。

来个流程图比较只在,同时来个类比,处理器是老板,中断处理器是小秘:

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

四、注册和释放中断处理函数

上面的介绍只是讲解了一个设备产生中断后要经过怎么样的步骤才能让处理器接收到中断信号。传入处理器后,接下来的工作就是由内核来实现了,那是一个复杂的机制,我们这里先不说。但是,内核提供了相关的接口给我们,我们只要通过接口告诉内核,当来了指定中断时,内核你该执行哪个中断处理函数。

注册中断处理函数:

/*include

*/

int

request_irq(unsigned int irq, irq_handler_t handler,

unsigned

long irqflags, const char *devname, void *dev_id)

使用:

将中断号irq与中断处理函数handler对应

参数:

irq:指定要分配的中断号,中断号的定义在“include/mach/irqs.h”中。注意,不管是单独占有中断请求线的中断,还是共享中断请求线的每个中断,都有一个对应的中断号。,所以,调用该函数不需要考虑是哪种中断(是否共享寄存器),你想哪种中断响应,你就填对应的中断号。

handler:中断处理函数指针。

irqflags:中断处理标记,待会介绍:

devname:该字符串将显示在/proc/irq和/pro/interrupt中。

dev_id:ID号,待会会介绍。

返回值:成功返回0,失败返回非0。

注册函数需要注意两件事:

1)该函数会睡眠。

2)必须判断返回值。

中断处理标志irqflags,这里先介绍几个待会要用的:

/*linux-2.6.29/include/linux/interrupt.h*/

29

#define IRQF_TRIGGER_NONE 0x00000000

30

#define IRQF_TRIGGER_RISING 0x00000001 //上升沿触发中断

31

#define IRQF_TRIGGER_FALLING 0x00000002 //下降沿触发中断

32

#define IRQF_TRIGGER_HIGH 0x00000004 //高电平触发中断

33

#define IRQF_TRIGGER_LOW 0x00000008 //低电平触发中断

34

#define IRQF_TRIGGER_MASK (IRQF_TRIGGER_HIGH | IRQF_TRIGGER_LOW | \

35

IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)

36

#define IRQF_TRIGGER_PROBE 0x00000010

释放中断处理函数:

void

free_irq(unsigned int irq, void *dev_id)

编写中断处理函数:

中断处理函数声明如下:

static

irqreturn_t intr_handler(int irq, void *dev_id)

先看第一个参数irq,这是调用中断处理函数时传给它的中断号,对于新版本的内核,这个参数已经用处不大,一般只用于打印。

第二个参数dev_id,这个参数与request_irq()的参数dev_id一致,由于待会的程序我并不需要用这个参数,所以先不介绍。

再看返回值,中断处理函数的返回值有三个:

/*linux-2.6.29/include/linux/interrupt..h*/

21

#define IRQ_NONE (0) //如果产生的中断并不会执行该中断处理函数时返回该值

22

#define IRQ_HANDLED (1) //中断处理函数正确调用会返回

23

#define IRQ_RETVAL(x) ((x) != 0) //指定返回的数值,如果非0,返回IRQ_HADLER,否则

26

#ifndef IRQ_NONE //返回IRQ_NONE。

接下来就要写函数了,在我的开发板中,有一个按键是对应EINT1,我要实现的操作是,当我按下按键,终端打印”key

down”。在这个程序中,我并没有使用dev_id。这将在会以后的章节介绍。

/*6th_irq_1/1st/test.c*/

1

#include

2

#include

3

4

#include

5

。。。省略。。。

13

irqreturn_t irq_handler(int irqno, void *dev_id) //中断处理函数

14

{

15

printk("key down\n");

16

return IRQ_HANDLED;

17

}

18

19

static int __init test_init(void) //模块初始化函数

20

{

21

int ret;

22

23

/*注册中断处理函数,必须查看返回值

24

* IRQ_EINT1:中断号,定义在"include/mach/irqs.h"中

25

* irq_handler:中断处理函数

26

* IRQ_TIRGGER_FALLING:中断类型标记,下降沿触发中断

27

* ker_INT_EINT1:中断的名字,显示在/proc/interrupts等文件中

28

*NULL;现在我不使用dev_id,所以这里不传参数

29

*/

30

ret = request_irq(IRQ_EINT1, irq_handler, IRQF_TRIGGER_FALLING,

31

"key INT_EINT1", NULL);

32

if(ret){

33

P_DEBUG("request irq failed!\n");

34

return -1;

35

}

36

printk("hello irq\n");

37

return 0;

38

}

39

40

static void __exit test_exit(void) //模块卸载函数

41

{

42

free_irq(IRQ_EINT1, NULL);

43

printk("good bye irq\n");

44

}

45

46

module_init(test_init);

47

module_exit(test_exit);

48

49

MODULE_LICENSE("GPL");

50

MODULE_AUTHOR("xoao bai");

51

MODULE_VERSION("v0.1");

接下来验证一下:

[root:

1st]# insmod test.ko

hello

irq

[root:

1st]# key down //按下按键显示

key

down

key

down

key

down

[root:

1st]# cat /proc/interrupts

CPU0

17:

11 s3c-ext0 key INT_EINT1显示我注册和中断名字

30:

423482 s3c S3C2410 Timer Tick

32:

0 s3c s3c2410-lcd

51:

2782 s3c-ext eth0

70:

49 s3c-uart0 s3c2440-uart

71:

69 s3c-uart0 s3c2440-uart

79:

0 s3c-adc s3c2410_action

80:

0 s3c-adc adc, s3c2410_action

83:

0 - s3c2410-wdt

Err:

0

[root:

key INT_EINT1]# rmmod test //卸载

good

bye irq

[root:

key INT_EINT1]# cat /proc/interrupts //卸载后,我的中断名字消失了

CPU0

30:

828977 s3c S3C2410 Timer Tick

32:

0 s3c s3c2410-lcd

51:

3202 s3c-ext eth0

70:

192 s3c-uart0 s3c2440-uart

71:

277 s3c-uart0 s3c2440-uart

79:

0 s3c-adc s3c2410_action

80:

0 s3c-adc adc, s3c2410_action

83:

0 - s3c2410-wdt

Err:

0

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

五、proc/interrupt

接下来,稍稍介绍一下proc/interrupt

[root:

1st]# cat /proc/interrupts

CPU0

17:

11 s3c-ext0 key INT_EINT1显示我注册和中断名字

首先,第一列是中断号,之前的程序应该有人会有疑问:

中断号在哪里找的?

在S3C2440中,这些中断号定义在文件"include/mach/irqs.h"中,在这里,可以找到对应的中断:

25

/* main cpu interrupts */

26

#define IRQ_EINT0 S3C2410_IRQ(0) /* 16 */

27

#define IRQ_EINT1 S3C2410_IRQ(1)

28

#define IRQ_EINT2 S3C2410_IRQ(2)

29

#define IRQ_EINT3 S3C2410_IRQ(3)

30

#define IRQ_EINT4t7 S3C2410_IRQ(4) /* 20 */

在这里我标了两处红笔:

第一处:可以看到,S3C2440所有的中断号在原来的基值上加了16构成中断号,但不同的芯片或许有不同的定义方法。

第二处:有些中断号是共享的。在S3C2440中,EINT4---EINT7是共享寄存器SRCPND中的一位,所以,linux系统给这样的中断分配了一个共享的中断号。那就是说,如果你使用IRQ_EINT4t7,当收到这些中断时,都会调用对应的中断处理函数。这就需要在中断处理函数中通过第一个传参irq来辨别中断并执行相应的操作。如:

13

irqreturn_t irq_handler(int irqno, void *dev_id) //中断处理函数

14

{

15switch(irqno){

16。。。。}

17

}

那肯定有人会说,这太麻烦了吧,有没有更好的办法处理共享中断号?

那当然是有,继续看文件"include/mach/irqs.h":

61

/* interrupts generated from the external interrupts sources */

62

#define IRQ_EINT4 S3C2410_IRQ(32) /* 48 */

63

#define IRQ_EINT5 S3C2410_IRQ(33)

64

#define IRQ_EINT6 S3C2410_IRQ(34)

65

#define IRQ_EINT7 S3C2410_IRQ(35)

66

#define IRQ_EINT8 S3C2410_IRQ(36)

看到了吧?内核把共享的中断分离出来,只要使用这些标记就可以了。

其实上面我只是想说明:无论在硬件上ARM是怎么实现中断的(是否共享),在内核看来所有的中断都是一样的,都可以独自获得一个中断号。

第二列“11”是对应处理器响应该中断的次数。

第三列“s3c-ext0”是处理这个中断的中断控制器

第四列一看就知道调用irq_request()时定义的中断名字。

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

六、总结

其实要实现中断,大部分的工作已经给内核包了,我们只需要做的就是告诉内核,当来了什么中断要执行怎么样的函数,这也是今天介绍的重点,其实步骤很简单:

1)调用两个函数:requesr_irq和free_irq。

2)实现中断处理函数:irq_handler()。

还有没讲的知识:

1)还有几个irqflag没介绍。

2)没有介绍dev_id。

可能有人会加载上面的模块失败,这也是我今天没介绍的只是,共享中断号。这里说的共享和硬件的共享不一样性质,下节会介绍。

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/366792.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

(8)Python判断结构

转载于:https://www.cnblogs.com/hankleo/p/9170325.html

Java:本地最小语言

在1996年至2002年之间,我用Java编写了成千上万行代码。我用Java 1.0到Java 1.4编写了Web框架,电子表格以及更多内容。 与90年代中期(预模板)的C 相比,Java是一种完全令人惊奇的语言。 JVM是所有计算机语言的最佳运行时…

History of program(1950-2020)

1957年 约翰巴科斯(John Backus)创建了是全世界第一套高阶语言:FORTRAN。 John Backus1959年 葛丽丝霍普(Grace Hopper)创造了现代第一个编译器A-0 系统,以及商用电脑编程语言“COBOL”,被誉为C…

关于 Nuxt 集成ueditor的一些坑(包括图片上传)前端部分

最近公司接了一个项目,里面用到富文本编辑器,刚开始用的是vue-quill-editor,这个编辑器轻量、好用。最重要的是它有专门正对nuxt的版本,很容易配置,可以放心使用,不用担心bug之类的,遇到问题&am…

linux 线程带参数,Linux中多线程编程并传递多个参数的简单例子

今天上午实验了Linux下的多线程编程,并将多个参数传递给线程要执行的函数。以下是实验程序的源代码:/*********************** pthread.c ***************************/#include #include #include #include #include struct argument{int num;char stri…

*Codeforces989D. A Shade of Moonlight

数轴上$n \leq 100000$个不重叠的云,给坐标,长度都是$l$,有些云速度1,有些云速度-1,风速记为$w$,问在风速不大于$w_{max}$时,有几对云可能在0相遇。每一对云单独考虑。 多动一不动--相对运动。假…

undefined reference 问题各种情况分析

扒自网友文章 关于undefined reference这样的问题,大家其实经常会遇到,在此,我以详细地示例给出常见错误的各种原因以及解决方法,希望对初学者有所帮助。 1. 链接时缺失了相关目标文件(.o) 测试代码如下&a…

Spring交易可见性

在初始化应用程序上下文时,Spring遇到带有Transactional标记的类时会创建代理。 Transactional可以应用于类级别或方法级别。 在类级别应用它意味着该类中定义的所有公共方法都是事务性的。 Spring创建的代理类型,即Jdk代理或CGLIB代理,取决于…

Axios 作弊表(Cheat Sheet)

英文原文链接 GET 请求 // Make a request for a user with a given ID axios.get(/user?ID12345).then(function (response) {console.log(response);}).catch(function (error) {console.log(error);});// Optionally the request above could also be done as axios.get(…

loadrunner post请求

注意:loadrunner参数中的引号,需要自己加"\" post 请求,分为header 和body两个部分处理 header部分比较容易处理,使用函数实现,如web_add_header("pid","1")即可,具体参数可…

2018-2019-1 20165203 《信息安全系统设计基础》第六周学习总结

2018-2019-1 20165203 《信息安全系统设计基础》第六周学习总结 教材学习内容总结 重要知识点 I/O:在主存和外部设备(例如磁盘存储器、终端和网络)之间复制数据的过程。输入操作:从I/O设备复制数据到主存。输出操作:从…

linux 使用VI命令怎么删除输入内容,linux系统vi编辑器常用命令及使用方法。

在linux系统中编辑文档我们常用到vi编辑器。vi编辑器,通常称之为vi,是一种广泛存在于各种UNIX和Linux系统中的文本编辑程序。它的功能十分强大,但是命令繁多,不容易掌握,它可以执行输出、删除、查找、替换、块操作等众多文本操作&…

Spring安全–幕后

安全任务(例如,用户身份验证和用户查看应用程序资源的授权)通常由应用程序服务器处理。 可以将这些任务委托给Spring安全性流程,以减轻应用程序服务器处理这些任务的负担。 Spring安全性基本上通过实现标准javax.servlet.Filter来…

在react中使用svg的各种骚姿势

开头先抛个可供参考的项目ts-react-webpack4, 或脚手架steamer-react-ts 优势 SVG可被非常多的工具读取和修改(比如vscode)不失真, 放大缩小图像都很清晰SVG文件是纯粹的XML, 也是一种DOM结构使用方便, 设计软件可以直接导出 兼容性 上一张兼容性图表, 或到caniuse.com查看 …

chrome 浏览器的插件权限有多大?

转自:https://segmentfault.com/q/1010000003777353 1)Chrome插件本身有机制控制,不会无限制的开放很多权限给你2)页面的DOM元素时可以操作的,Chrome插件是和宿主页面之间是共享DOM对象,但是不共享Javascri…

3.2自定义方法

方法是类的一种行为,方会使我们的代码容易修改,方便阅读,实现封装和重用。比如前面使用的很多.net定义好的类的方法,当然我们也可以自定义方法。 3.2.1定义方法 语法: 访问修饰符 返回类型 方法名(参数列表) &#xff…

linux live cd ubuntu,在Windows 7上体验Ubuntu Live CD

http://download.gna.org/grub4dos/grub4dos-0.4.4.zip把grldr.mbr,grldr放到C盘根目录.注意:以管理员身份运行cmd,进行以下操作.备份/Boot/BCD:bcdedit /export "D:\BCD.backup"用bcdedit编辑启动文件/Boot/BCD添加GRUB引导项:bcdedit /create /d "GRUB" …

[Electron]仿写一个课堂随机点名小项目

自从前几个月下了抖音,无聊闲暇时就打会打开抖音,因为打开它有种莫名其妙打开了全世界的感觉... 无意中看到这个小视频:随机点名 于是仿写了一个课堂点名小项目,算是对Electron的一个简单的认识,有时间再深入。 项目…

何时以及如何使用ThreadLocal

正如我们的读者可能已经猜到的那样,我每天都在处理内存泄漏。 最近,一种特殊类型的OutOfMemoryError消息开始引起我的注意-滥用ThreadLocals触发的问题变得越来越频繁。 在查看此类泄漏的原因时,我开始相信其中一半以上是由于开发人员导致的&…

linux redis安装使用,linux安装redis

Linux(CentOS)中Redis介绍、安装、使用【一篇就够】2018-05-13 13:36:16 sjmz30071360 阅读数 1590更多分类专栏: redis版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。一、介绍Redis is…