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;
}
时间: 2024-10-09 19:28:20

linux驱动之poll操作的相关文章

linux驱动编写之poll机制

一.概念 1.poll情景描述 以按键驱动为例进行说明,用阻塞的方式打开按键驱动文件/dev/buttons,应用程序使用read()函数来读取按键的键值.这样做的效果是:如果有按键按下了,调用该read()函数的进程,就成功读取到数据,应用程序得到继续执行:倘若没有按键按下,则要一直处于休眠状态,等待这有按键按下这样的事件发生. 这种功能在一些场合是适用的,但是并不能满足我们所有的需要,有时我们需要一个时间节点.倘若没有按键按下,那么超过多少时间之后,也要返回超时错误信息,进程能够继续得到执行

Linux驱动开发盲点笔记1

1. vim中在找到搜索目标后,使用n与N进行定位查找 2. vim中使用gg到最好第一行,使用xxxG到某一行,否则G直接到最后一行: 3. ln -s 产生的链接文件最终指向的目标文件src 新产生的当前软链接文件dst. ln -s project(磁盘上实际存在的文件或者目录) a.lnk ln -s src dst(新产生的文件dst,dst链接到src) symlink功能类似 4 tar -czvf 最终生产的tar打包好的文件 待打包的文件或者文件夹 tar czvf a.tar

【Linux 驱动】设备驱动程序再理解

学习设备驱动编程也有一段时间了,也写过了几个驱动程序,因此有对设备驱动程序有了一些新的理解和认识,总结一下.学习设备驱动编程也有一段时间了,也写过了几个驱动程序,因此有对设备驱动程序有了一些新的理解和认识,总结一下. ★什么是驱动程序 刚开始学习设备驱动程序的时候,产生了许多的问题.什么是驱动程序?驱动程序是干嘛的?它是如何工作的?它又是如何跟操作系统联系起来的?一系列的问题,现在有些地方还是不一定清楚,但是相比起刚开始的那个阶段,感觉自己还是清楚了很多. 设备驱动程序说白了(实质)就是为应用程

嵌入式linux驱动开发之点亮led未遂(驱动编程思想之初体验)

有了上两篇文章的基础,我们就可以开始开始进行实战啦!这里顺便说一下啊,出来做开发的基础很重要啊,基础不好,迟早是要恶补的.个人深刻觉得像这种嵌入式的开发对C语言和微机接口与原理是非常依赖的,必须要有深厚的基础才能hold的住,不然真像一些人说的,学了一年嵌入式感觉还没找到门. 另外实践很重要,一年多以前就知道了arm,那时整天用单片机的思维去yy着arm,直到前段时间弄来一块arm板,烧上linux系统后才知道,坑呀!根本不是那回事,所以实践是学习计算机类最重要的基本素质,如果整天看书,那基本上

【Linux驱动】文件描述符以及相关知识

1.文件描述符 Linux操作系统中,几乎所有的设备都被抽象成为设备文件.因此,当我们想对设备进行操作的时候可以直接去操作其相应的设备文件.设备文件即是文件,要想对文件进行操作,无非就是:打开文件.关闭文件.写入数据.读出数据等,它们分别对应的函数有open(),close(),write(),read(),就以其中的open()函数做一个分析.open函数的作用是打开一个文件. (1)它的定义:int open( const char * pathname, int flags); int o

Linux驱动学习步骤(转载)

1. 学会写简单的makefile 2. 编一应用程序,可以用makefile跑起来 3. 学会写驱动的makefile 4. 写一简单char驱动,makefile编译通过,可以insmod, lsmod, rmmod. 在驱动的init函数里打印hello world, insmod后应该能够通过dmesg看到输出. 5. 写一完整驱动, 加上read, write, ioctl, polling等各种函数的驱动实现. 在ioctl里完成从用户空间向内核空间传递结构体的实现. 6. 写一bl

Linux驱动之输入子系统框架

    好记性不如烂笔头,整理一下笔记~ Linux驱动之输入子系统框架 输入子系统将该类驱动划分为3部分 1.核心层 input.c 2.设备层 Gpio_keys.c ... 3.事件处理层 Evdev.c 事件处理层为纯软件的东西,设备层涉及底层硬件,它们通过核心层建立联系,对外提供open write等接口. 1.我们首先来看,核心层 input.c如何向外界提供接口 在 input_init 中注册了字符设备驱动 register_chrdev(INPUT_MAJOR, "input&

(57)Linux驱动开发之三Linux字符设备驱动

1.一般情况下,对每一种设备驱动都会定义一个软件模块,这个工程模块包含.h和.c文件,前者定义该设备驱动的数据结构并声明外部函数,后者进行设备驱动的具体实现. 2.典型的无操作系统下的逻辑开发程序是:这种三层的裸机驱动模型是足够满足低耦合.高内聚的特点的. 3.当有操作系统存在时,设备驱动成为了连接硬件和内核的桥梁,这时候的设备驱动对外表现为操作系统的API,与直接裸机开发不同,裸机开发时的设备驱动是应用工程师的API.如果设备驱动都按照操作系统给出的独立于设备的接口而设计,应用程序将可以使用统

Linux驱动开发学习的一些必要步骤

1. 学会写简单的makefile 2. 编一应用程序,可以用makefile跑起来 3. 学会写驱动的makefile 4. 写一简单char驱动,makefile编译通过,可以insmod, lsmod, rmmod. 在驱动的init函数里打印hello world, insmod后应该能够通过dmesg看到输出. 5. 写一完整驱动, 加上read, write, ioctl, polling等各种函数的驱动实现. 在ioctl里完成从用户空间向内核空间传递结构体的实现. 6. 写一bl