globalfifo设备驱动

把globalmem中的全局内存变成一个FIFO,只有当FIFO中有数据的时候(即有进程把数据写到这个FIFO而且没有被读进程读空),读进程才能把数据读出,而且读取后的数据会从globalmem的全局内存中被拿掉;只有当fifo非满时(即还有一些空间未被写,或写满后被读进程读出了数据),写进程才能写入数据。现在将globalmem重命名为“globalfifo",在globalfifo中,读fifo将唤醒写fifo,写fifo也将唤醒读fifo.

(一) 支持阻塞操作的globalfifo设备驱动

   在globalfifo设备结构体上需要添加两个等待队列头,分别对应于读和写。

   struct globalfifo_dev

   {

    struct cdev cdev;                            //cdev结构体

    unsigned int current_len;                    //fifo有效数据长度

    unsigned char mem[GLOBALFIFO_SIZE];          //全局内存

    struct semaphore sem;                        //并发控制用的信号量

    wait_queue_head_t r_wait;                    //阻塞读用的等待队列头

    wait_queue_head_t w_wait;                    //阻塞写用的等待dui lie

   };

    等待队列需要在设备驱动模块加载函数中调用init_waitqueue_head()被初始化。

    int gloaalfifo_init(void)

    {

     int ret;

     dev_t devno=MKDEV(globalfifo_major,0);

     //申请设备号

    if(globalfifo_major)

      ret=register_chrdev_region(devno,1,"globalfifo");

    else

      {          //动态申请设备号

       ret=alloc_chrdev_region(&devno,0,1,"globalfifo");

       globalfifo_major=MAJOR(devno);

      }

     if(ret<0)

        return ret;

       //动态申请设备结构体的内存

       globalfifo_devp=kmalloc(sizeof(struct globalfifo_dev),GFP_KERNEL);

       if(!globalfifo_devp)

       {

         ret=-ENOMEM;

         goto fail_malloc;

       }

       memset(globalfifo_devp,0,sizeof(struct_globalfifo_dev));

       globalfifo_setup_cdev(globalfifo_devp,0);

       init_MUTEX(&globalfifo_devp->sem);                 //初始化信号量

       init_waitqueue_head(&globalfifo_devp->r_wait);      //初始化读等待队列

       init_waitqueue_head(&globalfifo_devp->w_wait);      //初始化写等待队列

       return 0;

       fail_malloc:unregister_chrdev_region(devno,1);

       return ret;

 }

