原文: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. Html学习之三(列表)

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  2. pandas分组统计:groupby,melt,pivot_table,crosstab的用法

    groupby: 分组 melt: 宽表转长表 pivot_table: 长表转宽表,数据透视表 crosstab: 交叉表 / 列联表,主要用于分组频数统计 import numpy as np i ...

  3. django中对数据库生成记录操作失败

    在终端执行以下语句时,会发现一点效果也没有,但是在manage.py中会成功: python3 manage.py makemigrations # 仅仅是在小本本上(migrations文件夹)记录 ...

  4. bzoj2093 Frog

    题目链接 思路 非常有趣的一道题. 先考虑如何找出第K远的位置. 因为给出的序列是单调的,所以对于位置\(i\)的前\(K\)远位置肯定是一个包含位置\(i\)的长度为\(k+1\)的区间.我们用\( ...

  5. JWT签名与验签

    签名Token生产 using System; using System.Collections.Generic; using System.IdentityModel.Tokens.Jwt; usi ...

  6. es6中reduce()方法和reduceRight()方法

    es6中reduce()方法从左往右开始 参数:prev:它是上一次调用回调时返回的结果,每次调用的结果都会给prev cur:当前的元素 index:当前的索引 arr:循环的数组 返回值:函数累计 ...

  7. Appium 环境配置

    前言 Appium 作为移动端自动化测试工具在业界非常流行,特别是在当前移动互联网背景下,很多公司基于此框架来开展自动化测试.但 appium 的环境配置相对 selenium 来说复杂的多,让很多同 ...

  8. 【UOJ#74】【UR #6】破解密码

    [UOJ#74][UR #6]破解密码 题面 UOJ 题解 发现这个过程是一个字符串哈希的过程. 把第一位单独拿出来考虑,假设这个串是\(p+S\),旋转后变成了\(S+p\). 其哈希值分别是:\( ...

  9. FreeMarker实现网页静态化

    1.FreeMarker实现网页静态化. FreeMarker是一个用Java语言编写的模板引擎,它基于模板来生成文本输出.FreeMarker与Web容器无关,即在Web运行时,它并不知道Servl ...

  10. 用ASP.NET创建数据库

    小白的第一次使用: 程序员写程序,就好比一个物品的慢慢诞生,我们今天的这个例子就可以想象成一个物品慢慢的在编译的过程中,让我们所看到 一.创建我们所测试的项目 1.创建一个简单的带有模型层(Model ...