52 阻塞与非阻塞

一、阻塞io和非阻塞io

  • 这里的“IO”并不是我们学习 STM32 或者其他单片机的时候所说的“GPIO”(也就是引脚)。
    这里的 IO 指的是 Input/Output,也就是输入/输出,是应用程序对驱动 / 设备的输入/输出操作。

  • 当应用程序对设备驱动进行操作的时候,如果不能获取到设备资源:
    阻塞式 IO:就会将 应用程序对应的线程 挂起,直到设备资源可以获取为止。当资源可用的时候唤醒任务 / 线程,注意是让应用程序休眠。
    应用程序调用 read 函数从设备中读取数据,当设备不可用或数据未准备好的时候就会进入到休眠态。在read处暂停,等设备可用的时候就会从休眠态唤醒,然后从设备中读取数据返回给应用程序。
    在这里插入图片描述
    fd = open("/dev/xxx_dev", O_RDWR); /* 默认阻塞方式打开 */

  • 当应用程序对设备驱动进行操作的时候,如果不能获取到设备资源:
    非阻塞 IO:应用程序对应的线程不会挂起,它要么一直轮询等待(直到设备资源可以使用),要么就直接放弃。一般会有超时处理机制
    应用程序使用非阻塞访问方式从设备读取数据,当设备不可用或数据未准备好的时候会立即向内核返回一个错误码,表示数据读取失败。应用程序会再次重新读取数据,这样一直往复循环,直到数据读取成功。
    在这里插入图片描述
    fd = open("/dev/xxx_dev", O_RDWR | O_NONBLOCK); /* 非阻塞方式打开,这样从设备中读取数据的时候就是非阻塞方式的了。*/

二、阻塞IO

  • 等待队列头
    阻塞访问最大的好处就是当设备文件不可操作的时候进程可以进入休眠态,这样可以将CPU 资源让出来。但是,当设备文件可以操作的时候就必须唤醒进程,一般在中断函数里面完成唤醒工作。
    Linux 内核提供了等待队列(wait queue)来实现阻塞进程的唤醒工作,如果我们要在驱动中使用等待队列,必须创建并初始化一个等待队列头,等待队列头使用结构体 wait_queue_head_t 表示
    函数原型:void init_waitqueue_head(wait_queue_head_t *q) 参数 q 就是要初始化的等待队列头。
    也可以使用宏 DECLARE_WAIT_QUEUE_HEAD 来一次性完成等待队列头的定义和初始化
    用法:DECLARE_WAIT_QUEUE_HEAD( 等待队列头名称 )

  • 等待队列项
    等待队列头就是一个等待队列的头部,每个访问设备的进程都是一个队列项
    当设备不可用的时候就要将这些进程对应的等待队列项添加到等待队列里面。结构体 wait_queue_t 表示等待队列项
    等待队列项数据类型:typedef struct __wait_queue wait_queue_t;
    使用宏 DECLARE_WAITQUEUE 定义并初始化一个等待队列项,宏的内容如下:DECLARE_WAITQUEUE(name, tsk)
    name 就是等待队列项的名字, tsk 表示这个等待队列项属于哪个任务(进程),一般设置为 current , 在 Linux 内 核 中 current 相 当 于 一 个 全 局 变 量 , 表 示 当 前 进 程 。
    因 此 宏 DECLARE_WAITQUEUE 就是给当前正在运行的进程创建并初始化了一个等待队列项。

  • 等待队列项添加/移除等待队列头
    当设备不可访问的时候就需要将进程对应的等待队列项添加到前面创建的等待队列头中,只有添加到等待队列头中以后进程才能进入休眠态。当设备可以访问以后再将进程对应的等待队列项从等待队列头中移除即可
    等待队列项添加 API 函数如下:
    void add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait)
    函数参数和返回值含义如下:
    q : 等待队列项要加入的等待队列头。
    wait :要加入的等待队列项。
    等待队列项移除 API 函数如下:
    void remove_wait_queue(wait_queue_head_t *q, wait_queue_t *wait)
    函数参数和返回值含义如下:
    q: 要删除的等待队列项所处的等待队列头。
    wait:要删除的等待队列项。
    返回值:无。

  • 等待唤醒
    当设备可以使用的时候就要唤醒进入休眠态的进程,唤醒可以使用如下两个函数:
    void wake_up(wait_queue_head_t *q)
    void wake_up_interruptible(wait_queue_head_t *q)
    参数 q 就是要唤醒的等待队列头,这两个函数会将这个等待队列头中的所有进程都唤醒。
    wake_up 函数可以唤醒处于 TASK_INTERRUPTIBLE 和 TASK_UNINTERRUPTIBLE 状态的进程
    wake_up_interruptible 函数只能唤醒处于 TASK_INTERRUPTIBLE 状态的进程

  • 等待事件
    除了主动唤醒以外,也可以设置等待队列等待某个事件,当这个事件满足以后才能接收唤醒,和等待事件有关的 API 函数如表 52.1.2.1 所示:

函数描述
wait_event(wq, condition)等待以 wq 为等待队列头的等待队列被唤醒,前提是 condition 条件必须满足(为真),否则一直阻塞 。 此 函 数 会 将 进 程 设 置 为TASK_UNINTERRUPTIBLE 状态
wait_event_timeout(wq, condition, timeout)功能和 wait_event 类似,但是此函数可以添加超时时间,以 jiffies 为单位。此函数有返回值,如 果返回 0 的话表示超时时间到,而且 condition为假。为 1 的话表示 condition 为真,也就是条件满足了。
wait_event_interruptible(wq, condition)与 wait_event 函数类似,但是此函数将进程设置为 TASK_INTERRUPTIBLE,就是可以被信号打断
wait_event_interruptible_timeout(wq,condition, timeout)与 wait_event_timeout 函数类似,此函数也将进程设置为 TASK_INTERRUPTIBLE,可以被信号打断

三、非阻塞IO

  • 如果用户应用程序以非阻塞的方式访问设备,设备驱动程序就要提供非阻塞的处理方式,也就是轮询

  • poll、 epoll 和 select 可以用于处理轮询,应用程序通过 select、 epoll 或 poll 函数查询设备是否可以操作,如果可以操作的话就从设备读取或者向设备写入数据。
    当应用程序调用 select、 epoll 或 poll 函数的时候设备驱动程序中的 poll 函数就会执行,因此需要在设备驱动程序中编写 poll 函数。

  • select 函数
    select 函数原型如下:

int select(int nfds,
			fd_set *readfds,
			fd_set *writefds,
			fd_set *exceptfds,
			struct timeval *timeout);

函数参数和返回值含义如下:
nfds : 所要监视的这三类文件描述集合中, 最大文件描述符加 1。
readfds、 writefds 和 exceptfds:这三个描述符集合指针,这三个参数指明了关心哪些描述符、需要满足哪些条件等等,这三个参数都是 fd_set 类型的, fd_set 类型变量的每一个位都代表了一个文件描述符。
readfds 用于监视指定描述符集的读变化,也就是监视这些文件是否可以读取,只要这些集合里面有一个文件可以读取那么 seclect 就会返回一个大于 0 的值表示文件可以读取。如果没有文件可以读取,那么就会根据 timeout 参数来判断是否超时。可以将 readfs
设置为 NULL,表示不关心任何文件的读变化。
writefs 用于监视这些文件是否可以进行写操作
exceptfds 用于监视这些文件的异常。
timeout:超时时间,当我们调用 select 函数等待某些文件描述符可以设置超时时间,超时时间使用结构体 timeval 表示,结构体定义如下所示:

struct timeval {
	long tv_sec; /* 秒 */
	long tv_usec; /* 微妙 */
};

当 timeout 为 NULL 的时候就表示无限期的等待
返回值:
0,表示的话就表示超时发生,但是没有任何文件描述符可以进行操作;
-1,发生错误;
其他值,可以进行操作的文件描述符个数

  • 比如我们现在要从一个设备文件中读取数据,那么就可以定义一个 fd_set 变量,这个变量要传递给参数 readfds。当我们定义好一个 fd_set 变量以后可以使用如下所示几个宏进行操作:
void FD_ZERO(fd_set *set)
void FD_SET(int fd, fd_set *set)
void FD_CLR(int fd, fd_set *set)
int FD_ISSET(int fd, fd_set *set)

FD_ZERO 用于将 fd_set 变量的所有位都清零
FD_SET 用于将 fd_set 变量的某个位置 1,也就是向 fd_set 添加一个文件描述符,参数 fd 就是要加入的文件描述符。
FD_CLR 用户将 fd_set 变量的某个位清零,也就是将一个文件描述符从 fd_set 中删除,参数 fd 就是要删除的文件描述符。
FD_ISSET 用于测试一个文件是否属于某个集合,参数 fd 就是要判断的文件描述符。

  • 使用 select 函数对某个设备驱动文件进行读非阻塞访问的操作示例如下所示
void main(void)
{
	int ret, fd; /* 要监视的文件描述符 */
	fd_set readfds; /* 读操作文件描述符集 */
	struct timeval timeout; /* 超时结构体 */

	fd = open("dev_xxx", O_RDWR | O_NONBLOCK); /* 非阻塞式访问 */

	FD_ZERO(&readfds); /* 清除 readfds */
	FD_SET(fd, &readfds); /* 将 fd 添加到 readfds 里面 */

	/* 构造超时时间 */
	timeout.tv_sec = 0;
	timeout.tv_usec = 500000; /* 500ms */

	ret = select(fd + 1, &readfds, NULL, NULL, &timeout);
	switch (ret) 
	{
		case 0: /* 超时 */
			printf("timeout!\r\n");
			break;
		case -1: /* 错误 */
			printf("error!\r\n");
			break;
		default: /* 可以读取数据 */
			if(FD_ISSET(fd, &readfds)) 
			{ 
				/* 判断是否为 fd 文件描述符 */
				/* 使用 read 函数读取数据 */
			}
			break;
	}
}
  • poll 函数
    在单个线程中, select 函数能够监视的文件描述符数量有限制,一般为 1024,可以修改内核将监视的文件描述符数量改大,但是这样会降低效率!这个时候就可以使用 poll 函数,poll 函数本质上和 select 没有太大的差别,但是 poll 函数没有最大文件描述符限制, Linux 应用程序中 poll 函数原型如下所示:
int poll(struct pollfd *fds,
			nfds_t nfds,
			int timeout);

函数参数和返回值含义如下:
fds: 要监视的文件描述符集合以及要监视的事件,为一个数组,数组元素都是结构体 pollfd
类型的, pollfd 结构体如下所示:

struct pollfd 
{
	int fd; /* 文件描述符 */
	short events; /* 请求的事件 */
	short revents; /* 返回的事件,由内核返回,不需要去写 */
};

fd 是要监视的文件描述符,如果 fd 无效的话那么 events 监视事件也就无效,并且 revents 返回 0。
events 是要监视的事件,可监视的事件类型如下所示:

POLLIN 有数据可以读取。
POLLPRI 有紧急的数据需要读取。
POLLOUT 可以写数据。
POLLERR 指定的文件描述符发生错误。
POLLHUP 指定的文件描述符挂起。
POLLNVAL 无效的请求。
POLLRDNORM 等同于 POLLIN

revents 是返回参数,也就是返回的事件, 由 Linux 内核设置具体的返回事件。
nfds: poll 函数要监视的文件描述符数量。
timeout: 超时时间,单位为 ms。
返回值:
返回 revents 域中不为 0 的 pollfd 结构体个数,也就是发生事件或错误的文件描述符数量;
0,超时;
-1,发生错误,并且设置 errno 为错误类型。

  • 使用 poll 函数对某个设备驱动文件进行读非阻塞访问的操作示例如下所示:
void main(void)
{
	int ret;
	int fd; /* 要监视的文件描述符 */
	struct pollfd fds;

	fd = open(filename, O_RDWR | O_NONBLOCK); /* 非阻塞式访问 */

	/* 构造结构体 */
	fds.fd = fd;
	fds.events = POLLIN; /* 监视数据是否可以读取 */

	ret = poll(&fds, 1, 500); /* 轮询文件是否可操作,超时 500ms */
	if (ret > 0) 
	{ 
		/* 数据有效 */
		......
		/* 读取数据 */
		......
	} 
	else if (ret == 0) 
	{ 
		/* 超时 */
		......
	} 
	else if (ret < 0) 
	{ 
		/* 错误 */
		......
	}
}
  • epoll 函数
    传统的 selcet 和 poll 函数都会随着所监听的 fd 数量的增加,出现效率低下的问题,而且poll 函数每次必须遍历所有的描述符来检查就绪的描述符,这个过程很浪费时间。为此, epoll应运而生, epoll 就是为处理大并发而准备的,一般常常在网络编程中使用 epoll 函数
    应用程序需要先使用 epoll_create 函数 创建一个 epoll 句柄, epoll_create 函数原型如下:
    int epoll_create(int size)
    函数参数和返回值含义如下:
    size: 从 Linux2.6.8 开始此参数已经没有意义了,随便填写一个大于 0 的值就可以。
    返回值:
    epoll 句柄
    如果为-1 的话表示创建失败。
    epoll 句柄创建成功以后使用 epoll_ctl 函数 向其中添加要监视的文件描述符 以及 监视的事件
    epoll_ctl 函数原型如下所示:
int epoll_ctl(int epfd,
				int op,
				int fd,
				struct epoll_event *event);

函数参数和返回值含义如下:
epfd: 要操作的 epoll 句柄,也就是使用 epoll_create 函数创建的 epoll 句柄。
op: 表示要对 epfd(epoll 句柄)进行的操作,可以设置为:

EPOLL_CTL_ADD 向 epfd 添加文件参数 fd 表示的描述符。
EPOLL_CTL_MOD 修改参数 fd 的 event 事件。
EPOLL_CTL_DEL 从 epfd 中删除 fd 描述符。

fd:要监视的文件描述符。
event: 要监视的事件类型,为 epoll_event 结构体类型指针, epoll_event 结构体类型如下所示

struct epoll_event {
		uint32_t events; /* epoll 事件 */
		epoll_data_t data; /* 用户数据 */
};

结构体 epoll_event 的 events 成员变量表示要监视的事件,可选的事件如下所示:

EPOLLIN 有数据可以读取。
EPOLLOUT 可以写数据。
EPOLLPRI 有紧急的数据需要读取。
EPOLLERR 指定的文件描述符发生错误。
EPOLLHUP 指定的文件描述符挂起。
EPOLLET 设置 epoll 为边沿触发,默认触发模式为水平触发。
EPOLLONESHOT 一次性的监视,当监视完成以后还需要再次监视某个 fd,那么就需要将 fd 重新添加到 epoll 里面。

上面这些事件可以进行**“或”操作**,也就是说可以设置同时监视多个事件
返回值:
0,成功;
-1,失败,并且设置 errno 的值为相应的错误码。
一切都设置好以后应用程序就可以通过 epoll_wait 函数来等待事件的发生,类似 select 函数。
epoll_wait 函数原型如下所示:

int epoll_wait(int epfd,
				struct epoll_event *events,
				int maxevents,
				int timeout);

函数参数和返回值含义如下:
epfd: 要等待的 epoll。
events: 指向 epoll_event 结构体的数组,当有事件发生的时候 Linux 内核会填写 events,调用者可以根据 events 判断发生了哪些事件。
maxevents: events 数组大小,必须大于 0。
timeout: 超时时间,单位为 ms。
返回值:
0,超时;
-1,错误;
其他值,准备就绪的文件描述符数量。
epoll 更多的是用在大规模的并发服务器上,因为在这种场合下 select 和 poll 并不适合。
涉及到的文件描述符(fd)比较少的时候就适合用 selcet 和 poll,本章我们就使用 sellect 和 poll 这两个函数

  • Linux 驱动下的 poll 操作函数
    当应用程序调用 select 或 poll 函数来对驱动程序进行非阻塞访问的时候,驱动程序 file_operations 操作集中的 poll 函数就会执行。所以驱动程序的编写者需要提供对应的 poll 函数, poll 函数原型如下所示:
