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
中断服务程序的执行不存在于进程上下文,要求中断服务程序的时间短,内核中对时钟的处理也采用中断方式
中断机制提供了硬件和软件之间异步传递信息的方式,硬件设备在发生某个事件时通过中断通知软件进行处理
中断实现了硬件设备按需获得处理器关注的机制,与查询方式相比可以大大节省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次