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

参考原文:https://blog.csdn.net/leesagacious/article/details/49948163

/* Register video devices. Note that if video_register_device fails,
the release() callback of the video_device structure is *not* called, so
the caller is responsible for freeing any data. Usually that means that
you call video_device_release() on failure. */

/*注册video_device,注意如果注册失败,video_device结构体中的release函数不会被调用, 调用者负责释放所有的数据,通常是调用video_device_release()函数来释放*/

static inline int  video_register_device(struct video_device *vdev,int type, int nr)
{
   //调用这个函数来注册video_device

   return __video_register_device(vdev, type, nr, 1, vdev->fops->owner);
}

参数一:video_devide ------即我们想要注册的video_device结构体

参数二:type-----------要注册的device类型,其中包括:

    define VFL_TYPE_GRABBER  0   图像采集设备,包括阿摄像头、调谐器

    define VFL_TYPE_VBI1          1    从视频消隐的时间段取得信息的设备(1)

    #define VFL_TYPE_RADIO 2           无线电设备

    #define VFL_TYPE_SUBDEV 3        视频传播设备

    #define VFL_TYPE_MAX 4

参数三:int nr------------device node number

        0 == /dev/video 0       1 == /dev/video1     ........    -1 == first free

int __video_register_device(struct video_device *vdev, int type, int nr,
int warn_if_nr_in_use, struct module *owner)
{
  int i = 0;
  int ret;
  int minor_offset = 0;     //minor = i+minor_offset
  int minor_cnt = VIDEO_NUM_DEVICES;  //用于设备节点序号
  const char *name_base; //设备的名称会根据传入的type来选择

/* A minor value of -1 marks this video device as never having been registered

   次设备号为-1,表明这个设备还没有被注册 */

  vdev->minor = -1;

/* the release callback MUST be present

   如果要注册的video_device没有提供release函数,就要出错返回*/

  if (WARN_ON(!vdev->release))
    return -EINVAL;

  /* v4l2_fh support */
  spin_lock_init(&vdev->fh_lock);       //获取自旋锁
  INIT_LIST_HEAD(&vdev->fh_list);  //初始化链表头

  /* Part 1: check device type

  根据传入的类型type,来选择设备的名称。后面会调用函数dev_set_name(&vdev->dev, "%s%d", name_base, vdev->num)来设置设备的名称

*/
  switch (type) {
  case VFL_TYPE_GRABBER:
    name_base = "video";
    break;
  case VFL_TYPE_VBI:
    name_base = "vbi";
    break;
  case VFL_TYPE_RADIO:
    name_base = "radio";
    break;
  case VFL_TYPE_SUBDEV:
    name_base = "v4l-subdev";
    break;
  default:
    printk(KERN_ERR "%s called with unknown type: %d\n",__func__, type);
 return -EINVAL;
}

  vdev->vfl_type = type;
  vdev->cdev = NULL;
  if (vdev->v4l2_dev) {
    if (vdev->v4l2_dev->dev)
      vdev->parent = vdev->v4l2_dev->dev;
  if (vdev->ctrl_handler == NULL)

  /*在app应用程序中,可以通过Ioctl来设置、获得亮度等信息。那么驱动程序谁来接收/存储/ 设置到硬件,或从硬件中获得这些信息?
  在驱动程序里面抽象出一个结构体V4L2_ctrl。
  那么谁来管理v4l2_ctrl呢?
  是利用v4l2_ctrl_handler进行管理的,它像链表一样,里面需要填充各个属 性,也可理解为设置各个属性。
  在注册video_device之前的vivi_create_instance()函数中,
  初始化v4l2_ctrl_handler
  v4l2_ctrl_handler_init(hdl, 11);
  创建v4l2_ctrl 并放入到v4l2_ctrl_handler链表
  v4l2_ctrl_new_std()
  v4l2_ctrl_new_custom()
  dev->v4l2_dev.ctrl_handler = hdl;       //dev->v4l2_dev : v4l2_device 每一个v4l2设备都用这个结构来描述
  下面的代码是将v4l2_ctrl_handler与video_device进行了关联
*/

  vdev->ctrl_handler = vdev->v4l2_dev->ctrl_handler;
  /* If the prio state pointer is NULL, then use the v4l2_device prio state. */
  if (vdev->prio == NULL)
    vdev->prio = &vdev->v4l2_dev->prio;
}

 /* Part 2: find a free minor, device node number and device index. */
 #ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES
  /* Keep the ranges for the first four types for historical
  * reasons.
  * Newer devices (not yet in place) should use the range
  * of 128-191 and just pick the first free minor there
  * (new style).

  根据传入的类型type,为选择次设备号、设备节点序号作准备。可见可见次设备号是分段使用的

   minor = i + 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;
}
#endif

/* Pick a device node number */
  mutex_lock(&videodev_lock);   //获取互斥锁

  /*获取一个没有被使用的设备节点序号。如果上面传入的是-1,VFL_TYPE_GRABBER,它会从0-64中选择*/
  nr = devnode_find(vdev, nr == -1 ? 0 : nr, minor_cnt);
  if (nr == minor_cnt)
    nr = devnode_find(vdev, 0, minor_cnt);
  if (nr == minor_cnt) {
    printk(KERN_ERR "could not get a free device node number\n");
  mutex_unlock(&videodev_lock);
  return -ENFILE;
  }
#ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES
/* 1-on-1 mapping of device node number to minor number */
  i = nr;
#else
  /* The device node number and minor numbers are independent, so we just find the first free minor number.

  static struct video_device *video_device[256];  从video_device[]数组中选择一个空缺项,这个空缺项的索引值放到i中

  为下面把video_device放入到video_device[i]中做准备,这个设计和registered_fb[]设计的类似

  */

  for (i = 0; i < VIDEO_NUM_DEVICES; i++)
    if (video_device[i] == NULL)
      break;

     //检查i的值有没有超过256,否则出错返回
    if (i == VIDEO_NUM_DEVICES) {
      mutex_unlock(&videodev_lock);
      printk(KERN_ERR "could not get a free minor\n");
      return -ENFILE;
    }
#endif

  /*设备的次设备号*/
  vdev->minor = i + minor_offset;
  vdev->num = nr;
  devnode_set(vdev);

  /* Should not happen since we thought this minor was free

  再一次测试这个video_device[i]是否是空缺项*/

  WARN_ON(video_device[vdev->minor] != NULL);
  vdev->index = get_index(vdev);
  mutex_unlock(&videodev_lock);   //释放互斥锁

  /* Part 3: Initialize the character device

  分配cdev结构体 cdev代表了字符设备的通用信息,通常被嵌入在一个更大的结构体中*/

  vdev->cdev = cdev_alloc();
  if (vdev->cdev == NULL) {
    ret = -ENOMEM;
    goto cleanup;
  }

  /*设置file_operations为v4l2_fops
  用户层调用open、mmap、ioctl、read...的时候,这个v4l2_fops中的对应的方法会响应。

  如 : 用户层调用mmap(),那么v4l2_fops中的v4l2_mmap()会被调用。
  在v4l2_mmap()中,会调用具体设备提供的mmap函数

  static int v4l2_mmap(struct file *filp, struct vm_area_struct *vm)
  {
    ....
    ret = vdev->fops->mmap(filp, vm);
    ....
  }
  */

  vdev->cdev->ops = &v4l2_fops;
  vdev->cdev->owner = owner;

   /*添加字符设备到系统*/
  ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1);
  if (ret < 0) {
    printk(KERN_ERR "%s: cdev_add failed\n", __func__);
    kfree(vdev->cdev);
    vdev->cdev = NULL;
    goto cleanup;
  }

  /* Part 4: register the device with sysfs

  填充video_device中的成员值       设置video_device所属的类,会在/sys/class/下创建目录*/

  vdev->dev.class = &video_class;

   /*通过主设备号和次设备号生成dev_t,表示的是一个设备号*/
  vdev->dev.devt = MKDEV(VIDEO_MAJOR, vdev->minor);
  if (vdev->parent)
    vdev->dev.parent = vdev->parent;

   /*设置设备的名称*/
  dev_set_name(&vdev->dev, "%s%d", name_base, vdev->num);

  /*利用device_register,注册字符设备驱动。这个地方和我们写的字符设备驱动程序是一样的*/
  ret = device_register(&vdev->dev);
  if (ret < 0) {
    printk(KERN_ERR "%s: device_register failed\n", __func__);
    goto cleanup;
  }