unsigned int (*poll) (struct file *filp, struct poll_table_struct *wait)
函数参数和返回值含义如下:
filp: 要打开的设备文件(文件描述符)。
wait: 结构体 poll_table_struct 类型指针, 由应用程序传递进来的。一般将此参数传递给poll_wait 函数。
返回值:向应用程序返回设备或者资源状态,可以返回的资源状态如下:
POLLIN 有数据可以读取。
POLLPRI 有紧急的数据需要读取。
POLLOUT 可以写数据。
POLLERR 指定的文件描述符发生错误。
POLLHUP 指定的文件描述符挂起。
POLLNVAL 无效的请求。
POLLRDNORM 等同于 POLLIN,普通数据可读

我们需要在驱动程序的 poll 函数中调用 poll_wait 函数, poll_wait 函数不会引起阻塞,只是将应用程序添加到 poll_table 中
poll_wait 函数原型如下:

void poll_wait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p)
参数 wait_address 是要添加到 poll_table 中的等待队列头
参数 p 就是 poll_table,就是 file_operations 中 poll 函数的 wait 参数。

驱动源码

1、wait_event

若不按button,测试app中的read将会一直阻塞,驱动中对应位置在imx6ulirq_read函数中的 wait_event_interruptible 处

#include<linux/module.h>
#include<linux/kernel.h>
#include<linux/init.h>
#include <linux/fs.h>
#include<linux/slab.h>
#include<linux/io.h>
#include<linux/uaccess.h>
#include<linux/cdev.h>
#include<linux/device.h>
#include<linux/of.h>
#include<linux/of_address.h>
#include<linux/of_irq.h>
#include<linux/gpio.h>
#include<linux/of_gpio.h>
#include<linux/atomic.h>
#include<linux/timer.h>
#include<linux/jiffies.h>
#include<linux/interrupt.h>
#include<linux/wait.h>
#include<linux/ide.h>

#define     IRQ_CNT         1
#define     IMX6ULIRQ_NAME  "imx6ulirq"
#define     KEY0_ACTIVE     1
#define     KEY0_INACTIVE   2

struct irq_keydesc
{
    int gpio;
    int irqnum;
    u8 value;
    char name[8];
    irqreturn_t (*handler )(int, void *);
};

struct imx6ulirq_dev
{
    dev_t devid;
    u32 major;
    u32 minor;
    struct cdev cdev;
    struct class *class;
    struct device *device;
    struct device_node *nd;
    atomic_t keyvalue;
    atomic_t releasekey;
    struct irq_keydesc irqkey;
    struct timer_list timer;
    int timer_period;
	// 等待队列头
    wait_queue_head_t r_wait;
};

struct imx6ulirq_dev imx6ulirqdev;

static int imx6ulirq_open(struct inode *inode, struct file *filp);
static int imx6ulirq_release(struct inode *inode, struct file *filp);
static ssize_t imx6ulirq_read(struct file *filp, 
                                char __user *buf, 
                                size_t cnt, 
                                loff_t *offt);
static irqreturn_t key0_handler(int irq, void *dev1);

static const struct file_operations imx6ulirqdev_fops = 
{
    .owner = THIS_MODULE,
    .open = imx6ulirq_open,
    .release = imx6ulirq_release, 
    .read = imx6ulirq_read,
};

static int imx6ulirq_open(struct inode *inode, struct file *filp)
{
    filp->private_data = &imx6ulirqdev;
    return 0;
}

static ssize_t imx6ulirq_read(struct file *filp, 
                                char __user *buf, 
                                size_t cnt, 
                                loff_t *offt)
{
    
    int ret = 0;
    u8 keyvalue = 0;
    u8 releasekey = 0;
    struct imx6ulirq_dev *dev = filp->private_data;
    printk("%s(%d):\n", __FILE__, __LINE__);
    // 运行到此函数就会阻塞(应用层对应函数也会阻塞),等待唤醒。
    // 唤醒后从此函数后面开始执行
    // 只有当第二个参数为真的时候才接收唤醒
    wait_event_interruptible(dev->r_wait, atomic_read(&dev->releasekey));

    keyvalue = atomic_read(&dev->keyvalue);
    releasekey = atomic_read(&dev->releasekey);
    printk("%s(%d):\n", __FILE__, __LINE__);
    if(releasekey)
    {
        if (keyvalue == KEY0_INACTIVE)
        {
            printk("%s(%d):\n", __FILE__, __LINE__);
            ret = copy_to_user(buf, &keyvalue, sizeof(keyvalue));
            keyvalue = KEY0_INACTIVE;
        }
        atomic_set(&dev->releasekey, 0);
    }
    return 0;
}

static int imx6ulirq_release(struct inode *inode, struct file *filp)
{
    return 0;
}


static int keygpio_init(struct imx6ulirq_dev *dev)
{
    int ret = 0;
    // 1.
    dev->nd = of_find_node_by_path("/gpiokey");
    if(NULL == dev->nd)
    {
        printk("%s(%d):fail\n", __FILE__, __LINE__);
        ret = -1;
        goto fail_nd;
    }
    // 2.
    dev->irqkey.gpio = of_get_named_gpio(dev->nd, "key-gpios", 0);
    if(dev->irqkey.gpio < 0)
    {
        ret = -1;
        printk("%s(%d):fail\n", __FILE__, __LINE__);
        goto fail_gpio;
    }
    // 3.
    memset(dev->irqkey.name, 0, sizeof(dev->irqkey.name));
    memcpy(dev->irqkey.name,"key_0", strlen("key_0"));
    ret = gpio_request(dev->irqkey.gpio, dev->irqkey.name);
    if(ret)
    {
        ret = -1;
        printk("%s(%d):fail\n", __FILE__, __LINE__);
        goto fail_request;
    }
    // 4.
    ret = gpio_direction_input(dev->irqkey.gpio);
    if(ret)
    {
        ret = -1;
        printk("%s(%d):fail\n", __FILE__, __LINE__);
        goto fail_setdir;
    }
    // 5.
#if 1
    dev->irqkey.irqnum = gpio_to_irq(dev->irqkey.gpio);
#else
    dev->irqkey.irqnum = irq_of_parse_and_map(dev->nd, 0);
#endif
    dev->irqkey.handler = key0_handler;
    dev->irqkey.value = KEY0_ACTIVE;
    ret = request_irq(dev->irqkey.irqnum,
                        dev->irqkey.handler,
                        IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING,
                        dev->irqkey.name,
                        &imx6ulirqdev); // 此参数传递给中断处理函数第二个参数
    if(ret < 0)
    {
        ret = -1;
        printk("%s(%d):fail\n", __FILE__, __LINE__);
        goto fail_irq;
    }
    

    return 0;

fail_irq:
fail_setdir:
    gpio_free(dev->irqkey.gpio);
fail_request:
fail_gpio:
fail_nd:
    return ret;
}

// 中断处理函数
// 第二个参数由 request_irq 最后一个参数传递
static irqreturn_t key0_handler(int irq, void *dev1)
{
    struct imx6ulirq_dev *dev = dev1;
    dev->timer.data = (u32 )dev1;
    mod_timer(&dev->timer, jiffies+msecs_to_jiffies(10));

    return IRQ_HANDLED;
}

static void timer_func(unsigned long arg)
{
    int val = 0;
    struct imx6ulirq_dev *dev = (void *)arg;
    val = gpio_get_value(dev->irqkey.gpio);
    if(0 == val)
    {
        printk("Pushed.\n");
        atomic_set(&dev->keyvalue, KEY0_ACTIVE);
    }
    else if(1 == val)
    {
        printk("Released.\n");
        atomic_set(&dev->keyvalue, KEY0_INACTIVE);
        atomic_set(&dev->releasekey, 1);
    }

    if(atomic_read(&dev->releasekey))
    {
    	// 此处的唤醒操作不能省略
        wake_up(&dev->r_wait);
    }
}


