[PATCH v1 03/19] uvcvideo: Add support for multiple chains with common roots.

Pawel Osciak
2013-08-30T02:17:33Z

This adds support for pipelines that fork into branches consisting of more
than one entity, creating a chain for each fork and putting common entities
on all chains that share them.

This requires us to share the ctrl_mutex across forked chains. Whenever we
discover a shared part of a chain, we assign the pointer to an existing
mutex to the sharing chain instead of creating a new one.

The "forward scan" is not needed anymore, as after scanning back from OTs,
we go over all entities which are not on a path from an OT and accept
single-XU branches, adding them to the existing chains.

Also extract control locking into __uvc_ctrl_{lock,unlock} functions.

Signed-off-by: Pawel Osciak <posciak@chromium.org>
---
drivers/media/usb/uvc/uvc_ctrl.c | 70 ++++++++-----
drivers/media/usb/uvc/uvc_driver.c | 210 +++++++++++++++++++++----------------
drivers/media/usb/uvc/uvc_entity.c | 15 ++-
drivers/media/usb/uvc/uvc_v4l2.c | 9 +-
drivers/media/usb/uvc/uvcvideo.h | 20 +++-
5 files changed, 199 insertions(+), 125 deletions(-)

diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
index a2f4501..ba159a4 100644
--- a/drivers/media/usb/uvc/uvc_ctrl.c
+++ b/drivers/media/usb/uvc/uvc_ctrl.c
@@ -841,6 +841,7 @@ static struct uvc_control *uvc_find_control(struct uvc_video_chain *chain,
{
struct uvc_control *ctrl = NULL;
struct uvc_entity *entity;
+ struct uvc_chain_entry *entry;
int next = v4l2_id & V4L2_CTRL_FLAG_NEXT_CTRL;

*mapping = NULL;
@@ -849,7 +850,8 @@ static struct uvc_control *uvc_find_control(struct uvc_video_chain *chain,
v4l2_id &= V4L2_CTRL_ID_MASK;

/* Find the control. */
- list_for_each_entry(entity, &chain->entities, chain) {
+ list_for_each_entry(entry, &chain->entities, chain_entry) {
+ entity = entry->entity;
__uvc_find_control(entity, v4l2_id, mapping, &ctrl, next);
if (ctrl && !next)
return ctrl;
@@ -1048,6 +1050,16 @@ static int __uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
return 0;
}

+int __uvc_ctrl_lock(struct uvc_video_chain *chain)
+{
+ return mutex_lock_interruptible(&chain->pipeline->ctrl_mutex) ?
+ -ERESTARTSYS : 0;
+}
+void __uvc_ctrl_unlock(struct uvc_video_chain *chain)
+{
+ mutex_unlock(&chain->pipeline->ctrl_mutex);
+}
+
int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
struct v4l2_queryctrl *v4l2_ctrl)
{
@@ -1055,9 +1067,9 @@ int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
struct uvc_control_mapping *mapping;
int ret;

- ret = mutex_lock_interruptible(&chain->ctrl_mutex);
+ ret = __uvc_ctrl_lock(chain);
if (ret < 0)
- return -ERESTARTSYS;
+ return ret;

ctrl = uvc_find_control(chain, v4l2_ctrl->id, &mapping);
if (ctrl == NULL) {
@@ -1067,7 +1079,7 @@ int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,

ret = __uvc_query_v4l2_ctrl(chain, ctrl, mapping, v4l2_ctrl);
done:
- mutex_unlock(&chain->ctrl_mutex);
+ __uvc_ctrl_unlock(chain);
return ret;
}

@@ -1094,9 +1106,9 @@ int uvc_query_v4l2_menu(struct uvc_video_chain *chain,
query_menu->id = id;
query_menu->index = index;

- ret = mutex_lock_interruptible(&chain->ctrl_mutex);
+ ret = __uvc_ctrl_lock(chain);
if (ret < 0)
- return -ERESTARTSYS;
+ return ret;

ctrl = uvc_find_control(chain, query_menu->id, &mapping);
if (ctrl == NULL || mapping->v4l2_type != V4L2_CTRL_TYPE_MENU) {
@@ -1132,7 +1144,7 @@ int uvc_query_v4l2_menu(struct uvc_video_chain *chain,
strlcpy(query_menu->name, menu_info->name, sizeof query_menu->name);

done:
- mutex_unlock(&chain->ctrl_mutex);
+ __uvc_ctrl_unlock(chain);
return ret;
}

@@ -1257,9 +1269,9 @@ static int uvc_ctrl_add_event(struct v4l2_subscribed_event *sev, unsigned elems)
struct uvc_control *ctrl;
int ret;

- ret = mutex_lock_interruptible(&handle->chain->ctrl_mutex);
+ ret = __uvc_ctrl_lock(handle->chain);
if (ret < 0)
- return -ERESTARTSYS;
+ return ret;

ctrl = uvc_find_control(handle->chain, sev->id, &mapping);
if (ctrl == NULL) {
@@ -1285,7 +1297,7 @@ static int uvc_ctrl_add_event(struct v4l2_subscribed_event *sev, unsigned elems)
}

done:
- mutex_unlock(&handle->chain->ctrl_mutex);
+ __uvc_ctrl_unlock(handle->chain);
return ret;
}

@@ -1293,9 +1305,9 @@ static void uvc_ctrl_del_event(struct v4l2_subscribed_event *sev)
{
struct uvc_fh *handle = container_of(sev->fh, struct uvc_fh, vfh);

- mutex_lock(&handle->chain->ctrl_mutex);
+ __uvc_ctrl_lock(handle->chain);
list_del(&sev->node);
- mutex_unlock(&handle->chain->ctrl_mutex);
+ __uvc_ctrl_unlock(handle->chain);
}

const struct v4l2_subscribed_event_ops uvc_ctrl_sub_ev_ops = {
@@ -1331,7 +1343,7 @@ const struct v4l2_subscribed_event_ops uvc_ctrl_sub_ev_ops = {
*/
int uvc_ctrl_begin(struct uvc_video_chain *chain)
{
- return mutex_lock_interruptible(&chain->ctrl_mutex) ? -ERESTARTSYS : 0;
+ return __uvc_ctrl_lock(chain);
}

static int uvc_ctrl_commit_entity(struct uvc_device *dev,
@@ -1390,10 +1402,12 @@ int __uvc_ctrl_commit(struct uvc_fh *handle, int rollback,
{
struct uvc_video_chain *chain = handle->chain;
struct uvc_entity *entity;
+ struct uvc_chain_entry *entry;
int ret = 0;

/* Find the control. */
- list_for_each_entry(entity, &chain->entities, chain) {
+ list_for_each_entry(entry, &chain->entities, chain_entry) {
+ entity = entry->entity;
ret = uvc_ctrl_commit_entity(chain->dev, entity, rollback);
if (ret < 0)
goto done;
@@ -1402,7 +1416,7 @@ int __uvc_ctrl_commit(struct uvc_fh *handle, int rollback,
if (!rollback)
uvc_ctrl_send_events(handle, xctrls, xctrls_count);
done:
- mutex_unlock(&chain->ctrl_mutex);
+ __uvc_ctrl_unlock(chain);
return ret;
}

@@ -1667,7 +1681,8 @@ static int uvc_ctrl_init_xu_ctrl(struct uvc_device *dev,
int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
struct uvc_xu_control_query *xqry)
{
- struct uvc_entity *entity;
+ struct uvc_entity *entity = NULL;
+ struct uvc_chain_entry *entry;
struct uvc_control *ctrl;
unsigned int i, found = 0;
__u32 reqflags;
@@ -1676,13 +1691,14 @@ int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
int ret;

/* Find the extension unit. */
- list_for_each_entry(entity, &chain->entities, chain) {
+ list_for_each_entry(entry, &chain->entities, chain_entry) {
+ entity = entry->entity;
if (UVC_ENTITY_TYPE(entity) == UVC_VC_EXTENSION_UNIT &&
entity->id == xqry->unit)
break;
}

- if (entity->id != xqry->unit) {
+ if (!entity || entity->id != xqry->unit) {
uvc_trace(UVC_TRACE_CONTROL, "Extension unit %u not found.\n",
xqry->unit);
return -ENOENT;
@@ -1703,8 +1719,9 @@ int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
return -ENOENT;
}

- if (mutex_lock_interruptible(&chain->ctrl_mutex))
- return -ERESTARTSYS;
+ ret = __uvc_ctrl_lock(chain);
+ if (ret < 0)
+ return ret;

ret = uvc_ctrl_init_xu_ctrl(chain->dev, ctrl);
if (ret < 0) {
@@ -1778,7 +1795,7 @@ int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
ret = -EFAULT;
done:
kfree(data);
- mutex_unlock(&chain->ctrl_mutex);
+ __uvc_ctrl_unlock(chain);
return ret;
}

@@ -1904,6 +1921,7 @@ int uvc_ctrl_add_mapping(struct uvc_video_chain *chain,
const struct uvc_control_mapping *mapping)
{
struct uvc_device *dev = chain->dev;
+ struct uvc_chain_entry *entry;
struct uvc_control_mapping *map;
struct uvc_entity *entity;
struct uvc_control *ctrl;
@@ -1918,8 +1936,9 @@ int uvc_ctrl_add_mapping(struct uvc_video_chain *chain,
}

/* Search for the matching (GUID/CS) control on the current chain */
- list_for_each_entry(entity, &chain->entities, chain) {
+ list_for_each_entry(entry, &chain->entities, chain_entry) {
unsigned int i;
+ entity = entry->entity;

if (UVC_ENTITY_TYPE(entity) != UVC_VC_EXTENSION_UNIT ||
!uvc_entity_match_guid(entity, mapping->entity))
@@ -1939,8 +1958,9 @@ int uvc_ctrl_add_mapping(struct uvc_video_chain *chain,
if (!found)
return -ENOENT;

- if (mutex_lock_interruptible(&chain->ctrl_mutex))
- return -ERESTARTSYS;
+ ret = __uvc_ctrl_lock(chain);
+ if (ret < 0)
+ return ret;

/* Perform delayed initialization of XU controls */
ret = uvc_ctrl_init_xu_ctrl(dev, ctrl);
@@ -1974,7 +1994,7 @@ int uvc_ctrl_add_mapping(struct uvc_video_chain *chain,
atomic_dec(&dev->nmappings);

done:
- mutex_unlock(&chain->ctrl_mutex);
+ __uvc_ctrl_unlock(chain);
return ret;
}

diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c
index 81695d4..d7ff707 100644
--- a/drivers/media/usb/uvc/uvc_driver.c
+++ b/drivers/media/usb/uvc/uvc_driver.c
@@ -1215,6 +1215,36 @@ next_descriptor:
* UVC device scan
*/

+static int uvc_add_chain_entry(struct uvc_video_chain *chain,
+ struct uvc_entity *entity)
+{
+ struct uvc_chain_entry *chain_entry;
+
+ chain_entry = kzalloc(sizeof(struct uvc_chain_entry), GFP_KERNEL);
+ if (!chain_entry)
+ return -ENOMEM;
+
+ chain_entry->entity = entity;
+ list_add_tail(&chain_entry->chain_entry, &chain->entities);
+ if (!entity->chain)
+ entity->chain = chain;
+
+ return 0;
+}
+
+static void uvc_delete_chain(struct uvc_video_chain *chain)
+{
+ struct list_head *p, *n;
+ struct uvc_chain_entry *entry;
+
+ list_for_each_safe(p, n, &chain->entities) {
+ entry = list_entry(p, struct uvc_chain_entry, chain_entry);
+ kfree(entry);
+ }
+
+ kfree(chain);
+}
+
/*
* Scan the UVC descriptors to locate a chain starting at an Output Terminal
* and containing the following units:
@@ -1320,72 +1350,7 @@ static int uvc_scan_chain_entity(struct uvc_video_chain *chain,
return -1;
}

- list_add_tail(&entity->chain, &chain->entities);
- return 0;
-}
-
-static int uvc_scan_chain_forward(struct uvc_video_chain *chain,
- struct uvc_entity *entity, struct uvc_entity *prev)
-{
- struct uvc_entity *forward;
- int found;
-
- /* Forward scan */
- forward = NULL;
- found = 0;
-
- while (1) {
- forward = uvc_entity_by_reference(chain->dev, entity->id,
- forward);
- if (forward == NULL)
- break;
- if (forward == prev)
- continue;
-
- switch (UVC_ENTITY_TYPE(forward)) {
- case UVC_VC_EXTENSION_UNIT:
- if (forward->bNrInPins != 1) {
- uvc_trace(UVC_TRACE_DESCR, "Extension unit %d "
- "has more than 1 input pin.\n",
- entity->id);
- return -EINVAL;
- }
-
- list_add_tail(&forward->chain, &chain->entities);
- if (uvc_trace_param & UVC_TRACE_PROBE) {
- if (!found)
- printk(" (->");
-
- printk(" XU %d", forward->id);
- found = 1;
- }
- break;
-
- case UVC_OTT_VENDOR_SPECIFIC:
- case UVC_OTT_DISPLAY:
- case UVC_OTT_MEDIA_TRANSPORT_OUTPUT:
- case UVC_TT_STREAMING:
- if (UVC_ENTITY_IS_ITERM(forward)) {
- uvc_trace(UVC_TRACE_DESCR, "Unsupported input "
- "terminal %u.\n", forward->id);
- return -EINVAL;
- }
-
- list_add_tail(&forward->chain, &chain->entities);
- if (uvc_trace_param & UVC_TRACE_PROBE) {
- if (!found)
- printk(" (->");
-
- printk(" OT %d", forward->id);
- found = 1;
- }
- break;
- }
- }
- if (found)
- printk(")");
-
- return 0;
+ return uvc_add_chain_entry(chain, entity);
}

static int uvc_scan_chain_backward(struct uvc_video_chain *chain,
@@ -1394,6 +1359,7 @@ static int uvc_scan_chain_backward(struct uvc_video_chain *chain,
struct uvc_entity *entity = *_entity;
struct uvc_entity *term;
int id = -EINVAL, i;
+ int ret;

switch (UVC_ENTITY_TYPE(entity)) {
case UVC_VC_EXTENSION_UNIT:
@@ -1425,8 +1391,9 @@ static int uvc_scan_chain_backward(struct uvc_video_chain *chain,
if (uvc_trace_param & UVC_TRACE_PROBE)
printk(" %d", term->id);

- list_add_tail(&term->chain, &chain->entities);
- uvc_scan_chain_forward(chain, term, entity);
+ ret = uvc_add_chain_entry(chain, term);
+ if (ret)
+ return ret;
}

if (uvc_trace_param & UVC_TRACE_PROBE)
@@ -1473,23 +1440,23 @@ static int uvc_scan_chain(struct uvc_video_chain *chain,
prev = NULL;

while (entity != NULL) {
- /* Entity must not be part of an existing chain */
- if (entity->chain.next || entity->chain.prev) {
- uvc_trace(UVC_TRACE_DESCR, "Found reference to "
- "entity %d already in chain.\n", entity->id);
+ if (entity->chain == chain) {
+ uvc_trace(UVC_TRACE_DESCR, "Found a cycle in the "
+ "chain");
return -EINVAL;
}

+ /* If this entity is a part of an existing chain, the
+ * current chain belongs to the same pipeline.
+ */
+ if (entity->chain)
+ chain->pipeline = entity->chain->pipeline;
+
/* Process entity */
if (uvc_scan_chain_entity(chain, entity) < 0)
return -EINVAL;

- /* Forward scan */
- if (uvc_scan_chain_forward(chain, entity, prev) < 0)
- return -EINVAL;
-
/* Backward scan */
- prev = entity;
if (uvc_scan_chain_backward(chain, &entity) < 0)
return -EINVAL;
}
@@ -1501,10 +1468,12 @@ static unsigned int uvc_print_terms(struct list_head *terms, u16 dir,
char *buffer)
{
struct uvc_entity *term;
+ struct uvc_chain_entry *entry;
unsigned int nterms = 0;
char *p = buffer;

- list_for_each_entry(term, terms, chain) {
+ list_for_each_entry(entry, terms, chain_entry) {
+ term = entry->entity;
if (!UVC_ENTITY_IS_TERM(term) ||
UVC_TERM_DIRECTION(term) != dir)
continue;
@@ -1541,39 +1510,58 @@ static const char *uvc_print_chain(struct uvc_video_chain *chain)
static int uvc_scan_device(struct uvc_device *dev)
{
struct uvc_video_chain *chain;
- struct uvc_entity *term;
+ struct uvc_entity *entity, *source;
+ int ret;

- list_for_each_entry(term, &dev->entities, list) {
- if (!UVC_ENTITY_IS_OTERM(term))
+ list_for_each_entry(entity, &dev->entities, list) {
+ if (!UVC_ENTITY_IS_OTERM(entity))
continue;

- /* If the terminal is already included in a chain, skip it.
- * This can happen for chains that have multiple output
- * terminals, where all output terminals beside the first one
- * will be inserted in the chain in forward scans.
+ /* Allow single-unit branches of Output Terminals to reside
+ * on the existing chains.
*/
- if (term->chain.next || term->chain.prev)
- continue;
+ source = uvc_entity_by_id(dev, entity->baSourceID[0]);
+ if (entity == NULL) {
+ uvc_trace(UVC_TRACE_DESCR, "Found reference to "
+ "unknown entity %d.\n", entity->baSourceID[0]);
+ return -EINVAL;
+ }

chain = kzalloc(sizeof(*chain), GFP_KERNEL);
if (chain == NULL)
return -ENOMEM;

INIT_LIST_HEAD(&chain->entities);
- mutex_init(&chain->ctrl_mutex);
chain->dev = dev;
v4l2_prio_init(&chain->prio);

- term->flags |= UVC_ENTITY_FLAG_DEFAULT;
+ entity->flags |= UVC_ENTITY_FLAG_DEFAULT;

- if (uvc_scan_chain(chain, term) < 0) {
- kfree(chain);
+ if (uvc_scan_chain(chain, entity) < 0) {
+ uvc_delete_chain(chain);
continue;
}

uvc_trace(UVC_TRACE_PROBE, "Found a valid video chain (%s).\n",
uvc_print_chain(chain));

+ /*
+ * If none of the entities are shared, allocate a new pipeline.
+ * Otherwise, the shared pipeline is already set up.
+ */
+ if (!chain->pipeline) {
+ chain->pipeline = kzalloc(sizeof(*chain->pipeline),
+ GFP_KERNEL);
+ if (!chain->pipeline) {
+ uvc_delete_chain(chain);
+ return -ENOMEM;
+ }
+ mutex_init(&chain->pipeline->ctrl_mutex);
+ atomic_set(&chain->pipeline->num_chains, 1);
+ } else {
+ atomic_inc(&chain->pipeline->num_chains);
+ }
+
list_add_tail(&chain->list, &dev->chains);
}

@@ -1582,6 +1570,38 @@ static int uvc_scan_device(struct uvc_device *dev)
return -1;
}

+ /* Find branches with no OTERMs (if any) by looking for entities not
+ * on any chain. Accept only branches with a single Extension Unit.
+ */
+ list_for_each_entry(entity, &dev->entities, list) {
+ if (entity->chain)
+ continue;
+
+ if (UVC_ENTITY_TYPE(entity) != UVC_VC_EXTENSION_UNIT
+ || entity->bNrInPins != 1
+ || uvc_entity_by_reference(dev, entity->id, NULL)) {
+ uvc_printk(KERN_INFO, "Found an invalid branch "
+ "starting at entity id %d.\n", entity->id);
+ return -1;
+ }
+
+ /* Single-unit XU branch. */
+ source = uvc_entity_by_id(dev, entity->baSourceID[0]);
+ if (source == NULL) {
+ uvc_trace(UVC_TRACE_DESCR, "Found reference to "
+ "unknown entity %d.\n", entity->baSourceID[0]);
+ return -EINVAL;
+ }
+ if (!source->chain)
+ continue;
+
+ ret = uvc_add_chain_entry(source->chain, entity);
+ if (ret)
+ return ret;
+ uvc_trace(UVC_TRACE_DESCR, "XU %d <- (%d)\n",
+ entity->id, source->id);
+ }
+
return 0;
}

@@ -1619,7 +1639,9 @@ static void uvc_delete(struct uvc_device *dev)
list_for_each_safe(p, n, &dev->chains) {
struct uvc_video_chain *chain;
chain = list_entry(p, struct uvc_video_chain, list);
- kfree(chain);
+ if (atomic_dec_and_test(&chain->pipeline->num_chains))
+ kfree(chain->pipeline);
+ uvc_delete_chain(chain);
}

list_for_each_safe(p, n, &dev->entities) {
@@ -1763,9 +1785,11 @@ static int uvc_register_terms(struct uvc_device *dev,
{
struct uvc_streaming *stream;
struct uvc_entity *term;
+ struct uvc_chain_entry *entry;
int ret;

- list_for_each_entry(term, &chain->entities, chain) {
+ list_for_each_entry(entry, &chain->entities, chain_entry) {
+ term = entry->entity;
if (UVC_ENTITY_TYPE(term) != UVC_TT_STREAMING)
continue;

diff --git a/drivers/media/usb/uvc/uvc_entity.c b/drivers/media/usb/uvc/uvc_entity.c
index dc56a59..657f49a 100644
--- a/drivers/media/usb/uvc/uvc_entity.c
+++ b/drivers/media/usb/uvc/uvc_entity.c
@@ -104,9 +104,14 @@ static int uvc_mc_init_entity(struct uvc_entity *entity)
int uvc_mc_register_entities(struct uvc_video_chain *chain)
{
struct uvc_entity *entity;
+ struct uvc_chain_entry *entry;
int ret;

- list_for_each_entry(entity, &chain->entities, chain) {
+ list_for_each_entry(entry, &chain->entities, chain_entry) {
+ entity = entry->entity;
+ if (entity->registered)
+ continue;
+
ret = uvc_mc_init_entity(entity);
if (ret < 0) {
uvc_printk(KERN_INFO, "Failed to initialize entity for "
@@ -115,13 +120,19 @@ int uvc_mc_register_entities(struct uvc_video_chain *chain)
}
}

- list_for_each_entry(entity, &chain->entities, chain) {
+ list_for_each_entry(entry, &chain->entities, chain_entry) {
+ entity = entry->entity;
+ if (entity->registered)
+ continue;
+
ret = uvc_mc_register_entity(chain, entity);
if (ret < 0) {
uvc_printk(KERN_INFO, "Failed to register entity for "
"entity %u\n", entity->id);
return ret;
}
+
+ entity->registered = true;
}

return 0;
diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
index 3afff92..a899159 100644
--- a/drivers/media/usb/uvc/uvc_v4l2.c
+++ b/drivers/media/usb/uvc/uvc_v4l2.c
@@ -713,6 +713,7 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
const struct uvc_entity *selector = chain->selector;
struct v4l2_input *input = arg;
struct uvc_entity *iterm = NULL;
+ struct uvc_chain_entry *entry;
u32 index = input->index;
int pin = 0;

@@ -720,14 +721,18 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
(chain->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) {
if (index != 0)
return -EINVAL;
- list_for_each_entry(iterm, &chain->entities, chain) {
+ list_for_each_entry(entry, &chain->entities,
+ chain_entry) {
+ iterm = entry->entity;
if (UVC_ENTITY_IS_ITERM(iterm))
break;
}
pin = iterm->id;
} else if (index < selector->bNrInPins) {
pin = selector->baSourceID[index];
- list_for_each_entry(iterm, &chain->entities, chain) {
+ list_for_each_entry(entry, &chain->entities,
+ chain_entry) {
+ iterm = entry->entity;
if (!UVC_ENTITY_IS_ITERM(iterm))
continue;
if (iterm->id == pin)
diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
index 75e0153..731b378 100644
--- a/drivers/media/usb/uvc/uvcvideo.h
+++ b/drivers/media/usb/uvc/uvcvideo.h
@@ -229,8 +229,8 @@ struct uvc_format_desc {

struct uvc_entity {
struct list_head list; /* Entity as part of a UVC device. */
- struct list_head chain; /* Entity as part of a video device
- * chain. */
+ struct uvc_video_chain *chain; /* Entity as a part of a video device
+ chain. */
unsigned int flags;

__u8 id;
@@ -243,6 +243,7 @@ struct uvc_entity {
unsigned int num_pads;
unsigned int num_links;
struct media_pad *pads;
+ bool registered; /* True if already registered with MC */

union {
struct {
@@ -289,6 +290,12 @@ struct uvc_entity {
struct uvc_control *controls;
};

+struct uvc_chain_entry {
+ struct list_head chain_entry;
+ struct uvc_entity *entity;
+ struct uvc_video_chain *chain;
+};
+
struct uvc_frame {
__u8 bFrameIndex;
__u8 bmCapabilities;
@@ -366,6 +373,12 @@ struct uvc_video_queue {
struct list_head irqqueue;
};

+struct uvc_video_pipeline {
+ struct mutex ctrl_mutex; /* Protects controls in all
+ chains of this pipeline */
+ atomic_t num_chains;
+};
+
struct uvc_video_chain {
struct uvc_device *dev;
struct list_head list;
@@ -374,7 +387,8 @@ struct uvc_video_chain {
struct uvc_entity *processing; /* Processing unit */
struct uvc_entity *selector; /* Selector unit */

- struct mutex ctrl_mutex; /* Protects ctrl.info */
+ struct uvc_video_pipeline *pipeline; /* Pipeline this chain
+ belongs to */

struct v4l2_prio_state prio; /* V4L2 priority state */
u32 caps; /* V4L2 chain-wide caps */
--
1.8.4