我的uvc开源地址:gitee-uvc

  • 字符设备驱动程序核心:V4L2本身就是一个字符设备,具有字符设备所有的特性,暴露接口给用户空间。
  • V4L2 驱动核心:主要是构建一个内核中标准视频设备驱动的框架,为视频操作提供统一的接口函数。
  • 平台V4L2设备驱动:在V4L2框架下,根据平台自身的特性实现与平台相关的V4L2驱动部分,包括注册video_device和v4l2_dev。
  • 具体的sensor驱动:主要上电、提供工作时钟、视频图像裁剪、流IO开启等,实现各种设备控制方法供上层调用并注册v4l2_subdev。

1 从字符设备开始:

熟悉v4l2用户空间编程的都知道, v4l2编程主要是调用一系列的ioctl函数去对v4l2设备进行打开, 关闭, 查询, 设置等操作. v4l2设备是一个字符设备, 而且其驱动的主要工作就是实现各种各样的ioctl.

v4l2的整体框架如下图所示:

V4L2 :video for linux version 2 ,是 linux 里一套标准的视频驱动。本文来分析一下它的核心框架。

在v4l2的核心中对这个file_operations的实现如下:

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_fops函数最终绑定在一个cdev上, 并注册到系统中。

v4l2_open为例(代码在kernel\drivers\media\v4l2-core中):

/* Override for the open function */
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);
/* 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 */
video_get(vdev);
mutex_unlock(&videodev_lock);
if (vdev->fops->open) {
if (video_is_registered(vdev))
//这里就是调用了file_operations的open函数
ret = vdev->fops->open(filp);
else
ret = -ENODEV;
} if (vdev->debug)
printk(KERN_DEBUG "%s: open (%d)\n",
video_device_node_name(vdev), ret);
/* decrease the refcount in case of an error */
if (ret)
video_put(vdev);
return ret;
}

2. video_device结构体:

video_device结构体用于在/dev目录下生成设备节点文件,把操作设备的接口暴露给用户空间。

我们是使用video_device来操作的,看看video_device这个结构体:

struct video_device
{
#if defined(CONFIG_MEDIA_CONTROLLER)
struct media_entity entity;
#endif
/* device ops */
const struct v4l2_file_operations *fops; /* sysfs */
struct device dev; /* v4l device */
struct cdev *cdev; /* character device */ /* Set either parent or v4l2_dev if your driver uses v4l2_device */
struct device *parent; /* device parent */
struct v4l2_device *v4l2_dev; /* v4l2_device parent */ /* Control handler associated with this device node. May be NULL. */
struct v4l2_ctrl_handler *ctrl_handler; /* vb2_queue associated with this device node. May be NULL. */
struct vb2_queue *queue; /* Priority state. If NULL, then v4l2_dev->prio will be used. */
struct v4l2_prio_state *prio; /* device info */
char name[32];
int vfl_type; /* device type */
int vfl_dir; /* receiver, transmitter or m2m */
/* 'minor' is set to -1 if the registration failed */
int minor;
u16 num;
/* use bitops to set/clear/test flags */
unsigned long flags;
/* attribute to differentiate multiple indices on one physical device */
int index; /* V4L2 file handles */
spinlock_t fh_lock; /* Lock for all v4l2_fhs */
struct list_head fh_list; /* List of struct v4l2_fh */ int debug; /* Activates debug level*/ /* Video standard vars */
v4l2_std_id tvnorms; /* Supported tv norms */
v4l2_std_id current_norm; /* Current tvnorm */ /* callbacks */
void (*release)(struct video_device *vdev); /* ioctl callbacks */
const struct v4l2_ioctl_ops *ioctl_ops;
DECLARE_BITMAP(valid_ioctls, BASE_VIDIOC_PRIVATE); /* serialization lock */
DECLARE_BITMAP(disable_locking, BASE_VIDIOC_PRIVATE);
struct mutex *lock;
};

3. V4L2_device结构体:

这个结构体中包含了一个非常重要的结构体v4l2_device

struct v4l2_device {
/* dev->driver_data points to this struct.
Note: dev might be NULL if there is no parent device
as is the case with e.g. ISA devices. */
struct device *dev;
#if defined(CONFIG_MEDIA_CONTROLLER)
struct media_device *mdev;
#endif
/* used to keep track of the registered subdevs */
struct list_head subdevs;
/* lock this struct; can be used by the driver as well if this
struct is embedded into a larger struct. */
spinlock_t lock;
/* unique device name, by default the driver name + bus ID */
char name[V4L2_DEVICE_NAME_SIZE];
/* notify callback called by some sub-devices. */
void (*notify)(struct v4l2_subdev *sd,
unsigned int notification, void *arg);
/* The control handler. May be NULL. */
struct v4l2_ctrl_handler *ctrl_handler;
/* Device's priority state */
struct v4l2_prio_state prio;
/* BKL replacement mutex. Temporary solution only. */
struct mutex ioctl_lock;
/* Keep track of the references to this struct. */
struct kref ref;
/* Release function that is called when the ref count goes to 0. */
void (*release)(struct v4l2_device *v4l2_dev);
};

