linux v4l2架构分析之media_graph_walk_next

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值