static int __init imx6ulirq_init(void)
{
    int ret = 0;

    // 1.
    imx6ulirqdev.major = 0;
    if(imx6ulirqdev.major)
    {
        imx6ulirqdev.devid = MKDEV(imx6ulirqdev.major, 0);
        ret = register_chrdev_region(imx6ulirqdev.devid, 1, IMX6ULIRQ_NAME);
    }
    else
    {
        ret = alloc_chrdev_region(&imx6ulirqdev.devid, 0, 1, IMX6ULIRQ_NAME);
        imx6ulirqdev.major = MAJOR(imx6ulirqdev.devid);
        imx6ulirqdev.minor = MAJOR(imx6ulirqdev.devid);
    }
    if(ret < 0)
    {
        printk("%s(%d):fail\n", __FILE__, __LINE__);
        goto fail_devid;
    }

    //2
    imx6ulirqdev.cdev.owner = THIS_MODULE;
    cdev_init(&imx6ulirqdev.cdev, &imx6ulirqdev_fops);
    ret = cdev_add(&imx6ulirqdev.cdev, imx6ulirqdev.devid, 1);
    if(ret < 0)
    {
        printk("%s(%d):fail\n", __FILE__, __LINE__);
        goto fail_cdev;
    }

    //3
    imx6ulirqdev.class = class_create(THIS_MODULE, IMX6ULIRQ_NAME);
    if(IS_ERR(imx6ulirqdev.class))
    {
        printk("%s(%d):fail\n", __FILE__, __LINE__);
        ret = PTR_ERR(imx6ulirqdev.class);
        goto fail_class;
    }

    imx6ulirqdev.device = device_create(imx6ulirqdev.class, 
                                    NULL, 
                                    imx6ulirqdev.devid, 
                                    NULL, 
                                    IMX6ULIRQ_NAME);
    if(IS_ERR(imx6ulirqdev.device))
    {
        printk("%s(%d):fail\n", __FILE__, __LINE__);
        ret = PTR_ERR(imx6ulirqdev.device);
        goto fail_device;
    }

    // 4.
    ret = keygpio_init(&imx6ulirqdev);
    if(ret < 0)
    {
        printk("%s(%d):fail\n", __FILE__, __LINE__);
        goto fail_keyinit;
    }

    //5
    init_timer(&imx6ulirqdev.timer);
    imx6ulirqdev.timer.function = timer_func;
    imx6ulirqdev.timer.data = (unsigned long )&imx6ulirqdev;

    //6
    atomic_set(&imx6ulirqdev.keyvalue, KEY0_INACTIVE);
    atomic_set(&imx6ulirqdev.keyvalue, 0);
    //6 初始化等待队列头
    init_waitqueue_head(&imx6ulirqdev.r_wait);
    return 0;


fail_keyinit:
fail_device:
    class_destroy(imx6ulirqdev.class);
fail_class:
    cdev_del(&imx6ulirqdev.cdev);
fail_cdev:
    unregister_chrdev_region(imx6ulirqdev.devid, 1);
fail_devid:
    
    return ret;

}

static void __exit imx6ulirq_exit(void)
{
    free_irq(imx6ulirqdev.irqkey.irqnum, NULL);
    gpio_free(imx6ulirqdev.irqkey.gpio);
    del_timer_sync(&imx6ulirqdev.timer);
    cdev_del(&imx6ulirqdev.cdev);
    unregister_chrdev_region(imx6ulirqdev.devid, IRQ_CNT);
    device_destroy(imx6ulirqdev.class, imx6ulirqdev.devid);
    class_destroy(imx6ulirqdev.class);
}

module_init(imx6ulirq_init);
module_exit(imx6ulirq_exit);
MODULE_LICENSE("GPL");

2、add_wait_queue

一个队列项

#include<linux/module.h>
#include<linux/kernel.h>
#include<linux/init.h>
#include <linux/fs.h>
#include<linux/slab.h>
#include<linux/io.h>
#include<linux/uaccess.h>
#include<linux/cdev.h>
#include<linux/device.h>
#include<linux/of.h>
#include<linux/of_address.h>
#include<linux/of_irq.h>
#include<linux/gpio.h>
#include<linux/of_gpio.h>
#include<linux/atomic.h>
#include<linux/timer.h>
#include<linux/jiffies.h>
#include<linux/interrupt.h>
#include<linux/wait.h>
#include<linux/ide.h>

#define     IRQ_CNT         1
#define     IMX6ULIRQ_NAME  "imx6ulirq"
#define     KEY0_ACTIVE     1
#define     KEY0_INACTIVE   2

struct irq_keydesc
{
    int gpio;
    int irqnum;
    u8 value;
    char name[8];
    irqreturn_t (*handler )(int, void *);
};

struct imx6ulirq_dev
{
    dev_t devid;
    u32 major;
    u32 minor;
    struct cdev cdev;
    struct class *class;
    struct device *device;
    struct device_node *nd;
    atomic_t keyvalue;
    atomic_t releasekey;
    struct irq_keydesc irqkey;
    struct timer_list timer;
    int timer_period;

    wait_queue_head_t r_wait;
};

struct imx6ulirq_dev imx6ulirqdev;

static int imx6ulirq_open(struct inode *inode, struct file *filp);
static int imx6ulirq_release(struct inode *inode, struct file *filp);
static ssize_t imx6ulirq_read(struct file *filp, 
                                char __user *buf, 
                                size_t cnt, 
                                loff_t *offt);
static irqreturn_t key0_handler(int irq, void *dev1);

static const struct file_operations imx6ulirqdev_fops = 
{
    .owner = THIS_MODULE,
    .open = imx6ulirq_open,
    .release = imx6ulirq_release, 
    .read = imx6ulirq_read,
};

static int imx6ulirq_open(struct inode *inode, struct file *filp)
{
    filp->private_data = &imx6ulirqdev;
    return 0;
}

static ssize_t imx6ulirq_read(struct file *filp, 
                                char __user *buf, 
                                size_t cnt, 
                                loff_t *offt)
{
    
    int ret = 0;
    u8 keyvalue = 0;
    u8 releasekey = 0;
    struct imx6ulirq_dev *dev = filp->private_data;
    printk("%s(%d):\n", __FILE__, __LINE__);
  	// 定义并初始化一个等待队列项,current 相 当 于 一 个 全 局 变 量 , 表 示 当 前 进 程 
  	// 就是给当前正在运行的进程创建并初始化了一个等待队列项
    DECLARE_WAITQUEUE(wait, current);
    printk("%s(%d):\n", __FILE__, __LINE__);
	// 将队列项添加到等待队列头
    add_wait_queue(&dev->r_wait, &wait);
    // 将当前进程设置为可被打断
    __set_current_state(TASK_INTERRUPTIBLE);
    schedule();// Schedule()函数最主要作用就是从就绪进程中选择一个优先级最高的进程来代替当前进程运行。
	printk("%s(%d):\n", __FILE__, __LINE__);
    // 唤醒后从这运行 ,先判断是否是被信号唤醒的
    // 下面这个 signal_pending 用于判断当前进程是否有信号需要处理
    if(signal_pending(current))
    {
    	// 返回非0值表示有信号处理,是被信号唤醒的
		ret = -ERESTARTSYS;
    	goto data_error;
     }
    keyvalue = atomic_read(&dev->keyvalue);
    releasekey = atomic_read(&dev->releasekey);
    printk("%s(%d):\n", __FILE__, __LINE__);
    
    if(releasekey)
    {
        if (keyvalue == KEY0_INACTIVE)
        {
            printk("%s(%d):\n", __FILE__, __LINE__);
            ret = copy_to_user(buf, &keyvalue, sizeof(keyvalue));
            keyvalue = KEY0_INACTIVE;
        }
        atomic_set(&dev->releasekey, 0);
    }
  

data_error:
	// 设置当前进程/线程为运行状态
    __set_current_state(TASK_RUNNING);
    // 将 wait 队列项从等待队列中删除
    remove_wait_queue(&dev->r_wait, &wait);
    return ret;
}

static int imx6ulirq_release(struct inode *inode, struct file *filp)
{
    return 0;
}


