Linux中断

Linux 的中断处理分为两个半部,顶半部处理紧急的硬件操作,底半部处理不紧急的耗时操
作。tasklet 和工作队列都是调度中断底半部的良好机制,tasklet 基于软中断实现。内核定时器也
依靠软中断实现。

1.申请和释放中断

申请中断 int request_irq(unsigned int irq, irq_handler_t handler,  unsigned long irqflags, const char *devname, void *dev_id)

irq 是要申请的硬件中断号。

handler 是向系统登记的中断处理函数(顶半部),是一个回调函数,中断发生时,系统调用 这个函数,

dev_id 参数将被传递给它。

irqflags是中断处理的属性,可以指定中断的触发方式以及处理方式。在触发方式方面,可以是 IRQF_TRIGGER_RISING、IRQF_TRIGGER_FALLING、IRQF_TRIGGER_HIGH、IRQF_TRIGGER_ LOW 等。在处理方式方面,若设置了 IRQF_DISABLED,表明中断处理程序是快速处理程序,快 速处理程序被调用时屏蔽所有中断,慢速处理程序则不会屏蔽其他设备的驱动;若设置了 IRQF_SHARED,则表示多个设备共享中断,

dev_id 在中断共享时会用到,一般设置为这个设备 的设备结构体或者 NULL。

request_irq()返回 0 表示成功,返回-EINVAL 表示中断号无效或处理函数指针为 NULL,返 回-EBUSY 表示中断已经被占用且不能共享。

顶半部 handler 的类型 irq_handler_t 定义为:
typedef irqreturn_t (*irq_handler_t)(int, void *);
typedef int irqreturn_t;

释放中断

void free_irq(unsigned int irq,void *dev_id);

2.使能和屏蔽中断

void disable_irq(int irq);

void disable_irq_nosync(int irq);

void enable_irq(int irq); disable_irq_nosync()与 disable_irq()的区别在于前者立即返回,而后者等待目前的中断处理完 成。由于 disable_irq()会等待指定的中断被处理完,因此如果在 n 号中断的顶半部调用 disable_irq(n),会引起系统的死锁,这种情况下,只能调用 disable_irq_nosync(n)。

下列两个函数(或宏,具体实现依赖于 CPU 体系结构)将屏蔽本 CPU 内的所有中断:

#define local_irq_save(flags) ...

void local_irq_disable(void);

与上述两个禁止中断对应的恢复中断的函数(或宏)是:

#define local_irq_restore(flags) ...

void local_irq_enable(void);

3.底半部机制
Linux 实现底半部的机制主要有 tasklet、工作队列和软中断。

(1)tasklet

tasklet 的使用较简单,我们只需要定义 tasklet 及其处理函数并将两者关联,例如:
void my_tasklet_func(unsigned long); /*定义一个处理函数*/
DECLARE_TASKLET(my_tasklet, my_tasklet_func, data);
   /*定义一个 tasklet 结构 my_tasklet,与 my_tasklet_func(data)函数相关联 */

代码 DECLARE_TASKLET(my_tasklet,my_tasklet_func,data)实现了定义名称为 my_tasklet 的
tasklet 并将其与 my_tasklet_func()这个函数绑定,而传入这个函数的参数为 data。

在需要调度 tasklet 的时候引用一个 tasklet_schedule()函数就能使系统在适当的时候进行调度
运行:
tasklet_schedule(&my_tasklet);

(2).工作队列 工作队列的使用方法和 tasklet 非常相似,下面的代码用于定义一个工作队列和一个底半部执 行函数:

struct work_struct my_wq; /*定义一个工作队列*/

void my_wq_func(unsigned long); /*定义一个处理函数*/

通过 INIT_WORK()可以初始化这个工作队列并将工作队列与处理函数绑定:

INIT_WORK(&my_wq, (void (*)(void *)) my_wq_func, NULL);  /*初始化工作队列并将其与处理函数绑定*/

与 tasklet_schedule()对应的用于调度工作队列执行的函数为 schedule_work(),如:

schedule_work(&my_wq);/*调度工作队列执行*/

(3).软中断
软中断(softirq)也是一种传统的底半部处理机制,它的执行时机通常是顶半部返回的时候,asklet 是基于软中断实现的,因此也运行于软中断上下文。

在 Linux 内核中,用 softirq_action 结构体表征一个软中断,这个结构体中包含软中断处理函数指针和传递给该函数的参数。使用 open_softirq()函数可以注册软中断对应的处理函数,而raise_softirq()函数可以触发一个软中断。

软中断和 tasklet 运行于软中断上下文,仍然属于原子上下文的一种,而工作队列则运行于进
程上下文。因此,软中断和 tasklet 处理函数中不能睡眠,而工作队列处理函数中允许睡眠。

按键中断驱动程序:

四个按键(对应的IO口为GPH2(0),GPH2(1),GPH2(2),GPH2(3))) 分别对应外部中断为EINT16,17,18,19

加入了poll机制和异步通知fasync

#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/poll.h>
#include <linux/irq.h>
#include <asm/irq.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <asm/uaccess.h>
#include <linux/gpio.h>
#include <mach/regs-gpio.h>
#include <mach/hardware.h>
#include <linux/platform_device.h>
#include <linux/cdev.h>
#include <linux/miscdevice.h> #define KEYNAME "mykey"
static unsigned char keymajor = ; static struct cdev key_cdev; static struct class *key_class;
static struct device*key_device;
static struct fasync_struct *fasync_queue; static DECLARE_WAIT_QUEUE_HEAD(my_key_waitq); /*DECLARE_WAIT_QUEUE_HEAD (name)声明且初始化*/
static unsigned char pressed = ; static unsigned char key_value = ; struct my_key_desc {
unsigned int irq;
unsigned int pin;
unsigned char value;
const char* name;
}; static struct my_key_desc key_desc[] = {
{IRQ_EINT16_31, S5PV210_GPH2(), 0x01, "KEY1"},
{IRQ_EINT16_31, S5PV210_GPH2(), 0x02, "KEY2"},
{IRQ_EINT16_31, S5PV210_GPH2(), 0x03, "KEY3"},
{IRQ_EINT16_31, S5PV210_GPH2(), 0x04, "KEY4"},
}; static irqreturn_t my_key_irq(int irq, void* dev_id)
{
volatile struct my_key_desc key_dr = *(volatile struct my_key_desc*)dev_id;
int value;
value = gpio_get_value(key_dr.pin); if(value == ) /*有键按下key_desc.value = 0x8x*/
key_value = key_dr.value|0x80;
else
key_value = key_dr.value ; pressed = ;
wake_up_interruptible(&my_key_waitq); kill_fasync(&fasync_queue, SIGIO, POLLIN | POLLOUT);
return ;
} static int my_key_open(struct inode *inode, struct file *file)
{
int ret;
printk("request_irq setting\n");
ret = request_irq( IRQ_EINT(), my_key_irq, IRQ_TYPE_EDGE_BOTH, "KEY1", (void*)&key_desc[]);
if(ret) {
printk("ret is %d\n", ret);
return ret;
}
ret = request_irq( IRQ_EINT(), my_key_irq, IRQ_TYPE_EDGE_BOTH, "KEY2", (void*)&key_desc[]);
if(ret)
return ret;
ret = request_irq( IRQ_EINT(), my_key_irq, IRQ_TYPE_EDGE_BOTH, "KEY3", (void*)&key_desc[]);
if(ret)
return ret;
ret = request_irq( IRQ_EINT(), my_key_irq, IRQ_TYPE_EDGE_BOTH, "KEY4", (void*)&key_desc[]);
if(ret)
return ret;
return ;
} /*static int my_key_open(struct inode * inode, struct file * file)
{
int ret;
unsigned char i;
for(i = 0; i < 4; i++) {
// s3c_gpio_cfg(key_desc[i].pin, EINT_MODE ); sizeof(key_desc) / sizeof(key_desc[0])
ret = request_irq(key_desc[i].irq, my_key_irq , IRQ_TYPE_EDGE_BOTH, key_desc[i].name,(void*)&key_desc[i]);
if(ret) {
printk(KERN_EMERG"request_irq error %d\n" , i);
break;
}
}
if(ret) {
i--;
for(; i > 0; i--) {
free_irq(key_desc[i].irq, (void*)&key_desc[i]);
return -EBUSY;
}
}
return 0;
}*/ static int my_key_fasync(int fd, struct file * file, int on){
return fasync_helper(fd, file, on, &fasync_queue);
} static ssize_t my_key_read(struct file *filep, char *outbuf, size_t n, loff_t *ppos)
{
int ret;
if(n != ) {
printk(KERN_EMERG"The driver can only give one key value once!\n");
return -EINVAL;
} if(filep->f_flags & O_NONBLOCK) {
if(!pressed)
return -EBUSY;
} else {
wait_event_interruptible(my_key_waitq, pressed);/*pressed为1时继续执行后面,否则休眠*/
pressed = ; ret = copy_to_user(outbuf, &key_value, );
if(ret) {
printk(KERN_EMERG"key copy_to_user error\n");
return -ENOMEM;
}
}
return ;
} static int my_key_write(struct file * filep,const char * buf,size_t n,loff_t * ppos)
{ return ;
} static int my_key_close(struct inode * inode,struct file * file)
{
free_irq(IRQ_EINT(), (void*)&key_desc[]);
free_irq(IRQ_EINT(), (void*)&key_desc[]);
free_irq(IRQ_EINT(), (void*)&key_desc[]);
free_irq(IRQ_EINT(), (void*)&key_desc[]);
my_key_fasync(-, file, );
return ;
} static unsigned int my_key_poll(struct file *filep, poll_table *wait)
{
unsigned int mask = ;
poll_wait(filep, &my_key_waitq, wait); if(pressed) {
mask |= POLLIN | POLLRDNORM;
} return mask;
} struct file_operations key_fops= {
.owner = THIS_MODULE,
.open = my_key_open,
.read = my_key_read,
.write = my_key_write,
.release = my_key_close,
.poll = my_key_poll,
.fasync = my_key_fasync,
}; static int __init my_key_init(void)
{
int ret;
int deno;
deno = MKDEV(keymajor, );
cdev_init(&key_cdev, &key_fops);
if (keymajor) {
register_chrdev_region(deno, , KEYNAME);
printk(KERN_EMERG"key major is %d\n", keymajor);
} else {
alloc_chrdev_region(&deno, , , KEYNAME);
keymajor = MAJOR(deno);
printk(KERN_EMERG"key major is %d\n", keymajor);
} key_cdev.owner = THIS_MODULE;
key_cdev.ops = &key_fops; ret = cdev_add(&key_cdev, deno, );
if(ret) {
printk(KERN_EMERG"cdev_add error\n");
goto add_error;
} key_class= class_create(THIS_MODULE, KEYNAME);
if(IS_ERR(key_class)) {
printk(KERN_EMERG"class_create error\n");
goto class_error;
} key_device= device_create(key_class, NULL, deno, NULL, KEYNAME);
if(IS_ERR(key_device)) {
printk(KERN_EMERG"device_create error\n");
goto device_error;
} // init_waitqueue_head(&my_key_waitq);
return ; device_error: class_destroy(key_class); class_error: cdev_del(&key_cdev); add_error: unregister_chrdev_region(deno,); return -ENODEV; } static void __exit my_key_exit(void)
{
device_destroy(key_class, MKDEV(keymajor, ));
class_destroy(key_class);
cdev_del(&key_cdev);
unregister_chrdev_region(MKDEV(keymajor, ), );
} MODULE_LICENSE("GPL");
MODULE_AUTHOR("qigaohua");
module_init(my_key_init); module_exit(my_key_exit);

