tiny4412 linux-4.2 移植(十四)v4l2 camera(3)v4l2与media framework

概述

上两节我们介绍了v4l2的api使用方法,然后通过api深入框架之中了解其中的原理。这一节我们以tiny4412平台上的fimc和ov7740为例子介绍v4l2与media framework。
本文涉及到的驱动有:fimc-capture.c & fimc_core.c(capture驱动)、media_dev.c(SoC series camera host interface media device driver)和ov7740.c。capture驱动会根据设备树的配置(我设备树只配置了一个fimc)生成fimc.x(x = 0~4),fimc.x是芯片内部的摄像头接口模块,属于cameraif(camera interface),它主要负责获取摄像头的图像。media_dev.c可以理解为桥接器,它会去桥接各个subdev,并且提供一些核心的处理函数。

框架图

tiny4412 linux-4.2 移植(十四)v4l2 camera(3)v4l2与media framework_第1张图片
注:我这里省略了一些中间结构体,比如media_gobj。因为如果要画上这些结构体,以完整的形式表示,那一张框架图会变得杂乱不堪。
struct video_device:视频设备结构体,通过video_register_device注册到系统后会在/dev/下生成videoX节点。在capture驱动中,为videoX节点配置了ops如v4l2_file_operations和v4l2_ioctl_ops。这样应用层就可以open、mmap和ioctl视频设备节点了。
struct v4l2_device:v4l2设备结构体,在fimc的media_dev中创建的结构体。在注册这个设备的同时也注册了异步的subdev_notifier,有了这个subdev_notifier之后,只要subdev通过v4l2_async_register_subdev以异步的方式注册到系统,那么就会匹配到这个v4l2_device然后建立链接关系。
struct v4l2_subdev:v4l2子设备结构体,在这里我有两个子设备,一个是fimc.0,一个是ov7740。这里的ov7740以异步的方式注册,所以会跟上面的v4l2_device建立联系。
struct media_entity:属于media framework的概念,可以类比成电子元件。这个结构体代表一个media实体,它一般嵌入到更高级的一个数据结构中,比如video_device、v4l2_subdev中。
struct medi_pad:属于media framework的概念,可以类比成电子元件上面的引脚。比如我ov7740定义了一个source pad、fimc.0定义了属于video_device的一个sink pad、属于subdev的一个sink pad和src pad。
struct medi_link:用于链接的结构体,在调用media_create_pad_link的时候会生成一个link和一个back link,然后建立指定pad,以及对应的entity之间的链接。
通过对这些结构体的初始化和注册,结合media框架中提供的media_create_pad_link就会产生图中的链接图。media各个结构体的关系是你中有我,我中有你。这种密切的关系为media pipeline的运行时控制提供了基础。

相关函数

建立media_entity与media_pad之间的链接:

int media_entity_pads_init(struct media_entity *entity, u16 num_pads,struct media_pad *pads){
     
	struct media_device *mdev = entity->graph_obj.mdev;
	.....
	entity->num_pads = num_pads;	
	entity->pads = pads;	//保存pad到entity
	for (i = 0; i < num_pads; i++) {
     
		pads[i].entity = entity;	//保存entity到pad
		pads[i].index = i;
		if (mdev)
			media_gobj_create(mdev, MEDIA_GRAPH_PAD,
					&entity->pads[i].graph_obj);
	}
}

建立media_device与media_entity之间的链接:

video_register_device
	__video_register_device
		video_register_media_controller(vdev)
			media_device_register_entity(vdev->v4l2_dev->mdev, &vdev->entity);
				entity->graph_obj.mdev = mdev;//存在中间结构体,所以我框架图的连线是虚线

建立两个entity之间的链接:

int
media_create_pad_link(struct media_entity *source, u16 source_pad,
			 struct media_entity *sink, u16 sink_pad, u32 flags){
     
	struct media_link *link;
	struct media_link *backlink;
	link = media_add_link(&source->links);
		link = kzalloc(sizeof(*link), GFP_KERNEL);
		list_add_tail(&link->list, head);
	link->source = &source->pads[source_pad];
	link->sink = &sink->pads[sink_pad];
	link->flags = flags & ~MEDIA_LNK_FL_INTERFACE_LINK;

	backlink = media_add_link(&sink->links);
	backlink->source = &source->pads[source_pad];
	backlink->sink = &sink->pads[sink_pad];
	backlink->flags = flags;
	backlink->is_backlink = true;

	link->reverse = backlink;
	backlink->reverse = link;
}

找到远程的pad:
假如我这个pad是一个sink,那么我会通过link去找到它的远程source;假如我这个pad是一个source,那么我会通过link去找到它的远程sink。

struct media_pad *media_entity_remote_pad(const struct media_pad *pad){
     
	struct media_link *link;
	list_for_each_entry(link, &pad->entity->links, list) {
     
		if (!(link->flags & MEDIA_LNK_FL_ENABLED))//必须是已经使能的链接
			continue;
		if (link->source == pad)	
			return link->sink;
		if (link->sink == pad)
			return link->source;
	}
	return NULL;
}

启动一个pipeline。在启动流传输的时候需要调用media_pipeline_start去告知每一个entity,并设置对应的pad状态,然后调用entity->ops->link_validate(link)去验证每一个entity是否准备好了。这里传入的media_pipeline充当一个管理者角色,保存一些数据,保障后面的stop操作。

__must_check int media_pipeline_start(struct media_entity *entity,
				      struct media_pipeline *pipe){
     
	ret = __media_pipeline_start(entity, pipe);
	while ((entity = media_graph_walk_next(graph))) {
     
		entity->pipe = pipe;
		....
		list_for_each_entry(link, &entity->links, list) {
     
			struct media_pad *pad = link->sink->entity == entity
						? link->sink : link->source;
			....
			ret = entity->ops->link_validate(link);
		}
	}
}

你可能感兴趣的:(Linux,V4l2)