media_graph_walk_next函数用于深度优先遍历媒体设备节点之间的关系拓扑图,该函数只会遍历被使能的媒体节点,如果从最顶端的媒体节点开始遍历,能够查找到最底层的产生原始数据的sensor的媒体节点。
相关数据结构:
/**
* struct media_graph - Media graph traversal state
*
* @stack: Graph traversal stack; the stack contains information
* on the path the media entities to be walked and the
* links through which they were reached.
* @stack.entity: pointer to &struct media_entity at the graph.
* @stack.link: pointer to &struct list_head.
* @ent_enum: Visited entities
* @top: The top of the stack
*/
struct media_graph {
/*
正在被访问的节点会被存在堆栈中,在访问完后会被弹出
*/
struct {
struct media_entity *entity;
struct list_head *link;//节点的链接,可以找到entity链接的另一端的media_entity
} stack[MEDIA_ENTITY_ENUM_MAX_DEPTH];
struct media_entity_enum ent_enum;
int top;
};
/**
* struct media_entity_enum - An enumeration of media entities.
*
* @bmap: Bit map in which each bit represents one entity at struct
* media_entity->internal_idx.
* @idx_max: Number of bits in bmap
*/
struct media_entity_enum {
//每个media_entity唯一对应ent_enum->bmap中一个位,置一表示该位的media_entity被遍历过,所以这里都清0
unsigned long *bmap;
int idx_max;
};
出入堆栈操作:
/* push an entity to traversal stack */
static void stack_push(struct media_graph *graph,
struct media_entity *entity)
{
if (graph->top == MEDIA_ENTITY_ENUM_MAX_DEPTH - 1) {
WARN_ON(1);
return;
}
graph->top++;
graph->stack[graph->top].link = entity->links.next;
graph->stack[graph->top].entity = entity;
}
static struct media_entity *stack_pop(struct media_graph *graph)
{
struct media_entity *entity;
entity = graph->stack[graph->top].entity;
graph->top--;
return entity;
}
开始遍历前的初始化会依次调用media_graph_walk_init、media_graph_walk_start,两个函数的分析如下:
int media_graph_walk_init(
struct media_graph *graph, struct media_device *mdev)
{
return media_entity_enum_init(&graph->ent_enum, mdev);
}
static inline __must_check int media_entity_enum_init(
struct media_entity_enum *ent_enum, struct media_device *mdev)
{
/*
mdev->entity_internal_idx_max是媒体设备拥有的entity节点总数,
加一为了对应后面初始化时在栈顶增加了一个NULL的entity
*/
return __media_entity_enum_init(ent_enum,
mdev->entity_internal_idx_max + 1);
}
int __media_entity_enum_init(struct media_entity_enum *ent_enum,
int idx_max)
{
//BITS_PER_LONG为long的位数,按BITS_PER_LONG对齐
idx_max = ALIGN(idx_max, BITS_PER_LONG);
ent_enum->bmap = kcalloc(idx_max / BITS_PER_LONG, sizeof(long),
GFP_KERNEL);
if (!ent_enum->bmap)
return -ENOMEM;
bitmap_zero(ent_enum->bmap, idx_max);
ent_enum->idx_max = idx_max;
return 0;
}
void media_graph_walk_start(struct media_graph *graph,
struct media_entity *entity)
{
media_entity_enum_zero(&graph->ent_enum);
media_entity_enum_set(&graph->ent_enum, entity);
graph->top = 0;
graph->stack[graph->top].entity = NULL;//栈底,用来判断堆栈已经到底
stack_push(graph, entity);
dev_dbg(entity->graph_obj.mdev->dev,
"begin graph walk at '%s'\n", entity->name);
}
media_graph_walk_next函数用深度优先遍历的方式访问下一个节点:
//返回栈顶数据
#define link_top(en) ((en)->stack[(en)->top].link)
#define stack_top(en) ((en)->stack[(en)->top].entity)
/*
通过link返回entity的另一端的media_entity
*/
static struct media_entity *
media_entity_other(struct media_entity *entity, struct media_link *link)
{
if (link->source->entity == entity)
return link->sink->entity;
else
return link->source->entity;
}
static void media_graph_walk_iter(struct media_graph *graph)
{
struct media_entity *entity = stack_top(graph);
struct media_link *link;
struct media_entity *next;
/*
结合上面的stack_pop函数,可知media_entity.links挂载着属于本media_entity的media_link.list.
包括前向链接和后向链接
*/
link = list_entry(link_top(graph), typeof(*link), list);
/* The link is not enabled so we do not follow.
没有被使能的链接不会被遍历,直接跳过
*/
if (!(link->flags & MEDIA_LNK_FL_ENABLED)) {
link_top(graph) = link_top(graph)->next;//更新link top为entity的下个link
dev_dbg(entity->graph_obj.mdev->dev,
"walk: skipping disabled link '%s':%u -> '%s':%u\n",
link->source->entity->name, link->source->index,
link->sink->entity->name, link->sink->index);
return;
}
/* Get the entity in the other end of the link . */
next = media_entity_other(entity, link);//与entity链接的另一端的entity
/* Has the entity already been visited?
设置并判断graph->ent_enum的第entity->internal_idx位是否置位
*/
if (media_entity_enum_test_and_set(&graph->ent_enum, next)) {
link_top(graph) = link_top(graph)->next;//更新link top为entity的下个link
dev_dbg(entity->graph_obj.mdev->dev,
"walk: skipping entity '%s' (already seen)\n",
next->name);
return;
}
/* Push the new entity to stack and start over.
换entity的下个链接遍历,需要在下面的next入栈出栈后(即next遍历完成后)才开始这个entity遍历
*/
link_top(graph) = link_top(graph)->next;
//前面media_entity_enum_test_and_set已经置位,下个链接入栈。可以看出这是深度优先遍历
stack_push(graph, next);
dev_dbg(entity->graph_obj.mdev->dev, "walk: pushing '%s' on stack\n",
next->name);
}
struct media_entity *media_graph_walk_next(struct media_graph *graph)
{
struct media_entity *entity;
if (stack_top(graph) == NULL)
return NULL;
/*
* Depth first search. Push entity to stack and continue from
* top of the stack until no more entities on the level can be
* found.
stack_top(graph)->links为entity->links的链表头,link_top(graph) == &stack_top(graph)->links表示链表遍历完成
所以while循环是为了找到一个与最初栈顶的entity链接(直接或间接链接)且其链接都被遍历过的entity,
该entity为while结束后栈顶的entity
*/
while (link_top(graph) != &stack_top(graph)->links)
media_graph_walk_iter(graph);
entity = stack_pop(graph);//出栈
dev_dbg(entity->graph_obj.mdev->dev,
"walk: returning entity '%s'\n", entity->name);
return entity;
}