/* Register the release callback that will be called when the last reference to the device goes away. */
  vdev->dev.release = v4l2_device_release;

  if (nr != -1 && nr != vdev->num && warn_if_nr_in_use)
    printk(KERN_WARNING "%s: requested %s%d, got %s\n", __func__,name_base, nr, video_device_node_name(vdev));

/* Increase v4l2_device refcount */
  if (vdev->v4l2_dev)
  v4l2_device_get(vdev->v4l2_dev);

#if defined(CONFIG_MEDIA_CONTROLLER)
/* Part 5: Register the entity. */
  if (vdev->v4l2_dev && vdev->v4l2_dev->mdev &&vdev->vfl_type != VFL_TYPE_SUBDEV) {
    vdev->entity.type = MEDIA_ENT_T_DEVNODE_V4L;
    vdev->entity.name = vdev->name;
    vdev->entity.info.v4l.major = VIDEO_MAJOR;
    vdev->entity.info.v4l.minor = vdev->minor;
    ret = media_device_register_entity(vdev->v4l2_dev->mdev,&vdev->entity);
    if (ret < 0)
      printk(KERN_WARNING"%s: media_device_register_entity failed\n",__func__);
  }
#endif
/* Part 6: Activate this minor. The char device can now be used. */

  /*将这个unsigned long flags 的第0 位 置1,表示这个video_device 是注册过的了,

  在其他位置,会调用video_is_registeried( ) 来判断,其依据 还是测试这个flags的第0位。
  video_is_registered( )
  {
    test_bit( V4L2_FL_REGISTERED, &vdev->flags )
  }
  */                                                                                                                                                                                                                           

   //获取锁 ---- 访问临界区 -----释放锁

  set_bit(V4L2_FL_REGISTERED, &vdev->flags);
  mutex_lock(&videodev_lock);

  /*依据次设备号为下标,将设置好的video_device放入到video_device[]中,其他函数会依据次设备号从这个数组中获取对应的video_device,这个和registered_fb[]设计的类似   static struct video_device*video_device[256];*/

  video_device[vdev->minor] = vdev;
  mutex_unlock(&videodev_lock);

