全志T7 vin core驱动

vin core驱动入口

vin core驱动由vin驱动调用被注册到系统中:(vin.c vin_init())
在这里插入图片描述
这里调用vin_core.c:
在这里插入图片描述

Probe方法

根据DST配置信息,系统共有8个vinc设备,分别是vinc0vinc7,DST中使能了vinc0vinc5。驱动和设备匹配后将运行probe方法:

static int vin_core_probe(struct platform_device *pdev)
{
	//1、从DST中获取信息填充vinc结构体
	//2、分配DMA内存
	//3、中断请求
	//4、初始化capture这个v4l2子设备
	ret = vin_initialize_capture_subdev(vinc);
}

在这里插入图片描述
参考《nvp6134驱动》的解释,这个函数首先调用了v4l2_subdev_init()初始化v4l2_subdev结构体变量sd,然后调用median_entity_init()初始化了一个名字叫"vin_cap.0"之类的entity,最后将vinc结构体设置为subdev的私有数据。与nvp6134驱动不同的是,这里注册的entity是具有sink pad和source pad的,即它前面有entity,后面也有entity,而nvp6134的只有source pad,即它前面没有entity。
另外,这个函数最后设置了两个ops。其中“vin_capture_sd_internal_ops”很重要。下文会详解。

这里有两个疑问:

  1. 这个“vin_cap.0”子设备如何挂载到“sunxi-vin”的v4l2总设备上?
  2. 这个entity的下一个entity是什么?

回答问题1:
上面vin_initialize_capture_subdev()函数中初始化的subdev实际是vinc–>vid_cap.subdev。
在vin.c vin_probe()–>vin_md_register_entities()方法调用sunxi_vin_core_get_dev()获得vinc并保存到vind–>vinc[]数组中,而sunxi_vin_core_get_dev()实则返回全局数组变量vin_core_gbl[]中指定的成员(vin_core_gbl[]在probe被初始化):
在这里插入图片描述
在前面调用vin_core_probe()时会把每个vinc的信息保存到全局变量vin_core_gb1,此时sunxi_vin_core_get_dev()将逐个获取vin_core,然后调用vin_md_register_core_entity()注册到V4l2_device:
在这里插入图片描述
注:vin_pipe_ops是结构体vin_pipeline_ops变量,很重要,后续分析。
通过vin_md_register_core_entity()–>v4l2_device_register_subdev()将“vin_cap.0”子设备注册到“sunxi-vin”。
这样构成一下的层次关系:
在这里插入图片描述
回答问题2:
下一个entity就是一个video device,看接下来的分析。

初始化video device

在本文上面提到的vin_core_probe()–>vin_initialize_capture_subdev()函数最后有:
在这里插入图片描述
在这里插入图片描述
vin_capture_sd_internal_ops是v4l2_subdev_internal_ops结构体变量,根据v4l2_subdev_internal_ops结构体的注释,当subdev被注册时registered函数被调用,被调用时vin_capture_subdev_registered()的参数将被设置为v4l2_subdev对象的指针,对于vin驱动来说,就是上面注册的子设备vinc–>vid_cap.subdev。
注册时会回调registered 函数,即vin_capture_subdev_registered()。
在这里插入图片描述
涉及v4l2_ctrl,暂时不分析。vin_init_video()是个重要函数,其创建/dev/video%d设备给应用程序使用,即应用程序可以通过她open\ioctl\close,获得Camera视频数据。在《T7 v4l2 videobuf 2.docx》中有简单介绍这个函数如何对videobuf和vb2_queue初始化。而应用程序如何通过Ioctl调到vin_ioctl_ops[]的过程可参考《V4L2框架-control.docx》。
vin_init_video()前面代码如下:
在这里插入图片描述

1.介绍一下video_device结构体

video_device 可以动态的分配:

struct video_device *vdev = video_device_alloc();
if (vdev == NULL)
    return -ENOMEM;
vdev->release = video_device_release;

如果需要将 video_device 结构体嵌入到更大的结构体里面的话,就需要设置 vdev 的 release 成员。内核提供了两个默认的 release回调函数,如下:

video_device_release()       // 仅仅调用kfree释放分配的内存,用于动态分配情况下
video_device_release_empty() // 不做任何事情,静态变量

以下的函数成员需要被设置:

  • v4l2_dev:必须指向v4l2_device父设备
  • vfl_dir:VFL_DIR_RX(capture设备)、VFL_DIR_TX(输出设备)、VFL_DIR_M2M(codec设备)
  • fops:设置v4l2_file_operations结构体
  • ioctl_ops:ioctls,可以通过设备节点被用户空间程序访问,需设置fops的.unlocked_ioctl指向video_ioctl2
  • lock:如果想要在驱动空间里做锁操作,可以设置为NULL。否则需要指向一个已经初始化的mutex_lock结构体
  • queue:指向一个vb2_queue结构体,如果queue->lock不为空,那么与队列相关的ioctls就会使用queue内部的锁,这样的话就不用等待其它类型的ioctls操作
  • prio:对优先级进行跟踪,用在VIDIOC_G/S_PRIORITY上,如果为空的话就会使用v4l2_device里面的v4l2_prio_state
  • dev_parent:指向v4l2_device即可

