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

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

 参考---->https://blog.csdn.net/qq_27136111/article/details/103631493

概述
上两节我们介绍了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,并且提供一些核心的处理函数。

框架图

 

 

注:我这里省略了一些中间结构体,比如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);
}
}
}

posted @ 2021-12-09 15:43  DMCF  阅读(819)  评论(0编辑  收藏  举报