1. 程式人生 > 其它 >tiny4412 linux-4.2 移植(十四)v4l2 camera(3)v4l2與media framework

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);
}
}
}