内核驱动程序——中断处理

注册中断处理程序

驱动程序可以通过下面的函数注册并激活一个中断处理程序:

函数原型:

int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char * name, void * dev);

irq:表示要申请的中断号(中断线);
handler:指向处理这个中断的实际中断处理程序
flags:中断标志(设置触发方式);
name:是与中断相关的设备的ASCII文本(就是给它一个名字,基本没用);
dev:主要用于共享中断线;

编写中断处理程序

中断处理程序声明:

static irqreturn_t handler(int irq,void *dev);

irq:
dev:它与request_irq()的参数dev必须一致,将request_irq指定的dev传递给这个参数;

Linux中的中断处理程序是无需重入的。
当一个给定的中断处理程序正在执行时,相应的中断线在所有处理器上都会被屏蔽掉。
通常情况下,所有其他的中断都是打开的,所以这些不同中断线上的其他中断都能被处理,但当前中断线总是被禁止的。
由此可以看出,同一个中断处理程序绝对不会被同时调用以处理嵌套的中断。

中断上下文

等待队列

例如,应用程序在读取键值时,如果用户还没有按下按键,应用层就应该睡眠;
内核里面使用一个队列来保存等待某个实现的睡眠进程;
例如:
我们的按键驱动,就要设置一个按键等待队列;
等进程读数据,没有数据读时,将进程放入等待队列中;
按键按下时,唤醒等待队列中的进程;

等待队列操作:

  • 等待队列的操作方法在<linux/wait.h>中声明;
  • 定义“等待队列头部”,队列头部用"wait_queue_head_t"类型来表示; 初始化“等待队列头部”

函数init_waitqueue_head()用来初始化队列头 队列头定义后,必须初始化。
DECLARE_WAIT_QUEUE_HEAD()宏可以作为定义并初始化等待队列头部的“快捷方式”

等待事件

wait_event(queue, condition)
wait_event_interruptible(queue, condition)
wait_event_timeout(queue, condition, timeout)
wait_event_interruptible_timeout(queue, condition, timeout)

唤醒队列

void wake_up(wait_queue_head_t *queue);
void wake_up_interruptible(wait_queue_head_t *queue);

总结
涉及到的头文件如下:

#include <linux/interrupt.h>	  //request_irq free_irq
#include <linux/gpio.h>		  //gpio_to_irq
#include <linux/irq.h>		//flags 中断触发方式
#include <mach/gpio.h>		//GPIO编号定义

编程实例:
以tiny4412的按键驱动为例

应用层使用read()函数读取按键时,调用到内核层read()函数,若没有按键按下,此时应该进入睡眠等待,因此在内核层read()函数中需要使用wait_event_interruptible(); 这个函数会阻塞将进程放入等待队列,因此,我们同时需要声明一个等待队列:

static DECLARE_WAIT_QUEUE_HEAD(btnwq);//声明等待队列为btnwq

然后在内核层的read()函数中调用wait_event_interruptible()

static int btn_read(struct file *filp, char __user *buf, size_t len, loff_t *offp)
{
	wait_event_interruptible(btnwq, key_press);//将进程放入等待队列,直到有按键按下,唤醒进程
	copy_to_user(buf,&value,sizeof(int));
	key_press = 0;
	return sizeof(int);
}

完整代码

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/uaccess.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/irq.h>// IRQ_TYPE_EDGE_BOTH
#include <linux/interrupt.h>//request_irq free_irq
#include <linux/gpio.h>//gpio_to_irq
#include <mach/gpio.h>//EXYNOS4_GPX3(2)
#include <linux/wait.h>
#include <linux/sched.h>


typedef struct{
	unsigned int gpio;
	unsigned int irqno;
	int value;
	char *name;
}tsjbtn_t;


static tsjbtn_t btns[4]={
		{
			.gpio = EXYNOS4_GPX3(2),
			.value = 1,
			.name = "TINY4412-K1"
		},

		{
			.gpio = EXYNOS4_GPX3(3),
			.value = 2,
			.name = "TINY4412-K2"
		},

		{
			.gpio = EXYNOS4_GPX3(4),
			.value = 3,
			.name = "TINY4412-K3"
		},

		{
			.gpio = EXYNOS4_GPX3(5),
			.value = 4,
			.name = "TINY4412-K4"
		}
};


static int btn_open(struct inode *inode, struct file *file)
{
	printk("btn open\n");
	return 0;
}
static int btn_close(struct inode *inode, struct file *file)
{
	printk("btn close\n");
	return 0;
}

static DECLARE_WAIT_QUEUE_HEAD(btnwq);
static int value;
static int key_press = 0;

static int btn_read(struct file *filp, char __user *buf, size_t len, loff_t *offp)
{
	wait_event_interruptible(btnwq, key_press);
	copy_to_user(buf,&value,sizeof(int));
	key_press = 0;
	return sizeof(int);
}

static irqreturn_t my_hander(int no,void *data)
{
	tsjbtn_t *p = (tsjbtn_t *)data;
	value = p->value;
	key_press = 1;
	wake_up_interruptible(&btnwq);
	return IRQ_HANDLED;	
}

static struct file_operations btn_ops = {
	.owner = THIS_MODULE,
	.open = btn_open,
	.release = btn_close,
	.read = btn_read,
};

static int btn_major = 0;
static struct class *btn_class;
static int __init buttons_init(void)
{
	int i;
	for(i=0;i<4;i++){
			btns[i].irqno = gpio_to_irq(btns[i].gpio);
			request_irq(btns[i].irqno, my_hander, IRQF_TRIGGER_FALLING, btns[i].name, &btns[i]);
	}

	btn_major = register_chrdev(0, "btn_dev", &btn_ops);
	btn_class = class_create(THIS_MODULE, "btn_dir");
	device_create(btn_class, NULL, MKDEV(btn_major,0), NULL,"btn_node");
	return 0;
}
static void __exit buttons_exit(void)
{

	device_destroy(btn_class, MKDEV(btn_major,0));
	class_destroy(btn_class);
	unregister_chrdev(btn_major, "btn_dev");
	int i;
	for(i=0;i<4;i++){
		free_irq(btns[i].irqno,&btns[i]);
	}
}
module_init(buttons_init);
module_exit(buttons_exit);
MODULE_LICENSE("GPL");


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

嵌入式Linux充电站

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值