《嵌入式驱动(七):等待队列和中断》
本文介绍了Linux驱动开发中的关键机制:1. 等待队列实现阻塞IO,通过wait_event等函数实现进程阻塞/唤醒;2. 中断处理分为顶半部(紧急操作)和底半部(耗时任务),提供四种实现方式(软中断、tasklet、工作队列、线程化irq);3. 详细分析了四种IO模型(阻塞/非阻塞/异步/多路复用)的实现原理,并给出对应驱动程序代码示例。文中还涵盖中断资源竞争处理、设备树中断配置等关键技术,
一、等待队列(阻塞IO)
临时存放任务的队练,Linux内核中可以通过等待队列来实现阻塞进程的唤醒。
等待队列有如下函数接口:
init_waitqueue_head
wait_event
wait_event_interruptible
wait_event_timeout
wait_event_interruptuble_timeout
可以通过如下两个函数唤醒:
wake_up
wake_up_interruptible
接口原型
init_waitqueue_head(q);
wait_event(wq, condition);
wait_event_interruptible(wq, condition);
wait_event_timeout(wq, condition, timeout);
wait_event_interruptible_timeout(wq, condition, timeout);
wake_up(x);
wake_up_interruptible(x);
二、中断
1.基本概念
Linux有中断子系统,分为中断顶半部和中断底半部

上半部:也称为中断顶半部,用于完成尽量少的比较紧急的功能,通常被设置为不可被新中断打断。
下半部:也称为中断底半部,用来完成中断事件绝大多数任务,可以被新的中断打断,底半部相对来说并不是非常紧急,而且相对比较耗时。
上半部的使用场景:
1)要处理的内容不希望被其他中断打断
2)要处理的任务对时间敏感
3)要处理的任务与硬件有关
下半部的使用场景:
1)完成并不是非常紧急的耗时的任务
2)可以被打断
中断下半部有4中方式实现:
1. 软中断
软中断是一种传统的底半部处理机制,它的执行时机通常是顶半部返回的时候
软中断和tasklet运行于中断上下文,不允许睡眠
2. tasklettasklet是利用软中断来实现的
tasklet不允许睡眠
3. 工作队列
工作队列运行于进程上下文,允许睡眠
4. 线程化irq
申请中断号时,内核会为相应的中断号分配一个对应的内核线程,中断handler结束后,返回值IRQ_WAKE_THREAD,内核调度对应线程执行thread_fun对应的函数
可以通过request_irq来注册中断顶半部处理函数
可以通过tasklet、workqueue、线程化的irq实现中断底半部
中断顶半部完成中断对硬件必须完成的操作,实时性要求比较高的操作
中断底半部完成比较费时的操作
2. request_irq
request_irq的中断编号可以通过gpio_to_irq由gpio编号获得中断编号
中断编号也可以通过设备树中配置得到,驱动可以通过of_irq_get来获得中断编号
putekey {
#address-cells = <1>;
#size-cells = <1>;
compatible = "pute-key";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_putekey>;
gpio-key = <&gpio1 18 GPIO_ACTIVE_LOW>;interrupt-parent = <&gpio1>;
interrupts = <18 0>;
status = "okay";
};
cat /proc/interrupts 查看当前系统中所有注册的中断号信息
3. 中断涉及的函数接口
request_irq 注册中断处理程序,绑定中断号与处理函数
free_irq 释放已注册的中断资源
enable_irq 使能中断
disable_irq 禁止中断(立即返回不等待中断处理结束)
原型
static inline int __must_check request_irq(unsigned int irq, irq_handler_t
handler, unsigned long flags, const char *name, void *dev);
void free_irq(unsigned int irq, void *dev_id);
void disable_irq_nosync(unsigned int irq);
void enable_irq(unsigned int irq);
4. 中断资源竞争问题
操作资源之前关中断,操作资源后开中断
使用自旋锁对共享资源进行互斥操作保护
5. 中断底半部
tasklet实现中断底半部:
tasklet基于软中断
是工作在中断上下文,中断底半部执行时仍处于中断发生过程中,不允许睡眠
接口:
DECLARE_TASKLET 定义TASKLET
tasklet_schedule 使系统在适当的时候进行调度tasklet
原型:
DECLARE_TASKLET(name, func, data);
static inline void tasklet_schedule(struct tasklet_struct *t);
workqueue实现中断底半部:
workqueue基于内核线程
是工作在进程上下文,中断底半部执行时中断已经结束了,所以进程中可以睡眠
接口:
INIT_WORK 初始化工作队列
schedule_work 使系统在适当的时候进行调度队列原型:
INIT_WORK(_work, _func);
static inline bool schedule_work(struct work_struct *work);
三、linux四种IO模型
IO模型包括:阻塞IO、非阻塞IO、异步IO(信号驱动IO)、多路复用IO
1. 阻塞IO:事件没有到来时阻塞等待直到事件发生,继续向下执行,等待时间过程中,不需要占用CPU资源
2. 非阻塞IO:事件没有发生继续向下执行,事件发生读取事件,继续向下执行
3. 异步IO:当事件发生时,内核会向应用层发送通知,应用层捕捉到信号执行处理函数
4. 多路复用IO:阻塞监听事件集合,事件发生时不再阻塞等待,判断发生的事件,继续向下执行
异步IO接口:

