一、poll机制功能
poll的是一种查询的方式,英文解释 :民意调查
函数原型:int poll(struct pollfd *fds ,nfds_t nfds ,int timeout);
fds为指向待查询的设备文件数组;
nfds描述第一个参数fds中有多少个设备;
timeout为查询不到我们期望的结果进程睡眠的时间;
返回值:查询到期望状态的设备文件个数
struct pollfd {
int fd; /* 文件描述符 */ (待查询的设备)
short events; /* 等待的事件 */(待查询的设备的状态)
short revents; /* 实际发生了的事件 */
}
功能过程描述:应用程序中调用poll查询文件的状态,首先将fds里面的每个设备文件fd取出,调用它们驱动程序的poll函数,查询是否出现我们期望状态,查询完fds里面所有的设备文件得到满足期望状态的设备文件的数量,如果这个数为0,则poll调用将导致进程就进入睡眠状态,睡眠时间由poll函数设定,如果程序在睡眠状态中fds的某个文件出现我们期望状态,那么poll立即返回,否则一直睡眠到睡眠时间结束为止,返回值为0;如果这个数大于0 ,poll返回满足条件的设备数量。
poll相当于open("/dev/xxx",O_RDWR)阻塞打开文件,区别在于当设备文件无数据可读时poll只导致程序休眠固定时间,而open将导致程序一直休眠到有数据为止。
二、poll应用举例
int main(int argc, char **argv)
{
int fd;
unsigned char key_val;
int ret;
struct pollfd fds[1];//查询数组的大小,这里我们仅查询一个设备文件
fd = open("/dev/buttons", O_RDWR);
if (fd < 0)
printf("can't open!\n");
fds[0].fd = fd;//查询的设备文件描述符为fd,也就是查询的设备是/dev/buttons
fds[0].events = POLLIN;//查询事件是POLLIN,也就是/dev/buttons是否按下
while (1)
{
ret = poll(fds, 1, 5000);//查询的设备队列是fds,里面有1个设备,查询不到就睡眠5s,在睡眠中如果有期望状态出现也是可以返回
if (ret == 0)
printf("time out\n"); //没有查询到按键按下,睡眠中也没有按键按下
else
{
read(fd, &key_val, 1); //查询到按键按下,读取这个按键的值
printf("key_val = 0x%x\n", key_val);
}
}
return 0;
}
三、poll内核实现过程(kernel-2.6.30.4)
(应用程序)poll->sys_poll->do_sys_poll->do_poll->do_pollfd->f_op->poll(驱动)
在do_poll中:
for (;;)
{ //逐个取出待查询数组中的每个文件描述符来查询
for (; pfd != pfd_end; pfd++)
{
if (do_pollfd(pfd, pt))
{
count++;
pt = NULL;
}
}
pt = NULL;
if (!count)//如果没有一个设备文件发生可读
{
count = wait->error;
if (signal_pending(current))
count = -EINTR;
}
if (count || timed_out)//超时或者有设备文件可读,程序直接返回
break;
if (end_time && !to)
{
expire = timespec_to_ktime(*end_time);
to = &expire;
}
//程序睡眠
if (!poll_schedule_timeout(wait, TASK_INTERRUPTIBLE, to, slack))
timed_out = 1;
}
在do_pollfd中:
if (file->f_op && file->f_op->poll)
mask = file->f_op->poll(file, pwait);//调用设备的poll函数,返回是否发生期望的设备状态
在f_op->poll中(针对按键驱动举例):
unsigned int mask = 0;
poll_wait(file, &button_waitq, wait); //将当前的设备加入到等待队列中,当它不是马上就休眠
if (ev_press)
mask |= POLLIN | POLLRDNORM;//返回设备文件现在的状态
return mask;
按键驱动poll机制驱动实现:(可在TQ2440运行)
#include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/init.h> #include <linux/delay.h> #include <linux/poll.h> #include <linux/irq.h> #include <asm/irq.h> #include <linux/interrupt.h> #include <asm/uaccess.h> #include <mach/regs-gpio.h> #include <mach/hardware.h> #include <linux/platform_device.h> #include <linux/cdev.h> #include <linux/miscdevice.h> #define DEVICE_NAME "buttons_poll" static DECLARE_WAIT_QUEUE_HEAD(button_waitq); /*中断事件标志*/ static volatile int ev_press = 0; struct pin_desc
{ unsigned int pin; unsigned int key_val; }; /* 键值: 按下时, 0x01, 0x02, 0x03, 0x04 */ /* 键值: 松开时, 0x81, 0x82, 0x83, 0x84 */ static unsigned char key_val; /* K1,K2,K3,K4对应GPF1、GPF4、GPF2、GPF0*/ 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) { /* 松开 */ key_val = 0x80 | pindesc->key_val; } else { /* 按下 */ key_val = pindesc->key_val; } ev_press = 1; /* 表示中断发生了 */ wake_up_interruptible(&button_waitq); /* 唤醒休眠的进程 */ return IRQ_RETVAL(IRQ_HANDLED); } static int button_drv_open(struct inode *inode, struct file *file) { /* GPF1、GPF4、GPF2、GPF0为中断引脚 */ request_irq(IRQ_EINT1, buttons_irq, IRQ_TYPE_EDGE_BOTH, "K1", &pins_desc[0]); request_irq(IRQ_EINT4, buttons_irq, IRQ_TYPE_EDGE_BOTH, "K2", &pins_desc[1]); request_irq(IRQ_EINT2, buttons_irq, IRQ_TYPE_EDGE_BOTH, "K3", &pins_desc[2]); request_irq(IRQ_EINT0, buttons_irq, IRQ_TYPE_EDGE_BOTH, "K4", &pins_desc[3]); return 0; } ssize_t button_drv_read(struct file *file, char __user *buf, size_t size, loff_t *ppos) { if (size != 1) return -EINVAL; /* 如果没有按键动作, 休眠 */ wait_event_interruptible(button_waitq, ev_press); /* 如果有按键动作, 返回键值 */ copy_to_user(buf, &key_val, 1); ev_press = 0; return 1; } int button_drv_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 button_drv_poll(struct file *file, poll_table *wait) { unsigned int mask = 0; /*不会立即休眠 *当调用完所有的设备文件的poll无结果后,程序开始睡眠 *由驱动程序唤醒 */ poll_wait(file, &button_waitq, wait); if (ev_press) mask |= POLLIN | POLLRDNORM; return mask; } static struct file_operations dev_fops =
{ .owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */ .open = button_drv_open, .read = button_drv_read, .release = button_drv_close, .poll = button_drv_poll, }; static struct miscdevice misc =
{ .minor = MISC_DYNAMIC_MINOR, .name = DEVICE_NAME, .fops = &dev_fops, }; static int __init dev_init(void) { int ret; /*杂项设备注册,和register_chrdev差不多 *不同点在于它的主设备号固定为10,并且自动在/dev目录下创建好设备 *注册后次设备号、设备名、设备的fops就都融为一体了 */ ret = misc_register(&misc); printk (DEVICE_NAME" initialized\n"); return ret; } static void __exit dev_exit(void) { misc_deregister(&misc); } module_init(dev_init); module_exit(dev_exit); MODULE_LICENSE("GPL");
四、开发板测试过程(TQ2440)
在PC机上编译好驱动和测试程序在开发板上
#rz (传送PC上的文件到开发板)
#chmod 777 * (改变文件的属性为可执行)
#insmod buttondrv_poll.ko (加载模块)
#ls /dev (可以看到buttons)
#./buttondrvtest
按下开发板上的按键就会打印出按键信息了
五、源代码下载链接