中断poll测试程序:

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <stdio.h>

#include <poll.h>

#include <signal.h>

int main()
{
int fd;
int ret;
unsigned char key_value;
struct pollfd fds[]; fd = open("/dev/mykey", O_RDWR);
if(fd < ) {
perror("/dev/mykey open error\n");
return ;
} fds[].fd = fd;
fds[].events = POLLIN; while() {
ret = poll(fds, , );
if(ret == ) {
printf("timeout\n");
}else { read(fd, &key_value, );
printf("key_value = 0x%x\n", key_value);
}
}
}

异步通知fasync中断测试程序:

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <stdio.h>

#include <poll.h>

#include <signal.h>

int main()
{
int oflags; fd = open("/dev/mykey", O_RDWR);
if(fd < ) {
printf("/dev/mykey open error\n");
return -EBUSY;
} signal(SIGIO, my_sinal_fun);
fcntl(fd, F_SETOWN, getpid());
oflags = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, oflags | FASYNC); while() {
sleep();
printf("hello\n");
} }

Smart210学习记录-------linux驱动中断的更多相关文章

  1. Smart210学习记录-------linux内核模块

    Linux 驱动工程师需要牢固地掌握 Linux 内核的编译方法以为嵌入式系统构建可运行的Linux 操作系统映像.在编译 LDD6410 的内核时,需要配置内核,可以使用下面命令中的 一个: #ma ...

  2. Smart210学习记录-----Linux i2c驱动

    一:Linux i2c子系统简介: 1.Linux 的 I2C 体系结构分为 3 个组成部分: (1) I2C 核心. I2C 核心提供了 I2C 总线驱动和设备驱动的注册.注销方法,I2C 通信方法 ...

  3. Smart210学习记录------linux串口驱动

    转自:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=27025492&id=327609 一.核心数据结构 串口驱动有 ...

  4. Smart210学习记录-------Linux设备驱动结构

    cdev结构体 1 struct cdev { 2 struct kobject kobj; /* 内嵌的 kobject 对象 */ 3 struct module *owner; /*所属模块*/ ...

  5. Smart210学习记录-----linux定时器

    1.内核定时器: Linux 内核所提供的用于操作定时器的数据结构和函数如下: (1) timer_list 在 Linux 内核中,timer_list 结构体的一个实例对应一个定时器 1 stru ...

  6. Smart210学习记录-----SD/MMC/SDIO驱动

    转自:http://jingpin.jikexueyuan.com/article/23369.html http://blog.csdn.net/evilcode/article/details/7 ...

  7. Smart210学习记录------块设备

    转自:http://bbs.chinaunix.net/thread-2017377-1-1.html 本章的目的用尽可能最简单的方法写出一个能用的块设备驱动.所谓的能用,是指我们可以对这个驱动生成的 ...

  8. Smart210学习记录----beep linux字符设备驱动

    今天搞定了beep linux字符设备驱动,心里还是很开心的,哈哈...但在完成的过程中却遇到了一个非常棘手的问题,花费了我大量的时间,,,, 还是把问题描述一下吧,好像这个问题很普遍的,网上许多解决 ...

  9. Smart210学习记录------nor flash驱动

    nor flash驱动与nand flash驱动的差别不大,只是设置不同的结构体而已,, nor flash驱动代码: #include <linux/module.h> #include ...

随机推荐

  1. Spring使用jdbcJdbcTemplate和三种方法配置数据源

    三种方法配置数据源 1.需要引入jar包:spring-jdbc-4.3.2.RELEASE.jar <!-- spring内置,springJdbc,配置数据源 --> <bean ...

  2. 9. shell环境

    • printenv –打印部分或所有的环境变量 • set –设置 shell 选项 • export —导出环境变量,让随后执行的程序知道. • alias –创建命令别名 1.shell环境:s ...

  3. backbonejs中的模型篇(二)

    一:模型标识符 每个模型都有一个用作唯一标识符的ID属性,以便在不同模型间进行区分.通过id属性我们可以直接访问模型对象当中用于标识符存放的属性,默认属性名为id,但也可以通过设置idAttribut ...

  4. 使用window.navigator.userAgent属性判断浏览器类型及版本

    使用window.navigator.userAgent属性判断浏览器类型及版本 2011-12-11 22:03:11 window.navigator.userAgent属性包含了浏览器类型.版本 ...

  5. Go语言并发与并行学习笔记(三)

    转:http://blog.csdn.net/kjfcpua/article/details/18265475 Go语言并发的设计模式和应用场景 以下设计模式和应用场景来自Google IO上的关于G ...

  6. httpClient 4.x post get方法

    public static String doPost(String url, String encoding, String contentType, String sendData) throws ...

  7. Group by的使用方法

    sql中如果要分组查询,一般都会使用到group by语句,如何熟练使用group by语句呢,我分以下几点进行总结. Group by与聚合函数 Group by与Having 需要注意的地方 Gr ...

  8. 在ASP.NET MVC中使用Area

    前言: 这段时间小猪花了不少功夫在研究ASP.NET MVC的源码上面,可谓思想是了解了不少,用的上用不上却是另外一回事了.! 应用场景: ASP.NET MVC中,是依靠某些文件夹以及类的固定命名规 ...

  9. js打印对象(object)

    function printObject(obj){//obj = {"cid":"C0","ctext":"区县"}; ...

  10. 数组越界保护与消息传递black机制

    数组越界保护if(index.row <= [array count]) 发送消息[[NSNotificationCenter defaultCenter]     postNotificati ...