原型:
int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct
**fapp);
void kill_fasync(struct fasync_struct **fp, int sig, int band);
band参数表示事件类型:
| 事件值 | 含义 | 应用场景 |
|---|---|---|
| POLL_IN | 设备有数据可读 | 串口接收缓冲区非空、网络数据包到达 |
| POLL_OUT | 设备可写入数据 | 发送缓冲区空闲、可继续发送数据 |
| POLL_ERR | 设备发生错误 | 硬件故障、通信超时 |
| POLL_PRI | 高优先级数据到达 | TCP带外数据(如紧急指针数据) |
| POLL_HUP | 设备断开连接 | 串口线拔出、Socket连接关闭 |
多路复用IO接口

原型:
static inline void poll_wait(struct file * filp, wait_queue_head_t *
wait_address, poll_table *p);
四、代码
1.非阻塞IO
key_drv.c
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/of_address.h>
#include <linux/of.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
static struct device_node *pkeynode = NULL;
static int gpiokey = 0;
static ssize_t key_read(struct file *fp, char __user *puser, size_t n, loff_t *off)
{
int status = 0;
unsigned long nret = 0;
if (gpio_get_value(gpiokey)) {
status = 0;
} else {
status = 1;
}
nret = copy_to_user(puser, &status, sizeof(status));
if (nret) {
pr_info("copy_to_user failed\n");
return -1;
}
return nret;
}
static struct file_operations fops = {
.owner = THIS_MODULE,
.read = key_read,
};
static struct miscdevice misc_key = {
.minor = MISC_DYNAMIC_MINOR,
.name = "misc_key",
.fops = &fops,
};
static __init int key_drv_init(void)
{
int ret = 0;
ret = misc_register(&misc_key);
if (ret) {
pr_info("misc_register failed\n");
return -1;
}
pkeynode = of_find_node_by_path("/putekey");
if (NULL == pkeynode) {
pr_info("of_find_node_by_path failed\n");
return -1;
}
gpiokey = of_get_named_gpio(pkeynode, "gpio-key", 0);
if (gpiokey < 0) {
pr_info("of_get_named_gpio failed\n");
return -1;
}
ret = gpio_request(gpiokey, "pute-key-gpio");
if (ret) {
pr_info("gpio_request failed\n");
return -1;
}
ret = gpio_direction_input(gpiokey);
if (ret) {
pr_info("gpio_direction_input failed\n");
return -1;
}
pr_info("key_drv_init success\n");
return 0;
}
static __exit void key_drv_exit(void)
{
int ret = 0;
gpio_free(gpiokey);
ret = misc_deregister(&misc_key);
if (ret) {
pr_info("misc_deregister failed\n");
return;
}
pr_info("key_drv_exit success\n");
return;
}
module_init(key_drv_init);
module_exit(key_drv_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("pute");
key_app.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
int main(void)
{
int fd = 0;
int stat = 0;
fd = open("/dev/misc_key", O_RDWR);
if (-1 == fd)
{
perror("fail to open");
return -1;
}
while (1)
{
read(fd, &stat, sizeof(stat));
printf("stat = %d\n", stat);
}
close(fd);
return 0;
}
2.阻塞IO
key_drv.c
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/of_address.h>
#include <linux/of.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/irqreturn.h>
#include <linux/of_irq.h>
static struct device_node *pkeynode = NULL;
static int gpiokey = 0;
static int irqno = 0;
static wait_queue_head_t wq;
static int condition = 0;
static int status = 0;
static ssize_t key_read(struct file *fp, char __user *puser, size_t n, loff_t *off)
{
unsigned long nret = 0;
condition = 0;
status = 0;
wait_event_interruptible(wq, condition);
nret = copy_to_user(puser, &status, sizeof(status));
if (nret) {
pr_info("copy_to_user failed\n");
return -1;
}
return nret;
}
static struct file_operations fops = {
.owner = THIS_MODULE,
.read = key_read,
};
static struct miscdevice misc_key = {
.minor = MISC_DYNAMIC_MINOR,
.name = "misc_key",
.fops = &fops,
};
static irqreturn_t key_irq_handler(int irqno, void *pdev)
{
condition = 1;
status = 1;
wake_up_interruptible(&wq);
return IRQ_HANDLED;
}
static __init int key_drv_init(void)
{
int ret = 0;
init_waitqueue_head(&wq);
ret = misc_register(&misc_key);
if (ret) {
pr_info("misc_register failed\n");
return -1;
}
pkeynode = of_find_node_by_path("/putekey");
if (NULL == pkeynode) {
pr_info("of_find_node_by_path failed\n");
return -1;
}
gpiokey = of_get_named_gpio(pkeynode, "gpio-key", 0);
if (gpiokey < 0) {
pr_info("of_get_named_gpio failed\n");
return -1;
}
ret = gpio_request(gpiokey, "pute-key-gpio");
if (ret) {
pr_info("gpio_request failed\n");
return -1;
}
ret = gpio_direction_input(gpiokey);
if (ret) {
pr_info("gpio_direction_input failed\n");
return -1;
}
#if 0
irqno = gpio_to_irq(gpiokey);
if (irqno < 0) {
pr_info("gpio_to_irq failed\n");
return -1;
}
#endif
irqno = of_irq_get(pkeynode, 0);
if (irqno < 0) {
pr_info("of_irq_get failed\n");
return -1;
}
ret = request_irq(irqno, key_irq_handler, IRQF_TRIGGER_FALLING, "pute-irq-key", NULL);
if (ret) {
pr_info("request_irq failed\n");
return -1;
}
pr_info("key_drv_init success\n");
return 0;
}
static __exit void key_drv_exit(void)
{
int ret = 0;
free_irq(irqno, NULL);
gpio_free(gpiokey);
ret = misc_deregister(&misc_key);
if (ret) {
pr_info("misc_deregister failed\n");
return;
}
pr_info("key_drv_exit success\n");
return;
}
module_init(key_drv_init);
module_exit(key_drv_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("pute");
key_app.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
int main(void)
{
int fd = 0;
int stat = 0;
fd = open("/dev/misc_key", O_RDWR);
if (-1 == fd)
{
perror("fail to open");
return -1;
}
while (1)
{
read(fd, &stat, sizeof(stat));
printf("stat = %d\n", stat);
}
close(fd);
return 0;
}
3.异步IO
key_drv.c
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/of_address.h>
#include <linux/of.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/irqreturn.h>
#include <linux/of_irq.h>
static struct device_node *pkeynode = NULL;
static int gpiokey = 0;
static int irqno = 0;
static wait_queue_head_t wq;
static int condition = 0;
static int status = 0;
struct fasync_struct *fapp = NULL;
static ssize_t key_read(struct file *fp, char __user *puser, size_t n, loff_t *off)
{
unsigned long nret = 0;
condition = 0;
status = 0;
wait_event_interruptible(wq, condition);
nret = copy_to_user(puser, &status, sizeof(status));
if (nret) {
pr_info("copy_to_user failed\n");
return -1;
}
return sizeof(status);
}
static int key_fasync(int fd, struct file *fp, int on)
{
return fasync_helper(fd, fp, on, &fapp);
}
static struct file_operations fops = {
.owner = THIS_MODULE,
.read = key_read,
.fasync = key_fasync,
};
static struct miscdevice misc_key = {
.minor = MISC_DYNAMIC_MINOR,
.name = "misc_key",
.fops = &fops,
};
static irqreturn_t key_irq_handler(int irqno, void *pdev)
{
if (fapp != NULL) {
kill_fasync(&fapp, SIGIO, POLLIN);
}
status = 1;
condition = 1;
wake_up_interruptible(&wq);
return IRQ_HANDLED;
}
static __init int key_drv_init(void)
{
int ret = 0;
init_waitqueue_head(&wq);
ret = misc_register(&misc_key);
if (ret) {
pr_info("misc_register failed\n");
return -1;
}
pkeynode = of_find_node_by_path("/putekey");
if (NULL == pkeynode) {
pr_info("of_find_node_by_path failed\n");
return -1;
}
gpiokey = of_get_named_gpio(pkeynode, "gpio-key", 0);
if (gpiokey < 0) {
pr_info("of_get_named_gpio failed\n");
return -1;
}
ret = gpio_request(gpiokey, "pute-key-gpio");
if (ret) {
pr_info("gpio_request failed\n");
return -1;
}
ret = gpio_direction_input(gpiokey);
if (ret) {
pr_info("gpio_direction_input failed\n");
return -1;
}
#if 0
irqno = gpio_to_irq(gpiokey);
if (irqno < 0) {
pr_info("gpio_to_irq failed\n");
return -1;
}
#endif
irqno = of_irq_get(pkeynode, 0);
if (irqno < 0) {
pr_info("of_irq_get failed\n");
return -1;
}
ret = request_irq(irqno, key_irq_handler, IRQF_TRIGGER_FALLING, "pute-irq-key", NULL);
if (ret) {
pr_info("request_irq failed\n");
return -1;
}
pr_info("key_drv_init success\n");
return 0;
}
static __exit void key_drv_exit(void)
{
int ret = 0;
free_irq(irqno, NULL);
gpio_free(gpiokey);
ret = misc_deregister(&misc_key);
if (ret) {
pr_info("misc_deregister failed\n");
return;
}
pr_info("key_drv_exit success\n");
return;
}
module_init(key_drv_init);
module_exit(key_drv_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("pute");
key_app.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/select.h>
int fd = 0;
void handler(int signo)
{
int stat = 0;
read(fd, &stat, sizeof(stat));
printf("stat = %d\n", stat);
return;
}
int main(void)
{
int flags = 0;
signal(SIGIO, handler);
fd = open("/dev/misc_key", O_RDWR);
if (-1 == fd)
{
perror("fail to open");
return -1;
}
flags = fcntl(fd, F_GETFL); //获得文件描述符属性
flags |= O_ASYNC; //在文件描述符属性基础上加入异步IO属性
fcntl(fd, F_SETFL, flags); //重新设置文件描述符属性
fcntl(fd, F_SETOWN, getpid()); //产生时间发送给当前进程任务
while (1)
{
}
close(fd);
return 0;
}
4.多路复用IO
key_drv.c
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/of_address.h>
#include <linux/of.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/irqreturn.h>
#include <linux/of_irq.h>
#include <asm-generic/poll.h>
#include <linux/poll.h>
static struct device_node *pkeynode = NULL;
static int gpiokey = 0;
static int irqno = 0;
static wait_queue_head_t wq;
static int condition = 0;
static int status = 0;
struct fasync_struct *fapp = NULL;
static ssize_t key_read(struct file *fp, char __user *puser, size_t n, loff_t *off)
{
unsigned long nret = 0;
condition = 0;
status = 0;
wait_event_interruptible(wq, condition);
nret = copy_to_user(puser, &status, sizeof(status));
if (nret) {
pr_info("copy_to_user failed\n");
return -1;
}
return sizeof(status);
}
static int key_fasync(int fd, struct file *fp, int on)
{
return fasync_helper(fd, fp, on, &fapp);
}
static unsigned int key_poll(struct file *fp, struct poll_table_struct *ptable)
{
unsigned int umask = 0;
poll_wait(fp, &wq, ptable);
if (0 == gpio_get_value(gpiokey)) {
umask |= POLLIN;
}
return umask;
}
static struct file_operations fops = {
.owner = THIS_MODULE,
.read = key_read,
.fasync = key_fasync,
.poll = key_poll,
};
static struct miscdevice misc_key = {
.minor = MISC_DYNAMIC_MINOR,
.name = "misc_key",
.fops = &fops,
};
static irqreturn_t key_irq_handler(int irqno, void *pdev)
{
if (fapp != NULL) {
kill_fasync(&fapp, SIGIO, POLLIN);
}
status = 1;
condition = 1;
wake_up_interruptible(&wq);
return IRQ_HANDLED;
}
static __init int key_drv_init(void)
{
int ret = 0;
init_waitqueue_head(&wq);
ret = misc_register(&misc_key);
if (ret) {
pr_info("misc_register failed\n");
return -1;
}
pkeynode = of_find_node_by_path("/putekey");
if (NULL == pkeynode) {
pr_info("of_find_node_by_path failed\n");
return -1;
}
gpiokey = of_get_named_gpio(pkeynode, "gpio-key", 0);
if (gpiokey < 0) {
pr_info("of_get_named_gpio failed\n");
return -1;
}
ret = gpio_request(gpiokey, "pute-key-gpio");
if (ret) {
pr_info("gpio_request failed\n");
return -1;
}
ret = gpio_direction_input(gpiokey);
if (ret) {
pr_info("gpio_direction_input failed\n");
return -1;
}
#if 0
irqno = gpio_to_irq(gpiokey);
if (irqno < 0) {
pr_info("gpio_to_irq failed\n");
return -1;
}
#endif
irqno = of_irq_get(pkeynode, 0);
if (irqno < 0) {
pr_info("of_irq_get failed\n");
return -1;
}
ret = request_irq(irqno, key_irq_handler, IRQF_TRIGGER_FALLING, "pute-irq-key", NULL);
if (ret) {
pr_info("request_irq failed\n");
return -1;
}
pr_info("key_drv_init success\n");
return 0;
}
static __exit void key_drv_exit(void)
{
int ret = 0;
free_irq(irqno, NULL);
gpio_free(gpiokey);
ret = misc_deregister(&misc_key);
if (ret) {
pr_info("misc_deregister failed\n");
return;
}
pr_info("key_drv_exit success\n");
return;
}
module_init(key_drv_init);
module_exit(key_drv_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("pute");
key_app.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/select.h>
int fd = 0;
void handler(int signo)
{
int stat = 0;
read(fd, &stat, sizeof(stat));
printf("stat = %d\n", stat);
return;
}
int main(void)
{
int flags = 0;
int nready = 0;
int stat = 0;
fd_set rdfds;
fd_set tmpfds;
signal(SIGIO, handler);
fd = open("/dev/misc_key", O_RDWR);
if (-1 == fd)
{
perror("fail to open");
return -1;
}
#if 0
flags = fcntl(fd, F_GETFL); //获得文件描述符属性
flags |= O_ASYNC; //在文件描述符属性基础上加入异步IO属性
fcntl(fd, F_SETFL, flags); //重新设置文件描述符属性
fcntl(fd, F_SETOWN, getpid()); //产生时间发送给当前进程任务
#endif
FD_ZERO(&rdfds);
FD_SET(fd, &rdfds);
while (1)
{
tmpfds = rdfds;
nready = select(fd+1, &tmpfds, NULL, NULL, NULL);
if (-1 == nready)
{
perror("fail to select");
return -1;
}
if (FD_ISSET(fd, &tmpfds))
{
read(fd, &stat, sizeof(stat));
printf("stat = %d\n", stat);
}
}
close(fd);
return 0;
}
5.底半部
key_drv.c
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/of_address.h>
#include <linux/of.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/irqreturn.h>
#include <linux/of_irq.h>
#include <asm-generic/poll.h>
#include <linux/poll.h>
#include <linux/workqueue.h>
#include <linux/timer.h>
extern void msleep(unsigned int msecs);
static void tasklet_fun(unsigned long arg);
static struct device_node *pkeynode = NULL;
static int gpiokey = 0;
static int irqno = 0;
static wait_queue_head_t wq;
static int condition = 0;
static int status = 0;
struct fasync_struct *fapp = NULL;
static DECLARE_TASKLET(m_tasklet, tasklet_fun, 0);
static DEFINE_TIMER(m_timer, timeout_func, 0, 0);
static struct work_struct work_queue;
static ssize_t key_read(struct file *fp, char __user *puser, size_t n, loff_t *off)
{
unsigned long nret = 0;
condition = 0;
status = 0;
wait_event_interruptible(wq, condition);
nret = copy_to_user(puser, &status, sizeof(status));
if (nret) {
pr_info("copy_to_user failed\n");
return -1;
}
return sizeof(status);
}
static int key_fasync(int fd, struct file *fp, int on)
{
return fasync_helper(fd, fp, on, &fapp);
}
static unsigned int key_poll(struct file *fp, struct poll_table_struct *ptable)
{
unsigned int umask = 0;
poll_wait(fp, &wq, ptable);
if (0 == gpio_get_value(gpiokey)) {
umask |= POLLIN;
}
return umask;
}
static void tasklet_fun(unsigned long arg)
{
if (fapp != NULL) {
kill_fasync(&fapp, SIGIO, POLLIN);
}
status = 1;
condition = 1;
wake_up_interruptible(&wq);
return;
}
static void work_queue_fun(struct work_struct *work)
{
if (fapp != NULL) {
kill_fasync(&fapp, SIGIO, POLLIN);
}
status = 1;
condition = 1;
wake_up_interruptible(&wq);
return;
}
static struct file_operations fops = {
.owner = THIS_MODULE,
.read = key_read,
.fasync = key_fasync,
.poll = key_poll,
};
static struct miscdevice misc_key = {
.minor = MISC_DYNAMIC_MINOR,
.name = "misc_key",
.fops = &fops,
};
static irqreturn_t key_irq_handler(int irqno, void *pdev)
{
// tasklet_schedule(&m_tasklet);
schedule_work(&work_queue);
return IRQ_HANDLED;
}
static __init int key_drv_init(void)
{
int ret = 0;
init_waitqueue_head(&wq);
INIT_WORK(&work_queue, work_queue_fun);
ret = misc_register(&misc_key);
if (ret) {
pr_info("misc_register failed\n");
return -1;
}
pkeynode = of_find_node_by_path("/putekey");
if (NULL == pkeynode) {
pr_info("of_find_node_by_path failed\n");
return -1;
}
gpiokey = of_get_named_gpio(pkeynode, "gpio-key", 0);
if (gpiokey < 0) {
pr_info("of_get_named_gpio failed\n");
return -1;
}
ret = gpio_request(gpiokey, "pute-key-gpio");
if (ret) {
pr_info("gpio_request failed\n");
return -1;
}
ret = gpio_direction_input(gpiokey);
if (ret) {
pr_info("gpio_direction_input failed\n");
return -1;
}
#if 0
irqno = gpio_to_irq(gpiokey);
if (irqno < 0) {
pr_info("gpio_to_irq failed\n");
return -1;
}
#endif
irqno = of_irq_get(pkeynode, 0);
if (irqno < 0) {
pr_info("of_irq_get failed\n");
return -1;
}
ret = request_irq(irqno, key_irq_handler, IRQF_TRIGGER_FALLING, "pute-irq-key", NULL);
if (ret) {
pr_info("request_irq failed\n");
return -1;
}
pr_info("key_drv_init success\n");
return 0;
}
static __exit void key_drv_exit(void)
{
int ret = 0;
free_irq(irqno, NULL);
gpio_free(gpiokey);
ret = misc_deregister(&misc_key);
if (ret) {
pr_info("misc_deregister failed\n");
return;
}
pr_info("key_drv_exit success\n");
return;
}
module_init(key_drv_init);
module_exit(key_drv_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("pute");
key_app.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/select.h>
int fd = 0;
void handler(int signo)
{
int stat = 0;
read(fd, &stat, sizeof(stat));
printf("stat = %d\n", stat);
return;
}
int main(void)
{
int flags = 0;
int nready = 0;
int stat = 0;
fd_set rdfds;
fd_set tmpfds;
signal(SIGIO, handler);
fd = open("/dev/misc_key", O_RDWR);
if (-1 == fd)
{
perror("fail to open");
return -1;
}
#if 0
flags = fcntl(fd, F_GETFL); //获得文件描述符属性
flags |= O_ASYNC; //在文件描述符属性基础上加入异步IO属性
fcntl(fd, F_SETFL, flags); //重新设置文件描述符属性
fcntl(fd, F_SETOWN, getpid()); //产生时间发送给当前进程任务
FD_ZERO(&rdfds);
FD_SET(fd, &rdfds);
while (1)
{
tmpfds = rdfds;
nready = select(fd+1, &tmpfds, NULL, NULL, NULL);
if (-1 == nready)
{
perror("fail to select");
return -1;
}
if (FD_ISSET(fd, &tmpfds))
{
read(fd, &stat, sizeof(stat));
printf("stat = %d\n", stat);
}
}
#endif
while (1)
{
read(fd, &stat, sizeof(stat));
printf("stat = %d\n", stat);
}
close(fd);
return 0;
}
五、总结
1. 驱动中中断的使用?
1)中断注册:驱动程序通过request_irq函数向内核注册中断处理程序,指定中断号、处理函数、触发方式(如边沿触发或电平触发)及设备标识。
2)中断触发:硬件设备通过中断线向CPU发送信号,CPU暂停当前任务,保存上下文并跳转到中断处理程序。
3)中断处理:中断处理函数(ISR)需快速完成关键操作(如读取设备状态),通常分为上半部(立即处理)和下半部(延迟处理,如通过tasklet或工作队列)。
4)中断注销:驱动卸载时需释放中断资源。
2.驱动中的四种IO模型怎么实现?
1)阻塞式IO模型
等待队列实现阻塞IO,事件发生后中断,来唤醒事件,返回应用层
2) 非阻塞IO
直接操作寄存器,应用层调用驱动层的read,不管什么情况,都会返回
3)异步IO
重写file_oprations里的fasync函数,事件发生时向应用层发送通知
4) 多路复用IO执行
重写file_oprations里的poll函数,应用层的select就会调用驱动层的poll,没有产生事件的时候会把他加入到监听表里并且关联一个等待队列,事件发生的时候,会把等待队列唤醒,那么表中的事件就会被会唤醒,只需要返回事件即可。
更多推荐


所有评论(0)