3.1 v4l2_device的注册和注销:

int v4l2_device_register(struct device*dev, struct v4l2_device *v4l2_dev)

static void v4l2_device_release(struct kref *ref)

4. v4l2_subdev结构体

V4l2_subdev代表子设备,包含了子设备的相关属性和操作。先来看下结构体原型:

struct v4l2_subdev {

         struct v4l2_device *v4l2_dev;  //指向父设备

         //提供一些控制v4l2设备的接口

         const struct v4l2_subdev_ops *ops;

         //向V4L2框架提供的接口函数

         const struct v4l2_subdev_internal_ops *internal_ops;

         //subdev控制接口

         struct v4l2_ctrl_handler *ctrl_handler;

         /* name must be unique */

         charname[V4L2_SUBDEV_NAME_SIZE];

         /*subdev device node */

         struct video_device *devnode;  

};

其中 list 域作为链表节点链接至 v4l2_dev 指向的 v4l2_device 结构中,这个结构中最重要的成员就是 struct v4l2_subdev_ops *ops,该域包含了 v4l2 设备支持的所有操作,定义如下:

struct v4l2_subdev_ops {
const struct v4l2_subdev_core_ops *core; /* 通用操作合集 */
const struct v4l2_subdev_tuner_ops *tuner; /* 调谐器操作合集 */
const struct v4l2_subdev_audio_ops *audio; /* 音频操作合集 */
const struct v4l2_subdev_video_ops *video; /* 视频操作合集 */
};

v4l2_subdev_core_ops 包含的操作合集是各种类型设备通用的:

struct v4l2_subdev_core_ops {
int (*g_chip_ident)(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip); /* 获取设备id */
int (*log_status)(struct v4l2_subdev *sd); /* 状态消息 */
int (*s_config)(struct v4l2_subdev *sd, int irq, void *platform_data); /* 设置配置信息 */
int (*init)(struct v4l2_subdev *sd, u32 val); /* 初始化设备 */
int (*load_fw)(struct v4l2_subdev *sd); /* 加载firmware */
int (*reset)(struct v4l2_subdev *sd, u32 val); /* 重置设备 */
int (*s_gpio)(struct v4l2_subdev *sd, u32 val); /* 设置gpio */
int (*queryctrl)(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc); /* 查询设备支持的操作 */
int (*g_ctrl)(struct v4l2_subdev *sd, struct v4l2_control *ctrl); /* 获取当前命令值 */
int (*s_ctrl)(struct v4l2_subdev *sd, struct v4l2_control *ctrl); /* 设置当前命令值 */
int (*g_ext_ctrls)(struct v4l2_subdev *sd, struct v4l2_ext_controls *ctrls); /* 获取外置命令值 */
int (*s_ext_ctrls)(struct v4l2_subdev *sd, struct v4l2_ext_controls *ctrls); /* 设置外置命令值 */
int (*try_ext_ctrls)(struct v4l2_subdev *sd, struct v4l2_ext_controls *ctrls);
int (*querymenu)(struct v4l2_subdev *sd, struct v4l2_querymenu *qm); /* 查询操作菜单 */
int (*s_std)(struct v4l2_subdev *sd, v4l2_std_id norm); /* 设置数据标准 */
long (*ioctl)(struct v4l2_subdev *sd, unsigned int cmd, void *arg); /* 处理特殊命令 */
#ifdef CONFIG_VIDEO_ADV_DEBUG
int (*g_register)(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg); /* 获取寄存器值 */
int (*s_register)(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg); /* 设置寄存器值 */
#endif
};

v4l2_subdev_tuner_ops 包含的操作合集则是调谐器独有的:

struct v4l2_subdev_tuner_ops {
int (*s_mode)(struct v4l2_subdev *sd, enum v4l2_tuner_type); /* 设置调谐器模式 */
int (*s_radio)(struct v4l2_subdev *sd); /* 设置无线设备信息 */
int (*s_frequency)(struct v4l2_subdev *sd, struct v4l2_frequency *freq); /* 设置频率 */
int (*g_frequency)(struct v4l2_subdev *sd, struct v4l2_frequency *freq); /* 获取频率 */
int (*g_tuner)(struct v4l2_subdev *sd, struct v4l2_tuner *vt); /* 获取调谐器信息 */
int (*s_tuner)(struct v4l2_subdev *sd, struct v4l2_tuner *vt); /* 设置调谐器信息 */
int (*g_modulator)(struct v4l2_subdev *sd, struct v4l2_modulator *vm); /* 获取调幅器信息 */
int (*s_modulator)(struct v4l2_subdev *sd, struct v4l2_modulator *vm); /* 设置调幅器信息 */
int (*s_type_addr)(struct v4l2_subdev *sd, struct tuner_setup *type); /* 安装调谐器 */
int (*s_config)(struct v4l2_subdev *sd, const struct v4l2_priv_tun_config *config); /* 设置配置信息 */
int (*s_standby)(struct v4l2_subdev *sd); /* 设置标准 */
};

v4l2_subdev_audio_ops 包含的操作合集则是音频部分独有的:

struct v4l2_subdev_audio_ops {
int (*s_clock_freq)(struct v4l2_subdev *sd, u32 freq); /* 设置音频设备频率 */
int (*s_i2s_clock_freq)(struct v4l2_subdev *sd, u32 freq); /* 设置i2s总线频率 */
int (*s_routing)(struct v4l2_subdev *sd, u32 input, u32 output, u32 config); /* 设置音频路由 */
};

v4l2_subdev_video_ops 包含的操作合集则是视频部分独有的:

struct v4l2_subdev_video_ops {
int (*s_routing)(struct v4l2_subdev *sd, u32 input, u32 output, u32 config); /* 设置视频路由 */
int (*s_crystal_freq)(struct v4l2_subdev *sd, u32 freq, u32 flags); /* 设置设备频率 */
int (*decode_vbi_line)(struct v4l2_subdev *sd, struct v4l2_decode_vbi_line *vbi_line); /* 消隐区信息解码 */
int (*s_vbi_data)(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *vbi_data); /* 设置消隐区数据 */
int (*g_vbi_data)(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_data *vbi_data); /* 获取消隐区数据 */
int (*g_sliced_vbi_cap)(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_cap *cap);
int (*s_std_output)(struct v4l2_subdev *sd, v4l2_std_id std); /* 设置标准输出 */
int (*querystd)(struct v4l2_subdev *sd, v4l2_std_id *std); /* 查询标准 */
int (*g_input_status)(struct v4l2_subdev *sd, u32 *status); /* 获取输入状态 */
int (*s_stream)(struct v4l2_subdev *sd, int enable); /* 设置数据流 */
int (*enum_fmt)(struct v4l2_subdev *sd, struct v4l2_fmtdesc *fmtdesc); /* 枚举视频格式 */
int (*g_fmt)(struct v4l2_subdev *sd, struct v4l2_format *fmt); /* 获取视频格式 */
int (*try_fmt)(struct v4l2_subdev *sd, struct v4l2_format *fmt); /* 尝试设置视频格式 */
int (*s_fmt)(struct v4l2_subdev *sd, struct v4l2_format *fmt); /* 设置视频格式 */
int (*cropcap)(struct v4l2_subdev *sd, struct v4l2_cropcap *cc); /* 视频剪辑功能 */
int (*g_crop)(struct v4l2_subdev *sd, struct v4l2_crop *crop); /* 获取剪辑功能 */
int (*s_crop)(struct v4l2_subdev *sd, struct v4l2_crop *crop); /* 设置剪辑功能 */
int (*g_parm)(struct v4l2_subdev *sd, struct v4l2_streamparm *param); /* 获取参数 */
int (*s_parm)(struct v4l2_subdev *sd, struct v4l2_streamparm *param); /* 设置参数 */
int (*enum_framesizes)(struct v4l2_subdev *sd, struct v4l2_frmsizeenum *fsize); /* 枚举帧大小 */
int (*enum_frameintervals)(struct v4l2_subdev *sd, struct v4l2_frmivalenum *fival); /* 枚举帧间隔 */
};

4.1 subdev的注册和注销

当我们把v4l2_subdev需要实现的成员都已经实现,就可以调用以下函数把子设备注册到V4L2核心层:

int v4l2_device_register_subdev(struct v4l2_device*v4l2_dev, struct v4l2_subdev *sd)

当卸载子设备时,可以调用以下函数进行注销:

void v4l2_device_unregister_subdev(struct v4l2_subdev*sd)

5. 应用层具体流程框架:

我们使用一张图来体现吧:

