From 4057ac6ca9a77c4275b34b5925ab5c99557913b1 Mon Sep 17 00:00:00 2001
From: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Date: Wed, 25 Nov 2009 12:00:29 -0300
Subject: [PATCH] V4L/DVB (13505): uvcvideo: Refactor chain scan

Don't handle the first output terminal in a chain in a special way. Use
uvc_scan_chain_entity() like for all other entities, making the chain
scan code more generic.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
---
 drivers/media/video/uvc/uvc_driver.c | 75 ++++++++++++++++------------
 1 file changed, 43 insertions(+), 32 deletions(-)

diff --git a/drivers/media/video/uvc/uvc_driver.c b/drivers/media/video/uvc/uvc_driver.c
index 8d51a6a130b04..e7ce898ad7065 100644
--- a/drivers/media/video/uvc/uvc_driver.c
+++ b/drivers/media/video/uvc/uvc_driver.c
@@ -1265,13 +1265,12 @@ static int uvc_scan_chain_entity(struct uvc_video_chain *chain,
 		break;
 
 	case UVC_TT_STREAMING:
-		if (uvc_trace_param & UVC_TRACE_PROBE)
-			printk(" <- IT %d\n", entity->id);
-
-		if (!UVC_ENTITY_IS_ITERM(entity)) {
-			uvc_trace(UVC_TRACE_DESCR, "Unsupported input "
-				"terminal %u.\n", entity->id);
-			return -1;
+		if (UVC_ENTITY_IS_ITERM(entity)) {
+			if (uvc_trace_param & UVC_TRACE_PROBE)
+				printk(" <- IT %d\n", entity->id);
+		} else {
+			if (uvc_trace_param & UVC_TRACE_PROBE)
+				printk(" OT %d", entity->id);
 		}
 
 		break;
@@ -1351,10 +1350,11 @@ static int uvc_scan_chain_forward(struct uvc_video_chain *chain,
 }
 
 static int uvc_scan_chain_backward(struct uvc_video_chain *chain,
-	struct uvc_entity *entity)
+	struct uvc_entity **_entity)
 {
+	struct uvc_entity *entity = *_entity;
 	struct uvc_entity *term;
-	int id = -1, i;
+	int id = -EINVAL, i;
 
 	switch (UVC_ENTITY_TYPE(entity)) {
 	case UVC_VC_EXTENSION_UNIT:
@@ -1398,34 +1398,49 @@ static int uvc_scan_chain_backward(struct uvc_video_chain *chain,
 
 		id = 0;
 		break;
+
+	case UVC_ITT_VENDOR_SPECIFIC:
+	case UVC_ITT_CAMERA:
+	case UVC_ITT_MEDIA_TRANSPORT_INPUT:
+	case UVC_OTT_VENDOR_SPECIFIC:
+	case UVC_OTT_DISPLAY:
+	case UVC_OTT_MEDIA_TRANSPORT_OUTPUT:
+	case UVC_TT_STREAMING:
+		id = UVC_ENTITY_IS_OTERM(entity) ? entity->output.bSourceID : 0;
+		break;
 	}
 
-	return id;
+	if (id <= 0) {
+		*_entity = NULL;
+		return id;
+	}
+
+	entity = uvc_entity_by_id(chain->dev, id);
+	if (entity == NULL) {
+		uvc_trace(UVC_TRACE_DESCR, "Found reference to "
+			"unknown entity %d.\n", id);
+		return -EINVAL;
+	}
+
+	*_entity = entity;
+	return 0;
 }
 
 static int uvc_scan_chain(struct uvc_video_chain *chain,
-			  struct uvc_entity *oterm)
+			  struct uvc_entity *term)
 {
 	struct uvc_entity *entity, *prev;
-	int id;
 
-	entity = oterm;
-	list_add_tail(&entity->chain, &chain->entities);
-	uvc_trace(UVC_TRACE_PROBE, "Scanning UVC chain: OT %d", entity->id);
+	uvc_trace(UVC_TRACE_PROBE, "Scanning UVC chain:");
 
-	id = entity->output.bSourceID;
-	while (id != 0) {
-		prev = entity;
-		entity = uvc_entity_by_id(chain->dev, id);
-		if (entity == NULL) {
-			uvc_trace(UVC_TRACE_DESCR, "Found reference to "
-				"unknown entity %d.\n", id);
-			return -EINVAL;
-		}
+	entity = term;
+	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", id);
+				"entity %d already in chain.\n", entity->id);
 			return -EINVAL;
 		}
 
@@ -1437,14 +1452,10 @@ static int uvc_scan_chain(struct uvc_video_chain *chain,
 		if (uvc_scan_chain_forward(chain, entity, prev) < 0)
 			return -EINVAL;
 
-		/* Stop when a terminal is found. */
-		if (UVC_ENTITY_IS_TERM(entity))
-			break;
-
 		/* Backward scan */
-		id = uvc_scan_chain_backward(chain, entity);
-		if (id < 0)
-			return id;
+		prev = entity;
+		if (uvc_scan_chain_backward(chain, &entity) < 0)
+			return -EINVAL;
 	}
 
 	return 0;
-- 
2.30.2