Linux按键驱动程序设计(2)-Linux中断处理程序

写过按键裸机程序的同学都知道按键的读取一般都采用中断的方式,如果采用轮询的方式真的是太浪费CPU资源了。下面将介绍Linux中的中断处理程序。


1、裸机中断处理流程回顾

对所有芯片而言,中断都有一个通用的入口,进入后会保存保存当前的环境把它压栈,然后查询中断向量表,根据中断源去调用对应的中断处理程序。
因此总结出以下3点:
1、中断统一入口
2、事先注册中断处理程序
3、根据中断源编号查询中断向量表,调用中断处理程序


2、Linux中断处理流程分析

在Linux内核代码里也有统一的入口,这个函数叫做__irq_svc函数,这个函数可以在内核代码的entry-armv.S文件中找到。

这个函数保存现场之后会调用一个irq_handler的宏,

/*
 * Interrupt handling.  Preserves r7, r8, r9
 */
	.macro	irq_handler
#ifdef CONFIG_MULTI_IRQ_HANDLER
	ldr	r5, =handle_arch_irq
	mov	r0, sp
	ldr	r5, [r5]
	adr	lr, BSYM(9997f)
	teq	r5, #0
	movne	pc, r5
#endif
	arch_irq_handler_default

irq_handle这个宏又由arch_irq_default这个宏完成,找出这个宏,它在entry-macro-multi.S中实现。

/*
 * Interrupt handling.  Preserves r7, r8, r9
 */
	.macro	arch_irq_handler_default
	get_irqnr_preamble r5, lr
1:	get_irqnr_and_base r0, r6, r5, lr
	movne	r1, sp
	@
	@ routine called with r0 = irq number, r1 = struct pt_regs *
	@
	adrne	lr, BSYM(1b)
	bne	asm_do_IRQ

arch_irq_default函数完成中断向量表的查询

	.macro	get_irqnr_and_base, irqnr, irqstat, base, tmp

		mov	\base, #S3C24XX_VA_IRQ

		@@ try the interrupt offset register, since it is there

		ldr	\irqstat, [ \base, #INTPND ]
		teq	\irqstat, #0
		beq	1002f
		ldr	\irqnr, [ \base, #INTOFFSET ]
		mov	\tmp, #1
		tst	\irqstat, \tmp, lsl \irqnr
		bne	1001f

		@@ the number specified is not a valid irq, so try
		@@ and work it out for ourselves

		mov	\irqnr, #0		@@ start here

		@@ work out which irq (if any) we got

总结如下:

1、进入同一的入口

2、拿到产生中断源的编号

3、根据中断号找到irq_desc结构,这个结构中有一个action选项,这个选项中填写的就是用户注册的中断服务器函数。

3、Linux中断处理程序设计

由上面的分析可以知道,对应驱动程序的中断函数需要完成:
1、中断服务函数的编写
2、在内核中注册中断服务函数
3、驱动不用时注销中断服务函数

3.1中断注册

request_irq函数用于注册中断。
int request_irq(unsigned int irq,
           void (*handler)(int, void*, structpt_regs *),
           unsigned long flags,
           const char *devname,
           void *dev_id)
返回值:
返回0表示成功,或者返回一个错误码

参数:
unsigned int irq                   中断号。
void (*handler)(int,void *)  中断处理函数。
unsigned long flags           与中断管理有关的各种选项。
const char * devname       设备名
void *dev_id                         共享中断时使用。

在flags参数中,可以选择一些与中断管理有关的选项,如:
• IRQF_DISABLED(SA_INTERRUPT) 如果设置该位,表示是一个“快速”中断处理程序;如果没有设置这位,那么是一个“慢速”中断处理程序。
• IRQF_SHARED(SA_SHIRQ)              该位表明该中断号是多个设备共享的。比如串口和网卡都可以使用这个中断号,它们挂在一个链表下,Linux检测到这个中断号时会把每个中断服务函数都调用一遍,因此在中断服务函数中需要判断自己是否产生了中断,如果没有产生需要退出。

快/慢速中断的主要区别在于:快速中断保证中断处理的原子性(不被打断),而慢速中断则不保证。换句话说,也就是“开启中断”标志位(处理器IF)在运行快速中断处理程序时是关闭的,因此在服务该中断时,不会被其他类型的中断打断;而调用慢速中断处理时,其它类型的中断仍可以得到服务。

3.2中断处理程序

中断处理程序的特别之处是在中断上下文中运行的,它的行为受到某些限制:
1.不能使用可能引起阻塞的函数    如果阻塞了所有的中断都不能得到及时的处理
2.不能使用可能引起调度的函数

中断处理程序编写流程:
1、检查设备是否产生中断
2、清楚中断标志位
3、完成相应的硬件操作

3.3注销中断

当设备不再需要使用中断时(通常在驱动卸载时), 应当把它们注销, 使用函数:
void free_irq(unsigned int irq, void *dev_id)
这里有2个参数,中断号和dev_id,由于共享中断中不能直接注销中断号,而是注销dev_id。

3.4代码编写

在上面的框架中实现中断处理程序。
1、中断服务函数的编写
2、在内核中注册中断服务函数
3、驱动不用时注销中断服务函数
如果有函数不知道怎么使用可以参考内核源代码。整体的框架如下:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/miscdevice.h>

/*按键中断服务函数*/
irqreturn_t key_interupter(int irq, void *dev_id)
{
	//检测设备是否产生中断

	//清除中断标志位

	//打印信息
}

/*设备文件打开操作*/
int key_open(struct inode *node, struct file *filp)
{
	return 0;
}

/*初始化文件操作函数*/
struct file_operations key_fops
{
	.open = key_open;
};

/*初始化设备文件*/
struct miscdevice key_miscdev{
	.minor = 200;
	.name = "key"
	.fops = &key_fops
};

/*模块初始化函数*/
static int key_init(void)
{
	/*注册混杂设备*/
	misc_register(&key_miscdev);

	/*注册中断函数*/
	request_irq(irqno?, key_interupter, IRQF_SHARED, "key", 0);
	return 0;
}

static void key_exit(void)
{
	/*注销设备*/
	misc_deregister(&key_miscdev);

	/*注销中断服务程序*/
	free_irq(irqno, 0);
}


MODULE_LICENSE("GPL");

module_init(key_init);
module_exit(key_exit);



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值