V4L2 driver -整体架构的更多相关文章

  1. OpenRisc-37-OpenRISC的CPU&core的整体架构分析

    引言 前面我们分析了ORPSoC的整体架构,并对其子系统进行了深入的分析和了解.但对于ORPSoC的核心模块or1200_top及其内部的core--or1200_cpu模块却鲜有涉及,算是ORPSo ...

  2. Spark技术内幕:Storage 模块整体架构

    Storage模块负责了Spark计算过程中所有的存储,包括基于Disk的和基于Memory的.用户在实际编程中,面对的是RDD,可以将RDD的数据通过调用org.apache.spark.rdd.R ...

  3. TiDB 整体架构 结合yarn zookeeper分析架构

    TiDB 简介与整体架构| PingCAP https://www.pingcap.com/docs-cn/overview/ 真正金融级高可用 相比于传统主从 (M-S) 复制方案,基于 Raft ...

  4. 万字详解TDengine 2.0整体架构设计思路

    ​导读:涛思数据8月3日将TDengine 的集群功能开源,TDengine具有超强的性能和功能,为什么能做到?它到底有哪些技术创新?今将TDengine的整体设计文档分享出来. 1: 数据模型 物联 ...

  5. 【深入浅出jQuery】源码浅析--整体架构

    最近一直在研读 jQuery 源码,初看源码一头雾水毫无头绪,真正静下心来细看写的真是精妙,让你感叹代码之美. 其结构明晰,高内聚.低耦合,兼具优秀的性能与便利的扩展性,在浏览器的兼容性(功能缺陷.渐 ...

  6. Underscore 整体架构浅析

    前言 终于,楼主的「Underscore 源码解读系列」underscore-analysis 即将进入尾声,关注下 timeline 会发现楼主最近加快了解读速度.十一月,多事之秋,最近好多事情搞的 ...

  7. jQuery 2.0.3 源码分析core - 整体架构

    拜读一个开源框架,最想学到的就是设计的思想和实现的技巧. 废话不多说,jquery这么多年了分析都写烂了,老早以前就拜读过, 不过这几年都是做移动端,一直御用zepto, 最近抽出点时间把jquery ...

  8. [转]Android App整体架构设计的思考

    1. 架构设计的目的 对程序进行架构设计的原因,归根到底是为了提高生产力.通过设计使程序模块化,做到模块内部的高聚合和模块之间的低耦合.这样做的好处是使得程序在开发的过程中,开发人员只需要专注于一点, ...

  9. jQuery整体架构源码解析(转载)

    jQuery整体架构源码解析 最近一直在研读 jQuery 源码,初看源码一头雾水毫无头绪,真正静下心来细看写的真是精妙,让你感叹代码之美. 其结构明晰,高内聚.低耦合,兼具优秀的性能与便利的扩展性, ...

随机推荐

  1. 如何开始DDD(续)

    上一篇针对用户注册案例简单介绍了如何使用 DDD,接下来我将继续针对这个例子做一下补充.先将User模型丰富起来,因为目前看上去他和贫血模型还没有啥大的区别. 首先还是由领域专家来说明业务,他提出了用 ...

  2. rsync实现目录同步

    rsync rsync是linux系统下的数据镜像备份工具.使用快速增量备份工具Remote Sync可以远程同步,支持本地复制,或者与其他SSH.rsync主机同步. 外文名 rsync 全     ...

  3. 使用Java代码自定义Ribbon配置

    很多场景下,需要实现不同的微服务采用不同的策略,例如修改Ribbon的负载均衡规则等.Spring Cloud允许使用Java代码自定义Ribbon的配置. 在Spring Cloud中,Ribbon ...

  4. 扩展方法IEnumerable<T>转换为IList<SelectListItem> ,提供@Html.DropDownList使用

    由于在MVC中经常会使用到@Html.DropDownList方法,而该方法接收的是List<SelectListItem> 参数,因此就想着写一个扩展方法,直接把IEnumerable转 ...

  5. leetcode — letter-combinations-of-a-phone-number

    import java.util.*; /** * Source : https://oj.leetcode.com/problems/letter-combinations-of-a-phone-n ...

  6. 解决 "Script Error" 的另类思路

    本文由小芭乐发表 前端的同学如果用 window.onerror 事件做过监控,应该知道,跨域的脚本会给出 "Script Error." 提示,拿不到具体的错误信息和堆栈信息. ...

  7. Storm 性能优化

    目录 场景假设 调优步骤和方法 Storm 的部分特性 Storm 并行度 Storm 消息机制 Storm UI 解析 性能优化 场景假设 在介绍 Storm 的性能调优方法之前,假设一个场景:项目 ...

  8. 【Spark笔记】Windows10 本地搭建单机版Spark开发环境

    0x00 环境及软件 1.系统环境 OS:Windows10_x64 专业版 2.所需软件或工具 JDK1.8.0_131 spark-2.3.0-bin-hadoop2.7.tgz hadoop-2 ...

  9. 获取多个checkbox的选中值

    我在这个div中添加了多个input. 拼接一下呢.最老的方法. jquery获取值: var strSel=""; $("[name='jbbm']:checked&q ...

  10. HashMap源码解读(JDK1.7)

    哈希表(hash table)也叫散列表,是一种非常重要的数据结构,应用场景及其丰富,许多缓存技术(比如memcached)的核心其实就是在内存中维护一张大的哈希表,而HashMap的实现原理也常常出 ...