驱动-linux 中断处理
/linux-2.6.35/arch/arm/mach-s5pc100/include/mach/irqs.h
中断号:
#define IRQ_UART0 S5P_IRQ_VIC1(10)
#define IRQ_UART1 S5P_IRQ_VIC1(11)
#define IRQ_UART2 S5P_IRQ_VIC1(12)
#define IRQ_UART3 S5P_IRQ_VIC1(13)
#define IRQ_IIC S5P_IRQ_VIC1(14)
#define IRQ_SPI0 S5P_IRQ_VIC1(15)
#define IRQ_SPI1 S5P_IRQ_VIC1(16)
#define IRQ_SPI2 S5P_IRQ_VIC1(17)
/linux-2.6.35/include/linux/interrupt.h
中断触发方式:
#define IRQF_TRIGGER_NONE 0x00000000
#define IRQF_TRIGGER_RISING 0x00000001
#define IRQF_TRIGGER_FALLING 0x00000002
#define IRQF_TRIGGER_HIGH 0x00000004
#define IRQF_TRIGGER_LOW 0x00000008
/linux-2.6.35/include/linux/interrupt.h
中断类型:
#define IRQF_DISABLED 0x00000020
#define IRQF_SAMPLE_RANDOM 0x00000040
#define IRQF_SHARED 0x00000080
IRQF_DISABLED 一旦进入中断不会再响应其他中断
IRQF_DISABLED 一旦进入中断不会再响应其他中断,且有用随机数生成器时设定。
IRQF_SHARED 进入中断后,还可以响应其他中断,中断共享。
申请IRQ: ( 外部中断:IRQ_EINT(N) )
注册中断号,注册中断处理函数。。。。。
request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev)
1>:irq:表示申请的硬件中断号。
2>:handler:表示中断服务例程,中断处理函数名
关于中断处理函数的返回值:中断程序的返回值是一个特殊类型—irqreturn_t。中断程序的返回值却只有两个—IRQ_NONE和IRQ_HANDLED。
早期内核中断处理函数三个参数
typedef irqreturn_t (*irq_handler_t)(int,void *, struct pt_regs *)
handler(int irq,void *dev_id, struct pt_regs *regs)
struct pt_regs *regs :它指向一个数据结构,此结构保存的是中断之前处理器的寄存器和状态。主要用在程序调试。
现在 内核中断处理函数两个参数
typedef irqreturn_t (*irq_handler_t)(int,void *)
handler(int irq,void *dev_id)
Handler是向系统登记的中断处理函数。这是一个回调函数,中断发生时,系统调用这个函数,传入的参数包括硬件中断号,device id,寄存器值。dev_id就是request_irq时传递给系统的参数。。
返回值若是IRQ_NONE表示无效, 为 IRQ_HANDLED 正常返回。。。
irqreturn_t是这么来的。。。:
enum irqreturn {
IRQ_NONE,
IRQ_HANDLED,
IRQ_WAKE_THREAD,
};
typedef enum irqreturn irqreturn_t;
irq是要申请的硬件中断号。在Intel平台,范围0--15。
3>:flags:表示中断处理的一些属性。可以是中断类型、中断触发方式。
4>:name:表示请求中断的设备的名称。
5>:dev_id: 对应于request_irq()函数中所传递的第五个参数,dev_id在中断共享时会用到。一般设置为这个设备的device结构本身或者NULL。但必须唯一能够代表发出中断请求的设备, free_irq()时所用
中断共享时,识别是哪个中断,有硬件完成,通过中断状态寄存器。。。。
安装中断处理函数: 可以再驱动程序初始化时安装,也可在用户打开设备时再安装。。。。
注意中断处理函数应1、尽快返回(若处理程序长了,怎么办?所以引入了 中断底半部机制 )
2、不能有可能产生调度的语句。。
释放IRQ:
Void free_irq(unsigned int irq,void *dev_id);
使能和屏蔽中断:
3个函数作用于可编程中断控制器。。。
extern void enable_irq(unsigned int irq);
extern void disable_irq(unsigned int irq); //等待目前的中断处理完成。。再屏蔽。
extern void disable_irq_nosync(unsigned int irq); //尝试去屏蔽,立即返回
中断处理程序描述符irqaction(include/linux/interrupt.h)
struct irqaction {
irq_handler_t handler;
unsigned long flags;
const char *name;
void *dev_id;
struct irqaction *next;
int irq;
struct proc_dir_entry *dir;
irq_handler_t thread_fn;
struct task_struct *thread;
unsigned long thread_flags;
};
IRQ描述符irq_desc(include/linux/irq.h)
对于每个IRQ中断线,Linux都用一个irq_desc_t数据结构来描述,我们把它叫做IRQ描述符,NR_IRQS个IRQ形成一个全局数组irq_desc[],其定义在/include/linux/irq.h中:
struct irq_desc {
void fastcall (*handle_irq)(unsigned int irq,
struct irq_desc *desc,
struct pt_regs *regs);
struct irq_chip *chip;
void *handler_data;
void *chip_data;
struct irqaction *action;
unsigned int status;
unsigned int depth;
unsigned int wake_depth;
unsigned int irq_count;
unsigned int irqs_unhandled;
spinlock_t lock;
#ifdef CONFIG_SMP
cpumask_t affinity;
unsigned int cpu;
#endif
#if defined(CONFIG_GENERIC_PENDING_IRQ) || defined(CONFIG_IRQBALANCE)
cpumask_t pending_mask;
unsigned int move_irq;
#endif
#ifdef CONFIG_PROC_FS
struct proc_dir_entry *dir;
#endif
} ____cacheline_aligned;
extern struct irq_desc irq_desc[NR_IRQS];
handle_irq:上层的通用中断处理函数指针,如果未设置则默认为__do_IRQ()。通常针对电平触发或者边沿触发有不同的处理函数。每个中断线可分别设置,该函数负责使用chip中提供的特定于控制器的方法,进行处理终端所必须的一些底层操作。
chip:指向irq_chip的指针,初始化默认是no_irq_chip;
handler_data:附加参数,用于handle_irq,特定于处理程序;
chip_data:平台相关的附加参数,用于chip;
action:指向 struct irqaction 结构组成的队列的头,正常情况下每个irq只有一个操作,因此链表的正常长度是1或0。但是,如果IRQ被两个或多个设备所共享,那么这个队列就有多个操作了
status:中断线状态;
depth:如果启用这条IRQ中断线,depth则为0;如果禁用这条IRQ中断线不止一次,则为一个正数。如果depth等于0,每当调用一次disable_irq( ),该函数就对这个域的值加1,同时该函数就禁用这条IRQ中断线。相反,每当调用enable_irq( )函数时,该函数就对这个域的值减1;如果depth变为0,该函数就启用这条IRQ中断线。
lock:用于串行访问IRQ描述符和PIC的自旋锁
irq_count: 统计IRQ线上发生中断的次数(诊断时使用)
irq_unhandled:对在IRQ线上无法处理的中断进行计数(仅在诊断时使用)。当100000次中断产生时,如果意外中断次数超过99900,内核禁用这条IRQ线。
“____cacheline_aligned”表示这个数据结构的存放按32字节(高速缓存行的大小)进行对齐,以便于将来存放在高速缓存并容易存取
linux 中断处理
1.Linux中断的注册与释放:
在<linux/interrupt.h>, , 实现中断注册接口:
int request_irq(unsigned int irq,
irqreturn_t (*handler)(int, void *,
struct pt_regs *),
unsigned long flags,
const char *dev_name,
void *dev_id);
void free_irq(unsigned int irq, void *dev_id);
函数参数说明
unsigned int irq:所要注册的中断号
irqreturn_t (*handler)(int, void *, struct pt_regs *):中断服务程序的入口地址。
unsigned long flags:与中断管理有关的位掩码选项,有三组值:
1. SA_INTERRUPT :快速中断处理程序,当使用它的是后处理器上所有的其他中断都被禁用。
2. SA_SHIRQ :该中断是在设备之间可共享的
3. SA_SAMPLE_RANDOM :这个位表示产生的中断能够有贡献给 /dev/random
和 /dev/urandom 使用的加密池.(此处不理解)
const char *dev_name:设备描述,表示那一个设备在使用这个中断。
void *dev_id:用作共享中断线的指针. 它是一个独特的标识, 用在当释放中断线时以及可能还被驱动用来指向它自己的私有数据区(来标识哪个设备在中断) 。这个参数在真正的驱动程序中一般是指向设备数据结构的指针.在调用中断处理程序的时候它就会传递给中断处理程序的void *dev_id。(这是我的理解)如果中断没有被共享, dev_id 可以设置为 NULL, 但是使用这个项指向设备结构不管如何是个好主意. 我们将在"实现一个处理"一节中看到 dev_id 的一个实际应用。
中断号的查看可以使用下面的命令:“cat /proc/interrupts”。
/proc/stat 记录了几个关于系统活动的低级统计量, 包括(但是不限于)自系统启动以来收到的中断数. stat 的每一行以一个文本字串开始, 是该行的关键词; intr 标志是我们在找的.
第一个数是所有中断的总数, 而其他每一个代表一个单个 IRQ 线, 从中断 0 开始. 所有的计数跨系统中所有处理器而汇总的. 这个快照显示, 中断号 4 已使用 1 次, 尽管当前没有安装处理. 如果你在测试的驱动请求并释放中断在每个打开和关闭循环, 你可能发现 /proc/stat 比 /proc/interrupts 更加有用.