linux驱动开发中断例程学习
来源:互联网 发布:商业台账软件 编辑:程序博客网 时间:2024/05/23 15:38
原文:你的第一个中断程序!
作者: edsionte
地址:http://edsionte.com/techblog/archives/1521
之前在学习中断知识时,都是概念,理解的不是很好,虽然使用时钟中断写了一个例程,但是终究不是主要关于中断的程序,所以在就在网上找啊找啊,终于发现了这个大神的博文,很是经典啊,博客里还有其他的关于linux内核的文章,大家有学习兴趣的都可以去看看,
本文通过一个简单的中断程序来描述一般中断程序的基本框架。完整代码在此处。
代码需要修改一下,init()函数和exit()的传入参数需要追加void,
修改后为
static int __init myirq_init(void)
static void __exit myirq_exit(void)
中断程序一般会包含在某个设备的驱动程序中,因此,接下来的程序本质上还是一个内核模块。说到内核模块,你应该知道首先去看什么了吧?对了,就是内核模块加载函数。
01
static
int
__init myirq_init()
02
{
03
printk(
"Module is working..\n"
);
04
if
(request_irq(irq,myirq_handler,IRQF_SHARED,devname,&mydev)!=0)
05
{
06
printk(
"%s request IRQ:%d failed..\n"
,devname,irq);
07
return
-1;
08
}
09
printk(
"%s rquest IRQ:%d success..\n"
,devname,irq);
10
return
0;
11
}
在内核加载函数中,我们除了显示一些信息外,最重要的工作就是申请一根中断请求线,也就是注册中断处理程序。很明显,这一动作是通过request_irq函数来完成的。这个函数的原型如下:
1
static
int
request_irq(unsigned
int
irq, irq_handler_t handler, unsigned
long
flags,
const
char
*name,
void
*dev);
第一个参数是中断号,这个中断号对应的就是中断控制器上IRQ线的编号。
第二个参数是一个irq_handler_t类型个函数指针:
1
typedef
irqreturn_t (*irq_handler_t)(
int
,
void
*);
handler所指向的函数即为中断处理程序,需要具体来实现。
第三个参数为标志位,可以取IRQF_DISABLED、IRQF_SHARED和IRQF_SAMPLE_RANDOM之一。在本实例程序中取IRQF_SHARED,该标志表示多个设备共享一条IRQ线,因此相应的每个设备都需要各自的中断服务例程。一般某个中断线上的中断服务程序在执行时会屏蔽请求该线的其他中断,如果取IRQF_DISABLED标志,则在执行该中断服务程序时会屏蔽所有其他的中断。取IRQF_SAMPLE_RANDOM则表示设备可以被看做是事件随见的发生源。
第四个参数是请求中断的设备的名称。可以在/proc/interface中查看到具体设备的名称,与此同时也可以查看到这个设备对应的中断号以及请求次数,甚至中断控制器的名称。
第五个参数为一个指针型变量。注意此参数为void型,也就是说通过强制转换可以转换为任意类型。这个变量在IRQF_SHARED标志时使用,目的是为即将要释放中断处理程序提供唯一标志。因为多个设备共享一条中断线,因此要释放某个中断处理程序时,必须通过此标志来唯一指定这个中断处理程序。习惯上,会给这个参数传递一个与设备驱动程序对应的设备结构体指针。关于中断程序,可参考这里的文章。
以上就是request_irq函数各个参数的意义。
与中断处理程序的注册相对应的是free_irq函数,它会注销相应的中断处理程序,并释放中断线。这个函数一般被在内核模块卸载函数中被调用。
1
static
void
__exit myirq_exit()
2
{
3
printk(
"Module is leaving..\n"
);
4
free_irq(irq,&mydev);
5
printk(
"%s request IRQ:%d success..\n"
,devname,irq);
6
}
如果该中断线不是共享的,那么该函数在释放中断处理程序的同时也将禁用此条中断线。如果是共享中断线,只是释放与mydev对应的中断处理程序。除非该中断处理程序恰好为该中断线上的最后一员,此条中断线才会被禁用。在此处,你也可以感受到mydev的重要性。
下面具体分析中断处理函数。该函数的功能很简单,只是显示一些提示信息。
01
static
irqreturn_t myirq_handler(
int
irq,
void
* dev)
02
{
03
struct
myirq mydev;
04
static
int
count=1;
05
mydev=*(
struct
myirq*)dev;
06
printk(
"key: %d..\n"
,count);
07
printk(
"devid:%d ISR is working..\n"
,mydev.devid);
08
printk(
"ISR is leaving..\n"
);
09
count++;
10
return
IRQ_HANDLED;
11
}
另外,本内核模块在插入时还需要附带参数,下面的语句首先定义两个参数,然后利用宏module_param宏来接受参数。
1
static
int
irq;
2
static
char
* devname;
3
4
module_param(devname,charp,0644);
5
module_param(irq,
int
,0644);
使用方法:
1.通过cat /proc/interrupts查看中断号,以确定一个即将要共享的中断号。本程序因为是与键盘共享1号中断线,因此irq=1;
说明:这个键盘中断有待研究,我的台式机使用USB键盘,死活不能出现key:xxx的情报,问了一下其他大神,说什么软中断/硬中断的原因。
后来拿到我的笔记本虚拟机上运行,OK,没有问题,准确的出现log,证明中断函数被正确执行了。
2.使用如下命令就可以插入内核:
sudo insmod filename.ko irq=1 devname=myirq
3.再次查看/proc/interrupts文件,可以发现1号中断线对应的的设备名处多了myirq设备名;
4.dmesg查看内核日志文件,可看到在中断处理程序中所显示的信息;
[ 683.910314] tasklet is wroking..
[ 684.012115] key:157..
[ 684.012126] devid:1119 ISR is working..
[ 684.012128] Bottom half will be working..
[ 684.012131] ISR is leaving..
[ 684.012148] tasklet is wroking..
[ 684.176827] key:158..
[ 684.176841] devid:1119 ISR is working..
[ 684.176843] Bottom half will be working..
[ 684.176845] ISR is leaving..
[ 684.176865] tasklet is wroking..
[ 684.265099] key:159..
[ 684.265102] devid:1119 ISR is working..
[ 684.265103] Bottom half will be working..
[ 684.265103] ISR is leaving..
[ 684.265106] tasklet is wroking..
[ 686.885017] key:160..
[ 686.885024] devid:1119 ISR is working..
[ 686.885025] Bottom half will be working..
[ 686.885027] ISR is leaving..
5.卸载内核模块;
可以看到,内核模块加载后,我们所写中断处理程序是被自动调用的,主要是因为该中断线上有键盘所发出的中断请求,因此内核会执行该中断线上的所有中断处理程序,当然就包括我们上述所写的那个中断处理程序。关于中断处理程序的执行,可参考这里的文章。
这样,一个最基本的中断程序就编写完成了!try!
- linux驱动开发中断例程学习
- linux驱动开发--中断:按键中断
- Hasen的linux设备驱动开发学习之旅--中断
- linux设备驱动开发学习之旅--中断
- linux驱动开发之入门例程
- 中断服务例程 学习
- Linux驱动学习之:按键中断驱动
- Linux内核与驱动开发学习总结:中断和中断处理(八)
- 嵌入式学习-驱动开发-lesson3-混杂设备驱动模型与linux中断处理流程
- 《Linux设备驱动开发详解》-- 中断屏蔽
- Linux驱动开发二:外部中断分析
- linux驱动开发--中断:tasklet实现中断底半部
- linux驱动开发--中断:工作者队列实现中断底半部
- Linux驱动开发-8、中断分层机制_软中断
- linux驱动开发学习--对中断和内核定时器的学习笔记
- WINCE 6.0中断计数流驱动例程
- Linux驱动学习——中断、定时器
- s5pv210开发板 sin210 linux 例程 LED 驱动
- HDU--5389(01背包)
- Git最快捷的方法. 自家用
- AndroidManifest.xml文件详解(service)
- java.util.ConcurrentModificationException
- UDP协议
- linux驱动开发中断例程学习
- 黑马程序员-----------C语言基础-----------循环结构
- Laravel 5基础教程 || 1.安装与部署 - 表严肃
- zoj 2100 seeding(dfs)
- Android-应用开发-网络编程(四)
- A20开发之cubie2 编译过程详解(一)
- hdu5387 Clock
- MFC的CRichEditCtrl控件,用了函数ReplaceSel(str);怎样才能取消全部选中?
- 相册 可以上下滑动选择