首页 > 代码库 > linux驱动之poll操作

linux驱动之poll操作

POLL操作


1、POLL运行过程:

poll是一个系统调用,其内核入口函数为sys_poll,sys_poll差点儿不做不论什么处理直接调用do_sys_poll,do_sys_poll的运行过程能够分为三个部分:

 1,将用户传入的pollfd数组复制到内核空间,由于拷贝操作和数组长度相关。时间上这是一个O(n)操作,这一步的代码在do_sys_poll中包含从函数開始到调用do_poll前的部分。


 2,查询每一个文件描写叙述符相应设备的状态,假设该设备尚未就绪,则在该设备的等待队列中增加一项并继续查询下一设备的状态。

查询全然部设备后假设没有一个设备就绪,这时则须要挂起当前进程等待。直到设备就绪或者超时,挂起操作是通过调用schedule_timeout执行的。设备就绪后进程被通知继续执行,这时再次遍历全部设备,以查找就绪设备。这一步由于两次遍历全部设备。时间复杂度也是O(n),这里面不包含等待时间。相关代码在do_poll函数中。
 3,将获得的数据传送到用户空间并运行释放内存和剥离等待队列等善后工作,向用户空间拷贝数据与剥离等待队列等操作的的时间复杂度相同是O(n),详细代码包含do_sys_poll函数中调用do_poll后到结束的部分。


2、代码编写

1、在file_operations结构体中加入 poll

static struct file_operations button_sdv_fops =
{
	.owner 		= THIS_MODULE,
	.open  		= button_dev_open,
	.read 		= button_dev_read,
	.release 	= button_dev_close,
	.poll		= button_dev_poll,
};

2、完毕 button_dev_poll函数

static unsigned int button_dev_poll(struct file *file, poll_table * wait)
{
	unsigned int mask =0;
	poll_wait(file, &button_wait_q, wait);//把当前进程挂到队列里面,不会立马休眠

	if(ev_press)
		mask |=POLLIN |POLLRDNORM;

	return mask;
}

当中ev_press是上一篇博文中的中断事件发生标志,假设中断动作发生,则mask值将被告诉内核。当中 mask的值的含义:

常量
说明
POLLIN
普通或优先级带数据可读
POLLRDNORM
普通数据可读
POLLRDBAND
优先级带数据可读
POLLPRI
高优先级数据可读
POLLOUT
普通数据可写
POLLWRNORM
普通数据可写
POLLWRBAND
优先级带数据可写
POLLERR
错误发生
POLLHUP
发生挂起
POLLNVAL
描写叙述字不是一个打开的文件

3、測试程序编写

ret = poll(fds, 1, 3000);

poll函数原型:

int poll(struct pollfd *fds, nfds_t nfds, int timeout);

当中:pollfd结构体为:

struct pollfd {
               int   fd;         /* file descriptor */
               short events;     /* requested events */
               short revents;    /* returned events */
           };
fd是要查询的设备。events是期望获得的事件,这里我们将他设置为:POLLIN

fds定义一个数组,存放须要poll的全部设备。poll操作会同一时候查询这些设备。

nfds为查询的文件数量

timeout为超时时间


驱动程序完整代码:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <asm/io.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/irq.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <mach/regs-gpio.h>
#include <mach/irqs.h>//这个在/opt/EmbedSky/linux-2.6.30.4/arch/arm/mach-s3c2410/include/mach 路径
#include <linux/interrupt.h>
#include <linux/poll.h>

MODULE_LICENSE("Dual BSD/GPL");

static struct class *buttondrv_class;
static struct class_devices *buttondrv_class_dev;

/*  */
static DECLARE_WAIT_QUEUE_HEAD(button_wait_q);
/*中断事件标志,中断服务程序将他置1,read函数将他置0*/
static volatile int ev_press =0;

volatile unsigned long *gpfcon = NULL;
volatile unsigned long *gpfdat = NULL;

static unsigned keyval;

struct pin_desc{
	unsigned int pin;
	unsigned int key_value;
};
/*按键按下时是:0x01 0x02 0x03 0x04*/
/*按键松开时是:0x81 0x82 0x83 0x84*/
struct pin_desc pins_desc[4] =
{
	{S3C2410_GPF1,0x01},
	{S3C2410_GPF4,0x02},
	{S3C2410_GPF2,0x03},
	{S3C2410_GPF0,0x04},
};
/*
 * 确定按键值
 */
