Linux 中断机制

1. 中断处理程序架构
中断服务程序的执行不存在于进程上下文,要求中断服务程序的时间短,内核中对时钟的处理也采用中断方式
中断机制提供了硬件和软件之间异步传递信息的方式,硬件设备在发生某个事件时通过中断通知软件进行处理
中断实现了硬件设备按需获得处理器关注的机制,与查询方式相比可以大大节省CPU时间
中断处理程序可以分成两部分上半部是实际ISR,在中断发生时被调用。
下半部是tasklet或work_queue,被上半部调度,在稍后的安全时间被调用


顶半部完成比较紧急的功能,(简单地读取寄存器中的中断状态并清除中断标志),执行速度快。
底半部可以被中断打断,完成绝大多数费时的任务(底半部机制有tasklet, work_queue)


2. 申请IRQ
request_irq()函数来注册中断服务函数,Linux2.6内核所需包含的头文件是#include <linux/interrupt.h>
定义如下
request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
   const char *name, void *dev)
{
return request_threaded_irq(irq, handler, NULL, flags, name, dev);
}


参数:
@irq:是要申请的硬件中断号
@handler:向系统登记的中断处理函数(顶半部),是一个回调函数,中断发生时,系统调用这个函数
dev参数将传给它。
@flags:中断处理属性在<linux/interrupt.h>中有定义
#define IRQF_DISABLED 0x00000020
表明中断处理程序是快速处理程序,被调用时屏掉所有中断
#define IRQF_SHARED 0x00000080
表示多个设备共享中断,参数dev在中断共享时会用到。
@name:设备驱动的名称用在/proc/interrupt系统文件上


3. 释放IRQ
void free_irq(unsigned int irq, void *dev_id)


4. 中断底半部机制
Linux实现底半部的机制主要有tasklet、workqueue、soft_irq
1)tasklet
每个tasklet都和一个函数相关联。当tasklet被运行时,该函数就被调用,并且tasklet可以调度自己
I)定义一个tasklet 结构
struct tasklet_struct keyboard_tasklet;
II)定义一个tasklet处理函数
static void keyboard_func(unsigned long);
III)tasklet结构与函数绑定
static DECLARE_TASKLET(keyboard_tasklet, keyboard_func, data);
传入这个函数的参数为data
IV)调度tasklet

tasklet_schedule(&keyboard_tasklet);

tasklet使用模板

/**定义tasklet和底半部函数并关联*/
struct tasklet_struct xxx_tasklet;
void xxx_do_tasklet(unsigned long);
DECLARE_TASKLET(xxx_tasklet, xxx_do_tasklet, 0);

/**中断处理底半部*/
void xxx_do_tasklet(unsigned long)
{
	
}

/**中断处理顶半部*/
irqreturn_t xxx_interrupt(int irq, void *dev_id)
{
	...
	taklet_schedule(&xxx_tasklet);
	...
	
	return IRQ_HANDLED;
}

/**设备驱动模块加载函数*/
int __init xxx_init(void)
{
	...
	/**申请中断*/
	result = request_irq(xxx_irq, xxx_interrupt, IRQF_DISABLED, "xxx", NULL);
	...
}

/**设备驱移除函数*/
void __exit xxx_exit(void)
{
	...
	/**释放中断*/
	free_irq(dev->irq, dev);
	...
}

2)工作队列
tasklet运行于中断上下文 工作队列运行于进程上下文 
tasklet处理函数中不能睡眠,而工作队列处理函数中允许睡眠 
I)定义一个工作队列结构
struct work_struct my_wq;
II)定义一个处理函数
void my_wq_func(unsigned long);
III)初始化这个工作队列并将工作队列与函数绑定
INIT_WORK(&my_wq, (void(*)(void *))my_wq_func, NULL);
IV)调度工作队列执行schedule_work()
schedule_work(&my_wq);
-----------------------------------------------------
工作队列使用模板
/**定义工作队列和关联函数*/
struct work_struct xxx_wq;
void xxx_do_work(unsigned long);

/**中断处理底半部*/
void xxx_do_work(unsigned long)
{
	...
}

/**中断处理顶半部*/
irqreturn_t xxx_interrupt(int irq, void *dev_id)
{
	...
	schedule_work(&xxx_wq);
	...
	return IRQ_HANDLED;
}

/**设备驱动模块加载函数*/
int __init xxx_init(void)
{
	...
	/**申请中断*/
	result = request_irq(xxx_irq, xxx_interrupt, IRQF_DISABLED, "xxx", NULL);
	...
	/**初始化工作队列*/
	INIT_WORK(&xxx_wq, (void(*)(void*))xxx_do_work, NULL);
	...
}

/**设备驱动移除函数*/
void xxx_exit(void)
{
	...
	/**释放中断*/
	free_irq(&dev->irq, dev);
	...
}


5. 中断例子
/**
*interrupt key kernel 2.6.35.7
*/

#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/fs.h>
#include <mach/gpio.h>

#define KEY_MAJOR 233
#define DEVICE_NAME "gpio_key1"

struct work_struct work;

static void gpio_key_work_func(struct work_struct *work)
{
	printk("the code is 101\n");
}

static irqreturn_t gpio_key_isr(int irq, void *dev_id)
{

	schedule_work(&work);
	return IRQ_HANDLED;
}

int key_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
{
	
	return 0;
}

static const struct file_operations key_ops = {
	.owner = THIS_MODULE,
	.ioctl = key_ioctl,
};

static int __init gpio_keys_init(void)
{
	int ret = 0;
	int irq;//中断号
	unsigned long irqflags;
	ret = register_chrdev(KEY_MAJOR, DEVICE_NAME, &key_ops);
	if (ret < 0) {
		printk("can't register gpio_keys_number\n");
		return -1;
	}
	
	//申请管脚
	gpio_request(S3C64XX_GPN(0), "HOME");
	//设置为输入
	gpio_direction_input(S3C64XX_GPN(0));
	
	irq = gpio_to_irq(S3C64XX_GPN(0));
	printk("the gpio_key irq is [%d]\n ", irq);
	
	irqflags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;
	
	ret = request_irq(irq, gpio_key_isr, irqflags, "HOME", NULL);
	if (ret) {
		printk("can't get gpio irq\n");
		return -1;
	}
	
	INIT_WORK(&work, gpio_key_work_func);
	
	printk("gpio_keys init\n");
	return 0;
}

static void __exit gpio_keys_exit(void)
{
	unregister_chrdev(KEY_MAJOR, DEVICE_NAME);
}

module_init(gpio_keys_init);
module_exit(gpio_keys_exit);
MODULE_LICENSE("GPL");
-------------------------------
测试,按键
# the code is 101
# cat /proc/interrupts 
101:          6    s3c-eint  HOME
中断被用了6次


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值