zoukankan      html  css  js  c++  java
  • linux按键驱动之poll

       上一节应用程序的死循环里的读函数是一直在读的;在实际的应用场所里,有没有那么一种情况,偶尔有数据、偶尔没有数据,答案当然是有的。--》poll机制:Poll机制实现的是一定时间如果没有按键的话就返回。以前是如果没有按键不返回  一直处于休眠状态。

    poll调用(应用层) :

    #include <poll.h>
    int poll(struct pollfd fds[], nfds_t nfds, int timeout);

    struct pollfd结构如下:【在源码文件poll.h文件中】

    struct pollfd {
      int fd;
      short events;
      short revents;
    };

     这个结构中fd表示文件描述符,events表示请求检测的事件,revents表示检测之后返回的事件,如果当某个文件描述符有状态变化时,revents的值就不为空。

    1. fds:存放需要被检测状态的Socket描述符;与select不同(select函数在调用之后,会清空检测socket描述符的数组),每当调用这个函数之后,系统不会清空这个数组,而是将有状态变化的描述符结构的revents变量状态变化,操作起来比较方便;
    2. nfds:用于标记数组fds中的struct pollfd结构元素的总数量;
    3. timeout:poll函数调用阻塞的时间,单位是MS(毫秒)

    return value:

    1. 大于0:表示数组fds中有socket描述符的状态发生变化,或可以读取、或可以写入、或出错。并且返回的值表示这些状态有变化的socket描述符的总数量;此时可以对fds数组进行遍历,以寻找那些revents不空的socket描述符,然后判断这个里面有哪些事件以读取数据。
    2. 等于0:表示没有socket描述符有状态变化,并且调用超时。
    3. 小于0:此时表示有错误发生,此时全局变量errno保存错误码。

    内核实现:

    poll机制总结:

    1. poll > sys_poll > do_sys_poll >poll_initwait,poll_initwait函数注册一下回调函数__pollwait,它就是我们的驱动程序执行poll_wait时,真正被调用的函数。

    2. 接下来执行file->f_op->poll,即我们驱动程序里自己实现的poll函数

       它会调用poll_wait把自己挂入某个队列,这个队列也是我们的驱动自己定义的;

       它还判断一下设备是否就绪。

    3. 如果设备未就绪,do_sys_poll里会让进程休眠一定时间

    4. 进程被唤醒的条件有2:一是上面说的“一定时间”到了,二是被驱动程序唤醒。驱动程序发现条件就绪时,就把“某个队列”上挂着的进程唤醒,这个队列,就是前面通过poll_wait把本进程挂过去的队列。

    5. 如果驱动程序没有去唤醒进程,那么chedule_timeout(__timeou)超时后,会重复2、3动作,直到应用程序的poll调用传入的时间到达。

    博客中http://blog.csdn.net/u012719256/article/details/52663292提到:

    内核中poll实现非常固定,就以下两个步骤

    static unsigned forth_drv_poll(struct file *file, poll_table *wait)
    {
        unsigned int mask = 0;
    
    // 1. 将button_waitq添加到等待队列    
        poll_wait(file, &button_waitq, wait); // 不会立即休眠
    
        if (ev_press) // 如果有按键按下了,返回的掩码表示不休眠
            mask |= POLLIN | POLLRDNORM;
    
    // 2. 返回掩码
        return mask;
    }
    

    暂时做个标记吧,现在只知道这个格式

    代码:

    #include <linux/module.h>
    #include <linux/kernel.h>
    #include <linux/fs.h>
    #include <linux/init.h>
    #include <linux/delay.h>
    #include <asm/uaccess.h>
    #include <linux/interrupt.h>
    #include <asm/irq.h>
    #include <asm/io.h>
    #include <asm/arch/regs-gpio.h>
    #include <asm/hardware.h>
    #include <linux/poll.h>
    #define DEVICE_NAME "mybutton"   /* 加载模式后,执行”cat /proc/devices”命令看到的设备名称 */
    static struct class *button_class;
    static struct class_device *button_dev_class;
    int major;
    
    static volatile int press_cnt=0;/* 按键被按下的次数(准确地说,是发生中断的次数) */
    
    
    static DECLARE_WAIT_QUEUE_HEAD(button_waitq);//定义等待队列
    
    /* 中断事件标志, 中断服务程序将它置1,s3c24xx_buttons_read将它清0 */
    static volatile int ev_press = 0;
    
    static irqreturn_t buttons_interrupt(int irq, void *dev_id)
    {
        // volatile int *press_cnt = (volatile int *)dev_id;
         press_cnt =press_cnt + 1; /* 按键计数加1 */
         ev_press = 1;                /* 表示中断发生了 */
         wake_up_interruptible(&button_waitq);   /* 唤醒休眠的进程 */
         return IRQ_RETVAL(IRQ_HANDLED);//中断处理程序应该返回一个值,用来表明是否真正处理了一个中断,如果中断例程发现其设备的确要处理,则应该返回IRQ_HANDLED, //否则应该返回IRQ_NONE,我们可以通过这个宏来产生返回值,不是本设备的中断应该返回IRQ_NONE
    
    }
    /* 应用程序对设备文件/dev/xxx 执行open(...)时,
     * 就会调用button_open函数
     * 就会调用button_open函数
     */
    static int button_open (struct inode *inode, struct file *filep)  
    {   
        int err;
        err=request_irq(IRQ_EINT2,buttons_interrupt,IRQF_TRIGGER_FALLING,"KEY3",NULL);
        
        if (err) {
            // 释放已经注册的中断
             free_irq(IRQ_EINT2, NULL);
            return -EBUSY;
        }
        
        return 0;  
    }  
    
    /* 应用程序对设备文件/dev/buttons执行close(...)时,
     * 就会调用buttons_close函数
     */
     static int buttons_close(struct inode *inode, struct file *file)
    {
        
        free_irq(IRQ_EINT2, NULL);
        return 0;
    }
    
    ssize_t button_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
    {
         unsigned long err;
        
        /* 如果ev_press等于0,休眠 */
        wait_event_interruptible(button_waitq, ev_press);//阻塞   
         /* 执行到这里时,ev_press等于1,将它清0 */
        ev_press = 0;
         /* 将按键状态复制给用户,并清0 */
        err = copy_to_user(buf, (const void *)&press_cnt,  count);
        //memset((void *)&press_cnt, 0, sizeof(press_cnt));
        return  err ? -EFAULT : 0;
    }
    
    
    
    static unsigned buttons_poll(struct file *file, poll_table *wait)
    {
            unsigned int mask = 0;
            poll_wait(file, &button_waitq, wait); // 不会立即休眠    将进程挂接到button_waitq队列中
              /* 当没有按键按下时,即不会进入按键中断处理函数,此时ev_press = 0  
         * 当按键按下时,就会进入按键中断处理函数,此时ev_press被设置为1 
         */  
          if(ev_press)  
        {  
           mask |= POLLIN | POLLRDNORM;  /* POLLIN表示有数据可读   POLLRDNORM表示有普通数据可读*/  
        } 
     /* 如果有按键按下时,mask |= POLLIN | POLLRDNORM,否则mask = 0 */      
        return mask;
    }
    
    /* 这个结构是字符设备驱动程序的核心
     * 当应用程序操作设备文件时所调用的open、read、write等函数,
     * 最终会调用这个结构中指定的对应函数
     */
    static struct file_operations button_ops=  
    {    
        .owner    =  THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
        .open     =  button_open,  
        .read      =     button_read,       
        .release  =  buttons_close, 
        .poll     =  buttons_poll,
    }; 
     
    /*
     * 执行insmod命令时就会调用这个函数 
     */
      
    static int button_init(void)  
    {  
        
        /* 注册字符设备
         * 参数为主设备号、设备名字、file_operations结构;
         * 这样,主设备号就和具体的file_operations结构联系起来了,
         * 操作主设备为LED_MAJOR的设备文件时,就会调用s3c24xx_leds_fops中的相关成员函数
         * LED_MAJOR可以设为0,表示由内核自动分配主设备号
         */
         major = register_chrdev(0, DEVICE_NAME, &button_ops);
          if (major < 0) 
          {
          printk(DEVICE_NAME  " can't register major number number::%d
    ",major);
          return 0;
          }
        printk(DEVICE_NAME " initialized1
    ");
        button_class = class_create(THIS_MODULE, "button");
        if (IS_ERR(button_class))
            return PTR_ERR(button_class);
        button_dev_class = class_device_create(button_class, NULL, MKDEV(major, 0), NULL, "my_button"); /* /dev/my_button */ 
        
        
        return 0;
    
    }
    
    /*
     * 执行rmmod命令时就会调用这个函数 
     */
    static void button_exit(void)
    {
        class_device_unregister(button_dev_class);
        class_destroy(button_class);       
         /* 卸载驱动程序 */
        unregister_chrdev(major, DEVICE_NAME);
    }
    
    /* 这两行指定驱动程序的初始化函数和卸载函数 */
    module_init(button_init);  
    module_exit(button_exit);  
    
    /* 描述驱动程序的一些信息,不是必须的 */
    MODULE_AUTHOR("http://www.100ask.net");// 驱动程序的作者
    MODULE_DESCRIPTION("S3C2410/S3C2440 LED Driver");// 一些描述信息
    MODULE_LICENSE("GPL");   // 遵循的协议

    test.c

    #include <sys/types.h>  
    #include <sys/stat.h>  
    #include <fcntl.h>  
    #include <stdio.h> 
    #include <poll.h>
     
    int main(int argc,char**argv)  
    {  
        int fd;  
    	int cnt = 0;
    	int ret;
    	
    	struct pollfd fds[1];/*每一个pollfd结构体指定了一个被监视的文件描述符*/
    	
      //打开设备
        fd = open("/dev/my_button",O_RDWR);  
        
    	if(fd<0)  
        {  
            perror("open fail 
    ");  
            return -1;  
        } 
       
       
       fds[0].fd=fd;
       fds[0].events=POLLIN;
       while(1)
    	{
    		/*poll函数返回0时,表示5s时间到了,而这段时间里,没有事件发生"数据可读" */
    		ret=poll(fds,1,5000);
    		
    	   if (ret==0)
    		{
    			printf("time out
    ");
    		}
    		else /* 如果没有超时,则读出按键值 */  
    		{
    			read(fd,&cnt, sizeof(cnt)); //read()
    			printf("button has been pressed %d timea
    ",cnt);
    	   }
          }
    	close(fd);
    	return 0;
    }	
    

      

    结果:

    参考:

    http://blog.csdn.net/lwj103862095/article/details/17536069

    http://blog.csdn.net/u012719256/article/details/52663292

    http://www.cnblogs.com/mylinux/p/5090264.html

    http://blog.csdn.net/ruoyunliufeng/article/details/24188693

  • 相关阅读:
    C#基础 const和readonly关键字
    C#基础 base与this关键字
    ASP.NET Web Form 与 ASP.NET MVC 区别
    qt 零星笔记
    我应该记录一下我不太了解的一些c语言函数
    Linux学习书籍推荐
    更改arch的默认终端
    让arch阻止某个软件包的升级
    python pachong zhuanzai
    从贴吧看的逆向网络协议过程逆向校园网客户端
  • 原文地址:https://www.cnblogs.com/zhaobinyouth/p/6252511.html
Copyright © 2011-2022 走看看