static int keygpio_init(struct imx6ulirq_dev *dev)
{
    int ret = 0;
    // 1.
    dev->nd = of_find_node_by_path("/gpiokey");
    if(NULL == dev->nd)
    {
        printk("%s(%d):fail\n", __FILE__, __LINE__);
        ret = -1;
        goto fail_nd;
    }
    // 2.
    dev->irqkey.gpio = of_get_named_gpio(dev->nd, "key-gpios", 0);
    if(dev->irqkey.gpio < 0)
    {
        ret = -1;
        printk("%s(%d):fail\n", __FILE__, __LINE__);
        goto fail_gpio;
    }
    // 3.
    memset(dev->irqkey.name, 0, sizeof(dev->irqkey.name));
    memcpy(dev->irqkey.name,"key_0", strlen("key_0"));
    ret = gpio_request(dev->irqkey.gpio, dev->irqkey.name);
    if(ret)
    {
        ret = -1;
        printk("%s(%d):fail\n", __FILE__, __LINE__);
        goto fail_request;
    }
    // 4.
    ret = gpio_direction_input(dev->irqkey.gpio);
    if(ret)
    {
        ret = -1;
        printk("%s(%d):fail\n", __FILE__, __LINE__);
        goto fail_setdir;
    }
    // 5.
#if 1
    dev->irqkey.irqnum = gpio_to_irq(dev->irqkey.gpio);
#else
    dev->irqkey.irqnum = irq_of_parse_and_map(dev->nd, 0);
#endif
    dev->irqkey.handler = key0_handler;
    dev->irqkey.value = KEY0_ACTIVE;
    ret = request_irq(dev->irqkey.irqnum,
                        dev->irqkey.handler,
                        IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING,
                        dev->irqkey.name,
                        &imx6ulirqdev);
    if(ret < 0)
    {
        ret = -1;
        printk("%s(%d):fail\n", __FILE__, __LINE__);
        goto fail_irq;
    }
    

    return 0;

fail_irq:
fail_setdir:
    gpio_free(dev->irqkey.gpio);
fail_request:
fail_gpio:
fail_nd:
    return ret;
}

static irqreturn_t key0_handler(int irq, void *dev1)
{
    struct imx6ulirq_dev *dev = dev1;
    dev->timer.data = (u32 )dev1;
    mod_timer(&dev->timer, jiffies+msecs_to_jiffies(10));

    return IRQ_HANDLED;
}

static void timer_func(unsigned long arg)
{
    int val = 0;
    struct imx6ulirq_dev *dev = (void *)arg;
    val = gpio_get_value(dev->irqkey.gpio);
    if(0 == val)
    {
        printk("Pushed.\n");
        atomic_set(&dev->keyvalue, KEY0_ACTIVE);
    }
    else if(1 == val)
    {
        printk("Released.\n");
        atomic_set(&dev->keyvalue, KEY0_INACTIVE);
        atomic_set(&dev->releasekey, 1);
    }

    if(atomic_read(&dev->releasekey))
    {
        wake_up(&dev->r_wait);
    }
 
}


static int __init imx6ulirq_init(void)
{
    int ret = 0;

    // 1.
    imx6ulirqdev.major = 0;
    if(imx6ulirqdev.major)
    {
        imx6ulirqdev.devid = MKDEV(imx6ulirqdev.major, 0);
        ret = register_chrdev_region(imx6ulirqdev.devid, 1, IMX6ULIRQ_NAME);
    }
    else
    {
        ret = alloc_chrdev_region(&imx6ulirqdev.devid, 0, 1, IMX6ULIRQ_NAME);
        imx6ulirqdev.major = MAJOR(imx6ulirqdev.devid);
        imx6ulirqdev.minor = MAJOR(imx6ulirqdev.devid);
    }
    if(ret < 0)
    {
        printk("%s(%d):fail\n", __FILE__, __LINE__);
        goto fail_devid;
    }

    //2
    imx6ulirqdev.cdev.owner = THIS_MODULE;
    cdev_init(&imx6ulirqdev.cdev, &imx6ulirqdev_fops);
    ret = cdev_add(&imx6ulirqdev.cdev, imx6ulirqdev.devid, 1);
    if(ret < 0)
    {
        printk("%s(%d):fail\n", __FILE__, __LINE__);
        goto fail_cdev;
    }

    //3
    imx6ulirqdev.class = class_create(THIS_MODULE, IMX6ULIRQ_NAME);
    if(IS_ERR(imx6ulirqdev.class))
    {
        printk("%s(%d):fail\n", __FILE__, __LINE__);
        ret = PTR_ERR(imx6ulirqdev.class);
        goto fail_class;
    }

    imx6ulirqdev.device = device_create(imx6ulirqdev.class, 
                                    NULL, 
                                    imx6ulirqdev.devid, 
                                    NULL, 
                                    IMX6ULIRQ_NAME);
    if(IS_ERR(imx6ulirqdev.device))
    {
        printk("%s(%d):fail\n", __FILE__, __LINE__);
        ret = PTR_ERR(imx6ulirqdev.device);
        goto fail_device;
    }

    // 4.
    ret = keygpio_init(&imx6ulirqdev);
    if(ret < 0)
    {
        printk("%s(%d):fail\n", __FILE__, __LINE__);
        goto fail_keyinit;
    }

    //5
    init_timer(&imx6ulirqdev.timer);
    imx6ulirqdev.timer.function = timer_func;
    imx6ulirqdev.timer.data = (unsigned long )&imx6ulirqdev;

    //6
    atomic_set(&imx6ulirqdev.keyvalue, KEY0_INACTIVE);
    atomic_set(&imx6ulirqdev.keyvalue, 0);
    //6.若要在驱动中使用等待队列,必须创建并初始化一个等待队列头
    init_waitqueue_head(&imx6ulirqdev.r_wait);
    return 0;


fail_keyinit:
fail_device:
    class_destroy(imx6ulirqdev.class);
fail_class:
    cdev_del(&imx6ulirqdev.cdev);
fail_cdev:
    unregister_chrdev_region(imx6ulirqdev.devid, 1);
fail_devid:
    
    return ret;

}

static void __exit imx6ulirq_exit(void)
{
    free_irq(imx6ulirqdev.irqkey.irqnum, NULL);
    gpio_free(imx6ulirqdev.irqkey.gpio);
    del_timer_sync(&imx6ulirqdev.timer);
    cdev_del(&imx6ulirqdev.cdev);
    unregister_chrdev_region(imx6ulirqdev.devid, IRQ_CNT);
    device_destroy(imx6ulirqdev.class, imx6ulirqdev.devid);
    class_destroy(imx6ulirqdev.class);
}

module_init(imx6ulirq_init);
module_exit(imx6ulirq_exit);
MODULE_LICENSE("GPL");

1、2、的测试app

#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
#include "linux/ioctl.h"

int main(int argc, char *argv[])
{
	int fd;
	int ret = 0;
	char *filename;
	unsigned char data;
	
	if (argc != 2) {
		printf("Error Usage!\r\n");
		return -1;
	}

	filename = argv[1];
	fd = open(filename, O_RDWR);
	if (fd < 0) {
		printf("Can't open file %s\r\n", filename);
		return -1;
	}
    printf("%s(%d):\n", __FILE__, __LINE__);
	while (1) {
		ret = read(fd, &data, sizeof(data));
		if (ret < 0) {  /* 数据读取错误或者无效 */
			
		} else {		/* 数据读取正确 */
			if (data)	/* 读取到数据 */
				printf("\n\nkey value = %#X\r\n", data);
		}
	}
	close(fd);
	return ret;
}

3、非阻塞 采用select

若应用层app的select未设置超时时间,驱动中的imx6ulirq_poll只执行一次
app调用select,在驱动中会执行imx6ulirq_poll,若应用层app的select设置了超时时间,那么驱动中的imx6ulirq_poll会被执行2次
第一次:应用层app 调用 select 之后立即执行
第二次:当有文件可读 / 可写 / 发生错误 时 ,或者超时时间到时。会再执行一次 imx6ulirq_poll

#include<linux/module.h>
#include<linux/kernel.h>
#include<linux/init.h>
#include <linux/fs.h>
#include<linux/slab.h>
#include<linux/io.h>
#include<linux/uaccess.h>
#include<linux/cdev.h>
#include<linux/device.h>
#include<linux/of.h>
#include<linux/of_address.h>
#include<linux/of_irq.h>
#include<linux/gpio.h>
#include<linux/of_gpio.h>
#include<linux/atomic.h>
#include<linux/timer.h>
#include<linux/jiffies.h>
#include<linux/interrupt.h>
#include<linux/wait.h>
#include<linux/ide.h>
#include<linux/poll.h>

