Int request_irq (unsigned int irq, irq_handler_t handler, unsigned long flags, const char *devname, void *dev_id)
A.先来分析形参:
第一个参数irq: 表示要分配的中断号。对于一些设备(系统时钟或键盘)它的值是预先固定的,而对于大多数设备来说,这个值是动态确定的。
第二个参数 handler: 表示要挂入到中断请求对列中的中断服务例程, 这个中断服务函数的原型是static irqreturn_t handler(int , void *);
中断处理程序的前缀为static,因为它从来不会被别的文件中的代码直接调用。
第三个参数flags:为标志位。可以取IRQF_DISABLED、IRQF_SHARED和IRQF_SAMPLE_RANDOM之一。在本实例程序中取 IRQF_SHARED,该标志表示多个中断处理程序共享irq中断线。一般某个中断线上的中断服务程序在执行时会屏蔽请求该线的其他中断,如果取 IRQF_DISABLED标志,则在执行该中断服务程序时会屏蔽所有其他的中断。取IRQF_SAMPLE_RANDOM则表示设备可以被看作是事件随见的发生源。
第四个参数devname:是请求中断的设备的名称。当你加载模块成功后可以在/proc/interrupts中查看到具体设备的名称,与此同时也可以看到这个设备对应的中断号以及请求次数。
第五个参数dev_id:为一个指针型变量。注意该参数为void型,也就是说通过强制转换可以转换为任意类型。dev_id主要用于共享中断线,对每个注册的中断处理程序来说,( Dev_id must be globally unique. Normally the address of the
B:函数返回值
requset_irq()函数成功执行后返回0。如果返回非0值,就表示错误发生。此时,指定的中断处理程序不会被注册。
这里面有几个疑问:
为什么要注册中断函数?????
共享中断线的概念,参数dev_id的作用是什么????
看一个图进行说明:
1>由图可知:有16个中断线。要使用中断线,就要进行中断线的申请也常把申请一条中断线称为申请一个中断号,这就与request_irq()函数中的第一个形参 irq 有关系 。
2>Linux有256个中断向量,而外部中中断向量只有16个(32~47)。由于硬件上的限制,很多外部设备不得不共享中断线。
(例如:一些PC机所用的网卡和图形卡可以把它们分配到一条中断线上)
让每个中断源独自占用一条中断线是不实现的。
3>共享中断线的话虽然解决了中断资源的问题,但是,此时引出了另一个问题( 任何事物都有其两面性 ),此时仅仅用中断描述符并不能提供中断产生的所有信息。为了解决这个问题,内核必须对中断线给出近一步的描述,所以在Linux设计中,为每个中断请求IRQ设置了一个专用队列(中断请求队列) 。
4>中断服务程序和中断处理程序的区别:
a.中断服务例程(interrupt service routine):
Linux中,15条中断线对应15个中断处理程序,依次命名是IRQ0x00_interrupt(),IRQ0x01_interrupt()..... IRQ0X0f_interrupt().
中断处理程序相当于某个中断向量的总处理程序。
eg:IRQ0X05_interupt()是5号中断(向量为37)的总处理程序。
中断向量:中断服务程序的入口地址
b.中断服务例程是针对一个具体设备的中断。
5>.注册中断服务例程:
在IDT表完成初始化时,每个中断服务队列还为空。此时即使打开中断且某个外设的中断真的发生了,也得不到实际的服务。因为CPU虽然通过中断门进入了某个中断向量的总处理程序。但是,具体的中断服务例程还没有挂入中断请求队列。所以,在设备驱动程序的初始化阶段,必须通过request_irq()函数将响应的中断服务例程挂入中断请求队列,也就是进行注册。
6>分析一下中断服务程序,即request_irq()函数中第二个参数所对应的函数
Static irqreturn_t myirq_handler (int irq, void *dev_id)
{
}
中断服务例程的形参:
A. int irq :中断号。
B. void *dev_id :与request_irq () 的参数dev_id一致,可以根据这个设备id号得到相应设备的数据结构,进而得到相应设备的信息和相关数据。
C.返回值:中断程序的返回值是一个特殊类型 rqreturn_t。但是中断程序的返回值却只有两个值IRQ_NONE和IRQ_HANDLED。
IRQ_NONE:中断程序接收到中断信号后发现这并不是注册时指定的中断源发出的中断信号。
IRQ_HANDLED:接收到了准确的中断信号,并且作了相应正确的处理。
一般 中断处理程序要做什么service,主要取决于产生的设备和该设备为什么要发送中断。
1.当一个给定的中断处理程序正在执行时,这条中断线上的其它中断都会被屏蔽。but,所有其他中断线上的中断都是打开的。因此这些不同中断线上的其他中断都能被处理。
2.request_irq()函数可能会睡眠,所以,不能在中断上下文或其它不允许阻塞的代码中调用该函数。
4.在深入分析request_irq()函数之前,先来看几个重要的数据结构。
A.
113
114
115
116
117
118
119
120
121
122
123
124
1>handler:指向具体的一个中断服务例程。
2>flags:表示中断标志位,对应于request_irq()函数中所传递的第三个参数,可取IRQF_DISABLED、IRQF_SAMPLE_RANDOM和IRQF_SHARED其中之一。
3>name:请求中断的设备名称,对应request_irq()函数中所传递的第四个参数
4>dev_id: 共享中断时有用。 对应于request_irq()函数中所传递的第五个参数,可取任意值,但必须唯一能够代表发出中断请求的设备,通常取描述该设备的结构体。
5>struct irqaction *next:指向irqaction描述符的下一个元素。用一条链表将共享同一条中断线上的中断服务例程链接起来。
6>irq:所申请的中断号
7>dir:指向proc/irq/NN/name entry
8>thread_fn:指向具体的一个线程化的中断。
9>thread:指向线程中断的指针。
10>thread_flags:线程中断的标志。
B.
每个中断向量都有它自己的irq_desc 描述符。即用irq_desc来描述中断向量。所有的这些中断描述符组织在一起就形成了irq_desc irq_desc[NR_IRQS]数组
175struct irq_desc {
176
177
178
179#ifdef CONFIG_INTR_REMAP
180
181#endif
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196#ifdef CONFIG_SMP
197
198
199
200#ifdef CONFIG_GENERIC_PENDING_IRQ
201
202#endif
203#endif
204
205
206#ifdef CONFIG_PROC_FS
207
208#endif
209
210} ____cacheline_internodealigned_in_smp;
211
212extern void arch_init_copy_chip_data (struct irq_desc *old_desc,
213
214extern void arch_free_chip_data (struct irq_desc *old_desc, struct irq_desc *desc);
215
216#ifndef CONFIG_SPARSE_IRQ
217extern struct irq_desc irq_desc[NR_IRQS];
1>irq:表示这个描述符所对应的中断号。
2>handle_irq:指向该IRQ线的公共服务程序(即该IRQ所对应的中断处理程序。
3>chip:它是一个struct irq_chip类型的指针,是中断控制器的描述符 。在2.6以前的版本中它是hw_irq_controller。
4>handler_data:是handler_irq的参数。
5>chip_data:是指向irq_chip的指针。
6>action:一个struct irqaction类型的指针,它指向一个单链表。该链表是由该中断线上所有中断服务例程链接起来的。
7>status:表示中断线当前的状态。
8>depth:中断线被激活时,值为0;当值为正数时,表示被禁止的次数
。
9>irq_count:表示该中断线上发生中断的次数
10>irqs_unhandled:该IRQ线上未处理中断发生的次数
11>name:申请中断设备的名字。
C.
struct irq_chip是一个中断控制器的描述符。Linux支持N种可编程中断控制器PIC(中断控制器),通常不同的体系结构就有一套自己的中断处理方式。内核为了统一的处理中断,提供了底层的中断处理抽象接口,对于每个平台都需要实现底层的接口函数。这样对于上层的中断通用处理程序就无需任何改动。
struct irq_chip的具体代码如下:
111struct irq_chip {
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135#ifdef CONFIG_IRQ_RELEASE_METHOD
136
137#endif
138
142
143};
name:中断控制器的名字;
Startup:启动中断线;
Shutdown:关闭中断线;
Enable:允许中断;
Disable:禁止中断;
分析了struct irq_desc, struct irq_chip和irqaction的数据结构之后我们来看看他们之间的关系 。
现在深入分析request_irq()内部是如何实现的。
135request_irq (unsigned int irq, irq_handler_t handler, unsigned long flags,
136
137{
138
139}
可以看到request_irq()函数里面有封装了request_threaded_irq(irq, handler, NULL, flags, name, dev)函数。
5.首先分析request_threaded_irq()函数中的各个形参
1>:irq:表示申请的中断号。
2>:handler:表示中断服务例程
3.> thread_fn:中断线程化,此处传递的是NULL。NULL表示没有中断线程化。
此参数是最新版本中才出现的。为什么要提出中断线程化?
在 Linux 中,中断具有最高的优先级。不论在任何时刻,只要产生中断事件,内核将立即执行相应的中断处理程序,等到所有挂起的中断和软中断处理完毕后才能执行正常的任务,因此有可能造成实时任务得不到及时的处理。中断线程化之后,中断将作为内核线程运行而且被赋予不同的实时优先级,实时任务可以有比中断线程更高的优先级。这样,具有最高优先级的实时任务就能得到优先处理,即使在严重负载下仍有实时性保证。but,并不是所有的中断都可以被线程化,比如时钟中断,主要用来维护系统时间以及定时器等,其中定时器是操作系统的脉搏,一旦被线程化,就有可能被挂起,这样后果将不堪设想,所以不应当被线程化。
4>.irqflags:表示中断标志位。
5>.devname:表示请求中断的设备的名称。
6>.dev_id: 对应于request_irq()函数中所传递的第五个参数,可取任意值,但必须唯一能够代表发出中断请求的设备,通常取描述该设备的结构体。 共享中断时所用。
现在继续迭代深入 request_threaded_irq()内部是如何实现的。
1049int request_threaded_irq(unsigned int irq, irq_handler_t handler,
1050
1051
1052{
1053
1054
1055
1056
1057
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096#ifdef CONFIG_DEBUG_SHIRQ
1097
1098
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114#endif
1115
1116}
1049int request_threaded_irq(unsigned int irq, irq_handler_t handler,
1050
1051
1052{
1053
1054
1055
1056
1057
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096#ifdef CONFIG_DEBUG_SHIRQ
1097
1098
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114#endif
1115
1116}
程序的第一行和第二行分别定义了:
(1) struct irqaction *action;
(2)2struct irq_desc *desc;
两个指针action和desc,它们分别指向了结构体irqaction和 irq_desc。
(3)
作用是:判断中断标志位,如果是共享中断的话就必须要有一个唯一的dev_id,否则返回一个错误。
(4)
irq_to_desc(irq):根据中断号irq在 irq_desc[NR_IRQS]数组中 返回一个具体的irq_desc。即根据irq找到它的中断处理程序。
(5)
当返回一个空值时返回一个错误。说明申请中断号失败。
(6)if (desc->status & IRQ_NOREQUEST)
判断中断线的状态,若为IRQ_NOREQUEST时( IRQ_NOREQUEST表示 IRQ 不能被申请)
(7)
判断中断服务例程是否为空,如果handler为空,则判断线程中断服务例程,若线程中断服务例程也为空,则返回一个错误值。否则中断服务例程指向: rq_default_primary_handler。
(8)
1079
1080
1081
1082
1083
1084
1085
1086
1087
从1079~1087:根据requst_irq()函数中传递的参数生成一个irqaction.
1097
1098
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1097~1113:如果为共享中断的话,在执行中断服务例程之前,要先把这条中断线上的中断屏蔽,让后在执行,执行完之后打开中断。
6.有注册中断服务函数,那必然有相应的释放中断函数。
可以调用void free_irq(unsigned int irq, void *dev_id)来释放我们申请的中断线。
函数形参:
1>unsigned int riq:表示申请的中断号与request_irq()函数中的第一个形参对应。
2>void *dev_id:与request_irq()函数中的最后一个形参含义和用法相同,在此不再说明。
函数功能:
如果指定的中断线不是共享的,那么,该函数删除处理程序的同时将禁用这条中断线。如果中断线是共享的,则仅删除dev_id所对应的处理程序,而这条中断线本身只有在删除了最后一个处理程序时才会被禁止。
切记:This function must not be called from interrupt context
freee_irq()函数不能在中断上下文中被调用。
3>深入分析下free_irq()函数内部是如何实现的
1000
1001
1002
1003}
1000
1001
1002
1003}
可以看到free_irq()函数了封装了_free_irq(irq,dev_id)函数。
free_irq()调用_free_irq()把每一个具体的中断服务例程()释放。
转自http://blog.sina.com.cn/s/blog_aafd61a201018r0h.html