字符设备驱动学习笔记----异步通知机制

来源:互联网 发布:java gc 编辑:程序博客网 时间:2024/05/15 23:53

========使用poll修改按建驱动程序====================================

场景是在一段时间内没的中断请求则程序也有返回

 

应用app调用poll函数-->

内核中的sysPoll函数-->

Do_sys_poll函数--->

                            poll_initwait(&table);-->

                            fdcount = do_poll(nfds, head, &table,end_time);--->

For(;;){

for (; pfd != pfd_end; pfd++) {

                                                 if(do_pollfd(pfd, pt)) {

                                                        count++;

                                                        pt = NULL;

                                                 }

}

                                          if (count || timed_out)

                                                 break;

poll_schedule_timeout(wait, TASK_INTERRUPTIBLE, to, slack)

}

 

 

 

do_sys_poll函数在fs/select.c文件中定义,示例代码如下:

int do_sys_poll(struct pollfd __user *ufds,unsigned int nfds,

              struct timespec *end_time)

{

       structpoll_wqueues table;

      int err = -EFAULT, fdcount, len, size;

       /*Allocate small arguments on the stack to save memory and be

          faster - use long to make sure the buffer isaligned properly

          on 64 bit archs to avoid unaligned access */

       longstack_pps[POLL_STACK_ALLOC/sizeof(long)];

       structpoll_list *const head = (struct poll_list *)stack_pps;

      struct poll_list *walk = head;

      unsigned long todo = nfds;

 

       if(nfds > rlimit(RLIMIT_NOFILE))

              return -EINVAL;

 

       len= min_t(unsigned int, nfds, N_STACK_PPS);

       for(;;) {

              walk->next = NULL;

              walk->len = len;

              if (!len)

                     break;

 

              if (copy_from_user(walk->entries, ufds+ nfds-todo,

                                   sizeof(structpollfd) * walk->len))

                     gotoout_fds;

 

              todo -= walk->len;

              if (!todo)

                     break;

 

              len = min(todo, POLLFD_PER_PAGE);

              size = sizeof(struct poll_list) +sizeof(struct pollfd) * len;

              walk = walk->next = kmalloc(size,GFP_KERNEL);

              if (!walk) {

                     err= -ENOMEM;

                     gotoout_fds;

              }

       }

 

       poll_initwait(&table);

       fdcount= do_poll(nfds, head, &table, end_time);

       poll_freewait(&table);

 

       for(walk = head; walk; walk = walk->next) {

              struct pollfd *fds = walk->entries;

              int j;

 

              for (j = 0; j < walk->len; j++,ufds++)

                     if(__put_user(fds[j].revents, &ufds->revents))

                            goto out_fds;

      }

 

       err= fdcount;

out_fds:

       walk= head->next;

       while(walk) {

              struct poll_list *pos = walk;

              walk = walk->next;

              kfree(pos);

       }

 

       returnerr;

}

 

 

 

 

 

 

void poll_initwait(struct poll_wqueues*pwq)

{

       init_poll_funcptr(&pwq->pt,__pollwait);

       pwq->polling_task= current;

       pwq->triggered= 0;

       pwq->error= 0;

       pwq->table= NULL;

       pwq->inline_index= 0;

}

 

init_poll_funcptr函数在include/linux/poll.h文件中定义

static inline voidinit_poll_funcptr(poll_table *pt, poll_queue_proc qproc)

{

//将函数指针指向 __pollwait

       pt->qproc= qproc;

       pt->key   = ~0UL; /* all events enabled */

}

 

 

 

static int do_poll(unsigned int nfds,  struct poll_list *list,

                struct poll_wqueues *wait, struct timespec *end_time)

{

       poll_table*pt = &wait->pt;

       ktime_texpire, *to = NULL;

       inttimed_out = 0, count = 0;

       unsignedlong slack = 0;

 

       /*Optimise the no-wait case */

       if(end_time && !end_time->tv_sec && !end_time->tv_nsec) {

              pt = NULL;

              timed_out = 1;

       }

 

       if(end_time && !timed_out)

              slack =select_estimate_accuracy(end_time);

 