static irqreturn_t buttons_irq(int irq,void *dev_id)
{
	struct pin_desc * pindesc = (struct pin_desc *) dev_id;
	unsigned int pinval;
	pinval = s3c2410_gpio_getpin(pindesc -> pin);
	if(pinval)//松开
	{
		keyval = 0x80|pindesc->key_value;
	}
	else
	{
		keyval = pindesc->key_value;
	}
	ev_press =1;//中断发生
	wake_up_interruptible(&button_wait_q);
	printk("button is pressed : %d \n",irq);
	return IRQ_HANDLED;
}
static int button_dev_open(struct inode *inode ,struct file* file)
{
	//配置按键的引脚 GPF0,1,2,4为输入引脚
	request_irq(IRQ_EINT1,buttons_irq, IRQ_TYPE_EDGE_BOTH,"key1",&pins_desc[0]);
	request_irq(IRQ_EINT4,buttons_irq, IRQ_TYPE_EDGE_BOTH,"key2",&pins_desc[1]);
	request_irq(IRQ_EINT2,buttons_irq, IRQ_TYPE_EDGE_BOTH,"key3",&pins_desc[2]);
	request_irq(IRQ_EINT0,buttons_irq, IRQ_TYPE_EDGE_BOTH,"key4",&pins_desc[3]);

	return 0;
}
ssize_t button_dev_read(struct file *file,char __user *buf,size_t size,loff_t *ppos)
{
	if(size !=1)
	{
		return -EINVAL;
	}
	/*假设没有按键动作发生  就休眠*/
	wait_event_interruptible(button_wait_q,ev_press);
	/*假设有按键动作发生,直接返回*/
	copy_to_user(buf,&keyval,1);
	ev_press = 0;
	return 0;
}
int button_dev_close(struct inode* inode ,struct file *file)
{
	free_irq(IRQ_EINT1,&pins_desc[0]);
	free_irq(IRQ_EINT4,&pins_desc[1]);
	free_irq(IRQ_EINT2,&pins_desc[2]);
	free_irq(IRQ_EINT0,&pins_desc[3]);

	return 0;
}
static unsigned int button_dev_poll(struct file *file, poll_table * wait)
{
	unsigned int mask =0;
	poll_wait(file, &button_wait_q, wait);//把当前进程挂到队列里面,不会立马休眠

	if(ev_press)
		mask |=POLLIN |POLLRDNORM;

	return mask;
}
static struct file_operations button_sdv_fops =
{
	.owner 		= THIS_MODULE,
	.open  		= button_dev_open,
	.read 		= button_dev_read,
	.release 	= button_dev_close,
	.poll		= button_dev_poll,
};
int major;
static int button_dev_init(void)//入口函数
{
	major = register_chrdev(0,"button_drv",&button_sdv_fops);

	buttondrv_class = class_create(THIS_MODULE,"button_drv");
	if(IS_ERR(buttondrv_class))
		return PTR_ERR(buttondrv_class);
	buttondrv_class_dev= device_create(buttondrv_class,NULL,MKDEV(major,0),NULL,"wq_button");
		if(unlikely(IS_ERR(buttondrv_class_dev)))
			return PTR_ERR(buttondrv_class_dev);

	/*映射物理地址*/
	gpfcon = (volatile unsigned long *) ioremap(0x56000050 ,16);
	gpfdat = gpfcon + 1;

	return 0;
}
static void button_dev_exit(void)
{
	unregister_chrdev(major,"button_drv");
	device_unregister(buttondrv_class_dev);
	class_destroy(buttondrv_class);

	iounmap(gpfcon);
}
module_init(button_dev_init);
module_exit(button_dev_exit);

測试程序完整代码:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <poll.h>

int main(int argc, char **argv)
{
	int ret=0;
	int cnt=0;
	int fd;
	unsigned char key_val;
	struct pollfd fds[1];
	fd = open("/dev/wq_button",	O_RDWR);
	if(fd<0)
	{
		printf("can't open \n");
	}
	fds[0].fd 	  = fd;
	fds[0].events = POLLIN;
	while(1)
	{
		ret = poll(fds, 1, 3000);
		if(ret == 0)
		{
			printf("time out \n");
		}
		else
		{
			read(fd,&key_val,1);
			printf("key_val = 0x%x\n",key_val);
		}

	}
	return 0;
}









linux驱动之poll操作