#define     IRQ_CNT         1
#define     IMX6ULIRQ_NAME  "imx6ulirq"
#define     KEY0_ACTIVE     1
#define     KEY0_INACTIVE   2

struct irq_keydesc
{
    int gpio;
    int irqnum;
    u8 value;
    char name[8];
    irqreturn_t (*handler )(int, void *);
};

struct imx6ulirq_dev
{
    dev_t devid;
    u32 major;
    u32 minor;
    struct cdev cdev;
    struct class *class;
    struct device *device;
    struct device_node *nd;
    atomic_t keyvalue;
    atomic_t releasekey;
    struct irq_keydesc irqkey;
    struct timer_list timer;
    int timer_period;

    wait_queue_head_t r_wait;
};

struct imx6ulirq_dev imx6ulirqdev;

static int imx6ulirq_open(struct inode *inode, struct file *filp);
static int imx6ulirq_release(struct inode *inode, struct file *filp);
static ssize_t imx6ulirq_read(struct file *filp, 
                                char __user *buf, 
                                size_t cnt, 
                                loff_t *offt);
static u32 imx6ulirq_poll(struct file *filp, struct poll_table_struct *wait);
static irqreturn_t key0_handler(int irq, void *dev1);

static const struct file_operations imx6ulirqdev_fops = 
{
    .owner = THIS_MODULE,
    .open = imx6ulirq_open,
    .release = imx6ulirq_release, 
    .read = imx6ulirq_read,
    .poll = imx6ulirq_poll,
};

static int imx6ulirq_open(struct inode *inode, struct file *filp)
{
    filp->private_data = &imx6ulirqdev;
    return 0;
}

static ssize_t imx6ulirq_read(struct file *filp, 
                                char __user *buf, 
                                size_t cnt, 
                                loff_t *offt)
{
    
    int ret = 0;
    u8 keyvalue = 0;
    u8 releasekey = 0;
    struct imx6ulirq_dev *dev = filp->private_data;
    printk("%s(%d):\n", __FILE__, __LINE__);
  
    if(filp->f_flags & O_NONBLOCK)
    {
        if(0 == atomic_read(&dev->releasekey))
        {
            return -EAGAIN;
        }
    }
    else
    {
        wait_event_interruptible(dev->r_wait, atomic_read(&dev->releasekey));
    }
    


    keyvalue = atomic_read(&dev->keyvalue);
    releasekey = atomic_read(&dev->releasekey);
    printk("%s(%d):\n", __FILE__, __LINE__);
    if(releasekey)
    {
        if (keyvalue == KEY0_INACTIVE)
        {
            printk("%s(%d):\n", __FILE__, __LINE__);
            ret = copy_to_user(buf, &keyvalue, sizeof(keyvalue));
            keyvalue = KEY0_INACTIVE;
        }
        atomic_set(&dev->releasekey, 0);
    }

   
    return ret;
}

static int imx6ulirq_release(struct inode *inode, struct file *filp)
{
    return 0;
}

static u32 imx6ulirq_poll(struct file *filp, struct poll_table_struct  *wait)
{
    int mask = 0;
    struct imx6ulirq_dev *dev = filp->private_data;

    poll_wait(filp, &dev->r_wait, wait);

    // 按键按下并释放了才可读
    if(atomic_read(&dev->releasekey))
    {
    	// 返回POLL_IN
        mask = POLLIN | POLLRDNORM; 
    }

    return mask;
}

static int keygpio_init(struct imx6ulirq_dev *dev)
{
    int ret = 0;
    // 1.
    dev->nd = of_find_node_by_path("/gpiokey");
    if(NULL == dev->nd)
    {
        printk("%s(%d):fail\n", __FILE__, __LINE__);
        ret = -1;
        goto fail_nd;
    }
    // 2.
    dev->irqkey.gpio = of_get_named_gpio(dev->nd, "key-gpios", 0);
    if(dev->irqkey.gpio < 0)
    {
        ret = -1;
        printk("%s(%d):fail\n", __FILE__, __LINE__);
        goto fail_gpio;
    }
    // 3.
    memset(dev->irqkey.name, 0, sizeof(dev->irqkey.name));
    memcpy(dev->irqkey.name,"key_0", strlen("key_0"));
    ret = gpio_request(dev->irqkey.gpio, dev->irqkey.name);
    if(ret)
    {
        ret = -1;
        printk("%s(%d):fail\n", __FILE__, __LINE__);
        goto fail_request;
    }
    // 4.
    ret = gpio_direction_input(dev->irqkey.gpio);
    if(ret)
    {
        ret = -1;
        printk("%s(%d):fail\n", __FILE__, __LINE__);
        goto fail_setdir;
    }
    // 5.
#if 1
    dev->irqkey.irqnum = gpio_to_irq(dev->irqkey.gpio);
#else
    dev->irqkey.irqnum = irq_of_parse_and_map(dev->nd, 0);
#endif
    dev->irqkey.handler = key0_handler;
    dev->irqkey.value = KEY0_ACTIVE;
    ret = request_irq(dev->irqkey.irqnum,
                        dev->irqkey.handler,
                        IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING,
                        dev->irqkey.name,
                        &imx6ulirqdev);
    if(ret < 0)
    {
        ret = -1;
        printk("%s(%d):fail\n", __FILE__, __LINE__);
        goto fail_irq;
    }

    return 0;

fail_irq:
fail_setdir:
    gpio_free(dev->irqkey.gpio);
fail_request:
fail_gpio:
fail_nd:
    return ret;
}

static irqreturn_t key0_handler(int irq, void *dev1)
{
    struct imx6ulirq_dev *dev = dev1;
    dev->timer.data = (u32 )dev1;
    mod_timer(&dev->timer, jiffies+msecs_to_jiffies(10));

    return IRQ_HANDLED;
}

static void timer_func(unsigned long arg)
{
    int val = 0;
    struct imx6ulirq_dev *dev = (void *)arg;
    val = gpio_get_value(dev->irqkey.gpio);
    if(0 == val)
    {
        printk("Pushed.\n");
        atomic_set(&dev->keyvalue, KEY0_ACTIVE);
    }
    else if(1 == val)
    {
        printk("Released.\n");
        atomic_set(&dev->keyvalue, KEY0_INACTIVE);
        atomic_set(&dev->releasekey, 1);
    }

    if(atomic_read(&dev->releasekey))
    {
        wake_up(&dev->r_wait);
    }
 
}

static int __init imx6ulirq_init(void)
{
    int ret = 0;

    // 1.
    imx6ulirqdev.major = 0;
    if(imx6ulirqdev.major)
    {
        imx6ulirqdev.devid = MKDEV(imx6ulirqdev.major, 0);
        ret = register_chrdev_region(imx6ulirqdev.devid, 1, IMX6ULIRQ_NAME);
    }
    else
    {
        ret = alloc_chrdev_region(&imx6ulirqdev.devid, 0, 1, IMX6ULIRQ_NAME);
        imx6ulirqdev.major = MAJOR(imx6ulirqdev.devid);
        imx6ulirqdev.minor = MAJOR(imx6ulirqdev.devid);
    }
    if(ret < 0)
    {
        printk("%s(%d):fail\n", __FILE__, __LINE__);
        goto fail_devid;
    }

    //2
    imx6ulirqdev.cdev.owner = THIS_MODULE;
    cdev_init(&imx6ulirqdev.cdev, &imx6ulirqdev_fops);
    ret = cdev_add(&imx6ulirqdev.cdev, imx6ulirqdev.devid, 1);
    if(ret < 0)
    {
        printk("%s(%d):fail\n", __FILE__, __LINE__);
        goto fail_cdev;
    }

    //3
    imx6ulirqdev.class = class_create(THIS_MODULE, IMX6ULIRQ_NAME);
    if(IS_ERR(imx6ulirqdev.class))
    {
        printk("%s(%d):fail\n", __FILE__, __LINE__);
        ret = PTR_ERR(imx6ulirqdev.class);
        goto fail_class;
    }

    imx6ulirqdev.device = device_create(imx6ulirqdev.class, 
                                    	NULL, 
                                    	imx6ulirqdev.devid, 
                                    	NULL, 
                                    	IMX6ULIRQ_NAME);
    if(IS_ERR(imx6ulirqdev.device))
    {
        printk("%s(%d):fail\n", __FILE__, __LINE__);
        ret = PTR_ERR(imx6ulirqdev.device);
        goto fail_device;
    }

    // 4.
    ret = keygpio_init(&imx6ulirqdev);
    if(ret < 0)
    {
        printk("%s(%d):fail\n", __FILE__, __LINE__);
        goto fail_keyinit;
    }

    //5
    init_timer(&imx6ulirqdev.timer);
    imx6ulirqdev.timer.function = timer_func;
    imx6ulirqdev.timer.data = (unsigned long )&imx6ulirqdev;

    //6
    atomic_set(&imx6ulirqdev.keyvalue, KEY0_INACTIVE);
    atomic_set(&imx6ulirqdev.keyvalue, 0);
    //6
    init_waitqueue_head(&imx6ulirqdev.r_wait);
    return 0;


fail_keyinit:
fail_device:
    class_destroy(imx6ulirqdev.class);
fail_class:
    cdev_del(&imx6ulirqdev.cdev);
fail_cdev:
    unregister_chrdev_region(imx6ulirqdev.devid, 1);
fail_devid:
    
    return ret;

}