       for(;;) {

              struct poll_list *walk;

 

              for (walk = list; walk != NULL; walk =walk->next) {

                     structpollfd * pfd, * pfd_end;

 

                     pfd= walk->entries;

                     pfd_end= pfd + walk->len;

                     for(; pfd != pfd_end; pfd++) { //查询多个设备文件

                            /*

                             *Fish for events. If we found one, record it

                             *and kill the poll_table, so we don't

                             *needlessly register any other waiters after

                             *this. They'll get immediately deregistered

                             *when we break out and return.

                             */

//最终会调用到驱动程序中的poll函数

                            if (do_pollfd(pfd, pt)) {

                                   count++;  //如果驱动的poll返回的为非0,count++

                                   pt= NULL;

                            }

                     }

              }

              /*

               *All waiters have already been registered, so don't provide

               *a poll_table to them on the next loop iteration.

               */

//有信号在等待,程序也会返回

              pt = NULL;

              if (!count) {

                     count= wait->error;

                     if(signal_pending(current))

                            count = -EINTR;

              }

//返回的条件为: count0或是timed_out超时

              if (count || timed_out)

                     break;

 

              /*

               *If this is the first loop and we have a timeout

               *given, then we convert to ktime_t and set the to

               *pointer to the expiry value.

               */

              if (end_time && !to) {

                     expire= timespec_to_ktime(*end_time);

                     to= &expire;

              }

//修眠

              if (!poll_schedule_timeout(wait,TASK_INTERRUPTIBLE, to, slack))

                     timed_out= 1;

       }

       returncount;

}

 

 

驱动程序示例代码如下:

/****************************************

*第五个驱动程序   添加poll机制的中断方式实现按键驱动

*****************************************/

#include <linux/module.h>

#include <linux/kernel.h>

#include <linux/fs.h>

#include <linux/init.h>

#include <linux/delay.h>

#include <linux/poll.h>

#include <linux/irq.h>

#include <asm/irq.h>

#include <asm/io.h>

#include <linux/interrupt.h>

#include <asm/uaccess.h>

#include <mach/hardware.h>

#include <linux/platform_device.h>

#include <linux/cdev.h>

#include <linux/miscdevice.h>

#include <linux/device.h>

 

#include <mach/map.h>

#include <mach/regs-clock.h>

#include <mach/regs-gpio.h>

 

#include <plat/gpio-cfg.h>

#include <mach/gpio-bank-n.h>

#include <mach/gpio-bank-l.h>

 

#define DEVICE_NAME"fifth_button_dev"

 

static struct class *fifth_button_dev_class;

int major;

staticDECLARE_WAIT_QUEUE_HEAD(button_waitq);

/*中断事件标志,中断处理函数将其置1,third_drv_read将其置0*/

static volatile int ev_press=0;

 

/*自定义中断结构体*/

struct button_irq_desc{

       intirq;//按键中断号

       intnumber;//

       char*name;//按键名

};

 

//按键数组

static struct button_irq_descbutton_irqs[]={

       {IRQ_EINT(0),0,"K0"},

      {IRQ_EINT(1),1,"K1"},

       {IRQ_EINT(2),2,"K2"},

       {IRQ_EINT(3),3,"K3"},

       {IRQ_EINT(4),4,"K4"},

       {IRQ_EINT(5),5,"K5"},

       {IRQ_EINT(19),6,"K6"},

       {IRQ_EINT(20),7,"K7"},

};

 

//static volatile char key_values[]={'0','0','0','0','0','0','0','0'};

static volatile int keyValue=0;

 

 

static irqreturn_t buttons_irq(int irq,void *dev_id)

{

       printk("irq=%d\n",irq);

       //确定按键值

       structbutton_irq_desc *button_irqs=(struct button_irq_desc *)dev_id;

       intnumber;

       intdown;

       unsignedtmp;

      

              number = button_irqs->number;

       //检查哪个键按下

       switch(number){

       case0: case 1: case 2: case 3: case 4: case 5:

              tmp = readl(S3C64XX_GPNDAT);

              down = !(tmp & (1<<number));

              break;

       case6: case 7:

              tmp = readl(S3C64XX_GPLDAT);

              down = !(tmp & (1 << (number +5)));

              break;

       default:

              down = 0;

       }

       printk("number=%d\n",number);

       printk("down=%d\n",down);

       /*按下down=1*/

        if (down) {

 

              keyValue=10+number;

              printk("key %d down,key value= %d\n",number,keyValue);

             

           }else{//松开

              keyValue=number;

              printk("key %d up,keyvalue = %d\n",number,keyValue);

       }

       ev_press= 1;

       wake_up_interruptible(&button_waitq);

       returnIRQ_RETVAL(IRQ_HANDLED);

}

 

