1. 程式人生 > >linux驅動之poll操作

linux驅動之poll操作

node snippet 內存 ins 按鍵 服務 null head 隊列

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操作