static void __exit imx6ulirq_exit(void)
{
    free_irq(imx6ulirqdev.irqkey.irqnum, NULL);
    gpio_free(imx6ulirqdev.irqkey.gpio);
    del_timer_sync(&imx6ulirqdev.timer);
    cdev_del(&imx6ulirqdev.cdev);
    unregister_chrdev_region(imx6ulirqdev.devid, IRQ_CNT);
    device_destroy(imx6ulirqdev.class, imx6ulirqdev.devid);
    class_destroy(imx6ulirqdev.class);
}

module_init(imx6ulirq_init);
module_exit(imx6ulirq_exit);
MODULE_LICENSE("GPL");

select app

若应用层app的poll未设置超时时间,驱动中的imx6ulirq_poll只执行一次
app调用poll,在驱动中会执行imx6ulirq_poll,若应用层app的poll设置了超时时间,那么驱动中的imx6ulirq_poll会被执行2次
第一次:应用层app 调用 poll 之后立即执行
第二次:当有文件可读 / 可写时 ,或者超时时间到时。会再执行一次 imx6ulirq_poll

#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
#include "linux/ioctl.h"
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

/*
 * @description		: main主程序
 * @param - argc 	: argv数组元素个数
 * @param - argv 	: 具体参数
 * @return 			: 0 成功;其他 失败
 */
int main(int argc, char *argv[])
{
	int fd;
	int ret = 0;
	char *filename;
	fd_set readfds;
	struct timeval timeout;
	unsigned char data;
	
	if (argc != 2) {
		printf("Error Usage!\r\n");
		return -1;
	}

	filename = argv[1];
	fd = open(filename, O_RDWR | O_NONBLOCK);
	if (fd < 0) {
		printf("Can't open file %s\r\n", filename);
		return -1;
	}
    printf("%s(%d):\n", __FILE__, __LINE__);
	while (1) {
		FD_ZERO(&readfds);
		FD_SET(fd, &readfds);
		timeout.tv_sec = 0;
		timeout.tv_usec = 500000;
		ret = select(fd + 1,
						&readfds,
						NULL,
						NULL,
						&timeout);
		switch(ret)
		{
			case 0:
				printf("App read time out\n\n");
				break;
			case -1:
				printf("App read error\n\n");
				break;
			default:
				if(FD_ISSET(fd, &readfds))
				{
					ret = read(fd, &data, sizeof(data));
					if(ret < 0)
					{

					}
					else
					{
						if(data)
						{
							printf("\n\nKey value = %#x\n", data);
						}
					}
				}
				break;
			
		}
	}
	close(fd);
	return ret;
}

4、非阻塞 使用poll

#include<linux/module.h>
#include<linux/kernel.h>
#include<linux/init.h>
#include <linux/fs.h>
#include<linux/slab.h>
#include<linux/io.h>
#include<linux/uaccess.h>
#include<linux/cdev.h>
#include<linux/device.h>
#include<linux/of.h>
#include<linux/of_address.h>
#include<linux/of_irq.h>
#include<linux/gpio.h>
#include<linux/of_gpio.h>
#include<linux/atomic.h>
#include<linux/timer.h>
#include<linux/jiffies.h>
#include<linux/interrupt.h>
#include<linux/wait.h>
#include<linux/ide.h>
#include<linux/poll.h>

#define     IRQ_CNT         1
#define     IMX6ULIRQ_NAME  "imx6ulirq"
#define     KEY0_ACTIVE     1
#define     KEY0_INACTIVE   2

struct irq_keydesc
{
    int gpio;
    int irqnum;
    u8 value;
    char name[8];
    irqreturn_t (*handler )(int, void *);
};

struct imx6ulirq_dev
{
    dev_t devid;
    u32 major;
    u32 minor;
    struct cdev cdev;
    struct class *class;
    struct device *device;
    struct device_node *nd;
    atomic_t keyvalue;
    atomic_t releasekey;
    struct irq_keydesc irqkey;
    struct timer_list timer;
    int timer_period;

    wait_queue_head_t r_wait;
};

struct imx6ulirq_dev imx6ulirqdev;

static int imx6ulirq_open(struct inode *inode, struct file *filp);
static int imx6ulirq_release(struct inode *inode, struct file *filp);
static ssize_t imx6ulirq_read(struct file *filp, 
                                char __user *buf, 
                                size_t cnt, 
                                loff_t *offt);
static u32 imx6ulirq_poll(struct file *filp, struct poll_table_struct *wait);
static irqreturn_t key0_handler(int irq, void *dev1);

static const struct file_operations imx6ulirqdev_fops = 
{
    .owner = THIS_MODULE,
    .open = imx6ulirq_open,
    .release = imx6ulirq_release, 
    .read = imx6ulirq_read,
    .poll = imx6ulirq_poll,
};

static int imx6ulirq_open(struct inode *inode, struct file *filp)
{
    filp->private_data = &imx6ulirqdev;
    return 0;
}

static ssize_t imx6ulirq_read(struct file *filp, 
                                char __user *buf, 
                                size_t cnt, 
                                loff_t *offt)
{
    
    int ret = 0;
    u8 keyvalue = 0;
    u8 releasekey = 0;
    struct imx6ulirq_dev *dev = filp->private_data;
    printk("%s(%d):\n", __FILE__, __LINE__);
  
    if(filp->f_flags & O_NONBLOCK)
    {
        if(0 == atomic_read(&dev->releasekey))
        {
            return -EAGAIN;
        }
    }
    else
    {
        wait_event_interruptible(dev->r_wait, atomic_read(&dev->releasekey));
    }
    


    keyvalue = atomic_read(&dev->keyvalue);
    releasekey = atomic_read(&dev->releasekey);
    printk("%s(%d):\n", __FILE__, __LINE__);
    if(releasekey)
    {
        if (keyvalue == KEY0_INACTIVE)
        {
            printk("%s(%d):\n", __FILE__, __LINE__);
            ret = copy_to_user(buf, &keyvalue, sizeof(keyvalue));
            keyvalue = KEY0_INACTIVE;
        }
        atomic_set(&dev->releasekey, 0);
    }

   
    return ret;
}

static int imx6ulirq_release(struct inode *inode, struct file *filp)
{
    return 0;
}

static u32 imx6ulirq_poll(struct file *filp, struct poll_table_struct  *wait)
{
    int mask = 0;
    struct imx6ulirq_dev *dev = filp->private_data;

    poll_wait(filp, &dev->r_wait, wait);

    // pan duan shi fou ke du
    if(atomic_read(&dev->releasekey)) // anjian anxia,kedu
    {
        mask = POLLIN | POLLRDNORM; // fanhui POLLIN
    }

    return mask;
}

