当前位置: 代码迷 >> 综合 >> tiny4412 linux-4.2 移植(十四)v4l2 camera(3)v4l2与media framework
  详细解决方案

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

热度:97   发布时间:2024-01-24 12:41:58.0

概述

上两节我们介绍了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到entityfor (i = 0; i < num_pads; i++) {pads[i].entity = entity;	//保存entity到padpads[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_devicevideo_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);}}
}
  相关解决方案