staticinlineconstchar *intf_type(struct media_interface *intf)
{ switch (intf->type) { case MEDIA_INTF_T_DVB_FE: return"dvb-frontend"; case MEDIA_INTF_T_DVB_DEMUX: return"dvb-demux"; case MEDIA_INTF_T_DVB_DVR: return"dvb-dvr"; case MEDIA_INTF_T_DVB_CA: return"dvb-ca"; case MEDIA_INTF_T_DVB_NET: return"dvb-net"; case MEDIA_INTF_T_V4L_VIDEO: return"v4l-video"; case MEDIA_INTF_T_V4L_VBI: return"v4l-vbi"; case MEDIA_INTF_T_V4L_RADIO: return"v4l-radio"; case MEDIA_INTF_T_V4L_SUBDEV: return"v4l-subdev"; case MEDIA_INTF_T_V4L_SWRADIO: return"v4l-swradio"; case MEDIA_INTF_T_V4L_TOUCH: return"v4l-touch"; default: return"unknown-intf";
}
};
staticinlineconstchar *link_type_name(struct media_link *link)
{ switch (link->flags & MEDIA_LNK_FL_LINK_TYPE) { case MEDIA_LNK_FL_DATA_LINK: return"data"; case MEDIA_LNK_FL_INTERFACE_LINK: return"interface"; case MEDIA_LNK_FL_ANCILLARY_LINK: return"ancillary"; default: return"unknown";
}
}
__must_check int media_entity_enum_init(struct media_entity_enum *ent_enum, struct media_device *mdev)
{ int idx_max;
/** * dev_dbg_obj - Prints in debug mode a change on some object * * @event_name: Name of the event to report. Could be __func__ * @gobj: Pointer to the object * * Enabled only if DEBUG or CONFIG_DYNAMIC_DEBUG. Otherwise, it * won't produce any code.
*/ staticvoid dev_dbg_obj(constchar *event_name, struct media_gobj *gobj)
{ #ifdefined(DEBUG) || defined (CONFIG_DYNAMIC_DEBUG) switch (media_type(gobj)) { case MEDIA_GRAPH_ENTITY:
dev_dbg(gobj->mdev->dev, "%s id %u: entity '%s'\n",
event_name, media_id(gobj),
gobj_to_entity(gobj)->name); break; case MEDIA_GRAPH_LINK:
{ struct media_link *link = gobj_to_link(gobj);
dev_dbg(gobj->mdev->dev, "%s id %u: %s link id %u ==> id %u\n",
event_name, media_id(gobj), link_type_name(link),
media_id(link->gobj0),
media_id(link->gobj1)); break;
} case MEDIA_GRAPH_PAD:
{ struct media_pad *pad = gobj_to_pad(gobj);
/** * media_entity_has_pad_interdep - Check interdependency between two pads * * @entity: The entity * @pad0: The first pad index * @pad1: The second pad index * * This function checks the interdependency inside the entity between @pad0 * and @pad1. If two pads are interdependent they are part of the same pipeline * and enabling one of the pads means that the other pad will become "locked" * and doesn't allow configuration changes. * * This function uses the &media_entity_operations.has_pad_interdep() operation * to check the dependency inside the entity between @pad0 and @pad1. If the * has_pad_interdep operation is not implemented, all pads of the entity are * considered to be interdependent. * * One of @pad0 and @pad1 must be a sink pad and the other one a source pad. * The function returns false if both pads are sinks or sources. * * The caller must hold entity->graph_obj.mdev->mutex. * * Return: true if the pads are connected internally and false otherwise.
*/ staticbool media_entity_has_pad_interdep(struct media_entity *entity, unsignedint pad0, unsignedint pad1)
{ if (pad0 >= entity->num_pads || pad1 >= entity->num_pads) returnfalse;
if (entity->pads[pad0].flags & entity->pads[pad1].flags &
(MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_SOURCE)) returnfalse;
if (!entity->ops || !entity->ops->has_pad_interdep) returntrue;
/** * media_graph_walk_init - Allocate resources for graph walk * @graph: Media graph structure that will be used to walk the graph * @mdev: Media device * * Reserve resources for graph walk in media device's current * state. The memory must be released using * media_graph_walk_cleanup(). * * Returns error on failure, zero on success.
*/
__must_check int media_graph_walk_init( struct media_graph *graph, struct media_device *mdev)
{ return media_entity_enum_init(&graph->ent_enum, mdev);
}
EXPORT_SYMBOL_GPL(media_graph_walk_init);
/** * media_graph_walk_cleanup - Release resources related to graph walking * @graph: Media graph structure that was used to walk the graph
*/ void media_graph_walk_cleanup(struct media_graph *graph)
{
media_entity_enum_cleanup(&graph->ent_enum);
}
EXPORT_SYMBOL_GPL(media_graph_walk_cleanup);
link = list_entry(link_top(graph), typeof(*link), list);
/* If the link is not a data link, don't follow it */ if ((link->flags & MEDIA_LNK_FL_LINK_TYPE) != MEDIA_LNK_FL_DATA_LINK) {
link_top(graph) = link_top(graph)->next; return;
}
/* The link is not enabled so we do not follow. */ if (!(link->flags & MEDIA_LNK_FL_ENABLED)) {
link_top(graph) = link_top(graph)->next;
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 at the other end of the link. */
next = media_entity_other(entity, link);
/* Has the entity already been visited? */ if (media_entity_enum_test_and_set(&graph->ent_enum, next)) {
link_top(graph) = link_top(graph)->next;
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. */
link_top(graph) = link_top(graph)->next;
stack_push(graph, next);
dev_dbg(entity->graph_obj.mdev->dev, "walk: pushing '%s' on stack\n",
next->name);
lockdep_assert_held(&entity->graph_obj.mdev->graph_mutex);
}
/* * Depth first search. Push entity to stack and continue from * top of the stack until no more entities on the level can be * found.
*/ while (link_top(graph) != &stack_top(graph)->links)
media_graph_walk_iter(graph);
/* * The pipeline traversal stack stores pads that are reached during graph * traversal, with a list of links to be visited to continue the traversal. * When a new pad is reached, an entry is pushed on the top of the stack and * points to the incoming pad and the first link of the entity. * * To find further pads in the pipeline, the traversal algorithm follows * internal pad dependencies in the entity, and then links in the graph. It * does so by iterating over all links of the entity, and following enabled * links that originate from a pad that is internally connected to the incoming * pad, as reported by the media_entity_has_pad_interdep() function.
*/
/** * struct media_pipeline_walk_entry - Entry in the pipeline traversal stack * * @pad: The media pad being visited * @links: Links left to be visited
*/ struct media_pipeline_walk_entry { struct media_pad *pad; struct list_head *links;
};
/** * struct media_pipeline_walk - State used by the media pipeline traversal * algorithm * * @mdev: The media device * @stack: Depth-first search stack * @stack.size: Number of allocated entries in @stack.entries * @stack.top: Index of the top stack entry (-1 if the stack is empty) * @stack.entries: Stack entries
*/ struct media_pipeline_walk { struct media_device *mdev;
/* Push a new entry on the stack. */ staticint media_pipeline_walk_push(struct media_pipeline_walk *walk, struct media_pad *pad)
{ struct media_pipeline_walk_entry *entry; int ret;
if (walk->stack.top + 1 >= walk->stack.size) {
ret = media_pipeline_walk_resize(walk); if (ret) return ret;
}
/* * Move the top entry link cursor to the next link. If all links of the entry * have been visited, pop the entry itself. Return true if the entry has been * popped.
*/ staticbool media_pipeline_walk_pop(struct media_pipeline_walk *walk)
{ struct media_pipeline_walk_entry *entry;
if (WARN_ON(walk->stack.top < 0)) returnfalse;
entry = media_pipeline_walk_top(walk);
if (entry->links->next == &entry->pad->entity->links) {
dev_dbg(walk->mdev->dev, "media pipeline: entry %u has no more links, popping\n",
walk->stack.top);
walk->stack.top--; returntrue;
}
entry->links = entry->links->next;
dev_dbg(walk->mdev->dev, "media pipeline: moved entry %u to next link\n",
walk->stack.top);
returnfalse;
}
/* Free all memory allocated while walking the pipeline. */ staticvoid media_pipeline_walk_destroy(struct media_pipeline_walk *walk)
{
kfree(walk->stack.entries);
}
/* Add a pad to the pipeline and push it to the stack. */ staticint media_pipeline_add_pad(struct media_pipeline *pipe, struct media_pipeline_walk *walk, struct media_pad *pad)
{ struct media_pipeline_pad *ppad;
ppad = kzalloc(sizeof(*ppad), GFP_KERNEL); if (!ppad) return -ENOMEM;
ppad->pipe = pipe;
ppad->pad = pad;
list_add_tail(&ppad->list, &pipe->pads);
dev_dbg(pad->graph_obj.mdev->dev, "media pipeline: added pad '%s':%u\n",
pad->entity->name, pad->index);
return media_pipeline_walk_push(walk, pad);
}
/* Explore the next link of the entity at the top of the stack. */ staticint media_pipeline_explore_next_link(struct media_pipeline *pipe, struct media_pipeline_walk *walk)
{ struct media_pipeline_walk_entry *entry = media_pipeline_walk_top(walk); struct media_pad *origin; struct media_link *link; struct media_pad *local; struct media_pad *remote; bool last_link; int ret;
/* Get the local pad and remote pad. */ if (link->source->entity == origin->entity) {
local = link->source;
remote = link->sink;
} else {
local = link->sink;
remote = link->source;
}
/* * Skip links that originate from a different pad than the incoming pad * that is not connected internally in the entity to the incoming pad.
*/ if (origin != local &&
!media_entity_has_pad_interdep(origin->entity, origin->index,
local->index)) {
dev_dbg(walk->mdev->dev, "media pipeline: skipping link (no route)\n"); goto done;
}
/* * Add the local pad of the link to the pipeline and push it to the * stack, if not already present.
*/
ret = media_pipeline_add_pad(pipe, walk, local); if (ret) return ret;
/* Similarly, add the remote pad, but only if the link is enabled. */ if (!(link->flags & MEDIA_LNK_FL_ENABLED)) {
dev_dbg(walk->mdev->dev, "media pipeline: skipping link (disabled)\n"); goto done;
}
ret = media_pipeline_add_pad(pipe, walk, remote); if (ret) return ret;
done: /* * If we're done iterating over links, iterate over pads of the entity. * This is necessary to discover pads that are not connected with any * link. Those are dead ends from a pipeline exploration point of view, * but are still part of the pipeline and need to be added to enable * proper validation.
*/ if (!last_link) return 0;
dev_dbg(walk->mdev->dev, "media pipeline: adding unconnected pads of '%s'\n",
local->entity->name);
media_entity_for_each_pad(origin->entity, local) { /* * Skip the origin pad (already handled), pad that have links * (already discovered through iterating over links) and pads * not internally connected.
*/ if (origin == local || local->num_links ||
!media_entity_has_pad_interdep(origin->entity, origin->index,
local->index)) continue;
ret = media_pipeline_add_pad(pipe, walk, local); if (ret) return ret;
}
staticint media_pipeline_populate(struct media_pipeline *pipe, struct media_pad *pad)
{ struct media_pipeline_walk walk = { }; struct media_pipeline_pad *ppad; int ret;
/* * Populate the media pipeline by walking the media graph, starting * from @pad.
*/
INIT_LIST_HEAD(&pipe->pads);
pipe->mdev = pad->graph_obj.mdev;
walk.mdev = pipe->mdev;
walk.stack.top = -1;
ret = media_pipeline_add_pad(pipe, &walk, pad); if (ret) goto done;
/* * Use a depth-first search algorithm: as long as the stack is not * empty, explore the next link of the top entry. The * media_pipeline_explore_next_link() function will either move to the * next link, pop the entry if fully visited, or add new entries on * top.
*/ while (!media_pipeline_walk_empty(&walk)) {
ret = media_pipeline_explore_next_link(pipe, &walk); if (ret) goto done;
}
dev_dbg(pad->graph_obj.mdev->dev, "media pipeline populated, found pads:\n");
/* * If the pad is already part of a pipeline, that pipeline must be the * same as the pipe given to media_pipeline_start().
*/ if (WARN_ON(origin->pipe && origin->pipe != pipe)) return -EINVAL;
/* * If the pipeline has already been started, it is guaranteed to be * valid, so just increase the start count.
*/ if (pipe->start_count) {
pipe->start_count++; return 0;
}
/* * Populate the pipeline. This populates the media_pipeline pads list * with media_pipeline_pad instances for each pad found during graph * walk.
*/
ret = media_pipeline_populate(pipe, origin); if (ret) return ret;
/* * Now that all the pads in the pipeline have been gathered, perform * the validation steps.
*/
dev_dbg(mdev->dev, "Validating pad '%s':%u\n", pad->entity->name,
pad->index);
/* * 1. Ensure that the pad doesn't already belong to a different * pipeline.
*/ if (pad->pipe) {
dev_dbg(mdev->dev, "Failed to start pipeline: pad '%s':%u busy\n",
pad->entity->name, pad->index);
ret = -EBUSY; goto error;
}
/* * 2. Validate all active links whose sink is the current pad. * Validation of the source pads is performed in the context of * the connected sink pad to avoid duplicating checks.
*/
for_each_media_entity_data_link(entity, link) { /* Skip links unrelated to the current pad. */ if (link->sink != pad && link->source != pad) continue;
/* Record if the pad has links and enabled links. */ if (link->flags & MEDIA_LNK_FL_ENABLED)
has_enabled_link = true;
/* * Validate the link if it's enabled and has the * current pad as its sink.
*/ if (!(link->flags & MEDIA_LNK_FL_ENABLED)) continue;
if (link->sink != pad) continue;
if (!entity->ops || !entity->ops->link_validate) continue;
/* * 3. If the pad has the MEDIA_PAD_FL_MUST_CONNECT flag set, * ensure that it has either no link or an enabled link.
*/ if ((pad->flags & MEDIA_PAD_FL_MUST_CONNECT) &&
!has_enabled_link) {
dev_dbg(mdev->dev, "Pad '%s':%u must be connected by an enabled link\n",
pad->entity->name, pad->index);
ret = -ENOLINK; goto error;
}
/* Validation passed, store the pipe pointer in the pad. */
pad->pipe = pipe;
}
pipe->start_count++;
return 0;
error: /* * Link validation on graph failed. We revert what we did and * return the error.
*/
list_for_each_entry(err_ppad, &pipe->pads, list) { if (err_ppad == ppad) break;
/* * Is the pad already part of a pipeline? If not, we need to allocate * a pipe.
*/
pipe = media_pad_pipeline(pad); if (!pipe) {
new_pipe = kzalloc(sizeof(*new_pipe), GFP_KERNEL); if (!new_pipe) {
ret = -ENOMEM; goto out;
}
pipe = new_pipe;
pipe->allocated = true;
}
ret = __media_pipeline_start(pad, pipe); if (ret)
kfree(new_pipe);
/* Remove the reverse links for a data link. */ if ((link->flags & MEDIA_LNK_FL_LINK_TYPE) == MEDIA_LNK_FL_DATA_LINK) {
link->source->num_links--;
link->sink->num_links--;
/* Initialize graph object embedded at the new link */
media_gobj_create(source->graph_obj.mdev, MEDIA_GRAPH_LINK,
&link->graph_obj);
/* Create the backlink. Backlinks are used to help graph traversal and * are not reported to userspace.
*/
backlink = media_add_link(&sink->links); if (backlink == NULL) {
__media_entity_remove_link(source, link); return -ENOMEM;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.