Linux设备驱动程序——高级字符驱动程序操作(poll机制)
Posted 贺二公子
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux设备驱动程序——高级字符驱动程序操作(poll机制)相关的知识,希望对你有一定的参考价值。
原文地址:Linux设备驱动程序——高级字符驱动程序操作(poll机制) -wangtisheng-ChinaUnix博客
###############################################################################################################################
阻塞与非阻塞访问是I/O 操作的两种不同模式,前者在I/O 操作暂时不可进行时会让进程睡眠,后者则不然。(可参考上一篇阻塞型I/O示例)在设备驱动中阻塞I/O 一般基于等待队列来实现,等待队列可用于同步驱动中事件发生的先后顺序。使用非阻塞I/O 的应用程序也可借助轮询函数来查询设备是否能立即被访问,用户空间调用select()和poll()接口,设备驱动提供poll()函数。设备驱动的poll()本身不会阻塞,但是poll()和select()系统调用则会阻塞地等待文件描述符集合中的至少一个可访问或超时。-----引自宋宝华《Linux设备驱动开发详解》。
使用非阻塞I/O的应用程序经常使用poll、select和epoll系统调用,其本质是都允许进程决定是否可以对一个或多个打开的文件做非阻塞的读取或写入。
当应用程序需要进行对多文件读写时,若某个文件没有准备好,则系统会处于读写阻塞的状态,并影响了其他文件的读写。为了避免这种情况,在必须使用多输入输出流又不想阻塞在它们任何一个上的应用程序常将非阻塞 I/O 和 poll(System V)、select(BSD Unix)、 epoll(linux2.5.45开始)系统调用配合使用。当poll函数返回时,会给出一个文件是否可读写的标志,应用程序根据不同的标志读写相应的文件,实现非阻塞的读写。这些系统调用功能相同: 允许进程来决定它是否可读或写一个或多个文件而不阻塞。这些调用也可阻塞进程直到任何一个给定集合的文件描述符可用来读或写。这些调用都需要来自设备驱动中poll 方法的支持,poll返回不同的标志,告诉主进程文件是否可以读写,其原型(定义在 )------引自Linux设备驱动程序学习(5)-高级字符驱动程序操作[(2)阻塞型I/O和休眠]-tekkamanninja-ChinaUnix博客。
unsigned int (*poll) (struct file *filp, struct poll_table *wait);
当用户空间程序在驱动程序关联的文件描述符上执行poll、select或epoll系统调用时,该驱动方法将被调用。
该设备方法分为两步:
① 在一个或多个可指示查询状态变化的等待队列上调用 poll_wait. 如果没有文件描述符可用来执行 I/O, 则内核使进程在传递到该系统调用的所有文件描述符对应的等待队列上等待。
② 返回一个用来描述操作是否可以立即无阻塞执行的位掩码。
poll_table_struct结构,用于在内核中实现oll、select以及epoll系统调用,它被传递给驱动程序方法,以使每个可以唤醒进程和修改poll操作状态的等待队列都可以被驱动程序装在。驱动程序想poll_table结构添加一个等待队列用poll_wait函数:
static inline void poll_wait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p) ;
poll方法执行的第二项任务是返回描述符哪个操作可以立即执行的位掩码。例如,如果设备已经数据就绪,一个read可以立刻完成而不用休眠,位掩码的具体定义可参考《Linux设备驱动程序》。
一个示例:
该示例来自韦东山老师的视频教程第一期,下载地址百度网盘-链接不存在,其针对JZ2440进行了讲解,我将其修改到了我的mini2440上,教程中提到了正在上传…重新上传取消poll机制分析.pdf可供参考。完整驱动及测试例程正在上传…重新上传取消poll.rar
buttons_int_poll_drv.c
点击(此处)折叠或打开
- #include <linux/module.h>
- #include <linux/kernel.h>
- #include <linux/fs.h>
- #include <linux/init.h>
- #include <linux/device.h>
- #include <linux/irq.h>
- #include <linux/interrupt.h>
- #include <linux/poll.h>
- #include <asm/arch/regs-gpio.h>
- #include <asm/hardware.h>
- #include <linux/uaccess.h>
- #define DEVICE_NAME "mini2440_buttons" //设备名称
- static struct class *buttons_int_poll_drv_class;
- static struct class_device *buttons_int_poll_drv_class_dev;
- static DECLARE_WAIT_QUEUE_HEAD(button_waitq); //等待队列:当没有按键被按下时,如果有进程调用buttons_int_poll_drv_read函数,它将休眠
- static volatile int ev_press = 0; //中断事件标志, 中断服务程序将它置1,s3c24xx_buttons_read将它清0
- static unsigned char key_val; //保存键值
- //引脚描述结构体
- struct pin_desc
- unsigned int pin;
- unsigned int key_value;
- ;
- //管脚数组,按键赋键值,按下时是0x01,松开是0x81(最高位为1)
- struct pin_desc pins_desc[6] =
- S3C2410_GPG0, 0x01,
- S3C2410_GPG3, 0x02,
- S3C2410_GPG5, 0x03,
- S3C2410_GPG6, 0x04,
- S3C2410_GPG7, 0x05,
- S3C2410_GPG11,0x06,
- ;
- //确定按键值
- static irqreturn_t buttons_irq(int irq, void *dev_id)
- //printk("irq = %d\\n",irq);
- struct pin_desc *pindesc = (struct pin_desc *)dev_id;
- unsigned int pinval;
- pinval = s3c2410_gpio_getpin(pindesc->pin);
- if(pinval)
- //松开
- key_val = 0x80 | pindesc->key_value;
- else
- //按下
- key_val = pindesc->key_value;
- ev_press = 1;
- wake_up_interruptible(&button_waitq);
- return IRQ_HANDLED;
- static int buttons_int_poll_drv_open(struct inode *inode, struct file *file)
- int ret;
- ret = request_irq(IRQ_EINT8, buttons_irq,IRQT_BOTHEDGE,"KEY1",&pins_desc[0]); //pins_desc[0]传递给buttons_irq
- ret = request_irq(IRQ_EINT11,buttons_irq,IRQT_BOTHEDGE,"KEY2",&pins_desc[1]);
- ret = request_irq(IRQ_EINT13,buttons_irq,IRQT_BOTHEDGE,"KEY3",&pins_desc[2]);
- ret = request_irq(IRQ_EINT14,buttons_irq,IRQT_BOTHEDGE,"KEY4",&pins_desc[3]);
- ret = request_irq(IRQ_EINT15,buttons_irq,IRQT_BOTHEDGE,"KEY5",&pins_desc[4]);
- ret = request_irq(IRQ_EINT19,buttons_irq,IRQT_BOTHEDGE,"KEY6",&pins_desc[5]);
- return 0;
- ssize_t buttons_int_poll_drv_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
- int ret;
- if(size != 1) //读取单个按键
- return -EINVAL;
- wait_event_interruptible(button_waitq, ev_press); //如果没有按键动作,休眠,让出CPU
- ret = copy_to_user(buf, &key_val, 1); //返回键值
- ev_press = 0;
- return 1;
- int buttons_int_poll_drv_close(struct inode *inode, struct file *file)
- free_irq(IRQ_EINT8, &pins_desc[0]);
- free_irq(IRQ_EINT11,&pins_desc[1]);
- free_irq(IRQ_EINT13,&pins_desc[2]);
- free_irq(IRQ_EINT14,&pins_desc[3]);
- free_irq(IRQ_EINT15,&pins_desc[4]);
- free_irq(IRQ_EINT19,&pins_desc[5]);
- return 0;
- static unsigned buttons_int_poll_drv_poll(struct file *file, poll_table *wait)
- unsigned int mask = 0;
- poll_wait(file, &button_waitq, wait); // 不会立即休眠
- if (ev_press)
- mask |= POLLIN | POLLRDNORM; //标识设备可以读取了
- return mask;
- static struct file_operations buttons_int_poll_drv_fops =
- .owner = THIS_MODULE,
- .open = buttons_int_poll_drv_open,
- .read = buttons_int_poll_drv_read,
- .release = buttons_int_poll_drv_close,
- .poll = buttons_int_poll_drv_poll,
- ;
- int major;
- static int __init buttons_int_poll_drv_init(void)
- major = register_chrdev(0, DEVICE_NAME, &buttons_int_poll_drv_fops);
- if(major < 0)
- printk(DEVICE_NAME "\\t can't register major number!\\n");
- buttons_int_poll_drv_class = class_create(THIS_MODULE, DEVICE_NAME);
- buttons_int_poll_drv_class_dev = class_device_create(buttons_int_poll_drv_class, NULL, MKDEV(major, 0), NULL, DEVICE_NAME); /* /dev/mini2440_buttons */
- printk(DEVICE_NAME"\\t initialized!\\n");
- return 0;
- static void __exit buttons_int_poll_drv_exit(void)
- unregister_chrdev(major, DEVICE_NAME);
- class_device_unregister(buttons_int_poll_drv_class_dev);
- class_destroy(buttons_int_poll_drv_class);
- module_init(buttons_int_poll_drv_init);
- module_exit(buttons_int_poll_drv_exit);
- MODULE_LICENSE("GPL");
点击(此处)折叠或打开
- #include <fcntl.h>
- #include <stdio.h>
- #include <poll.h>
- //int poll(struct pollfd *fds, nfds_t nfds, int timeout);
- int main(int argc,char **argv)
- int fd;
- unsigned char key_val;
- int ret;
- struct pollfd fds[1];
- fd = open("/dev/mini2440_buttons", O_RDWR);
- if(fd < 0)
- printf("can't open\\n");
- fds[0].fd = fd;
- fds[0].events = POLLIN;
- while(1)
- //ssize_t read(int fd, void *buf, size_t count);
- ret = poll(fds,1,5000);
- if(ret == 0)
- printf("Time Out!\\n");
- else
- read(fd,&key_val,1);
- printf("key_val = 0x%x\\n",key_val);
- return 0;
结果验证了 进程被唤醒的条件有2:一是“一定时间”到了,二是被驱动程序唤醒。程序中并未加入按键防抖动。
以上是关于Linux设备驱动程序——高级字符驱动程序操作(poll机制)的主要内容,如果未能解决你的问题,请参考以下文章
Linux高级字符设备驱动 poll方法(select多路监控原理与实现)