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

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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@xxxxxxxxxxxx>
---
 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

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




[Index of Archives]     [Linux Input]     [Video for Linux]     [Gstreamer Embedded]     [Mplayer Users]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux