分类: LINUX
2012-11-09 14:53:59
在内核加载模块中最重要的的action就是注册中断处理程序。很明显,这一动作是通过request_irq()函数来完成的。
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则表示设备可以被看做是事件随机的发生源。
以下是官方解释:
/*
* These flags used only by the kernel as part of the
* irq handling routines.
*
* IRQF_DISABLED - keep irqs disabled when calling the action handler
* IRQF_SAMPLE_RANDOM - irq is used to feed the random generator
* IRQF_SHARED - allow sharing the irq among several devices
* IRQF_PROBE_SHARED - set by callers when they expect sharing mismatches to occur
* IRQF_TIMER - Flag to mark this interrupt as timer interrupt
* IRQF_PERCPU - Interrupt is per cpu
* IRQF_NOBALANCING - Flag to exclude this interrupt from irq balancing
* IRQF_IRQPOLL - Interrupt is used for polling (only the interrupt that is
* registered first in an shared interrupt is considered for
* performance reasons)
*/
#define IRQF_DISABLED 0x00000020
#define IRQF_SAMPLE_RANDOM 0x00000040
#define IRQF_SHARED 0x00000080
#define IRQF_PROBE_SHARED 0x00000100
#define IRQF_TIMER 0x00000200
#define IRQF_PERCPU 0x00000400
#define IRQF_NOBALANCING 0x00000800
#define IRQF_IRQPOLL 0x00001000
第四个参数devname:是请求中断的设备的名称。当你加载模块成功后可以在/proc/interrupts中查看到具体设备的名称,与此同时也可以看到这个设备对应的中断号以及请求次数。
第五个参数dev_id:为一个指针型变量。注意该参数为void型,也就是说通过强制转换可以转换为任意类型。dev_id 主要用于共享中断线,对每个注册的中断处理程序来说, 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()..... IRQ0X1f_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)
{
printk("ISR is Working/n");
return IRQ_HANDLED;
}