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

初识v4l2(四)-------v4l2_open、v4l2_read、v4l2_write浅析的更多相关文章

  1. 初识v4l2(五)-------v4l2_ioctl浅析

    上一篇文章中,已经介绍了v4l2_open.v4l2_read.v4l2_write的调用过程,相对于v4l2_ioctl,它们是比较简单的.下面来分析v4l2_ioctl.注意在这里还是分析以viv ...

  2. 初识V4l2(二)-------浅析video_register_device

    在V4l2初识(一)中,我们已经知道当插上一个摄像头的时候,在uvc_driver.c中最终会调用函数video_register_device函数.接下来我们就简要分析这个函数做了哪些事情,揭开其神 ...

  3. 初识V4L2(三)-------分析vivi.c 虚拟视频驱动

    1.分配video_device结构体 2.设置 3.注册  video_register_device 分析vivi.c: vivi_init( )//入口函数 vivi_create_instan ...

  4. 初识V4L2(一)

    V4L2驱动框架概述 V4L2(video for linux two)是linux为视频设备提供的一套标准接口.它也属于字符设备驱动程序. 首先回顾普通字符设备驱动程序的写法: app :      ...

  5. 初识Haskell 四:函数function之二 常见函数

    对Discrete Mathematics Using a Computer的第一章Introduction to Haskell进行总结.环境Windows,关于函数的部分太长了,分开写. 常用的对 ...

  6. pandas 初识(四)

    Pandas 和 sqlalchemy 配合实现分页查询 Mysql 并获取总条数 @api.route('/show', methods=["POST"]) def api_sh ...

  7. 初识PHP(四)PDO对象配置于使用

    一.PDO的概念 PDO其实就是一个数据库的抽象层,使用PDO编程可以方便的在之后的实际运营中随时更改数据库而不用变更源代码.PDO的位置如下图所示: 二.PDO的开启 PDO需要使用php 5.1 ...

  8. Hive初识(四)

    Hive本质上是一个数据仓库,但不存储数据(只存储元数据(metadata),Hive中的元数据包括表的名字,表的列和分区及分区及其属性,表的属性(是否为外部表等),表的数据所在目录等),用户可以借助 ...

  9. Python正则表达式初识(四)

    今天继续给大家分享Python正则表达式基础知识,主要给大家介绍一下特殊字符“{}”的用法,具体的教程如下. 特殊字符“{}”实质上也是一个限定词的用法,其限定前面字符所出现的次数,其常用的模式有三种 ...

随机推荐

  1. [C0] 人工智能大师访谈 by 吴恩达

    人工智能大师访谈 by 吴恩达 吴恩达采访 Geoffery Hinton Geoffery Hinton主要观点:要阅读文献,但不要读太多,绝对不要停止编程. Geoffrey Hinton:谢谢你 ...

  2. SpringBoot(十八)_springboot打成war包部署

    最近在做项目的时候,由于使用的是springboot,需要打成war包.我就按照正常的思路去打包,结果部署后无法访问,一直报错404.后续问了问 公司同事,他给解决了.说大部分都是这个原因. 如果需要 ...

  3. luoguP2463 [SDOI2008]Sandy的卡片

    题意 显然加上一个数相等就是差分数组相等,于是问题变为求几个串的最长公共子串. 这里我学习了如何用SA求LCS. 首先问题要转化成求一些后缀的最长公共前缀,要求这些后缀分属不同的串. 于是二分答案,于 ...

  4. Docker 简单发布dotnet core项目 文本版

    原文:https://www.cnblogs.com/chuankang/p/9474591.html docker发布dotnet core简单流程 照着步骤来基本没错 但是有几个要注意的地方: v ...

  5. luogu4570 元素

    题目链接 problem 有\(n\)个二元组, \((x,y)\),要选出一些二元组,使得他们的\(x\)的任何一个子集的异或和不为\(0\)并且\(y\)的和最大. solution 考虑是\(x ...

  6. 关于 Noip的考纲

    关于 \(\text{Noip}\) 的考纲 先放一张图 : 此图包含了 \(\text{Noip}\) 自开始到结束 的所有真题的考察知识点 算法分类标准主要来自于 \(\text{Luogu}\) ...

  7. 【转】java MessageDigest类的作用 :提供信息摘要 算法( MD5 或 SHA 等)的功能

    一.结构和概述: java.lang.Object java.security.MessageDigestSpi java.security.MessageDigest 声明:public abstr ...

  8. 【新特性速递】优化Shift/Ctrl行多选逻辑,和Windows的文件资源管理器保持一致!

    别告诉我你不知道 别告诉我你不知道可以使用键盘的 Shift 和 Ctrl 来多选表格行,因为这个在 Windows 文件资源管理器中的常用操作,此时的画风是这样的: 这个动图中进行了如下操作: 1. ...

  9. Emgucv 4 下载、安装、配置

    1.下载.安装 过程可查看之前Emgucv 3的说明:https://www.cnblogs.com/doget/p/7776377.html 安装完成后,安装目录下会生成如下文件及文件夹: 2.配置 ...

  10. ZooKeeper 入门看这篇就够了

    什么是 ZooKeeper? ZooKeeper 是一个分布式的,开放源码的分布式应用程序协同服务.ZooKeeper 的设计目标是将那些复杂且容易出错的分布式一致性服务封装起来,构成一个高效可靠的原 ...