zoukankan      html  css  js  c++  java
  • 初识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;
    }
  • 相关阅读:
    点云数据的存储格式
    模块编写流程
    特征描述子
    指针和引用的差别
    内联函数和宏定义的差别
    哪些函数不能为virtual函数
    如何定义一个只能在堆上(栈上)生成对象的类
    对象深拷贝问题
    Warning: Failed prop type: Invalid prop `value` supplied to `Picker`.报错问题
    解决多层数组、对象深拷贝问题
  • 原文地址:https://www.cnblogs.com/-glb/p/10294523.html
Copyright © 2011-2022 走看看