初識v4l2(四)-------v4l2_open、v4l2_read、v4l2_write淺析


原文:https://blog.csdn.net/leesagacious/article/details/49995729

1、app:     open("/dev/video0",....)

   drv:      v4l2_fops

       .v4l2_open  //這個函數主要做的是,調用具體設備提供的open函數

 /* 問題來了,應用程序調用open("/dev/video0",....),v4l2_open為什么會最終會被調用?

  video_register_device
  {
    __video_register_device(...)
    {
      vdev->cdev = cdev_alloc();
      if (vdev->cdev == NULL) {
        ret = -ENOMEM;
        goto cleanup;
      }
      vdev->cdev->ops = &v4l2_fops;
      vdev->cdev->owner = owner;
      ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1);
    }
  }

  static const struct file_operations v4l2_fops = {
    .owner = THIS_MODULE,
    .read = v4l2_read,
    .write = v4l2_write,
    .open = v4l2_open,
    .get_unmapped_area = v4l2_get_unmapped_area,
    .mmap = v4l2_mmap,
    .unlocked_ioctl = v4l2_ioctl,
    #ifdef CONFIG_COMPAT
    .compat_ioctl = v4l2_compat_ioctl32,
    #endif
    .release = v4l2_release,
    .poll = v4l2_poll,
    .llseek = no_llseek,
  };

*/

static int v4l2_open(struct inode *inode, struct file *filp)
{
  struct video_device *vdev;
  int ret = 0;

  /* Check if the video device is available */
  mutex_lock(&videodev_lock);
  vdev = video_devdata(filp);

  /*

    video_device[iminor(file->f_path.dentry->d_inode)];  

      iminor(const struct inode *inode)
        MINOR(inode->i_rdev);

   分析: struct inode *inode = file->f_path.dentry->d_inode;

       int  minor = MINOR(inode ->i_rdev)

       根據次設備號minor獲取在video_register_device函數中注冊的video_device,它是放在video_device[]數組中

   問題:video_device是在什么時候放入video_device[]數組的,

      次設備號又是怎樣構建的呢?

      下面簡要分析一下:

    i : video_device[]第一個空缺項的下標
    minor_offset : 根據傳入的類型 選擇得到
    switch (type) {
    case VFL_TYPE_GRABBER:
      minor_offset = 0;
      minor_cnt = 64;
      break;
    case VFL_TYPE_RADIO:
      minor_offset = 64;
      minor_cnt = 64;
      break;
    case VFL_TYPE_VBI:
      minor_offset = 224;
      minor_cnt = 32;
      break;
    default:
      minor_offset = 128;
      minor_cnt = 64;
      break;
    }
    vdev->minor = i + minor_offset;//這就回答了上面的次設備號是如何被創建的
    ...
    ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1);

    是video_register_device()來實現的。
    video_register_device()
    {
      return __video_register_device(vdev, type, nr, 1, vdev->fops->owner)
    {
      再將設置好的video_device放入到video_device[]之前,設置它的成員flags(unsigned long 類型)
      的第0位,表示這個video_device是注冊過的了,
      在其他位置會調用video_is_registered()來判斷,其依據還是flags的第0位的值 
      set_bit(V4L2_FL_REGISTERED, &vdev->flags);
      mutex_lock(&videodev_lock);
      依據次設備號為下標,將設置好的video_device放入到video_device[]中
      video_device[vdev->minor] = vdev;

      mutex_unlock(&videodev_lock);
    }
  }

  */

  /*
  static inline int video_is_registered(struct video_device *vdev)
  {
    返回flags的第0位,
    如果為1 表示已經注冊過了,否則表明沒有注冊過。
    在注冊的時候 ,就已經設置flags的第0位了。
    看注冊的源碼v4l2_dev.c : 
    video_register_device()
    {
      __video_register_device()
      {
        1 : 設置flags的第0位,表示它已經被注冊過了。 
        set_bit(V4L2_FL_REGISTERED, &vdev->flags);

        2 : 獲取互斥鎖 --- 訪問臨界區 ----- 加鎖
        mutex_lock(&videodev_lock);
        //依據次設備號為索引值將設置好的video_device放到video_device[]中
        video_device[vdev->minor] = vdev;
        mutex_unlock(&videodev_lock);
      }
    }
    return test_bit(V4L2_FL_REGISTERED, &vdev->flags);
  }
  */

  if (vdev == NULL || !video_is_registered(vdev)) {
  mutex_unlock(&videodev_lock);
  return -ENODEV;
  }
  /* and increase the device refcount*/

  /*
    增加設備的引用計數
    底層是通過kobject_get()來實現
    kobject是通過內嵌的struct kref 來實現的。
    struct kref { //kref是一個引用計數器,它被嵌套進其他的結構體中,記錄所嵌套結構的引用計數
    atomic_t refcount;
    }
  */

  video_get(vdev);
  mutex_unlock(&videodev_lock);//釋放互斥鎖
  if (vdev->fops->open) {

  /*這個lock是再v4l2_device_register()中初始化過了

  這里主要是判斷有沒有進行初始化,如果初始化了,core層會幫你釋放這個鎖。具體是在這個函數的最后一段代碼中。

  */
  if (vdev->lock && mutex_lock_interruptible(vdev->lock)) {
    ret = -ERESTARTSYS;
    goto err;
  }

  //如果設備已經是被注冊的了,就調用它提供的open函數
  if (video_is_registered(vdev))
    ret = vdev->fops->open(filp);//調用具體設備的fops的open函數
  else
    ret = -ENODEV;

  //如果使用v4l2_device_register()進行初始化了,就釋放這把鎖 
  if (vdev->lock)
    mutex_unlock(vdev->lock);
  }

  err:
  /* decrease the refcount in case of an error */
  if (ret)
    video_put(vdev);
  return ret;
}

-----------------------------------------------------------------------------------

2、app:  read

  drv:v4l2_fops

      .v4l2_read //這個函數主要做的就是調用具體設備提供的read函數

如果清楚了v4l2_open的過程,那么對於v4l2_read的過程應該是很簡單了,現只將源碼貼出:

static ssize_t v4l2_read(struct file *filp, char __user *buf,
size_t sz, loff_t *off)
{
  struct video_device *vdev = video_devdata(filp);
  int ret = -ENODEV;

  if (!vdev->fops->read)
    return -EINVAL;
  if (vdev->lock && mutex_lock_interruptible(vdev->lock))
    return -ERESTARTSYS;
  if (video_is_registered(vdev))
    ret = vdev->fops->read(filp, buf, sz, off);
  if (vdev->lock)
    mutex_unlock(vdev->lock);
  return ret;
}

-------------------------------------------------------------------------------------------------

3、app:   write

   drv:   v4l2_fops

      .v4l2_write//這個函數主要做的就是調用具體設備提供的read函數

static ssize_t v4l2_write(struct file *filp, const char __user *buf,
size_t sz, loff_t *off)
{
  struct video_device *vdev = video_devdata(filp);
  int ret = -ENODEV;

  if (!vdev->fops->write)
    return -EINVAL;
  if (vdev->lock && mutex_lock_interruptible(vdev->lock))
    return -ERESTARTSYS;
  if (video_is_registered(vdev))
    ret = vdev->fops->write(filp, buf, sz, off);
  if (vdev->lock)
    mutex_unlock(vdev->lock);
  return ret;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM