轮询操作:应用程序通常会使用select()系统调用查询是否可对设备进行无阻塞的访问。
该函数通过系统调用最终会引发设备驱动中的poll()函数被执行
该机制还可以实现一个用户进程对多个设备驱动文件的监测
轮询操作应用层接口函数介绍:
文件描述符集合的基本操作:文件描述符集合的变量的定义
fd_set fds;
清空描述符集合
FD_ZERO(fd_set *set);
加入一个文件描述符到集合中
FD_SET(int fd, fd_set *set);
文件描述符集合的辅助操作:
从集合中清除一个文件描述符
FD_CLR(int fd, fd_set *set);
判断文件描述符是否被置位
FD_ISSET(int fd, fd_set *set);
返回非0,表示置位
(该文件描述集合中有文件可进行读写操作,或产
生错误)
#include <sys/select.h>
在应用程序中调用的文件描述符监测函数
int select(int numfds,
fd_set *readfds,
fd_set *writefds,
fd_set *exceptfds,
struct timeval *timeout);
参数1 numfds: 待监听中最大描述符值加一
参数2 readfds: 监听读操作的文件描述符集合
参数3 writefds: 监听写操作的文件描述符集合
参数4 exceptfds:监听异常处理的文件描述符集合
参数5 timeout: 监听等待超时退出select()
轮询操作驱动层接口函数介绍:
poll()函数:为file_operation成员之一
static unsigned int poll(struct file *file,
struct poll_table_struct *wait)
参数file:是文件结构指针
参数wait:轮询表指针,管理着一系列等待列表
注:以上两个参数是由内核传递给poll函数
//添加等待队列到wait参数指定的轮询列表中
void poll_wait(struct file *filp,
wait_queue_heat_t *wq, poll_table *wait);
poll_wait()将可能引起文件状态变化的进程添加到轮询列
表,由内核去监听进程状态的变化,不会阻塞进程
一旦进程有变化(wake_up),内核就会自动去调用poll()而 poll()是返回给select()的, 所以当进程被唤醒poll(应该将状态掩码返回给 select(),从而select()退出阻塞。 完成一次监测,poll函数被调用一次或两次
第一次为用户执行select函数时被执行
第二次调用poll为内核监测到进程的wake_up操作时或进程 休眠时间到唤醒再或被信号唤醒时
poll函数返回的状态掩码
可读状态掩码
POLLIN:有数据可读
POLLRDNORM:有普通数据可读
POLLRDBAND:有优先数据可读POLLPRI:有紧迫数据可读
可写状态掩码
POLLOUT:写数据不会导致阻塞POLLWRNORM:写普通数据不会导致阻塞POLLWRBAND:写优先数据不会导致阻塞POLLMSG/SIGPOLL:消息可用
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/select.h>
int main()
{
char *devname1 = "/dev/key8_eint";
char *devname2 = "/dev/key2_eint";
int fd1,fd2,fd_max;
unsigned char key;
int retval;
fd1 = open(devname1, O_RDONLY);
if (fd1 < 0){
perror("open key_eint2_poll");
exit(1);
}
fd2 = open(devname2, O_RDONLY);
if (fd2 < 0){
perror("open key_eint3_poll");
exit(1);
}
while(1){
fd_set rds;
int ret;
FD_ZERO(&rds);
FD_SET(fd1, &rds);
FD_SET(fd2, &rds);
if(fd1 > fd2)
fd_max = fd1;
else
fd_max = fd2;
/*使用select监听设备描述符是否有变化*/
retval = select(fd_max + 1, &rds, NULL,NULL, NULL);
switch(retval){
case -1 :
perror("select");
exit(1);
break;
case 0 :
printf("Timeout.\n");
break;
default:
printf("the ret is %d\n",ret);
if (FD_ISSET(fd1, &rds)) /*检查是否有数据可读*/
{
ret = read(fd1,&key, sizeof(key));
printf("the key = %d\n",key);
}
else if (FD_ISSET(fd2, &rds)) /*检查是否有数据可读*/
{
ret = read(fd2,&key, sizeof(key));
printf("the key = %d\n",key);
}
}
}
close(fd1);
close(fd2);
}
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/irq.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <mach/gpio.h>
#include <mach/regs-gpio.h>
#include <linux/poll.h> //poll_wait()
#define EINT_DEVICE_ID 1
#define DRIVER_NAME "key8_eint"
#define err(msg) printk(KERN_ERR "%s: " msg "\n", DRIVER_NAME)
#define __debug(fmt, arg...) printk(KERN_DEBUG fmt, ##arg)
#define GPH3CON (unsigned long)(S5PV210_GPH3_BASE+ 0x00)
#define GPH3DAT (unsigned long)(S5PV210_GPH3_BASE + 0x04)
#define GPH2UP (unsigned long)(S5PV210_GPH2_BASE + 0x08)
static int major = 0;
static int minor = 0;
struct class *key_class;
static struct device *key_device;
/*定义等待队列头,该等待队列头属于该驱动程序*/
static wait_queue_head_t wait_queue;
static unsigned char key;
irqreturn_t buttons_interrupt(int irq, void *dev_id)
{
key = (unsigned int)dev_id;
wake_up_interruptible(&wait_queue); //唤醒等待队列
return IRQ_HANDLED;
}
static void key_io_port_init(void)
{
unsigned long reg_val;
reg_val = readl(GPH3CON);
reg_val &= ~((0x0f<<0) | (0x0f<<4));
reg_val |= ((0x01<<0) | (0x01<<4));
writel(reg_val, GPH3CON);
reg_val = readl(GPH3DAT);
reg_val &= ~((0x01<<0) | (0x01<<1));
writel(reg_val, GPH3DAT);
reg_val = readl(GPH2UP);
reg_val &= ~(0x03<<8);
reg_val |= 0x02<<8;
writel(reg_val, GPH2UP);
}
static ssize_t key_read(struct file *filp, char *buf, size_t count, loff_t *f_pos)
{
int key_num;
int cpy_len;
int retval;
key_num = key; //读取键值
key = 0;
cpy_len = min(sizeof(key_num), count);
retval = copy_to_user(buf, &key_num, cpy_len);
return (cpy_len - retval);
}
static unsigned int key_poll(struct file *file, struct poll_table_struct *wait)
{
/*将当前进程添加到队列列表中,但不挂起该进程*/
poll_wait(file, &wait_queue, wait);
/*根据当前的键值返回适当的状态掩码*/
return key==0 ? 0 : POLLIN | POLLRDNORM;
}
/* Driver Operation structure */
static struct file_operations key_fops = {
.owner = THIS_MODULE,
.read = key_read,
.poll = key_poll,
};
static int __init key_eint_init(void)
{
int retval;
key_io_port_init();
init_waitqueue_head(&wait_queue); //初始化等待队列头
retval = set_irq_type(IRQ_EINT(20),IRQ_TYPE_EDGE_FALLING);
if(retval){
err("IRQ_EINT20 set irq type failed");
goto error;
}
retval = request_irq(IRQ_EINT(20), buttons_interrupt, IRQF_DISABLED,
"KEY1", (void *)EINT_DEVICE_ID);
if(retval){
err("request eint20 failed");
goto error;
}
major = register_chrdev(major, DRIVER_NAME, &key_fops);
if(major < 0){
err("register char device fail");
retval = major;
goto error_register;
}
key_class=class_create(THIS_MODULE,DRIVER_NAME);
if(IS_ERR(key_class)){
err("class create failed!");
retval = PTR_ERR(key_class);
goto error_class;
}
key_device=device_create(key_class,NULL, MKDEV(major, minor), NULL,DRIVER_NAME);
if(IS_ERR(key_device)){
err("device create failed!");
retval = PTR_ERR(key_device);
goto error_device;
}
__debug("register myDriver OK! Major = %d\n", major);
return 0;
error_device:
class_destroy(key_class);
error_class:
unregister_chrdev(major, DRIVER_NAME);
error_register:
free_irq(IRQ_EINT(20), (void *)EINT_DEVICE_ID);
error:
return retval;
}
static void __exit key_eint_exit(void)
{
//__debug("in key_eint_exit\n");
free_irq(IRQ_EINT(20), (void *)EINT_DEVICE_ID);
unregister_chrdev(major, DRIVER_NAME);
device_destroy(key_class,MKDEV(major, minor));
class_destroy(key_class);
return;
}
module_init(key_eint_init);
module_exit(key_eint_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Eric");
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/irq.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <mach/gpio.h>
#include <mach/regs-gpio.h>
#include <linux/poll.h> //poll_wait()
#define EINT_DEVICE_ID 2
#define DRIVER_NAME "key2_eint"
#define err(msg) printk(KERN_ERR "%s: " msg "\n", DRIVER_NAME)
#define __debug(fmt, arg...) printk(KERN_DEBUG fmt, ##arg)
#define GPH3CON (unsigned long)(S5PV210_GPH3_BASE+ 0x00)
#define GPH3DAT (unsigned long)(S5PV210_GPH3_BASE + 0x04)
#define GPH2UP (unsigned long)(S5PV210_GPH2_BASE + 0x08)
static int major = 0;
static int minor = 0;
struct class *key_class;
static struct device *key_device;
/*定义等待队列头,该等待队列头属于该驱动程序*/
static wait_queue_head_t wait_queue;
static unsigned char key;
irqreturn_t buttons_interrupt(int irq, void *dev_id)
{
key = (unsigned int)dev_id;
wake_up_interruptible(&wait_queue); //唤醒等待队列
return IRQ_HANDLED;
}
static void key_io_port_init(void)
{
unsigned long reg_val;
reg_val = readl(GPH3CON);
reg_val &= ~((0x0f<<0) | (0x0f<<4));
reg_val |= ((0x01<<0) | (0x01<<4));
writel(reg_val, GPH3CON);
reg_val = readl(GPH3DAT);
reg_val &= ~((0x01<<0) | (0x01<<1));
writel(reg_val, GPH3DAT);
reg_val = readl(GPH2UP);
reg_val &= ~(0x03<<6);
reg_val |= 0x02<<6;
writel(reg_val, GPH2UP);
}
static ssize_t key_read(struct file *filp, char *buf, size_t count, loff_t *f_pos)
{
int key_num;
int cpy_len;
int retval;
key_num = key; //读取键值
key = 0;
cpy_len = min(sizeof(key_num), count);
retval = copy_to_user(buf, &key_num, cpy_len);
return (cpy_len - retval);
}
static unsigned int key_poll(struct file *file, struct poll_table_struct *wait)
{
/*将当前进程添加到队列列表中,但不挂起该进程*/
poll_wait(file, &wait_queue, wait);
/*根据当前的键值返回适当的状态掩码*/
return key==0 ? 0 : POLLIN | POLLRDNORM;
}
/* Driver Operation structure */
static struct file_operations key_fops = {
.owner = THIS_MODULE,
.read = key_read,
.poll = key_poll,
};
static int __init key_eint_init(void)
{
int retval;
key_io_port_init();
init_waitqueue_head(&wait_queue); //初始化等待队列头
retval = set_irq_type(IRQ_EINT(19),IRQ_TYPE_EDGE_FALLING);
if(retval){
err("IRQ_EINT20 set irq type failed");
goto error;
}
retval = request_irq(IRQ_EINT(19), buttons_interrupt, IRQF_DISABLED,
"KEY1", (void *)EINT_DEVICE_ID);
if(retval){
err("request eint20 failed");
goto error;
}
major = register_chrdev(major, DRIVER_NAME, &key_fops);
if(major < 0){
err("register char device fail");
retval = major;
goto error_register;
}
key_class=class_create(THIS_MODULE,DRIVER_NAME);
if(IS_ERR(key_class)){
err("class create failed!");
retval = PTR_ERR(key_class);
goto error_class;
}
key_device=device_create(key_class,NULL, MKDEV(major, minor), NULL,DRIVER_NAME);
if(IS_ERR(key_device)){
err("device create failed!");
retval = PTR_ERR(key_device);
goto error_device;
}
__debug("register myDriver OK! Major = %d\n", major);
return 0;
error_device:
class_destroy(key_class);
error_class:
unregister_chrdev(major, DRIVER_NAME);
error_register:
free_irq(IRQ_EINT(19), (void *)EINT_DEVICE_ID);
error:
return retval;
}
static void __exit key_eint_exit(void)
{
//__debug("in key_eint_exit\n");
free_irq(IRQ_EINT(19), (void *)EINT_DEVICE_ID);
unregister_chrdev(major, DRIVER_NAME);
device_destroy(key_class,MKDEV(major, minor));
class_destroy(key_class);
return;
}
module_init(key_eint_init);
module_exit(key_eint_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("eric");