2.video_device 的注册

video_device 的注册函数如下:

    ret = video_register_device(&cap->vdev, VFL_TYPE_GRABBER, cap->vinc->id);

该代码会注册一个字符设备驱动程序并在用户空间生成一个设备节点。如果 v4l2_device 父设备的 mdev 成员不为空的话,video_device 的 entity 会被自动的注册到 media framework 里面。函数最后一个参数是设备节点索引号,如果是 -1 的话就取用第一个内核中可用的索引号值。注册的设备类型以及用户空间中的节点名称取决于以下标识:

    VFL_TYPE_GRABBER: videoX 输入输出设备
    VFL_TYPE_VBI: vbiX 
    VFL_TYPE_RADIO: radioX 硬件定义的音频调谐设备
    VFL_TYPE_SDR: swradioX 软件定义的音频调谐设备

所以,对于vin core驱动来说,生成了类似/dev/videoXX的节点,其中XX是第三个参数的值。
当一个设备节点被创建时,相关属性也会被创建,可以在 /sys/class/video4linux 里面看到这些设备文件夹,在文件夹里面可以看到 ‘name’,‘debug’,'index’等属性,可以使用 cat 命令查看。’ debug’ 可以用于 video 设备调试,每个 video 设备都会创建一个 ‘debug’ 属性,该属性以文件夹的形式存在与 /sys/class/video4linux// 下面以供使能 log file operation。’ debug’是一个位掩码,以下位可以被设置:

    0x01:记录ioctl名字与错误码。设置0x08位可以只记录VIDIOC_(D)QBUF
    0x02:记录ioctl的参数与错误码。设置0x08位可以只记录VIDIOC_(D)QBUF
    0x04:记录file ops操作。设置0x08位可以只记录read&write成员的操作
    0x08:如上所示
    0x10:记录poll操作

当以上的位被设置的时候,发生相关的调用或者操作的时候内核就会打印出来相关的调用信息到终端上面。

3.video设备的清理

当 video 设备节点需要被移除或者USB设备断开时,需要执行以下函数:

    video_unregister_device(vdev);

来进行设备的卸载,该函数会移除 /dev 下的设备节点文件,同时不要忘记调用 media_entity_cleanup 来清理 entity。

4.其他

如果要集成到 media_framework 里面,就需要设置 video_device 里面的 media_entity 成员,同时需要提供 media_pad,例如vin_init_video()下部分有:
在这里插入图片描述
cap->vd_pad只有MEDIA_PAD_FL_SINK,没有MEDIA_PAD_FL_SOURCE,即它只接受收据,所以它将是vin驱动中整个media link的终点——最后一个Media Entity。
但是,从打印信息来看,8个vinc设备却只有4次调用vin_capture_subdev_registered(),为什么?
在这里插入图片描述
在这里插入图片描述
在调用vin_md_register_core_entity()有个判断(15行到18行代码),即但valid_idx无效时直接跳过,根据代码或下面打印信息提示:
在这里插入图片描述
只有sensor 0是有效的,所以在DST中,这是rear_sensor为<0x0>的才能调用vin_md_register_core_entity(),也就是这样vinc0~vinc3能通过,这难道对应4路摄像头?。(答案是肯定的)

vin_init_video()其他代码是关于video buffer的,这里不分析,可以参考《V4L2框架-videobuf2》。

vin驱动的Ioctl入口

这一段解释为什么应用程序的IOCTL调用会转到vin_ioctl_ops结构体所指定的函数。
下面摘自《V4L2驱动框架简单分析.docx》,先简单说明ioctl框架:

=====================》
你可能观察到用户空间对V4L2设备的操作基本都是ioctl来实现的,V4L2设备都有大量可操作的功能(配置寄存器),所以V4L2的ioctl也是十分庞大的。它是一个怎样的框架,是怎么实现的呢?
Ioctl框架是由v4l2_ioctl.c文件实现,文件中定义结构体数组v4l2_ioctls,可以看做是ioctl指令和回调函数的关系表。用户空间调用系统调用ioctl,传递下来ioctl指令,然后通过查找此关系表找到对应回调函数。
以下是截取数组的两项:

IOCTL_INFO_FNC(VIDIOC_QUERYBUF, v4l_querybuf,v4l_print_buffer, INFO_FL_QUEUE | INFO_FL_CLEAR(v4l2_buffer, length)),
IOCTL_INFO_STD(VIDIOC_G_FBUF, vidioc_g_fbuf,v4l_print_framebuffer, 0),

内核提供两个宏(IOCTL_INFO_FNC和IOCTL_INFO_STD)来初始化结构体,参数依次是ioctl指令、回调函数或者v4l2_ioctl_ops结构体成员、debug函数、flag。如果回调函数是v4l2_ioctl_ops结构体成员,则使用IOCTL_INFO_STD;如果回调函数是v4l2_ioctl.c自己实现的,则使用IOCTL_INFO_FNC。
IOCTL调用的流程图如下:
在这里插入图片描述
用户空间通过打开/dev/目录下的设备节点,获取到文件的file结构体,通过系统调用ioctl把cmd和arg传入到内核。通过一系列的调用后最终会调用到__video_do_ioctl函数,然后通过cmd检索v4l2_ioctls[],判断是INFO_FL_STD还是INFO_FL_FUNC。如果是INFO_FL_STD会直接调用到视频设备驱动中video_device->v4l2_ioctl_ops函数集。如果是INFO_FL_FUNC会先调用到v4l2自己实现的标准回调函数,然后根据arg再调用到video_device->v4l2_ioctl_ops或v4l2_fh->v4l2_ctrl_handler函数集。

《===============================
部分IOCTL最终会调用到vin_ioctl_ops里面的函数:
在这里插入图片描述

ioctls 与 locking

V4L 核心层提供了可选的锁服务,最主要的就是 video_device 里面的锁,用来进行 ioctls 的同步。如果使用了 videobuf2 框架,那么 video_device->queue->lock 锁也会被用来做 queue 相关的 ioctls 同步。使用不同的锁有很多优点,比如一些设置相关的 ioctls 花费的时间比较长,如果使用独立的锁,VIDIOC_DQBUF就不用等待设置操作的完成就可以执行,这个在网络摄像机驱动中很常见。当然,也可以完全由驱动本身去完成锁操作,这时可以设置所有的锁成员为NULL并实现一个驱动自己的锁。
vin core驱动里面,cap->vdev.lock和q->lock的锁使用同一个的,所以在启动流传输后,应用程序尽量不要再操作ioctls。
如果使用旧的 videobuf,需要将 video_device 的锁传递给 videobuf queue 初始化函数,如果 videobuf 正在等待一帧数据的到达,此时会将锁暂时释放,等数据到达之后再次加锁,否则别的处理程序就无法访问。所以不推荐使用旧的 videobuf。如果是在 videobuf2 框架下,需要实现 wait_prepare 与 wait_finish 回调函数去释放或者获取锁,如果使用了 queue->lock,可以使用 V4L2 提供的回调 vb2_ops_wait_prepare/finish 帮助函数来完成加锁与解锁的操作,它们会使用 queue->lock这个锁(此时一定要将该锁初始化)。

vin core驱动在vin.c中的相关代码

最后再提一提vin_pipeline_ops结构体:
在这里插入图片描述
应用程序调用VIDIOC_S_INPUT时,最终调用到vin_video.c的vidioc_s_input()–>__vin_pipeline_open(&cap->pipe, &cap->vdev.entity, true)。
在这里插入图片描述

static int __vin_pipeline_open(struct vin_pipeline *p,
				struct media_entity *me, bool prepare)
{
	struct vin_md *vind;
	struct v4l2_subdev *sd;
	struct sensor_item sensor;
	int ret;

	if (prepare)
		vin_md_prepare_pipeline(p, me);

	sd = p->sd[VIN_IND_SENSOR];
	if (sd == NULL)
		return -EINVAL;

	ret = vin_pipeline_s_power(p, 1);
	if (!ret)
		return 0;
	return ret;
}

首先调用vin_md_prepare_pipeline(),如果应用程序操作的是/dev/video0节点,则打印信息如下:

[VIN_LOG_MD]vin_md_prepare_pipeline entity is vin_cap.0, group id is 0x2000
[VIN_LOG_MD]vin_md_prepare_pipeline entity is sunxi_scaler.0, group id is 0x1000
[VIN_LOG_MD]vin_md_prepare_pipeline entity is sunxi_isp.0, group id is 0x800
[VIN_LOG_MD]vin_md_prepare_pipeline entity is sunxi_csi.2, group id is 0x400
[VIN_LOG_MD]vin_md_prepare_pipeline entity is nvp6134, group id is 0x100

运行目的就是将pipe涉及到的Sensor、CSI、ISP、Scaler、vin_cap这些subdev保存到cap->pipe->sd[],方便管理。
函数接下来调用vin_pipeline_s_power()启动各个子模块的电源,分别调用了子模块中v4l2_subdev_core_ops结构体定义的s_power所指函数,看了代码,除nvp6134的sensor_power()有比较重要的设置,其他模块都很简单。

__vin_pipeline_s_stream()参考《nvp6134驱动》

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值