return 0;

cleanup:
  mutex_lock(&videodev_lock);
  if (vdev->cdev)
    cdev_del(vdev->cdev);
    devnode_clear(vdev);
  mutex_unlock(&videodev_lock);
/* Mark this video device as never having been registered. */
  vdev->minor = -1;
return ret;
}
EXPORT_SYMBOL(__video_register_device);

注1:VBI:
VBI= vertical blanking interval,也就是我们讲的场消隐期间。
VBI在电视处理中是用来是垂直扫描完成从屏幕底部回到屏幕顶部的时间。在这期间,没有任何的图像信息,在原来是基本废置不用的。后来,利用这期间来传输一些信息,比如CC,图文,VPS/PDC,GEMSTAR等等信息服务。

初识V4l2(二)-------浅析video_register_device的更多相关文章

  1. 初识JavaScript(二)

    初识JavaScript(二) 我从上一篇<初识JavaScript(一)>知道和认识JavaScript的词法结构,也开始慢慢接触到了JavaScript的使用方法,是必须按照JavaS ...

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

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

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

    原文:https://blog.csdn.net/leesagacious/article/details/49995729 1.app:     open("/dev/video0&quo ...

  4. python初识(二)

    目录: 进制 基本数据类型 整形 布尔值 字符串 列表 元祖 字典 集合 range & enumerate 一.进制 二进制转换十进制计算: 十进制:==47 一个字节公式:==128 64 ...

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

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

  6. 初识V4L2(一)

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

  7. webpack入门教程之初识loader(二)

    上一节我们学习了webpack的安装和编译,这一节我们来一起学习webpack的加载器和配置文件. 要想让网页看起来绚丽多彩,那么css就是必不可少的一份子.如果想要在应用中增加一个css文件,那么w ...

  8. python day2:python 初识(二)

    大纲: 一.运算符 1.算数运算符 notice: 除法运算在python2.7和python3.x 的不同 2.比较运算符 3.赋值运算符 4.逻辑运算符 5.成员运算符 二.基本数据类型和方法介绍 ...

  9. Python 基础系列一:初识python(二)基本数据类型

    上节拾遗 1.编码转换过程,utf-8转换gbk 过程 经过解码(py27): x.decode('utf-8')-->unicode-->编码x.encode('gbk') ps:py3 ...