static int keygpio_init(struct imx6ulirq_dev *dev)
{
    int ret = 0;
    // 1.
    dev->nd = of_find_node_by_path("/gpiokey");
    if(NULL == dev->nd)
    {
        printk("%s(%d):fail\n", __FILE__, __LINE__);
        ret = -1;
        goto fail_nd;
    }
    // 2.
    dev->irqkey.gpio = of_get_named_gpio(dev->nd, "key-gpios", 0);
    if(dev->irqkey.gpio < 0)
    {
        ret = -1;
        printk("%s(%d):fail\n", __FILE__, __LINE__);
        goto fail_gpio;
    }
    // 3.
    memset(dev->irqkey.name, 0, sizeof(dev->irqkey.name));
    memcpy(dev->irqkey.name,"key_0", strlen("key_0"));
    ret = gpio_request(dev->irqkey.gpio, dev->irqkey.name);
    if(ret)
    {
        ret = -1;
        printk("%s(%d):fail\n", __FILE__, __LINE__);
        goto fail_request;
    }
    // 4.
    ret = gpio_direction_input(dev->irqkey.gpio);
    if(ret)
    {
        ret = -1;
        printk("%s(%d):fail\n", __FILE__, __LINE__);
        goto fail_setdir;
    }
    // 5.
#if 1
    dev->irqkey.irqnum = gpio_to_irq(dev->irqkey.gpio);
#else
    dev->irqkey.irqnum = irq_of_parse_and_map(dev->nd, 0);
#endif
    dev->irqkey.handler = key0_handler;
    dev->irqkey.value = KEY0_ACTIVE;
    ret = request_irq(dev->irqkey.irqnum,
                        dev->irqkey.handler,
                        IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING,
                        dev->irqkey.name,
                        &imx6ulirqdev);
    if(ret < 0)
    {
        ret = -1;
        printk("%s(%d):fail\n", __FILE__, __LINE__);
        goto fail_irq;
    }
    

    return 0;

fail_irq:
fail_setdir:
    gpio_free(dev->irqkey.gpio);
fail_request:
fail_gpio:
fail_nd:
    return ret;
}

static irqreturn_t key0_handler(int irq, void *dev1)
{
    struct imx6ulirq_dev *dev = dev1;
    dev->timer.data = (u32 )dev1;
    mod_timer(&dev->timer, jiffies+msecs_to_jiffies(10));

    return IRQ_HANDLED;
}

static void timer_func(unsigned long arg)
{
    int val = 0;
    struct imx6ulirq_dev *dev = (void *)arg;
    val = gpio_get_value(dev->irqkey.gpio);
    if(0 == val)
    {
        printk("Pushed.\n");
        atomic_set(&dev->keyvalue, KEY0_ACTIVE);
    }
    else if(1 == val)
    {
        printk("Released.\n");
        atomic_set(&dev->keyvalue, KEY0_INACTIVE);
        atomic_set(&dev->releasekey, 1);
    }

    if(atomic_read(&dev->releasekey))
    {
        wake_up(&dev->r_wait);
    }
 
}


static int __init imx6ulirq_init(void)
{
    int ret = 0;

    // 1.
    imx6ulirqdev.major = 0;
    if(imx6ulirqdev.major)
    {
        imx6ulirqdev.devid = MKDEV(imx6ulirqdev.major, 0);
        ret = register_chrdev_region(imx6ulirqdev.devid, 1, IMX6ULIRQ_NAME);
    }
    else
    {
        ret = alloc_chrdev_region(&imx6ulirqdev.devid, 0, 1, IMX6ULIRQ_NAME);
        imx6ulirqdev.major = MAJOR(imx6ulirqdev.devid);
        imx6ulirqdev.minor = MAJOR(imx6ulirqdev.devid);
    }
    if(ret < 0)
    {
        printk("%s(%d):fail\n", __FILE__, __LINE__);
        goto fail_devid;
    }

    //2
    imx6ulirqdev.cdev.owner = THIS_MODULE;
    cdev_init(&imx6ulirqdev.cdev, &imx6ulirqdev_fops);
    ret = cdev_add(&imx6ulirqdev.cdev, imx6ulirqdev.devid, 1);
    if(ret < 0)
    {
        printk("%s(%d):fail\n", __FILE__, __LINE__);
        goto fail_cdev;
    }

    //3
    imx6ulirqdev.class = class_create(THIS_MODULE, IMX6ULIRQ_NAME);
    if(IS_ERR(imx6ulirqdev.class))
    {
        printk("%s(%d):fail\n", __FILE__, __LINE__);
        ret = PTR_ERR(imx6ulirqdev.class);
        goto fail_class;
    }

    imx6ulirqdev.device = device_create(imx6ulirqdev.class, 
                                    NULL, 
                                    imx6ulirqdev.devid, 
                                    NULL, 
                                    IMX6ULIRQ_NAME);
    if(IS_ERR(imx6ulirqdev.device))
    {
        printk("%s(%d):fail\n", __FILE__, __LINE__);
        ret = PTR_ERR(imx6ulirqdev.device);
        goto fail_device;
    }

    // 4.
    ret = keygpio_init(&imx6ulirqdev);
    if(ret < 0)
    {
        printk("%s(%d):fail\n", __FILE__, __LINE__);
        goto fail_keyinit;
    }

    //5
    init_timer(&imx6ulirqdev.timer);
    imx6ulirqdev.timer.function = timer_func;
    imx6ulirqdev.timer.data = (unsigned long )&imx6ulirqdev;

    //6
    atomic_set(&imx6ulirqdev.keyvalue, KEY0_INACTIVE);
    atomic_set(&imx6ulirqdev.keyvalue, 0);
    //6
    init_waitqueue_head(&imx6ulirqdev.r_wait);
    return 0;


fail_keyinit:
fail_device:
    class_destroy(imx6ulirqdev.class);
fail_class:
    cdev_del(&imx6ulirqdev.cdev);
fail_cdev:
    unregister_chrdev_region(imx6ulirqdev.devid, 1);
fail_devid:
    
    return ret;

}

static void __exit imx6ulirq_exit(void)
{
    free_irq(imx6ulirqdev.irqkey.irqnum, NULL);
    gpio_free(imx6ulirqdev.irqkey.gpio);
    del_timer_sync(&imx6ulirqdev.timer);
    cdev_del(&imx6ulirqdev.cdev);
    unregister_chrdev_region(imx6ulirqdev.devid, IRQ_CNT);
    device_destroy(imx6ulirqdev.class, imx6ulirqdev.devid);
    class_destroy(imx6ulirqdev.class);
}

module_init(imx6ulirq_init);
module_exit(imx6ulirq_exit);
MODULE_LICENSE("GPL");

poll 测试app

#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
#include "linux/ioctl.h"
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <poll.h>


/***************************************************************
Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved.
文件名		: imx6uirqApp.c
作者	  	: 左忠凯
版本	   	: V1.0
描述	   	: 定时器测试应用程序
其他	   	: 无
使用方法	:./imx6uirqApp /dev/imx6uirq 打开测试App
论坛 	   	: www.openedv.com
日志	   	: 初版V1.0 2019/7/26 左忠凯创建
***************************************************************/

/*
 * @description		: main主程序
 * @param - argc 	: argv数组元素个数
 * @param - argv 	: 具体参数
 * @return 			: 0 成功;其他 失败
 */
int main(int argc, char *argv[])
{
	int fd;
	int ret = 0;
	char *filename;
	struct pollfd fds;
	unsigned char data;
	
	if (argc != 2) {
		printf("Error Usage!\r\n");
		return -1;
	}

	filename = argv[1];
	fd = open(filename, O_RDWR | O_NONBLOCK);
	if (fd < 0) {
		printf("Can't open file %s\r\n", filename);
		return -1;
	}
    printf("%s(%d):\n", __FILE__, __LINE__);
	while (1) {
		fds.fd = fd;
		fds.events = POLLIN;

		ret = poll(&fds, 1, 500 );

		switch(ret)
		{
			case 0:
				printf("App read time out\n\n");
				break;
			case -1:
				printf("App read error\n\n");
				break;
			default:
				if(fds.revents & POLLIN)
				{
					ret = read(fd, &data, sizeof(data));
					if(ret < 0)
					{

					}
					else
					{
						if(data)
						{
							printf("\n\nKey value = %#x\n", data);
						}
					}
				}
				break;
			
		}
	}
	close(fd);
	return ret;
}

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值