设备驱动读函数中应该增加等待globalfifo_devp->w_wait被唤醒的语句;而在写操作中唤醒globalfifo_devp->r_wait.

    static ssize_t globalfifo_read(struct file *filp,char_user *buf,size_t count,loff_t *ppos)

    {

      int ret;

      struct globalfifo_dev *dev=filp->private_data;       //获得设备结构体指针

      DECLARE_WAITQUEUE(wait,current);                     //定义等待队列

      down(&dev->sem);                                     //获得信号量

      add_wait_queue(&dev->r_wait,&wait);                  //进入读等待队列头

      //等待fifo非空

      while(dev->current_len==0)

       {

        if(filp->f_flags &o_nonblock)

        {

          ret=-EAGAIN;

          goto out;

        }

       _set_current_state(TASK_INTERRUPTIBLE);           //改变进程状态为睡眠

      up(&dev->sem);

      schedule();                                        //调度其他进程执行

      if(signal_pending(current))                        //如果是因为信号唤醒

      {

       ret=-ERESTARTSYS;

       goto out2;

      }

       down(&dev->sem);

     }

     //拷贝到用户空间

     if(count > dev->current_len)

       count=dev->current_len;

     if(copy_to_user(buf,dev->mem,count)

       {

         ret=-EFAULT;

         goto out;

        }

        else

            {

              memcpy(dev->mem,dev->mem+count,dev->current_len-count);         //fifo数据前移

              dev->current_len-=count;                                        //有效数据长度减少。

             printk(KERN_INFO "read %d bytes(s),current_len:%d\n",count,dev->current_len);

             wake_up_interruptible(&dev->w_wait);                             //唤醒写等待队列

             ret=count;

           }

           out:up(&dev->sem);                                                 //释放信号量

           out2:remove_wait_queue(&dev->w_wait,&wait);                        //移除等待队列

           set_current_state(TASK_RUNNING);

           return ret;

     }

     //globalfifo写操作

    static ssize_t globalfifo_write(struct file *filp,const char _ _user *buf,size_t count, loff_t *ppos)

   {

    struct globalfifo_dev *dev=filp->private_data;          //获得设备结构体指针

    int ret;

    DECLARE_WAITQUEUE(&dev->w_wait,&wait);                  //进入写等待队列

    down(&dev->sem);                                        //获得信号量

    add_wait_queue(&dev->w_wait,&wait);                     //进入写等待队列头

    //等待fifo非满

    while(dev->current_len=GLOBALFIFO_SIZE)

    {

      if(filp->f_flags &O_NONBLOCK )

      {

            //如果是非阻塞访问

           ret=-EAGAIN;

           goto out;

       }

     _set_current_state(TASK_INTERRUPTIBLE);  //改变进程状态为睡眠

    up(&dev->sem);                   

    schedule();                    //调度其它进程执行

    if(signal_pending(current))

    {

           //如果是因为信号唤醒

      ret=-ERESTARTSYS;

      goto out2;

     }

    down(&dev->sem);

  }

  if(count>GLOBALFIFO_SIZE-dev->current_len)

  count=GLOBALFIFO_SIZE-dev->current_len;

  if(copy_from_user(dev->mem+dev->current_len,buf,count))

  {

   ret=-EFAULT;

   goto out;

   }

  else

     {
     dev->current_len+=count;

     printk(KERNEL_INFO"written %d byte(S),current_len:%d\n",count,dev->current_len);

     wake_up_interruptible(&dev->r_wait);     //唤醒读等待队列

     ret=count;

    }

    out:up(&dev->sem);           

    out2:remove_wait_queue(&dev->w_wait,&wait);

    set_current_state(TASK_RUNNING);

    return ret;

  }

                                         (二)支持轮询操作的globalfifo驱动

 在global的poll()函数中,首先将设备结构体中的r_wait和w_wait等待队列添加到等待列表,然后通过判断dev->current_len是否等于0来获得设备的可读状态,通过判断dev->current_len是否等于GLOBALFIFO_SIZE来获得设备的可写状态。

   global设备驱动的poll()函数

                            static unsigned int globalfifo_poll(struct file *filp,poll_table *wait)

                            {

                             unsigned int mask=0;

                             struct globalfifo_dev *dev=filp->private_data;     //获得设备结构体指针

                             down(&dev-sem);                                    //获得信号量

                             poll_wait(filp,&dev->r_wait,wait);

                             poll_wait(filp,&dev->w_wait,wait);                  

                             //fifo非空

                             if(dev->current_len!=0)

                               mask|=POLLIN|POLLRDNORM;             //标示数据可获得

                             if(dev->current_len!=GLOBALFIFO_SIZE)

                               mask|=POLLOUT|POLLWRNORM;            //标示数据可写入

                          up(&dev->sem);          //释放信号量

                          return mask;   

              }

     注意要把globalfifo_poll赋给globalfifo_fops的poll成员。

                                       (三) 支持异步通知的globalfifo设备驱动

 首先应该将异步通知的结构体指针加入到globalfifo设备的结构体里面去。

  

struct globalfifo_dev

   {

    struct cdev cdev;                            //cdev结构体

    unsigned int current_len;                    //fifo有效数据长度

    unsigned char mem[GLOBALFIFO_SIZE];          //全局内存

    struct semaphore sem;                        //并发控制用的信号量

    wait_queue_head_t r_wait;                    //阻塞读用的等待队列头

    wait_queue_head_t w_wait;                    //阻塞写用的等待dui lie

    struct fasync_struct *async_queue;           //异步结构体指针,用于读

   };

    支持异步通知的globalfifo设备驱动fasync()函数

 

        static int globalfifo_fasync(int fd,struct file *filp,int mode)

        {

          struct globalfifo_dev *dev=filp->private_data;

          return fasync_helper(fd,filp,mode,&dev->async_queue);

        }

  支持异步通知的globalfifo设备驱动写函数

        static ssize_t globalfifo_write(struct file *filp,const char _user *buf,size_t count,loff_t *ppos)

        {

          struct globalfifo_dev *dev=filp->private_data;                 //获得设备结构体指针

          int ret;

          DECLARE_WAITQUEUE(wait,current);                               //定义等待队列

          down(&dev->sem);                                               //取得信号量

          add_wait_queue(&dev->w_wait,&wait);                            //进入写等待队列

          //等待fifo未满

        if(dev->current_len==GLOBALFIFO_SIZE)

           {

             if(filp->f_flags&O_NOBLOCK)

             {           //如果是非阻塞访问

               ret=-EAGAIN;

               goto out;

             }

             _set_current_state(TASK_INTERRUPTIBLE)                 //改变进程状态为睡眠

             up(&dev->sem);

            schedule();

            if(signal_pending(current)

              ret=-ERESTARTSYS;

              goto out2;

           }

         down(&dev->sem);

   }

  //从用户空间拷贝到内核空间

   if(count >GLOBALFIFO_SIZE- dev->current_len)

    count=GLOBALFIFO_SIZE-dev->current_len;

   if(copy_form_user(dev->mem+dev->current_len,buf,count))

    {
     ret=-EFAULT;

     goto out;

  }

   else

       {

           dev->current_len+=count;

           wake_up_interruptible(&dev->r-wait);      //唤醒读等待队列

        }

     //产生异步读信号

          if(dev->async_queue)

            kill_fasync(&dev->async_queue,SIGIO,POLL_IN);

             ret=count;

 }

 out:up(&dev->sem);

 out2:remove_wait_queue(&dev->w_wait,&wait);

set_current_state(TASK_RUNNING);

return ret;

}

最后记得在release()函数里用globalfifo_fasync()函数将文件从异步通知列表中删除

 int globalfifo_release(struct inode *inode,struct file *filp)

 {

   globalfifo_fasync(-1,filp,0);

   return 0;

 }

                

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

posted on 2014-04-17 22:11  黑主优姬  阅读(941)  评论(0编辑  收藏  举报

导航