poll函數用於監測多個等待事件,若事件未發生,進程睡眠,放棄CPU控制權,若監測的任何一個事件發生,poll將喚醒睡眠的進程,並判斷是什么等待事件發生,執行相應的操作。poll函數退出后,struct pollfd變量的所有值被清零,需要重新設置。
示例是使用poll函數來監測按鍵的輸入
----------------------------------------------------------------------------------------------------------
驅動代碼:
- #include <linux/config.h>
- #include <linux/module.h>
- #include <linux/kernel.h>
- #include <linux/fs.h>
- #include <linux/init.h>
- #include <linux/devfs_fs_kernel.h>
- #include <linux/miscdevice.h>
- #include <linux/delay.h>
- #include <asm/irq.h>
- #include <asm/arch/regs-gpio.h>
- #include <asm/hardware.h>
- #include <linux/cdev.h>
- #include <linux/mm.h>
- #include <linux/interrupt.h>
- #include <linux/poll.h>
- #include <asm/uaccess.h>
- #include <asm/ioctl.h>
- #include <asm/arch/regs-irq.h>
- #include <asm/io.h>
- #define key S3C2410_GPF0
- #define key_irq IRQ_EINT0 //IRQ_EINT0是中斷號
- #define key_cfg S3C2410_GPF0_EINT0 //設置為外部中斷功能
- #define DEVICE_NAME "key" //注意加上雙引號
- #define DEVICE_MAJOR major
- #define DEVICE_MINOR 0
- static dev_t dev; //dev_t類型用於存放主設備號和次設備號
- static int major;
- struct cdev *p_cdev; //聲明一個指向字符設備結構的指針
- static int key_event=0; //喚醒中斷的條件標記,1時滿足喚醒條件,靜態全局變量
- static int key_value=1; //按鍵鍵值
- static DECLARE_WAIT_QUEUE_HEAD(wq); //調用宏定義,靜態創建一個名為wq的等待隊列
- static void key_interrupt(void) //中斷處理函數,注冊中斷時已注冊了中斷程序的入口地址
- {
- key_value=s3c2410_gpio_getpin(key);
- key_event=1; //喚醒標記置位,表示條件達到,可以喚醒進程繼續執行
- wake_up_interruptible(&wq); //調用宏定義,喚醒標記置位后調用此函數,&wq是隊列入口地址
- }
- static int key_read(struct file *filp,char __user *buff,size_t count,loff_t *offp)
- {
- // wait_event_interruptible(wq,key_event); //若key_event為0,從此處將進程放入wq等待隊列休眠,等待中斷;key_event==1時,此宏不執行操作
- //調用poll的時候來等待,這里可以不用wait_event_interrupt()
- key_value=s3c2410_gpio_getpin(key);
- copy_to_user(buff,&key_value,sizeof(key_event)); //將&key_value地址的值從內核空間復制到用戶空間
- key_event=0; //完成中斷操作,將喚醒標記清零,繼續休眠
- return 0;
- }
- static unsigned int key_poll(struct file *filp,poll_table *wait)
- {
- unsigned int mask=0; //用來記錄發生的事件,以unsigned int類型返回
- poll_wait(filp,&wq,wait); //將當前進程添加到wq等待隊列中
- if(key_event==1)mask|=POLLIN|POLLRDNORM; //中斷事件發生,這時有數據可讀,在mask中標記是可讀事件發生
- return mask; //返回事件記錄,返回0則表示等待事件超時
- }
- //設置寄存器,申請中斷號等在open函數中完成
- static int key_open(struct inode *inode,struct file *filp)
- {
- int ret;
- s3c2410_gpio_cfgpin(key,key_cfg); //設置引腳功能
- s3c2410_gpio_pullup(key,1); //第二個參數1為禁止內部上拉
- ret=request_irq(key_irq,(void *)key_interrupt,SA_INTERRUPT,DEVICE_NAME,NULL); //注冊中斷,中斷不共享時最后一個參數為NULL
- if(ret) {
- printk("Could not register interrupt\n");
- return ret;
- }
- set_irq_type(key_irq,IRQT_BOTHEDGE); //設置中斷方式為雙邊觸發
- return 0;
- }
- static int key_close(struct inode *inode,struct file *filp)
- {
- free_irq(key_irq,NULL); //中斷無共享時第二個參數為NULL
- return 0;
- }
- static struct file_operations key_fops={
- .owner=THIS_MODULE,
- .open=key_open,
- .release=key_close,
- .read=key_read,
- .poll=key_poll,
- };
- int key_init(void)
- {
- int ret;
- ret=alloc_chrdev_region(&dev,DEVICE_MINOR,1,DEVICE_NAME); //采用主設備號動態分配
- if(ret<0){
- printk("Register /dev/key failed!\n");
- return ret;
- }
- else printk("Register /dev/key successfully!\n");
- major=MAJOR(dev); //取得分配到的主設備號
- p_cdev=cdev_alloc(); //申請一個字符設備結構並返回指向它的指針
- cdev_init(p_cdev,&key_fops); //相當於p_cdev->ops=&key_fops
- p_cdev->owner=THIS_MODULE;
- ret=cdev_add(p_cdev,dev,1); //向系統添加這個字符設備
- if(ret<0){
- printk("Add cdev failed!\n");
- return ret;
- }
- devfs_mk_cdev(dev,S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP,DEVICE_NAME);
- return 0;
- }
- void key_exit(void)
- {
- unregister_chrdev_region(dev,1);
- cdev_del(p_cdev); //刪除字符設備
- devfs_remove(DEVICE_NAME);
- printk("Device unregister!\n");
- }
- MODULE_LICENSE("GPL");
- MODULE_AUTHOR("HJW");
- module_init(key_init);
- module_exit(key_exit);
- ---------------------------------------------------------------------------------------------------
- 測試程序代碼:
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <sys/ioctl.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <fcntl.h> /*文件控制*/
- #include <sys/select.h>
- #include <sys/time.h> /*時間方面的函數*/
- #include <errno.h> /*有關錯誤方面的宏*/
- #include<sys/poll.h> //poll()
- #include<fcntl.h>
- #include<string.h> //memset()
- int main(void)
- {
- int fd,key_value,ret;
- struct pollfd event; //創建一個struct pollfd結構體變量,存放文件描述符、要等待發生的事件
- fd=open("/dev/key",O_RDWR);
- if(fd<0){
- perror("open /dev/key error!\n");
- exit(1);
- }
- printf("open /dev/key sucessfully!\n");
- while(1){ //poll結束后struct pollfd結構體變量的內容被全部清零,需要再次設置
- memset(&event,0,sizeof(event)); //memst函數對對象的內容設置為同一值
- event.fd=fd; //存放打開的文件描述符
- event.events=POLLIN; //存放要等待發生的事件
- ret=poll((struct pollfd *)&event,1,5000); //監測event,一個對象,等待5000毫秒后超時,-1為無限等待
- //判斷poll的返回值,負數是出錯,0是設定的時間超時,整數表示等待的時間發生
- if(ret<0){
- printf("poll error!\n");
- exit(1);
- }
- if(ret==0){
- printf("Time out!\n");
- continue;
- }
- if(event.revents&POLLERR){ //revents是由內核記錄的實際發生的事件,events是進程等待的事件
- printf("Device error!\n");
- exit(1);
- }
- if(event.revents&POLLIN){
- read(fd,&key_value,sizeof(key_value));
- printf("Key value is '%d'\n",key_value);
- }
- }
- close(fd);
- return 0;
- }