初识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;
}

 

转载于:https://www.cnblogs.com/-glb/p/10294523.html

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值