一 简介
1 V4L2
定义:V4L2(Video ForLinux Two) 是内核提供给应用程序访问音、视频驱动的统一接口。
核心:v4L2的核心源码位于drivers/media/v4l2-core
应用:V4L2是linux操作系统下用于采集图片、视频和音频数据的API接口,配合适当的视频采集设备和相应的驱动程序,可以实现图片、视频、音频等的采集。在远程会议、可视电话、视频监控系统和嵌入式多媒体终端中都有广泛的应用。
基础结构:
可见V4L2是个树形结构,通过链表来管理注册其下的设备。里面包含很多的子系统。我们今天要讲的就是其中一个子系统:camera
V4L2支持两种 IO 访问方式:
- read/write:帧IO访问方式,每一帧都要通过IO操作。数据需要在内核和用户之间拷贝,访问速度可能会非常慢。常用于静态图片数据的采集
- mmap内存映射:流IO访问方式,不需要内存拷贝,访问速度比较快。一般用于连续视频数据的采集
参考:
- https://www.cnblogs.com/vedic/p/10763838.html
- https://blog.csdn.net/smartvincent88/article/details/18987207
2 camera
也就是 soc camera 子系统。是V4L2的一个子系统,用来写摄像头驱动
意义:
- 为soc camera device 和 soc camera host定义了标准的接口或者说是回调函数,在soc_camera.c中进行管理
- 将camera sensor 和camera host的实现独立起来,减少两者之间的依赖关系,提高camera sensor驱动在不同平台之间的可移植性。
名词解释:
- device:设备
- host:cpu内部的模块控制器
- sensor:嵌入进camera内部的传感器,一般是i2c接口
- i2c:也就是i2c总线,用来绑定设备和驱动
二 层次结构
1 用户程序方面
用户程序通过V4L2接口采集视频数据的步骤
- 开视频设备文件,进行视频采集的参数初始化,通过V4L2接口设置视频图像的采集窗口、采集的点阵大小和格式
- 申请若干视频采集的帧缓冲区,并将这些帧缓冲区从内核空间映射到用户空间,便于应用程序读取/处理视频数据
- 将申请到的帧缓冲区在视频采集输入队列排队,并启动视频采集
- 驱动开始视频数据的采集,应用程序从视频采集输出队列取出帧缓冲区,处理完后,将帧缓冲区重新放入视频采集输入队列,循环往复采集连续的视频数据
- 停止视频采集
2 整体结构
整体结构图:
图片分析:
首先用户程序通过open、ioctl指令或者mmap内存映射访问到V4L2系统。就拿open举例。比如我们要打开一个摄像头设备:/dev/video0。它是个字符设备,所以是通过cdev的ops访问到v4l2_open函数,然后v4l2_open函数中会调用vdev->fops->open。对于soc camera来说也就是调用soc_camera_open函数。随之就进入soc camera sub-system,也就是camera子系统。它对应的就是soc_camera.c 和 soc_camera_platform.c两个文件。
soc camera host:camera模块控制器,由平台厂商实现。向上实现soc_camera_host_ops接口,向下操作Camera host硬件以及通过平台特定的接口操作Soc camera device。
soc camera device :平台的设备,系统会为设备创建设备节点/dev/videoX。比如,将笔记本电脑的摄像头连接到虚拟机之后就会出现这样两个设备:
v4l2_subdev:可以是camera内部的sensor、video AD芯片,每个soc_camera_device可以管理多个v4l2_subdev。v4l2_subdev可以通过i2c挂接到v4l2_device,也可以通过soc_camera_link提供的add_device来增加
核心源码位置:
- soc_camera.c 和 soc_camera_platform.c:在 drivers/media/platform/soc_camera/ 文件夹下(不同内核版本可能位置不同,也可能在drivers/media/video/下)
- soc_camera.h 和 soc_camera_platform.h:在include/media/下
参考:
- https://blog.csdn.net/kickxxx/article/details/8484498
三 具体驱动实现方法
0 soc_camera_link结构体
/* Prepare to replace this struct: don't change its layout any more! */
struct soc_camera_link {
/*
* Subdevice part - keep at top and compatible to
* struct soc_camera_subdev_desc
*/
//...
/*
* Host part - keep at bottom and compatible to
* struct soc_camera_host_desc
*/
//...
};
在系统实现中会被拆分为以下结构体,注释中也表明了兼容以下结构体:
/*
* Platform data for "soc-camera-pdrv"
* This MUST be kept binary-identical to struct soc_camera_link below, until
* it is completely replaced by this one, after which we can split it into its
* two components.
*/
struct soc_camera_desc {
struct soc_camera_subdev_desc subdev_desc;
struct soc_camera_host_desc host_desc;
};
这两个结构体的结构在soc_camera_link当中都有。在平台设备注册时会用到
1 soc_camera_device
系统中是如何注册camera_device。
首先看 soc_camera.c 文件中的module_platform_driver函数:
static struct platform_driver __refdata soc_camera_pdrv = {
.probe = soc_camera_pdrv_probe,
//...
};
module_platform_driver(soc_camera_pdrv)
module_platform_driver的参数是soc_camera_pdrv,是一个platform_driver结构体,其中最重要的就是probe,也就是soc_camera_pdrv_probe。probe字面意思就是探测的,就是循环来探测驱动的。soc_camera_pdrv_probe会probe系统内名称为"soc-camera-pdrv"的平台设备,系统内有几个这样的平台设备,那么就会创建几个soc_camera_device。
以ov7725传感器为例:
struct i2c_board_info jz_v4l2_camera_