static int fifth_button_dev_open(structinode *inode, struct file *file)

{

       printk("fifth_button_dev_open!\n");

       //采用中断的方式

       //注册中断处理函数    

       inti;

       interr=0;

       for(i=0;i<sizeof(button_irqs)/sizeof(button_irqs[0]);i++){

              if(button_irqs[i].irq<0){

                     continue;

              }                  

              err=request_irq(button_irqs[i].irq,buttons_irq,IRQ_TYPE_EDGE_BOTH,button_irqs[i].name,(void*)&button_irqs[i]);

              if(err)

                     break;

       }

       return0;

}

 

int fifth_button_dev_close(struct inode*inode, struct file *file){

       //注销中断处理程序

       inti;

           for (i = 0; i <sizeof(button_irqs)/sizeof(button_irqs[0]); i++) {

              if (button_irqs[i].irq < 0) {

                         continue;

              }

              free_irq(button_irqs[i].irq, (void*)&button_irqs[i]);

     }    

       return0;

}    

 

static ssize_t fifth_button_dev_read(structfile *file,const char __user *buf,size_t size,loff_t * ppos)

{

       if(size!=1){

              return -EINVAL;

       }

       //如果没有按键动作发生就休眠

       wait_event_interruptible(button_waitq,ev_press);

       //如果有动作发生直接返回

       copy_to_user(buf,&keyValue,1);

       ev_press=0;

       return1;

}

 

static unsignedfifth_button_dev_poll(struct file *file, poll_table *wait)

{

       unsignedint mask=0;

       //不会产即休眠

       poll_wait(file,&button_waitq, wait);

       if(ev_press)

       {

              mask |= POLLIN | POLLWRNORM;

       }

       returnmask;

}

 

static struct file_operationsfifth_button_dev_fops = {

   .owner      =   THIS_MODULE,

   .open       =   fifth_button_dev_open,

   .release    =   fifth_button_dev_close,

   .read       =   fifth_button_dev_read,

   .poll =   fifth_button_dev_poll,

};

 

/*注册驱动程序*/

static int __initfifth_button_dev_init(void){

       /*major设备的主设备号,name是驱动程序的名称,fops默认的是file_operations结构*/

       //如果主设备号为0,系统会自动分配

       major=register_chrdev(0,DEVICE_NAME,&fifth_button_dev_fops);

       fifth_button_dev_class= class_create(THIS_MODULE,DEVICE_NAME);

       //创建设备节点

       device_create(fifth_button_dev_class,//

                            NULL,//

                            MKDEV(major,0),//

                            NULL,//                      

                            DEVICE_NAME);//

       return0;

}

 

static void __exitfifth_button_dev_exit(void){

       //删除设备节点

       device_destroy(fifth_button_dev_class,MKDEV(major,0));

       if(fifth_button_dev_class){

              class_destroy(fifth_button_dev_class);

       }

 

       /*major和name必须和注册时的值一致*/

       unregister_chrdev(major,DEVICE_NAME);

       return;

}

 

 

module_init(fifth_button_dev_init);

module_exit(fifth_button_dev_exit);

 

MODULE_AUTHOR("RETACN");

MODULE_DESCRIPTION("FIFTH BUTTONdriver");

MODULE_LICENSE("GPL");

测试程序示例代码如下:

#include <sys/types.h>

#include <sys/stat.h>

#include <stdio.h>

#include <fcntl.h>

#include <poll.h>

 

/*测试添加pull机制后的中断方式按键驱动*/

int main(int argc,char **argv){

       intfd;    

       unsignedchar key_vals;

      int ret;

       structpollfd fds[1];

 

       fd=open("/dev/fifth_button_dev",O_RDWR);     

       if(fd<0){

              printf("can not open!\n");

       }

 

       fds[0].fd     = fd;

       fds[0].events= POLLIN;             

       while(1){

              ret=poll(fds,1,5000);

              if(ret==0){    

                     printf("timeout!\n");

              }else{

                     read(fd,&key_vals,1);

                     printf("userkey value= %d \n",key_vals);

              }

             

       }

       return0;

}

 

查看poll函数的使用方法:

#include <poll.h>

 

      int poll(struct pollfd *fds, nfds_t nfds, int timeout);

 

 

 

0 0