随机推荐

  1. BZOJ1688 「USACO05OPEN」Disease Manangement 背包+状压DP

    问题描述 BZOJ1688 题解 背包,在转移过程中使用状压. \(\mathrm{Code}\) #include<bits/stdc++.h> using namespace std; ...

  2. mysql数据库的批量数据导入与导出,性能提升。

    少量数据批量导入:1. 先从数据库把唯一键的值查询出来,放在列表2. 将导入的数据遍历取出,看是否存在列表中,若不在,说明数据库没有.3. 定义两个空列表,一个做为插入数据,一个做为更新数据4. 步骤 ...

  3. 【2019.8.14 慈溪模拟赛 T2】黑心老板(gamble)(2-SAT)

    \(2-SAT\) 考虑每个点只能选择\(R\)或\(B\),可以看作选\(0\)或\(1\). 然后对于给出的关系式,若其中一个位置满足关系式,另两个位置就必须不满足关系式,这样就可以对于每个关系式 ...

  4. Windows编译运行webrtc全过程

    年纪大了,不想写什么开头.摘要,咱直接开始吧. 不过首先还是要感谢声网提供的webrtc国内源码镜像. 首先,编译webrtc你需要一台win10,而且必须得是一直在更新版本的.因为编译过程需要用到c ...

  5. 详解 IaaS、PaaS和SaaS 以及他们各自的代表公司

    ——IaaS,PaaS和SaaS 是云计算领域的专业术语,也是云计算的三种服务模式.   (1)SaaS:Software as a Service,软件即服务(也称为云应用程序服务) . 云市场中企 ...

  6. Unity Shader NPR 卡通渲染

    卡通渲染的主要原理包含两个方面: 1.轮廓线的描边效果 2.模型漫反射离散和纯色高光区域的模拟 描边: 描边的实现方法采用将模型的轮廓线顶点向法线(或顶点)的方向扩展一定的像素得到.也可通过边缘检测( ...

  7. Algorithm: CRT、EX-CRT & Lucas、Ex-Lucas

    中国剩余定理 中国剩余定理,Chinese Remainder Theorem,又称孙子定理,给出了一元线性同余方程组的有解判定条件,并用构造法给出了通解的具体形式. \[ \begin{aligne ...

  8. 关于 C# 8.0 的 Switch Case When 的用法

    直接贴代码了: static void Main(string[] args) { SwitchSample(); } private static void SwitchSample() { Swi ...

  9. sysstat工具包之mpstat

    mpstat 1 简介 mpstat是一个实时监控工具,主要报告与CPU相关统计信息,信息存放在/proc/stat文件中: 在多核心cpu系统中,不仅可以查看cpu平均信息,还可以查看指定cpu信息 ...

  10. 转 Yolov3转化Caffe框架详解

    转自https://blog.csdn.net/watermelon1123/article/details/82083522 前些日子因工程需求,需要将yolov3从基于darknet转化为基于Ca ...