[media] media: Links setup
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 ENABLED link flag to enable/disable a link. Links marked with the IMMUTABLE link flag can not be enabled or disabled. Enabling or disabling a link has effects on entities' use count. Those changes are automatically propagated through the graph. Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Stanimir Varbanov <svarbanov@mm-sol.com> Signed-off-by: Sakari Ailus <sakari.ailus@iki.fi> Acked-by: Hans Verkuil <hverkuil@xs4all.nl> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
This commit is contained in:
Родитель
1651333b09
Коммит
97548ed4c4
|
@ -94,6 +94,7 @@
|
|||
<!ENTITY MEDIA-IOC-DEVICE-INFO "<link linkend='media-ioc-device-info'><constant>MEDIA_IOC_DEVICE_INFO</constant></link>">
|
||||
<!ENTITY MEDIA-IOC-ENUM-ENTITIES "<link linkend='media-ioc-enum-entities'><constant>MEDIA_IOC_ENUM_ENTITIES</constant></link>">
|
||||
<!ENTITY MEDIA-IOC-ENUM-LINKS "<link linkend='media-ioc-enum-links'><constant>MEDIA_IOC_ENUM_LINKS</constant></link>">
|
||||
<!ENTITY MEDIA-IOC-SETUP-LINK "<link linkend='media-ioc-setup-link'><constant>MEDIA_IOC_SETUP_LINK</constant></link>">
|
||||
|
||||
<!-- Types -->
|
||||
<!ENTITY v4l2-std-id "<link linkend='v4l2-std-id'>v4l2_std_id</link>">
|
||||
|
@ -348,6 +349,7 @@
|
|||
<!ENTITY sub-media-ioc-device-info SYSTEM "v4l/media-ioc-device-info.xml">
|
||||
<!ENTITY sub-media-ioc-enum-entities SYSTEM "v4l/media-ioc-enum-entities.xml">
|
||||
<!ENTITY sub-media-ioc-enum-links SYSTEM "v4l/media-ioc-enum-links.xml">
|
||||
<!ENTITY sub-media-ioc-setup-link SYSTEM "v4l/media-ioc-setup-link.xml">
|
||||
|
||||
<!-- Function Reference -->
|
||||
<!ENTITY close SYSTEM "v4l/func-close.xml">
|
||||
|
|
|
@ -85,4 +85,5 @@
|
|||
&sub-media-ioc-device-info;
|
||||
&sub-media-ioc-enum-entities;
|
||||
&sub-media-ioc-enum-links;
|
||||
&sub-media-ioc-setup-link;
|
||||
</appendix>
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
<refentry id="media-ioc-setup-link">
|
||||
<refmeta>
|
||||
<refentrytitle>ioctl MEDIA_IOC_SETUP_LINK</refentrytitle>
|
||||
&manvol;
|
||||
</refmeta>
|
||||
|
||||
<refnamediv>
|
||||
<refname>MEDIA_IOC_SETUP_LINK</refname>
|
||||
<refpurpose>Modify the properties of a link</refpurpose>
|
||||
</refnamediv>
|
||||
|
||||
<refsynopsisdiv>
|
||||
<funcsynopsis>
|
||||
<funcprototype>
|
||||
<funcdef>int <function>ioctl</function></funcdef>
|
||||
<paramdef>int <parameter>fd</parameter></paramdef>
|
||||
<paramdef>int <parameter>request</parameter></paramdef>
|
||||
<paramdef>struct media_link_desc *<parameter>argp</parameter></paramdef>
|
||||
</funcprototype>
|
||||
</funcsynopsis>
|
||||
</refsynopsisdiv>
|
||||
|
||||
<refsect1>
|
||||
<title>Arguments</title>
|
||||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term><parameter>fd</parameter></term>
|
||||
<listitem>
|
||||
<para>File descriptor returned by
|
||||
<link linkend='media-func-open'><function>open()</function></link>.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><parameter>request</parameter></term>
|
||||
<listitem>
|
||||
<para>MEDIA_IOC_ENUM_LINKS</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><parameter>argp</parameter></term>
|
||||
<listitem>
|
||||
<para></para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>Description</title>
|
||||
|
||||
<para>To change link properties applications fill a &media-link-desc; 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.</para>
|
||||
<para>The only configurable property is the <constant>ENABLED</constant>
|
||||
link flag to enable/disable a link. Links marked with the
|
||||
<constant>IMMUTABLE</constant> link flag can not be enabled or disabled.
|
||||
</para>
|
||||
<para>Link configuration has no side effect on other links. If an enabled
|
||||
link at the sink pad prevents the link from being enabled, the driver
|
||||
returns with an &EBUSY;.</para>
|
||||
<para>If the specified link can't be found the driver returns with an
|
||||
&EINVAL;.</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
&return-value;
|
||||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term><errorcode>EBUSY</errorcode></term>
|
||||
<listitem>
|
||||
<para>The link properties can't be changed because the link is
|
||||
currently busy. This can be caused, for instance, by an active media
|
||||
stream (audio or video) on the link. The ioctl shouldn't be retried if
|
||||
no other action is performed before to fix the problem.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><errorcode>EINVAL</errorcode></term>
|
||||
<listitem>
|
||||
<para>The &media-link-desc; references a non-existing link, or the
|
||||
link is immutable and an attempt to modify its configuration was made.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
</refentry>
|
|
@ -259,6 +259,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 enabled link
|
||||
|
||||
media_entity_find_link(struct media_pad *source,
|
||||
struct media_pad *sink);
|
||||
|
||||
media_entity_remote_source(struct media_pad *pad);
|
||||
|
||||
Refer to the kerneldoc documentation for more information.
|
||||
|
||||
|
||||
Use count and power handling
|
||||
----------------------------
|
||||
|
@ -271,3 +281,35 @@ track the number of users of every entity for power management needs.
|
|||
The use_count field is owned by media drivers and must not be touched by entity
|
||||
drivers. Access to the field must be protected by the media device graph_mutex
|
||||
lock.
|
||||
|
||||
|
||||
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 ENABLED link flag to enable/disable a
|
||||
link. Links marked with the IMMUTABLE link flag can not be enabled or disabled.
|
||||
|
||||
When a link is enabled or disabled, 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.
|
||||
|
||||
Media device drivers can be notified of link setup operations by setting the
|
||||
media_device::link_notify pointer to a callback function. If provided, the
|
||||
notification callback will be called before enabling and after disabling
|
||||
links.
|
||||
|
||||
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 configuration must not have any side effect on other links. If an enabled
|
||||
link at a sink pad prevents another link at the same pad from being disabled,
|
||||
the link_setup operation must return -EBUSY and can't implicitly disable the
|
||||
first enabled link.
|
||||
|
|
|
@ -172,6 +172,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)
|
||||
{
|
||||
|
@ -197,6 +235,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;
|
||||
}
|
||||
|
|
|
@ -306,3 +306,158 @@ media_entity_create_link(struct media_entity *source, u16 source_pad,
|
|||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(media_entity_create_link);
|
||||
|
||||
static int __media_entity_setup_link_notify(struct media_link *link, u32 flags)
|
||||
{
|
||||
const u32 mask = MEDIA_LNK_FL_ENABLED;
|
||||
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_device *mdev;
|
||||
struct media_entity *source, *sink;
|
||||
int ret = -EBUSY;
|
||||
|
||||
if (link == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
if (link->flags & MEDIA_LNK_FL_IMMUTABLE)
|
||||
return link->flags == flags ? 0 : -EINVAL;
|
||||
|
||||
if (link->flags == flags)
|
||||
return 0;
|
||||
|
||||
source = link->source->entity;
|
||||
sink = link->sink->entity;
|
||||
|
||||
mdev = source->parent;
|
||||
|
||||
if ((flags & MEDIA_LNK_FL_ENABLED) && mdev->link_notify) {
|
||||
ret = mdev->link_notify(link->source, link->sink,
|
||||
MEDIA_LNK_FL_ENABLED);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = __media_entity_setup_link_notify(link, flags);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
if (!(flags & MEDIA_LNK_FL_ENABLED) && mdev->link_notify)
|
||||
mdev->link_notify(link->source, link->sink, 0);
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
if ((flags & MEDIA_LNK_FL_ENABLED) && mdev->link_notify)
|
||||
mdev->link_notify(link->source, link->sink, 0);
|
||||
|
||||
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_source - Find the source pad at the remote end of a link
|
||||
* @pad: Sink pad at the local end of the link
|
||||
*
|
||||
* Search for a remote source pad connected to the given sink pad by iterating
|
||||
* over all links originating or terminating at that pad until an enabled link
|
||||
* is found.
|
||||
*
|
||||
* Return a pointer to the pad at the remote end of the first found enabled
|
||||
* link, or NULL if no enabled link has been found.
|
||||
*/
|
||||
struct media_pad *media_entity_remote_source(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_LNK_FL_ENABLED))
|
||||
continue;
|
||||
|
||||
if (link->source == pad)
|
||||
return link->sink;
|
||||
|
||||
if (link->sink == pad)
|
||||
return link->source;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(media_entity_remote_source);
|
||||
|
|
|
@ -126,5 +126,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 */
|
||||
|
|
|
@ -73,6 +73,9 @@ struct media_device {
|
|||
spinlock_t lock;
|
||||
/* Serializes graph operations. */
|
||||
struct mutex graph_mutex;
|
||||
|
||||
int (*link_notify)(struct media_pad *source,
|
||||
struct media_pad *sink, u32 flags);
|
||||
};
|
||||
|
||||
/* media_devnode to media_device */
|
||||
|
|
|
@ -39,6 +39,12 @@ struct media_pad {
|
|||
unsigned long flags; /* Pad flags (MEDIA_PAD_FL_*) */
|
||||
};
|
||||
|
||||
struct media_entity_operations {
|
||||
int (*link_setup)(struct media_entity *entity,
|
||||
const struct media_pad *local,
|
||||
const struct media_pad *remote, u32 flags);
|
||||
};
|
||||
|
||||
struct media_entity {
|
||||
struct list_head list;
|
||||
struct media_device *parent; /* Media device this entity belongs to*/
|
||||
|
@ -59,6 +65,8 @@ struct media_entity {
|
|||
struct media_pad *pads; /* Pads array (num_pads elements) */
|
||||
struct media_link *links; /* Links array (max_links elements)*/
|
||||
|
||||
const struct media_entity_operations *ops; /* Entity operations */
|
||||
|
||||
/* Reference counts must never be negative, but are signed integers on
|
||||
* purpose: a simple WARN_ON(<0) check can be used to detect reference
|
||||
* count bugs that would make them negative.
|
||||
|
@ -112,6 +120,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_source(struct media_pad *pad);
|
||||
|
||||
struct media_entity *media_entity_get(struct media_entity *entity);
|
||||
void media_entity_put(struct media_entity *entity);
|
||||
|
@ -121,4 +134,8 @@ void media_entity_graph_walk_start(struct media_entity_graph *graph,
|
|||
struct media_entity *
|
||||
media_entity_graph_walk_next(struct media_entity_graph *graph);
|
||||
|
||||
#define media_entity_call(entity, operation, args...) \
|
||||
(((entity)->ops && (entity)->ops->operation) ? \
|
||||
(entity)->ops->operation((entity) , ##args) : -ENOIOCTLCMD)
|
||||
|
||||
#endif
|
||||
|
|
Загрузка…
Ссылка в новой задаче