初识v4l2(四)-------v4l2_open、v4l2_read、v4l2_write浅析

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);
    }
  }

接下来看一下v4l2_fops结构体

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,
  };

分析v4l2_open函数:

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);

    /*根据次设备号minor获取在video_register_device函数中注册的video_device*/
    vdev = video_devdata(filp);
/* return ENODEV if the video device has already been removed. */ 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) { if (vdev->lock && mutex_lock_interruptible(vdev->lock)) { ret = -ERESTARTSYS; goto err; } /*如果设备已经注册,就调用具体设备提供的open函数*/ if (video_is_registered(vdev)) ret = vdev->fops->open(filp); 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; }

在v4l2_open函数中的关键操作就是vdev = video_devdata(filp),这个函数的作用就是根据设备的次设备号minor获取在video_register_device函数中注册的video_device。接下来分析该函数是如何获取video_device的。

struct video_device *video_devdata(struct file *file)
{
    return video_device[iminor(file->f_path.dentry->d_inode)];
}
static inline unsigned iminor(const struct inode *inode)
{
    return MINOR(inode->i_rdev);//通过inode节点返回设备的次设备号。
}

因此video_devdata函数实际上就是调用了video_device[设备的次设备号],即以设备的次设备号为下标,从数组video_device中获取一个video_device。

问题1:这个video_device是什么时候放入数组video_device中去的?

问题2:次设备是如何构建的呢?

对于这两个问题的分析在浅析video_register_device一文中已深入分析过。

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

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

 

posted @ 2019-01-20 12:39  一代枭雄  阅读(1928)  评论(0编辑  收藏  举报