初识v4l2(四)-------v4l2_open、v4l2_read、v4l2_write浅析
阅读原文时间:2023年07月10日阅读:1

原文: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;
}