Em 20-08-2010 12:29, Laurent Pinchart escreveu: > Create the following ioctl and implement it at the media device level to > setup links. > > - MEDIA_IOC_SETUP_LINK: Modify the properties of a given link > > The only property that can currently be modified is the ACTIVE link flag > to activate/deactivate a link. Links marked with the IMMUTABLE link flag > can not be activated or deactivated. > > Activating and deactivating a link has effects on entities' use count. > Those changes are automatically propagated through the graph. You need to address here the release() call: if the userspace application dies or just exits, the device should be set into a sane state, e. g. devices powered on should be turned off, and links activated by the application should be de-activated. > > Signed-off-by: Laurent Pinchart <laurent.pinchart@xxxxxxxxxxxxxxxx> > Signed-off-by: Stanimir Varbanov <svarbanov@xxxxxxxxxx> > Signed-off-by: Sakari Ailus <sakari.ailus@xxxxxxxxxxxxxxxxxxxxxxxxxx> > --- > Documentation/media-framework.txt | 81 ++++++++++++++- > drivers/media/media-device.c | 45 ++++++++ > drivers/media/media-entity.c | 208 +++++++++++++++++++++++++++++++++++++ > include/linux/media.h | 1 + > include/media/media-entity.h | 8 ++ > 5 files changed, 340 insertions(+), 3 deletions(-) > > diff --git a/Documentation/media-framework.txt b/Documentation/media-framework.txt > index 74a137d..7894ef3 100644 > --- a/Documentation/media-framework.txt > +++ b/Documentation/media-framework.txt > @@ -278,6 +278,16 @@ When the graph traversal is complete the function will return NULL. > Graph traversal can be interrupted at any moment. No cleanup function call is > required and the graph structure can be freed normally. > > +Helper functions can be used to find a link between two given pads, or a pad > +connected to another pad through an active link > + > + media_entity_find_link(struct media_pad *source, > + struct media_pad *sink); > + > + media_entity_remote_pad(struct media_pad *pad); > + > +Refer to the kerneldoc documentation for more information. > + > > Reference counting and power handling > ------------------------------------- > @@ -316,6 +326,46 @@ is allowed to fail when turning power on, in which case the media_entity_get > function will return NULL. > > > +Links setup > +----------- > + > +Link properties can be modified at runtime by calling > + > + media_entity_setup_link(struct media_link *link, u32 flags); > + > +The flags argument contains the requested new link flags. > + > +The only configurable property is the ACTIVE link flag to activate/deactivate > +a link. Links marked with the IMMUTABLE link flag can not be activated or > +deactivated. > + > +When a link is activated or deactivated, the media framework calls the > +link_setup operation for the two entities at the source and sink of the link, > +in that order. If the second link_setup call fails, another link_setup call is > +made on the first entity to restore the original link flags. > + > +Entity drivers must implement the link_setup operation if any of their links > +is non-immutable. The operation must either configure the hardware or store > +the configuration information to be applied later. > + > +Link activation must not have any side effect on other links. If an active > +link at a sink pad prevents another link at the same pad from being > +deactivated, the link_setup operation must return -EBUSY and can't implicitly > +deactivate the first active link. > + > +Activating and deactivating a link has effects on entities' reference counts. > +When two sub-graphs are connected, the reference count of each of them is > +incremented by the total reference count of all node entities in the other > +sub-graph. When two sub-graphs are disconnected, the reverse operation is > +performed. In both cases the set_power operations are called accordingly, > +ensuring that the link_setup calls are made with power active on the source > +and sink entities. > + > +In other words, activating or deactivating a link propagates reference count > +changes through the graph, and the final state is identical to what it would > +have been if the link had been active or inactive from the start. > + > + > Userspace application API > ------------------------- > > @@ -439,9 +489,6 @@ Valid entity flags are > > ioctl(int fd, int request, struct media_links_enum *argp); > > -Only forward links that originate at one of the entity's source pads are > -returned during the enumeration process. > - > To enumerate pads and/or links for a given entity, applications set the entity > field of a media_links_enum structure and initialize the media_pad_desc and > media_link_desc structure arrays pointed by the pads and links fields. They then > @@ -457,6 +504,9 @@ information about the entity's outbound links. The array must have enough room > to store all the entity's outbound links. The number of outbound links can be > retrieved with the MEDIA_IOC_ENUM_ENTITIES ioctl. > > +Only outbound (forward) links that originate at one of the entity's source > +pads are returned during the enumeration process. > + > The media_pad_desc, media_link_desc and media_links_enum structures are defined > as > > @@ -497,3 +547,28 @@ struct media_pad_desc *pads Pointer to a pads array allocated by the > application. Ignored if NULL. > struct media_link_desc *links Pointer to a links array allocated by the > application. Ignored if NULL. > + > + > + MEDIA_IOC_SETUP_LINK - Modify the properties of a link > + ------------------------------------------------------ > + > + ioctl(int fd, int request, struct media_link_desc *argp); > + > +To change link properties applications fill a media_link_desc structure with > +link identification information (source and sink pad) and the new requested link > +flags. They then call the MEDIA_IOC_SETUP_LINK ioctl with a pointer to that > +structure. > + > +The only configurable property is the ACTIVE link flag to activate/deactivate > +a link. Links marked with the IMMUTABLE link flag can not be activated or > +deactivated. > + > +Link activation has no side effect on other links. If an active link at the > +sink pad prevents the link from being activated, the driver returns with a > +EBUSY error code. > + > +If the specified link can't be found the driver returns with a EINVAL error > +code. > + > +The media_pad_desc and media_link_desc structures are described in the > +MEDIA_IOC_ENUM_LINKS ioctl documentation. > diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c > index 7e020f9..06655d9 100644 > --- a/drivers/media/media-device.c > +++ b/drivers/media/media-device.c > @@ -170,6 +170,44 @@ static long media_device_enum_links(struct media_device *mdev, > return 0; > } > > +static long media_device_setup_link(struct media_device *mdev, > + struct media_link_desc __user *_ulink) > +{ > + struct media_link *link = NULL; > + struct media_link_desc ulink; > + struct media_entity *source; > + struct media_entity *sink; > + int ret; > + > + if (copy_from_user(&ulink, _ulink, sizeof(ulink))) > + return -EFAULT; > + > + /* Find the source and sink entities and link. > + */ > + source = find_entity(mdev, ulink.source.entity); > + sink = find_entity(mdev, ulink.sink.entity); > + > + if (source == NULL || sink == NULL) > + return -EINVAL; > + > + if (ulink.source.index >= source->num_pads || > + ulink.sink.index >= sink->num_pads) > + return -EINVAL; > + > + link = media_entity_find_link(&source->pads[ulink.source.index], > + &sink->pads[ulink.sink.index]); > + if (link == NULL) > + return -EINVAL; > + > + /* Setup the link on both entities. */ > + ret = __media_entity_setup_link(link, ulink.flags); > + > + if (copy_to_user(_ulink, &ulink, sizeof(ulink))) > + return -EFAULT; > + > + return ret; > +} > + > static long media_device_ioctl(struct file *filp, unsigned int cmd, > unsigned long arg) > { > @@ -195,6 +233,13 @@ static long media_device_ioctl(struct file *filp, unsigned int cmd, > mutex_unlock(&dev->graph_mutex); > break; > > + case MEDIA_IOC_SETUP_LINK: > + mutex_lock(&dev->graph_mutex); > + ret = media_device_setup_link(dev, > + (struct media_link_desc __user *)arg); > + mutex_unlock(&dev->graph_mutex); > + break; > + > default: > ret = -ENOIOCTLCMD; > } > diff --git a/drivers/media/media-entity.c b/drivers/media/media-entity.c > index da4fef6..bc97b78 100644 > --- a/drivers/media/media-entity.c > +++ b/drivers/media/media-entity.c > @@ -198,6 +198,25 @@ EXPORT_SYMBOL_GPL(media_entity_graph_walk_next); > * Power state handling > */ > > +/* > + * Return power count of nodes directly or indirectly connected to > + * a given entity. > + */ > +static int media_entity_count_node(struct media_entity *entity) > +{ > + struct media_entity_graph graph; > + int use = 0; > + > + media_entity_graph_walk_start(&graph, entity); > + > + while ((entity = media_entity_graph_walk_next(&graph))) { > + if (media_entity_type(entity) == MEDIA_ENTITY_TYPE_NODE) > + use += entity->use_count; > + } > + > + return use; > +} > + > /* Apply use count to an entity. */ > static void media_entity_use_apply_one(struct media_entity *entity, int change) > { > @@ -261,6 +280,32 @@ static int media_entity_power_apply(struct media_entity *entity, int change) > return ret; > } > > +/* Apply the power state changes when connecting two entities. */ > +static int media_entity_power_connect(struct media_entity *one, > + struct media_entity *theother) > +{ > + int power_one = media_entity_count_node(one); > + int power_theother = media_entity_count_node(theother); > + int ret = 0; > + > + ret = media_entity_power_apply(one, power_theother); > + if (ret < 0) > + return ret; > + > + return media_entity_power_apply(theother, power_one); > +} > + > +static void media_entity_power_disconnect(struct media_entity *one, > + struct media_entity *theother) > +{ > + int power_one = media_entity_count_node(one); > + int power_theother = media_entity_count_node(theother); > + > + /* Powering off entities is assumed to never fail. */ > + media_entity_power_apply(one, -power_theother); > + media_entity_power_apply(theother, -power_one); > +} > + > /* > * Apply use count change to graph and change power state of entities > * accordingly. > @@ -404,3 +449,166 @@ media_entity_create_link(struct media_entity *source, u16 source_pad, > return 0; > } > EXPORT_SYMBOL(media_entity_create_link); > + > +static int __media_entity_setup_link_notify(struct media_link *link, u32 flags) > +{ > + const u32 mask = MEDIA_LINK_FLAG_ACTIVE; > + int ret; > + > + /* Notify both entities. */ > + ret = media_entity_call(link->source->entity, link_setup, > + link->source, link->sink, flags); > + if (ret < 0 && ret != -ENOIOCTLCMD) > + return ret; > + > + ret = media_entity_call(link->sink->entity, link_setup, > + link->sink, link->source, flags); > + if (ret < 0 && ret != -ENOIOCTLCMD) { > + media_entity_call(link->source->entity, link_setup, > + link->source, link->sink, link->flags); > + return ret; > + } > + > + link->flags = (link->flags & ~mask) | (flags & mask); > + link->reverse->flags = link->flags; > + > + return 0; > +} > + > +/** > + * __media_entity_setup_link - Configure a media link > + * @link: The link being configured > + * @flags: Link configuration flags > + * > + * The bulk of link setup is handled by the two entities connected through the > + * link. This function notifies both entities of the link configuration change. > + * > + * If the link is immutable or if the current and new configuration are > + * identical, return immediately. > + * > + * The user is expected to hold link->source->parent->mutex. If not, > + * media_entity_setup_link() should be used instead. > + */ > +int __media_entity_setup_link(struct media_link *link, u32 flags) > +{ > + struct media_entity *source, *sink; > + int ret = -EBUSY; > + > + if (link == NULL) > + return -EINVAL; > + > + if (link->flags & MEDIA_LINK_FLAG_IMMUTABLE) > + return link->flags == flags ? 0 : -EINVAL; > + > + if (link->flags == flags) > + return 0; > + > + source = __media_entity_get(link->source->entity); > + if (!source) > + return ret; > + > + sink = __media_entity_get(link->sink->entity); > + if (!sink) > + goto err___media_entity_get; > + > + if (flags & MEDIA_LINK_FLAG_ACTIVE) { > + ret = media_entity_power_connect(source, sink); > + if (ret < 0) > + goto err_media_entity_power_connect; > + } > + > + ret = __media_entity_setup_link_notify(link, flags); > + if (ret < 0) > + goto err___media_entity_setup_link_notify; > + > + if (!(flags & MEDIA_LINK_FLAG_ACTIVE)) > + media_entity_power_disconnect(source, sink); > + > + __media_entity_put(sink); > + __media_entity_put(source); > + > + return 0; > + > +err___media_entity_setup_link_notify: > + if (flags & MEDIA_LINK_FLAG_ACTIVE) > + media_entity_power_disconnect(source, sink); > +err_media_entity_power_connect: > + __media_entity_put(sink); > +err___media_entity_get: > + __media_entity_put(source); > + > + return ret; > +} > + > +int media_entity_setup_link(struct media_link *link, u32 flags) > +{ > + int ret; > + > + mutex_lock(&link->source->entity->parent->graph_mutex); > + ret = __media_entity_setup_link(link, flags); > + mutex_unlock(&link->source->entity->parent->graph_mutex); > + > + return ret; > +} > +EXPORT_SYMBOL_GPL(media_entity_setup_link); > + > +/** > + * media_entity_find_link - Find a link between two pads > + * @source: Source pad > + * @sink: Sink pad > + * > + * Return a pointer to the link between the two entities. If no such link > + * exists, return NULL. > + */ > +struct media_link * > +media_entity_find_link(struct media_pad *source, struct media_pad *sink) > +{ > + struct media_link *link; > + unsigned int i; > + > + for (i = 0; i < source->entity->num_links; ++i) { > + link = &source->entity->links[i]; > + > + if (link->source->entity == source->entity && > + link->source->index == source->index && > + link->sink->entity == sink->entity && > + link->sink->index == sink->index) > + return link; > + } > + > + return NULL; > +} > +EXPORT_SYMBOL_GPL(media_entity_find_link); > + > +/** > + * media_entity_remote_pad - Locate the pad at the remote end of a link > + * @entity: Local entity > + * @pad: Pad at the local end of the link > + * > + * Search for a remote pad connected to the given pad by iterating over all > + * links originating or terminating at that pad until an active link is found. > + * > + * Return a pointer to the pad at the remote end of the first found active link, > + * or NULL if no active link has been found. > + */ > +struct media_pad *media_entity_remote_pad(struct media_pad *pad) > +{ > + unsigned int i; > + > + for (i = 0; i < pad->entity->num_links; i++) { > + struct media_link *link = &pad->entity->links[i]; > + > + if (!(link->flags & MEDIA_LINK_FLAG_ACTIVE)) > + continue; > + > + if (link->source == pad) > + return link->sink; > + > + if (link->sink == pad) > + return link->source; > + } > + > + return NULL; > + > +} > +EXPORT_SYMBOL_GPL(media_entity_remote_pad); > diff --git a/include/linux/media.h b/include/linux/media.h > index 542509b..edb53c2 100644 > --- a/include/linux/media.h > +++ b/include/linux/media.h > @@ -100,5 +100,6 @@ struct media_links_enum { > #define MEDIA_IOC_DEVICE_INFO _IOWR('M', 1, struct media_device_info) > #define MEDIA_IOC_ENUM_ENTITIES _IOWR('M', 2, struct media_entity_desc) > #define MEDIA_IOC_ENUM_LINKS _IOWR('M', 3, struct media_links_enum) > +#define MEDIA_IOC_SETUP_LINK _IOWR('M', 4, struct media_link_desc) > > #endif /* __LINUX_MEDIA_H */ > diff --git a/include/media/media-entity.h b/include/media/media-entity.h > index 8c40d5e..0f0697b 100644 > --- a/include/media/media-entity.h > +++ b/include/media/media-entity.h > @@ -18,6 +18,9 @@ struct media_pad { > }; > > struct media_entity_operations { > + int (*link_setup)(struct media_entity *entity, > + const struct media_pad *local, > + const struct media_pad *remote, u32 flags); > int (*set_power)(struct media_entity *entity, int power); > }; > > @@ -88,6 +91,11 @@ int media_entity_init(struct media_entity *entity, u16 num_pads, > void media_entity_cleanup(struct media_entity *entity); > int media_entity_create_link(struct media_entity *source, u16 source_pad, > struct media_entity *sink, u16 sink_pad, u32 flags); > +int __media_entity_setup_link(struct media_link *link, u32 flags); > +int media_entity_setup_link(struct media_link *link, u32 flags); > +struct media_link *media_entity_find_link(struct media_pad *source, > + struct media_pad *sink); > +struct media_pad *media_entity_remote_pad(struct media_pad *pad); > > struct media_entity *media_entity_get(struct media_entity *entity); > void media_entity_put(struct media_entity *entity); -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html