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;}